^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * dwc3-haps.c - Synopsys HAPS PCI Specific glue layer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2018 Synopsys, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Authors: Thinh Nguyen <thinhn@synopsys.com>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * John Youn <johnyoun@synopsys.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/property.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * struct dwc3_haps - Driver private structure
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * @dwc3: child dwc3 platform_device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * @pci: our link to PCI bus
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) struct dwc3_haps {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct platform_device *dwc3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) struct pci_dev *pci;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static const struct property_entry initial_properties[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) { },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static int dwc3_haps_probe(struct pci_dev *pci,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) const struct pci_device_id *id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct dwc3_haps *dwc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) struct device *dev = &pci->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) struct resource res[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) ret = pcim_enable_device(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) dev_err(dev, "failed to enable pci device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) pci_set_master(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if (!dwc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) if (!dwc->dwc3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) res[0].start = pci_resource_start(pci, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) res[0].end = pci_resource_end(pci, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) res[0].name = "dwc_usb3";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) res[0].flags = IORESOURCE_MEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) res[1].start = pci->irq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) res[1].name = "dwc_usb3";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) res[1].flags = IORESOURCE_IRQ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) dev_err(dev, "couldn't add resources to dwc3 device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) dwc->pci = pci;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) dwc->dwc3->dev.parent = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) ret = platform_device_add_properties(dwc->dwc3, initial_properties);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) ret = platform_device_add(dwc->dwc3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) dev_err(dev, "failed to register dwc3 device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) pci_set_drvdata(pci, dwc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) platform_device_put(dwc->dwc3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static void dwc3_haps_remove(struct pci_dev *pci)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) struct dwc3_haps *dwc = pci_get_drvdata(pci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) platform_device_unregister(dwc->dwc3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static const struct pci_device_id dwc3_haps_id_table[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) * i.MX6QP and i.MX7D platform use a PCIe controller with the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) * same VID and PID as this USB controller. The system may
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) * incorrectly match this driver to that PCIe controller. To
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) * workaround this, specifically use class type USB to prevent
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) * incorrect driver matching.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) .class = (PCI_CLASS_SERIAL_USB << 8),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) .class_mask = 0xffff00,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) { } /* Terminating Entry */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) MODULE_DEVICE_TABLE(pci, dwc3_haps_id_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) static struct pci_driver dwc3_haps_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) .name = "dwc3-haps",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) .id_table = dwc3_haps_id_table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .probe = dwc3_haps_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .remove = dwc3_haps_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) MODULE_AUTHOR("Thinh Nguyen <thinhn@synopsys.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) MODULE_DESCRIPTION("Synopsys HAPS PCI Glue Layer");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) module_pci_driver(dwc3_haps_driver);