xref: /linux/drivers/pci/hotplug/octep_hp.c (revision c771600c6af14749609b49565ffb4cac2959710d)
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