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:
Kumaravel Thiagarajan 2022-08-25 01:30:43 +05:30 committed by Greg Kroah-Hartman
parent a68108c837
commit 393fc2f594
7 changed files with 216 additions and 1 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1 @@
obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o

View File

@ -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");

View File

@ -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