irqchip updates for 4.21

- A bunch of new irqchip drivers (RDA8810PL, Madera, imx-irqsteer)
 - Updates for new (and old) platforms (i.MX8MQ, F1C100s)
 - A number of SPDX cleanups
 - A workaround for a very broken GICv3 implementation
 - A platform-msi fix
 - Various cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQJJBAABCgAzFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAlwZI8cVHG1hcmMuenlu
 Z2llckBhcm0uY29tAAoJECPQ0LrRPXpDyokP+gKoKbZMc1E7dX6WxUrKh2N+fMJF
 uVbuGF2s57CLG955YNuyo8BK4meWJIHGO3JahwE8I/9eu0G7PaudYvpZgP7s/sxD
 XHLWFVHB1mq4lExMcluT0jG4ZpX7EKvYB1KGqgYM1ScOS9Uubb4ZG9T5GPhUT/YM
 w1BAtHaZmCAg8d0wNPUMaAFc9Bd2B9Z1C8nwS+wpdJRxYxE9x8BES42r95rbXCG6
 5Cq2ol/NbF4RbFodel4YdiAIKfrQtXyQ3N3twC5GRXln4XLjUfzs4mA5rxLLoeGZ
 2UGXeIk0GcokSWF/e+0p3tQDWKwdbqoBhbRbqk7u5ZWuEWTRf4Zot3IlCVpJAMM3
 iRw5XChWxovC+/oqgin4sp1gNpSRgf5mMvR1EauR5DTVtwlOjUBKaPEyKLrPITOo
 B42EJugJ94J0YVdT9RUJsOSXIdOiYFE6I9F4i/XioLYq5FItBB56/81ARZgEncpg
 FEdtseCCtRC3WWGzghxZsSzCW3iGi8wdddRdZmOXCNdPtH03TZg0dGPS+KIn8Soh
 eVSGImV/4efN6hh6fSryeR02fYT3DKGgDQUiV4e/1SOSzxy6VjjrOh48tB8qn/M7
 NbFZMqDKnltsXT2C+bh6zjhorbVCkj8AEtx1oF0d7iIyBxor3eHUelTz6VglNlLq
 RFetH+Yjh9nt9ReO
 =1Mk9
 -----END PGP SIGNATURE-----

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

Pull irqchip updates from Marc Zyngier:

 - A bunch of new irqchip drivers (RDA8810PL, Madera, imx-irqsteer)
 - Updates for new (and old) platforms (i.MX8MQ, F1C100s)
 - A number of SPDX cleanups
 - A workaround for a very broken GICv3 implementation
 - A platform-msi fix
 - Various cleanups
This commit is contained in:
Thomas Gleixner 2018-12-18 18:37:27 +01:00
commit ff3730a497
28 changed files with 1190 additions and 127 deletions

View File

@ -2,7 +2,9 @@ Allwinner Sunxi Interrupt Controller
Required properties:
- compatible : should be "allwinner,sun4i-a10-ic"
- compatible : should be one of the following:
"allwinner,sun4i-a10-ic"
"allwinner,suniv-f1c100s-ic"
- reg : Specifies base physical address and size of the registers.
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an

View File

@ -7,7 +7,9 @@ Interrupts (LPI).
Main node required properties:
- compatible : should at least contain "arm,gic-v3".
- compatible : should at least contain "arm,gic-v3" or either
"qcom,msm8996-gic-v3", "arm,gic-v3" for msm8996 SoCs
to address SoC specific bugs/quirks
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. Must be a single cell with a value of at least 3.

View File

@ -0,0 +1,34 @@
Freescale IRQSTEER Interrupt multiplexer
Required properties:
- compatible: should be:
- "fsl,imx8m-irqsteer"
- "fsl,imx-irqsteer"
- reg: Physical base address and size of registers.
- interrupts: Should contain the parent interrupt line used to multiplex the
input interrupts.
- clocks: Should contain one clock for entry in clock-names
see Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names:
- "ipg": main logic clock
- interrupt-controller: Identifies the node as an interrupt controller.
- #interrupt-cells: Specifies the number of cells needed to encode an
interrupt source. The value must be 1.
- fsl,channel: The output channel that all input IRQs should be steered into.
- fsl,irq-groups: Number of IRQ groups managed by this controller instance.
Each group manages 64 input interrupts.
Example:
interrupt-controller@32e2d000 {
compatible = "fsl,imx8m-irqsteer", "fsl,imx-irqsteer";
reg = <0x32e2d000 0x1000>;
interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MQ_CLK_DISP_APB_ROOT>;
clock-names = "ipg";
fsl,channel = <0>;
fsl,irq-groups = <1>;
interrupt-controller;
#interrupt-cells = <1>;
};

View File

@ -0,0 +1,61 @@
RDA Micro RDA8810PL Interrupt Controller
The interrupt controller in RDA8810PL SoC is a custom interrupt controller
which supports up to 32 interrupts.
Required properties:
- compatible: Should be "rda,8810pl-intc".
- reg: Specifies base physical address of the registers set.
- interrupt-controller: Identifies the node as an interrupt controller.
- #interrupt-cells: Specifies the number of cells needed to encode an
interrupt source. The value shall be 2.
The interrupt sources are as follows:
ID Name
------------
0: PULSE_DUMMY
1: I2C
2: NAND_NFSC
3: SDMMC1
4: SDMMC2
5: SDMMC3
6: SPI1
7: SPI2
8: SPI3
9: UART1
10: UART2
11: UART3
12: GPIO1
13: GPIO2
14: GPIO3
15: KEYPAD
16: TIMER
17: TIMEROS
18: COMREG0
19: COMREG1
20: USB
21: DMC
22: DMA
23: CAMERA
24: GOUDA
25: GPU
26: VPU_JPG
27: VPU_HOST
28: VOC
29: AUIFC0
30: AUIFC1
31: L2CC
Example:
apb@20800000 {
compatible = "simple-bus";
...
intc: interrupt-controller@0 {
compatible = "rda,8810pl-intc";
reg = <0x0 0x1000>;
interrupt-controller;
#interrupt-cells = <2>;
};
};

View File

