Unverified Commit cfd00b7e authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'soc_fsl-6.20-1' of...

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: default avatarArnd Bergmann <arnd@arndb.de>
parents 1b65492f 52f527d0
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
# 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>;
    };
+38 −51
Original line number Diff line number Diff line
@@ -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;
}
+1 −1
Original line number Diff line number Diff line
@@ -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
+141 −0
Original line number Diff line number Diff line
// 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);
+2 −11
Original line number Diff line number Diff line
@@ -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;
		}