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, aux_device_wrapper->aux_dev.id); 29 kfree(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(&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 114 err_aux_dev_add_1: 115 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev); 116 goto err_aux_dev_add_0; 117 118 err_aux_dev_init_1: 119 ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id); 120 121 err_ida_alloc_1: 122 kfree(aux_bus->aux_device_wrapper[1]); 123 124 err_aux_dev_add_0: 125 auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev); 126 goto err_ret; 127 128 err_aux_dev_init_0: 129 ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id); 130 131 err_ida_alloc_0: 132 kfree(aux_bus->aux_device_wrapper[0]); 133 134 err_ret: 135 return retval; 136 } 137 138 static 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 148 static 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 }; 157 MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl); 158 159 static 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 166 module_pci_driver(pci1xxxx_gp_driver); 167 168 MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander"); 169 MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>"); 170 MODULE_LICENSE("GPL"); 171