aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRandy Vinson <rvinson@mvista.com>2006-01-20 16:53:38 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-03-20 17:49:55 -0500
commit80cb9aee01245b38325dd84f1359b14a3f01f10d (patch)
treeaa261392fa976e86dba2bea43b8afc9a64228b89
parent469d02293d494d30dba81895cd3d34b0a3a6d51a (diff)
[PATCH] USB: EHCI for Freescale 83xx
Adding a Host Mode USB driver for the Freescale 83xx. This driver supports both the Dual-Role (DR) controller and the Multi-Port-Host (MPH) controller present in the Freescale MPC8349. It has been tested with the MPC8349CDS reference system. This driver depends on platform support code for setting up the pins on the device package in a manner appropriate for the board in use. Note that this patch requires selecting the EHCI controller option under the USB Host menu. Signed-off-by: Randy Vinson <rvinson@mvista.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/ehci-fsl.c357
-rw-r--r--drivers/usb/host/ehci-fsl.h37
-rw-r--r--drivers/usb/host/ehci-hcd.c8
-rw-r--r--include/linux/fsl_devices.h27
6 files changed, 430 insertions, 2 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 85dacc92545a..7772e28a303f 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -11,6 +11,7 @@ config USB_ARCH_HAS_HCD
11 boolean 11 boolean
12 default y if USB_ARCH_HAS_OHCI 12 default y if USB_ARCH_HAS_OHCI
13 default y if ARM # SL-811 13 default y if ARM # SL-811
14 default y if PPC_83xx
14 default PCI 15 default PCI
15 16
16# many non-PCI SOC chips embed OHCI 17# many non-PCI SOC chips embed OHCI
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index be3fd9bce573..a45293673906 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -6,7 +6,7 @@ comment "USB Host Controller Drivers"
6 6
7config USB_EHCI_HCD 7config USB_EHCI_HCD
8 tristate "EHCI HCD (USB 2.0) support" 8 tristate "EHCI HCD (USB 2.0) support"
9 depends on USB && PCI 9 depends on USB && (PCI || PPC_83xx)
10 ---help--- 10 ---help---
11 The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0 11 The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
12 "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware. 12 "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
new file mode 100644
index 000000000000..c6012d6cd527
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.c
@@ -0,0 +1,357 @@
1/*
2 * (C) Copyright David Brownell 2000-2002
3 * Copyright (c) 2005 MontaVista Software
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
20 * by Hunter Wu.
21 */
22
23#include <linux/platform_device.h>
24#include <linux/fsl_devices.h>
25
26#include "ehci-fsl.h"
27
28/* FIXME: Power Managment is un-ported so temporarily disable it */
29#undef CONFIG_PM
30
31/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
32
33/* configure so an HC device and id are always provided */
34/* always called with process context; sleeping is OK */
35
36/**
37 * usb_hcd_fsl_probe - initialize FSL-based HCDs
38 * @drvier: Driver to be used for this HCD
39 * @pdev: USB Host Controller being probed
40 * Context: !in_interrupt()
41 *
42 * Allocates basic resources for this USB host controller.
43 *
44 */
45int usb_hcd_fsl_probe(const struct hc_driver *driver,
46 struct platform_device *pdev)
47{
48 struct fsl_usb2_platform_data *pdata;
49 struct usb_hcd *hcd;
50 struct resource *res;
51 int irq;
52 int retval;
53 unsigned int temp;
54
55 pr_debug("initializing FSL-SOC USB Controller\n");
56
57 /* Need platform data for setup */
58 pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
59 if (!pdata) {
60 dev_err(&pdev->dev,
61 "No platform data for %s.\n", pdev->dev.bus_id);
62 return -ENODEV;
63 }
64
65 /*
66 * This is a host mode driver, verify that we're supposed to be
67 * in host mode.
68 */
69 if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
70 (pdata->operating_mode == FSL_USB2_MPH_HOST))) {
71 dev_err(&pdev->dev,
72 "Non Host Mode configured for %s. Wrong driver linked.\n",
73 pdev->dev.bus_id);
74 return -ENODEV;
75 }
76
77 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
78 if (!res) {
79 dev_err(&pdev->dev,
80 "Found HC with no IRQ. Check %s setup!\n",
81 pdev->dev.bus_id);
82 return -ENODEV;
83 }
84 irq = res->start;
85
86 hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
87 if (!hcd) {
88 retval = -ENOMEM;
89 goto err1;
90 }
91
92 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
93 if (!res) {
94 dev_err(&pdev->dev,
95 "Found HC with no register addr. Check %s setup!\n",
96 pdev->dev.bus_id);
97 retval = -ENODEV;
98 goto err2;
99 }
100 hcd->rsrc_start = res->start;
101 hcd->rsrc_len = res->end - res->start + 1;
102 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
103 driver->description)) {
104 dev_dbg(&pdev->dev, "controller already in use\n");
105 retval = -EBUSY;
106 goto err2;
107 }
108 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
109
110 if (hcd->regs == NULL) {
111 dev_dbg(&pdev->dev, "error mapping memory\n");
112 retval = -EFAULT;
113 goto err3;
114 }
115
116 /* Enable USB controller */
117 temp = in_be32(hcd->regs + 0x500);
118 out_be32(hcd->regs + 0x500, temp | 0x4);
119
120 /* Set to Host mode */
121 temp = in_le32(hcd->regs + 0x1a8);
122 out_le32(hcd->regs + 0x1a8, temp | 0x3);
123
124 retval = usb_add_hcd(hcd, irq, SA_SHIRQ);
125 if (retval != 0)
126 goto err4;
127 return retval;
128
129 err4:
130 iounmap(hcd->regs);
131 err3:
132 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
133 err2:
134 usb_put_hcd(hcd);
135 err1:
136 dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
137 return retval;
138}
139
140/* may be called without controller electrically present */
141/* may be called with controller, bus, and devices active */
142
143/**
144 * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
145 * @dev: USB Host Controller being removed
146 * Context: !in_interrupt()
147 *
148 * Reverses the effect of usb_hcd_fsl_probe().
149 *
150 */
151void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev)
152{
153 usb_remove_hcd(hcd);
154 iounmap(hcd->regs);
155 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
156 usb_put_hcd(hcd);
157}
158
159static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
160 enum fsl_usb2_phy_modes phy_mode,
161 unsigned int port_offset)
162{
163 u32 portsc = readl(&ehci->regs->port_status[port_offset]);
164 portsc &= ~PORT_PTS_MSK;
165 switch (phy_mode) {
166 case FSL_USB2_PHY_ULPI:
167 portsc |= PORT_PTS_ULPI;
168 break;
169 case FSL_USB2_PHY_SERIAL:
170 portsc |= PORT_PTS_SERIAL;
171 break;
172 case FSL_USB2_PHY_UTMI_WIDE:
173 portsc |= PORT_PTS_PTW;
174 /* fall through */
175 case FSL_USB2_PHY_UTMI:
176 portsc |= PORT_PTS_UTMI;
177 break;
178 case FSL_USB2_PHY_NONE:
179 break;
180 }
181 writel(portsc, &ehci->regs->port_status[port_offset]);
182}
183
184static void mpc83xx_usb_setup(struct usb_hcd *hcd)
185{
186 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
187 struct fsl_usb2_platform_data *pdata;
188 void __iomem *non_ehci = hcd->regs;
189
190 pdata =
191 (struct fsl_usb2_platform_data *)hcd->self.controller->
192 platform_data;
193 /* Enable PHY interface in the control reg. */
194 out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004);
195 out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
196
197 if (pdata->operating_mode == FSL_USB2_DR_HOST)
198 mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
199
200 if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
201 if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
202 mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
203 if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
204 mpc83xx_setup_phy(ehci, pdata->phy_mode, 1);
205 }
206
207 /* put controller in host mode. */
208 writel(0x00000003, non_ehci + FSL_SOC_USB_USBMODE);
209 out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
210 out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
211 out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
212}
213
214/* called after powerup, by probe or system-pm "wakeup" */
215static int ehci_fsl_reinit(struct ehci_hcd *ehci)
216{
217 mpc83xx_usb_setup(ehci_to_hcd(ehci));
218 ehci_port_power(ehci, 0);
219
220 return 0;
221}
222
223/* called during probe() after chip reset completes */
224static int ehci_fsl_setup(struct usb_hcd *hcd)
225{
226 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
227 int retval;
228
229 /* EHCI registers start at offset 0x100 */
230 ehci->caps = hcd->regs + 0x100;
231 ehci->regs = hcd->regs + 0x100 +
232 HC_LENGTH(readl(&ehci->caps->hc_capbase));
233 dbg_hcs_params(ehci, "reset");
234 dbg_hcc_params(ehci, "reset");
235
236 /* cache this readonly data; minimize chip reads */
237 ehci->hcs_params = readl(&ehci->caps->hcs_params);
238
239 retval = ehci_halt(ehci);
240 if (retval)
241 return retval;
242
243 /* data structure init */
244 retval = ehci_init(hcd);
245 if (retval)
246 return retval;
247
248 ehci->is_tdi_rh_tt = 1;
249
250 ehci->sbrn = 0x20;
251
252 ehci_reset(ehci);
253
254 retval = ehci_fsl_reinit(ehci);
255 return retval;
256}
257
258static const struct hc_driver ehci_fsl_hc_driver = {
259 .description = hcd_name,
260 .product_desc = "Freescale On-Chip EHCI Host Controller",
261 .hcd_priv_size = sizeof(struct ehci_hcd),
262
263 /*
264 * generic hardware linkage
265 */
266 .irq = ehci_irq,
267 .flags = HCD_USB2,
268
269 /*
270 * basic lifecycle operations
271 */
272 .reset = ehci_fsl_setup,
273 .start = ehci_run,
274#ifdef CONFIG_PM
275 .suspend = ehci_bus_suspend,
276 .resume = ehci_bus_resume,
277#endif
278 .stop = ehci_stop,
279
280 /*
281 * managing i/o requests and associated device resources
282 */
283 .urb_enqueue = ehci_urb_enqueue,
284 .urb_dequeue = ehci_urb_dequeue,
285 .endpoint_disable = ehci_endpoint_disable,
286
287 /*
288 * scheduling support
289 */
290 .get_frame_number = ehci_get_frame,
291
292 /*
293 * root hub support
294 */
295 .hub_status_data = ehci_hub_status_data,
296 .hub_control = ehci_hub_control,
297 .bus_suspend = ehci_bus_suspend,
298 .bus_resume = ehci_bus_resume,
299};
300
301static int ehci_fsl_drv_probe(struct platform_device *pdev)
302{
303 if (usb_disabled())
304 return -ENODEV;
305
306 return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
307}
308
309static int ehci_fsl_drv_remove(struct platform_device *pdev)
310{
311 struct usb_hcd *hcd = platform_get_drvdata(pdev);
312
313 usb_hcd_fsl_remove(hcd, pdev);
314
315 return 0;
316}
317
318static struct platform_driver ehci_fsl_dr_driver = {
319 .probe = ehci_fsl_drv_probe,
320 .remove = ehci_fsl_drv_remove,
321 .driver = {
322 .name = "fsl-usb2-dr",
323 },
324};
325
326static struct platform_driver ehci_fsl_mph_driver = {
327 .probe = ehci_fsl_drv_probe,
328 .remove = ehci_fsl_drv_remove,
329 .driver = {
330 .name = "fsl-usb2-mph",
331 },
332};
333
334static int __init ehci_fsl_init(void)
335{
336 int retval;
337
338 pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
339 hcd_name,
340 sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
341 sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
342
343 retval = platform_driver_register(&ehci_fsl_dr_driver);
344 if (retval)
345 return retval;
346
347 return platform_driver_register(&ehci_fsl_mph_driver);
348}
349
350static void __exit ehci_fsl_cleanup(void)
351{
352 platform_driver_unregister(&ehci_fsl_mph_driver);
353 platform_driver_unregister(&ehci_fsl_dr_driver);
354}
355
356module_init(ehci_fsl_init);
357module_exit(ehci_fsl_cleanup);
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
new file mode 100644
index 000000000000..caac0d1967d0
--- /dev/null
+++ b/drivers/usb/host/ehci-fsl.h
@@ -0,0 +1,37 @@
1/* Copyright (c) 2005 freescale semiconductor
2 * Copyright (c) 2005 MontaVista Software
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18#ifndef _EHCI_FSL_H
19#define _EHCI_FSL_H
20
21/* offsets for the non-ehci registers in the FSL SOC USB controller */
22#define FSL_SOC_USB_ULPIVP 0x170
23#define FSL_SOC_USB_PORTSC1 0x184
24#define PORT_PTS_MSK (3<<30)
25#define PORT_PTS_UTMI (0<<30)
26#define PORT_PTS_ULPI (2<<30)
27#define PORT_PTS_SERIAL (3<<30)
28#define PORT_PTS_PTW (1<<28)
29#define FSL_SOC_USB_PORTSC2 0x188
30#define FSL_SOC_USB_USBMODE 0x1a8
31#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
32#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
33#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
34#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */
35#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */
36#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
37#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 9dd3d14c64f3..8730babb5771 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -891,6 +891,12 @@ MODULE_LICENSE ("GPL");
891#include "ehci-pci.c" 891#include "ehci-pci.c"
892#endif 892#endif
893 893
894#if !defined(CONFIG_PCI) 894#ifdef CONFIG_PPC_83xx
895#include "ehci-fsl.c"
896#endif
897
898#if !(defined(CONFIG_PCI) || \
899 defined(CONFIG_PPC_83xx) \
900 )
895#error "missing bus glue for ehci-hcd" 901#error "missing bus glue for ehci-hcd"
896#endif 902#endif
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index a9f1cfd096ff..a3a0e078f79d 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -83,5 +83,32 @@ struct fsl_i2c_platform_data {
83#define FSL_I2C_DEV_SEPARATE_DFSRR 0x00000001 83#define FSL_I2C_DEV_SEPARATE_DFSRR 0x00000001
84#define FSL_I2C_DEV_CLOCK_5200 0x00000002 84#define FSL_I2C_DEV_CLOCK_5200 0x00000002
85 85
86
87enum fsl_usb2_operating_modes {
88 FSL_USB2_MPH_HOST,
89 FSL_USB2_DR_HOST,
90 FSL_USB2_DR_DEVICE,
91 FSL_USB2_DR_OTG,
92};
93
94enum fsl_usb2_phy_modes {
95 FSL_USB2_PHY_NONE,
96 FSL_USB2_PHY_ULPI,
97 FSL_USB2_PHY_UTMI,
98 FSL_USB2_PHY_UTMI_WIDE,
99 FSL_USB2_PHY_SERIAL,
100};
101
102struct fsl_usb2_platform_data {
103 /* board specific information */
104 enum fsl_usb2_operating_modes operating_mode;
105 enum fsl_usb2_phy_modes phy_mode;
106 unsigned int port_enables;
107};
108
109/* Flags in fsl_usb2_mph_platform_data */
110#define FSL_USB2_PORT0_ENABLED 0x00000001
111#define FSL_USB2_PORT1_ENABLED 0x00000002
112
86#endif /* _FSL_DEVICE_H_ */ 113#endif /* _FSL_DEVICE_H_ */
87#endif /* __KERNEL__ */ 114#endif /* __KERNEL__ */