This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM3352: USB RNDIS connection doesn't work at times

Part Number: AM3352

Tool/software: Linux

We are using kernel version 4.9.31 (Arago Krogoth). The issue we face at times is as follows:

  1. Connect USB flash drive to the system
  2. Disconnect USB flash drive
  3. Connect USB cable (to get ethernet over USB connection (RNDIS)) 
  4. USB cable doesn’t get detected. To get it to work, connect and disconnect USB flash drive onto the system again or we need to reload the driver (musb_dsps). After this sequence, connecting the USB cable results in RNDIS connection to work

Please note this issue we do not face all the time.. I suspect there is something caused because of switching between USB host and device modes which results in this situation. Are there any patches or is this is a known issue? Any pointers or steps to debug?



  • Hi Fariya,

    Is this custom hardware ? Do you see any failures in dmesg logs ?
  • Hi Fariya,

    Please run the following commands on your board after your step #2 but before #3, to get more information.

    # grep DevCtl /sys/kernel/debug/musb-hdrc.0/regdump
    # cat /sys/devices/platform/ocp/47400000.usb/47401400.usb/musb-hdrc.0/mode

    This assumes you use USB0. Please adjust the path accordingly if you use USB1 port.
  • Hi Bin Liu,

    After step #2 and before step #3, the output is as follows:

    cat /sys/devices/platform/ocp/47400000.usb/47401400.usb/musb-hdrc.0/mode
    a_wait_vrise

    grep DevCtl /sys/kernel/debug/musb-hdrc.0/regdump
    Devctl:19

    Any input on what could have gone wrong? Why is the USB stuck up at a_wait_vrise state? Thanks for your help.

    Regards,
    Fariya
  • Hi Fariya,

    The issue is caused by a sw bug in the kernel. Please use the kernel patch in the link below to fix the problem.

    e2e.ti.com/.../2779439
  • Thanks so much Bin Liu! This solved the issue.
  • Thanks so much Bin Liu! The patch resolved the issue.
  • Glad to see the issue is solved. Thanks for the update.
  • Hi Bin Liu,

    Unfortunately after having applied this patch, one specific USB flash drive just doesn't get detected. Please note my kernel version is 4.9.31. The state transitions that I can see are a_idle to a_wait_vrise-->a_idle->a_wait_vrise... this keeps on happening and the device doesn't get detected. If I revert the patch that you had suggested, the device gets detected (a_idle --> a_wait_vrise -->a_host). Could you suggest how to resolve this issue? I need to have the patch (that you gave earlier) as it solves my issue but applying the patch is giving me another issue where-in a USB flash drive just doesn't get detected.

    Would greatly appreciate your help.

    Thanks
    Fariya

  • Hi Bin Liu,

    Do you think the patch should have checked for if (musb->port_mode == MUSB_PORT_MODE_HOST || musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) instead of just musb->port_mode == MUSB_PORT_MODE_HOST?

    case OTG_STATE_A_WAIT_VRISE:
    if (musb->port_mode == MUSB_PORT_MODE_HOST) {
    musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;

    Awaiting for your reply.

    Regards,
    Fariya
  • Fariya,

    No, the patch is correct, we only want to advance the state for host-only port.

    Is only this specific usb thumb drive failed or all your usb devices are failed?
    is this on your custom board or TI supported am335x platforms? If your board, please provide the usb portion of the schematics.
  • Hi Bin Liu,

    This seems to be the only USB thumb drive which failed with the latest patch (the very same thumb drive works if I revert your patch). Can I have your official TI email id so I could share the schematics with you?

    Regards,
    Fariya
  • Fariya, I have sent your a friendship request, you should see my email there. Thanks.
  • There should be no resistor on USBn_ID. If DRD or Device-only operation is desired, _ID should float. If Host-only operation is desired, _ID should be strapped directly to GND.

    There isn't enough capacitance on USBn_VBUS. 10uF will not work reliably for all USB devices due to VBUS droop. Try replacing this with 120uF and see if the problem persists in Host mode. Note that 100% support for both USB Host and USB Device cannot be achieved using the same VBUS capacitance and although 10uF is a common solution, it cannot be relied on to support all USB devices.
  • Hi Fariya,

    I am not sure if this is related, but recently we fixed a race condition in the driver, please apply the following patch.

    marc.info/
  • Thanks. We made the recommended h/w modifications but then that particular USB flash drive(Sandisk 64GB) still doesn’t get detected. We’ve got some more similar make Sandisk flash drives in-house… we are performing more testing and shall keep you posted on this.
  • Hi Bin,

    This patch doesn't help. Even after making the h/w modifications + applying this patch + the earlier patch (musb_dsps) we still get the same detection issue. This issue is very consistent with one of the Sandisk 64GB pen drive and with other Sandisk 64GB pendrives that we have, we get this issue but not all the time. Please note if we revert the musb_dsps driver patch, there are no detection issues at all.

    Do let me know what info I can capture to help in debugging.

    Regards,
    Fariya

  • Hi Fariya,

    Can you please first use a scope to probe the USB0_VBUS and USB0_DRVVBUS pin when plugging in the thumb drive? Based on this result I will give you the instructions how to get a kernel log.
  • The first capture is taken when a USB flash drive has got detected. 

    Below capture is when USB flash drive hasnt got detected


  • Hi Fariya,

    Thanks for the captures, it shows the usb controller itself seems behaving correctly in both cases. Can you please add D+ line in the probing, assuming the failing thumb drive is a high-speed device? I'd like to see if there is any timing difference between USB0_VBUS and D+ pullup in both cases.

    If you cannot use 3 probes in the same time, you can remove probing USB0_DRVVBUS signal, since we know how it behaves along with USB0_VBUS signal.

  • Not detected.zip

    Attached is the captures in the scenario where-in the USB flash drive doesnt get detected.

    REgards,

    Fariya

  • Hi Fariya,

    Does this thumb drive take longer time than others to get enumerated in the working kernel?

    Please apply the following debug patch in the failure case. It increase a time from 2sec to 10 sec, and I hope the drive can get enumerated with this patch, and please provide the console enumeration log, I want to check the timestamps.

    diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
    index 358feca54945..427494e74ec2 100644
    --- a/drivers/usb/musb/musb_core.c
    +++ b/drivers/usb/musb/musb_core.c
    @@ -790,6 +790,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
     
                    musb->ep0_stage = MUSB_EP0_START;
     
    +               dev_info(musb->controller, "int 0x10\n");
                    musb->intrtxe = musb->epmask;
                    musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
                    musb->intrrxe = musb->epmask & 0xfffe;
    diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
    index 9f125e179acd..866c6801fe49 100644
    --- a/drivers/usb/musb/musb_dsps.c
    +++ b/drivers/usb/musb/musb_dsps.c
    @@ -226,8 +226,10 @@ static int dsps_check_status(struct musb *musb, void *unused)
                            musb->xceiv->otg->state = OTG_STATE_A_IDLE;
                            MUSB_HST_MODE(musb);
                    }
    -               if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
    +               if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) {
    +                       dev_info(musb->controller, "start session\n");
                            musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
    +               }
                    mod_timer(&glue->timer, jiffies +
                                    msecs_to_jiffies(wrp->poll_timeout));
                    break;
    @@ -339,8 +341,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
                            MUSB_HST_MODE(musb);
                            musb->xceiv->otg->default_a = 1;
                            musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
    +                       dev_info(musb->controller, "drvvbus high\n");
                            mod_timer(&glue->timer, jiffies +
    -                                 msecs_to_jiffies(wrp->poll_timeout));
    +                                 msecs_to_jiffies(10000));
                    } else {
                            musb->is_active = 0;
                            MUSB_DEV_MODE(musb)
    
  • Hi Bin,

    Even after applying the patch, the USB device doesn't get detected. Below are the logs:

    4278.719891] musb-hdrc musb-hdrc.0: start session
    [ 4278.772454] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4278.772551] VBUS on (a_wait_vrise), devctl 09
    [ 4291.199894] musb-hdrc musb-hdrc.0: start session
    [ 4291.252740] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4291.252843] VBUS on (a_wait_vrise), devctl 09
    [ 4303.359885] musb-hdrc musb-hdrc.0: start session
    [ 4303.412777] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4303.412880] VBUS on (a_wait_vrise), devctl 09
    [ 4315.519874] musb-hdrc musb-hdrc.0: start session
    [ 4315.572738] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4315.572841] VBUS on (a_wait_vrise), devctl 09
    [ 4327.679756] musb-hdrc musb-hdrc.0: start session
    [ 4327.732517] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4327.732619] VBUS on (a_wait_vrise), devctl 01
    [ 4339.839886] musb-hdrc musb-hdrc.0: start session
    [ 4339.892456] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4339.892556] VBUS on (a_wait_vrise), devctl 01

    I had to port this patch for my kernel (4.9.30). Attaching the musb_core.c and musb_dsps.c for your reference:

    4478.musb_core.c

    /*
     * Texas Instruments DSPS platforms "glue layer"
     *
     * Copyright (C) 2012, by Texas Instruments
     *
     * Based on the am35x "glue layer" code.
     *
     * This file is part of the Inventra Controller Driver for Linux.
     *
     * The Inventra Controller Driver for Linux is free software; you
     * can redistribute it and/or modify it under the terms of the GNU
     * General Public License version 2 as published by the Free Software
     * Foundation.
     *
     * The Inventra Controller Driver for Linux is distributed in
     * the hope that it will be useful, but WITHOUT ANY WARRANTY;
     * without even the implied warranty of MERCHANTABILITY or
     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     * License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with The Inventra Controller Driver for Linux ; if not,
     * write to the Free Software Foundation, Inc., 59 Temple Place,
     * Suite 330, Boston, MA  02111-1307  USA
     *
     * musb_dsps.c will be a common file for all the TI DSPS platforms
     * such as dm64x, dm36x, dm35x, da8x, am35x and ti81x.
     * For now only ti81x is using this and in future davinci.c, am35x.c
     * da8xx.c would be merged to this file after testing.
     */
    
    #include <linux/io.h>
    #include <linux/err.h>
    #include <linux/platform_device.h>
    #include <linux/dma-mapping.h>
    #include <linux/pm_runtime.h>
    #include <linux/module.h>
    #include <linux/usb/usb_phy_generic.h>
    #include <linux/platform_data/usb-omap.h>
    #include <linux/sizes.h>
    
    #include <linux/of.h>
    #include <linux/of_device.h>
    #include <linux/of_address.h>
    #include <linux/of_irq.h>
    #include <linux/usb/of.h>
    
    #include <linux/debugfs.h>
    
    #include "musb_core.h"
    
    static const struct of_device_id musb_dsps_of_match[];
    
    /**
     * DSPS musb wrapper register offset.
     * FIXME: This should be expanded to have all the wrapper registers from TI DSPS
     * musb ips.
     */
    struct dsps_musb_wrapper {
    	u16	revision;
    	u16	control;
    	u16	status;
    	u16	epintr_set;
    	u16	epintr_clear;
    	u16	epintr_status;
    	u16	coreintr_set;
    	u16	coreintr_clear;
    	u16	coreintr_status;
    	u16	phy_utmi;
    	u16	mode;
    	u16	tx_mode;
    	u16	rx_mode;
    
    	/* bit positions for control */
    	unsigned	reset:5;
    
    	/* bit positions for interrupt */
    	unsigned	usb_shift:5;
    	u32		usb_mask;
    	u32		usb_bitmap;
    	unsigned	drvvbus:5;
    
    	unsigned	txep_shift:5;
    	u32		txep_mask;
    	u32		txep_bitmap;
    
    	unsigned	rxep_shift:5;
    	u32		rxep_mask;
    	u32		rxep_bitmap;
    
    	/* bit positions for phy_utmi */
    	unsigned	otg_disable:5;
    
    	/* bit positions for mode */
    	unsigned	iddig:5;
    	unsigned	iddig_mux:5;
    	/* miscellaneous stuff */
    	unsigned	poll_timeout;
    };
    
    /*
     * register shadow for suspend
     */
    struct dsps_context {
    	u32 control;
    	u32 epintr;
    	u32 coreintr;
    	u32 phy_utmi;
    	u32 mode;
    	u32 tx_mode;
    	u32 rx_mode;
    };
    
    /**
     * DSPS glue structure.
     */
    struct dsps_glue {
    	struct device *dev;
    	struct platform_device *musb;	/* child musb pdev */
    	const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
    	int vbus_irq;			/* optional vbus irq */
    	struct timer_list timer;	/* otg_workaround timer */
    	unsigned long last_timer;    /* last timer data for each instance */
    	bool sw_babble_enabled;
    	void __iomem *usbss_base;
    
    	struct dsps_context context;
    	struct debugfs_regset32 regset;
    	struct dentry *dbgfs_root;
    };
    
    static const struct debugfs_reg32 dsps_musb_regs[] = {
    	{ "revision",		0x00 },
    	{ "control",		0x14 },
    	{ "status",		0x18 },
    	{ "eoi",		0x24 },
    	{ "intr0_stat",		0x30 },
    	{ "intr1_stat",		0x34 },
    	{ "intr0_set",		0x38 },
    	{ "intr1_set",		0x3c },
    	{ "txmode",		0x70 },
    	{ "rxmode",		0x74 },
    	{ "autoreq",		0xd0 },
    	{ "srpfixtime",		0xd4 },
    	{ "tdown",		0xd8 },
    	{ "phy_utmi",		0xe0 },
    	{ "mode",		0xe8 },
    };
    
    static void dsps_mod_timer(struct dsps_glue *glue, int wait_ms)
    {
    	int wait;
    
    	if (wait_ms < 0)
    		wait = msecs_to_jiffies(glue->wrp->poll_timeout);
    	else
    		wait = msecs_to_jiffies(wait_ms);
    
    	mod_timer(&glue->timer, jiffies + wait);
    }
    
    /*
     * If no vbus irq from the PMIC is configured, we need to poll VBUS status.
     */
    static void dsps_mod_timer_optional(struct dsps_glue *glue)
    {
    	if (glue->vbus_irq)
    		return;
    	dsps_mod_timer(glue, -1);
    }
    
    /* USBSS  / USB AM335x */
    #define USBSS_IRQ_STATUS	0x28
    #define USBSS_IRQ_ENABLER	0x2c
    #define USBSS_IRQ_CLEARR	0x30
    
    #define USBSS_IRQ_PD_COMP	(1 << 2)
    
    /**
     * dsps_musb_enable - enable interrupts
     */
    static void dsps_musb_enable(struct musb *musb)
    {
    	struct device *dev = musb->controller;
    	struct platform_device *pdev = to_platform_device(dev->parent);
    	struct dsps_glue *glue = platform_get_drvdata(pdev);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	void __iomem *reg_base = musb->ctrl_base;
    	u32 epmask, coremask;
    
    	/* Workaround: setup IRQs through both register sets. */
    	epmask = ((musb->epmask & wrp->txep_mask) << wrp->txep_shift) |
    	       ((musb->epmask & wrp->rxep_mask) << wrp->rxep_shift);
    	coremask = (wrp->usb_bitmap & ~MUSB_INTR_SOF);
    
    	musb_writel(reg_base, wrp->epintr_set, epmask);
    	musb_writel(reg_base, wrp->coreintr_set, coremask);
    	/* start polling for ID change in dual-role idle mode */
    	if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
    			musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
    		dsps_mod_timer(glue, -1);
    }
    
    /**
     * dsps_musb_disable - disable HDRC and flush interrupts
     */
    static void dsps_musb_disable(struct musb *musb)
    {
    	struct device *dev = musb->controller;
    	struct platform_device *pdev = to_platform_device(dev->parent);
    	struct dsps_glue *glue = platform_get_drvdata(pdev);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	void __iomem *reg_base = musb->ctrl_base;
    
    	musb_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
    	musb_writel(reg_base, wrp->epintr_clear,
    			 wrp->txep_bitmap | wrp->rxep_bitmap);
    	del_timer_sync(&glue->timer);
    }
    
    /* Caller must take musb->lock */
    static int dsps_check_status(struct musb *musb, void *unused)
    {
    	void __iomem *mregs = musb->mregs;
    	struct device *dev = musb->controller;
    	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	u8 devctl;
    	int skip_session = 0;
    
    	if (glue->vbus_irq)
    		del_timer(&glue->timer);
    
    	/*
    	 * We poll because DSPS IP's won't expose several OTG-critical
    	 * status change events (from the transceiver) otherwise.
    	 */
    	devctl = musb_readb(mregs, MUSB_DEVCTL);
    	dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
    				usb_otg_state_string(musb->xceiv->otg->state));
    
    	switch (musb->xceiv->otg->state) {
    	case OTG_STATE_A_WAIT_VRISE:
    		if (musb->port_mode == MUSB_PORT_MODE_HOST) {
    			musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
    			dsps_mod_timer_optional(glue);
    			break;
    		}
    		/* fall through */
    
    	case OTG_STATE_A_WAIT_BCON:
    		if (musb->port_mode == MUSB_PORT_MODE_HOST) {
    			dsps_mod_timer_optional(glue);
    			break;
    		}
    		musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
    		skip_session = 1;
    		/* fall */
    
    	case OTG_STATE_A_IDLE:
    	case OTG_STATE_B_IDLE:
    		if (!glue->vbus_irq) {
    			/* Check if USB controller is operating in B mode or A mode */
    			if (devctl & MUSB_DEVCTL_BDEVICE) {
    				musb->xceiv->otg->state = OTG_STATE_B_IDLE;
    				MUSB_DEV_MODE(musb);
    			} else {
    				musb->xceiv->otg->state = OTG_STATE_A_IDLE;
    				MUSB_HST_MODE(musb);
    			}
    			/* Initiate the SRP */
    			if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) {
    				dev_info(musb->controller, "start session\n");
    				musb_writeb(mregs, MUSB_DEVCTL,
    					    MUSB_DEVCTL_SESSION);
    			}
    		}
    		dsps_mod_timer_optional(glue);
    		break;
    	case OTG_STATE_A_WAIT_VFALL:
    		musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
    		musb_writel(musb->ctrl_base, wrp->coreintr_set,
    			    MUSB_INTR_VBUSERROR << wrp->usb_shift);
    		break;
    	default:
    		break;
    	}
    
    	return 0;
    }
    
    static void otg_timer(unsigned long _musb)
    {
    	struct musb *musb = (void *)_musb;
    	struct device *dev = musb->controller;
    	unsigned long flags;
    	int err;
    
    	err = pm_runtime_get(dev);
    	if ((err != -EINPROGRESS) && err < 0) {
    		dev_err(dev, "Poll could not pm_runtime_get: %i\n", err);
    		pm_runtime_put_noidle(dev);
    
    		return;
    	}
    
    	spin_lock_irqsave(&musb->lock, flags);
    	err = musb_queue_resume_work(musb, dsps_check_status, NULL);
    	if (err < 0)
    		dev_err(dev, "%s resume work: %i\n", __func__, err);
    	spin_unlock_irqrestore(&musb->lock, flags);
    	pm_runtime_mark_last_busy(dev);
    	pm_runtime_put_autosuspend(dev);
    }
    
    static void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum)
    {
    	u32 epintr;
    	struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    
    	/* musb->lock might already been held */
    	epintr = (1 << epnum) << wrp->rxep_shift;
    	musb_writel(musb->ctrl_base, wrp->epintr_status, epintr);
    }
    
    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 = musb_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)
    		musb_writel(reg_base, wrp->epintr_status, epintr);
    
    	/* Get usb core interrupts */
    	usbintr = musb_readl(reg_base, wrp->coreintr_status);
    	if (!usbintr && !epintr)
    		goto out;
    
    	musb->int_usb =	(usbintr & wrp->usb_bitmap) >> wrp->usb_shift;
    	if (usbintr)
    		musb_writel(reg_base, wrp->coreintr_status, usbintr);
    
    	dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
    			usbintr, epintr);
    
    	if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
    		int drvvbus = musb_readl(reg_base, wrp->status);
    		void __iomem *mregs = musb->mregs;
    		u8 devctl = musb_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->otg->state = OTG_STATE_A_WAIT_VFALL;
    			dsps_mod_timer_optional(glue);
    			WARNING("VBUS error workaround (delay coming)\n");
    		} else if (drvvbus) {
    			MUSB_HST_MODE(musb);
    			musb->xceiv->otg->default_a = 1;
    			musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
    			dev_info(musb->controller, "drvvbus high\n");
    //			dsps_mod_timer_optional(glue);
    			mod_timer(&glue->timer, jiffies + msecs_to_jiffies(10000));
    
    		} else {
    			musb->is_active = 0;
    			MUSB_DEV_MODE(musb);
    			musb->xceiv->otg->default_a = 0;
    			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
    		}
    
    		/* NOTE: this must complete power-on within 100 ms. */
    		printk( "VBUS %s (%s)%s, devctl %02x\n",
    				drvvbus ? "on" : "off",
    				usb_otg_state_string(musb->xceiv->otg->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 and connect */
    	switch (musb->xceiv->otg->state) {
    	case OTG_STATE_B_IDLE:
    	case OTG_STATE_A_WAIT_BCON:
    		dsps_mod_timer_optional(glue);
    		break;
    	default:
    		break;
    	}
    
    out:
    	spin_unlock_irqrestore(&musb->lock, flags);
    
    	return ret;
    }
    
    static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue)
    {
    	struct dentry *root;
    	struct dentry *file;
    	char buf[128];
    
    	sprintf(buf, "%s.dsps", dev_name(musb->controller));
    	root = debugfs_create_dir(buf, NULL);
    	if (!root)
    		return -ENOMEM;
    	glue->dbgfs_root = root;
    
    	glue->regset.regs = dsps_musb_regs;
    	glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs);
    	glue->regset.base = musb->ctrl_base;
    
    	file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset);
    	if (!file) {
    		debugfs_remove_recursive(root);
    		return -ENOMEM;
    	}
    	return 0;
    }
    
    static int dsps_musb_init(struct musb *musb)
    {
    	struct device *dev = musb->controller;
    	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
    	struct platform_device *parent = to_platform_device(dev->parent);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	void __iomem *reg_base;
    	struct resource *r;
    	u32 rev, val;
    	int ret;
    
    	r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control");
    	reg_base = devm_ioremap_resource(dev, r);
    	if (IS_ERR(reg_base))
    		return PTR_ERR(reg_base);
    	musb->ctrl_base = reg_base;
    
    	/* NOP driver needs change if supporting dual instance */
    	musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0);
    	if (IS_ERR(musb->xceiv))
    		return PTR_ERR(musb->xceiv);
    
    	musb->phy = devm_phy_get(dev->parent, "usb2-phy");
    
    	/* Returns zero if e.g. not clocked */
    	rev = musb_readl(reg_base, wrp->revision);
    	if (!rev)
    		return -ENODEV;
    
    	usb_phy_init(musb->xceiv);
    	if (IS_ERR(musb->phy))  {
    		musb->phy = NULL;
    	} else {
    		ret = phy_init(musb->phy);
    		if (ret < 0)
    			return ret;
    		ret = phy_power_on(musb->phy);
    		if (ret) {
    			phy_exit(musb->phy);
    			return ret;
    		}
    	}
    
    	setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
    
    	/* Reset the musb */
    	musb_writel(reg_base, wrp->control, (1 << wrp->reset));
    
    	musb->isr = dsps_interrupt;
    
    	/* reset the otgdisable bit, needed for host mode to work */
    	val = musb_readl(reg_base, wrp->phy_utmi);
    	val &= ~(1 << wrp->otg_disable);
    	musb_writel(musb->ctrl_base, wrp->phy_utmi, val);
    
    	/*
    	 *  Check whether the dsps version has babble control enabled.
    	 * In latest silicon revision the babble control logic is enabled.
    	 * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control
    	 * logic enabled.
    	 */
    	val = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
    	if (val & MUSB_BABBLE_RCV_DISABLE) {
    		glue->sw_babble_enabled = true;
    		val |= MUSB_BABBLE_SW_SESSION_CTRL;
    		musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
    	}
    
    	dsps_mod_timer(glue, -1);
    
    	return dsps_musb_dbg_init(musb, glue);
    }
    
    static int dsps_musb_exit(struct musb *musb)
    {
    	struct device *dev = musb->controller;
    	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
    
    	del_timer_sync(&glue->timer);
    	usb_phy_shutdown(musb->xceiv);
    	phy_power_off(musb->phy);
    	phy_exit(musb->phy);
    	debugfs_remove_recursive(glue->dbgfs_root);
    
    	return 0;
    }
    
    static int dsps_musb_set_mode(struct musb *musb, u8 mode)
    {
    	struct device *dev = musb->controller;
    	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	void __iomem *ctrl_base = musb->ctrl_base;
    	u32 reg;
    
    	reg = musb_readl(ctrl_base, wrp->mode);
    
    	switch (mode) {
    	case MUSB_HOST:
    		reg &= ~(1 << wrp->iddig);
    
    		/*
    		 * if we're setting mode to host-only or device-only, we're
    		 * going to ignore whatever the PHY sends us and just force
    		 * ID pin status by SW
    		 */
    		reg |= (1 << wrp->iddig_mux);
    
    		musb_writel(ctrl_base, wrp->mode, reg);
    		musb_writel(ctrl_base, wrp->phy_utmi, 0x02);
    		break;
    	case MUSB_PERIPHERAL:
    		reg |= (1 << wrp->iddig);
    
    		/*
    		 * if we're setting mode to host-only or device-only, we're
    		 * going to ignore whatever the PHY sends us and just force
    		 * ID pin status by SW
    		 */
    		reg |= (1 << wrp->iddig_mux);
    
    		musb_writel(ctrl_base, wrp->mode, reg);
    		break;
    	case MUSB_OTG:
    		musb_writel(ctrl_base, wrp->phy_utmi, 0x02);
    		break;
    	default:
    		dev_err(glue->dev, "unsupported mode %d\n", mode);
    		return -EINVAL;
    	}
    
    	return 0;
    }
    
    static bool dsps_sw_babble_control(struct musb *musb)
    {
    	u8 babble_ctl;
    	bool session_restart =  false;
    
    	babble_ctl = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
    	printk( "babble: MUSB_BABBLE_CTL value %x\n",
    		babble_ctl);
    	/*
    	 * check line monitor flag to check whether babble is
    	 * due to noise
    	 */
    	printk( "STUCK_J is %s\n",
    		babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset");
    
    	if (babble_ctl & MUSB_BABBLE_STUCK_J) {
    		int timeout = 10;
    
    		/*
    		 * babble is due to noise, then set transmit idle (d7 bit)
    		 * to resume normal operation
    		 */
    		babble_ctl = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
    		babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE;
    		musb_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl);
    
    		/* wait till line monitor flag cleared */
    		printk( "Set TXIDLE, wait J to clear\n");
    		do {
    			babble_ctl = musb_readb(musb->mregs, MUSB_BABBLE_CTL);
    			udelay(1);
    		} while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--);
    
    		/* check whether stuck_at_j bit cleared */
    		if (babble_ctl & MUSB_BABBLE_STUCK_J) {
    			/*
    			 * real babble condition has occurred
    			 * restart the controller to start the
    			 * session again
    			 */
    			printk( "J not cleared, misc (%x)\n",
    				babble_ctl);
    			session_restart = true;
    		}
    	} else {
    		session_restart = true;
    	}
    
    	return session_restart;
    }
    
    static int dsps_musb_recover(struct musb *musb)
    {
    	struct device *dev = musb->controller;
    	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
    	int session_restart = 0;
    
    	if (glue->sw_babble_enabled)
    		session_restart = dsps_sw_babble_control(musb);
    	else
    		session_restart = 1;
    
    	return session_restart ? 0 : -EPIPE;
    }
    
    /* Similar to am35x, dm81xx support only 32-bit read operation */
    static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
    {
    	void __iomem *fifo = hw_ep->fifo;
    
    	if (len >= 4) {
    		ioread32_rep(fifo, dst, len >> 2);
    		dst += len & ~0x03;
    		len &= 0x03;
    	}
    
    	/* Read any remaining 1 to 3 bytes */
    	if (len > 0) {
    		u32 val = musb_readl(fifo, 0);
    		memcpy(dst, &val, len);
    	}
    }
    
    #ifdef CONFIG_USB_TI_CPPI41_DMA
    static void dsps_dma_controller_callback(struct dma_controller *c)
    {
    	struct musb *musb = c->musb;
    	struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
    	void __iomem *usbss_base = glue->usbss_base;
    	u32 status;
    
    	status = musb_readl(usbss_base, USBSS_IRQ_STATUS);
    	if (status & USBSS_IRQ_PD_COMP)
    		musb_writel(usbss_base, USBSS_IRQ_STATUS, USBSS_IRQ_PD_COMP);
    }
    
    static struct dma_controller *
    dsps_dma_controller_create(struct musb *musb, void __iomem *base)
    {
    	struct dma_controller *controller;
    	struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
    	void __iomem *usbss_base = glue->usbss_base;
    
    	controller = cppi41_dma_controller_create(musb, base);
    	if (IS_ERR_OR_NULL(controller))
    		return controller;
    
    	musb_writel(usbss_base, USBSS_IRQ_ENABLER, USBSS_IRQ_PD_COMP);
    	controller->dma_callback = dsps_dma_controller_callback;
    
    	return controller;
    }
    
    static void dsps_dma_controller_destroy(struct dma_controller *c)
    {
    	struct musb *musb = c->musb;
    	struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
    	void __iomem *usbss_base = glue->usbss_base;
    
    	musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP);
    	cppi41_dma_controller_destroy(c);
    }
    
    #ifdef CONFIG_PM_SLEEP
    static void dsps_dma_controller_suspend(struct dsps_glue *glue)
    {
    	void __iomem *usbss_base = glue->usbss_base;
    
    	musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP);
    }
    
    static void dsps_dma_controller_resume(struct dsps_glue *glue)
    {
    	void __iomem *usbss_base = glue->usbss_base;
    
    	musb_writel(usbss_base, USBSS_IRQ_ENABLER, USBSS_IRQ_PD_COMP);
    }
    #endif
    #else /* CONFIG_USB_TI_CPPI41_DMA */
    #ifdef CONFIG_PM_SLEEP
    static void dsps_dma_controller_suspend(struct dsps_glue *glue) {}
    static void dsps_dma_controller_resume(struct dsps_glue *glue) {}
    #endif
    #endif /* CONFIG_USB_TI_CPPI41_DMA */
    
    static struct musb_platform_ops dsps_ops = {
    	.quirks		= MUSB_DMA_CPPI41 | MUSB_INDEXED_EP,
    	.init		= dsps_musb_init,
    	.exit		= dsps_musb_exit,
    
    #ifdef CONFIG_USB_TI_CPPI41_DMA
    	.dma_init	= dsps_dma_controller_create,
    	.dma_exit	= dsps_dma_controller_destroy,
    #endif
    	.enable		= dsps_musb_enable,
    	.disable	= dsps_musb_disable,
    
    	.set_mode	= dsps_musb_set_mode,
    	.recover	= dsps_musb_recover,
    	.clear_ep_rxintr = dsps_musb_clear_ep_rxintr,
    };
    
    static u64 musb_dmamask = DMA_BIT_MASK(32);
    
    static int get_int_prop(struct device_node *dn, const char *s)
    {
    	int ret;
    	u32 val;
    
    	ret = of_property_read_u32(dn, s, &val);
    	if (ret)
    		return 0;
    	return val;
    }
    
    static int get_musb_port_mode(struct device *dev)
    {
    	enum usb_dr_mode mode;
    
    	mode = usb_get_dr_mode(dev);
    	switch (mode) {
    	case USB_DR_MODE_HOST:
    		return MUSB_PORT_MODE_HOST;
    
    	case USB_DR_MODE_PERIPHERAL:
    		return MUSB_PORT_MODE_GADGET;
    
    	case USB_DR_MODE_UNKNOWN:
    	case USB_DR_MODE_OTG:
    	default:
    		return MUSB_PORT_MODE_DUAL_ROLE;
    	}
    }
    
    static int dsps_create_musb_pdev(struct dsps_glue *glue,
    		struct platform_device *parent)
    {
    	struct musb_hdrc_platform_data pdata;
    	struct resource	resources[2];
    	struct resource	*res;
    	struct device *dev = &parent->dev;
    	struct musb_hdrc_config	*config;
    	struct platform_device *musb;
    	struct device_node *dn = parent->dev.of_node;
    	int ret, val;
    
    	memset(resources, 0, sizeof(resources));
    	res = platform_get_resource_byname(parent, IORESOURCE_MEM, "mc");
    	if (!res) {
    		dev_err(dev, "failed to get memory.\n");
    		return -EINVAL;
    	}
    	resources[0] = *res;
    
    	res = platform_get_resource_byname(parent, IORESOURCE_IRQ, "mc");
    	if (!res) {
    		dev_err(dev, "failed to get irq.\n");
    		return -EINVAL;
    	}
    	resources[1] = *res;
    
    	/* allocate the child platform device */
    	musb = platform_device_alloc("musb-hdrc",
    			(resources[0].start & 0xFFF) == 0x400 ? 0 : 1);
    	if (!musb) {
    		dev_err(dev, "failed to allocate musb device\n");
    		return -ENOMEM;
    	}
    
    	musb->dev.parent		= dev;
    	musb->dev.dma_mask		= &musb_dmamask;
    	musb->dev.coherent_dma_mask	= musb_dmamask;
    
    	glue->musb = musb;
    
    	ret = platform_device_add_resources(musb, resources,
    			ARRAY_SIZE(resources));
    	if (ret) {
    		dev_err(dev, "failed to add resources\n");
    		goto err;
    	}
    
    	config = devm_kzalloc(&parent->dev, sizeof(*config), GFP_KERNEL);
    	if (!config) {
    		ret = -ENOMEM;
    		goto err;
    	}
    	pdata.config = config;
    	pdata.platform_ops = &dsps_ops;
    
    	config->num_eps = get_int_prop(dn, "mentor,num-eps");
    	config->ram_bits = get_int_prop(dn, "mentor,ram-bits");
    	config->host_port_deassert_reset_at_resume = 1;
    	pdata.mode = get_musb_port_mode(dev);
    	/* DT keeps this entry in mA, musb expects it as per USB spec */
    	pdata.power = get_int_prop(dn, "mentor,power") / 2;
    
    	ret = of_property_read_u32(dn, "mentor,multipoint", &val);
    	if (!ret && val)
    		config->multipoint = true;
    
    	config->maximum_speed = usb_get_maximum_speed(&parent->dev);
    	switch (config->maximum_speed) {
    	case USB_SPEED_LOW:
    	case USB_SPEED_FULL:
    		break;
    	case USB_SPEED_SUPER:
    		dev_warn(dev, "ignore incorrect maximum_speed "
    				"(super-speed) setting in dts");
    		/* fall through */
    	default:
    		config->maximum_speed = USB_SPEED_HIGH;
    	}
    
    	ret = platform_device_add_data(musb, &pdata, sizeof(pdata));
    	if (ret) {
    		dev_err(dev, "failed to add platform_data\n");
    		goto err;
    	}
    
    	ret = platform_device_add(musb);
    	if (ret) {
    		dev_err(dev, "failed to register musb device\n");
    		goto err;
    	}
    	return 0;
    
    err:
    	platform_device_put(musb);
    	return ret;
    }
    
    static irqreturn_t dsps_vbus_threaded_irq(int irq, void *priv)
    {
    	struct dsps_glue *glue = priv;
    	struct musb *musb = platform_get_drvdata(glue->musb);
    
    	if (!musb)
    		return IRQ_NONE;
    
    	dev_dbg(glue->dev, "VBUS interrupt\n");
    	dsps_mod_timer(glue, 0);
    
    	return IRQ_HANDLED;
    }
    
    static int dsps_setup_optional_vbus_irq(struct platform_device *pdev,
    					struct dsps_glue *glue)
    {
    	int error;
    
    	glue->vbus_irq = platform_get_irq_byname(pdev, "vbus");
    	if (glue->vbus_irq == -EPROBE_DEFER)
    		return -EPROBE_DEFER;
    
    	if (glue->vbus_irq <= 0) {
    		glue->vbus_irq = 0;
    		return 0;
    	}
    
    	error = devm_request_threaded_irq(glue->dev, glue->vbus_irq,
    					  NULL, dsps_vbus_threaded_irq,
    					  IRQF_ONESHOT,
    					  "vbus", glue);
    	if (error) {
    		glue->vbus_irq = 0;
    		return error;
    	}
    	dev_dbg(glue->dev, "VBUS irq %i configured\n", glue->vbus_irq);
    
    	return 0;
    }
    
    static int dsps_probe(struct platform_device *pdev)
    {
    	const struct of_device_id *match;
    	const struct dsps_musb_wrapper *wrp;
    	struct dsps_glue *glue;
    	int ret;
    
    	if (!strcmp(pdev->name, "musb-hdrc"))
    		return -ENODEV;
    
    	match = of_match_node(musb_dsps_of_match, pdev->dev.of_node);
    	if (!match) {
    		dev_err(&pdev->dev, "fail to get matching of_match struct\n");
    		return -EINVAL;
    	}
    	wrp = match->data;
    
    	if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816"))
    		dsps_ops.read_fifo = dsps_read_fifo32;
    
    	/* allocate glue */
    	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
    	if (!glue)
    		return -ENOMEM;
    
    	glue->dev = &pdev->dev;
    	glue->wrp = wrp;
    	glue->usbss_base = of_iomap(pdev->dev.parent->of_node, 0);
    	if (!glue->usbss_base)
    		return -ENXIO;
    
    	if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) {
    		ret = dsps_setup_optional_vbus_irq(pdev, glue);
    		if (ret)
    			return ret;
    	}
    
    	platform_set_drvdata(pdev, glue);
    	pm_runtime_enable(&pdev->dev);
    	ret = dsps_create_musb_pdev(glue, pdev);
    	if (ret)
    		goto err;
    
    	return 0;
    
    err:
    	pm_runtime_disable(&pdev->dev);
    	return ret;
    }
    
    static int dsps_remove(struct platform_device *pdev)
    {
    	struct dsps_glue *glue = platform_get_drvdata(pdev);
    
    	platform_device_unregister(glue->musb);
    
    	pm_runtime_disable(&pdev->dev);
    
    	return 0;
    }
    
    static const struct dsps_musb_wrapper am33xx_driver_data = {
    	.revision		= 0x00,
    	.control		= 0x14,
    	.status			= 0x18,
    	.epintr_set		= 0x38,
    	.epintr_clear		= 0x40,
    	.epintr_status		= 0x30,
    	.coreintr_set		= 0x3c,
    	.coreintr_clear		= 0x44,
    	.coreintr_status	= 0x34,
    	.phy_utmi		= 0xe0,
    	.mode			= 0xe8,
    	.tx_mode		= 0x70,
    	.rx_mode		= 0x74,
    	.reset			= 0,
    	.otg_disable		= 21,
    	.iddig			= 8,
    	.iddig_mux		= 7,
    	.usb_shift		= 0,
    	.usb_mask		= 0x1ff,
    	.usb_bitmap		= (0x1ff << 0),
    	.drvvbus		= 8,
    	.txep_shift		= 0,
    	.txep_mask		= 0xffff,
    	.txep_bitmap		= (0xffff << 0),
    	.rxep_shift		= 16,
    	.rxep_mask		= 0xfffe,
    	.rxep_bitmap		= (0xfffe << 16),
    	.poll_timeout		= 2000, /* ms */
    };
    
    static const struct of_device_id musb_dsps_of_match[] = {
    	{ .compatible = "ti,musb-am33xx",
    		.data = &am33xx_driver_data, },
    	{ .compatible = "ti,musb-dm816",
    		.data = &am33xx_driver_data, },
    	{  },
    };
    MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
    
    #ifdef CONFIG_PM_SLEEP
    static int dsps_suspend(struct device *dev)
    {
    	struct dsps_glue *glue = dev_get_drvdata(dev);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	struct musb *musb = platform_get_drvdata(glue->musb);
    	void __iomem *mbase;
    
    	del_timer_sync(&glue->timer);
    
    	if (!musb)
    		/* This can happen if the musb device is in -EPROBE_DEFER */
    		return 0;
    
    	mbase = musb->ctrl_base;
    	glue->context.control = musb_readl(mbase, wrp->control);
    	glue->context.epintr = musb_readl(mbase, wrp->epintr_set);
    	glue->context.coreintr = musb_readl(mbase, wrp->coreintr_set);
    	glue->context.phy_utmi = musb_readl(mbase, wrp->phy_utmi);
    	glue->context.mode = musb_readl(mbase, wrp->mode);
    	glue->context.tx_mode = musb_readl(mbase, wrp->tx_mode);
    	glue->context.rx_mode = musb_readl(mbase, wrp->rx_mode);
    
    	dsps_dma_controller_suspend(glue);
    
    	return 0;
    }
    
    static int dsps_resume(struct device *dev)
    {
    	struct dsps_glue *glue = dev_get_drvdata(dev);
    	const struct dsps_musb_wrapper *wrp = glue->wrp;
    	struct musb *musb = platform_get_drvdata(glue->musb);
    	void __iomem *mbase;
    
    	if (!musb)
    		return 0;
    
    	dsps_dma_controller_resume(glue);
    
    	mbase = musb->ctrl_base;
    	musb_writel(mbase, wrp->control, glue->context.control);
    	musb_writel(mbase, wrp->epintr_set, glue->context.epintr);
    	musb_writel(mbase, wrp->coreintr_set, glue->context.coreintr);
    	musb_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi);
    	musb_writel(mbase, wrp->mode, glue->context.mode);
    	musb_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
    	musb_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
    	if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
    	    musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
    		dsps_mod_timer(glue, -1);
    
    	return 0;
    }
    #endif
    
    static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume);
    
    static struct platform_driver dsps_usbss_driver = {
    	.probe		= dsps_probe,
    	.remove         = dsps_remove,
    	.driver         = {
    		.name   = "musb-dsps",
    		.pm	= &dsps_pm_ops,
    		.of_match_table	= musb_dsps_of_match,
    	},
    };
    
    MODULE_DESCRIPTION("TI DSPS MUSB Glue Layer");
    MODULE_AUTHOR("Ravi B <ravibabu@ti.com>");
    MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>");
    MODULE_LICENSE("GPL v2");
    
    module_platform_driver(dsps_usbss_driver);
    

  • Fariya,

    It seems the usb host waited for 10 seconds but still didn't detect the thumb drive.

    When you connect the thumb drive in the working case (the kernel without the first fix patch), did you notice that it takes a while the serial console shows the enumeration log or enumeration happens almost immediately after plug it in?

    Can you please apply the last debug patch to the working kernel (which is without the first fix patch) and provide the kernel log? I'd like to check the message timestamps between

    musb-hdrc musb-hdrc.0: drvvbus high
    musb-hdrc musb-hdrc.0: int 0x10
  • Hi Bin,

    Thanks so much ... This particular Sandisk drive doesn't take so much time (~3secs) to detect (without any patches). However after I apply latest patch + without first fix patch, it seems to be taking 20+ seconds to detect.. below are the logs:

    [ 4331.599724] musb-hdrc musb-hdrc.0: start session
    [ 4331.652355] musb-hdrc musb-hdrc.0: drvvbus high
    [ 4351.894962] musb-hdrc musb-hdrc.0: int 0x10
    [ 4352.149603] usb 1-1: new high-speed USB device number 3 using musb-hdrc
    [ 4352.328518] usb-storage 1-1:1.0: USB Mass Storage device detected
    [ 4352.341106] scsi host0: usb-storage 1-1:1.0
    [ 4353.361494] scsi 0:0:0:0: Direct-Access SanDisk Cruzer Blade 1.00 PQ: 0 ANSI: 6
    [ 4353.368754] sd 0:0:0:0: [sda] 124059648 512-byte logical blocks: (63.5 GB/59.2 GiB)
    [ 4353.374432] sd 0:0:0:0: [sda] Write Protect is off
    [ 4353.374522] sd 0:0:0:0: [sda] Mode Sense: 43 00 00 00
    [ 4353.375533] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
    [ 4353.391548] sda: sda1
    [ 4353.399443] sd 0:0:0:0: [sda] Attached SCSI removable disk


    Regards,
    Fariya
  • Hi Fariya,

    Do you mean the Sandisk drive was detected about 3secs without any patch? Please do the same test again with the latest patch only but don't apply the last chunk in the patch (which changes the timer). So the only change is the 3 print statements. Sorry I should have asked this test yesterday.

    Basically don't apply this chunk:

    -                                 msecs_to_jiffies(wrp->poll_timeout));
    +                                 msecs_to_jiffies(10000));
    
  • Hi Bin Liu,

    Below are the logs as requested by you (3 secs from "start session" i.e when the device is plugged in to "new high speed USB device")

    1773.759860] musb-hdrc musb-hdrc.0: start session

    [ 1773.812495] musb-hdrc musb-hdrc.0: drvvbus high

    [ 1775.924915] musb-hdrc musb-hdrc.0: int 0x10

    [ 1776.209768] usb 1-1: new high-speed USB device number 2 using musb-hdrc

    [ 1776.389361] usb-storage 1-1:1.0: USB Mass Storage device detected

    [ 1776.405424] scsi host0: usb-storage 1-1:1.0

    [ 1777.441514] scsi 0:0:0:0: Direct-Access     SanDisk  Cruzer Blade     1.00 PQ: 0 ANSI: 6

    [ 1777.446101] sd 0:0:0:0: [sda] 124059648 512-byte logical blocks: (63.5 GB/59.2 GiB)

    [ 1777.448091] sd 0:0:0:0: [sda] Write Protect is off

    [ 1777.448166] sd 0:0:0:0: [sda] Mode Sense: 43 00 00 00

    [ 1777.448973] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA

    [ 1777.468559]  sda: sda1

    [ 1777.473201] sd 0:0:0:0: [sda] Attached SCSI removable disk

     Another iteration:

    1821.599851] musb-hdrc musb-hdrc.0: start session

    [ 1821.652488] musb-hdrc musb-hdrc.0: drvvbus high

    [ 1825.879840] musb-hdrc musb-hdrc.0: int 0x10

    [ 1826.139746] usb 1-1: new high-speed USB device number 2 using musb-hdrc

    [ 1826.317139] usb-storage 1-1:1.0: USB Mass Storage device detected

    [ 1826.328451] scsi host0: usb-storage 1-1:1.0

    [ 1827.361764] scsi 0:0:0:0: Direct-Access     SanDisk  Cruzer Blade     1.00 PQ: 0 ANSI: 6

    [ 1827.366732] sd 0:0:0:0: [sda] 124059648 512-byte logical blocks: (63.5 GB/59.2 GiB)

    [ 1827.368748] sd 0:0:0:0: [sda] Write Protect is off

    [ 1827.368822] sd 0:0:0:0: [sda] Mode Sense: 43 00 00 00

    [ 1827.373324] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA

    [ 1827.387482]  sda: sda1

    [ 1827.399143] sd 0:0:0:0: [sda] Attached SCSI removable disk

    Regards,

    Fariya

  • Hi Fariya,

    I reviewed the musb drivers and did some tests again, but don't see anything obvious which could lead to the enumeration problem you are facing with the Sandisk drive, and I am unable to replicate the issue either.

    You mentioned you use kernel v4.9.31 in your first post. v4.9.31 is not used in any TI Processor SDK Linux release. And the driver file musb_dsps.c and musb_core.c you posted a few days ago do not match those in the kernel community v4.9.31 release either, there are some additional patches on top of v4.9.31 release, I am wondering if there is any code change in the kernel you use which causes the issue. So to eliminate this factor, can you test the Sandisk thumb drive with any recently released TI Processor SDK Linux kernel with the first fix patch I sent to see if the problem still happens?

  • Hi Bin Liu,

    Thanks for the reply. The linux kernel version that we are using is 4.9.30 and not 4.9.31.

    Regards,
    Fariya

  • Hi Fariya,

    The musb drivers didn't have any change from v4.9.30 to v4.9.31. So please test with the latest Processor SDK kernel so that we are on the same picture.
  • Ok Bin Liu. Thanks for the help. We will test with latest processor SDK and shall get back.

    Regards,
    Fariya
  • Hi Fariya, any update on the test?
  • Hi Fariya,

    I haven’t heard back from you, I’m assuming you were able to resolve your issue. If not, just post a reply below (or create a new thread if the thread has locked due to time-out). thanks.