Merge tag 'irqchip-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull irqchip updates for 4.18 from Marc Zyngier:

 - Support for Meson-AXG GPIO irqchip

 - Large stm32 irqchip rework (suspend/resume, hierarchical domains)
This commit is contained in:
Thomas Gleixner
2018-06-01 22:19:50 +02:00
7 changed files with 630 additions and 110 deletions
@@ -9,11 +9,12 @@ number of interrupt exposed depends on the SoC.
Required properties:
- compatible : must have "amlogic,meson8-gpio-intc and either
amlogic,meson8-gpio-intc for meson8 SoCs (S802) or
amlogic,meson8b-gpio-intc for meson8b SoCs (S805) or
amlogic,meson-gxbb-gpio-intc for GXBB SoCs (S905) or
amlogic,meson-gxl-gpio-intc for GXL SoCs (S905X, S912)
- compatible : must have "amlogic,meson8-gpio-intc" and either
"amlogic,meson8-gpio-intc" for meson8 SoCs (S802) or
"amlogic,meson8b-gpio-intc" for meson8b SoCs (S805) or
"amlogic,meson-gxbb-gpio-intc" for GXBB SoCs (S905) or
"amlogic,meson-gxl-gpio-intc" for GXL SoCs (S905X, S912)
"amlogic,meson-axg-gpio-intc" for AXG SoCs (A113D, A113X)
- interrupt-parent : a phandle to the GIC the interrupts are routed to.
Usually this is provided at the root level of the device tree as it is
common to most of the SoC.
@@ -5,11 +5,14 @@ Required properties:
- compatible: Should be:
"st,stm32-exti"
"st,stm32h7-exti"
"st,stm32mp1-exti"
- reg: Specifies base physical address and size of the registers
- interrupt-controller: Indentifies the node as an interrupt controller
- #interrupt-cells: Specifies the number of cells to encode an interrupt
specifier, shall be 2
- interrupts: interrupts references to primary interrupt controller
(only needed for exti controller with multiple exti under
same parent interrupt: st,stm32-exti and st,stm32h7-exti")
Example:
@@ -12,6 +12,8 @@
#size-cells = <1>;
compatible = "st,stm32mp157-pinctrl";
ranges = <0 0x50002000 0xa400>;
interrupt-parent = <&exti>;
st,syscfg = <&exti 0x60 0xff>;
pins-are-numbered;
gpioa: gpio@50002000 {
@@ -166,6 +168,8 @@
compatible = "st,stm32mp157-z-pinctrl";
ranges = <0 0x54004000 0x400>;
pins-are-numbered;
interrupt-parent = <&exti>;
st,syscfg = <&exti 0x60 0xff>;
status = "disabled";
gpioz: gpio@54004000 {
+7
View File
@@ -183,6 +183,13 @@
status = "disabled";
};
exti: interrupt-controller@5000d000 {
compatible = "st,stm32mp1-exti", "syscon";
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x5000d000 0x400>;
};
usart1: serial@5c000000 {
compatible = "st,stm32h7-uart";
reg = <0x5c000000 0x400>;
+5
View File
@@ -63,11 +63,16 @@ static const struct meson_gpio_irq_params gxl_params = {
.nr_hwirq = 110,
};
static const struct meson_gpio_irq_params axg_params = {
.nr_hwirq = 100,
};
static const struct of_device_id meson_irq_gpio_matches[] = {
{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
{ .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params },
{ .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params },
{ .compatible = "amlogic,meson-axg-gpio-intc", .data = &axg_params },
{ }
};
+598 -99
View File
@@ -14,6 +14,9 @@
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/syscore_ops.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#define IRQS_PER_BANK 32
@@ -23,29 +26,69 @@ struct stm32_exti_bank {
u32 rtsr_ofst;
u32 ftsr_ofst;
u32 swier_ofst;
u32 pr_ofst;
u32 rpr_ofst;
u32 fpr_ofst;
};
#define UNDEF_REG ~0
struct stm32_desc_irq {
u32 exti;
u32 irq_parent;
};
struct stm32_exti_drv_data {
const struct stm32_exti_bank **exti_banks;
const struct stm32_desc_irq *desc_irqs;
u32 bank_nr;
u32 irq_nr;
};
struct stm32_exti_chip_data {
struct stm32_exti_host_data *host_data;
const struct stm32_exti_bank *reg_bank;
struct raw_spinlock rlock;
u32 wake_active;
u32 mask_cache;
u32 rtsr_cache;
u32 ftsr_cache;
};
struct stm32_exti_host_data {
void __iomem *base;
struct stm32_exti_chip_data *chips_data;
const struct stm32_exti_drv_data *drv_data;
};
static struct stm32_exti_host_data *stm32_host_data;
static const struct stm32_exti_bank stm32f4xx_exti_b1 = {
.imr_ofst = 0x00,
.emr_ofst = 0x04,
.rtsr_ofst = 0x08,
.ftsr_ofst = 0x0C,
.swier_ofst = 0x10,
.pr_ofst = 0x14,
.rpr_ofst = 0x14,
.fpr_ofst = UNDEF_REG,
};
static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = {
&stm32f4xx_exti_b1,
};
static const struct stm32_exti_drv_data stm32f4xx_drv_data = {
.exti_banks = stm32f4xx_exti_banks,
.bank_nr = ARRAY_SIZE(stm32f4xx_exti_banks),
};
static const struct stm32_exti_bank stm32h7xx_exti_b1 = {
.imr_ofst = 0x80,
.emr_ofst = 0x84,
.rtsr_ofst = 0x00,
.ftsr_ofst = 0x04,
.swier_ofst = 0x08,
.pr_ofst = 0x88,
.rpr_ofst = 0x88,
.fpr_ofst = UNDEF_REG,
};
static const struct stm32_exti_bank stm32h7xx_exti_b2 = {
@@ -54,7 +97,8 @@ static const struct stm32_exti_bank stm32h7xx_exti_b2 = {
.rtsr_ofst = 0x20,
.ftsr_ofst = 0x24,
.swier_ofst = 0x28,
.pr_ofst = 0x98,
.rpr_ofst = 0x98,
.fpr_ofst = UNDEF_REG,
};
static const struct stm32_exti_bank stm32h7xx_exti_b3 = {
@@ -63,7 +107,8 @@ static const struct stm32_exti_bank stm32h7xx_exti_b3 = {
.rtsr_ofst = 0x40,
.ftsr_ofst = 0x44,
.swier_ofst = 0x48,
.pr_ofst = 0xA8,
.rpr_ofst = 0xA8,
.fpr_ofst = UNDEF_REG,
};
static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = {
@@ -72,18 +117,105 @@ static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = {
&stm32h7xx_exti_b3,
};
static unsigned long stm32_exti_pending(struct irq_chip_generic *gc)
{
const struct stm32_exti_bank *stm32_bank = gc->private;
static const struct stm32_exti_drv_data stm32h7xx_drv_data = {
.exti_banks = stm32h7xx_exti_banks,
.bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks),
};
return irq_reg_readl(gc, stm32_bank->pr_ofst);
static const struct stm32_exti_bank stm32mp1_exti_b1 = {
.imr_ofst = 0x80,
.emr_ofst = 0x84,
.rtsr_ofst = 0x00,
.ftsr_ofst = 0x04,
.swier_ofst = 0x08,
.rpr_ofst = 0x0C,
.fpr_ofst = 0x10,
};
static const struct stm32_exti_bank stm32mp1_exti_b2 = {
.imr_ofst = 0x90,
.emr_ofst = 0x94,
.rtsr_ofst = 0x20,
.ftsr_ofst = 0x24,
.swier_ofst = 0x28,
.rpr_ofst = 0x2C,
.fpr_ofst = 0x30,
};
static const struct stm32_exti_bank stm32mp1_exti_b3 = {
.imr_ofst = 0xA0,
.emr_ofst = 0xA4,
.rtsr_ofst = 0x40,
.ftsr_ofst = 0x44,
.swier_ofst = 0x48,
.rpr_ofst = 0x4C,
.fpr_ofst = 0x50,
};
static const struct stm32_exti_bank *stm32mp1_exti_banks[] = {
&stm32mp1_exti_b1,
&stm32mp1_exti_b2,
&stm32mp1_exti_b3,
};
static const struct stm32_desc_irq stm32mp1_desc_irq[] = {
{ .exti = 1, .irq_parent = 7 },
{ .exti = 2, .irq_parent = 8 },
{ .exti = 3, .irq_parent = 9 },
{ .exti = 4, .irq_parent = 10 },
{ .exti = 5, .irq_parent = 23 },
{ .exti = 6, .irq_parent = 64 },
{ .exti = 7, .irq_parent = 65 },
{ .exti = 8, .irq_parent = 66 },
{ .exti = 9, .irq_parent = 67 },
{ .exti = 10, .irq_parent = 40 },
{ .exti = 11, .irq_parent = 42 },
{ .exti = 12, .irq_parent = 76 },
{ .exti = 13, .irq_parent = 77 },
{ .exti = 14, .irq_parent = 121 },
{ .exti = 15, .irq_parent = 127 },
{ .exti = 16, .irq_parent = 1 },
{ .exti = 65, .irq_parent = 144 },
{ .exti = 68, .irq_parent = 143 },
{ .exti = 73, .irq_parent = 129 },
};
static const struct stm32_exti_drv_data stm32mp1_drv_data = {
.exti_banks = stm32mp1_exti_banks,
.bank_nr = ARRAY_SIZE(stm32mp1_exti_banks),
.desc_irqs = stm32mp1_desc_irq,
.irq_nr = ARRAY_SIZE(stm32mp1_desc_irq),
};
static int stm32_exti_to_irq(const struct stm32_exti_drv_data *drv_data,
irq_hw_number_t hwirq)
{
const struct stm32_desc_irq *desc_irq;
int i;
if (!drv_data->desc_irqs)
return -EINVAL;
for (i = 0; i < drv_data->irq_nr; i++) {
desc_irq = &drv_data->desc_irqs[i];
if (desc_irq->exti == hwirq)
return desc_irq->irq_parent;
}
return -EINVAL;
}
static void stm32_exti_irq_ack(struct irq_chip_generic *gc, u32 mask)
static unsigned long stm32_exti_pending(struct irq_chip_generic *gc)
{
const struct stm32_exti_bank *stm32_bank = gc->private;
struct stm32_exti_chip_data *chip_data = gc->private;
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
unsigned long pending;
irq_reg_writel(gc, mask, stm32_bank->pr_ofst);
pending = irq_reg_readl(gc, stm32_bank->rpr_ofst);
if (stm32_bank->fpr_ofst != UNDEF_REG)
pending |= irq_reg_readl(gc, stm32_bank->fpr_ofst);
return pending;
}
static void stm32_irq_handler(struct irq_desc *desc)
@@ -92,7 +224,6 @@ static void stm32_irq_handler(struct irq_desc *desc)
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int virq, nbanks = domain->gc->num_chips;
struct irq_chip_generic *gc;
const struct stm32_exti_bank *stm32_bank;
unsigned long pending;
int n, i, irq_base = 0;
@@ -100,13 +231,11 @@ static void stm32_irq_handler(struct irq_desc *desc)
for (i = 0; i < nbanks; i++, irq_base += IRQS_PER_BANK) {
gc = irq_get_domain_generic_chip(domain, irq_base);
stm32_bank = gc->private;
while ((pending = stm32_exti_pending(gc))) {
for_each_set_bit(n, &pending, IRQS_PER_BANK) {
virq = irq_find_mapping(domain, irq_base + n);
generic_handle_irq(virq);
stm32_exti_irq_ack(gc, BIT(n));
}
}
}
@@ -114,34 +243,48 @@ static void stm32_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
static int stm32_irq_set_type(struct irq_data *data, unsigned int type)
static int stm32_exti_set_type(struct irq_data *d,
unsigned int type, u32 *rtsr, u32 *ftsr)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
const struct stm32_exti_bank *stm32_bank = gc->private;
int pin = data->hwirq % IRQS_PER_BANK;
u32 mask = BIT(d->hwirq % IRQS_PER_BANK);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
*rtsr |= mask;
*ftsr &= ~mask;
break;
case IRQ_TYPE_EDGE_FALLING:
*rtsr &= ~mask;
*ftsr |= mask;
break;
case IRQ_TYPE_EDGE_BOTH:
*rtsr |= mask;
*ftsr |= mask;
break;
default:
return -EINVAL;
}
return 0;
}
static int stm32_irq_set_type(struct irq_data *d, unsigned int type)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct stm32_exti_chip_data *chip_data = gc->private;
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
u32 rtsr, ftsr;
int err;
irq_gc_lock(gc);
rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst);
ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
rtsr |= BIT(pin);
ftsr &= ~BIT(pin);
break;
case IRQ_TYPE_EDGE_FALLING:
rtsr &= ~BIT(pin);
ftsr |= BIT(pin);
break;
case IRQ_TYPE_EDGE_BOTH:
rtsr |= BIT(pin);
ftsr |= BIT(pin);
break;
default:
err = stm32_exti_set_type(d, type, &rtsr, &ftsr);
if (err) {
irq_gc_unlock(gc);
return -EINVAL;
return err;
}
irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst);
@@ -152,40 +295,59 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type)
return 0;
}
static int stm32_irq_set_wake(struct irq_data *data, unsigned int on)
static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data,
u32 wake_active)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
const struct stm32_exti_bank *stm32_bank = gc->private;
int pin = data->hwirq % IRQS_PER_BANK;
u32 imr;
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
void __iomem *base = chip_data->host_data->base;
/* save rtsr, ftsr registers */
chip_data->rtsr_cache = readl_relaxed(base + stm32_bank->rtsr_ofst);
chip_data->ftsr_cache = readl_relaxed(base + stm32_bank->ftsr_ofst);
writel_relaxed(wake_active, base + stm32_bank->imr_ofst);
}
static void stm32_chip_resume(struct stm32_exti_chip_data *chip_data,
u32 mask_cache)
{
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
void __iomem *base = chip_data->host_data->base;
/* restore rtsr, ftsr, registers */
writel_relaxed(chip_data->rtsr_cache, base + stm32_bank->rtsr_ofst);
writel_relaxed(chip_data->ftsr_cache, base + stm32_bank->ftsr_ofst);
writel_relaxed(mask_cache, base + stm32_bank->imr_ofst);
}
static void stm32_irq_suspend(struct irq_chip_generic *gc)
{
struct stm32_exti_chip_data *chip_data = gc->private;
irq_gc_lock(gc);
imr = irq_reg_readl(gc, stm32_bank->imr_ofst);
if (on)
imr |= BIT(pin);
else
imr &= ~BIT(pin);
irq_reg_writel(gc, imr, stm32_bank->imr_ofst);
stm32_chip_suspend(chip_data, gc->wake_active);
irq_gc_unlock(gc);
}
return 0;
static void stm32_irq_resume(struct irq_chip_generic *gc)
{
struct stm32_exti_chip_data *chip_data = gc->private;
irq_gc_lock(gc);
stm32_chip_resume(chip_data, gc->mask_cache);
irq_gc_unlock(gc);
}
static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *data)
{
struct irq_chip_generic *gc;
struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq;
hwirq = fwspec->param[0];
gc = irq_get_domain_generic_chip(d, hwirq);
irq_map_generic_chip(d, virq, hwirq);
irq_domain_set_info(d, virq, hwirq, &gc->chip_types->chip, gc,
handle_simple_irq, NULL, NULL);
return 0;
}
@@ -198,30 +360,318 @@ static void stm32_exti_free(struct irq_domain *d, unsigned int virq,
irq_domain_reset_irq_data(data);
}
struct irq_domain_ops irq_exti_domain_ops = {
static const struct irq_domain_ops irq_exti_domain_ops = {
.map = irq_map_generic_chip,
.xlate = irq_domain_xlate_onetwocell,
.alloc = stm32_exti_alloc,
.free = stm32_exti_free,
};
static int
__init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks,
int bank_nr, struct device_node *node)
static void stm32_irq_ack(struct irq_data *d)
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
int nr_irqs, nr_exti, ret, i;
struct irq_chip_generic *gc;
struct irq_domain *domain;
void *base;
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct stm32_exti_chip_data *chip_data = gc->private;
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
base = of_iomap(node, 0);
if (!base) {
pr_err("%pOF: Unable to map registers\n", node);
return -ENOMEM;
irq_gc_lock(gc);
irq_reg_writel(gc, d->mask, stm32_bank->rpr_ofst);
if (stm32_bank->fpr_ofst != UNDEF_REG)
irq_reg_writel(gc, d->mask, stm32_bank->fpr_ofst);
irq_gc_unlock(gc);
}
static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
void __iomem *base = chip_data->host_data->base;
u32 val;
val = readl_relaxed(base + reg);
val |= BIT(d->hwirq % IRQS_PER_BANK);
writel_relaxed(val, base + reg);
return val;
}
static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
void __iomem *base = chip_data->host_data->base;
u32 val;
val = readl_relaxed(base + reg);
val &= ~BIT(d->hwirq % IRQS_PER_BANK);
writel_relaxed(val, base + reg);
return val;
}
static void stm32_exti_h_eoi(struct irq_data *d)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
raw_spin_lock(&chip_data->rlock);
stm32_exti_set_bit(d, stm32_bank->rpr_ofst);
if (stm32_bank->fpr_ofst != UNDEF_REG)
stm32_exti_set_bit(d, stm32_bank->fpr_ofst);
raw_spin_unlock(&chip_data->rlock);
if (d->parent_data->chip)
irq_chip_eoi_parent(d);
}
static void stm32_exti_h_mask(struct irq_data *d)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
raw_spin_lock(&chip_data->rlock);
chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst);
raw_spin_unlock(&chip_data->rlock);
if (d->parent_data->chip)
irq_chip_mask_parent(d);
}
static void stm32_exti_h_unmask(struct irq_data *d)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
raw_spin_lock(&chip_data->rlock);
chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst);
raw_spin_unlock(&chip_data->rlock);
if (d->parent_data->chip)
irq_chip_unmask_parent(d);
}
static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
void __iomem *base = chip_data->host_data->base;
u32 rtsr, ftsr;
int err;
raw_spin_lock(&chip_data->rlock);
rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst);
ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst);
err = stm32_exti_set_type(d, type, &rtsr, &ftsr);
if (err) {
raw_spin_unlock(&chip_data->rlock);
return err;
}
domain = irq_domain_add_linear(node, bank_nr * IRQS_PER_BANK,
writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst);
writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst);
raw_spin_unlock(&chip_data->rlock);
return 0;
}
static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on)
{
struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d);
u32 mask = BIT(d->hwirq % IRQS_PER_BANK);
raw_spin_lock(&chip_data->rlock);
if (on)
chip_data->wake_active |= mask;
else
chip_data->wake_active &= ~mask;
raw_spin_unlock(&chip_data->rlock);
return 0;
}
static int stm32_exti_h_set_affinity(struct irq_data *d,
const struct cpumask *dest, bool force)
{
if (d->parent_data->chip)
return irq_chip_set_affinity_parent(d, dest, force);
return -EINVAL;
}
#ifdef CONFIG_PM
static int stm32_exti_h_suspend(void)
{
struct stm32_exti_chip_data *chip_data;
int i;
for (i = 0; i < stm32_host_data->drv_data->bank_nr; i++) {
chip_data = &stm32_host_data->chips_data[i];
raw_spin_lock(&chip_data->rlock);
stm32_chip_suspend(chip_data, chip_data->wake_active);
raw_spin_unlock(&chip_data->rlock);
}
return 0;
}
static void stm32_exti_h_resume(void)
{
struct stm32_exti_chip_data *chip_data;
int i;
for (i = 0; i < stm32_host_data->drv_data->bank_nr; i++) {
chip_data = &stm32_host_data->chips_data[i];
raw_spin_lock(&chip_data->rlock);
stm32_chip_resume(chip_data, chip_data->mask_cache);
raw_spin_unlock(&chip_data->rlock);
}
}
static struct syscore_ops stm32_exti_h_syscore_ops = {
.suspend = stm32_exti_h_suspend,
.resume = stm32_exti_h_resume,
};
static void stm32_exti_h_syscore_init(void)
{
register_syscore_ops(&stm32_exti_h_syscore_ops);
}
#else
static inline void stm32_exti_h_syscore_init(void) {}
#endif
static struct irq_chip stm32_exti_h_chip = {
.name = "stm32-exti-h",
.irq_eoi = stm32_exti_h_eoi,
.irq_mask = stm32_exti_h_mask,
.irq_unmask = stm32_exti_h_unmask,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_type = stm32_exti_h_set_type,
.irq_set_wake = stm32_exti_h_set_wake,
.flags = IRQCHIP_MASK_ON_SUSPEND,
#ifdef CONFIG_SMP
.irq_set_affinity = stm32_exti_h_set_affinity,
#endif
};
static int stm32_exti_h_domain_alloc(struct irq_domain *dm,
unsigned int virq,
unsigned int nr_irqs, void *data)
{
struct stm32_exti_host_data *host_data = dm->host_data;
struct stm32_exti_chip_data *chip_data;
struct irq_fwspec *fwspec = data;
struct irq_fwspec p_fwspec;
irq_hw_number_t hwirq;
int p_irq, bank;
hwirq = fwspec->param[0];
bank = hwirq / IRQS_PER_BANK;
chip_data = &host_data->chips_data[bank];
irq_domain_set_hwirq_and_chip(dm, virq, hwirq,
&stm32_exti_h_chip, chip_data);
p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq);
if (p_irq >= 0) {
p_fwspec.fwnode = dm->parent->fwnode;
p_fwspec.param_count = 3;
p_fwspec.param[0] = GIC_SPI;
p_fwspec.param[1] = p_irq;
p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec);
}
return 0;
}
static struct
stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
struct device_node *node)
{
struct stm32_exti_host_data *host_data;
host_data = kzalloc(sizeof(*host_data), GFP_KERNEL);
if (!host_data)
return NULL;
host_data->drv_data = dd;
host_data->chips_data = kcalloc(dd->bank_nr,
sizeof(struct stm32_exti_chip_data),
GFP_KERNEL);
if (!host_data->chips_data)
return NULL;
host_data->base = of_iomap(node, 0);
if (!host_data->base) {
pr_err("%pOF: Unable to map registers\n", node);
return NULL;
}
stm32_host_data = host_data;
return host_data;
}
static struct
stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
u32 bank_idx,
struct device_node *node)
{
const struct stm32_exti_bank *stm32_bank;
struct stm32_exti_chip_data *chip_data;
void __iomem *base = h_data->base;
u32 irqs_mask;
stm32_bank = h_data->drv_data->exti_banks[bank_idx];
chip_data = &h_data->chips_data[bank_idx];
chip_data->host_data = h_data;
chip_data->reg_bank = stm32_bank;
raw_spin_lock_init(&chip_data->rlock);
/* Determine number of irqs supported */
writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst);
irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst);
/*
* This IP has no reset, so after hot reboot we should
* clear registers to avoid residue
*/
writel_relaxed(0, base + stm32_bank->imr_ofst);
writel_relaxed(0, base + stm32_bank->emr_ofst);
writel_relaxed(0, base + stm32_bank->rtsr_ofst);
writel_relaxed(0, base + stm32_bank->ftsr_ofst);
writel_relaxed(~0UL, base + stm32_bank->rpr_ofst);
if (stm32_bank->fpr_ofst != UNDEF_REG)
writel_relaxed(~0UL, base + stm32_bank->fpr_ofst);
pr_info("%s: bank%d, External IRQs available:%#x\n",
node->full_name, bank_idx, irqs_mask);
return chip_data;
}
static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data,
struct device_node *node)
{
struct stm32_exti_host_data *host_data;
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
int nr_irqs, ret, i;
struct irq_chip_generic *gc;
struct irq_domain *domain;
host_data = stm32_exti_host_init(drv_data, node);
if (!host_data) {
ret = -ENOMEM;
goto out_free_mem;
}
domain = irq_domain_add_linear(node, drv_data->bank_nr * IRQS_PER_BANK,
&irq_exti_domain_ops, NULL);
if (!domain) {
pr_err("%s: Could not register interrupt domain.\n",
@@ -234,44 +684,32 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks,
handle_edge_irq, clr, 0, 0);
if (ret) {
pr_err("%pOF: Could not allocate generic interrupt chip.\n",
node);
node);
goto out_free_domain;
}
for (i = 0; i < bank_nr; i++) {
const struct stm32_exti_bank *stm32_bank = stm32_exti_banks[i];
u32 irqs_mask;
for (i = 0; i < drv_data->bank_nr; i++) {
const struct stm32_exti_bank *stm32_bank;
struct stm32_exti_chip_data *chip_data;
stm32_bank = drv_data->exti_banks[i];
chip_data = stm32_exti_chip_init(host_data, i, node);
gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK);
gc->reg_base = base;
gc->reg_base = host_data->base;
gc->chip_types->type = IRQ_TYPE_EDGE_BOTH;
gc->chip_types->chip.irq_ack = irq_gc_ack_set_bit;
gc->chip_types->chip.irq_ack = stm32_irq_ack;
gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit;
gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit;
gc->chip_types->chip.irq_set_type = stm32_irq_set_type;
gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake;
gc->chip_types->regs.ack = stm32_bank->pr_ofst;
gc->chip_types->chip.irq_set_wake = irq_gc_set_wake;
gc->suspend = stm32_irq_suspend;
gc->resume = stm32_irq_resume;
gc->wake_enabled = IRQ_MSK(IRQS_PER_BANK);
gc->chip_types->regs.mask = stm32_bank->imr_ofst;
gc->private = (void *)stm32_bank;
/* Determine number of irqs supported */
writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst);
irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst);
nr_exti = fls(readl_relaxed(base + stm32_bank->rtsr_ofst));
/*
* This IP has no reset, so after hot reboot we should
* clear registers to avoid residue
*/
writel_relaxed(0, base + stm32_bank->imr_ofst);
writel_relaxed(0, base + stm32_bank->emr_ofst);
writel_relaxed(0, base + stm32_bank->rtsr_ofst);
writel_relaxed(0, base + stm32_bank->ftsr_ofst);
writel_relaxed(~0UL, base + stm32_bank->pr_ofst);
pr_info("%s: bank%d, External IRQs available:%#x\n",
node->full_name, i, irqs_mask);
gc->private = (void *)chip_data;
}
nr_irqs = of_irq_count(node);
@@ -287,15 +725,69 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks,
out_free_domain:
irq_domain_remove(domain);
out_unmap:
iounmap(base);
iounmap(host_data->base);
out_free_mem:
kfree(host_data->chips_data);
kfree(host_data);
return ret;
}
static const struct irq_domain_ops stm32_exti_h_domain_ops = {
.alloc = stm32_exti_h_domain_alloc,
.free = irq_domain_free_irqs_common,
};
static int
__init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data,
struct device_node *node,
struct device_node *parent)
{
struct irq_domain *parent_domain, *domain;
struct stm32_exti_host_data *host_data;
int ret, i;
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("interrupt-parent not found\n");
return -EINVAL;
}
host_data = stm32_exti_host_init(drv_data, node);
if (!host_data) {
ret = -ENOMEM;
goto out_free_mem;
}
for (i = 0; i < drv_data->bank_nr; i++)
stm32_exti_chip_init(host_data, i, node);
domain = irq_domain_add_hierarchy(parent_domain, 0,
drv_data->bank_nr * IRQS_PER_BANK,
node, &stm32_exti_h_domain_ops,
host_data);
if (!domain) {
pr_err("%s: Could not register exti domain.\n", node->name);
ret = -ENOMEM;
goto out_unmap;
}
stm32_exti_h_syscore_init();
return 0;
out_unmap:
iounmap(host_data->base);
out_free_mem:
kfree(host_data->chips_data);
kfree(host_data);
return ret;
}
static int __init stm32f4_exti_of_init(struct device_node *np,
struct device_node *parent)
{
return stm32_exti_init(stm32f4xx_exti_banks,
ARRAY_SIZE(stm32f4xx_exti_banks), np);
return stm32_exti_init(&stm32f4xx_drv_data, np);
}
IRQCHIP_DECLARE(stm32f4_exti, "st,stm32-exti", stm32f4_exti_of_init);
@@ -303,8 +795,15 @@ IRQCHIP_DECLARE(stm32f4_exti, "st,stm32-exti", stm32f4_exti_of_init);
static int __init stm32h7_exti_of_init(struct device_node *np,
struct device_node *parent)
{
return stm32_exti_init(stm32h7xx_exti_banks,
ARRAY_SIZE(stm32h7xx_exti_banks), np);
return stm32_exti_init(&stm32h7xx_drv_data, np);
}
IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init);
static int __init stm32mp1_exti_of_init(struct device_node *np,
struct device_node *parent)
{
return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent);
}
IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init);
+7 -6
View File
@@ -267,12 +267,13 @@ static void stm32_gpio_irq_release_resources(struct irq_data *irq_data)
}
static struct irq_chip stm32_gpio_irq_chip = {
.name = "stm32gpio",
.irq_ack = irq_chip_ack_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_set_wake = irq_chip_set_wake_parent,
.name = "stm32gpio",
.irq_eoi = irq_chip_eoi_parent,
.irq_ack = irq_chip_ack_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_set_type = irq_chip_set_type_parent,
.irq_set_wake = irq_chip_set_wake_parent,
.irq_request_resources = stm32_gpio_irq_request_resources,
.irq_release_resources = stm32_gpio_irq_release_resources,
};