aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy
diff options
context:
space:
mode:
authorAaro Koskinen <aaro.koskinen@iki.fi>2013-12-06 09:13:06 -0500
committerFelipe Balbi <balbi@ti.com>2013-12-06 15:40:26 -0500
commit449d2ba613a551046544ea75b99563ee643c8d7c (patch)
tree916ecc4e0927306aa9ea1e6fdbac9fdaee9c1025 /drivers/usb/phy
parent339e008895a912a5049e076c9d27b29eb7ce80a7 (diff)
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>
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/Kconfig10
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-omap-otg.c169
3 files changed, 180 insertions, 0 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 921a9f43e309..0dbab6f5c2d4 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -142,6 +142,16 @@ config USB_GPIO_VBUS
142 optionally control of a D+ pullup GPIO as well as a VBUS 142 optionally control of a D+ pullup GPIO as well as a VBUS
143 current limit regulator. 143 current limit regulator.
144 144
145config 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
145config USB_ISP1301 155config USB_ISP1301
146 tristate "NXP ISP1301 USB transceiver support" 156 tristate "NXP ISP1301 USB transceiver support"
147 depends on USB || USB_GADGET 157 depends on USB || USB_GADGET
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 1bf6c083a36e..64a9345e5633 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o
15obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o 15obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
16obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o 16obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
17obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o 17obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
18obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o
18obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o 19obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
19obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o 20obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
20obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o 21obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c
new file mode 100644
index 000000000000..11598cdb3189
--- /dev/null
+++ b/drivers/usb/phy/phy-omap-otg.c
@@ -0,0 +1,169 @@
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
29struct 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
49static 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
59static 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
72static 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
83static 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
95static 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
147static 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
157static 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};
165module_platform_driver(omap_otg_driver);
166
167MODULE_DESCRIPTION("OMAP USB OTG controller driver");
168MODULE_LICENSE("GPL");
169MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");