17733f6c3SPawel Laszczak // SPDX-License-Identifier: GPL-2.0
27733f6c3SPawel Laszczak /*
37733f6c3SPawel Laszczak * Cadence USBSS PCI Glue driver
47733f6c3SPawel Laszczak *
57733f6c3SPawel Laszczak * Copyright (C) 2018-2019 Cadence.
67733f6c3SPawel Laszczak *
77733f6c3SPawel Laszczak * Author: Pawel Laszczak <pawell@cadence.com>
87733f6c3SPawel Laszczak */
97733f6c3SPawel Laszczak
107733f6c3SPawel Laszczak #include <linux/kernel.h>
117733f6c3SPawel Laszczak #include <linux/module.h>
127733f6c3SPawel Laszczak #include <linux/pci.h>
137733f6c3SPawel Laszczak #include <linux/platform_device.h>
147733f6c3SPawel Laszczak #include <linux/dma-mapping.h>
157733f6c3SPawel Laszczak #include <linux/slab.h>
167733f6c3SPawel Laszczak
177733f6c3SPawel Laszczak struct cdns3_wrap {
187733f6c3SPawel Laszczak struct platform_device *plat_dev;
197733f6c3SPawel Laszczak struct resource dev_res[6];
207733f6c3SPawel Laszczak int devfn;
217733f6c3SPawel Laszczak };
227733f6c3SPawel Laszczak
237733f6c3SPawel Laszczak #define RES_IRQ_HOST_ID 0
247733f6c3SPawel Laszczak #define RES_IRQ_PERIPHERAL_ID 1
257733f6c3SPawel Laszczak #define RES_IRQ_OTG_ID 2
267733f6c3SPawel Laszczak #define RES_HOST_ID 3
277733f6c3SPawel Laszczak #define RES_DEV_ID 4
287733f6c3SPawel Laszczak #define RES_DRD_ID 5
297733f6c3SPawel Laszczak
307733f6c3SPawel Laszczak #define PCI_BAR_HOST 0
317733f6c3SPawel Laszczak #define PCI_BAR_DEV 2
327733f6c3SPawel Laszczak #define PCI_BAR_OTG 0
337733f6c3SPawel Laszczak
347733f6c3SPawel Laszczak #define PCI_DEV_FN_HOST_DEVICE 0
357733f6c3SPawel Laszczak #define PCI_DEV_FN_OTG 1
367733f6c3SPawel Laszczak
377733f6c3SPawel Laszczak #define PCI_DRIVER_NAME "cdns3-pci-usbss"
387733f6c3SPawel Laszczak #define PLAT_DRIVER_NAME "cdns-usb3"
397733f6c3SPawel Laszczak
407733f6c3SPawel Laszczak #define CDNS_VENDOR_ID 0x17cd
417733f6c3SPawel Laszczak #define CDNS_DEVICE_ID 0x0100
427733f6c3SPawel Laszczak
cdns3_get_second_fun(struct pci_dev * pdev)437733f6c3SPawel Laszczak static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
447733f6c3SPawel Laszczak {
457733f6c3SPawel Laszczak struct pci_dev *func;
467733f6c3SPawel Laszczak
477733f6c3SPawel Laszczak /*
487733f6c3SPawel Laszczak * Gets the second function.
497733f6c3SPawel Laszczak * It's little tricky, but this platform has two function.
507733f6c3SPawel Laszczak * The fist keeps resources for Host/Device while the second
517733f6c3SPawel Laszczak * keeps resources for DRD/OTG.
527733f6c3SPawel Laszczak */
537733f6c3SPawel Laszczak func = pci_get_device(pdev->vendor, pdev->device, NULL);
547733f6c3SPawel Laszczak if (unlikely(!func))
557733f6c3SPawel Laszczak return NULL;
567733f6c3SPawel Laszczak
577733f6c3SPawel Laszczak if (func->devfn == pdev->devfn) {
587733f6c3SPawel Laszczak func = pci_get_device(pdev->vendor, pdev->device, func);
597733f6c3SPawel Laszczak if (unlikely(!func))
607733f6c3SPawel Laszczak return NULL;
617733f6c3SPawel Laszczak }
627733f6c3SPawel Laszczak
63*1272fd65SPawel Laszczak if (func->devfn != PCI_DEV_FN_HOST_DEVICE &&
64*1272fd65SPawel Laszczak func->devfn != PCI_DEV_FN_OTG) {
65*1272fd65SPawel Laszczak return NULL;
66*1272fd65SPawel Laszczak }
67*1272fd65SPawel Laszczak
687733f6c3SPawel Laszczak return func;
697733f6c3SPawel Laszczak }
707733f6c3SPawel Laszczak
cdns3_pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)717733f6c3SPawel Laszczak static int cdns3_pci_probe(struct pci_dev *pdev,
727733f6c3SPawel Laszczak const struct pci_device_id *id)
737733f6c3SPawel Laszczak {
747733f6c3SPawel Laszczak struct platform_device_info plat_info;
757733f6c3SPawel Laszczak struct cdns3_wrap *wrap;
767733f6c3SPawel Laszczak struct resource *res;
777733f6c3SPawel Laszczak struct pci_dev *func;
787733f6c3SPawel Laszczak int err;
797733f6c3SPawel Laszczak
807733f6c3SPawel Laszczak /*
817733f6c3SPawel Laszczak * for GADGET/HOST PCI (devfn) function number is 0,
827733f6c3SPawel Laszczak * for OTG PCI (devfn) function number is 1
837733f6c3SPawel Laszczak */
847733f6c3SPawel Laszczak if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
857733f6c3SPawel Laszczak pdev->devfn != PCI_DEV_FN_OTG))
867733f6c3SPawel Laszczak return -EINVAL;
877733f6c3SPawel Laszczak
887733f6c3SPawel Laszczak func = cdns3_get_second_fun(pdev);
897733f6c3SPawel Laszczak if (unlikely(!func))
907733f6c3SPawel Laszczak return -EINVAL;
917733f6c3SPawel Laszczak
927733f6c3SPawel Laszczak err = pcim_enable_device(pdev);
937733f6c3SPawel Laszczak if (err) {
947733f6c3SPawel Laszczak dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
957733f6c3SPawel Laszczak return err;
967733f6c3SPawel Laszczak }
977733f6c3SPawel Laszczak
987733f6c3SPawel Laszczak pci_set_master(pdev);
997733f6c3SPawel Laszczak
1007733f6c3SPawel Laszczak if (pci_is_enabled(func)) {
1017733f6c3SPawel Laszczak wrap = pci_get_drvdata(func);
1027733f6c3SPawel Laszczak } else {
1037733f6c3SPawel Laszczak wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
1047733f6c3SPawel Laszczak if (!wrap) {
1057733f6c3SPawel Laszczak pci_disable_device(pdev);
1067733f6c3SPawel Laszczak return -ENOMEM;
1077733f6c3SPawel Laszczak }
1087733f6c3SPawel Laszczak }
1097733f6c3SPawel Laszczak
1107733f6c3SPawel Laszczak res = wrap->dev_res;
1117733f6c3SPawel Laszczak
1127733f6c3SPawel Laszczak if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
1137733f6c3SPawel Laszczak /* function 0: host(BAR_0) + device(BAR_1).*/
1147733f6c3SPawel Laszczak dev_dbg(&pdev->dev, "Initialize Device resources\n");
1157733f6c3SPawel Laszczak res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
1167733f6c3SPawel Laszczak res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV);
1177733f6c3SPawel Laszczak res[RES_DEV_ID].name = "dev";
1187733f6c3SPawel Laszczak res[RES_DEV_ID].flags = IORESOURCE_MEM;
1197733f6c3SPawel Laszczak dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
1207733f6c3SPawel Laszczak &res[RES_DEV_ID].start);
1217733f6c3SPawel Laszczak
1227733f6c3SPawel Laszczak res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
1237733f6c3SPawel Laszczak res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
1247733f6c3SPawel Laszczak res[RES_HOST_ID].name = "xhci";
1257733f6c3SPawel Laszczak res[RES_HOST_ID].flags = IORESOURCE_MEM;
1267733f6c3SPawel Laszczak dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
1277733f6c3SPawel Laszczak &res[RES_HOST_ID].start);
1287733f6c3SPawel Laszczak
1297733f6c3SPawel Laszczak /* Interrupt for XHCI */
1307733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
1317733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
1327733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
1337733f6c3SPawel Laszczak
1347733f6c3SPawel Laszczak /* Interrupt device. It's the same as for HOST. */
1357733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
1367733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
1377733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
1387733f6c3SPawel Laszczak } else {
1397733f6c3SPawel Laszczak res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
1407733f6c3SPawel Laszczak res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
1417733f6c3SPawel Laszczak res[RES_DRD_ID].name = "otg";
1427733f6c3SPawel Laszczak res[RES_DRD_ID].flags = IORESOURCE_MEM;
1437733f6c3SPawel Laszczak dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
1447733f6c3SPawel Laszczak &res[RES_DRD_ID].start);
1457733f6c3SPawel Laszczak
1467733f6c3SPawel Laszczak /* Interrupt for OTG/DRD. */
1477733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
1487733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
1497733f6c3SPawel Laszczak wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
1507733f6c3SPawel Laszczak }
1517733f6c3SPawel Laszczak
1527733f6c3SPawel Laszczak if (pci_is_enabled(func)) {
1537733f6c3SPawel Laszczak /* set up platform device info */
1547733f6c3SPawel Laszczak memset(&plat_info, 0, sizeof(plat_info));
1557733f6c3SPawel Laszczak plat_info.parent = &pdev->dev;
1567733f6c3SPawel Laszczak plat_info.fwnode = pdev->dev.fwnode;
1577733f6c3SPawel Laszczak plat_info.name = PLAT_DRIVER_NAME;
1587733f6c3SPawel Laszczak plat_info.id = pdev->devfn;
1597733f6c3SPawel Laszczak wrap->devfn = pdev->devfn;
1607733f6c3SPawel Laszczak plat_info.res = wrap->dev_res;
1617733f6c3SPawel Laszczak plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
1627733f6c3SPawel Laszczak plat_info.dma_mask = pdev->dma_mask;
1637733f6c3SPawel Laszczak /* register platform device */
1647733f6c3SPawel Laszczak wrap->plat_dev = platform_device_register_full(&plat_info);
1657733f6c3SPawel Laszczak if (IS_ERR(wrap->plat_dev)) {
1667733f6c3SPawel Laszczak pci_disable_device(pdev);
1675f318dadSDan Carpenter err = PTR_ERR(wrap->plat_dev);
1687733f6c3SPawel Laszczak kfree(wrap);
1695f318dadSDan Carpenter return err;
1707733f6c3SPawel Laszczak }
1717733f6c3SPawel Laszczak }
1727733f6c3SPawel Laszczak
1737733f6c3SPawel Laszczak pci_set_drvdata(pdev, wrap);
1747733f6c3SPawel Laszczak return err;
1757733f6c3SPawel Laszczak }
1767733f6c3SPawel Laszczak
cdns3_pci_remove(struct pci_dev * pdev)1777733f6c3SPawel Laszczak static void cdns3_pci_remove(struct pci_dev *pdev)
1787733f6c3SPawel Laszczak {
1797733f6c3SPawel Laszczak struct cdns3_wrap *wrap;
1807733f6c3SPawel Laszczak struct pci_dev *func;
1817733f6c3SPawel Laszczak
1827733f6c3SPawel Laszczak func = cdns3_get_second_fun(pdev);
1837733f6c3SPawel Laszczak
1847733f6c3SPawel Laszczak wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
1857733f6c3SPawel Laszczak if (wrap->devfn == pdev->devfn)
1867733f6c3SPawel Laszczak platform_device_unregister(wrap->plat_dev);
1877733f6c3SPawel Laszczak
1887733f6c3SPawel Laszczak if (!pci_is_enabled(func))
1897733f6c3SPawel Laszczak kfree(wrap);
1907733f6c3SPawel Laszczak }
1917733f6c3SPawel Laszczak
1927733f6c3SPawel Laszczak static const struct pci_device_id cdns3_pci_ids[] = {
1937733f6c3SPawel Laszczak { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
1947733f6c3SPawel Laszczak { 0, }
1957733f6c3SPawel Laszczak };
1967733f6c3SPawel Laszczak
1977733f6c3SPawel Laszczak static struct pci_driver cdns3_pci_driver = {
1987733f6c3SPawel Laszczak .name = PCI_DRIVER_NAME,
1997733f6c3SPawel Laszczak .id_table = cdns3_pci_ids,
2007733f6c3SPawel Laszczak .probe = cdns3_pci_probe,
2017733f6c3SPawel Laszczak .remove = cdns3_pci_remove,
2027733f6c3SPawel Laszczak };
2037733f6c3SPawel Laszczak
2047733f6c3SPawel Laszczak module_pci_driver(cdns3_pci_driver);
2057733f6c3SPawel Laszczak MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
2067733f6c3SPawel Laszczak
2077733f6c3SPawel Laszczak MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
2087733f6c3SPawel Laszczak MODULE_LICENSE("GPL v2");
20923a73711SColin Ian King MODULE_DESCRIPTION("Cadence USBSS PCI wrapper");
210