@ -14,6 +14,10 @@ Required properties:
(only needed for exti controller with multiple exti under
same parent interrupt: st,stm32-exti and st,stm32h7-exti)
Optional properties:
- hwlocks: reference to a phandle of a hardware spinlock provider node.
Example:
exti: interrupt-controller@40013c00 {

View File

@ -3669,8 +3669,10 @@ W: https://github.com/CirrusLogic/linux-drivers/wiki
S: Supported
F: Documentation/devicetree/bindings/mfd/madera.txt
F: Documentation/devicetree/bindings/pinctrl/cirrus,madera-pinctrl.txt
F: include/linux/irqchip/irq-madera*
F: include/linux/mfd/madera/*
F: drivers/gpio/gpio-madera*
F: drivers/irqchip/irq-madera*
F: drivers/mfd/madera*
F: drivers/mfd/cs47l*
F: drivers/pinctrl/cirrus/*

View File

@ -368,14 +368,16 @@ void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
unsigned int nvec)
{
struct platform_msi_priv_data *data = domain->host_data;
struct msi_desc *desc;
for_each_msi_entry(desc, data->dev) {
struct msi_desc *desc, *tmp;
for_each_msi_entry_safe(desc, tmp, data->dev) {
if (WARN_ON(!desc->irq || desc->nvec_used != 1))
return;
if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
continue;
irq_domain_free_irqs_common(domain, desc->irq, 1);
list_del(&desc->list);
free_msi_entry(desc);
}
}

View File

@ -150,6 +150,9 @@ config IMGPDC_IRQ
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
config MADERA_IRQ
tristate
config IRQ_MIPS_CPU
bool
select GENERIC_IRQ_CHIP
@ -195,6 +198,10 @@ config JCORE_AIC
help
Support for the J-Core integrated AIC.
config RDA_INTC
bool
select IRQ_DOMAIN
config RENESAS_INTC_IRQPIN
bool
select IRQ_DOMAIN
@ -391,6 +398,14 @@ config CSKY_APB_INTC
by C-SKY single core SOC system. It use mmio map apb-bus to visit
the controller's register.
config IMX_IRQSTEER
bool "i.MX IRQSTEER support"
depends on ARCH_MXC || COMPILE_TEST
default ARCH_MXC
select IRQ_DOMAIN
help
Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
endmenu
config SIFIVE_PLIC

View File

@ -43,6 +43,7 @@ obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o
obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
@ -91,3 +92,5 @@ obj-$(CONFIG_QCOM_PDC) += qcom-pdc.o
obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
obj-$(CONFIG_MADERA_IRQ) += irq-madera.o

View File

@ -1,17 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2010 Broadcom
* Copyright 2012 Simon Arlott, Chris Boot, Stephen Warren
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Quirk 1: Shortcut interrupts don't set the bank 1/2 register pending bits
*
* If an interrupt fires on bank 1 that isn't in the shortcuts list, bit 8

View File

@ -1,17 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Root interrupt controller for the BCM2836 (Raspberry Pi 2).
*
* Copyright 2015 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/cpu.h>

View File

@ -36,6 +36,18 @@ void gic_set_kvm_info(const struct gic_kvm_info *info)
gic_kvm_info = info;
}
void gic_enable_of_quirks(const struct device_node *np,
const struct gic_quirk *quirks, void *data)
{
for (; quirks->desc; quirks++) {
if (!of_device_is_compatible(np, quirks->compatible))
continue;
if (quirks->init(data))
pr_info("GIC: enabling workaround for %s\n",
quirks->desc);
}
}
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
void *data)
{

View File

@ -23,6 +23,7 @@
struct gic_quirk {
const char *desc;
const char *compatible;
bool (*init)(void *data);
u32 iidr;
u32 mask;
@ -35,6 +36,8 @@ void gic_dist_config(void __iomem *base, int gic_irqs,
void gic_cpu_config(void __iomem *base, void (*sync_access)(void));
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
void *data);
void gic_enable_of_quirks(const struct device_node *np,
const struct gic_quirk *quirks, void *data);
void gic_set_kvm_info(const struct gic_kvm_info *info);

View File

@ -41,6 +41,8 @@
#include "irq-gic-common.h"
#define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0)
struct redist_region {
void __iomem *redist_base;
phys_addr_t phys_base;
@ -55,6 +57,7 @@ struct gic_chip_data {
struct irq_domain *domain;
u64 redist_stride;
u32 nr_redist_regions;
u64 flags;
bool has_rss;
unsigned int irq_nr;
struct partition_desc *ppi_descs[16];
@ -139,6 +142,9 @@ static void gic_enable_redist(bool enable)
u32 count = 1000000; /* 1s! */
u32 val;
if (gic_data.flags & FLAGS_WORKAROUND_GICR_WAKER_MSM8996)
return;
rbase = gic_data_rdist_rd_base();
val = readl_relaxed(rbase + GICR_WAKER);
@ -1067,6 +1073,15 @@ static const struct irq_domain_ops partition_domain_ops = {
.select = gic_irq_domain_select,
};
static bool gic_enable_quirk_msm8996(void *data)
{
struct gic_chip_data *d = data;
d->flags |= FLAGS_WORKAROUND_GICR_WAKER_MSM8996;
return true;
}
static int __init gic_init_bases(void __iomem *dist_base,
struct redist_region *rdist_regs,
u32 nr_redist_regions,
@ -1271,6 +1286,16 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
gic_set_kvm_info(&gic_v3_kvm_info);
}
static const struct gic_quirk gic_quirks[] = {
{
.desc = "GICv3: Qualcomm MSM8996 broken firmware",
.compatible = "qcom,msm8996-gic-v3",
.init = gic_enable_quirk_msm8996,
},
{
}
};
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *dist_base;
@ -1318,6 +1343,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
redist_stride = 0;
gic_enable_of_quirks(node, gic_quirks, &gic_data);
err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
redist_stride, &node->fwnode);
if (err)

View File

