diff options
Diffstat (limited to 'drivers/usb/musb/musb_dsps.c')
| -rw-r--r-- | drivers/usb/musb/musb_dsps.c | 302 | 
1 files changed, 246 insertions, 56 deletions
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4047cbb91ba..09529f94e72 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -29,14 +29,13 @@   * da8xx.c would be merged to this file after testing.   */ -#include <linux/init.h>  #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_gen_xceiv.h> +#include <linux/usb/usb_phy_generic.h>  #include <linux/platform_data/usb-omap.h>  #include <linux/sizes.h> @@ -46,6 +45,8 @@  #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[]; @@ -83,6 +84,8 @@ struct dsps_musb_wrapper {  	u16	coreintr_status;  	u16	phy_utmi;  	u16	mode; +	u16	tx_mode; +	u16	rx_mode;  	/* bit positions for control */  	unsigned	reset:5; @@ -106,10 +109,24 @@ struct dsps_musb_wrapper {  	/* bit positions for mode */  	unsigned	iddig:5; +	unsigned	iddig_mux:5;  	/* miscellaneous stuff */  	u8		poll_seconds;  }; +/* + * 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.   */ @@ -119,8 +136,67 @@ struct dsps_glue {  	const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */  	struct timer_list timer;	/* otg_workaround timer */  	unsigned long last_timer;    /* last timer data for each instance */ + +	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_musb_try_idle(struct musb *musb, unsigned long timeout) +{ +	struct device *dev = musb->controller; +	struct dsps_glue *glue = dev_get_drvdata(dev->parent); + +	if (timeout == 0) +		timeout = jiffies + msecs_to_jiffies(3); + +	/* Never idle if active, or when VBUS timeout is not set as host */ +	if (musb->is_active || (musb->a_wait_bcon == 0 && +				musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { +		dev_dbg(musb->controller, "%s active, deleting timer\n", +				usb_otg_state_string(musb->xceiv->state)); +		del_timer(&glue->timer); +		glue->last_timer = jiffies; +		return; +	} +	if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) +		return; + +	if (!musb->g.dev.driver) +		return; + +	if (time_after(glue->last_timer, timeout) && +				timer_pending(&glue->timer)) { +		dev_dbg(musb->controller, +			"Longer idle timer already pending, ignoring...\n"); +		return; +	} +	glue->last_timer = timeout; + +	dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", +		usb_otg_state_string(musb->xceiv->state), +			jiffies_to_msecs(timeout - jiffies)); +	mod_timer(&glue->timer, timeout); +} +  /**   * dsps_musb_enable - enable interrupts   */ @@ -143,6 +219,7 @@ static void dsps_musb_enable(struct musb *musb)  	/* Force the DRVVBUS IRQ so we can start polling for ID change. */  	dsps_writel(reg_base, wrp->coreintr_set,  		    (1 << wrp->drvvbus) << wrp->usb_shift); +	dsps_musb_try_idle(musb, 0);  }  /** @@ -171,6 +248,7 @@ static void otg_timer(unsigned long _musb)  	const struct dsps_musb_wrapper *wrp = glue->wrp;  	u8 devctl;  	unsigned long flags; +	int skip_session = 0;  	/*  	 * We poll because DSPS IP's won't expose several OTG-critical @@ -183,10 +261,12 @@ static void otg_timer(unsigned long _musb)  	spin_lock_irqsave(&musb->lock, flags);  	switch (musb->xceiv->state) {  	case OTG_STATE_A_WAIT_BCON: -		devctl &= ~MUSB_DEVCTL_SESSION; -		dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl); +		dsps_writeb(musb->mregs, MUSB_DEVCTL, 0); +		skip_session = 1; +		/* fall */ -		devctl = dsps_readb(musb->mregs, MUSB_DEVCTL); +	case OTG_STATE_A_IDLE: +	case OTG_STATE_B_IDLE:  		if (devctl & MUSB_DEVCTL_BDEVICE) {  			musb->xceiv->state = OTG_STATE_B_IDLE;  			MUSB_DEV_MODE(musb); @@ -194,60 +274,21 @@ static void otg_timer(unsigned long _musb)  			musb->xceiv->state = OTG_STATE_A_IDLE;  			MUSB_HST_MODE(musb);  		} +		if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) +			dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); +		mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);  		break;  	case OTG_STATE_A_WAIT_VFALL:  		musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;  		dsps_writel(musb->ctrl_base, wrp->coreintr_set,  			    MUSB_INTR_VBUSERROR << wrp->usb_shift);  		break; -	case OTG_STATE_B_IDLE: -		devctl = dsps_readb(mregs, MUSB_DEVCTL); -		if (devctl & MUSB_DEVCTL_BDEVICE) -			mod_timer(&glue->timer, -					jiffies + wrp->poll_seconds * HZ); -		else -			musb->xceiv->state = OTG_STATE_A_IDLE; -		break;  	default:  		break;  	}  	spin_unlock_irqrestore(&musb->lock, flags);  } -static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) -{ -	struct device *dev = musb->controller; -	struct dsps_glue *glue = dev_get_drvdata(dev->parent); - -	if (timeout == 0) -		timeout = jiffies + msecs_to_jiffies(3); - -	/* Never idle if active, or when VBUS timeout is not set as host */ -	if (musb->is_active || (musb->a_wait_bcon == 0 && -				musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { -		dev_dbg(musb->controller, "%s active, deleting timer\n", -				usb_otg_state_string(musb->xceiv->state)); -		del_timer(&glue->timer); -		glue->last_timer = jiffies; -		return; -	} -	if (musb->port_mode == MUSB_PORT_MODE_HOST) -		return; - -	if (time_after(glue->last_timer, timeout) && -				timer_pending(&glue->timer)) { -		dev_dbg(musb->controller, -			"Longer idle timer already pending, ignoring...\n"); -		return; -	} -	glue->last_timer = timeout; - -	dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", -		usb_otg_state_string(musb->xceiv->state), -			jiffies_to_msecs(timeout - jiffies)); -	mod_timer(&glue->timer, timeout); -} -  static irqreturn_t dsps_interrupt(int irq, void *hci)  {  	struct musb  *musb = hci; @@ -288,9 +329,21 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)  	 * 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) +	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; @@ -339,8 +392,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)  	if (musb->int_tx || musb->int_rx || musb->int_usb)  		ret |= musb_interrupt(musb); -	/* Poll for ID change */ -	if (musb->xceiv->state == OTG_STATE_B_IDLE) +	/* 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); @@ -348,6 +402,30 @@ out:  	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; @@ -357,6 +435,7 @@ static int dsps_musb_init(struct musb *musb)  	void __iomem *reg_base;  	struct resource *r;  	u32 rev, val; +	int ret;  	r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control");  	if (!r) @@ -390,6 +469,10 @@ static int dsps_musb_init(struct musb *musb)  	val &= ~(1 << wrp->otg_disable);  	dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); +	ret = dsps_musb_dbg_init(musb, glue); +	if (ret) +		return ret; +  	return 0;  } @@ -399,11 +482,69 @@ static int dsps_musb_exit(struct musb *musb)  	struct dsps_glue *glue = dev_get_drvdata(dev->parent);  	del_timer_sync(&glue->timer); -  	usb_phy_shutdown(musb->xceiv); +	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 = dsps_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); + +		dsps_writel(ctrl_base, wrp->mode, reg); +		dsps_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); + +		dsps_writel(ctrl_base, wrp->mode, reg); +		break; +	case MUSB_OTG: +		dsps_writel(ctrl_base, wrp->phy_utmi, 0x02); +		break; +	default: +		dev_err(glue->dev, "unsupported mode %d\n", mode); +		return -EINVAL; +	} +  	return 0;  } +static void dsps_musb_reset(struct musb *musb) +{ +	struct device *dev = musb->controller; +	struct dsps_glue *glue = dev_get_drvdata(dev->parent); +	const struct dsps_musb_wrapper *wrp = glue->wrp; + +	dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); +	udelay(100); +} +  static struct musb_platform_ops dsps_ops = {  	.init		= dsps_musb_init,  	.exit		= dsps_musb_exit, @@ -412,6 +553,8 @@ static struct musb_platform_ops dsps_ops = {  	.disable	= dsps_musb_disable,  	.try_idle	= dsps_musb_try_idle, +	.set_mode	= dsps_musb_set_mode, +	.reset		= dsps_musb_reset,  };  static u64 musb_dmamask = DMA_BIT_MASK(32); @@ -443,7 +586,7 @@ static int get_musb_port_mode(struct device *dev)  	case USB_DR_MODE_OTG:  	default:  		return MUSB_PORT_MODE_DUAL_ROLE; -	}; +	}  }  static int dsps_create_musb_pdev(struct dsps_glue *glue, @@ -505,6 +648,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,  	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; @@ -535,6 +679,9 @@ static int dsps_probe(struct platform_device *pdev)  	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"); @@ -543,7 +690,7 @@ static int dsps_probe(struct platform_device *pdev)  	wrp = match->data;  	/* allocate glue */ -	glue = kzalloc(sizeof(*glue), GFP_KERNEL); +	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);  	if (!glue) {  		dev_err(&pdev->dev, "unable to allocate glue memory\n");  		return -ENOMEM; @@ -571,7 +718,6 @@ err3:  	pm_runtime_put(&pdev->dev);  err2:  	pm_runtime_disable(&pdev->dev); -	kfree(glue);  	return ret;  } @@ -584,7 +730,7 @@ static int dsps_remove(struct platform_device *pdev)  	/* disable usbss clocks */  	pm_runtime_put(&pdev->dev);  	pm_runtime_disable(&pdev->dev); -	kfree(glue); +  	return 0;  } @@ -600,9 +746,12 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {  	.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), @@ -623,12 +772,53 @@ static const struct of_device_id musb_dsps_of_match[] = {  };  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 = musb->ctrl_base; + +	glue->context.control = dsps_readl(mbase, wrp->control); +	glue->context.epintr = dsps_readl(mbase, wrp->epintr_set); +	glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set); +	glue->context.phy_utmi = dsps_readl(mbase, wrp->phy_utmi); +	glue->context.mode = dsps_readl(mbase, wrp->mode); +	glue->context.tx_mode = dsps_readl(mbase, wrp->tx_mode); +	glue->context.rx_mode = dsps_readl(mbase, wrp->rx_mode); + +	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 = musb->ctrl_base; + +	dsps_writel(mbase, wrp->control, glue->context.control); +	dsps_writel(mbase, wrp->epintr_set, glue->context.epintr); +	dsps_writel(mbase, wrp->coreintr_set, glue->context.coreintr); +	dsps_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi); +	dsps_writel(mbase, wrp->mode, glue->context.mode); +	dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode); +	dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); + +	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", -		.of_match_table	= of_match_ptr(musb_dsps_of_match), +		.pm	= &dsps_pm_ops, +		.of_match_table	= musb_dsps_of_match,  	},  };  | 
