static void musb_port_reset(struct musb *musb, bool do_reset) { u8 power; void __iomem *mbase = musb->mregs; #ifdef CONFIG_USB_MUSB_OTG if (musb->xceiv.state == OTG_STATE_B_IDLE) { DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n"); musb->port1_status &= ~USB_PORT_STAT_RESET; return; } #endif if (!is_host_active(musb)) return; /* NOTE: caller guarantees it will turn off the reset when * the appropriate amount of time has passed */ power = musb_readb(mbase, MUSB_POWER); if (do_reset) { /* * If RESUME is set, we must make sure it stays minimum 20 ms. * Then we must clear RESUME and wait a bit to let musb start * generating SOFs. If we don't do this, OPT HS A 6.8 tests * fail with "Error! Did not receive an SOF before suspend * detected". */ if (power & MUSB_POWER_RESUME) { while (time_before(jiffies, musb->rh_timer)) msleep(1); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESUME); msleep(1); } musb->ignore_disconnect = true; power &= 0xf0; musb_writeb(mbase, MUSB_POWER, power | MUSB_POWER_RESET); musb->port1_status |= USB_PORT_STAT_RESET; musb->port1_status &= ~USB_PORT_STAT_ENABLE; musb->rh_timer = jiffies + msecs_to_jiffies(50); } else { DBG(4, "root port reset stopped\n"); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); musb->ignore_disconnect = false; power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { DBG(4, "high-speed device connected\n"); musb->port1_status |= USB_PORT_STAT_HIGH_SPEED; } musb->port1_status &= ~USB_PORT_STAT_RESET; musb->port1_status |= USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET << 16) | (USB_PORT_STAT_C_ENABLE << 16); usb_hcd_poll_rh_status(musb_to_hcd(musb)); musb->vbuserr_retry = VBUSERR_RETRY_COUNT; } }
int musb_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct musb *musb = hcd_to_musb(hcd); u32 temp; int retval = 0; unsigned long flags; spin_lock_irqsave(&musb->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } /* hub features: always zero, setting is a NOP * port features: reported, sometimes updated when host is active * no indicators */ switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, false); break; case USB_PORT_FEAT_POWER: if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) musb_set_vbus(musb, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_SUSPEND: break; default: goto error; } DBG(5, "clear feature %d\n", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; desc->wHubCharacteristics = __constant_cpu_to_le16( 0x0001 /* per-port power switching */ | 0x0010 /* no overcurrent reporting */ ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; /* workaround bogus struct definition */ desc->DeviceRemovable[0] = 0x02; /* port 1 */ desc->DeviceRemovable[1] = 0xff; } break; case GetHubStatus: temp = 0; *(__le32 *) buf = cpu_to_le32(temp); break; case GetPortStatus: if (wIndex != 1) goto error; /* finish RESET signaling? */ if ((musb->port1_status & USB_PORT_STAT_RESET) && time_after_eq(jiffies, musb->rh_timer)) musb_port_reset(musb, false); /* finish RESUME signaling? */ if ((musb->port1_status & MUSB_PORT_STAT_RESUME) && time_after_eq(jiffies, musb->rh_timer)) { u8 power; power = musb_readb(musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; DBG(4, "root port resume stopped, power %02x\n", power); musb_writeb(musb->mregs, MUSB_POWER, power); /* ISSUE: DaVinci (RTL 1.300) disconnects after * resume of high speed peripherals (but not full * speed ones). */ musb->is_active = 1; musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME); musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; usb_hcd_poll_rh_status(musb_to_hcd(musb)); /* NOTE: it might really be A_WAIT_BCON ... */ musb->xceiv.state = OTG_STATE_A_HOST; } put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); /* port change status is more interesting */ DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n", musb->port1_status); break; case SetPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_POWER: /* NOTE: this controller has a strange state machine * that involves "requesting sessions" according to * magic side effects from incompletely-described * rules about startup... * * This call is what really starts the host mode; be * very careful about side effects if you reorder any * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) musb_start(musb); break; case USB_PORT_FEAT_RESET: musb_port_reset(musb, true); break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, true); break; case USB_PORT_FEAT_TEST: if (unlikely(is_host_active(musb))) goto error; wIndex >>= 8; switch (wIndex) { case 1: pr_debug("TEST_J\n"); temp = MUSB_TEST_J; break; case 2: pr_debug("TEST_K\n"); temp = MUSB_TEST_K; break; case 3: pr_debug("TEST_SE0_NAK\n"); temp = MUSB_TEST_SE0_NAK; break; case 4: pr_debug("TEST_PACKET\n"); temp = MUSB_TEST_PACKET; musb_load_testpacket(musb); break; case 5: pr_debug("TEST_FORCE_ENABLE\n"); temp = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; musb_writeb(musb->mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); break; case 6: pr_debug("TEST_FIFO_ACCESS\n"); temp = MUSB_TEST_FIFO_ACCESS; break; default: goto error; } musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; default: goto error; } DBG(5, "set feature %d\n", wValue); musb->port1_status |= 1 << wValue; break; default: error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&musb->lock, flags); return retval; }
/* ******************************************************************************* * sw_hcd_port_reset * * Description: * reset USB port * * Parameters: * sw_hcd : input. USB¿ØÖÆÆ÷ * do_reset : input. flag. is reset USB port or not? * * Return value: * void * * note: * void * ******************************************************************************* */ static void sw_hcd_port_reset(struct sw_hcd *sw_hcd, bool do_reset) { u8 power = 0; void __iomem *usbc_base = sw_hcd->mregs; if (!is_host_active(sw_hcd)){ DMSG_PANIC("ERR: usb host is not active\n"); return; } /* NOTE: caller guarantees it will turn off the reset when * the appropriate amount of time has passed */ power = USBC_Readb(USBC_REG_PCTL(usbc_base)); if (do_reset) { DMSG_INFO("[sw_hcd]: reset port. \n"); /* * If RESUME is set, we must make sure it stays minimum 20 ms. * Then we must clear RESUME and wait a bit to let sw_hcd start * generating SOFs. If we don't do this, OPT HS A 6.8 tests * fail with "Error! Did not receive an SOF before suspend * detected". */ if (power & (1 << USBC_BP_POWER_H_RESUME)) { while (time_before(jiffies, sw_hcd->rh_timer)){ msleep(1); } power &= ~(1 << USBC_BP_POWER_H_RESUME); USBC_Writeb(power, USBC_REG_PCTL(usbc_base)); msleep(1); } sw_hcd->ignore_disconnect = true; power &= 0xf0; power |= (1 << USBC_BP_POWER_H_RESET); USBC_Writeb(power, USBC_REG_PCTL(usbc_base)); sw_hcd->port1_status |= USB_PORT_STAT_RESET; sw_hcd->port1_status &= ~USB_PORT_STAT_ENABLE; sw_hcd->rh_timer = jiffies + msecs_to_jiffies(50); USBC_Host_SetFunctionAddress_Deafult(sw_hcd->sw_hcd_io->usb_bsp_hdle, USBC_EP_TYPE_TX, 0); //set address ep0 { __u32 i = 1; __u8 old_ep_index = 0; old_ep_index = USBC_GetActiveEp(sw_hcd->sw_hcd_io->usb_bsp_hdle); USBC_SelectActiveEp(sw_hcd->sw_hcd_io->usb_bsp_hdle, 0); USBC_Host_SetFunctionAddress_Deafult(sw_hcd->sw_hcd_io->usb_bsp_hdle, USBC_EP_TYPE_TX, 0); for( i = 1 ; i <= 5; i++){ USBC_SelectActiveEp(sw_hcd->sw_hcd_io->usb_bsp_hdle, i); USBC_Host_SetFunctionAddress_Deafult(sw_hcd->sw_hcd_io->usb_bsp_hdle, USBC_EP_TYPE_TX, i); USBC_Host_SetFunctionAddress_Deafult(sw_hcd->sw_hcd_io->usb_bsp_hdle, USBC_EP_TYPE_RX, i); } USBC_SelectActiveEp(sw_hcd->sw_hcd_io->usb_bsp_hdle, old_ep_index); } }else{ DMSG_INFO("[sw_hcd]: reset port stopped.\n"); UsbPhyEndReset(0); power &= ~(1 << USBC_BP_POWER_H_RESET); USBC_Writeb(power, USBC_REG_PCTL(usbc_base)); sw_hcd->ignore_disconnect = false; power = USBC_Readb(USBC_REG_PCTL(usbc_base)); if(power & (1 << USBC_BP_POWER_H_HIGH_SPEED_FLAG)){ DMSG_DBG_HCD("high-speed device connected\n"); sw_hcd->port1_status |= USB_PORT_STAT_HIGH_SPEED; } sw_hcd->port1_status &= ~USB_PORT_STAT_RESET; sw_hcd->port1_status |= USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET << 16) | (USB_PORT_STAT_C_ENABLE << 16); usb_hcd_poll_rh_status(sw_hcd_to_hcd(sw_hcd)); sw_hcd->vbuserr_retry = VBUSERR_RETRY_COUNT; } return ; }
/* ******************************************************************************* * sw_hcd_hub_control * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ int sw_hcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct sw_hcd *sw_hcd = hcd_to_sw_hcd(hcd); u32 temp = 0; int retval = 0; unsigned long flags = 0; void __iomem *usbc_base = sw_hcd->mregs; if(hcd == NULL){ DMSG_PANIC("ERR: invalid argment\n"); return -ESHUTDOWN; } spin_lock_irqsave(&sw_hcd->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { spin_unlock_irqrestore(&sw_hcd->lock, flags); return -ESHUTDOWN; } DMSG_DBG_HCD("sw_hcd_hub_control: typeReq = %x, wValue = 0x%x, wIndex = 0x%x\n", typeReq, wValue, wIndex); /* hub features: always zero, setting is a NOP * port features: reported, sometimes updated when host is active * no indicators */ switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if ((wIndex & 0xff) != 1){ goto error; } switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: sw_hcd_port_suspend(sw_hcd, false); break; case USB_PORT_FEAT_POWER: /* fixme */ sw_hcd_set_vbus(sw_hcd, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_SUSPEND: break; default: goto error; } DMSG_DBG_HCD("DBG: clear feature %d\n", wValue); sw_hcd->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; desc->wHubCharacteristics = cpu_to_le16( 0x0001 /* per-port power switching */ | 0x0010 /* no overcurrent reporting */ ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; /* workaround bogus struct definition */ desc->u.hs.DeviceRemovable[0] = 0x02; /* port 1 */ desc->u.hs.DeviceRemovable[1] = 0xff; } break; case GetHubStatus: temp = 0; *(__le32 *) buf = cpu_to_le32(temp); break; case GetPortStatus: { if (wIndex != 1){ DMSG_PANIC("ERR: GetPortStatus parameter wIndex is not 1.\n"); goto error; } /* finish RESET signaling? */ if ((sw_hcd->port1_status & USB_PORT_STAT_RESET) && time_after_eq(jiffies, sw_hcd->rh_timer)){ sw_hcd_port_reset(sw_hcd, false); } /* finish RESUME signaling? */ if ((sw_hcd->port1_status & SW_HCD_PORT_STAT_RESUME) && time_after_eq(jiffies, sw_hcd->rh_timer)) { u8 power = 0; power = USBC_Readb(USBC_REG_PCTL(usbc_base)); power &= ~(1 << USBC_BP_POWER_H_RESUME); USBC_Writeb(power, USBC_REG_PCTL(usbc_base)); DMSG_DBG_HCD("DBG: root port resume stopped, power %02x\n", power); /* ISSUE: DaVinci (RTL 1.300) disconnects after * resume of high speed peripherals (but not full * speed ones). */ sw_hcd->is_active = 1; sw_hcd->port1_status &= ~(USB_PORT_STAT_SUSPEND | SW_HCD_PORT_STAT_RESUME); sw_hcd->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; usb_hcd_poll_rh_status(sw_hcd_to_hcd(sw_hcd)); } put_unaligned(cpu_to_le32(sw_hcd->port1_status & ~SW_HCD_PORT_STAT_RESUME), (__le32 *) buf); /* port change status is more interesting */ DMSG_DBG_HCD("DBG: port status %08x\n", sw_hcd->port1_status); } break; case SetPortFeature: { if ((wIndex & 0xff) != 1){ goto error; } switch (wValue) { case USB_PORT_FEAT_POWER: /* NOTE: this controller has a strange state machine * that involves "requesting sessions" according to * magic side effects from incompletely-described * rules about startup... * * This call is what really starts the host mode; be * very careful about side effects if you reorder any * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ sw_hcd_start(sw_hcd); break; case USB_PORT_FEAT_RESET: sw_hcd_port_reset(sw_hcd, true); break; case USB_PORT_FEAT_SUSPEND: sw_hcd_port_suspend(sw_hcd, true); break; case USB_PORT_FEAT_TEST: { if (unlikely(is_host_active(sw_hcd))){ DMSG_PANIC("ERR: usb host is not active\n"); goto error; } wIndex >>= 8; switch (wIndex) { case 1: DMSG_DBG_HCD("TEST_J\n"); temp = 1 << USBC_BP_TMCTL_TEST_J; break; case 2: DMSG_DBG_HCD("TEST_K\n"); temp = 1 << USBC_BP_TMCTL_TEST_K; break; case 3: DMSG_DBG_HCD("TEST_SE0_NAK\n"); temp = 1 << USBC_BP_TMCTL_TEST_SE0_NAK; break; case 4: DMSG_DBG_HCD("TEST_PACKET\n"); temp = 1 << USBC_BP_TMCTL_TEST_PACKET; sw_hcd_load_testpacket(sw_hcd); break; case 5: DMSG_DBG_HCD("TEST_FORCE_ENABLE\n"); temp = (1 << USBC_BP_TMCTL_FORCE_HOST) | (1 << USBC_BP_TMCTL_FORCE_HS); USBC_REG_set_bit_b(USBC_BP_DEVCTL_SESSION, USBC_REG_DEVCTL(usbc_base)); break; case 6: DMSG_DBG_HCD("TEST_FIFO_ACCESS\n"); temp = 1 << USBC_BP_TMCTL_FIFO_ACCESS; break; default: DMSG_PANIC("ERR: unkown SetPortFeature USB_PORT_FEAT_TEST wIndex(%d)\n", wIndex); goto error; } USBC_Writeb(temp, USBC_REG_TMCTL(usbc_base)); } break; default:{ DMSG_PANIC("ERR: unkown SetPortFeature wValue(%d)\n", wValue); goto error; } } DMSG_DBG_HCD("DBG: set feature %d\n", wValue); sw_hcd->port1_status |= 1 << wValue; } break; default: error: DMSG_PANIC("ERR: protocol stall on error\n"); /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&sw_hcd->lock, flags); return retval; }
int musb_port_suspend(struct musb *musb, bool do_suspend) { struct usb_otg *otg = musb->xceiv->otg; u8 power; void __iomem *mbase = musb->mregs; if (!is_host_active(musb)) return 0; /* NOTE: this doesn't necessarily put PHY into low power mode, * turning off its clock; that's a function of PHY integration and * MUSB_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect * SE0 changing to connect (J) or wakeup (K) states. */ power = musb_readb(mbase, MUSB_POWER); if (do_suspend) { int retries = 10000; if (power & MUSB_POWER_RESUME) return -EBUSY; if (!(power & MUSB_POWER_SUSPENDM)) { power |= MUSB_POWER_SUSPENDM; musb_writeb(mbase, MUSB_POWER, power); /* Needed for OPT A tests */ power = musb_readb(mbase, MUSB_POWER); while (power & MUSB_POWER_SUSPENDM) { power = musb_readb(mbase, MUSB_POWER); if (retries-- < 1) break; } } musb_dbg(musb, "Root port suspended, power %02x", power); musb->port1_status |= USB_PORT_STAT_SUSPEND; switch (musb->xceiv->otg->state) { case OTG_STATE_A_HOST: musb->xceiv->otg->state = OTG_STATE_A_SUSPEND; musb->is_active = otg->host->b_hnp_enable; if (musb->is_active) mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies( OTG_TIME_A_AIDL_BDIS)); musb_platform_try_idle(musb, 0); break; case OTG_STATE_B_HOST: musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON; musb->is_active = otg->host->b_hnp_enable; musb_platform_try_idle(musb, 0); break; default: musb_dbg(musb, "bogus rh suspend? %s", usb_otg_state_string(musb->xceiv->otg->state)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; power |= MUSB_POWER_RESUME; musb_writeb(mbase, MUSB_POWER, power); musb_dbg(musb, "Root port resuming, power %02x", power); musb->port1_status |= MUSB_PORT_STAT_RESUME; schedule_delayed_work(&musb->finish_resume_work, msecs_to_jiffies(USB_RESUME_TIMEOUT)); } return 0; }
int musb_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct musb *musb = hcd_to_musb(hcd); u32 temp; int retval = 0; unsigned long flags; bool start_musb = false; spin_lock_irqsave(&musb->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } /* hub features: always zero, setting is a NOP * port features: reported, sometimes updated when host is active * no indicators */ switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, false); break; case USB_PORT_FEAT_POWER: if (!hcd->self.is_b_host) musb_platform_set_vbus(musb, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_SUSPEND: break; default: goto error; } musb_dbg(musb, "clear feature %d", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = USB_DT_HUB; desc->bNbrPorts = 1; desc->wHubCharacteristics = cpu_to_le16( HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */ | HUB_CHAR_NO_OCPM /* no overcurrent reporting */ ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; /* workaround bogus struct definition */ desc->u.hs.DeviceRemovable[0] = 0x02; /* port 1 */ desc->u.hs.DeviceRemovable[1] = 0xff; } break; case GetHubStatus: temp = 0; *(__le32 *) buf = cpu_to_le32(temp); break; case GetPortStatus: if (wIndex != 1) goto error; put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); /* port change status is more interesting */ musb_dbg(musb, "port status %08x", musb->port1_status); break; case SetPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_POWER: /* NOTE: this controller has a strange state machine * that involves "requesting sessions" according to * magic side effects from incompletely-described * rules about startup... * * This call is what really starts the host mode; be * very careful about side effects if you reorder any * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ if (!hcd->self.is_b_host && musb_has_gadget(musb)) start_musb = true; break; case USB_PORT_FEAT_RESET: musb_port_reset(musb, true); break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, true); break; case USB_PORT_FEAT_TEST: if (unlikely(is_host_active(musb))) goto error; wIndex >>= 8; switch (wIndex) { case 1: pr_debug("TEST_J\n"); temp = MUSB_TEST_J; break; case 2: pr_debug("TEST_K\n"); temp = MUSB_TEST_K; break; case 3: pr_debug("TEST_SE0_NAK\n"); temp = MUSB_TEST_SE0_NAK; break; case 4: pr_debug("TEST_PACKET\n"); temp = MUSB_TEST_PACKET; musb_load_testpacket(musb); break; case 5: pr_debug("TEST_FORCE_ENABLE\n"); temp = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; musb_writeb(musb->mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); break; case 6: pr_debug("TEST_FIFO_ACCESS\n"); temp = MUSB_TEST_FIFO_ACCESS; break; default: goto error; } musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; default: goto error; } musb_dbg(musb, "set feature %d", wValue); musb->port1_status |= 1 << wValue; break; default: error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&musb->lock, flags); if (start_musb) musb_start(musb); return retval; }
void musb_port_reset(struct musb *musb, bool do_reset) { u8 power; void __iomem *mbase = musb->mregs; if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) { musb_dbg(musb, "HNP: Returning from HNP; no hub reset from b_idle"); musb->port1_status &= ~USB_PORT_STAT_RESET; return; } if (!is_host_active(musb)) return; /* NOTE: caller guarantees it will turn off the reset when * the appropriate amount of time has passed */ power = musb_readb(mbase, MUSB_POWER); if (do_reset) { /* * If RESUME is set, we must make sure it stays minimum 20 ms. * Then we must clear RESUME and wait a bit to let musb start * generating SOFs. If we don't do this, OPT HS A 6.8 tests * fail with "Error! Did not receive an SOF before suspend * detected". */ if (power & MUSB_POWER_RESUME) { long remain = (unsigned long) musb->rh_timer - jiffies; if (musb->rh_timer > 0 && remain > 0) { /* take into account the minimum delay after resume */ schedule_delayed_work( &musb->deassert_reset_work, remain); return; } musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESUME); /* Give the core 1 ms to clear MUSB_POWER_RESUME */ schedule_delayed_work(&musb->deassert_reset_work, msecs_to_jiffies(1)); return; } power &= 0xf0; musb_writeb(mbase, MUSB_POWER, power | MUSB_POWER_RESET); musb->port1_status |= USB_PORT_STAT_RESET; musb->port1_status &= ~USB_PORT_STAT_ENABLE; schedule_delayed_work(&musb->deassert_reset_work, msecs_to_jiffies(50)); } else { musb_dbg(musb, "root port reset stopped"); musb_platform_pre_root_reset_end(musb); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); musb_platform_post_root_reset_end(musb); power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { musb_dbg(musb, "high-speed device connected"); musb->port1_status |= USB_PORT_STAT_HIGH_SPEED; } musb->port1_status &= ~USB_PORT_STAT_RESET; musb->port1_status |= USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET << 16) | (USB_PORT_STAT_C_ENABLE << 16); usb_hcd_poll_rh_status(musb->hcd); musb->vbuserr_retry = VBUSERR_RETRY_COUNT; } }
static irqreturn_t dsps_interrupt(int irq, void *hci) { struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; unsigned long flags; irqreturn_t ret = IRQ_NONE; u32 epintr, usbintr; spin_lock_irqsave(&musb->lock, flags); /* Get endpoint interrupts */ epintr = dsps_readl(reg_base, wrp->epintr_status); musb->int_rx = (epintr & wrp->rxep_bitmap) >> wrp->rxep_shift; musb->int_tx = (epintr & wrp->txep_bitmap) >> wrp->txep_shift; if (epintr) dsps_writel(reg_base, wrp->epintr_status, epintr); /* Get usb core interrupts */ usbintr = dsps_readl(reg_base, wrp->coreintr_status); if (!usbintr && !epintr) goto out; musb->int_usb = (usbintr & wrp->usb_bitmap) >> wrp->usb_shift; if (usbintr) dsps_writel(reg_base, wrp->coreintr_status, usbintr); 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); void __iomem *mregs = musb->mregs; u8 devctl = dsps_readb(mregs, MUSB_DEVCTL); int err; err = musb->int_usb & MUSB_INTR_VBUSERROR; if (err) { /* * The Mentor core doesn't debounce VBUS as needed * to cope with device connect current spikes. This * means it's not uncommon for bus-powered devices * to get VBUS errors during enumeration. * * This is a workaround, but newer RTL from Mentor * seems to allow a better one: "re"-starting sessions * without waiting for VBUS to stop registering in * devctl. */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; del_timer(&glue->timer); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); musb->xceiv->otg->default_a = 0; musb->xceiv->state = OTG_STATE_B_IDLE; } /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; } if (musb->int_tx || musb->int_rx || musb->int_usb) ret |= musb_interrupt(musb); /* Poll for ID change in OTG port mode */ if (musb->xceiv->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); out: spin_unlock_irqrestore(&musb->lock, flags); return ret; }
static void musb_port_suspend(struct musb *musb, bool do_suspend) { u8 power; void __iomem *mbase = musb->mregs; if (!is_host_active(musb)) return; /* NOTE: this doesn't necessarily put PHY into low power mode, * turning off its clock; that's a function of PHY integration and * MUSB_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect * SE0 changing to connect (J) or wakeup (K) states. */ power = musb_readb(mbase, MUSB_POWER); if (do_suspend) { int retries = 10000; power &= ~MUSB_POWER_RESUME; power |= MUSB_POWER_SUSPENDM; musb_writeb(mbase, MUSB_POWER, power); /* Needed for OPT A tests */ power = musb_readb(mbase, MUSB_POWER); while (power & MUSB_POWER_SUSPENDM) { power = musb_readb(mbase, MUSB_POWER); if (retries-- < 1) break; } DBG(3, "Root port suspended, power %02x\n", power); musb->port1_status |= USB_PORT_STAT_SUSPEND; switch (musb->xceiv->state) { 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; if (musb->is_active) mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies( OTG_TIME_A_AIDL_BDIS)); musb_platform_try_idle(musb, 0); break; #ifdef CONFIG_USB_MUSB_OTG case OTG_STATE_B_HOST: musb->xceiv->state = OTG_STATE_B_WAIT_ACON; musb->is_active = is_otg_enabled(musb) && musb->xceiv->host->b_hnp_enable; musb_platform_try_idle(musb, 0); break; #endif default: DBG(1, "bogus rh suspend? %s\n", otg_state_string(musb)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; power |= MUSB_POWER_RESUME; musb_writeb(mbase, MUSB_POWER, power); DBG(3, "Root port resuming, power %02x\n", power); /* later, GetPortStatus will stop RESUME signaling */ musb->port1_status |= MUSB_PORT_STAT_RESUME; musb->rh_timer = jiffies + msecs_to_jiffies(20); } }