18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Glue code for the ISP1760 driver and bus 48c2ecf20Sopenharmony_ci * Currently there is support for 58c2ecf20Sopenharmony_ci * - OpenFirmware 68c2ecf20Sopenharmony_ci * - PCI 78c2ecf20Sopenharmony_ci * - PDEV (generic platform device centralized driver model) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/usb.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/usb/isp1760.h> 208c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "isp1760-core.h" 238c2ecf20Sopenharmony_ci#include "isp1760-regs.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PCI 268c2ecf20Sopenharmony_ci#include <linux/pci.h> 278c2ecf20Sopenharmony_ci#endif 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PCI 308c2ecf20Sopenharmony_cistatic int isp1761_pci_init(struct pci_dev *dev) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci resource_size_t mem_start; 338c2ecf20Sopenharmony_ci resource_size_t mem_length; 348c2ecf20Sopenharmony_ci u8 __iomem *iobase; 358c2ecf20Sopenharmony_ci u8 latency, limit; 368c2ecf20Sopenharmony_ci int retry_count; 378c2ecf20Sopenharmony_ci u32 reg_data; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* Grab the PLX PCI shared memory of the ISP 1761 we need */ 408c2ecf20Sopenharmony_ci mem_start = pci_resource_start(dev, 3); 418c2ecf20Sopenharmony_ci mem_length = pci_resource_len(dev, 3); 428c2ecf20Sopenharmony_ci if (mem_length < 0xffff) { 438c2ecf20Sopenharmony_ci printk(KERN_ERR "memory length for this resource is wrong\n"); 448c2ecf20Sopenharmony_ci return -ENOMEM; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) { 488c2ecf20Sopenharmony_ci printk(KERN_ERR "host controller already in use\n"); 498c2ecf20Sopenharmony_ci return -EBUSY; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* map available memory */ 538c2ecf20Sopenharmony_ci iobase = ioremap(mem_start, mem_length); 548c2ecf20Sopenharmony_ci if (!iobase) { 558c2ecf20Sopenharmony_ci printk(KERN_ERR "Error ioremap failed\n"); 568c2ecf20Sopenharmony_ci release_mem_region(mem_start, mem_length); 578c2ecf20Sopenharmony_ci return -ENOMEM; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* bad pci latencies can contribute to overruns */ 618c2ecf20Sopenharmony_ci pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency); 628c2ecf20Sopenharmony_ci if (latency) { 638c2ecf20Sopenharmony_ci pci_read_config_byte(dev, PCI_MAX_LAT, &limit); 648c2ecf20Sopenharmony_ci if (limit && limit < latency) 658c2ecf20Sopenharmony_ci pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* Try to check whether we can access Scratch Register of 698c2ecf20Sopenharmony_ci * Host Controller or not. The initial PCI access is retried until 708c2ecf20Sopenharmony_ci * local init for the PCI bridge is completed 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci retry_count = 20; 738c2ecf20Sopenharmony_ci reg_data = 0; 748c2ecf20Sopenharmony_ci while ((reg_data != 0xFACE) && retry_count) { 758c2ecf20Sopenharmony_ci /*by default host is in 16bit mode, so 768c2ecf20Sopenharmony_ci * io operations at this stage must be 16 bit 778c2ecf20Sopenharmony_ci * */ 788c2ecf20Sopenharmony_ci writel(0xface, iobase + HC_SCRATCH_REG); 798c2ecf20Sopenharmony_ci udelay(100); 808c2ecf20Sopenharmony_ci reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff; 818c2ecf20Sopenharmony_ci retry_count--; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci iounmap(iobase); 858c2ecf20Sopenharmony_ci release_mem_region(mem_start, mem_length); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Host Controller presence is detected by writing to scratch register 888c2ecf20Sopenharmony_ci * and reading back and checking the contents are same or not 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (reg_data != 0xFACE) { 918c2ecf20Sopenharmony_ci dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data); 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Grab the PLX PCI mem maped port start address we need */ 968c2ecf20Sopenharmony_ci mem_start = pci_resource_start(dev, 0); 978c2ecf20Sopenharmony_ci mem_length = pci_resource_len(dev, 0); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) { 1008c2ecf20Sopenharmony_ci printk(KERN_ERR "request region #1\n"); 1018c2ecf20Sopenharmony_ci return -EBUSY; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci iobase = ioremap(mem_start, mem_length); 1058c2ecf20Sopenharmony_ci if (!iobase) { 1068c2ecf20Sopenharmony_ci printk(KERN_ERR "ioremap #1\n"); 1078c2ecf20Sopenharmony_ci release_mem_region(mem_start, mem_length); 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* configure PLX PCI chip to pass interrupts */ 1128c2ecf20Sopenharmony_ci#define PLX_INT_CSR_REG 0x68 1138c2ecf20Sopenharmony_ci reg_data = readl(iobase + PLX_INT_CSR_REG); 1148c2ecf20Sopenharmony_ci reg_data |= 0x900; 1158c2ecf20Sopenharmony_ci writel(reg_data, iobase + PLX_INT_CSR_REG); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* done with PLX IO access */ 1188c2ecf20Sopenharmony_ci iounmap(iobase); 1198c2ecf20Sopenharmony_ci release_mem_region(mem_start, mem_length); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int isp1761_pci_probe(struct pci_dev *dev, 1258c2ecf20Sopenharmony_ci const struct pci_device_id *id) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci unsigned int devflags = 0; 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!dev->irq) 1318c2ecf20Sopenharmony_ci return -ENODEV; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (pci_enable_device(dev) < 0) 1348c2ecf20Sopenharmony_ci return -ENODEV; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = isp1761_pci_init(dev); 1378c2ecf20Sopenharmony_ci if (ret < 0) 1388c2ecf20Sopenharmony_ci goto error; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pci_set_master(dev); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev, 1438c2ecf20Sopenharmony_ci devflags); 1448c2ecf20Sopenharmony_ci if (ret < 0) 1458c2ecf20Sopenharmony_ci goto error; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cierror: 1508c2ecf20Sopenharmony_ci pci_disable_device(dev); 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void isp1761_pci_remove(struct pci_dev *dev) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci isp1760_unregister(&dev->dev); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci pci_disable_device(dev); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void isp1761_pci_shutdown(struct pci_dev *dev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci printk(KERN_ERR "ips1761_pci_shutdown\n"); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct pci_device_id isp1760_plx[] = { 1678c2ecf20Sopenharmony_ci { 1688c2ecf20Sopenharmony_ci .class = PCI_CLASS_BRIDGE_OTHER << 8, 1698c2ecf20Sopenharmony_ci .class_mask = ~0, 1708c2ecf20Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 1718c2ecf20Sopenharmony_ci .device = 0x5406, 1728c2ecf20Sopenharmony_ci .subvendor = PCI_VENDOR_ID_PLX, 1738c2ecf20Sopenharmony_ci .subdevice = 0x9054, 1748c2ecf20Sopenharmony_ci }, 1758c2ecf20Sopenharmony_ci { } 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, isp1760_plx); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic struct pci_driver isp1761_pci_driver = { 1808c2ecf20Sopenharmony_ci .name = "isp1760", 1818c2ecf20Sopenharmony_ci .id_table = isp1760_plx, 1828c2ecf20Sopenharmony_ci .probe = isp1761_pci_probe, 1838c2ecf20Sopenharmony_ci .remove = isp1761_pci_remove, 1848c2ecf20Sopenharmony_ci .shutdown = isp1761_pci_shutdown, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci#endif 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int isp1760_plat_probe(struct platform_device *pdev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned long irqflags; 1918c2ecf20Sopenharmony_ci unsigned int devflags = 0; 1928c2ecf20Sopenharmony_ci struct resource *mem_res; 1938c2ecf20Sopenharmony_ci struct resource *irq_res; 1948c2ecf20Sopenharmony_ci int ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 1998c2ecf20Sopenharmony_ci if (!irq_res) { 2008c2ecf20Sopenharmony_ci pr_warn("isp1760: IRQ resource not available\n"); 2018c2ecf20Sopenharmony_ci return -ENODEV; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci irqflags = irq_res->flags & IRQF_TRIGGER_MASK; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { 2068c2ecf20Sopenharmony_ci struct device_node *dp = pdev->dev.of_node; 2078c2ecf20Sopenharmony_ci u32 bus_width = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (of_device_is_compatible(dp, "nxp,usb-isp1761")) 2108c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_ISP1761; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Some systems wire up only 16 of the 32 data lines */ 2138c2ecf20Sopenharmony_ci of_property_read_u32(dp, "bus-width", &bus_width); 2148c2ecf20Sopenharmony_ci if (bus_width == 16) 2158c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_BUS_WIDTH_16; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (of_property_read_bool(dp, "port1-otg")) 2188c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_OTG_EN; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (of_property_read_bool(dp, "analog-oc")) 2218c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_ANALOG_OC; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (of_property_read_bool(dp, "dack-polarity")) 2248c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_DACK_POL_HIGH; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (of_property_read_bool(dp, "dreq-polarity")) 2278c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_DREQ_POL_HIGH; 2288c2ecf20Sopenharmony_ci } else if (dev_get_platdata(&pdev->dev)) { 2298c2ecf20Sopenharmony_ci struct isp1760_platform_data *pdata = 2308c2ecf20Sopenharmony_ci dev_get_platdata(&pdev->dev); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (pdata->is_isp1761) 2338c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_ISP1761; 2348c2ecf20Sopenharmony_ci if (pdata->bus_width_16) 2358c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_BUS_WIDTH_16; 2368c2ecf20Sopenharmony_ci if (pdata->port1_otg) 2378c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_OTG_EN; 2388c2ecf20Sopenharmony_ci if (pdata->analog_oc) 2398c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_ANALOG_OC; 2408c2ecf20Sopenharmony_ci if (pdata->dack_polarity_high) 2418c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_DACK_POL_HIGH; 2428c2ecf20Sopenharmony_ci if (pdata->dreq_polarity_high) 2438c2ecf20Sopenharmony_ci devflags |= ISP1760_FLAG_DREQ_POL_HIGH; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev, 2478c2ecf20Sopenharmony_ci devflags); 2488c2ecf20Sopenharmony_ci if (ret < 0) 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_info("ISP1760 USB device initialised\n"); 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int isp1760_plat_remove(struct platform_device *pdev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci isp1760_unregister(&pdev->dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2638c2ecf20Sopenharmony_cistatic const struct of_device_id isp1760_of_match[] = { 2648c2ecf20Sopenharmony_ci { .compatible = "nxp,usb-isp1760", }, 2658c2ecf20Sopenharmony_ci { .compatible = "nxp,usb-isp1761", }, 2668c2ecf20Sopenharmony_ci { }, 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, isp1760_of_match); 2698c2ecf20Sopenharmony_ci#endif 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic struct platform_driver isp1760_plat_driver = { 2728c2ecf20Sopenharmony_ci .probe = isp1760_plat_probe, 2738c2ecf20Sopenharmony_ci .remove = isp1760_plat_remove, 2748c2ecf20Sopenharmony_ci .driver = { 2758c2ecf20Sopenharmony_ci .name = "isp1760", 2768c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(isp1760_of_match), 2778c2ecf20Sopenharmony_ci }, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int __init isp1760_init(void) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int ret, any_ret = -ENODEV; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci isp1760_init_kmem_once(); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ret = platform_driver_register(&isp1760_plat_driver); 2878c2ecf20Sopenharmony_ci if (!ret) 2888c2ecf20Sopenharmony_ci any_ret = 0; 2898c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PCI 2908c2ecf20Sopenharmony_ci ret = pci_register_driver(&isp1761_pci_driver); 2918c2ecf20Sopenharmony_ci if (!ret) 2928c2ecf20Sopenharmony_ci any_ret = 0; 2938c2ecf20Sopenharmony_ci#endif 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (any_ret) 2968c2ecf20Sopenharmony_ci isp1760_deinit_kmem_cache(); 2978c2ecf20Sopenharmony_ci return any_ret; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_cimodule_init(isp1760_init); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void __exit isp1760_exit(void) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci platform_driver_unregister(&isp1760_plat_driver); 3048c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_PCI 3058c2ecf20Sopenharmony_ci pci_unregister_driver(&isp1761_pci_driver); 3068c2ecf20Sopenharmony_ci#endif 3078c2ecf20Sopenharmony_ci isp1760_deinit_kmem_cache(); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_cimodule_exit(isp1760_exit); 310