1393fc2f5SKumaravel Thiagarajan // SPDX-License-Identifier: GPL-2.0
2393fc2f5SKumaravel Thiagarajan // Copyright (C) 2022 Microchip Technology Inc.
3393fc2f5SKumaravel Thiagarajan 
4393fc2f5SKumaravel Thiagarajan #include <linux/mfd/core.h>
5393fc2f5SKumaravel Thiagarajan #include <linux/module.h>
6393fc2f5SKumaravel Thiagarajan #include <linux/pci.h>
7393fc2f5SKumaravel Thiagarajan #include <linux/spinlock.h>
8393fc2f5SKumaravel Thiagarajan #include <linux/gpio/driver.h>
9393fc2f5SKumaravel Thiagarajan #include <linux/interrupt.h>
10393fc2f5SKumaravel Thiagarajan #include <linux/io.h>
11393fc2f5SKumaravel Thiagarajan #include <linux/idr.h>
12393fc2f5SKumaravel Thiagarajan #include "mchp_pci1xxxx_gp.h"
13393fc2f5SKumaravel Thiagarajan 
14393fc2f5SKumaravel Thiagarajan struct aux_bus_device {
15393fc2f5SKumaravel Thiagarajan 	struct auxiliary_device_wrapper *aux_device_wrapper[2];
16393fc2f5SKumaravel Thiagarajan };
17393fc2f5SKumaravel Thiagarajan 
18393fc2f5SKumaravel Thiagarajan static DEFINE_IDA(gp_client_ida);
19393fc2f5SKumaravel Thiagarajan static const char aux_dev_otp_e2p_name[15] = "gp_otp_e2p";
20393fc2f5SKumaravel Thiagarajan static const char aux_dev_gpio_name[15] = "gp_gpio";
21393fc2f5SKumaravel Thiagarajan 
gp_auxiliary_device_release(struct device * dev)22393fc2f5SKumaravel Thiagarajan static void gp_auxiliary_device_release(struct device *dev)
23393fc2f5SKumaravel Thiagarajan {
24393fc2f5SKumaravel Thiagarajan 	struct auxiliary_device_wrapper *aux_device_wrapper =
25393fc2f5SKumaravel Thiagarajan 		(struct auxiliary_device_wrapper *)container_of(dev,
26393fc2f5SKumaravel Thiagarajan 				struct auxiliary_device_wrapper, aux_dev.dev);
27393fc2f5SKumaravel Thiagarajan 
28393fc2f5SKumaravel Thiagarajan 	ida_free(&gp_client_ida, aux_device_wrapper->aux_dev.id);
29393fc2f5SKumaravel Thiagarajan 	kfree(aux_device_wrapper);
30393fc2f5SKumaravel Thiagarajan }
31393fc2f5SKumaravel Thiagarajan 
gp_aux_bus_probe(struct pci_dev * pdev,const struct pci_device_id * id)32393fc2f5SKumaravel Thiagarajan static int gp_aux_bus_probe(struct pci_dev *pdev, const struct pci_device_id *id)
33393fc2f5SKumaravel Thiagarajan {
34393fc2f5SKumaravel Thiagarajan 	struct aux_bus_device *aux_bus;
35393fc2f5SKumaravel Thiagarajan 	int retval;
36393fc2f5SKumaravel Thiagarajan 
37393fc2f5SKumaravel Thiagarajan 	retval = pcim_enable_device(pdev);
38393fc2f5SKumaravel Thiagarajan 	if (retval)
39393fc2f5SKumaravel Thiagarajan 		return retval;
40393fc2f5SKumaravel Thiagarajan 
4162e5d006SChristophe JAILLET 	aux_bus = devm_kzalloc(&pdev->dev, sizeof(*aux_bus), GFP_KERNEL);
42393fc2f5SKumaravel Thiagarajan 	if (!aux_bus)
43393fc2f5SKumaravel Thiagarajan 		return -ENOMEM;
44393fc2f5SKumaravel Thiagarajan 
45393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0] = kzalloc(sizeof(*aux_bus->aux_device_wrapper[0]),
46393fc2f5SKumaravel Thiagarajan 						 GFP_KERNEL);
47393fc2f5SKumaravel Thiagarajan 	if (!aux_bus->aux_device_wrapper[0])
48393fc2f5SKumaravel Thiagarajan 		return -ENOMEM;
49393fc2f5SKumaravel Thiagarajan 
50393fc2f5SKumaravel Thiagarajan 	retval = ida_alloc(&gp_client_ida, GFP_KERNEL);
51393fc2f5SKumaravel Thiagarajan 	if (retval < 0)
52393fc2f5SKumaravel Thiagarajan 		goto err_ida_alloc_0;
53393fc2f5SKumaravel Thiagarajan 
54393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0]->aux_dev.name = aux_dev_otp_e2p_name;
55393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0]->aux_dev.dev.parent = &pdev->dev;
56393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0]->aux_dev.dev.release = gp_auxiliary_device_release;
57393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0]->aux_dev.id = retval;
58393fc2f5SKumaravel Thiagarajan 
59393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0]->gp_aux_data.region_start = pci_resource_start(pdev, 0);
60393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[0]->gp_aux_data.region_length = pci_resource_end(pdev, 0);
61393fc2f5SKumaravel Thiagarajan 
62393fc2f5SKumaravel Thiagarajan 	retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[0]->aux_dev);
63393fc2f5SKumaravel Thiagarajan 	if (retval < 0)
64393fc2f5SKumaravel Thiagarajan 		goto err_aux_dev_init_0;
65393fc2f5SKumaravel Thiagarajan 
66393fc2f5SKumaravel Thiagarajan 	retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[0]->aux_dev);
67393fc2f5SKumaravel Thiagarajan 	if (retval)
68393fc2f5SKumaravel Thiagarajan 		goto err_aux_dev_add_0;
69393fc2f5SKumaravel Thiagarajan 
70393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1] = kzalloc(sizeof(*aux_bus->aux_device_wrapper[1]),
71393fc2f5SKumaravel Thiagarajan 						 GFP_KERNEL);
72*77427e3dSYongzhi Liu 	if (!aux_bus->aux_device_wrapper[1]) {
73*77427e3dSYongzhi Liu 		retval =  -ENOMEM;
74*77427e3dSYongzhi Liu 		goto err_aux_dev_add_0;
75*77427e3dSYongzhi Liu 	}
76393fc2f5SKumaravel Thiagarajan 
77393fc2f5SKumaravel Thiagarajan 	retval = ida_alloc(&gp_client_ida, GFP_KERNEL);
78393fc2f5SKumaravel Thiagarajan 	if (retval < 0)
79393fc2f5SKumaravel Thiagarajan 		goto err_ida_alloc_1;
80393fc2f5SKumaravel Thiagarajan 
81393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->aux_dev.name = aux_dev_gpio_name;
82393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->aux_dev.dev.parent = &pdev->dev;
83393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->aux_dev.dev.release = gp_auxiliary_device_release;
84393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->aux_dev.id = retval;
85393fc2f5SKumaravel Thiagarajan 
86393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->gp_aux_data.region_start = pci_resource_start(pdev, 0);
87393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->gp_aux_data.region_length = pci_resource_end(pdev, 0);
88393fc2f5SKumaravel Thiagarajan 
89393fc2f5SKumaravel Thiagarajan 	retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
90393fc2f5SKumaravel Thiagarajan 
91393fc2f5SKumaravel Thiagarajan 	if (retval < 0)
92c15d7e11SWei Yongjun 		goto err_aux_dev_init_1;
93393fc2f5SKumaravel Thiagarajan 
94c15d7e11SWei Yongjun 	retval = pci_irq_vector(pdev, 0);
95c15d7e11SWei Yongjun 	if (retval < 0)
96c15d7e11SWei Yongjun 		goto err_aux_dev_init_1;
97393fc2f5SKumaravel Thiagarajan 
98c15d7e11SWei Yongjun 	pdev->irq = retval;
99393fc2f5SKumaravel Thiagarajan 	aux_bus->aux_device_wrapper[1]->gp_aux_data.irq_num = pdev->irq;
100393fc2f5SKumaravel Thiagarajan 
101393fc2f5SKumaravel Thiagarajan 	retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[1]->aux_dev);
102393fc2f5SKumaravel Thiagarajan 	if (retval < 0)
103393fc2f5SKumaravel Thiagarajan 		goto err_aux_dev_init_1;
104393fc2f5SKumaravel Thiagarajan 
105393fc2f5SKumaravel Thiagarajan 	retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[1]->aux_dev);
106393fc2f5SKumaravel Thiagarajan 	if (retval)
107393fc2f5SKumaravel Thiagarajan 		goto err_aux_dev_add_1;
108393fc2f5SKumaravel Thiagarajan 
109393fc2f5SKumaravel Thiagarajan 	pci_set_drvdata(pdev, aux_bus);
110393fc2f5SKumaravel Thiagarajan 	pci_set_master(pdev);
111393fc2f5SKumaravel Thiagarajan 
112393fc2f5SKumaravel Thiagarajan 	return 0;
113393fc2f5SKumaravel Thiagarajan 
114393fc2f5SKumaravel Thiagarajan err_aux_dev_add_1:
115393fc2f5SKumaravel Thiagarajan 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
116086c6cbcSYongzhi Liu 	goto err_aux_dev_add_0;
117393fc2f5SKumaravel Thiagarajan 
118393fc2f5SKumaravel Thiagarajan err_aux_dev_init_1:
119393fc2f5SKumaravel Thiagarajan 	ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id);
120393fc2f5SKumaravel Thiagarajan 
121393fc2f5SKumaravel Thiagarajan err_ida_alloc_1:
122393fc2f5SKumaravel Thiagarajan 	kfree(aux_bus->aux_device_wrapper[1]);
123393fc2f5SKumaravel Thiagarajan 
124393fc2f5SKumaravel Thiagarajan err_aux_dev_add_0:
125393fc2f5SKumaravel Thiagarajan 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
126086c6cbcSYongzhi Liu 	goto err_ret;
127393fc2f5SKumaravel Thiagarajan 
128393fc2f5SKumaravel Thiagarajan err_aux_dev_init_0:
129393fc2f5SKumaravel Thiagarajan 	ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id);
130393fc2f5SKumaravel Thiagarajan 
131393fc2f5SKumaravel Thiagarajan err_ida_alloc_0:
132393fc2f5SKumaravel Thiagarajan 	kfree(aux_bus->aux_device_wrapper[0]);
133393fc2f5SKumaravel Thiagarajan 
134086c6cbcSYongzhi Liu err_ret:
135393fc2f5SKumaravel Thiagarajan 	return retval;
136393fc2f5SKumaravel Thiagarajan }
137393fc2f5SKumaravel Thiagarajan 
gp_aux_bus_remove(struct pci_dev * pdev)138393fc2f5SKumaravel Thiagarajan static void gp_aux_bus_remove(struct pci_dev *pdev)
139393fc2f5SKumaravel Thiagarajan {
140393fc2f5SKumaravel Thiagarajan 	struct aux_bus_device *aux_bus = pci_get_drvdata(pdev);
141393fc2f5SKumaravel Thiagarajan 
142393fc2f5SKumaravel Thiagarajan 	auxiliary_device_delete(&aux_bus->aux_device_wrapper[0]->aux_dev);
143393fc2f5SKumaravel Thiagarajan 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
144393fc2f5SKumaravel Thiagarajan 	auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev);
145393fc2f5SKumaravel Thiagarajan 	auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
146393fc2f5SKumaravel Thiagarajan }
147393fc2f5SKumaravel Thiagarajan 
148393fc2f5SKumaravel Thiagarajan static const struct pci_device_id pci1xxxx_tbl[] = {
149393fc2f5SKumaravel Thiagarajan 	{ PCI_DEVICE(0x1055, 0xA005) },
150393fc2f5SKumaravel Thiagarajan 	{ PCI_DEVICE(0x1055, 0xA015) },
151393fc2f5SKumaravel Thiagarajan 	{ PCI_DEVICE(0x1055, 0xA025) },
152393fc2f5SKumaravel Thiagarajan 	{ PCI_DEVICE(0x1055, 0xA035) },
153393fc2f5SKumaravel Thiagarajan 	{ PCI_DEVICE(0x1055, 0xA045) },
154393fc2f5SKumaravel Thiagarajan 	{ PCI_DEVICE(0x1055, 0xA055) },
155393fc2f5SKumaravel Thiagarajan 	{0,}
156393fc2f5SKumaravel Thiagarajan };
157393fc2f5SKumaravel Thiagarajan MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
158393fc2f5SKumaravel Thiagarajan 
159393fc2f5SKumaravel Thiagarajan static struct pci_driver pci1xxxx_gp_driver = {
160393fc2f5SKumaravel Thiagarajan 	.name = "PCI1xxxxGP",
161393fc2f5SKumaravel Thiagarajan 	.id_table = pci1xxxx_tbl,
162393fc2f5SKumaravel Thiagarajan 	.probe = gp_aux_bus_probe,
163393fc2f5SKumaravel Thiagarajan 	.remove = gp_aux_bus_remove,
164393fc2f5SKumaravel Thiagarajan };
165393fc2f5SKumaravel Thiagarajan 
166393fc2f5SKumaravel Thiagarajan module_pci_driver(pci1xxxx_gp_driver);
167393fc2f5SKumaravel Thiagarajan 
168393fc2f5SKumaravel Thiagarajan MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander");
169393fc2f5SKumaravel Thiagarajan MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
170393fc2f5SKumaravel Thiagarajan MODULE_LICENSE("GPL");
171