/* 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;
}