]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * OHCI HCD (Host Controller Driver) for USB. | |
3 | * | |
4 | * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> | |
5 | * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> | |
6 | * (C) Copyright 2002 Hewlett-Packard Company | |
dd9048af | 7 | * |
1da177e4 LT |
8 | * SA1111 Bus Glue |
9 | * | |
10 | * Written by Christopher Hoover <ch@hpl.hp.com> | |
ac310bb5 | 11 | * Based on fragments of previous driver by Russell King et al. |
1da177e4 LT |
12 | * |
13 | * This file is licenced under the GPL. | |
14 | */ | |
dd9048af | 15 | |
a09e64fb | 16 | #include <mach/hardware.h> |
1da177e4 | 17 | #include <asm/mach-types.h> |
a09e64fb | 18 | #include <mach/assabet.h> |
1da177e4 LT |
19 | #include <asm/hardware/sa1111.h> |
20 | ||
21 | #ifndef CONFIG_SA1111 | |
22 | #error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined." | |
23 | #endif | |
24 | ||
2213536d RK |
25 | #define USB_STATUS 0x0118 |
26 | #define USB_RESET 0x011c | |
27 | #define USB_IRQTEST 0x0120 | |
28 | ||
29 | #define USB_RESET_FORCEIFRESET (1 << 0) | |
30 | #define USB_RESET_FORCEHCRESET (1 << 1) | |
31 | #define USB_RESET_CLKGENRESET (1 << 2) | |
32 | #define USB_RESET_SIMSCALEDOWN (1 << 3) | |
33 | #define USB_RESET_USBINTTEST (1 << 4) | |
34 | #define USB_RESET_SLEEPSTBYEN (1 << 5) | |
35 | #define USB_RESET_PWRSENSELOW (1 << 6) | |
36 | #define USB_RESET_PWRCTRLLOW (1 << 7) | |
37 | ||
38 | #define USB_STATUS_IRQHCIRMTWKUP (1 << 7) | |
39 | #define USB_STATUS_IRQHCIBUFFACC (1 << 8) | |
40 | #define USB_STATUS_NIRQHCIM (1 << 9) | |
41 | #define USB_STATUS_NHCIMFCLR (1 << 10) | |
42 | #define USB_STATUS_USBPWRSENSE (1 << 11) | |
43 | ||
132db99a RK |
44 | #if 0 |
45 | static void dump_hci_status(struct usb_hcd *hcd, const char *label) | |
46 | { | |
47 | unsigned long status = sa1111_readl(hcd->regs + USB_STATUS); | |
48 | ||
3879e304 | 49 | printk(KERN_DEBUG "%s USB_STATUS = { %s%s%s%s%s}\n", label, |
132db99a RK |
50 | ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""), |
51 | ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""), | |
52 | ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "), | |
53 | ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "), | |
54 | ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : "")); | |
55 | } | |
56 | #endif | |
57 | ||
81e6ca3e RK |
58 | static int ohci_sa1111_reset(struct usb_hcd *hcd) |
59 | { | |
60 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | |
61 | ||
62 | ohci_hcd_init(ohci); | |
63 | return ohci_init(ohci); | |
64 | } | |
65 | ||
41ac7b3a | 66 | static int ohci_sa1111_start(struct usb_hcd *hcd) |
132db99a RK |
67 | { |
68 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | |
69 | int ret; | |
70 | ||
132db99a RK |
71 | ret = ohci_run(ohci); |
72 | if (ret < 0) { | |
81e6ca3e | 73 | ohci_err(ohci, "can't start\n"); |
132db99a | 74 | ohci_stop(hcd); |
132db99a | 75 | } |
81e6ca3e | 76 | return ret; |
132db99a RK |
77 | } |
78 | ||
79 | static const struct hc_driver ohci_sa1111_hc_driver = { | |
80 | .description = hcd_name, | |
81 | .product_desc = "SA-1111 OHCI", | |
82 | .hcd_priv_size = sizeof(struct ohci_hcd), | |
83 | ||
84 | /* | |
85 | * generic hardware linkage | |
86 | */ | |
87 | .irq = ohci_irq, | |
88 | .flags = HCD_USB11 | HCD_MEMORY, | |
89 | ||
90 | /* | |
91 | * basic lifecycle operations | |
92 | */ | |
81e6ca3e | 93 | .reset = ohci_sa1111_reset, |
132db99a RK |
94 | .start = ohci_sa1111_start, |
95 | .stop = ohci_stop, | |
846a7048 | 96 | .shutdown = ohci_shutdown, |
132db99a RK |
97 | |
98 | /* | |
99 | * managing i/o requests and associated device resources | |
100 | */ | |
101 | .urb_enqueue = ohci_urb_enqueue, | |
102 | .urb_dequeue = ohci_urb_dequeue, | |
103 | .endpoint_disable = ohci_endpoint_disable, | |
104 | ||
105 | /* | |
106 | * scheduling support | |
107 | */ | |
108 | .get_frame_number = ohci_get_frame, | |
109 | ||
110 | /* | |
111 | * root hub support | |
112 | */ | |
113 | .hub_status_data = ohci_hub_status_data, | |
114 | .hub_control = ohci_hub_control, | |
115 | #ifdef CONFIG_PM | |
116 | .bus_suspend = ohci_bus_suspend, | |
117 | .bus_resume = ohci_bus_resume, | |
118 | #endif | |
119 | .start_port_reset = ohci_start_port_reset, | |
120 | }; | |
1da177e4 | 121 | |
ae99ddbc | 122 | static int sa1111_start_hc(struct sa1111_dev *dev) |
1da177e4 LT |
123 | { |
124 | unsigned int usb_rst = 0; | |
ae99ddbc | 125 | int ret; |
1da177e4 | 126 | |
3f878dbc | 127 | dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n"); |
1da177e4 | 128 | |
1da177e4 LT |
129 | if (machine_is_xp860() || |
130 | machine_has_neponset() || | |
131 | machine_is_pfs168() || | |
132 | machine_is_badge4()) | |
133 | usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; | |
134 | ||
135 | /* | |
136 | * Configure the power sense and control lines. Place the USB | |
137 | * host controller in reset. | |
138 | */ | |
139 | sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, | |
2213536d | 140 | dev->mapbase + USB_RESET); |
1da177e4 LT |
141 | |
142 | /* | |
143 | * Now, carefully enable the USB clock, and take | |
144 | * the USB host controller out of reset. | |
145 | */ | |
ae99ddbc RK |
146 | ret = sa1111_enable_device(dev); |
147 | if (ret == 0) { | |
148 | udelay(11); | |
2213536d | 149 | sa1111_writel(usb_rst, dev->mapbase + USB_RESET); |
ae99ddbc RK |
150 | } |
151 | ||
152 | return ret; | |
1da177e4 LT |
153 | } |
154 | ||
155 | static void sa1111_stop_hc(struct sa1111_dev *dev) | |
156 | { | |
157 | unsigned int usb_rst; | |
9cb0f819 | 158 | |
3f878dbc | 159 | dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n"); |
1da177e4 LT |
160 | |
161 | /* | |
162 | * Put the USB host controller into reset. | |
163 | */ | |
2213536d | 164 | usb_rst = sa1111_readl(dev->mapbase + USB_RESET); |
1da177e4 | 165 | sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, |
2213536d | 166 | dev->mapbase + USB_RESET); |
1da177e4 LT |
167 | |
168 | /* | |
169 | * Stop the USB clock. | |
170 | */ | |
171 | sa1111_disable_device(dev); | |
1da177e4 LT |
172 | } |
173 | ||
1da177e4 | 174 | /** |
132db99a | 175 | * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs |
1da177e4 LT |
176 | * |
177 | * Allocates basic resources for this USB host controller, and | |
132db99a | 178 | * then invokes the start() method for the HCD associated with it. |
1da177e4 | 179 | */ |
132db99a | 180 | static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) |
1da177e4 LT |
181 | { |
182 | struct usb_hcd *hcd; | |
132db99a RK |
183 | int ret; |
184 | ||
185 | if (usb_disabled()) | |
186 | return -ENODEV; | |
1da177e4 | 187 | |
132db99a | 188 | hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111"); |
1da177e4 LT |
189 | if (!hcd) |
190 | return -ENOMEM; | |
9cb0f819 | 191 | |
1da177e4 | 192 | hcd->rsrc_start = dev->res.start; |
28f65c11 | 193 | hcd->rsrc_len = resource_size(&dev->res); |
1da177e4 LT |
194 | |
195 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { | |
3879e304 | 196 | dev_dbg(&dev->dev, "request_mem_region failed\n"); |
132db99a | 197 | ret = -EBUSY; |
1da177e4 LT |
198 | goto err1; |
199 | } | |
9cb0f819 | 200 | |
1da177e4 LT |
201 | hcd->regs = dev->mapbase; |
202 | ||
ae99ddbc RK |
203 | ret = sa1111_start_hc(dev); |
204 | if (ret) | |
205 | goto err2; | |
206 | ||
132db99a RK |
207 | ret = usb_add_hcd(hcd, dev->irq[1], 0); |
208 | if (ret == 0) | |
209 | return ret; | |
1da177e4 LT |
210 | |
211 | sa1111_stop_hc(dev); | |
ae99ddbc | 212 | err2: |
1da177e4 LT |
213 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
214 | err1: | |
215 | usb_put_hcd(hcd); | |
132db99a | 216 | return ret; |
1da177e4 LT |
217 | } |
218 | ||
1da177e4 | 219 | /** |
132db99a | 220 | * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs |
1da177e4 | 221 | * @dev: USB Host Controller being removed |
1da177e4 | 222 | * |
132db99a RK |
223 | * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking |
224 | * the HCD's stop() method. | |
1da177e4 | 225 | */ |
132db99a | 226 | static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev) |
1da177e4 | 227 | { |
132db99a RK |
228 | struct usb_hcd *hcd = sa1111_get_drvdata(dev); |
229 | ||
1da177e4 LT |
230 | usb_remove_hcd(hcd); |
231 | sa1111_stop_hc(dev); | |
232 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | |
233 | usb_put_hcd(hcd); | |
1da177e4 | 234 | |
1da177e4 LT |
235 | return 0; |
236 | } | |
237 | ||
846a7048 RK |
238 | static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev) |
239 | { | |
240 | struct usb_hcd *hcd = sa1111_get_drvdata(dev); | |
241 | ||
242 | if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { | |
243 | hcd->driver->shutdown(hcd); | |
244 | sa1111_stop_hc(dev); | |
245 | } | |
246 | } | |
247 | ||
1da177e4 LT |
248 | static struct sa1111_driver ohci_hcd_sa1111_driver = { |
249 | .drv = { | |
250 | .name = "sa1111-ohci", | |
1ebcd765 | 251 | .owner = THIS_MODULE, |
1da177e4 LT |
252 | }, |
253 | .devid = SA1111_DEVID_USB, | |
132db99a RK |
254 | .probe = ohci_hcd_sa1111_probe, |
255 | .remove = ohci_hcd_sa1111_remove, | |
846a7048 | 256 | .shutdown = ohci_hcd_sa1111_shutdown, |
1da177e4 | 257 | }; |