Commit 449d2ba613a551046544ea75b99563ee643c8d7c

Authored by Aaro Koskinen
Committed by Felipe Balbi
1 parent 339e008895

usb: omap1: OTG controller driver

Transceivers need to manage OTG controller state on OMAP1 to enable
switching between peripheral and host modes. Provide a driver for that.

Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Felipe Balbi <balbi@ti.com>

Showing 3 changed files with 180 additions and 0 deletions Side-by-side Diff

drivers/usb/phy/Kconfig
... ... @@ -142,6 +142,16 @@
142 142 optionally control of a D+ pullup GPIO as well as a VBUS
143 143 current limit regulator.
144 144  
  145 +config OMAP_OTG
  146 + tristate "OMAP USB OTG controller driver"
  147 + depends on ARCH_OMAP_OTG && EXTCON
  148 + help
  149 + Enable this to support some transceivers on OMAP1 platforms. OTG
  150 + controller is needed to switch between host and peripheral modes.
  151 +
  152 + This driver can also be built as a module. If so, the module
  153 + will be called omap-otg.
  154 +
145 155 config USB_ISP1301
146 156 tristate "NXP ISP1301 USB transceiver support"
147 157 depends on USB || USB_GADGET
drivers/usb/phy/Makefile
... ... @@ -15,6 +15,7 @@
15 15 obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
16 16 obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
17 17 obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
  18 +obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o
