Skip to content

Commit

Permalink
Merge branch 'irq/ipi-as-irq', remote-tracking branches 'origin/irq/d…
Browse files Browse the repository at this point in the history
…w' and 'origin/irq/owl' into irq/irqchip-next

Signed-off-by: Marc Zyngier <[email protected]>
  • Loading branch information
Marc Zyngier committed Sep 28, 2020
4 parents eff65bd + 8156b80 + aa52429 + 2203870 commit 9b64efa
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/actions,owl-sirq.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Actions Semi Owl SoCs SIRQ interrupt controller

maintainers:
- Manivannan Sadhasivam <[email protected]>
- Cristian Ciocaltea <[email protected]>

description: |
This interrupt controller is found in the Actions Semi Owl SoCs (S500, S700
and S900) and provides support for handling up to 3 external interrupt lines.
properties:
compatible:
enum:
- actions,s500-sirq
- actions,s700-sirq
- actions,s900-sirq

reg:
maxItems: 1

interrupt-controller: true

'#interrupt-cells':
const: 2
description:
The first cell is the input IRQ number, between 0 and 2, while the second
cell is the trigger type as defined in interrupt.txt in this directory.

'interrupts':
description: |
Contains the GIC SPI IRQs mapped to the external interrupt lines.
They shall be specified sequentially from output 0 to 2.
minItems: 3
maxItems: 3

required:
- compatible
- reg
- interrupt-controller
- '#interrupt-cells'
- 'interrupts'

additionalProperties: false

examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
sirq: interrupt-controller@b01b0200 {
compatible = "actions,s500-sirq";
reg = <0xb01b0200 0x4>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>, /* SIRQ0 */
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>, /* SIRQ1 */
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>; /* SIRQ2 */
};
...
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ Synopsys DesignWare APB interrupt controller (dw_apb_ictl)

Synopsys DesignWare provides interrupt controller IP for APB known as
dw_apb_ictl. The IP is used as secondary interrupt controller in some SoCs with
APB bus, e.g. Marvell Armada 1500.
APB bus, e.g. Marvell Armada 1500. It can also be used as primary interrupt
controller in some SoCs, e.g. Hisilicon SD5203.

Required properties:
- compatible: shall be "snps,dw-apb-ictl"
- reg: physical base address of the controller and length of memory mapped
region starting with ENABLE_LOW register
- interrupt-controller: identifies the node as an interrupt controller
- #interrupt-cells: number of cells to encode an interrupt-specifier, shall be 1

Additional required property when it's used as secondary interrupt controller:
- interrupts: interrupt reference to primary interrupt controller

The interrupt sources map to the corresponding bits in the interrupt
Expand All @@ -21,6 +24,7 @@ registers, i.e.
- (optional) fast interrupts start at 64.

Example:
/* dw_apb_ictl is used as secondary interrupt controller */
aic: interrupt-controller@3000 {
compatible = "snps,dw-apb-ictl";
reg = <0x3000 0xc00>;
Expand All @@ -29,3 +33,11 @@ Example:
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
};

/* dw_apb_ictl is used as primary interrupt controller */
vic: interrupt-controller@10130000 {
compatible = "snps,dw-apb-ictl";
reg = <0x10130000 0x1000>;
interrupt-controller;
#interrupt-cells = <1>;
};
2 changes: 2 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,7 @@ F: Documentation/devicetree/bindings/arm/actions.yaml
F: Documentation/devicetree/bindings/clock/actions,owl-cmu.txt
F: Documentation/devicetree/bindings/dma/owl-dma.txt
F: Documentation/devicetree/bindings/i2c/i2c-owl.txt
F: Documentation/devicetree/bindings/interrupt-controller/actions,owl-sirq.yaml
F: Documentation/devicetree/bindings/mmc/owl-mmc.yaml
F: Documentation/devicetree/bindings/pinctrl/actions,s900-pinctrl.txt
F: Documentation/devicetree/bindings/power/actions,owl-sps.txt
Expand All @@ -1536,6 +1537,7 @@ F: drivers/clk/actions/
F: drivers/clocksource/timer-owl*
F: drivers/dma/owl-dma.c
F: drivers/i2c/busses/i2c-owl.c
F: drivers/irqchip/irq-owl-sirq.c
F: drivers/mmc/host/owl-mmc.c
F: drivers/pinctrl/actions/*
F: drivers/soc/actions/
Expand Down
30 changes: 17 additions & 13 deletions arch/arm/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ static int nr_ipi __read_mostly = NR_IPI;
static struct irq_desc *ipi_desc[MAX_IPI] __read_mostly;

static void ipi_setup(int cpu);
static void ipi_teardown(int cpu);

static DECLARE_COMPLETION(cpu_running);

Expand Down Expand Up @@ -236,6 +235,17 @@ int platform_can_hotplug_cpu(unsigned int cpu)
return cpu != 0;
}

static void ipi_teardown(int cpu)
{
int i;

if (WARN_ON_ONCE(!ipi_irq_base))
return;

for (i = 0; i < nr_ipi; i++)
disable_percpu_irq(ipi_irq_base + i);
}

/*
* __cpu_disable runs on the processor to be shutdown.
*/
Expand Down Expand Up @@ -531,7 +541,12 @@ void show_ipi_list(struct seq_file *p, int prec)
unsigned int cpu, i;

for (i = 0; i < NR_IPI; i++) {
unsigned int irq = irq_desc_get_irq(ipi_desc[i]);
unsigned int irq;

if (!ipi_desc[i])
continue;

irq = irq_desc_get_irq(ipi_desc[i]);
seq_printf(p, "%*s%u: ", prec - 1, "IPI", i);

for_each_online_cpu(cpu)
Expand Down Expand Up @@ -707,17 +722,6 @@ static void ipi_setup(int cpu)
enable_percpu_irq(ipi_irq_base + i, 0);
}

static void ipi_teardown(int cpu)
{
int i;

if (WARN_ON_ONCE(!ipi_irq_base))
return;

for (i = 0; i < nr_ipi; i++)
disable_percpu_irq(ipi_irq_base + i);
}

void __init set_smp_ipi_range(int ipi_base, int n)
{
int i;
Expand Down
4 changes: 3 additions & 1 deletion arch/arm64/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ static int nr_ipi __read_mostly = NR_IPI;
static struct irq_desc *ipi_desc[NR_IPI] __read_mostly;

static void ipi_setup(int cpu);
static void ipi_teardown(int cpu);

#ifdef CONFIG_HOTPLUG_CPU
static void ipi_teardown(int cpu);
static int op_cpu_kill(unsigned int cpu);
#else
static inline int op_cpu_kill(unsigned int cpu)
Expand Down Expand Up @@ -964,6 +964,7 @@ static void ipi_setup(int cpu)
enable_percpu_irq(ipi_irq_base + i, 0);
}

#ifdef CONFIG_HOTPLUG_CPU
static void ipi_teardown(int cpu)
{
int i;
Expand All @@ -974,6 +975,7 @@ static void ipi_teardown(int cpu)
for (i = 0; i < nr_ipi; i++)
disable_percpu_irq(ipi_irq_base + i);
}
#endif

void __init set_smp_ipi_range(int ipi_base, int n)
{
Expand Down
2 changes: 1 addition & 1 deletion drivers/irqchip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ config DAVINCI_CP_INTC
config DW_APB_ICTL
bool
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY

config FARADAY_FTINTC010
bool
Expand Down
1 change: 1 addition & 0 deletions drivers/irqchip/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ obj-$(CONFIG_ATH79) += irq-ath79-cpu.o
obj-$(CONFIG_ATH79) += irq-ath79-misc.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o
obj-$(CONFIG_DAVINCI_AINTC) += irq-davinci-aintc.o
obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o
obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o
Expand Down
81 changes: 70 additions & 11 deletions drivers/irqchip/irq-dw-apb-ictl.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>

#define APB_INT_ENABLE_L 0x00
#define APB_INT_ENABLE_H 0x04
Expand All @@ -26,7 +27,28 @@
#define APB_INT_FINALSTATUS_H 0x34
#define APB_INT_BASE_OFFSET 0x04

static void dw_apb_ictl_handler(struct irq_desc *desc)
/* irq domain of the primary interrupt controller. */
static struct irq_domain *dw_apb_ictl_irq_domain;

static void __irq_entry dw_apb_ictl_handle_irq(struct pt_regs *regs)
{
struct irq_domain *d = dw_apb_ictl_irq_domain;
int n;

for (n = 0; n < d->revmap_size; n += 32) {
struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, n);
u32 stat = readl_relaxed(gc->reg_base + APB_INT_FINALSTATUS_L);

while (stat) {
u32 hwirq = ffs(stat) - 1;

handle_domain_irq(d, hwirq, regs);
stat &= ~BIT(hwirq);
}
}
}

static void dw_apb_ictl_handle_irq_cascaded(struct irq_desc *desc)
{
struct irq_domain *d = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
Expand All @@ -43,13 +65,37 @@ static void dw_apb_ictl_handler(struct irq_desc *desc)
u32 virq = irq_find_mapping(d, gc->irq_base + hwirq);

generic_handle_irq(virq);
stat &= ~(1 << hwirq);
stat &= ~BIT(hwirq);
}
}

chained_irq_exit(chip, desc);
}

static int dw_apb_ictl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
int i, ret;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
struct irq_fwspec *fwspec = arg;

ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
if (ret)
return ret;

for (i = 0; i < nr_irqs; i++)
irq_map_generic_chip(domain, virq + i, hwirq + i);

return 0;
}

static const struct irq_domain_ops dw_apb_ictl_irq_domain_ops = {
.translate = irq_domain_translate_onecell,
.alloc = dw_apb_ictl_irq_domain_alloc,
.free = irq_domain_free_irqs_top,
};

#ifdef CONFIG_PM
static void dw_apb_ictl_resume(struct irq_data *d)
{
Expand All @@ -68,19 +114,27 @@ static void dw_apb_ictl_resume(struct irq_data *d)
static int __init dw_apb_ictl_init(struct device_node *np,
struct device_node *parent)
{
const struct irq_domain_ops *domain_ops;
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
struct resource r;
struct irq_domain *domain;
struct irq_chip_generic *gc;
void __iomem *iobase;
int ret, nrirqs, irq, i;
int ret, nrirqs, parent_irq, i;
u32 reg;

/* Map the parent interrupt for the chained handler */
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
pr_err("%pOF: unable to parse irq\n", np);
return -EINVAL;
if (!parent) {
/* Used as the primary interrupt controller */
parent_irq = 0;
domain_ops = &dw_apb_ictl_irq_domain_ops;
} else {
/* Map the parent interrupt for the chained handler */
parent_irq = irq_of_parse_and_map(np, 0);
if (parent_irq <= 0) {
pr_err("%pOF: unable to parse irq\n", np);
return -EINVAL;
}
domain_ops = &irq_generic_chip_ops;
}

ret = of_address_to_resource(np, 0, &r);
Expand Down Expand Up @@ -120,8 +174,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
else
nrirqs = fls(readl_relaxed(iobase + APB_INT_ENABLE_L));

domain = irq_domain_add_linear(np, nrirqs,
&irq_generic_chip_ops, NULL);
domain = irq_domain_add_linear(np, nrirqs, domain_ops, NULL);
if (!domain) {
pr_err("%pOF: unable to add irq domain\n", np);
ret = -ENOMEM;
Expand All @@ -146,7 +199,13 @@ static int __init dw_apb_ictl_init(struct device_node *np,
gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume;
}

irq_set_chained_handler_and_data(irq, dw_apb_ictl_handler, domain);
if (parent_irq) {
irq_set_chained_handler_and_data(parent_irq,
dw_apb_ictl_handle_irq_cascaded, domain);
} else {
dw_apb_ictl_irq_domain = domain;
set_handle_irq(dw_apb_ictl_handle_irq);
}

return 0;

Expand Down
Loading

0 comments on commit 9b64efa

Please sign in to comment.