forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
misc: microchip: pci1xxxx: load auxiliary bus driver for the PIO func…
…tion 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>
- Loading branch information
Showing
7 changed files
with
216 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
obj-$(CONFIG_GP_PCI1XXXX) := mchp_pci1xxxx_gp.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |