RE: [PATCH v3] PCI: hotplug: Add OCTEON PCI hotplug controller driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Just a gentle reminder.
Could you please comments if there are improvements needed?

>Hi Helgaas,
>
>Please check this patch.
>
>>This patch introduces a PCI hotplug controller driver for the OCTEON
>>PCIe device, a multi-function PCIe device where the first function acts
>>as a hotplug controller. It is equipped with MSI-x interrupts to notify
>>the host of hotplug events from the OCTEON firmware.
>>
>>The driver facilitates the hotplugging of non-controller functions
>>within the same device. During probe, non-controller functions are
>>removed and registered as PCI hotplug slots. The slots are added back
>>only upon request from the device firmware. The driver also allows the
>>enabling and disabling of the slots via sysfs slot entries, provided by
>>the PCI hotplug framework.
>>
>>Signed-off-by: Shijith Thotton <sthotton@xxxxxxxxxxx>
>>Co-developed-by: Vamsi Attunuru <vattunuru@xxxxxxxxxxx>
>>Signed-off-by: Vamsi Attunuru <vattunuru@xxxxxxxxxxx>
>>---
>>
>>This patch introduces a PCI hotplug controller driver for OCTEON PCIe hotplug
>>controller. The OCTEON PCIe device is a multi-function device where the first
>>function acts as a PCI hotplug controller.
>>
>>              +--------------------------------+
>>              |           Root Port            |
>>              +--------------------------------+
>>                              |
>>                             PCIe
>>                              |
>>+---------------------------------------------------------------+
>>|              OCTEON PCIe Multifunction Device                 |
>>+---------------------------------------------------------------+
>>            |                    |              |            |
>>            |                    |              |            |
>>+---------------------+  +----------------+  +-----+  +----------------+
>>|      Function 0     |  |   Function 1   |  | ... |  |   Function 7   |
>>| (Hotplug controller)|  | (Hotplug slot) |  |     |  | (Hotplug slot) |
>>+---------------------+  +----------------+  +-----+  +----------------+
>>            |
>>            |
>>+-------------------------+
>>|   Controller Firmware   |
>>+-------------------------+
>>
>>The hotplug controller driver facilitates the hotplugging of non-controller
>>functions in the same device. During the probe of the driver, the non-
>controller
>>function are removed and registered as PCI hotplug slots. They are added back
>>only upon request from the device firmware. The driver also allows the user to
>>enable/disable the functions using sysfs slot entries provided by PCI hotplug
>>framework.
>>
>>This solution adopts a hardware + software approach for several reasons:
>>
>>1. To reduce hardware implementation cost. Supporting complete hotplug
>>   capability within the card would require a PCI switch implemented within.
>>
>>2. In the multi-function device, non-controller functions can act as emulated
>>   devices. The firmware can dynamically enable or disable them at runtime.
>>
>>3. Not all root ports support PCI hotplug. This approach provides greater
>>   flexibility and compatibility across different hardware configurations.
>>
>>The hotplug controller function is lightweight and is equipped with MSI-x
>>interrupts to notify the host about hotplug events. Upon receiving an
>>interrupt, the hotplug register is read, and the required function is enabled
>>or disabled.
>>
>>This driver will be beneficial for managing PCI hotplug events on the OCTEON
>>PCIe device without requiring complex hardware solutions.
>>
>>Changes in v2:
>>- Added missing include files.
>>- Used dev_err_probe() for error handling.
>>- Used guard() for mutex locking.
>>- Splited cleanup actions and added per-slot cleanup action.
>>- Fixed coding style issues.
>>- Added co-developed-by tag.
>>
>>Changes in v3:
>>- Explicit assignment of enum values.
>>- Use pcim_iomap_region() instead of pcim_iomap_regions().
>>
>> MAINTAINERS                    |   6 +
>> drivers/pci/hotplug/Kconfig    |  10 +
>> drivers/pci/hotplug/Makefile   |   1 +
>> drivers/pci/hotplug/octep_hp.c | 409
>>+++++++++++++++++++++++++++++++++
>> 4 files changed, 426 insertions(+)
>> create mode 100644 drivers/pci/hotplug/octep_hp.c
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index 42decde38320..7b5a618eed1c 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -13677,6 +13677,12 @@ R:	schalla@xxxxxxxxxxx
>> R:	vattunuru@xxxxxxxxxxx
>> F:	drivers/vdpa/octeon_ep/
>>
>>+MARVELL OCTEON HOTPLUG CONTROLLER DRIVER
>>+R:	Shijith Thotton <sthotton@xxxxxxxxxxx>
>>+R:	Vamsi Attunuru <vattunuru@xxxxxxxxxxx>
>>+S:	Supported
>>+F:	drivers/pci/hotplug/octep_hp.c
>>+
>> MATROX FRAMEBUFFER DRIVER
>> L:	linux-fbdev@xxxxxxxxxxxxxxx
>> S:	Orphan
>>diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
>>index 1472aef0fb81..2e38fd25f7ef 100644
>>--- a/drivers/pci/hotplug/Kconfig
>>+++ b/drivers/pci/hotplug/Kconfig
>>@@ -173,4 +173,14 @@ config HOTPLUG_PCI_S390
>>
>> 	  When in doubt, say Y.
>>
>>+config HOTPLUG_PCI_OCTEONEP
>>+	bool "OCTEON PCI device Hotplug controller driver"
>>+	depends on HOTPLUG_PCI
>>+	help
>>+	  Say Y here if you have an OCTEON PCIe device with a hotplug
>>+	  controller. This driver enables the non-controller functions of the
>>+	  device to be registered as hotplug slots.
>>+
>>+	  When in doubt, say N.
>>+
>> endif # HOTPLUG_PCI
>>diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
>>index 240c99517d5e..40aaf31fe338 100644
>>--- a/drivers/pci/hotplug/Makefile
>>+++ b/drivers/pci/hotplug/Makefile
>>@@ -20,6 +20,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA)		+=
>>rpaphp.o
>> obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
>> obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
>> obj-$(CONFIG_HOTPLUG_PCI_S390)		+= s390_pci_hpc.o
>>+obj-$(CONFIG_HOTPLUG_PCI_OCTEONEP)	+= octep_hp.o
>>
>> # acpiphp_ibm extends acpiphp, so should be linked afterwards.
>>
>>diff --git a/drivers/pci/hotplug/octep_hp.c b/drivers/pci/hotplug/octep_hp.c
>>new file mode 100644
>>index 000000000000..77dc2740f43e
>>--- /dev/null
>>+++ b/drivers/pci/hotplug/octep_hp.c
>>@@ -0,0 +1,409 @@
>>+// SPDX-License-Identifier: GPL-2.0-only
>>+/* Copyright (C) 2024 Marvell. */
>>+
>>+#include <linux/cleanup.h>
>>+#include <linux/container_of.h>
>>+#include <linux/delay.h>
>>+#include <linux/dev_printk.h>
>>+#include <linux/init.h>
>>+#include <linux/interrupt.h>
>>+#include <linux/io-64-nonatomic-lo-hi.h>
>>+#include <linux/kernel.h>
>>+#include <linux/list.h>
>>+#include <linux/module.h>
>>+#include <linux/mutex.h>
>>+#include <linux/pci.h>
>>+#include <linux/pci_hotplug.h>
>>+#include <linux/slab.h>
>>+#include <linux/spinlock.h>
>>+#include <linux/workqueue.h>
>>+
>>+#define OCTEP_HP_INTR_OFFSET(x) (0x20400 + ((x) << 4))
>>+#define OCTEP_HP_INTR_VECTOR(x) (16 + (x))
>>+#define OCTEP_HP_DRV_NAME "octep_hp"
>>+
>>+/*
>>+ * Type of MSI-X interrupts.
>>+ * The macros OCTEP_HP_INTR_VECTOR and OCTEP_HP_INTR_OFFSET are
>>used to
>>+ * generate the vector and offset for an interrupt type.
>>+ */
>>+enum octep_hp_intr_type {
>>+	OCTEP_HP_INTR_INVALID = -1,
>>+	OCTEP_HP_INTR_ENA = 0,
>>+	OCTEP_HP_INTR_DIS = 1,
>>+	OCTEP_HP_INTR_MAX = 2,
>>+};
>>+
>>+struct octep_hp_cmd {
>>+	struct list_head list;
>>+	enum octep_hp_intr_type intr_type;
>>+	u64 intr_val;
>>+};
>>+
>>+struct octep_hp_slot {
>>+	struct list_head list;
>>+	struct hotplug_slot slot;
>>+	u16 slot_number;
>>+	struct pci_dev *hp_pdev;
>>+	unsigned int hp_devfn;
>>+	struct octep_hp_controller *ctrl;
>>+};
>>+
>>+struct octep_hp_intr_info {
>>+	enum octep_hp_intr_type type;
>>+	int number;
>>+	char name[16];
>>+};
>>+
>>+struct octep_hp_controller {
>>+	void __iomem *base;
>>+	struct pci_dev *pdev;
>>+	struct octep_hp_intr_info intr[OCTEP_HP_INTR_MAX];
>>+	struct work_struct work;
>>+	struct list_head slot_list;
>>+	struct mutex slot_lock; /* Protects slot_list */
>>+	struct list_head hp_cmd_list;
>>+	spinlock_t hp_cmd_lock; /* Protects hp_cmd_list */
>>+};
>>+
>>+static void octep_hp_enable_pdev(struct octep_hp_controller *hp_ctrl,
>>+				 struct octep_hp_slot *hp_slot)
>>+{
>>+	guard(mutex)(&hp_ctrl->slot_lock);
>>+	if (hp_slot->hp_pdev) {
>>+		dev_dbg(&hp_slot->hp_pdev->dev, "Slot %u already
>>enabled\n",
>>+			hp_slot->slot_number);
>>+		return;
>>+	}
>>+
>>+	/* Scan the device and add it to the bus */
>>+	hp_slot->hp_pdev = pci_scan_single_device(hp_ctrl->pdev->bus,
>>+						  hp_slot->hp_devfn);
>>+	pci_bus_assign_resources(hp_ctrl->pdev->bus);
>>+	pci_bus_add_device(hp_slot->hp_pdev);
>>+
>>+	dev_dbg(&hp_slot->hp_pdev->dev, "Enabled slot %u\n",
>>+		hp_slot->slot_number);
>>+}
>>+
>>+static void octep_hp_disable_pdev(struct octep_hp_controller *hp_ctrl,
>>+				  struct octep_hp_slot *hp_slot)
>>+{
>>+	guard(mutex)(&hp_ctrl->slot_lock);
>>+	if (!hp_slot->hp_pdev) {
>>+		dev_dbg(&hp_ctrl->pdev->dev, "Slot %u already disabled\n",
>>+			hp_slot->slot_number);
>>+		return;
>>+	}
>>+
>>+	dev_dbg(&hp_slot->hp_pdev->dev, "Disabling slot %u\n",
>>+		hp_slot->slot_number);
>>+
>>+	/* Remove the device from the bus */
>>+	pci_stop_and_remove_bus_device_locked(hp_slot->hp_pdev);
>>+	hp_slot->hp_pdev = NULL;
>>+}
>>+
>>+static int octep_hp_enable_slot(struct hotplug_slot *slot)
>>+{
>>+	struct octep_hp_slot *hp_slot =
>>+		container_of(slot, struct octep_hp_slot, slot);
>>+
>>+	octep_hp_enable_pdev(hp_slot->ctrl, hp_slot);
>>+	return 0;
>>+}
>>+
>>+static int octep_hp_disable_slot(struct hotplug_slot *slot)
>>+{
>>+	struct octep_hp_slot *hp_slot =
>>+		container_of(slot, struct octep_hp_slot, slot);
>>+
>>+	octep_hp_disable_pdev(hp_slot->ctrl, hp_slot);
>>+	return 0;
>>+}
>>+
>>+static struct hotplug_slot_ops octep_hp_slot_ops = {
>>+	.enable_slot = octep_hp_enable_slot,
>>+	.disable_slot = octep_hp_disable_slot,
>>+};
>>+
>>+#define SLOT_NAME_SIZE 16
>>+static struct octep_hp_slot *
>>+octep_hp_register_slot(struct octep_hp_controller *hp_ctrl,
>>+		       struct pci_dev *pdev, u16 slot_number)
>>+{
>>+	char slot_name[SLOT_NAME_SIZE];
>>+	struct octep_hp_slot *hp_slot;
>>+	int ret;
>>+
>>+	hp_slot = kzalloc(sizeof(*hp_slot), GFP_KERNEL);
>>+	if (!hp_slot)
>>+		return ERR_PTR(-ENOMEM);
>>+
>>+	hp_slot->ctrl = hp_ctrl;
>>+	hp_slot->hp_pdev = pdev;
>>+	hp_slot->hp_devfn = pdev->devfn;
>>+	hp_slot->slot_number = slot_number;
>>+	hp_slot->slot.ops = &octep_hp_slot_ops;
>>+
>>+	snprintf(slot_name, sizeof(slot_name), "octep_hp_%u", slot_number);
>>+	ret = pci_hp_register(&hp_slot->slot, hp_ctrl->pdev->bus,
>>+			      PCI_SLOT(pdev->devfn), slot_name);
>>+	if (ret) {
>>+		kfree(hp_slot);
>>+		return ERR_PTR(ret);
>>+	}
>>+
>>+	list_add_tail(&hp_slot->list, &hp_ctrl->slot_list);
>>+	octep_hp_disable_pdev(hp_ctrl, hp_slot);
>>+
>>+	return hp_slot;
>>+}
>>+
>>+static void octep_hp_deregister_slot(void *data)
>>+{
>>+	struct octep_hp_slot *hp_slot = data;
>>+	struct octep_hp_controller *hp_ctrl = hp_slot->ctrl;
>>+
>>+	pci_hp_deregister(&hp_slot->slot);
>>+	octep_hp_enable_pdev(hp_ctrl, hp_slot);
>>+	list_del(&hp_slot->list);
>>+	kfree(hp_slot);
>>+}
>>+
>>+static bool octep_hp_is_slot(struct octep_hp_controller *hp_ctrl,
>>+			     struct pci_dev *pdev)
>>+{
>>+	/* Check if the PCI device can be hotplugged */
>>+	return pdev != hp_ctrl->pdev && pdev->bus == hp_ctrl->pdev->bus &&
>>+	       PCI_SLOT(pdev->devfn) == PCI_SLOT(hp_ctrl->pdev->devfn);
>>+}
>>+
>>+static void octep_hp_cmd_handler(struct octep_hp_controller *hp_ctrl,
>>+				 struct octep_hp_cmd *hp_cmd)
>>+{
>>+	struct octep_hp_slot *hp_slot;
>>+
>>+	/*
>>+	 * Enable or disable the slots based on the slot mask.
>>+	 * intr_val is a bit mask where each bit represents a slot.
>>+	 */
>>+	list_for_each_entry(hp_slot, &hp_ctrl->slot_list, list) {
>>+		if (!(hp_cmd->intr_val & BIT(hp_slot->slot_number)))
>>+			continue;
>>+
>>+		if (hp_cmd->intr_type == OCTEP_HP_INTR_ENA)
>>+			octep_hp_enable_pdev(hp_ctrl, hp_slot);
>>+		else
>>+			octep_hp_disable_pdev(hp_ctrl, hp_slot);
>>+	}
>>+}
>>+
>>+static void octep_hp_work_handler(struct work_struct *work)
>>+{
>>+	struct octep_hp_controller *hp_ctrl;
>>+	struct octep_hp_cmd *hp_cmd;
>>+	unsigned long flags;
>>+
>>+	hp_ctrl = container_of(work, struct octep_hp_controller, work);
>>+
>>+	/* Process all the hotplug commands */
>>+	spin_lock_irqsave(&hp_ctrl->hp_cmd_lock, flags);
>>+	while (!list_empty(&hp_ctrl->hp_cmd_list)) {
>>+		hp_cmd = list_first_entry(&hp_ctrl->hp_cmd_list,
>>+					  struct octep_hp_cmd, list);
>>+		list_del(&hp_cmd->list);
>>+		spin_unlock_irqrestore(&hp_ctrl->hp_cmd_lock, flags);
>>+
>>+		octep_hp_cmd_handler(hp_ctrl, hp_cmd);
>>+		kfree(hp_cmd);
>>+
>>+		spin_lock_irqsave(&hp_ctrl->hp_cmd_lock, flags);
>>+	}
>>+	spin_unlock_irqrestore(&hp_ctrl->hp_cmd_lock, flags);
>>+}
>>+
>>+static enum octep_hp_intr_type octep_hp_intr_type(struct
>>octep_hp_intr_info *intr,
>>+						  int irq)
>>+{
>>+	enum octep_hp_intr_type type;
>>+
>>+	for (type = OCTEP_HP_INTR_ENA; type < OCTEP_HP_INTR_MAX;
>>type++) {
>>+		if (intr[type].number == irq)
>>+			return type;
>>+	}
>>+
>>+	return OCTEP_HP_INTR_INVALID;
>>+}
>>+
>>+static irqreturn_t octep_hp_intr_handler(int irq, void *data)
>>+{
>>+	struct octep_hp_controller *hp_ctrl = data;
>>+	struct pci_dev *pdev = hp_ctrl->pdev;
>>+	enum octep_hp_intr_type type;
>>+	struct octep_hp_cmd *hp_cmd;
>>+	u64 intr_val;
>>+
>>+	type = octep_hp_intr_type(hp_ctrl->intr, irq);
>>+	if (type == OCTEP_HP_INTR_INVALID) {
>>+		dev_err(&pdev->dev, "Invalid interrupt %d\n", irq);
>>+		return IRQ_HANDLED;
>>+	}
>>+
>>+	/* Read and clear the interrupt */
>>+	intr_val = readq(hp_ctrl->base + OCTEP_HP_INTR_OFFSET(type));
>>+	writeq(intr_val, hp_ctrl->base + OCTEP_HP_INTR_OFFSET(type));
>>+
>>+	hp_cmd = kzalloc(sizeof(*hp_cmd), GFP_ATOMIC);
>>+	if (!hp_cmd)
>>+		return IRQ_HANDLED;
>>+
>>+	hp_cmd->intr_val = intr_val;
>>+	hp_cmd->intr_type = type;
>>+
>>+	/* Add the command to the list and schedule the work */
>>+	spin_lock(&hp_ctrl->hp_cmd_lock);
>>+	list_add_tail(&hp_cmd->list, &hp_ctrl->hp_cmd_list);
>>+	spin_unlock(&hp_ctrl->hp_cmd_lock);
>>+	schedule_work(&hp_ctrl->work);
>>+
>>+	return IRQ_HANDLED;
>>+}
>>+
>>+static void octep_hp_irq_cleanup(void *data)
>>+{
>>+	struct octep_hp_controller *hp_ctrl = data;
>>+
>>+	pci_free_irq_vectors(hp_ctrl->pdev);
>>+	flush_work(&hp_ctrl->work);
>>+}
>>+
>>+static int octep_hp_request_irq(struct octep_hp_controller *hp_ctrl,
>>+				enum octep_hp_intr_type type)
>>+{
>>+	struct pci_dev *pdev = hp_ctrl->pdev;
>>+	struct octep_hp_intr_info *intr;
>>+	int irq;
>>+
>>+	irq = pci_irq_vector(pdev, OCTEP_HP_INTR_VECTOR(type));
>>+	if (irq < 0)
>>+		return irq;
>>+
>>+	intr = &hp_ctrl->intr[type];
>>+	intr->number = irq;
>>+	intr->type = type;
>>+	snprintf(intr->name, sizeof(intr->name), "octep_hp_%d", type);
>>+
>>+	return devm_request_irq(&pdev->dev, irq, octep_hp_intr_handler,
>>+				IRQF_SHARED, intr->name, hp_ctrl);
>>+}
>>+
>>+static int octep_hp_controller_setup(struct pci_dev *pdev,
>>+				     struct octep_hp_controller *hp_ctrl)
>>+{
>>+	struct device *dev = &pdev->dev;
>>+	enum octep_hp_intr_type type;
>>+	int ret;
>>+
>>+	ret = pcim_enable_device(pdev);
>>+	if (ret)
>>+		return dev_err_probe(dev, ret, "Failed to enable PCI device\n");
>>+
>>+	hp_ctrl->base = pcim_iomap_region(pdev, 0, OCTEP_HP_DRV_NAME);
>>+	if (IS_ERR(hp_ctrl->base))
>>+		return dev_err_probe(dev, PTR_ERR(hp_ctrl->base),
>>+				     "Failed to map PCI device region\n");
>>+
>>+	pci_set_master(pdev);
>>+	pci_set_drvdata(pdev, hp_ctrl);
>>+
>>+	INIT_LIST_HEAD(&hp_ctrl->slot_list);
>>+	INIT_LIST_HEAD(&hp_ctrl->hp_cmd_list);
>>+	mutex_init(&hp_ctrl->slot_lock);
>>+	spin_lock_init(&hp_ctrl->hp_cmd_lock);
>>+	INIT_WORK(&hp_ctrl->work, octep_hp_work_handler);
>>+	hp_ctrl->pdev = pdev;
>>+
>>+	ret = pci_alloc_irq_vectors(pdev, 1,
>>+
>>OCTEP_HP_INTR_VECTOR(OCTEP_HP_INTR_MAX),
>>+				    PCI_IRQ_MSIX);
>>+	if (ret < 0)
>>+		return dev_err_probe(dev, ret, "Failed to alloc MSI-X
>>vectors\n");
>>+
>>+	ret = devm_add_action(&pdev->dev, octep_hp_irq_cleanup, hp_ctrl);
>>+	if (ret)
>>+		return dev_err_probe(&pdev->dev, ret, "Failed to add IRQ
>>cleanup action\n");
>>+
>>+	for (type = OCTEP_HP_INTR_ENA; type < OCTEP_HP_INTR_MAX;
>>type++) {
>>+		ret = octep_hp_request_irq(hp_ctrl, type);
>>+		if (ret)
>>+			return dev_err_probe(dev, ret,
>>+					     "Failed to request IRQ for vector
>>%d\n",
>>+					     OCTEP_HP_INTR_VECTOR(type));
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int octep_hp_pci_probe(struct pci_dev *pdev,
>>+			      const struct pci_device_id *id)
>>+{
>>+	struct octep_hp_controller *hp_ctrl;
>>+	struct pci_dev *tmp_pdev = NULL;
>>+	struct octep_hp_slot *hp_slot;
>>+	u16 slot_number = 0;
>>+	int ret;
>>+
>>+	hp_ctrl = devm_kzalloc(&pdev->dev, sizeof(*hp_ctrl), GFP_KERNEL);
>>+	if (!hp_ctrl)
>>+		return -ENOMEM;
>>+
>>+	ret = octep_hp_controller_setup(pdev, hp_ctrl);
>>+	if (ret)
>>+		return ret;
>>+
>>+	/*
>>+	 * Register all hotplug slots. Hotplug controller is the first function
>>+	 * of the PCI device. The hotplug slots are the remaining functions of
>>+	 * the PCI device. They are removed from the bus and are added back
>>when
>>+	 * the hotplug event is triggered.
>>+	 */
>>+	for_each_pci_dev(tmp_pdev) {
>>+		if (!octep_hp_is_slot(hp_ctrl, tmp_pdev))
>>+			continue;
>>+
>>+		hp_slot = octep_hp_register_slot(hp_ctrl, tmp_pdev,
>>slot_number);
>>+		if (IS_ERR(hp_slot))
>>+			return dev_err_probe(&pdev->dev, PTR_ERR(hp_slot),
>>+					     "Failed to register hotplug slot
>>%u\n",
>>+					     slot_number);
>>+
>>+		ret = devm_add_action(&pdev->dev, octep_hp_deregister_slot,
>>+				      hp_slot);
>>+		if (ret)
>>+			return dev_err_probe(&pdev->dev, ret,
>>+					     "Failed to add action for
>>deregistering slot %u\n",
>>+					     slot_number);
>>+		slot_number++;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+#define OCTEP_DEVID_HP_CONTROLLER 0xa0e3
>>+static struct pci_device_id octep_hp_pci_map[] = {
>>+	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM,
>>OCTEP_DEVID_HP_CONTROLLER) },
>>+	{ },
>>+};
>>+
>>+static struct pci_driver octep_hp = {
>>+	.name = OCTEP_HP_DRV_NAME,
>>+	.id_table = octep_hp_pci_map,
>>+	.probe = octep_hp_pci_probe,
>>+};
>>+
>>+module_pci_driver(octep_hp);
>>+
>>+MODULE_LICENSE("GPL");
>>+MODULE_AUTHOR("Marvell");
>>+MODULE_DESCRIPTION("OCTEON PCIe device hotplug controller driver");
>>--
>>2.25.1
>

Thanks,
Shijith





[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux