MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MUSB_DRIVER_NAME);
+
+
+inline void mbusywait(int ms)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(ms);
+ while(time_before(jiffies,end_time))
+ cpu_relax();
+
+}
+
+void musb_force_term(void __iomem *addr, enum musb_term term)
+{
+ u8 r;
+
+
+ r = musb_ulpi_readb(addr, ISP1704_OTG_CTRL);
+ r |= ISP1704_OTG_CTRL_DP_PULLDOWN | ISP1704_OTG_CTRL_DM_PULLDOWN;
+ musb_ulpi_writeb(addr, ISP1704_OTG_CTRL, r);
+
+ r = musb_ulpi_readb(addr, ISP1704_FUNC_CTRL);
+
+ switch(term) {
+
+ case MUSB_TERM_HOST_HIGHSPEED:
+ r &= ~ISP1704_FUNC_CTRL_XCVRSELECT;
+ r &= ~ISP1704_FUNC_CTRL_TERMSELECT;
+ r &= ~ISP1704_FUNC_CTRL_OPMODE;
+ break;
+
+ case MUSB_TERM_HOST_FULLSPEED:
+ r |= 1 << ISP1704_FUNC_CTRL_XCVRSELECT_SHIFT;
+ r |= ISP1704_FUNC_CTRL_TERMSELECT;
+ r &= ~ISP1704_FUNC_CTRL_OPMODE;
+ break;
+
+ case MUSB_TERM_HOST_LOWSPEED:
+ r |= 2 << ISP1704_FUNC_CTRL_XCVRSELECT_SHIFT;
+ r |= ISP1704_FUNC_CTRL_TERMSELECT;
+ r &= ~ISP1704_FUNC_CTRL_OPMODE;
+ break;
+
+ default:
+ ERR("Unknown musb termination\n");
+ return;
+ }
+
+ r |= ISP1704_OTG_CTRL_IDPULLUP;
+ musb_ulpi_writeb(addr, ISP1704_FUNC_CTRL, r);
+
+}
+
+
+
static inline int musb_verify_charger(void __iomem *addr)
{
u8 r, ret = 0;
u8 vdat = 0;
u8 r;
+ u8 testmode;
+ testmode = musb_readb(musb->mregs,MUSB_TESTMODE);
msleep(5);
break;
}
- if (vdat) {
+ if (vdat && !(testmode & MUSB_TEST_FORCE_HOST)) {
/* REVISIT: This code works only with dedicated chargers!
* When support for HOST/HUB chargers is added, don't
* forget this.
prefetch((u8 *)src);
- DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
+ DBG(6, "%cX ep%d fifo %p count %d buf %p\n",
'T', hw_ep->epnum, fifo, len, src);
/* we can't assume unaligned reads work */
{
void __iomem *fifo = hw_ep->fifo;
- DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
+ DBG(6, "%cX ep%d fifo %p count %d buf %p\n",
'R', hw_ep->epnum, fifo, len, dst);
/* we can't assume unaligned writes work */
{
struct musb *musb = (struct musb *)data;
unsigned long flags;
-
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->state) {
case OTG_STATE_B_WAIT_ACON:
static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
u8 devctl, u8 power)
{
+ u8 testmode;
irqreturn_t handled = IRQ_NONE;
void __iomem *mbase = musb->mregs;
u8 r;
+ testmode = musb_readb(mbase,MUSB_TESTMODE);
+ if(testmode & MUSB_TEST_FORCE_HOST) {
+ if(int_usb & MUSB_INTR_SESSREQ) {
+ DBG(1,"Piggybacking CONNECT on SESS REQ\n");
+ musb->int_usb |= MUSB_INTR_CONNECT;
+ }
+ }
+
DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
int_usb);
} else {
switch (musb->xceiv->state) {
#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_HOST:
case OTG_STATE_A_SUSPEND:
/* possibly DISCONNECT is upcoming */
musb->xceiv->state = OTG_STATE_A_HOST;
* be discarded silently.
*/
if ((devctl & MUSB_DEVCTL_VBUS)
- && !(devctl & MUSB_DEVCTL_BDEVICE)) {
+ && host_mode(musb->mregs)) {
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
musb->ep0_stage = MUSB_EP0_START;
musb->xceiv->state = OTG_STATE_A_IDLE;
+ msecs_to_jiffies(musb->a_wait_bcon));
break;
case OTG_STATE_A_HOST:
+ if(testmode & MUSB_TEST_FORCE_HOST) {
+ // musb->int_usb |= MUSB_INTR_RESUME;
+ break;
+ }
+
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 */
musb->is_active = 1;
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
musb->ep0_stage = MUSB_EP0_START;
#ifdef CONFIG_USB_MUSB_OTG
musb->port1_status |= USB_PORT_STAT_CONNECTION
|(USB_PORT_STAT_C_CONNECTION << 16);
- /* high vs full speed is just a guess until after reset */
- if (devctl & MUSB_DEVCTL_LSDEV)
- musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+ if (testmode & MUSB_TEST_FORCE_HOST) {
+ u8 r,reg;
+ void __iomem *mbase = musb->mregs;
+
+ musb_force_term(musb->mregs,MUSB_TERM_HOST_HIGHSPEED);
+
+ r = musb_ulpi_readb(mbase, ISP1704_DEBUG);
+ DBG(1,"Linestate %x\n",r);
+ switch(r) {
+ case 2:
+ musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+ reg = musb_readb(mbase, MUSB_TESTMODE);
+ reg &= ~MUSB_TEST_FORCE_FS;
+ reg &= ~MUSB_TEST_FORCE_HS;
+ musb_writeb(mbase, MUSB_TESTMODE, reg);
+
+ reg = musb_readb(mbase, MUSB_POWER);
+ reg &= ~MUSB_POWER_HSENAB;
+ musb_writeb(mbase, MUSB_POWER, reg);
+
+ musb_force_term(musb->mregs,MUSB_TERM_HOST_LOWSPEED);
+ break;
+ case 1:
+ /*High or full speed*/
+ reg = musb_readb(mbase, MUSB_TESTMODE);
+ if(reg & MUSB_TEST_FORCE_HS) {
+ /*High speed*/
+ reg &= ~MUSB_TEST_FORCE_FS;
+ musb_writeb(mbase, MUSB_TESTMODE, reg);
+
+ reg = musb_readb(mbase, MUSB_POWER);
+ reg |= MUSB_POWER_HSENAB;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ } else {
+ /*Full speed*/
+ reg |= MUSB_TEST_FORCE_FS;
+ musb_writeb(mbase, MUSB_TESTMODE, reg);
+
+ reg = musb_readb(mbase, MUSB_POWER);
+ reg &= ~MUSB_POWER_HSENAB;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ }
+
+ musb_force_term(mbase,MUSB_TERM_HOST_FULLSPEED);
+
+ break;
+ case 0:
+ case 3:
+ /*invalid*/
+ WARNING("Invalid line state of %d\n",r);
+ break;
+
+ }
+ } else {
+
+ /* high vs full speed is just a guess until after reset */
+ if (devctl & MUSB_DEVCTL_LSDEV)
+ musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+ }
if (hcd->status_urb)
usb_hcd_poll_rh_status(hcd);
musb->ignore_disconnect = 1;
musb_g_reset(musb);
/* FALLTHROUGH */
+ case OTG_STATE_A_HOST:
+ musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
DBG(1, "HNP: Setting timer as %s\n",
otg_state_string(musb));
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
+ (!host_mode(musb->mregs)
? 'B' : 'A'));
} else /* peripheral is enabled */ {