Skip to content

Commit 185686b

Browse files
hcodinapH5
authored andcommitted
misc: Add support for LAN966x PCI device
Add a PCI driver that handles the LAN966x PCI device using a device-tree overlay. This overlay is applied to the PCI device DT node and allows to describe components that are present in the device. The memory from the device-tree is remapped to the BAR memory thanks to "ranges" properties computed at runtime by the PCI core during the PCI enumeration. The PCI device itself acts as an interrupt controller and is used as the parent of the internal LAN966x interrupt controller to route the interrupts to the assigned PCI INTx interrupt. Signed-off-by: Herve Codina <[email protected]> Acked-by: Greg Kroah-Hartman <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> # quirks.c Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Philipp Zabel <[email protected]>
1 parent c0260e2 commit 185686b

File tree

5 files changed

+410
-0
lines changed

5 files changed

+410
-0
lines changed

drivers/misc/Kconfig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,30 @@ config MARVELL_CN10K_DPI
610610
To compile this driver as a module, choose M here: the module
611611
will be called mrvl_cn10k_dpi.
612612

613+
config MCHP_LAN966X_PCI
614+
tristate "Microchip LAN966x PCIe Support"
615+
depends on PCI
616+
select OF
617+
select OF_OVERLAY
618+
select IRQ_DOMAIN
619+
help
620+
This enables the support for the LAN966x PCIe device.
621+
622+
This is used to drive the LAN966x PCIe device from the host system
623+
to which it is connected. The driver uses a device tree overlay to
624+
load other drivers to support for LAN966x internal components.
625+
626+
Even if this driver does not depend on those other drivers, in order
627+
to have a fully functional board, the following drivers are needed:
628+
- fixed-clock (COMMON_CLK)
629+
- lan966x-oic (LAN966X_OIC)
630+
- lan966x-cpu-syscon (MFD_SYSCON)
631+
- lan966x-switch-reset (RESET_MCHP_SPARX5)
632+
- lan966x-pinctrl (PINCTRL_OCELOT)
633+
- lan966x-serdes (PHY_LAN966X_SERDES)
634+
- lan966x-miim (MDIO_MSCC_MIIM)
635+
- lan966x-switch (LAN966X_SWITCH)
636+
613637
source "drivers/misc/c2port/Kconfig"
614638
source "drivers/misc/eeprom/Kconfig"
615639
source "drivers/misc/cb710/Kconfig"

drivers/misc/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,7 @@ obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o
7171
obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o
7272
obj-$(CONFIG_NSM) += nsm.o
7373
obj-$(CONFIG_MARVELL_CN10K_DPI) += mrvl_cn10k_dpi.o
74+
lan966x-pci-objs := lan966x_pci.o
75+
lan966x-pci-objs += lan966x_pci.dtbo.o
76+
obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o
7477
obj-y += keba/

