18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TUSB6010 USB 2.0 OTG Dual Role controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Nokia Corporation 68c2ecf20Sopenharmony_ci * Tony Lindgren <tony@atomide.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Notes: 98c2ecf20Sopenharmony_ci * - Driver assumes that interface to external host (main CPU) is 108c2ecf20Sopenharmony_ci * configured for NOR FLASH interface instead of VLYNQ serial 118c2ecf20Sopenharmony_ci * interface. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 198c2ecf20Sopenharmony_ci#include <linux/usb.h> 208c2ecf20Sopenharmony_ci#include <linux/irq.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/device.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 258c2ecf20Sopenharmony_ci#include <linux/usb/usb_phy_generic.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "musb_core.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct tusb6010_glue { 308c2ecf20Sopenharmony_ci struct device *dev; 318c2ecf20Sopenharmony_ci struct platform_device *musb; 328c2ecf20Sopenharmony_ci struct platform_device *phy; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define TUSB_REV_MAJOR(reg_val) ((reg_val >> 4) & 0xf) 388c2ecf20Sopenharmony_ci#define TUSB_REV_MINOR(reg_val) (reg_val & 0xf) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Checks the revision. We need to use the DMA register as 3.0 does not 428c2ecf20Sopenharmony_ci * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_cistatic u8 tusb_get_revision(struct musb *musb) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 478c2ecf20Sopenharmony_ci u32 die_id; 488c2ecf20Sopenharmony_ci u8 rev; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci rev = musb_readl(tbase, TUSB_DMA_CTRL_REV) & 0xff; 518c2ecf20Sopenharmony_ci if (TUSB_REV_MAJOR(rev) == 3) { 528c2ecf20Sopenharmony_ci die_id = TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, 538c2ecf20Sopenharmony_ci TUSB_DIDR1_HI)); 548c2ecf20Sopenharmony_ci if (die_id >= TUSB_DIDR1_HI_REV_31) 558c2ecf20Sopenharmony_ci rev |= 1; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return rev; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void tusb_print_revision(struct musb *musb) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 648c2ecf20Sopenharmony_ci u8 rev; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci rev = musb->tusb_revision; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci pr_info("tusb: %s%i.%i %s%i.%i %s%i.%i %s%i.%i %s%i %s%i.%i\n", 698c2ecf20Sopenharmony_ci "prcm", 708c2ecf20Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_PRCM_REV)), 718c2ecf20Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_PRCM_REV)), 728c2ecf20Sopenharmony_ci "int", 738c2ecf20Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_INT_CTRL_REV)), 748c2ecf20Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_INT_CTRL_REV)), 758c2ecf20Sopenharmony_ci "gpio", 768c2ecf20Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_GPIO_REV)), 778c2ecf20Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_GPIO_REV)), 788c2ecf20Sopenharmony_ci "dma", 798c2ecf20Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)), 808c2ecf20Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)), 818c2ecf20Sopenharmony_ci "dieid", 828c2ecf20Sopenharmony_ci TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, TUSB_DIDR1_HI)), 838c2ecf20Sopenharmony_ci "rev", 848c2ecf20Sopenharmony_ci TUSB_REV_MAJOR(rev), TUSB_REV_MINOR(rev)); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define WBUS_QUIRK_MASK (TUSB_PHY_OTG_CTRL_TESTM2 | TUSB_PHY_OTG_CTRL_TESTM1 \ 888c2ecf20Sopenharmony_ci | TUSB_PHY_OTG_CTRL_TESTM0) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Workaround for spontaneous WBUS wake-up issue #2 for tusb3.0. 928c2ecf20Sopenharmony_ci * Disables power detection in PHY for the duration of idle. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic void tusb_wbus_quirk(struct musb *musb, int enabled) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 978c2ecf20Sopenharmony_ci static u32 phy_otg_ctrl, phy_otg_ena; 988c2ecf20Sopenharmony_ci u32 tmp; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (enabled) { 1018c2ecf20Sopenharmony_ci phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL); 1028c2ecf20Sopenharmony_ci phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); 1038c2ecf20Sopenharmony_ci tmp = TUSB_PHY_OTG_CTRL_WRPROTECT 1048c2ecf20Sopenharmony_ci | phy_otg_ena | WBUS_QUIRK_MASK; 1058c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); 1068c2ecf20Sopenharmony_ci tmp = phy_otg_ena & ~WBUS_QUIRK_MASK; 1078c2ecf20Sopenharmony_ci tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2; 1088c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); 1098c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", 1108c2ecf20Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL), 1118c2ecf20Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); 1128c2ecf20Sopenharmony_ci } else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE) 1138c2ecf20Sopenharmony_ci & TUSB_PHY_OTG_CTRL_TESTM2) { 1148c2ecf20Sopenharmony_ci tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl; 1158c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); 1168c2ecf20Sopenharmony_ci tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena; 1178c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); 1188c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", 1198c2ecf20Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL), 1208c2ecf20Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); 1218c2ecf20Sopenharmony_ci phy_otg_ctrl = 0; 1228c2ecf20Sopenharmony_ci phy_otg_ena = 0; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic u32 tusb_fifo_offset(u8 epnum) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return 0x200 + (epnum * 0x20); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic u32 tusb_ep_offset(u8 epnum, u16 offset) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci return 0x10 + offset; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* TUSB mapping: "flat" plus ep0 special cases */ 1378c2ecf20Sopenharmony_cistatic void tusb_ep_select(void __iomem *mbase, u8 epnum) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci musb_writeb(mbase, MUSB_INDEX, epnum); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* 1438c2ecf20Sopenharmony_ci * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistatic u8 tusb_readb(void __iomem *addr, u32 offset) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci u16 tmp; 1488c2ecf20Sopenharmony_ci u8 val; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci tmp = __raw_readw(addr + (offset & ~1)); 1518c2ecf20Sopenharmony_ci if (offset & 1) 1528c2ecf20Sopenharmony_ci val = (tmp >> 8); 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci val = tmp & 0xff; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return val; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void tusb_writeb(void __iomem *addr, u32 offset, u8 data) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci u16 tmp; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci tmp = __raw_readw(addr + (offset & ~1)); 1648c2ecf20Sopenharmony_ci if (offset & 1) 1658c2ecf20Sopenharmony_ci tmp = (data << 8) | (tmp & 0xff); 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci tmp = (tmp & 0xff00) | data; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci __raw_writew(tmp, addr + (offset & ~1)); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * TUSB 6010 may use a parallel bus that doesn't support byte ops; 1748c2ecf20Sopenharmony_ci * so both loading and unloading FIFOs need explicit byte counts. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic inline void 1788c2ecf20Sopenharmony_citusb_fifo_write_unaligned(void __iomem *fifo, const u8 *buf, u16 len) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci u32 val; 1818c2ecf20Sopenharmony_ci int i; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (len > 4) { 1848c2ecf20Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 1858c2ecf20Sopenharmony_ci memcpy(&val, buf, 4); 1868c2ecf20Sopenharmony_ci musb_writel(fifo, 0, val); 1878c2ecf20Sopenharmony_ci buf += 4; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci len %= 4; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci if (len > 0) { 1928c2ecf20Sopenharmony_ci /* Write the rest 1 - 3 bytes to FIFO */ 1938c2ecf20Sopenharmony_ci val = 0; 1948c2ecf20Sopenharmony_ci memcpy(&val, buf, len); 1958c2ecf20Sopenharmony_ci musb_writel(fifo, 0, val); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic inline void tusb_fifo_read_unaligned(void __iomem *fifo, 2008c2ecf20Sopenharmony_ci void *buf, u16 len) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u32 val; 2038c2ecf20Sopenharmony_ci int i; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (len > 4) { 2068c2ecf20Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 2078c2ecf20Sopenharmony_ci val = musb_readl(fifo, 0); 2088c2ecf20Sopenharmony_ci memcpy(buf, &val, 4); 2098c2ecf20Sopenharmony_ci buf += 4; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci len %= 4; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci if (len > 0) { 2148c2ecf20Sopenharmony_ci /* Read the rest 1 - 3 bytes from FIFO */ 2158c2ecf20Sopenharmony_ci val = musb_readl(fifo, 0); 2168c2ecf20Sopenharmony_ci memcpy(buf, &val, len); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void tusb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct musb *musb = hw_ep->musb; 2238c2ecf20Sopenharmony_ci void __iomem *ep_conf = hw_ep->conf; 2248c2ecf20Sopenharmony_ci void __iomem *fifo = hw_ep->fifo; 2258c2ecf20Sopenharmony_ci u8 epnum = hw_ep->epnum; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci prefetch(buf); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 2308c2ecf20Sopenharmony_ci 'T', epnum, fifo, len, buf); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (epnum) 2338c2ecf20Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_TX_OFFSET, 2348c2ecf20Sopenharmony_ci TUSB_EP_CONFIG_XFR_SIZE(len)); 2358c2ecf20Sopenharmony_ci else 2368c2ecf20Sopenharmony_ci musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_DIR_TX | 2378c2ecf20Sopenharmony_ci TUSB_EP0_CONFIG_XFR_SIZE(len)); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (likely((0x01 & (unsigned long) buf) == 0)) { 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Best case is 32bit-aligned destination address */ 2428c2ecf20Sopenharmony_ci if ((0x02 & (unsigned long) buf) == 0) { 2438c2ecf20Sopenharmony_ci if (len >= 4) { 2448c2ecf20Sopenharmony_ci iowrite32_rep(fifo, buf, len >> 2); 2458c2ecf20Sopenharmony_ci buf += (len & ~0x03); 2468c2ecf20Sopenharmony_ci len &= 0x03; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } else { 2498c2ecf20Sopenharmony_ci if (len >= 2) { 2508c2ecf20Sopenharmony_ci u32 val; 2518c2ecf20Sopenharmony_ci int i; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Cannot use writesw, fifo is 32-bit */ 2548c2ecf20Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 2558c2ecf20Sopenharmony_ci val = (u32)(*(u16 *)buf); 2568c2ecf20Sopenharmony_ci buf += 2; 2578c2ecf20Sopenharmony_ci val |= (*(u16 *)buf) << 16; 2588c2ecf20Sopenharmony_ci buf += 2; 2598c2ecf20Sopenharmony_ci musb_writel(fifo, 0, val); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci len &= 0x03; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (len > 0) 2678c2ecf20Sopenharmony_ci tusb_fifo_write_unaligned(fifo, buf, len); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void tusb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct musb *musb = hw_ep->musb; 2738c2ecf20Sopenharmony_ci void __iomem *ep_conf = hw_ep->conf; 2748c2ecf20Sopenharmony_ci void __iomem *fifo = hw_ep->fifo; 2758c2ecf20Sopenharmony_ci u8 epnum = hw_ep->epnum; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 2788c2ecf20Sopenharmony_ci 'R', epnum, fifo, len, buf); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (epnum) 2818c2ecf20Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_RX_OFFSET, 2828c2ecf20Sopenharmony_ci TUSB_EP_CONFIG_XFR_SIZE(len)); 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_XFR_SIZE(len)); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (likely((0x01 & (unsigned long) buf) == 0)) { 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Best case is 32bit-aligned destination address */ 2898c2ecf20Sopenharmony_ci if ((0x02 & (unsigned long) buf) == 0) { 2908c2ecf20Sopenharmony_ci if (len >= 4) { 2918c2ecf20Sopenharmony_ci ioread32_rep(fifo, buf, len >> 2); 2928c2ecf20Sopenharmony_ci buf += (len & ~0x03); 2938c2ecf20Sopenharmony_ci len &= 0x03; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } else { 2968c2ecf20Sopenharmony_ci if (len >= 2) { 2978c2ecf20Sopenharmony_ci u32 val; 2988c2ecf20Sopenharmony_ci int i; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Cannot use readsw, fifo is 32-bit */ 3018c2ecf20Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 3028c2ecf20Sopenharmony_ci val = musb_readl(fifo, 0); 3038c2ecf20Sopenharmony_ci *(u16 *)buf = (u16)(val & 0xffff); 3048c2ecf20Sopenharmony_ci buf += 2; 3058c2ecf20Sopenharmony_ci *(u16 *)buf = (u16)(val >> 16); 3068c2ecf20Sopenharmony_ci buf += 2; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci len &= 0x03; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (len > 0) 3148c2ecf20Sopenharmony_ci tusb_fifo_read_unaligned(fifo, buf, len); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic struct musb *the_musb; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* This is used by gadget drivers, and OTG transceiver logic, allowing 3208c2ecf20Sopenharmony_ci * at most mA current to be drawn from VBUS during a Default-B session 3218c2ecf20Sopenharmony_ci * (that is, while VBUS exceeds 4.4V). In Default-A (including pure host 3228c2ecf20Sopenharmony_ci * mode), or low power Default-B sessions, something else supplies power. 3238c2ecf20Sopenharmony_ci * Caller must take care of locking. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_cistatic int tusb_draw_power(struct usb_phy *x, unsigned mA) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct musb *musb = the_musb; 3288c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 3298c2ecf20Sopenharmony_ci u32 reg; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* tps65030 seems to consume max 100mA, with maybe 60mA available 3328c2ecf20Sopenharmony_ci * (measured on one board) for things other than tps and tusb. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Boards sharing the CPU clock with CLKIN will need to prevent 3358c2ecf20Sopenharmony_ci * certain idle sleep states while the USB link is active. 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }. 3388c2ecf20Sopenharmony_ci * The actual current usage would be very board-specific. For now, 3398c2ecf20Sopenharmony_ci * it's simpler to just use an aggregate (also board-specific). 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (x->otg->default_a || mA < (musb->min_power << 1)) 3428c2ecf20Sopenharmony_ci mA = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_MNGMT); 3458c2ecf20Sopenharmony_ci if (mA) { 3468c2ecf20Sopenharmony_ci musb->is_bus_powered = 1; 3478c2ecf20Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN; 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci musb->is_bus_powered = 0; 3508c2ecf20Sopenharmony_ci reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, reg); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "draw max %d mA VBUS\n", mA); 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* workaround for issue 13: change clock during chip idle 3598c2ecf20Sopenharmony_ci * (to be fixed in rev3 silicon) ... symptoms include disconnect 3608c2ecf20Sopenharmony_ci * or looping suspend/resume cycles 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_cistatic void tusb_set_clock_source(struct musb *musb, unsigned mode) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 3658c2ecf20Sopenharmony_ci u32 reg; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_CONF); 3688c2ecf20Sopenharmony_ci reg &= ~TUSB_PRCM_CONF_SYS_CLKSEL(0x3); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* 0 = refclk (clkin, XI) 3718c2ecf20Sopenharmony_ci * 1 = PHY 60 MHz (internal PLL) 3728c2ecf20Sopenharmony_ci * 2 = not supported 3738c2ecf20Sopenharmony_ci * 3 = what? 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci if (mode > 0) 3768c2ecf20Sopenharmony_ci reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_CONF, reg); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* FIXME tusb6010_platform_retime(mode == 0); */ 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* 3848c2ecf20Sopenharmony_ci * Idle TUSB6010 until next wake-up event; NOR access always wakes. 3858c2ecf20Sopenharmony_ci * Other code ensures that we idle unless we're connected _and_ the 3868c2ecf20Sopenharmony_ci * USB link is not suspended ... and tells us the relevant wakeup 3878c2ecf20Sopenharmony_ci * events. SW_EN for voltage is handled separately. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 3928c2ecf20Sopenharmony_ci u32 reg; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if ((wakeup_enables & TUSB_PRCM_WBUS) 3958c2ecf20Sopenharmony_ci && (musb->tusb_revision == TUSB_REV_30)) 3968c2ecf20Sopenharmony_ci tusb_wbus_quirk(musb, 1); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci tusb_set_clock_source(musb, 0); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci wakeup_enables |= TUSB_PRCM_WNORCS; 4018c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* REVISIT writeup of WID implies that if WID set and ID is grounded, 4048c2ecf20Sopenharmony_ci * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared. 4058c2ecf20Sopenharmony_ci * Presumably that's mostly to save power, hence WID is immaterial ... 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_MNGMT); 4098c2ecf20Sopenharmony_ci /* issue 4: when driving vbus, use hipower (vbus_det) comparator */ 4108c2ecf20Sopenharmony_ci if (is_host_active(musb)) { 4118c2ecf20Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; 4128c2ecf20Sopenharmony_ci reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN; 4138c2ecf20Sopenharmony_ci } else { 4148c2ecf20Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN; 4158c2ecf20Sopenharmony_ci reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE; 4188c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, reg); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* 4248c2ecf20Sopenharmony_ci * Updates cable VBUS status. Caller must take care of locking. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_cistatic int tusb_musb_vbus_status(struct musb *musb) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 4298c2ecf20Sopenharmony_ci u32 otg_stat, prcm_mngmt; 4308c2ecf20Sopenharmony_ci int ret = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 4338c2ecf20Sopenharmony_ci prcm_mngmt = musb_readl(tbase, TUSB_PRCM_MNGMT); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Temporarily enable VBUS detection if it was disabled for 4368c2ecf20Sopenharmony_ci * suspend mode. Unless it's enabled otg_stat and devctl will 4378c2ecf20Sopenharmony_ci * not show correct VBUS state. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci if (!(prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) { 4408c2ecf20Sopenharmony_ci u32 tmp = prcm_mngmt; 4418c2ecf20Sopenharmony_ci tmp |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; 4428c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, tmp); 4438c2ecf20Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 4448c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, prcm_mngmt); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) 4488c2ecf20Sopenharmony_ci ret = 1; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return ret; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void musb_do_idle(struct timer_list *t) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct musb *musb = from_timer(musb, t, dev_timer); 4568c2ecf20Sopenharmony_ci unsigned long flags; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci switch (musb->xceiv->otg->state) { 4618c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 4628c2ecf20Sopenharmony_ci if ((musb->a_wait_bcon != 0) 4638c2ecf20Sopenharmony_ci && (musb->idle_timeout == 0 4648c2ecf20Sopenharmony_ci || time_after(jiffies, musb->idle_timeout))) { 4658c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n", 4668c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state)); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci fallthrough; 4698c2ecf20Sopenharmony_ci case OTG_STATE_A_IDLE: 4708c2ecf20Sopenharmony_ci tusb_musb_set_vbus(musb, 0); 4718c2ecf20Sopenharmony_ci default: 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (!musb->is_active) { 4768c2ecf20Sopenharmony_ci u32 wakeups; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* wait until hub_wq handles port change status */ 4798c2ecf20Sopenharmony_ci if (is_host_active(musb) && (musb->port1_status >> 16)) 4808c2ecf20Sopenharmony_ci goto done; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!musb->gadget_driver) { 4838c2ecf20Sopenharmony_ci wakeups = 0; 4848c2ecf20Sopenharmony_ci } else { 4858c2ecf20Sopenharmony_ci wakeups = TUSB_PRCM_WHOSTDISCON 4868c2ecf20Sopenharmony_ci | TUSB_PRCM_WBUS 4878c2ecf20Sopenharmony_ci | TUSB_PRCM_WVBUS; 4888c2ecf20Sopenharmony_ci wakeups |= TUSB_PRCM_WID; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci tusb_allow_idle(musb, wakeups); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_cidone: 4938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* 4978c2ecf20Sopenharmony_ci * Maybe put TUSB6010 into idle mode mode depending on USB link status, 4988c2ecf20Sopenharmony_ci * like "disconnected" or "suspended". We'll be woken out of it by 4998c2ecf20Sopenharmony_ci * connect, resume, or disconnect. 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Needs to be called as the last function everywhere where there is 5028c2ecf20Sopenharmony_ci * register access to TUSB6010 because of NOR flash wake-up. 5038c2ecf20Sopenharmony_ci * Caller should own controller spinlock. 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * Delay because peripheral enables D+ pullup 3msec after SE0, and 5068c2ecf20Sopenharmony_ci * we don't want to treat that full speed J as a wakeup event. 5078c2ecf20Sopenharmony_ci * ... peripherals must draw only suspend current after 10 msec. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_cistatic void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci unsigned long default_timeout = jiffies + msecs_to_jiffies(3); 5128c2ecf20Sopenharmony_ci static unsigned long last_timer; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (timeout == 0) 5158c2ecf20Sopenharmony_ci timeout = default_timeout; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Never idle if active, or when VBUS timeout is not set as host */ 5188c2ecf20Sopenharmony_ci if (musb->is_active || ((musb->a_wait_bcon == 0) 5198c2ecf20Sopenharmony_ci && (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) { 5208c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%s active, deleting timer\n", 5218c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state)); 5228c2ecf20Sopenharmony_ci del_timer(&musb->dev_timer); 5238c2ecf20Sopenharmony_ci last_timer = jiffies; 5248c2ecf20Sopenharmony_ci return; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (time_after(last_timer, timeout)) { 5288c2ecf20Sopenharmony_ci if (!timer_pending(&musb->dev_timer)) 5298c2ecf20Sopenharmony_ci last_timer = timeout; 5308c2ecf20Sopenharmony_ci else { 5318c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); 5328c2ecf20Sopenharmony_ci return; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci last_timer = timeout; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", 5388c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), 5398c2ecf20Sopenharmony_ci (unsigned long)jiffies_to_msecs(timeout - jiffies)); 5408c2ecf20Sopenharmony_ci mod_timer(&musb->dev_timer, timeout); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/* ticks of 60 MHz clock */ 5448c2ecf20Sopenharmony_ci#define DEVCLOCK 60000000 5458c2ecf20Sopenharmony_ci#define OTG_TIMER_MS(msecs) ((msecs) \ 5468c2ecf20Sopenharmony_ci ? (TUSB_DEV_OTG_TIMER_VAL((DEVCLOCK/1000)*(msecs)) \ 5478c2ecf20Sopenharmony_ci | TUSB_DEV_OTG_TIMER_ENABLE) \ 5488c2ecf20Sopenharmony_ci : 0) 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 5538c2ecf20Sopenharmony_ci u32 conf, prcm, timer; 5548c2ecf20Sopenharmony_ci u8 devctl; 5558c2ecf20Sopenharmony_ci struct usb_otg *otg = musb->xceiv->otg; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* HDRC controls CPEN, but beware current surges during device 5588c2ecf20Sopenharmony_ci * connect. They can trigger transient overcurrent conditions 5598c2ecf20Sopenharmony_ci * that must be ignored. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci prcm = musb_readl(tbase, TUSB_PRCM_MNGMT); 5638c2ecf20Sopenharmony_ci conf = musb_readl(tbase, TUSB_DEV_CONF); 5648c2ecf20Sopenharmony_ci devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (is_on) { 5678c2ecf20Sopenharmony_ci timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE); 5688c2ecf20Sopenharmony_ci otg->default_a = 1; 5698c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; 5708c2ecf20Sopenharmony_ci devctl |= MUSB_DEVCTL_SESSION; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci conf |= TUSB_DEV_CONF_USB_HOST_MODE; 5738c2ecf20Sopenharmony_ci MUSB_HST_MODE(musb); 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci u32 otg_stat; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci timer = 0; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* If ID pin is grounded, we want to be a_idle */ 5808c2ecf20Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 5818c2ecf20Sopenharmony_ci if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) { 5828c2ecf20Sopenharmony_ci switch (musb->xceiv->otg->state) { 5838c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_VRISE: 5848c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 5858c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_VFALL: 5888c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_IDLE; 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci default: 5918c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_IDLE; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci musb->is_active = 0; 5948c2ecf20Sopenharmony_ci otg->default_a = 1; 5958c2ecf20Sopenharmony_ci MUSB_HST_MODE(musb); 5968c2ecf20Sopenharmony_ci } else { 5978c2ecf20Sopenharmony_ci musb->is_active = 0; 5988c2ecf20Sopenharmony_ci otg->default_a = 0; 5998c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_B_IDLE; 6008c2ecf20Sopenharmony_ci MUSB_DEV_MODE(musb); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci devctl &= ~MUSB_DEVCTL_SESSION; 6048c2ecf20Sopenharmony_ci conf &= ~TUSB_DEV_CONF_USB_HOST_MODE; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, prcm); 6098c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DEV_OTG_TIMER, timer); 6108c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DEV_CONF, conf); 6118c2ecf20Sopenharmony_ci musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", 6148c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), 6158c2ecf20Sopenharmony_ci musb_readb(musb->mregs, MUSB_DEVCTL), 6168c2ecf20Sopenharmony_ci musb_readl(tbase, TUSB_DEV_OTG_STAT), 6178c2ecf20Sopenharmony_ci conf, prcm); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/* 6218c2ecf20Sopenharmony_ci * Sets the mode to OTG, peripheral or host by changing the ID detection. 6228c2ecf20Sopenharmony_ci * Caller must take care of locking. 6238c2ecf20Sopenharmony_ci * 6248c2ecf20Sopenharmony_ci * Note that if a mini-A cable is plugged in the ID line will stay down as 6258c2ecf20Sopenharmony_ci * the weak ID pull-up is not able to pull the ID up. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_cistatic int tusb_musb_set_mode(struct musb *musb, u8 musb_mode) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 6308c2ecf20Sopenharmony_ci u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 6338c2ecf20Sopenharmony_ci phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL); 6348c2ecf20Sopenharmony_ci phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); 6358c2ecf20Sopenharmony_ci dev_conf = musb_readl(tbase, TUSB_DEV_CONF); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci switch (musb_mode) { 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci case MUSB_HOST: /* Disable PHY ID detect, ground ID */ 6408c2ecf20Sopenharmony_ci phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 6418c2ecf20Sopenharmony_ci phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 6428c2ecf20Sopenharmony_ci dev_conf |= TUSB_DEV_CONF_ID_SEL; 6438c2ecf20Sopenharmony_ci dev_conf &= ~TUSB_DEV_CONF_SOFT_ID; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci case MUSB_PERIPHERAL: /* Disable PHY ID detect, keep ID pull-up on */ 6468c2ecf20Sopenharmony_ci phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 6478c2ecf20Sopenharmony_ci phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 6488c2ecf20Sopenharmony_ci dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID); 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case MUSB_OTG: /* Use PHY ID detection */ 6518c2ecf20Sopenharmony_ci phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 6528c2ecf20Sopenharmony_ci phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 6538c2ecf20Sopenharmony_ci dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID); 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci default: 6578c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode); 6588c2ecf20Sopenharmony_ci return -EINVAL; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, 6628c2ecf20Sopenharmony_ci TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl); 6638c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, 6648c2ecf20Sopenharmony_ci TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena); 6658c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DEV_CONF, dev_conf); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 6688c2ecf20Sopenharmony_ci if ((musb_mode == MUSB_PERIPHERAL) && 6698c2ecf20Sopenharmony_ci !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) 6708c2ecf20Sopenharmony_ci INFO("Cannot be peripheral with mini-A cable " 6718c2ecf20Sopenharmony_ci "otg_stat: %08x\n", otg_stat); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic inline unsigned long 6778c2ecf20Sopenharmony_citusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci u32 otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 6808c2ecf20Sopenharmony_ci unsigned long idle_timeout = 0; 6818c2ecf20Sopenharmony_ci struct usb_otg *otg = musb->xceiv->otg; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* ID pin */ 6848c2ecf20Sopenharmony_ci if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) { 6858c2ecf20Sopenharmony_ci int default_a; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS); 6888c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B'); 6898c2ecf20Sopenharmony_ci otg->default_a = default_a; 6908c2ecf20Sopenharmony_ci tusb_musb_set_vbus(musb, default_a); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* Don't allow idling immediately */ 6938c2ecf20Sopenharmony_ci if (default_a) 6948c2ecf20Sopenharmony_ci idle_timeout = jiffies + (HZ * 3); 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* VBUS state change */ 6988c2ecf20Sopenharmony_ci if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) { 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* B-dev state machine: no vbus ~= disconnect */ 7018c2ecf20Sopenharmony_ci if (!otg->default_a) { 7028c2ecf20Sopenharmony_ci /* ? musb_root_disconnect(musb); */ 7038c2ecf20Sopenharmony_ci musb->port1_status &= 7048c2ecf20Sopenharmony_ci ~(USB_PORT_STAT_CONNECTION 7058c2ecf20Sopenharmony_ci | USB_PORT_STAT_ENABLE 7068c2ecf20Sopenharmony_ci | USB_PORT_STAT_LOW_SPEED 7078c2ecf20Sopenharmony_ci | USB_PORT_STAT_HIGH_SPEED 7088c2ecf20Sopenharmony_ci | USB_PORT_STAT_TEST 7098c2ecf20Sopenharmony_ci ); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) { 7128c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n"); 7138c2ecf20Sopenharmony_ci if (musb->xceiv->otg->state != OTG_STATE_B_IDLE) { 7148c2ecf20Sopenharmony_ci /* INTR_DISCONNECT can hide... */ 7158c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_B_IDLE; 7168c2ecf20Sopenharmony_ci musb->int_usb |= MUSB_INTR_DISCONNECT; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci musb->is_active = 0; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", 7218c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), otg_stat); 7228c2ecf20Sopenharmony_ci idle_timeout = jiffies + (1 * HZ); 7238c2ecf20Sopenharmony_ci schedule_delayed_work(&musb->irq_work, 0); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci } else /* A-dev state machine */ { 7268c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", 7278c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), otg_stat); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci switch (musb->xceiv->otg->state) { 7308c2ecf20Sopenharmony_ci case OTG_STATE_A_IDLE: 7318c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "Got SRP, turning on VBUS\n"); 7328c2ecf20Sopenharmony_ci musb_platform_set_vbus(musb, 1); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* CONNECT can wake if a_wait_bcon is set */ 7358c2ecf20Sopenharmony_ci if (musb->a_wait_bcon != 0) 7368c2ecf20Sopenharmony_ci musb->is_active = 0; 7378c2ecf20Sopenharmony_ci else 7388c2ecf20Sopenharmony_ci musb->is_active = 1; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* 7418c2ecf20Sopenharmony_ci * OPT FS A TD.4.6 needs few seconds for 7428c2ecf20Sopenharmony_ci * A_WAIT_VRISE 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci idle_timeout = jiffies + (2 * HZ); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_VRISE: 7488c2ecf20Sopenharmony_ci /* ignore; A-session-valid < VBUS_VALID/2, 7498c2ecf20Sopenharmony_ci * we monitor this with the timer 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_VFALL: 7538c2ecf20Sopenharmony_ci /* REVISIT this irq triggers during short 7548c2ecf20Sopenharmony_ci * spikes caused by enumeration ... 7558c2ecf20Sopenharmony_ci */ 7568c2ecf20Sopenharmony_ci if (musb->vbuserr_retry) { 7578c2ecf20Sopenharmony_ci musb->vbuserr_retry--; 7588c2ecf20Sopenharmony_ci tusb_musb_set_vbus(musb, 1); 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci musb->vbuserr_retry 7618c2ecf20Sopenharmony_ci = VBUSERR_RETRY_COUNT; 7628c2ecf20Sopenharmony_ci tusb_musb_set_vbus(musb, 0); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci break; 7658c2ecf20Sopenharmony_ci default: 7668c2ecf20Sopenharmony_ci break; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* OTG timer expiration */ 7728c2ecf20Sopenharmony_ci if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { 7738c2ecf20Sopenharmony_ci u8 devctl; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "%s timer, %03x\n", 7768c2ecf20Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), otg_stat); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci switch (musb->xceiv->otg->state) { 7798c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_VRISE: 7808c2ecf20Sopenharmony_ci /* VBUS has probably been valid for a while now, 7818c2ecf20Sopenharmony_ci * but may well have bounced out of range a bit 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ci devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 7848c2ecf20Sopenharmony_ci if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { 7858c2ecf20Sopenharmony_ci if ((devctl & MUSB_DEVCTL_VBUS) 7868c2ecf20Sopenharmony_ci != MUSB_DEVCTL_VBUS) { 7878c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "devctl %02x\n", devctl); 7888c2ecf20Sopenharmony_ci break; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; 7918c2ecf20Sopenharmony_ci musb->is_active = 0; 7928c2ecf20Sopenharmony_ci idle_timeout = jiffies 7938c2ecf20Sopenharmony_ci + msecs_to_jiffies(musb->a_wait_bcon); 7948c2ecf20Sopenharmony_ci } else { 7958c2ecf20Sopenharmony_ci /* REVISIT report overcurrent to hub? */ 7968c2ecf20Sopenharmony_ci ERR("vbus too slow, devctl %02x\n", devctl); 7978c2ecf20Sopenharmony_ci tusb_musb_set_vbus(musb, 0); 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 8018c2ecf20Sopenharmony_ci if (musb->a_wait_bcon != 0) 8028c2ecf20Sopenharmony_ci idle_timeout = jiffies 8038c2ecf20Sopenharmony_ci + msecs_to_jiffies(musb->a_wait_bcon); 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci case OTG_STATE_A_SUSPEND: 8068c2ecf20Sopenharmony_ci break; 8078c2ecf20Sopenharmony_ci case OTG_STATE_B_WAIT_ACON: 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci default: 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci schedule_delayed_work(&musb->irq_work, 0); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return idle_timeout; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic irqreturn_t tusb_musb_interrupt(int irq, void *__hci) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct musb *musb = __hci; 8218c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 8228c2ecf20Sopenharmony_ci unsigned long flags, idle_timeout = 0; 8238c2ecf20Sopenharmony_ci u32 int_mask, int_src; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* Mask all interrupts to allow using both edge and level GPIO irq */ 8288c2ecf20Sopenharmony_ci int_mask = musb_readl(tbase, TUSB_INT_MASK); 8298c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS; 8328c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "TUSB IRQ %08x\n", int_src); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci musb->int_usb = (u8) int_src; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* Acknowledge wake-up source interrupts */ 8378c2ecf20Sopenharmony_ci if (int_src & TUSB_INT_SRC_DEV_WAKEUP) { 8388c2ecf20Sopenharmony_ci u32 reg; 8398c2ecf20Sopenharmony_ci u32 i; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (musb->tusb_revision == TUSB_REV_30) 8428c2ecf20Sopenharmony_ci tusb_wbus_quirk(musb, 0); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* there are issues re-locking the PLL on wakeup ... */ 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* work around issue 8 */ 8478c2ecf20Sopenharmony_ci for (i = 0xf7f7f7; i > 0xf7f7f7 - 1000; i--) { 8488c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_SCRATCH_PAD, 0); 8498c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_SCRATCH_PAD, i); 8508c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_SCRATCH_PAD); 8518c2ecf20Sopenharmony_ci if (reg == i) 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "TUSB NOR not ready\n"); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* work around issue 13 (2nd half) */ 8578c2ecf20Sopenharmony_ci tusb_set_clock_source(musb, 1); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_WAKEUP_SOURCE); 8608c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_WAKEUP_CLEAR, reg); 8618c2ecf20Sopenharmony_ci if (reg & ~TUSB_PRCM_WNORCS) { 8628c2ecf20Sopenharmony_ci musb->is_active = 1; 8638c2ecf20Sopenharmony_ci schedule_delayed_work(&musb->irq_work, 0); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "wake %sactive %02x\n", 8668c2ecf20Sopenharmony_ci musb->is_active ? "" : "in", reg); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */ 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (int_src & TUSB_INT_SRC_USB_IP_CONN) 8728c2ecf20Sopenharmony_ci del_timer(&musb->dev_timer); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* OTG state change reports (annoyingly) not issued by Mentor core */ 8758c2ecf20Sopenharmony_ci if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG 8768c2ecf20Sopenharmony_ci | TUSB_INT_SRC_OTG_TIMEOUT 8778c2ecf20Sopenharmony_ci | TUSB_INT_SRC_ID_STATUS_CHNG)) 8788c2ecf20Sopenharmony_ci idle_timeout = tusb_otg_ints(musb, int_src, tbase); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* 8818c2ecf20Sopenharmony_ci * Just clear the DMA interrupt if it comes as the completion for both 8828c2ecf20Sopenharmony_ci * TX and RX is handled by the DMA callback in tusb6010_omap 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) { 8858c2ecf20Sopenharmony_ci u32 dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); 8888c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_CLEAR, dma_src); 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* EP interrupts. In OCP mode tusb6010 mirrors the MUSB interrupts */ 8928c2ecf20Sopenharmony_ci if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX)) { 8938c2ecf20Sopenharmony_ci u32 musb_src = musb_readl(tbase, TUSB_USBIP_INT_SRC); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_CLEAR, musb_src); 8968c2ecf20Sopenharmony_ci musb->int_rx = (((musb_src >> 16) & 0xffff) << 1); 8978c2ecf20Sopenharmony_ci musb->int_tx = (musb_src & 0xffff); 8988c2ecf20Sopenharmony_ci } else { 8998c2ecf20Sopenharmony_ci musb->int_rx = 0; 9008c2ecf20Sopenharmony_ci musb->int_tx = 0; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff)) 9048c2ecf20Sopenharmony_ci musb_interrupt(musb); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* Acknowledge TUSB interrupts. Clear only non-reserved bits */ 9078c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_SRC_CLEAR, 9088c2ecf20Sopenharmony_ci int_src & ~TUSB_INT_MASK_RESERVED_BITS); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci tusb_musb_try_idle(musb, idle_timeout); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, int_mask); 9138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic int dma_off; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* 9218c2ecf20Sopenharmony_ci * Enables TUSB6010. Caller must take care of locking. 9228c2ecf20Sopenharmony_ci * REVISIT: 9238c2ecf20Sopenharmony_ci * - Check what is unnecessary in MGC_HdrcStart() 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_cistatic void tusb_musb_enable(struct musb *musb) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Setup TUSB6010 main interrupt mask. Enable all interrupts except SOF. 9308c2ecf20Sopenharmony_ci * REVISIT: Enable and deal with TUSB_INT_SRC_USB_IP_SOF */ 9318c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, TUSB_INT_SRC_USB_IP_SOF); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci /* Setup TUSB interrupt, disable DMA and GPIO interrupts */ 9348c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_MASK, 0); 9358c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); 9368c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* Clear all subsystem interrups */ 9398c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_CLEAR, 0x7fffffff); 9408c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_CLEAR, 0x7fffffff); 9418c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_INT_CLEAR, 0x1ff); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Acknowledge pending interrupt(s) */ 9448c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_SRC_CLEAR, ~TUSB_INT_MASK_RESERVED_BITS); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* Only 0 clock cycles for minimum interrupt de-assertion time and 9478c2ecf20Sopenharmony_ci * interrupt polarity active low seems to work reliably here */ 9488c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_CTRL_CONF, 9498c2ecf20Sopenharmony_ci TUSB_INT_CTRL_CONF_INT_RELCYC(0)); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci irq_set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* maybe force into the Default-A OTG state machine */ 9548c2ecf20Sopenharmony_ci if (!(musb_readl(tbase, TUSB_DEV_OTG_STAT) 9558c2ecf20Sopenharmony_ci & TUSB_DEV_OTG_STAT_ID_STATUS)) 9568c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_SRC_SET, 9578c2ecf20Sopenharmony_ci TUSB_INT_SRC_ID_STATUS_CHNG); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (is_dma_capable() && dma_off) 9608c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s %s: dma not reactivated\n", 9618c2ecf20Sopenharmony_ci __FILE__, __func__); 9628c2ecf20Sopenharmony_ci else 9638c2ecf20Sopenharmony_ci dma_off = 1; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci/* 9678c2ecf20Sopenharmony_ci * Disables TUSB6010. Caller must take care of locking. 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_cistatic void tusb_musb_disable(struct musb *musb) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* FIXME stop DMA, IRQs, timers, ... */ 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* disable all IRQs */ 9768c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); 9778c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_MASK, 0x7fffffff); 9788c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); 9798c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci del_timer(&musb->dev_timer); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (is_dma_capable() && !dma_off) { 9848c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s %s: dma still active\n", 9858c2ecf20Sopenharmony_ci __FILE__, __func__); 9868c2ecf20Sopenharmony_ci dma_off = 1; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci/* 9918c2ecf20Sopenharmony_ci * Sets up TUSB6010 CPU interface specific signals and registers 9928c2ecf20Sopenharmony_ci * Note: Settings optimized for OMAP24xx 9938c2ecf20Sopenharmony_ci */ 9948c2ecf20Sopenharmony_cistatic void tusb_setup_cpu_interface(struct musb *musb) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* 9998c2ecf20Sopenharmony_ci * Disable GPIO[5:0] pullups (used as output DMA requests) 10008c2ecf20Sopenharmony_ci * Don't disable GPIO[7:6] as they are needed for wake-up. 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PULLUP_1_CTRL, 0x0000003F); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* Disable all pullups on NOR IF, DMAREQ0 and DMAREQ1 */ 10058c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PULLUP_2_CTRL, 0x01FFFFFF); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Turn GPIO[5:0] to DMAREQ[5:0] signals */ 10088c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_CONF, TUSB_GPIO_CONF_DMAREQ(0x3f)); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Burst size 16x16 bits, all six DMA requests enabled, DMA request 10118c2ecf20Sopenharmony_ci * de-assertion time 2 system clocks p 62 */ 10128c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_DMA_REQ_CONF, 10138c2ecf20Sopenharmony_ci TUSB_DMA_REQ_CONF_BURST_SIZE(2) | 10148c2ecf20Sopenharmony_ci TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) | 10158c2ecf20Sopenharmony_ci TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2)); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* Set 0 wait count for synchronous burst access */ 10188c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_WAIT_COUNT, 1); 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int tusb_musb_start(struct musb *musb) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 10248c2ecf20Sopenharmony_ci int ret = 0; 10258c2ecf20Sopenharmony_ci unsigned long flags; 10268c2ecf20Sopenharmony_ci u32 reg; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (musb->board_set_power) 10298c2ecf20Sopenharmony_ci ret = musb->board_set_power(1); 10308c2ecf20Sopenharmony_ci if (ret != 0) { 10318c2ecf20Sopenharmony_ci printk(KERN_ERR "tusb: Cannot enable TUSB6010\n"); 10328c2ecf20Sopenharmony_ci return ret; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (musb_readl(tbase, TUSB_PROD_TEST_RESET) != 10388c2ecf20Sopenharmony_ci TUSB_PROD_TEST_RESET_VAL) { 10398c2ecf20Sopenharmony_ci printk(KERN_ERR "tusb: Unable to detect TUSB6010\n"); 10408c2ecf20Sopenharmony_ci goto err; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci musb->tusb_revision = tusb_get_revision(musb); 10448c2ecf20Sopenharmony_ci tusb_print_revision(musb); 10458c2ecf20Sopenharmony_ci if (musb->tusb_revision < 2) { 10468c2ecf20Sopenharmony_ci printk(KERN_ERR "tusb: Unsupported TUSB6010 revision %i\n", 10478c2ecf20Sopenharmony_ci musb->tusb_revision); 10488c2ecf20Sopenharmony_ci goto err; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* The uint bit for "USB non-PDR interrupt enable" has to be 1 when 10528c2ecf20Sopenharmony_ci * NOR FLASH interface is used */ 10538c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_VLYNQ_CTRL, 8); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* Select PHY free running 60MHz as a system clock */ 10568c2ecf20Sopenharmony_ci tusb_set_clock_source(musb, 1); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* VBus valid timer 1us, disable DFT/Debug and VLYNQ clocks for 10598c2ecf20Sopenharmony_ci * power saving, enable VBus detect and session end comparators, 10608c2ecf20Sopenharmony_ci * enable IDpullup, enable VBus charging */ 10618c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, 10628c2ecf20Sopenharmony_ci TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(0xa) | 10638c2ecf20Sopenharmony_ci TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN | 10648c2ecf20Sopenharmony_ci TUSB_PRCM_MNGMT_OTG_SESS_END_EN | 10658c2ecf20Sopenharmony_ci TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN | 10668c2ecf20Sopenharmony_ci TUSB_PRCM_MNGMT_OTG_ID_PULLUP); 10678c2ecf20Sopenharmony_ci tusb_setup_cpu_interface(musb); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* simplify: always sense/pullup ID pins, as if in OTG mode */ 10708c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); 10718c2ecf20Sopenharmony_ci reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 10728c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, reg); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL); 10758c2ecf20Sopenharmony_ci reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 10768c2ecf20Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, reg); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cierr: 10838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (musb->board_set_power) 10868c2ecf20Sopenharmony_ci musb->board_set_power(0); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return -ENODEV; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic int tusb_musb_init(struct musb *musb) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct platform_device *pdev; 10948c2ecf20Sopenharmony_ci struct resource *mem; 10958c2ecf20Sopenharmony_ci void __iomem *sync = NULL; 10968c2ecf20Sopenharmony_ci int ret; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); 10998c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(musb->xceiv)) 11008c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci pdev = to_platform_device(musb->controller); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* dma address for async dma */ 11058c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11068c2ecf20Sopenharmony_ci if (!mem) { 11078c2ecf20Sopenharmony_ci pr_debug("no async dma resource?\n"); 11088c2ecf20Sopenharmony_ci ret = -ENODEV; 11098c2ecf20Sopenharmony_ci goto done; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci musb->async = mem->start; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* dma address for sync dma */ 11148c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 11158c2ecf20Sopenharmony_ci if (!mem) { 11168c2ecf20Sopenharmony_ci pr_debug("no sync dma resource?\n"); 11178c2ecf20Sopenharmony_ci ret = -ENODEV; 11188c2ecf20Sopenharmony_ci goto done; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci musb->sync = mem->start; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci sync = ioremap(mem->start, resource_size(mem)); 11238c2ecf20Sopenharmony_ci if (!sync) { 11248c2ecf20Sopenharmony_ci pr_debug("ioremap for sync failed\n"); 11258c2ecf20Sopenharmony_ci ret = -ENOMEM; 11268c2ecf20Sopenharmony_ci goto done; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci musb->sync_va = sync; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Offsets from base: VLYNQ at 0x000, MUSB regs at 0x400, 11318c2ecf20Sopenharmony_ci * FIFOs at 0x600, TUSB at 0x800 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_ci musb->mregs += TUSB_BASE_OFFSET; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci ret = tusb_musb_start(musb); 11368c2ecf20Sopenharmony_ci if (ret) { 11378c2ecf20Sopenharmony_ci printk(KERN_ERR "Could not start tusb6010 (%d)\n", 11388c2ecf20Sopenharmony_ci ret); 11398c2ecf20Sopenharmony_ci goto done; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci musb->isr = tusb_musb_interrupt; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci musb->xceiv->set_power = tusb_draw_power; 11448c2ecf20Sopenharmony_ci the_musb = musb; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci timer_setup(&musb->dev_timer, musb_do_idle, 0); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cidone: 11498c2ecf20Sopenharmony_ci if (ret < 0) { 11508c2ecf20Sopenharmony_ci if (sync) 11518c2ecf20Sopenharmony_ci iounmap(sync); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci usb_put_phy(musb->xceiv); 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci return ret; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic int tusb_musb_exit(struct musb *musb) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci del_timer_sync(&musb->dev_timer); 11618c2ecf20Sopenharmony_ci the_musb = NULL; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (musb->board_set_power) 11648c2ecf20Sopenharmony_ci musb->board_set_power(0); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci iounmap(musb->sync_va); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci usb_put_phy(musb->xceiv); 11698c2ecf20Sopenharmony_ci return 0; 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic const struct musb_platform_ops tusb_ops = { 11738c2ecf20Sopenharmony_ci .quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB | 11748c2ecf20Sopenharmony_ci MUSB_G_NO_SKB_RESERVE, 11758c2ecf20Sopenharmony_ci .init = tusb_musb_init, 11768c2ecf20Sopenharmony_ci .exit = tusb_musb_exit, 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci .ep_offset = tusb_ep_offset, 11798c2ecf20Sopenharmony_ci .ep_select = tusb_ep_select, 11808c2ecf20Sopenharmony_ci .fifo_offset = tusb_fifo_offset, 11818c2ecf20Sopenharmony_ci .readb = tusb_readb, 11828c2ecf20Sopenharmony_ci .writeb = tusb_writeb, 11838c2ecf20Sopenharmony_ci .read_fifo = tusb_read_fifo, 11848c2ecf20Sopenharmony_ci .write_fifo = tusb_write_fifo, 11858c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_TUSB_OMAP_DMA 11868c2ecf20Sopenharmony_ci .dma_init = tusb_dma_controller_create, 11878c2ecf20Sopenharmony_ci .dma_exit = tusb_dma_controller_destroy, 11888c2ecf20Sopenharmony_ci#endif 11898c2ecf20Sopenharmony_ci .enable = tusb_musb_enable, 11908c2ecf20Sopenharmony_ci .disable = tusb_musb_disable, 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci .set_mode = tusb_musb_set_mode, 11938c2ecf20Sopenharmony_ci .try_idle = tusb_musb_try_idle, 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci .vbus_status = tusb_musb_vbus_status, 11968c2ecf20Sopenharmony_ci .set_vbus = tusb_musb_set_vbus, 11978c2ecf20Sopenharmony_ci}; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic const struct platform_device_info tusb_dev_info = { 12008c2ecf20Sopenharmony_ci .name = "musb-hdrc", 12018c2ecf20Sopenharmony_ci .id = PLATFORM_DEVID_AUTO, 12028c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 12038c2ecf20Sopenharmony_ci}; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic int tusb_probe(struct platform_device *pdev) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci struct resource musb_resources[3]; 12088c2ecf20Sopenharmony_ci struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); 12098c2ecf20Sopenharmony_ci struct platform_device *musb; 12108c2ecf20Sopenharmony_ci struct tusb6010_glue *glue; 12118c2ecf20Sopenharmony_ci struct platform_device_info pinfo; 12128c2ecf20Sopenharmony_ci int ret; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); 12158c2ecf20Sopenharmony_ci if (!glue) 12168c2ecf20Sopenharmony_ci return -ENOMEM; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci glue->dev = &pdev->dev; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci pdata->platform_ops = &tusb_ops; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci usb_phy_generic_register(); 12238c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, glue); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci memset(musb_resources, 0x00, sizeof(*musb_resources) * 12268c2ecf20Sopenharmony_ci ARRAY_SIZE(musb_resources)); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci musb_resources[0].name = pdev->resource[0].name; 12298c2ecf20Sopenharmony_ci musb_resources[0].start = pdev->resource[0].start; 12308c2ecf20Sopenharmony_ci musb_resources[0].end = pdev->resource[0].end; 12318c2ecf20Sopenharmony_ci musb_resources[0].flags = pdev->resource[0].flags; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci musb_resources[1].name = pdev->resource[1].name; 12348c2ecf20Sopenharmony_ci musb_resources[1].start = pdev->resource[1].start; 12358c2ecf20Sopenharmony_ci musb_resources[1].end = pdev->resource[1].end; 12368c2ecf20Sopenharmony_ci musb_resources[1].flags = pdev->resource[1].flags; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci musb_resources[2].name = pdev->resource[2].name; 12398c2ecf20Sopenharmony_ci musb_resources[2].start = pdev->resource[2].start; 12408c2ecf20Sopenharmony_ci musb_resources[2].end = pdev->resource[2].end; 12418c2ecf20Sopenharmony_ci musb_resources[2].flags = pdev->resource[2].flags; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci pinfo = tusb_dev_info; 12448c2ecf20Sopenharmony_ci pinfo.parent = &pdev->dev; 12458c2ecf20Sopenharmony_ci pinfo.res = musb_resources; 12468c2ecf20Sopenharmony_ci pinfo.num_res = ARRAY_SIZE(musb_resources); 12478c2ecf20Sopenharmony_ci pinfo.data = pdata; 12488c2ecf20Sopenharmony_ci pinfo.size_data = sizeof(*pdata); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci glue->musb = musb = platform_device_register_full(&pinfo); 12518c2ecf20Sopenharmony_ci if (IS_ERR(musb)) { 12528c2ecf20Sopenharmony_ci ret = PTR_ERR(musb); 12538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); 12548c2ecf20Sopenharmony_ci return ret; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return 0; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic int tusb_remove(struct platform_device *pdev) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct tusb6010_glue *glue = platform_get_drvdata(pdev); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci platform_device_unregister(glue->musb); 12658c2ecf20Sopenharmony_ci usb_phy_generic_unregister(glue->phy); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return 0; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic struct platform_driver tusb_driver = { 12718c2ecf20Sopenharmony_ci .probe = tusb_probe, 12728c2ecf20Sopenharmony_ci .remove = tusb_remove, 12738c2ecf20Sopenharmony_ci .driver = { 12748c2ecf20Sopenharmony_ci .name = "musb-tusb", 12758c2ecf20Sopenharmony_ci }, 12768c2ecf20Sopenharmony_ci}; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TUSB6010 MUSB Glue Layer"); 12798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); 12808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 12818c2ecf20Sopenharmony_cimodule_platform_driver(tusb_driver); 1282