misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO function in the multi-function endpoint of pci1xxxx device.
pci1xxxx is a PCIe switch with a multi-function endpoint on one of its downstream ports. PIO function is one of the functions in the multi-function endpoint. PIO function combines a GPIO controller and also an interface to program pci1xxxx's OTP & EEPROM. This auxiliary bus driver is loaded for the PIO function and separate child devices are enumerated for GPIO controller and OTP/EEPROM interface. Signed-off-by: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Link: https://lore.kernel.org/r/20220824200047.150308-2-kumaravel.thiagarajan@microchip.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
a68108c837
commit
393fc2f594
|
@ -13422,6 +13422,13 @@ S: Supported
|
|||
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
|
||||
F: drivers/mtd/nand/raw/atmel/*
|
||||
|
||||
MICROCHIP PCI1XXXX GP DRIVER
|
||||
M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
|
||||
F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h
|
||||
|
||||
MICROCHIP OTPC DRIVER
|
||||
M: Claudiu Beznea <claudiu.beznea@microchip.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
|
@ -513,4 +513,5 @@ source "drivers/misc/cardreader/Kconfig"
|
|||
source "drivers/misc/habanalabs/Kconfig"
|
||||
source "drivers/misc/uacce/Kconfig"
|
||||
source "drivers/misc/pvpanic/Kconfig"
|
||||
source "drivers/misc/mchp_pci1xxxx/Kconfig"
|
||||
endmenu
|
||||
|
|
|
@ -60,4 +60,5 @@ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
|
|||
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
|
||||
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
||||
obj-$(CONFIG_OPEN_DICE) += open-dice.o
|
||||
obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
|
||||
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
|
||||
obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
config GP_PCI1XXXX
|
||||
tristate "Microchip PCI1XXXX PCIe to GPIO Expander + OTP/EEPROM manager"
|
||||
depends on PCI
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
PCI1XXXX is a PCIe GEN 3 switch with one of the endpoints having
|
||||
multiple functions and one of the functions is a GPIO controller
|
||||
which also has registers to interface with the OTP and EEPROM.
|
||||
Select yes, no or module here to include or exclude the driver
|
||||
for the GPIO function.
|
||||
|
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o
|
|
@ -0,0 +1,166 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2022 Microchip Technology Inc.
|
||||
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/idr.h>
|
||||
#include "mchp_pci1xxxx_gp.h"
|
||||
|
||||
struct aux_bus_device {
|
||||
struct auxiliary_device_wrapper *aux_device_wrapper[2];
|
||||
};
|
||||
|
||||
static DEFINE_IDA(gp_client_ida);
|
||||
static const char aux_dev_otp_e2p_name[15] = "gp_otp_e2p";
|
||||
static const char aux_dev_gpio_name[15] = "gp_gpio";
|
||||
|
||||
static void gp_auxiliary_device_release(struct device *dev)
|
||||
{
|
||||
struct auxiliary_device_wrapper *aux_device_wrapper =
|
||||
(struct auxiliary_device_wrapper *)container_of(dev,
|
||||
struct auxiliary_device_wrapper, aux_dev.dev);
|
||||
|
||||
ida_free(&gp_client_ida, aux_device_wrapper->aux_dev.id);
|
||||
kfree(aux_device_wrapper);
|
||||
}
|
||||
|
||||
static int gp_aux_bus_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct aux_bus_device *aux_bus;
|
||||
int retval;
|
||||
|
||||
retval = pcim_enable_device(pdev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
aux_bus = kzalloc(sizeof(*aux_bus), GFP_KERNEL);
|
||||
if (!aux_bus)
|
||||
return -ENOMEM;
|
||||
|
||||
aux_bus->aux_device_wrapper[0] = kzalloc(sizeof(*aux_bus->aux_device_wrapper[0]),
|
||||
GFP_KERNEL);
|
||||
if (!aux_bus->aux_device_wrapper[0])
|
||||
return -ENOMEM;
|
||||
|
||||
retval = ida_alloc(&gp_client_ida, GFP_KERNEL);
|
||||
if (retval < 0)
|
||||
goto err_ida_alloc_0;
|
||||
|
||||
aux_bus->aux_device_wrapper[0]->aux_dev.name = aux_dev_otp_e2p_name;
|
||||
aux_bus->aux_device_wrapper[0]->aux_dev.dev.parent = &pdev->dev;
|
||||
aux_bus->aux_device_wrapper[0]->aux_dev.dev.release = gp_auxiliary_device_release;
|
||||
aux_bus->aux_device_wrapper[0]->aux_dev.id = retval;
|
||||
|
||||
aux_bus->aux_device_wrapper[0]->gp_aux_data.region_start = pci_resource_start(pdev, 0);
|
||||
aux_bus->aux_device_wrapper[0]->gp_aux_data.region_length = pci_resource_end(pdev, 0);
|
||||
|
||||
retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[0]->aux_dev);
|
||||
if (retval < 0)
|
||||
goto err_aux_dev_init_0;
|
||||
|
||||
retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[0]->aux_dev);
|
||||
if (retval)
|
||||
goto err_aux_dev_add_0;
|
||||
|
||||
aux_bus->aux_device_wrapper[1] = kzalloc(sizeof(*aux_bus->aux_device_wrapper[1]),
|
||||
GFP_KERNEL);
|
||||
if (!aux_bus->aux_device_wrapper[1])
|
||||
return -ENOMEM;
|
||||
|
||||
retval = ida_alloc(&gp_client_ida, GFP_KERNEL);
|
||||
if (retval < 0)
|
||||
goto err_ida_alloc_1;
|
||||
|
||||
aux_bus->aux_device_wrapper[1]->aux_dev.name = aux_dev_gpio_name;
|
||||
aux_bus->aux_device_wrapper[1]->aux_dev.dev.parent = &pdev->dev;
|
||||
aux_bus->aux_device_wrapper[1]->aux_dev.dev.release = gp_auxiliary_device_release;
|
||||
aux_bus->aux_device_wrapper[1]->aux_dev.id = retval;
|
||||
|
||||
aux_bus->aux_device_wrapper[1]->gp_aux_data.region_start = pci_resource_start(pdev, 0);
|
||||
aux_bus->aux_device_wrapper[1]->gp_aux_data.region_length = pci_resource_end(pdev, 0);
|
||||
|
||||
retval = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
pdev->irq = pci_irq_vector(pdev, 0);
|
||||
if (pdev->irq < 0)
|
||||
return retval;
|
||||
|
||||
aux_bus->aux_device_wrapper[1]->gp_aux_data.irq_num = pdev->irq;
|
||||
|
||||
retval = auxiliary_device_init(&aux_bus->aux_device_wrapper[1]->aux_dev);
|
||||
if (retval < 0)
|
||||
goto err_aux_dev_init_1;
|
||||
|
||||
retval = auxiliary_device_add(&aux_bus->aux_device_wrapper[1]->aux_dev);
|
||||
if (retval)
|
||||
goto err_aux_dev_add_1;
|
||||
|
||||
pci_set_drvdata(pdev, aux_bus);
|
||||
pci_set_master(pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_aux_dev_add_1:
|
||||
auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
|
||||
|
||||
err_aux_dev_init_1:
|
||||
ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[1]->aux_dev.id);
|
||||
|
||||
err_ida_alloc_1:
|
||||
kfree(aux_bus->aux_device_wrapper[1]);
|
||||
|
||||
err_aux_dev_add_0:
|
||||
auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
|
||||
|
||||
err_aux_dev_init_0:
|
||||
ida_free(&gp_client_ida, aux_bus->aux_device_wrapper[0]->aux_dev.id);
|
||||
|
||||
err_ida_alloc_0:
|
||||
kfree(aux_bus->aux_device_wrapper[0]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void gp_aux_bus_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct aux_bus_device *aux_bus = pci_get_drvdata(pdev);
|
||||
|
||||
auxiliary_device_delete(&aux_bus->aux_device_wrapper[0]->aux_dev);
|
||||
auxiliary_device_uninit(&aux_bus->aux_device_wrapper[0]->aux_dev);
|
||||
auxiliary_device_delete(&aux_bus->aux_device_wrapper[1]->aux_dev);
|
||||
auxiliary_device_uninit(&aux_bus->aux_device_wrapper[1]->aux_dev);
|
||||
kfree(aux_bus);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci1xxxx_tbl[] = {
|
||||
{ PCI_DEVICE(0x1055, 0xA005) },
|
||||
{ PCI_DEVICE(0x1055, 0xA015) },
|
||||
{ PCI_DEVICE(0x1055, 0xA025) },
|
||||
{ PCI_DEVICE(0x1055, 0xA035) },
|
||||
{ PCI_DEVICE(0x1055, 0xA045) },
|
||||
{ PCI_DEVICE(0x1055, 0xA055) },
|
||||
{0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci1xxxx_tbl);
|
||||
|
||||
static struct pci_driver pci1xxxx_gp_driver = {
|
||||
.name = "PCI1xxxxGP",
|
||||
.id_table = pci1xxxx_tbl,
|
||||
.probe = gp_aux_bus_probe,
|
||||
.remove = gp_aux_bus_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(pci1xxxx_gp_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Microchip Technology Inc. PCI1xxxx GP expander");
|
||||
MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2022 Microchip Technology Inc. */
|
||||
|
||||
#ifndef _GPIO_PCI1XXXX_H
|
||||
#define _GPIO_PCI1XXXX_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
|
||||
/* Perform operations like variable length write, read and write with read back for OTP / EEPROM
|
||||
* Perform bit mode write in OTP
|
||||
*/
|
||||
|
||||
struct gp_aux_data_type {
|
||||
int irq_num;
|
||||
resource_size_t region_start;
|
||||
resource_size_t region_length;
|
||||
};
|
||||
|
||||
struct auxiliary_device_wrapper {
|
||||
struct auxiliary_device aux_dev;
|
||||
struct gp_aux_data_type gp_aux_data;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue