]> Git Repo - linux.git/blob - drivers/misc/lan966x_pci.c
Merge tag 'i2c-for-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux.git] / drivers / misc / lan966x_pci.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Microchip LAN966x PCI driver
4  *
5  * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
6  *
7  * Authors:
8  *      Clément Léger <[email protected]>
9  *      Hervé Codina <[email protected]>
10  */
11
12 #include <linux/device.h>
13 #include <linux/irq.h>
14 #include <linux/irqdomain.h>
15 #include <linux/module.h>
16 #include <linux/of_platform.h>
17 #include <linux/pci.h>
18 #include <linux/pci_ids.h>
19 #include <linux/slab.h>
20
21 /* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */
22 extern char __dtbo_lan966x_pci_begin[];
23 extern char __dtbo_lan966x_pci_end[];
24
25 struct pci_dev_intr_ctrl {
26         struct pci_dev *pci_dev;
27         struct irq_domain *irq_domain;
28         int irq;
29 };
30
31 static int pci_dev_irq_domain_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw)
32 {
33         irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
34         return 0;
35 }
36
37 static const struct irq_domain_ops pci_dev_irq_domain_ops = {
38         .map = pci_dev_irq_domain_map,
39         .xlate = irq_domain_xlate_onecell,
40 };
41
42 static irqreturn_t pci_dev_irq_handler(int irq, void *data)
43 {
44         struct pci_dev_intr_ctrl *intr_ctrl = data;
45         int ret;
46
47         ret = generic_handle_domain_irq(intr_ctrl->irq_domain, 0);
48         return ret ? IRQ_NONE : IRQ_HANDLED;
49 }
50
51 static struct pci_dev_intr_ctrl *pci_dev_create_intr_ctrl(struct pci_dev *pdev)
52 {
53         struct pci_dev_intr_ctrl *intr_ctrl __free(kfree) = NULL;
54         struct fwnode_handle *fwnode;
55         int ret;
56
57         fwnode = dev_fwnode(&pdev->dev);
58         if (!fwnode)
59                 return ERR_PTR(-ENODEV);
60
61         intr_ctrl = kmalloc(sizeof(*intr_ctrl), GFP_KERNEL);
62         if (!intr_ctrl)
63                 return ERR_PTR(-ENOMEM);
64
65         intr_ctrl->pci_dev = pdev;
66
67         intr_ctrl->irq_domain = irq_domain_create_linear(fwnode, 1, &pci_dev_irq_domain_ops,
68                                                          intr_ctrl);
69         if (!intr_ctrl->irq_domain) {
70                 pci_err(pdev, "Failed to create irqdomain\n");
71                 return ERR_PTR(-ENOMEM);
72         }
73
74         ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX);
75         if (ret < 0) {
76                 pci_err(pdev, "Unable alloc irq vector (%d)\n", ret);
77                 goto err_remove_domain;
78         }
79         intr_ctrl->irq = pci_irq_vector(pdev, 0);
80         ret = request_irq(intr_ctrl->irq, pci_dev_irq_handler, IRQF_SHARED,
81                           pci_name(pdev), intr_ctrl);
82         if (ret) {
83                 pci_err(pdev, "Unable to request irq %d (%d)\n", intr_ctrl->irq, ret);
84                 goto err_free_irq_vector;
85         }
86
87         return_ptr(intr_ctrl);
88
89 err_free_irq_vector:
90         pci_free_irq_vectors(pdev);
91 err_remove_domain:
92         irq_domain_remove(intr_ctrl->irq_domain);
93         return ERR_PTR(ret);
94 }
95
96 static void pci_dev_remove_intr_ctrl(struct pci_dev_intr_ctrl *intr_ctrl)
97 {
98         free_irq(intr_ctrl->irq, intr_ctrl);
99         pci_free_irq_vectors(intr_ctrl->pci_dev);
100         irq_dispose_mapping(irq_find_mapping(intr_ctrl->irq_domain, 0));
101         irq_domain_remove(intr_ctrl->irq_domain);
102         kfree(intr_ctrl);
103 }
104
105 static void devm_pci_dev_remove_intr_ctrl(void *intr_ctrl)
106 {
107         pci_dev_remove_intr_ctrl(intr_ctrl);
108 }
109
110 static int devm_pci_dev_create_intr_ctrl(struct pci_dev *pdev)
111 {
112         struct pci_dev_intr_ctrl *intr_ctrl;
113
114         intr_ctrl = pci_dev_create_intr_ctrl(pdev);
115         if (IS_ERR(intr_ctrl))
116                 return PTR_ERR(intr_ctrl);
117
118         return devm_add_action_or_reset(&pdev->dev, devm_pci_dev_remove_intr_ctrl, intr_ctrl);
119 }
120
121 struct lan966x_pci {
122         struct device *dev;
123         int ovcs_id;
124 };
125
126 static int lan966x_pci_load_overlay(struct lan966x_pci *data)
127 {
128         u32 dtbo_size = __dtbo_lan966x_pci_end - __dtbo_lan966x_pci_begin;
129         void *dtbo_start = __dtbo_lan966x_pci_begin;
130
131         return of_overlay_fdt_apply(dtbo_start, dtbo_size, &data->ovcs_id, dev_of_node(data->dev));
132 }
133
134 static void lan966x_pci_unload_overlay(struct lan966x_pci *data)
135 {
136         of_overlay_remove(&data->ovcs_id);
137 }
138
139 static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
140 {
141         struct device *dev = &pdev->dev;
142         struct lan966x_pci *data;
143         int ret;
144
145         /*
146          * On ACPI system, fwnode can point to the ACPI node.
147          * This driver needs an of_node to be used as the device-tree overlay
148          * target. This of_node should be set by the PCI core if it succeeds in
149          * creating it (CONFIG_PCI_DYNAMIC_OF_NODES feature).
150          * Check here for the validity of this of_node.
151          */
152         if (!dev_of_node(dev))
153                 return dev_err_probe(dev, -EINVAL, "Missing of_node for device\n");
154
155         /* Need to be done before devm_pci_dev_create_intr_ctrl.
156          * It allocates an IRQ and so pdev->irq is updated.
157          */
158         ret = pcim_enable_device(pdev);
159         if (ret)
160                 return ret;
161
162         ret = devm_pci_dev_create_intr_ctrl(pdev);
163         if (ret)
164                 return ret;
165
166         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
167         if (!data)
168                 return -ENOMEM;
169
170         pci_set_drvdata(pdev, data);
171         data->dev = dev;
172
173         ret = lan966x_pci_load_overlay(data);
174         if (ret)
175                 return ret;
176
177         pci_set_master(pdev);
178
179         ret = of_platform_default_populate(dev_of_node(dev), NULL, dev);
180         if (ret)
181                 goto err_unload_overlay;
182
183         return 0;
184
185 err_unload_overlay:
186         lan966x_pci_unload_overlay(data);
187         return ret;
188 }
189
190 static void lan966x_pci_remove(struct pci_dev *pdev)
191 {
192         struct lan966x_pci *data = pci_get_drvdata(pdev);
193
194         of_platform_depopulate(data->dev);
195
196         lan966x_pci_unload_overlay(data);
197 }
198
199 static struct pci_device_id lan966x_pci_ids[] = {
200         { PCI_DEVICE(PCI_VENDOR_ID_EFAR, 0x9660) },
201         { }
202 };
203 MODULE_DEVICE_TABLE(pci, lan966x_pci_ids);
204
205 static struct pci_driver lan966x_pci_driver = {
206         .name = "mchp_lan966x_pci",
207         .id_table = lan966x_pci_ids,
208         .probe = lan966x_pci_probe,
209         .remove = lan966x_pci_remove,
210 };
211 module_pci_driver(lan966x_pci_driver);
212
213 MODULE_AUTHOR("Herve Codina <[email protected]>");
214 MODULE_DESCRIPTION("Microchip LAN966x PCI driver");
215 MODULE_LICENSE("GPL");
This page took 0.042495 seconds and 4 git commands to generate.