blob: 2c7fc830c9e48b39080188a295d443cffa11fa38 [file] [log] [blame]
Sebastian Siewiordb11e472008-04-24 00:37:04 +02001/*
2 * Glue code for the ISP1760 driver and bus
3 * Currently there is support for
4 * - OpenFirmware
5 * - PCI
Michael Hennerich9da69c62009-07-15 23:22:54 -04006 * - PDEV (generic platform device centralized driver model)
Sebastian Siewiordb11e472008-04-24 00:37:04 +02007 *
8 * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
9 *
10 */
11
12#include <linux/usb.h>
13#include <linux/io.h>
Catalin Marinasf7e7aa52009-02-10 16:55:51 +000014#include <linux/platform_device.h>
Michael Hennerich9da69c62009-07-15 23:22:54 -040015#include <linux/usb/isp1760.h>
Eric Lescouet27729aa2010-04-24 23:21:52 +020016#include <linux/usb/hcd.h>
Sebastian Siewiordb11e472008-04-24 00:37:04 +020017
Sebastian Siewiordb11e472008-04-24 00:37:04 +020018#include "isp1760-hcd.h"
19
Joachim Foerster8f5d6212011-10-10 18:06:54 +020020#ifdef CONFIG_OF
Joachim Foerster3a7655f2011-10-19 14:18:41 +020021#include <linux/slab.h>
Sebastian Siewiordb11e472008-04-24 00:37:04 +020022#include <linux/of.h>
23#include <linux/of_platform.h>
Joachim Foerster8f5d6212011-10-10 18:06:54 +020024#include <linux/of_address.h>
25#include <linux/of_irq.h>
Joachim Foerster3a7655f2011-10-19 14:18:41 +020026#include <linux/of_gpio.h>
Sebastian Siewiordb11e472008-04-24 00:37:04 +020027#endif
28
Sebastian Andrzej Siewiorff30bf12008-11-02 15:25:42 +010029#ifdef CONFIG_PCI
Sebastian Siewiordb11e472008-04-24 00:37:04 +020030#include <linux/pci.h>
31#endif
32
Joachim Foerster8f5d6212011-10-10 18:06:54 +020033#ifdef CONFIG_OF
Joachim Foerster3a7655f2011-10-19 14:18:41 +020034struct isp1760 {
35 struct usb_hcd *hcd;
36 int rst_gpio;
37};
38
Grant Likelyd35fb642011-02-22 21:08:34 -070039static int of_isp1760_probe(struct platform_device *dev)
Sebastian Siewiordb11e472008-04-24 00:37:04 +020040{
Joachim Foerster3a7655f2011-10-19 14:18:41 +020041 struct isp1760 *drvdata;
Grant Likely61c7a082010-04-13 16:12:29 -070042 struct device_node *dp = dev->dev.of_node;
Sebastian Siewiordb11e472008-04-24 00:37:04 +020043 struct resource *res;
44 struct resource memory;
45 struct of_irq oirq;
46 int virq;
Tobias Klausere07afd32010-05-05 11:18:41 +020047 resource_size_t res_len;
Sebastian Siewiordb11e472008-04-24 00:37:04 +020048 int ret;
Nate Case3faefc82008-06-17 11:11:38 -050049 const unsigned int *prop;
50 unsigned int devflags = 0;
Joachim Foerster3a7655f2011-10-19 14:18:41 +020051 enum of_gpio_flags gpio_flags;
52
53 drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
54 if (!drvdata)
55 return -ENOMEM;
Sebastian Siewiordb11e472008-04-24 00:37:04 +020056
57 ret = of_address_to_resource(dp, 0, &memory);
58 if (ret)
59 return -ENXIO;
60
Tobias Klausere07afd32010-05-05 11:18:41 +020061 res_len = resource_size(&memory);
62
63 res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
Sebastian Siewiordb11e472008-04-24 00:37:04 +020064 if (!res)
65 return -EBUSY;
66
Sebastian Siewiordb11e472008-04-24 00:37:04 +020067 if (of_irq_map_one(dp, 0, &oirq)) {
68 ret = -ENODEV;
69 goto release_reg;
70 }
71
72 virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
73 oirq.size);
74
Nate Case3faefc82008-06-17 11:11:38 -050075 if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
76 devflags |= ISP1760_FLAG_ISP1761;
77
Nate Case3faefc82008-06-17 11:11:38 -050078 /* Some systems wire up only 16 of the 32 data lines */
79 prop = of_get_property(dp, "bus-width", NULL);
80 if (prop && *prop == 16)
81 devflags |= ISP1760_FLAG_BUS_WIDTH_16;
82
83 if (of_get_property(dp, "port1-otg", NULL) != NULL)
84 devflags |= ISP1760_FLAG_OTG_EN;
85
86 if (of_get_property(dp, "analog-oc", NULL) != NULL)
87 devflags |= ISP1760_FLAG_ANALOG_OC;
88
89 if (of_get_property(dp, "dack-polarity", NULL) != NULL)
90 devflags |= ISP1760_FLAG_DACK_POL_HIGH;
91
92 if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
93 devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
94
Joachim Foerster3a7655f2011-10-19 14:18:41 +020095 drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags);
96 if (gpio_is_valid(drvdata->rst_gpio)) {
97 ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev));
98 if (!ret) {
99 if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) {
100 devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH;
101 gpio_direction_output(drvdata->rst_gpio, 0);
102 } else {
103 gpio_direction_output(drvdata->rst_gpio, 1);
104 }
105 } else {
106 drvdata->rst_gpio = ret;
107 }
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200108 }
109
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200110 drvdata->hcd = isp1760_register(memory.start, res_len, virq,
111 IRQF_SHARED, drvdata->rst_gpio,
112 &dev->dev, dev_name(&dev->dev),
113 devflags);
114 if (IS_ERR(drvdata->hcd)) {
115 ret = PTR_ERR(drvdata->hcd);
116 goto free_gpio;
117 }
118
119 dev_set_drvdata(&dev->dev, drvdata);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200120 return ret;
121
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200122free_gpio:
123 if (gpio_is_valid(drvdata->rst_gpio))
124 gpio_free(drvdata->rst_gpio);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200125release_reg:
Tobias Klausere07afd32010-05-05 11:18:41 +0200126 release_mem_region(memory.start, res_len);
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200127 kfree(drvdata);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200128 return ret;
129}
130
Grant Likely2dc11582010-08-06 09:25:50 -0600131static int of_isp1760_remove(struct platform_device *dev)
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200132{
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200133 struct isp1760 *drvdata = dev_get_drvdata(&dev->dev);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200134
135 dev_set_drvdata(&dev->dev, NULL);
136
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200137 usb_remove_hcd(drvdata->hcd);
138 iounmap(drvdata->hcd->regs);
139 release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len);
140 usb_put_hcd(drvdata->hcd);
141
142 if (gpio_is_valid(drvdata->rst_gpio))
143 gpio_free(drvdata->rst_gpio);
144
145 kfree(drvdata);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200146 return 0;
147}
148
Németh Mártonc4386ad2010-01-10 15:35:03 +0100149static const struct of_device_id of_isp1760_match[] = {
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200150 {
151 .compatible = "nxp,usb-isp1760",
152 },
Nate Case3faefc82008-06-17 11:11:38 -0500153 {
154 .compatible = "nxp,usb-isp1761",
155 },
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200156 { },
157};
158MODULE_DEVICE_TABLE(of, of_isp1760_match);
159
Grant Likelyd35fb642011-02-22 21:08:34 -0700160static struct platform_driver isp1760_of_driver = {
Grant Likely40182942010-04-13 16:13:02 -0700161 .driver = {
162 .name = "nxp-isp1760",
163 .owner = THIS_MODULE,
164 .of_match_table = of_isp1760_match,
165 },
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200166 .probe = of_isp1760_probe,
167 .remove = of_isp1760_remove,
168};
169#endif
170
Sebastian Andrzej Siewiorff30bf12008-11-02 15:25:42 +0100171#ifdef CONFIG_PCI
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200172static int __devinit isp1761_pci_probe(struct pci_dev *dev,
173 const struct pci_device_id *id)
174{
175 u8 latency, limit;
176 __u32 reg_data;
177 int retry_count;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200178 struct usb_hcd *hcd;
Nate Case3faefc82008-06-17 11:11:38 -0500179 unsigned int devflags = 0;
Karl Bongers6013bbb2008-12-01 11:47:40 +0100180 int ret_status = 0;
181
182 resource_size_t pci_mem_phy0;
183 resource_size_t memlength;
184
185 u8 __iomem *chip_addr;
186 u8 __iomem *iobase;
187 resource_size_t nxp_pci_io_base;
188 resource_size_t iolength;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200189
190 if (usb_disabled())
191 return -ENODEV;
192
193 if (pci_enable_device(dev) < 0)
194 return -ENODEV;
195
196 if (!dev->irq)
197 return -ENODEV;
198
199 /* Grab the PLX PCI mem maped port start address we need */
200 nxp_pci_io_base = pci_resource_start(dev, 0);
201 iolength = pci_resource_len(dev, 0);
202
203 if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) {
204 printk(KERN_ERR "request region #1\n");
205 return -EBUSY;
206 }
207
208 iobase = ioremap_nocache(nxp_pci_io_base, iolength);
209 if (!iobase) {
210 printk(KERN_ERR "ioremap #1\n");
Karl Bongers6013bbb2008-12-01 11:47:40 +0100211 ret_status = -ENOMEM;
212 goto cleanup1;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200213 }
214 /* Grab the PLX PCI shared memory of the ISP 1761 we need */
215 pci_mem_phy0 = pci_resource_start(dev, 3);
Karl Bongers6013bbb2008-12-01 11:47:40 +0100216 memlength = pci_resource_len(dev, 3);
217 if (memlength < 0xffff) {
218 printk(KERN_ERR "memory length for this resource is wrong\n");
219 ret_status = -ENOMEM;
220 goto cleanup2;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200221 }
222
Karl Bongers6013bbb2008-12-01 11:47:40 +0100223 if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200224 printk(KERN_ERR "host controller already in use\n");
Karl Bongers6013bbb2008-12-01 11:47:40 +0100225 ret_status = -EBUSY;
226 goto cleanup2;
227 }
228
229 /* map available memory */
230 chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
231 if (!chip_addr) {
232 printk(KERN_ERR "Error ioremap failed\n");
233 ret_status = -ENOMEM;
234 goto cleanup3;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200235 }
236
237 /* bad pci latencies can contribute to overruns */
238 pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
239 if (latency) {
240 pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
241 if (limit && limit < latency)
242 pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
243 }
244
245 /* Try to check whether we can access Scratch Register of
246 * Host Controller or not. The initial PCI access is retried until
247 * local init for the PCI bridge is completed
248 */
249 retry_count = 20;
250 reg_data = 0;
251 while ((reg_data != 0xFACE) && retry_count) {
252 /*by default host is in 16bit mode, so
253 * io operations at this stage must be 16 bit
254 * */
255 writel(0xface, chip_addr + HC_SCRATCH_REG);
256 udelay(100);
Karl Bongers6013bbb2008-12-01 11:47:40 +0100257 reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200258 retry_count--;
259 }
260
Karl Bongers6013bbb2008-12-01 11:47:40 +0100261 iounmap(chip_addr);
262
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200263 /* Host Controller presence is detected by writing to scratch register
264 * and reading back and checking the contents are same or not
265 */
266 if (reg_data != 0xFACE) {
Greg Kroah-Hartman802f3892008-08-14 09:37:34 -0700267 dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
Karl Bongers6013bbb2008-12-01 11:47:40 +0100268 ret_status = -ENOMEM;
269 goto cleanup3;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200270 }
271
272 pci_set_master(dev);
273
Karl Bongers6013bbb2008-12-01 11:47:40 +0100274 /* configure PLX PCI chip to pass interrupts */
275#define PLX_INT_CSR_REG 0x68
276 reg_data = readl(iobase + PLX_INT_CSR_REG);
277 reg_data |= 0x900;
278 writel(reg_data, iobase + PLX_INT_CSR_REG);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200279
280 dev->dev.dma_mask = NULL;
Karl Bongers6013bbb2008-12-01 11:47:40 +0100281 hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200282 IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev),
Nate Case3faefc82008-06-17 11:11:38 -0500283 devflags);
Karl Bongers6013bbb2008-12-01 11:47:40 +0100284 if (IS_ERR(hcd)) {
285 ret_status = -ENODEV;
286 goto cleanup3;
Julien Brunelce5dee52008-09-24 18:00:36 +0200287 }
Karl Bongers6013bbb2008-12-01 11:47:40 +0100288
289 /* done with PLX IO access */
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200290 iounmap(iobase);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200291 release_mem_region(nxp_pci_io_base, iolength);
Karl Bongers6013bbb2008-12-01 11:47:40 +0100292
293 pci_set_drvdata(dev, hcd);
294 return 0;
295
296cleanup3:
297 release_mem_region(pci_mem_phy0, memlength);
298cleanup2:
299 iounmap(iobase);
300cleanup1:
301 release_mem_region(nxp_pci_io_base, iolength);
302 return ret_status;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200303}
Karl Bongers6013bbb2008-12-01 11:47:40 +0100304
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200305static void isp1761_pci_remove(struct pci_dev *dev)
306{
307 struct usb_hcd *hcd;
308
309 hcd = pci_get_drvdata(dev);
310
311 usb_remove_hcd(hcd);
312 iounmap(hcd->regs);
313 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
314 usb_put_hcd(hcd);
315
316 pci_disable_device(dev);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200317}
318
319static void isp1761_pci_shutdown(struct pci_dev *dev)
320{
321 printk(KERN_ERR "ips1761_pci_shutdown\n");
322}
323
Sebastian Andrzej Siewior6c073562008-11-30 16:50:04 +0100324static const struct pci_device_id isp1760_plx [] = {
325 {
326 .class = PCI_CLASS_BRIDGE_OTHER << 8,
327 .class_mask = ~0,
328 .vendor = PCI_VENDOR_ID_PLX,
329 .device = 0x5406,
330 .subvendor = PCI_VENDOR_ID_PLX,
331 .subdevice = 0x9054,
332 },
333 { }
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200334};
335MODULE_DEVICE_TABLE(pci, isp1760_plx);
336
337static struct pci_driver isp1761_pci_driver = {
338 .name = "isp1760",
339 .id_table = isp1760_plx,
340 .probe = isp1761_pci_probe,
341 .remove = isp1761_pci_remove,
342 .shutdown = isp1761_pci_shutdown,
343};
344#endif
345
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000346static int __devinit isp1760_plat_probe(struct platform_device *pdev)
347{
348 int ret = 0;
349 struct usb_hcd *hcd;
350 struct resource *mem_res;
351 struct resource *irq_res;
352 resource_size_t mem_size;
Michael Hennerich9da69c62009-07-15 23:22:54 -0400353 struct isp1760_platform_data *priv = pdev->dev.platform_data;
354 unsigned int devflags = 0;
Yong Zhangb5dd18d2011-09-07 16:10:52 +0800355 unsigned long irqflags = IRQF_SHARED;
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000356
357 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
358 if (!mem_res) {
359 pr_warning("isp1760: Memory resource not available\n");
360 ret = -ENODEV;
361 goto out;
362 }
363 mem_size = resource_size(mem_res);
364 if (!request_mem_region(mem_res->start, mem_size, "isp1760")) {
365 pr_warning("isp1760: Cannot reserve the memory resource\n");
366 ret = -EBUSY;
367 goto out;
368 }
369
370 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
371 if (!irq_res) {
372 pr_warning("isp1760: IRQ resource not available\n");
373 return -ENODEV;
374 }
375 irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;
376
Michael Hennerich9da69c62009-07-15 23:22:54 -0400377 if (priv) {
378 if (priv->is_isp1761)
379 devflags |= ISP1760_FLAG_ISP1761;
380 if (priv->bus_width_16)
381 devflags |= ISP1760_FLAG_BUS_WIDTH_16;
382 if (priv->port1_otg)
383 devflags |= ISP1760_FLAG_OTG_EN;
384 if (priv->analog_oc)
385 devflags |= ISP1760_FLAG_ANALOG_OC;
386 if (priv->dack_polarity_high)
387 devflags |= ISP1760_FLAG_DACK_POL_HIGH;
388 if (priv->dreq_polarity_high)
389 devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
390 }
391
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000392 hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
Joachim Foerster3a7655f2011-10-19 14:18:41 +0200393 irqflags, -ENOENT,
394 &pdev->dev, dev_name(&pdev->dev), devflags);
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000395 if (IS_ERR(hcd)) {
396 pr_warning("isp1760: Failed to register the HCD device\n");
397 ret = -ENODEV;
398 goto cleanup;
399 }
400
401 pr_info("ISP1760 USB device initialised\n");
402 return ret;
403
404cleanup:
405 release_mem_region(mem_res->start, mem_size);
406out:
407 return ret;
408}
409
410static int __devexit isp1760_plat_remove(struct platform_device *pdev)
411{
412 struct resource *mem_res;
413 resource_size_t mem_size;
414
415 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416 mem_size = resource_size(mem_res);
417 release_mem_region(mem_res->start, mem_size);
418
419 return 0;
420}
421
422static struct platform_driver isp1760_plat_driver = {
423 .probe = isp1760_plat_probe,
Mike Frysinger4198e4f2009-06-11 21:59:00 -0400424 .remove = __devexit_p(isp1760_plat_remove),
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000425 .driver = {
426 .name = "isp1760",
427 },
428};
429
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200430static int __init isp1760_init(void)
431{
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000432 int ret, any_ret = -ENODEV;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200433
434 init_kmem_once();
435
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000436 ret = platform_driver_register(&isp1760_plat_driver);
437 if (!ret)
438 any_ret = 0;
Joachim Foerster8f5d6212011-10-10 18:06:54 +0200439#ifdef CONFIG_OF
Grant Likelyd35fb642011-02-22 21:08:34 -0700440 ret = platform_driver_register(&isp1760_of_driver);
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000441 if (!ret)
442 any_ret = 0;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200443#endif
Sebastian Andrzej Siewiorff30bf12008-11-02 15:25:42 +0100444#ifdef CONFIG_PCI
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200445 ret = pci_register_driver(&isp1761_pci_driver);
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000446 if (!ret)
447 any_ret = 0;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200448#endif
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200449
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000450 if (any_ret)
451 deinit_kmem_cache();
452 return any_ret;
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200453}
454module_init(isp1760_init);
455
456static void __exit isp1760_exit(void)
457{
Catalin Marinasf7e7aa52009-02-10 16:55:51 +0000458 platform_driver_unregister(&isp1760_plat_driver);
Joachim Foerster8f5d6212011-10-10 18:06:54 +0200459#ifdef CONFIG_OF
Grant Likelyd35fb642011-02-22 21:08:34 -0700460 platform_driver_unregister(&isp1760_of_driver);
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200461#endif
Sebastian Andrzej Siewiorff30bf12008-11-02 15:25:42 +0100462#ifdef CONFIG_PCI
Sebastian Siewiordb11e472008-04-24 00:37:04 +0200463 pci_unregister_driver(&isp1761_pci_driver);
464#endif
465 deinit_kmem_cache();
466}
467module_exit(isp1760_exit);