1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2db11e47dSSebastian Siewior /* 3db11e47dSSebastian Siewior * Glue code for the ISP1760 driver and bus 4db11e47dSSebastian Siewior * Currently there is support for 5db11e47dSSebastian Siewior * - OpenFirmware 6db11e47dSSebastian Siewior * - PCI 79da69c60SMichael Hennerich * - PDEV (generic platform device centralized driver model) 8db11e47dSSebastian Siewior * 9db11e47dSSebastian Siewior * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> 10db11e47dSSebastian Siewior * 11db11e47dSSebastian Siewior */ 12db11e47dSSebastian Siewior 13db11e47dSSebastian Siewior #include <linux/usb.h> 14db11e47dSSebastian Siewior #include <linux/io.h> 156eb0de82SPaul Gortmaker #include <linux/module.h> 16c3cc40ccSLaurent Pinchart #include <linux/of.h> 17f7e7aa58SCatalin Marinas #include <linux/platform_device.h> 18c3cc40ccSLaurent Pinchart #include <linux/slab.h> 199da69c60SMichael Hennerich #include <linux/usb/isp1760.h> 2027729aadSEric Lescouet #include <linux/usb/hcd.h> 21db11e47dSSebastian Siewior 224b1a577dSLaurent Pinchart #include "isp1760-core.h" 23e19c99e7SLaurent Pinchart #include "isp1760-regs.h" 24db11e47dSSebastian Siewior 252c93e790Syuan linyu #ifdef CONFIG_USB_PCI 26db11e47dSSebastian Siewior #include <linux/pci.h> 27db11e47dSSebastian Siewior #endif 28db11e47dSSebastian Siewior 292c93e790Syuan linyu #ifdef CONFIG_USB_PCI 30c770e001SLaurent Pinchart static int isp1761_pci_init(struct pci_dev *dev) 31db11e47dSSebastian Siewior { 32c770e001SLaurent Pinchart resource_size_t mem_start; 33c770e001SLaurent Pinchart resource_size_t mem_length; 346013bbbaSKarl Bongers u8 __iomem *iobase; 35c770e001SLaurent Pinchart u8 latency, limit; 36c770e001SLaurent Pinchart int retry_count; 37c770e001SLaurent Pinchart u32 reg_data; 38db11e47dSSebastian Siewior 39c770e001SLaurent Pinchart /* Grab the PLX PCI shared memory of the ISP 1761 we need */ 40c770e001SLaurent Pinchart mem_start = pci_resource_start(dev, 3); 41c770e001SLaurent Pinchart mem_length = pci_resource_len(dev, 3); 42c770e001SLaurent Pinchart if (mem_length < 0xffff) { 43c770e001SLaurent Pinchart printk(KERN_ERR "memory length for this resource is wrong\n"); 44c770e001SLaurent Pinchart return -ENOMEM; 45c770e001SLaurent Pinchart } 46db11e47dSSebastian Siewior 47c770e001SLaurent Pinchart if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) { 48c770e001SLaurent Pinchart printk(KERN_ERR "host controller already in use\n"); 49db11e47dSSebastian Siewior return -EBUSY; 50db11e47dSSebastian Siewior } 51db11e47dSSebastian Siewior 526013bbbaSKarl Bongers /* map available memory */ 53*4bdc0d67SChristoph Hellwig iobase = ioremap(mem_start, mem_length); 54c770e001SLaurent Pinchart if (!iobase) { 556013bbbaSKarl Bongers printk(KERN_ERR "Error ioremap failed\n"); 56c770e001SLaurent Pinchart release_mem_region(mem_start, mem_length); 57c770e001SLaurent Pinchart return -ENOMEM; 58db11e47dSSebastian Siewior } 59db11e47dSSebastian Siewior 60db11e47dSSebastian Siewior /* bad pci latencies can contribute to overruns */ 61db11e47dSSebastian Siewior pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency); 62db11e47dSSebastian Siewior if (latency) { 63db11e47dSSebastian Siewior pci_read_config_byte(dev, PCI_MAX_LAT, &limit); 64db11e47dSSebastian Siewior if (limit && limit < latency) 65db11e47dSSebastian Siewior pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit); 66db11e47dSSebastian Siewior } 67db11e47dSSebastian Siewior 68db11e47dSSebastian Siewior /* Try to check whether we can access Scratch Register of 69db11e47dSSebastian Siewior * Host Controller or not. The initial PCI access is retried until 70db11e47dSSebastian Siewior * local init for the PCI bridge is completed 71db11e47dSSebastian Siewior */ 72db11e47dSSebastian Siewior retry_count = 20; 73db11e47dSSebastian Siewior reg_data = 0; 74db11e47dSSebastian Siewior while ((reg_data != 0xFACE) && retry_count) { 75db11e47dSSebastian Siewior /*by default host is in 16bit mode, so 76db11e47dSSebastian Siewior * io operations at this stage must be 16 bit 77db11e47dSSebastian Siewior * */ 78c770e001SLaurent Pinchart writel(0xface, iobase + HC_SCRATCH_REG); 79db11e47dSSebastian Siewior udelay(100); 80c770e001SLaurent Pinchart reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff; 81db11e47dSSebastian Siewior retry_count--; 82db11e47dSSebastian Siewior } 83db11e47dSSebastian Siewior 84c770e001SLaurent Pinchart iounmap(iobase); 85c770e001SLaurent Pinchart release_mem_region(mem_start, mem_length); 866013bbbaSKarl Bongers 87db11e47dSSebastian Siewior /* Host Controller presence is detected by writing to scratch register 88db11e47dSSebastian Siewior * and reading back and checking the contents are same or not 89db11e47dSSebastian Siewior */ 90db11e47dSSebastian Siewior if (reg_data != 0xFACE) { 91802f389aSGreg Kroah-Hartman dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data); 92c770e001SLaurent Pinchart return -ENOMEM; 93db11e47dSSebastian Siewior } 94db11e47dSSebastian Siewior 95c770e001SLaurent Pinchart /* Grab the PLX PCI mem maped port start address we need */ 96c770e001SLaurent Pinchart mem_start = pci_resource_start(dev, 0); 97c770e001SLaurent Pinchart mem_length = pci_resource_len(dev, 0); 98c770e001SLaurent Pinchart 99c770e001SLaurent Pinchart if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) { 100c770e001SLaurent Pinchart printk(KERN_ERR "request region #1\n"); 101c770e001SLaurent Pinchart return -EBUSY; 102c770e001SLaurent Pinchart } 103c770e001SLaurent Pinchart 104*4bdc0d67SChristoph Hellwig iobase = ioremap(mem_start, mem_length); 105c770e001SLaurent Pinchart if (!iobase) { 106c770e001SLaurent Pinchart printk(KERN_ERR "ioremap #1\n"); 107c770e001SLaurent Pinchart release_mem_region(mem_start, mem_length); 108c770e001SLaurent Pinchart return -ENOMEM; 109c770e001SLaurent Pinchart } 110db11e47dSSebastian Siewior 1116013bbbaSKarl Bongers /* configure PLX PCI chip to pass interrupts */ 1126013bbbaSKarl Bongers #define PLX_INT_CSR_REG 0x68 1136013bbbaSKarl Bongers reg_data = readl(iobase + PLX_INT_CSR_REG); 1146013bbbaSKarl Bongers reg_data |= 0x900; 1156013bbbaSKarl Bongers writel(reg_data, iobase + PLX_INT_CSR_REG); 116db11e47dSSebastian Siewior 1176013bbbaSKarl Bongers /* done with PLX IO access */ 1186013bbbaSKarl Bongers iounmap(iobase); 119c770e001SLaurent Pinchart release_mem_region(mem_start, mem_length); 120c770e001SLaurent Pinchart 121c770e001SLaurent Pinchart return 0; 122c770e001SLaurent Pinchart } 123c770e001SLaurent Pinchart 124c770e001SLaurent Pinchart static int isp1761_pci_probe(struct pci_dev *dev, 125c770e001SLaurent Pinchart const struct pci_device_id *id) 126c770e001SLaurent Pinchart { 127c770e001SLaurent Pinchart unsigned int devflags = 0; 128c770e001SLaurent Pinchart int ret; 129c770e001SLaurent Pinchart 130c770e001SLaurent Pinchart if (!dev->irq) 131c770e001SLaurent Pinchart return -ENODEV; 132c770e001SLaurent Pinchart 133c770e001SLaurent Pinchart if (pci_enable_device(dev) < 0) 134c770e001SLaurent Pinchart return -ENODEV; 135c770e001SLaurent Pinchart 136c770e001SLaurent Pinchart ret = isp1761_pci_init(dev); 137c770e001SLaurent Pinchart if (ret < 0) 138c770e001SLaurent Pinchart goto error; 139c770e001SLaurent Pinchart 140c770e001SLaurent Pinchart pci_set_master(dev); 1416013bbbaSKarl Bongers 142667c45c2SLaurent Pinchart ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev, 143667c45c2SLaurent Pinchart devflags); 144c770e001SLaurent Pinchart if (ret < 0) 145c770e001SLaurent Pinchart goto error; 1466013bbbaSKarl Bongers 147c770e001SLaurent Pinchart return 0; 148c770e001SLaurent Pinchart 149c770e001SLaurent Pinchart error: 150c770e001SLaurent Pinchart pci_disable_device(dev); 151c770e001SLaurent Pinchart return ret; 152db11e47dSSebastian Siewior } 1536013bbbaSKarl Bongers 154db11e47dSSebastian Siewior static void isp1761_pci_remove(struct pci_dev *dev) 155db11e47dSSebastian Siewior { 15610c73f09SLaurent Pinchart isp1760_unregister(&dev->dev); 157db11e47dSSebastian Siewior 158db11e47dSSebastian Siewior pci_disable_device(dev); 159db11e47dSSebastian Siewior } 160db11e47dSSebastian Siewior 161db11e47dSSebastian Siewior static void isp1761_pci_shutdown(struct pci_dev *dev) 162db11e47dSSebastian Siewior { 163db11e47dSSebastian Siewior printk(KERN_ERR "ips1761_pci_shutdown\n"); 164db11e47dSSebastian Siewior } 165db11e47dSSebastian Siewior 1666c073568SSebastian Andrzej Siewior static const struct pci_device_id isp1760_plx[] = { 1676c073568SSebastian Andrzej Siewior { 1686c073568SSebastian Andrzej Siewior .class = PCI_CLASS_BRIDGE_OTHER << 8, 1696c073568SSebastian Andrzej Siewior .class_mask = ~0, 1706c073568SSebastian Andrzej Siewior .vendor = PCI_VENDOR_ID_PLX, 1716c073568SSebastian Andrzej Siewior .device = 0x5406, 1726c073568SSebastian Andrzej Siewior .subvendor = PCI_VENDOR_ID_PLX, 1736c073568SSebastian Andrzej Siewior .subdevice = 0x9054, 174db11e47dSSebastian Siewior }, 1756c073568SSebastian Andrzej Siewior { } 176db11e47dSSebastian Siewior }; 177db11e47dSSebastian Siewior MODULE_DEVICE_TABLE(pci, isp1760_plx); 178db11e47dSSebastian Siewior 179db11e47dSSebastian Siewior static struct pci_driver isp1761_pci_driver = { 180db11e47dSSebastian Siewior .name = "isp1760", 181db11e47dSSebastian Siewior .id_table = isp1760_plx, 182db11e47dSSebastian Siewior .probe = isp1761_pci_probe, 183db11e47dSSebastian Siewior .remove = isp1761_pci_remove, 184db11e47dSSebastian Siewior .shutdown = isp1761_pci_shutdown, 185db11e47dSSebastian Siewior }; 186db11e47dSSebastian Siewior #endif 187db11e47dSSebastian Siewior 18841ac7b3aSBill Pemberton static int isp1760_plat_probe(struct platform_device *pdev) 189f7e7aa58SCatalin Marinas { 190667c45c2SLaurent Pinchart unsigned long irqflags; 191c3cc40ccSLaurent Pinchart unsigned int devflags = 0; 192f7e7aa58SCatalin Marinas struct resource *mem_res; 193f7e7aa58SCatalin Marinas struct resource *irq_res; 194c3cc40ccSLaurent Pinchart int ret; 195f7e7aa58SCatalin Marinas 196f7e7aa58SCatalin Marinas mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197f7e7aa58SCatalin Marinas 198f7e7aa58SCatalin Marinas irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 199f7e7aa58SCatalin Marinas if (!irq_res) { 200a4e6a852SJoe Perches pr_warn("isp1760: IRQ resource not available\n"); 20110feee14SLaurent Pinchart return -ENODEV; 202f7e7aa58SCatalin Marinas } 203667c45c2SLaurent Pinchart irqflags = irq_res->flags & IRQF_TRIGGER_MASK; 204f7e7aa58SCatalin Marinas 205c3cc40ccSLaurent Pinchart if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { 206c3cc40ccSLaurent Pinchart struct device_node *dp = pdev->dev.of_node; 207c3cc40ccSLaurent Pinchart u32 bus_width = 0; 208c3cc40ccSLaurent Pinchart 209c3cc40ccSLaurent Pinchart if (of_device_is_compatible(dp, "nxp,usb-isp1761")) 2109da69c60SMichael Hennerich devflags |= ISP1760_FLAG_ISP1761; 211c3cc40ccSLaurent Pinchart 212c3cc40ccSLaurent Pinchart /* Some systems wire up only 16 of the 32 data lines */ 213c3cc40ccSLaurent Pinchart of_property_read_u32(dp, "bus-width", &bus_width); 214c3cc40ccSLaurent Pinchart if (bus_width == 16) 2159da69c60SMichael Hennerich devflags |= ISP1760_FLAG_BUS_WIDTH_16; 216c3cc40ccSLaurent Pinchart 217c3cc40ccSLaurent Pinchart if (of_property_read_bool(dp, "port1-otg")) 2189da69c60SMichael Hennerich devflags |= ISP1760_FLAG_OTG_EN; 219c3cc40ccSLaurent Pinchart 220c3cc40ccSLaurent Pinchart if (of_property_read_bool(dp, "analog-oc")) 2219da69c60SMichael Hennerich devflags |= ISP1760_FLAG_ANALOG_OC; 222c3cc40ccSLaurent Pinchart 223c3cc40ccSLaurent Pinchart if (of_property_read_bool(dp, "dack-polarity")) 2249da69c60SMichael Hennerich devflags |= ISP1760_FLAG_DACK_POL_HIGH; 225c3cc40ccSLaurent Pinchart 226c3cc40ccSLaurent Pinchart if (of_property_read_bool(dp, "dreq-polarity")) 227c3cc40ccSLaurent Pinchart devflags |= ISP1760_FLAG_DREQ_POL_HIGH; 228c3cc40ccSLaurent Pinchart } else if (dev_get_platdata(&pdev->dev)) { 229c3cc40ccSLaurent Pinchart struct isp1760_platform_data *pdata = 230c3cc40ccSLaurent Pinchart dev_get_platdata(&pdev->dev); 231c3cc40ccSLaurent Pinchart 232c3cc40ccSLaurent Pinchart if (pdata->is_isp1761) 233c3cc40ccSLaurent Pinchart devflags |= ISP1760_FLAG_ISP1761; 234c3cc40ccSLaurent Pinchart if (pdata->bus_width_16) 235c3cc40ccSLaurent Pinchart devflags |= ISP1760_FLAG_BUS_WIDTH_16; 236c3cc40ccSLaurent Pinchart if (pdata->port1_otg) 237c3cc40ccSLaurent Pinchart devflags |= ISP1760_FLAG_OTG_EN; 238c3cc40ccSLaurent Pinchart if (pdata->analog_oc) 239c3cc40ccSLaurent Pinchart devflags |= ISP1760_FLAG_ANALOG_OC; 240c3cc40ccSLaurent Pinchart if (pdata->dack_polarity_high) 241c3cc40ccSLaurent Pinchart devflags |= ISP1760_FLAG_DACK_POL_HIGH; 242c3cc40ccSLaurent Pinchart if (pdata->dreq_polarity_high) 2439da69c60SMichael Hennerich devflags |= ISP1760_FLAG_DREQ_POL_HIGH; 2449da69c60SMichael Hennerich } 2459da69c60SMichael Hennerich 2464942e00eSLaurent Pinchart ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev, 2474942e00eSLaurent Pinchart devflags); 248f0bdbb0eSLaurent Pinchart if (ret < 0) 24910feee14SLaurent Pinchart return ret; 250f7e7aa58SCatalin Marinas 251f7e7aa58SCatalin Marinas pr_info("ISP1760 USB device initialised\n"); 252c3cc40ccSLaurent Pinchart return 0; 253f7e7aa58SCatalin Marinas } 254f7e7aa58SCatalin Marinas 255fb4e98abSBill Pemberton static int isp1760_plat_remove(struct platform_device *pdev) 256f7e7aa58SCatalin Marinas { 25710c73f09SLaurent Pinchart isp1760_unregister(&pdev->dev); 258310e9e33SMichael Grzeschik 259f7e7aa58SCatalin Marinas return 0; 260f7e7aa58SCatalin Marinas } 261f7e7aa58SCatalin Marinas 262c3cc40ccSLaurent Pinchart #ifdef CONFIG_OF 263c3cc40ccSLaurent Pinchart static const struct of_device_id isp1760_of_match[] = { 264c3cc40ccSLaurent Pinchart { .compatible = "nxp,usb-isp1760", }, 265c3cc40ccSLaurent Pinchart { .compatible = "nxp,usb-isp1761", }, 266c3cc40ccSLaurent Pinchart { }, 267c3cc40ccSLaurent Pinchart }; 268c3cc40ccSLaurent Pinchart MODULE_DEVICE_TABLE(of, isp1760_of_match); 269c3cc40ccSLaurent Pinchart #endif 270c3cc40ccSLaurent Pinchart 271f7e7aa58SCatalin Marinas static struct platform_driver isp1760_plat_driver = { 272f7e7aa58SCatalin Marinas .probe = isp1760_plat_probe, 2737690417dSBill Pemberton .remove = isp1760_plat_remove, 274f7e7aa58SCatalin Marinas .driver = { 275f7e7aa58SCatalin Marinas .name = "isp1760", 276c3cc40ccSLaurent Pinchart .of_match_table = of_match_ptr(isp1760_of_match), 277f7e7aa58SCatalin Marinas }, 278f7e7aa58SCatalin Marinas }; 279f7e7aa58SCatalin Marinas 280db11e47dSSebastian Siewior static int __init isp1760_init(void) 281db11e47dSSebastian Siewior { 282f7e7aa58SCatalin Marinas int ret, any_ret = -ENODEV; 283db11e47dSSebastian Siewior 2845a6356acSLaurent Pinchart isp1760_init_kmem_once(); 285db11e47dSSebastian Siewior 286f7e7aa58SCatalin Marinas ret = platform_driver_register(&isp1760_plat_driver); 287f7e7aa58SCatalin Marinas if (!ret) 288f7e7aa58SCatalin Marinas any_ret = 0; 2892c93e790Syuan linyu #ifdef CONFIG_USB_PCI 290db11e47dSSebastian Siewior ret = pci_register_driver(&isp1761_pci_driver); 291f7e7aa58SCatalin Marinas if (!ret) 292f7e7aa58SCatalin Marinas any_ret = 0; 293db11e47dSSebastian Siewior #endif 294db11e47dSSebastian Siewior 295f7e7aa58SCatalin Marinas if (any_ret) 2965a6356acSLaurent Pinchart isp1760_deinit_kmem_cache(); 297f7e7aa58SCatalin Marinas return any_ret; 298db11e47dSSebastian Siewior } 299db11e47dSSebastian Siewior module_init(isp1760_init); 300db11e47dSSebastian Siewior 301db11e47dSSebastian Siewior static void __exit isp1760_exit(void) 302db11e47dSSebastian Siewior { 303f7e7aa58SCatalin Marinas platform_driver_unregister(&isp1760_plat_driver); 3042c93e790Syuan linyu #ifdef CONFIG_USB_PCI 305db11e47dSSebastian Siewior pci_unregister_driver(&isp1761_pci_driver); 306db11e47dSSebastian Siewior #endif 3075a6356acSLaurent Pinchart isp1760_deinit_kmem_cache(); 308db11e47dSSebastian Siewior } 309db11e47dSSebastian Siewior module_exit(isp1760_exit); 310