18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * dwc3-haps.c - Synopsys HAPS PCI Specific glue layer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Synopsys, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Thinh Nguyen <thinhn@synopsys.com>, 88c2ecf20Sopenharmony_ci * John Youn <johnyoun@synopsys.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/property.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/** 198c2ecf20Sopenharmony_ci * struct dwc3_haps - Driver private structure 208c2ecf20Sopenharmony_ci * @dwc3: child dwc3 platform_device 218c2ecf20Sopenharmony_ci * @pci: our link to PCI bus 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cistruct dwc3_haps { 248c2ecf20Sopenharmony_ci struct platform_device *dwc3; 258c2ecf20Sopenharmony_ci struct pci_dev *pci; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct property_entry initial_properties[] = { 298c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"), 308c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), 318c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"), 328c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), 338c2ecf20Sopenharmony_ci { }, 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int dwc3_haps_probe(struct pci_dev *pci, 378c2ecf20Sopenharmony_ci const struct pci_device_id *id) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct dwc3_haps *dwc; 408c2ecf20Sopenharmony_ci struct device *dev = &pci->dev; 418c2ecf20Sopenharmony_ci struct resource res[2]; 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci ret = pcim_enable_device(pci); 458c2ecf20Sopenharmony_ci if (ret) { 468c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable pci device\n"); 478c2ecf20Sopenharmony_ci return -ENODEV; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci pci_set_master(pci); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); 538c2ecf20Sopenharmony_ci if (!dwc) 548c2ecf20Sopenharmony_ci return -ENOMEM; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); 578c2ecf20Sopenharmony_ci if (!dwc->dwc3) 588c2ecf20Sopenharmony_ci return -ENOMEM; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci res[0].start = pci_resource_start(pci, 0); 638c2ecf20Sopenharmony_ci res[0].end = pci_resource_end(pci, 0); 648c2ecf20Sopenharmony_ci res[0].name = "dwc_usb3"; 658c2ecf20Sopenharmony_ci res[0].flags = IORESOURCE_MEM; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci res[1].start = pci->irq; 688c2ecf20Sopenharmony_ci res[1].name = "dwc_usb3"; 698c2ecf20Sopenharmony_ci res[1].flags = IORESOURCE_IRQ; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res)); 728c2ecf20Sopenharmony_ci if (ret) { 738c2ecf20Sopenharmony_ci dev_err(dev, "couldn't add resources to dwc3 device\n"); 748c2ecf20Sopenharmony_ci goto err; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci dwc->pci = pci; 788c2ecf20Sopenharmony_ci dwc->dwc3->dev.parent = dev; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ret = platform_device_add_properties(dwc->dwc3, initial_properties); 818c2ecf20Sopenharmony_ci if (ret) 828c2ecf20Sopenharmony_ci goto err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = platform_device_add(dwc->dwc3); 858c2ecf20Sopenharmony_ci if (ret) { 868c2ecf20Sopenharmony_ci dev_err(dev, "failed to register dwc3 device\n"); 878c2ecf20Sopenharmony_ci goto err; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci pci_set_drvdata(pci, dwc); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_cierr: 948c2ecf20Sopenharmony_ci platform_device_put(dwc->dwc3); 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void dwc3_haps_remove(struct pci_dev *pci) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct dwc3_haps *dwc = pci_get_drvdata(pci); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci platform_device_unregister(dwc->dwc3); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct pci_device_id dwc3_haps_id_table[] = { 1068c2ecf20Sopenharmony_ci { 1078c2ecf20Sopenharmony_ci PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 1088c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * i.MX6QP and i.MX7D platform use a PCIe controller with the 1118c2ecf20Sopenharmony_ci * same VID and PID as this USB controller. The system may 1128c2ecf20Sopenharmony_ci * incorrectly match this driver to that PCIe controller. To 1138c2ecf20Sopenharmony_ci * workaround this, specifically use class type USB to prevent 1148c2ecf20Sopenharmony_ci * incorrect driver matching. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci .class = (PCI_CLASS_SERIAL_USB << 8), 1178c2ecf20Sopenharmony_ci .class_mask = 0xffff00, 1188c2ecf20Sopenharmony_ci }, 1198c2ecf20Sopenharmony_ci { 1208c2ecf20Sopenharmony_ci PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 1218c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI), 1228c2ecf20Sopenharmony_ci }, 1238c2ecf20Sopenharmony_ci { 1248c2ecf20Sopenharmony_ci PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 1258c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31), 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci { } /* Terminating Entry */ 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, dwc3_haps_id_table); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct pci_driver dwc3_haps_driver = { 1328c2ecf20Sopenharmony_ci .name = "dwc3-haps", 1338c2ecf20Sopenharmony_ci .id_table = dwc3_haps_id_table, 1348c2ecf20Sopenharmony_ci .probe = dwc3_haps_probe, 1358c2ecf20Sopenharmony_ci .remove = dwc3_haps_remove, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thinh Nguyen <thinhn@synopsys.com>"); 1398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys HAPS PCI Glue Layer"); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cimodule_pci_driver(dwc3_haps_driver); 143