@ -17,6 +17,9 @@
#define GPC_IMR1_CORE0 0x30
#define GPC_IMR1_CORE1 0x40
#define GPC_IMR1_CORE2 0x1c0
#define GPC_IMR1_CORE3 0x1d0
struct gpcv2_irqchip_data {
struct raw_spinlock rlock;
@ -28,6 +31,11 @@ struct gpcv2_irqchip_data {
static struct gpcv2_irqchip_data *imx_gpcv2_instance;
static void __iomem *gpcv2_idx_to_reg(struct gpcv2_irqchip_data *cd, int i)
{
return cd->gpc_base + cd->cpu2wakeup + i * 4;
}
static int gpcv2_wakeup_source_save(void)
{
struct gpcv2_irqchip_data *cd;
@ -39,7 +47,7 @@ static int gpcv2_wakeup_source_save(void)
return 0;
for (i = 0; i < IMR_NUM; i++) {
reg = cd->gpc_base + cd->cpu2wakeup + i * 4;
reg = gpcv2_idx_to_reg(cd, i);
cd->saved_irq_mask[i] = readl_relaxed(reg);
writel_relaxed(cd->wakeup_sources[i], reg);
}
@ -50,17 +58,14 @@ static int gpcv2_wakeup_source_save(void)
static void gpcv2_wakeup_source_restore(void)
{
struct gpcv2_irqchip_data *cd;
void __iomem *reg;
int i;
cd = imx_gpcv2_instance;
if (!cd)
return;
for (i = 0; i < IMR_NUM; i++) {
reg = cd->gpc_base + cd->cpu2wakeup + i * 4;
writel_relaxed(cd->saved_irq_mask[i], reg);
}
for (i = 0; i < IMR_NUM; i++)
writel_relaxed(cd->saved_irq_mask[i], gpcv2_idx_to_reg(cd, i));
}
static struct syscore_ops imx_gpcv2_syscore_ops = {
@ -73,12 +78,10 @@ static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on)
struct gpcv2_irqchip_data *cd = d->chip_data;
unsigned int idx = d->hwirq / 32;
unsigned long flags;
void __iomem *reg;
u32 mask, val;
raw_spin_lock_irqsave(&cd->rlock, flags);
reg = cd->gpc_base + cd->cpu2wakeup + idx * 4;
mask = 1 << d->hwirq % 32;
mask = BIT(d->hwirq % 32);
val = cd->wakeup_sources[idx];
cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask);
@ -99,9 +102,9 @@ static void imx_gpcv2_irq_unmask(struct irq_data *d)
u32 val;
raw_spin_lock(&cd->rlock);
reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4;
reg = gpcv2_idx_to_reg(cd, d->hwirq / 32);
val = readl_relaxed(reg);
val &= ~(1 << d->hwirq % 32);
val &= ~BIT(d->hwirq % 32);
writel_relaxed(val, reg);
raw_spin_unlock(&cd->rlock);
@ -115,9 +118,9 @@ static void imx_gpcv2_irq_mask(struct irq_data *d)
u32 val;
raw_spin_lock(&cd->rlock);
reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4;
reg = gpcv2_idx_to_reg(cd, d->hwirq / 32);
val = readl_relaxed(reg);
val |= 1 << (d->hwirq % 32);
val |= BIT(d->hwirq % 32);
writel_relaxed(val, reg);
raw_spin_unlock(&cd->rlock);
@ -192,11 +195,19 @@ static const struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
.free = irq_domain_free_irqs_common,
};
static const struct of_device_id gpcv2_of_match[] = {
{ .compatible = "fsl,imx7d-gpc", .data = (const void *) 2 },
{ .compatible = "fsl,imx8mq-gpc", .data = (const void *) 4 },
{ /* END */ }
};
static int __init imx_gpcv2_irqchip_init(struct device_node *node,
struct device_node *parent)
{
struct irq_domain *parent_domain, *domain;
struct gpcv2_irqchip_data *cd;
const struct of_device_id *id;
unsigned long core_num;
int i;
if (!parent) {
@ -204,6 +215,14 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node,
return -ENODEV;
}
id = of_match_node(gpcv2_of_match, node);
if (!id) {
pr_err("%pOF: unknown compatibility string\n", node);
return -ENODEV;
}
core_num = (unsigned long)id->data;
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("%pOF: unable to get parent domain\n", node);
@ -212,7 +231,7 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node,
cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL);
if (!cd) {
pr_err("kzalloc failed!\n");
pr_err("%pOF: kzalloc failed!\n", node);
return -ENOMEM;
}
@ -220,7 +239,7 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node,
cd->gpc_base = of_iomap(node, 0);
if (!cd->gpc_base) {
pr_err("fsl-gpcv2: unable to map gpc registers\n");
pr_err("%pOF: unable to map gpc registers\n", node);
kfree(cd);
return -ENOMEM;
}
@ -236,8 +255,17 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node,
/* Initially mask all interrupts */
for (i = 0; i < IMR_NUM; i++) {
writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE0 + i * 4);
writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE1 + i * 4);
void __iomem *reg = cd->gpc_base + i * 4;
switch (core_num) {
case 4:
writel_relaxed(~0, reg + GPC_IMR1_CORE2);
writel_relaxed(~0, reg + GPC_IMR1_CORE3);
/* fall through */
case 2:
writel_relaxed(~0, reg + GPC_IMR1_CORE0);
writel_relaxed(~0, reg + GPC_IMR1_CORE1);
}
cd->wakeup_sources[i] = ~0;
}
@ -262,4 +290,5 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node,
return 0;
}
IRQCHIP_DECLARE(imx_gpcv2, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init);
IRQCHIP_DECLARE(imx_gpcv2_imx7d, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init);
IRQCHIP_DECLARE(imx_gpcv2_imx8mq, "fsl,imx8mq-gpc", imx_gpcv2_irqchip_init);

View File

@ -0,0 +1,261 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017 NXP
* Copyright (C) 2018 Pengutronix, Lucas Stach <kernel@pengutronix.de>
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/of_platform.h>
#include <linux/spinlock.h>
#define CTRL_STRIDE_OFF(_t, _r) (_t * 8 * _r)
#define CHANCTRL 0x0
#define CHANMASK(n, t) (CTRL_STRIDE_OFF(t, 0) + 0x4 * (n) + 0x4)
#define CHANSET(n, t) (CTRL_STRIDE_OFF(t, 1) + 0x4 * (n) + 0x4)
#define CHANSTATUS(n, t) (CTRL_STRIDE_OFF(t, 2) + 0x4 * (n) + 0x4)
#define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4)
#define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8)
struct irqsteer_data {
void __iomem *regs;
struct clk *ipg_clk;
int irq;
raw_spinlock_t lock;
int irq_groups;
int channel;
struct irq_domain *domain;
u32 *saved_reg;
};
static int imx_irqsteer_get_reg_index(struct irqsteer_data *data,
unsigned long irqnum)
{
return (data->irq_groups * 2 - irqnum / 32 - 1);
}
static void imx_irqsteer_irq_unmask(struct irq_data *d)
{
struct irqsteer_data *data = d->chip_data;
int idx = imx_irqsteer_get_reg_index(data, d->hwirq);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&data->lock, flags);
val = readl_relaxed(data->regs + CHANMASK(idx, data->irq_groups));
val |= BIT(d->hwirq % 32);
writel_relaxed(val, data->regs + CHANMASK(idx, data->irq_groups));
raw_spin_unlock_irqrestore(&data->lock, flags);
}
static void imx_irqsteer_irq_mask(struct irq_data *d)
{
struct irqsteer_data *data = d->chip_data;
int idx = imx_irqsteer_get_reg_index(data, d->hwirq);
unsigned long flags;
u32 val;
raw_spin_lock_irqsave(&data->lock, flags);
val = readl_relaxed(data->regs + CHANMASK(idx, data->irq_groups));
val &= ~BIT(d->hwirq % 32);
writel_relaxed(val, data->regs + CHANMASK(idx, data->irq_groups));
raw_spin_unlock_irqrestore(&data->lock, flags);
}
static struct irq_chip imx_irqsteer_irq_chip = {
.name = "irqsteer",
.irq_mask = imx_irqsteer_irq_mask,
.irq_unmask = imx_irqsteer_irq_unmask,
};
static int imx_irqsteer_irq_map(struct irq_domain *h, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_status_flags(irq, IRQ_LEVEL);
irq_set_chip_data(irq, h->host_data);
irq_set_chip_and_handler(irq, &imx_irqsteer_irq_chip, handle_level_irq);
return 0;
}
static const struct irq_domain_ops imx_irqsteer_domain_ops = {
.map = imx_irqsteer_irq_map,
.xlate = irq_domain_xlate_onecell,
};
static void imx_irqsteer_irq_handler(struct irq_desc *desc)
{
struct irqsteer_data *data = irq_desc_get_handler_data(desc);
int i;
chained_irq_enter(irq_desc_get_chip(desc), desc);
for (i = 0; i < data->irq_groups * 64; i += 32) {
int idx = imx_irqsteer_get_reg_index(data, i);
unsigned long irqmap;
int pos, virq;
irqmap = readl_relaxed(data->regs +
CHANSTATUS(idx, data->irq_groups));
for_each_set_bit(pos, &irqmap, 32) {
virq = irq_find_mapping(data->domain, pos + i);
if (virq)
generic_handle_irq(virq);
}
}
chained_irq_exit(irq_desc_get_chip(desc), desc);
}
static int imx_irqsteer_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct irqsteer_data *data;
struct resource *res;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->regs)) {
dev_err(&pdev->dev, "failed to initialize reg\n");
return PTR_ERR(data->regs);
}
data->irq = platform_get_irq(pdev, 0);
if (data->irq <= 0) {
dev_err(&pdev->dev, "failed to get irq\n");
return -ENODEV;
}
data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(data->ipg_clk)) {
ret = PTR_ERR(data->ipg_clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
return ret;
}
raw_spin_lock_init(&data->lock);
of_property_read_u32(np, "fsl,irq-groups", &data->irq_groups);
of_property_read_u32(np, "fsl,channel", &data->channel);
if (IS_ENABLED(CONFIG_PM_SLEEP)) {
data->saved_reg = devm_kzalloc(&pdev->dev,
sizeof(u32) * data->irq_groups * 2,
GFP_KERNEL);
if (!data->saved_reg)
return -ENOMEM;
}
ret = clk_prepare_enable(data->ipg_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret);
return ret;
}
/* steer all IRQs into configured channel */
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
data->domain = irq_domain_add_linear(np, data->irq_groups * 64,
&imx_irqsteer_domain_ops, data);
if (!data->domain) {
dev_err(&pdev->dev, "failed to create IRQ domain\n");
clk_disable_unprepare(data->ipg_clk);
return -ENOMEM;
}
irq_set_chained_handler_and_data(data->irq, imx_irqsteer_irq_handler,
data);
platform_set_drvdata(pdev, data);
return 0;
}
static int imx_irqsteer_remove(struct platform_device *pdev)
{
struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev);
irq_set_chained_handler_and_data(irqsteer_data->irq, NULL, NULL);
irq_domain_remove(irqsteer_data->domain);
clk_disable_unprepare(irqsteer_data->ipg_clk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static void imx_irqsteer_save_regs(struct irqsteer_data *data)
{
int i;
for (i = 0; i < data->irq_groups * 2; i++)
data->saved_reg[i] = readl_relaxed(data->regs +
CHANMASK(i, data->irq_groups));
}
static void imx_irqsteer_restore_regs(struct irqsteer_data *data)
{
int i;
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
for (i = 0; i < data->irq_groups * 2; i++)
writel_relaxed(data->saved_reg[i],
data->regs + CHANMASK(i, data->irq_groups));
}
static int imx_irqsteer_suspend(struct device *dev)
{
struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev);
imx_irqsteer_save_regs(irqsteer_data);
clk_disable_unprepare(irqsteer_data->ipg_clk);
return 0;
}
static int imx_irqsteer_resume(struct device *dev)
{
struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(irqsteer_data->ipg_clk);
if (ret) {
dev_err(dev, "failed to enable ipg clk: %d\n", ret);
return ret;
}
imx_irqsteer_restore_regs(irqsteer_data);
return 0;
}
#endif
static const struct dev_pm_ops imx_irqsteer_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_irqsteer_suspend, imx_irqsteer_resume)
};
static const struct of_device_id imx_irqsteer_dt_ids[] = {
{ .compatible = "fsl,imx-irqsteer", },
{},
};
static struct platform_driver imx_irqsteer_driver = {
.driver = {
.name = "imx-irqsteer",
.of_match_table = imx_irqsteer_dt_ids,
.pm = &imx_irqsteer_pm_ops,
},
.probe = imx_irqsteer_probe,
.remove = imx_irqsteer_remove,
};
builtin_platform_driver(imx_irqsteer_driver);

