diff options
-rw-r--r-- | drivers/usb/chipidea/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/chipidea/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/bits.h | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci.h | 7 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 15 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 158 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.h | 17 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 8 |
9 files changed, 212 insertions, 9 deletions
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 553c1976a66e..fd36dc8b889b 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig | |||
@@ -18,6 +18,12 @@ config USB_CHIPIDEA_UDC | |||
18 | Say Y here to enable device controller functionality of the | 18 | Say Y here to enable device controller functionality of the |
19 | ChipIdea driver. | 19 | ChipIdea driver. |
20 | 20 | ||
21 | config USB_CHIPIDEA_HOST | ||
22 | bool "ChipIdea host controller" | ||
23 | help | ||
24 | Say Y here to enable host controller functionality of the | ||
25 | ChipIdea driver. | ||
26 | |||
21 | config USB_CHIPIDEA_DEBUG | 27 | config USB_CHIPIDEA_DEBUG |
22 | bool "ChipIdea driver debug" | 28 | bool "ChipIdea driver debug" |
23 | help | 29 | help |
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index a8279aac6a4a..cc3493769724 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile | |||
@@ -2,6 +2,7 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o | |||
2 | 2 | ||
3 | ci_hdrc-y := core.o | 3 | ci_hdrc-y := core.o |
4 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o | 4 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o |
5 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o | ||
5 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o | 6 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o |
6 | 7 | ||
7 | ifneq ($(CONFIG_PCI),) | 8 | ifneq ($(CONFIG_PCI),) |
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index 44b3e254b6a5..050de8562a04 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h | |||
@@ -21,6 +21,7 @@ | |||
21 | /* DCCPARAMS */ | 21 | /* DCCPARAMS */ |
22 | #define DCCPARAMS_DEN (0x1F << 0) | 22 | #define DCCPARAMS_DEN (0x1F << 0) |
23 | #define DCCPARAMS_DC BIT(7) | 23 | #define DCCPARAMS_DC BIT(7) |
24 | #define DCCPARAMS_HC BIT(8) | ||
24 | 25 | ||
25 | /* TESTMODE */ | 26 | /* TESTMODE */ |
26 | #define TESTMODE_FORCE BIT(0) | 27 | #define TESTMODE_FORCE BIT(0) |
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 0ab83411d800..c605acc5568a 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h | |||
@@ -15,6 +15,7 @@ | |||
15 | 15 | ||
16 | #include <linux/list.h> | 16 | #include <linux/list.h> |
17 | #include <linux/irqreturn.h> | 17 | #include <linux/irqreturn.h> |
18 | #include <linux/usb.h> | ||
18 | #include <linux/usb/gadget.h> | 19 | #include <linux/usb/gadget.h> |
19 | 20 | ||
20 | /****************************************************************************** | 21 | /****************************************************************************** |
@@ -84,6 +85,7 @@ struct ci_role_driver { | |||
84 | /** | 85 | /** |
85 | * struct hw_bank - hardware register mapping representation | 86 | * struct hw_bank - hardware register mapping representation |
86 | * @lpm: set if the device is LPM capable | 87 | * @lpm: set if the device is LPM capable |
88 | * @phys: physical address of the controller's registers | ||
87 | * @abs: absolute address of the beginning of register window | 89 | * @abs: absolute address of the beginning of register window |
88 | * @cap: capability registers | 90 | * @cap: capability registers |
89 | * @op: operational registers | 91 | * @op: operational registers |
@@ -92,6 +94,7 @@ struct ci_role_driver { | |||
92 | */ | 94 | */ |
93 | struct hw_bank { | 95 | struct hw_bank { |
94 | unsigned lpm; | 96 | unsigned lpm; |
97 | resource_size_t phys; | ||
95 | void __iomem *abs; | 98 | void __iomem *abs; |
96 | void __iomem *cap; | 99 | void __iomem *cap; |
97 | void __iomem *op; | 100 | void __iomem *op; |
@@ -128,6 +131,7 @@ struct hw_bank { | |||
128 | * @udc_driver: platform specific information supplied by parent device | 131 | * @udc_driver: platform specific information supplied by parent device |
129 | * @vbus_active: is VBUS active | 132 | * @vbus_active: is VBUS active |
130 | * @transceiver: pointer to USB PHY, if any | 133 | * @transceiver: pointer to USB PHY, if any |
134 | * @hcd: pointer to usb_hcd for ehci host driver | ||
131 | */ | 135 | */ |
132 | struct ci13xxx { | 136 | struct ci13xxx { |
133 | struct device *dev; | 137 | struct device *dev; |
@@ -160,6 +164,7 @@ struct ci13xxx { | |||
160 | struct ci13xxx_udc_driver *udc_driver; | 164 | struct ci13xxx_udc_driver *udc_driver; |
161 | int vbus_active; | 165 | int vbus_active; |
162 | struct usb_phy *transceiver; | 166 | struct usb_phy *transceiver; |
167 | struct usb_hcd *hcd; | ||
163 | }; | 168 | }; |
164 | 169 | ||
165 | static inline struct ci_role_driver *ci_role(struct ci13xxx *ci) | 170 | static inline struct ci_role_driver *ci_role(struct ci13xxx *ci) |
@@ -302,7 +307,7 @@ static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, | |||
302 | return (val & mask) >> ffs_nr(mask); | 307 | return (val & mask) >> ffs_nr(mask); |
303 | } | 308 | } |
304 | 309 | ||
305 | int hw_device_reset(struct ci13xxx *ci); | 310 | int hw_device_reset(struct ci13xxx *ci, u32 mode); |
306 | 311 | ||
307 | int hw_port_test_set(struct ci13xxx *ci, u8 mode); | 312 | int hw_port_test_set(struct ci13xxx *ci, u8 mode); |
308 | 313 | ||
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 3d48c9be6923..f568b8e86cee 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c | |||
@@ -70,6 +70,7 @@ | |||
70 | #include "ci.h" | 70 | #include "ci.h" |
71 | #include "udc.h" | 71 | #include "udc.h" |
72 | #include "bits.h" | 72 | #include "bits.h" |
73 | #include "host.h" | ||
73 | #include "debug.h" | 74 | #include "debug.h" |
74 | 75 | ||
75 | /* Controller register map */ | 76 | /* Controller register map */ |
@@ -215,7 +216,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) | |||
215 | * | 216 | * |
216 | * This function returns an error code | 217 | * This function returns an error code |
217 | */ | 218 | */ |
218 | int hw_device_reset(struct ci13xxx *ci) | 219 | int hw_device_reset(struct ci13xxx *ci, u32 mode) |
219 | { | 220 | { |
220 | /* should flush & stop before reset */ | 221 | /* should flush & stop before reset */ |
221 | hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); | 222 | hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); |
@@ -235,12 +236,12 @@ int hw_device_reset(struct ci13xxx *ci) | |||
235 | 236 | ||
236 | /* USBMODE should be configured step by step */ | 237 | /* USBMODE should be configured step by step */ |
237 | hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); | 238 | hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); |
238 | hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); | 239 | hw_write(ci, OP_USBMODE, USBMODE_CM, mode); |
239 | /* HW >= 2.3 */ | 240 | /* HW >= 2.3 */ |
240 | hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); | 241 | hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); |
241 | 242 | ||
242 | if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { | 243 | if (hw_read(ci, OP_USBMODE, USBMODE_CM) != mode) { |
243 | pr_err("cannot enter in device mode"); | 244 | pr_err("cannot enter in %s mode", ci_role(ci)->name); |
244 | pr_err("lpm = %i", ci->hw_bank.lpm); | 245 | pr_err("lpm = %i", ci->hw_bank.lpm); |
245 | return -ENODEV; | 246 | return -ENODEV; |
246 | } | 247 | } |
@@ -371,6 +372,8 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) | |||
371 | return -ENODEV; | 372 | return -ENODEV; |
372 | } | 373 | } |
373 | 374 | ||
375 | ci->hw_bank.phys = res->start; | ||
376 | |||
374 | ci->irq = platform_get_irq(pdev, 0); | 377 | ci->irq = platform_get_irq(pdev, 0); |
375 | if (ci->irq < 0) { | 378 | if (ci->irq < 0) { |
376 | dev_err(dev, "missing IRQ\n"); | 379 | dev_err(dev, "missing IRQ\n"); |
@@ -385,6 +388,10 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) | |||
385 | } | 388 | } |
386 | 389 | ||
387 | /* initialize role(s) before the interrupt is requested */ | 390 | /* initialize role(s) before the interrupt is requested */ |
391 | ret = ci_hdrc_host_init(ci); | ||
392 | if (ret) | ||
393 | dev_info(dev, "doesn't support host\n"); | ||
394 | |||
388 | ret = ci_hdrc_gadget_init(ci); | 395 | ret = ci_hdrc_gadget_init(ci); |
389 | if (ret) | 396 | if (ret) |
390 | dev_info(dev, "doesn't support gadget\n"); | 397 | dev_info(dev, "doesn't support gadget\n"); |
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c new file mode 100644 index 000000000000..8c8362c89a8c --- /dev/null +++ b/drivers/usb/chipidea/host.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * host.c - ChipIdea USB host controller driver | ||
3 | * | ||
4 | * Copyright (c) 2012 Intel Corporation | ||
5 | * | ||
6 | * Author: Alexander Shishkin | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/usb.h> | ||
24 | #include <linux/usb/hcd.h> | ||
25 | #include <linux/usb/chipidea.h> | ||
26 | |||
27 | #define CHIPIDEA_EHCI | ||
28 | #include "../host/ehci-hcd.c" | ||
29 | |||
30 | #include "ci.h" | ||
31 | #include "bits.h" | ||
32 | #include "host.h" | ||
33 | |||
34 | static int ci_ehci_setup(struct usb_hcd *hcd) | ||
35 | { | ||
36 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
37 | int ret; | ||
38 | |||
39 | hcd->has_tt = 1; | ||
40 | |||
41 | ret = ehci_setup(hcd); | ||
42 | if (ret) | ||
43 | return ret; | ||
44 | |||
45 | ehci_port_power(ehci, 0); | ||
46 | |||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | static const struct hc_driver ci_ehci_hc_driver = { | ||
51 | .description = "ehci_hcd", | ||
52 | .product_desc = "ChipIdea HDRC EHCI", | ||
53 | .hcd_priv_size = sizeof(struct ehci_hcd), | ||
54 | |||
55 | /* | ||
56 | * generic hardware linkage | ||
57 | */ | ||
58 | .irq = ehci_irq, | ||
59 | .flags = HCD_MEMORY | HCD_USB2, | ||
60 | |||
61 | /* | ||
62 | * basic lifecycle operations | ||
63 | */ | ||
64 | .reset = ci_ehci_setup, | ||
65 | .start = ehci_run, | ||
66 | .stop = ehci_stop, | ||
67 | .shutdown = ehci_shutdown, | ||
68 | |||
69 | /* | ||
70 | * managing i/o requests and associated device resources | ||
71 | */ | ||
72 | .urb_enqueue = ehci_urb_enqueue, | ||
73 | .urb_dequeue = ehci_urb_dequeue, | ||
74 | .endpoint_disable = ehci_endpoint_disable, | ||
75 | .endpoint_reset = ehci_endpoint_reset, | ||
76 | |||
77 | /* | ||
78 | * scheduling support | ||
79 | */ | ||
80 | .get_frame_number = ehci_get_frame, | ||
81 | |||
82 | /* | ||
83 | * root hub support | ||
84 | */ | ||
85 | .hub_status_data = ehci_hub_status_data, | ||
86 | .hub_control = ehci_hub_control, | ||
87 | .bus_suspend = ehci_bus_suspend, | ||
88 | .bus_resume = ehci_bus_resume, | ||
89 | .relinquish_port = ehci_relinquish_port, | ||
90 | .port_handed_over = ehci_port_handed_over, | ||
91 | |||
92 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | ||
93 | }; | ||
94 | |||
95 | static irqreturn_t host_irq(struct ci13xxx *ci) | ||
96 | { | ||
97 | return usb_hcd_irq(ci->irq, ci->hcd); | ||
98 | } | ||
99 | |||
100 | static int host_start(struct ci13xxx *ci) | ||
101 | { | ||
102 | struct usb_hcd *hcd; | ||
103 | struct ehci_hcd *ehci; | ||
104 | int ret; | ||
105 | |||
106 | if (usb_disabled()) | ||
107 | return -ENODEV; | ||
108 | |||
109 | hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev)); | ||
110 | if (!hcd) | ||
111 | return -ENOMEM; | ||
112 | |||
113 | dev_set_drvdata(ci->dev, ci); | ||
114 | hcd->rsrc_start = ci->hw_bank.phys; | ||
115 | hcd->rsrc_len = ci->hw_bank.size; | ||
116 | hcd->regs = ci->hw_bank.abs; | ||
117 | hcd->has_tt = 1; | ||
118 | |||
119 | ehci = hcd_to_ehci(hcd); | ||
120 | ehci->caps = ci->hw_bank.cap; | ||
121 | ehci->has_hostpc = ci->hw_bank.lpm; | ||
122 | |||
123 | ret = usb_add_hcd(hcd, 0, 0); | ||
124 | if (ret) | ||
125 | usb_remove_hcd(hcd); | ||
126 | else | ||
127 | ci->hcd = hcd; | ||
128 | |||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | static void host_stop(struct ci13xxx *ci) | ||
133 | { | ||
134 | struct usb_hcd *hcd = ci->hcd; | ||
135 | |||
136 | usb_remove_hcd(hcd); | ||
137 | usb_put_hcd(hcd); | ||
138 | } | ||
139 | |||
140 | int ci_hdrc_host_init(struct ci13xxx *ci) | ||
141 | { | ||
142 | struct ci_role_driver *rdrv; | ||
143 | |||
144 | if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC)) | ||
145 | return -ENXIO; | ||
146 | |||
147 | rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL); | ||
148 | if (!rdrv) | ||
149 | return -ENOMEM; | ||
150 | |||
151 | rdrv->start = host_start; | ||
152 | rdrv->stop = host_stop; | ||
153 | rdrv->irq = host_irq; | ||
154 | rdrv->name = "host"; | ||
155 | ci->roles[CI_ROLE_HOST] = rdrv; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h new file mode 100644 index 000000000000..761fb1fd6d99 --- /dev/null +++ b/drivers/usb/chipidea/host.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef __DRIVERS_USB_CHIPIDEA_HOST_H | ||
2 | #define __DRIVERS_USB_CHIPIDEA_HOST_H | ||
3 | |||
4 | #ifdef CONFIG_USB_CHIPIDEA_HOST | ||
5 | |||
6 | int ci_hdrc_host_init(struct ci13xxx *ci); | ||
7 | |||
8 | #else | ||
9 | |||
10 | static inline int ci_hdrc_host_init(struct ci13xxx *ci) | ||
11 | { | ||
12 | return -ENXIO; | ||
13 | } | ||
14 | |||
15 | #endif | ||
16 | |||
17 | #endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */ | ||
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 290946d618d8..fb0a91158006 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * udc.h - ChipIdea UDC driver | 2 | * udc.c - ChipIdea UDC driver |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. | 4 | * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. |
5 | * | 5 | * |
@@ -1396,7 +1396,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1396 | if (gadget_ready) { | 1396 | if (gadget_ready) { |
1397 | if (is_active) { | 1397 | if (is_active) { |
1398 | pm_runtime_get_sync(&_gadget->dev); | 1398 | pm_runtime_get_sync(&_gadget->dev); |
1399 | hw_device_reset(udc); | 1399 | hw_device_reset(udc, USBMODE_CM_DC); |
1400 | hw_device_state(udc, udc->ep0out->qh.dma); | 1400 | hw_device_state(udc, udc->ep0out->qh.dma); |
1401 | } else { | 1401 | } else { |
1402 | hw_device_state(udc, 0); | 1402 | hw_device_state(udc, 0); |
@@ -1540,7 +1540,7 @@ static int ci13xxx_start(struct usb_gadget *gadget, | |||
1540 | if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { | 1540 | if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { |
1541 | if (udc->vbus_active) { | 1541 | if (udc->vbus_active) { |
1542 | if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) | 1542 | if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) |
1543 | hw_device_reset(udc); | 1543 | hw_device_reset(udc, USBMODE_CM_DC); |
1544 | } else { | 1544 | } else { |
1545 | pm_runtime_put_sync(&udc->gadget.dev); | 1545 | pm_runtime_put_sync(&udc->gadget.dev); |
1546 | goto done; | 1546 | goto done; |
@@ -1720,7 +1720,7 @@ static int udc_start(struct ci13xxx *udc) | |||
1720 | } | 1720 | } |
1721 | 1721 | ||
1722 | if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { | 1722 | if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { |
1723 | retval = hw_device_reset(udc); | 1723 | retval = hw_device_reset(udc, USBMODE_CM_DC); |
1724 | if (retval) | 1724 | if (retval) |
1725 | goto put_transceiver; | 1725 | goto put_transceiver; |
1726 | } | 1726 | } |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index de1e689d3df0..5cb775b1802d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -1246,6 +1246,13 @@ static int ehci_get_frame (struct usb_hcd *hcd) | |||
1246 | } | 1246 | } |
1247 | 1247 | ||
1248 | /*-------------------------------------------------------------------------*/ | 1248 | /*-------------------------------------------------------------------------*/ |
1249 | /* | ||
1250 | * The EHCI in ChipIdea HDRC cannot be a separate module or device, | ||
1251 | * because its registers (and irq) are shared between host/gadget/otg | ||
1252 | * functions and in order to facilitate role switching we cannot | ||
1253 | * give the ehci driver exclusive access to those. | ||
1254 | */ | ||
1255 | #ifndef CHIPIDEA_EHCI | ||
1249 | 1256 | ||
1250 | MODULE_DESCRIPTION(DRIVER_DESC); | 1257 | MODULE_DESCRIPTION(DRIVER_DESC); |
1251 | MODULE_AUTHOR (DRIVER_AUTHOR); | 1258 | MODULE_AUTHOR (DRIVER_AUTHOR); |
@@ -1504,3 +1511,4 @@ static void __exit ehci_hcd_cleanup(void) | |||
1504 | } | 1511 | } |
1505 | module_exit(ehci_hcd_cleanup); | 1512 | module_exit(ehci_hcd_cleanup); |
1506 | 1513 | ||
1514 | #endif /* CHIPIDEA_EHCI */ | ||