summaryrefslogtreecommitdiff
path: root/drivers/usb/musb
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-04-21 12:33:03 +0300
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-04-21 15:44:55 +0300
commit64131a87f2aae2ed9e05d8227c5b009ca6c50d98 (patch)
treefdea23fd59216120bf54a48c60ca24489a733f14 /drivers/usb/musb
parent676ee36be04985062522804c2de04f0764212be6 (diff)
parent2c33ce009ca2389dbf0535d0672214d09738e35e (diff)
downloadlinux-64131a87f2aae2ed9e05d8227c5b009ca6c50d98.tar.xz
Merge branch 'drm-next-merged' of git://people.freedesktop.org/~airlied/linux into v4l_for_linus
* 'drm-next-merged' of git://people.freedesktop.org/~airlied/linux: (9717 commits) media-bus: Fixup RGB444_1X12, RGB565_1X16, and YUV8_1X24 media bus format hexdump: avoid warning in test function fs: take i_mutex during prepare_binprm for set[ug]id executables smp: Fix error case handling in smp_call_function_*() iommu-common: Fix PARISC compile-time warnings sparc: Make LDC use common iommu poll management functions sparc: Make sparc64 use scalable lib/iommu-common.c functions Break up monolithic iommu table/lock into finer graularity pools and lock sparc: Revert generic IOMMU allocator. tools/power turbostat: correct dumped pkg-cstate-limit value tools/power turbostat: calculate TSC frequency from CPUID(0x15) on SKL tools/power turbostat: correct DRAM RAPL units on recent Xeon processors tools/power turbostat: Initial Skylake support tools/power turbostat: Use $(CURDIR) instead of $(PWD) and add support for O= option in Makefile tools/power turbostat: modprobe msr, if needed tools/power turbostat: dump MSR_TURBO_RATIO_LIMIT2 tools/power turbostat: use new MSR_TURBO_RATIO_LIMIT names Bluetooth: hidp: Fix regression with older userspace and flags validation config: Enable NEED_DMA_MAP_STATE by default when SWIOTLB is selected perf/x86/intel/pt: Fix and clean up error handling in pt_event_add() ... That solves several merge conflicts: Documentation/DocBook/media/v4l/subdev-formats.xml Documentation/devicetree/bindings/vendor-prefixes.txt drivers/staging/media/mn88473/mn88473.c include/linux/kconfig.h include/uapi/linux/media-bus-format.h The ones at subdev-formats.xml and media-bus-format.h are not trivial. That's why we opted to merge from DRM.
Diffstat (limited to 'drivers/usb/musb')
-rw-r--r--drivers/usb/musb/Kconfig3
-rw-r--r--drivers/usb/musb/musb_core.c231
-rw-r--r--drivers/usb/musb/musb_core.h14
-rw-r--r--drivers/usb/musb/musb_cppi41.c84
-rw-r--r--drivers/usb/musb/musb_dsps.c121
-rw-r--r--drivers/usb/musb/musb_gadget.c40
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/musb/musb_virthub.c4
-rw-r--r--drivers/usb/musb/omap2430.c7
9 files changed, 242 insertions, 264 deletions
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 14e1628483d9..39db8b603627 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -79,7 +79,8 @@ config USB_MUSB_TUSB6010
config USB_MUSB_OMAP2PLUS
tristate "OMAP2430 and onwards"
- depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY
+ depends on ARCH_OMAP2PLUS && USB
+ depends on OMAP_CONTROL_PHY || !OMAP_CONTROL_PHY
select GENERIC_PHY
config USB_MUSB_AM35X
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index e6f4cbfeed97..3789b08ef67b 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -99,6 +99,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/usb.h>
#include "musb_core.h"
@@ -507,7 +508,8 @@ void musb_hnp_stop(struct musb *musb)
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
}
-static void musb_generic_disable(struct musb *musb);
+static void musb_recover_from_babble(struct musb *musb);
+
/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
@@ -534,35 +536,21 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/
if (int_usb & MUSB_INTR_RESUME) {
handled = IRQ_HANDLED;
- dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state));
+ dev_dbg(musb->controller, "RESUME (%s)\n",
+ usb_otg_state_string(musb->xceiv->otg->state));
if (devctl & MUSB_DEVCTL_HM) {
- void __iomem *mbase = musb->mregs;
- u8 power;
-
switch (musb->xceiv->otg->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;
- dev_dbg(musb->controller, "Spurious SUSPENDM\n");
- break;
- }
-
- power &= ~MUSB_POWER_SUSPENDM;
- musb_writeb(mbase, MUSB_POWER,
- power | MUSB_POWER_RESUME);
-
musb->port1_status |=
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
- + msecs_to_jiffies(20);
+ + msecs_to_jiffies(USB_RESUME_TIMEOUT);
musb->need_finish_resume = 1;
musb->xceiv->otg->state = OTG_STATE_A_HOST;
@@ -775,10 +763,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->ep0_stage = MUSB_EP0_START;
- /* flush endpoints when transitioning from Device Mode */
- if (is_peripheral_active(musb)) {
- /* REVISIT HNP; just force disconnect */
- }
musb->intrtxe = musb->epmask;
musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = musb->epmask & 0xfffe;
@@ -879,20 +863,19 @@ b_host:
*/
if (int_usb & MUSB_INTR_RESET) {
handled = IRQ_HANDLED;
- if ((devctl & MUSB_DEVCTL_HM) != 0) {
+ if (devctl & MUSB_DEVCTL_HM) {
/*
- * Looks like non-HS BABBLE can be ignored, but
- * HS BABBLE is an error condition. For HS the solution
- * is to avoid babble in the first place and fix what
- * caused BABBLE. When HS BABBLE happens we can only
- * stop the session.
+ * When BABBLE happens what we can depends on which
+ * platform MUSB is running, because some platforms
+ * implemented proprietary means for 'recovering' from
+ * Babble conditions. One such platform is AM335x. In
+ * most cases, however, the only thing we can do is
+ * drop the session.
*/
- if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV))
- dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl);
- else {
- ERR("Stopping host session -- babble\n");
- musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
- }
+ dev_err(musb->controller, "Babble\n");
+
+ if (is_host_active(musb))
+ musb_recover_from_babble(musb);
} else {
dev_dbg(musb->controller, "BUS RESET as %s\n",
usb_otg_state_string(musb->xceiv->otg->state));
@@ -931,13 +914,6 @@ b_host:
}
}
- /* handle babble condition */
- if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) {
- musb_generic_disable(musb);
- schedule_delayed_work(&musb->recover_work,
- msecs_to_jiffies(100));
- }
-
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
* supporting transfer phasing to prevent exceeding ISO bandwidth
@@ -990,7 +966,7 @@ b_host:
/*-------------------------------------------------------------------------*/
-static void musb_generic_disable(struct musb *musb)
+static void musb_disable_interrupts(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
u16 temp;
@@ -1002,16 +978,35 @@ static void musb_generic_disable(struct musb *musb)
musb->intrrxe = 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);
+}
+
+static void musb_enable_interrupts(struct musb *musb)
+{
+ void __iomem *regs = musb->mregs;
+
+ /* Set INT enable registers, enable interrupts */
+ 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);
}
+static void musb_generic_disable(struct musb *musb)
+{
+ void __iomem *mbase = musb->mregs;
+
+ musb_disable_interrupts(musb);
+
+ /* off */
+ musb_writeb(mbase, MUSB_DEVCTL, 0);
+}
+
/*
* Program the HDRC to start (enable interrupts, dma, etc.).
*/
@@ -1022,13 +1017,7 @@ void musb_start(struct musb *musb)
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
- /* Set INT enable registers, enable interrupts */
- 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_enable_interrupts(musb);
musb_writeb(regs, MUSB_TESTMODE, 0);
/* put into basic highspeed mode and start session */
@@ -1587,9 +1576,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
irqreturn_t musb_interrupt(struct musb *musb)
{
irqreturn_t retval = IRQ_NONE;
+ unsigned long status;
+ unsigned long epnum;
u8 devctl;
- int ep_num;
- u32 reg;
+
+ if (!musb->int_usb && !musb->int_tx && !musb->int_rx)
+ return IRQ_NONE;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
@@ -1597,56 +1589,57 @@ irqreturn_t musb_interrupt(struct musb *musb)
is_host_active(musb) ? "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
+ /**
+ * According to Mentor Graphics' documentation, flowchart on page 98,
+ * IRQ should be handled as follows:
+ *
+ * . Resume IRQ
+ * . Session Request IRQ
+ * . VBUS Error IRQ
+ * . Suspend IRQ
+ * . Connect IRQ
+ * . Disconnect IRQ
+ * . Reset/Babble IRQ
+ * . SOF IRQ (we're not using this one)
+ * . Endpoint 0 IRQ
+ * . TX Endpoints
+ * . RX Endpoints
+ *
+ * We will be following that flowchart in order to avoid any problems
+ * that might arise with internal Finite State Machine.
*/
- if (musb->int_usb)
- retval |= musb_stage0_irq(musb, musb->int_usb,
- devctl);
- /* "stage 1" is handling endpoint irqs */
+ if (musb->int_usb)
+ retval |= musb_stage0_irq(musb, musb->int_usb, devctl);
- /* handle endpoint 0 first */
if (musb->int_tx & 1) {
if (is_host_active(musb))
retval |= musb_h_ep0_irq(musb);
else
retval |= musb_g_ep0_irq(musb);
+
+ /* we have just handled endpoint 0 IRQ, clear it */
+ musb->int_tx &= ~BIT(0);
}
- /* RX on endpoints 1-15 */
- reg = musb->int_rx >> 1;
- ep_num = 1;
- while (reg) {
- if (reg & 1) {
- /* musb_ep_select(musb->mregs, ep_num); */
- /* REVISIT just retval = ep->rx_irq(...) */
- retval = IRQ_HANDLED;
- if (is_host_active(musb))
- musb_host_rx(musb, ep_num);
- else
- musb_g_rx(musb, ep_num);
- }
+ status = musb->int_tx;
- reg >>= 1;
- ep_num++;
+ for_each_set_bit(epnum, &status, 16) {
+ retval = IRQ_HANDLED;
+ if (is_host_active(musb))
+ musb_host_tx(musb, epnum);
+ else
+ musb_g_tx(musb, epnum);
}
- /* TX on endpoints 1-15 */
- reg = musb->int_tx >> 1;
- ep_num = 1;
- while (reg) {
- if (reg & 1) {
- /* musb_ep_select(musb->mregs, ep_num); */
- /* REVISIT just retval |= ep->tx_irq(...) */
- retval = IRQ_HANDLED;
- if (is_host_active(musb))
- musb_host_tx(musb, ep_num);
- else
- musb_g_tx(musb, ep_num);
- }
- reg >>= 1;
- ep_num++;
+ status = musb->int_rx;
+
+ for_each_set_bit(epnum, &status, 16) {
+ retval = IRQ_HANDLED;
+ if (is_host_active(musb))
+ musb_host_rx(musb, epnum);
+ else
+ musb_g_rx(musb, epnum);
}
return retval;
@@ -1825,33 +1818,44 @@ static void musb_irq_work(struct work_struct *data)
}
}
-/* Recover from babble interrupt conditions */
-static void musb_recover_work(struct work_struct *data)
+static void musb_recover_from_babble(struct musb *musb)
{
- struct musb *musb = container_of(data, struct musb, recover_work.work);
- int status, ret;
+ int ret;
+ u8 devctl;
+
+ musb_disable_interrupts(musb);
+
+ /*
+ * wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give
+ * it some slack and wait for 10us.
+ */
+ udelay(10);
- ret = musb_platform_reset(musb);
- if (ret)
+ ret = musb_platform_recover(musb);
+ if (ret) {
+ musb_enable_interrupts(musb);
return;
+ }
- usb_phy_vbus_off(musb->xceiv);
- usleep_range(100, 200);
+ /* drop session bit */
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
- usb_phy_vbus_on(musb->xceiv);
- usleep_range(100, 200);
+ /* tell usbcore about it */
+ musb_root_disconnect(musb);
/*
* 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);
+ ret = ep_config_from_table(musb);
else
- status = ep_config_from_hw(musb);
+ ret = ep_config_from_hw(musb);
- /* start the session again */
- if (status == 0)
+ /* restart session */
+ if (ret == 0)
musb_start(musb);
}
@@ -1969,10 +1973,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
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_set_power = plat->set_power;
musb->min_power = plat->min_power;
@@ -1991,6 +1991,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_readl = musb_default_readl;
musb_writel = musb_default_writel;
+ /* We need musb_read/write functions initialized for PM */
+ pm_runtime_use_autosuspend(musb->controller);
+ pm_runtime_set_autosuspend_delay(musb->controller, 200);
+ pm_runtime_irq_safe(musb->controller);
+ pm_runtime_enable(musb->controller);
+
/* The musb_platform_init() call:
* - adjusts musb->mregs
* - sets the musb->isr
@@ -2085,7 +2091,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
- INIT_DELAYED_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);
@@ -2181,7 +2186,6 @@ fail4:
fail3:
cancel_work_sync(&musb->irq_work);
- cancel_delayed_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)
@@ -2247,7 +2251,6 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
- cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb);
@@ -2461,7 +2464,7 @@ static int musb_resume(struct device *dev)
if (musb->need_finish_resume) {
musb->need_finish_resume = 0;
schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(20));
+ msecs_to_jiffies(USB_RESUME_TIMEOUT));
}
/*
@@ -2504,7 +2507,7 @@ static int musb_runtime_resume(struct device *dev)
if (musb->need_finish_resume) {
musb->need_finish_resume = 0;
schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(20));
+ msecs_to_jiffies(USB_RESUME_TIMEOUT));
}
return 0;
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 5e65958f7915..3877249a8b2d 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -160,7 +160,8 @@ struct musb_io;
* @init: turns on clocks, sets up platform-specific registers, etc
* @exit: undoes @init
* @set_mode: forcefully changes operating mode
- * @try_ilde: tries to idle the IP
+ * @try_idle: tries to idle the IP
+ * @recover: platform-specific babble recovery
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
* @adjust_channel_params: pre check for standard dma channel_program func
@@ -196,7 +197,7 @@ struct musb_platform_ops {
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
- int (*reset)(struct musb *musb);
+ int (*recover)(struct musb *musb);
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
@@ -300,7 +301,6 @@ struct musb {
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
- struct delayed_work recover_work;
struct delayed_work deassert_reset_work;
struct delayed_work finish_resume_work;
u16 hwvers;
@@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
musb->ops->try_idle(musb, timeout);
}
-static inline int musb_platform_reset(struct musb *musb)
+static inline int musb_platform_recover(struct musb *musb)
{
- if (!musb->ops->reset)
- return -EINVAL;
+ if (!musb->ops->recover)
+ return 0;
- return musb->ops->reset(musb);
+ return musb->ops->recover(musb);
}
static inline int musb_platform_get_vbus_status(struct musb *musb)
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index be84562d021b..8bd8c5e26921 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data)
struct dma_channel *channel = private_data;
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
+ struct cppi41_dma_controller *controller;
struct musb *musb = hw_ep->musb;
unsigned long flags;
struct dma_tx_state txstate;
u32 transferred;
+ int is_hs = 0;
bool empty;
spin_lock_irqsave(&musb->lock, flags);
@@ -248,61 +250,59 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
- empty = musb_is_tx_fifo_empty(hw_ep);
- if (empty) {
+ if (cppi41_channel->is_tx)
+ empty = musb_is_tx_fifo_empty(hw_ep);
+
+ if (!cppi41_channel->is_tx || empty) {
cppi41_trans_done(cppi41_channel);
- } else {
- struct cppi41_dma_controller *controller;
- int is_hs = 0;
- /*
- * On AM335x it has been observed that the TX interrupt fires
- * too early that means the TXFIFO is not yet empty but the DMA
- * engine says that it is done with the transfer. We don't
- * receive a FIFO empty interrupt so the only thing we can do is
- * to poll for the bit. On HS it usually takes 2us, on FS around
- * 110us - 150us depending on the transfer size.
- * We spin on HS (no longer than than 25us and setup a timer on
- * FS to check for the bit and complete the transfer.
- */
- controller = cppi41_channel->controller;
+ goto out;
+ }
- if (is_host_active(musb)) {
- if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
- is_hs = 1;
- } else {
- if (musb->g.speed == USB_SPEED_HIGH)
- is_hs = 1;
- }
- if (is_hs) {
- unsigned wait = 25;
-
- do {
- empty = musb_is_tx_fifo_empty(hw_ep);
- if (empty)
- break;
- wait--;
- if (!wait)
- break;
- udelay(1);
- } while (1);
+ /*
+ * On AM335x it has been observed that the TX interrupt fires
+ * too early that means the TXFIFO is not yet empty but the DMA
+ * engine says that it is done with the transfer. We don't
+ * receive a FIFO empty interrupt so the only thing we can do is
+ * to poll for the bit. On HS it usually takes 2us, on FS around
+ * 110us - 150us depending on the transfer size.
+ * We spin on HS (no longer than than 25us and setup a timer on
+ * FS to check for the bit and complete the transfer.
+ */
+ controller = cppi41_channel->controller;
+
+ if (is_host_active(musb)) {
+ if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
+ is_hs = 1;
+ } else {
+ if (musb->g.speed == USB_SPEED_HIGH)
+ is_hs = 1;
+ }
+ if (is_hs) {
+ unsigned wait = 25;
+ do {
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
cppi41_trans_done(cppi41_channel);
goto out;
}
- }
- list_add_tail(&cppi41_channel->tx_check,
- &controller->early_tx_list);
- if (!hrtimer_is_queued(&controller->early_tx)) {
- unsigned long usecs = cppi41_channel->total_len / 10;
+ wait--;
+ if (!wait)
+ break;
+ cpu_relax();
+ } while (1);
+ }
+ list_add_tail(&cppi41_channel->tx_check,
+ &controller->early_tx_list);
+ if (!hrtimer_is_queued(&controller->early_tx)) {
+ unsigned long usecs = cppi41_channel->total_len / 10;
- hrtimer_start_range_ns(&controller->early_tx,
+ hrtimer_start_range_ns(&controller->early_tx,
ktime_set(0, usecs * NSEC_PER_USEC),
20 * NSEC_PER_USEC,
HRTIMER_MODE_REL);
- }
}
+
out:
spin_unlock_irqrestore(&musb->lock, flags);
}
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 53bd0e71d19f..65d931a28a14 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -119,7 +119,7 @@ struct dsps_musb_wrapper {
unsigned iddig:5;
unsigned iddig_mux:5;
/* miscellaneous stuff */
- u8 poll_seconds;
+ unsigned poll_timeout;
};
/*
@@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb)
dsps_writel(reg_base, wrp->epintr_set, epmask);
dsps_writel(reg_base, wrp->coreintr_set, coremask);
- /* Force the DRVVBUS IRQ so we can start polling for ID change. */
- dsps_writel(reg_base, wrp->coreintr_set,
- (1 << wrp->drvvbus) << wrp->usb_shift);
+ /* start polling for ID change. */
+ mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout));
dsps_musb_try_idle(musb, 0);
}
@@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb)
}
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
- mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(wrp->poll_timeout));
break;
case OTG_STATE_A_WAIT_VFALL:
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
@@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
usbintr, epintr);
- /*
- * DRVVBUS IRQs are the only proxy we have (a very poor one!) for
- * DSPS IP's missing ID change IRQ. We need an ID change IRQ to
- * switch appropriately between halves of the OTG state machine.
- * Managing DEVCTL.SESSION per Mentor docs requires that we know its
- * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
- * Also, DRVVBUS pulses for SRP (but not at 5V) ...
- */
- if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) {
- pr_info("CAUTION: musb: Babble Interrupt Occurred\n");
-
- /*
- * When a babble condition occurs, the musb controller removes
- * the session and is no longer in host mode. Hence, all
- * devices connected to its root hub get disconnected.
- *
- * Hand this error down to the musb core isr, so it can
- * recover.
- */
- musb->int_usb = MUSB_INTR_BABBLE | MUSB_INTR_DISCONNECT;
- musb->int_tx = musb->int_rx = 0;
- }
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
int drvvbus = dsps_readl(reg_base, wrp->status);
@@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
- mod_timer(&glue->timer,
- jiffies + wrp->poll_seconds * HZ);
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(wrp->poll_timeout));
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
@@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
/* Poll for ID change in OTG port mode */
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
- mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(wrp->poll_timeout));
out:
spin_unlock_irqrestore(&musb->lock, flags);
@@ -453,16 +432,31 @@ static int dsps_musb_init(struct musb *musb)
musb->ctrl_base = reg_base;
/* NOP driver needs change if supporting dual instance */
- musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
+ musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0);
if (IS_ERR(musb->xceiv))
return PTR_ERR(musb->xceiv);
+ musb->phy = devm_phy_get(dev->parent, "usb2-phy");
+
/* Returns zero if e.g. not clocked */
rev = dsps_readl(reg_base, wrp->revision);
if (!rev)
return -ENODEV;
usb_phy_init(musb->xceiv);
+ if (IS_ERR(musb->phy)) {
+ musb->phy = NULL;
+ } else {
+ ret = phy_init(musb->phy);
+ if (ret < 0)
+ return ret;
+ ret = phy_power_on(musb->phy);
+ if (ret) {
+ phy_exit(musb->phy);
+ return ret;
+ }
+ }
+
setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
/* Reset the musb */
@@ -482,7 +476,7 @@ static int dsps_musb_init(struct musb *musb)
* logic enabled.
*/
val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
- if (val == MUSB_BABBLE_RCV_DISABLE) {
+ if (val & MUSB_BABBLE_RCV_DISABLE) {
glue->sw_babble_enabled = true;
val |= MUSB_BABBLE_SW_SESSION_CTRL;
dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
@@ -502,6 +496,8 @@ static int dsps_musb_exit(struct musb *musb)
del_timer_sync(&glue->timer);
usb_phy_shutdown(musb->xceiv);
+ phy_power_off(musb->phy);
+ phy_exit(musb->phy);
debugfs_remove_recursive(glue->dbgfs_root);
return 0;
@@ -554,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
return 0;
}
-static bool sw_babble_control(struct musb *musb)
+static bool dsps_sw_babble_control(struct musb *musb)
{
u8 babble_ctl;
bool session_restart = false;
@@ -605,31 +601,36 @@ static bool sw_babble_control(struct musb *musb)
return session_restart;
}
-static int dsps_musb_reset(struct musb *musb)
+static int dsps_musb_recover(struct musb *musb)
{
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
- const struct dsps_musb_wrapper *wrp = glue->wrp;
int session_restart = 0;
if (glue->sw_babble_enabled)
- session_restart = sw_babble_control(musb);
- /*
- * In case of new silicon version babble condition can be recovered
- * without resetting the MUSB. But for older silicon versions, MUSB
- * reset is needed
- */
- if (session_restart || !glue->sw_babble_enabled) {
- dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
- dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
- usleep_range(100, 200);
- usb_phy_shutdown(musb->xceiv);
- usleep_range(100, 200);
- usb_phy_init(musb->xceiv);
+ session_restart = dsps_sw_babble_control(musb);
+ else
session_restart = 1;
+
+ return session_restart ? 0 : -EPIPE;
+}
+
+/* Similar to am35x, dm81xx support only 32-bit read operation */
+static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+{
+ void __iomem *fifo = hw_ep->fifo;
+
+ if (len >= 4) {
+ ioread32_rep(fifo, dst, len >> 2);
+ dst += len & ~0x03;
+ len &= 0x03;
}
- return !session_restart;
+ /* Read any remaining 1 to 3 bytes */
+ if (len > 0) {
+ u32 val = musb_readl(fifo, 0);
+ memcpy(dst, &val, len);
+ }
}
static struct musb_platform_ops dsps_ops = {
@@ -642,7 +643,7 @@ static struct musb_platform_ops dsps_ops = {
.try_idle = dsps_musb_try_idle,
.set_mode = dsps_musb_set_mode,
- .reset = dsps_musb_reset,
+ .recover = dsps_musb_recover,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
@@ -687,7 +688,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
struct musb_hdrc_config *config;
struct platform_device *musb;
struct device_node *dn = parent->dev.of_node;
- int ret;
+ int ret, val;
memset(resources, 0, sizeof(resources));
res = platform_get_resource_byname(parent, IORESOURCE_MEM, "mc");
@@ -714,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
musb->dev.parent = dev;
musb->dev.dma_mask = &musb_dmamask;
musb->dev.coherent_dma_mask = musb_dmamask;
- musb->dev.of_node = of_node_get(dn);
glue->musb = musb;
@@ -739,7 +739,10 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
pdata.mode = get_musb_port_mode(dev);
/* DT keeps this entry in mA, musb expects it as per USB spec */
pdata.power = get_int_prop(dn, "mentor,power") / 2;
- config->multipoint = of_property_read_bool(dn, "mentor,multipoint");
+
+ ret = of_property_read_u32(dn, "mentor,multipoint", &val);
+ if (!ret && val)
+ config->multipoint = true;
ret = platform_device_add_data(musb, &pdata, sizeof(pdata));
if (ret) {
@@ -776,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev)
}
wrp = match->data;
+ if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816"))
+ dsps_ops.read_fifo = dsps_read_fifo32;
+
/* allocate glue */
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
@@ -847,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {
.rxep_shift = 16,
.rxep_mask = 0xfffe,
.rxep_bitmap = (0xfffe << 16),
- .poll_seconds = 2,
+ .poll_timeout = 2000, /* ms */
};
static const struct of_device_id musb_dsps_of_match[] = {
{ .compatible = "ti,musb-am33xx",
- .data = (void *) &am33xx_driver_data, },
+ .data = &am33xx_driver_data, },
+ { .compatible = "ti,musb-dm816",
+ .data = &am33xx_driver_data, },
{ },
};
MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
@@ -903,7 +911,8 @@ static int dsps_resume(struct device *dev)
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
- mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(wrp->poll_timeout));
return 0;
}
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index b2d9040c7685..4c481cd66c77 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1876,44 +1876,6 @@ err:
return retval;
}
-static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
-{
- int i;
- struct musb_hw_ep *hw_ep;
-
- /* don't disconnect if it's not connected */
- if (musb->g.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
- else
- musb->g.speed = USB_SPEED_UNKNOWN;
-
- /* deactivate the hardware */
- if (musb->softconnect) {
- musb->softconnect = 0;
- musb_pullup(musb, 0);
- }
- musb_stop(musb);
-
- /* killing any outstanding requests will quiesce the driver;
- * then report disconnect
- */
- if (driver) {
- for (i = 0, hw_ep = musb->endpoints;
- i < musb->nr_endpoints;
- i++, hw_ep++) {
- musb_ep_select(musb->mregs, i);
- if (hw_ep->is_shared_fifo /* || !epnum */) {
- nuke(&hw_ep->ep_in, -ESHUTDOWN);
- } else {
- if (hw_ep->max_packet_sz_tx)
- nuke(&hw_ep->ep_in, -ESHUTDOWN);
- if (hw_ep->max_packet_sz_rx)
- nuke(&hw_ep->ep_out, -ESHUTDOWN);
- }
- }
- }
-}
-
/*
* Unregister the gadget driver. Used by gadget drivers when
* unregistering themselves from the controller.
@@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g)
(void) musb_gadget_vbus_draw(&musb->g, 0);
musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
- stop_activity(musb, NULL);
+ musb_stop(musb);
otg_set_peripheral(musb->xceiv->otg, NULL);
musb->is_active = 0;
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 883a9adfdfff..c3d5fc9dfb5b 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2613,7 +2613,7 @@ static const struct hc_driver musb_hc_driver = {
.description = "musb-hcd",
.product_desc = "MUSB HDRC host driver",
.hcd_priv_size = sizeof(struct musb *),
- .flags = HCD_USB2 | HCD_MEMORY,
+ .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
/* not using irq handler or reset hooks from usbcore, since
* those must be shared with peripheral code for OTG configs
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 294e159f4afe..86c4b533e90b 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -136,7 +136,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
/* later, GetPortStatus will stop RESUME signaling */
musb->port1_status |= MUSB_PORT_STAT_RESUME;
schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(20));
+ msecs_to_jiffies(USB_RESUME_TIMEOUT));
}
}
@@ -345,7 +345,7 @@ int musb_hub_control(
struct usb_hub_descriptor *desc = (void *)buf;
desc->bDescLength = 9;
- desc->bDescriptorType = 0x29;
+ desc->bDescriptorType = USB_DT_HUB;
desc->bNbrPorts = 1;
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 763649eb4987..cc752d8c7773 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -516,7 +516,7 @@ static int omap2430_probe(struct platform_device *pdev)
struct omap2430_glue *glue;
struct device_node *np = pdev->dev.of_node;
struct musb_hdrc_config *config;
- int ret = -ENOMEM;
+ int ret = -ENOMEM, val;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
@@ -559,7 +559,10 @@ static int omap2430_probe(struct platform_device *pdev)
of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
of_property_read_u32(np, "power", (u32 *)&pdata->power);
- config->multipoint = of_property_read_bool(np, "multipoint");
+
+ ret = of_property_read_u32(np, "multipoint", &val);
+ if (!ret && val)
+ config->multipoint = true;
pdata->board_data = data;
pdata->config = config;