View File

@ -0,0 +1,256 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interrupt support for Cirrus Logic Madera codecs
*
* Copyright (C) 2015-2018 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irqchip/irq-madera.h>
#include <linux/mfd/madera/core.h>
#include <linux/mfd/madera/pdata.h>
#include <linux/mfd/madera/registers.h>
#define MADERA_IRQ(_irq, _reg) \
[MADERA_IRQ_ ## _irq] = { \
.reg_offset = (_reg) - MADERA_IRQ1_STATUS_2, \
.mask = MADERA_ ## _irq ## _EINT1 \
}
/* Mappings are the same for all Madera codecs */
static const struct regmap_irq madera_irqs[MADERA_NUM_IRQ] = {
MADERA_IRQ(FLL1_LOCK, MADERA_IRQ1_STATUS_2),
MADERA_IRQ(FLL2_LOCK, MADERA_IRQ1_STATUS_2),
MADERA_IRQ(FLL3_LOCK, MADERA_IRQ1_STATUS_2),
MADERA_IRQ(FLLAO_LOCK, MADERA_IRQ1_STATUS_2),
MADERA_IRQ(MICDET1, MADERA_IRQ1_STATUS_6),
MADERA_IRQ(MICDET2, MADERA_IRQ1_STATUS_6),
MADERA_IRQ(HPDET, MADERA_IRQ1_STATUS_6),
MADERA_IRQ(MICD_CLAMP_RISE, MADERA_IRQ1_STATUS_7),
MADERA_IRQ(MICD_CLAMP_FALL, MADERA_IRQ1_STATUS_7),
MADERA_IRQ(JD1_RISE, MADERA_IRQ1_STATUS_7),
MADERA_IRQ(JD1_FALL, MADERA_IRQ1_STATUS_7),
MADERA_IRQ(ASRC2_IN1_LOCK, MADERA_IRQ1_STATUS_9),
MADERA_IRQ(ASRC2_IN2_LOCK, MADERA_IRQ1_STATUS_9),
MADERA_IRQ(ASRC1_IN1_LOCK, MADERA_IRQ1_STATUS_9),
MADERA_IRQ(ASRC1_IN2_LOCK, MADERA_IRQ1_STATUS_9),
MADERA_IRQ(DRC2_SIG_DET, MADERA_IRQ1_STATUS_9),
MADERA_IRQ(DRC1_SIG_DET, MADERA_IRQ1_STATUS_9),
MADERA_IRQ(DSP_IRQ1, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ2, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ3, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ4, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ5, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ6, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ7, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ8, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ9, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ10, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ11, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ12, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ13, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ14, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ15, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(DSP_IRQ16, MADERA_IRQ1_STATUS_11),
MADERA_IRQ(HP3R_SC, MADERA_IRQ1_STATUS_12),
MADERA_IRQ(HP3L_SC, MADERA_IRQ1_STATUS_12),
MADERA_IRQ(HP2R_SC, MADERA_IRQ1_STATUS_12),
MADERA_IRQ(HP2L_SC, MADERA_IRQ1_STATUS_12),
MADERA_IRQ(HP1R_SC, MADERA_IRQ1_STATUS_12),
MADERA_IRQ(HP1L_SC, MADERA_IRQ1_STATUS_12),
MADERA_IRQ(SPK_OVERHEAT_WARN, MADERA_IRQ1_STATUS_15),
MADERA_IRQ(SPK_OVERHEAT, MADERA_IRQ1_STATUS_15),
MADERA_IRQ(DSP1_BUS_ERR, MADERA_IRQ1_STATUS_33),
MADERA_IRQ(DSP2_BUS_ERR, MADERA_IRQ1_STATUS_33),
MADERA_IRQ(DSP3_BUS_ERR, MADERA_IRQ1_STATUS_33),
MADERA_IRQ(DSP4_BUS_ERR, MADERA_IRQ1_STATUS_33),
MADERA_IRQ(DSP5_BUS_ERR, MADERA_IRQ1_STATUS_33),
MADERA_IRQ(DSP6_BUS_ERR, MADERA_IRQ1_STATUS_33),
MADERA_IRQ(DSP7_BUS_ERR, MADERA_IRQ1_STATUS_33),
};
static const struct regmap_irq_chip madera_irq_chip = {
.name = "madera IRQ",
.status_base = MADERA_IRQ1_STATUS_2,
.mask_base = MADERA_IRQ1_MASK_2,
.ack_base = MADERA_IRQ1_STATUS_2,
.runtime_pm = true,
.num_regs = 32,
.irqs = madera_irqs,
.num_irqs = ARRAY_SIZE(madera_irqs),
};
#ifdef CONFIG_PM_SLEEP
static int madera_suspend(struct device *dev)
{
struct madera *madera = dev_get_drvdata(dev->parent);
dev_dbg(madera->irq_dev, "Suspend, disabling IRQ\n");
/*
* A runtime resume would be needed to access the chip interrupt
* controller but runtime pm doesn't function during suspend.
* Temporarily disable interrupts until we reach suspend_noirq state.
*/
disable_irq(madera->irq);
return 0;
}
static int madera_suspend_noirq(struct device *dev)
{
struct madera *madera = dev_get_drvdata(dev->parent);
dev_dbg(madera->irq_dev, "No IRQ suspend, reenabling IRQ\n");
/* Re-enable interrupts to service wakeup interrupts from the chip */
enable_irq(madera->irq);
return 0;
}
static int madera_resume_noirq(struct device *dev)
{
struct madera *madera = dev_get_drvdata(dev->parent);
dev_dbg(madera->irq_dev, "No IRQ resume, disabling IRQ\n");
/*
* We can't handle interrupts until runtime pm is available again.
* Disable them temporarily.
*/
disable_irq(madera->irq);
return 0;
}
static int madera_resume(struct device *dev)
{
struct madera *madera = dev_get_drvdata(dev->parent);
dev_dbg(madera->irq_dev, "Resume, reenabling IRQ\n");
/* Interrupts can now be handled */
enable_irq(madera->irq);
return 0;
}
#endif
static const struct dev_pm_ops madera_irq_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(madera_suspend, madera_resume)
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(madera_suspend_noirq,
madera_resume_noirq)
};
static int madera_irq_probe(struct platform_device *pdev)
{
struct madera *madera = dev_get_drvdata(pdev->dev.parent);
struct irq_data *irq_data;
unsigned int irq_flags = 0;
int ret;
dev_dbg(&pdev->dev, "probe\n");
/*
* Read the flags from the interrupt controller if not specified
* by pdata
*/
irq_flags = madera->pdata.irq_flags;
if (!irq_flags) {
irq_data = irq_get_irq_data(madera->irq);
if (!irq_data) {
dev_err(&pdev->dev, "Invalid IRQ: %d\n", madera->irq);
return -EINVAL;
}
irq_flags = irqd_get_trigger_type(irq_data);
/* Codec defaults to trigger low, use this if no flags given */
if (irq_flags == IRQ_TYPE_NONE)
irq_flags = IRQF_TRIGGER_LOW;
}
if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
dev_err(&pdev->dev, "Host interrupt not level-triggered\n");
return -EINVAL;
}
/*
* The silicon always starts at active-low, check if we need to
* switch to active-high.
*/
if (irq_flags & IRQF_TRIGGER_HIGH) {
ret = regmap_update_bits(madera->regmap, MADERA_IRQ1_CTRL,
MADERA_IRQ_POL_MASK, 0);
if (ret) {
dev_err(&pdev->dev,
"Failed to set IRQ polarity: %d\n", ret);
return ret;
}
}
/*
* NOTE: regmap registers this against the OF node of the parent of
* the regmap - that is, against the mfd driver
*/
ret = regmap_add_irq_chip(madera->regmap, madera->irq, IRQF_ONESHOT, 0,
&madera_irq_chip, &madera->irq_data);
if (ret) {
dev_err(&pdev->dev, "add_irq_chip failed: %d\n", ret);
return ret;
}
/* Save dev in parent MFD struct so it is accessible to siblings */
madera->irq_dev = &pdev->dev;
return 0;
}
static int madera_irq_remove(struct platform_device *pdev)
{
struct madera *madera = dev_get_drvdata(pdev->dev.parent);
/*
* The IRQ is disabled by the parent MFD driver before
* it starts cleaning up all child drivers
*/
madera->irq_dev = NULL;
regmap_del_irq_chip(madera->irq, madera->irq_data);
return 0;
}
static struct platform_driver madera_irq_driver = {
.probe = &madera_irq_probe,
.remove = &madera_irq_remove,
.driver = {
.name = "madera-irq",
.pm = &madera_irq_pm_ops,
}
};
module_platform_driver(madera_irq_driver);
MODULE_SOFTDEP("pre: madera");
MODULE_DESCRIPTION("Madera IRQ driver");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");

