lkml.org 
[lkml]   [2019]   [Jul]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [PATCH 3/5] MIPS: lantiq: add an irq_domain and irq_chip for EBU
Hi Martin,

On Sat, 27 Jul 2019 18:53:13 +0100,
Martin Blumenstingl <martin.blumenstingl@googlemail.com> wrote:
>
> The PCI_INTA on Lantiq SoCs is a chained interrupt:
> EBU configures the interrupt type, has a registers to enable/disable
> and ACK the interrupt. This is chained with the ICU interrupt where the
> parent interrupt of the EBU IRQ has to be ACK'ed.
>
> Move all EBU interrupt logic into ebu.c and expose it using an
> irq_domain and irq_chip.
> Drop the hardcoded EBU interrupt configuration from pci-lantiq.c as this
> can now be expressed in device tree by passing the EBU interrupt line to
> PCI_INTA (using for example "... &ebu0 0 IRQ_TYPE_LEVEL_LOW").
> Also drop the EBU interrupt masking from irq.c because that's now
> managed by EBU's own irq_ack callback.
>
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
> .../include/asm/mach-lantiq/xway/lantiq_soc.h | 3 -
> arch/mips/lantiq/ebu.c | 149 ++++++++++++++++++
> arch/mips/lantiq/irq.c | 11 --
> arch/mips/pci/pci-lantiq.c | 4 -
> 4 files changed, 149 insertions(+), 18 deletions(-)
>
> diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
> index 02a64ad6c0cc..5555deb02397 100644
> --- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
> +++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
> @@ -79,9 +79,6 @@ extern __iomem void *ltq_cgu_membase;
> #define LTQ_EARLY_ASC KSEG1ADDR(LTQ_ASC1_BASE_ADDR)
>
> /* EBU - external bus unit */
> -#define LTQ_EBU_PCC_CON 0x0090
> -#define LTQ_EBU_PCC_IEN 0x00A4
> -#define LTQ_EBU_PCC_ISTAT 0x00A0
> #define LTQ_EBU_BUSCON1 0x0064
> #define LTQ_EBU_ADDRSEL1 0x0024
>
> diff --git a/arch/mips/lantiq/ebu.c b/arch/mips/lantiq/ebu.c
> index b2e84cf83f91..12951eb3c88f 100644
> --- a/arch/mips/lantiq/ebu.c
> +++ b/arch/mips/lantiq/ebu.c
> @@ -7,7 +7,11 @@
> #include <linux/bits.h>
> #include <linux/export.h>
> #include <linux/ioport.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> #include <linux/of.h>
> +#include <linux/of_irq.h>
> #include <linux/of_platform.h>
> #include <linux/of_address.h>
>
> @@ -15,6 +19,19 @@
>
> #define LTQ_EBU_BUSCON0 0x0060
> #define LTQ_EBU_BUSCON_WRDIS BIT(31)
> +#define LTQ_EBU_PCC_CON 0x0090
> +#define LTQ_EBU_PCC_CON_PCCARD_ON BIT(0)
> +#define LTQ_EBU_PCC_CON_IREQ_RISING_EDGE 0x2
> +#define LTQ_EBU_PCC_CON_IREQ_FALLING_EDGE 0x4
> +#define LTQ_EBU_PCC_CON_IREQ_BOTH_EDGE 0x6

So BOTH_EDGE is actually (RISING_EDGE | FALLING_EDGE). It'd be nice to
express it this way.

> +#define LTQ_EBU_PCC_CON_IREQ_DIS 0x8

What does "DIS" mean?

> +#define LTQ_EBU_PCC_CON_IREQ_HIGH_LEVEL_DETECT 0xa
> +#define LTQ_EBU_PCC_CON_IREQ_LOW_LEVEL_DETECT 0xc

Again, these two are (DIS | RISING) and (DIS | FALLING).

