diff options
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r-- | drivers/usb/otg/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/otg/Makefile | 3 | ||||
-rw-r--r-- | drivers/usb/otg/ab8500-usb.c | 6 | ||||
-rw-r--r-- | drivers/usb/otg/langwell_otg.c | 48 | ||||
-rw-r--r-- | drivers/usb/otg/msm_otg.c (renamed from drivers/usb/otg/msm72k_otg.c) | 31 | ||||
-rw-r--r-- | drivers/usb/otg/nop-usb-xceiv.c | 2 | ||||
-rw-r--r-- | drivers/usb/otg/twl4030-usb.c | 8 | ||||
-rw-r--r-- | drivers/usb/otg/twl6030-usb.c | 56 | ||||
-rw-r--r-- | drivers/usb/otg/ulpi_viewport.c | 80 |
9 files changed, 158 insertions, 85 deletions
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 9ffc8237fb4..daf3e5f1a0e 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -49,6 +49,13 @@ config USB_ULPI Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. +config USB_ULPI_VIEWPORT + bool + depends on USB_ULPI + help + Provides read/write operations to the ULPI phy register set for + controllers with a viewport register (e.g. Chipidea/ARC controllers). + config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 @@ -93,7 +100,7 @@ config USB_LANGWELL_OTG To compile this driver as a module, choose M here: the module will be called langwell_otg. -config USB_MSM_OTG_72K +config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" depends on (USB || USB_GADGET) && ARCH_MSM select USB_OTG_UTILS diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index a520e715cfd..e22d917de01 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -16,5 +16,6 @@ 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 -obj-$(CONFIG_USB_MSM_OTG_72K) += msm72k_otg.o +obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o +obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o obj-$(CONFIG_AB8500_USB) += ab8500-usb.o diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index d14736b3107..07ccea9ada4 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -212,7 +212,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) break; } - blocking_notifier_call_chain(&ab->otg.notifier, event, v); + atomic_notifier_call_chain(&ab->otg.notifier, event, v); return 0; } @@ -281,7 +281,7 @@ static int ab8500_usb_set_power(struct otg_transceiver *otg, unsigned mA) ab->vbus_draw = mA; if (mA) - blocking_notifier_call_chain(&ab->otg.notifier, + atomic_notifier_call_chain(&ab->otg.notifier, USB_EVENT_ENUMERATED, ab->otg.gadget); return 0; } @@ -500,7 +500,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ab); - BLOCKING_INIT_NOTIFIER_HEAD(&ab->otg.notifier); + ATOMIC_INIT_NOTIFIER_HEAD(&ab->otg.notifier); /* v1: Wait for link status to become stable. * all: Updates form set_host and set_peripheral as they are atomic. diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c index 9fea48264fa..7f9b8cd4514 100644 --- a/drivers/usb/otg/langwell_otg.c +++ b/drivers/usb/otg/langwell_otg.c @@ -174,50 +174,24 @@ static int langwell_otg_set_power(struct otg_transceiver *otg, return 0; } -/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ +/* A-device drives vbus, controlled through IPC commands */ static int langwell_otg_set_vbus(struct otg_transceiver *otg, bool enabled) { struct langwell_otg *lnw = the_transceiver; - u8 r; + u8 sub_id; dev_dbg(lnw->dev, "%s <--- %s\n", __func__, enabled ? "on" : "off"); - /* FIXME: surely we should cache this on the first read. If not use - readv to avoid two transactions */ - if (intel_scu_ipc_ioread8(0x00, &r) < 0) { - dev_dbg(lnw->dev, "Failed to read PMIC register 0xD2"); - return -EBUSY; - } - if ((r & 0x03) != 0x02) { - dev_dbg(lnw->dev, "not NEC PMIC attached\n"); - return -EBUSY; - } - - if (intel_scu_ipc_ioread8(0x20, &r) < 0) { - dev_dbg(lnw->dev, "Failed to read PMIC register 0xD2"); - return -EBUSY; - } - - if ((r & 0x20) == 0) { - dev_dbg(lnw->dev, "no battery attached\n"); - return -EBUSY; - } + if (enabled) + sub_id = 0x8; /* Turn on the VBus */ + else + sub_id = 0x9; /* Turn off the VBus */ - /* Workaround for battery attachment issue */ - if (r == 0x34) { - dev_dbg(lnw->dev, "no battery attached on SH\n"); + if (intel_scu_ipc_simple_command(0xef, sub_id)) { + dev_dbg(lnw->dev, "Failed to set Vbus via IPC commands\n"); return -EBUSY; } - dev_dbg(lnw->dev, "battery attached. 2 reg = %x\n", r); - - /* workaround: FW detect writing 0x20/0xc0 to d4 event. - * this is only for NEC PMIC. - */ - - if (intel_scu_ipc_iowrite8(0xD4, enabled ? 0x20 : 0xC0)) - dev_dbg(lnw->dev, "Failed to write PMIC.\n"); - dev_dbg(lnw->dev, "%s --->\n", __func__); return 0; @@ -394,14 +368,14 @@ static void langwell_otg_phy_low_power(int on) dev_dbg(lnw->dev, "%s <--- done\n", __func__); } -/* After drv vbus, add 2 ms delay to set PHCD */ +/* After drv vbus, add 5 ms delay to set PHCD */ static void langwell_otg_phy_low_power_wait(int on) { struct langwell_otg *lnw = the_transceiver; - dev_dbg(lnw->dev, "add 2ms delay before programing PHCD\n"); + dev_dbg(lnw->dev, "add 5ms delay before programing PHCD\n"); - mdelay(2); + mdelay(5); langwell_otg_phy_low_power(on); } diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm_otg.c index 1cd52edcd0c..296598628b8 100644 --- a/drivers/usb/otg/msm72k_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -253,6 +253,9 @@ static int msm_otg_reset(struct otg_transceiver *otg) } #define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) +#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) + +#ifdef CONFIG_PM_SLEEP static int msm_otg_suspend(struct msm_otg *motg) { struct otg_transceiver *otg = &motg->otg; @@ -334,7 +337,6 @@ static int msm_otg_suspend(struct msm_otg *motg) return 0; } -#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) static int msm_otg_resume(struct msm_otg *motg) { struct otg_transceiver *otg = &motg->otg; @@ -399,6 +401,7 @@ skip_phy_resume: return 0; } +#endif static void msm_otg_start_host(struct otg_transceiver *otg, int on) { @@ -720,7 +723,8 @@ static int msm_otg_mode_open(struct inode *inode, struct file *file) static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { - struct msm_otg *motg = file->private_data; + struct seq_file *s = file->private_data; + struct msm_otg *motg = s->private; char buf[16]; struct otg_transceiver *otg = &motg->otg; int status = count; @@ -972,7 +976,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) msm_otg_debugfs_cleanup(); cancel_work_sync(&motg->sm_work); - msm_otg_resume(motg); + pm_runtime_resume(&pdev->dev); device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); @@ -1050,13 +1054,9 @@ static int msm_otg_runtime_resume(struct device *dev) dev_dbg(dev, "OTG runtime resume\n"); return msm_otg_resume(motg); } -#else -#define msm_otg_runtime_idle NULL -#define msm_otg_runtime_suspend NULL -#define msm_otg_runtime_resume NULL #endif -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int msm_otg_pm_suspend(struct device *dev) { struct msm_otg *motg = dev_get_drvdata(dev); @@ -1086,25 +1086,24 @@ static int msm_otg_pm_resume(struct device *dev) return 0; } -#else -#define msm_otg_pm_suspend NULL -#define msm_otg_pm_resume NULL #endif +#ifdef CONFIG_PM static const struct dev_pm_ops msm_otg_dev_pm_ops = { - .runtime_suspend = msm_otg_runtime_suspend, - .runtime_resume = msm_otg_runtime_resume, - .runtime_idle = msm_otg_runtime_idle, - .suspend = msm_otg_pm_suspend, - .resume = msm_otg_pm_resume, + SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) + SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, + msm_otg_runtime_idle) }; +#endif static struct platform_driver msm_otg_driver = { .remove = __devexit_p(msm_otg_remove), .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM .pm = &msm_otg_dev_pm_ops, +#endif }, }; diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 8acf165fe13..c1e36004643 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -132,7 +132,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nop); - BLOCKING_INIT_NOTIFIER_HEAD(&nop->otg.notifier); + ATOMIC_INIT_NOTIFIER_HEAD(&nop->otg.notifier); return 0; exit: diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 6ca505f333e..e01b073cc48 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -275,6 +275,8 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl) dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", status, status, linkstat); + twl->otg.last_event = linkstat; + /* REVISIT this assumes host and peripheral controllers * are registered, and that both are active... */ @@ -512,7 +514,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) else twl4030_phy_resume(twl); - blocking_notifier_call_chain(&twl->otg.notifier, status, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -534,7 +536,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) twl->asleep = 0; } - blocking_notifier_call_chain(&twl->otg.notifier, status, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -623,7 +625,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) 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); + ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier); /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 28f77010364..8a91b4b832a 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -149,7 +149,6 @@ static int twl6030_set_phy_clk(struct otg_transceiver *x, int on) static int twl6030_phy_init(struct otg_transceiver *x) { - u8 hw_state; struct twl6030_usb *twl; struct device *dev; struct twl4030_usb_data *pdata; @@ -158,11 +157,7 @@ static int twl6030_phy_init(struct otg_transceiver *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) + if (twl->linkstat == USB_EVENT_ID) pdata->phy_power(twl->dev, 1, 1); else pdata->phy_power(twl->dev, 0, 1); @@ -180,7 +175,17 @@ static void twl6030_phy_shutdown(struct otg_transceiver *x) dev = twl->dev; pdata = dev->platform_data; pdata->phy_power(twl->dev, 0, 0); - regulator_disable(twl->usb3v3); +} + +static int twl6030_phy_suspend(struct otg_transceiver *x, int suspend) +{ + struct twl6030_usb *twl = xceiv_to_twl(x); + struct device *dev = twl->dev; + struct twl4030_usb_data *pdata = dev->platform_data; + + pdata->phy_suspend(dev, suspend); + + return 0; } static int twl6030_usb_ldo_init(struct twl6030_usb *twl) @@ -199,16 +204,6 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl) 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); @@ -261,16 +256,25 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) CONTROLLER_STAT1); if (!(hw_state & STS_USB_ID)) { if (vbus_state & VBUS_DET) { + regulator_enable(twl->usb3v3); + twl->asleep = 1; status = USB_EVENT_VBUS; twl->otg.default_a = false; twl->otg.state = OTG_STATE_B_IDLE; + twl->linkstat = status; + twl->otg.last_event = status; + atomic_notifier_call_chain(&twl->otg.notifier, + status, twl->otg.gadget); } else { status = USB_EVENT_NONE; - } - if (status >= 0) { twl->linkstat = status; - blocking_notifier_call_chain(&twl->otg.notifier, + twl->otg.last_event = status; + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); + if (twl->asleep) { + regulator_disable(twl->usb3v3); + twl->asleep = 0; + } } } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -288,13 +292,17 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) if (hw_state & STS_USB_ID) { + regulator_enable(twl->usb3v3); + twl->asleep = 1; 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->linkstat = status; + twl->otg.last_event = status; + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } else { twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, @@ -303,7 +311,6 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) 0x1); } twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status); - twl->linkstat = status; return IRQ_HANDLED; } @@ -395,6 +402,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->otg.set_vbus = twl6030_set_vbus; twl->otg.init = twl6030_phy_init; twl->otg.shutdown = twl6030_phy_shutdown; + twl->otg.set_suspend = twl6030_phy_suspend; /* init spinlock for workqueue */ spin_lock_init(&twl->lock); @@ -411,7 +419,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) 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); + ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier); twl->irq_enabled = true; status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, @@ -437,7 +445,9 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) return status; } + twl->asleep = 0; pdata->phy_init(dev); + twl6030_phy_suspend(&twl->otg, 0); twl6030_enable_irq(&twl->otg); dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c new file mode 100644 index 00000000000..e9a37f90994 --- /dev/null +++ b/drivers/usb/otg/ulpi_viewport.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/io.h> +#include <linux/usb/otg.h> +#include <linux/usb/ulpi.h> + +#define ULPI_VIEW_WAKEUP (1 << 31) +#define ULPI_VIEW_RUN (1 << 30) +#define ULPI_VIEW_WRITE (1 << 29) +#define ULPI_VIEW_READ (0 << 29) +#define ULPI_VIEW_ADDR(x) (((x) & 0xff) << 16) +#define ULPI_VIEW_DATA_READ(x) (((x) >> 8) & 0xff) +#define ULPI_VIEW_DATA_WRITE(x) ((x) & 0xff) + +static int ulpi_viewport_wait(void __iomem *view, u32 mask) +{ + unsigned long usec = 2000; + + while (usec--) { + if (!(readl(view) & mask)) + return 0; + + udelay(1); + }; + + return -ETIMEDOUT; +} + +static int ulpi_viewport_read(struct otg_transceiver *otg, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN); + if (ret) + return ret; + + return ULPI_VIEW_DATA_READ(readl(view)); +} + +static int ulpi_viewport_write(struct otg_transceiver *otg, u32 val, u32 reg) +{ + int ret; + void __iomem *view = otg->io_priv; + + writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view); + ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP); + if (ret) + return ret; + + writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) | + ULPI_VIEW_ADDR(reg), view); + + return ulpi_viewport_wait(view, ULPI_VIEW_RUN); +} + +struct otg_io_access_ops ulpi_viewport_access_ops = { + .read = ulpi_viewport_read, + .write = ulpi_viewport_write, +}; |