View File

@ -72,7 +72,7 @@ static int __init ocelot_irq_init(struct device_node *node,
domain = irq_domain_add_linear(node, OCELOT_NR_IRQ,
&irq_generic_chip_ops, NULL);
if (!domain) {
pr_err("%s: unable to add irq domain\n", node->name);
pr_err("%pOFn: unable to add irq domain\n", node);
return -ENOMEM;
}
@ -80,14 +80,14 @@ static int __init ocelot_irq_init(struct device_node *node,
"icpu", handle_level_irq,
0, 0, 0);
if (ret) {
pr_err("%s: unable to alloc irq domain gc\n", node->name);
pr_err("%pOFn: unable to alloc irq domain gc\n", node);
goto err_domain_remove;
}
gc = irq_get_domain_generic_chip(domain, 0);
gc->reg_base = of_iomap(node, 0);
if (!gc->reg_base) {
pr_err("%s: unable to map resource\n", node->name);
pr_err("%pOFn: unable to map resource\n", node);
ret = -ENOMEM;
goto err_gc_free;
}

View File

@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RDA8810PL SoC irqchip driver
*
* Copyright RDA Microelectronics Company Limited
* Copyright (c) 2017 Andreas Färber
* Copyright (c) 2018 Manivannan Sadhasivam
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <asm/exception.h>
#define RDA_INTC_FINALSTATUS 0x00
#define RDA_INTC_MASK_SET 0x08
#define RDA_INTC_MASK_CLR 0x0c
#define RDA_IRQ_MASK_ALL 0xFFFFFFFF
#define RDA_NR_IRQS 32
static void __iomem *rda_intc_base;
static struct irq_domain *rda_irq_domain;
static void rda_intc_mask_irq(struct irq_data *d)
{
writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_CLR);
}
static void rda_intc_unmask_irq(struct irq_data *d)
{
writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_SET);
}
static int rda_intc_set_type(struct irq_data *data, unsigned int flow_type)
{
/* Hardware supports only level triggered interrupts */
if ((flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) == flow_type)
return 0;
return -EINVAL;
}
static void __exception_irq_entry rda_handle_irq(struct pt_regs *regs)
{
u32 stat = readl_relaxed(rda_intc_base + RDA_INTC_FINALSTATUS);
u32 hwirq;
while (stat) {
hwirq = __fls(stat);
handle_domain_irq(rda_irq_domain, hwirq, regs);
stat &= ~BIT(hwirq);
}
}
static struct irq_chip rda_irq_chip = {
.name = "rda-intc",
.irq_mask = rda_intc_mask_irq,
.irq_unmask = rda_intc_unmask_irq,
.irq_set_type = rda_intc_set_type,
};
static int rda_irq_map(struct irq_domain *d,
unsigned int virq, irq_hw_number_t hw)
{
irq_set_status_flags(virq, IRQ_LEVEL);
irq_set_chip_and_handler(virq, &rda_irq_chip, handle_level_irq);
irq_set_chip_data(virq, d->host_data);
irq_set_probe(virq);
return 0;
}
static const struct irq_domain_ops rda_irq_domain_ops = {
.map = rda_irq_map,
.xlate = irq_domain_xlate_onecell,
};
static int __init rda8810_intc_init(struct device_node *node,
struct device_node *parent)
{
rda_intc_base = of_io_request_and_map(node, 0, "rda-intc");
if (IS_ERR(rda_intc_base))
return PTR_ERR(rda_intc_base);
/* Mask all interrupt sources */
writel_relaxed(RDA_IRQ_MASK_ALL, rda_intc_base + RDA_INTC_MASK_CLR);
rda_irq_domain = irq_domain_create_linear(&node->fwnode, RDA_NR_IRQS,
&rda_irq_domain_ops,
rda_intc_base);
if (!rda_irq_domain) {
iounmap(rda_intc_base);
return -ENOMEM;
}
set_handle_irq(rda_handle_irq);
return 0;
}
IRQCHIP_DECLARE(rda_intc, "rda,8810pl-intc", rda8810_intc_init);

