Commit | Line | Data |
---|---|---|
db11e47d SS |
1 | /* |
2 | * Glue code for the ISP1760 driver and bus | |
3 | * Currently there is support for | |
4 | * - OpenFirmware | |
5 | * - PCI | |
6 | * | |
7 | * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/usb.h> | |
12 | #include <linux/io.h> | |
13 | ||
14 | #include "../core/hcd.h" | |
15 | #include "isp1760-hcd.h" | |
16 | ||
17 | #ifdef CONFIG_USB_ISP1760_OF | |
18 | #include <linux/of.h> | |
19 | #include <linux/of_platform.h> | |
20 | #endif | |
21 | ||
22 | #ifdef CONFIG_USB_ISP1760_PCI | |
23 | #include <linux/pci.h> | |
24 | #endif | |
25 | ||
26 | #ifdef CONFIG_USB_ISP1760_OF | |
27 | static int of_isp1760_probe(struct of_device *dev, | |
28 | const struct of_device_id *match) | |
29 | { | |
30 | struct usb_hcd *hcd; | |
31 | struct device_node *dp = dev->node; | |
32 | struct resource *res; | |
33 | struct resource memory; | |
34 | struct of_irq oirq; | |
35 | int virq; | |
36 | u64 res_len; | |
37 | int ret; | |
3faefc88 NC |
38 | const unsigned int *prop; |
39 | unsigned int devflags = 0; | |
db11e47d SS |
40 | |
41 | ret = of_address_to_resource(dp, 0, &memory); | |
42 | if (ret) | |
43 | return -ENXIO; | |
44 | ||
45 | res = request_mem_region(memory.start, memory.end - memory.start + 1, | |
7071a3ce | 46 | dev_name(&dev->dev)); |
db11e47d SS |
47 | if (!res) |
48 | return -EBUSY; | |
49 | ||
50 | res_len = memory.end - memory.start + 1; | |
51 | ||
52 | if (of_irq_map_one(dp, 0, &oirq)) { | |
53 | ret = -ENODEV; | |
54 | goto release_reg; | |
55 | } | |
56 | ||
57 | virq = irq_create_of_mapping(oirq.controller, oirq.specifier, | |
58 | oirq.size); | |
59 | ||
3faefc88 NC |
60 | if (of_device_is_compatible(dp, "nxp,usb-isp1761")) |
61 | devflags |= ISP1760_FLAG_ISP1761; | |
62 | ||
63 | if (of_get_property(dp, "port1-disable", NULL) != NULL) | |
64 | devflags |= ISP1760_FLAG_PORT1_DIS; | |
65 | ||
66 | /* Some systems wire up only 16 of the 32 data lines */ | |
67 | prop = of_get_property(dp, "bus-width", NULL); | |
68 | if (prop && *prop == 16) | |
69 | devflags |= ISP1760_FLAG_BUS_WIDTH_16; | |
70 | ||
71 | if (of_get_property(dp, "port1-otg", NULL) != NULL) | |
72 | devflags |= ISP1760_FLAG_OTG_EN; | |
73 | ||
74 | if (of_get_property(dp, "analog-oc", NULL) != NULL) | |
75 | devflags |= ISP1760_FLAG_ANALOG_OC; | |
76 | ||
77 | if (of_get_property(dp, "dack-polarity", NULL) != NULL) | |
78 | devflags |= ISP1760_FLAG_DACK_POL_HIGH; | |
79 | ||
80 | if (of_get_property(dp, "dreq-polarity", NULL) != NULL) | |
81 | devflags |= ISP1760_FLAG_DREQ_POL_HIGH; | |
82 | ||
db11e47d | 83 | hcd = isp1760_register(memory.start, res_len, virq, |
3faefc88 NC |
84 | IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), |
85 | devflags); | |
db11e47d SS |
86 | if (IS_ERR(hcd)) { |
87 | ret = PTR_ERR(hcd); | |
88 | goto release_reg; | |
89 | } | |
90 | ||
91 | dev_set_drvdata(&dev->dev, hcd); | |
92 | return ret; | |
93 | ||
94 | release_reg: | |
95 | release_mem_region(memory.start, memory.end - memory.start + 1); | |
96 | return ret; | |
97 | } | |
98 | ||
99 | static int of_isp1760_remove(struct of_device *dev) | |
100 | { | |
101 | struct usb_hcd *hcd = dev_get_drvdata(&dev->dev); | |
102 | ||
103 | dev_set_drvdata(&dev->dev, NULL); | |
104 | ||
105 | usb_remove_hcd(hcd); | |
106 | iounmap(hcd->regs); | |
107 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | |
108 | usb_put_hcd(hcd); | |
109 | return 0; | |
110 | } | |
111 | ||
112 | static struct of_device_id of_isp1760_match[] = { | |
113 | { | |
114 | .compatible = "nxp,usb-isp1760", | |
115 | }, | |
3faefc88 NC |
116 | { |
117 | .compatible = "nxp,usb-isp1761", | |
118 | }, | |
db11e47d SS |
119 | { }, |
120 | }; | |
121 | MODULE_DEVICE_TABLE(of, of_isp1760_match); | |
122 | ||
123 | static struct of_platform_driver isp1760_of_driver = { | |
124 | .name = "nxp-isp1760", | |
125 | .match_table = of_isp1760_match, | |
126 | .probe = of_isp1760_probe, | |
127 | .remove = of_isp1760_remove, | |
128 | }; | |
129 | #endif | |
130 | ||
131 | #ifdef CONFIG_USB_ISP1760_PCI | |
132 | static u32 nxp_pci_io_base; | |
133 | static u32 iolength; | |
134 | static u32 pci_mem_phy0; | |
135 | static u32 length; | |
6399e7ac AV |
136 | static u8 __iomem *chip_addr; |
137 | static u8 __iomem *iobase; | |
db11e47d SS |
138 | |
139 | static int __devinit isp1761_pci_probe(struct pci_dev *dev, | |
140 | const struct pci_device_id *id) | |
141 | { | |
142 | u8 latency, limit; | |
143 | __u32 reg_data; | |
144 | int retry_count; | |
145 | int length; | |
146 | int status = 1; | |
147 | struct usb_hcd *hcd; | |
3faefc88 | 148 | unsigned int devflags = 0; |
db11e47d SS |
149 | |
150 | if (usb_disabled()) | |
151 | return -ENODEV; | |
152 | ||
153 | if (pci_enable_device(dev) < 0) | |
154 | return -ENODEV; | |
155 | ||
156 | if (!dev->irq) | |
157 | return -ENODEV; | |
158 | ||
159 | /* Grab the PLX PCI mem maped port start address we need */ | |
160 | nxp_pci_io_base = pci_resource_start(dev, 0); | |
161 | iolength = pci_resource_len(dev, 0); | |
162 | ||
163 | if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) { | |
164 | printk(KERN_ERR "request region #1\n"); | |
165 | return -EBUSY; | |
166 | } | |
167 | ||
168 | iobase = ioremap_nocache(nxp_pci_io_base, iolength); | |
169 | if (!iobase) { | |
170 | printk(KERN_ERR "ioremap #1\n"); | |
171 | release_mem_region(nxp_pci_io_base, iolength); | |
172 | return -ENOMEM; | |
173 | } | |
174 | /* Grab the PLX PCI shared memory of the ISP 1761 we need */ | |
175 | pci_mem_phy0 = pci_resource_start(dev, 3); | |
176 | length = pci_resource_len(dev, 3); | |
177 | ||
178 | if (length < 0xffff) { | |
179 | printk(KERN_ERR "memory length for this resource is less than " | |
180 | "required\n"); | |
181 | release_mem_region(nxp_pci_io_base, iolength); | |
182 | iounmap(iobase); | |
183 | return -ENOMEM; | |
184 | } | |
185 | ||
186 | if (!request_mem_region(pci_mem_phy0, length, "ISP-PCI")) { | |
187 | printk(KERN_ERR "host controller already in use\n"); | |
188 | release_mem_region(nxp_pci_io_base, iolength); | |
189 | iounmap(iobase); | |
190 | return -EBUSY; | |
191 | } | |
192 | ||
193 | /* bad pci latencies can contribute to overruns */ | |
194 | pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency); | |
195 | if (latency) { | |
196 | pci_read_config_byte(dev, PCI_MAX_LAT, &limit); | |
197 | if (limit && limit < latency) | |
198 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit); | |
199 | } | |
200 | ||
201 | /* Try to check whether we can access Scratch Register of | |
202 | * Host Controller or not. The initial PCI access is retried until | |
203 | * local init for the PCI bridge is completed | |
204 | */ | |
205 | retry_count = 20; | |
206 | reg_data = 0; | |
207 | while ((reg_data != 0xFACE) && retry_count) { | |
208 | /*by default host is in 16bit mode, so | |
209 | * io operations at this stage must be 16 bit | |
210 | * */ | |
211 | writel(0xface, chip_addr + HC_SCRATCH_REG); | |
212 | udelay(100); | |
213 | reg_data = readl(chip_addr + HC_SCRATCH_REG); | |
214 | retry_count--; | |
215 | } | |
216 | ||
217 | /* Host Controller presence is detected by writing to scratch register | |
218 | * and reading back and checking the contents are same or not | |
219 | */ | |
220 | if (reg_data != 0xFACE) { | |
221 | err("scratch register mismatch %x", reg_data); | |
222 | goto clean; | |
223 | } | |
224 | ||
225 | pci_set_master(dev); | |
226 | ||
227 | status = readl(iobase + 0x68); | |
228 | status |= 0x900; | |
229 | writel(status, iobase + 0x68); | |
230 | ||
231 | dev->dev.dma_mask = NULL; | |
232 | hcd = isp1760_register(pci_mem_phy0, length, dev->irq, | |
3faefc88 NC |
233 | IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), |
234 | devflags); | |
db11e47d SS |
235 | pci_set_drvdata(dev, hcd); |
236 | if (!hcd) | |
237 | return 0; | |
238 | clean: | |
239 | status = -ENODEV; | |
240 | iounmap(iobase); | |
241 | release_mem_region(pci_mem_phy0, length); | |
242 | release_mem_region(nxp_pci_io_base, iolength); | |
243 | return status; | |
244 | } | |
245 | static void isp1761_pci_remove(struct pci_dev *dev) | |
246 | { | |
247 | struct usb_hcd *hcd; | |
248 | ||
249 | hcd = pci_get_drvdata(dev); | |
250 | ||
251 | usb_remove_hcd(hcd); | |
252 | iounmap(hcd->regs); | |
253 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | |
254 | usb_put_hcd(hcd); | |
255 | ||
256 | pci_disable_device(dev); | |
257 | ||
258 | iounmap(iobase); | |
259 | iounmap(chip_addr); | |
260 | ||
261 | release_mem_region(nxp_pci_io_base, iolength); | |
262 | release_mem_region(pci_mem_phy0, length); | |
263 | } | |
264 | ||
265 | static void isp1761_pci_shutdown(struct pci_dev *dev) | |
266 | { | |
267 | printk(KERN_ERR "ips1761_pci_shutdown\n"); | |
268 | } | |
269 | ||
270 | static const struct pci_device_id isp1760_plx [] = { { | |
271 | /* handle any USB 2.0 EHCI controller */ | |
272 | PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_OTHER << 8) | (0x06 << 16)), ~0), | |
273 | .driver_data = 0, | |
274 | }, | |
275 | { /* end: all zeroes */ } | |
276 | }; | |
277 | MODULE_DEVICE_TABLE(pci, isp1760_plx); | |
278 | ||
279 | static struct pci_driver isp1761_pci_driver = { | |
280 | .name = "isp1760", | |
281 | .id_table = isp1760_plx, | |
282 | .probe = isp1761_pci_probe, | |
283 | .remove = isp1761_pci_remove, | |
284 | .shutdown = isp1761_pci_shutdown, | |
285 | }; | |
286 | #endif | |
287 | ||
288 | static int __init isp1760_init(void) | |
289 | { | |
fe312e77 | 290 | int ret = -ENODEV; |
db11e47d SS |
291 | |
292 | init_kmem_once(); | |
293 | ||
294 | #ifdef CONFIG_USB_ISP1760_OF | |
295 | ret = of_register_platform_driver(&isp1760_of_driver); | |
296 | if (ret) { | |
297 | deinit_kmem_cache(); | |
298 | return ret; | |
299 | } | |
300 | #endif | |
301 | #ifdef CONFIG_USB_ISP1760_PCI | |
302 | ret = pci_register_driver(&isp1761_pci_driver); | |
303 | if (ret) | |
304 | goto unreg_of; | |
305 | #endif | |
306 | return ret; | |
307 | ||
308 | #ifdef CONFIG_USB_ISP1760_PCI | |
309 | unreg_of: | |
310 | #endif | |
311 | #ifdef CONFIG_USB_ISP1760_OF | |
312 | of_unregister_platform_driver(&isp1760_of_driver); | |
313 | #endif | |
314 | deinit_kmem_cache(); | |
315 | return ret; | |
316 | } | |
317 | module_init(isp1760_init); | |
318 | ||
319 | static void __exit isp1760_exit(void) | |
320 | { | |
321 | #ifdef CONFIG_USB_ISP1760_OF | |
322 | of_unregister_platform_driver(&isp1760_of_driver); | |
323 | #endif | |
324 | #ifdef CONFIG_USB_ISP1760_PCI | |
325 | pci_unregister_driver(&isp1761_pci_driver); | |
326 | #endif | |
327 | deinit_kmem_cache(); | |
328 | } | |
329 | module_exit(isp1760_exit); |