1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ci_hdrc_pci.c - MIPS USB IP core family device controller
4  *
5  * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
6  *
7  * Author: David Lopo
8  */
9 
10 #include <linux/platform_device.h>
11 #include <linux/module.h>
12 #include <linux/pci.h>
13 #include <linux/interrupt.h>
14 #include <linux/usb/gadget.h>
15 #include <linux/usb/chipidea.h>
16 #include <linux/usb/usb_phy_generic.h>
17 
18 /* driver name */
19 #define UDC_DRIVER_NAME   "ci_hdrc_pci"
20 
21 struct ci_hdrc_pci {
22 	struct platform_device	*ci;
23 	struct platform_device	*phy;
24 };
25 
26 /******************************************************************************
27  * PCI block
28  *****************************************************************************/
29 static struct ci_hdrc_platform_data pci_platdata = {
30 	.name		= UDC_DRIVER_NAME,
31 	.capoffset	= DEF_CAPOFFSET,
32 };
33 
34 static struct ci_hdrc_platform_data langwell_pci_platdata = {
35 	.name		= UDC_DRIVER_NAME,
36 	.capoffset	= 0,
37 };
38 
39 static struct ci_hdrc_platform_data penwell_pci_platdata = {
40 	.name		= UDC_DRIVER_NAME,
41 	.capoffset	= 0,
42 	.power_budget	= 200,
43 };
44 
45 /**
46  * ci_hdrc_pci_probe: PCI probe
47  * @pdev: USB device controller being probed
48  * @id:   PCI hotplug ID connecting controller to UDC framework
49  *
50  * This function returns an error code
51  * Allocates basic PCI resources for this USB device controller, and then
52  * invokes the udc_probe() method to start the UDC associated with it
53  */
54 static int ci_hdrc_pci_probe(struct pci_dev *pdev,
55 				       const struct pci_device_id *id)
56 {
57 	struct ci_hdrc_platform_data *platdata = (void *)id->driver_data;
58 	struct ci_hdrc_pci *ci;
59 	struct resource res[3];
60 	int retval = 0, nres = 2;
61 
62 	if (!platdata) {
63 		dev_err(&pdev->dev, "device doesn't provide driver data\n");
64 		return -ENODEV;
65 	}
66 
67 	ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
68 	if (!ci)
69 		return -ENOMEM;
70 
71 	retval = pcim_enable_device(pdev);
72 	if (retval)
73 		return retval;
74 
75 	if (!pdev->irq) {
76 		dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
77 		return -ENODEV;
78 	}
79 
80 	pci_set_master(pdev);
81 	pci_try_set_mwi(pdev);
82 
83 	/* register a nop PHY */
84 	ci->phy = usb_phy_generic_register();
85 	if (IS_ERR(ci->phy))
86 		return PTR_ERR(ci->phy);
87 
88 	memset(res, 0, sizeof(res));
89 	res[0].start	= pci_resource_start(pdev, 0);
90 	res[0].end	= pci_resource_end(pdev, 0);
91 	res[0].flags	= IORESOURCE_MEM;
92 	res[1].start	= pdev->irq;
93 	res[1].flags	= IORESOURCE_IRQ;
94 
95 	ci->ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata);
96 	if (IS_ERR(ci->ci)) {
97 		dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
98 		usb_phy_generic_unregister(ci->phy);
99 		return PTR_ERR(ci->ci);
100 	}
101 
102 	pci_set_drvdata(pdev, ci);
103 
104 	return 0;
105 }
106 
107 /**
108  * ci_hdrc_pci_remove: PCI remove
109  * @pdev: USB Device Controller being removed
110  *
111  * Reverses the effect of ci_hdrc_pci_probe(),
112  * first invoking the udc_remove() and then releases
113  * all PCI resources allocated for this USB device controller
114  */
115 static void ci_hdrc_pci_remove(struct pci_dev *pdev)
116 {
117 	struct ci_hdrc_pci *ci = pci_get_drvdata(pdev);
118 
119 	ci_hdrc_remove_device(ci->ci);
120 	usb_phy_generic_unregister(ci->phy);
121 }
122 
123 /*
124  * PCI device table
125  * PCI device structure
126  *
127  * Check "pci.h" for details
128  *
129  * Note: ehci-pci driver may try to probe the device first. You have to add an
130  * ID to the bypass_pci_id_table in ehci-pci driver to prevent this.
131  */
132 static const struct pci_device_id ci_hdrc_pci_id_table[] = {
133 	{
134 		PCI_DEVICE(0x153F, 0x1004),
135 		.driver_data = (kernel_ulong_t)&pci_platdata,
136 	},
137 	{
138 		PCI_DEVICE(0x153F, 0x1006),
139 		.driver_data = (kernel_ulong_t)&pci_platdata,
140 	},
141 	{
142 		PCI_VDEVICE(INTEL, 0x0811),
143 		.driver_data = (kernel_ulong_t)&langwell_pci_platdata,
144 	},
145 	{
146 		PCI_VDEVICE(INTEL, 0x0829),
147 		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
148 	},
149 	{
150 		/* Intel Clovertrail */
151 		PCI_VDEVICE(INTEL, 0xe006),
152 		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
153 	},
154 	{ 0 } /* end: all zeroes */
155 };
156 MODULE_DEVICE_TABLE(pci, ci_hdrc_pci_id_table);
157 
158 static struct pci_driver ci_hdrc_pci_driver = {
159 	.name         =	UDC_DRIVER_NAME,
160 	.id_table     =	ci_hdrc_pci_id_table,
161 	.probe        =	ci_hdrc_pci_probe,
162 	.remove       =	ci_hdrc_pci_remove,
163 };
164 
165 module_pci_driver(ci_hdrc_pci_driver);
166 
167 MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
168 MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
169 MODULE_LICENSE("GPL");
170 MODULE_ALIAS("platform:ci13xxx_pci");
171