View File

@ -1,20 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas INTC External IRQ Pin Driver
*
* Copyright (C) 2013 Magnus Damm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>

View File

@ -1,20 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas IRQC Driver
*
* Copyright (C) 2013 Magnus Damm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>

View File

@ -6,6 +6,8 @@
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/hwspinlock.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
@ -20,6 +22,9 @@
#define IRQS_PER_BANK 32
#define HWSPNLCK_TIMEOUT 1000 /* usec */
#define HWSPNLCK_RETRY_DELAY 100 /* usec */
struct stm32_exti_bank {
u32 imr_ofst;
u32 emr_ofst;
@ -32,6 +37,12 @@ struct stm32_exti_bank {
#define UNDEF_REG ~0
enum stm32_exti_hwspinlock {
HWSPINLOCK_UNKNOWN,
HWSPINLOCK_NONE,
HWSPINLOCK_READY,
};
struct stm32_desc_irq {
u32 exti;
u32 irq_parent;
@ -58,6 +69,9 @@ struct stm32_exti_host_data {
void __iomem *base;
struct stm32_exti_chip_data *chips_data;
const struct stm32_exti_drv_data *drv_data;
struct device_node *node;
enum stm32_exti_hwspinlock hwlock_state;
struct hwspinlock *hwlock;
};
static struct stm32_exti_host_data *stm32_host_data;
@ -269,6 +283,64 @@ static int stm32_exti_set_type(struct irq_data *d,
return 0;
}
static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data)
{
struct stm32_exti_host_data *host_data = chip_data->host_data;
struct hwspinlock *hwlock;
int id, ret = 0, timeout = 0;
/* first time, check for hwspinlock availability */
if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) {
id = of_hwspin_lock_get_id(host_data->node, 0);
if (id >= 0) {
hwlock = hwspin_lock_request_specific(id);
if (hwlock) {
/* found valid hwspinlock */
host_data->hwlock_state = HWSPINLOCK_READY;
host_data->hwlock = hwlock;
pr_debug("%s hwspinlock = %d\n", __func__, id);
} else {
host_data->hwlock_state = HWSPINLOCK_NONE;
}
} else if (id != -EPROBE_DEFER) {
host_data->hwlock_state = HWSPINLOCK_NONE;
} else {
/* hwspinlock driver shall be ready at that stage */
ret = -EPROBE_DEFER;
}
}
if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) {
/*
* Use the x_raw API since we are under spin_lock protection.
* Do not use the x_timeout API because we are under irq_disable
* mode (see __setup_irq())
*/
do {
ret = hwspin_trylock_raw(host_data->hwlock);
if (!ret)
return 0;
udelay(HWSPNLCK_RETRY_DELAY);
timeout += HWSPNLCK_RETRY_DELAY;
} while (timeout < HWSPNLCK_TIMEOUT);
if (ret == -EBUSY)
ret = -ETIMEDOUT;
}
if (ret)
pr_err("%s can't get hwspinlock (%d)\n", __func__, ret);
return ret;
}
static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data)
{
if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY))
hwspin_unlock_raw(chip_data->host_data->hwlock);
}
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);
@ -279,21 +351,26 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type)
irq_gc_lock(gc);
err = stm32_exti_hwspin_lock(chip_data);
if (err)
goto unlock;
rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst);
ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst);
err = stm32_exti_set_type(d, type, &rtsr, &ftsr);
if (err) {
irq_gc_unlock(gc);
return err;
}
if (err)
goto unspinlock;
irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst);
irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst);
unspinlock:
stm32_exti_hwspin_unlock(chip_data);
unlock:
irq_gc_unlock(gc);
return 0;
return err;
}
static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data,
@ -460,20 +537,27 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type)
int err;
raw_spin_lock(&chip_data->rlock);
err = stm32_exti_hwspin_lock(chip_data);
if (err)
goto unlock;
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;
}
if (err)
goto unspinlock;
writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst);
writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst);
unspinlock:
stm32_exti_hwspin_unlock(chip_data);
unlock:
raw_spin_unlock(&chip_data->rlock);
return 0;
return err;
}
static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on)
@ -599,6 +683,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
return NULL;
host_data->drv_data = dd;
host_data->node = node;
host_data->hwlock_state = HWSPINLOCK_UNKNOWN;
host_data->chips_data = kcalloc(dd->bank_nr,
sizeof(struct stm32_exti_chip_data),
GFP_KERNEL);
@ -625,8 +711,7 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
static struct
stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
u32 bank_idx,
struct device_node *node)
u32 bank_idx)
{
const struct stm32_exti_bank *stm32_bank;
struct stm32_exti_chip_data *chip_data;
@ -656,8 +741,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
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);
pr_info("%pOF: bank%d\n", h_data->node, bank_idx);
return chip_data;
}
@ -678,8 +762,8 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data,
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",
node->name);
pr_err("%pOFn: Could not register interrupt domain.\n",
node);
ret = -ENOMEM;
goto out_unmap;
}
@ -697,7 +781,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data,
struct stm32_exti_chip_data *chip_data;
stm32_bank = drv_data->exti_banks[i];
chip_data = stm32_exti_chip_init(host_data, i, node);
chip_data = stm32_exti_chip_init(host_data, i);
gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK);
@ -760,7 +844,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data,
return -ENOMEM;
for (i = 0; i < drv_data->bank_nr; i++)
stm32_exti_chip_init(host_data, i, node);
stm32_exti_chip_init(host_data, i);
domain = irq_domain_add_hierarchy(parent_domain, 0,
drv_data->bank_nr * IRQS_PER_BANK,
@ -768,7 +852,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data,
host_data);
if (!domain) {
pr_err("%s: Could not register exti domain.\n", node->name);
pr_err("%pOFn: Could not register exti domain.\n", node);
ret = -ENOMEM;
goto out_unmap;
}

View File

