diff options
Diffstat (limited to 'drivers/usb/musb/blackfin.c')
| -rw-r--r-- | drivers/usb/musb/blackfin.c | 273 | 
1 files changed, 213 insertions, 60 deletions
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index fcb5206a65b..d40d5f0b552 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -11,21 +11,34 @@  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/sched.h> -#include <linux/init.h>  #include <linux/list.h>  #include <linux/gpio.h>  #include <linux/io.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/prefetch.h> +#include <linux/usb/usb_phy_generic.h>  #include <asm/cacheflush.h>  #include "musb_core.h" +#include "musbhsdma.h"  #include "blackfin.h" +struct bfin_glue { +	struct device		*dev; +	struct platform_device	*musb; +	struct platform_device	*phy; +}; +#define glue_to_musb(g)		platform_get_drvdata(g->musb) +  /*   * Load an endpoint's FIFO   */  void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)  { +	struct musb *musb = hw_ep->musb;  	void __iomem *fifo = hw_ep->fifo;  	void __iomem *epio = hw_ep->regs;  	u8 epnum = hw_ep->epnum; @@ -34,7 +47,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)  	musb_writew(epio, MUSB_TXCOUNT, len); -	DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n", +	dev_dbg(musb->controller, "TX ep%d fifo %p count %d buf %p, epio %p\n",  			hw_ep->epnum, fifo, len, src, epio);  	dump_fifo_data(src, len); @@ -64,7 +77,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)  		bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg);  		SSYNC(); -		/* Wait for compelete */ +		/* Wait for complete */  		while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum)))  			cpu_relax(); @@ -89,6 +102,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)   */  void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)  { +	struct musb *musb = hw_ep->musb;  	void __iomem *fifo = hw_ep->fifo;  	u8 epnum = hw_ep->epnum; @@ -117,7 +131,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)  		bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg);  		SSYNC(); -		/* Wait for compelete */ +		/* Wait for complete */  		while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum)))  			cpu_relax(); @@ -145,7 +159,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)  				*(dst + len - 1) = (u8)inw((unsigned long)fifo + 4);  		}  	} -	DBG(4, "%cX ep%d fifo %p count %d buf %p\n", +	dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",  			'R', hw_ep->epnum, fifo, len, dst);  	dump_fifo_data(dst, len); @@ -171,8 +185,8 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci)  	}  	/* Start sampling ID pin, when plug is removed from MUSB */ -	if ((is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE -		|| musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) || +	if ((musb->xceiv->state == OTG_STATE_B_IDLE +		|| musb->xceiv->state == OTG_STATE_A_WAIT_BCON) ||  		(musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))) {  		mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);  		musb->a_wait_bcon = TIMER_DELAY; @@ -215,18 +229,13 @@ static void musb_conn_timer_handler(unsigned long _musb)  			val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;  			musb_writeb(musb->mregs, MUSB_INTRUSB, val); -			if (is_otg_enabled(musb)) -				musb->xceiv->state = OTG_STATE_B_IDLE; -			else -				musb_writeb(musb->mregs, MUSB_POWER, MUSB_POWER_HSENAB); +			musb->xceiv->state = OTG_STATE_B_IDLE;  		}  		mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);  		break;  	case OTG_STATE_B_IDLE: - -		if (!is_peripheral_enabled(musb)) -			break; -		/* Start a new session.  It seems that MUSB needs taking +		/* +		 * Start a new session.  It seems that MUSB needs taking  		 * some time to recognize the type of the plug inserted?  		 */  		val = musb_readw(musb->mregs, MUSB_DEVCTL); @@ -270,61 +279,75 @@ static void musb_conn_timer_handler(unsigned long _musb)  		}  		break;  	default: -		DBG(1, "%s state not handled\n", otg_state_string(musb)); +		dev_dbg(musb->controller, "%s state not handled\n", +			usb_otg_state_string(musb->xceiv->state));  		break;  	}  	spin_unlock_irqrestore(&musb->lock, flags); -	DBG(4, "state is %s\n", otg_state_string(musb)); +	dev_dbg(musb->controller, "state is %s\n", +		usb_otg_state_string(musb->xceiv->state));  } -void musb_platform_enable(struct musb *musb) +static void bfin_musb_enable(struct musb *musb)  { -	if (!is_otg_enabled(musb) && is_host_enabled(musb)) { -		mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); -		musb->a_wait_bcon = TIMER_DELAY; -	} +	/* REVISIT is this really correct ? */  } -void musb_platform_disable(struct musb *musb) +static void bfin_musb_disable(struct musb *musb)  {  } -static void bfin_set_vbus(struct musb *musb, int is_on) +static void bfin_musb_set_vbus(struct musb *musb, int is_on)  {  	int value = musb->config->gpio_vrsel_active;  	if (!is_on)  		value = !value;  	gpio_set_value(musb->config->gpio_vrsel, value); -	DBG(1, "VBUS %s, devctl %02x " +	dev_dbg(musb->controller, "VBUS %s, devctl %02x "  		/* otg %3x conf %08x prcm %08x */ "\n", -		otg_state_string(musb), +		usb_otg_state_string(musb->xceiv->state),  		musb_readb(musb->mregs, MUSB_DEVCTL));  } -static int bfin_set_power(struct otg_transceiver *x, unsigned mA) +static int bfin_musb_set_power(struct usb_phy *x, unsigned mA)  {  	return 0;  } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static int bfin_musb_vbus_status(struct musb *musb)  { -	if (!is_otg_enabled(musb) && is_host_enabled(musb)) -		mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); +	return 0;  } -int musb_platform_get_vbus_status(struct musb *musb) +static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode)  { -	return 0; +	return -EIO;  } -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int bfin_musb_adjust_channel_params(struct dma_channel *channel, +				u16 packet_sz, u8 *mode, +				dma_addr_t *dma_addr, u32 *len)  { -	return -EIO; +	struct musb_dma_channel *musb_channel = channel->private_data; + +	/* +	 * Anomaly 05000450 might cause data corruption when using DMA +	 * MODE 1 transmits with short packet.  So to work around this, +	 * we truncate all MODE 1 transfers down to a multiple of the +	 * max packet size, and then do the last short packet transfer +	 * (if there is any) using MODE 0. +	 */ +	if (ANOMALY_05000450) { +		if (musb_channel->transmit && *mode == 1) +			*len = *len - (*len % packet_sz); +	} + +	return 0;  } -static void musb_platform_reg_init(struct musb *musb) +static void bfin_musb_reg_init(struct musb *musb)  {  	if (ANOMALY_05000346) {  		bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); @@ -362,7 +385,7 @@ static void musb_platform_reg_init(struct musb *musb)  	SSYNC();  } -int __init musb_platform_init(struct musb *musb, void *board_data) +static int bfin_musb_init(struct musb *musb)  {  	/* @@ -379,32 +402,149 @@ int __init musb_platform_init(struct musb *musb, void *board_data)  	}  	gpio_direction_output(musb->config->gpio_vrsel, 0); -	usb_nop_xceiv_register(); -	musb->xceiv = otg_get_transceiver(); -	if (!musb->xceiv) { +	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); +	if (IS_ERR_OR_NULL(musb->xceiv)) {  		gpio_free(musb->config->gpio_vrsel); -		return -ENODEV; +		return -EPROBE_DEFER;  	} -	musb_platform_reg_init(musb); +	bfin_musb_reg_init(musb); -	if (is_host_enabled(musb)) { -		musb->board_set_vbus = bfin_set_vbus; -		setup_timer(&musb_conn_timer, -			musb_conn_timer_handler, (unsigned long) musb); -	} -	if (is_peripheral_enabled(musb)) -		musb->xceiv->set_power = bfin_set_power; +	setup_timer(&musb_conn_timer, musb_conn_timer_handler, +			(unsigned long) musb); + +	musb->xceiv->set_power = bfin_musb_set_power;  	musb->isr = blackfin_interrupt; +	musb->double_buffer_not_ok = true; + +	return 0; +} + +static int bfin_musb_exit(struct musb *musb) +{ +	gpio_free(musb->config->gpio_vrsel); +	usb_put_phy(musb->xceiv); + +	return 0; +} + +static const struct musb_platform_ops bfin_ops = { +	.init		= bfin_musb_init, +	.exit		= bfin_musb_exit, + +	.enable		= bfin_musb_enable, +	.disable	= bfin_musb_disable, + +	.set_mode	= bfin_musb_set_mode, + +	.vbus_status	= bfin_musb_vbus_status, +	.set_vbus	= bfin_musb_set_vbus, + +	.adjust_channel_params = bfin_musb_adjust_channel_params, +}; + +static u64 bfin_dmamask = DMA_BIT_MASK(32); + +static int bfin_probe(struct platform_device *pdev) +{ +	struct resource musb_resources[2]; +	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); +	struct platform_device		*musb; +	struct bfin_glue		*glue; + +	int				ret = -ENOMEM; + +	glue = kzalloc(sizeof(*glue), GFP_KERNEL); +	if (!glue) { +		dev_err(&pdev->dev, "failed to allocate glue context\n"); +		goto err0; +	} + +	musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); +	if (!musb) { +		dev_err(&pdev->dev, "failed to allocate musb device\n"); +		goto err1; +	} + +	musb->dev.parent		= &pdev->dev; +	musb->dev.dma_mask		= &bfin_dmamask; +	musb->dev.coherent_dma_mask	= bfin_dmamask; + +	glue->dev			= &pdev->dev; +	glue->musb			= musb; + +	pdata->platform_ops		= &bfin_ops; + +	glue->phy = usb_phy_generic_register(); +	if (IS_ERR(glue->phy)) +		goto err2; +	platform_set_drvdata(pdev, glue); + +	memset(musb_resources, 0x00, sizeof(*musb_resources) * +			ARRAY_SIZE(musb_resources)); + +	musb_resources[0].name = pdev->resource[0].name; +	musb_resources[0].start = pdev->resource[0].start; +	musb_resources[0].end = pdev->resource[0].end; +	musb_resources[0].flags = pdev->resource[0].flags; + +	musb_resources[1].name = pdev->resource[1].name; +	musb_resources[1].start = pdev->resource[1].start; +	musb_resources[1].end = pdev->resource[1].end; +	musb_resources[1].flags = pdev->resource[1].flags; + +	ret = platform_device_add_resources(musb, musb_resources, +			ARRAY_SIZE(musb_resources)); +	if (ret) { +		dev_err(&pdev->dev, "failed to add resources\n"); +		goto err3; +	} + +	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); +	if (ret) { +		dev_err(&pdev->dev, "failed to add platform_data\n"); +		goto err3; +	} + +	ret = platform_device_add(musb); +	if (ret) { +		dev_err(&pdev->dev, "failed to register musb device\n"); +		goto err3; +	} + +	return 0; + +err3: +	usb_phy_generic_unregister(glue->phy); + +err2: +	platform_device_put(musb); + +err1: +	kfree(glue); + +err0: +	return ret; +} + +static int bfin_remove(struct platform_device *pdev) +{ +	struct bfin_glue		*glue = platform_get_drvdata(pdev); + +	platform_device_unregister(glue->musb); +	usb_phy_generic_unregister(glue->phy); +	kfree(glue);  	return 0;  }  #ifdef CONFIG_PM -void musb_platform_save_context(struct musb *musb, -			struct musb_context_registers *musb_context) +static int bfin_suspend(struct device *dev)  { +	struct bfin_glue	*glue = dev_get_drvdata(dev); +	struct musb		*musb = glue_to_musb(glue); +  	if (is_host_active(musb))  		/*  		 * During hibernate gpio_vrsel will change from high to low @@ -413,20 +553,33 @@ void musb_platform_save_context(struct musb *musb,  		 * wakeup event.  		 */  		gpio_set_value(musb->config->gpio_vrsel, 0); -} -void musb_platform_restore_context(struct musb *musb, -			struct musb_context_registers *musb_context) -{ -	musb_platform_reg_init(musb); +	return 0;  } -#endif -int musb_platform_exit(struct musb *musb) +static int bfin_resume(struct device *dev)  { -	gpio_free(musb->config->gpio_vrsel); +	struct bfin_glue	*glue = dev_get_drvdata(dev); +	struct musb		*musb = glue_to_musb(glue); + +	bfin_musb_reg_init(musb); -	otg_put_transceiver(musb->xceiv); -	usb_nop_xceiv_unregister();  	return 0;  } +#endif + +static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume); + +static struct platform_driver bfin_driver = { +	.probe		= bfin_probe, +	.remove		= __exit_p(bfin_remove), +	.driver		= { +		.name	= "musb-blackfin", +		.pm	= &bfin_pm_ops, +	}, +}; + +MODULE_DESCRIPTION("Blackfin MUSB Glue Layer"); +MODULE_AUTHOR("Bryan Wy <cooloney@kernel.org>"); +MODULE_LICENSE("GPL v2"); +module_platform_driver(bfin_driver);  | 
