1/**
2 * dwc3-pci.c - PCI Specific glue layer
3 *
4 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * Authors: Felipe Balbi <balbi@ti.com>,
7 *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
8 *
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.
12 *
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.
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/slab.h>
22#include <linux/pci.h>
23#include <linux/platform_device.h>
24
25#include "platform_data.h"
26
27#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3	0xabcd
28#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce
29#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf
30#define PCI_DEVICE_ID_INTEL_BYT		0x0f37
31#define PCI_DEVICE_ID_INTEL_MRFLD	0x119e
32#define PCI_DEVICE_ID_INTEL_BSW		0x22B7
33#define PCI_DEVICE_ID_INTEL_SPTLP	0x9d30
34#define PCI_DEVICE_ID_INTEL_SPTH	0xa130
35
36static int dwc3_pci_quirks(struct pci_dev *pdev)
37{
38	if (pdev->vendor == PCI_VENDOR_ID_AMD &&
39	    pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
40		struct dwc3_platform_data pdata;
41
42		memset(&pdata, 0, sizeof(pdata));
43
44		pdata.has_lpm_erratum = true;
45		pdata.lpm_nyet_threshold = 0xf;
46
47		pdata.u2exit_lfps_quirk = true;
48		pdata.u2ss_inp3_quirk = true;
49		pdata.req_p1p2p3_quirk = true;
50		pdata.del_p1p2p3_quirk = true;
51		pdata.del_phy_power_chg_quirk = true;
52		pdata.lfps_filter_quirk = true;
53		pdata.rx_detect_poll_quirk = true;
54
55		pdata.tx_de_emphasis_quirk = true;
56		pdata.tx_de_emphasis = 1;
57
58		/*
59		 * FIXME these quirks should be removed when AMD NL
60		 * taps out
61		 */
62		pdata.disable_scramble_quirk = true;
63		pdata.dis_u3_susphy_quirk = true;
64		pdata.dis_u2_susphy_quirk = true;
65
66		return platform_device_add_data(pci_get_drvdata(pdev), &pdata,
67						sizeof(pdata));
68	}
69
70	if (pdev->vendor == PCI_VENDOR_ID_SYNOPSYS &&
71	    (pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 ||
72	     pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI ||
73	     pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) {
74
75		struct dwc3_platform_data pdata;
76
77		memset(&pdata, 0, sizeof(pdata));
78		pdata.usb3_lpm_capable = true;
79		pdata.has_lpm_erratum = true;
80
81		return platform_device_add_data(pci_get_drvdata(pdev), &pdata,
82						sizeof(pdata));
83	}
84
85	return 0;
86}
87
88static int dwc3_pci_probe(struct pci_dev *pci,
89		const struct pci_device_id *id)
90{
91	struct resource		res[2];
92	struct platform_device	*dwc3;
93	int			ret;
94	struct device		*dev = &pci->dev;
95
96	ret = pcim_enable_device(pci);
97	if (ret) {
98		dev_err(dev, "failed to enable pci device\n");
99		return -ENODEV;
100	}
101
102	pci_set_master(pci);
103
104	dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
105	if (!dwc3) {
106		dev_err(dev, "couldn't allocate dwc3 device\n");
107		return -ENOMEM;
108	}
109
110	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
111
112	res[0].start	= pci_resource_start(pci, 0);
113	res[0].end	= pci_resource_end(pci, 0);
114	res[0].name	= "dwc_usb3";
115	res[0].flags	= IORESOURCE_MEM;
116
117	res[1].start	= pci->irq;
118	res[1].name	= "dwc_usb3";
119	res[1].flags	= IORESOURCE_IRQ;
120
121	ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
122	if (ret) {
123		dev_err(dev, "couldn't add resources to dwc3 device\n");
124		return ret;
125	}
126
127	pci_set_drvdata(pci, dwc3);
128	ret = dwc3_pci_quirks(pci);
129	if (ret)
130		goto err;
131
132	dwc3->dev.parent = dev;
133
134	ret = platform_device_add(dwc3);
135	if (ret) {
136		dev_err(dev, "failed to register dwc3 device\n");
137		goto err;
138	}
139
140	return 0;
141err:
142	platform_device_put(dwc3);
143	return ret;
144}
145
146static void dwc3_pci_remove(struct pci_dev *pci)
147{
148	platform_device_unregister(pci_get_drvdata(pci));
149}
150
151static const struct pci_device_id dwc3_pci_id_table[] = {
152	{
153		PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
154				PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
155	},
156	{
157		PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
158				PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI),
159	},
160	{
161		PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
162				PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31),
163	},
164	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), },
165	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
166	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
167	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), },
168	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), },
169	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
170	{  }	/* Terminating Entry */
171};
172MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
173
174static struct pci_driver dwc3_pci_driver = {
175	.name		= "dwc3-pci",
176	.id_table	= dwc3_pci_id_table,
177	.probe		= dwc3_pci_probe,
178	.remove		= dwc3_pci_remove,
179};
180
181MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
182MODULE_LICENSE("GPL v2");
183MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
184
185module_pci_driver(dwc3_pci_driver);
186