summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2026-02-04 08:41:48 +0100
committerArnd Bergmann <arnd@arndb.de>2026-02-04 08:41:49 +0100
commitcfd00b7e26c8331e3bb0f03ca770888866c15ff4 (patch)
treeabf1d2709085535c14a90d1739f8c3473d373875
parent1b65492f45c2ecb4590ccdf0fce35967531db2bf (diff)
parent52f527d0916bcdd7621a0c9e7e599b133294d495 (diff)
Merge tag 'soc_fsl-6.20-1' of https://git.kernel.org/pub/scm/linux/kernel/git/chleroy/linux into soc/drivers
FSL SOC Changes for 6.20 Freescale Management Complex: - Convert fsl-mc bus to bus callbacks - Fix a use-after-free - Drop redundant error messages - Fix ressources release on some error path Freescale QUICC Engine: - Add an interrupt controller for IO Ports - Use scoped for-each OF child loop * tag 'soc_fsl-6.20-1' of https://git.kernel.org/pub/scm/linux/kernel/git/chleroy/linux: bus: fsl-mc: fix an error handling in fsl_mc_device_add() soc: fsl: qe: qe_ports_ic: Consolidate chained IRQ handler install/remove dt-bindings: soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports soc: fsl: qe: Simplify with scoped for each OF child loop bus: fsl-mc: fix use-after-free in driver_override_show() bus: fsl-mc: Convert to bus callbacks bus: fsl-mc: Drop error message in probe function Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml51
-rw-r--r--drivers/bus/fsl-mc/fsl-mc-bus.c89
-rw-r--r--drivers/soc/fsl/qe/Makefile2
-rw-r--r--drivers/soc/fsl/qe/qe_ports_ic.c141
-rw-r--r--drivers/soc/fsl/qe/qmc.c13
5 files changed, 233 insertions, 63 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml
new file mode 100644
index 000000000000..2b8e7b9c6d7a
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/fsl,qe-ports-ic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale QUICC Engine I/O Ports Interrupt Controller
+
+maintainers:
+ - Christophe Leroy (CS GROUP) <chleroy@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - fsl,mpc8323-qe-ports-ic
+
+ reg:
+ maxItems: 1
+
+ interrupt-controller: true
+
+ '#address-cells':
+ const: 0
+
+ '#interrupt-cells':
+ const: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupt-controller
+ - '#address-cells'
+ - '#interrupt-cells'
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ interrupt-controller@c00 {
+ compatible = "fsl,mpc8323-qe-ports-ic";
+ reg = <0xc00 0x18>;
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupts = <74 0x8>;
+ interrupt-parent = <&ipic>;
+ };
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 25845c04e562..007223549887 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -137,6 +137,35 @@ static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *e
return 0;
}
+static int fsl_mc_probe(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (mc_drv->probe)
+ return mc_drv->probe(mc_dev);
+
+ return 0;
+}
+
+static void fsl_mc_remove(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (mc_drv->remove)
+ mc_drv->remove(mc_dev);
+}
+
+static void fsl_mc_shutdown(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ if (dev->driver && mc_drv->shutdown)
+ mc_drv->shutdown(mc_dev);
+}
+
static int fsl_mc_dma_configure(struct device *dev)
{
const struct device_driver *drv = READ_ONCE(dev->driver);
@@ -202,8 +231,12 @@ static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ ssize_t len;
- return sysfs_emit(buf, "%s\n", mc_dev->driver_override);
+ device_lock(dev);
+ len = sysfs_emit(buf, "%s\n", mc_dev->driver_override);
+ device_unlock(dev);
+ return len;
}
static DEVICE_ATTR_RW(driver_override);
@@ -314,6 +347,9 @@ const struct bus_type fsl_mc_bus_type = {
.name = "fsl-mc",
.match = fsl_mc_bus_match,
.uevent = fsl_mc_bus_uevent,
+ .probe = fsl_mc_probe,
+ .remove = fsl_mc_remove,
+ .shutdown = fsl_mc_shutdown,
.dma_configure = fsl_mc_dma_configure,
.dma_cleanup = fsl_mc_dma_cleanup,
.dev_groups = fsl_mc_dev_groups,
@@ -434,42 +470,6 @@ static const struct device_type *fsl_mc_get_device_type(const char *type)
return NULL;
}
-static int fsl_mc_driver_probe(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv;
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
- int error;
-
- mc_drv = to_fsl_mc_driver(dev->driver);
-
- error = mc_drv->probe(mc_dev);
- if (error < 0) {
- if (error != -EPROBE_DEFER)
- dev_err(dev, "%s failed: %d\n", __func__, error);
- return error;
- }
-
- return 0;
-}
-
-static int fsl_mc_driver_remove(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- mc_drv->remove(mc_dev);
-
- return 0;
-}
-
-static void fsl_mc_driver_shutdown(struct device *dev)
-{
- struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
-
- mc_drv->shutdown(mc_dev);
-}
-
/*
* __fsl_mc_driver_register - registers a child device driver with the
* MC bus
@@ -486,15 +486,6 @@ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
mc_driver->driver.owner = owner;
mc_driver->driver.bus = &fsl_mc_bus_type;
- if (mc_driver->probe)
- mc_driver->driver.probe = fsl_mc_driver_probe;
-
- if (mc_driver->remove)
- mc_driver->driver.remove = fsl_mc_driver_remove;
-
- if (mc_driver->shutdown)
- mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
-
error = driver_register(&mc_driver->driver);
if (error < 0) {
pr_err("driver_register() failed for %s: %d\n",
@@ -905,11 +896,7 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
return 0;
error_cleanup_dev:
- kfree(mc_dev->regions);
- if (mc_bus)
- kfree(mc_bus);
- else
- kfree(mc_dev);
+ put_device(&mc_dev->dev);
return error;
}
diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile
index ec8506e13113..901a9c40d5eb 100644
--- a/drivers/soc/fsl/qe/Makefile
+++ b/drivers/soc/fsl/qe/Makefile
@@ -11,4 +11,4 @@ obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
obj-$(CONFIG_UCC_FAST) += ucc_fast.o
obj-$(CONFIG_QE_TDM) += qe_tdm.o
obj-$(CONFIG_QE_USB) += usb.o
-obj-$(CONFIG_QE_GPIO) += gpio.o
+obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o
diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c
new file mode 100644
index 000000000000..8e2107e2cde5
--- /dev/null
+++ b/drivers/soc/fsl/qe/qe_ports_ic.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * QUICC ENGINE I/O Ports Interrupt Controller
+ *
+ * Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy@csgroup.eu)
+ */
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/platform_device.h>
+
+/* QE IC registers offset */
+#define CEPIER 0x0c
+#define CEPIMR 0x10
+#define CEPICR 0x14
+
+struct qepic_data {
+ void __iomem *reg;
+ struct irq_domain *host;
+};
+
+static void qepic_mask(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static void qepic_unmask(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static void qepic_end(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static int qepic_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+ unsigned int vec = (unsigned int)irqd_to_hwirq(d);
+
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ setbits32(data->reg + CEPICR, 1 << (31 - vec));
+ return 0;
+ case IRQ_TYPE_EDGE_BOTH:
+ case IRQ_TYPE_NONE:
+ clrbits32(data->reg + CEPICR, 1 << (31 - vec));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct irq_chip qepic = {
+ .name = "QEPIC",
+ .irq_mask = qepic_mask,
+ .irq_unmask = qepic_unmask,
+ .irq_eoi = qepic_end,
+ .irq_set_type = qepic_set_type,
+};
+
+static int qepic_get_irq(struct irq_desc *desc)
+{
+ struct qepic_data *data = irq_desc_get_handler_data(desc);
+ u32 event = in_be32(data->reg + CEPIER);
+
+ if (!event)
+ return -1;
+
+ return irq_find_mapping(data->host, 32 - ffs(event));
+}
+
+static void qepic_cascade(struct irq_desc *desc)
+{
+ generic_handle_irq(qepic_get_irq(desc));
+}
+
+static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw)
+{
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq);
+ return 0;
+}
+
+static const struct irq_domain_ops qepic_host_ops = {
+ .map = qepic_host_map,
+};
+
+static int qepic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qepic_data *data;
+ int irq;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->reg))
+ return PTR_ERR(data->reg);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ data->host = irq_domain_add_linear(dev->of_node, 32, &qepic_host_ops, data);
+ if (!data->host)
+ return -ENODEV;
+
+ irq_set_chained_handler_and_data(irq, qepic_cascade, data);
+
+ return 0;
+}
+
+static const struct of_device_id qepic_match[] = {
+ {
+ .compatible = "fsl,mpc8323-qe-ports-ic",
+ },
+ {},
+};
+
+static struct platform_driver qepic_driver = {
+ .driver = {
+ .name = "qe_ports_ic",
+ .of_match_table = qepic_match,
+ },
+ .probe = qepic_probe,
+};
+
+static int __init qepic_init(void)
+{
+ return platform_driver_register(&qepic_driver);
+}
+arch_initcall(qepic_init);
diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c
index da5ea6d35618..c4587b32a59b 100644
--- a/drivers/soc/fsl/qe/qmc.c
+++ b/drivers/soc/fsl/qe/qmc.c
@@ -1284,31 +1284,26 @@ static unsigned int qmc_nb_chans(struct qmc *qmc)
static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
{
- struct device_node *chan_np;
struct qmc_chan *chan;
const char *mode;
u32 chan_id;
u64 ts_mask;
int ret;
- for_each_available_child_of_node(np, chan_np) {
+ for_each_available_child_of_node_scoped(np, chan_np) {
ret = of_property_read_u32(chan_np, "reg", &chan_id);
if (ret) {
dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np);
- of_node_put(chan_np);
return ret;
}
if (chan_id > 63) {
dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np);
- of_node_put(chan_np);
return -EINVAL;
}
chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL);
- if (!chan) {
- of_node_put(chan_np);
+ if (!chan)
return -ENOMEM;
- }
chan->id = chan_id;
spin_lock_init(&chan->ts_lock);
@@ -1319,7 +1314,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
if (ret) {
dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n",
chan_np);
- of_node_put(chan_np);
return ret;
}
chan->tx_ts_mask_avail = ts_mask;
@@ -1329,7 +1323,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
if (ret) {
dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n",
chan_np);
- of_node_put(chan_np);
return ret;
}
chan->rx_ts_mask_avail = ts_mask;
@@ -1340,7 +1333,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
if (ret && ret != -EINVAL) {
dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n",
chan_np);
- of_node_put(chan_np);
return ret;
}
if (!strcmp(mode, "transparent")) {
@@ -1350,7 +1342,6 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
} else {
dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n",
chan_np, mode);
- of_node_put(chan_np);
return -EINVAL;
}