1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2022 Microchip Technology Inc. |
3 | |
4 | #include <linux/mfd/core.h> |
5 | #include <linux/module.h> |
6 | #include <linux/pci.h> |
7 | #include <linux/spinlock.h> |
8 | #include <linux/gpio/driver.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/io.h> |
11 | #include <linux/idr.h> |
12 | #include "mchp_pci1xxxx_gp.h" |
13 | |
14 | struct aux_bus_device { |
15 | struct auxiliary_device_wrapper *aux_device_wrapper[2]; |
16 | }; |
17 | |
18 | static DEFINE_IDA(gp_client_ida); |
19 | static const char aux_dev_otp_e2p_name[15] = "gp_otp_e2p" ; |
20 | static const char aux_dev_gpio_name[15] = "gp_gpio" ; |
21 | |
22 | static void gp_auxiliary_device_release(struct device *dev) |
23 | { |
24 | struct auxiliary_device_wrapper *aux_device_wrapper = |
25 | (struct auxiliary_device_wrapper *)container_of(dev, |
26 | struct auxiliary_device_wrapper, aux_dev.dev); |
27 | |
28 | ida_free(&gp_client_ida, id: aux_device_wrapper->aux_dev.id); |
29 | kfree(objp: aux_device_wrapper); |
30 | } |
31 | |
32 | static int gp_aux_bus_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
33 | { |
34 | struct aux_bus_device *aux_bus; |
35 | int retval; |
36 | |
37 | retval = pcim_enable_device(pdev); |
38 | if (retval) |
39 | return retval; |
40 | |
41 | aux_bus = devm_kzalloc(dev: &pdev->dev, size: sizeof(*aux_bus), GFP_KERNEL); |
42 | if (!aux_bus) |
43 | return -ENOMEM; |
44 | |
45 | aux_bus->aux_device_wrapper[0] = kzalloc(size: sizeof(*aux_bus->aux_device_wrapper[0]), |
46 | GFP_KERNEL); |
47 | if (!aux_bus->aux_device_wrapper[0]) |
48 | return -ENOMEM; |
49 | |
50 | retval = ida_alloc(ida: &gp_client_ida, GFP_KERNEL); |
51 | if (retval < 0) |
52 | goto err_ida_alloc_0; |
53 | |
54 | aux_bus->aux_device_wrapper[0]->aux_dev.name = aux_dev_otp_e2p_name; |
55 | aux_bus->aux_device_wrapper[0]->aux_dev.dev.parent = &pdev->dev; |
56 | aux_bus->aux_device_wrapper[0]->aux_dev.dev.release = gp_auxiliary_device_release; |
57 | aux_bus->aux_device_wrapper[0]->aux_dev.id = retval; |
58 | |
59 | aux_bus->aux_device_wrapper[0]->gp_aux_data.region_start = pci_resource_start(pdev, 0); |
60 | aux_bus->aux_device_wrapper[0]->gp_aux_data.region_length = pci_resource_end(pdev, 0); |
61 | |
62 | retval = auxiliary_device_init(auxdev: &aux_bus->aux_device_wrapper[0]->aux_dev); |
63 | if (retval < 0) |
64 | goto err_aux_dev_init_0; |
65 | |
66 | retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[0]->aux_dev); |
67 | if (retval) |
68 | goto err_aux_dev_add_0; |
69 | |
70 | aux_bus->aux_device_wrapper[1] = kzalloc(size: sizeof(*aux_bus->aux_device_wrapper[1]), |
71 | GFP_KERNEL); |
72 | if (!aux_bus->aux_device_wrapper[1]) |
73 | return -ENOMEM; |
74 | |
75 | retval = ida_alloc(ida: &gp_client_ida, GFP_KERNEL); |
76 | if (retval < 0) |
77 | goto err_ida_alloc_1; |
78 | |
79 | aux_bus->aux_device_wrapper[1]->aux_dev.name = aux_dev_gpio_name; |
80 | aux_bus->aux_device_wrapper[1]->aux_dev.dev.parent = &pdev->dev; |
81 | aux_bus->aux_device_wrapper[1]->aux_dev.dev.release = gp_auxiliary_device_release; |
82 | aux_bus->aux_device_wrapper[1]->aux_dev.id = retval; |
83 | |
84 | aux_bus->aux_device_wrapper[1]->gp_aux_data.region_start = pci_resource_start(pdev, 0); |
85 | aux_bus->aux_device_wrapper[1]->gp_aux_data.region_length = pci_resource_end(pdev, 0); |
86 | |
87 | retval = pci_alloc_irq_vectors(dev: pdev, min_vecs: 1, max_vecs: 1, PCI_IRQ_ALL_TYPES); |
88 | |
89 | if (retval < 0) |
90 | goto err_aux_dev_init_1; |
91 | |
92 | retval = pci_irq_vector(dev: pdev, nr: 0); |
93 | if (retval < 0) |
94 | goto err_aux_dev_init_1; |
95 | |
96 | pdev->irq = retval; |
97 | aux_bus->aux_device_wrapper[1]->gp_aux_data.irq_num = pdev->irq; |
98 | |
99 | retval = auxiliary_device_init(auxdev: &aux_bus->aux_device_wrapper[1]->aux_dev); |
100 | if (retval < 0) |
101 | goto err_aux_dev_init_1; |
102 | |
103 | retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[1]->aux_dev); |
104 | if (retval) |
105 | goto err_aux_dev_add_1; |
106 | |
107 | pci_set_drvdata(pdev, data: aux_bus); |
108 | pci_set_master(dev: pdev); |
109 | |
110 | return 0; |
111 | |
112 | err_aux_dev_add_1: |
113 | auxiliary_device_uninit(auxdev: &aux_bus->aux_device_wrapper[1]->aux_dev); |
114 | |
115 | err_aux_dev_init_1: |
116 | ida_free(&gp_client_ida, id: aux_bus->aux_device_wrapper[1]->aux_dev.id); |
117 | |
118 | err_ida_alloc_1: |
119 | kfree(objp: aux_bus->aux_device_wrapper[1]); |
120 | |
121 | err_aux_dev_add_0: |
122 | auxiliary_device_uninit(auxdev: &aux_bus->aux_device_wrapper[0]->aux_dev); |
123 | |
124 | err_aux_dev_init_0: |
125 | ida_free(&gp_client_ida, id: aux_bus->aux_device_wrapper[0]->aux_dev.id); |
126 | |
127 | err_ida_alloc_0: |
128 | kfree(objp: aux_bus->aux_device_wrapper[0]); |
129 | |
130 | return retval; |
131 | } |
132 | |
133 | static void gp_aux_bus_remove(struct pci_dev *pdev) |
134 | { |
135 | struct aux_bus_device *aux_bus = pci_get_drvdata(pdev); |
136 | |
137 | auxiliary_device_delete(auxdev: &aux_bus->aux_device_wrapper[0]->aux_dev); |
138 | auxiliary_device_uninit(auxdev: &aux_bus->aux_device_wrapper[0]->aux_dev); |
139 | auxiliary_device_delete(auxdev: &aux_bus->aux_device_wrapper[1]->aux_dev); |
140 | auxiliary_device_uninit(auxdev: &aux_bus->aux_device_wrapper[1]->aux_dev); |
141 | } |
142 | |
143 | static const struct pci_device_id pci1xxxx_tbl[] = { |
144 | { PCI_DEVICE(0x1055, 0xA005) }, |
145 | { PCI_DEVICE(0x1055, 0xA015) }, |
146 | { PCI_DEVICE(0x1055, 0xA025) }, |
147 | { PCI_DEVICE(0x1055, 0xA035) }, |
148 | { PCI_DEVICE(0x1055, 0xA045) }, |
149 | { PCI_DEVICE(0x1055, 0xA055) }, |
150 | {0,} |
151 | }; |
152 | MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl); |
153 | |
154 | static struct pci_driver pci1xxxx_gp_driver = { |
155 | .name = "PCI1xxxxGP" , |
156 | .id_table = pci1xxxx_tbl, |
157 | .probe = gp_aux_bus_probe, |
158 | .remove = gp_aux_bus_remove, |
159 | }; |
160 | |
161 | module_pci_driver(pci1xxxx_gp_driver); |
162 | |
163 | MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander" ); |
164 | MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>" ); |
165 | MODULE_LICENSE("GPL" ); |
166 | |