diff options
Diffstat (limited to 'drivers/usb/musb/musb_core.c')
| -rw-r--r-- | drivers/usb/musb/musb_core.c | 195 | 
1 files changed, 162 insertions, 33 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 18e877ffe7b..eff3c5cf84f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -83,7 +83,7 @@   * This gets many kinds of configuration information:   *	- Kconfig for everything user-configurable   *	- platform_device for addressing, irq, and platform_data - *	- platform_data is mostly for board-specific informarion + *	- platform_data is mostly for board-specific information   *	  (plus recentrly, SOC or family details)   *   * Most of the conditional compilation will (someday) vanish. @@ -93,7 +93,6 @@  #include <linux/kernel.h>  #include <linux/sched.h>  #include <linux/slab.h> -#include <linux/init.h>  #include <linux/list.h>  #include <linux/kobject.h>  #include <linux/prefetch.h> @@ -439,7 +438,6 @@ void musb_hnp_stop(struct musb *musb)  static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,  				u8 devctl)  { -	struct usb_otg *otg = musb->xceiv->otg;  	irqreturn_t handled = IRQ_NONE;  	dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl, @@ -479,7 +477,10 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,  						(USB_PORT_STAT_C_SUSPEND << 16)  						| MUSB_PORT_STAT_RESUME;  				musb->rh_timer = jiffies -						+ msecs_to_jiffies(20); +						 + msecs_to_jiffies(20); +				schedule_delayed_work( +					&musb->finish_resume_work, +					msecs_to_jiffies(20));  				musb->xceiv->state = OTG_STATE_A_HOST;  				musb->is_active = 1; @@ -617,7 +618,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,  				/* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */  				default:  					s = "VALID"; break; -				}; s; }), +				} s; }),  				VBUSERR_RETRY_COUNT - musb->vbuserr_retry,  				musb->port1_status); @@ -654,7 +655,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,  				break;  		case OTG_STATE_B_PERIPHERAL:  			musb_g_suspend(musb); -			musb->is_active = otg->gadget->b_hnp_enable; +			musb->is_active = musb->g.b_hnp_enable;  			if (musb->is_active) {  				musb->xceiv->state = OTG_STATE_B_WAIT_ACON;  				dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); @@ -670,7 +671,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,  			break;  		case OTG_STATE_A_HOST:  			musb->xceiv->state = OTG_STATE_A_SUSPEND; -			musb->is_active = otg->host->b_hnp_enable; +			musb->is_active = musb->hcd->self.b_hnp_enable;  			break;  		case OTG_STATE_B_HOST:  			/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ @@ -847,6 +848,10 @@ b_host:  		}  	} +	/* handle babble condition */ +	if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) +		schedule_work(&musb->recover_work); +  #if 0  /* REVISIT ... this would be for multiplexing periodic endpoints, or   * supporting transfer phasing to prevent exceeding ISO bandwidth @@ -922,6 +927,52 @@ static void musb_generic_disable(struct musb *musb)  }  /* + * Program the HDRC to start (enable interrupts, dma, etc.). + */ +void musb_start(struct musb *musb) +{ +	void __iomem    *regs = musb->mregs; +	u8              devctl = musb_readb(regs, MUSB_DEVCTL); + +	dev_dbg(musb->controller, "<== devctl %02x\n", devctl); + +	/*  Set INT enable registers, enable interrupts */ +	musb->intrtxe = musb->epmask; +	musb_writew(regs, MUSB_INTRTXE, musb->intrtxe); +	musb->intrrxe = musb->epmask & 0xfffe; +	musb_writew(regs, MUSB_INTRRXE, musb->intrrxe); +	musb_writeb(regs, MUSB_INTRUSBE, 0xf7); + +	musb_writeb(regs, MUSB_TESTMODE, 0); + +	/* put into basic highspeed mode and start session */ +	musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE +			| MUSB_POWER_HSENAB +			/* ENSUSPEND wedges tusb */ +			/* | MUSB_POWER_ENSUSPEND */ +		   ); + +	musb->is_active = 0; +	devctl = musb_readb(regs, MUSB_DEVCTL); +	devctl &= ~MUSB_DEVCTL_SESSION; + +	/* session started after: +	 * (a) ID-grounded irq, host mode; +	 * (b) vbus present/connect IRQ, peripheral mode; +	 * (c) peripheral initiates, using SRP +	 */ +	if (musb->port_mode != MUSB_PORT_MODE_HOST && +			(devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) { +		musb->is_active = 1; +	} else { +		devctl |= MUSB_DEVCTL_SESSION; +	} + +	musb_platform_enable(musb); +	musb_writeb(regs, MUSB_DEVCTL, devctl); +} + +/*   * Make the HDRC stop (disable interrupts, etc.);   * reversible by musb_start   * called on gadget driver unregister @@ -1141,7 +1192,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep  *hw_ep,  	musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum);  	/* EP0 reserved endpoint for control, bidirectional; -	 * EP1 reserved for bulk, two unidirection halves. +	 * EP1 reserved for bulk, two unidirectional halves.  	 */  	if (hw_ep->epnum == 1)  		musb->bulk_ep = hw_ep; @@ -1699,6 +1750,34 @@ static void musb_irq_work(struct work_struct *data)  	}  } +/* Recover from babble interrupt conditions */ +static void musb_recover_work(struct work_struct *data) +{ +	struct musb *musb = container_of(data, struct musb, recover_work); +	int status; + +	musb_platform_reset(musb); + +	usb_phy_vbus_off(musb->xceiv); +	udelay(100); + +	usb_phy_vbus_on(musb->xceiv); +	udelay(100); + +	/* +	 * When a babble condition occurs, the musb controller removes the +	 * session bit and the endpoint config is lost. +	 */ +	if (musb->dyn_fifo) +		status = ep_config_from_table(musb); +	else +		status = ep_config_from_hw(musb); + +	/* start the session again */ +	if (status == 0) +		musb_start(musb); +} +  /* --------------------------------------------------------------------------   * Init support   */ @@ -1763,12 +1842,25 @@ static void musb_free(struct musb *musb)  			disable_irq_wake(musb->nIrq);  		free_irq(musb->nIrq, musb);  	} -	if (musb->dma_controller) -		dma_controller_destroy(musb->dma_controller);  	musb_host_free(musb);  } +static void musb_deassert_reset(struct work_struct *work) +{ +	struct musb *musb; +	unsigned long flags; + +	musb = container_of(work, struct musb, deassert_reset_work.work); + +	spin_lock_irqsave(&musb->lock, flags); + +	if (musb->port1_status & USB_PORT_STAT_RESET) +		musb_port_reset(musb, false); + +	spin_unlock_irqrestore(&musb->lock, flags); +} +  /*   * Perform generic per-controller initialization.   * @@ -1813,7 +1905,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)  	/* The musb_platform_init() call:  	 *   - adjusts musb->mregs  	 *   - sets the musb->isr -	 *   - may initialize an integrated tranceiver +	 *   - may initialize an integrated transceiver  	 *   - initializes musb->xceiv, usually by otg_get_phy()  	 *   - stops powering VBUS  	 * @@ -1839,13 +1931,24 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)  	pm_runtime_get_sync(musb->controller); -	if (use_dma && dev->dma_mask) +	if (use_dma && dev->dma_mask) {  		musb->dma_controller = dma_controller_create(musb, musb->mregs); +		if (IS_ERR(musb->dma_controller)) { +			status = PTR_ERR(musb->dma_controller); +			goto fail2_5; +		} +	}  	/* be sure interrupts are disabled before connecting ISR */  	musb_platform_disable(musb);  	musb_generic_disable(musb); +	/* Init IRQ workqueue before request_irq */ +	INIT_WORK(&musb->irq_work, musb_irq_work); +	INIT_WORK(&musb->recover_work, musb_recover_work); +	INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); +	INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); +  	/* setup musb parts of the core (especially endpoints) */  	status = musb_core_init(plat->config->multipoint  			? MUSB_CONTROLLER_MHDRC @@ -1855,9 +1958,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)  	setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); -	/* Init IRQ workqueue before request_irq */ -	INIT_WORK(&musb->irq_work, musb_irq_work); -  	/* attach to the IRQ */  	if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {  		dev_err(dev, "request_irq %d failed!\n", nIrq); @@ -1891,15 +1991,26 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)  	switch (musb->port_mode) {  	case MUSB_PORT_MODE_HOST:  		status = musb_host_setup(musb, plat->power); +		if (status < 0) +			goto fail3; +		status = musb_platform_set_mode(musb, MUSB_HOST);  		break;  	case MUSB_PORT_MODE_GADGET:  		status = musb_gadget_setup(musb); +		if (status < 0) +			goto fail3; +		status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);  		break;  	case MUSB_PORT_MODE_DUAL_ROLE:  		status = musb_host_setup(musb, plat->power);  		if (status < 0)  			goto fail3;  		status = musb_gadget_setup(musb); +		if (status) { +			musb_host_cleanup(musb); +			goto fail3; +		} +		status = musb_platform_set_mode(musb, MUSB_OTG);  		break;  	default:  		dev_err(dev, "unsupported port mode %d\n", musb->port_mode); @@ -1926,10 +2037,16 @@ fail5:  fail4:  	musb_gadget_cleanup(musb); +	musb_host_cleanup(musb);  fail3: +	cancel_work_sync(&musb->irq_work); +	cancel_work_sync(&musb->recover_work); +	cancel_delayed_work_sync(&musb->finish_resume_work); +	cancel_delayed_work_sync(&musb->deassert_reset_work);  	if (musb->dma_controller)  		dma_controller_destroy(musb->dma_controller); +fail2_5:  	pm_runtime_put_sync(musb->controller);  fail2: @@ -1986,6 +2103,13 @@ static int musb_remove(struct platform_device *pdev)  	musb_exit_debugfs(musb);  	musb_shutdown(pdev); +	if (musb->dma_controller) +		dma_controller_destroy(musb->dma_controller); + +	cancel_work_sync(&musb->irq_work); +	cancel_work_sync(&musb->recover_work); +	cancel_delayed_work_sync(&musb->finish_resume_work); +	cancel_delayed_work_sync(&musb->deassert_reset_work);  	musb_free(musb);  	device_init_wakeup(dev, 0);  	return 0; @@ -2070,11 +2194,19 @@ static void musb_restore_context(struct musb *musb)  	void __iomem *musb_base = musb->mregs;  	void __iomem *ep_target_regs;  	void __iomem *epio; +	u8 power;  	musb_writew(musb_base, MUSB_FRAME, musb->context.frame);  	musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode);  	musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl); -	musb_writeb(musb_base, MUSB_POWER, musb->context.power); + +	/* Don't affect SUSPENDM/RESUME bits in POWER reg */ +	power = musb_readb(musb_base, MUSB_POWER); +	power &= MUSB_POWER_SUSPENDM | MUSB_POWER_RESUME; +	musb->context.power &= ~(MUSB_POWER_SUSPENDM | MUSB_POWER_RESUME); +	power |= musb->context.power; +	musb_writeb(musb_base, MUSB_POWER, power); +  	musb_writew(musb_base, MUSB_INTRTXE, musb->intrtxe);  	musb_writew(musb_base, MUSB_INTRRXE, musb->intrrxe);  	musb_writeb(musb_base, MUSB_INTRUSBE, musb->context.intrusbe); @@ -2158,16 +2290,28 @@ static int musb_suspend(struct device *dev)  		 */  	} +	musb_save_context(musb); +  	spin_unlock_irqrestore(&musb->lock, flags);  	return 0;  }  static int musb_resume_noirq(struct device *dev)  { -	/* for static cmos like DaVinci, register values were preserved +	struct musb	*musb = dev_to_musb(dev); + +	/* +	 * For static cmos like DaVinci, register values were preserved  	 * unless for some reason the whole soc powered down or the USB  	 * module got reset through the PSC (vs just being disabled). +	 * +	 * For the DSPS glue layer though, a full register restore has to +	 * be done. As it shouldn't harm other platforms, we do it +	 * unconditionally.  	 */ + +	musb_restore_context(musb); +  	return 0;  } @@ -2225,19 +2369,4 @@ static struct platform_driver musb_driver = {  	.shutdown	= musb_shutdown,  }; -/*-------------------------------------------------------------------------*/ - -static int __init musb_init(void) -{ -	if (usb_disabled()) -		return 0; - -	return platform_driver_register(&musb_driver); -} -module_init(musb_init); - -static void __exit musb_cleanup(void) -{ -	platform_driver_unregister(&musb_driver); -} -module_exit(musb_cleanup); +module_platform_driver(musb_driver);  | 
