Loading...
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
14struct aux_bus_device {
15 struct auxiliary_device_wrapper *aux_device_wrapper[2];
16};
17
18static DEFINE_IDA(gp_client_ida);
19static const char aux_dev_otp_e2p_name[15] = "gp_otp_e2p";
20static const char aux_dev_gpio_name[15] = "gp_gpio";
21
22static 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, aux_device_wrapper->aux_dev.id);
29 kfree(aux_device_wrapper);
30}
31
32static 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(&pdev->dev, sizeof(*aux_bus), GFP_KERNEL);
42 if (!aux_bus)
43 return -ENOMEM;
44
45 aux_bus->aux_device_wrapper[0] = kzalloc(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(&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(&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(sizeof(*aux_bus->aux_device_wrapper[1]),
71 GFP_KERNEL);
72 if (!aux_bus->aux_device_wrapper[1]) {
73 retval = -ENOMEM;
74 goto err_aux_dev_add_0;
75 }
76
77 retval = ida_alloc(&gp_client_ida, GFP_KERNEL);
78 if (retval < 0)
79 goto err_ida_alloc_1;
80
81 aux_bus->aux_device_wrapper[1]->aux_dev.name = aux_dev_gpio_name;
82 aux_bus->aux_device_wrapper[1]->aux_dev.dev.parent = &pdev->dev;
83 aux_bus->aux_device_wrapper[1]->aux_dev.dev.release = gp_auxiliary_device_release;
84 aux_bus->aux_device_wrapper[1]->aux_dev.id = retval;
85
86 aux_bus->aux_device_wrapper[1]->gp_aux_data.region_start = pci_resource_start(pdev, 0);
87 aux_bus->aux_device_wrapper[1]->gp_aux_data.region_length = pci_resource_end(pdev, 0);
88
89 retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
90
91 if (retval < 0)
92 goto err_aux_dev_init_1;
93
94 retval = pci_irq_vector(pdev, 0);
95 if (retval < 0)
96 goto err_aux_dev_init_1;
97
98 pdev->irq = retval;
99 aux_bus->aux_device_wrapper[1]->gp_aux_data.irq_num = pdev->irq;
100
101 retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[1]->aux_dev);
102 if (retval < 0)
103 goto err_aux_dev_init_1;
104
105 retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[1]->aux_dev);
106 if (retval)
107 goto err_aux_dev_add_1;
108
109 pci_set_drvdata(pdev, aux_bus);
110 pci_set_master(pdev);
111
112 return 0;
113
114err_aux_dev_add_1:
115 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
116 goto err_aux_dev_add_0;
117
118err_aux_dev_init_1:
119 ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id);
120
121err_ida_alloc_1:
122 kfree(aux_bus->aux_device_wrapper[1]);
123
124err_aux_dev_add_0:
125 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
126 goto err_ret;
127
128err_aux_dev_init_0:
129 ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id);
130
131err_ida_alloc_0:
132 kfree(aux_bus->aux_device_wrapper[0]);
133
134err_ret:
135 return retval;
136}
137
138static void gp_aux_bus_remove(struct pci_dev *pdev)
139{
140 struct aux_bus_device *aux_bus = pci_get_drvdata(pdev);
141
142 auxiliary_device_delete(&aux_bus->aux_device_wrapper[0]->aux_dev);
143 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
144 auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev);
145 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
146}
147
148static const struct pci_device_id pci1xxxx_tbl[] = {
149 { PCI_DEVICE(0x1055, 0xA005) },
150 { PCI_DEVICE(0x1055, 0xA015) },
151 { PCI_DEVICE(0x1055, 0xA025) },
152 { PCI_DEVICE(0x1055, 0xA035) },
153 { PCI_DEVICE(0x1055, 0xA045) },
154 { PCI_DEVICE(0x1055, 0xA055) },
155 {0,}
156};
157MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
158
159static struct pci_driver pci1xxxx_gp_driver = {
160 .name = "PCI1xxxxGP",
161 .id_table = pci1xxxx_tbl,
162 .probe = gp_aux_bus_probe,
163 .remove = gp_aux_bus_remove,
164};
165
166module_pci_driver(pci1xxxx_gp_driver);
167
168MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander");
169MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
170MODULE_LICENSE("GPL");
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
14struct aux_bus_device {
15 struct auxiliary_device_wrapper *aux_device_wrapper[2];
16};
17
18static DEFINE_IDA(gp_client_ida);
19static const char aux_dev_otp_e2p_name[15] = "gp_otp_e2p";
20static const char aux_dev_gpio_name[15] = "gp_gpio";
21
22static 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, aux_device_wrapper->aux_dev.id);
29 kfree(aux_device_wrapper);
30}
31
32static 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(&pdev->dev, sizeof(*aux_bus), GFP_KERNEL);
42 if (!aux_bus)
43 return -ENOMEM;
44
45 aux_bus->aux_device_wrapper[0] = kzalloc(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(&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(&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(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(&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(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
88
89 if (retval < 0)
90 goto err_aux_dev_init_1;
91
92 retval = pci_irq_vector(pdev, 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(&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, aux_bus);
108 pci_set_master(pdev);
109
110 return 0;
111
112err_aux_dev_add_1:
113 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
114
115err_aux_dev_init_1:
116 ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id);
117
118err_ida_alloc_1:
119 kfree(aux_bus->aux_device_wrapper[1]);
120
121err_aux_dev_add_0:
122 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
123
124err_aux_dev_init_0:
125 ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id);
126
127err_ida_alloc_0:
128 kfree(aux_bus->aux_device_wrapper[0]);
129
130 return retval;
131}
132
133static 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(&aux_bus->aux_device_wrapper[0]->aux_dev);
138 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
139 auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev);
140 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
141}
142
143static 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};
152MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
153
154static 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
161module_pci_driver(pci1xxxx_gp_driver);
162
163MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander");
164MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
165MODULE_LICENSE("GPL");