Commit | Line | Data |
---|---|---|
72246da4 FB |
1 | /** |
2 | * dwc3-pci.c - PCI Specific glue layer | |
3 | * | |
4 | * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com | |
72246da4 FB |
5 | * |
6 | * Authors: Felipe Balbi <balbi@ti.com>, | |
7 | * Sebastian Andrzej Siewior <bigeasy@linutronix.de> | |
8 | * | |
5945f789 FB |
9 | * This program is free software: you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 of | |
11 | * the License as published by the Free Software Foundation. | |
72246da4 | 12 | * |
5945f789 FB |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
72246da4 FB |
17 | */ |
18 | ||
19 | #include <linux/kernel.h> | |
46a57283 | 20 | #include <linux/module.h> |
72246da4 FB |
21 | #include <linux/slab.h> |
22 | #include <linux/pci.h> | |
23 | #include <linux/platform_device.h> | |
24 | ||
e3ec3eb7 | 25 | #include <linux/usb/otg.h> |
d7078df6 | 26 | #include <linux/usb/usb_phy_generic.h> |
e3ec3eb7 | 27 | |
8f317b47 HR |
28 | #include "platform_data.h" |
29 | ||
72246da4 FB |
30 | /* FIXME define these in <linux/pci_ids.h> */ |
31 | #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 | |
32 | #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd | |
b62cd96d | 33 | #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 |
85601f8c | 34 | #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e |
7d643664 | 35 | #define PCI_DEVICE_ID_INTEL_BSW 0x22B7 |
72246da4 | 36 | |
72246da4 FB |
37 | struct dwc3_pci { |
38 | struct device *dev; | |
39 | struct platform_device *dwc3; | |
e3ec3eb7 FB |
40 | struct platform_device *usb2_phy; |
41 | struct platform_device *usb3_phy; | |
72246da4 FB |
42 | }; |
43 | ||
41ac7b3a | 44 | static int dwc3_pci_register_phys(struct dwc3_pci *glue) |
e3ec3eb7 | 45 | { |
4525beeb | 46 | struct usb_phy_generic_platform_data pdata; |
e3ec3eb7 FB |
47 | struct platform_device *pdev; |
48 | int ret; | |
49 | ||
50 | memset(&pdata, 0x00, sizeof(pdata)); | |
51 | ||
4525beeb | 52 | pdev = platform_device_alloc("usb_phy_generic", 0); |
e3ec3eb7 FB |
53 | if (!pdev) |
54 | return -ENOMEM; | |
55 | ||
56 | glue->usb2_phy = pdev; | |
57 | pdata.type = USB_PHY_TYPE_USB2; | |
13518673 | 58 | pdata.gpio_reset = -1; |
e3ec3eb7 FB |
59 | |
60 | ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata)); | |
61 | if (ret) | |
62 | goto err1; | |
63 | ||
4525beeb | 64 | pdev = platform_device_alloc("usb_phy_generic", 1); |
e3ec3eb7 FB |
65 | if (!pdev) { |
66 | ret = -ENOMEM; | |
67 | goto err1; | |
68 | } | |
69 | ||
70 | glue->usb3_phy = pdev; | |
71 | pdata.type = USB_PHY_TYPE_USB3; | |
72 | ||
73 | ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata)); | |
74 | if (ret) | |
75 | goto err2; | |
76 | ||
77 | ret = platform_device_add(glue->usb2_phy); | |
78 | if (ret) | |
79 | goto err2; | |
80 | ||
81 | ret = platform_device_add(glue->usb3_phy); | |
82 | if (ret) | |
83 | goto err3; | |
84 | ||
85 | return 0; | |
86 | ||
87 | err3: | |
88 | platform_device_del(glue->usb2_phy); | |
89 | ||
90 | err2: | |
91 | platform_device_put(glue->usb3_phy); | |
92 | ||
93 | err1: | |
94 | platform_device_put(glue->usb2_phy); | |
95 | ||
96 | return ret; | |
97 | } | |
98 | ||
41ac7b3a | 99 | static int dwc3_pci_probe(struct pci_dev *pci, |
72246da4 FB |
100 | const struct pci_device_id *id) |
101 | { | |
102 | struct resource res[2]; | |
103 | struct platform_device *dwc3; | |
104 | struct dwc3_pci *glue; | |
b09e99ee | 105 | int ret; |
802ca850 | 106 | struct device *dev = &pci->dev; |
8f317b47 HR |
107 | struct dwc3_platform_data dwc3_pdata; |
108 | ||
109 | memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata)); | |
72246da4 | 110 | |
802ca850 | 111 | glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); |
734d5a53 | 112 | if (!glue) |
802ca850 | 113 | return -ENOMEM; |
72246da4 | 114 | |
1d046793 | 115 | glue->dev = dev; |
72246da4 | 116 | |
f1c7e710 | 117 | ret = pcim_enable_device(pci); |
72246da4 | 118 | if (ret) { |
802ca850 CP |
119 | dev_err(dev, "failed to enable pci device\n"); |
120 | return -ENODEV; | |
72246da4 FB |
121 | } |
122 | ||
72246da4 FB |
123 | pci_set_master(pci); |
124 | ||
e3ec3eb7 FB |
125 | ret = dwc3_pci_register_phys(glue); |
126 | if (ret) { | |
127 | dev_err(dev, "couldn't register PHYs\n"); | |
128 | return ret; | |
129 | } | |
130 | ||
124dafde | 131 | dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); |
72246da4 | 132 | if (!dwc3) { |
802ca850 | 133 | dev_err(dev, "couldn't allocate dwc3 device\n"); |
f1c7e710 | 134 | return -ENOMEM; |
72246da4 FB |
135 | } |
136 | ||
137 | memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); | |
138 | ||
139 | res[0].start = pci_resource_start(pci, 0); | |
140 | res[0].end = pci_resource_end(pci, 0); | |
141 | res[0].name = "dwc_usb3"; | |
142 | res[0].flags = IORESOURCE_MEM; | |
143 | ||
144 | res[1].start = pci->irq; | |
145 | res[1].name = "dwc_usb3"; | |
146 | res[1].flags = IORESOURCE_IRQ; | |
147 | ||
9755449d HR |
148 | if (pci->vendor == PCI_VENDOR_ID_AMD && |
149 | pci->device == PCI_DEVICE_ID_AMD_NL_USB) { | |
150 | dwc3_pdata.has_lpm_erratum = true; | |
151 | dwc3_pdata.lpm_nyet_threshold = 0xf; | |
152 | ||
153 | dwc3_pdata.u2exit_lfps_quirk = true; | |
154 | dwc3_pdata.u2ss_inp3_quirk = true; | |
155 | dwc3_pdata.req_p1p2p3_quirk = true; | |
156 | dwc3_pdata.del_p1p2p3_quirk = true; | |
157 | dwc3_pdata.del_phy_power_chg_quirk = true; | |
158 | dwc3_pdata.lfps_filter_quirk = true; | |
159 | dwc3_pdata.rx_detect_poll_quirk = true; | |
160 | ||
161 | dwc3_pdata.tx_de_emphasis_quirk = true; | |
162 | dwc3_pdata.tx_de_emphasis = 1; | |
163 | ||
164 | /* | |
165 | * FIXME these quirks should be removed when AMD NL | |
166 | * taps out | |
167 | */ | |
168 | dwc3_pdata.disable_scramble_quirk = true; | |
169 | dwc3_pdata.dis_u3_susphy_quirk = true; | |
170 | dwc3_pdata.dis_u2_susphy_quirk = true; | |
171 | } | |
172 | ||
72246da4 FB |
173 | ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); |
174 | if (ret) { | |
802ca850 | 175 | dev_err(dev, "couldn't add resources to dwc3 device\n"); |
f1c7e710 | 176 | return ret; |
72246da4 FB |
177 | } |
178 | ||
179 | pci_set_drvdata(pci, glue); | |
180 | ||
8f317b47 HR |
181 | ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata)); |
182 | if (ret) | |
183 | goto err3; | |
184 | ||
802ca850 | 185 | dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask); |
72246da4 | 186 | |
802ca850 CP |
187 | dwc3->dev.dma_mask = dev->dma_mask; |
188 | dwc3->dev.dma_parms = dev->dma_parms; | |
189 | dwc3->dev.parent = dev; | |
1d046793 | 190 | glue->dwc3 = dwc3; |
72246da4 FB |
191 | |
192 | ret = platform_device_add(dwc3); | |
193 | if (ret) { | |
802ca850 CP |
194 | dev_err(dev, "failed to register dwc3 device\n"); |
195 | goto err3; | |
72246da4 FB |
196 | } |
197 | ||
198 | return 0; | |
199 | ||
802ca850 | 200 | err3: |
72246da4 | 201 | platform_device_put(dwc3); |
72246da4 FB |
202 | return ret; |
203 | } | |
204 | ||
fb4e98ab | 205 | static void dwc3_pci_remove(struct pci_dev *pci) |
72246da4 FB |
206 | { |
207 | struct dwc3_pci *glue = pci_get_drvdata(pci); | |
208 | ||
f28c42c5 | 209 | platform_device_unregister(glue->dwc3); |
e3ec3eb7 FB |
210 | platform_device_unregister(glue->usb2_phy); |
211 | platform_device_unregister(glue->usb3_phy); | |
72246da4 FB |
212 | } |
213 | ||
782df20c | 214 | static const struct pci_device_id dwc3_pci_id_table[] = { |
72246da4 FB |
215 | { |
216 | PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, | |
217 | PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), | |
218 | }, | |
7d643664 | 219 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), }, |
b62cd96d | 220 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, |
85601f8c | 221 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, |
9755449d | 222 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, |
72246da4 FB |
223 | { } /* Terminating Entry */ |
224 | }; | |
225 | MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); | |
226 | ||
fb74d282 | 227 | #ifdef CONFIG_PM_SLEEP |
68907a77 FB |
228 | static int dwc3_pci_suspend(struct device *dev) |
229 | { | |
230 | struct pci_dev *pci = to_pci_dev(dev); | |
231 | ||
232 | pci_disable_device(pci); | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | static int dwc3_pci_resume(struct device *dev) | |
238 | { | |
239 | struct pci_dev *pci = to_pci_dev(dev); | |
240 | int ret; | |
241 | ||
242 | ret = pci_enable_device(pci); | |
243 | if (ret) { | |
244 | dev_err(dev, "can't re-enable device --> %d\n", ret); | |
245 | return ret; | |
246 | } | |
247 | ||
248 | pci_set_master(pci); | |
249 | ||
250 | return 0; | |
251 | } | |
fb74d282 | 252 | #endif /* CONFIG_PM_SLEEP */ |
68907a77 FB |
253 | |
254 | static const struct dev_pm_ops dwc3_pci_dev_pm_ops = { | |
255 | SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume) | |
256 | }; | |
257 | ||
72246da4 | 258 | static struct pci_driver dwc3_pci_driver = { |
0949e99b | 259 | .name = "dwc3-pci", |
72246da4 FB |
260 | .id_table = dwc3_pci_id_table, |
261 | .probe = dwc3_pci_probe, | |
7690417d | 262 | .remove = dwc3_pci_remove, |
68907a77 | 263 | .driver = { |
fb74d282 | 264 | .pm = &dwc3_pci_dev_pm_ops, |
68907a77 | 265 | }, |
72246da4 FB |
266 | }; |
267 | ||
268 | MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); | |
5945f789 | 269 | MODULE_LICENSE("GPL v2"); |
72246da4 FB |
270 | MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); |
271 | ||
95656336 | 272 | module_pci_driver(dwc3_pci_driver); |