Commit e3071039 authored by David Brownell's avatar David Brownell Committed by Tony Lindgren

MUSB: More TUSB OTG support

Add more host/OTG functionality.

New Features:
   -	Cable based role switching (using the ID pin) mostly works.  This gives
	much of the end-user value for USB OTG; there's also dynamic role
	switching (using special HNP protocol with another OTG device), which
	surely won't work yet.
   -	Root can write 0/N to /sys/module/musb_hdrc/parameters/musb_otg to
   	disable OTG functionality, forcing B-Default mode (with optional SRP
	support, but without 8mA current limits) when it's set.  Change this
	before the gadget driver initializes (e.g. before "modprobe").

Bugfixes:
   -	Fix many OTG mode startup glitches, notably for the root hub.
   -	Add workaround for "issue 4".

Cleanups:
   -	Remove more mode-specific #ifdeffery
   -	ReMoveSomeCamelCasing
   -	Quieter startup
   -	Quash another Kconfig rebellion
   -	Add missing bit declaration
   -	Tersify TUSB irq messages

Open Issues:
   -	Nothing passes remote wakeup down to root hub (unchanged)
   -	Strange VBUS errors (unchanged; workaround: use hub modified for
	N770, providing 5V supply till TUSB board does so reliably);
   -	Plug in/out as peripheral many times; fine.  As host (with modified
	hub), ditto.  Now peripheral again ... fails.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent 89c57744
