1 | // SPDX-License-Identifier: GPL-1.0+ |
2 | /* |
3 | * OHCI HCD (Host Controller Driver) for USB. |
4 | * |
5 | * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> |
6 | * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> |
7 | * (C) Copyright 2002 Hewlett-Packard Company |
8 | * |
9 | * SA1111 Bus Glue |
10 | * |
11 | * Written by Christopher Hoover <ch@hpl.hp.com> |
12 | * Based on fragments of previous driver by Russell King et al. |
13 | * |
14 | * This file is licenced under the GPL. |
15 | */ |
16 | |
17 | #include <asm/mach-types.h> |
18 | #include <asm/hardware/sa1111.h> |
19 | |
20 | #ifndef CONFIG_SA1111 |
21 | #error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined." |
22 | #endif |
23 | |
24 | #define USB_STATUS 0x0118 |
25 | #define USB_RESET 0x011c |
26 | #define USB_IRQTEST 0x0120 |
27 | |
28 | #define USB_RESET_FORCEIFRESET (1 << 0) |
29 | #define USB_RESET_FORCEHCRESET (1 << 1) |
30 | #define USB_RESET_CLKGENRESET (1 << 2) |
31 | #define USB_RESET_SIMSCALEDOWN (1 << 3) |
32 | #define USB_RESET_USBINTTEST (1 << 4) |
33 | #define USB_RESET_SLEEPSTBYEN (1 << 5) |
34 | #define USB_RESET_PWRSENSELOW (1 << 6) |
35 | #define USB_RESET_PWRCTRLLOW (1 << 7) |
36 | |
37 | #define USB_STATUS_IRQHCIRMTWKUP (1 << 7) |
38 | #define USB_STATUS_IRQHCIBUFFACC (1 << 8) |
39 | #define USB_STATUS_NIRQHCIM (1 << 9) |
40 | #define USB_STATUS_NHCIMFCLR (1 << 10) |
41 | #define USB_STATUS_USBPWRSENSE (1 << 11) |
42 | |
43 | #if 0 |
44 | static void dump_hci_status(struct usb_hcd *hcd, const char *label) |
45 | { |
46 | unsigned long status = readl_relaxed(hcd->regs + USB_STATUS); |
47 | |
48 | printk(KERN_DEBUG "%s USB_STATUS = { %s%s%s%s%s}\n" , label, |
49 | ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : "" ), |
50 | ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : "" ), |
51 | ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM " ), |
52 | ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR " ), |
53 | ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : "" )); |
54 | } |
55 | #endif |
56 | |
57 | static int ohci_sa1111_reset(struct usb_hcd *hcd) |
58 | { |
59 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
60 | |
61 | ohci_hcd_init(ohci); |
62 | return ohci_init(ohci); |
63 | } |
64 | |
65 | static int ohci_sa1111_start(struct usb_hcd *hcd) |
66 | { |
67 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
68 | int ret; |
69 | |
70 | ret = ohci_run(ohci); |
71 | if (ret < 0) { |
72 | ohci_err(ohci, "can't start\n" ); |
73 | ohci_stop(hcd); |
74 | } |
75 | return ret; |
76 | } |
77 | |
78 | static const struct hc_driver ohci_sa1111_hc_driver = { |
79 | .description = hcd_name, |
80 | .product_desc = "SA-1111 OHCI" , |
81 | .hcd_priv_size = sizeof(struct ohci_hcd), |
82 | |
83 | /* |
84 | * generic hardware linkage |
85 | */ |
86 | .irq = ohci_irq, |
87 | .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY, |
88 | |
89 | /* |
90 | * basic lifecycle operations |
91 | */ |
92 | .reset = ohci_sa1111_reset, |
93 | .start = ohci_sa1111_start, |
94 | .stop = ohci_stop, |
95 | .shutdown = ohci_shutdown, |
96 | |
97 | /* |
98 | * managing i/o requests and associated device resources |
99 | */ |
100 | .urb_enqueue = ohci_urb_enqueue, |
101 | .urb_dequeue = ohci_urb_dequeue, |
102 | .endpoint_disable = ohci_endpoint_disable, |
103 | |
104 | /* |
105 | * scheduling support |
106 | */ |
107 | .get_frame_number = ohci_get_frame, |
108 | |
109 | /* |
110 | * root hub support |
111 | */ |
112 | .hub_status_data = ohci_hub_status_data, |
113 | .hub_control = ohci_hub_control, |
114 | #ifdef CONFIG_PM |
115 | .bus_suspend = ohci_bus_suspend, |
116 | .bus_resume = ohci_bus_resume, |
117 | #endif |
118 | .start_port_reset = ohci_start_port_reset, |
119 | }; |
120 | |
121 | static int sa1111_start_hc(struct sa1111_dev *dev) |
122 | { |
123 | unsigned int usb_rst = 0; |
124 | int ret; |
125 | |
126 | dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n" ); |
127 | |
128 | if (machine_is_assabet()) |
129 | usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; |
130 | |
131 | /* |
132 | * Configure the power sense and control lines. Place the USB |
133 | * host controller in reset. |
134 | */ |
135 | writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, |
136 | dev->mapbase + USB_RESET); |
137 | |
138 | /* |
139 | * Now, carefully enable the USB clock, and take |
140 | * the USB host controller out of reset. |
141 | */ |
142 | ret = sa1111_enable_device(dev); |
143 | if (ret == 0) { |
144 | udelay(11); |
145 | writel_relaxed(usb_rst, dev->mapbase + USB_RESET); |
146 | } |
147 | |
148 | return ret; |
149 | } |
150 | |
151 | static void sa1111_stop_hc(struct sa1111_dev *dev) |
152 | { |
153 | unsigned int usb_rst; |
154 | |
155 | dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n" ); |
156 | |
157 | /* |
158 | * Put the USB host controller into reset. |
159 | */ |
160 | usb_rst = readl_relaxed(dev->mapbase + USB_RESET); |
161 | writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, |
162 | dev->mapbase + USB_RESET); |
163 | |
164 | /* |
165 | * Stop the USB clock. |
166 | */ |
167 | sa1111_disable_device(dev); |
168 | } |
169 | |
170 | /** |
171 | * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs |
172 | * |
173 | * Allocates basic resources for this USB host controller, and |
174 | * then invokes the start() method for the HCD associated with it. |
175 | */ |
176 | static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) |
177 | { |
178 | struct usb_hcd *hcd; |
179 | int ret, irq; |
180 | |
181 | if (usb_disabled()) |
182 | return -ENODEV; |
183 | |
184 | /* |
185 | * We don't call dma_set_mask_and_coherent() here because the |
186 | * DMA mask has already been appropraitely setup by the core |
187 | * SA-1111 bus code (which includes bug workarounds.) |
188 | */ |
189 | |
190 | hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111" ); |
191 | if (!hcd) |
192 | return -ENOMEM; |
193 | |
194 | hcd->rsrc_start = dev->res.start; |
195 | hcd->rsrc_len = resource_size(&dev->res); |
196 | |
197 | irq = sa1111_get_irq(dev, 1); |
198 | if (irq <= 0) { |
199 | ret = irq ? : -ENXIO; |
200 | goto err1; |
201 | } |
202 | |
203 | /* |
204 | * According to the "Intel StrongARM SA-1111 Microprocessor Companion |
205 | * Chip Specification Update" (June 2000), erratum #7, there is a |
206 | * significant bug in the SA1111 SDRAM shared memory controller. If |
207 | * an access to a region of memory above 1MB relative to the bank base, |
208 | * it is important that address bit 10 _NOT_ be asserted. Depending |
209 | * on the configuration of the RAM, bit 10 may correspond to one |
210 | * of several different (processor-relative) address bits. |
211 | * |
212 | * Section 4.6 of the "Intel StrongARM SA-1111 Development Module |
213 | * User's Guide" mentions that jumpers R51 and R52 control the |
214 | * target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or |
215 | * SDRAM bank 1 on Neponset). The default configuration selects |
216 | * Assabet, so any address in bank 1 is necessarily invalid. |
217 | * |
218 | * As a workaround, use a bounce buffer in addressable memory |
219 | * as local_mem, relying on ZONE_DMA to provide an area that |
220 | * fits within the above constraints. |
221 | * |
222 | * SZ_64K is an estimate for what size this might need. |
223 | */ |
224 | ret = usb_hcd_setup_local_mem(hcd, 0, 0, SZ_64K); |
225 | if (ret) |
226 | goto err1; |
227 | |
228 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { |
229 | dev_dbg(&dev->dev, "request_mem_region failed\n" ); |
230 | ret = -EBUSY; |
231 | goto err1; |
232 | } |
233 | |
234 | hcd->regs = dev->mapbase; |
235 | |
236 | ret = sa1111_start_hc(dev); |
237 | if (ret) |
238 | goto err2; |
239 | |
240 | ret = usb_add_hcd(hcd, irq, 0); |
241 | if (ret == 0) { |
242 | device_wakeup_enable(hcd->self.controller); |
243 | return ret; |
244 | } |
245 | |
246 | sa1111_stop_hc(dev); |
247 | err2: |
248 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
249 | err1: |
250 | usb_put_hcd(hcd); |
251 | return ret; |
252 | } |
253 | |
254 | /** |
255 | * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs |
256 | * @dev: USB Host Controller being removed |
257 | * |
258 | * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking |
259 | * the HCD's stop() method. |
260 | */ |
261 | static void ohci_hcd_sa1111_remove(struct sa1111_dev *dev) |
262 | { |
263 | struct usb_hcd *hcd = sa1111_get_drvdata(dev); |
264 | |
265 | usb_remove_hcd(hcd); |
266 | sa1111_stop_hc(dev); |
267 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
268 | usb_put_hcd(hcd); |
269 | } |
270 | |
271 | static void ohci_hcd_sa1111_shutdown(struct device *_dev) |
272 | { |
273 | struct sa1111_dev *dev = to_sa1111_device(_dev); |
274 | struct usb_hcd *hcd = sa1111_get_drvdata(dev); |
275 | |
276 | if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { |
277 | hcd->driver->shutdown(hcd); |
278 | sa1111_stop_hc(dev); |
279 | } |
280 | } |
281 | |
282 | static struct sa1111_driver ohci_hcd_sa1111_driver = { |
283 | .drv = { |
284 | .name = "sa1111-ohci" , |
285 | .owner = THIS_MODULE, |
286 | .shutdown = ohci_hcd_sa1111_shutdown, |
287 | }, |
288 | .devid = SA1111_DEVID_USB, |
289 | .probe = ohci_hcd_sa1111_probe, |
290 | .remove = ohci_hcd_sa1111_remove, |
291 | }; |
292 | |