drivers/misc/lan966x_pci.c

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Microchip LAN966x PCI driver
4+
*
5+
* Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
6+
*
7+
* Authors:
8+
* Clément Léger <[email protected]>
9+
* Hervé Codina <[email protected]>
10+
*/
11+
12+
#include <linux/device.h>
13+
#include <linux/irq.h>
14+
#include <linux/irqdomain.h>
15+
#include <linux/module.h>
16+
#include <linux/of_platform.h>
17+
#include <linux/pci.h>
18+
#include <linux/pci_ids.h>
19+
#include <linux/slab.h>
20+
21+
/* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */
22+
extern char __dtbo_lan966x_pci_begin[];
23+
extern char __dtbo_lan966x_pci_end[];
24+
25+
struct pci_dev_intr_ctrl {
26+
struct pci_dev *pci_dev;
27+
struct irq_domain *irq_domain;
28+
int irq;
29+
};
30+
31+
static int pci_dev_irq_domain_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw)
32+
{
33+
irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
34+
return 0;
35+
}
36+
37+
static const struct irq_domain_ops pci_dev_irq_domain_ops = {
38+
.map = pci_dev_irq_domain_map,
39+
.xlate = irq_domain_xlate_onecell,
40+
};
41+
42+
static irqreturn_t pci_dev_irq_handler(int irq, void *data)
43+
{
44+
struct pci_dev_intr_ctrl *intr_ctrl = data;
45+
int ret;
46+
47+
ret = generic_handle_domain_irq(intr_ctrl->irq_domain, 0);
48+
return ret ? IRQ_NONE : IRQ_HANDLED;
49+
}
50+
51+
static struct pci_dev_intr_ctrl *pci_dev_create_intr_ctrl(struct pci_dev *pdev)
52+
{
53+
struct pci_dev_intr_ctrl *intr_ctrl __free(kfree) = NULL;
54+
struct fwnode_handle *fwnode;
55+
int ret;
56+
57+
fwnode = dev_fwnode(&pdev->dev);
58+
if (!fwnode)
59+
return ERR_PTR(-ENODEV);
60+
61+
intr_ctrl = kmalloc(sizeof(*intr_ctrl), GFP_KERNEL);
62+
if (!intr_ctrl)
63+
return ERR_PTR(-ENOMEM);
64+
65+
intr_ctrl->pci_dev = pdev;
66+
67+
intr_ctrl->irq_domain = irq_domain_create_linear(fwnode, 1, &pci_dev_irq_domain_ops,
68+
intr_ctrl);
69+
if (!intr_ctrl->irq_domain) {
70+
pci_err(pdev, "Failed to create irqdomain\n");
71+
return ERR_PTR(-ENOMEM);
72+
}
73+
74+
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX);
75+
if (ret < 0) {
76+
pci_err(pdev, "Unable alloc irq vector (%d)\n", ret);
77+
goto err_remove_domain;
78+
}
79+
intr_ctrl->irq = pci_irq_vector(pdev, 0);
80+
ret = request_irq(intr_ctrl->irq, pci_dev_irq_handler, IRQF_SHARED,
81+
pci_name(pdev), intr_ctrl);
82+
if (ret) {
83+
pci_err(pdev, "Unable to request irq %d (%d)\n", intr_ctrl->irq, ret);
84+
goto err_free_irq_vector;
85+
}
86+
87+
return_ptr(intr_ctrl);
88+
89+
err_free_irq_vector:
90+
pci_free_irq_vectors(pdev);
91+
err_remove_domain:
92+
irq_domain_remove(intr_ctrl->irq_domain);
93+
return ERR_PTR(ret);
94+
}
95+
96+
static void pci_dev_remove_intr_ctrl(struct pci_dev_intr_ctrl *intr_ctrl)
97+
{
98+
free_irq(intr_ctrl->irq, intr_ctrl);
99+
pci_free_irq_vectors(intr_ctrl->pci_dev);
100+
irq_dispose_mapping(irq_find_mapping(intr_ctrl->irq_domain, 0));
101+
irq_domain_remove(intr_ctrl->irq_domain);
102+
kfree(intr_ctrl);
103+
}
104+
105+
static void devm_pci_dev_remove_intr_ctrl(void *intr_ctrl)
106+
{
107+
pci_dev_remove_intr_ctrl(intr_ctrl);
108+
}
109+
110+
static int devm_pci_dev_create_intr_ctrl(struct pci_dev *pdev)
111+
{
112+
struct pci_dev_intr_ctrl *intr_ctrl;
113+
114+
intr_ctrl = pci_dev_create_intr_ctrl(pdev);
115+
if (IS_ERR(intr_ctrl))
116+
return PTR_ERR(intr_ctrl);
117+
118+
return devm_add_action_or_reset(&pdev->dev, devm_pci_dev_remove_intr_ctrl, intr_ctrl);
119+
}
120+
121+
struct lan966x_pci {
122+
struct device *dev;
123+
int ovcs_id;
124+
};
125+
126+
static int lan966x_pci_load_overlay(struct lan966x_pci *data)
127+
{
128+
u32 dtbo_size = __dtbo_lan966x_pci_end - __dtbo_lan966x_pci_begin;
129+
void *dtbo_start = __dtbo_lan966x_pci_begin;
130+
131+
return of_overlay_fdt_apply(dtbo_start, dtbo_size, &data->ovcs_id, dev_of_node(data->dev));
132+
}
133+
134+
static void lan966x_pci_unload_overlay(struct lan966x_pci *data)
135+
{
136+
of_overlay_remove(&data->ovcs_id);
137+
}
138+
139+
static int lan966x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
140+
{
141+
struct device *dev = &pdev->dev;
142+
struct lan966x_pci *data;
143+
int ret;
144+
145+
/*
146+
* On ACPI system, fwnode can point to the ACPI node.
147+
* This driver needs an of_node to be used as the device-tree overlay
148+
* target. This of_node should be set by the PCI core if it succeeds in
149+
* creating it (CONFIG_PCI_DYNAMIC_OF_NODES feature).
150+
* Check here for the validity of this of_node.
151+
*/
152+
if (!dev_of_node(dev))
153+
return dev_err_probe(dev, -EINVAL, "Missing of_node for device\n");
154+
155+
/* Need to be done before devm_pci_dev_create_intr_ctrl.
156+
* It allocates an IRQ and so pdev->irq is updated.
157+
*/
158+
ret = pcim_enable_device(pdev);
159+
if (ret)
160+
return ret;
161+
162+
ret = devm_pci_dev_create_intr_ctrl(pdev);
163+
if (ret)
164+
return ret;
165+
166+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
167+
if (!data)
168+
return -ENOMEM;
169+
170+
pci_set_drvdata(pdev, data);
171+
data->dev = dev;
172+
173+
ret = lan966x_pci_load_overlay(data);
174+
if (ret)
175+
return ret;
176+
177+
pci_set_master(pdev);
178+
179+
ret = of_platform_default_populate(dev_of_node(dev), NULL, dev);
180+
if (ret)
181+
goto err_unload_overlay;
182+
183+
return 0;
184+
185+
err_unload_overlay:
186+
lan966x_pci_unload_overlay(data);
187+
return ret;
188+
}
189+
190+
static void lan966x_pci_remove(struct pci_dev *pdev)
191+
{
192+
struct lan966x_pci *data = pci_get_drvdata(pdev);
193+
194+
of_platform_depopulate(data->dev);
195+
196+
lan966x_pci_unload_overlay(data);
197+
}
198+
199+
static struct pci_device_id lan966x_pci_ids[] = {
200+
{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, 0x9660) },
201+
{ }
202+
};
203+
MODULE_DEVICE_TABLE(pci, lan966x_pci_ids);
204+
205+
static struct pci_driver lan966x_pci_driver = {
206+
.name = "mchp_lan966x_pci",
207+
.id_table = lan966x_pci_ids,
208+
.probe = lan966x_pci_probe,
209+
.remove = lan966x_pci_remove,
210+
};
211+
module_pci_driver(lan966x_pci_driver);
212+
213+
MODULE_AUTHOR("Herve Codina <[email protected]>");
214+
MODULE_DESCRIPTION("Microchip LAN966x PCI driver");
215+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)