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); |
