xref: /linux/drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c (revision 32a92f8c89326985e05dce8b22d3f0aa07a3e1bd)
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 
gp_auxiliary_device_release(struct device * dev)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, aux_device_wrapper->aux_dev.id);
29 	kfree(aux_device_wrapper);
30 }
31 
gp_aux_bus_probe(struct pci_dev * pdev,const struct pci_device_id * id)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(&pdev->dev, sizeof(*aux_bus), GFP_KERNEL);
42 	if (!aux_bus)
43 		return -ENOMEM;
44 
45 	aux_bus->aux_device_wrapper[0] = kzalloc_obj(*aux_bus->aux_device_wrapper[0]);
46 	if (!aux_bus->aux_device_wrapper[0])
47 		return -ENOMEM;
48 
49 	retval = ida_alloc(&gp_client_ida, GFP_KERNEL);
50 	if (retval < 0)
51 		goto err_ida_alloc_0;
52 
53 	aux_bus->aux_device_wrapper[0]->aux_dev.name = aux_dev_otp_e2p_name;
54 	aux_bus->aux_device_wrapper[0]->aux_dev.dev.parent = &pdev->dev;
55 	aux_bus->aux_device_wrapper[0]->aux_dev.dev.release = gp_auxiliary_device_release;
56 	aux_bus->aux_device_wrapper[0]->aux_dev.id = retval;
57 
58 	aux_bus->aux_device_wrapper[0]->gp_aux_data.region_start = pci_resource_start(pdev, 0);
59 	aux_bus->aux_device_wrapper[0]->gp_aux_data.region_length = pci_resource_end(pdev, 0);
60 
61 	retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[0]->aux_dev);
62 	if (retval < 0)
63 		goto err_aux_dev_init_0;
64 
65 	retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[0]->aux_dev);
66 	if (retval)
67 		goto err_aux_dev_add_0;
68 
69 	aux_bus->aux_device_wrapper[1] = kzalloc_obj(*aux_bus->aux_device_wrapper[1]);
70 	if (!aux_bus->aux_device_wrapper[1]) {
71 		retval =  -ENOMEM;
72 		goto err_aux_dev_add_0;
73 	}
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 
112 err_aux_dev_add_1:
113 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
114 	goto err_aux_dev_add_0;
115 
116 err_aux_dev_init_1:
117 	ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id);
118 
119 err_ida_alloc_1:
120 	kfree(aux_bus->aux_device_wrapper[1]);
121 
122 err_aux_dev_add_0:
123 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
124 	goto err_ret;
125 
126 err_aux_dev_init_0:
127 	ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id);
128 
129 err_ida_alloc_0:
130 	kfree(aux_bus->aux_device_wrapper[0]);
131 
132 err_ret:
133 	return retval;
134 }
135 
gp_aux_bus_remove(struct pci_dev * pdev)136 static void gp_aux_bus_remove(struct pci_dev *pdev)
137 {
138 	struct aux_bus_device *aux_bus = pci_get_drvdata(pdev);
139 
140 	auxiliary_device_delete(&aux_bus->aux_device_wrapper[0]->aux_dev);
141 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
142 	auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev);
143 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
144 }
145 
146 static const struct pci_device_id pci1xxxx_tbl[] = {
147 	{ PCI_DEVICE(0x1055, 0xA005) },
148 	{ PCI_DEVICE(0x1055, 0xA015) },
149 	{ PCI_DEVICE(0x1055, 0xA025) },
150 	{ PCI_DEVICE(0x1055, 0xA035) },
151 	{ PCI_DEVICE(0x1055, 0xA045) },
152 	{ PCI_DEVICE(0x1055, 0xA055) },
153 	{0,}
154 };
155 MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
156 
157 static struct pci_driver pci1xxxx_gp_driver = {
158 	.name = "PCI1xxxxGP",
159 	.id_table = pci1xxxx_tbl,
160 	.probe = gp_aux_bus_probe,
161 	.remove = gp_aux_bus_remove,
162 };
163 
164 module_pci_driver(pci1xxxx_gp_driver);
165 
166 MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander");
167 MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
168 MODULE_LICENSE("GPL");
169