171 lines · c
1// SPDX-License-Identifier: GPL-2.02// 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");171