18 19 obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
19 20 obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
20 21 obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
drivers/usb/phy/phy-omap-otg.c
  1 +/*
  2 + * OMAP OTG controller driver
  3 + *
  4 + * Based on code from tahvo-usb.c and isp1301_omap.c drivers.
  5 + *
  6 + * Copyright (C) 2005-2006 Nokia Corporation
  7 + * Copyright (C) 2004 Texas Instruments
  8 + * Copyright (C) 2004 David Brownell
  9 + *
  10 + * This file is subject to the terms and conditions of the GNU General
  11 + * Public License. See the file "COPYING" in the main directory of this
  12 + * archive for more details.
  13 + *
  14 + * This program is distributed in the hope that it will be useful,
  15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17 + * GNU General Public License for more details.
  18 + */
  19 +
  20 +#include <linux/io.h>
  21 +#include <linux/err.h>
  22 +#include <linux/extcon.h>
  23 +#include <linux/kernel.h>
  24 +#include <linux/module.h>
  25 +#include <linux/interrupt.h>
  26 +#include <linux/platform_device.h>
  27 +#include <linux/platform_data/usb-omap1.h>
  28 +
  29 +struct otg_device {
  30 + void __iomem *base;
  31 + bool id;
  32 + bool vbus;
  33 + struct extcon_specific_cable_nb vbus_dev;
  34 + struct extcon_specific_cable_nb id_dev;
  35 + struct notifier_block vbus_nb;
  36 + struct notifier_block id_nb;
  37 +};
  38 +
  39 +#define OMAP_OTG_CTRL 0x0c
  40 +#define OMAP_OTG_ASESSVLD (1 << 20)
  41 +#define OMAP_OTG_BSESSEND (1 << 19)
  42 +#define OMAP_OTG_BSESSVLD (1 << 18)
  43 +#define OMAP_OTG_VBUSVLD (1 << 17)
  44 +#define OMAP_OTG_ID (1 << 16)
  45 +#define OMAP_OTG_XCEIV_OUTPUTS \
  46 + (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
  47 + OMAP_OTG_VBUSVLD | OMAP_OTG_ID)
  48 +
  49 +static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
  50 +{
  51 + u32 l;
  52 +
  53 + l = readl(otg_dev->base + OMAP_OTG_CTRL);
  54 + l &= ~OMAP_OTG_XCEIV_OUTPUTS;
  55 + l |= outputs;
  56 + writel(l, otg_dev->base + OMAP_OTG_CTRL);
  57 +}
  58 +
  59 +static void omap_otg_set_mode(struct otg_device *otg_dev)
  60 +{
  61 + if (!otg_dev->id && otg_dev->vbus)
  62 + /* Set B-session valid. */
  63 + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
  64 + else if (otg_dev->vbus)
  65 + /* Set A-session valid. */
  66 + omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
  67 + else if (!otg_dev->id)
  68 + /* Set B-session end to indicate no VBUS. */
  69 + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
  70 +}
  71 +
  72 +static int omap_otg_id_notifier(struct notifier_block *nb,
  73 + unsigned long event, void *ptr)
  74 +{
  75 + struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
  76 +
  77 + otg_dev->id = event;
  78 + omap_otg_set_mode(otg_dev);
  79 +
  80 + return NOTIFY_DONE;
  81 +}
  82 +
  83 +static int omap_otg_vbus_notifier(struct notifier_block *nb,
  84 + unsigned long event, void *ptr)
  85 +{
  86 + struct otg_device *otg_dev = container_of(nb, struct otg_device,
  87 + vbus_nb);
  88 +
  89 + otg_dev->vbus = event;
  90 + omap_otg_set_mode(otg_dev);
  91 +
  92 + return NOTIFY_DONE;
  93 +}
  94 +
  95 +static int omap_otg_probe(struct platform_device *pdev)
  96 +{
  97 + const struct omap_usb_config *config = pdev->dev.platform_data;
  98 + struct otg_device *otg_dev;
  99 + struct extcon_dev *extcon;
  100 + int ret;
  101 + u32 rev;
  102 +
  103 + if (!config || !config->extcon)
  104 + return -ENODEV;
  105 +
  106 + extcon = extcon_get_extcon_dev(config->extcon);
  107 + if (!extcon)
  108 + return -EPROBE_DEFER;
  109 +
  110 + otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
  111 + if (!otg_dev)
  112 + return -ENOMEM;
  113 +
  114 + otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
  115 + if (IS_ERR(otg_dev->base))
  116 + return PTR_ERR(otg_dev->base);
  117 +
  118 + otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
  119 + otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
  120 +
  121 + ret = extcon_register_interest(&otg_dev->id_dev, config->extcon,
  122 + "USB-HOST", &otg_dev->id_nb);
  123 + if (ret)
  124 + return ret;
  125 +
  126 + ret = extcon_register_interest(&otg_dev->vbus_dev, config->extcon,
  127 + "USB", &otg_dev->vbus_nb);
  128 + if (ret) {
  129 + extcon_unregister_interest(&otg_dev->id_dev);
  130 + return ret;
  131 + }
  132 +
  133 + otg_dev->id = extcon_get_cable_state(extcon, "USB-HOST");
  134 + otg_dev->vbus = extcon_get_cable_state(extcon, "USB");
  135 + omap_otg_set_mode(otg_dev);
  136 +
  137 + rev = readl(otg_dev->base);
  138 +
  139 + dev_info(&pdev->dev,
  140 + "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
  141 + (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
  142 + otg_dev->vbus);
  143 +
  144 + return 0;
  145 +}
  146 +
  147 +static int omap_otg_remove(struct platform_device *pdev)
  148 +{
  149 + struct otg_device *otg_dev = platform_get_drvdata(pdev);
  150 +
  151 + extcon_unregister_interest(&otg_dev->id_dev);
  152 + extcon_unregister_interest(&otg_dev->vbus_dev);
  153 +
  154 + return 0;
  155 +}
  156 +
  157 +static struct platform_driver omap_otg_driver = {
  158 + .probe = omap_otg_probe,
  159 + .remove = omap_otg_remove,
  160 + .driver = {
  161 + .owner = THIS_MODULE,
  162 + .name = "omap_otg",
  163 + },
  164 +};
  165 +module_platform_driver(omap_otg_driver);
  166 +
  167 +MODULE_DESCRIPTION("OMAP USB OTG controller driver");
  168 +MODULE_LICENSE("GPL");
  169 +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");