@ -28,11 +28,21 @@
#define SUN4I_IRQ_NMI_CTRL_REG 0x0c
#define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x)
#define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x)
#define SUN4I_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x)
#define SUN4I_IRQ_MASK_REG(x) (0x50 + 0x4 * x)
#define SUN4I_IRQ_ENABLE_REG(data, x) ((data)->enable_reg_offset + 0x4 * x)
#define SUN4I_IRQ_MASK_REG(data, x) ((data)->mask_reg_offset + 0x4 * x)
#define SUN4I_IRQ_ENABLE_REG_OFFSET 0x40
#define SUN4I_IRQ_MASK_REG_OFFSET 0x50
#define SUNIV_IRQ_ENABLE_REG_OFFSET 0x20
#define SUNIV_IRQ_MASK_REG_OFFSET 0x30
static void __iomem *sun4i_irq_base;
static struct irq_domain *sun4i_irq_domain;
struct sun4i_irq_chip_data {
void __iomem *irq_base;
struct irq_domain *irq_domain;
u32 enable_reg_offset;
u32 mask_reg_offset;
};
static struct sun4i_irq_chip_data *irq_ic_data;
static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs);
@ -43,7 +53,7 @@ static void sun4i_irq_ack(struct irq_data *irqd)
if (irq != 0)
return; /* Only IRQ 0 / the ENMI needs to be acked */
writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));
writel(BIT(0), irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0));
}
static void sun4i_irq_mask(struct irq_data *irqd)
@ -53,9 +63,10 @@ static void sun4i_irq_mask(struct irq_data *irqd)
int reg = irq / 32;
u32 val;
val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
val = readl(irq_ic_data->irq_base +
SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg));
writel(val & ~(1 << irq_off),
sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg));
}
static void sun4i_irq_unmask(struct irq_data *irqd)
@ -65,9 +76,10 @@ static void sun4i_irq_unmask(struct irq_data *irqd)
int reg = irq / 32;
u32 val;
val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
val = readl(irq_ic_data->irq_base +
SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg));
writel(val | (1 << irq_off),
sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg));
}
static struct irq_chip sun4i_irq_chip = {
@ -95,42 +107,76 @@ static const struct irq_domain_ops sun4i_irq_ops = {
static int __init sun4i_of_init(struct device_node *node,
struct device_node *parent)
{
sun4i_irq_base = of_iomap(node, 0);
if (!sun4i_irq_base)
irq_ic_data->irq_base = of_iomap(node, 0);
if (!irq_ic_data->irq_base)
panic("%pOF: unable to map IC registers\n",
node);
/* Disable all interrupts */
writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0));
writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1));
writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2));
writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 0));
writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 1));
writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 2));
/* Unmask all the interrupts, ENABLE_REG(x) is used for masking */
writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0));
writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1));
writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2));
writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 0));
writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 1));
writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 2));
/* Clear all the pending interrupts */
writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));
writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1));
writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2));
writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0));
writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(1));
writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(2));
/* Enable protection mode */
writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG);
writel(0x01, irq_ic_data->irq_base + SUN4I_IRQ_PROTECTION_REG);
/* Configure the external interrupt source type */
writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG);
writel(0x00, irq_ic_data->irq_base + SUN4I_IRQ_NMI_CTRL_REG);
sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32,
irq_ic_data->irq_domain = irq_domain_add_linear(node, 3 * 32,
&sun4i_irq_ops, NULL);
if (!sun4i_irq_domain)
if (!irq_ic_data->irq_domain)
panic("%pOF: unable to create IRQ domain\n", node);
set_handle_irq(sun4i_handle_irq);
return 0;
}
IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init);
static int __init sun4i_ic_of_init(struct device_node *node,
struct device_node *parent)
{
irq_ic_data = kzalloc(sizeof(struct sun4i_irq_chip_data), GFP_KERNEL);
if (!irq_ic_data) {
pr_err("kzalloc failed!\n");
return -ENOMEM;
}
irq_ic_data->enable_reg_offset = SUN4I_IRQ_ENABLE_REG_OFFSET;
irq_ic_data->mask_reg_offset = SUN4I_IRQ_MASK_REG_OFFSET;
return sun4i_of_init(node, parent);
}
IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_ic_of_init);
static int __init suniv_ic_of_init(struct device_node *node,
struct device_node *parent)
{
irq_ic_data = kzalloc(sizeof(struct sun4i_irq_chip_data), GFP_KERNEL);
if (!irq_ic_data) {
pr_err("kzalloc failed!\n");
return -ENOMEM;
}
irq_ic_data->enable_reg_offset = SUNIV_IRQ_ENABLE_REG_OFFSET;
irq_ic_data->mask_reg_offset = SUNIV_IRQ_MASK_REG_OFFSET;
return sun4i_of_init(node, parent);
}
IRQCHIP_DECLARE(allwinner_sunvi_ic, "allwinner,suniv-f1c100s-ic",
suniv_ic_of_init);
static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
{
@ -146,13 +192,15 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
* the extra check in the common case of 1 hapening after having
* read the vector-reg once.
*/
hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
hwirq = readl(irq_ic_data->irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
if (hwirq == 0 &&
!(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0)))
!(readl(irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)) &
BIT(0)))
return;
do {
handle_domain_irq(sun4i_irq_domain, hwirq, regs);
hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
handle_domain_irq(irq_ic_data->irq_domain, hwirq, regs);
hwirq = readl(irq_ic_data->irq_base +
SUN4I_IRQ_VECTOR_REG) >> 2;
} while (hwirq != 0);
}

View File

