1*4882a593Smuzhiyun // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * pci.c - DesignWare HS OTG Controller PCI driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2004-2013 Synopsys, Inc.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Redistribution and use in source and binary forms, with or without
8*4882a593Smuzhiyun * modification, are permitted provided that the following conditions
9*4882a593Smuzhiyun * are met:
10*4882a593Smuzhiyun * 1. Redistributions of source code must retain the above copyright
11*4882a593Smuzhiyun * notice, this list of conditions, and the following disclaimer,
12*4882a593Smuzhiyun * without modification.
13*4882a593Smuzhiyun * 2. Redistributions in binary form must reproduce the above copyright
14*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer in the
15*4882a593Smuzhiyun * documentation and/or other materials provided with the distribution.
16*4882a593Smuzhiyun * 3. The names of the above-listed copyright holders may not be used
17*4882a593Smuzhiyun * to endorse or promote products derived from this software without
18*4882a593Smuzhiyun * specific prior written permission.
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun * ALTERNATIVELY, this software may be distributed under the terms of the
21*4882a593Smuzhiyun * GNU General Public License ("GPL") as published by the Free Software
22*4882a593Smuzhiyun * Foundation; either version 2 of the License, or (at your option) any
23*4882a593Smuzhiyun * later version.
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
26*4882a593Smuzhiyun * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27*4882a593Smuzhiyun * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28*4882a593Smuzhiyun * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29*4882a593Smuzhiyun * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30*4882a593Smuzhiyun * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31*4882a593Smuzhiyun * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32*4882a593Smuzhiyun * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33*4882a593Smuzhiyun * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34*4882a593Smuzhiyun * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35*4882a593Smuzhiyun * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36*4882a593Smuzhiyun */
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun /*
39*4882a593Smuzhiyun * Provides the initialization and cleanup entry points for the DWC_otg PCI
40*4882a593Smuzhiyun * driver
41*4882a593Smuzhiyun */
42*4882a593Smuzhiyun #include <linux/kernel.h>
43*4882a593Smuzhiyun #include <linux/module.h>
44*4882a593Smuzhiyun #include <linux/moduleparam.h>
45*4882a593Smuzhiyun #include <linux/spinlock.h>
46*4882a593Smuzhiyun #include <linux/interrupt.h>
47*4882a593Smuzhiyun #include <linux/io.h>
48*4882a593Smuzhiyun #include <linux/slab.h>
49*4882a593Smuzhiyun #include <linux/pci.h>
50*4882a593Smuzhiyun #include <linux/usb.h>
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun #include <linux/usb/hcd.h>
53*4882a593Smuzhiyun #include <linux/usb/ch11.h>
54*4882a593Smuzhiyun #include <linux/platform_device.h>
55*4882a593Smuzhiyun #include <linux/usb/usb_phy_generic.h>
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun #define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun static const char dwc2_driver_name[] = "dwc2-pci";
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun struct dwc2_pci_glue {
62*4882a593Smuzhiyun struct platform_device *dwc2;
63*4882a593Smuzhiyun struct platform_device *phy;
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun
dwc2_pci_quirks(struct pci_dev * pdev,struct platform_device * dwc2)66*4882a593Smuzhiyun static int dwc2_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc2)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun if (pdev->vendor == PCI_VENDOR_ID_SYNOPSYS &&
69*4882a593Smuzhiyun pdev->device == PCI_PRODUCT_ID_HAPS_HSOTG) {
70*4882a593Smuzhiyun struct property_entry properties[] = {
71*4882a593Smuzhiyun { },
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun return platform_device_add_properties(dwc2, properties);
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun return 0;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /**
81*4882a593Smuzhiyun * dwc2_pci_probe() - Provides the cleanup entry points for the DWC_otg PCI
82*4882a593Smuzhiyun * driver
83*4882a593Smuzhiyun *
84*4882a593Smuzhiyun * @pci: The programming view of DWC_otg PCI
85*4882a593Smuzhiyun */
dwc2_pci_remove(struct pci_dev * pci)86*4882a593Smuzhiyun static void dwc2_pci_remove(struct pci_dev *pci)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun platform_device_unregister(glue->dwc2);
91*4882a593Smuzhiyun usb_phy_generic_unregister(glue->phy);
92*4882a593Smuzhiyun pci_set_drvdata(pci, NULL);
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
dwc2_pci_probe(struct pci_dev * pci,const struct pci_device_id * id)95*4882a593Smuzhiyun static int dwc2_pci_probe(struct pci_dev *pci,
96*4882a593Smuzhiyun const struct pci_device_id *id)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct resource res[2];
99*4882a593Smuzhiyun struct platform_device *dwc2;
100*4882a593Smuzhiyun struct platform_device *phy;
101*4882a593Smuzhiyun int ret;
102*4882a593Smuzhiyun struct device *dev = &pci->dev;
103*4882a593Smuzhiyun struct dwc2_pci_glue *glue;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun ret = pcim_enable_device(pci);
106*4882a593Smuzhiyun if (ret) {
107*4882a593Smuzhiyun dev_err(dev, "failed to enable pci device\n");
108*4882a593Smuzhiyun return -ENODEV;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun pci_set_master(pci);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun phy = usb_phy_generic_register();
114*4882a593Smuzhiyun if (IS_ERR(phy)) {
115*4882a593Smuzhiyun dev_err(dev, "error registering generic PHY (%ld)\n",
116*4882a593Smuzhiyun PTR_ERR(phy));
117*4882a593Smuzhiyun return PTR_ERR(phy);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
121*4882a593Smuzhiyun if (!dwc2) {
122*4882a593Smuzhiyun dev_err(dev, "couldn't allocate dwc2 device\n");
123*4882a593Smuzhiyun ret = -ENOMEM;
124*4882a593Smuzhiyun goto err;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun res[0].start = pci_resource_start(pci, 0);
130*4882a593Smuzhiyun res[0].end = pci_resource_end(pci, 0);
131*4882a593Smuzhiyun res[0].name = "dwc2";
132*4882a593Smuzhiyun res[0].flags = IORESOURCE_MEM;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun res[1].start = pci->irq;
135*4882a593Smuzhiyun res[1].name = "dwc2";
136*4882a593Smuzhiyun res[1].flags = IORESOURCE_IRQ;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
139*4882a593Smuzhiyun if (ret) {
140*4882a593Smuzhiyun dev_err(dev, "couldn't add resources to dwc2 device\n");
141*4882a593Smuzhiyun goto err;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun dwc2->dev.parent = dev;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun ret = dwc2_pci_quirks(pci, dwc2);
147*4882a593Smuzhiyun if (ret)
148*4882a593Smuzhiyun goto err;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
151*4882a593Smuzhiyun if (!glue) {
152*4882a593Smuzhiyun ret = -ENOMEM;
153*4882a593Smuzhiyun goto err;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun ret = platform_device_add(dwc2);
157*4882a593Smuzhiyun if (ret) {
158*4882a593Smuzhiyun dev_err(dev, "failed to register dwc2 device\n");
159*4882a593Smuzhiyun goto err;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun glue->phy = phy;
163*4882a593Smuzhiyun glue->dwc2 = dwc2;
164*4882a593Smuzhiyun pci_set_drvdata(pci, glue);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun return 0;
167*4882a593Smuzhiyun err:
168*4882a593Smuzhiyun usb_phy_generic_unregister(phy);
169*4882a593Smuzhiyun platform_device_put(dwc2);
170*4882a593Smuzhiyun return ret;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun static const struct pci_device_id dwc2_pci_ids[] = {
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG),
176*4882a593Smuzhiyun },
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun PCI_DEVICE(PCI_VENDOR_ID_STMICRO,
179*4882a593Smuzhiyun PCI_DEVICE_ID_STMICRO_USB_OTG),
180*4882a593Smuzhiyun },
181*4882a593Smuzhiyun { /* end: all zeroes */ }
182*4882a593Smuzhiyun };
183*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun static struct pci_driver dwc2_pci_driver = {
186*4882a593Smuzhiyun .name = dwc2_driver_name,
187*4882a593Smuzhiyun .id_table = dwc2_pci_ids,
188*4882a593Smuzhiyun .probe = dwc2_pci_probe,
189*4882a593Smuzhiyun .remove = dwc2_pci_remove,
190*4882a593Smuzhiyun };
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun module_pci_driver(dwc2_pci_driver);
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun MODULE_DESCRIPTION("DESIGNWARE HS OTG PCI Bus Glue");
195*4882a593Smuzhiyun MODULE_AUTHOR("Synopsys, Inc.");
196*4882a593Smuzhiyun MODULE_LICENSE("Dual BSD/GPL");
197