diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-15 09:29:38 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-15 09:29:38 -0800 |
commit | 5cdc5bd8b2b1190cb54548c03194b154b4892e2a (patch) | |
tree | 79e9f66825a1155605a30e063ba59297a4ad0618 /drivers/usb | |
parent | 6c34d2888221ca3df81e29f598873b4fb6cf838d (diff) | |
parent | e4a2b3565fc7ac2d70361a36337be57a59d783da (diff) | |
download | blackbird-op-linux-5cdc5bd8b2b1190cb54548c03194b154b4892e2a.tar.gz blackbird-op-linux-5cdc5bd8b2b1190cb54548c03194b154b4892e2a.zip |
Merge branch 'musb-hw' of git://gitorious.org/usb/usb into musb
* 'musb-hw' of git://gitorious.org/usb/usb: (43 commits)
usb: musb: core: kill unneeded #include's
DA8xx: assign name to MUSB IRQ resource
arm: OMAP4430: musb: Configure musb to OTG mode
usb: musb: Adding musb support for OMAP4430
usb: otg: TWL6030: Add twl6030_usb file for compilation
mfd: TWL6030: OMAP4: Registering the TWL6030-usb device
usb: musb: TWL6030: Selecting TWL6030_USB transceiver
usb: otg: Kconfig: Add Kconfig option for TWL6030 transceiver.
usb: otg: Adding twl6030-usb transceiver driver for OMAP4430
mfd: TWL6030: USBOTG VBUS event generation on
usb: musb: add support for ux500 platform
musb: am35x: fix compile error due to control apis
arm: omap4: enable usb on 4430sdp
usb: musb: drop board_set_vbus
usb: musb: drop musb_platform_suspend/resume
usb: musb: blackfin: usb dev_pm_ops structure
usb: musb: am35x: usb dev_pm_ops structure
usb: musb: omap2430: use dev_pm_ops structure
usb: musb: omap2430: drop the nops
usb: musb: mark musb_save/restore_context static
...
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/gadget_chips.h | 2 | ||||
-rw-r--r-- | drivers/usb/musb/Kconfig | 77 | ||||
-rw-r--r-- | drivers/usb/musb/Makefile | 21 | ||||
-rw-r--r-- | drivers/usb/musb/am35x.c | 410 | ||||
-rw-r--r-- | drivers/usb/musb/blackfin.c | 229 | ||||
-rw-r--r-- | drivers/usb/musb/da8xx.c | 170 | ||||
-rw-r--r-- | drivers/usb/musb/davinci.c | 174 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.c | 191 | ||||
-rw-r--r-- | drivers/usb/musb/musb_core.h | 190 | ||||
-rw-r--r-- | drivers/usb/musb/musb_io.h | 4 | ||||
-rw-r--r-- | drivers/usb/musb/musb_regs.h | 4 | ||||
-rw-r--r-- | drivers/usb/musb/musb_virthub.c | 2 | ||||
-rw-r--r-- | drivers/usb/musb/omap2430.c | 374 | ||||
-rw-r--r-- | drivers/usb/musb/tusb6010.c | 181 | ||||
-rw-r--r-- | drivers/usb/musb/ux500.c | 216 | ||||
-rw-r--r-- | drivers/usb/otg/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/otg/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/otg/twl6030-usb.c | 493 |
18 files changed, 2166 insertions, 585 deletions
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index ad2114e87511..5c2720d64ffa 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -96,7 +96,7 @@ /* Mentor high speed "dual role" controller, in peripheral role */ #ifdef CONFIG_USB_GADGET_MUSB_HDRC -#define gadget_is_musbhdrc(g) !strcmp("musb_hdrc", (g)->name) +#define gadget_is_musbhdrc(g) !strcmp("musb-hdrc", (g)->name) #else #define gadget_is_musbhdrc(g) 0 #endif diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 341a37a469bd..4cbb7e4b368d 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -12,6 +12,7 @@ config USB_MUSB_HDRC depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523)) select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN) select TWL4030_USB if MACH_OMAP_3430SDP + select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help @@ -30,57 +31,41 @@ config USB_MUSB_HDRC If you do not know what this is, please say N. To compile this driver as a module, choose M here; the - module will be called "musb_hdrc". + module will be called "musb-hdrc". -config USB_MUSB_SOC - boolean +choice + prompt "Platform Glue Layer" depends on USB_MUSB_HDRC - default y if ARCH_DAVINCI - default y if ARCH_OMAP2430 - default y if ARCH_OMAP3 - default y if ARCH_OMAP4 - default y if (BF54x && !BF544) - default y if (BF52x && !BF522 && !BF523) -comment "DaVinci 35x and 644x USB support" - depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx +config USB_MUSB_DAVINCI + bool "DaVinci" + depends on ARCH_DAVINCI_DMx -comment "DA8xx/OMAP-L1x USB support" - depends on USB_MUSB_HDRC && ARCH_DAVINCI_DA8XX +config USB_MUSB_DA8XX + bool "DA8xx/OMAP-L1x" + depends on ARCH_DAVINCI_DA8XX -comment "OMAP 243x high speed USB support" - depends on USB_MUSB_HDRC && ARCH_OMAP2430 +config USB_MUSB_TUSB6010 + bool "TUSB6010" + depends on ARCH_OMAP -comment "OMAP 343x high speed USB support" - depends on USB_MUSB_HDRC && ARCH_OMAP3 +config USB_MUSB_OMAP2PLUS + bool "OMAP2430 and onwards" + depends on ARCH_OMAP2PLUS -comment "OMAP 44xx high speed USB support" - depends on USB_MUSB_HDRC && ARCH_OMAP4 +config USB_MUSB_AM35X + bool "AM35x" + depends on ARCH_OMAP -comment "Blackfin high speed USB Support" - depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523)) +config USB_MUSB_BLACKFIN + bool "Blackfin" + depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523) -config USB_MUSB_AM35X - bool - depends on USB_MUSB_HDRC && !ARCH_OMAP2430 && !ARCH_OMAP4 - select NOP_USB_XCEIV - default MACH_OMAP3517EVM - help - Select this option if your platform is based on AM35x. As - AM35x has an updated MUSB with CPPI4.1 DMA so this config - is introduced to differentiate musb ip between OMAP3x and - AM35x platforms. - -config USB_TUSB6010 - boolean "TUSB 6010 support" - depends on USB_MUSB_HDRC && !USB_MUSB_SOC - select NOP_USB_XCEIV - default y - help - The TUSB 6010 chip, from Texas Instruments, connects a discrete - HDRC core using a 16-bit parallel bus (NOR flash style) or VLYNQ - (a high speed serial link). It can use system-specific external - DMA controllers. +config USB_MUSB_UX500 + bool "U8500 and U5500" + depends on (ARCH_U8500 && AB8500_USB) || (ARCH_U5500) + +endchoice choice prompt "Driver Mode" @@ -158,7 +143,7 @@ config USB_MUSB_HDRC_HCD config MUSB_PIO_ONLY bool 'Disable DMA (always use PIO)' depends on USB_MUSB_HDRC - default USB_TUSB6010 || ARCH_DAVINCI_DA8XX || USB_MUSB_AM35X + default USB_MUSB_TUSB6010 || USB_MUSB_DA8XX || USB_MUSB_AM35X help All data is copied between memory and FIFO by the CPU. DMA controllers are ignored. @@ -171,21 +156,21 @@ config MUSB_PIO_ONLY config USB_INVENTRA_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY - default ARCH_OMAP2430 || ARCH_OMAP3 || BLACKFIN || ARCH_OMAP4 + default USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN help Enable DMA transfers using Mentor's engine. config USB_TI_CPPI_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY - default ARCH_DAVINCI + default USB_MUSB_DAVINCI help Enable DMA transfers when TI CPPI DMA is available. config USB_TUSB_OMAP_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY - depends on USB_TUSB6010 + depends on USB_MUSB_TUSB6010 depends on ARCH_OMAP default y help diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index ce164e8998d8..74df5284894f 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -8,22 +8,19 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o musb_hdrc-y := musb_core.o -musb_hdrc-$(CONFIG_ARCH_DAVINCI_DMx) += davinci.o -musb_hdrc-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx.o -musb_hdrc-$(CONFIG_USB_TUSB6010) += tusb6010.o -musb_hdrc-$(CONFIG_ARCH_OMAP2430) += omap2430.o -ifeq ($(CONFIG_USB_MUSB_AM35X),y) - musb_hdrc-$(CONFIG_ARCH_OMAP3430) += am35x.o -else - musb_hdrc-$(CONFIG_ARCH_OMAP3430) += omap2430.o -endif -musb_hdrc-$(CONFIG_ARCH_OMAP4) += omap2430.o -musb_hdrc-$(CONFIG_BF54x) += blackfin.o -musb_hdrc-$(CONFIG_BF52x) += blackfin.o musb_hdrc-$(CONFIG_USB_GADGET_MUSB_HDRC) += musb_gadget_ep0.o musb_gadget.o musb_hdrc-$(CONFIG_USB_MUSB_HDRC_HCD) += musb_virthub.o musb_host.o musb_hdrc-$(CONFIG_DEBUG_FS) += musb_debugfs.o +# Hardware Glue Layer +obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o +obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o +obj-$(CONFIG_USB_MUSB_TUSB6010) += tusb6010.o +obj-$(CONFIG_USB_MUSB_DAVINCI) += davinci.o +obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o +obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o +obj-$(CONFIG_USB_MUSB_UX500) += ux500.o + # the kconfig must guarantee that only one of the # possible I/O schemes will be enabled at a time ... # PIO only, or DMA (several potential schemes). diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index b0aabf3a606f..d5a3da37c90c 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -29,8 +29,9 @@ #include <linux/init.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> -#include <plat/control.h> #include <plat/usb.h> #include "musb_core.h" @@ -80,51 +81,18 @@ #define USB_MENTOR_CORE_OFFSET 0x400 -static inline void phy_on(void) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(100); - u32 devconf2; - - /* - * Start the on-chip PHY and its PLL. - */ - devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); - - devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN); - devconf2 |= CONF2_PHY_PLLON; - - omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); - - DBG(1, "Waiting for PHY clock good...\n"); - while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2) - & CONF2_PHYCLKGD)) { - cpu_relax(); - - if (time_after(jiffies, timeout)) { - DBG(1, "musb PHY clock good timed out\n"); - break; - } - } -} - -static inline void phy_off(void) -{ - u32 devconf2; - - /* - * Power down the on-chip PHY. - */ - devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); - - devconf2 &= ~CONF2_PHY_PLLON; - devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN; - omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); -} +struct am35x_glue { + struct device *dev; + struct platform_device *musb; + struct clk *phy_clk; + struct clk *clk; +}; +#define glue_to_musb(g) platform_get_drvdata(g->musb) /* - * musb_platform_enable - enable interrupts + * am35x_musb_enable - enable interrupts */ -void musb_platform_enable(struct musb *musb) +static void am35x_musb_enable(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; u32 epmask; @@ -143,9 +111,9 @@ void musb_platform_enable(struct musb *musb) } /* - * musb_platform_disable - disable HDRC and flush interrupts + * am35x_musb_disable - disable HDRC and flush interrupts */ -void musb_platform_disable(struct musb *musb) +static void am35x_musb_disable(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; @@ -162,7 +130,7 @@ void musb_platform_disable(struct musb *musb) #define portstate(stmt) #endif -static void am35x_set_vbus(struct musb *musb, int is_on) +static void am35x_musb_set_vbus(struct musb *musb, int is_on) { WARN_ON(is_on && is_peripheral_active(musb)); } @@ -221,7 +189,7 @@ static void otg_timer(unsigned long _musb) spin_unlock_irqrestore(&musb->lock, flags); } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) { static unsigned long last_timer; @@ -251,13 +219,16 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout) mod_timer(&otg_workaround, timeout); } -static irqreturn_t am35x_interrupt(int irq, void *hci) +static irqreturn_t am35x_musb_interrupt(int irq, void *hci) { struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; unsigned long flags; irqreturn_t ret = IRQ_NONE; - u32 epintr, usbintr, lvl_intr; + u32 epintr, usbintr; spin_lock_irqsave(&musb->lock, flags); @@ -346,9 +317,8 @@ eoi: /* EOI needs to be written for the IRQ to be re-asserted. */ if (ret == IRQ_HANDLED || epintr || usbintr) { /* clear level interrupt */ - lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); - lvl_intr |= AM35XX_USBOTGSS_INT_CLR; - omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR); + if (data->clear_irq) + data->clear_irq(); /* write EOI */ musb_writel(reg_base, USB_END_OF_INTR_REG, 0); } @@ -362,137 +332,85 @@ eoi: return ret; } -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode) { - u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + int retval = 0; - devconf2 &= ~CONF2_OTGMODE; - switch (musb_mode) { -#ifdef CONFIG_USB_MUSB_HDRC_HCD - case MUSB_HOST: /* Force VBUS valid, ID = 0 */ - devconf2 |= CONF2_FORCE_HOST; - break; -#endif -#ifdef CONFIG_USB_GADGET_MUSB_HDRC - case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */ - devconf2 |= CONF2_FORCE_DEVICE; - break; -#endif -#ifdef CONFIG_USB_MUSB_OTG - case MUSB_OTG: /* Don't override the VBUS/ID comparators */ - devconf2 |= CONF2_NO_OVERRIDE; - break; -#endif - default: - DBG(2, "Trying to set unsupported mode %u\n", musb_mode); - } + if (data->set_mode) + data->set_mode(musb_mode); + else + retval = -EIO; - omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); - return 0; + return retval; } -int __init musb_platform_init(struct musb *musb, void *board_data) +static int am35x_musb_init(struct musb *musb) { + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; void __iomem *reg_base = musb->ctrl_base; - u32 rev, lvl_intr, sw_reset; - int status; + u32 rev; musb->mregs += USB_MENTOR_CORE_OFFSET; - clk_enable(musb->clock); - DBG(2, "musb->clock=%lud\n", clk_get_rate(musb->clock)); - - musb->phy_clock = clk_get(musb->controller, "fck"); - if (IS_ERR(musb->phy_clock)) { - status = PTR_ERR(musb->phy_clock); - goto exit0; - } - clk_enable(musb->phy_clock); - DBG(2, "musb->phy_clock=%lud\n", clk_get_rate(musb->phy_clock)); - /* Returns zero if e.g. not clocked */ rev = musb_readl(reg_base, USB_REVISION_REG); - if (!rev) { - status = -ENODEV; - goto exit1; - } + if (!rev) + return -ENODEV; usb_nop_xceiv_register(); musb->xceiv = otg_get_transceiver(); - if (!musb->xceiv) { - status = -ENODEV; - goto exit1; - } + if (!musb->xceiv) + return -ENODEV; if (is_host_enabled(musb)) setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); - musb->board_set_vbus = am35x_set_vbus; - - /* Global reset */ - sw_reset = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); - - sw_reset |= AM35XX_USBOTGSS_SW_RST; - omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET); - - sw_reset &= ~AM35XX_USBOTGSS_SW_RST; - omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET); + /* Reset the musb */ + if (data->reset) + data->reset(); /* Reset the controller */ musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK); /* Start the on-chip PHY and its PLL. */ - phy_on(); + if (data->set_phy_power) + data->set_phy_power(1); msleep(5); - musb->isr = am35x_interrupt; + musb->isr = am35x_musb_interrupt; /* clear level interrupt */ - lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); - lvl_intr |= AM35XX_USBOTGSS_INT_CLR; - omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR); + if (data->clear_irq) + data->clear_irq(); + return 0; -exit1: - clk_disable(musb->phy_clock); - clk_put(musb->phy_clock); -exit0: - clk_disable(musb->clock); - return status; } -int musb_platform_exit(struct musb *musb) +static int am35x_musb_exit(struct musb *musb) { + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + if (is_host_enabled(musb)) del_timer_sync(&otg_workaround); - phy_off(); + /* Shutdown the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(0); otg_put_transceiver(musb->xceiv); usb_nop_xceiv_unregister(); - clk_disable(musb->clock); - - clk_disable(musb->phy_clock); - clk_put(musb->phy_clock); - return 0; } -#ifdef CONFIG_PM -void musb_platform_save_context(struct musb *musb, - struct musb_context_registers *musb_context) -{ - phy_off(); -} - -void musb_platform_restore_context(struct musb *musb, - struct musb_context_registers *musb_context) -{ - phy_on(); -} -#endif - /* AM35x supports only 32bit read operation */ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { @@ -522,3 +440,215 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) memcpy(dst, &val, len); } } + +static const struct musb_platform_ops am35x_ops = { + .init = am35x_musb_init, + .exit = am35x_musb_exit, + + .enable = am35x_musb_enable, + .disable = am35x_musb_disable, + + .set_mode = am35x_musb_set_mode, + .try_idle = am35x_musb_try_idle, + + .set_vbus = am35x_musb_set_vbus, +}; + +static u64 am35x_dmamask = DMA_BIT_MASK(32); + +static int __init am35x_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct am35x_glue *glue; + + struct clk *phy_clk; + struct clk *clk; + + 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", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } + + phy_clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(phy_clk)) { + dev_err(&pdev->dev, "failed to get PHY clock\n"); + ret = PTR_ERR(phy_clk); + goto err2; + } + + clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err3; + } + + ret = clk_enable(phy_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable PHY clock\n"); + goto err4; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err5; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &am35x_dmamask; + musb->dev.coherent_dma_mask = am35x_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->phy_clk = phy_clk; + glue->clk = clk; + + pdata->platform_ops = &am35x_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err6; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err6; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err6; + } + + return 0; + +err6: + clk_disable(clk); + +err5: + clk_disable(phy_clk); + +err4: + clk_put(clk); + +err3: + clk_put(phy_clk); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __exit am35x_remove(struct platform_device *pdev) +{ + struct am35x_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + clk_disable(glue->clk); + clk_disable(glue->phy_clk); + clk_put(glue->clk); + clk_put(glue->phy_clk); + kfree(glue); + + return 0; +} + +#ifdef CONFIG_PM +static int am35x_suspend(struct device *dev) +{ + struct am35x_glue *glue = dev_get_drvdata(dev); + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + + /* Shutdown the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(0); + + clk_disable(glue->phy_clk); + clk_disable(glue->clk); + + return 0; +} + +static int am35x_resume(struct device *dev) +{ + struct am35x_glue *glue = dev_get_drvdata(dev); + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct omap_musb_board_data *data = plat->board_data; + int ret; + + /* Start the on-chip PHY and its PLL. */ + if (data->set_phy_power) + data->set_phy_power(1); + + ret = clk_enable(glue->phy_clk); + if (ret) { + dev_err(dev, "failed to enable PHY clock\n"); + return ret; + } + + ret = clk_enable(glue->clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + + return 0; +} + +static struct dev_pm_ops am35x_pm_ops = { + .suspend = am35x_suspend, + .resume = am35x_resume, +}; + +#define DEV_PM_OPS &am35x_pm_ops +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver am35x_driver = { + .remove = __exit_p(am35x_remove), + .driver = { + .name = "musb-am35x", + .pm = DEV_PM_OPS, + }, +}; + +MODULE_DESCRIPTION("AM35x MUSB Glue Layer"); +MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init am35x_init(void) +{ + return platform_driver_probe(&am35x_driver, am35x_probe); +} +subsys_initcall(am35x_init); + +static void __exit am35x_exit(void) +{ + platform_driver_unregister(&am35x_driver); +} +module_exit(am35x_exit); diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index e8cbcc59c419..e72f762d214d 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -15,12 +15,20 @@ #include <linux/list.h> #include <linux/gpio.h> #include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include <asm/cacheflush.h> #include "musb_core.h" #include "blackfin.h" +struct bfin_glue { + struct device *dev; + struct platform_device *musb; +}; +#define glue_to_musb(g) platform_get_drvdata(g->musb) + /* * Load an endpoint's FIFO */ @@ -277,7 +285,7 @@ static void musb_conn_timer_handler(unsigned long _musb) DBG(4, "state is %s\n", otg_state_string(musb)); } -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); @@ -285,11 +293,11 @@ void musb_platform_enable(struct musb *musb) } } -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) @@ -302,51 +310,29 @@ static void bfin_set_vbus(struct musb *musb, int is_on) musb_readb(musb->mregs, MUSB_DEVCTL)); } -static int bfin_set_power(struct otg_transceiver *x, unsigned mA) +static int bfin_musb_set_power(struct otg_transceiver *x, unsigned mA) { return 0; } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout) { if (!is_otg_enabled(musb) && is_host_enabled(musb)) mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); } -int musb_platform_get_vbus_status(struct musb *musb) +static int bfin_musb_get_vbus_status(struct musb *musb) { return 0; } -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode) { return -EIO; } -int __init musb_platform_init(struct musb *musb) +static void bfin_musb_reg_init(struct musb *musb) { - - /* - * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE - * and OTG HOST modes, while rev 1.1 and greater require PE7 to - * be low for DEVICE mode and high for HOST mode. We set it high - * here because we are in host mode - */ - - if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) { - printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d \n", - musb->config->gpio_vrsel); - return -ENODEV; - } - gpio_direction_output(musb->config->gpio_vrsel, 0); - - usb_nop_xceiv_register(); - musb->xceiv = otg_get_transceiver(); - if (!musb->xceiv) { - gpio_free(musb->config->gpio_vrsel); - return -ENODEV; - } - if (ANOMALY_05000346) { bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); SSYNC(); @@ -380,21 +366,47 @@ int __init musb_platform_init(struct musb *musb) EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA | EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA); SSYNC(); +} + +static int bfin_musb_init(struct musb *musb) +{ + + /* + * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE + * and OTG HOST modes, while rev 1.1 and greater require PE7 to + * be low for DEVICE mode and high for HOST mode. We set it high + * here because we are in host mode + */ + + if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) { + printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d\n", + musb->config->gpio_vrsel); + return -ENODEV; + } + gpio_direction_output(musb->config->gpio_vrsel, 0); + + usb_nop_xceiv_register(); + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) { + gpio_free(musb->config->gpio_vrsel); + return -ENODEV; + } + + 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; + musb->xceiv->set_power = bfin_musb_set_power; musb->isr = blackfin_interrupt; return 0; } -int musb_platform_exit(struct musb *musb) +static int bfin_musb_exit(struct musb *musb) { gpio_free(musb->config->gpio_vrsel); @@ -402,3 +414,154 @@ int musb_platform_exit(struct musb *musb) usb_nop_xceiv_unregister(); 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, + .try_idle = bfin_musb_try_idle, + + .vbus_status = bfin_musb_vbus_status, + .set_vbus = bfin_musb_set_vbus, +}; + +static u64 bfin_dmamask = DMA_BIT_MASK(32); + +static int __init bfin_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + 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", -1); + 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; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err2; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err2; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err2; + } + + return 0; + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __exit bfin_remove(struct platform_device *pdev) +{ + struct bfin_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + kfree(glue); + + return 0; +} + +#ifdef CONFIG_PM +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 + * low which will generate wakeup event resume the system + * immediately. Set it to 0 before hibernate to avoid this + * wakeup event. + */ + gpio_set_value(musb->config->gpio_vrsel, 0); + + return 0; +} + +static int bfin_resume(struct device *dev) +{ + struct bfin_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + + bfin_musb_reg_init(musb); + + return 0; +} + +static struct dev_pm_ops bfin_pm_ops = { + .suspend = bfin_suspend, + .resume = bfin_resume, +}; + +#define DEV_PM_OPS &bfin_pm_op, +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver bfin_driver = { + .remove = __exit_p(bfin_remove), + .driver = { + .name = "musb-bfin", + .pm = DEV_PM_OPS, + }, +}; + +MODULE_DESCRIPTION("Blackfin MUSB Glue Layer"); +MODULE_AUTHOR("Bryan Wy <cooloney@kernel.org>"); +MODULE_LICENSE("GPL v2"); + +static int __init bfin_init(void) +{ + return platform_driver_probe(&bfin_driver, bfin_probe); +} +subsys_initcall(bfin_init); + +static void __exit bfin_exit(void) +{ + platform_driver_unregister(&bfin_driver); +} +module_exit(bfin_exit); diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 84427bebbf62..69a0da3c8f09 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -29,6 +29,8 @@ #include <linux/init.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include <mach/da8xx.h> #include <mach/usb.h> @@ -78,6 +80,12 @@ #define CFGCHIP2 IO_ADDRESS(DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP2_REG) +struct da8xx_glue { + struct device *dev; + struct platform_device *musb; + struct clk *clk; +}; + /* * REVISIT (PM): we should be able to keep the PHY in low power mode most * of the time (24 MHz oscillator and PLL off, etc.) by setting POWER.D0 @@ -131,9 +139,9 @@ static inline void phy_off(void) */ /** - * musb_platform_enable - enable interrupts + * da8xx_musb_enable - enable interrupts */ -void musb_platform_enable(struct musb *musb) +static void da8xx_musb_enable(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; u32 mask; @@ -151,9 +159,9 @@ void musb_platform_enable(struct musb *musb) } /** - * musb_platform_disable - disable HDRC and flush interrupts + * da8xx_musb_disable - disable HDRC and flush interrupts */ -void musb_platform_disable(struct musb *musb) +static void da8xx_musb_disable(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; @@ -170,7 +178,7 @@ void musb_platform_disable(struct musb *musb) #define portstate(stmt) #endif -static void da8xx_set_vbus(struct musb *musb, int is_on) +static void da8xx_musb_set_vbus(struct musb *musb, int is_on) { WARN_ON(is_on && is_peripheral_active(musb)); } @@ -252,7 +260,7 @@ static void otg_timer(unsigned long _musb) spin_unlock_irqrestore(&musb->lock, flags); } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) { static unsigned long last_timer; @@ -282,7 +290,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout) mod_timer(&otg_workaround, timeout); } -static irqreturn_t da8xx_interrupt(int irq, void *hci) +static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) { struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; @@ -380,7 +388,7 @@ static irqreturn_t da8xx_interrupt(int irq, void *hci) return ret; } -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode) { u32 cfgchip2 = __raw_readl(CFGCHIP2); @@ -409,15 +417,13 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode) return 0; } -int __init musb_platform_init(struct musb *musb, void *board_data) +static int da8xx_musb_init(struct musb *musb) { void __iomem *reg_base = musb->ctrl_base; u32 rev; musb->mregs += DA8XX_MENTOR_CORE_OFFSET; - clk_enable(musb->clock); - /* Returns zero if e.g. not clocked */ rev = musb_readl(reg_base, DA8XX_USB_REVISION_REG); if (!rev) @@ -431,8 +437,6 @@ int __init musb_platform_init(struct musb *musb, void *board_data) if (is_host_enabled(musb)) setup_timer(&otg_workaround, otg_timer, (unsigned long)musb); - musb->board_set_vbus = da8xx_set_vbus; - /* Reset the controller */ musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK); @@ -446,14 +450,13 @@ int __init musb_platform_init(struct musb *musb, void *board_data) rev, __raw_readl(CFGCHIP2), musb_readb(reg_base, DA8XX_USB_CTRL_REG)); - musb->isr = da8xx_interrupt; + musb->isr = da8xx_musb_interrupt; return 0; fail: - clk_disable(musb->clock); return -ENODEV; } -int musb_platform_exit(struct musb *musb) +static int da8xx_musb_exit(struct musb *musb) { if (is_host_enabled(musb)) del_timer_sync(&otg_workaround); @@ -463,7 +466,140 @@ int musb_platform_exit(struct musb *musb) otg_put_transceiver(musb->xceiv); usb_nop_xceiv_unregister(); - clk_disable(musb->clock); + return 0; +} + +static const struct musb_platform_ops da8xx_ops = { + .init = da8xx_musb_init, + .exit = da8xx_musb_exit, + + .enable = da8xx_musb_enable, + .disable = da8xx_musb_disable, + + .set_mode = da8xx_musb_set_mode, + .try_idle = da8xx_musb_try_idle, + + .set_vbus = da8xx_musb_set_vbus, +}; + +static u64 da8xx_dmamask = DMA_BIT_MASK(32); + +static int __init da8xx_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct da8xx_glue *glue; + + struct clk *clk; + + 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", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } + + clk = clk_get(&pdev->dev, "usb20"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err2; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err3; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &da8xx_dmamask; + musb->dev.coherent_dma_mask = da8xx_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->clk = clk; + + pdata->platform_ops = &da8xx_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err4; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err4; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err4; + } + + return 0; + +err4: + clk_disable(clk); + +err3: + clk_put(clk); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __exit da8xx_remove(struct platform_device *pdev) +{ + struct da8xx_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + clk_disable(glue->clk); + clk_put(glue->clk); + kfree(glue); return 0; } + +static struct platform_driver da8xx_driver = { + .remove = __exit_p(da8xx_remove), + .driver = { + .name = "musb-da8xx", + }, +}; + +MODULE_DESCRIPTION("DA8xx/OMAP-L1x MUSB Glue Layer"); +MODULE_AUTHOR("Sergei Shtylyov <sshtylyov@ru.mvista.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init da8xx_init(void) +{ + return platform_driver_probe(&da8xx_driver, da8xx_probe); +} +subsys_initcall(da8xx_init); + +static void __exit da8xx_exit(void) +{ + platform_driver_unregister(&da8xx_driver); +} +module_exit(da8xx_exit); diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 051e2bf1897a..e6de097fb7e8 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -30,6 +30,8 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include <mach/hardware.h> #include <mach/memory.h> @@ -51,6 +53,12 @@ #define USB_PHY_CTRL IO_ADDRESS(USBPHY_CTL_PADDR) #define DM355_DEEPSLEEP IO_ADDRESS(DM355_DEEPSLEEP_PADDR) +struct davinci_glue { + struct device *dev; + struct platform_device *musb; + struct clk *clk; +}; + /* REVISIT (PM) we should be able to keep the PHY in low power mode most * of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0 * and, when in host mode, autosuspending idle root ports... PHYPLLON @@ -83,7 +91,7 @@ static inline void phy_off(void) static int dma_off = 1; -void musb_platform_enable(struct musb *musb) +static void davinci_musb_enable(struct musb *musb) { u32 tmp, old, val; @@ -116,7 +124,7 @@ void musb_platform_enable(struct musb *musb) /* * Disable the HDRC and flush interrupts */ -void musb_platform_disable(struct musb *musb) +static void davinci_musb_disable(struct musb *musb) { /* because we don't set CTRLR.UINT, "important" to: * - not read/write INTRUSB/INTRUSBE @@ -167,7 +175,7 @@ static void evm_deferred_drvvbus(struct work_struct *ignored) #endif /* EVM */ -static void davinci_source_power(struct musb *musb, int is_on, int immediate) +static void davinci_musb_source_power(struct musb *musb, int is_on, int immediate) { #ifdef CONFIG_MACH_DAVINCI_EVM if (is_on) @@ -190,10 +198,10 @@ static void davinci_source_power(struct musb *musb, int is_on, int immediate) #endif } -static void davinci_set_vbus(struct musb *musb, int is_on) +static void davinci_musb_set_vbus(struct musb *musb, int is_on) { WARN_ON(is_on && is_peripheral_active(musb)); - davinci_source_power(musb, is_on, 0); + davinci_musb_source_power(musb, is_on, 0); } @@ -259,7 +267,7 @@ static void otg_timer(unsigned long _musb) spin_unlock_irqrestore(&musb->lock, flags); } -static irqreturn_t davinci_interrupt(int irq, void *__hci) +static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) { unsigned long flags; irqreturn_t retval = IRQ_NONE; @@ -345,7 +353,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) /* NOTE: this must complete poweron within 100 msec * (OTG_TIME_A_WAIT_VRISE) but we don't check for that. */ - davinci_source_power(musb, drvvbus, 0); + davinci_musb_source_power(musb, drvvbus, 0); DBG(2, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", otg_state_string(musb), @@ -370,13 +378,13 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) return retval; } -int musb_platform_set_mode(struct musb *musb, u8 mode) +static int davinci_musb_set_mode(struct musb *musb, u8 mode) { /* EVM can't do this (right?) */ return -EIO; } -int __init musb_platform_init(struct musb *musb) +static int davinci_musb_init(struct musb *musb) { void __iomem *tibase = musb->ctrl_base; u32 revision; @@ -388,8 +396,6 @@ int __init musb_platform_init(struct musb *musb) musb->mregs += DAVINCI_BASE_OFFSET; - clk_enable(musb->clock); - /* returns zero if e.g. not clocked */ revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG); if (revision == 0) @@ -398,8 +404,7 @@ int __init musb_platform_init(struct musb *musb) if (is_host_enabled(musb)) setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); - musb->board_set_vbus = davinci_set_vbus; - davinci_source_power(musb, 0, 1); + davinci_musb_source_power(musb, 0, 1); /* dm355 EVM swaps D+/D- for signal integrity, and * is clocked from the main 24 MHz crystal. @@ -440,18 +445,16 @@ int __init musb_platform_init(struct musb *musb) revision, __raw_readl(USB_PHY_CTRL), musb_readb(tibase, DAVINCI_USB_CTRL_REG)); - musb->isr = davinci_interrupt; + musb->isr = davinci_musb_interrupt; return 0; fail: - clk_disable(musb->clock); - otg_put_transceiver(musb->xceiv); usb_nop_xceiv_unregister(); return -ENODEV; } -int musb_platform_exit(struct musb *musb) +static int davinci_musb_exit(struct musb *musb) { if (is_host_enabled(musb)) del_timer_sync(&otg_workaround); @@ -465,7 +468,7 @@ int musb_platform_exit(struct musb *musb) __raw_writel(deepsleep, DM355_DEEPSLEEP); } - davinci_source_power(musb, 0 /*off*/, 1); + davinci_musb_source_power(musb, 0 /*off*/, 1); /* delay, to avoid problems with module reload */ if (is_host_enabled(musb) && musb->xceiv->default_a) { @@ -495,10 +498,141 @@ int musb_platform_exit(struct musb *musb) phy_off(); - clk_disable(musb->clock); - otg_put_transceiver(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } + +static const struct musb_platform_ops davinci_ops = { + .init = davinci_musb_init, + .exit = davinci_musb_exit, + + .enable = davinci_musb_enable, + .disable = davinci_musb_disable, + + .set_mode = davinci_musb_set_mode, + + .set_vbus = davinci_musb_set_vbus, +}; + +static u64 davinci_dmamask = DMA_BIT_MASK(32); + +static int __init davinci_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct davinci_glue *glue; + struct clk *clk; + + 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", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } + + clk = clk_get(&pdev->dev, "usb"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err2; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err3; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &davinci_dmamask; + musb->dev.coherent_dma_mask = davinci_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->clk = clk; + + pdata->platform_ops = &davinci_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err4; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err4; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err4; + } + + return 0; + +err4: + clk_disable(clk); + +err3: + clk_put(clk); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __exit davinci_remove(struct platform_device *pdev) +{ + struct davinci_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + clk_disable(glue->clk); + clk_put(glue->clk); + kfree(glue); + + return 0; +} + +static struct platform_driver davinci_driver = { + .remove = __exit_p(davinci_remove), + .driver = { + .name = "musb-davinci", + }, +}; + +MODULE_DESCRIPTION("DaVinci MUSB Glue Layer"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init davinci_init(void) +{ + return platform_driver_probe(&davinci_driver, davinci_probe); +} +subsys_initcall(davinci_init); + +static void __exit davinci_exit(void) +{ + platform_driver_unregister(&davinci_driver); +} +module_exit(davinci_exit); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a5ceddfe57d6..7816c0180430 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -99,19 +99,8 @@ #include <linux/platform_device.h> #include <linux/io.h> -#ifdef CONFIG_ARM -#include <mach/hardware.h> -#include <mach/memory.h> -#include <asm/mach-types.h> -#endif - #include "musb_core.h" - -#ifdef CONFIG_ARCH_DAVINCI -#include "davinci.h" -#endif - #define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) @@ -126,7 +115,7 @@ MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); #define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION -#define MUSB_DRIVER_NAME "musb_hdrc" +#define MUSB_DRIVER_NAME "musb-hdrc" const char musb_driver_name[] = MUSB_DRIVER_NAME; MODULE_DESCRIPTION(DRIVER_INFO); @@ -230,7 +219,7 @@ static struct otg_io_access_ops musb_ulpi_access = { /*-------------------------------------------------------------------------*/ -#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) +#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN) /* * Load an endpoint's FIFO @@ -390,7 +379,7 @@ void musb_otg_timer_func(unsigned long data) case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: DBG(1, "HNP: %s timeout\n", otg_state_string(musb)); - musb_set_vbus(musb, 0); + musb_platform_set_vbus(musb, 0); musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: @@ -570,7 +559,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->ep0_stage = MUSB_EP0_START; musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); - musb_set_vbus(musb, 1); + musb_platform_set_vbus(musb, 1); handled = IRQ_HANDLED; } @@ -641,7 +630,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, /* go through A_WAIT_VFALL then start a new session */ if (!ignore) - musb_set_vbus(musb, 0); + musb_platform_set_vbus(musb, 0); handled = IRQ_HANDLED; } @@ -1048,8 +1037,6 @@ static void musb_shutdown(struct platform_device *pdev) spin_lock_irqsave(&musb->lock, flags); musb_platform_disable(musb); musb_generic_disable(musb); - if (musb->clock) - clk_put(musb->clock); spin_unlock_irqrestore(&musb->lock, flags); /* FIXME power down */ @@ -1068,10 +1055,11 @@ static void musb_shutdown(struct platform_device *pdev) * We don't currently use dynamic fifo setup capability to do anything * more than selecting one of a bunch of predefined configurations. */ -#if defined(CONFIG_USB_TUSB6010) || \ - defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ - || defined(CONFIG_ARCH_OMAP4) +#if defined(CONFIG_USB_MUSB_TUSB6010) || defined(CONFIG_USB_MUSB_OMAP2PLUS) \ + || defined(CONFIG_USB_MUSB_AM35X) static ushort __initdata fifo_mode = 4; +#elif defined(CONFIG_USB_MUSB_UX500) +static ushort __initdata fifo_mode = 5; #else static ushort __initdata fifo_mode = 2; #endif @@ -1495,7 +1483,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) struct musb_hw_ep *hw_ep = musb->endpoints + i; hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase; -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i); hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i); hw_ep->fifo_sync_va = @@ -1542,7 +1530,8 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) /*-------------------------------------------------------------------------*/ #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) || \ - defined(CONFIG_ARCH_OMAP4) + defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) || \ + defined(CONFIG_ARCH_U5500) static irqreturn_t generic_interrupt(int irq, void *__hci) { @@ -1898,6 +1887,7 @@ allocate_instance(struct device *dev, } musb->controller = dev; + return musb; } @@ -1994,30 +1984,14 @@ bad_config: spin_lock_init(&musb->lock); musb->board_mode = plat->mode; musb->board_set_power = plat->set_power; - musb->set_clock = plat->set_clock; musb->min_power = plat->min_power; - - /* Clock usage is chip-specific ... functional clock (DaVinci, - * OMAP2430), or PHY ref (some TUSB6010 boards). All this core - * code does is make sure a clock handle is available; platform - * code manages it during start/stop and suspend/resume. - */ - if (plat->clock) { - musb->clock = clk_get(dev, plat->clock); - if (IS_ERR(musb->clock)) { - status = PTR_ERR(musb->clock); - musb->clock = NULL; - goto fail1; - } - } + musb->ops = plat->platform_ops; /* The musb_platform_init() call: * - adjusts musb->mregs and musb->isr if needed, * - may initialize an integrated tranceiver * - initializes musb->xceiv, usually by otg_get_transceiver() - * - activates clocks. * - stops powering VBUS - * - assigns musb->board_set_vbus if host mode is enabled * * There are various transciever configurations. Blackfin, * DaVinci, TUSB60x0, and others integrate them. OMAP3 uses @@ -2027,7 +2001,7 @@ bad_config: musb->isr = generic_interrupt; status = musb_platform_init(musb); if (status < 0) - goto fail2; + goto fail1; if (!musb->isr) { status = -ENODEV; @@ -2177,10 +2151,6 @@ fail3: device_init_wakeup(dev, 0); musb_platform_exit(musb); -fail2: - if (musb->clock) - clk_put(musb->clock); - fail1: dev_err(musb->controller, "musb_init_controller failed with status %d\n", status); @@ -2263,144 +2233,138 @@ static int __exit musb_remove(struct platform_device *pdev) #ifdef CONFIG_PM -static struct musb_context_registers musb_context; - -void musb_save_context(struct musb *musb) +static void musb_save_context(struct musb *musb) { int i; void __iomem *musb_base = musb->mregs; void __iomem *epio; if (is_host_enabled(musb)) { - musb_context.frame = musb_readw(musb_base, MUSB_FRAME); - musb_context.testmode = musb_readb(musb_base, MUSB_TESTMODE); - musb_context.busctl = musb_read_ulpi_buscontrol(musb->mregs); + musb->context.frame = musb_readw(musb_base, MUSB_FRAME); + musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE); + musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs); } - musb_context.power = musb_readb(musb_base, MUSB_POWER); - musb_context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE); - musb_context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE); - musb_context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE); - musb_context.index = musb_readb(musb_base, MUSB_INDEX); - musb_context.devctl = musb_readb(musb_base, MUSB_DEVCTL); + musb->context.power = musb_readb(musb_base, MUSB_POWER); + musb->context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE); + musb->context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE); + musb->context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE); + musb->context.index = musb_readb(musb_base, MUSB_INDEX); + musb->context.devctl = musb_readb(musb_base, MUSB_DEVCTL); for (i = 0; i < musb->config->num_eps; ++i) { epio = musb->endpoints[i].regs; - musb_context.index_regs[i].txmaxp = + musb->context.index_regs[i].txmaxp = musb_readw(epio, MUSB_TXMAXP); - musb_context.index_regs[i].txcsr = + musb->context.index_regs[i].txcsr = musb_readw(epio, MUSB_TXCSR); - musb_context.index_regs[i].rxmaxp = + musb->context.index_regs[i].rxmaxp = musb_readw(epio, MUSB_RXMAXP); - musb_context.index_regs[i].rxcsr = + musb->context.index_regs[i].rxcsr = musb_readw(epio, MUSB_RXCSR); if (musb->dyn_fifo) { - musb_context.index_regs[i].txfifoadd = + musb->context.index_regs[i].txfifoadd = musb_read_txfifoadd(musb_base); - musb_context.index_regs[i].rxfifoadd = + musb->context.index_regs[i].rxfifoadd = musb_read_rxfifoadd(musb_base); - musb_context.index_regs[i].txfifosz = + musb->context.index_regs[i].txfifosz = musb_read_txfifosz(musb_base); - musb_context.index_regs[i].rxfifosz = + musb->context.index_regs[i].rxfifosz = musb_read_rxfifosz(musb_base); } if (is_host_enabled(musb)) { - musb_context.index_regs[i].txtype = + musb->context.index_regs[i].txtype = musb_readb(epio, MUSB_TXTYPE); - musb_context.index_regs[i].txinterval = + musb->context.index_regs[i].txinterval = musb_readb(epio, MUSB_TXINTERVAL); - musb_context.index_regs[i].rxtype = + musb->context.index_regs[i].rxtype = musb_readb(epio, MUSB_RXTYPE); - musb_context.index_regs[i].rxinterval = + musb->context.index_regs[i].rxinterval = musb_readb(epio, MUSB_RXINTERVAL); - musb_context.index_regs[i].txfunaddr = + musb->context.index_regs[i].txfunaddr = musb_read_txfunaddr(musb_base, i); - musb_context.index_regs[i].txhubaddr = + musb->context.index_regs[i].txhubaddr = musb_read_txhubaddr(musb_base, i); - musb_context.index_regs[i].txhubport = + musb->context.index_regs[i].txhubport = musb_read_txhubport(musb_base, i); - musb_context.index_regs[i].rxfunaddr = + musb->context.index_regs[i].rxfunaddr = musb_read_rxfunaddr(musb_base, i); - musb_context.index_regs[i].rxhubaddr = + musb->context.index_regs[i].rxhubaddr = musb_read_rxhubaddr(musb_base, i); - musb_context.index_regs[i].rxhubport = + musb->context.index_regs[i].rxhubport = musb_read_rxhubport(musb_base, i); } } - - musb_platform_save_context(musb, &musb_context); } -void musb_restore_context(struct musb *musb) +static void musb_restore_context(struct musb *musb) { int i; void __iomem *musb_base = musb->mregs; void __iomem *ep_target_regs; void __iomem *epio; - musb_platform_restore_context(musb, &musb_context); - if (is_host_enabled(musb)) { - 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_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); - musb_writew(musb_base, MUSB_INTRTXE, musb_context.intrtxe); - musb_writew(musb_base, MUSB_INTRRXE, musb_context.intrrxe); - musb_writeb(musb_base, MUSB_INTRUSBE, musb_context.intrusbe); - musb_writeb(musb_base, MUSB_DEVCTL, musb_context.devctl); + musb_writeb(musb_base, MUSB_POWER, musb->context.power); + musb_writew(musb_base, MUSB_INTRTXE, musb->context.intrtxe); + musb_writew(musb_base, MUSB_INTRRXE, musb->context.intrrxe); + musb_writeb(musb_base, MUSB_INTRUSBE, musb->context.intrusbe); + musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl); for (i = 0; i < musb->config->num_eps; ++i) { epio = musb->endpoints[i].regs; musb_writew(epio, MUSB_TXMAXP, - musb_context.index_regs[i].txmaxp); + musb->context.index_regs[i].txmaxp); musb_writew(epio, MUSB_TXCSR, - musb_context.index_regs[i].txcsr); + musb->context.index_regs[i].txcsr); musb_writew(epio, MUSB_RXMAXP, - musb_context.index_regs[i].rxmaxp); + musb->context.index_regs[i].rxmaxp); musb_writew(epio, MUSB_RXCSR, - musb_context.index_regs[i].rxcsr); + musb->context.index_regs[i].rxcsr); if (musb->dyn_fifo) { musb_write_txfifosz(musb_base, - musb_context.index_regs[i].txfifosz); + musb->context.index_regs[i].txfifosz); musb_write_rxfifosz(musb_base, - musb_context.index_regs[i].rxfifosz); + musb->context.index_regs[i].rxfifosz); musb_write_txfifoadd(musb_base, - musb_context.index_regs[i].txfifoadd); + musb->context.index_regs[i].txfifoadd); musb_write_rxfifoadd(musb_base, - musb_context.index_regs[i].rxfifoadd); + musb->context.index_regs[i].rxfifoadd); } if (is_host_enabled(musb)) { musb_writeb(epio, MUSB_TXTYPE, - musb_context.index_regs[i].txtype); + musb->context.index_regs[i].txtype); musb_writeb(epio, MUSB_TXINTERVAL, - musb_context.index_regs[i].txinterval); + musb->context.index_regs[i].txinterval); musb_writeb(epio, MUSB_RXTYPE, - musb_context.index_regs[i].rxtype); + musb->context.index_regs[i].rxtype); musb_writeb(epio, MUSB_RXINTERVAL, - musb_context.index_regs[i].rxinterval); + musb->context.index_regs[i].rxinterval); musb_write_txfunaddr(musb_base, i, - musb_context.index_regs[i].txfunaddr); + musb->context.index_regs[i].txfunaddr); musb_write_txhubaddr(musb_base, i, - musb_context.index_regs[i].txhubaddr); + musb->context.index_regs[i].txhubaddr); musb_write_txhubport(musb_base, i, - musb_context.index_regs[i].txhubport); + musb->context.index_regs[i].txhubport); ep_target_regs = musb_read_target_reg_base(i, musb_base); musb_write_rxfunaddr(ep_target_regs, - musb_context.index_regs[i].rxfunaddr); + musb->context.index_regs[i].rxfunaddr); musb_write_rxhubaddr(ep_target_regs, - musb_context.index_regs[i].rxhubaddr); + musb->context.index_regs[i].rxhubaddr); musb_write_rxhubport(ep_target_regs, - musb_context.index_regs[i].rxhubport); + musb->context.index_regs[i].rxhubport); } } } @@ -2411,9 +2375,6 @@ static int musb_suspend(struct device *dev) unsigned long flags; struct musb *musb = dev_to_musb(&pdev->dev); - if (!musb->clock) - return 0; - spin_lock_irqsave(&musb->lock, flags); if (is_peripheral_active(musb)) { @@ -2428,10 +2389,6 @@ static int musb_suspend(struct device *dev) musb_save_context(musb); - if (musb->set_clock) - musb->set_clock(musb->clock, 0); - else - clk_disable(musb->clock); spin_unlock_irqrestore(&musb->lock, flags); return 0; } @@ -2441,14 +2398,6 @@ static int musb_resume_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct musb *musb = dev_to_musb(&pdev->dev); - if (!musb->clock) - return 0; - - if (musb->set_clock) - musb->set_clock(musb->clock, 1); - else - clk_enable(musb->clock); - musb_restore_context(musb); /* for static cmos like DaVinci, register values were preserved diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 6ad72f395e28..d0c236f8e191 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -222,7 +222,7 @@ enum musb_g_ep0_state { #endif /* TUSB mapping: "flat" plus ep0 special cases */ -#if defined(CONFIG_USB_TUSB6010) +#if defined(CONFIG_USB_MUSB_TUSB6010) #define musb_ep_select(_mbase, _epnum) \ musb_writeb((_mbase), MUSB_INDEX, (_epnum)) #define MUSB_EP_OFFSET MUSB_TUSB_OFFSET @@ -253,6 +253,29 @@ enum musb_g_ep0_state { /******************************** TYPES *************************************/ +/** + * struct musb_platform_ops - Operations passed to musb_core by HW glue layer + * @init: turns on clocks, sets up platform-specific registers, etc + * @exit: undoes @init + * @set_mode: forcefully changes operating mode + * @try_ilde: tries to idle the IP + * @vbus_status: returns vbus status if possible + * @set_vbus: forces vbus status + */ +struct musb_platform_ops { + int (*init)(struct musb *musb); + int (*exit)(struct musb *musb); + + void (*enable)(struct musb *musb); + void (*disable)(struct musb *musb); + + int (*set_mode)(struct musb *musb, u8 mode); + void (*try_idle)(struct musb *musb, unsigned long timeout); + + int (*vbus_status)(struct musb *musb); + void (*set_vbus)(struct musb *musb, int on); +}; + /* * struct musb_hw_ep - endpoint hardware (bidirectional) * @@ -263,7 +286,7 @@ struct musb_hw_ep { void __iomem *fifo; void __iomem *regs; -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 void __iomem *conf; #endif @@ -280,7 +303,7 @@ struct musb_hw_ep { struct dma_channel *tx_channel; struct dma_channel *rx_channel; -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 /* TUSB has "asynchronous" and "synchronous" dma modes */ dma_addr_t fifo_async; dma_addr_t fifo_sync; @@ -323,14 +346,43 @@ static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep) #endif } +struct musb_csr_regs { + /* FIFO registers */ + u16 txmaxp, txcsr, rxmaxp, rxcsr; + u16 rxfifoadd, txfifoadd; + u8 txtype, txinterval, rxtype, rxinterval; + u8 rxfifosz, txfifosz; + u8 txfunaddr, txhubaddr, txhubport; + u8 rxfunaddr, rxhubaddr, rxhubport; +}; + +struct musb_context_registers { + +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \ + defined(CONFIG_ARCH_OMAP4) + u32 otg_sysconfig, otg_forcestandby; +#endif + u8 power; + u16 intrtxe, intrrxe; + u8 intrusbe; + u16 frame; + u8 index, testmode; + + u8 devctl, busctl, misc; + + struct musb_csr_regs index_regs[MUSB_C_NUM_EPS]; +}; + /* * struct musb - Driver instance data. */ struct musb { /* device lock */ spinlock_t lock; - struct clk *clock; - struct clk *phy_clock; + + const struct musb_platform_ops *ops; + struct musb_context_registers context; + irqreturn_t (*isr)(int, void *); struct work_struct irq_work; u16 hwvers; @@ -359,11 +411,7 @@ struct musb { struct timer_list otg_timer; #endif - - /* called with IRQs blocked; ON/nonzero implies starting a session, - * and waiting at least a_wait_vrise_tmout. - */ - void (*board_set_vbus)(struct musb *, int is_on); + struct notifier_block nb; struct dma_controller *dma_controller; @@ -371,7 +419,7 @@ struct musb { void __iomem *ctrl_base; void __iomem *mregs; -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 dma_addr_t async; dma_addr_t sync; void __iomem *sync_va; @@ -398,8 +446,6 @@ struct musb { u8 board_mode; /* enum musb_mode */ int (*board_set_power)(int state); - int (*set_clock)(struct clk *clk, int is_active); - u8 min_power; /* vbus for periph, in mA/2 */ bool is_host; @@ -458,52 +504,6 @@ struct musb { #endif }; -#ifdef CONFIG_PM -struct musb_csr_regs { - /* FIFO registers */ - u16 txmaxp, txcsr, rxmaxp, rxcsr; - u16 rxfifoadd, txfifoadd; - u8 txtype, txinterval, rxtype, rxinterval; - u8 rxfifosz, txfifosz; - u8 txfunaddr, txhubaddr, txhubport; - u8 rxfunaddr, rxhubaddr, rxhubport; -}; - -struct musb_context_registers { - -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \ - defined(CONFIG_ARCH_OMAP4) - u32 otg_sysconfig, otg_forcestandby; -#endif - u8 power; - u16 intrtxe, intrrxe; - u8 intrusbe; - u16 frame; - u8 index, testmode; - - u8 devctl, busctl, misc; - - struct musb_csr_regs index_regs[MUSB_C_NUM_EPS]; -}; - -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \ - defined(CONFIG_ARCH_OMAP4) -extern void musb_platform_save_context(struct musb *musb, - struct musb_context_registers *musb_context); -extern void musb_platform_restore_context(struct musb *musb, - struct musb_context_registers *musb_context); -#else -#define musb_platform_save_context(m, x) do {} while (0) -#define musb_platform_restore_context(m, x) do {} while (0) -#endif - -#endif - -static inline void musb_set_vbus(struct musb *musb, int is_on) -{ - musb->board_set_vbus(musb, is_on); -} - #ifdef CONFIG_USB_GADGET_MUSB_HDRC static inline struct musb *gadget_to_musb(struct usb_gadget *g) { @@ -592,29 +592,63 @@ extern void musb_load_testpacket(struct musb *); extern irqreturn_t musb_interrupt(struct musb *); -extern void musb_platform_enable(struct musb *musb); -extern void musb_platform_disable(struct musb *musb); - extern void musb_hnp_stop(struct musb *musb); -extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode); +static inline void musb_platform_set_vbus(struct musb *musb, int is_on) +{ + if (musb->ops->set_vbus) + musb->ops->set_vbus(musb, is_on); +} -#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \ - defined(CONFIG_ARCH_DAVINCI_DA8XX) || \ - defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) || \ - defined(CONFIG_ARCH_OMAP4) -extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout); -#else -#define musb_platform_try_idle(x, y) do {} while (0) -#endif +static inline void musb_platform_enable(struct musb *musb) +{ + if (musb->ops->enable) + musb->ops->enable(musb); +} -#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) -extern int musb_platform_get_vbus_status(struct musb *musb); -#else -#define musb_platform_get_vbus_status(x) 0 -#endif +static inline void musb_platform_disable(struct musb *musb) +{ + if (musb->ops->disable) + musb->ops->disable(musb); +} + +static inline int musb_platform_set_mode(struct musb *musb, u8 mode) +{ + if (!musb->ops->set_mode) + return 0; + + return musb->ops->set_mode(musb, mode); +} + +static inline void musb_platform_try_idle(struct musb *musb, + unsigned long timeout) +{ + if (musb->ops->try_idle) + musb->ops->try_idle(musb, timeout); +} + +static inline int musb_platform_get_vbus_status(struct musb *musb) +{ + if (!musb->ops->vbus_status) + return 0; -extern int __init musb_platform_init(struct musb *musb); -extern int musb_platform_exit(struct musb *musb); + return musb->ops->vbus_status(musb); +} + +static inline int musb_platform_init(struct musb *musb) +{ + if (!musb->ops->init) + return -EINVAL; + + return musb->ops->init(musb); +} + +static inline int musb_platform_exit(struct musb *musb) +{ + if (!musb->ops->exit) + return -EINVAL; + + return musb->ops->exit(musb); +} #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index b06e9ef00cfc..03c6ccdbb3be 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -74,7 +74,7 @@ static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) { __raw_writel(data, addr + offset); } -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 /* * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum. @@ -114,7 +114,7 @@ static inline u8 musb_readb(const void __iomem *addr, unsigned offset) static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) { __raw_writeb(data, addr + offset); } -#endif /* CONFIG_USB_TUSB6010 */ +#endif /* CONFIG_USB_MUSB_TUSB6010 */ #else diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 244267527a60..9cb5fe044438 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -234,7 +234,7 @@ #define MUSB_TESTMODE 0x0F /* 8 bit */ /* Get offset for a given FIFO from musb->mregs */ -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 #define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20)) #else #define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4)) @@ -295,7 +295,7 @@ #define MUSB_FLAT_OFFSET(_epnum, _offset) \ (0x100 + (0x10*(_epnum)) + (_offset)) -#ifdef CONFIG_USB_TUSB6010 +#ifdef CONFIG_USB_MUSB_TUSB6010 /* TUSB6010 EP0 configuration register is special */ #define MUSB_TUSB_OFFSET(_epnum, _offset) \ (0x10 + _offset) diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 43233c397b6e..b46d1877e28e 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -276,7 +276,7 @@ int musb_hub_control( break; case USB_PORT_FEAT_POWER: if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) - musb_set_vbus(musb, 0); + musb_platform_set_vbus(musb, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 27dabcf0a868..a3f12333fc41 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -31,10 +31,18 @@ #include <linux/list.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include "musb_core.h" #include "omap2430.h" +struct omap2430_glue { + struct device *dev; + struct platform_device *musb; + struct clk *clk; +}; +#define glue_to_musb(g) platform_get_drvdata(g->musb) static struct timer_list musb_idle_timer; @@ -49,12 +57,8 @@ static void musb_do_idle(unsigned long _musb) spin_lock_irqsave(&musb->lock, flags); - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_BCON: - devctl &= ~MUSB_DEVCTL_SESSION; - musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) { @@ -98,7 +102,7 @@ static void musb_do_idle(unsigned long _musb) } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) { unsigned long default_timeout = jiffies + msecs_to_jiffies(3); static unsigned long last_timer; @@ -131,15 +135,11 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout) mod_timer(&musb_idle_timer, timeout); } -void musb_platform_enable(struct musb *musb) -{ -} -void musb_platform_disable(struct musb *musb) -{ -} -static void omap_set_vbus(struct musb *musb, int is_on) +static void omap2430_musb_set_vbus(struct musb *musb, int is_on) { u8 devctl; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + int ret = 1; /* HDRC controls CPEN, but beware current surges during device * connect. They can trigger transient overcurrent conditions * that must be ignored. @@ -148,12 +148,35 @@ static void omap_set_vbus(struct musb *musb, int is_on) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (is_on) { - musb->is_active = 1; - musb->xceiv->default_a = 1; - musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; - devctl |= MUSB_DEVCTL_SESSION; - - MUSB_HST_MODE(musb); + if (musb->xceiv->state == OTG_STATE_A_IDLE) { + /* start the session */ + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + /* + * Wait for the musb to set as A device to enable the + * VBUS + */ + while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) { + + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_err(musb->controller, + "configured as A device timeout"); + ret = -EINVAL; + break; + } + } + + if (ret && musb->xceiv->set_vbus) + otg_set_vbus(musb->xceiv, 1); + } else { + musb->is_active = 1; + musb->xceiv->default_a = 1; + musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; + devctl |= MUSB_DEVCTL_SESSION; + MUSB_HST_MODE(musb); + } } else { musb->is_active = 0; @@ -175,9 +198,7 @@ static void omap_set_vbus(struct musb *musb, int is_on) musb_readb(musb->mregs, MUSB_DEVCTL)); } -static int musb_platform_resume(struct musb *musb); - -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int omap2430_musb_set_mode(struct musb *musb, u8 musb_mode) { u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -187,9 +208,91 @@ int musb_platform_set_mode(struct musb *musb, u8 musb_mode) return 0; } -int __init musb_platform_init(struct musb *musb) +static inline void omap2430_low_level_exit(struct musb *musb) +{ + u32 l; + + /* in any role */ + l = musb_readl(musb->mregs, OTG_FORCESTDBY); + l |= ENABLEFORCE; /* enable MSTANDBY */ + musb_writel(musb->mregs, OTG_FORCESTDBY, l); + + l = musb_readl(musb->mregs, OTG_SYSCONFIG); + l |= ENABLEWAKEUP; /* enable wakeup */ + musb_writel(musb->mregs, OTG_SYSCONFIG, l); +} + +static inline void omap2430_low_level_init(struct musb *musb) { u32 l; + + l = musb_readl(musb->mregs, OTG_SYSCONFIG); + l &= ~ENABLEWAKEUP; /* disable wakeup */ + musb_writel(musb->mregs, OTG_SYSCONFIG, l); + + l = musb_readl(musb->mregs, OTG_FORCESTDBY); + l &= ~ENABLEFORCE; /* disable MSTANDBY */ + musb_writel(musb->mregs, OTG_FORCESTDBY, l); +} + +/* blocking notifier support */ +static int musb_otg_notifications(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct musb *musb = container_of(nb, struct musb, nb); + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *pdata = dev->platform_data; + struct omap_musb_board_data *data = pdata->board_data; + + switch (event) { + case USB_EVENT_ID: + DBG(4, "ID GND\n"); + + if (is_otg_enabled(musb)) { +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + if (musb->gadget_driver) { + otg_init(musb->xceiv); + + if (data->interface_type == + MUSB_INTERFACE_UTMI) + omap2430_musb_set_vbus(musb, 1); + + } +#endif + } else { + otg_init(musb->xceiv); + if (data->interface_type == + MUSB_INTERFACE_UTMI) + omap2430_musb_set_vbus(musb, 1); + } + break; + + case USB_EVENT_VBUS: + DBG(4, "VBUS Connect\n"); + + otg_init(musb->xceiv); + break; + + case USB_EVENT_NONE: + DBG(4, "VBUS Disconnect\n"); + + if (data->interface_type == MUSB_INTERFACE_UTMI) { + if (musb->xceiv->set_vbus) + otg_set_vbus(musb->xceiv, 0); + } + otg_shutdown(musb->xceiv); + break; + default: + DBG(4, "ID float\n"); + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int omap2430_musb_init(struct musb *musb) +{ + u32 l, status = 0; struct device *dev = musb->controller; struct musb_hdrc_platform_data *plat = dev->platform_data; struct omap_musb_board_data *data = plat->board_data; @@ -204,7 +307,7 @@ int __init musb_platform_init(struct musb *musb) return -ENODEV; } - musb_platform_resume(musb); + omap2430_low_level_init(musb); l = musb_readl(musb->mregs, OTG_SYSCONFIG); l &= ~ENABLEWAKEUP; /* disable wakeup */ @@ -241,87 +344,214 @@ int __init musb_platform_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - if (is_host_enabled(musb)) - musb->board_set_vbus = omap_set_vbus; + musb->nb.notifier_call = musb_otg_notifications; + status = otg_register_notifier(musb->xceiv, &musb->nb); + + if (status) + DBG(1, "notification register failed\n"); + + /* check whether cable is already connected */ + if (musb->xceiv->state ==OTG_STATE_B_IDLE) + musb_otg_notifications(&musb->nb, 1, + musb->xceiv->gadget); setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); return 0; } -#ifdef CONFIG_PM -void musb_platform_save_context(struct musb *musb, - struct musb_context_registers *musb_context) +static int omap2430_musb_exit(struct musb *musb) { - musb_context->otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG); - musb_context->otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY); -} -void musb_platform_restore_context(struct musb *musb, - struct musb_context_registers *musb_context) -{ - musb_writel(musb->mregs, OTG_SYSCONFIG, musb_context->otg_sysconfig); - musb_writel(musb->mregs, OTG_FORCESTDBY, musb_context->otg_forcestandby); + omap2430_low_level_exit(musb); + otg_put_transceiver(musb->xceiv); + + return 0; } -#endif -static int musb_platform_suspend(struct musb *musb) +static const struct musb_platform_ops omap2430_ops = { + .init = omap2430_musb_init, + .exit = omap2430_musb_exit, + + .set_mode = omap2430_musb_set_mode, + .try_idle = omap2430_musb_try_idle, + + .set_vbus = omap2430_musb_set_vbus, +}; + +static u64 omap2430_dmamask = DMA_BIT_MASK(32); + +static int __init omap2430_probe(struct platform_device *pdev) { - u32 l; + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct omap2430_glue *glue; + struct clk *clk; - if (!musb->clock) - return 0; + int ret = -ENOMEM; - /* in any role */ - l = musb_readl(musb->mregs, OTG_FORCESTDBY); - l |= ENABLEFORCE; /* enable MSTANDBY */ - musb_writel(musb->mregs, OTG_FORCESTDBY, l); + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pdev->dev, "failed to allocate glue context\n"); + goto err0; + } - l = musb_readl(musb->mregs, OTG_SYSCONFIG); - l |= ENABLEWAKEUP; /* enable wakeup */ - musb_writel(musb->mregs, OTG_SYSCONFIG, l); + musb = platform_device_alloc("musb-hdrc", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } - otg_set_suspend(musb->xceiv, 1); + clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err2; + } - if (musb->set_clock) - musb->set_clock(musb->clock, 0); - else - clk_disable(musb->clock); + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err3; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &omap2430_dmamask; + musb->dev.coherent_dma_mask = omap2430_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->clk = clk; + + pdata->platform_ops = &omap2430_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err4; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err4; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err4; + } return 0; + +err4: + clk_disable(clk); + +err3: + clk_put(clk); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; } -static int musb_platform_resume(struct musb *musb) +static int __exit omap2430_remove(struct platform_device *pdev) { - u32 l; + struct omap2430_glue *glue = platform_get_drvdata(pdev); - if (!musb->clock) - return 0; + platform_device_del(glue->musb); + platform_device_put(glue->musb); + clk_disable(glue->clk); + clk_put(glue->clk); + kfree(glue); - otg_set_suspend(musb->xceiv, 0); + return 0; +} - if (musb->set_clock) - musb->set_clock(musb->clock, 1); - else - clk_enable(musb->clock); +#ifdef CONFIG_PM +static void omap2430_save_context(struct musb *musb) +{ + musb->context.otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG); + musb->context.otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY); +} - l = musb_readl(musb->mregs, OTG_SYSCONFIG); - l &= ~ENABLEWAKEUP; /* disable wakeup */ - musb_writel(musb->mregs, OTG_SYSCONFIG, l); +static void omap2430_restore_context(struct musb *musb) +{ + musb_writel(musb->mregs, OTG_SYSCONFIG, musb->context.otg_sysconfig); + musb_writel(musb->mregs, OTG_FORCESTDBY, musb->context.otg_forcestandby); +} - l = musb_readl(musb->mregs, OTG_FORCESTDBY); - l &= ~ENABLEFORCE; /* disable MSTANDBY */ - musb_writel(musb->mregs, OTG_FORCESTDBY, l); +static int omap2430_suspend(struct device *dev) +{ + struct omap2430_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + + omap2430_low_level_exit(musb); + otg_set_suspend(musb->xceiv, 1); + omap2430_save_context(musb); + clk_disable(glue->clk); return 0; } - -int musb_platform_exit(struct musb *musb) +static int omap2430_resume(struct device *dev) { + struct omap2430_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + int ret; + + ret = clk_enable(glue->clk); + if (ret) { + dev_err(dev, "faled to enable clock\n"); + return ret; + } - musb_platform_suspend(musb); + omap2430_low_level_init(musb); + omap2430_restore_context(musb); + otg_set_suspend(musb->xceiv, 0); - otg_put_transceiver(musb->xceiv); return 0; } + +static struct dev_pm_ops omap2430_pm_ops = { + .suspend = omap2430_suspend, + .resume = omap2430_resume, +}; + +#define DEV_PM_OPS (&omap2430_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver omap2430_driver = { + .remove = __exit_p(omap2430_remove), + .driver = { + .name = "musb-omap2430", + .pm = DEV_PM_OPS, + }, +}; + +MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init omap2430_init(void) +{ + return platform_driver_probe(&omap2430_driver, omap2430_probe); +} +subsys_initcall(omap2430_init); + +static void __exit omap2430_exit(void) +{ + platform_driver_unregister(&omap2430_driver); +} +module_exit(omap2430_exit); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 41b04b906ce1..2ba3b070ed0b 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -21,10 +21,16 @@ #include <linux/usb.h> #include <linux/irq.h> #include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include "musb_core.h" -static void tusb_source_power(struct musb *musb, int is_on); +struct tusb6010_glue { + struct device *dev; + struct platform_device *musb; +}; + +static void tusb_musb_set_vbus(struct musb *musb, int is_on); #define TUSB_REV_MAJOR(reg_val) ((reg_val >> 4) & 0xf) #define TUSB_REV_MINOR(reg_val) (reg_val & 0xf) @@ -50,7 +56,7 @@ u8 tusb_get_revision(struct musb *musb) return rev; } -static int __init tusb_print_revision(struct musb *musb) +static int tusb_print_revision(struct musb *musb) { void __iomem *tbase = musb->ctrl_base; u8 rev; @@ -275,17 +281,6 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) void __iomem *tbase = musb->ctrl_base; u32 reg; - /* - * Keep clock active when enabled. Note that this is not tied to - * drawing VBUS, as with OTG mA can be less than musb->min_power. - */ - if (musb->set_clock) { - if (mA) - musb->set_clock(musb->clock, 1); - else - musb->set_clock(musb->clock, 0); - } - /* tps65030 seems to consume max 100mA, with maybe 60mA available * (measured on one board) for things other than tps and tusb. * @@ -348,7 +343,7 @@ static void tusb_set_clock_source(struct musb *musb, unsigned mode) * USB link is not suspended ... and tells us the relevant wakeup * events. SW_EN for voltage is handled separately. */ -void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) +static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) { void __iomem *tbase = musb->ctrl_base; u32 reg; @@ -385,7 +380,7 @@ void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) /* * Updates cable VBUS status. Caller must take care of locking. */ -int musb_platform_get_vbus_status(struct musb *musb) +static int tusb_musb_vbus_status(struct musb *musb) { void __iomem *tbase = musb->ctrl_base; u32 otg_stat, prcm_mngmt; @@ -431,7 +426,7 @@ static void musb_do_idle(unsigned long _musb) } /* FALLTHROUGH */ case OTG_STATE_A_IDLE: - tusb_source_power(musb, 0); + tusb_musb_set_vbus(musb, 0); default: break; } @@ -475,7 +470,7 @@ done: * we don't want to treat that full speed J as a wakeup event. * ... peripherals must draw only suspend current after 10 msec. */ -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) { unsigned long default_timeout = jiffies + msecs_to_jiffies(3); static unsigned long last_timer; @@ -515,7 +510,7 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout) | TUSB_DEV_OTG_TIMER_ENABLE) \ : 0) -static void tusb_source_power(struct musb *musb, int is_on) +static void tusb_musb_set_vbus(struct musb *musb, int is_on) { void __iomem *tbase = musb->ctrl_base; u32 conf, prcm, timer; @@ -531,8 +526,6 @@ static void tusb_source_power(struct musb *musb, int is_on) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); if (is_on) { - if (musb->set_clock) - musb->set_clock(musb->clock, 1); timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE); musb->xceiv->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; @@ -571,8 +564,6 @@ static void tusb_source_power(struct musb *musb, int is_on) devctl &= ~MUSB_DEVCTL_SESSION; conf &= ~TUSB_DEV_CONF_USB_HOST_MODE; - if (musb->set_clock) - musb->set_clock(musb->clock, 0); } prcm &= ~(TUSB_PRCM_MNGMT_15_SW_EN | TUSB_PRCM_MNGMT_33_SW_EN); @@ -599,7 +590,7 @@ static void tusb_source_power(struct musb *musb, int is_on) * and peripheral modes in non-OTG configurations by reconfiguring hardware * and then setting musb->board_mode. For now, only support OTG mode. */ -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int tusb_musb_set_mode(struct musb *musb, u8 musb_mode) { void __iomem *tbase = musb->ctrl_base; u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf; @@ -677,7 +668,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) default_a = is_host_enabled(musb); DBG(2, "Default-%c\n", default_a ? 'A' : 'B'); musb->xceiv->default_a = default_a; - tusb_source_power(musb, default_a); + tusb_musb_set_vbus(musb, default_a); /* Don't allow idling immediately */ if (default_a) @@ -722,7 +713,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: DBG(2, "Got SRP, turning on VBUS\n"); - musb_set_vbus(musb, 1); + musb_platform_set_vbus(musb, 1); /* CONNECT can wake if a_wait_bcon is set */ if (musb->a_wait_bcon != 0) @@ -748,11 +739,11 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) */ if (musb->vbuserr_retry) { musb->vbuserr_retry--; - tusb_source_power(musb, 1); + tusb_musb_set_vbus(musb, 1); } else { musb->vbuserr_retry = VBUSERR_RETRY_COUNT; - tusb_source_power(musb, 0); + tusb_musb_set_vbus(musb, 0); } break; default: @@ -786,7 +777,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) } else { /* REVISIT report overcurrent to hub? */ ERR("vbus too slow, devctl %02x\n", devctl); - tusb_source_power(musb, 0); + tusb_musb_set_vbus(musb, 0); } break; case OTG_STATE_A_WAIT_BCON: @@ -807,7 +798,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) return idle_timeout; } -static irqreturn_t tusb_interrupt(int irq, void *__hci) +static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) { struct musb *musb = __hci; void __iomem *tbase = musb->ctrl_base; @@ -911,7 +902,7 @@ static irqreturn_t tusb_interrupt(int irq, void *__hci) musb_writel(tbase, TUSB_INT_SRC_CLEAR, int_src & ~TUSB_INT_MASK_RESERVED_BITS); - musb_platform_try_idle(musb, idle_timeout); + tusb_musb_try_idle(musb, idle_timeout); musb_writel(tbase, TUSB_INT_MASK, int_mask); spin_unlock_irqrestore(&musb->lock, flags); @@ -926,7 +917,7 @@ static int dma_off; * REVISIT: * - Check what is unnecessary in MGC_HdrcStart() */ -void musb_platform_enable(struct musb *musb) +static void tusb_musb_enable(struct musb *musb) { void __iomem *tbase = musb->ctrl_base; @@ -970,7 +961,7 @@ void musb_platform_enable(struct musb *musb) /* * Disables TUSB6010. Caller must take care of locking. */ -void musb_platform_disable(struct musb *musb) +static void tusb_musb_disable(struct musb *musb) { void __iomem *tbase = musb->ctrl_base; @@ -995,7 +986,7 @@ void musb_platform_disable(struct musb *musb) * Sets up TUSB6010 CPU interface specific signals and registers * Note: Settings optimized for OMAP24xx */ -static void __init tusb_setup_cpu_interface(struct musb *musb) +static void tusb_setup_cpu_interface(struct musb *musb) { void __iomem *tbase = musb->ctrl_base; @@ -1022,7 +1013,7 @@ static void __init tusb_setup_cpu_interface(struct musb *musb) musb_writel(tbase, TUSB_WAIT_COUNT, 1); } -static int __init tusb_start(struct musb *musb) +static int tusb_musb_start(struct musb *musb) { void __iomem *tbase = musb->ctrl_base; int ret = 0; @@ -1091,7 +1082,7 @@ err: return -ENODEV; } -int __init musb_platform_init(struct musb *musb) +static int tusb_musb_init(struct musb *musb) { struct platform_device *pdev; struct resource *mem; @@ -1131,16 +1122,14 @@ int __init musb_platform_init(struct musb *musb) */ musb->mregs += TUSB_BASE_OFFSET; - ret = tusb_start(musb); + ret = tusb_musb_start(musb); if (ret) { printk(KERN_ERR "Could not start tusb6010 (%d)\n", ret); goto done; } - musb->isr = tusb_interrupt; + musb->isr = tusb_musb_interrupt; - if (is_host_enabled(musb)) - musb->board_set_vbus = tusb_source_power; if (is_peripheral_enabled(musb)) { musb->xceiv->set_power = tusb_draw_power; the_musb = musb; @@ -1159,7 +1148,7 @@ done: return ret; } -int musb_platform_exit(struct musb *musb) +static int tusb_musb_exit(struct musb *musb) { del_timer_sync(&musb_idle_timer); the_musb = NULL; @@ -1173,3 +1162,115 @@ int musb_platform_exit(struct musb *musb) usb_nop_xceiv_unregister(); return 0; } + +static const struct musb_platform_ops tusb_ops = { + .init = tusb_musb_init, + .exit = tusb_musb_exit, + + .enable = tusb_musb_enable, + .disable = tusb_musb_disable, + + .set_mode = tusb_musb_set_mode, + .try_idle = tusb_musb_try_idle, + + .vbus_status = tusb_musb_vbus_status, + .set_vbus = tusb_musb_set_vbus, +}; + +static u64 tusb_dmamask = DMA_BIT_MASK(32); + +static int __init tusb_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct tusb6010_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", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } + + musb->dev.parent = &pdev->dev; + musb->dev.dma_mask = &tusb_dmamask; + musb->dev.coherent_dma_mask = tusb_dmamask; + + glue->dev = &pdev->dev; + glue->musb = musb; + + pdata->platform_ops = &tusb_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err2; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err2; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err1; + } + + return 0; + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __exit tusb_remove(struct platform_device *pdev) +{ + struct tusb6010_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + kfree(glue); + + return 0; +} + +static struct platform_driver tusb_driver = { + .remove = __exit_p(tusb_remove), + .driver = { + .name = "musb-tusb", + }, +}; + +MODULE_DESCRIPTION("TUSB6010 MUSB Glue Layer"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init tusb_init(void) +{ + return platform_driver_probe(&tusb_driver, tusb_probe); +} +subsys_initcall(tusb_init); + +static void __exit tusb_exit(void) +{ + platform_driver_unregister(&tusb_driver); +} +module_exit(tusb_exit); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c new file mode 100644 index 000000000000..d6384e4aeef9 --- /dev/null +++ b/drivers/usb/musb/ux500.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2010 ST-Ericsson AB + * Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> + * + * Based on omap2430.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include "musb_core.h" + +struct ux500_glue { + struct device *dev; + struct platform_device *musb; + struct clk *clk; +}; +#define glue_to_musb(g) platform_get_drvdata(g->musb) + +static int ux500_musb_init(struct musb *musb) +{ + musb->xceiv = otg_get_transceiver(); + if (!musb->xceiv) { + pr_err("HS USB OTG: no transceiver configured\n"); + return -ENODEV; + } + + return 0; +} + +static int ux500_musb_exit(struct musb *musb) +{ + otg_put_transceiver(musb->xceiv); + + return 0; +} + +static const struct musb_platform_ops ux500_ops = { + .init = ux500_musb_init, + .exit = ux500_musb_exit, +}; + +static int __init ux500_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; + struct platform_device *musb; + struct ux500_glue *glue; + struct clk *clk; + + 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", -1); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + goto err1; + } + + clk = clk_get(&pdev->dev, "usb"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err2; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err3; + } + + musb->dev.parent = &pdev->dev; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->clk = clk; + + pdata->platform_ops = &ux500_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err4; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err4; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err4; + } + + return 0; + +err4: + clk_disable(clk); + +err3: + clk_put(clk); + +err2: + platform_device_put(musb); + +err1: + kfree(glue); + +err0: + return ret; +} + +static int __exit ux500_remove(struct platform_device *pdev) +{ + struct ux500_glue *glue = platform_get_drvdata(pdev); + + platform_device_del(glue->musb); + platform_device_put(glue->musb); + clk_disable(glue->clk); + clk_put(glue->clk); + kfree(glue); + + return 0; +} + +#ifdef CONFIG_PM +static int ux500_suspend(struct device *dev) +{ + struct ux500_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + + otg_set_suspend(musb->xceiv, 1); + clk_disable(glue->clk); + + return 0; +} + +static int ux500_resume(struct device *dev) +{ + struct ux500_glue *glue = dev_get_drvdata(dev); + struct musb *musb = glue_to_musb(glue); + int ret; + + ret = clk_enable(glue->clk); + if (ret) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + + otg_set_suspend(musb->xceiv, 0); + + return 0; +} + +static const struct dev_pm_ops ux500_pm_ops = { + .suspend = ux500_suspend, + .resume = ux500_resume, +}; + +#define DEV_PM_OPS (&ux500_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver ux500_driver = { + .remove = __exit_p(ux500_remove), + .driver = { + .name = "musb-ux500", + .pm = DEV_PM_OPS, + }, +}; + +MODULE_DESCRIPTION("UX500 MUSB Glue Layer"); +MODULE_AUTHOR("Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init ux500_init(void) +{ + return platform_driver_probe(&ux500_driver, ux500_probe); +} +subsys_initcall(ux500_init); + +static void __exit ux500_exit(void) +{ + platform_driver_unregister(&ux500_driver); +} +module_exit(ux500_exit); diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 6491717a636a..9fb875d5f09c 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -59,6 +59,18 @@ config TWL4030_USB This transceiver supports high and full speed devices plus, in host mode, low speed. +config TWL6030_USB + tristate "TWL6030 USB Transceiver Driver" + depends on TWL4030_CORE + select USB_OTG_UTILS + help + Enable this to support the USB OTG transceiver on TWL6030 + family chips. This TWL6030 transceiver has the VBUS and ID GND + and OTG SRP events capabilities. For all other transceiver functionality + UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs + are hooked to this driver through platform_data structure. + The definition of internal PHY APIs are in the mach-omap2 layer. + config NOP_USB_XCEIV tristate "NOP USB Transceiver Driver" select USB_OTG_UTILS diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 30a23f3b7a1f..a520e715cfd6 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o +obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o obj-$(CONFIG_USB_ULPI) += ulpi.o diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c new file mode 100644 index 000000000000..28f770103640 --- /dev/null +++ b/drivers/usb/otg/twl6030-usb.c @@ -0,0 +1,493 @@ +/* + * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Hema HK <hemahk@ti.com> + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/usb/otg.h> +#include <linux/i2c/twl.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/notifier.h> +#include <linux/slab.h> + +/* usb register definitions */ +#define USB_VENDOR_ID_LSB 0x00 +#define USB_VENDOR_ID_MSB 0x01 +#define USB_PRODUCT_ID_LSB 0x02 +#define USB_PRODUCT_ID_MSB 0x03 +#define USB_VBUS_CTRL_SET 0x04 +#define USB_VBUS_CTRL_CLR 0x05 +#define USB_ID_CTRL_SET 0x06 +#define USB_ID_CTRL_CLR 0x07 +#define USB_VBUS_INT_SRC 0x08 +#define USB_VBUS_INT_LATCH_SET 0x09 +#define USB_VBUS_INT_LATCH_CLR 0x0A +#define USB_VBUS_INT_EN_LO_SET 0x0B +#define USB_VBUS_INT_EN_LO_CLR 0x0C +#define USB_VBUS_INT_EN_HI_SET 0x0D +#define USB_VBUS_INT_EN_HI_CLR 0x0E +#define USB_ID_INT_SRC 0x0F +#define USB_ID_INT_LATCH_SET 0x10 +#define USB_ID_INT_LATCH_CLR 0x11 + +#define USB_ID_INT_EN_LO_SET 0x12 +#define USB_ID_INT_EN_LO_CLR 0x13 +#define USB_ID_INT_EN_HI_SET 0x14 +#define USB_ID_INT_EN_HI_CLR 0x15 +#define USB_OTG_ADP_CTRL 0x16 +#define USB_OTG_ADP_HIGH 0x17 +#define USB_OTG_ADP_LOW 0x18 +#define USB_OTG_ADP_RISE 0x19 +#define USB_OTG_REVISION 0x1A + +/* to be moved to LDO */ +#define TWL6030_MISC2 0xE5 +#define TWL6030_CFG_LDO_PD2 0xF5 +#define TWL6030_BACKUP_REG 0xFA + +#define STS_HW_CONDITIONS 0x21 + +/* In module TWL6030_MODULE_PM_MASTER */ +#define STS_HW_CONDITIONS 0x21 +#define STS_USB_ID BIT(2) + +/* In module TWL6030_MODULE_PM_RECEIVER */ +#define VUSB_CFG_TRANS 0x71 +#define VUSB_CFG_STATE 0x72 +#define VUSB_CFG_VOLTAGE 0x73 + +/* in module TWL6030_MODULE_MAIN_CHARGE */ + +#define CHARGERUSB_CTRL1 0x8 + +#define CONTROLLER_STAT1 0x03 +#define VBUS_DET BIT(2) + +struct twl6030_usb { + struct otg_transceiver otg; + struct device *dev; + + /* for vbus reporting with irqs disabled */ + spinlock_t lock; + + struct regulator *usb3v3; + + int irq1; + int irq2; + u8 linkstat; + u8 asleep; + bool irq_enabled; +}; + +#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg); + +/*-------------------------------------------------------------------------*/ + +static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, + u8 data, u8 address) +{ + int ret = 0; + + ret = twl_i2c_write_u8(module, data, address); + if (ret < 0) + dev_err(twl->dev, + "Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) +{ + u8 data, ret = 0; + + ret = twl_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_err(twl->dev, + "readb[0x%x,0x%x] Error %d\n", + module, address, ret); + return ret; +} + +/*-------------------------------------------------------------------------*/ +static int twl6030_set_phy_clk(struct otg_transceiver *x, int on) +{ + struct twl6030_usb *twl; + struct device *dev; + struct twl4030_usb_data *pdata; + + twl = xceiv_to_twl(x); + dev = twl->dev; + pdata = dev->platform_data; + + pdata->phy_set_clock(twl->dev, on); + + return 0; +} + +static int twl6030_phy_init(struct otg_transceiver *x) +{ + u8 hw_state; + struct twl6030_usb *twl; + struct device *dev; + struct twl4030_usb_data *pdata; + + twl = xceiv_to_twl(x); + dev = twl->dev; + pdata = dev->platform_data; + + regulator_enable(twl->usb3v3); + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + if (hw_state & STS_USB_ID) + pdata->phy_power(twl->dev, 1, 1); + else + pdata->phy_power(twl->dev, 0, 1); + + return 0; +} + +static void twl6030_phy_shutdown(struct otg_transceiver *x) +{ + struct twl6030_usb *twl; + struct device *dev; + struct twl4030_usb_data *pdata; + + twl = xceiv_to_twl(x); + dev = twl->dev; + pdata = dev->platform_data; + pdata->phy_power(twl->dev, 0, 0); + regulator_disable(twl->usb3v3); +} + +static int twl6030_usb_ldo_init(struct twl6030_usb *twl) +{ + + /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG); + + /* Program CFG_LDO_PD2 register and set VUSB bit */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2); + + /* Program MISC2 register and set bit VUSB_IN_VBAT */ + twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2); + + twl->usb3v3 = regulator_get(twl->dev, "vusb"); + if (IS_ERR(twl->usb3v3)) + return -ENODEV; + + regulator_enable(twl->usb3v3); + + /* Program the VUSB_CFG_TRANS for ACTIVE state. */ + twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0x3F, + VUSB_CFG_TRANS); + + /* Program the VUSB_CFG_STATE register to ON on all groups. */ + twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0xE1, + VUSB_CFG_STATE); + + /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ + twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); + + /* + * Program the USB_ID_CTRL_SET register to enable GND drive + * and the ID comparators + */ + twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); + + return 0; +} + +static ssize_t twl6030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl6030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + + switch (twl->linkstat) { + case USB_EVENT_VBUS: + ret = snprintf(buf, PAGE_SIZE, "vbus\n"); + break; + case USB_EVENT_ID: + ret = snprintf(buf, PAGE_SIZE, "id\n"); + break; + case USB_EVENT_NONE: + ret = snprintf(buf, PAGE_SIZE, "none\n"); + break; + default: + ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); + } + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); + +static irqreturn_t twl6030_usb_irq(int irq, void *_twl) +{ + struct twl6030_usb *twl = _twl; + int status; + u8 vbus_state, hw_state; + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, + CONTROLLER_STAT1); + if (!(hw_state & STS_USB_ID)) { + if (vbus_state & VBUS_DET) { + status = USB_EVENT_VBUS; + twl->otg.default_a = false; + twl->otg.state = OTG_STATE_B_IDLE; + } else { + status = USB_EVENT_NONE; + } + if (status >= 0) { + twl->linkstat = status; + blocking_notifier_call_chain(&twl->otg.notifier, + status, twl->otg.gadget); + } + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) +{ + struct twl6030_usb *twl = _twl; + int status = USB_EVENT_NONE; + u8 hw_state; + + hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); + + if (hw_state & STS_USB_ID) { + + twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1); + twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, + 0x10); + status = USB_EVENT_ID; + twl->otg.default_a = true; + twl->otg.state = OTG_STATE_A_IDLE; + blocking_notifier_call_chain(&twl->otg.notifier, status, + twl->otg.gadget); + } else { + twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, + 0x10); + twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, + 0x1); + } + twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status); + twl->linkstat = status; + + return IRQ_HANDLED; +} + +static int twl6030_set_peripheral(struct otg_transceiver *x, + struct usb_gadget *gadget) +{ + struct twl6030_usb *twl; + + if (!x) + return -ENODEV; + + twl = xceiv_to_twl(x); + twl->otg.gadget = gadget; + if (!gadget) + twl->otg.state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl6030_enable_irq(struct otg_transceiver *x) +{ + struct twl6030_usb *twl = xceiv_to_twl(x); + + twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x1); + twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); + twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); + + twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, + REG_INT_MSK_LINE_C); + twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, + REG_INT_MSK_STS_C); + twl6030_usb_irq(twl->irq2, twl); + twl6030_usbotg_irq(twl->irq1, twl); + + return 0; +} + +static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled) +{ + struct twl6030_usb *twl = xceiv_to_twl(x); + + /* + * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 + * register. This enables boost mode. + */ + if (enabled) + twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40, + CHARGERUSB_CTRL1); + else + twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00, + CHARGERUSB_CTRL1); + return 0; +} + +static int twl6030_set_host(struct otg_transceiver *x, struct usb_bus *host) +{ + struct twl6030_usb *twl; + + if (!x) + return -ENODEV; + + twl = xceiv_to_twl(x); + twl->otg.host = host; + if (!host) + twl->otg.state = OTG_STATE_UNDEFINED; + return 0; +} + +static int __devinit twl6030_usb_probe(struct platform_device *pdev) +{ + struct twl6030_usb *twl; + int status, err; + struct twl4030_usb_data *pdata; + struct device *dev = &pdev->dev; + pdata = dev->platform_data; + + twl = kzalloc(sizeof *twl, GFP_KERNEL); + if (!twl) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq1 = platform_get_irq(pdev, 0); + twl->irq2 = platform_get_irq(pdev, 1); + twl->otg.dev = twl->dev; + twl->otg.label = "twl6030"; + twl->otg.set_host = twl6030_set_host; + twl->otg.set_peripheral = twl6030_set_peripheral; + twl->otg.set_vbus = twl6030_set_vbus; + twl->otg.init = twl6030_phy_init; + twl->otg.shutdown = twl6030_phy_shutdown; + + /* init spinlock for workqueue */ + spin_lock_init(&twl->lock); + + err = twl6030_usb_ldo_init(twl); + if (err) { + dev_err(&pdev->dev, "ldo init failed\n"); + kfree(twl); + return err; + } + otg_set_transceiver(&twl->otg); + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier); + + twl->irq_enabled = true; + status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "twl6030_usb", twl); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq1, status); + device_remove_file(twl->dev, &dev_attr_vbus); + kfree(twl); + return status; + } + + status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "twl6030_usb", twl); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq2, status); + free_irq(twl->irq1, twl); + device_remove_file(twl->dev, &dev_attr_vbus); + kfree(twl); + return status; + } + + pdata->phy_init(dev); + twl6030_enable_irq(&twl->otg); + dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); + + return 0; +} + +static int __exit twl6030_usb_remove(struct platform_device *pdev) +{ + struct twl6030_usb *twl = platform_get_drvdata(pdev); + + struct twl4030_usb_data *pdata; + struct device *dev = &pdev->dev; + pdata = dev->platform_data; + + twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, + REG_INT_MSK_LINE_C); + twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, + REG_INT_MSK_STS_C); + free_irq(twl->irq1, twl); + free_irq(twl->irq2, twl); + regulator_put(twl->usb3v3); + pdata->phy_exit(twl->dev); + device_remove_file(twl->dev, &dev_attr_vbus); + kfree(twl); + + return 0; +} + +static struct platform_driver twl6030_usb_driver = { + .probe = twl6030_usb_probe, + .remove = __exit_p(twl6030_usb_remove), + .driver = { + .name = "twl6030_usb", + .owner = THIS_MODULE, + }, +}; + +static int __init twl6030_usb_init(void) +{ + return platform_driver_register(&twl6030_usb_driver); +} +subsys_initcall(twl6030_usb_init); + +static void __exit twl6030_usb_exit(void) +{ + platform_driver_unregister(&twl6030_usb_driver); +} +module_exit(twl6030_usb_exit); + +MODULE_ALIAS("platform:twl6030_usb"); +MODULE_AUTHOR("Hema HK <hemahk@ti.com>"); +MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); +MODULE_LICENSE("GPL"); |