/* Caller may or may not hold musbfsh->lock */ int musbfsh_hub_status_data(struct usb_hcd *hcd, char *buf) { struct musbfsh *musbfsh = hcd_to_musbfsh(hcd); int retval = 0; INFO("musbfsh_hub_status_data++\r\n"); /* called in_irq() via usb_hcd_poll_rh_status() */ if (musbfsh->port1_status & 0xffff0000) { *buf = 0x02; retval = 1; } return retval; }
static inline struct musbfsh *dev_to_musbfsh(struct device *dev) { return hcd_to_musbfsh(dev_get_drvdata(dev)); }
int musbfsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct musbfsh *musbfsh = hcd_to_musbfsh(hcd); u32 temp; int retval = 0; unsigned long flags; INFO("%s++, typeReq=0x%x, wValue=0x%x, wIndex=0x%x\r\n", __func__, typeReq, wValue, wIndex); spin_lock_irqsave(&musbfsh->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { spin_unlock_irqrestore(&musbfsh->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: /* wIndex indicate the port number, here it is should be 1 */ if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: /* here is clearing the suspend */ musbfsh_port_suspend(musbfsh, false); break; case USB_PORT_FEAT_POWER: #ifndef MTK_ALPS_BOX_SUPPORT /* only power off the vbus */ musbfsh_set_vbus(musbfsh, 0); #else /* only power off the vbus */ musbfsh_platform_set_vbus(musbfsh, 0); #endif 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; } INFO("clear feature %d\n", wValue); musbfsh->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; /* 0x0001: per-port power switching */ /* 0x0010: no overcurrent reporting */ desc->wHubCharacteristics = cpu_to_le16(0x0001 | 0x0010); /* msec/2 */ desc->bPwrOn2PwrGood = 5; 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; /* finish RESET signaling? */ /* if FALSE: stop the reset because the timeout of reset. */ if ((musbfsh->port1_status & USB_PORT_STAT_RESET) && time_after_eq(jiffies, musbfsh->rh_timer)) musbfsh_port_reset(musbfsh, false); /* finish RESUME signaling? */ if ((musbfsh->port1_status & MUSBFSH_PORT_STAT_RESUME) && time_after_eq(jiffies, musbfsh->rh_timer)) { u8 pwr; #ifdef CONFIG_MTK_DT_USB_SUPPORT if (!musbfsh_skip_port_resume) { pwr = musbfsh_readb(musbfsh->mregs, MUSBFSH_POWER); pwr &= ~MUSBFSH_POWER_RESUME; WARNING("Root port resume stopped\n"); WARNING("power 0x%02x\n", pwr); musbfsh_writeb(musbfsh->mregs, MUSBFSH_POWER, pwr); #if defined(CONFIG_PM_RUNTIME) && defined(USB11_REMOTE_IRQ_NON_AUTO_MASK) enable_remote_wake_up(); #endif } else { MYDBG("\n"); } #else pwr = musbfsh_readb(musbfsh->mregs, MUSBFSH_POWER); pwr &= ~MUSBFSH_POWER_RESUME; WARNING("Root port resume stopped, power 0x%02x\n", pwr); musbfsh_writeb(musbfsh->mregs, MUSBFSH_POWER, pwr); #endif #ifdef MTK_USB_RUNTIME_SUPPORT /* mt_eint_unmask(CUST_EINT_MT6280_USB_WAKEUP_NUM); */ #endif /* * ISSUE: DaVinci (RTL 1.300) disconnects after * resume of high speed peripherals (but not full * speed ones). */ musbfsh->is_active = 1; musbfsh->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSBFSH_PORT_STAT_RESUME); musbfsh->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; usb_hcd_poll_rh_status(musbfsh_to_hcd(musbfsh)); } put_unaligned(cpu_to_le32(musbfsh->port1_status & ~MUSBFSH_PORT_STAT_RESUME), (__le32 *)buf); /* port change status is more interesting */ WARNING("port status %08x,devctl=0x%x\n", musbfsh->port1_status, musbfsh_readb(musbfsh->mregs, MUSBFSH_DEVCTL)); 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. */ INFO("musbfsh_start is called in hub control\r\n"); #ifdef CONFIG_MTK_ICUSB_SUPPORT if (skip_mac_init_attr.value) MYDBG(""); else musbfsh_start(musbfsh); #else musbfsh_start(musbfsh); #endif break; case USB_PORT_FEAT_RESET: /* enable the reset, but not finish */ musbfsh_port_reset(musbfsh, true); break; case USB_PORT_FEAT_SUSPEND: musbfsh_port_suspend(musbfsh, true); break; case USB_PORT_FEAT_TEST: #if 0 if (unlikely(is_host_active(musbfsh))) goto error; wIndex >>= 8; switch (wIndex) { case 1: pr_debug("TEST_J\n"); temp = MUSBFSH_TEST_J; break; case 2: pr_debug("TEST_K\n"); temp = MUSBFSH_TEST_K; break; case 3: pr_debug("TEST_SE0_NAK\n"); temp = MUSBFSH_TEST_SE0_NAK; break; case 4: pr_debug("TEST_PACKET\n"); temp = MUSBFSH_TEST_PACKET; musbfsh_load_testpacket(musbfsh); break; case 5: pr_debug("TEST_FORCE_ENABLE\n"); temp = MUSBFSH_TEST_FORCE_HOST | MUSBFSH_TEST_FORCE_FS; musbfsh_writeb(musbfsh->mregs, MUSBFSH_DEVCTL, MUSBFSH_DEVCTL_SESSION); break; case 6: pr_debug("TEST_FIFO_ACCESS\n"); temp = MUSBFSH_TEST_FIFO_ACCESS; break; default: goto error; } musbfsh_writeb(musbfsh->mregs, MUSBFSH_TESTMODE, temp); #endif break; default: goto error; } INFO("set feature %d\n", wValue); musbfsh->port1_status |= 1 << wValue; break; default: error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&musbfsh->lock, flags); return retval; }