aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDeepak Sikri <deepak.sikri@st.com>2010-11-10 04:03:18 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-11-16 16:53:29 -0500
commitc8c38de9d8002578599222296b90696745ac0fe3 (patch)
tree11122dfc8a9b8bfeba387d04152c445f53b4c091 /drivers/usb
parentad78acafeed26f62c9e644f96eecb7c19bd78bb4 (diff)
USB host: Adding USB ehci & ohci support for spear platform
This patch adds support for ehci and ohci controller in the SPEAr platform. Changes since V2: added clear_tt_buffer_complete in ehci_spear_hc_driver Signed-off-by: Deepak Sikri <deepak.sikri@st.com> Signed-off-by: Viresh Kumar <viresh.kumar@st.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-spear.c212
-rw-r--r--drivers/usb/host/ohci-hcd.c5
-rw-r--r--drivers/usb/host/ohci-spear.c240
5 files changed, 464 insertions, 0 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 7d13506cc383..0f81b606cb13 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -41,6 +41,7 @@ config USB_ARCH_HAS_OHCI
41 default y if MFD_TC6393XB 41 default y if MFD_TC6393XB
42 default y if ARCH_W90X900 42 default y if ARCH_W90X900
43 default y if ARCH_DAVINCI_DA8XX 43 default y if ARCH_DAVINCI_DA8XX
44 default y if PLAT_SPEAR
44 # PPC: 45 # PPC:
45 default y if STB03xxx 46 default y if STB03xxx
46 default y if PPC_MPC52xx 47 default y if PPC_MPC52xx
@@ -67,6 +68,7 @@ config USB_ARCH_HAS_EHCI
67 default y if ARCH_MXC 68 default y if ARCH_MXC
68 default y if ARCH_OMAP3 69 default y if ARCH_OMAP3
69 default y if ARCH_VT8500 70 default y if ARCH_VT8500
71 default y if PLAT_SPEAR
70 default PCI 72 default PCI
71 73
72# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. 74# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 10f985def70f..87157db155f6 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1221,6 +1221,11 @@ MODULE_LICENSE ("GPL");
1221#define PLATFORM_DRIVER vt8500_ehci_driver 1221#define PLATFORM_DRIVER vt8500_ehci_driver
1222#endif 1222#endif
1223 1223
1224#ifdef CONFIG_PLAT_SPEAR
1225#include "ehci-spear.c"
1226#define PLATFORM_DRIVER spear_ehci_hcd_driver
1227#endif
1228
1224#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ 1229#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
1225 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ 1230 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
1226 !defined(XILINX_OF_PLATFORM_DRIVER) 1231 !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
new file mode 100644
index 000000000000..75c00873443d
--- /dev/null
+++ b/drivers/usb/host/ehci-spear.c
@@ -0,0 +1,212 @@
1/*
2* Driver for EHCI HCD on SPEAR SOC
3*
4* Copyright (C) 2010 ST Micro Electronics,
5* Deepak Sikri <deepak.sikri@st.com>
6*
7* Based on various ehci-*.c drivers
8*
9* This file is subject to the terms and conditions of the GNU General Public
10* License. See the file COPYING in the main directory of this archive for
11* more details.
12*/
13
14#include <linux/platform_device.h>
15#include <linux/clk.h>
16
17struct spear_ehci {
18 struct ehci_hcd ehci;
19 struct clk *clk;
20};
21
22#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd)
23
24static void spear_start_ehci(struct spear_ehci *ehci)
25{
26 clk_enable(ehci->clk);
27}
28
29static void spear_stop_ehci(struct spear_ehci *ehci)
30{
31 clk_disable(ehci->clk);
32}
33
34static int ehci_spear_setup(struct usb_hcd *hcd)
35{
36 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
37 int retval = 0;
38
39 /* registers start at offset 0x0 */
40 ehci->caps = hcd->regs;
41 ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci,
42 &ehci->caps->hc_capbase));
43 /* cache this readonly data; minimize chip reads */
44 ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
45 retval = ehci_halt(ehci);
46 if (retval)
47 return retval;
48
49 retval = ehci_init(hcd);
50 if (retval)
51 return retval;
52
53 ehci_reset(ehci);
54 ehci_port_power(ehci, 0);
55
56 return retval;
57}
58
59static const struct hc_driver ehci_spear_hc_driver = {
60 .description = hcd_name,
61 .product_desc = "SPEAr EHCI",
62 .hcd_priv_size = sizeof(struct spear_ehci),
63
64 /* generic hardware linkage */
65 .irq = ehci_irq,
66 .flags = HCD_MEMORY | HCD_USB2,
67
68 /* basic lifecycle operations */
69 .reset = ehci_spear_setup,
70 .start = ehci_run,
71 .stop = ehci_stop,
72 .shutdown = ehci_shutdown,
73
74 /* managing i/o requests and associated device resources */
75 .urb_enqueue = ehci_urb_enqueue,
76 .urb_dequeue = ehci_urb_dequeue,
77 .endpoint_disable = ehci_endpoint_disable,
78 .endpoint_reset = ehci_endpoint_reset,
79
80 /* scheduling support */
81 .get_frame_number = ehci_get_frame,
82
83 /* root hub support */
84 .hub_status_data = ehci_hub_status_data,
85 .hub_control = ehci_hub_control,
86 .bus_suspend = ehci_bus_suspend,
87 .bus_resume = ehci_bus_resume,
88 .relinquish_port = ehci_relinquish_port,
89 .port_handed_over = ehci_port_handed_over,
90 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
91};
92
93static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
94{
95 struct usb_hcd *hcd ;
96 struct spear_ehci *ehci;
97 struct resource *res;
98 struct clk *usbh_clk;
99 const struct hc_driver *driver = &ehci_spear_hc_driver;
100 int *pdata = pdev->dev.platform_data;
101 int irq, retval;
102 char clk_name[20] = "usbh_clk";
103
104 if (pdata == NULL)
105 return -EFAULT;
106
107 if (usb_disabled())
108 return -ENODEV;
109
110 irq = platform_get_irq(pdev, 0);
111 if (irq < 0) {
112 retval = irq;
113 goto fail_irq_get;
114 }
115
116 if (*pdata >= 0)
117 sprintf(clk_name, "usbh.%01d_clk", *pdata);
118
119 usbh_clk = clk_get(NULL, clk_name);
120 if (IS_ERR(usbh_clk)) {
121 dev_err(&pdev->dev, "Error getting interface clock\n");
122 retval = PTR_ERR(usbh_clk);
123 goto fail_get_usbh_clk;
124 }
125
126 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
127 if (!hcd) {
128 retval = -ENOMEM;
129 goto fail_create_hcd;
130 }
131
132 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
133 if (!res) {
134 retval = -ENODEV;
135 goto fail_request_resource;
136 }
137
138 hcd->rsrc_start = res->start;
139 hcd->rsrc_len = resource_size(res);
140 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
141 driver->description)) {
142 retval = -EBUSY;
143 goto fail_request_resource;
144 }
145
146 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
147 if (hcd->regs == NULL) {
148 dev_dbg(&pdev->dev, "error mapping memory\n");
149 retval = -ENOMEM;
150 goto fail_ioremap;
151 }
152
153 ehci = (struct spear_ehci *)hcd_to_ehci(hcd);
154 ehci->clk = usbh_clk;
155
156 spear_start_ehci(ehci);
157 retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
158 if (retval)
159 goto fail_add_hcd;
160
161 return retval;
162
163fail_add_hcd:
164 spear_stop_ehci(ehci);
165 iounmap(hcd->regs);
166fail_ioremap:
167 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
168fail_request_resource:
169 usb_put_hcd(hcd);
170fail_create_hcd:
171 clk_put(usbh_clk);
172fail_get_usbh_clk:
173fail_irq_get:
174 dev_err(&pdev->dev, "init fail, %d\n", retval);
175
176 return retval ;
177}
178
179static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
180{
181 struct usb_hcd *hcd = platform_get_drvdata(pdev);
182 struct spear_ehci *ehci_p = to_spear_ehci(hcd);
183
184 if (!hcd)
185 return 0;
186 if (in_interrupt())
187 BUG();
188 usb_remove_hcd(hcd);
189
190 if (ehci_p->clk)
191 spear_stop_ehci(ehci_p);
192 iounmap(hcd->regs);
193 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
194 usb_put_hcd(hcd);
195
196 if (ehci_p->clk)
197 clk_put(ehci_p->clk);
198
199 return 0;
200}
201
202static struct platform_driver spear_ehci_hcd_driver = {
203 .probe = spear_ehci_hcd_drv_probe,
204 .remove = spear_ehci_hcd_drv_remove,
205 .shutdown = usb_hcd_platform_shutdown,
206 .driver = {
207 .name = "spear-ehci",
208 .bus = &platform_bus_type
209 }
210};
211
212MODULE_ALIAS("platform:spear-ehci");
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 5179acb7aa2f..4a4a4f025385 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1081,6 +1081,11 @@ MODULE_LICENSE ("GPL");
1081#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver 1081#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver
1082#endif 1082#endif
1083 1083
1084#ifdef CONFIG_PLAT_SPEAR
1085#include "ohci-spear.c"
1086#define PLATFORM_DRIVER spear_ohci_hcd_driver
1087#endif
1088
1084#ifdef CONFIG_PPC_PS3 1089#ifdef CONFIG_PPC_PS3
1085#include "ohci-ps3.c" 1090#include "ohci-ps3.c"
1086#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver 1091#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
new file mode 100644
index 000000000000..4fd4bea9ac7a
--- /dev/null
+++ b/drivers/usb/host/ohci-spear.c
@@ -0,0 +1,240 @@
1/*
2* OHCI HCD (Host Controller Driver) for USB.
3*
4* Copyright (C) 2010 ST Microelectronics.
5* Deepak Sikri<deepak.sikri@st.com>
6*
7* Based on various ohci-*.c drivers
8*
9* This file is licensed under the terms of the GNU General Public
10* License version 2. This program is licensed "as is" without any
11* warranty of any kind, whether express or implied.
12*/
13
14#include <linux/signal.h>
15#include <linux/platform_device.h>
16#include <linux/clk.h>
17
18struct spear_ohci {
19 struct ohci_hcd ohci;
20 struct clk *clk;
21};
22
23#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd)
24
25static void spear_start_ohci(struct spear_ohci *ohci)
26{
27 clk_enable(ohci->clk);
28}
29
30static void spear_stop_ohci(struct spear_ohci *ohci)
31{
32 clk_disable(ohci->clk);
33}
34
35static int __devinit ohci_spear_start(struct usb_hcd *hcd)
36{
37 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
38 int ret;
39
40 ret = ohci_init(ohci);
41 if (ret < 0)
42 return ret;
43 ohci->regs = hcd->regs;
44
45 ret = ohci_run(ohci);
46 if (ret < 0) {
47 dev_err(hcd->self.controller, "can't start\n");
48 ohci_stop(hcd);
49 return ret;
50 }
51
52 create_debug_files(ohci);
53
54#ifdef DEBUG
55 ohci_dump(ohci, 1);
56#endif
57 return 0;
58}
59
60static const struct hc_driver ohci_spear_hc_driver = {
61 .description = hcd_name,
62 .product_desc = "SPEAr OHCI",
63 .hcd_priv_size = sizeof(struct spear_ohci),
64
65 /* generic hardware linkage */
66 .irq = ohci_irq,
67 .flags = HCD_USB11 | HCD_MEMORY,
68
69 /* basic lifecycle operations */
70 .start = ohci_spear_start,
71 .stop = ohci_stop,
72 .shutdown = ohci_shutdown,
73#ifdef CONFIG_PM
74 .bus_suspend = ohci_bus_suspend,
75 .bus_resume = ohci_bus_resume,
76#endif
77
78 /* managing i/o requests and associated device resources */
79 .urb_enqueue = ohci_urb_enqueue,
80 .urb_dequeue = ohci_urb_dequeue,
81 .endpoint_disable = ohci_endpoint_disable,
82
83 /* scheduling support */
84 .get_frame_number = ohci_get_frame,
85
86 /* root hub support */
87 .hub_status_data = ohci_hub_status_data,
88 .hub_control = ohci_hub_control,
89
90 .start_port_reset = ohci_start_port_reset,
91};
92
93static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
94{
95 const struct hc_driver *driver = &ohci_spear_hc_driver;
96 struct usb_hcd *hcd = NULL;
97 struct clk *usbh_clk;
98 struct spear_ohci *ohci_p;
99 struct resource *res;
100 int retval, irq;
101 int *pdata = pdev->dev.platform_data;
102 char clk_name[20] = "usbh_clk";
103
104 if (pdata == NULL)
105 return -EFAULT;
106
107 irq = platform_get_irq(pdev, 0);
108 if (irq < 0) {
109 retval = irq;
110 goto fail_irq_get;
111 }
112
113 if (*pdata >= 0)
114 sprintf(clk_name, "usbh.%01d_clk", *pdata);
115
116 usbh_clk = clk_get(NULL, clk_name);
117 if (IS_ERR(usbh_clk)) {
118 dev_err(&pdev->dev, "Error getting interface clock\n");
119 retval = PTR_ERR(usbh_clk);
120 goto fail_get_usbh_clk;
121 }
122
123 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
124 if (!hcd) {
125 retval = -ENOMEM;
126 goto fail_create_hcd;
127 }
128
129 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
130 if (!res) {
131 retval = -ENODEV;
132 goto fail_request_resource;
133 }
134
135 hcd->rsrc_start = pdev->resource[0].start;
136 hcd->rsrc_len = resource_size(res);
137 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
138 dev_dbg(&pdev->dev, "request_mem_region failed\n");
139 retval = -EBUSY;
140 goto fail_request_resource;
141 }
142
143 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
144 if (!hcd->regs) {
145 dev_dbg(&pdev->dev, "ioremap failed\n");
146 retval = -ENOMEM;
147 goto fail_ioremap;
148 }
149
150 ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd);
151 ohci_p->clk = usbh_clk;
152 spear_start_ohci(ohci_p);
153 ohci_hcd_init(hcd_to_ohci(hcd));
154
155 retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED);
156 if (retval == 0)
157 return retval;
158
159 spear_stop_ohci(ohci_p);
160 iounmap(hcd->regs);
161fail_ioremap:
162 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
163fail_request_resource:
164 usb_put_hcd(hcd);
165fail_create_hcd:
166 clk_put(usbh_clk);
167fail_get_usbh_clk:
168fail_irq_get:
169 dev_err(&pdev->dev, "init fail, %d\n", retval);
170
171 return retval;
172}
173
174static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
175{
176 struct usb_hcd *hcd = platform_get_drvdata(pdev);
177 struct spear_ohci *ohci_p = to_spear_ohci(hcd);
178
179 usb_remove_hcd(hcd);
180 if (ohci_p->clk)
181 spear_stop_ohci(ohci_p);
182
183 iounmap(hcd->regs);
184 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
185 usb_put_hcd(hcd);
186
187 if (ohci_p->clk)
188 clk_put(ohci_p->clk);
189 platform_set_drvdata(pdev, NULL);
190 return 0;
191}
192
193#if defined(CONFIG_PM)
194static int spear_ohci_hcd_drv_suspend(struct platform_device *dev,
195 pm_message_t message)
196{
197 struct usb_hcd *hcd = platform_get_drvdata(dev);
198 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
199 struct spear_ohci *ohci_p = to_spear_ohci(hcd);
200
201 if (time_before(jiffies, ohci->next_statechange))
202 msleep(5);
203 ohci->next_statechange = jiffies;
204
205 spear_stop_ohci(ohci_p);
206 ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
207 return 0;
208}
209
210static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
211{
212 struct usb_hcd *hcd = platform_get_drvdata(dev);
213 struct ohci_hcd *ohci = hcd_to_ohci(hcd);
214 struct spear_ohci *ohci_p = to_spear_ohci(hcd);
215
216 if (time_before(jiffies, ohci->next_statechange))
217 msleep(5);
218 ohci->next_statechange = jiffies;
219
220 spear_start_ohci(ohci_p);
221 ohci_finish_controller_resume(hcd);
222 return 0;
223}
224#endif
225
226/* Driver definition to register with the platform bus */
227static struct platform_driver spear_ohci_hcd_driver = {
228 .probe = spear_ohci_hcd_drv_probe,
229 .remove = spear_ohci_hcd_drv_remove,
230#ifdef CONFIG_PM
231 .suspend = spear_ohci_hcd_drv_suspend,
232 .resume = spear_ohci_hcd_drv_resume,
233#endif
234 .driver = {
235 .owner = THIS_MODULE,
236 .name = "spear-ohci",
237 },
238};
239
240MODULE_ALIAS("platform:spear-ohci");