/* ******************************************************************************* * sw_hcd_port_suspend * * Description: * suspend USB port * * Parameters: * sw_hcd : input. USB¿ØÖÆÆ÷ * do_suspend : input. flag. is suspend USB port or not? * * Return value: * void * * note: * void * ******************************************************************************* */ static void sw_hcd_port_suspend(struct sw_hcd *sw_hcd, bool do_suspend) { 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: this doesn't necessarily put PHY into low power mode, * turning off its clock; that's a function of PHY integration and * sw_hcd_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect * SE0 changing to connect (J) or wakeup (K) states. */ power = USBC_Readb(USBC_REG_PCTL(usbc_base)); if (do_suspend) { int retries = 10000; DMSG_INFO("[sw_hcd]: suspend port.\n"); power &= ~(1 << USBC_BP_POWER_H_RESUME); power |= (1 << USBC_BP_POWER_H_SUSPEND); USBC_Writeb(power, USBC_REG_PCTL(usbc_base)); /* Needed for OPT A tests */ power = USBC_Readb(USBC_REG_PCTL(usbc_base)); while (power & (1 << USBC_BP_POWER_H_SUSPEND)) { power = USBC_Readb(USBC_REG_PCTL(usbc_base)); if (retries-- < 1) break; } DMSG_DBG_HCD("DBG: Root port suspended, power %02x\n", power); sw_hcd->port1_status |= USB_PORT_STAT_SUSPEND; }else if (power & (1 << USBC_BP_POWER_H_SUSPEND)){ DMSG_INFO("[sw_hcd]: suspend portend, resume port.\n"); power &= ~(1 << USBC_BP_POWER_H_SUSPEND); power |= (1 << USBC_BP_POWER_H_RESUME); USBC_Writeb(power, USBC_REG_PCTL(usbc_base)); DMSG_DBG_HCD("DBG: Root port resuming, power %02x\n", power); /* later, GetPortStatus will stop RESUME signaling */ sw_hcd->port1_status |= SW_HCD_PORT_STAT_RESUME; sw_hcd->rh_timer = jiffies + msecs_to_jiffies(20); }else{ DMSG_PANIC("WRN: sw_hcd_port_suspend nothing to do\n"); } return ; }
/* ******************************************************************************* * sw_hcd_write_fifo * * Description: * Load an endpoint's FIFO * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ void sw_hcd_write_fifo(struct sw_hcd_hw_ep *hw_ep, u16 len, const u8 *src) { void __iomem *fifo = hw_ep->fifo; __u32 old_ep_index = 0; prefetch((u8 *)src); DMSG_DBG_HCD("sw_hcd_write_fifo: %cX ep%d fifo %p count %d buf %p\n", 'T', hw_ep->epnum, fifo, len, src); old_ep_index = USBC_GetActiveEp(hw_ep->sw_hcd->sw_hcd_io->usb_bsp_hdle); USBC_SelectActiveEp(hw_ep->sw_hcd->sw_hcd_io->usb_bsp_hdle, hw_ep->epnum); /* we can't assume unaligned reads work */ if (likely((0x01 & (unsigned long) src) == 0)) { u16 index = 0; /* best case is 32bit-aligned source address */ if ((0x02 & (unsigned long) src) == 0) { if (len >= 4) { sw_hcd_writesl(fifo, src + index, len >> 2); index += len & ~0x03; } if (len & 0x02) { USBC_Writew(*(u16 *)&src[index], fifo); index += 2; } } else {
/* ******************************************************************************* * 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; }
/* ******************************************************************************* * 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 ; }