1*e434e54dSShijith Thotton // SPDX-License-Identifier: GPL-2.0-only 2*e434e54dSShijith Thotton /* Copyright (C) 2024 Marvell. */ 3*e434e54dSShijith Thotton 4*e434e54dSShijith Thotton #include <linux/cleanup.h> 5*e434e54dSShijith Thotton #include <linux/container_of.h> 6*e434e54dSShijith Thotton #include <linux/delay.h> 7*e434e54dSShijith Thotton #include <linux/dev_printk.h> 8*e434e54dSShijith Thotton #include <linux/init.h> 9*e434e54dSShijith Thotton #include <linux/interrupt.h> 10*e434e54dSShijith Thotton #include <linux/io-64-nonatomic-lo-hi.h> 11*e434e54dSShijith Thotton #include <linux/kernel.h> 12*e434e54dSShijith Thotton #include <linux/list.h> 13*e434e54dSShijith Thotton #include <linux/module.h> 14*e434e54dSShijith Thotton #include <linux/mutex.h> 15*e434e54dSShijith Thotton #include <linux/pci.h> 16*e434e54dSShijith Thotton #include <linux/pci_hotplug.h> 17*e434e54dSShijith Thotton #include <linux/slab.h> 18*e434e54dSShijith Thotton #include <linux/spinlock.h> 19*e434e54dSShijith Thotton #include <linux/workqueue.h> 20*e434e54dSShijith Thotton 21*e434e54dSShijith Thotton #define OCTEP_HP_INTR_OFFSET(x) (0x20400 + ((x) << 4)) 22*e434e54dSShijith Thotton #define OCTEP_HP_INTR_VECTOR(x) (16 + (x)) 23*e434e54dSShijith Thotton #define OCTEP_HP_DRV_NAME "octep_hp" 24*e434e54dSShijith Thotton 25*e434e54dSShijith Thotton /* 26*e434e54dSShijith Thotton * Type of MSI-X interrupts. OCTEP_HP_INTR_VECTOR() and 27*e434e54dSShijith Thotton * OCTEP_HP_INTR_OFFSET() generate the vector and offset for an interrupt 28*e434e54dSShijith Thotton * type. 29*e434e54dSShijith Thotton */ 30*e434e54dSShijith Thotton enum octep_hp_intr_type { 31*e434e54dSShijith Thotton OCTEP_HP_INTR_INVALID = -1, 32*e434e54dSShijith Thotton OCTEP_HP_INTR_ENA = 0, 33*e434e54dSShijith Thotton OCTEP_HP_INTR_DIS = 1, 34*e434e54dSShijith Thotton OCTEP_HP_INTR_MAX = 2, 35*e434e54dSShijith Thotton }; 36*e434e54dSShijith Thotton 37*e434e54dSShijith Thotton struct octep_hp_cmd { 38*e434e54dSShijith Thotton struct list_head list; 39*e434e54dSShijith Thotton enum octep_hp_intr_type intr_type; 40*e434e54dSShijith Thotton u64 intr_val; 41*e434e54dSShijith Thotton }; 42*e434e54dSShijith Thotton 43*e434e54dSShijith Thotton struct octep_hp_slot { 44*e434e54dSShijith Thotton struct list_head list; 45*e434e54dSShijith Thotton struct hotplug_slot slot; 46*e434e54dSShijith Thotton u16 slot_number; 47*e434e54dSShijith Thotton struct pci_dev *hp_pdev; 48*e434e54dSShijith Thotton unsigned int hp_devfn; 49*e434e54dSShijith Thotton struct octep_hp_controller *ctrl; 50*e434e54dSShijith Thotton }; 51*e434e54dSShijith Thotton 52*e434e54dSShijith Thotton struct octep_hp_intr_info { 53*e434e54dSShijith Thotton enum octep_hp_intr_type type; 54*e434e54dSShijith Thotton int number; 55*e434e54dSShijith Thotton char name[16]; 56*e434e54dSShijith Thotton }; 57*e434e54dSShijith Thotton 58*e434e54dSShijith Thotton struct octep_hp_controller { 59*e434e54dSShijith Thotton void __iomem *base; 60*e434e54dSShijith Thotton struct pci_dev *pdev; 61*e434e54dSShijith Thotton struct octep_hp_intr_info intr[OCTEP_HP_INTR_MAX]; 62*e434e54dSShijith Thotton struct work_struct work; 63*e434e54dSShijith Thotton struct list_head slot_list; 64*e434e54dSShijith Thotton struct mutex slot_lock; /* Protects slot_list */ 65*e434e54dSShijith Thotton struct list_head hp_cmd_list; 66*e434e54dSShijith Thotton spinlock_t hp_cmd_lock; /* Protects hp_cmd_list */ 67*e434e54dSShijith Thotton }; 68*e434e54dSShijith Thotton 69*e434e54dSShijith Thotton static void octep_hp_enable_pdev(struct octep_hp_controller *hp_ctrl, 70*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot) 71*e434e54dSShijith Thotton { 72*e434e54dSShijith Thotton guard(mutex)(&hp_ctrl->slot_lock); 73*e434e54dSShijith Thotton if (hp_slot->hp_pdev) { 74*e434e54dSShijith Thotton pci_dbg(hp_slot->hp_pdev, "Slot %s is already enabled\n", 75*e434e54dSShijith Thotton hotplug_slot_name(&hp_slot->slot)); 76*e434e54dSShijith Thotton return; 77*e434e54dSShijith Thotton } 78*e434e54dSShijith Thotton 79*e434e54dSShijith Thotton /* Scan the device and add it to the bus */ 80*e434e54dSShijith Thotton hp_slot->hp_pdev = pci_scan_single_device(hp_ctrl->pdev->bus, 81*e434e54dSShijith Thotton hp_slot->hp_devfn); 82*e434e54dSShijith Thotton pci_bus_assign_resources(hp_ctrl->pdev->bus); 83*e434e54dSShijith Thotton pci_bus_add_device(hp_slot->hp_pdev); 84*e434e54dSShijith Thotton 85*e434e54dSShijith Thotton dev_dbg(&hp_slot->hp_pdev->dev, "Enabled slot %s\n", 86*e434e54dSShijith Thotton hotplug_slot_name(&hp_slot->slot)); 87*e434e54dSShijith Thotton } 88*e434e54dSShijith Thotton 89*e434e54dSShijith Thotton static void octep_hp_disable_pdev(struct octep_hp_controller *hp_ctrl, 90*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot) 91*e434e54dSShijith Thotton { 92*e434e54dSShijith Thotton guard(mutex)(&hp_ctrl->slot_lock); 93*e434e54dSShijith Thotton if (!hp_slot->hp_pdev) { 94*e434e54dSShijith Thotton pci_dbg(hp_ctrl->pdev, "Slot %s is already disabled\n", 95*e434e54dSShijith Thotton hotplug_slot_name(&hp_slot->slot)); 96*e434e54dSShijith Thotton return; 97*e434e54dSShijith Thotton } 98*e434e54dSShijith Thotton 99*e434e54dSShijith Thotton pci_dbg(hp_slot->hp_pdev, "Disabling slot %s\n", 100*e434e54dSShijith Thotton hotplug_slot_name(&hp_slot->slot)); 101*e434e54dSShijith Thotton 102*e434e54dSShijith Thotton /* Remove the device from the bus */ 103*e434e54dSShijith Thotton pci_stop_and_remove_bus_device_locked(hp_slot->hp_pdev); 104*e434e54dSShijith Thotton hp_slot->hp_pdev = NULL; 105*e434e54dSShijith Thotton } 106*e434e54dSShijith Thotton 107*e434e54dSShijith Thotton static int octep_hp_enable_slot(struct hotplug_slot *slot) 108*e434e54dSShijith Thotton { 109*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot = 110*e434e54dSShijith Thotton container_of(slot, struct octep_hp_slot, slot); 111*e434e54dSShijith Thotton 112*e434e54dSShijith Thotton octep_hp_enable_pdev(hp_slot->ctrl, hp_slot); 113*e434e54dSShijith Thotton return 0; 114*e434e54dSShijith Thotton } 115*e434e54dSShijith Thotton 116*e434e54dSShijith Thotton static int octep_hp_disable_slot(struct hotplug_slot *slot) 117*e434e54dSShijith Thotton { 118*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot = 119*e434e54dSShijith Thotton container_of(slot, struct octep_hp_slot, slot); 120*e434e54dSShijith Thotton 121*e434e54dSShijith Thotton octep_hp_disable_pdev(hp_slot->ctrl, hp_slot); 122*e434e54dSShijith Thotton return 0; 123*e434e54dSShijith Thotton } 124*e434e54dSShijith Thotton 125*e434e54dSShijith Thotton static struct hotplug_slot_ops octep_hp_slot_ops = { 126*e434e54dSShijith Thotton .enable_slot = octep_hp_enable_slot, 127*e434e54dSShijith Thotton .disable_slot = octep_hp_disable_slot, 128*e434e54dSShijith Thotton }; 129*e434e54dSShijith Thotton 130*e434e54dSShijith Thotton #define SLOT_NAME_SIZE 16 131*e434e54dSShijith Thotton static struct octep_hp_slot * 132*e434e54dSShijith Thotton octep_hp_register_slot(struct octep_hp_controller *hp_ctrl, 133*e434e54dSShijith Thotton struct pci_dev *pdev, u16 slot_number) 134*e434e54dSShijith Thotton { 135*e434e54dSShijith Thotton char slot_name[SLOT_NAME_SIZE]; 136*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot; 137*e434e54dSShijith Thotton int ret; 138*e434e54dSShijith Thotton 139*e434e54dSShijith Thotton hp_slot = kzalloc(sizeof(*hp_slot), GFP_KERNEL); 140*e434e54dSShijith Thotton if (!hp_slot) 141*e434e54dSShijith Thotton return ERR_PTR(-ENOMEM); 142*e434e54dSShijith Thotton 143*e434e54dSShijith Thotton hp_slot->ctrl = hp_ctrl; 144*e434e54dSShijith Thotton hp_slot->hp_pdev = pdev; 145*e434e54dSShijith Thotton hp_slot->hp_devfn = pdev->devfn; 146*e434e54dSShijith Thotton hp_slot->slot_number = slot_number; 147*e434e54dSShijith Thotton hp_slot->slot.ops = &octep_hp_slot_ops; 148*e434e54dSShijith Thotton 149*e434e54dSShijith Thotton snprintf(slot_name, sizeof(slot_name), "octep_hp_%u", slot_number); 150*e434e54dSShijith Thotton ret = pci_hp_register(&hp_slot->slot, hp_ctrl->pdev->bus, 151*e434e54dSShijith Thotton PCI_SLOT(pdev->devfn), slot_name); 152*e434e54dSShijith Thotton if (ret) { 153*e434e54dSShijith Thotton kfree(hp_slot); 154*e434e54dSShijith Thotton return ERR_PTR(ret); 155*e434e54dSShijith Thotton } 156*e434e54dSShijith Thotton 157*e434e54dSShijith Thotton pci_info(pdev, "Registered slot %s for device %s\n", 158*e434e54dSShijith Thotton slot_name, pci_name(pdev)); 159*e434e54dSShijith Thotton 160*e434e54dSShijith Thotton list_add_tail(&hp_slot->list, &hp_ctrl->slot_list); 161*e434e54dSShijith Thotton octep_hp_disable_pdev(hp_ctrl, hp_slot); 162*e434e54dSShijith Thotton 163*e434e54dSShijith Thotton return hp_slot; 164*e434e54dSShijith Thotton } 165*e434e54dSShijith Thotton 166*e434e54dSShijith Thotton static void octep_hp_deregister_slot(void *data) 167*e434e54dSShijith Thotton { 168*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot = data; 169*e434e54dSShijith Thotton struct octep_hp_controller *hp_ctrl = hp_slot->ctrl; 170*e434e54dSShijith Thotton 171*e434e54dSShijith Thotton pci_hp_deregister(&hp_slot->slot); 172*e434e54dSShijith Thotton octep_hp_enable_pdev(hp_ctrl, hp_slot); 173*e434e54dSShijith Thotton list_del(&hp_slot->list); 174*e434e54dSShijith Thotton kfree(hp_slot); 175*e434e54dSShijith Thotton } 176*e434e54dSShijith Thotton 177*e434e54dSShijith Thotton static const char *octep_hp_cmd_name(enum octep_hp_intr_type type) 178*e434e54dSShijith Thotton { 179*e434e54dSShijith Thotton switch (type) { 180*e434e54dSShijith Thotton case OCTEP_HP_INTR_ENA: 181*e434e54dSShijith Thotton return "hotplug enable"; 182*e434e54dSShijith Thotton case OCTEP_HP_INTR_DIS: 183*e434e54dSShijith Thotton return "hotplug disable"; 184*e434e54dSShijith Thotton default: 185*e434e54dSShijith Thotton return "invalid"; 186*e434e54dSShijith Thotton } 187*e434e54dSShijith Thotton } 188*e434e54dSShijith Thotton 189*e434e54dSShijith Thotton static void octep_hp_cmd_handler(struct octep_hp_controller *hp_ctrl, 190*e434e54dSShijith Thotton struct octep_hp_cmd *hp_cmd) 191*e434e54dSShijith Thotton { 192*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot; 193*e434e54dSShijith Thotton 194*e434e54dSShijith Thotton /* 195*e434e54dSShijith Thotton * Enable or disable the slots based on the slot mask. 196*e434e54dSShijith Thotton * intr_val is a bit mask where each bit represents a slot. 197*e434e54dSShijith Thotton */ 198*e434e54dSShijith Thotton list_for_each_entry(hp_slot, &hp_ctrl->slot_list, list) { 199*e434e54dSShijith Thotton if (!(hp_cmd->intr_val & BIT(hp_slot->slot_number))) 200*e434e54dSShijith Thotton continue; 201*e434e54dSShijith Thotton 202*e434e54dSShijith Thotton pci_info(hp_ctrl->pdev, "Received %s command for slot %s\n", 203*e434e54dSShijith Thotton octep_hp_cmd_name(hp_cmd->intr_type), 204*e434e54dSShijith Thotton hotplug_slot_name(&hp_slot->slot)); 205*e434e54dSShijith Thotton 206*e434e54dSShijith Thotton switch (hp_cmd->intr_type) { 207*e434e54dSShijith Thotton case OCTEP_HP_INTR_ENA: 208*e434e54dSShijith Thotton octep_hp_enable_pdev(hp_ctrl, hp_slot); 209*e434e54dSShijith Thotton break; 210*e434e54dSShijith Thotton case OCTEP_HP_INTR_DIS: 211*e434e54dSShijith Thotton octep_hp_disable_pdev(hp_ctrl, hp_slot); 212*e434e54dSShijith Thotton break; 213*e434e54dSShijith Thotton default: 214*e434e54dSShijith Thotton break; 215*e434e54dSShijith Thotton } 216*e434e54dSShijith Thotton } 217*e434e54dSShijith Thotton } 218*e434e54dSShijith Thotton 219*e434e54dSShijith Thotton static void octep_hp_work_handler(struct work_struct *work) 220*e434e54dSShijith Thotton { 221*e434e54dSShijith Thotton struct octep_hp_controller *hp_ctrl; 222*e434e54dSShijith Thotton struct octep_hp_cmd *hp_cmd; 223*e434e54dSShijith Thotton unsigned long flags; 224*e434e54dSShijith Thotton 225*e434e54dSShijith Thotton hp_ctrl = container_of(work, struct octep_hp_controller, work); 226*e434e54dSShijith Thotton 227*e434e54dSShijith Thotton /* Process all the hotplug commands */ 228*e434e54dSShijith Thotton spin_lock_irqsave(&hp_ctrl->hp_cmd_lock, flags); 229*e434e54dSShijith Thotton while (!list_empty(&hp_ctrl->hp_cmd_list)) { 230*e434e54dSShijith Thotton hp_cmd = list_first_entry(&hp_ctrl->hp_cmd_list, 231*e434e54dSShijith Thotton struct octep_hp_cmd, list); 232*e434e54dSShijith Thotton list_del(&hp_cmd->list); 233*e434e54dSShijith Thotton spin_unlock_irqrestore(&hp_ctrl->hp_cmd_lock, flags); 234*e434e54dSShijith Thotton 235*e434e54dSShijith Thotton octep_hp_cmd_handler(hp_ctrl, hp_cmd); 236*e434e54dSShijith Thotton kfree(hp_cmd); 237*e434e54dSShijith Thotton 238*e434e54dSShijith Thotton spin_lock_irqsave(&hp_ctrl->hp_cmd_lock, flags); 239*e434e54dSShijith Thotton } 240*e434e54dSShijith Thotton spin_unlock_irqrestore(&hp_ctrl->hp_cmd_lock, flags); 241*e434e54dSShijith Thotton } 242*e434e54dSShijith Thotton 243*e434e54dSShijith Thotton static enum octep_hp_intr_type octep_hp_intr_type(struct octep_hp_intr_info *intr, 244*e434e54dSShijith Thotton int irq) 245*e434e54dSShijith Thotton { 246*e434e54dSShijith Thotton enum octep_hp_intr_type type; 247*e434e54dSShijith Thotton 248*e434e54dSShijith Thotton for (type = OCTEP_HP_INTR_ENA; type < OCTEP_HP_INTR_MAX; type++) { 249*e434e54dSShijith Thotton if (intr[type].number == irq) 250*e434e54dSShijith Thotton return type; 251*e434e54dSShijith Thotton } 252*e434e54dSShijith Thotton 253*e434e54dSShijith Thotton return OCTEP_HP_INTR_INVALID; 254*e434e54dSShijith Thotton } 255*e434e54dSShijith Thotton 256*e434e54dSShijith Thotton static irqreturn_t octep_hp_intr_handler(int irq, void *data) 257*e434e54dSShijith Thotton { 258*e434e54dSShijith Thotton struct octep_hp_controller *hp_ctrl = data; 259*e434e54dSShijith Thotton struct pci_dev *pdev = hp_ctrl->pdev; 260*e434e54dSShijith Thotton enum octep_hp_intr_type type; 261*e434e54dSShijith Thotton struct octep_hp_cmd *hp_cmd; 262*e434e54dSShijith Thotton u64 intr_val; 263*e434e54dSShijith Thotton 264*e434e54dSShijith Thotton type = octep_hp_intr_type(hp_ctrl->intr, irq); 265*e434e54dSShijith Thotton if (type == OCTEP_HP_INTR_INVALID) { 266*e434e54dSShijith Thotton pci_err(pdev, "Invalid interrupt %d\n", irq); 267*e434e54dSShijith Thotton return IRQ_HANDLED; 268*e434e54dSShijith Thotton } 269*e434e54dSShijith Thotton 270*e434e54dSShijith Thotton /* Read and clear the interrupt */ 271*e434e54dSShijith Thotton intr_val = readq(hp_ctrl->base + OCTEP_HP_INTR_OFFSET(type)); 272*e434e54dSShijith Thotton writeq(intr_val, hp_ctrl->base + OCTEP_HP_INTR_OFFSET(type)); 273*e434e54dSShijith Thotton 274*e434e54dSShijith Thotton hp_cmd = kzalloc(sizeof(*hp_cmd), GFP_ATOMIC); 275*e434e54dSShijith Thotton if (!hp_cmd) 276*e434e54dSShijith Thotton return IRQ_HANDLED; 277*e434e54dSShijith Thotton 278*e434e54dSShijith Thotton hp_cmd->intr_val = intr_val; 279*e434e54dSShijith Thotton hp_cmd->intr_type = type; 280*e434e54dSShijith Thotton 281*e434e54dSShijith Thotton /* Add the command to the list and schedule the work */ 282*e434e54dSShijith Thotton spin_lock(&hp_ctrl->hp_cmd_lock); 283*e434e54dSShijith Thotton list_add_tail(&hp_cmd->list, &hp_ctrl->hp_cmd_list); 284*e434e54dSShijith Thotton spin_unlock(&hp_ctrl->hp_cmd_lock); 285*e434e54dSShijith Thotton schedule_work(&hp_ctrl->work); 286*e434e54dSShijith Thotton 287*e434e54dSShijith Thotton return IRQ_HANDLED; 288*e434e54dSShijith Thotton } 289*e434e54dSShijith Thotton 290*e434e54dSShijith Thotton static void octep_hp_irq_cleanup(void *data) 291*e434e54dSShijith Thotton { 292*e434e54dSShijith Thotton struct octep_hp_controller *hp_ctrl = data; 293*e434e54dSShijith Thotton 294*e434e54dSShijith Thotton pci_free_irq_vectors(hp_ctrl->pdev); 295*e434e54dSShijith Thotton flush_work(&hp_ctrl->work); 296*e434e54dSShijith Thotton } 297*e434e54dSShijith Thotton 298*e434e54dSShijith Thotton static int octep_hp_request_irq(struct octep_hp_controller *hp_ctrl, 299*e434e54dSShijith Thotton enum octep_hp_intr_type type) 300*e434e54dSShijith Thotton { 301*e434e54dSShijith Thotton struct pci_dev *pdev = hp_ctrl->pdev; 302*e434e54dSShijith Thotton struct octep_hp_intr_info *intr; 303*e434e54dSShijith Thotton int irq; 304*e434e54dSShijith Thotton 305*e434e54dSShijith Thotton irq = pci_irq_vector(pdev, OCTEP_HP_INTR_VECTOR(type)); 306*e434e54dSShijith Thotton if (irq < 0) 307*e434e54dSShijith Thotton return irq; 308*e434e54dSShijith Thotton 309*e434e54dSShijith Thotton intr = &hp_ctrl->intr[type]; 310*e434e54dSShijith Thotton intr->number = irq; 311*e434e54dSShijith Thotton intr->type = type; 312*e434e54dSShijith Thotton snprintf(intr->name, sizeof(intr->name), "octep_hp_%d", type); 313*e434e54dSShijith Thotton 314*e434e54dSShijith Thotton return devm_request_irq(&pdev->dev, irq, octep_hp_intr_handler, 315*e434e54dSShijith Thotton IRQF_SHARED, intr->name, hp_ctrl); 316*e434e54dSShijith Thotton } 317*e434e54dSShijith Thotton 318*e434e54dSShijith Thotton static int octep_hp_controller_setup(struct pci_dev *pdev, 319*e434e54dSShijith Thotton struct octep_hp_controller *hp_ctrl) 320*e434e54dSShijith Thotton { 321*e434e54dSShijith Thotton struct device *dev = &pdev->dev; 322*e434e54dSShijith Thotton enum octep_hp_intr_type type; 323*e434e54dSShijith Thotton int ret; 324*e434e54dSShijith Thotton 325*e434e54dSShijith Thotton ret = pcim_enable_device(pdev); 326*e434e54dSShijith Thotton if (ret) 327*e434e54dSShijith Thotton return dev_err_probe(dev, ret, "Failed to enable PCI device\n"); 328*e434e54dSShijith Thotton 329*e434e54dSShijith Thotton hp_ctrl->base = pcim_iomap_region(pdev, 0, OCTEP_HP_DRV_NAME); 330*e434e54dSShijith Thotton if (IS_ERR(hp_ctrl->base)) 331*e434e54dSShijith Thotton return dev_err_probe(dev, PTR_ERR(hp_ctrl->base), 332*e434e54dSShijith Thotton "Failed to map PCI device region\n"); 333*e434e54dSShijith Thotton 334*e434e54dSShijith Thotton pci_set_master(pdev); 335*e434e54dSShijith Thotton pci_set_drvdata(pdev, hp_ctrl); 336*e434e54dSShijith Thotton 337*e434e54dSShijith Thotton INIT_LIST_HEAD(&hp_ctrl->slot_list); 338*e434e54dSShijith Thotton INIT_LIST_HEAD(&hp_ctrl->hp_cmd_list); 339*e434e54dSShijith Thotton mutex_init(&hp_ctrl->slot_lock); 340*e434e54dSShijith Thotton spin_lock_init(&hp_ctrl->hp_cmd_lock); 341*e434e54dSShijith Thotton INIT_WORK(&hp_ctrl->work, octep_hp_work_handler); 342*e434e54dSShijith Thotton hp_ctrl->pdev = pdev; 343*e434e54dSShijith Thotton 344*e434e54dSShijith Thotton ret = pci_alloc_irq_vectors(pdev, 1, 345*e434e54dSShijith Thotton OCTEP_HP_INTR_VECTOR(OCTEP_HP_INTR_MAX), 346*e434e54dSShijith Thotton PCI_IRQ_MSIX); 347*e434e54dSShijith Thotton if (ret < 0) 348*e434e54dSShijith Thotton return dev_err_probe(dev, ret, "Failed to alloc MSI-X vectors\n"); 349*e434e54dSShijith Thotton 350*e434e54dSShijith Thotton ret = devm_add_action(&pdev->dev, octep_hp_irq_cleanup, hp_ctrl); 351*e434e54dSShijith Thotton if (ret) 352*e434e54dSShijith Thotton return dev_err_probe(&pdev->dev, ret, "Failed to add IRQ cleanup action\n"); 353*e434e54dSShijith Thotton 354*e434e54dSShijith Thotton for (type = OCTEP_HP_INTR_ENA; type < OCTEP_HP_INTR_MAX; type++) { 355*e434e54dSShijith Thotton ret = octep_hp_request_irq(hp_ctrl, type); 356*e434e54dSShijith Thotton if (ret) 357*e434e54dSShijith Thotton return dev_err_probe(dev, ret, 358*e434e54dSShijith Thotton "Failed to request IRQ for vector %d\n", 359*e434e54dSShijith Thotton OCTEP_HP_INTR_VECTOR(type)); 360*e434e54dSShijith Thotton } 361*e434e54dSShijith Thotton 362*e434e54dSShijith Thotton return 0; 363*e434e54dSShijith Thotton } 364*e434e54dSShijith Thotton 365*e434e54dSShijith Thotton static int octep_hp_pci_probe(struct pci_dev *pdev, 366*e434e54dSShijith Thotton const struct pci_device_id *id) 367*e434e54dSShijith Thotton { 368*e434e54dSShijith Thotton struct octep_hp_controller *hp_ctrl; 369*e434e54dSShijith Thotton struct pci_dev *tmp_pdev, *next; 370*e434e54dSShijith Thotton struct octep_hp_slot *hp_slot; 371*e434e54dSShijith Thotton u16 slot_number = 0; 372*e434e54dSShijith Thotton int ret; 373*e434e54dSShijith Thotton 374*e434e54dSShijith Thotton hp_ctrl = devm_kzalloc(&pdev->dev, sizeof(*hp_ctrl), GFP_KERNEL); 375*e434e54dSShijith Thotton if (!hp_ctrl) 376*e434e54dSShijith Thotton return -ENOMEM; 377*e434e54dSShijith Thotton 378*e434e54dSShijith Thotton ret = octep_hp_controller_setup(pdev, hp_ctrl); 379*e434e54dSShijith Thotton if (ret) 380*e434e54dSShijith Thotton return ret; 381*e434e54dSShijith Thotton 382*e434e54dSShijith Thotton /* 383*e434e54dSShijith Thotton * Register all hotplug slots. Hotplug controller is the first function 384*e434e54dSShijith Thotton * of the PCI device. The hotplug slots are the remaining functions of 385*e434e54dSShijith Thotton * the PCI device. The hotplug slot functions are logically removed from 386*e434e54dSShijith Thotton * the bus during probing and are re-enabled by the driver when a 387*e434e54dSShijith Thotton * hotplug event is received. 388*e434e54dSShijith Thotton */ 389*e434e54dSShijith Thotton list_for_each_entry_safe(tmp_pdev, next, &pdev->bus->devices, bus_list) { 390*e434e54dSShijith Thotton if (tmp_pdev == pdev) 391*e434e54dSShijith Thotton continue; 392*e434e54dSShijith Thotton 393*e434e54dSShijith Thotton hp_slot = octep_hp_register_slot(hp_ctrl, tmp_pdev, slot_number); 394*e434e54dSShijith Thotton if (IS_ERR(hp_slot)) 395*e434e54dSShijith Thotton return dev_err_probe(&pdev->dev, PTR_ERR(hp_slot), 396*e434e54dSShijith Thotton "Failed to register hotplug slot %u\n", 397*e434e54dSShijith Thotton slot_number); 398*e434e54dSShijith Thotton 399*e434e54dSShijith Thotton ret = devm_add_action(&pdev->dev, octep_hp_deregister_slot, 400*e434e54dSShijith Thotton hp_slot); 401*e434e54dSShijith Thotton if (ret) 402*e434e54dSShijith Thotton return dev_err_probe(&pdev->dev, ret, 403*e434e54dSShijith Thotton "Failed to add action for deregistering slot %u\n", 404*e434e54dSShijith Thotton slot_number); 405*e434e54dSShijith Thotton slot_number++; 406*e434e54dSShijith Thotton } 407*e434e54dSShijith Thotton 408*e434e54dSShijith Thotton return 0; 409*e434e54dSShijith Thotton } 410*e434e54dSShijith Thotton 411*e434e54dSShijith Thotton #define PCI_DEVICE_ID_CAVIUM_OCTEP_HP_CTLR 0xa0e3 412*e434e54dSShijith Thotton static struct pci_device_id octep_hp_pci_map[] = { 413*e434e54dSShijith Thotton { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_OCTEP_HP_CTLR) }, 414*e434e54dSShijith Thotton { }, 415*e434e54dSShijith Thotton }; 416*e434e54dSShijith Thotton 417*e434e54dSShijith Thotton static struct pci_driver octep_hp = { 418*e434e54dSShijith Thotton .name = OCTEP_HP_DRV_NAME, 419*e434e54dSShijith Thotton .id_table = octep_hp_pci_map, 420*e434e54dSShijith Thotton .probe = octep_hp_pci_probe, 421*e434e54dSShijith Thotton }; 422*e434e54dSShijith Thotton 423*e434e54dSShijith Thotton module_pci_driver(octep_hp); 424*e434e54dSShijith Thotton 425*e434e54dSShijith Thotton MODULE_LICENSE("GPL"); 426*e434e54dSShijith Thotton MODULE_AUTHOR("Marvell"); 427*e434e54dSShijith Thotton MODULE_DESCRIPTION("Marvell OCTEON PCI Hotplug driver"); 428