1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2 /*
3  * pci.c - DesignWare HS OTG Controller PCI driver
4  *
5  * Copyright (C) 2004-2013 Synopsys, Inc.
6  */
7 
8 /*
9  * Provides the initialization and cleanup entry points for the DWC_otg PCI
10  * driver
11  */
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/moduleparam.h>
15 #include <linux/spinlock.h>
16 #include <linux/interrupt.h>
17 #include <linux/io.h>
18 #include <linux/slab.h>
19 #include <linux/pci.h>
20 #include <linux/usb.h>
21 
22 #include <linux/usb/hcd.h>
23 #include <linux/usb/ch11.h>
24 #include <linux/platform_device.h>
25 #include <linux/usb/usb_phy_generic.h>
26 
27 #define PCI_PRODUCT_ID_HAPS_HSOTG	0xabc0
28 
29 static const char dwc2_driver_name[] = "dwc2-pci";
30 
31 struct dwc2_pci_glue {
32 	struct platform_device *dwc2;
33 	struct platform_device *phy;
34 };
35 
36 /**
37  * dwc2_pci_remove() - Provides the cleanup entry points for the DWC_otg PCI
38  * driver
39  *
40  * @pci: The programming view of DWC_otg PCI
41  */
dwc2_pci_remove(struct pci_dev * pci)42 static void dwc2_pci_remove(struct pci_dev *pci)
43 {
44 	struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
45 
46 	platform_device_unregister(glue->dwc2);
47 	usb_phy_generic_unregister(glue->phy);
48 	pci_set_drvdata(pci, NULL);
49 }
50 
dwc2_pci_probe(struct pci_dev * pci,const struct pci_device_id * id)51 static int dwc2_pci_probe(struct pci_dev *pci,
52 			  const struct pci_device_id *id)
53 {
54 	struct resource		res[2];
55 	struct platform_device	*dwc2;
56 	struct platform_device	*phy;
57 	int			ret;
58 	struct device		*dev = &pci->dev;
59 	struct dwc2_pci_glue	*glue;
60 
61 	ret = pcim_enable_device(pci);
62 	if (ret) {
63 		dev_err(dev, "failed to enable pci device\n");
64 		return -ENODEV;
65 	}
66 
67 	pci_set_master(pci);
68 
69 	phy = usb_phy_generic_register();
70 	if (IS_ERR(phy)) {
71 		dev_err(dev, "error registering generic PHY (%ld)\n",
72 			PTR_ERR(phy));
73 		return PTR_ERR(phy);
74 	}
75 
76 	dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
77 	if (!dwc2) {
78 		dev_err(dev, "couldn't allocate dwc2 device\n");
79 		ret = -ENOMEM;
80 		goto err;
81 	}
82 
83 	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
84 
85 	res[0].start	= pci_resource_start(pci, 0);
86 	res[0].end	= pci_resource_end(pci, 0);
87 	res[0].name	= "dwc2";
88 	res[0].flags	= IORESOURCE_MEM;
89 
90 	res[1].start	= pci->irq;
91 	res[1].name	= "dwc2";
92 	res[1].flags	= IORESOURCE_IRQ;
93 
94 	ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
95 	if (ret) {
96 		dev_err(dev, "couldn't add resources to dwc2 device\n");
97 		goto err;
98 	}
99 
100 	dwc2->dev.parent = dev;
101 
102 	glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
103 	if (!glue) {
104 		ret = -ENOMEM;
105 		goto err;
106 	}
107 
108 	ret = platform_device_add(dwc2);
109 	if (ret) {
110 		dev_err(dev, "failed to register dwc2 device\n");
111 		goto err;
112 	}
113 
114 	glue->phy = phy;
115 	glue->dwc2 = dwc2;
116 	pci_set_drvdata(pci, glue);
117 
118 	return 0;
119 err:
120 	usb_phy_generic_unregister(phy);
121 	platform_device_put(dwc2);
122 	return ret;
123 }
124 
125 static const struct pci_device_id dwc2_pci_ids[] = {
126 	{
127 		PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG),
128 	},
129 	{
130 		PCI_DEVICE(PCI_VENDOR_ID_STMICRO,
131 			   PCI_DEVICE_ID_STMICRO_USB_OTG),
132 	},
133 	{ /* end: all zeroes */ }
134 };
135 MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
136 
137 static struct pci_driver dwc2_pci_driver = {
138 	.name = dwc2_driver_name,
139 	.id_table = dwc2_pci_ids,
140 	.probe = dwc2_pci_probe,
141 	.remove = dwc2_pci_remove,
142 };
143 
144 module_pci_driver(dwc2_pci_driver);
145 
146 MODULE_DESCRIPTION("DESIGNWARE HS OTG PCI Bus Glue");
147 MODULE_AUTHOR("Synopsys, Inc.");
148 MODULE_LICENSE("Dual BSD/GPL");
149