> +#define LTQ_EBU_PCC_CON_IREQ_MASK 0xe
> +#define LTQ_EBU_PCC_ISTAT 0x00a0
> +#define LTQ_EBU_PCC_ISTAT_PCI BIT(4)
> +#define LTQ_EBU_PCC_IEN 0x00a4
> +#define LTQ_EBU_PCC_IEN_PCI_EN BIT(4)
>
> void __iomem *ltq_ebu_membase;
>
> @@ -52,6 +69,131 @@ static const struct of_device_id of_ebu_ids[] __initconst = {
> { /* sentinel */ },
> };
>
> +static void ltq_ebu_ack_irq(struct irq_data *d)
> +{
> + ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_ISTAT) | LTQ_EBU_PCC_ISTAT_PCI,
> + LTQ_EBU_PCC_ISTAT);
> +}
> +
> +static void ltq_ebu_mask_irq(struct irq_data *d)
> +{
> + ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) & ~LTQ_EBU_PCC_IEN_PCI_EN,
> + LTQ_EBU_PCC_IEN);
> +}
> +
> +static void ltq_ebu_unmask_irq(struct irq_data *d)
> +{
> + ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) | LTQ_EBU_PCC_IEN_PCI_EN,
> + LTQ_EBU_PCC_IEN);
> +}
> +
> +static int ltq_ebu_set_irq_type(struct irq_data *d, unsigned int flow_type)
> +{
> + u32 val = ltq_ebu_r32(LTQ_EBU_PCC_CON);
> +
> + val &= ~LTQ_EBU_PCC_CON_IREQ_MASK;
> +
> + switch (flow_type & IRQ_TYPE_SENSE_MASK) {
> + case IRQ_TYPE_NONE:
> + val |= LTQ_EBU_PCC_CON_IREQ_DIS;
> + break;

I'm not sure IRQ_TYPE_NONE makes much sense here. What's the expected
semantic?

> +
> + case IRQ_TYPE_EDGE_RISING:
> + val |= LTQ_EBU_PCC_CON_IREQ_RISING_EDGE;
> + break;
> +
> + case IRQ_TYPE_EDGE_FALLING:
> + val |= LTQ_EBU_PCC_CON_IREQ_FALLING_EDGE;
> + break;
> +
> + case IRQ_TYPE_EDGE_BOTH:
> + val |= LTQ_EBU_PCC_CON_IREQ_BOTH_EDGE;
> + break;
> +
> + case IRQ_TYPE_LEVEL_HIGH:
> + val |= LTQ_EBU_PCC_CON_IREQ_HIGH_LEVEL_DETECT;
> + break;
> +
> + case IRQ_TYPE_LEVEL_LOW:
> + val |= LTQ_EBU_PCC_CON_IREQ_LOW_LEVEL_DETECT;
> + break;
> +
> + default:
> + pr_err("Invalid trigger mode %x for IRQ %d\n", flow_type,
> + d->irq);

irq_set_type will already complain in the kernel log, no need to add
an extra message.

> + return -EINVAL;
> + }
> +
> + ltq_ebu_w32(val, LTQ_EBU_PCC_CON);
> +
> + return 0;
> +}
> +
> +static void ltq_ebu_irq_handler(struct irq_desc *desc)
> +{
> + struct irq_domain *domain = irq_desc_get_handler_data(desc);
> + struct irq_chip *irqchip = irq_desc_get_chip(desc);
> +
> + chained_irq_enter(irqchip, desc);
> +
> + generic_handle_irq(irq_find_mapping(domain, 0));

Having an irqdomain for a single interrupt is a bit over the top... Is
that for the convenience of the DT infrastructure?

> +
> + chained_irq_exit(irqchip, desc);
> +}
> +
> +static struct irq_chip ltq_ebu_irq_chip = {
> + .name = "EBU",
> + .irq_ack = ltq_ebu_ack_irq,
> + .irq_mask = ltq_ebu_mask_irq,
> + .irq_unmask = ltq_ebu_unmask_irq,
> + .irq_set_type = ltq_ebu_set_irq_type,
> +};
> +
> +static int ltq_ebu_irq_map(struct irq_domain *domain, unsigned int irq,
> + irq_hw_number_t hwirq)
> +{
> + irq_set_chip_and_handler(irq, &ltq_ebu_irq_chip, handle_edge_irq);
> + irq_set_chip_data(irq, domain->host_data);
> +
> + return 0;
> +}
> +
> +static const struct irq_domain_ops ltq_ebu_irqdomain_ops = {
> + .map = ltq_ebu_irq_map,
> + .xlate = irq_domain_xlate_twocell,
> +};
> +
> +static int ltq_ebu_add_irqchip(struct device_node *np)
> +{
> + struct irq_domain *parent_domain, *domain;
> + int irq;
> +
> + parent_domain = irq_find_host(of_irq_find_parent(np));
> + if (!parent_domain) {
> + pr_err("%pOF: No interrupt-parent found\n", np);
> + return -EINVAL;
> + }
> +
> + domain = irq_domain_add_linear(np, 1, &ltq_ebu_irqdomain_ops, NULL);
> + if (!domain) {
> + pr_err("%pOF: Could not register EBU IRQ domain\n", np);
> + return -ENOMEM;
> + }
> +
> + irq = irq_of_parse_and_map(np, 0);
> + if (!irq) {
> + pr_err("%pOF: Failed to map interrupt\n", np);
> + irq_domain_remove(domain);
> + return -EINVAL;
> + }
> +
> + irq_create_mapping(domain, 0);
Why do you need to perform this eagerly? I'd expect this interrupt to
be mapped when it is actually claimed by a driver.

> +
> + irq_set_chained_handler_and_data(irq, ltq_ebu_irq_handler, domain);

And there is no HW initialisation whatsoever? I'd expect, at the very
least, the sole interrupt to be configured as disabled/masked.

> +
> + return 0;
> +}
> +
> static int __init ltq_ebu_setup(void)
> {
> const struct ltq_ebu_data *ebu_data;
> @@ -59,6 +201,7 @@ static int __init ltq_ebu_setup(void)
> struct resource res_ebu;
> struct device_node *np;
> u32 val;
> + int ret;
>
> np = of_find_matching_node_and_match(NULL, of_ebu_ids, &match);
> if (!np)
> @@ -83,6 +226,12 @@ static int __init ltq_ebu_setup(void)
> ltq_ebu_w32(val, LTQ_EBU_BUSCON0);
> }
>
> + if (of_property_read_bool(np, "interrupt-controller")) {
> + ret = ltq_ebu_add_irqchip(np);
> + if (ret)
> + return ret;
> + }
> +
> return 0;
> }
>
> diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c
> index 115b417dfb8e..cb9ab51fa748 100644
> --- a/arch/mips/lantiq/irq.c
> +++ b/arch/mips/lantiq/irq.c
> @@ -40,12 +40,6 @@
> /* the performance counter */
> #define LTQ_PERF_IRQ (INT_NUM_IM4_IRL0 + 31)
>
> -/*
> - * irqs generated by devices attached to the EBU need to be acked in
> - * a special manner
> - */
> -#define LTQ_ICU_EBU_IRQ 22
> -
> #define ltq_icu_w32(vpe, m, x, y) \
> ltq_w32((x), ltq_icu_membase[vpe] + m*LTQ_ICU_IM_SIZE + (y))
>
> @@ -300,11 +294,6 @@ static void ltq_hw_irq_handler(struct irq_desc *desc)
> irq = __fls(irq);
> hwirq = irq + MIPS_CPU_IRQ_CASCADE + (INT_NUM_IM_OFFSET * module);
> generic_handle_irq(irq_linear_revmap(ltq_domain, hwirq));
> -
> - /* if this is a EBU irq, we need to ack it or get a deadlock */
> - if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0) && LTQ_EBU_PCC_ISTAT)
> - ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_ISTAT) | 0x10,
> - LTQ_EBU_PCC_ISTAT);
> }
>
> static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
> diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c
> index 1ca42f482130..a3f6ab94ee2c 100644
> --- a/arch/mips/pci/pci-lantiq.c
> +++ b/arch/mips/pci/pci-lantiq.c
> @@ -190,10 +190,6 @@ static int ltq_pci_startup(struct platform_device *pdev)
> ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_MOD) | (1 << 24), PCI_CR_PCI_MOD);
> wmb();
>
> - /* setup irq line */
> - ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_CON) | 0xc, LTQ_EBU_PCC_CON);
> - ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) | 0x10, LTQ_EBU_PCC_IEN);
> -
> /* toggle reset pin */
> if (gpio_is_valid(reset_gpio)) {
> __gpio_set_value(reset_gpio, 0);
> --
> 2.22.0
>

Thanks,

M.

--
Jazz is not dead, it just smells funny.

\
 
 \ /
  Last update: 2019-07-28 12:02    [W:0.087 / U:9.956 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site