@ -184,11 +184,11 @@ static int __init tangox_irq_init(void __iomem *base, struct resource *baseres,
irq = irq_of_parse_and_map(node, 0);
if (!irq)
panic("%s: failed to get IRQ", node->name);
panic("%pOFn: failed to get IRQ", node);
err = of_address_to_resource(node, 0, &res);
if (err)
panic("%s: failed to get address", node->name);
panic("%pOFn: failed to get address", node);
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip->ctl = res.start - baseres->start;
@ -196,12 +196,12 @@ static int __init tangox_irq_init(void __iomem *base, struct resource *baseres,
dom = irq_domain_add_linear(node, 64, &irq_generic_chip_ops, chip);
if (!dom)
panic("%s: failed to create irqdomain", node->name);
panic("%pOFn: failed to create irqdomain", node);
err = irq_alloc_domain_generic_chips(dom, 32, 2, node->name,
handle_level_irq, 0, 0, 0);
if (err)
panic("%s: failed to allocate irqchip", node->name);
panic("%pOFn: failed to allocate irqchip", node);
tangox_irq_domain_init(dom);
@ -219,7 +219,7 @@ static int __init tangox_of_irq_init(struct device_node *node,
base = of_iomap(node, 0);
if (!base)
panic("%s: of_iomap failed", node->name);
panic("%pOFn: of_iomap failed", node);
of_address_to_resource(node, 0, &res);

View File

@ -16,7 +16,7 @@
struct irq_sim_work_ctx {
struct irq_work work;
int irq;
unsigned long *pending;
};
struct irq_sim_irq_ctx {

View File

@ -0,0 +1,132 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interrupt support for Cirrus Logic Madera codecs
*
* Copyright (C) 2016-2018 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#ifndef IRQCHIP_MADERA_H
#define IRQCHIP_MADERA_H
#include <linux/interrupt.h>
#include <linux/mfd/madera/core.h>
#define MADERA_IRQ_FLL1_LOCK 0
#define MADERA_IRQ_FLL2_LOCK 1
#define MADERA_IRQ_FLL3_LOCK 2
#define MADERA_IRQ_FLLAO_LOCK 3
#define MADERA_IRQ_CLK_SYS_ERR 4
#define MADERA_IRQ_CLK_ASYNC_ERR 5
#define MADERA_IRQ_CLK_DSP_ERR 6
#define MADERA_IRQ_HPDET 7
#define MADERA_IRQ_MICDET1 8
#define MADERA_IRQ_MICDET2 9
#define MADERA_IRQ_JD1_RISE 10
#define MADERA_IRQ_JD1_FALL 11
#define MADERA_IRQ_JD2_RISE 12
#define MADERA_IRQ_JD2_FALL 13
#define MADERA_IRQ_MICD_CLAMP_RISE 14
#define MADERA_IRQ_MICD_CLAMP_FALL 15
#define MADERA_IRQ_DRC2_SIG_DET 16
#define MADERA_IRQ_DRC1_SIG_DET 17
#define MADERA_IRQ_ASRC1_IN1_LOCK 18
#define MADERA_IRQ_ASRC1_IN2_LOCK 19
#define MADERA_IRQ_ASRC2_IN1_LOCK 20
#define MADERA_IRQ_ASRC2_IN2_LOCK 21
#define MADERA_IRQ_DSP_IRQ1 22
#define MADERA_IRQ_DSP_IRQ2 23
#define MADERA_IRQ_DSP_IRQ3 24
#define MADERA_IRQ_DSP_IRQ4 25
#define MADERA_IRQ_DSP_IRQ5 26
#define MADERA_IRQ_DSP_IRQ6 27
#define MADERA_IRQ_DSP_IRQ7 28
#define MADERA_IRQ_DSP_IRQ8 29
#define MADERA_IRQ_DSP_IRQ9 30
#define MADERA_IRQ_DSP_IRQ10 31
#define MADERA_IRQ_DSP_IRQ11 32
#define MADERA_IRQ_DSP_IRQ12 33
#define MADERA_IRQ_DSP_IRQ13 34
#define MADERA_IRQ_DSP_IRQ14 35
#define MADERA_IRQ_DSP_IRQ15 36
#define MADERA_IRQ_DSP_IRQ16 37
#define MADERA_IRQ_HP1L_SC 38
#define MADERA_IRQ_HP1R_SC 39
#define MADERA_IRQ_HP2L_SC 40
#define MADERA_IRQ_HP2R_SC 41
#define MADERA_IRQ_HP3L_SC 42
#define MADERA_IRQ_HP3R_SC 43
#define MADERA_IRQ_SPKOUTL_SC 44
#define MADERA_IRQ_SPKOUTR_SC 45
#define MADERA_IRQ_HP1L_ENABLE_DONE 46
#define MADERA_IRQ_HP1R_ENABLE_DONE 47
#define MADERA_IRQ_HP2L_ENABLE_DONE 48
#define MADERA_IRQ_HP2R_ENABLE_DONE 49
#define MADERA_IRQ_HP3L_ENABLE_DONE 50
#define MADERA_IRQ_HP3R_ENABLE_DONE 51
#define MADERA_IRQ_SPKOUTL_ENABLE_DONE 52
#define MADERA_IRQ_SPKOUTR_ENABLE_DONE 53
#define MADERA_IRQ_SPK_SHUTDOWN 54
#define MADERA_IRQ_SPK_OVERHEAT 55
#define MADERA_IRQ_SPK_OVERHEAT_WARN 56
#define MADERA_IRQ_GPIO1 57
#define MADERA_IRQ_GPIO2 58
#define MADERA_IRQ_GPIO3 59
#define MADERA_IRQ_GPIO4 60
#define MADERA_IRQ_GPIO5 61
#define MADERA_IRQ_GPIO6 62
#define MADERA_IRQ_GPIO7 63
#define MADERA_IRQ_GPIO8 64
#define MADERA_IRQ_DSP1_BUS_ERR 65
#define MADERA_IRQ_DSP2_BUS_ERR 66
#define MADERA_IRQ_DSP3_BUS_ERR 67
#define MADERA_IRQ_DSP4_BUS_ERR 68
#define MADERA_IRQ_DSP5_BUS_ERR 69
#define MADERA_IRQ_DSP6_BUS_ERR 70
#define MADERA_IRQ_DSP7_BUS_ERR 71
#define MADERA_NUM_IRQ 72
/*
* These wrapper functions are for use by other child drivers of the
* same parent MFD.
*/
static inline int madera_get_irq_mapping(struct madera *madera, int irq)
{
if (!madera->irq_dev)
return -ENODEV;
return regmap_irq_get_virq(madera->irq_data, irq);
}
static inline int madera_request_irq(struct madera *madera, int irq,
const char *name,
irq_handler_t handler, void *data)
{
irq = madera_get_irq_mapping(madera, irq);
if (irq < 0)
return irq;
return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name,
data);
}
static inline void madera_free_irq(struct madera *madera, int irq, void *data)
{
irq = madera_get_irq_mapping(madera, irq);
if (irq < 0)
return;
free_irq(irq, data);
}
static inline int madera_set_irq_wake(struct madera *madera, int irq, int on)
{
irq = madera_get_irq_mapping(madera, irq);
if (irq < 0)
return irq;
return irq_set_irq_wake(irq, on);
}
#endif

View File

@ -116,6 +116,8 @@ struct msi_desc {
list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list)
#define for_each_msi_entry(desc, dev) \
list_for_each_entry((desc), dev_to_msi_list((dev)), list)
#define for_each_msi_entry_safe(desc, tmp, dev) \
list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list)
#ifdef CONFIG_PCI_MSI
#define first_pci_msi_entry(pdev) first_msi_entry(&(pdev)->dev)

View File

@ -34,9 +34,20 @@ static struct irq_chip irq_sim_irqchip = {
static void irq_sim_handle_irq(struct irq_work *work)
{
struct irq_sim_work_ctx *work_ctx;
unsigned int offset = 0;
struct irq_sim *sim;
int irqnum;
work_ctx = container_of(work, struct irq_sim_work_ctx, work);
handle_simple_irq(irq_to_desc(work_ctx->irq));
sim = container_of(work_ctx, struct irq_sim, work_ctx);
while (!bitmap_empty(work_ctx->pending, sim->irq_count)) {
offset = find_next_bit(work_ctx->pending,
sim->irq_count, offset);
clear_bit(offset, work_ctx->pending);
irqnum = irq_sim_irqnum(sim, offset);
handle_simple_irq(irq_to_desc(irqnum));
}
}
/**
@ -63,6 +74,13 @@ int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs)
return sim->irq_base;
}
sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
if (!sim->work_ctx.pending) {
kfree(sim->irqs);
irq_free_descs(sim->irq_base, num_irqs);
return -ENOMEM;
}
for (i = 0; i < num_irqs; i++) {
sim->irqs[i].irqnum = sim->irq_base + i;
sim->irqs[i].enabled = false;
@ -89,6 +107,7 @@ EXPORT_SYMBOL_GPL(irq_sim_init);
void irq_sim_fini(struct irq_sim *sim)
{
irq_work_sync(&sim->work_ctx.work);
bitmap_free(sim->work_ctx.pending);
irq_free_descs(sim->irq_base, sim->irq_count);
kfree(sim->irqs);
}
@ -143,7 +162,7 @@ EXPORT_SYMBOL_GPL(devm_irq_sim_init);
void irq_sim_fire(struct irq_sim *sim, unsigned int offset)
{
if (sim->irqs[offset].enabled) {
sim->work_ctx.irq = irq_sim_irqnum(sim, offset);
set_bit(offset, sim->work_ctx.pending);
irq_work_queue(&sim->work_ctx.work);
}
}