diff options
author | Aaro Koskinen <aaro.koskinen@iki.fi> | 2013-12-06 09:13:06 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2013-12-06 15:40:26 -0500 |
commit | 449d2ba613a551046544ea75b99563ee643c8d7c (patch) | |
tree | 916ecc4e0927306aa9ea1e6fdbac9fdaee9c1025 /drivers/usb/phy | |
parent | 339e008895a912a5049e076c9d27b29eb7ce80a7 (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/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/phy/phy-omap-otg.c | 169 |
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 | ||
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 | config USB_ISP1301 | 155 | config 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 | |||
15 | obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o | 15 | obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o |
16 | obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o | 16 | obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o |
17 | obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o | 17 | obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o |
18 | obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o | ||
18 | obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o | 19 | obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o |
19 | obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o | 20 | obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o |
20 | obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o | 21 | obj-$(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 | |||
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>"); | ||