Merge tag 'media/v5.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-block.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         return func;
64 }
65
66 static int cdns3_pci_probe(struct pci_dev *pdev,
67                            const struct pci_device_id *id)
68 {
69         struct platform_device_info plat_info;
70         struct cdns3_wrap *wrap;
71         struct resource *res;
72         struct pci_dev *func;
73         int err;
74
75         /*
76          * for GADGET/HOST PCI (devfn) function number is 0,
77          * for OTG PCI (devfn) function number is 1
78          */
79         if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
80                     pdev->devfn != PCI_DEV_FN_OTG))
81                 return -EINVAL;
82
83         func = cdns3_get_second_fun(pdev);
84         if (unlikely(!func))
85                 return -EINVAL;
86
87         err = pcim_enable_device(pdev);
88         if (err) {
89                 dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
90                 return err;
91         }
92
93         pci_set_master(pdev);
94
95         if (pci_is_enabled(func)) {
96                 wrap = pci_get_drvdata(func);
97         } else {
98                 wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
99                 if (!wrap) {
100                         pci_disable_device(pdev);
101                         return -ENOMEM;
102                 }
103         }
104
105         res = wrap->dev_res;
106
107         if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
108                 /* function 0: host(BAR_0) + device(BAR_1).*/
109                 dev_dbg(&pdev->dev, "Initialize Device resources\n");
110                 res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
111                 res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
112                 res[RES_DEV_ID].name = "dev";
113                 res[RES_DEV_ID].flags = IORESOURCE_MEM;
114                 dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
115                         &res[RES_DEV_ID].start);
116
117                 res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
118                 res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
119                 res[RES_HOST_ID].name = "xhci";
120                 res[RES_HOST_ID].flags = IORESOURCE_MEM;
121                 dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
122                         &res[RES_HOST_ID].start);
123
124                 /* Interrupt for XHCI */
125                 wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
126                 wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
127                 wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
128
129                 /* Interrupt device. It's the same as for HOST. */
130                 wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
131                 wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
132                 wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
133         } else {
134                 res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
135                 res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
136                 res[RES_DRD_ID].name = "otg";
137                 res[RES_DRD_ID].flags = IORESOURCE_MEM;
138                 dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
139                         &res[RES_DRD_ID].start);
140
141                 /* Interrupt for OTG/DRD. */
142                 wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
143                 wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
144                 wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
145         }
146
147         if (pci_is_enabled(func)) {
148                 /* set up platform device info */
149                 memset(&plat_info, 0, sizeof(plat_info));
150                 plat_info.parent = &pdev->dev;
151                 plat_info.fwnode = pdev->dev.fwnode;
152                 plat_info.name = PLAT_DRIVER_NAME;
153                 plat_info.id = pdev->devfn;
154                 wrap->devfn  = pdev->devfn;
155                 plat_info.res = wrap->dev_res;
156                 plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
157                 plat_info.dma_mask = pdev->dma_mask;
158                 /* register platform device */
159                 wrap->plat_dev = platform_device_register_full(&plat_info);
160                 if (IS_ERR(wrap->plat_dev)) {
161                         pci_disable_device(pdev);
162                         err = PTR_ERR(wrap->plat_dev);
163                         kfree(wrap);
164                         return err;
165                 }
166         }
167
168         pci_set_drvdata(pdev, wrap);
169         return err;
170 }
171
172 static void cdns3_pci_remove(struct pci_dev *pdev)
173 {
174         struct cdns3_wrap *wrap;
175         struct pci_dev *func;
176
177         func = cdns3_get_second_fun(pdev);
178
179         wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
180         if (wrap->devfn == pdev->devfn)
181                 platform_device_unregister(wrap->plat_dev);
182
183         if (!pci_is_enabled(func))
184                 kfree(wrap);
185 }
186
187 static const struct pci_device_id cdns3_pci_ids[] = {
188         { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
189         { 0, }
190 };
191
192 static struct pci_driver cdns3_pci_driver = {
193         .name = PCI_DRIVER_NAME,
194         .id_table = cdns3_pci_ids,
195         .probe = cdns3_pci_probe,
196         .remove = cdns3_pci_remove,
197 };
198
199 module_pci_driver(cdns3_pci_driver);
200 MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
201
202 MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
203 MODULE_LICENSE("GPL v2");
204 MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");