162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TUSB6010 USB 2.0 OTG Dual Role controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006 Nokia Corporation 662306a36Sopenharmony_ci * Tony Lindgren <tony@atomide.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Notes: 962306a36Sopenharmony_ci * - Driver assumes that interface to external host (main CPU) is 1062306a36Sopenharmony_ci * configured for NOR FLASH interface instead of VLYNQ serial 1162306a36Sopenharmony_ci * interface. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/prefetch.h> 2162306a36Sopenharmony_ci#include <linux/usb.h> 2262306a36Sopenharmony_ci#include <linux/irq.h> 2362306a36Sopenharmony_ci#include <linux/io.h> 2462306a36Sopenharmony_ci#include <linux/iopoll.h> 2562306a36Sopenharmony_ci#include <linux/device.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2862306a36Sopenharmony_ci#include <linux/usb/usb_phy_generic.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "musb_core.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct tusb6010_glue { 3362306a36Sopenharmony_ci struct device *dev; 3462306a36Sopenharmony_ci struct platform_device *musb; 3562306a36Sopenharmony_ci struct platform_device *phy; 3662306a36Sopenharmony_ci struct gpio_desc *enable; 3762306a36Sopenharmony_ci struct gpio_desc *intpin; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define TUSB_REV_MAJOR(reg_val) ((reg_val >> 4) & 0xf) 4362306a36Sopenharmony_ci#define TUSB_REV_MINOR(reg_val) (reg_val & 0xf) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Checks the revision. We need to use the DMA register as 3.0 does not 4762306a36Sopenharmony_ci * have correct versions for TUSB_PRCM_REV or TUSB_INT_CTRL_REV. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic u8 tusb_get_revision(struct musb *musb) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 5262306a36Sopenharmony_ci u32 die_id; 5362306a36Sopenharmony_ci u8 rev; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci rev = musb_readl(tbase, TUSB_DMA_CTRL_REV) & 0xff; 5662306a36Sopenharmony_ci if (TUSB_REV_MAJOR(rev) == 3) { 5762306a36Sopenharmony_ci die_id = TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, 5862306a36Sopenharmony_ci TUSB_DIDR1_HI)); 5962306a36Sopenharmony_ci if (die_id >= TUSB_DIDR1_HI_REV_31) 6062306a36Sopenharmony_ci rev |= 1; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return rev; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void tusb_print_revision(struct musb *musb) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 6962306a36Sopenharmony_ci u8 rev; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci rev = musb->tusb_revision; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci pr_info("tusb: %s%i.%i %s%i.%i %s%i.%i %s%i.%i %s%i %s%i.%i\n", 7462306a36Sopenharmony_ci "prcm", 7562306a36Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_PRCM_REV)), 7662306a36Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_PRCM_REV)), 7762306a36Sopenharmony_ci "int", 7862306a36Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_INT_CTRL_REV)), 7962306a36Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_INT_CTRL_REV)), 8062306a36Sopenharmony_ci "gpio", 8162306a36Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_GPIO_REV)), 8262306a36Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_GPIO_REV)), 8362306a36Sopenharmony_ci "dma", 8462306a36Sopenharmony_ci TUSB_REV_MAJOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)), 8562306a36Sopenharmony_ci TUSB_REV_MINOR(musb_readl(tbase, TUSB_DMA_CTRL_REV)), 8662306a36Sopenharmony_ci "dieid", 8762306a36Sopenharmony_ci TUSB_DIDR1_HI_CHIP_REV(musb_readl(tbase, TUSB_DIDR1_HI)), 8862306a36Sopenharmony_ci "rev", 8962306a36Sopenharmony_ci TUSB_REV_MAJOR(rev), TUSB_REV_MINOR(rev)); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define WBUS_QUIRK_MASK (TUSB_PHY_OTG_CTRL_TESTM2 | TUSB_PHY_OTG_CTRL_TESTM1 \ 9362306a36Sopenharmony_ci | TUSB_PHY_OTG_CTRL_TESTM0) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Workaround for spontaneous WBUS wake-up issue #2 for tusb3.0. 9762306a36Sopenharmony_ci * Disables power detection in PHY for the duration of idle. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic void tusb_wbus_quirk(struct musb *musb, int enabled) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 10262306a36Sopenharmony_ci static u32 phy_otg_ctrl, phy_otg_ena; 10362306a36Sopenharmony_ci u32 tmp; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (enabled) { 10662306a36Sopenharmony_ci phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL); 10762306a36Sopenharmony_ci phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); 10862306a36Sopenharmony_ci tmp = TUSB_PHY_OTG_CTRL_WRPROTECT 10962306a36Sopenharmony_ci | phy_otg_ena | WBUS_QUIRK_MASK; 11062306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); 11162306a36Sopenharmony_ci tmp = phy_otg_ena & ~WBUS_QUIRK_MASK; 11262306a36Sopenharmony_ci tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2; 11362306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); 11462306a36Sopenharmony_ci dev_dbg(musb->controller, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", 11562306a36Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL), 11662306a36Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); 11762306a36Sopenharmony_ci } else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE) 11862306a36Sopenharmony_ci & TUSB_PHY_OTG_CTRL_TESTM2) { 11962306a36Sopenharmony_ci tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl; 12062306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); 12162306a36Sopenharmony_ci tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena; 12262306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); 12362306a36Sopenharmony_ci dev_dbg(musb->controller, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", 12462306a36Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL), 12562306a36Sopenharmony_ci musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); 12662306a36Sopenharmony_ci phy_otg_ctrl = 0; 12762306a36Sopenharmony_ci phy_otg_ena = 0; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic u32 tusb_fifo_offset(u8 epnum) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return 0x200 + (epnum * 0x20); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic u32 tusb_ep_offset(u8 epnum, u16 offset) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci return 0x10 + offset; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* TUSB mapping: "flat" plus ep0 special cases */ 14262306a36Sopenharmony_cistatic void tusb_ep_select(void __iomem *mbase, u8 epnum) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci musb_writeb(mbase, MUSB_INDEX, epnum); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic u8 tusb_readb(void __iomem *addr, u32 offset) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u16 tmp; 15362306a36Sopenharmony_ci u8 val; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci tmp = __raw_readw(addr + (offset & ~1)); 15662306a36Sopenharmony_ci if (offset & 1) 15762306a36Sopenharmony_ci val = (tmp >> 8); 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci val = tmp & 0xff; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return val; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void tusb_writeb(void __iomem *addr, u32 offset, u8 data) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci u16 tmp; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci tmp = __raw_readw(addr + (offset & ~1)); 16962306a36Sopenharmony_ci if (offset & 1) 17062306a36Sopenharmony_ci tmp = (data << 8) | (tmp & 0xff); 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci tmp = (tmp & 0xff00) | data; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci __raw_writew(tmp, addr + (offset & ~1)); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * TUSB 6010 may use a parallel bus that doesn't support byte ops; 17962306a36Sopenharmony_ci * so both loading and unloading FIFOs need explicit byte counts. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic inline void 18362306a36Sopenharmony_citusb_fifo_write_unaligned(void __iomem *fifo, const u8 *buf, u16 len) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci u32 val; 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (len > 4) { 18962306a36Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 19062306a36Sopenharmony_ci memcpy(&val, buf, 4); 19162306a36Sopenharmony_ci musb_writel(fifo, 0, val); 19262306a36Sopenharmony_ci buf += 4; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci len %= 4; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci if (len > 0) { 19762306a36Sopenharmony_ci /* Write the rest 1 - 3 bytes to FIFO */ 19862306a36Sopenharmony_ci val = 0; 19962306a36Sopenharmony_ci memcpy(&val, buf, len); 20062306a36Sopenharmony_ci musb_writel(fifo, 0, val); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline void tusb_fifo_read_unaligned(void __iomem *fifo, 20562306a36Sopenharmony_ci void *buf, u16 len) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u32 val; 20862306a36Sopenharmony_ci int i; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (len > 4) { 21162306a36Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 21262306a36Sopenharmony_ci val = musb_readl(fifo, 0); 21362306a36Sopenharmony_ci memcpy(buf, &val, 4); 21462306a36Sopenharmony_ci buf += 4; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci len %= 4; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci if (len > 0) { 21962306a36Sopenharmony_ci /* Read the rest 1 - 3 bytes from FIFO */ 22062306a36Sopenharmony_ci val = musb_readl(fifo, 0); 22162306a36Sopenharmony_ci memcpy(buf, &val, len); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void tusb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct musb *musb = hw_ep->musb; 22862306a36Sopenharmony_ci void __iomem *ep_conf = hw_ep->conf; 22962306a36Sopenharmony_ci void __iomem *fifo = hw_ep->fifo; 23062306a36Sopenharmony_ci u8 epnum = hw_ep->epnum; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci prefetch(buf); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 23562306a36Sopenharmony_ci 'T', epnum, fifo, len, buf); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (epnum) 23862306a36Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_TX_OFFSET, 23962306a36Sopenharmony_ci TUSB_EP_CONFIG_XFR_SIZE(len)); 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_DIR_TX | 24262306a36Sopenharmony_ci TUSB_EP0_CONFIG_XFR_SIZE(len)); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (likely((0x01 & (unsigned long) buf) == 0)) { 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Best case is 32bit-aligned destination address */ 24762306a36Sopenharmony_ci if ((0x02 & (unsigned long) buf) == 0) { 24862306a36Sopenharmony_ci if (len >= 4) { 24962306a36Sopenharmony_ci iowrite32_rep(fifo, buf, len >> 2); 25062306a36Sopenharmony_ci buf += (len & ~0x03); 25162306a36Sopenharmony_ci len &= 0x03; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci if (len >= 2) { 25562306a36Sopenharmony_ci u32 val; 25662306a36Sopenharmony_ci int i; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Cannot use writesw, fifo is 32-bit */ 25962306a36Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 26062306a36Sopenharmony_ci val = (u32)(*(u16 *)buf); 26162306a36Sopenharmony_ci buf += 2; 26262306a36Sopenharmony_ci val |= (*(u16 *)buf) << 16; 26362306a36Sopenharmony_ci buf += 2; 26462306a36Sopenharmony_ci musb_writel(fifo, 0, val); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci len &= 0x03; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (len > 0) 27262306a36Sopenharmony_ci tusb_fifo_write_unaligned(fifo, buf, len); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void tusb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct musb *musb = hw_ep->musb; 27862306a36Sopenharmony_ci void __iomem *ep_conf = hw_ep->conf; 27962306a36Sopenharmony_ci void __iomem *fifo = hw_ep->fifo; 28062306a36Sopenharmony_ci u8 epnum = hw_ep->epnum; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 28362306a36Sopenharmony_ci 'R', epnum, fifo, len, buf); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (epnum) 28662306a36Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_RX_OFFSET, 28762306a36Sopenharmony_ci TUSB_EP_CONFIG_XFR_SIZE(len)); 28862306a36Sopenharmony_ci else 28962306a36Sopenharmony_ci musb_writel(ep_conf, 0, TUSB_EP0_CONFIG_XFR_SIZE(len)); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (likely((0x01 & (unsigned long) buf) == 0)) { 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Best case is 32bit-aligned destination address */ 29462306a36Sopenharmony_ci if ((0x02 & (unsigned long) buf) == 0) { 29562306a36Sopenharmony_ci if (len >= 4) { 29662306a36Sopenharmony_ci ioread32_rep(fifo, buf, len >> 2); 29762306a36Sopenharmony_ci buf += (len & ~0x03); 29862306a36Sopenharmony_ci len &= 0x03; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci if (len >= 2) { 30262306a36Sopenharmony_ci u32 val; 30362306a36Sopenharmony_ci int i; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Cannot use readsw, fifo is 32-bit */ 30662306a36Sopenharmony_ci for (i = 0; i < (len >> 2); i++) { 30762306a36Sopenharmony_ci val = musb_readl(fifo, 0); 30862306a36Sopenharmony_ci *(u16 *)buf = (u16)(val & 0xffff); 30962306a36Sopenharmony_ci buf += 2; 31062306a36Sopenharmony_ci *(u16 *)buf = (u16)(val >> 16); 31162306a36Sopenharmony_ci buf += 2; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci len &= 0x03; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (len > 0) 31962306a36Sopenharmony_ci tusb_fifo_read_unaligned(fifo, buf, len); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic struct musb *the_musb; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* This is used by gadget drivers, and OTG transceiver logic, allowing 32562306a36Sopenharmony_ci * at most mA current to be drawn from VBUS during a Default-B session 32662306a36Sopenharmony_ci * (that is, while VBUS exceeds 4.4V). In Default-A (including pure host 32762306a36Sopenharmony_ci * mode), or low power Default-B sessions, something else supplies power. 32862306a36Sopenharmony_ci * Caller must take care of locking. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic int tusb_draw_power(struct usb_phy *x, unsigned mA) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct musb *musb = the_musb; 33362306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 33462306a36Sopenharmony_ci u32 reg; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* tps65030 seems to consume max 100mA, with maybe 60mA available 33762306a36Sopenharmony_ci * (measured on one board) for things other than tps and tusb. 33862306a36Sopenharmony_ci * 33962306a36Sopenharmony_ci * Boards sharing the CPU clock with CLKIN will need to prevent 34062306a36Sopenharmony_ci * certain idle sleep states while the USB link is active. 34162306a36Sopenharmony_ci * 34262306a36Sopenharmony_ci * REVISIT we could use VBUS to supply only _one_ of { 1.5V, 3.3V }. 34362306a36Sopenharmony_ci * The actual current usage would be very board-specific. For now, 34462306a36Sopenharmony_ci * it's simpler to just use an aggregate (also board-specific). 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci if (x->otg->default_a || mA < (musb->min_power << 1)) 34762306a36Sopenharmony_ci mA = 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_MNGMT); 35062306a36Sopenharmony_ci if (mA) { 35162306a36Sopenharmony_ci musb->is_bus_powered = 1; 35262306a36Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN; 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci musb->is_bus_powered = 0; 35562306a36Sopenharmony_ci reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, reg); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dev_dbg(musb->controller, "draw max %d mA VBUS\n", mA); 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* workaround for issue 13: change clock during chip idle 36462306a36Sopenharmony_ci * (to be fixed in rev3 silicon) ... symptoms include disconnect 36562306a36Sopenharmony_ci * or looping suspend/resume cycles 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_cistatic void tusb_set_clock_source(struct musb *musb, unsigned mode) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 37062306a36Sopenharmony_ci u32 reg; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_CONF); 37362306a36Sopenharmony_ci reg &= ~TUSB_PRCM_CONF_SYS_CLKSEL(0x3); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 0 = refclk (clkin, XI) 37662306a36Sopenharmony_ci * 1 = PHY 60 MHz (internal PLL) 37762306a36Sopenharmony_ci * 2 = not supported 37862306a36Sopenharmony_ci * 3 = what? 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci if (mode > 0) 38162306a36Sopenharmony_ci reg |= TUSB_PRCM_CONF_SYS_CLKSEL(mode & 0x3); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_CONF, reg); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* FIXME tusb6010_platform_retime(mode == 0); */ 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * Idle TUSB6010 until next wake-up event; NOR access always wakes. 39062306a36Sopenharmony_ci * Other code ensures that we idle unless we're connected _and_ the 39162306a36Sopenharmony_ci * USB link is not suspended ... and tells us the relevant wakeup 39262306a36Sopenharmony_ci * events. SW_EN for voltage is handled separately. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 39762306a36Sopenharmony_ci u32 reg; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if ((wakeup_enables & TUSB_PRCM_WBUS) 40062306a36Sopenharmony_ci && (musb->tusb_revision == TUSB_REV_30)) 40162306a36Sopenharmony_ci tusb_wbus_quirk(musb, 1); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci tusb_set_clock_source(musb, 0); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci wakeup_enables |= TUSB_PRCM_WNORCS; 40662306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* REVISIT writeup of WID implies that if WID set and ID is grounded, 40962306a36Sopenharmony_ci * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared. 41062306a36Sopenharmony_ci * Presumably that's mostly to save power, hence WID is immaterial ... 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_MNGMT); 41462306a36Sopenharmony_ci /* issue 4: when driving vbus, use hipower (vbus_det) comparator */ 41562306a36Sopenharmony_ci if (is_host_active(musb)) { 41662306a36Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; 41762306a36Sopenharmony_ci reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN; 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN; 42062306a36Sopenharmony_ci reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE; 42362306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, reg); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/* 42962306a36Sopenharmony_ci * Updates cable VBUS status. Caller must take care of locking. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_cistatic int tusb_musb_vbus_status(struct musb *musb) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 43462306a36Sopenharmony_ci u32 otg_stat, prcm_mngmt; 43562306a36Sopenharmony_ci int ret = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 43862306a36Sopenharmony_ci prcm_mngmt = musb_readl(tbase, TUSB_PRCM_MNGMT); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Temporarily enable VBUS detection if it was disabled for 44162306a36Sopenharmony_ci * suspend mode. Unless it's enabled otg_stat and devctl will 44262306a36Sopenharmony_ci * not show correct VBUS state. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if (!(prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) { 44562306a36Sopenharmony_ci u32 tmp = prcm_mngmt; 44662306a36Sopenharmony_ci tmp |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; 44762306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, tmp); 44862306a36Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 44962306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, prcm_mngmt); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) 45362306a36Sopenharmony_ci ret = 1; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return ret; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void musb_do_idle(struct timer_list *t) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct musb *musb = from_timer(musb, t, dev_timer); 46162306a36Sopenharmony_ci unsigned long flags; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci switch (musb->xceiv->otg->state) { 46662306a36Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 46762306a36Sopenharmony_ci if ((musb->a_wait_bcon != 0) 46862306a36Sopenharmony_ci && (musb->idle_timeout == 0 46962306a36Sopenharmony_ci || time_after(jiffies, musb->idle_timeout))) { 47062306a36Sopenharmony_ci dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n", 47162306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state)); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci fallthrough; 47462306a36Sopenharmony_ci case OTG_STATE_A_IDLE: 47562306a36Sopenharmony_ci tusb_musb_set_vbus(musb, 0); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci default: 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (!musb->is_active) { 48262306a36Sopenharmony_ci u32 wakeups; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* wait until hub_wq handles port change status */ 48562306a36Sopenharmony_ci if (is_host_active(musb) && (musb->port1_status >> 16)) 48662306a36Sopenharmony_ci goto done; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!musb->gadget_driver) { 48962306a36Sopenharmony_ci wakeups = 0; 49062306a36Sopenharmony_ci } else { 49162306a36Sopenharmony_ci wakeups = TUSB_PRCM_WHOSTDISCON 49262306a36Sopenharmony_ci | TUSB_PRCM_WBUS 49362306a36Sopenharmony_ci | TUSB_PRCM_WVBUS; 49462306a36Sopenharmony_ci wakeups |= TUSB_PRCM_WID; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci tusb_allow_idle(musb, wakeups); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_cidone: 49962306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * Maybe put TUSB6010 into idle mode depending on USB link status, 50462306a36Sopenharmony_ci * like "disconnected" or "suspended". We'll be woken out of it by 50562306a36Sopenharmony_ci * connect, resume, or disconnect. 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * Needs to be called as the last function everywhere where there is 50862306a36Sopenharmony_ci * register access to TUSB6010 because of NOR flash wake-up. 50962306a36Sopenharmony_ci * Caller should own controller spinlock. 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * Delay because peripheral enables D+ pullup 3msec after SE0, and 51262306a36Sopenharmony_ci * we don't want to treat that full speed J as a wakeup event. 51362306a36Sopenharmony_ci * ... peripherals must draw only suspend current after 10 msec. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_cistatic void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci unsigned long default_timeout = jiffies + msecs_to_jiffies(3); 51862306a36Sopenharmony_ci static unsigned long last_timer; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (timeout == 0) 52162306a36Sopenharmony_ci timeout = default_timeout; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Never idle if active, or when VBUS timeout is not set as host */ 52462306a36Sopenharmony_ci if (musb->is_active || ((musb->a_wait_bcon == 0) 52562306a36Sopenharmony_ci && (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) { 52662306a36Sopenharmony_ci dev_dbg(musb->controller, "%s active, deleting timer\n", 52762306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state)); 52862306a36Sopenharmony_ci del_timer(&musb->dev_timer); 52962306a36Sopenharmony_ci last_timer = jiffies; 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (time_after(last_timer, timeout)) { 53462306a36Sopenharmony_ci if (!timer_pending(&musb->dev_timer)) 53562306a36Sopenharmony_ci last_timer = timeout; 53662306a36Sopenharmony_ci else { 53762306a36Sopenharmony_ci dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci last_timer = timeout; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", 54462306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), 54562306a36Sopenharmony_ci (unsigned long)jiffies_to_msecs(timeout - jiffies)); 54662306a36Sopenharmony_ci mod_timer(&musb->dev_timer, timeout); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* ticks of 60 MHz clock */ 55062306a36Sopenharmony_ci#define DEVCLOCK 60000000 55162306a36Sopenharmony_ci#define OTG_TIMER_MS(msecs) ((msecs) \ 55262306a36Sopenharmony_ci ? (TUSB_DEV_OTG_TIMER_VAL((DEVCLOCK/1000)*(msecs)) \ 55362306a36Sopenharmony_ci | TUSB_DEV_OTG_TIMER_ENABLE) \ 55462306a36Sopenharmony_ci : 0) 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void tusb_musb_set_vbus(struct musb *musb, int is_on) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 55962306a36Sopenharmony_ci u32 conf, prcm, timer; 56062306a36Sopenharmony_ci u8 devctl; 56162306a36Sopenharmony_ci struct usb_otg *otg = musb->xceiv->otg; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* HDRC controls CPEN, but beware current surges during device 56462306a36Sopenharmony_ci * connect. They can trigger transient overcurrent conditions 56562306a36Sopenharmony_ci * that must be ignored. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci prcm = musb_readl(tbase, TUSB_PRCM_MNGMT); 56962306a36Sopenharmony_ci conf = musb_readl(tbase, TUSB_DEV_CONF); 57062306a36Sopenharmony_ci devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (is_on) { 57362306a36Sopenharmony_ci timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE); 57462306a36Sopenharmony_ci otg->default_a = 1; 57562306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; 57662306a36Sopenharmony_ci devctl |= MUSB_DEVCTL_SESSION; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci conf |= TUSB_DEV_CONF_USB_HOST_MODE; 57962306a36Sopenharmony_ci MUSB_HST_MODE(musb); 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci u32 otg_stat; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci timer = 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* If ID pin is grounded, we want to be a_idle */ 58662306a36Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 58762306a36Sopenharmony_ci if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) { 58862306a36Sopenharmony_ci switch (musb->xceiv->otg->state) { 58962306a36Sopenharmony_ci case OTG_STATE_A_WAIT_VRISE: 59062306a36Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 59162306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci case OTG_STATE_A_WAIT_VFALL: 59462306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_IDLE; 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci default: 59762306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_IDLE; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci musb->is_active = 0; 60062306a36Sopenharmony_ci otg->default_a = 1; 60162306a36Sopenharmony_ci MUSB_HST_MODE(musb); 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci musb->is_active = 0; 60462306a36Sopenharmony_ci otg->default_a = 0; 60562306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_B_IDLE; 60662306a36Sopenharmony_ci MUSB_DEV_MODE(musb); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci devctl &= ~MUSB_DEVCTL_SESSION; 61062306a36Sopenharmony_ci conf &= ~TUSB_DEV_CONF_USB_HOST_MODE; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, prcm); 61562306a36Sopenharmony_ci musb_writel(tbase, TUSB_DEV_OTG_TIMER, timer); 61662306a36Sopenharmony_ci musb_writel(tbase, TUSB_DEV_CONF, conf); 61762306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", 62062306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), 62162306a36Sopenharmony_ci musb_readb(musb->mregs, MUSB_DEVCTL), 62262306a36Sopenharmony_ci musb_readl(tbase, TUSB_DEV_OTG_STAT), 62362306a36Sopenharmony_ci conf, prcm); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci/* 62762306a36Sopenharmony_ci * Sets the mode to OTG, peripheral or host by changing the ID detection. 62862306a36Sopenharmony_ci * Caller must take care of locking. 62962306a36Sopenharmony_ci * 63062306a36Sopenharmony_ci * Note that if a mini-A cable is plugged in the ID line will stay down as 63162306a36Sopenharmony_ci * the weak ID pull-up is not able to pull the ID up. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_cistatic int tusb_musb_set_mode(struct musb *musb, u8 musb_mode) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 63662306a36Sopenharmony_ci u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 63962306a36Sopenharmony_ci phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL); 64062306a36Sopenharmony_ci phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); 64162306a36Sopenharmony_ci dev_conf = musb_readl(tbase, TUSB_DEV_CONF); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci switch (musb_mode) { 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci case MUSB_HOST: /* Disable PHY ID detect, ground ID */ 64662306a36Sopenharmony_ci phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 64762306a36Sopenharmony_ci phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 64862306a36Sopenharmony_ci dev_conf |= TUSB_DEV_CONF_ID_SEL; 64962306a36Sopenharmony_ci dev_conf &= ~TUSB_DEV_CONF_SOFT_ID; 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci case MUSB_PERIPHERAL: /* Disable PHY ID detect, keep ID pull-up on */ 65262306a36Sopenharmony_ci phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 65362306a36Sopenharmony_ci phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 65462306a36Sopenharmony_ci dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID); 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci case MUSB_OTG: /* Use PHY ID detection */ 65762306a36Sopenharmony_ci phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 65862306a36Sopenharmony_ci phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 65962306a36Sopenharmony_ci dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID); 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci default: 66362306a36Sopenharmony_ci dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode); 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, 66862306a36Sopenharmony_ci TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl); 66962306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, 67062306a36Sopenharmony_ci TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena); 67162306a36Sopenharmony_ci musb_writel(tbase, TUSB_DEV_CONF, dev_conf); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 67462306a36Sopenharmony_ci if ((musb_mode == MUSB_PERIPHERAL) && 67562306a36Sopenharmony_ci !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) 67662306a36Sopenharmony_ci INFO("Cannot be peripheral with mini-A cable " 67762306a36Sopenharmony_ci "otg_stat: %08x\n", otg_stat); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic inline unsigned long 68362306a36Sopenharmony_citusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci u32 otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); 68662306a36Sopenharmony_ci unsigned long idle_timeout = 0; 68762306a36Sopenharmony_ci struct usb_otg *otg = musb->xceiv->otg; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* ID pin */ 69062306a36Sopenharmony_ci if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) { 69162306a36Sopenharmony_ci int default_a; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS); 69462306a36Sopenharmony_ci dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B'); 69562306a36Sopenharmony_ci otg->default_a = default_a; 69662306a36Sopenharmony_ci tusb_musb_set_vbus(musb, default_a); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Don't allow idling immediately */ 69962306a36Sopenharmony_ci if (default_a) 70062306a36Sopenharmony_ci idle_timeout = jiffies + (HZ * 3); 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* VBUS state change */ 70462306a36Sopenharmony_ci if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) { 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* B-dev state machine: no vbus ~= disconnect */ 70762306a36Sopenharmony_ci if (!otg->default_a) { 70862306a36Sopenharmony_ci /* ? musb_root_disconnect(musb); */ 70962306a36Sopenharmony_ci musb->port1_status &= 71062306a36Sopenharmony_ci ~(USB_PORT_STAT_CONNECTION 71162306a36Sopenharmony_ci | USB_PORT_STAT_ENABLE 71262306a36Sopenharmony_ci | USB_PORT_STAT_LOW_SPEED 71362306a36Sopenharmony_ci | USB_PORT_STAT_HIGH_SPEED 71462306a36Sopenharmony_ci | USB_PORT_STAT_TEST 71562306a36Sopenharmony_ci ); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) { 71862306a36Sopenharmony_ci dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n"); 71962306a36Sopenharmony_ci if (musb->xceiv->otg->state != OTG_STATE_B_IDLE) { 72062306a36Sopenharmony_ci /* INTR_DISCONNECT can hide... */ 72162306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_B_IDLE; 72262306a36Sopenharmony_ci musb->int_usb |= MUSB_INTR_DISCONNECT; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci musb->is_active = 0; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", 72762306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), otg_stat); 72862306a36Sopenharmony_ci idle_timeout = jiffies + (1 * HZ); 72962306a36Sopenharmony_ci schedule_delayed_work(&musb->irq_work, 0); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci } else /* A-dev state machine */ { 73262306a36Sopenharmony_ci dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", 73362306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), otg_stat); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci switch (musb->xceiv->otg->state) { 73662306a36Sopenharmony_ci case OTG_STATE_A_IDLE: 73762306a36Sopenharmony_ci dev_dbg(musb->controller, "Got SRP, turning on VBUS\n"); 73862306a36Sopenharmony_ci musb_platform_set_vbus(musb, 1); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* CONNECT can wake if a_wait_bcon is set */ 74162306a36Sopenharmony_ci if (musb->a_wait_bcon != 0) 74262306a36Sopenharmony_ci musb->is_active = 0; 74362306a36Sopenharmony_ci else 74462306a36Sopenharmony_ci musb->is_active = 1; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* 74762306a36Sopenharmony_ci * OPT FS A TD.4.6 needs few seconds for 74862306a36Sopenharmony_ci * A_WAIT_VRISE 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_ci idle_timeout = jiffies + (2 * HZ); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case OTG_STATE_A_WAIT_VRISE: 75462306a36Sopenharmony_ci /* ignore; A-session-valid < VBUS_VALID/2, 75562306a36Sopenharmony_ci * we monitor this with the timer 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case OTG_STATE_A_WAIT_VFALL: 75962306a36Sopenharmony_ci /* REVISIT this irq triggers during short 76062306a36Sopenharmony_ci * spikes caused by enumeration ... 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci if (musb->vbuserr_retry) { 76362306a36Sopenharmony_ci musb->vbuserr_retry--; 76462306a36Sopenharmony_ci tusb_musb_set_vbus(musb, 1); 76562306a36Sopenharmony_ci } else { 76662306a36Sopenharmony_ci musb->vbuserr_retry 76762306a36Sopenharmony_ci = VBUSERR_RETRY_COUNT; 76862306a36Sopenharmony_ci tusb_musb_set_vbus(musb, 0); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci default: 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* OTG timer expiration */ 77862306a36Sopenharmony_ci if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { 77962306a36Sopenharmony_ci u8 devctl; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dev_dbg(musb->controller, "%s timer, %03x\n", 78262306a36Sopenharmony_ci usb_otg_state_string(musb->xceiv->otg->state), otg_stat); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci switch (musb->xceiv->otg->state) { 78562306a36Sopenharmony_ci case OTG_STATE_A_WAIT_VRISE: 78662306a36Sopenharmony_ci /* VBUS has probably been valid for a while now, 78762306a36Sopenharmony_ci * but may well have bounced out of range a bit 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_ci devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 79062306a36Sopenharmony_ci if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { 79162306a36Sopenharmony_ci if ((devctl & MUSB_DEVCTL_VBUS) 79262306a36Sopenharmony_ci != MUSB_DEVCTL_VBUS) { 79362306a36Sopenharmony_ci dev_dbg(musb->controller, "devctl %02x\n", devctl); 79462306a36Sopenharmony_ci break; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON; 79762306a36Sopenharmony_ci musb->is_active = 0; 79862306a36Sopenharmony_ci idle_timeout = jiffies 79962306a36Sopenharmony_ci + msecs_to_jiffies(musb->a_wait_bcon); 80062306a36Sopenharmony_ci } else { 80162306a36Sopenharmony_ci /* REVISIT report overcurrent to hub? */ 80262306a36Sopenharmony_ci ERR("vbus too slow, devctl %02x\n", devctl); 80362306a36Sopenharmony_ci tusb_musb_set_vbus(musb, 0); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci case OTG_STATE_A_WAIT_BCON: 80762306a36Sopenharmony_ci if (musb->a_wait_bcon != 0) 80862306a36Sopenharmony_ci idle_timeout = jiffies 80962306a36Sopenharmony_ci + msecs_to_jiffies(musb->a_wait_bcon); 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci case OTG_STATE_A_SUSPEND: 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci case OTG_STATE_B_WAIT_ACON: 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci default: 81662306a36Sopenharmony_ci break; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci schedule_delayed_work(&musb->irq_work, 0); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return idle_timeout; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic irqreturn_t tusb_musb_interrupt(int irq, void *__hci) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct musb *musb = __hci; 82762306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 82862306a36Sopenharmony_ci unsigned long flags, idle_timeout = 0; 82962306a36Sopenharmony_ci u32 int_mask, int_src; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Mask all interrupts to allow using both edge and level GPIO irq */ 83462306a36Sopenharmony_ci int_mask = musb_readl(tbase, TUSB_INT_MASK); 83562306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS; 83862306a36Sopenharmony_ci dev_dbg(musb->controller, "TUSB IRQ %08x\n", int_src); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci musb->int_usb = (u8) int_src; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* Acknowledge wake-up source interrupts */ 84362306a36Sopenharmony_ci if (int_src & TUSB_INT_SRC_DEV_WAKEUP) { 84462306a36Sopenharmony_ci u32 reg; 84562306a36Sopenharmony_ci u32 i; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (musb->tusb_revision == TUSB_REV_30) 84862306a36Sopenharmony_ci tusb_wbus_quirk(musb, 0); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* there are issues re-locking the PLL on wakeup ... */ 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* work around issue 8 */ 85362306a36Sopenharmony_ci for (i = 0xf7f7f7; i > 0xf7f7f7 - 1000; i--) { 85462306a36Sopenharmony_ci musb_writel(tbase, TUSB_SCRATCH_PAD, 0); 85562306a36Sopenharmony_ci musb_writel(tbase, TUSB_SCRATCH_PAD, i); 85662306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_SCRATCH_PAD); 85762306a36Sopenharmony_ci if (reg == i) 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci dev_dbg(musb->controller, "TUSB NOR not ready\n"); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* work around issue 13 (2nd half) */ 86362306a36Sopenharmony_ci tusb_set_clock_source(musb, 1); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_PRCM_WAKEUP_SOURCE); 86662306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_WAKEUP_CLEAR, reg); 86762306a36Sopenharmony_ci if (reg & ~TUSB_PRCM_WNORCS) { 86862306a36Sopenharmony_ci musb->is_active = 1; 86962306a36Sopenharmony_ci schedule_delayed_work(&musb->irq_work, 0); 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci dev_dbg(musb->controller, "wake %sactive %02x\n", 87262306a36Sopenharmony_ci musb->is_active ? "" : "in", reg); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */ 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (int_src & TUSB_INT_SRC_USB_IP_CONN) 87862306a36Sopenharmony_ci del_timer(&musb->dev_timer); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* OTG state change reports (annoyingly) not issued by Mentor core */ 88162306a36Sopenharmony_ci if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG 88262306a36Sopenharmony_ci | TUSB_INT_SRC_OTG_TIMEOUT 88362306a36Sopenharmony_ci | TUSB_INT_SRC_ID_STATUS_CHNG)) 88462306a36Sopenharmony_ci idle_timeout = tusb_otg_ints(musb, int_src, tbase); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* 88762306a36Sopenharmony_ci * Just clear the DMA interrupt if it comes as the completion for both 88862306a36Sopenharmony_ci * TX and RX is handled by the DMA callback in tusb6010_omap 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ci if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) { 89162306a36Sopenharmony_ci u32 dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); 89462306a36Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_CLEAR, dma_src); 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* EP interrupts. In OCP mode tusb6010 mirrors the MUSB interrupts */ 89862306a36Sopenharmony_ci if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX)) { 89962306a36Sopenharmony_ci u32 musb_src = musb_readl(tbase, TUSB_USBIP_INT_SRC); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_CLEAR, musb_src); 90262306a36Sopenharmony_ci musb->int_rx = (((musb_src >> 16) & 0xffff) << 1); 90362306a36Sopenharmony_ci musb->int_tx = (musb_src & 0xffff); 90462306a36Sopenharmony_ci } else { 90562306a36Sopenharmony_ci musb->int_rx = 0; 90662306a36Sopenharmony_ci musb->int_tx = 0; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff)) 91062306a36Sopenharmony_ci musb_interrupt(musb); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* Acknowledge TUSB interrupts. Clear only non-reserved bits */ 91362306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_SRC_CLEAR, 91462306a36Sopenharmony_ci int_src & ~TUSB_INT_MASK_RESERVED_BITS); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci tusb_musb_try_idle(musb, idle_timeout); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, int_mask); 91962306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return IRQ_HANDLED; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int dma_off; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* 92762306a36Sopenharmony_ci * Enables TUSB6010. Caller must take care of locking. 92862306a36Sopenharmony_ci * REVISIT: 92962306a36Sopenharmony_ci * - Check what is unnecessary in MGC_HdrcStart() 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistatic void tusb_musb_enable(struct musb *musb) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* Setup TUSB6010 main interrupt mask. Enable all interrupts except SOF. 93662306a36Sopenharmony_ci * REVISIT: Enable and deal with TUSB_INT_SRC_USB_IP_SOF */ 93762306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, TUSB_INT_SRC_USB_IP_SOF); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Setup TUSB interrupt, disable DMA and GPIO interrupts */ 94062306a36Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_MASK, 0); 94162306a36Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); 94262306a36Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* Clear all subsystem interrups */ 94562306a36Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_CLEAR, 0x7fffffff); 94662306a36Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_CLEAR, 0x7fffffff); 94762306a36Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_INT_CLEAR, 0x1ff); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Acknowledge pending interrupt(s) */ 95062306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_SRC_CLEAR, ~TUSB_INT_MASK_RESERVED_BITS); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Only 0 clock cycles for minimum interrupt de-assertion time and 95362306a36Sopenharmony_ci * interrupt polarity active low seems to work reliably here */ 95462306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_CTRL_CONF, 95562306a36Sopenharmony_ci TUSB_INT_CTRL_CONF_INT_RELCYC(0)); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci irq_set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* maybe force into the Default-A OTG state machine */ 96062306a36Sopenharmony_ci if (!(musb_readl(tbase, TUSB_DEV_OTG_STAT) 96162306a36Sopenharmony_ci & TUSB_DEV_OTG_STAT_ID_STATUS)) 96262306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_SRC_SET, 96362306a36Sopenharmony_ci TUSB_INT_SRC_ID_STATUS_CHNG); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (is_dma_capable() && dma_off) 96662306a36Sopenharmony_ci printk(KERN_WARNING "%s %s: dma not reactivated\n", 96762306a36Sopenharmony_ci __FILE__, __func__); 96862306a36Sopenharmony_ci else 96962306a36Sopenharmony_ci dma_off = 1; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci/* 97362306a36Sopenharmony_ci * Disables TUSB6010. Caller must take care of locking. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_cistatic void tusb_musb_disable(struct musb *musb) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* FIXME stop DMA, IRQs, timers, ... */ 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* disable all IRQs */ 98262306a36Sopenharmony_ci musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); 98362306a36Sopenharmony_ci musb_writel(tbase, TUSB_USBIP_INT_MASK, 0x7fffffff); 98462306a36Sopenharmony_ci musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); 98562306a36Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci del_timer(&musb->dev_timer); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (is_dma_capable() && !dma_off) { 99062306a36Sopenharmony_ci printk(KERN_WARNING "%s %s: dma still active\n", 99162306a36Sopenharmony_ci __FILE__, __func__); 99262306a36Sopenharmony_ci dma_off = 1; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci/* 99762306a36Sopenharmony_ci * Sets up TUSB6010 CPU interface specific signals and registers 99862306a36Sopenharmony_ci * Note: Settings optimized for OMAP24xx 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_cistatic void tusb_setup_cpu_interface(struct musb *musb) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* 100562306a36Sopenharmony_ci * Disable GPIO[5:0] pullups (used as output DMA requests) 100662306a36Sopenharmony_ci * Don't disable GPIO[7:6] as they are needed for wake-up. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci musb_writel(tbase, TUSB_PULLUP_1_CTRL, 0x0000003F); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* Disable all pullups on NOR IF, DMAREQ0 and DMAREQ1 */ 101162306a36Sopenharmony_ci musb_writel(tbase, TUSB_PULLUP_2_CTRL, 0x01FFFFFF); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* Turn GPIO[5:0] to DMAREQ[5:0] signals */ 101462306a36Sopenharmony_ci musb_writel(tbase, TUSB_GPIO_CONF, TUSB_GPIO_CONF_DMAREQ(0x3f)); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* Burst size 16x16 bits, all six DMA requests enabled, DMA request 101762306a36Sopenharmony_ci * de-assertion time 2 system clocks p 62 */ 101862306a36Sopenharmony_ci musb_writel(tbase, TUSB_DMA_REQ_CONF, 101962306a36Sopenharmony_ci TUSB_DMA_REQ_CONF_BURST_SIZE(2) | 102062306a36Sopenharmony_ci TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) | 102162306a36Sopenharmony_ci TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2)); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* Set 0 wait count for synchronous burst access */ 102462306a36Sopenharmony_ci musb_writel(tbase, TUSB_WAIT_COUNT, 1); 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int tusb_musb_start(struct musb *musb) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent); 103062306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 103162306a36Sopenharmony_ci unsigned long flags; 103262306a36Sopenharmony_ci u32 reg; 103362306a36Sopenharmony_ci int ret; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* 103662306a36Sopenharmony_ci * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and 103762306a36Sopenharmony_ci * 1.5 V voltage regulators of PM companion chip. Companion chip will then 103862306a36Sopenharmony_ci * provide then PGOOD signal to TUSB6010 which will release it from reset. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci gpiod_set_value(glue->enable, 1); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Wait for 100ms until TUSB6010 pulls INT pin down */ 104362306a36Sopenharmony_ci ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true, 104462306a36Sopenharmony_ci glue->intpin); 104562306a36Sopenharmony_ci if (ret) { 104662306a36Sopenharmony_ci pr_err("tusb: Powerup response failed\n"); 104762306a36Sopenharmony_ci return ret; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (musb_readl(tbase, TUSB_PROD_TEST_RESET) != 105362306a36Sopenharmony_ci TUSB_PROD_TEST_RESET_VAL) { 105462306a36Sopenharmony_ci printk(KERN_ERR "tusb: Unable to detect TUSB6010\n"); 105562306a36Sopenharmony_ci goto err; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci musb->tusb_revision = tusb_get_revision(musb); 105962306a36Sopenharmony_ci tusb_print_revision(musb); 106062306a36Sopenharmony_ci if (musb->tusb_revision < 2) { 106162306a36Sopenharmony_ci printk(KERN_ERR "tusb: Unsupported TUSB6010 revision %i\n", 106262306a36Sopenharmony_ci musb->tusb_revision); 106362306a36Sopenharmony_ci goto err; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* The uint bit for "USB non-PDR interrupt enable" has to be 1 when 106762306a36Sopenharmony_ci * NOR FLASH interface is used */ 106862306a36Sopenharmony_ci musb_writel(tbase, TUSB_VLYNQ_CTRL, 8); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* Select PHY free running 60MHz as a system clock */ 107162306a36Sopenharmony_ci tusb_set_clock_source(musb, 1); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* VBus valid timer 1us, disable DFT/Debug and VLYNQ clocks for 107462306a36Sopenharmony_ci * power saving, enable VBus detect and session end comparators, 107562306a36Sopenharmony_ci * enable IDpullup, enable VBus charging */ 107662306a36Sopenharmony_ci musb_writel(tbase, TUSB_PRCM_MNGMT, 107762306a36Sopenharmony_ci TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(0xa) | 107862306a36Sopenharmony_ci TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN | 107962306a36Sopenharmony_ci TUSB_PRCM_MNGMT_OTG_SESS_END_EN | 108062306a36Sopenharmony_ci TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN | 108162306a36Sopenharmony_ci TUSB_PRCM_MNGMT_OTG_ID_PULLUP); 108262306a36Sopenharmony_ci tusb_setup_cpu_interface(musb); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* simplify: always sense/pullup ID pins, as if in OTG mode */ 108562306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE); 108662306a36Sopenharmony_ci reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 108762306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, reg); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci reg = musb_readl(tbase, TUSB_PHY_OTG_CTRL); 109062306a36Sopenharmony_ci reg |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP; 109162306a36Sopenharmony_ci musb_writel(tbase, TUSB_PHY_OTG_CTRL, reg); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cierr: 109862306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci gpiod_set_value(glue->enable, 0); 110162306a36Sopenharmony_ci msleep(10); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci return -ENODEV; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic int tusb_musb_init(struct musb *musb) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct platform_device *pdev; 110962306a36Sopenharmony_ci struct resource *mem; 111062306a36Sopenharmony_ci void __iomem *sync = NULL; 111162306a36Sopenharmony_ci int ret; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); 111462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(musb->xceiv)) 111562306a36Sopenharmony_ci return -EPROBE_DEFER; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci pdev = to_platform_device(musb->controller); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* dma address for async dma */ 112062306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 112162306a36Sopenharmony_ci if (!mem) { 112262306a36Sopenharmony_ci pr_debug("no async dma resource?\n"); 112362306a36Sopenharmony_ci ret = -ENODEV; 112462306a36Sopenharmony_ci goto done; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci musb->async = mem->start; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* dma address for sync dma */ 112962306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 113062306a36Sopenharmony_ci if (!mem) { 113162306a36Sopenharmony_ci pr_debug("no sync dma resource?\n"); 113262306a36Sopenharmony_ci ret = -ENODEV; 113362306a36Sopenharmony_ci goto done; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci musb->sync = mem->start; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci sync = ioremap(mem->start, resource_size(mem)); 113862306a36Sopenharmony_ci if (!sync) { 113962306a36Sopenharmony_ci pr_debug("ioremap for sync failed\n"); 114062306a36Sopenharmony_ci ret = -ENOMEM; 114162306a36Sopenharmony_ci goto done; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci musb->sync_va = sync; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Offsets from base: VLYNQ at 0x000, MUSB regs at 0x400, 114662306a36Sopenharmony_ci * FIFOs at 0x600, TUSB at 0x800 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci musb->mregs += TUSB_BASE_OFFSET; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci ret = tusb_musb_start(musb); 115162306a36Sopenharmony_ci if (ret) { 115262306a36Sopenharmony_ci printk(KERN_ERR "Could not start tusb6010 (%d)\n", 115362306a36Sopenharmony_ci ret); 115462306a36Sopenharmony_ci goto done; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci musb->isr = tusb_musb_interrupt; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci musb->xceiv->set_power = tusb_draw_power; 115962306a36Sopenharmony_ci the_musb = musb; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci timer_setup(&musb->dev_timer, musb_do_idle, 0); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cidone: 116462306a36Sopenharmony_ci if (ret < 0) { 116562306a36Sopenharmony_ci if (sync) 116662306a36Sopenharmony_ci iounmap(sync); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci usb_put_phy(musb->xceiv); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci return ret; 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic int tusb_musb_exit(struct musb *musb) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci del_timer_sync(&musb->dev_timer); 117862306a36Sopenharmony_ci the_musb = NULL; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci gpiod_set_value(glue->enable, 0); 118162306a36Sopenharmony_ci msleep(10); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci iounmap(musb->sync_va); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci usb_put_phy(musb->xceiv); 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic const struct musb_platform_ops tusb_ops = { 119062306a36Sopenharmony_ci .quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB | 119162306a36Sopenharmony_ci MUSB_G_NO_SKB_RESERVE, 119262306a36Sopenharmony_ci .init = tusb_musb_init, 119362306a36Sopenharmony_ci .exit = tusb_musb_exit, 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci .ep_offset = tusb_ep_offset, 119662306a36Sopenharmony_ci .ep_select = tusb_ep_select, 119762306a36Sopenharmony_ci .fifo_offset = tusb_fifo_offset, 119862306a36Sopenharmony_ci .readb = tusb_readb, 119962306a36Sopenharmony_ci .writeb = tusb_writeb, 120062306a36Sopenharmony_ci .read_fifo = tusb_read_fifo, 120162306a36Sopenharmony_ci .write_fifo = tusb_write_fifo, 120262306a36Sopenharmony_ci#ifdef CONFIG_USB_TUSB_OMAP_DMA 120362306a36Sopenharmony_ci .dma_init = tusb_dma_controller_create, 120462306a36Sopenharmony_ci .dma_exit = tusb_dma_controller_destroy, 120562306a36Sopenharmony_ci#endif 120662306a36Sopenharmony_ci .enable = tusb_musb_enable, 120762306a36Sopenharmony_ci .disable = tusb_musb_disable, 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci .set_mode = tusb_musb_set_mode, 121062306a36Sopenharmony_ci .try_idle = tusb_musb_try_idle, 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci .vbus_status = tusb_musb_vbus_status, 121362306a36Sopenharmony_ci .set_vbus = tusb_musb_set_vbus, 121462306a36Sopenharmony_ci}; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic const struct platform_device_info tusb_dev_info = { 121762306a36Sopenharmony_ci .name = "musb-hdrc", 121862306a36Sopenharmony_ci .id = PLATFORM_DEVID_AUTO, 121962306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 122062306a36Sopenharmony_ci}; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int tusb_probe(struct platform_device *pdev) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct resource musb_resources[3]; 122562306a36Sopenharmony_ci struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); 122662306a36Sopenharmony_ci struct platform_device *musb; 122762306a36Sopenharmony_ci struct tusb6010_glue *glue; 122862306a36Sopenharmony_ci struct platform_device_info pinfo; 122962306a36Sopenharmony_ci int ret; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); 123262306a36Sopenharmony_ci if (!glue) 123362306a36Sopenharmony_ci return -ENOMEM; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci glue->dev = &pdev->dev; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci glue->enable = devm_gpiod_get(glue->dev, "enable", GPIOD_OUT_LOW); 123862306a36Sopenharmony_ci if (IS_ERR(glue->enable)) 123962306a36Sopenharmony_ci return dev_err_probe(glue->dev, PTR_ERR(glue->enable), 124062306a36Sopenharmony_ci "could not obtain power on/off GPIO\n"); 124162306a36Sopenharmony_ci glue->intpin = devm_gpiod_get(glue->dev, "int", GPIOD_IN); 124262306a36Sopenharmony_ci if (IS_ERR(glue->intpin)) 124362306a36Sopenharmony_ci return dev_err_probe(glue->dev, PTR_ERR(glue->intpin), 124462306a36Sopenharmony_ci "could not obtain INT GPIO\n"); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci pdata->platform_ops = &tusb_ops; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci usb_phy_generic_register(); 124962306a36Sopenharmony_ci platform_set_drvdata(pdev, glue); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci memset(musb_resources, 0x00, sizeof(*musb_resources) * 125262306a36Sopenharmony_ci ARRAY_SIZE(musb_resources)); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci musb_resources[0].name = pdev->resource[0].name; 125562306a36Sopenharmony_ci musb_resources[0].start = pdev->resource[0].start; 125662306a36Sopenharmony_ci musb_resources[0].end = pdev->resource[0].end; 125762306a36Sopenharmony_ci musb_resources[0].flags = pdev->resource[0].flags; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci musb_resources[1].name = pdev->resource[1].name; 126062306a36Sopenharmony_ci musb_resources[1].start = pdev->resource[1].start; 126162306a36Sopenharmony_ci musb_resources[1].end = pdev->resource[1].end; 126262306a36Sopenharmony_ci musb_resources[1].flags = pdev->resource[1].flags; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci musb_resources[2] = DEFINE_RES_IRQ_NAMED(gpiod_to_irq(glue->intpin), "mc"); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci pinfo = tusb_dev_info; 126762306a36Sopenharmony_ci pinfo.parent = &pdev->dev; 126862306a36Sopenharmony_ci pinfo.res = musb_resources; 126962306a36Sopenharmony_ci pinfo.num_res = ARRAY_SIZE(musb_resources); 127062306a36Sopenharmony_ci pinfo.data = pdata; 127162306a36Sopenharmony_ci pinfo.size_data = sizeof(*pdata); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci glue->musb = musb = platform_device_register_full(&pinfo); 127462306a36Sopenharmony_ci if (IS_ERR(musb)) { 127562306a36Sopenharmony_ci ret = PTR_ERR(musb); 127662306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); 127762306a36Sopenharmony_ci return ret; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return 0; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic void tusb_remove(struct platform_device *pdev) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct tusb6010_glue *glue = platform_get_drvdata(pdev); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci platform_device_unregister(glue->musb); 128862306a36Sopenharmony_ci usb_phy_generic_unregister(glue->phy); 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistatic struct platform_driver tusb_driver = { 129262306a36Sopenharmony_ci .probe = tusb_probe, 129362306a36Sopenharmony_ci .remove_new = tusb_remove, 129462306a36Sopenharmony_ci .driver = { 129562306a36Sopenharmony_ci .name = "musb-tusb", 129662306a36Sopenharmony_ci }, 129762306a36Sopenharmony_ci}; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ciMODULE_DESCRIPTION("TUSB6010 MUSB Glue Layer"); 130062306a36Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); 130162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 130262306a36Sopenharmony_cimodule_platform_driver(tusb_driver); 1303