xref: /openbmc/linux/drivers/usb/cdns3/cdns3-pci-wrap.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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