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