All of lore.kernel.org
 help / color / mirror / Atom feed
From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Mark Rutland <mark.rutland@arm.com>, Andrew Lunn <andrew@lunn.ch>,
	Jason Cooper <jason@lakedaemon.net>,
	devicetree@vger.kernel.org, Marc Zyngier <marc.zyngier@arm.com>,
	Catalin Marinas <catalin.marinas@arm.com>,
	Gregory Clement <gregory.clement@bootlin.com>,
	Haim Boot <hayim@marvell.com>, Will Deacon <will.deacon@arm.com>,
	Maxime Chevallier <maxime.chevallier@bootlin.com>,
	Nadav Haklai <nadavh@marvell.com>,
	Antoine Tenart <antoine.tenart@bootlin.com>,
	Rob Herring <robh+dt@kernel.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Hanna Hawa <hannah@marvell.com>,
	linux-arm-kernel@lists.infradead.org,
	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Subject: Re: [PATCH 10/17] irqchip/irq-mvebu-sei: add new driver for Marvell SEI
Date: Fri, 18 May 2018 15:22:21 +0200	[thread overview]
Message-ID: <20180518152221.0c3583fc@xps13> (raw)
In-Reply-To: <20180502111744.5391afff@windsurf.home>

Hi Thomas,

On Wed, 2 May 2018 11:17:44 +0200, Thomas Petazzoni
<thomas.petazzoni@bootlin.com> wrote:

> Hello,
> 
> On Sat, 21 Apr 2018 15:55:30 +0200, Miquel Raynal wrote:
> 
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 5ed465ab1c76..6b5b75cb4694 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -73,6 +73,7 @@ obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
> >  obj-$(CONFIG_PIC32_EVIC)		+= irq-pic32-evic.o
> >  obj-$(CONFIG_MSCC_OCELOT_IRQ)		+= irq-mscc-ocelot.o
> >  obj-$(CONFIG_MVEBU_GICP)		+= irq-mvebu-gicp.o
> > +obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
> >  obj-$(CONFIG_MVEBU_ICU)			+= irq-mvebu-icu.o
> >  obj-$(CONFIG_MVEBU_ODMI)		+= irq-mvebu-odmi.o
> >  obj-$(CONFIG_MVEBU_PIC)			+= irq-mvebu-pic.o  
> 
> Alphabetic ordering would put SEI after PIC I guess :)

Sure.

> 
> > diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c
> > new file mode 100644
> > index 000000000000..5c12c74e3f09
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mvebu-sei.c
> > @@ -0,0 +1,449 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR X11  
> 
> License for code is GPL-2.0 only. We use GPL-2.0 OR X11 for Device
> Trees.

I did not know, thanks for the explanation.

> 
> > +#define pr_fmt(fmt) "mvebu-sei: " fmt
> > +
> > +#include <linux/irq.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/kernel.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/msi.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/irqchip.h>
> > +
> > +#include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > +#define GICP_SET_SEI_OFFSET	0x30
> > +#define GICP_CLR_SEI_OFFSET	GICP_SET_SEI_OFFSET  
> 
> Why do you have this concept of set/clr if there is only one register ?
> I assume that if there is only a "set" register, it means that SEI
> interrupts can only be edge triggered, contrary to NSR interrupts,
> which have separate set/clr to support level-triggered interrupts.
> 
> Having only a "set" offset means that we can rely on the existing
> "struct msi_msg" to convey both the MSI doorbell address and payload,
> and we don't need the mvebu_gicp_get_doorbells() hack.

I misunderstood the specification at first (when copying code from the
gicp driver), and then continued in my mistake. I removed the whole
doorbell thing.

> 
> > +/* Cause register */
> > +#define GICP_SECR(idx)		(0x0  + (idx * 0x4))
> > +/* Mask register */
> > +#define GICP_SEMR(idx)		(0x20 + (idx * 0x4))  
> 
> Minor nit: order register definitions by order of increasing offset,
> i.e the GICP_SET_SEI_OFFSET should be defined here.

Ok.

> 
> > +#define SEI_IRQ_NB_PER_REG	32
> > +#define SEI_IRQ_REG_NB		2  
> 
> s/NB/COUNT/

Changed.

