Linux Audio

Check our new training course

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