Commit 449d2ba613a551046544ea75b99563ee643c8d7c
Committed by
Felipe Balbi
1 parent
339e008895
Exists in
master
and in
16 other branches
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>"); |