> 
> > +#define SEI_IRQ_NB		(SEI_IRQ_NB_PER_REG * SEI_IRQ_REG_NB)  
> 
> Ditto.
> 
> > +#define SEI_IRQ_REG_IDX(irq_id)	(irq_id / SEI_IRQ_NB_PER_REG)
> > +#define SEI_IRQ_REG_BIT(irq_id)	(irq_id % SEI_IRQ_NB_PER_REG)
> > +
> > +struct mvebu_sei_interrupt_range {
> > +	u32 first;
> > +	u32 number;
> > +};
> > +
> > +struct mvebu_sei {
> > +	struct device *dev;
> > +	void __iomem *base;
> > +	struct resource *res;
> > +	struct irq_domain *ap_domain;
> > +	struct irq_domain *cp_domain;
> > +	struct mvebu_sei_interrupt_range ap_interrupts;
> > +	struct mvebu_sei_interrupt_range cp_interrupts;
> > +	/* Lock on MSI allocations/releases */
> > +	spinlock_t cp_msi_lock;
> > +	DECLARE_BITMAP(cp_msi_bitmap, SEI_IRQ_NB);
> > +};
> > +
> > +static int mvebu_sei_domain_to_sei_irq(struct mvebu_sei *sei,
> > +				       struct irq_domain *domain,
> > +				       irq_hw_number_t hwirq)
> > +{
> > +	if (domain == sei->ap_domain)
> > +		return sei->ap_interrupts.first + hwirq;
> > +	else
> > +		return sei->cp_interrupts.first + hwirq;  
> 
> I am not entirely clear whether we need subnodes or not in this
> binding, but I guess we do because we have one subset of the interrupts
> that are wired interrupts, and another part that are MSI triggered.
> 
> Perhaps this is one aspect on which Marc Zyngier can comment ?
> 
> > +static void mvebu_sei_reset(struct mvebu_sei *sei)
> > +{
> > +	u32 reg_idx;
> > +
> > +	for (reg_idx = 0; reg_idx < SEI_IRQ_REG_NB; reg_idx++) {
> > +		/* Clear all cause bits */
> > +		writel(0xFFFFFFFF, sei->base + GICP_SECR(reg_idx));
> > +		/* Enable all interrupts */
> > +		writel(0, sei->base + GICP_SEMR(reg_idx));  
> 
> Enabling interrupts by default ? This looks weird. They should only be
> enabled... when enabled.

That's right, no need to enable them here.

> 
> > +int mvebu_sei_get_doorbells(struct device_node *dn, phys_addr_t *set,
> > +			    phys_addr_t *clr)
> > +{
> > +	struct platform_device *pdev;
> > +	struct mvebu_sei *sei;
> > +
> > +	pdev = of_find_device_by_node(dn->parent);
> > +	if (!pdev)
> > +		return -ENODEV;
> > +
> > +	sei = platform_get_drvdata(pdev);
> > +	if (!sei)
> > +		return -ENODEV;
> > +
> > +	*set = (phys_addr_t)(sei->res->start + GICP_SET_SEI_OFFSET);
> > +	*clr = (phys_addr_t)(sei->res->start + GICP_CLR_SEI_OFFSET);
> > +
> > +	return 0;
> > +}  
> 
> As I said above, I believe this hack is not needed, because SEIs are
> edge-triggered, and we have a single SET_SEI_OFFSET MSI doorbell
> address to convey, which makes "struct msi_msg" as it is today
> sufficient.

Removed.

> 
> > +static void mvebu_sei_mask_irq(struct irq_data *d)
> > +{
> > +	struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
> > +	u32 reg_idx = SEI_IRQ_REG_IDX(d->hwirq);  
> 
> This doesn't look right. The d->hwirq is relative to the beginning of
> the domain, while you should use sei_irq here. For example, the SEI
> n°32 (first interrupt in the second register) will have d->hwirq = 11,
> because it is the 11th SEI interrupt for the CP. So here, you will
> conclude that reg_idx = 0, while it should be reg_idx = 1.

This is true. I did not saw it because... well... all interrupts were
enabled by default.

> 
> > +	u32 sei_irq = mvebu_sei_domain_to_sei_irq(sei, d->domain, d->hwirq);
> > +	u32 irq_mask = BIT(SEI_IRQ_REG_BIT(sei_irq));
> > +	u32 reg;
> > +
> > +	/* 1 disables the interrupt */
> > +	reg =  readl(sei->base + GICP_SEMR(reg_idx));
> > +	writel(reg | irq_mask, sei->base + GICP_SEMR(reg_idx));  
> 
> Personal taste here, but I prefer:
> 
> 	reg =  readl(sei->base + GICP_SEMR(reg_idx));
> 	reg |= BIT(SEI_IRQ_REG_BIT(sei_irq));
> 	writel(reg, sei->base + GICP_SEMR(reg_idx));

No problem.

> 
> > +static void mvebu_sei_unmask_irq(struct irq_data *d)
> > +{
> > +	struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
> > +	u32 reg_idx = SEI_IRQ_REG_IDX(d->hwirq);  
> 
> Same mistake as above I believe.
> 
> > +	u32 sei_irq = mvebu_sei_domain_to_sei_irq(sei, d->domain, d->hwirq);
> > +	u32 irq_mask = BIT(SEI_IRQ_REG_BIT(sei_irq));
> > +	u32 reg;
> > +
> > +	/* 0 enables the interrupt */
> > +	reg = readl(sei->base + GICP_SEMR(reg_idx));
> > +	writel(reg & ~irq_mask, sei->base + GICP_SEMR(reg_idx));  
> 
> And same nitpick comment :-)
> 
> > +static int mvebu_sei_irq_domain_alloc(struct irq_domain *domain,
> > +				      unsigned int virq, unsigned int nr_irqs,
> > +				      void *args)  
> 
> I think the coding style says that arguments should be aligned, no ?
> 
> > +{
> > +	struct mvebu_sei *sei = domain->host_data;
> > +	struct irq_fwspec *fwspec = args;
> > +	struct irq_chip *irq_chip;
> > +	int sei_hwirq, hwirq;
> > +	int ret;
> > +
> > +	/* Software only supports single allocations for now */
> > +	if (nr_irqs != 1)
> > +		return -ENOTSUPP;
> > +
> > +	if (domain == sei->ap_domain) {
> > +		irq_chip = &mvebu_sei_ap_wired_irq_chip;
> > +		hwirq = fwspec->param[0];
> > +	} else {
> > +		irq_chip = &mvebu_sei_cp_msi_irq_chip;
> > +		spin_lock(&sei->cp_msi_lock);
> > +		hwirq = bitmap_find_free_region(sei->cp_msi_bitmap, SEI_IRQ_NB,
> > +						0);
> > +		spin_unlock(&sei->cp_msi_lock);
> > +		if (hwirq < 0)
> > +			return -ENOSPC;
> > +	}
> > +
> > +	sei_hwirq = mvebu_sei_domain_to_sei_irq(sei, domain, hwirq);
> > +
> > +	fwspec->fwnode = domain->parent->fwnode;
> > +	fwspec->param_count = 3;
> > +	fwspec->param[0] = GIC_SPI;
> > +	fwspec->param[1] = sei_hwirq;
> > +	fwspec->param[2] = IRQ_TYPE_EDGE_RISING;  
> 
> Maybe it's me being confused, but I thought all SEI interrupts were
> muxed together to a single SPI for the parent GIC. But here, you
> allocate different SPIs at the GIC level. Intuitively, this doesn't
> look good. Haven't you copy/pasted too much from the gicp driver, where
> we have a 1:1 mapping between interrupts coming into the GICP and
> interrupts signaled by the GICP to the GIC, while here we have a N:1
> mapping, with N interrupts coming into the GICP_SEI, and only one
> interrupt leaving the GICP_SEI to the GIC ?

I am a bit in troubles understanding what fwspec->param[1] exactly
means here. I suppose I should s/sei_hwirq/0/ as there is only one SPI
interrupt to refer to?

Maybe Marc can comment on this too?

> 
> > +static const struct irq_domain_ops mvebu_sei_ap_domain_ops = {
> > +	.xlate = irq_domain_xlate_onecell,
> > +	.alloc = mvebu_sei_irq_domain_alloc,
> > +	.free = mvebu_sei_irq_domain_free,
> > +};
> > +
> > +static const struct irq_domain_ops mvebu_sei_cp_domain_ops = {
> > +	.xlate = irq_domain_xlate_twocell,
> > +	.alloc = mvebu_sei_irq_domain_alloc,
> > +	.free = mvebu_sei_irq_domain_free,
> > +};  
> 
> Why do you need two cells for the interrupts coming from the CP and
> only one cell for the interrupts coming from the AP ?
> 
> For thermal in the AP, you do:
> 
> +					interrupt-parent = <&sei_wired_controller>;
> +					interrupts = <18>;
> 
> i.e, you don't specify an interrupt type. For thermal in the CP, you do:
> 
> +				interrupts-extended =
> +					<&CP110_LABEL(icu_sei) 116 IRQ_TYPE_LEVEL_HIGH>;
> 
> here you specify an interrupt type. I'm not sure why you have this
> difference. Even more so because I think a SEI level interrupt is not
> possible, since you only have a "SET" register and no "CLR" register.

and then you wrote:

<quote>
> OK, my comment is not very correct here, I'm comparing apple to
> oranges. The former its an interrupt directly pointing to the GICP_SEI,
> while the latter is an interrupt of the ICU, which itself will notify
> the GICP_SEI through an MSI.

> However, I'm still confused as to why you have .xlate =
> irq_domain_xlate_twocell for the mvebu_sei_cp_domain_ops. I think there
> is no need for ->xlate() call back here because it's going to be a MSI
> domain.
</quote>

For thermal in the AP I should probably add an IRQ_TYPE_LEVEL_HIGH in
order to describe properly the interrupt (wired).

I also removed the .xlate entry for the CP domain, I was not sure it
was useless for MSI but it looks it is.

> 
> Some guidance from Marc here might be useful perhaps.
> 
> 
> > +static int mvebu_sei_probe(struct platform_device *pdev)
> > +{
> > +	struct device_node *node = pdev->dev.of_node, *parent, *child;
> > +	struct irq_domain *parent_domain, *plat_domain;
> > +	struct mvebu_sei *sei;
> > +	const __be32 *property;
> > +	u32 top_level_spi, size;
> > +	int ret;
> > +
> > +	sei = devm_kzalloc(&pdev->dev, sizeof(*sei), GFP_KERNEL);
> > +	if (!sei)
> > +		return -ENOMEM;
> > +
> > +	sei->dev = &pdev->dev;
> > +
> > +	spin_lock_init(&sei->cp_msi_lock);
> > +
> > +	sei->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!sei->res) {
> > +		dev_err(sei->dev, "Failed to retrieve SEI resource\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	sei->base = devm_ioremap(sei->dev, sei->res->start,
> > +				 resource_size(sei->res));
> > +	if (!sei->base) {
> > +		dev_err(sei->dev, "Failed to remap SEI resource\n");
> > +		return -ENODEV;
> > +	}  
> 
> Use devm_ioremap_resource() here, and remove the error handling of
> platform_get_resource(), because it's already taken care of by
> devm_ioremap_resource().

Good tip, I'll remember.

> 
> > +	/*
> > +	 * Reserve the single (top-level) parent SPI IRQ from which all the
> > +	 * interrupts handled by this driver will be signaled.
> > +	 */
> > +	top_level_spi = irq_of_parse_and_map(node, 0);
> > +	if (top_level_spi <= 0) {
> > +		dev_err(sei->dev, "Failed to retrieve top-level SPI IRQ\n");
> > +		return -ENODEV;
> > +	}  
> 
> Rather than top_level_spi, something like parent_irq would make more
> sense to me.

Renamed.

> 
> > +	irq_set_chained_handler(top_level_spi, mvebu_sei_handle_cascade_irq);
> > +	irq_set_handler_data(top_level_spi, sei);
> > +
> > +	/*
> > +	 * SEIs in the range [ 0; 20] are wired and come from the AP.
> > +	 * SEIs in the range [21; 63] are CP SEI and are triggered through MSIs.
> > +	 *
> > +	 * Each SEI 'domain' is represented as a subnode.
> > +	 */
> > +
> > +	/* Get a reference to the parent domain to create a hierarchy */
> > +	parent = of_irq_find_parent(node);
> > +	if (!parent) {
> > +		dev_err(sei->dev, "Failed to find parent IRQ node\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}
> > +
> > +	parent_domain = irq_find_host(parent);
> > +	if (!parent_domain) {
> > +		dev_err(sei->dev, "Failed to find parent IRQ domain\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}
> > +
> > +	/* Create the 'wired' hierarchy */
> > +	child = of_find_node_by_name(node, "sei-wired-controller");
> > +	if (!child) {
> > +		dev_err(sei->dev, "Missing 'sei-wired-controller' subnode\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}  
> 
> Don't forget to of_node_put(child) once you're done using this DT node
> reference.

Ok.

> 
> > +
> > +	property = of_get_property(child, "reg", &size);
> > +	if (!property || size != (2 * sizeof(u32))) {
> > +		dev_err(sei->dev, "Missing subnode 'reg' property\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}  
> 
> As Rob said, I don't think the "reg" property is appropriate for this
> usage.

I will change.

> 
> > +	sei->ap_interrupts.first = be32_to_cpu(property[0]);
> > +	sei->ap_interrupts.number = be32_to_cpu(property[1]);
> > +	sei->ap_domain = irq_domain_create_hierarchy(parent_domain, 0,
> > +						     sei->ap_interrupts.number,
> > +						     of_node_to_fwnode(child),
> > +						     &mvebu_sei_ap_domain_ops,
> > +						     sei);
> > +	if (!sei->ap_domain) {
> > +		dev_err(sei->dev, "Failed to create AP IRQ domain\n");
> > +		ret = -ENOMEM;
> > +		goto dispose_irq;
> > +	}
> > +
> > +	/* Create the 'MSI' hierarchy */
> > +	child = of_find_node_by_name(node, "sei-msi-controller");
> > +	if (!child) {
> > +		dev_err(sei->dev, "Missing 'sei-msi-controller' subnode\n");
> > +		ret = -ENODEV;
> > +		goto remove_ap_domain;
> > +	}  
> 
> Ditto: missing of_node_put(child) somewhere below to balance
> of_find_node_by_name().

Ok.

> 
> > +	property = of_get_property(child, "reg", &size);
> > +	if (!property || size != (2 * sizeof(u32))) {
> > +		dev_err(sei->dev, "Missing subnode 'reg' property\n");
> > +		ret = -ENODEV;
> > +		goto remove_ap_domain;
> > +	}
> > +
> > +	sei->cp_interrupts.first = be32_to_cpu(property[0]);
> > +	sei->cp_interrupts.number = be32_to_cpu(property[1]);
> > +	sei->cp_domain = irq_domain_create_hierarchy(parent_domain, 0,
> > +						     sei->cp_interrupts.number,
> > +						     of_node_to_fwnode(child),
> > +						     &mvebu_sei_cp_domain_ops,
> > +						     sei);
> > +	if (!sei->cp_domain) {
> > +		pr_err("Failed to create CPs IRQ domain\n");
> > +		ret = -ENOMEM;
> > +		goto remove_ap_domain;
> > +	}
> > +
> > +	plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(child),
> > +						     &mvebu_sei_msi_domain_info,
> > +						     sei->cp_domain);
> > +	if (!plat_domain) {
> > +		pr_err("Failed to create CPs MSI domain\n");
> > +		ret = -ENOMEM;
> > +		goto remove_cp_domain;
> > +	}  
> 
> 
> 
> > +
> > +	platform_set_drvdata(pdev, sei);
> > +
> > +	mvebu_sei_reset(sei);  
> 
> Please do the reset *before* registering the IRQ domains, it's more
> logical to have the HW ready and then expose it to Linux rather than
> the opposite.

Sure.

> 
> It would be nice to have the review from Marc on this driver,
> especially on whether the SEI is properly modeled in terms of IRQ
> domains;
> 
> > diff --git a/drivers/irqchip/irq-mvebu-sei.h b/drivers/irqchip/irq-mvebu-sei.h
> > new file mode 100644
> > index 000000000000..f0c12a441923
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mvebu-sei.h
> > @@ -0,0 +1,12 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef __MVEBU_SEI_H__
> > +#define __MVEBU_SEI_H__
> > +
> > +#include <linux/types.h>
> > +
> > +struct device_node;
> > +
> > +int mvebu_sei_get_doorbells(struct device_node *dn, phys_addr_t *set,
> > +			    phys_addr_t *clr);
> > +
> > +#endif /* __MVEBU_SEI_H__ */  
> 
> This header file can be removed if you drop mvebu_sei_get_doorbells(),
> as suggested above.

Indeed.

> 
> Best regards,
> 
> Thomas

Thanks!
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

WARNING: multiple messages have this Message-ID (diff)
From: miquel.raynal@bootlin.com (Miquel Raynal)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 10/17] irqchip/irq-mvebu-sei: add new driver for Marvell SEI
Date: Fri, 18 May 2018 15:22:21 +0200	[thread overview]
Message-ID: <20180518152221.0c3583fc@xps13> (raw)
In-Reply-To: <20180502111744.5391afff@windsurf.home>

Hi Thomas,

On Wed, 2 May 2018 11:17:44 +0200, Thomas Petazzoni
<thomas.petazzoni@bootlin.com> wrote:

> Hello,
> 
> On Sat, 21 Apr 2018 15:55:30 +0200, Miquel Raynal wrote:
> 
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 5ed465ab1c76..6b5b75cb4694 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -73,6 +73,7 @@ obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
> >  obj-$(CONFIG_PIC32_EVIC)		+= irq-pic32-evic.o
> >  obj-$(CONFIG_MSCC_OCELOT_IRQ)		+= irq-mscc-ocelot.o
> >  obj-$(CONFIG_MVEBU_GICP)		+= irq-mvebu-gicp.o
> > +obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
> >  obj-$(CONFIG_MVEBU_ICU)			+= irq-mvebu-icu.o
> >  obj-$(CONFIG_MVEBU_ODMI)		+= irq-mvebu-odmi.o
> >  obj-$(CONFIG_MVEBU_PIC)			+= irq-mvebu-pic.o  
> 
> Alphabetic ordering would put SEI after PIC I guess :)

Sure.

> 
> > diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c
> > new file mode 100644
> > index 000000000000..5c12c74e3f09
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mvebu-sei.c
> > @@ -0,0 +1,449 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR X11  
> 
> License for code is GPL-2.0 only. We use GPL-2.0 OR X11 for Device
> Trees.

I did not know, thanks for the explanation.

> 
> > +#define pr_fmt(fmt) "mvebu-sei: " fmt
> > +
> > +#include <linux/irq.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/kernel.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/msi.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/irqchip.h>
> > +
> > +#include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > +#define GICP_SET_SEI_OFFSET	0x30
> > +#define GICP_CLR_SEI_OFFSET	GICP_SET_SEI_OFFSET  
> 
> Why do you have this concept of set/clr if there is only one register ?
> I assume that if there is only a "set" register, it means that SEI
> interrupts can only be edge triggered, contrary to NSR interrupts,
> which have separate set/clr to support level-triggered interrupts.
> 
> Having only a "set" offset means that we can rely on the existing
> "struct msi_msg" to convey both the MSI doorbell address and payload,
> and we don't need the mvebu_gicp_get_doorbells() hack.

I misunderstood the specification at first (when copying code from the
gicp driver), and then continued in my mistake. I removed the whole
doorbell thing.

> 
> > +/* Cause register */
> > +#define GICP_SECR(idx)		(0x0  + (idx * 0x4))
> > +/* Mask register */
> > +#define GICP_SEMR(idx)		(0x20 + (idx * 0x4))  
> 
> Minor nit: order register definitions by order of increasing offset,
> i.e the GICP_SET_SEI_OFFSET should be defined here.

Ok.

> 
> > +#define SEI_IRQ_NB_PER_REG	32
> > +#define SEI_IRQ_REG_NB		2  
> 
> s/NB/COUNT/

Changed.

> 
> > +#define SEI_IRQ_NB		(SEI_IRQ_NB_PER_REG * SEI_IRQ_REG_NB)  
> 
> Ditto.
> 
> > +#define SEI_IRQ_REG_IDX(irq_id)	(irq_id / SEI_IRQ_NB_PER_REG)
> > +#define SEI_IRQ_REG_BIT(irq_id)	(irq_id % SEI_IRQ_NB_PER_REG)
> > +
> > +struct mvebu_sei_interrupt_range {
> > +	u32 first;
> > +	u32 number;
> > +};
> > +
> > +struct mvebu_sei {
> > +	struct device *dev;
> > +	void __iomem *base;
> > +	struct resource *res;
> > +	struct irq_domain *ap_domain;
> > +	struct irq_domain *cp_domain;
> > +	struct mvebu_sei_interrupt_range ap_interrupts;
> > +	struct mvebu_sei_interrupt_range cp_interrupts;
> > +	/* Lock on MSI allocations/releases */
> > +	spinlock_t cp_msi_lock;
> > +	DECLARE_BITMAP(cp_msi_bitmap, SEI_IRQ_NB);
> > +};
> > +
> > +static int mvebu_sei_domain_to_sei_irq(struct mvebu_sei *sei,
> > +				       struct irq_domain *domain,
> > +				       irq_hw_number_t hwirq)
> > +{
> > +	if (domain == sei->ap_domain)
> > +		return sei->ap_interrupts.first + hwirq;
> > +	else
> > +		return sei->cp_interrupts.first + hwirq;  
> 
> I am not entirely clear whether we need subnodes or not in this
> binding, but I guess we do because we have one subset of the interrupts
> that are wired interrupts, and another part that are MSI triggered.
> 
> Perhaps this is one aspect on which Marc Zyngier can comment ?
> 
> > +static void mvebu_sei_reset(struct mvebu_sei *sei)
> > +{
> > +	u32 reg_idx;
> > +
> > +	for (reg_idx = 0; reg_idx < SEI_IRQ_REG_NB; reg_idx++) {
> > +		/* Clear all cause bits */
> > +		writel(0xFFFFFFFF, sei->base + GICP_SECR(reg_idx));
> > +		/* Enable all interrupts */
> > +		writel(0, sei->base + GICP_SEMR(reg_idx));  
> 
> Enabling interrupts by default ? This looks weird. They should only be
> enabled... when enabled.

That's right, no need to enable them here.

> 
> > +int mvebu_sei_get_doorbells(struct device_node *dn, phys_addr_t *set,
> > +			    phys_addr_t *clr)
> > +{
> > +	struct platform_device *pdev;
> > +	struct mvebu_sei *sei;
> > +
> > +	pdev = of_find_device_by_node(dn->parent);
> > +	if (!pdev)
> > +		return -ENODEV;
> > +
> > +	sei = platform_get_drvdata(pdev);
> > +	if (!sei)
> > +		return -ENODEV;
> > +
> > +	*set = (phys_addr_t)(sei->res->start + GICP_SET_SEI_OFFSET);
> > +	*clr = (phys_addr_t)(sei->res->start + GICP_CLR_SEI_OFFSET);
> > +
> > +	return 0;
> > +}  
> 
> As I said above, I believe this hack is not needed, because SEIs are
> edge-triggered, and we have a single SET_SEI_OFFSET MSI doorbell
> address to convey, which makes "struct msi_msg" as it is today
> sufficient.

Removed.

> 
> > +static void mvebu_sei_mask_irq(struct irq_data *d)
> > +{
> > +	struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
> > +	u32 reg_idx = SEI_IRQ_REG_IDX(d->hwirq);  
> 
> This doesn't look right. The d->hwirq is relative to the beginning of
> the domain, while you should use sei_irq here. For example, the SEI
> n?32 (first interrupt in the second register) will have d->hwirq = 11,
> because it is the 11th SEI interrupt for the CP. So here, you will
> conclude that reg_idx = 0, while it should be reg_idx = 1.

This is true. I did not saw it because... well... all interrupts were
enabled by default.

> 
> > +	u32 sei_irq = mvebu_sei_domain_to_sei_irq(sei, d->domain, d->hwirq);
> > +	u32 irq_mask = BIT(SEI_IRQ_REG_BIT(sei_irq));
> > +	u32 reg;
> > +
> > +	/* 1 disables the interrupt */
> > +	reg =  readl(sei->base + GICP_SEMR(reg_idx));
> > +	writel(reg | irq_mask, sei->base + GICP_SEMR(reg_idx));  
> 
> Personal taste here, but I prefer:
> 
> 	reg =  readl(sei->base + GICP_SEMR(reg_idx));
> 	reg |= BIT(SEI_IRQ_REG_BIT(sei_irq));
> 	writel(reg, sei->base + GICP_SEMR(reg_idx));

No problem.

> 
> > +static void mvebu_sei_unmask_irq(struct irq_data *d)
> > +{
> > +	struct mvebu_sei *sei = irq_data_get_irq_chip_data(d);
> > +	u32 reg_idx = SEI_IRQ_REG_IDX(d->hwirq);  
> 
> Same mistake as above I believe.
> 
> > +	u32 sei_irq = mvebu_sei_domain_to_sei_irq(sei, d->domain, d->hwirq);
> > +	u32 irq_mask = BIT(SEI_IRQ_REG_BIT(sei_irq));
> > +	u32 reg;
> > +
> > +	/* 0 enables the interrupt */
> > +	reg = readl(sei->base + GICP_SEMR(reg_idx));
> > +	writel(reg & ~irq_mask, sei->base + GICP_SEMR(reg_idx));  
> 
> And same nitpick comment :-)
> 
> > +static int mvebu_sei_irq_domain_alloc(struct irq_domain *domain,
> > +				      unsigned int virq, unsigned int nr_irqs,
> > +				      void *args)  
> 
> I think the coding style says that arguments should be aligned, no ?
> 
> > +{
> > +	struct mvebu_sei *sei = domain->host_data;
> > +	struct irq_fwspec *fwspec = args;
> > +	struct irq_chip *irq_chip;
> > +	int sei_hwirq, hwirq;
> > +	int ret;
> > +
> > +	/* Software only supports single allocations for now */
> > +	if (nr_irqs != 1)
> > +		return -ENOTSUPP;
> > +
> > +	if (domain == sei->ap_domain) {
> > +		irq_chip = &mvebu_sei_ap_wired_irq_chip;
> > +		hwirq = fwspec->param[0];
> > +	} else {
> > +		irq_chip = &mvebu_sei_cp_msi_irq_chip;
> > +		spin_lock(&sei->cp_msi_lock);
> > +		hwirq = bitmap_find_free_region(sei->cp_msi_bitmap, SEI_IRQ_NB,
> > +						0);
> > +		spin_unlock(&sei->cp_msi_lock);
> > +		if (hwirq < 0)
> > +			return -ENOSPC;
> > +	}
> > +
> > +	sei_hwirq = mvebu_sei_domain_to_sei_irq(sei, domain, hwirq);
> > +
> > +	fwspec->fwnode = domain->parent->fwnode;
> > +	fwspec->param_count = 3;
> > +	fwspec->param[0] = GIC_SPI;
> > +	fwspec->param[1] = sei_hwirq;
> > +	fwspec->param[2] = IRQ_TYPE_EDGE_RISING;  
> 
> Maybe it's me being confused, but I thought all SEI interrupts were
> muxed together to a single SPI for the parent GIC. But here, you
> allocate different SPIs at the GIC level. Intuitively, this doesn't
> look good. Haven't you copy/pasted too much from the gicp driver, where
> we have a 1:1 mapping between interrupts coming into the GICP and
> interrupts signaled by the GICP to the GIC, while here we have a N:1
> mapping, with N interrupts coming into the GICP_SEI, and only one
> interrupt leaving the GICP_SEI to the GIC ?

I am a bit in troubles understanding what fwspec->param[1] exactly
means here. I suppose I should s/sei_hwirq/0/ as there is only one SPI
interrupt to refer to?

Maybe Marc can comment on this too?

> 
> > +static const struct irq_domain_ops mvebu_sei_ap_domain_ops = {
> > +	.xlate = irq_domain_xlate_onecell,
> > +	.alloc = mvebu_sei_irq_domain_alloc,
> > +	.free = mvebu_sei_irq_domain_free,
> > +};
> > +
> > +static const struct irq_domain_ops mvebu_sei_cp_domain_ops = {
> > +	.xlate = irq_domain_xlate_twocell,
> > +	.alloc = mvebu_sei_irq_domain_alloc,
> > +	.free = mvebu_sei_irq_domain_free,
> > +};  
> 
> Why do you need two cells for the interrupts coming from the CP and
> only one cell for the interrupts coming from the AP ?
> 
> For thermal in the AP, you do:
> 
> +					interrupt-parent = <&sei_wired_controller>;
> +					interrupts = <18>;
> 
> i.e, you don't specify an interrupt type. For thermal in the CP, you do:
> 
> +				interrupts-extended =
> +					<&CP110_LABEL(icu_sei) 116 IRQ_TYPE_LEVEL_HIGH>;
> 
> here you specify an interrupt type. I'm not sure why you have this
> difference. Even more so because I think a SEI level interrupt is not
> possible, since you only have a "SET" register and no "CLR" register.

and then you wrote:

<quote>
> OK, my comment is not very correct here, I'm comparing apple to
> oranges. The former its an interrupt directly pointing to the GICP_SEI,
> while the latter is an interrupt of the ICU, which itself will notify
> the GICP_SEI through an MSI.

> However, I'm still confused as to why you have .xlate =
> irq_domain_xlate_twocell for the mvebu_sei_cp_domain_ops. I think there
> is no need for ->xlate() call back here because it's going to be a MSI
> domain.
</quote>

For thermal in the AP I should probably add an IRQ_TYPE_LEVEL_HIGH in
order to describe properly the interrupt (wired).

I also removed the .xlate entry for the CP domain, I was not sure it
was useless for MSI but it looks it is.

> 
> Some guidance from Marc here might be useful perhaps.
> 
> 
> > +static int mvebu_sei_probe(struct platform_device *pdev)
> > +{
> > +	struct device_node *node = pdev->dev.of_node, *parent, *child;
> > +	struct irq_domain *parent_domain, *plat_domain;
> > +	struct mvebu_sei *sei;
> > +	const __be32 *property;
> > +	u32 top_level_spi, size;
> > +	int ret;
> > +
> > +	sei = devm_kzalloc(&pdev->dev, sizeof(*sei), GFP_KERNEL);
> > +	if (!sei)
> > +		return -ENOMEM;
> > +
> > +	sei->dev = &pdev->dev;
> > +
> > +	spin_lock_init(&sei->cp_msi_lock);
> > +
> > +	sei->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!sei->res) {
> > +		dev_err(sei->dev, "Failed to retrieve SEI resource\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	sei->base = devm_ioremap(sei->dev, sei->res->start,
> > +				 resource_size(sei->res));
> > +	if (!sei->base) {
> > +		dev_err(sei->dev, "Failed to remap SEI resource\n");
> > +		return -ENODEV;
> > +	}  
> 
> Use devm_ioremap_resource() here, and remove the error handling of
> platform_get_resource(), because it's already taken care of by
> devm_ioremap_resource().

Good tip, I'll remember.

> 
> > +	/*
> > +	 * Reserve the single (top-level) parent SPI IRQ from which all the
> > +	 * interrupts handled by this driver will be signaled.
> > +	 */
> > +	top_level_spi = irq_of_parse_and_map(node, 0);
> > +	if (top_level_spi <= 0) {
> > +		dev_err(sei->dev, "Failed to retrieve top-level SPI IRQ\n");
> > +		return -ENODEV;
> > +	}  
> 
> Rather than top_level_spi, something like parent_irq would make more
> sense to me.

Renamed.

> 
> > +	irq_set_chained_handler(top_level_spi, mvebu_sei_handle_cascade_irq);
> > +	irq_set_handler_data(top_level_spi, sei);
> > +
> > +	/*
> > +	 * SEIs in the range [ 0; 20] are wired and come from the AP.
> > +	 * SEIs in the range [21; 63] are CP SEI and are triggered through MSIs.
> > +	 *
> > +	 * Each SEI 'domain' is represented as a subnode.
> > +	 */
> > +
> > +	/* Get a reference to the parent domain to create a hierarchy */
> > +	parent = of_irq_find_parent(node);
> > +	if (!parent) {
> > +		dev_err(sei->dev, "Failed to find parent IRQ node\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}
> > +
> > +	parent_domain = irq_find_host(parent);
> > +	if (!parent_domain) {
> > +		dev_err(sei->dev, "Failed to find parent IRQ domain\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}
> > +
> > +	/* Create the 'wired' hierarchy */
> > +	child = of_find_node_by_name(node, "sei-wired-controller");
> > +	if (!child) {
> > +		dev_err(sei->dev, "Missing 'sei-wired-controller' subnode\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}  
> 
> Don't forget to of_node_put(child) once you're done using this DT node
> reference.

Ok.

> 
> > +
> > +	property = of_get_property(child, "reg", &size);
> > +	if (!property || size != (2 * sizeof(u32))) {
> > +		dev_err(sei->dev, "Missing subnode 'reg' property\n");
> > +		ret = -ENODEV;
> > +		goto dispose_irq;
> > +	}  
> 
> As Rob said, I don't think the "reg" property is appropriate for this
> usage.

I will change.

> 
> > +	sei->ap_interrupts.first = be32_to_cpu(property[0]);
> > +	sei->ap_interrupts.number = be32_to_cpu(property[1]);
> > +	sei->ap_domain = irq_domain_create_hierarchy(parent_domain, 0,
> > +						     sei->ap_interrupts.number,
> > +						     of_node_to_fwnode(child),
> > +						     &mvebu_sei_ap_domain_ops,
> > +						     sei);
> > +	if (!sei->ap_domain) {
> > +		dev_err(sei->dev, "Failed to create AP IRQ domain\n");
> > +		ret = -ENOMEM;
> > +		goto dispose_irq;
> > +	}
> > +
> > +	/* Create the 'MSI' hierarchy */
> > +	child = of_find_node_by_name(node, "sei-msi-controller");
> > +	if (!child) {
> > +		dev_err(sei->dev, "Missing 'sei-msi-controller' subnode\n");
> > +		ret = -ENODEV;
> > +		goto remove_ap_domain;
> > +	}  
> 
> Ditto: missing of_node_put(child) somewhere below to balance
> of_find_node_by_name().

Ok.

> 
> > +	property = of_get_property(child, "reg", &size);
> > +	if (!property || size != (2 * sizeof(u32))) {
> > +		dev_err(sei->dev, "Missing subnode 'reg' property\n");
> > +		ret = -ENODEV;
> > +		goto remove_ap_domain;
> > +	}
> > +
> > +	sei->cp_interrupts.first = be32_to_cpu(property[0]);
> > +	sei->cp_interrupts.number = be32_to_cpu(property[1]);
> > +	sei->cp_domain = irq_domain_create_hierarchy(parent_domain, 0,
> > +						     sei->cp_interrupts.number,
> > +						     of_node_to_fwnode(child),
> > +						     &mvebu_sei_cp_domain_ops,
> > +						     sei);
> > +	if (!sei->cp_domain) {
> > +		pr_err("Failed to create CPs IRQ domain\n");
> > +		ret = -ENOMEM;
> > +		goto remove_ap_domain;
> > +	}
> > +
> > +	plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(child),
> > +						     &mvebu_sei_msi_domain_info,
> > +						     sei->cp_domain);
> > +	if (!plat_domain) {
> > +		pr_err("Failed to create CPs MSI domain\n");
> > +		ret = -ENOMEM;
> > +		goto remove_cp_domain;
> > +	}  
> 
> 
> 
> > +
> > +	platform_set_drvdata(pdev, sei);
> > +
> > +	mvebu_sei_reset(sei);  
> 
> Please do the reset *before* registering the IRQ domains, it's more
> logical to have the HW ready and then expose it to Linux rather than
> the opposite.

Sure.

> 
> It would be nice to have the review from Marc on this driver,
> especially on whether the SEI is properly modeled in terms of IRQ
> domains;
> 
> > diff --git a/drivers/irqchip/irq-mvebu-sei.h b/drivers/irqchip/irq-mvebu-sei.h
> > new file mode 100644
> > index 000000000000..f0c12a441923
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mvebu-sei.h
> > @@ -0,0 +1,12 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef __MVEBU_SEI_H__
> > +#define __MVEBU_SEI_H__
> > +
> > +#include <linux/types.h>
> > +
> > +struct device_node;
> > +
> > +int mvebu_sei_get_doorbells(struct device_node *dn, phys_addr_t *set,
> > +			    phys_addr_t *clr);
> > +
> > +#endif /* __MVEBU_SEI_H__ */  
> 
> This header file can be removed if you drop mvebu_sei_get_doorbells(),
> as suggested above.

Indeed.

> 
> Best regards,
> 
> Thomas

Thanks!
Miqu?l

  parent reply	other threads:[~2018-05-18 13:22 UTC|newest]

Thread overview: 92+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-04-21 13:55 [PATCH 00/17] Add System Error Interrupt support to Armada SoCs Miquel Raynal
2018-04-21 13:55 ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 01/17] dt-bindings/interrupt-controller: fix Marvell ICU length in the example Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-27 20:16   ` Rob Herring
2018-04-27 20:16     ` Rob Herring
2018-04-30 13:44   ` Thomas Petazzoni
2018-04-30 13:44     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 02/17] arm64: dts: marvell: fix CP110 ICU node size Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-30 12:38   ` Gregory CLEMENT
2018-04-30 12:38     ` Gregory CLEMENT
2018-04-30 13:44   ` Thomas Petazzoni
2018-04-30 13:44     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 03/17] arm64: dts: marvell: add syscon compatible to CP110 ICU node Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-30 13:45   ` Thomas Petazzoni
2018-04-30 13:45     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 04/17] irqchip/irq-mvebu-icu: fix wrong user data retrieval Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-30 13:49   ` Thomas Petazzoni
2018-04-30 13:49     ` Thomas Petazzoni
2018-05-03 14:57     ` Miquel Raynal
2018-05-03 14:57       ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 05/17] irqchip/irq-mvebu-icu: clarify the reset operation of configured interrupts Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-30 13:51   ` Thomas Petazzoni
2018-04-30 13:51     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 06/17] irqchip/irq-mvebu-icu: switch to regmap Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-30 12:42   ` Gregory CLEMENT
2018-04-30 12:42     ` Gregory CLEMENT
2018-04-30 13:53   ` Thomas Petazzoni
2018-04-30 13:53     ` Thomas Petazzoni
2018-05-03 15:05     ` Miquel Raynal
2018-05-03 15:05       ` Miquel Raynal
2018-04-30 13:58   ` Thomas Petazzoni
2018-04-30 13:58     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 07/17] irqchip/irq-mvebu-icu: make irq_domain local Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-05-02  8:02   ` Thomas Petazzoni
2018-05-02  8:02     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 08/17] irqchip/irq-mvebu-icu: disociate ICU and NSR Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-05-02  8:03   ` Thomas Petazzoni
2018-05-02  8:03     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 09/17] irqchip/irq-mvebu-icu: support ICU subnodes Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-05-02  8:13   ` Thomas Petazzoni
2018-05-02  8:13     ` Thomas Petazzoni
2018-05-04  8:32     ` Miquel Raynal
2018-05-04  8:32       ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 10/17] irqchip/irq-mvebu-sei: add new driver for Marvell SEI Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-05-02  9:17   ` Thomas Petazzoni
2018-05-02  9:17     ` Thomas Petazzoni
2018-05-02 15:56     ` Thomas Petazzoni
2018-05-02 15:56       ` Thomas Petazzoni
2018-05-18 13:22     ` Miquel Raynal [this message]
2018-05-18 13:22       ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 11/17] arm64: marvell: enable SEI driver Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-30 13:01   ` Gregory CLEMENT
2018-04-30 13:01     ` Gregory CLEMENT
2018-04-21 13:55 ` [PATCH 12/17] irqchip/irq-mvebu-icu: add support for System Error Interrupts (SEI) Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 13/17] dt-bindings/interrupt-controller: update Marvell ICU bindings Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-27 20:47   ` Rob Herring
2018-04-27 20:47     ` Rob Herring
2018-04-28 10:42     ` Miquel Raynal
2018-04-28 10:42       ` Miquel Raynal
2018-04-28 10:50       ` Thomas Petazzoni
2018-04-28 10:50         ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 14/17] dt-bindings/interrupt-controller: add description for Marvell SEI node Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-27 20:50   ` Rob Herring
2018-04-27 20:50     ` Rob Herring
2018-04-28 10:48     ` Miquel Raynal
2018-04-28 10:48       ` Miquel Raynal
2018-04-30 14:09       ` Rob Herring
2018-04-30 14:09         ` Rob Herring
2018-05-18 14:48         ` Miquel Raynal
2018-05-18 14:48           ` Miquel Raynal
2018-04-30 14:24   ` Thomas Petazzoni
2018-04-30 14:24     ` Thomas Petazzoni
2018-04-21 13:55 ` [PATCH 15/17] arm64: dts: marvell: add AP806 SEI subnode Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 16/17] arm64: dts: marvell: use new bindings for CP110 interrupts Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal
2018-04-21 13:55 ` [PATCH 17/17] arm64: dts: marvell: add CP110 ICU SEI subnode Miquel Raynal
2018-04-21 13:55   ` Miquel Raynal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180518152221.0c3583fc@xps13 \
    --to=miquel.raynal@bootlin.com \
    --cc=andrew@lunn.ch \
    --cc=antoine.tenart@bootlin.com \
    --cc=catalin.marinas@arm.com \
    --cc=devicetree@vger.kernel.org \
    --cc=gregory.clement@bootlin.com \
    --cc=hannah@marvell.com \
    --cc=hayim@marvell.com \
    --cc=jason@lakedaemon.net \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=marc.zyngier@arm.com \
    --cc=mark.rutland@arm.com \
    --cc=maxime.chevallier@bootlin.com \
    --cc=nadavh@marvell.com \
    --cc=robh+dt@kernel.org \
    --cc=sebastian.hesselbarth@gmail.com \
    --cc=tglx@linutronix.de \
    --cc=thomas.petazzoni@bootlin.com \
    --cc=will.deacon@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.