summaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg
diff options
context:
space:
mode:
authorLinus Torvalds2012-07-26 19:23:47 +0200
committerLinus Torvalds2012-07-26 19:23:47 +0200
commit9fc377799bc9bfd8d5cb35d0d1ea2e2458cbdbb3 (patch)
treefe93603b4e33dd50ff5f95ff769a0748b230cdf9 /drivers/usb/otg
parentMerge tag 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/j... (diff)
parentusb: Add USB_QUIRK_RESET_RESUME for all Logitech UVC webcams (diff)
downloadkernel-qcow2-linux-9fc377799bc9bfd8d5cb35d0d1ea2e2458cbdbb3.tar.gz
kernel-qcow2-linux-9fc377799bc9bfd8d5cb35d0d1ea2e2458cbdbb3.tar.xz
kernel-qcow2-linux-9fc377799bc9bfd8d5cb35d0d1ea2e2458cbdbb3.zip
Merge tag 'usb-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB patches from Greg Kroah-Hartman: "Here's the big USB patch set for the 3.6-rc1 merge window. Lots of little changes in here, primarily for gadget controllers and drivers. There's some scsi changes that I think also went in through the scsi tree, but they merge just fine. All of these patches have been in the linux-next tree for a while now. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" Fix up trivial conflicts in include/scsi/scsi_device.h (same libata conflict that Jeff had already encountered) * tag 'usb-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (207 commits) usb: Add USB_QUIRK_RESET_RESUME for all Logitech UVC webcams usb: Add quirk detection based on interface information usb: s3c-hsotg: Add header file protection macros in s3c-hsotg.h USB: ehci-s5p: Add vbus setup function to the s5p ehci glue layer USB: add USB_VENDOR_AND_INTERFACE_INFO() macro USB: notify phy when root hub port connect change USB: remove 8 bytes of padding from usb_host_interface on 64 bit builds USB: option: add ZTE MF821D USB: sierra: QMI mode MC7710 moved to qcserial USB: qcserial: adding Sierra Wireless devices USB: qcserial: support generic Qualcomm serial ports USB: qcserial: make probe more flexible USB: qcserial: centralize probe exit path USB: qcserial: consolidate usb_set_interface calls USB: ehci-s5p: Add support for device tree USB: ohci-exynos: Add support for device tree USB: ehci-omap: fix compile failure(v1) usb: host: tegra: pass correct pointer in ehci_setup() USB: ehci-fsl: Update ifdef check to work on 64-bit ppc USB: serial: keyspan: Removed unrequired parentheses. ...
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r--drivers/usb/otg/Kconfig10
-rw-r--r--drivers/usb/otg/Makefile1
-rw-r--r--drivers/usb/otg/ab8500-usb.c4
-rw-r--r--drivers/usb/otg/fsl_otg.c6
-rw-r--r--drivers/usb/otg/gpio_vbus.c4
-rw-r--r--drivers/usb/otg/isp1301_omap.c19
-rw-r--r--drivers/usb/otg/msm_otg.c6
-rw-r--r--drivers/usb/otg/mv_otg.c6
-rw-r--r--drivers/usb/otg/mxs-phy.c186
-rw-r--r--drivers/usb/otg/nop-usb-xceiv.c4
-rw-r--r--drivers/usb/otg/otg.c181
-rw-r--r--drivers/usb/otg/twl4030-usb.c73
-rw-r--r--drivers/usb/otg/twl6030-usb.c71
13 files changed, 433 insertions, 138 deletions
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 5c87db06b598..13fd1ddf742f 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -116,6 +116,16 @@ config FSL_USB2_OTG
help
Enable this to support Freescale USB OTG transceiver.
+config USB_MXS_PHY
+ tristate "Freescale MXS USB PHY support"
+ depends on ARCH_MXC || ARCH_MXS
+ select STMP_DEVICE
+ select USB_OTG_UTILS
+ help
+ Enable this to support the Freescale MXS USB PHY.
+
+ MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+
config USB_MV_OTG
tristate "Marvell USB OTG support"
depends on USB_EHCI_MV && USB_MV_UDC && USB_SUSPEND
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 41aa5098b139..a844b8d35d14 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -20,4 +20,5 @@ obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o
obj-$(CONFIG_AB8500_USB) += ab8500-usb.o
fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o
obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o
+obj-$(CONFIG_USB_MXS_PHY) += mxs-phy.o
obj-$(CONFIG_USB_MV_OTG) += mv_otg.o
diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c
index a84af677dc59..ae8ad561f083 100644
--- a/drivers/usb/otg/ab8500-usb.c
+++ b/drivers/usb/otg/ab8500-usb.c
@@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev)
if (err < 0)
goto fail0;
- err = usb_set_transceiver(&ab->phy);
+ err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2);
if (err) {
dev_err(&pdev->dev, "Can't register transceiver\n");
goto fail1;
@@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev)
cancel_work_sync(&ab->phy_dis_work);
- usb_set_transceiver(NULL);
+ usb_remove_phy(&ab->phy);
ab8500_usb_host_phy_dis(ab);
ab8500_usb_peri_phy_dis(ab);
diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c
index be4a63e8302f..23c798cb2d7f 100644
--- a/drivers/usb/otg/fsl_otg.c
+++ b/drivers/usb/otg/fsl_otg.c
@@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
fsl_otg_dev = fsl_otg_tc;
/* Store the otg transceiver */
- status = usb_set_transceiver(&fsl_otg_tc->phy);
+ status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2);
if (status) {
pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n");
goto err;
@@ -824,7 +824,7 @@ err:
int usb_otg_start(struct platform_device *pdev)
{
struct fsl_otg *p_otg;
- struct usb_phy *otg_trans = usb_get_transceiver();
+ struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
struct otg_fsm *fsm;
int status;
struct resource *res;
@@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
- usb_set_transceiver(NULL);
+ usb_remove_phy(&fsl_otg_dev->phy);
free_irq(fsl_otg_dev->irq, fsl_otg_dev);
iounmap((void *)usb_dr_regs);
diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c
index bde6298a9693..a67ffe22179a 100644
--- a/drivers/usb/otg/gpio_vbus.c
+++ b/drivers/usb/otg/gpio_vbus.c
@@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev)
}
/* only active when a gadget is registered */
- err = usb_set_transceiver(&gpio_vbus->phy);
+ err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2);
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
@@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&gpio_vbus->work);
regulator_put(gpio_vbus->vbus_draw);
- usb_set_transceiver(NULL);
+ usb_remove_phy(&gpio_vbus->phy);
free_irq(gpio_vbus->irq, pdev);
if (gpio_is_valid(pdata->gpio_pullup))
diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c
index e0558dfcfafc..575fc815c932 100644
--- a/drivers/usb/otg/isp1301_omap.c
+++ b/drivers/usb/otg/isp1301_omap.c
@@ -1336,9 +1336,6 @@ static int
isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
{
struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
-#ifndef CONFIG_USB_OTG
- u32 l;
-#endif
if (!otg || isp != the_transceiver)
return -ENODEV;
@@ -1365,10 +1362,14 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
otg->gadget = gadget;
// FIXME update its refcount
- l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
- l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
- l |= OTG_ID;
- omap_writel(l, OTG_CTRL);
+ {
+ u32 l;
+
+ l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+ l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
+ l |= OTG_ID;
+ omap_writel(l, OTG_CTRL);
+ }
power_up(isp);
isp->phy.state = OTG_STATE_B_IDLE;
@@ -1610,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES);
#endif
- status = usb_set_transceiver(&isp->phy);
+ status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2);
if (status < 0)
dev_err(&i2c->dev, "can't register transceiver, %d\n",
status);
@@ -1649,7 +1650,7 @@ subsys_initcall(isp_init);
static void __exit isp_exit(void)
{
if (the_transceiver)
- usb_set_transceiver(NULL);
+ usb_remove_phy(&the_transceiver->phy);
i2c_del_driver(&isp1301_driver);
}
module_exit(isp_exit);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 1d0347c247d1..9f5fc906041a 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1555,9 +1555,9 @@ static int __init msm_otg_probe(struct platform_device *pdev)
phy->otg->set_host = msm_otg_set_host;
phy->otg->set_peripheral = msm_otg_set_peripheral;
- ret = usb_set_transceiver(&motg->phy);
+ ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2);
if (ret) {
- dev_err(&pdev->dev, "usb_set_transceiver failed\n");
+ dev_err(&pdev->dev, "usb_add_phy failed\n");
goto free_irq;
}
@@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
pm_runtime_disable(&pdev->dev);
- usb_set_transceiver(NULL);
+ usb_remove_phy(phy);
free_irq(motg->irq, motg);
/*
diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c
index 6cc6c3ffbb83..3f124e8f5792 100644
--- a/drivers/usb/otg/mv_otg.c
+++ b/drivers/usb/otg/mv_otg.c
@@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev)
for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++)
clk_put(mvotg->clk[clk_i]);
- usb_set_transceiver(NULL);
+ usb_remove_phy(&mvotg->phy);
platform_set_drvdata(pdev, NULL);
kfree(mvotg->phy.otg);
@@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev)
goto err_disable_clk;
}
- retval = usb_set_transceiver(&mvotg->phy);
+ retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2);
if (retval < 0) {
dev_err(&pdev->dev, "can't register transceiver, %d\n",
retval);
@@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev)
return 0;
err_set_transceiver:
- usb_set_transceiver(NULL);
+ usb_remove_phy(&mvotg->phy);
err_free_irq:
free_irq(mvotg->irq, mvotg);
err_disable_clk:
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
new file mode 100644
index 000000000000..c1a67cb8e244
--- /dev/null
+++ b/drivers/usb/otg/mxs-phy.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Marek Vasut <marex@denx.de>
+ * on behalf of DENX Software Engineering GmbH
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/usb/otg.h>
+#include <linux/stmp_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "mxs_phy"
+
+#define HW_USBPHY_PWD 0x00
+#define HW_USBPHY_CTRL 0x30
+#define HW_USBPHY_CTRL_SET 0x34
+#define HW_USBPHY_CTRL_CLR 0x38
+
+#define BM_USBPHY_CTRL_SFTRST BIT(31)
+#define BM_USBPHY_CTRL_CLKGATE BIT(30)
+#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15)
+#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14)
+#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1)
+
+struct mxs_phy {
+ struct usb_phy phy;
+ struct clk *clk;
+};
+
+#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
+
+static void mxs_phy_hw_init(struct mxs_phy *mxs_phy)
+{
+ void __iomem *base = mxs_phy->phy.io_priv;
+
+ stmp_reset_block(base + HW_USBPHY_CTRL);
+
+ /* Power up the PHY */
+ writel_relaxed(0, base + HW_USBPHY_PWD);
+
+ /* enable FS/LS device */
+ writel_relaxed(BM_USBPHY_CTRL_ENUTMILEVEL2 |
+ BM_USBPHY_CTRL_ENUTMILEVEL3,
+ base + HW_USBPHY_CTRL_SET);
+}
+
+static int mxs_phy_init(struct usb_phy *phy)
+{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+ clk_prepare_enable(mxs_phy->clk);
+ mxs_phy_hw_init(mxs_phy);
+
+ return 0;
+}
+
+static void mxs_phy_shutdown(struct usb_phy *phy)
+{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+ writel_relaxed(BM_USBPHY_CTRL_CLKGATE,
+ phy->io_priv + HW_USBPHY_CTRL_SET);
+
+ clk_disable_unprepare(mxs_phy->clk);
+}
+
+static int mxs_phy_on_connect(struct usb_phy *phy, int port)
+{
+ dev_dbg(phy->dev, "Connect on port %d\n", port);
+
+ mxs_phy_hw_init(to_mxs_phy(phy));
+ writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+ phy->io_priv + HW_USBPHY_CTRL_SET);
+
+ return 0;
+}
+
+static int mxs_phy_on_disconnect(struct usb_phy *phy, int port)
+{
+ dev_dbg(phy->dev, "Disconnect on port %d\n", port);
+
+ writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+ phy->io_priv + HW_USBPHY_CTRL_CLR);
+
+ return 0;
+}
+
+static int mxs_phy_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+ struct clk *clk;
+ struct mxs_phy *mxs_phy;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't get device resources\n");
+ return -ENOENT;
+ }
+
+ base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!base)
+ return -EBUSY;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev,
+ "can't get the clock, err=%ld", PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
+ if (!mxs_phy) {
+ dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n");
+ return -ENOMEM;
+ }
+
+ mxs_phy->phy.io_priv = base;
+ mxs_phy->phy.dev = &pdev->dev;
+ mxs_phy->phy.label = DRIVER_NAME;
+ mxs_phy->phy.init = mxs_phy_init;
+ mxs_phy->phy.shutdown = mxs_phy_shutdown;
+ mxs_phy->phy.notify_connect = mxs_phy_on_connect;
+ mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
+
+ ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier);
+
+ mxs_phy->clk = clk;
+
+ platform_set_drvdata(pdev, &mxs_phy->phy);
+
+ return 0;
+}
+
+static int __devexit mxs_phy_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id mxs_phy_dt_ids[] = {
+ { .compatible = "fsl,imx23-usbphy", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
+
+static struct platform_driver mxs_phy_driver = {
+ .probe = mxs_phy_probe,
+ .remove = __devexit_p(mxs_phy_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mxs_phy_dt_ids,
+ },
+};
+
+static int __init mxs_phy_module_init(void)
+{
+ return platform_driver_register(&mxs_phy_driver);
+}
+postcore_initcall(mxs_phy_module_init);
+
+static void __exit mxs_phy_module_exit(void)
+{
+ platform_driver_unregister(&mxs_phy_driver);
+}
+module_exit(mxs_phy_module_exit);
+
+MODULE_ALIAS("platform:mxs-usb-phy");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
+MODULE_DESCRIPTION("Freescale MXS USB PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c
index 58b26df6afd1..803f958f4133 100644
--- a/drivers/usb/otg/nop-usb-xceiv.c
+++ b/drivers/usb/otg/nop-usb-xceiv.c
@@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
nop->phy.otg->set_host = nop_set_host;
nop->phy.otg->set_peripheral = nop_set_peripheral;
- err = usb_set_transceiver(&nop->phy);
+ err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2);
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
@@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev)
{
struct nop_usb_xceiv *nop = platform_get_drvdata(pdev);
- usb_set_transceiver(NULL);
+ usb_remove_phy(&nop->phy);
platform_set_drvdata(pdev, NULL);
kfree(nop->phy.otg);
diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c
index 801e597a1541..1bf60a22595c 100644
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -11,60 +11,195 @@
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/err.h>
#include <linux/device.h>
+#include <linux/slab.h>
#include <linux/usb/otg.h>
-static struct usb_phy *phy;
+static LIST_HEAD(phy_list);
+static DEFINE_SPINLOCK(phy_lock);
+
+static struct usb_phy *__usb_find_phy(struct list_head *list,
+ enum usb_phy_type type)
+{
+ struct usb_phy *phy = NULL;
+
+ list_for_each_entry(phy, list, head) {
+ if (phy->type != type)
+ continue;
+
+ return phy;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
+static void devm_usb_phy_release(struct device *dev, void *res)
+{
+ struct usb_phy *phy = *(struct usb_phy **)res;
+
+ usb_put_phy(phy);
+}
+
+static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
+{
+ return res == match_data;
+}
/**
- * usb_get_transceiver - find the (single) USB transceiver
+ * devm_usb_get_phy - find the USB PHY
+ * @dev - device that requests this phy
+ * @type - the type of the phy the controller requires
*
- * Returns the transceiver driver, after getting a refcount to it; or
- * null if there is no such transceiver. The caller is responsible for
- * calling usb_put_transceiver() to release that count.
+ * Gets the phy using usb_get_phy(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
*
* For use by USB host and peripheral drivers.
*/
-struct usb_phy *usb_get_transceiver(void)
+struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type)
{
- if (phy)
- get_device(phy->dev);
+ struct usb_phy **ptr, *phy;
+
+ ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return NULL;
+
+ phy = usb_get_phy(type);
+ if (!IS_ERR(phy)) {
+ *ptr = phy;
+ devres_add(dev, ptr);
+ } else
+ devres_free(ptr);
+
+ return phy;
+}
+EXPORT_SYMBOL(devm_usb_get_phy);
+
+/**
+ * usb_get_phy - find the USB PHY
+ * @type - the type of the phy the controller requires
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling usb_put_phy() to release that count.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *usb_get_phy(enum usb_phy_type type)
+{
+ struct usb_phy *phy = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy_lock, flags);
+
+ phy = __usb_find_phy(&phy_list, type);
+ if (IS_ERR(phy)) {
+ pr_err("unable to find transceiver of type %s\n",
+ usb_phy_type_string(type));
+ goto err0;
+ }
+
+ get_device(phy->dev);
+
+err0:
+ spin_unlock_irqrestore(&phy_lock, flags);
+
return phy;
}
-EXPORT_SYMBOL(usb_get_transceiver);
+EXPORT_SYMBOL(usb_get_phy);
+
+/**
+ * devm_usb_put_phy - release the USB PHY
+ * @dev - device that wants to release this phy
+ * @phy - the phy returned by devm_usb_get_phy()
+ *
+ * destroys the devres associated with this phy and invokes usb_put_phy
+ * to release the phy.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+void devm_usb_put_phy(struct device *dev, struct usb_phy *phy)
+{
+ int r;
+
+ r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL(devm_usb_put_phy);
/**
- * usb_put_transceiver - release the (single) USB transceiver
- * @x: the transceiver returned by usb_get_transceiver()
+ * usb_put_phy - release the USB PHY
+ * @x: the phy returned by usb_get_phy()
*
- * Releases a refcount the caller received from usb_get_transceiver().
+ * Releases a refcount the caller received from usb_get_phy().
*
* For use by USB host and peripheral drivers.
*/
-void usb_put_transceiver(struct usb_phy *x)
+void usb_put_phy(struct usb_phy *x)
{
if (x)
put_device(x->dev);
}
-EXPORT_SYMBOL(usb_put_transceiver);
+EXPORT_SYMBOL(usb_put_phy);
/**
- * usb_set_transceiver - declare the (single) USB transceiver
- * @x: the USB transceiver to be used; or NULL
+ * usb_add_phy - declare the USB PHY
+ * @x: the USB phy to be used; or NULL
+ * @type - the type of this PHY
*
- * This call is exclusively for use by transceiver drivers, which
+ * This call is exclusively for use by phy drivers, which
* coordinate the activities of drivers for host and peripheral
* controllers, and in some cases for VBUS current regulation.
*/
-int usb_set_transceiver(struct usb_phy *x)
+int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct usb_phy *phy;
+
+ if (x && x->type != USB_PHY_TYPE_UNDEFINED) {
+ dev_err(x->dev, "not accepting initialized PHY %s\n", x->label);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&phy_lock, flags);
+
+ list_for_each_entry(phy, &phy_list, head) {
+ if (phy->type == type) {
+ ret = -EBUSY;
+ dev_err(x->dev, "transceiver type %s already exists\n",
+ usb_phy_type_string(type));
+ goto out;
+ }
+ }
+
+ x->type = type;
+ list_add_tail(&x->head, &phy_list);
+
+out:
+ spin_unlock_irqrestore(&phy_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(usb_add_phy);
+
+/**
+ * usb_remove_phy - remove the OTG PHY
+ * @x: the USB OTG PHY to be removed;
+ *
+ * This reverts the effects of usb_add_phy
+ */
+void usb_remove_phy(struct usb_phy *x)
{
- if (phy && x)
- return -EBUSY;
- phy = x;
- return 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy_lock, flags);
+ if (x)
+ list_del(&x->head);
+ spin_unlock_irqrestore(&phy_lock, flags);
}
-EXPORT_SYMBOL(usb_set_transceiver);
+EXPORT_SYMBOL(usb_remove_phy);
const char *otg_state_string(enum usb_otg_state state)
{
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index c4a86da858e2..523cad5bfea9 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -33,11 +33,11 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
+#include <linux/usb/musb-omap.h>
#include <linux/usb/ulpi.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
-#include <linux/notifier.h>
#include <linux/slab.h>
/* Register defines */
@@ -159,7 +159,7 @@ struct twl4030_usb {
enum twl4030_usb_mode usb_mode;
int irq;
- u8 linkstat;
+ enum omap_musb_vbus_id_status linkstat;
bool vbus_supplied;
u8 asleep;
bool irq_enabled;
@@ -246,11 +246,11 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
/*-------------------------------------------------------------------------*/
-static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
+static enum omap_musb_vbus_id_status
+ twl4030_usb_linkstat(struct twl4030_usb *twl)
{
int status;
- int linkstat = USB_EVENT_NONE;
- struct usb_otg *otg = twl->phy.otg;
+ enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
twl->vbus_supplied = false;
@@ -273,30 +273,23 @@ static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl)
twl->vbus_supplied = true;
if (status & BIT(2))
- linkstat = USB_EVENT_ID;
+ linkstat = OMAP_MUSB_ID_GROUND;
else
- linkstat = USB_EVENT_VBUS;
- } else
- linkstat = USB_EVENT_NONE;
+ linkstat = OMAP_MUSB_VBUS_VALID;
+ } else {
+ if (twl->linkstat != OMAP_MUSB_UNKNOWN)
+ linkstat = OMAP_MUSB_VBUS_OFF;
+ }
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
status, status, linkstat);
- twl->phy.last_event = linkstat;
-
/* REVISIT this assumes host and peripheral controllers
* are registered, and that both are active...
*/
spin_lock_irq(&twl->lock);
twl->linkstat = linkstat;
- if (linkstat == USB_EVENT_ID) {
- otg->default_a = true;
- twl->phy.state = OTG_STATE_A_IDLE;
- } else {
- otg->default_a = false;
- twl->phy.state = OTG_STATE_B_IDLE;
- }
spin_unlock_irq(&twl->lock);
return linkstat;
@@ -501,10 +494,10 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{
struct twl4030_usb *twl = _twl;
- int status;
+ enum omap_musb_vbus_id_status status;
status = twl4030_usb_linkstat(twl);
- if (status >= 0) {
+ if (status > 0) {
/* FIXME add a set_power() method so that B-devices can
* configure the charger appropriately. It's not always
* correct to consume VBUS power, and how much current to
@@ -516,13 +509,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
* USB_LINK_VBUS state. musb_hdrc won't care until it
* starts to handle softconnect right.
*/
- if (status == USB_EVENT_NONE)
+ if (status == OMAP_MUSB_VBUS_OFF ||
+ status == OMAP_MUSB_ID_FLOAT)
twl4030_phy_suspend(twl, 0);
else
twl4030_phy_resume(twl);
- atomic_notifier_call_chain(&twl->phy.notifier, status,
- twl->phy.otg->gadget);
+ omap_musb_mailbox(twl->linkstat);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
@@ -531,11 +524,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
static void twl4030_usb_phy_init(struct twl4030_usb *twl)
{
- int status;
+ enum omap_musb_vbus_id_status status;
status = twl4030_usb_linkstat(twl);
- if (status >= 0) {
- if (status == USB_EVENT_NONE) {
+ if (status > 0) {
+ if (status == OMAP_MUSB_VBUS_OFF ||
+ status == OMAP_MUSB_ID_FLOAT) {
__twl4030_phy_power(twl, 0);
twl->asleep = 1;
} else {
@@ -543,8 +537,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl)
twl->asleep = 0;
}
- atomic_notifier_call_chain(&twl->phy.notifier, status,
- twl->phy.otg->gadget);
+ omap_musb_mailbox(twl->linkstat);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
}
@@ -598,21 +591,20 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
return -EINVAL;
}
- twl = kzalloc(sizeof *twl, GFP_KERNEL);
+ twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
- otg = kzalloc(sizeof *otg, GFP_KERNEL);
- if (!otg) {
- kfree(twl);
+ otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
+ if (!otg)
return -ENOMEM;
- }
twl->dev = &pdev->dev;
twl->irq = platform_get_irq(pdev, 0);
twl->usb_mode = pdata->usb_mode;
twl->vbus_supplied = false;
twl->asleep = 1;
+ twl->linkstat = OMAP_MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
twl->phy.label = "twl4030";
@@ -629,18 +621,14 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
err = twl4030_usb_ldo_init(twl);
if (err) {
dev_err(&pdev->dev, "ldo init failed\n");
- kfree(otg);
- kfree(twl);
return err;
}
- usb_set_transceiver(&twl->phy);
+ usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
platform_set_drvdata(pdev, twl);
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
- ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
-
/* Our job is to use irqs and status from the power module
* to keep the transceiver disabled when nothing's connected.
*
@@ -651,13 +639,11 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
*/
twl->irq_enabled = true;
status = request_threaded_irq(twl->irq, NULL, twl4030_usb_irq,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "twl4030_usb", twl);
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "twl4030_usb", twl);
if (status < 0) {
dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
twl->irq, status);
- kfree(otg);
- kfree(twl);
return status;
}
@@ -701,9 +687,6 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
regulator_put(twl->usb1v8);
regulator_put(twl->usb3v1);
- kfree(twl->phy.otg);
- kfree(twl);
-
return 0;
}
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 0eabb049b6a9..6907d8df7a27 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -26,10 +26,10 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/usb/otg.h>
+#include <linux/usb/musb-omap.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
-#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -100,7 +100,7 @@ struct twl6030_usb {
int irq1;
int irq2;
- u8 linkstat;
+ enum omap_musb_vbus_id_status linkstat;
u8 asleep;
bool irq_enabled;
bool vbus_enable;
@@ -147,7 +147,7 @@ static int twl6030_phy_init(struct usb_phy *x)
dev = twl->dev;
pdata = dev->platform_data;
- if (twl->linkstat == USB_EVENT_ID)
+ if (twl->linkstat == OMAP_MUSB_ID_GROUND)
pdata->phy_power(twl->dev, 1, 1);
else
pdata->phy_power(twl->dev, 0, 1);
@@ -235,13 +235,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev,
spin_lock_irqsave(&twl->lock, flags);
switch (twl->linkstat) {
- case USB_EVENT_VBUS:
+ case OMAP_MUSB_VBUS_VALID:
ret = snprintf(buf, PAGE_SIZE, "vbus\n");
break;
- case USB_EVENT_ID:
+ case OMAP_MUSB_ID_GROUND:
ret = snprintf(buf, PAGE_SIZE, "id\n");
break;
- case USB_EVENT_NONE:
+ case OMAP_MUSB_VBUS_OFF:
ret = snprintf(buf, PAGE_SIZE, "none\n");
break;
default:
@@ -256,8 +256,7 @@ 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;
- struct usb_otg *otg = twl->phy.otg;
- int status;
+ enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
u8 vbus_state, hw_state;
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
@@ -268,22 +267,18 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
if (vbus_state & VBUS_DET) {
regulator_enable(twl->usb3v3);
twl->asleep = 1;
- status = USB_EVENT_VBUS;
- otg->default_a = false;
- twl->phy.state = OTG_STATE_B_IDLE;
+ status = OMAP_MUSB_VBUS_VALID;
twl->linkstat = status;
- twl->phy.last_event = status;
- atomic_notifier_call_chain(&twl->phy.notifier,
- status, otg->gadget);
+ omap_musb_mailbox(status);
} else {
- status = USB_EVENT_NONE;
- twl->linkstat = status;
- twl->phy.last_event = status;
- atomic_notifier_call_chain(&twl->phy.notifier,
- status, otg->gadget);
- if (twl->asleep) {
- regulator_disable(twl->usb3v3);
- twl->asleep = 0;
+ if (twl->linkstat != OMAP_MUSB_UNKNOWN) {
+ status = OMAP_MUSB_VBUS_OFF;
+ twl->linkstat = status;
+ omap_musb_mailbox(status);
+ if (twl->asleep) {
+ regulator_disable(twl->usb3v3);
+ twl->asleep = 0;
+ }
}
}
}
@@ -295,8 +290,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
- struct usb_otg *otg = twl->phy.otg;
- int status = USB_EVENT_NONE;
+ enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
u8 hw_state;
hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
@@ -307,13 +301,9 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
twl->asleep = 1;
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR);
twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET);
- status = USB_EVENT_ID;
- otg->default_a = true;
- twl->phy.state = OTG_STATE_A_IDLE;
+ status = OMAP_MUSB_ID_GROUND;
twl->linkstat = status;
- twl->phy.last_event = status;
- atomic_notifier_call_chain(&twl->phy.notifier, status,
- otg->gadget);
+ omap_musb_mailbox(status);
} else {
twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR);
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
@@ -402,20 +392,19 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
pdata = dev->platform_data;
- twl = kzalloc(sizeof *twl, GFP_KERNEL);
+ twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
- otg = kzalloc(sizeof *otg, GFP_KERNEL);
- if (!otg) {
- kfree(twl);
+ otg = devm_kzalloc(dev, sizeof *otg, GFP_KERNEL);
+ if (!otg)
return -ENOMEM;
- }
twl->dev = &pdev->dev;
twl->irq1 = platform_get_irq(pdev, 0);
twl->irq2 = platform_get_irq(pdev, 1);
twl->features = pdata->features;
+ twl->linkstat = OMAP_MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
twl->phy.label = "twl6030";
@@ -436,18 +425,14 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
err = twl6030_usb_ldo_init(twl);
if (err) {
dev_err(&pdev->dev, "ldo init failed\n");
- kfree(otg);
- kfree(twl);
return err;
}
- usb_set_transceiver(&twl->phy);
+ usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
platform_set_drvdata(pdev, twl);
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
- ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
-
INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
twl->irq_enabled = true;
@@ -458,8 +443,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
twl->irq1, status);
device_remove_file(twl->dev, &dev_attr_vbus);
- kfree(otg);
- kfree(twl);
return status;
}
@@ -471,8 +454,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
twl->irq2, status);
free_irq(twl->irq1, twl);
device_remove_file(twl->dev, &dev_attr_vbus);
- kfree(otg);
- kfree(twl);
return status;
}
@@ -503,8 +484,6 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
pdata->phy_exit(twl->dev);
device_remove_file(twl->dev, &dev_attr_vbus);
cancel_work_sync(&twl->set_vbus_work);
- kfree(twl->phy.otg);
- kfree(twl);
return 0;
}