diff options
Diffstat (limited to 'drivers/usb/musb/musb_core.c')
| -rw-r--r-- | drivers/usb/musb/musb_core.c | 1768 |
1 files changed, 944 insertions, 824 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4000cf6d1e8..eff3c5cf84f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -83,7 +83,7 @@ * This gets many kinds of configuration information: * - Kconfig for everything user-configurable * - platform_device for addressing, irq, and platform_data - * - platform_data is mostly for board-specific informarion + * - platform_data is mostly for board-specific information * (plus recentrly, SOC or family details) * * Most of the conditional compilation will (someday) vanish. @@ -93,31 +93,18 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/kobject.h> +#include <linux/prefetch.h> #include <linux/platform_device.h> #include <linux/io.h> - -#ifdef CONFIG_ARM -#include <mach/hardware.h> -#include <mach/memory.h> -#include <asm/mach-types.h> -#endif +#include <linux/dma-mapping.h> #include "musb_core.h" - -#ifdef CONFIG_ARCH_DAVINCI -#include "davinci.h" -#endif - +#define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) -unsigned musb_debug; -module_param_named(debug, musb_debug, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); - #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" #define DRIVER_DESC "Inventra Dual-Role USB Controller Driver" @@ -125,7 +112,7 @@ MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); #define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION -#define MUSB_DRIVER_NAME "musb_hdrc" +#define MUSB_DRIVER_NAME "musb-hdrc" const char musb_driver_name[] = MUSB_DRIVER_NAME; MODULE_DESCRIPTION(DRIVER_INFO); @@ -138,28 +125,121 @@ MODULE_ALIAS("platform:" MUSB_DRIVER_NAME); static inline struct musb *dev_to_musb(struct device *dev) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD - /* usbcore insists dev->driver_data is a "struct hcd *" */ - return hcd_to_musb(dev_get_drvdata(dev)); -#else return dev_get_drvdata(dev); -#endif } /*-------------------------------------------------------------------------*/ -#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) +#ifndef CONFIG_BLACKFIN +static int musb_ulpi_read(struct usb_phy *phy, u32 offset) +{ + void __iomem *addr = phy->io_priv; + int i = 0; + u8 r; + u8 power; + int ret; + + pm_runtime_get_sync(phy->io_dev); + + /* Make sure the transceiver is not in low power mode */ + power = musb_readb(addr, MUSB_POWER); + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(addr, MUSB_POWER, power); + + /* REVISIT: musbhdrc_ulpi_an.pdf recommends setting the + * ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM. + */ + + musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, + MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR); + + while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) + & MUSB_ULPI_REG_CMPLT)) { + i++; + if (i == 10000) { + ret = -ETIMEDOUT; + goto out; + } + + } + r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); + r &= ~MUSB_ULPI_REG_CMPLT; + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); + + ret = musb_readb(addr, MUSB_ULPI_REG_DATA); + +out: + pm_runtime_put(phy->io_dev); + + return ret; +} + +static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data) +{ + void __iomem *addr = phy->io_priv; + int i = 0; + u8 r = 0; + u8 power; + int ret = 0; + + pm_runtime_get_sync(phy->io_dev); + + /* Make sure the transceiver is not in low power mode */ + power = musb_readb(addr, MUSB_POWER); + power &= ~MUSB_POWER_SUSPENDM; + musb_writeb(addr, MUSB_POWER, power); + + musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset); + musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)data); + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ); + + while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) + & MUSB_ULPI_REG_CMPLT)) { + i++; + if (i == 10000) { + ret = -ETIMEDOUT; + goto out; + } + } + + r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); + r &= ~MUSB_ULPI_REG_CMPLT; + musb_writeb(addr, MUSB_ULPI_REG_CONTROL, r); + +out: + pm_runtime_put(phy->io_dev); + + return ret; +} +#else +#define musb_ulpi_read NULL +#define musb_ulpi_write NULL +#endif + +static struct usb_phy_io_ops musb_ulpi_access = { + .read = musb_ulpi_read, + .write = musb_ulpi_write, +}; + +/*-------------------------------------------------------------------------*/ + +#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN) /* * Load an endpoint's FIFO */ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; + if (unlikely(len == 0)) + return; + prefetch((u8 *)src); - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', hw_ep->epnum, fifo, len, src); /* we can't assume unaligned reads work */ @@ -169,7 +249,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) /* best case is 32bit-aligned source address */ if ((0x02 & (unsigned long) src) == 0) { if (len >= 4) { - writesl(fifo, src + index, len >> 2); + iowrite32_rep(fifo, src + index, len >> 2); index += len & ~0x03; } if (len & 0x02) { @@ -178,7 +258,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) } } else { if (len >= 2) { - writesw(fifo, src + index, len >> 1); + iowrite16_rep(fifo, src + index, len >> 1); index += len & ~0x01; } } @@ -186,18 +266,23 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) musb_writeb(fifo, 0, src[index]); } else { /* byte aligned */ - writesb(fifo, src, len); + iowrite8_rep(fifo, src, len); } } +#if !defined(CONFIG_USB_MUSB_AM35X) /* * Unload an endpoint's FIFO */ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + if (unlikely(len == 0)) + return; + + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', hw_ep->epnum, fifo, len, dst); /* we can't assume unaligned writes work */ @@ -207,7 +292,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) /* best case is 32bit-aligned destination address */ if ((0x02 & (unsigned long) dst) == 0) { if (len >= 4) { - readsl(fifo, dst, len >> 2); + ioread32_rep(fifo, dst, len >> 2); index = len & ~0x03; } if (len & 0x02) { @@ -216,7 +301,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } else { if (len >= 2) { - readsw(fifo, dst, len >> 1); + ioread16_rep(fifo, dst, len >> 1); index = len & ~0x01; } } @@ -224,9 +309,10 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) dst[index] = musb_readb(fifo, 0); } else { /* byte aligned */ - readsb(fifo, dst, len); + ioread8_rep(fifo, dst, len); } } +#endif #endif /* normal PIO */ @@ -265,86 +351,59 @@ void musb_load_testpacket(struct musb *musb) /*-------------------------------------------------------------------------*/ -const char *otg_state_string(struct musb *musb) -{ - switch (musb->xceiv.state) { - case OTG_STATE_A_IDLE: return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; - case OTG_STATE_A_HOST: return "a_host"; - case OTG_STATE_A_SUSPEND: return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; - case OTG_STATE_B_IDLE: return "b_idle"; - case OTG_STATE_B_SRP_INIT: return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; - case OTG_STATE_B_HOST: return "b_host"; - default: return "UNDEFINED"; - } -} - -#ifdef CONFIG_USB_MUSB_OTG - -/* - * See also USB_OTG_1-3.pdf 6.6.5 Timers - * REVISIT: Are the other timers done in the hardware? - */ -#define TB_ASE0_BRST 100 /* Min 3.125 ms */ - /* * Handles OTG hnp timeouts, such as b_ase0_brst */ -void musb_otg_timer_func(unsigned long data) +static void musb_otg_timer_func(unsigned long data) { struct musb *musb = (struct musb *)data; unsigned long flags; spin_lock_irqsave(&musb->lock, flags); - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n"); + dev_dbg(musb->controller, "HNP: b_wait_acon timeout; back to b_peripheral\n"); musb_g_disconnect(musb); - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 0; break; + case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n"); - musb_hnp_stop(musb); + dev_dbg(musb->controller, "HNP: %s timeout\n", + usb_otg_state_string(musb->xceiv->state)); + musb_platform_set_vbus(musb, 0); + musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: - DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: Unhandled mode %s\n", + usb_otg_state_string(musb->xceiv->state)); } - musb->ignore_disconnect = 0; spin_unlock_irqrestore(&musb->lock, flags); } -static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0); - /* - * Stops the B-device HNP state. Caller must take care of locking. + * Stops the HNP transition. Caller must take care of locking. */ void musb_hnp_stop(struct musb *musb) { - struct usb_hcd *hcd = musb_to_hcd(musb); + struct usb_hcd *hcd = musb->hcd; void __iomem *mbase = musb->mregs; u8 reg; - switch (musb->xceiv.state) { + dev_dbg(musb->controller, "HNP: stop from %s\n", + usb_otg_state_string(musb->xceiv->state)); + + switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: Switching back to A-host\n"); musb_g_disconnect(musb); - musb->xceiv.state = OTG_STATE_A_IDLE; - MUSB_HST_MODE(musb); - musb->is_active = 0; + dev_dbg(musb->controller, "HNP: back to %s\n", + usb_otg_state_string(musb->xceiv->state)); break; case OTG_STATE_B_HOST: - DBG(1, "HNP: Disabling HR\n"); - hcd->self.is_b_host = 0; - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + dev_dbg(musb->controller, "HNP: Disabling HR\n"); + if (hcd) + hcd->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; MUSB_DEV_MODE(musb); reg = musb_readb(mbase, MUSB_POWER); reg |= MUSB_POWER_SUSPENDM; @@ -352,8 +411,8 @@ void musb_hnp_stop(struct musb *musb) /* REVISIT: Start SESSION_REQUEST here? */ break; default: - DBG(1, "HNP: Stopping in unknown state %s\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n", + usb_otg_state_string(musb->xceiv->state)); } /* @@ -361,12 +420,9 @@ void musb_hnp_stop(struct musb *musb) * which cause occasional OPT A "Did not receive reset after connect" * errors. */ - musb->port1_status &= - ~(1 << USB_PORT_FEAT_C_CONNECTION); + musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); } -#endif - /* * Interrupt Service Routine to record USB "global" interrupts. * Since these do not happen often and signify things of @@ -379,17 +435,12 @@ void musb_hnp_stop(struct musb *musb) * @param power */ -#define STAGE0_MASK (MUSB_INTR_RESUME | MUSB_INTR_SESSREQ \ - | MUSB_INTR_VBUSERROR | MUSB_INTR_CONNECT \ - | MUSB_INTR_RESET) - static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, - u8 devctl, u8 power) + u8 devctl) { irqreturn_t handled = IRQ_NONE; - void __iomem *mbase = musb->mregs; - DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, + dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl, int_usb); /* in host mode, the peripheral may issue remote wakeup. @@ -398,20 +449,23 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - DBG(3, "RESUME (%s)\n", otg_state_string(musb)); + dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->state)); if (devctl & MUSB_DEVCTL_HM) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD - switch (musb->xceiv.state) { + void __iomem *mbase = musb->mregs; + u8 power; + + switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: /* remote wakeup? later, GetPortStatus * will stop RESUME signaling */ + power = musb_readb(musb->mregs, MUSB_POWER); if (power & MUSB_POWER_SUSPENDM) { /* spurious */ musb->int_usb &= ~MUSB_INTR_SUSPEND; - DBG(2, "Spurious SUSPENDM\n"); + dev_dbg(musb->controller, "Spurious SUSPENDM\n"); break; } @@ -423,33 +477,32 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, (USB_PORT_STAT_C_SUSPEND << 16) | MUSB_PORT_STAT_RESUME; musb->rh_timer = jiffies - + msecs_to_jiffies(20); + + msecs_to_jiffies(20); + schedule_delayed_work( + &musb->finish_resume_work, + msecs_to_jiffies(20)); - musb->xceiv.state = OTG_STATE_A_HOST; + musb->xceiv->state = OTG_STATE_A_HOST; musb->is_active = 1; - usb_hcd_resume_root_hub(musb_to_hcd(musb)); + musb_host_resume_root_hub(musb); break; case OTG_STATE_B_WAIT_ACON: - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 1; MUSB_DEV_MODE(musb); break; default: WARNING("bogus %s RESUME (%s)\n", "host", - otg_state_string(musb)); + usb_otg_state_string(musb->xceiv->state)); } -#endif } else { - switch (musb->xceiv.state) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD + switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: /* possibly DISCONNECT is upcoming */ - musb->xceiv.state = OTG_STATE_A_HOST; - usb_hcd_resume_root_hub(musb_to_hcd(musb)); + musb->xceiv->state = OTG_STATE_A_HOST; + musb_host_resume_root_hub(musb); break; -#endif -#ifdef CONFIG_USB_GADGET_MUSB_HDRC case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_PERIPHERAL: /* disconnect while suspended? we may @@ -467,19 +520,26 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, case OTG_STATE_B_IDLE: musb->int_usb &= ~MUSB_INTR_SUSPEND; break; -#endif default: WARNING("bogus %s RESUME (%s)\n", "peripheral", - otg_state_string(musb)); + usb_otg_state_string(musb->xceiv->state)); } } } -#ifdef CONFIG_USB_MUSB_HDRC_HCD /* see manual for the order of the tests */ if (int_usb & MUSB_INTR_SESSREQ) { - DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(musb)); + void __iomem *mbase = musb->mregs; + + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS + && (devctl & MUSB_DEVCTL_BDEVICE)) { + dev_dbg(musb->controller, "SessReq while on B state\n"); + return IRQ_HANDLED; + } + + dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n", + usb_otg_state_string(musb->xceiv->state)); /* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: @@ -490,9 +550,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); musb->ep0_stage = MUSB_EP0_START; - musb->xceiv.state = OTG_STATE_A_IDLE; + musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); - musb_set_vbus(musb, 1); + musb_platform_set_vbus(musb, 1); handled = IRQ_HANDLED; } @@ -516,7 +576,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, * REVISIT: do delays from lots of DEBUG_KERNEL checks * make trouble here, keeping VBUS < 4.4V ? */ - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_A_HOST: /* recovery is dicey once we've gotten past the * initial stages of enumeration, but if VBUS @@ -527,22 +587,25 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, case OTG_STATE_A_WAIT_BCON: case OTG_STATE_A_WAIT_VRISE: if (musb->vbuserr_retry) { + void __iomem *mbase = musb->mregs; + musb->vbuserr_retry--; ignore = 1; devctl |= MUSB_DEVCTL_SESSION; musb_writeb(mbase, MUSB_DEVCTL, devctl); } else { musb->port1_status |= - (1 << USB_PORT_FEAT_OVER_CURRENT) - | (1 << USB_PORT_FEAT_C_OVER_CURRENT); + USB_PORT_STAT_OVERCURRENT + | (USB_PORT_STAT_C_OVERCURRENT << 16); } break; default: break; } - DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", - otg_state_string(musb), + dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller, + "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", + usb_otg_state_string(musb->xceiv->state), devctl, ({ char *s; switch (devctl & MUSB_DEVCTL_VBUS) { @@ -555,34 +618,89 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */ default: s = "VALID"; break; - }; s; }), + } s; }), VBUSERR_RETRY_COUNT - musb->vbuserr_retry, musb->port1_status); /* go through A_WAIT_VFALL then start a new session */ if (!ignore) - musb_set_vbus(musb, 0); + musb_platform_set_vbus(musb, 0); handled = IRQ_HANDLED; } + if (int_usb & MUSB_INTR_SUSPEND) { + dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x\n", + usb_otg_state_string(musb->xceiv->state), devctl); + handled = IRQ_HANDLED; + + switch (musb->xceiv->state) { + case OTG_STATE_A_PERIPHERAL: + /* We also come here if the cable is removed, since + * this silicon doesn't report ID-no-longer-grounded. + * + * We depend on T(a_wait_bcon) to shut us down, and + * hope users don't do anything dicey during this + * undesired detour through A_WAIT_BCON. + */ + musb_hnp_stop(musb); + musb_host_resume_root_hub(musb); + musb_root_disconnect(musb); + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon + ? : OTG_TIME_A_WAIT_BCON)); + + break; + case OTG_STATE_B_IDLE: + if (!musb->is_active) + break; + case OTG_STATE_B_PERIPHERAL: + musb_g_suspend(musb); + musb->is_active = musb->g.b_hnp_enable; + if (musb->is_active) { + musb->xceiv->state = OTG_STATE_B_WAIT_ACON; + dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies( + OTG_TIME_B_ASE0_BRST)); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (musb->a_wait_bcon != 0) + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon)); + break; + case OTG_STATE_A_HOST: + musb->xceiv->state = OTG_STATE_A_SUSPEND; + musb->is_active = musb->hcd->self.b_hnp_enable; + break; + case OTG_STATE_B_HOST: + /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ + dev_dbg(musb->controller, "REVISIT: SUSPEND as B_HOST\n"); + break; + default: + /* "should not happen" */ + musb->is_active = 0; + break; + } + } + if (int_usb & MUSB_INTR_CONNECT) { - struct usb_hcd *hcd = musb_to_hcd(musb); + struct usb_hcd *hcd = musb->hcd; handled = IRQ_HANDLED; musb->is_active = 1; - set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); musb->ep0_stage = MUSB_EP0_START; -#ifdef CONFIG_USB_MUSB_OTG /* flush endpoints when transitioning from Device Mode */ if (is_peripheral_active(musb)) { /* REVISIT HNP; just force disconnect */ } - musb_writew(mbase, MUSB_INTRTXE, musb->epmask); - musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe); - musb_writeb(mbase, MUSB_INTRUSBE, 0xf7); -#endif + musb->intrtxe = musb->epmask; + musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe); + musb->intrrxe = musb->epmask & 0xfffe; + musb_writew(musb->mregs, MUSB_INTRRXE, musb->intrrxe); + musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7); musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED |USB_PORT_STAT_HIGH_SPEED |USB_PORT_STAT_ENABLE @@ -594,47 +712,91 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (devctl & MUSB_DEVCTL_LSDEV) musb->port1_status |= USB_PORT_STAT_LOW_SPEED; - if (hcd->status_urb) - usb_hcd_poll_rh_status(hcd); - else - usb_hcd_resume_root_hub(hcd); - - MUSB_HST_MODE(musb); - /* indicate new connection to OTG machine */ - switch (musb->xceiv.state) { + switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { - DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); - musb->xceiv.state = OTG_STATE_B_HOST; - hcd->self.is_b_host = 1; + dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n"); int_usb &= ~MUSB_INTR_SUSPEND; + goto b_host; } else - DBG(1, "CONNECT as b_peripheral???\n"); + dev_dbg(musb->controller, "CONNECT as b_peripheral???\n"); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: Waiting to switch to b_host state\n"); - musb->xceiv.state = OTG_STATE_B_HOST; - hcd->self.is_b_host = 1; + dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n"); +b_host: + musb->xceiv->state = OTG_STATE_B_HOST; + if (musb->hcd) + musb->hcd->self.is_b_host = 1; + del_timer(&musb->otg_timer); break; default: if ((devctl & MUSB_DEVCTL_VBUS) == (3 << MUSB_DEVCTL_VBUS_SHIFT)) { - musb->xceiv.state = OTG_STATE_A_HOST; - hcd->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_A_HOST; + if (hcd) + hcd->self.is_b_host = 0; } break; } - DBG(1, "CONNECT (%s) devctl %02x\n", - otg_state_string(musb), devctl); + + musb_host_poke_root_hub(musb); + + dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n", + usb_otg_state_string(musb->xceiv->state), devctl); + } + + if (int_usb & MUSB_INTR_DISCONNECT) { + dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n", + usb_otg_state_string(musb->xceiv->state), + MUSB_MODE(musb), devctl); + handled = IRQ_HANDLED; + + switch (musb->xceiv->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_SUSPEND: + musb_host_resume_root_hub(musb); + musb_root_disconnect(musb); + if (musb->a_wait_bcon != 0) + musb_platform_try_idle(musb, jiffies + + msecs_to_jiffies(musb->a_wait_bcon)); + break; + case OTG_STATE_B_HOST: + /* REVISIT this behaves for "real disconnect" + * cases; make sure the other transitions from + * from B_HOST act right too. The B_HOST code + * in hnp_stop() is currently not used... + */ + musb_root_disconnect(musb); + if (musb->hcd) + musb->hcd->self.is_b_host = 0; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + MUSB_DEV_MODE(musb); + musb_g_disconnect(musb); + break; + case OTG_STATE_A_PERIPHERAL: + musb_hnp_stop(musb); + musb_root_disconnect(musb); + /* FALLTHROUGH */ + case OTG_STATE_B_WAIT_ACON: + /* FALLTHROUGH */ + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_IDLE: + musb_g_disconnect(musb); + break; + default: + WARNING("unhandled DISCONNECT transition (%s)\n", + usb_otg_state_string(musb->xceiv->state)); + break; + } } -#endif /* CONFIG_USB_MUSB_HDRC_HCD */ /* mentor saves a bit: bus reset and babble share the same irq. * only host sees babble; only peripheral sees bus reset. */ if (int_usb & MUSB_INTR_RESET) { - if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) { + handled = IRQ_HANDLED; + if ((devctl & MUSB_DEVCTL_HM) != 0) { /* * Looks like non-HS BABBLE can be ignored, but * HS BABBLE is an error condition. For HS the solution @@ -643,74 +805,52 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, * stop the session. */ if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) - DBG(1, "BABBLE devctl: %02x\n", devctl); + dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); else { ERR("Stopping host session -- babble\n"); - musb_writeb(mbase, MUSB_DEVCTL, 0); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } - } else if (is_peripheral_capable()) { - DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); - switch (musb->xceiv.state) { -#ifdef CONFIG_USB_OTG + } else { + dev_dbg(musb->controller, "BUS RESET as %s\n", + usb_otg_state_string(musb->xceiv->state)); + switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: - /* We need to ignore disconnect on suspend - * otherwise tusb 2.0 won't reconnect after a - * power cycle, which breaks otg compliance. - */ - musb->ignore_disconnect = 1; musb_g_reset(musb); /* FALLTHROUGH */ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ - DBG(1, "HNP: Setting timer as %s\n", - otg_state_string(musb)); - musb_otg_timer.data = (unsigned long)musb; - mod_timer(&musb_otg_timer, jiffies - + msecs_to_jiffies(100)); + /* never use invalid T(a_wait_bcon) */ + dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n", + usb_otg_state_string(musb->xceiv->state), + TA_WAIT_BCON(musb)); + mod_timer(&musb->otg_timer, jiffies + + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL: - musb_hnp_stop(musb); + del_timer(&musb->otg_timer); + musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: RESET (%s), to b_peripheral\n", - otg_state_string(musb)); - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n", + usb_otg_state_string(musb->xceiv->state)); + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; -#endif case OTG_STATE_B_IDLE: - musb->xceiv.state = OTG_STATE_B_PERIPHERAL; + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; /* FALLTHROUGH */ case OTG_STATE_B_PERIPHERAL: musb_g_reset(musb); break; default: - DBG(1, "Unhandled BUS RESET as %s\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n", + usb_otg_state_string(musb->xceiv->state)); } } - - handled = IRQ_HANDLED; } - schedule_work(&musb->irq_work); - - return handled; -} -/* - * Interrupt Service Routine to record USB "global" interrupts. - * Since these do not happen often and signify things of - * paramount importance, it seems OK to check them individually; - * the order of the tests is specified in the manual - * - * @param musb instance pointer - * @param int_usb register contents - * @param devctl - * @param power - */ -static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, - u8 devctl, u8 power) -{ - irqreturn_t handled = IRQ_NONE; + /* handle babble condition */ + if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) + schedule_work(&musb->recover_work); #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or @@ -730,7 +870,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, u8 epnum; u16 frame; - DBG(6, "START_OF_FRAME\n"); + dev_dbg(musb->controller, "START_OF_FRAME\n"); handled = IRQ_HANDLED; /* start any periodic Tx transfers waiting for current frame */ @@ -757,178 +897,81 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, } #endif - if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { - DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", - otg_state_string(musb), - MUSB_MODE(musb), devctl); - handled = IRQ_HANDLED; + schedule_work(&musb->irq_work); - switch (musb->xceiv.state) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD - case OTG_STATE_A_HOST: - case OTG_STATE_A_SUSPEND: - usb_hcd_resume_root_hub(musb_to_hcd(musb)); - musb_root_disconnect(musb); - if (musb->a_wait_bcon != 0 && is_otg_enabled(musb)) - musb_platform_try_idle(musb, jiffies - + msecs_to_jiffies(musb->a_wait_bcon)); - break; -#endif /* HOST */ -#ifdef CONFIG_USB_MUSB_OTG - case OTG_STATE_B_HOST: - musb_hnp_stop(musb); - break; - case OTG_STATE_A_PERIPHERAL: - musb_hnp_stop(musb); - musb_root_disconnect(musb); - /* FALLTHROUGH */ - case OTG_STATE_B_WAIT_ACON: - /* FALLTHROUGH */ -#endif /* OTG */ -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - case OTG_STATE_B_PERIPHERAL: - case OTG_STATE_B_IDLE: - musb_g_disconnect(musb); - break; -#endif /* GADGET */ - default: - WARNING("unhandled DISCONNECT transition (%s)\n", - otg_state_string(musb)); - break; - } + return handled; +} - schedule_work(&musb->irq_work); - } +/*-------------------------------------------------------------------------*/ - if (int_usb & MUSB_INTR_SUSPEND) { - DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", - otg_state_string(musb), devctl, power); - handled = IRQ_HANDLED; +static void musb_generic_disable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + u16 temp; - switch (musb->xceiv.state) { -#ifdef CONFIG_USB_MUSB_OTG - case OTG_STATE_A_PERIPHERAL: - /* - * We cannot stop HNP here, devctl BDEVICE might be - * still set. - */ - break; -#endif - case OTG_STATE_B_PERIPHERAL: - musb_g_suspend(musb); - musb->is_active = is_otg_enabled(musb) - && musb->xceiv.gadget->b_hnp_enable; - if (musb->is_active) { -#ifdef CONFIG_USB_MUSB_OTG - musb->xceiv.state = OTG_STATE_B_WAIT_ACON; - DBG(1, "HNP: Setting timer for b_ase0_brst\n"); - musb_otg_timer.data = (unsigned long)musb; - mod_timer(&musb_otg_timer, jiffies - + msecs_to_jiffies(TB_ASE0_BRST)); -#endif - } - break; - case OTG_STATE_A_WAIT_BCON: - if (musb->a_wait_bcon != 0) - musb_platform_try_idle(musb, jiffies - + msecs_to_jiffies(musb->a_wait_bcon)); - break; - case OTG_STATE_A_HOST: - musb->xceiv.state = OTG_STATE_A_SUSPEND; - musb->is_active = is_otg_enabled(musb) - && musb->xceiv.host->b_hnp_enable; - break; - case OTG_STATE_B_HOST: - /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ - DBG(1, "REVISIT: SUSPEND as B_HOST\n"); - break; - default: - /* "should not happen" */ - musb->is_active = 0; - break; - } - schedule_work(&musb->irq_work); - } + /* disable interrupts */ + musb_writeb(mbase, MUSB_INTRUSBE, 0); + musb->intrtxe = 0; + musb_writew(mbase, MUSB_INTRTXE, 0); + musb->intrrxe = 0; + musb_writew(mbase, MUSB_INTRRXE, 0); + /* off */ + musb_writeb(mbase, MUSB_DEVCTL, 0); - return handled; -} + /* flush pending interrupts */ + temp = musb_readb(mbase, MUSB_INTRUSB); + temp = musb_readw(mbase, MUSB_INTRTX); + temp = musb_readw(mbase, MUSB_INTRRX); -/*-------------------------------------------------------------------------*/ +} /* -* Program the HDRC to start (enable interrupts, dma, etc.). -*/ + * Program the HDRC to start (enable interrupts, dma, etc.). + */ void musb_start(struct musb *musb) { - void __iomem *regs = musb->mregs; - u8 devctl = musb_readb(regs, MUSB_DEVCTL); + void __iomem *regs = musb->mregs; + u8 devctl = musb_readb(regs, MUSB_DEVCTL); - DBG(2, "<== devctl %02x\n", devctl); + dev_dbg(musb->controller, "<== devctl %02x\n", devctl); /* Set INT enable registers, enable interrupts */ - musb_writew(regs, MUSB_INTRTXE, musb->epmask); - musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe); + musb->intrtxe = musb->epmask; + musb_writew(regs, MUSB_INTRTXE, musb->intrtxe); + musb->intrrxe = musb->epmask & 0xfffe; + musb_writew(regs, MUSB_INTRRXE, musb->intrrxe); musb_writeb(regs, MUSB_INTRUSBE, 0xf7); musb_writeb(regs, MUSB_TESTMODE, 0); /* put into basic highspeed mode and start session */ musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE - | MUSB_POWER_SOFTCONN - | MUSB_POWER_HSENAB - /* ENSUSPEND wedges tusb */ - /* | MUSB_POWER_ENSUSPEND */ - ); + | MUSB_POWER_HSENAB + /* ENSUSPEND wedges tusb */ + /* | MUSB_POWER_ENSUSPEND */ + ); musb->is_active = 0; devctl = musb_readb(regs, MUSB_DEVCTL); devctl &= ~MUSB_DEVCTL_SESSION; - if (is_otg_enabled(musb)) { - /* session started after: - * (a) ID-grounded irq, host mode; - * (b) vbus present/connect IRQ, peripheral mode; - * (c) peripheral initiates, using SRP - */ - if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) - musb->is_active = 1; - else - devctl |= MUSB_DEVCTL_SESSION; - - } else if (is_host_enabled(musb)) { - /* assume ID pin is hard-wired to ground */ + /* session started after: + * (a) ID-grounded irq, host mode; + * (b) vbus present/connect IRQ, peripheral mode; + * (c) peripheral initiates, using SRP + */ + if (musb->port_mode != MUSB_PORT_MODE_HOST && + (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) { + musb->is_active = 1; + } else { devctl |= MUSB_DEVCTL_SESSION; - - } else /* peripheral is enabled */ { - if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) - musb->is_active = 1; } + musb_platform_enable(musb); musb_writeb(regs, MUSB_DEVCTL, devctl); } - -static void musb_generic_disable(struct musb *musb) -{ - void __iomem *mbase = musb->mregs; - u16 temp; - - /* disable interrupts */ - musb_writeb(mbase, MUSB_INTRUSBE, 0); - musb_writew(mbase, MUSB_INTRTXE, 0); - musb_writew(mbase, MUSB_INTRRXE, 0); - - /* off */ - musb_writeb(mbase, MUSB_DEVCTL, 0); - - /* flush pending interrupts */ - temp = musb_readb(mbase, MUSB_INTRUSB); - temp = musb_readw(mbase, MUSB_INTRTX); - temp = musb_readw(mbase, MUSB_INTRRX); - -} - /* * Make the HDRC stop (disable interrupts, etc.); * reversible by musb_start @@ -941,7 +984,7 @@ void musb_stop(struct musb *musb) /* stop IRQs, timers, ... */ musb_platform_disable(musb); musb_generic_disable(musb); - DBG(3, "HDRC disabled\n"); + dev_dbg(musb->controller, "HDRC disabled\n"); /* FIXME * - mark host and/or peripheral drivers unusable/inactive @@ -958,15 +1001,20 @@ static void musb_shutdown(struct platform_device *pdev) struct musb *musb = dev_to_musb(&pdev->dev); unsigned long flags; + pm_runtime_get_sync(musb->controller); + + musb_host_cleanup(musb); + musb_gadget_cleanup(musb); + spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); - if (musb->clock) { - clk_put(musb->clock); - musb->clock = NULL; - } spin_unlock_irqrestore(&musb->lock, flags); + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + musb_platform_exit(musb); + + pm_runtime_put(musb->controller); /* FIXME power down */ } @@ -983,35 +1031,33 @@ static void musb_shutdown(struct platform_device *pdev) * We don't currently use dynamic fifo setup capability to do anything * more than selecting one of a bunch of predefined configurations. */ -#if defined(CONFIG_USB_TUSB6010) || \ - defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) -static ushort __initdata fifo_mode = 4; +#if defined(CONFIG_USB_MUSB_TUSB6010) \ + || defined(CONFIG_USB_MUSB_TUSB6010_MODULE) \ + || defined(CONFIG_USB_MUSB_OMAP2PLUS) \ + || defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE) \ + || defined(CONFIG_USB_MUSB_AM35X) \ + || defined(CONFIG_USB_MUSB_AM35X_MODULE) \ + || defined(CONFIG_USB_MUSB_DSPS) \ + || defined(CONFIG_USB_MUSB_DSPS_MODULE) +static ushort fifo_mode = 4; +#elif defined(CONFIG_USB_MUSB_UX500) \ + || defined(CONFIG_USB_MUSB_UX500_MODULE) +static ushort fifo_mode = 5; #else -static ushort __initdata fifo_mode = 2; +static ushort fifo_mode = 2; #endif /* "modprobe ... fifo_mode=1" etc */ module_param(fifo_mode, ushort, 0); MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration"); - -enum fifo_style { FIFO_RXTX, FIFO_TX, FIFO_RX } __attribute__ ((packed)); -enum buf_mode { BUF_SINGLE, BUF_DOUBLE } __attribute__ ((packed)); - -struct fifo_cfg { - u8 hw_ep_num; - enum fifo_style style; - enum buf_mode mode; - u16 maxpacket; -}; - /* * tables defining fifo_mode values. define more if you like. * for host side, make sure both halves of ep1 are set up. */ /* mode 0 - fits in 2KB */ -static struct fifo_cfg __initdata mode_0_cfg[] = { +static struct musb_fifo_cfg mode_0_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, }, @@ -1020,7 +1066,7 @@ static struct fifo_cfg __initdata mode_0_cfg[] = { }; /* mode 1 - fits in 4KB */ -static struct fifo_cfg __initdata mode_1_cfg[] = { +static struct musb_fifo_cfg mode_1_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, }, @@ -1029,7 +1075,7 @@ static struct fifo_cfg __initdata mode_1_cfg[] = { }; /* mode 2 - fits in 4KB */ -static struct fifo_cfg __initdata mode_2_cfg[] = { +static struct musb_fifo_cfg mode_2_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1039,7 +1085,7 @@ static struct fifo_cfg __initdata mode_2_cfg[] = { }; /* mode 3 - fits in 4KB */ -static struct fifo_cfg __initdata mode_3_cfg[] = { +static struct musb_fifo_cfg mode_3_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1049,7 +1095,7 @@ static struct fifo_cfg __initdata mode_3_cfg[] = { }; /* mode 4 - fits in 16KB */ -static struct fifo_cfg __initdata mode_4_cfg[] = { +static struct musb_fifo_cfg mode_4_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1068,18 +1114,47 @@ static struct fifo_cfg __initdata mode_4_cfg[] = { { .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 512, }, -{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 512, }, -{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 256, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 64, }, +{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 4096, }, { .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, { .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, }; +/* mode 5 - fits in 8KB */ +static struct musb_fifo_cfg mode_5_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 32, }, +{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 32, }, +{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 512, }, +{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, }, +{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, }, +}; /* * configure a fifo; for non-shared endpoints, this may be called @@ -1087,9 +1162,9 @@ static struct fifo_cfg __initdata mode_4_cfg[] = { * * returns negative errno or offset for next fifo. */ -static int __init +static int fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, - const struct fifo_cfg *cfg, u16 offset) + const struct musb_fifo_cfg *cfg, u16 offset) { void __iomem *mbase = musb->mregs; int size = 0; @@ -1116,14 +1191,12 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, /* configure the FIFO */ musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum); -#ifdef CONFIG_USB_MUSB_HDRC_HCD /* EP0 reserved endpoint for control, bidirectional; - * EP1 reserved for bulk, two unidirection halves. + * EP1 reserved for bulk, two unidirectional halves. */ if (hw_ep->epnum == 1) musb->bulk_ep = hw_ep; /* REVISIT error check: be sure ep0 can both rx and tx ... */ -#endif switch (cfg->style) { case FIFO_TX: musb_write_txfifosz(mbase, c_size); @@ -1160,17 +1233,23 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0)); } -static struct fifo_cfg __initdata ep0_cfg = { +static struct musb_fifo_cfg ep0_cfg = { .style = FIFO_RXTX, .maxpacket = 64, }; -static int __init ep_config_from_table(struct musb *musb) +static int ep_config_from_table(struct musb *musb) { - const struct fifo_cfg *cfg; + const struct musb_fifo_cfg *cfg; unsigned i, n; int offset; struct musb_hw_ep *hw_ep = musb->endpoints; + if (musb->config->fifo_cfg) { + cfg = musb->config->fifo_cfg; + n = musb->config->fifo_cfg_size; + goto done; + } + switch (fifo_mode) { default: fifo_mode = 0; @@ -1195,12 +1274,17 @@ static int __init ep_config_from_table(struct musb *musb) cfg = mode_4_cfg; n = ARRAY_SIZE(mode_4_cfg); break; + case 5: + cfg = mode_5_cfg; + n = ARRAY_SIZE(mode_5_cfg); + break; } printk(KERN_DEBUG "%s: setup fifo_mode %d\n", musb_driver_name, fifo_mode); +done: offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0); /* assert(offset > 0) */ @@ -1220,7 +1304,7 @@ static int __init ep_config_from_table(struct musb *musb) if (offset < 0) { pr_debug("%s: mem overrun, ep %d\n", musb_driver_name, epn); - return -EINVAL; + return offset; } epn++; musb->nr_endpoints = max(epn, musb->nr_endpoints); @@ -1231,12 +1315,10 @@ static int __init ep_config_from_table(struct musb *musb) n + 1, musb->config->num_eps * 2 - 1, offset, (1 << (musb->config->ram_bits + 2))); -#ifdef CONFIG_USB_MUSB_HDRC_HCD if (!musb->bulk_ep) { pr_debug("%s: missing bulk\n", musb_driver_name); return -EINVAL; } -#endif return 0; } @@ -1246,14 +1328,14 @@ static int __init ep_config_from_table(struct musb *musb) * ep_config_from_hw - when MUSB_C_DYNFIFO_DEF is false * @param musb the controller */ -static int __init ep_config_from_hw(struct musb *musb) +static int ep_config_from_hw(struct musb *musb) { u8 epnum = 0; struct musb_hw_ep *hw_ep; - void *mbase = musb->mregs; + void __iomem *mbase = musb->mregs; int ret = 0; - DBG(2, "<== static silicon ep config\n"); + dev_dbg(musb->controller, "<== static silicon ep config\n"); /* FIXME pick up ep0 maxpacket size */ @@ -1267,7 +1349,6 @@ static int __init ep_config_from_hw(struct musb *musb) /* FIXME set up hw_ep->{rx,tx}_double_buffered */ -#ifdef CONFIG_USB_MUSB_HDRC_HCD /* pick an RX/TX endpoint for bulk */ if (hw_ep->max_packet_sz_tx < 512 || hw_ep->max_packet_sz_rx < 512) @@ -1279,15 +1360,12 @@ static int __init ep_config_from_hw(struct musb *musb) if (musb->bulk_ep) continue; musb->bulk_ep = hw_ep; -#endif } -#ifdef CONFIG_USB_MUSB_HDRC_HCD if (!musb->bulk_ep) { pr_debug("%s: missing bulk\n", musb_driver_name); return -EINVAL; } -#endif return 0; } @@ -1297,49 +1375,38 @@ enum { MUSB_CONTROLLER_MHDRC, MUSB_CONTROLLER_HDRC, }; /* Initialize MUSB (M)HDRC part of the USB hardware subsystem; * configure endpoints, or take their config from silicon */ -static int __init musb_core_init(u16 musb_type, struct musb *musb) +static int musb_core_init(u16 musb_type, struct musb *musb) { -#ifdef MUSB_AHB_ID - u32 data; -#endif u8 reg; char *type; - u16 hwvers, rev_major, rev_minor; - char aInfo[78], aRevision[32], aDate[12]; + char aInfo[90], aRevision[32], aDate[12]; void __iomem *mbase = musb->mregs; int status = 0; int i; /* log core options (read using indexed model) */ - musb_ep_select(mbase, 0); reg = musb_read_configdata(mbase); strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); - if (reg & MUSB_CONFIGDATA_DYNFIFO) + if (reg & MUSB_CONFIGDATA_DYNFIFO) { strcat(aInfo, ", dyn FIFOs"); + musb->dyn_fifo = true; + } if (reg & MUSB_CONFIGDATA_MPRXE) { strcat(aInfo, ", bulk combine"); -#ifdef C_MP_RX musb->bulk_combine = true; -#else - strcat(aInfo, " (X)"); /* no driver support */ -#endif } if (reg & MUSB_CONFIGDATA_MPTXE) { strcat(aInfo, ", bulk split"); -#ifdef C_MP_TX musb->bulk_split = true; -#else - strcat(aInfo, " (X)"); /* no driver support */ -#endif } if (reg & MUSB_CONFIGDATA_HBRXE) { strcat(aInfo, ", HB-ISO Rx"); - strcat(aInfo, " (X)"); /* no driver support */ + musb->hb_iso_rx = true; } if (reg & MUSB_CONFIGDATA_HBTXE) { strcat(aInfo, ", HB-ISO Tx"); - strcat(aInfo, " (X)"); /* no driver support */ + musb->hb_iso_tx = true; } if (reg & MUSB_CONFIGDATA_SOFTCONE) strcat(aInfo, ", SoftConn"); @@ -1347,41 +1414,25 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) printk(KERN_DEBUG "%s: ConfigData=0x%02x (%s)\n", musb_driver_name, reg, aInfo); -#ifdef MUSB_AHB_ID - data = musb_readl(mbase, 0x404); - sprintf(aDate, "%04d-%02x-%02x", (data & 0xffff), - (data >> 16) & 0xff, (data >> 24) & 0xff); - /* FIXME ID2 and ID3 are unused */ - data = musb_readl(mbase, 0x408); - printk(KERN_DEBUG "ID2=%lx\n", (long unsigned)data); - data = musb_readl(mbase, 0x40c); - printk(KERN_DEBUG "ID3=%lx\n", (long unsigned)data); - reg = musb_readb(mbase, 0x400); - musb_type = ('M' == reg) ? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC; -#else aDate[0] = 0; -#endif if (MUSB_CONTROLLER_MHDRC == musb_type) { musb->is_multipoint = 1; type = "M"; } else { musb->is_multipoint = 0; type = ""; -#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifndef CONFIG_USB_OTG_BLACKLIST_HUB printk(KERN_ERR "%s: kernel must blacklist external hubs\n", musb_driver_name); #endif -#endif } /* log release info */ - hwvers = musb_read_hwvers(mbase); - rev_major = (hwvers >> 10) & 0x1f; - rev_minor = hwvers & 0x3ff; - snprintf(aRevision, 32, "%d.%d%s", rev_major, - rev_minor, (hwvers & 0x8000) ? "RC" : ""); + musb->hwvers = musb_read_hwvers(mbase); + snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers), + MUSB_HWVERS_MINOR(musb->hwvers), + (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : ""); printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n", musb_driver_name, type, aRevision, aDate); @@ -1392,21 +1443,10 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) musb->nr_endpoints = 1; musb->epmask = 1; - if (reg & MUSB_CONFIGDATA_DYNFIFO) { - if (musb->config->dyn_fifo) - status = ep_config_from_table(musb); - else { - ERR("reconfigure software for Dynamic FIFOs\n"); - status = -ENODEV; - } - } else { - if (!musb->config->dyn_fifo) - status = ep_config_from_hw(musb); - else { - ERR("reconfigure software for static FIFOs\n"); - return -ENODEV; - } - } + if (musb->dyn_fifo) + status = ep_config_from_table(musb); + else + status = ep_config_from_hw(musb); if (status < 0) return status; @@ -1416,7 +1456,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) struct musb_hw_ep *hw_ep = musb->endpoints + i; hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase; -#ifdef CONFIG_USB_TUSB6010 +#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE) hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i); hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i); hw_ep->fifo_sync_va = @@ -1429,14 +1469,12 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) #endif hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase; -#ifdef CONFIG_USB_MUSB_HDRC_HCD hw_ep->target_regs = musb_read_target_reg_base(i, mbase); hw_ep->rx_reinit = 1; hw_ep->tx_reinit = 1; -#endif if (hw_ep->max_packet_sz_tx) { - printk(KERN_DEBUG + dev_dbg(musb->controller, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, hw_ep->is_shared_fifo ? "shared" : "tx", @@ -1445,7 +1483,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_tx); } if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) { - printk(KERN_DEBUG + dev_dbg(musb->controller, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, "rx", @@ -1454,7 +1492,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_rx); } if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx)) - DBG(1, "hw_ep %d not configured\n", i); + dev_dbg(musb->controller, "hw_ep %d not configured\n", i); } return 0; @@ -1462,38 +1500,6 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) - -static irqreturn_t generic_interrupt(int irq, void *__hci) -{ - unsigned long flags; - irqreturn_t retval = IRQ_NONE; - struct musb *musb = __hci; - - spin_lock_irqsave(&musb->lock, flags); - - musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); - musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); - musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); - - if (musb->int_usb || musb->int_tx || musb->int_rx) - retval = musb_interrupt(musb); - - spin_unlock_irqrestore(&musb->lock, flags); - - /* REVISIT we sometimes get spurious IRQs on g_ep0 - * not clear why... - */ - if (retval != IRQ_HANDLED) - DBG(5, "spurious?\n"); - - return IRQ_HANDLED; -} - -#else -#define generic_interrupt NULL -#endif - /* * handle all the irqs defined by the HDRC core. for now we expect: other * irq sources (phy, dma, etc) will be handled first, musb->int_* values @@ -1504,23 +1510,22 @@ static irqreturn_t generic_interrupt(int irq, void *__hci) irqreturn_t musb_interrupt(struct musb *musb) { irqreturn_t retval = IRQ_NONE; - u8 devctl, power; + u8 devctl; int ep_num; u32 reg; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - power = musb_readb(musb->mregs, MUSB_POWER); - DBG(4, "** IRQ %s usb%04x tx%04x rx%04x\n", + dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); /* the core can interrupt us for multiple reasons; docs have * a generic interrupt flowchart to follow */ - if (musb->int_usb & STAGE0_MASK) + if (musb->int_usb) retval |= musb_stage0_irq(musb, musb->int_usb, - devctl, power); + devctl); /* "stage 1" is handling endpoint irqs */ @@ -1540,13 +1545,10 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval = ep->rx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) { - if (is_host_capable()) - musb_host_rx(musb, ep_num); - } else { - if (is_peripheral_capable()) - musb_g_rx(musb, ep_num); - } + if (devctl & MUSB_DEVCTL_HM) + musb_host_rx(musb, ep_num); + else + musb_g_rx(musb, ep_num); } reg >>= 1; @@ -1561,29 +1563,21 @@ irqreturn_t musb_interrupt(struct musb *musb) /* musb_ep_select(musb->mregs, ep_num); */ /* REVISIT just retval |= ep->tx_irq(...) */ retval = IRQ_HANDLED; - if (devctl & MUSB_DEVCTL_HM) { - if (is_host_capable()) - musb_host_tx(musb, ep_num); - } else { - if (is_peripheral_capable()) - musb_g_tx(musb, ep_num); - } + if (devctl & MUSB_DEVCTL_HM) + musb_host_tx(musb, ep_num); + else + musb_g_tx(musb, ep_num); } reg >>= 1; ep_num++; } - /* finish handling "global" interrupts after handling fifos */ - if (musb->int_usb) - retval |= musb_stage2_irq(musb, - musb->int_usb, devctl, power); - return retval; } - +EXPORT_SYMBOL_GPL(musb_interrupt); #ifndef CONFIG_MUSB_PIO_ONLY -static int __initdata use_dma = 1; +static bool use_dma = 1; /* "modprobe ... use_dma=0" etc */ module_param(use_dma, bool, 0); @@ -1608,25 +1602,20 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) } else { /* endpoints 1..15 */ if (transmit) { - if (devctl & MUSB_DEVCTL_HM) { - if (is_host_capable()) - musb_host_tx(musb, epnum); - } else { - if (is_peripheral_capable()) - musb_g_tx(musb, epnum); - } + if (devctl & MUSB_DEVCTL_HM) + musb_host_tx(musb, epnum); + else + musb_g_tx(musb, epnum); } else { /* receive */ - if (devctl & MUSB_DEVCTL_HM) { - if (is_host_capable()) - musb_host_rx(musb, epnum); - } else { - if (is_peripheral_capable()) - musb_g_rx(musb, epnum); - } + if (devctl & MUSB_DEVCTL_HM) + musb_host_rx(musb, epnum); + else + musb_g_rx(musb, epnum); } } } +EXPORT_SYMBOL_GPL(musb_dma_completion); #else #define use_dma 0 @@ -1634,8 +1623,6 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_SYSFS - static ssize_t musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1644,7 +1631,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) int ret = -EINVAL; spin_lock_irqsave(&musb->lock, flags); - ret = sprintf(buf, "%s\n", otg_state_string(musb)); + ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->state)); spin_unlock_irqrestore(&musb->lock, flags); return ret; @@ -1682,13 +1669,14 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr, unsigned long val; if (sscanf(buf, "%lu", &val) < 1) { - printk(KERN_ERR "Invalid VBUS timeout ms value\n"); + dev_err(dev, "Invalid VBUS timeout ms value\n"); return -EINVAL; } spin_lock_irqsave(&musb->lock, flags); - musb->a_wait_bcon = val; - if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON) + /* force T(a_wait_bcon) to be zero/unlimited *OR* valid */ + musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ; + if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON) musb->is_active = 0; musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val)); spin_unlock_irqrestore(&musb->lock, flags); @@ -1706,16 +1694,17 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) spin_lock_irqsave(&musb->lock, flags); val = musb->a_wait_bcon; + /* FIXME get_vbus_status() is normally #defined as false... + * and is effectively TUSB-specific. + */ vbus = musb_platform_get_vbus_status(musb); spin_unlock_irqrestore(&musb->lock, flags); - return sprintf(buf, "Vbus %s, timeout %lu\n", + return sprintf(buf, "Vbus %s, timeout %lu msec\n", vbus ? "on" : "off", val); } static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - /* Gadget drivers can't know that a host is connected so they might want * to start SRP, but users can. This allows userspace to trigger SRP. */ @@ -1728,7 +1717,7 @@ musb_srp_store(struct device *dev, struct device_attribute *attr, if (sscanf(buf, "%hu", &srp) != 1 || (srp != 1)) { - printk(KERN_ERR "SRP: Value must be 1\n"); + dev_err(dev, "SRP: Value must be 1\n"); return -EINVAL; } @@ -1739,57 +1728,78 @@ musb_srp_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store); -#endif /* CONFIG_USB_GADGET_MUSB_HDRC */ +static struct attribute *musb_attributes[] = { + &dev_attr_mode.attr, + &dev_attr_vbus.attr, + &dev_attr_srp.attr, + NULL +}; -#endif /* sysfs */ +static const struct attribute_group musb_attr_group = { + .attrs = musb_attributes, +}; /* Only used to provide driver mode change events */ static void musb_irq_work(struct work_struct *data) { struct musb *musb = container_of(data, struct musb, irq_work); - static int old_state; - if (musb->xceiv.state != old_state) { - old_state = musb->xceiv.state; + if (musb->xceiv->state != musb->xceiv_old_state) { + musb->xceiv_old_state = musb->xceiv->state; sysfs_notify(&musb->controller->kobj, NULL, "mode"); } } +/* Recover from babble interrupt conditions */ +static void musb_recover_work(struct work_struct *data) +{ + struct musb *musb = container_of(data, struct musb, recover_work); + int status; + + musb_platform_reset(musb); + + usb_phy_vbus_off(musb->xceiv); + udelay(100); + + usb_phy_vbus_on(musb->xceiv); + udelay(100); + + /* + * When a babble condition occurs, the musb controller removes the + * session bit and the endpoint config is lost. + */ + if (musb->dyn_fifo) + status = ep_config_from_table(musb); + else + status = ep_config_from_hw(musb); + + /* start the session again */ + if (status == 0) + musb_start(musb); +} + /* -------------------------------------------------------------------------- * Init support */ -static struct musb *__init -allocate_instance(struct device *dev, +static struct musb *allocate_instance(struct device *dev, struct musb_hdrc_config *config, void __iomem *mbase) { struct musb *musb; struct musb_hw_ep *ep; int epnum; -#ifdef CONFIG_USB_MUSB_HDRC_HCD - struct usb_hcd *hcd; + int ret; - hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev)); - if (!hcd) + musb = devm_kzalloc(dev, sizeof(*musb), GFP_KERNEL); + if (!musb) return NULL; - /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */ - musb = hcd_to_musb(hcd); INIT_LIST_HEAD(&musb->control); INIT_LIST_HEAD(&musb->in_bulk); INIT_LIST_HEAD(&musb->out_bulk); - hcd->uses_new_polling = 1; - musb->vbuserr_retry = VBUSERR_RETRY_COUNT; -#else - musb = kzalloc(sizeof *musb, GFP_KERNEL); - if (!musb) - return NULL; - dev_set_drvdata(dev, musb); - -#endif - + musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON; musb->mregs = mbase; musb->ctrl_base = mbase; musb->nIrq = -ENODEV; @@ -1803,7 +1813,17 @@ allocate_instance(struct device *dev, } musb->controller = dev; + + ret = musb_host_alloc(musb); + if (ret < 0) + goto err_free; + + dev_set_drvdata(dev, musb); + return musb; + +err_free: + return NULL; } static void musb_free(struct musb *musb) @@ -1814,15 +1834,7 @@ static void musb_free(struct musb *musb) */ #ifdef CONFIG_SYSFS - device_remove_file(musb->controller, &dev_attr_mode); - device_remove_file(musb->controller, &dev_attr_vbus); -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - device_remove_file(musb->controller, &dev_attr_srp); -#endif -#endif - -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - musb_gadget_cleanup(musb); + sysfs_remove_group(&musb->controller->kobj, &musb_attr_group); #endif if (musb->nIrq >= 0) { @@ -1830,155 +1842,130 @@ static void musb_free(struct musb *musb) disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } - if (is_dma_capable() && musb->dma_controller) { - struct dma_controller *c = musb->dma_controller; - (void) c->stop(c); - dma_controller_destroy(c); - } + musb_host_free(musb); +} - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); - musb_platform_exit(musb); - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); +static void musb_deassert_reset(struct work_struct *work) +{ + struct musb *musb; + unsigned long flags; - if (musb->clock) { - clk_disable(musb->clock); - clk_put(musb->clock); - } + musb = container_of(work, struct musb, deassert_reset_work.work); -#ifdef CONFIG_USB_MUSB_OTG - put_device(musb->xceiv.dev); -#endif + spin_lock_irqsave(&musb->lock, flags); -#ifdef CONFIG_USB_MUSB_HDRC_HCD - usb_put_hcd(musb_to_hcd(musb)); -#else - kfree(musb); -#endif + if (musb->port1_status & USB_PORT_STAT_RESET) + musb_port_reset(musb, false); + + spin_unlock_irqrestore(&musb->lock, flags); } /* * Perform generic per-controller initialization. * - * @pDevice: the controller (already clocked, etc) - * @nIrq: irq - * @mregs: virtual address of controller registers, + * @dev: the controller (already clocked, etc) + * @nIrq: IRQ number + * @ctrl: virtual address of controller registers, * not yet corrected for platform-specific offsets */ -static int __init +static int musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) { int status; struct musb *musb; - struct musb_hdrc_platform_data *plat = dev->platform_data; + struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); /* The driver might handle more features than the board; OK. * Fail when the board needs a feature that's not enabled. */ if (!plat) { dev_dbg(dev, "no platform_data?\n"); - return -ENODEV; - } - switch (plat->mode) { - case MUSB_HOST: -#ifdef CONFIG_USB_MUSB_HDRC_HCD - break; -#else - goto bad_config; -#endif - case MUSB_PERIPHERAL: -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - break; -#else - goto bad_config; -#endif - case MUSB_OTG: -#ifdef CONFIG_USB_MUSB_OTG - break; -#else -bad_config: -#endif - default: - dev_err(dev, "incompatible Kconfig role setting\n"); - return -EINVAL; + status = -ENODEV; + goto fail0; } /* allocate */ musb = allocate_instance(dev, plat->config, ctrl); - if (!musb) - return -ENOMEM; + if (!musb) { + status = -ENOMEM; + goto fail0; + } + + pm_runtime_use_autosuspend(musb->controller); + pm_runtime_set_autosuspend_delay(musb->controller, 200); + pm_runtime_enable(musb->controller); spin_lock_init(&musb->lock); - musb->board_mode = plat->mode; musb->board_set_power = plat->set_power; - musb->set_clock = plat->set_clock; musb->min_power = plat->min_power; - - /* Clock usage is chip-specific ... functional clock (DaVinci, - * OMAP2430), or PHY ref (some TUSB6010 boards). All this core - * code does is make sure a clock handle is available; platform - * code manages it during start/stop and suspend/resume. + musb->ops = plat->platform_ops; + musb->port_mode = plat->mode; + + /* The musb_platform_init() call: + * - adjusts musb->mregs + * - sets the musb->isr + * - may initialize an integrated transceiver + * - initializes musb->xceiv, usually by otg_get_phy() + * - stops powering VBUS + * + * There are various transceiver configurations. Blackfin, + * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses + * external/discrete ones in various flavors (twl4030 family, + * isp1504, non-OTG, etc) mostly hooking up through ULPI. */ - if (plat->clock) { - musb->clock = clk_get(dev, plat->clock); - if (IS_ERR(musb->clock)) { - status = PTR_ERR(musb->clock); - musb->clock = NULL; - goto fail; - } - } - - /* assume vbus is off */ - - /* platform adjusts musb->mregs and musb->isr if needed, - * and activates clocks - */ - musb->isr = generic_interrupt; status = musb_platform_init(musb); - if (status < 0) - goto fail; + goto fail1; + if (!musb->isr) { status = -ENODEV; goto fail2; } -#ifndef CONFIG_MUSB_PIO_ONLY - if (use_dma && dev->dma_mask) { - struct dma_controller *c; + if (!musb->xceiv->io_ops) { + musb->xceiv->io_dev = musb->controller; + musb->xceiv->io_priv = musb->mregs; + musb->xceiv->io_ops = &musb_ulpi_access; + } - c = dma_controller_create(musb, musb->mregs); - musb->dma_controller = c; - if (c) - (void) c->start(c); + pm_runtime_get_sync(musb->controller); + + if (use_dma && dev->dma_mask) { + musb->dma_controller = dma_controller_create(musb, musb->mregs); + if (IS_ERR(musb->dma_controller)) { + status = PTR_ERR(musb->dma_controller); + goto fail2_5; + } } -#endif - /* ideally this would be abstracted in platform setup */ - if (!is_dma_capable() || !musb->dma_controller) - dev->dma_mask = NULL; /* be sure interrupts are disabled before connecting ISR */ musb_platform_disable(musb); musb_generic_disable(musb); + /* Init IRQ workqueue before request_irq */ + INIT_WORK(&musb->irq_work, musb_irq_work); + INIT_WORK(&musb->recover_work, musb_recover_work); + INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); + INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); + /* setup musb parts of the core (especially endpoints) */ status = musb_core_init(plat->config->multipoint ? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC, musb); if (status < 0) - goto fail2; + goto fail3; - /* Init IRQ workqueue before request_irq */ - INIT_WORK(&musb->irq_work, musb_irq_work); + setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); /* attach to the IRQ */ if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) { dev_err(dev, "request_irq %d failed!\n", nIrq); status = -ENODEV; - goto fail2; + goto fail3; } musb->nIrq = nIrq; -/* FIXME this handles wakeup irqs wrong */ + /* FIXME this handles wakeup irqs wrong */ if (enable_irq_wake(nIrq) == 0) { musb->irq_wake = 1; device_init_wakeup(dev, 1); @@ -1986,98 +1973,96 @@ bad_config: musb->irq_wake = 0; } - pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n", - musb_driver_name, - ({char *s; - switch (musb->board_mode) { - case MUSB_HOST: s = "Host"; break; - case MUSB_PERIPHERAL: s = "Peripheral"; break; - default: s = "OTG"; break; - }; s; }), - ctrl, - (is_dma_capable() && musb->dma_controller) - ? "DMA" : "PIO", - musb->nIrq); - -#ifdef CONFIG_USB_MUSB_HDRC_HCD - /* host side needs more setup, except for no-host modes */ - if (musb->board_mode != MUSB_PERIPHERAL) { - struct usb_hcd *hcd = musb_to_hcd(musb); - - if (musb->board_mode == MUSB_OTG) - hcd->self.otg_port = 1; - musb->xceiv.host = &hcd->self; - hcd->power_budget = 2 * (plat->power ? : 250); + /* program PHY to use external vBus if required */ + if (plat->extvbus) { + u8 busctl = musb_read_ulpi_buscontrol(musb->mregs); + busctl |= MUSB_ULPI_USE_EXTVBUS; + musb_write_ulpi_buscontrol(musb->mregs, busctl); } -#endif /* CONFIG_USB_MUSB_HDRC_HCD */ - /* For the host-only role, we can activate right away. - * (We expect the ID pin to be forcibly grounded!!) - * Otherwise, wait till the gadget driver hooks up. - */ - if (!is_otg_enabled(musb) && is_host_enabled(musb)) { + if (musb->xceiv->otg->default_a) { MUSB_HST_MODE(musb); - musb->xceiv.default_a = 1; - musb->xceiv.state = OTG_STATE_A_IDLE; - - status = usb_add_hcd(musb_to_hcd(musb), -1, 0); - if (status) - goto fail; - - DBG(1, "%s mode, status %d, devctl %02x %c\n", - "HOST", status, - musb_readb(musb->mregs, MUSB_DEVCTL), - (musb_readb(musb->mregs, MUSB_DEVCTL) - & MUSB_DEVCTL_BDEVICE - ? 'B' : 'A')); - - } else /* peripheral is enabled */ { + musb->xceiv->state = OTG_STATE_A_IDLE; + } else { MUSB_DEV_MODE(musb); - musb->xceiv.default_a = 0; - musb->xceiv.state = OTG_STATE_B_IDLE; + musb->xceiv->state = OTG_STATE_B_IDLE; + } + switch (musb->port_mode) { + case MUSB_PORT_MODE_HOST: + status = musb_host_setup(musb, plat->power); + if (status < 0) + goto fail3; + status = musb_platform_set_mode(musb, MUSB_HOST); + break; + case MUSB_PORT_MODE_GADGET: + status = musb_gadget_setup(musb); + if (status < 0) + goto fail3; + status = musb_platform_set_mode(musb, MUSB_PERIPHERAL); + break; + case MUSB_PORT_MODE_DUAL_ROLE: + status = musb_host_setup(musb, plat->power); + if (status < 0) + goto fail3; status = musb_gadget_setup(musb); - if (status) - goto fail; + if (status) { + musb_host_cleanup(musb); + goto fail3; + } + status = musb_platform_set_mode(musb, MUSB_OTG); + break; + default: + dev_err(dev, "unsupported port mode %d\n", musb->port_mode); + break; + } - DBG(1, "%s mode, status %d, dev%02x\n", - is_otg_enabled(musb) ? "OTG" : "PERIPHERAL", - status, - musb_readb(musb->mregs, MUSB_DEVCTL)); + if (status < 0) + goto fail3; - } + status = musb_init_debugfs(musb); + if (status < 0) + goto fail4; -#ifdef CONFIG_SYSFS - status = device_create_file(dev, &dev_attr_mode); - status = device_create_file(dev, &dev_attr_vbus); -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - status = device_create_file(dev, &dev_attr_srp); -#endif /* CONFIG_USB_GADGET_MUSB_HDRC */ - status = 0; -#endif + status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group); if (status) - goto fail2; + goto fail5; + + pm_runtime_put(musb->controller); return 0; +fail5: + musb_exit_debugfs(musb); + +fail4: + musb_gadget_cleanup(musb); + musb_host_cleanup(musb); + +fail3: + cancel_work_sync(&musb->irq_work); + cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->finish_resume_work); + cancel_delayed_work_sync(&musb->deassert_reset_work); + if (musb->dma_controller) + dma_controller_destroy(musb->dma_controller); +fail2_5: + pm_runtime_put_sync(musb->controller); + fail2: -#ifdef CONFIG_SYSFS - device_remove_file(musb->controller, &dev_attr_mode); - device_remove_file(musb->controller, &dev_attr_vbus); -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - device_remove_file(musb->controller, &dev_attr_srp); -#endif -#endif + if (musb->irq_wake) + device_init_wakeup(dev, 0); musb_platform_exit(musb); -fail: + +fail1: + pm_runtime_disable(musb->controller); dev_err(musb->controller, "musb_init_controller failed with status %d\n", status); - if (musb->clock) - clk_put(musb->clock); - device_init_wakeup(dev, 0); musb_free(musb); +fail0: + return status; } @@ -2087,68 +2072,211 @@ fail: /* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just * bridge to a platform device; this driver then suffices. */ - -#ifndef CONFIG_MUSB_PIO_ONLY -static u64 *orig_dma_mask; -#endif - -static int __init musb_probe(struct platform_device *pdev) +static int musb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - int irq = platform_get_irq(pdev, 0); + int irq = platform_get_irq_byname(pdev, "mc"); struct resource *iomem; void __iomem *base; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iomem || irq == 0) + if (!iomem || irq <= 0) return -ENODEV; - base = ioremap(iomem->start, iomem->end - iomem->start + 1); - if (!base) { - dev_err(dev, "ioremap failed\n"); - return -ENOMEM; - } + base = devm_ioremap_resource(dev, iomem); + if (IS_ERR(base)) + return PTR_ERR(base); -#ifndef CONFIG_MUSB_PIO_ONLY - /* clobbered by use_dma=n */ - orig_dma_mask = dev->dma_mask; -#endif return musb_init_controller(dev, irq, base); } -static int __devexit musb_remove(struct platform_device *pdev) +static int musb_remove(struct platform_device *pdev) { - struct musb *musb = dev_to_musb(&pdev->dev); - void __iomem *ctrl_base = musb->ctrl_base; + struct device *dev = &pdev->dev; + struct musb *musb = dev_to_musb(dev); /* this gets called on rmmod. * - Host mode: host may still be active * - Peripheral mode: peripheral is deactivated (or never-activated) * - OTG mode: both roles are deactivated (or never-activated) */ + musb_exit_debugfs(musb); musb_shutdown(pdev); -#ifdef CONFIG_USB_MUSB_HDRC_HCD - if (musb->board_mode == MUSB_HOST) - usb_remove_hcd(musb_to_hcd(musb)); -#endif + + if (musb->dma_controller) + dma_controller_destroy(musb->dma_controller); + + cancel_work_sync(&musb->irq_work); + cancel_work_sync(&musb->recover_work); + cancel_delayed_work_sync(&musb->finish_resume_work); + cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); - iounmap(ctrl_base); - device_init_wakeup(&pdev->dev, 0); -#ifndef CONFIG_MUSB_PIO_ONLY - pdev->dev.dma_mask = orig_dma_mask; -#endif + device_init_wakeup(dev, 0); return 0; } #ifdef CONFIG_PM -static int musb_suspend(struct platform_device *pdev, pm_message_t message) +static void musb_save_context(struct musb *musb) { - unsigned long flags; - struct musb *musb = dev_to_musb(&pdev->dev); + int i; + void __iomem *musb_base = musb->mregs; + void __iomem *epio; + + musb->context.frame = musb_readw(musb_base, MUSB_FRAME); + musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE); + musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs); + musb->context.power = musb_readb(musb_base, MUSB_POWER); + musb->context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE); + musb->context.index = musb_readb(musb_base, MUSB_INDEX); + musb->context.devctl = musb_readb(musb_base, MUSB_DEVCTL); + + for (i = 0; i < musb->config->num_eps; ++i) { + struct musb_hw_ep *hw_ep; + + hw_ep = &musb->endpoints[i]; + if (!hw_ep) + continue; + + epio = hw_ep->regs; + if (!epio) + continue; + + musb_writeb(musb_base, MUSB_INDEX, i); + musb->context.index_regs[i].txmaxp = + musb_readw(epio, MUSB_TXMAXP); + musb->context.index_regs[i].txcsr = + musb_readw(epio, MUSB_TXCSR); + musb->context.index_regs[i].rxmaxp = + musb_readw(epio, MUSB_RXMAXP); + musb->context.index_regs[i].rxcsr = + musb_readw(epio, MUSB_RXCSR); + + if (musb->dyn_fifo) { + musb->context.index_regs[i].txfifoadd = + musb_read_txfifoadd(musb_base); + musb->context.index_regs[i].rxfifoadd = + musb_read_rxfifoadd(musb_base); + musb->context.index_regs[i].txfifosz = + musb_read_txfifosz(musb_base); + musb->context.index_regs[i].rxfifosz = + musb_read_rxfifosz(musb_base); + } - if (!musb->clock) - return 0; + musb->context.index_regs[i].txtype = + musb_readb(epio, MUSB_TXTYPE); + musb->context.index_regs[i].txinterval = + musb_readb(epio, MUSB_TXINTERVAL); + musb->context.index_regs[i].rxtype = + musb_readb(epio, MUSB_RXTYPE); + musb->context.index_regs[i].rxinterval = + musb_readb(epio, MUSB_RXINTERVAL); + + musb->context.index_regs[i].txfunaddr = + musb_read_txfunaddr(musb_base, i); + musb->context.index_regs[i].txhubaddr = + musb_read_txhubaddr(musb_base, i); + musb->context.index_regs[i].txhubport = + musb_read_txhubport(musb_base, i); + + musb->context.index_regs[i].rxfunaddr = + musb_read_rxfunaddr(musb_base, i); + musb->context.index_regs[i].rxhubaddr = + musb_read_rxhubaddr(musb_base, i); + musb->context.index_regs[i].rxhubport = + musb_read_rxhubport(musb_base, i); + } +} + +static void musb_restore_context(struct musb *musb) +{ + int i; + void __iomem *musb_base = musb->mregs; + void __iomem *ep_target_regs; + void __iomem *epio; + u8 power; + + musb_writew(musb_base, MUSB_FRAME, musb->context.frame); + musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode); + musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl); + + /* Don't affect SUSPENDM/RESUME bits in POWER reg */ + power = musb_readb(musb_base, MUSB_POWER); + power &= MUSB_POWER_SUSPENDM | MUSB_POWER_RESUME; + musb->context.power &= ~(MUSB_POWER_SUSPENDM | MUSB_POWER_RESUME); + power |= musb->context.power; + musb_writeb(musb_base, MUSB_POWER, power); + + musb_writew(musb_base, MUSB_INTRTXE, musb->intrtxe); + musb_writew(musb_base, MUSB_INTRRXE, musb->intrrxe); + musb_writeb(musb_base, MUSB_INTRUSBE, musb->context.intrusbe); + musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl); + + for (i = 0; i < musb->config->num_eps; ++i) { + struct musb_hw_ep *hw_ep; + + hw_ep = &musb->endpoints[i]; + if (!hw_ep) + continue; + + epio = hw_ep->regs; + if (!epio) + continue; + + musb_writeb(musb_base, MUSB_INDEX, i); + musb_writew(epio, MUSB_TXMAXP, + musb->context.index_regs[i].txmaxp); + musb_writew(epio, MUSB_TXCSR, + musb->context.index_regs[i].txcsr); + musb_writew(epio, MUSB_RXMAXP, + musb->context.index_regs[i].rxmaxp); + musb_writew(epio, MUSB_RXCSR, + musb->context.index_regs[i].rxcsr); + + if (musb->dyn_fifo) { + musb_write_txfifosz(musb_base, + musb->context.index_regs[i].txfifosz); + musb_write_rxfifosz(musb_base, + musb->context.index_regs[i].rxfifosz); + musb_write_txfifoadd(musb_base, + musb->context.index_regs[i].txfifoadd); + musb_write_rxfifoadd(musb_base, + musb->context.index_regs[i].rxfifoadd); + } + + musb_writeb(epio, MUSB_TXTYPE, + musb->context.index_regs[i].txtype); + musb_writeb(epio, MUSB_TXINTERVAL, + musb->context.index_regs[i].txinterval); + musb_writeb(epio, MUSB_RXTYPE, + musb->context.index_regs[i].rxtype); + musb_writeb(epio, MUSB_RXINTERVAL, + + musb->context.index_regs[i].rxinterval); + musb_write_txfunaddr(musb_base, i, + musb->context.index_regs[i].txfunaddr); + musb_write_txhubaddr(musb_base, i, + musb->context.index_regs[i].txhubaddr); + musb_write_txhubport(musb_base, i, + musb->context.index_regs[i].txhubport); + + ep_target_regs = + musb_read_target_reg_base(i, musb_base); + + musb_write_rxfunaddr(ep_target_regs, + musb->context.index_regs[i].rxfunaddr); + musb_write_rxhubaddr(ep_target_regs, + musb->context.index_regs[i].rxhubaddr); + musb_write_rxhubport(ep_target_regs, + musb->context.index_regs[i].rxhubport); + } + musb_writeb(musb_base, MUSB_INDEX, musb->context.index); +} + +static int musb_suspend(struct device *dev) +{ + struct musb *musb = dev_to_musb(dev); + unsigned long flags; spin_lock_irqsave(&musb->lock, flags); @@ -2162,36 +2290,71 @@ static int musb_suspend(struct platform_device *pdev, pm_message_t message) */ } - if (musb->set_clock) - musb->set_clock(musb->clock, 0); - else - clk_disable(musb->clock); + musb_save_context(musb); + spin_unlock_irqrestore(&musb->lock, flags); return 0; } -static int musb_resume_early(struct platform_device *pdev) +static int musb_resume_noirq(struct device *dev) { - struct musb *musb = dev_to_musb(&pdev->dev); - - if (!musb->clock) - return 0; - - if (musb->set_clock) - musb->set_clock(musb->clock, 1); - else - clk_enable(musb->clock); + struct musb *musb = dev_to_musb(dev); - /* for static cmos like DaVinci, register values were preserved + /* + * For static cmos like DaVinci, register values were preserved * unless for some reason the whole soc powered down or the USB * module got reset through the PSC (vs just being disabled). + * + * For the DSPS glue layer though, a full register restore has to + * be done. As it shouldn't harm other platforms, we do it + * unconditionally. + */ + + musb_restore_context(musb); + + return 0; +} + +static int musb_runtime_suspend(struct device *dev) +{ + struct musb *musb = dev_to_musb(dev); + + musb_save_context(musb); + + return 0; +} + +static int musb_runtime_resume(struct device *dev) +{ + struct musb *musb = dev_to_musb(dev); + static int first = 1; + + /* + * When pm_runtime_get_sync called for the first time in driver + * init, some of the structure is still not initialized which is + * used in restore function. But clock needs to be + * enabled before any register access, so + * pm_runtime_get_sync has to be called. + * Also context restore without save does not make + * any sense */ + if (!first) + musb_restore_context(musb); + first = 0; + return 0; } +static const struct dev_pm_ops musb_dev_pm_ops = { + .suspend = musb_suspend, + .resume_noirq = musb_resume_noirq, + .runtime_suspend = musb_runtime_suspend, + .runtime_resume = musb_runtime_resume, +}; + +#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops) #else -#define musb_suspend NULL -#define musb_resume_early NULL +#define MUSB_DEV_PM_OPS NULL #endif static struct platform_driver musb_driver = { @@ -2199,54 +2362,11 @@ static struct platform_driver musb_driver = { .name = (char *)musb_driver_name, .bus = &platform_bus_type, .owner = THIS_MODULE, + .pm = MUSB_DEV_PM_OPS, }, - .remove = __devexit_p(musb_remove), + .probe = musb_probe, + .remove = musb_remove, .shutdown = musb_shutdown, - .suspend = musb_suspend, - .resume_early = musb_resume_early, }; -/*-------------------------------------------------------------------------*/ - -static int __init musb_init(void) -{ -#ifdef CONFIG_USB_MUSB_HDRC_HCD - if (usb_disabled()) - return 0; -#endif - - pr_info("%s: version " MUSB_VERSION ", " -#ifdef CONFIG_MUSB_PIO_ONLY - "pio" -#elif defined(CONFIG_USB_TI_CPPI_DMA) - "cppi-dma" -#elif defined(CONFIG_USB_INVENTRA_DMA) - "musb-dma" -#elif defined(CONFIG_USB_TUSB_OMAP_DMA) - "tusb-omap-dma" -#else - "?dma?" -#endif - ", " -#ifdef CONFIG_USB_MUSB_OTG - "otg (peripheral+host)" -#elif defined(CONFIG_USB_GADGET_MUSB_HDRC) - "peripheral" -#elif defined(CONFIG_USB_MUSB_HDRC_HCD) - "host" -#endif - ", debug=%d\n", - musb_driver_name, musb_debug); - return platform_driver_probe(&musb_driver, musb_probe); -} - -/* make us init after usbcore and i2c (transceivers, regulators, etc) - * and before usb gadget and host-side drivers start to register - */ -fs_initcall(musb_init); - -static void __exit musb_cleanup(void) -{ - platform_driver_unregister(&musb_driver); -} -module_exit(musb_cleanup); +module_platform_driver(musb_driver); |
