GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / usb / cdns3 / cdns3-pci-wrap.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Cadence USBSS PCI Glue driver
4  *
5  * Copyright (C) 2018-2019 Cadence.
6  *
7  * Author: Pawel Laszczak <pawell@cadence.com>
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/pci.h>
13 #include <linux/platform_device.h>
14 #include <linux/dma-mapping.h>
15 #include <linux/slab.h>
16
17 struct cdns3_wrap {
18         struct platform_device *plat_dev;
19         struct resource dev_res[6];
20         int devfn;
21 };
22
23 #define RES_IRQ_HOST_ID         0
24 #define RES_IRQ_PERIPHERAL_ID   1
25 #define RES_IRQ_OTG_ID          2
26 #define RES_HOST_ID             3
27 #define RES_DEV_ID              4
28 #define RES_DRD_ID              5
29
30 #define PCI_BAR_HOST            0
31 #define PCI_BAR_DEV             2
32 #define PCI_BAR_OTG             0
33
34 #define PCI_DEV_FN_HOST_DEVICE  0
35 #define PCI_DEV_FN_OTG          1
36
37 #define PCI_DRIVER_NAME         "cdns3-pci-usbss"
38 #define PLAT_DRIVER_NAME        "cdns-usb3"
39
40 #define CDNS_VENDOR_ID          0x17cd
41 #define CDNS_DEVICE_ID          0x0100
42
43 static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
44 {
45         struct pci_dev *func;
46
47         /*
48          * Gets the second function.
49          * It's little tricky, but this platform has two function.
50          * The fist keeps resources for Host/Device while the second
51          * keeps resources for DRD/OTG.
52          */
53         func = pci_get_device(pdev->vendor, pdev->device, NULL);
54         if (unlikely(!func))
55                 return NULL;
56
57         if (func->devfn == pdev->devfn) {
58                 func = pci_get_device(pdev->vendor, pdev->device, func);
59                 if (unlikely(!func))
60                         return NULL;
61         }
62
63         if (func->devfn != PCI_DEV_FN_HOST_DEVICE &&
64             func->devfn != PCI_DEV_FN_OTG) {
65                 return NULL;
66         }
67
68         return func;
69 }
70
71 static int cdns3_pci_probe(struct pci_dev *pdev,
72                            const struct pci_device_id *id)
73 {
74         struct platform_device_info plat_info;
75         struct cdns3_wrap *wrap;
76         struct resource *res;
77         struct pci_dev *func;
78         int err;
79
80         /*
81          * for GADGET/HOST PCI (devfn) function number is 0,
82          * for OTG PCI (devfn) function number is 1
83          */
84         if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
85                     pdev->devfn != PCI_DEV_FN_OTG))
86                 return -EINVAL;
87
88         func = cdns3_get_second_fun(pdev);
89         if (unlikely(!func))
90                 return -EINVAL;
91
92         err = pcim_enable_device(pdev);
93         if (err) {
94                 dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
95                 return err;
96         }
97
98         pci_set_master(pdev);
99
100         if (pci_is_enabled(func)) {
101                 wrap = pci_get_drvdata(func);
102         } else {
103                 wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
104                 if (!wrap) {
105                         pci_disable_device(pdev);
106                         return -ENOMEM;
107                 }
108         }
109
110         res = wrap->dev_res;
111
112         if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
113                 /* function 0: host(BAR_0) + device(BAR_1).*/
114                 dev_dbg(&pdev->dev, "Initialize Device resources\n");
115                 res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
116                 res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
117                 res[RES_DEV_ID].name = "dev";
118                 res[RES_DEV_ID].flags = IORESOURCE_MEM;
119                 dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
120                         &res[RES_DEV_ID].start);
121
122                 res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
123                 res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
124                 res[RES_HOST_ID].name = "xhci";
125                 res[RES_HOST_ID].flags = IORESOURCE_MEM;
126                 dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
127                         &res[RES_HOST_ID].start);
128
129                 /* Interrupt for XHCI */
130                 wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
131                 wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
132                 wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
133
134                 /* Interrupt device. It's the same as for HOST. */
135                 wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
136                 wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
137                 wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
138         } else {
139                 res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
140                 res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
141                 res[RES_DRD_ID].name = "otg";
142                 res[RES_DRD_ID].flags = IORESOURCE_MEM;
143                 dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
144                         &res[RES_DRD_ID].start);
145
146                 /* Interrupt for OTG/DRD. */
147                 wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
148                 wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
149                 wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
150         }
151
152         if (pci_is_enabled(func)) {
153                 /* set up platform device info */
154                 memset(&plat_info, 0, sizeof(plat_info));
155                 plat_info.parent = &pdev->dev;
156                 plat_info.fwnode = pdev->dev.fwnode;
157                 plat_info.name = PLAT_DRIVER_NAME;
158                 plat_info.id = pdev->devfn;
159                 wrap->devfn  = pdev->devfn;
160                 plat_info.res = wrap->dev_res;
161                 plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
162                 plat_info.dma_mask = pdev->dma_mask;
163                 /* register platform device */
164                 wrap->plat_dev = platform_device_register_full(&plat_info);
165                 if (IS_ERR(wrap->plat_dev)) {
166                         pci_disable_device(pdev);
167                         err = PTR_ERR(wrap->plat_dev);
168                         kfree(wrap);
169                         return err;
170                 }
171         }
172
173         pci_set_drvdata(pdev, wrap);
174         return err;
175 }
176
177 static void cdns3_pci_remove(struct pci_dev *pdev)
178 {
179         struct cdns3_wrap *wrap;
180         struct pci_dev *func;
181
182         func = cdns3_get_second_fun(pdev);
183
184         wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
185         if (wrap->devfn == pdev->devfn)
186                 platform_device_unregister(wrap->plat_dev);
187
188         if (!pci_is_enabled(func))
189                 kfree(wrap);
190 }
191
192 static const struct pci_device_id cdns3_pci_ids[] = {
193         { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
194         { 0, }
195 };
196
197 static struct pci_driver cdns3_pci_driver = {
198         .name = PCI_DRIVER_NAME,
199         .id_table = cdns3_pci_ids,
200         .probe = cdns3_pci_probe,
201         .remove = cdns3_pci_remove,
202 };
203
204 module_pci_driver(cdns3_pci_driver);
205 MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
206
207 MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
208 MODULE_LICENSE("GPL v2");
209 MODULE_DESCRIPTION("Cadence USBSS PCI wrapper");