...@@ -1598,8 +1598,6 @@ init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 bEnd, int is_in) ...@@ -1598,8 +1598,6 @@ init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 bEnd, int is_in)
ep->end_point.ops = &musb_ep_ops; ep->end_point.ops = &musb_ep_ops;
list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list); list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list);
} }
DBG(4, "periph: %s, maxpacket %d\n", ep->end_point.name,
ep->end_point.maxpacket);
} }
/* /*
...@@ -1634,8 +1632,6 @@ static inline void __devinit musb_g_init_endpoints(struct musb *pThis) ...@@ -1634,8 +1632,6 @@ static inline void __devinit musb_g_init_endpoints(struct musb *pThis)
} }
} }
} }
DBG(2, "initialized %d (max %d) endpoints\n", count,
MUSB_C_NUM_EPS * 2 - 1);
} }
/* called once during driver setup to initialize and link into /* called once during driver setup to initialize and link into
...@@ -1656,10 +1652,6 @@ int __devinit musb_gadget_setup(struct musb *pThis) ...@@ -1656,10 +1652,6 @@ int __devinit musb_gadget_setup(struct musb *pThis)
pThis->g.ops = &musb_gadget_operations; pThis->g.ops = &musb_gadget_operations;
pThis->g.is_dualspeed = 1; pThis->g.is_dualspeed = 1;
pThis->g.speed = USB_SPEED_UNKNOWN; pThis->g.speed = USB_SPEED_UNKNOWN;
#ifdef CONFIG_USB_MUSB_OTG
if (pThis->board_mode == MUSB_OTG)
pThis->g.is_otg = 1;
#endif
/* this "gadget" abstracts/virtualizes the controller */ /* this "gadget" abstracts/virtualizes the controller */
strcpy(pThis->g.dev.bus_id, "gadget"); strcpy(pThis->g.dev.bus_id, "gadget");
...@@ -1762,11 +1754,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) ...@@ -1762,11 +1754,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
* hosts only see fully functional devices. * hosts only see fully functional devices.
*/ */
if (!is_otg_enabled(pThis))
musb_start(pThis); musb_start(pThis);
spin_unlock_irqrestore(&pThis->Lock, flags); spin_unlock_irqrestore(&pThis->Lock, flags);
#ifdef CONFIG_USB_MUSB_OTG if (is_otg_enabled(pThis)) {
if (pThis->board_mode == MUSB_OTG) {
DBG(3, "OTG startup...\n"); DBG(3, "OTG startup...\n");
/* REVISIT: funcall to other code, which also /* REVISIT: funcall to other code, which also
...@@ -1784,7 +1777,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) ...@@ -1784,7 +1777,6 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
spin_unlock_irqrestore(&pThis->Lock, flags); spin_unlock_irqrestore(&pThis->Lock, flags);
} }
} }
#endif
} }
return retval; return retval;
...@@ -1872,15 +1864,14 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ...@@ -1872,15 +1864,14 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
retval = -EINVAL; retval = -EINVAL;
spin_unlock_irqrestore(&musb->Lock, flags); spin_unlock_irqrestore(&musb->Lock, flags);
#ifdef CONFIG_USB_MUSB_OTG if (is_otg_enabled(musb) && retval == 0) {
if (retval == 0 && musb->board_mode == MUSB_OTG) {
usb_remove_hcd(musb_to_hcd(musb)); usb_remove_hcd(musb_to_hcd(musb));
/* FIXME we need to be able to register another /* FIXME we need to be able to register another
* gadget driver here and have everything work; * gadget driver here and have everything work;
* that currently misbehaves. * that currently misbehaves.
*/ */
} }
#endif
return retval; return retval;
} }
EXPORT_SYMBOL(usb_gadget_unregister_driver); EXPORT_SYMBOL(usb_gadget_unregister_driver);
...@@ -2002,13 +1993,16 @@ __acquires(pThis->Lock) ...@@ -2002,13 +1993,16 @@ __acquires(pThis->Lock)
pThis->g.a_alt_hnp_support = 0; pThis->g.a_alt_hnp_support = 0;
pThis->g.a_hnp_support = 0; pThis->g.a_hnp_support = 0;
if (is_otg_enabled(pThis))
pThis->g.is_otg = !!musb_otg;
/* Normal reset, as B-Device; /* Normal reset, as B-Device;
* or else after HNP, as A-Device * or else after HNP, as A-Device
*/ */
if (devctl & MGC_M_DEVCTL_BDEVICE) { if (devctl & MGC_M_DEVCTL_BDEVICE) {
pThis->xceiv.state = OTG_STATE_B_PERIPHERAL; pThis->xceiv.state = OTG_STATE_B_PERIPHERAL;
pThis->g.is_a_peripheral = 0; pThis->g.is_a_peripheral = 0;
} else if (is_otg_enabled(pThis) && pThis->board_mode == MUSB_OTG) { } else if (is_otg_enabled(pThis) && musb_otg) {
pThis->xceiv.state = OTG_STATE_A_PERIPHERAL; pThis->xceiv.state = OTG_STATE_A_PERIPHERAL;
pThis->g.is_a_peripheral = 1; pThis->g.is_a_peripheral = 1;
} else } else
...@@ -2016,5 +2010,5 @@ __acquires(pThis->Lock) ...@@ -2016,5 +2010,5 @@ __acquires(pThis->Lock)
/* start with default limits on VBUS power draw */ /* start with default limits on VBUS power draw */
(void) musb_gadget_vbus_draw(&pThis->g, (void) musb_gadget_vbus_draw(&pThis->g,
is_otg_enabled(pThis) ? 8 : 100); (is_otg_enabled(pThis) && musb_otg) ? 8 : 100);
} }
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/usb_ch9.h> #include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include <linux/usb.h>
#include <linux/usb_otg.h> #include <linux/usb_otg.h>
#include <linux/usb/musb.h> #include <linux/usb/musb.h>
...@@ -67,13 +69,17 @@ struct musb_ep; ...@@ -67,13 +69,17 @@ struct musb_ep;
#include "plat_arc.h" #include "plat_arc.h"
#include "musbhdrc.h" #include "musbhdrc.h"
#include "musb_gadget.h"
#include "../core/hcd.h"
#include "musb_host.h"
#include "otg.h"
/* REVISIT tune this */ /* REVISIT tune this */
#define MIN_DMA_REQUEST 1 /* use PIO below this xfer size */ #define MIN_DMA_REQUEST 1 /* use PIO below this xfer size */
#ifdef CONFIG_USB_MUSB_OTG #ifdef CONFIG_USB_MUSB_OTG
#include "otg.h"
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST) #define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL) #define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
...@@ -82,15 +88,8 @@ struct musb_ep; ...@@ -82,15 +88,8 @@ struct musb_ep;
/* NOTE: otg and peripheral-only state machines start at B_IDLE. /* NOTE: otg and peripheral-only state machines start at B_IDLE.
* OTG or host-only go to A_IDLE when ID is sensed. * OTG or host-only go to A_IDLE when ID is sensed.
*/ */
#define is_peripheral_active(m) (is_peripheral_capable() && !(m)->bIsHost) #define is_peripheral_active(m) (!(m)->bIsHost)
#define is_host_active(m) (is_host_capable() && (m)->bIsHost) #define is_host_active(m) ((m)->bIsHost)
/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't really
* override that choice selection (often USB_GADGET_DUMMY_HCD).
*/
#ifndef CONFIG_USB_GADGET_MUSB_HDRC
#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
#endif
#else #else
#define is_peripheral_enabled(musb) is_peripheral_capable() #define is_peripheral_enabled(musb) is_peripheral_capable()
...@@ -101,6 +100,15 @@ struct musb_ep; ...@@ -101,6 +100,15 @@ struct musb_ep;
#define is_host_active(musb) is_host_capable() #define is_host_active(musb) is_host_capable()
#endif #endif
#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
* override that choice selection (often USB_GADGET_DUMMY_HCD).
*/
#ifndef CONFIG_USB_GADGET_MUSB_HDRC
#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
#endif
#endif /* need MUSB gadget selection */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#include <linux/fs.h> #include <linux/fs.h>
...@@ -111,9 +119,6 @@ struct musb_ep; ...@@ -111,9 +119,6 @@ struct musb_ep;
#ifdef CONFIG_USB_GADGET_MUSB_HDRC #ifdef CONFIG_USB_GADGET_MUSB_HDRC
#include <linux/usb_gadget.h>
#include "musb_gadget.h"
#define is_peripheral_capable() (1) #define is_peripheral_capable() (1)
extern irqreturn_t musb_g_ep0_irq(struct musb *); extern irqreturn_t musb_g_ep0_irq(struct musb *);
...@@ -129,8 +134,6 @@ extern void musb_g_disconnect(struct musb *); ...@@ -129,8 +134,6 @@ extern void musb_g_disconnect(struct musb *);
#define is_peripheral_capable() (0) #define is_peripheral_capable() (0)
static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; } static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
static inline void musb_g_tx(struct musb *m, u8 e) {}
static inline void musb_g_rx(struct musb *m, u8 e) {}
static inline void musb_g_reset(struct musb *m) {} static inline void musb_g_reset(struct musb *m) {}
static inline void musb_g_suspend(struct musb *m) {} static inline void musb_g_suspend(struct musb *m) {}
static inline void musb_g_resume(struct musb *m) {} static inline void musb_g_resume(struct musb *m) {}
...@@ -142,10 +145,6 @@ static inline void musb_g_disconnect(struct musb *m) {} ...@@ -142,10 +145,6 @@ static inline void musb_g_disconnect(struct musb *m) {}
#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifdef CONFIG_USB_MUSB_HDRC_HCD
#include <linux/usb.h>
#include "../core/hcd.h"
#include "musb_host.h"
#define is_host_capable() (1) #define is_host_capable() (1)
extern irqreturn_t musb_h_ep0_irq(struct musb *); extern irqreturn_t musb_h_ep0_irq(struct musb *);
...@@ -160,8 +159,6 @@ static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; } ...@@ -160,8 +159,6 @@ static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; }
static inline void musb_host_tx(struct musb *m, u8 e) {} static inline void musb_host_tx(struct musb *m, u8 e) {}
static inline void musb_host_rx(struct musb *m, u8 e) {} static inline void musb_host_rx(struct musb *m, u8 e) {}
static inline void musb_root_disconnect(struct musb *musb) { BUG(); }
#endif #endif
......
...@@ -55,6 +55,9 @@ ...@@ -55,6 +55,9 @@
* Finally, it provides the necessary bus control service. * Finally, it provides the necessary bus control service.
*/ */
/* sysfs flag to seletively force peripheral-only operation */
extern int musb_otg;
/****************************** CONSTANTS ********************************/ /****************************** CONSTANTS ********************************/
/* /*
......
...@@ -171,6 +171,19 @@ MODULE_LICENSE("GPL"); ...@@ -171,6 +171,19 @@ MODULE_LICENSE("GPL");
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_MUSB_OTG
/* For debugging/prototyping: allow disabling host side support on boards
* with Mini-AB (or Mini-A) connectors, making peripheral side support look
* like pure peripherals (not reporting OTG capabilities, and able to
* draw a full 100mA unit load).
*/
int musb_otg = 1;
module_param(musb_otg, bool, 0600);
MODULE_PARM_DESC(musb_otg, "enable/disable OTG capabilities");
#endif
static inline struct musb *dev_to_musb(struct device *dev) static inline struct musb *dev_to_musb(struct device *dev)
{ {
#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifdef CONFIG_USB_MUSB_HDRC_HCD
...@@ -629,12 +642,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, ...@@ -629,12 +642,8 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
/* peripheral suspend, may trigger HNP */ /* peripheral suspend, may trigger HNP */
if (!(devctl & MGC_M_DEVCTL_HM)) { if (!(devctl & MGC_M_DEVCTL_HM)) {
musb_g_suspend(pThis); musb_g_suspend(pThis);
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
pThis->is_active = is_otg_enabled(pThis) pThis->is_active = is_otg_enabled(pThis)
&& pThis->xceiv.gadget->b_hnp_enable; && pThis->xceiv.gadget->b_hnp_enable;
#else
pThis->is_active = 0;
#endif
otg_input_changed(pThis, devctl, FALSE, FALSE, TRUE); otg_input_changed(pThis, devctl, FALSE, FALSE, TRUE);
} else } else
pThis->is_active = 0; pThis->is_active = 0;
...@@ -648,39 +657,44 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB, ...@@ -648,39 +657,44 @@ static irqreturn_t musb_stage2_irq(struct musb * pThis, u8 bIntrUSB,
/* /*
* Program the HDRC to start (enable interrupts, dma, etc.). * Program the HDRC to start (enable interrupts, dma, etc.).
*/ */
void musb_start(struct musb * pThis) void musb_start(struct musb *musb)
{ {
void __iomem *pBase = pThis->pRegs; void __iomem *regs = musb->pRegs;
u8 state; u8 devctl;
DBG(2, "<==\n"); DBG(2, "<==\n");
/* TODO: always set ISOUPDATE in POWER (periph mode) and leave it on! */ /* TODO: always set ISOUPDATE in POWER (periph mode) and leave it on! */
/* Set INT enable registers, enable interrupts */ /* Set INT enable registers, enable interrupts */
musb_writew(pBase, MGC_O_HDRC_INTRTXE, pThis->wEndMask); musb_writew(regs, MGC_O_HDRC_INTRTXE, musb->wEndMask);
musb_writew(pBase, MGC_O_HDRC_INTRRXE, pThis->wEndMask & 0xfffe); musb_writew(regs, MGC_O_HDRC_INTRRXE, musb->wEndMask & 0xfffe);
musb_writeb(pBase, MGC_O_HDRC_INTRUSBE, 0xf7); musb_writeb(regs, MGC_O_HDRC_INTRUSBE, 0xf7);
musb_writeb(pBase, MGC_O_HDRC_TESTMODE, 0); musb_writeb(regs, MGC_O_HDRC_TESTMODE, 0);
musb_platform_enable(pThis); musb_platform_enable(musb);
/* enable high-speed/low-power and start session */ /* enable high-speed/low-power and start session */
musb_writeb(pBase, MGC_O_HDRC_POWER, musb_writeb(regs, MGC_O_HDRC_POWER,
MGC_M_POWER_SOFTCONN | MGC_M_POWER_HSENAB); MGC_M_POWER_SOFTCONN | MGC_M_POWER_HSENAB);
switch (pThis->board_mode) { musb->is_active = 0;
switch (musb->board_mode) {
case MUSB_HOST: case MUSB_HOST:
musb_set_vbus(pThis, 1); musb_set_vbus(musb, 1);
break; break;
case MUSB_OTG: case MUSB_OTG:
WARN("how to start OTG session?\n"); /* session started after:
* (a) ID-grounded irq, host mode;
* (b) vbus present/connect IRQ, peripheral mode;
* (c) peripheral initiates, using SRP
*/
break; break;
case MUSB_PERIPHERAL: case MUSB_PERIPHERAL:
state = musb_readb(pBase, MGC_O_HDRC_DEVCTL); devctl = musb_readb(regs, MGC_O_HDRC_DEVCTL);
musb_writeb(pBase, MGC_O_HDRC_DEVCTL, musb_writeb(regs, MGC_O_HDRC_DEVCTL,
state & ~MGC_M_DEVCTL_SESSION); devctl & ~MGC_M_DEVCTL_SESSION);
break; break;
} }
} }
...@@ -958,7 +972,7 @@ static const struct fifo_cfg __devinitdata ep0_cfg = { ...@@ -958,7 +972,7 @@ static const struct fifo_cfg __devinitdata ep0_cfg = {
static int __devinit ep_config_from_table(struct musb *musb) static int __devinit ep_config_from_table(struct musb *musb)
{ {
const struct fifo_cfg *cfg; const struct fifo_cfg *cfg;
unsigned n; unsigned i, n;
int offset; int offset;
struct musb_hw_ep *hw_ep = musb->aLocalEnd; struct musb_hw_ep *hw_ep = musb->aLocalEnd;
...@@ -995,13 +1009,13 @@ static int __devinit ep_config_from_table(struct musb *musb) ...@@ -995,13 +1009,13 @@ static int __devinit ep_config_from_table(struct musb *musb)
offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0); offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
// assert(offset > 0) // assert(offset > 0)
while (n--) { for (i = 0; i < n; i++) {
u8 epn = cfg->hw_ep_num; u8 epn = cfg->hw_ep_num;
if (epn >= MUSB_C_NUM_EPS) { if (epn >= MUSB_C_NUM_EPS) {
pr_debug( "%s: invalid ep %d\n", pr_debug( "%s: invalid ep %d\n",
musb_driver_name, epn); musb_driver_name, epn);
return -EINVAL; continue;
} }
offset = fifo_setup(musb, hw_ep + epn, cfg++, offset); offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
if (offset < 0) { if (offset < 0) {
...@@ -1015,7 +1029,7 @@ static int __devinit ep_config_from_table(struct musb *musb) ...@@ -1015,7 +1029,7 @@ static int __devinit ep_config_from_table(struct musb *musb)
printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n", printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
musb_driver_name, musb_driver_name,
musb->bEndCount, MUSB_C_NUM_EPS * 2 - 1, n + 1, MUSB_C_NUM_EPS * 2 - 1,
offset, DYN_FIFO_SIZE); offset, DYN_FIFO_SIZE);
#ifdef CONFIG_USB_MUSB_HDRC_HCD #ifdef CONFIG_USB_MUSB_HDRC_HCD
...@@ -1346,11 +1360,14 @@ irqreturn_t musb_interrupt(struct musb *musb) ...@@ -1346,11 +1360,14 @@ irqreturn_t musb_interrupt(struct musb *musb)
// MGC_SelectEnd(musb->pRegs, ep_num); // MGC_SelectEnd(musb->pRegs, ep_num);
/* REVISIT just retval = ep->rx_irq(...) */ /* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
if (devctl & MGC_M_DEVCTL_HM) if (devctl & MGC_M_DEVCTL_HM) {
if (is_host_capable())
musb_host_rx(musb, ep_num); musb_host_rx(musb, ep_num);
else } else {
if (is_peripheral_capable())
musb_g_rx(musb, ep_num); musb_g_rx(musb, ep_num);
} }
}
reg >>= 1; reg >>= 1;
ep_num++; ep_num++;
...@@ -1364,11 +1381,14 @@ irqreturn_t musb_interrupt(struct musb *musb) ...@@ -1364,11 +1381,14 @@ irqreturn_t musb_interrupt(struct musb *musb)
// MGC_SelectEnd(musb->pRegs, ep_num); // MGC_SelectEnd(musb->pRegs, ep_num);
/* REVISIT just retval |= ep->tx_irq(...) */ /* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
if (devctl & MGC_M_DEVCTL_HM) if (devctl & MGC_M_DEVCTL_HM) {
if (is_host_capable())
musb_host_tx(musb, ep_num); musb_host_tx(musb, ep_num);
else } else {
if (is_peripheral_capable())
musb_g_tx(musb, ep_num); musb_g_tx(musb, ep_num);
} }
}
reg >>= 1; reg >>= 1;
ep_num++; ep_num++;
} }
...@@ -1406,18 +1426,24 @@ void musb_dma_completion(struct musb *musb, u8 bLocalEnd, u8 bTransmit) ...@@ -1406,18 +1426,24 @@ void musb_dma_completion(struct musb *musb, u8 bLocalEnd, u8 bTransmit)
} else { } else {
/* endpoints 1..15 */ /* endpoints 1..15 */
if (bTransmit) { if (bTransmit) {
if (devctl & MGC_M_DEVCTL_HM) if (devctl & MGC_M_DEVCTL_HM) {
if (is_host_capable())
musb_host_tx(musb, bLocalEnd); musb_host_tx(musb, bLocalEnd);
else } else {
if (is_peripheral_capable())
musb_g_tx(musb, bLocalEnd); musb_g_tx(musb, bLocalEnd);
}
} else { } else {
/* receive */ /* receive */
if (devctl & MGC_M_DEVCTL_HM) if (devctl & MGC_M_DEVCTL_HM) {
if (is_host_capable())
musb_host_rx(musb, bLocalEnd); musb_host_rx(musb, bLocalEnd);
else } else {
if (is_peripheral_capable())
musb_g_rx(musb, bLocalEnd); musb_g_rx(musb, bLocalEnd);
} }
} }
}
} }
#else #else
......
...@@ -171,7 +171,7 @@ static int tusb_set_power(struct otg_transceiver *x, unsigned mA) ...@@ -171,7 +171,7 @@ static int tusb_set_power(struct otg_transceiver *x, unsigned mA)
reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); reg &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
musb_writel(base, TUSB_PRCM_MNGMT, reg); musb_writel(base, TUSB_PRCM_MNGMT, reg);
DBG(3, "draw max %d mA VBUS\n", mA); DBG(2, "draw max %d mA VBUS\n", mA);
return 0; return 0;
} }
...@@ -209,20 +209,21 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) ...@@ -209,20 +209,21 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables)
wakeup_enables |= TUSB_PRCM_WNORCS; wakeup_enables |= TUSB_PRCM_WNORCS;
musb_writel(base, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables); musb_writel(base, TUSB_PRCM_WAKEUP_MASK, ~wakeup_enables);
// FIXME issue 4, when host (driving vbus), enable hipower comparator
/* REVISIT writeup of WLD implies that if WLD set and ID is grounded, /* REVISIT writeup of WLD implies that if WLD set and ID is grounded,
* TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared. * TUSB_PHY_OTG_CTRL.TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP must be cleared.
* Presumably that's mostly to save power, hence WLD is immaterial ... * Presumably that's mostly to save power, hence WLD is immaterial ...
*/ */
reg = musb_readl(base, TUSB_PRCM_MNGMT); reg = musb_readl(base, TUSB_PRCM_MNGMT);
/* issue 4: when driving vbus, leave hipower comparator active */ /* issue 4: when driving vbus, use hipower (vbus_det) comparator */
if (!is_host_active(musb)) if (is_host_active(musb)) {
reg |= TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
reg &= ~TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
} else {
reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN;
reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN; reg &= ~TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN;
reg |= TUSB_PRCM_MNGMT_OTG_SESS_END_EN }
| TUSB_PRCM_MNGMT_PM_IDLE reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE;
| TUSB_PRCM_MNGMT_DEV_IDLE;
musb_writel(base, TUSB_PRCM_MNGMT, reg); musb_writel(base, TUSB_PRCM_MNGMT, reg);
DBG(2, "idle, wake on %02x\n", wakeup_enables); DBG(2, "idle, wake on %02x\n", wakeup_enables);
...@@ -344,9 +345,11 @@ static void tusb_set_vbus(struct musb *musb, int is_on) ...@@ -344,9 +345,11 @@ static void tusb_set_vbus(struct musb *musb, int is_on)
if (musb->xceiv.default_a) { if (musb->xceiv.default_a) {
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL; musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
devctl &= ~MGC_M_DEVCTL_SESSION; devctl &= ~MGC_M_DEVCTL_SESSION;
} else } else {
musb->xceiv.state = OTG_STATE_B_IDLE;
musb->is_active = 0; musb->is_active = 0;
} }
}
prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN);
musb_writel(base, TUSB_PRCM_MNGMT, prcm); musb_writel(base, TUSB_PRCM_MNGMT, prcm);
...@@ -355,7 +358,7 @@ static void tusb_set_vbus(struct musb *musb, int is_on) ...@@ -355,7 +358,7 @@ static void tusb_set_vbus(struct musb *musb, int is_on)
musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl); musb_writeb(musb->pRegs, MGC_O_HDRC_DEVCTL, devctl);
DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n",
is_on ? "on" : "off", otg_state_string(musb),
musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL), musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL),
musb_readl(base, TUSB_DEV_OTG_STAT), musb_readl(base, TUSB_DEV_OTG_STAT),
conf, prcm); conf, prcm);
...@@ -371,14 +374,13 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) ...@@ -371,14 +374,13 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
int default_a; int default_a;
if (is_otg_enabled(musb)) if (is_otg_enabled(musb))
default_a = !!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS); default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
else else
default_a = is_host_enabled(musb); default_a = is_host_enabled(musb);
if (default_a != musb->xceiv.default_a) { DBG(2, "Default-%c\n", default_a ? 'A' : 'B');
musb->xceiv.default_a = default_a; musb->xceiv.default_a = default_a;
tusb_set_vbus(musb, default_a); tusb_set_vbus(musb, default_a);
} }
}
/* VBUS state change */ /* VBUS state change */
if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) { if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
...@@ -387,21 +389,16 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) ...@@ -387,21 +389,16 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
if ((is_otg_enabled(musb) && !musb->xceiv.default_a) if ((is_otg_enabled(musb) && !musb->xceiv.default_a)
|| !is_host_enabled(musb)) { || !is_host_enabled(musb)) {
/* REVISIT use the b_sess_valid comparator, not if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
* lowpower one; TUSB_DEV_OTG_STAT_SESS_VALID ? if (musb->xceiv.state != OTG_STATE_B_IDLE) {
*/ /* INTR_DISCONNECT can hide... */
if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_SENSE) {
musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
musb->is_active = 1;
/* REVISIT start the session? */
} else {
musb->xceiv.state = OTG_STATE_B_IDLE; musb->xceiv.state = OTG_STATE_B_IDLE;
musb->int_usb |= MGC_M_INTR_DISCONNECT;
}
musb->is_active = 0; musb->is_active = 0;
} }
DBG(1, "%s\n", musb->is_active DBG(2, "vbus change, %s, otg %03x\n",
? "b_peripheral" : "b_idle"); otg_state_string(musb), otg_stat);
schedule_work(&musb->irq_work); schedule_work(&musb->irq_work);
} else /* A-dev state machine */ { } else /* A-dev state machine */ {
...@@ -432,16 +429,15 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) ...@@ -432,16 +429,15 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
/* OTG timer expiration */ /* OTG timer expiration */
if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) {
u8 devctl;
DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat); DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat);
switch (musb->xceiv.state) { switch (musb->xceiv.state) {
case OTG_STATE_A_WAIT_VRISE: case OTG_STATE_A_WAIT_VRISE:
/* VBUS has probably been valid for a while now */ /* VBUS has probably been valid for a while now */
devctl = musb_readb(musb->pRegs, MGC_O_HDRC_DEVCTL);
if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) {
u8 devctl;
devctl = musb_readb(musb->pRegs,
MGC_O_HDRC_DEVCTL);
if ((devctl & MGC_M_DEVCTL_VBUS) if ((devctl & MGC_M_DEVCTL_VBUS)
!= MGC_M_DEVCTL_VBUS) { != MGC_M_DEVCTL_VBUS) {
DBG(2, "devctl %02x\n", devctl); DBG(2, "devctl %02x\n", devctl);
...@@ -463,7 +459,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base) ...@@ -463,7 +459,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
else else
musb_writel(base, TUSB_DEV_OTG_TIMER, 0); musb_writel(base, TUSB_DEV_OTG_TIMER, 0);
} else { } else {
ERR("vbus rise time too slow\n"); ERR("vbus too slow, devctl %02x\n", devctl);
tusb_set_vbus(musb, 0); tusb_set_vbus(musb, 0);
} }
break; break;
...@@ -487,17 +483,15 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r) ...@@ -487,17 +483,15 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r)
struct musb *musb = __hci; struct musb *musb = __hci;
void __iomem *base = musb->ctrl_base; void __iomem *base = musb->ctrl_base;
unsigned long flags; unsigned long flags;
u32 dma_src = 0, int_src; u32 int_src;
spin_lock_irqsave(&musb->Lock, flags); spin_lock_irqsave(&musb->Lock, flags);
int_src = musb_readl(base, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS; int_src = musb_readl(base, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS;
if (int_src & TUSB_INT_SRC_TXRX_DMA_DONE) DBG(3, "TUSB IRQ %08x\n", int_src);
dma_src = musb_readl(base, TUSB_DMA_INT_SRC);
DBG(3, "TUSB interrupt dma: %08x int: %08x\n", dma_src, int_src);
musb->int_regs = r; musb->int_regs = r;
musb->int_usb = (u8) int_src;
/* Acknowledge wake-up source interrupts */ /* Acknowledge wake-up source interrupts */
if (int_src & TUSB_INT_SRC_DEV_WAKEUP) { if (int_src & TUSB_INT_SRC_DEV_WAKEUP) {
...@@ -540,11 +534,13 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r) ...@@ -540,11 +534,13 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r)
/* TX dma callback must be handled here, RX dma callback is /* TX dma callback must be handled here, RX dma callback is
* handled in tusb_omap_dma_cb. * handled in tusb_omap_dma_cb.
*/ */
if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE) && dma_src) { if ((int_src & TUSB_INT_SRC_TXRX_DMA_DONE)) {
u32 dma_src = musb_readl(base, TUSB_DMA_INT_SRC);
u32 real_dma_src = musb_readl(base, TUSB_DMA_INT_MASK); u32 real_dma_src = musb_readl(base, TUSB_DMA_INT_MASK);
DBG(3, "DMA IRQ %08x\n", dma_src);
real_dma_src = ~real_dma_src & dma_src; real_dma_src = ~real_dma_src & dma_src;
if (tusb_dma_omap()) { if (tusb_dma_omap() && real_dma_src) {
int tx_source = (real_dma_src & 0xffff); int tx_source = (real_dma_src & 0xffff);
int i; int i;
...@@ -568,10 +564,8 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r) ...@@ -568,10 +564,8 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci, struct pt_regs *r)
} else } else
musb->int_rx = musb->int_tx = 0; musb->int_rx = musb->int_tx = 0;
if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff)) { if (int_src & (TUSB_INT_SRC_USB_IP_TX | TUSB_INT_SRC_USB_IP_RX | 0xff))
musb->int_usb = (u8) int_src;
musb_interrupt(musb); musb_interrupt(musb);
}
/* Acknowledge TUSB interrupts. Clear only non-reserved bits */ /* Acknowledge TUSB interrupts. Clear only non-reserved bits */
musb_writel(base, TUSB_INT_SRC_CLEAR, musb_writel(base, TUSB_INT_SRC_CLEAR,
...@@ -619,6 +613,9 @@ void musb_platform_enable(struct musb * musb) ...@@ -619,6 +613,9 @@ void musb_platform_enable(struct musb * musb)
set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW); set_irq_type(musb->nIrq, IRQ_TYPE_LEVEL_LOW);
/* kickstart: force into the correct OTG state machine */
musb_writel(base, TUSB_INT_SRC_SET, TUSB_INT_SRC_ID_STATUS_CHNG);
if (is_dma_capable() && dma_off) if (is_dma_capable() && dma_off)
printk(KERN_WARNING "%s %s: dma not reactivated\n", printk(KERN_WARNING "%s %s: dma not reactivated\n",
__FILE__, __FUNCTION__); __FILE__, __FUNCTION__);
......
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) #define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) #define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) #define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) #define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) #define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) #define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
......
...@@ -51,6 +51,9 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend) ...@@ -51,6 +51,9 @@ static void musb_port_suspend(struct musb *musb, u8 bSuspend)
u8 power; u8 power;
void __iomem *pBase = musb->pRegs; void __iomem *pBase = musb->pRegs;
if (!is_host_active(musb))
return;
power = musb_readb(pBase, MGC_O_HDRC_POWER); power = musb_readb(pBase, MGC_O_HDRC_POWER);
if (bSuspend) { if (bSuspend) {
...@@ -181,12 +184,11 @@ int musb_hub_control( ...@@ -181,12 +184,11 @@ int musb_hub_control(
int retval = 0; int retval = 0;
unsigned long flags; unsigned long flags;
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
|| !is_host_active(musb)))
return -ESHUTDOWN; return -ESHUTDOWN;
/* hub features: always zero, setting is a NOP /* hub features: always zero, setting is a NOP
* port features: reported, sometimes updated * port features: reported, sometimes updated when host is active
* no indicators * no indicators
*/ */
spin_lock_irqsave(&musb->Lock, flags); spin_lock_irqsave(&musb->Lock, flags);
...@@ -279,6 +281,7 @@ int musb_hub_control( ...@@ -279,6 +281,7 @@ int musb_hub_control(
* initialization logic, e.g. for OTG, or change any * initialization logic, e.g. for OTG, or change any
* logic relating to VBUS power-up. * logic relating to VBUS power-up.
*/ */
if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
musb_start(musb); musb_start(musb);
break; break;
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
...@@ -288,6 +291,9 @@ int musb_hub_control( ...@@ -288,6 +291,9 @@ int musb_hub_control(
musb_port_suspend(musb, TRUE); musb_port_suspend(musb, TRUE);
break; break;
case USB_PORT_FEAT_TEST: case USB_PORT_FEAT_TEST:
if (unlikely(is_host_active(musb)))
goto error;
wIndex >>= 8; wIndex >>= 8;
switch (wIndex) { switch (wIndex) {
case 1: case 1:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment