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;
}
Example #5
0
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;
}
Example #6
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;
}
Example #7
0
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;
	}
}
Example #8
0
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);
	}
}