irqchip core changes for v4.6 (round 2)
- mvebu: - Add odmi driver for Marvell 7K/8K SoCs - Replace driver-specific set_affinity with generic version - mips: - Move ath79 MISC and CPU drivers from arch/ code to irqchip/ - tango: - Add support for Sigma Designs SMP8[67]xx ctrl -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWyc/LAAoJEP45WPkGe8ZnUFUP/2IIftSnSACokXIcXjLPNJ+X YPtBSpxAjDGM1btHOFqeUvf/FEeFKbYvs57SSHA9WfjQSOuEfZInoeiOwEYIBtBp VYWRsQowVcBPre3jJLlG8Lf+MooC/bkAdesNSefdwvzT5yiYrscR8dFuJ3yG5SbI uu+YoDPMJhlDW0ePOJbWc+8ZoS5Y6CVpcHwO7AwI4uU9xSkZgOIa/P63QCTO7PI9 4Mh835Qngvpvyv1El3C6cKZBBzSulDYqPzFuoc2MV2TeOlwYFVavMChC7yOmfy+E tOJNtWnMBOGQfPWpLjJ0sPz0rqYTRPDuspcp6/sbuAmeAZq+iwU00cueNPd3n26b 7WgolO0uoMb0XHooXFeaL9EkDVOk+GZkCnisaytgrGDKZi4uUzFgiAxzMSEWfKTs XKavNbAKaUOldjkvfnbGUcytBU9DhhmKrnIqJtTwLbgnmD9mZdk/NyJrooHkROze G565vhJkYJk45kRXORJdeZ8BlwosSDqtPuzS6htn3LZo9H4bVWPigPbjtRxPHdSJ cRsA2op2GssGKIoreDx1do2Lm17K2oSI+VftrgG2vmzsvDr9RugqF1gPZ8B/ow5e ye8yhmlkRI5iIlAqFq5DaFUpVuY8PbBUe9RDOjRx4UInVGrniCmWpDNHhPEhMEuk rUounoqu2SxduQKuwGus =4dMI -----END PGP SIGNATURE----- Merge tag 'irqchip-core-4.6-2' of git://git.infradead.org/users/jcooper/linux into irq/core Pull the second round of irqchip core changes for v4.6 from Jason Cooper: - mvebu: - Add odmi driver for Marvell 7K/8K SoCs - Replace driver-specific set_affinity with generic version - mips: - Move ath79 MISC and CPU drivers from arch/ code to irqchip/ - tango: - Add support for Sigma Designs SMP8[67]xx ctrl
This commit is contained in:
Коммит
16aba533fb
|
@ -0,0 +1,41 @@
|
|||
|
||||
* Marvell ODMI for MSI support
|
||||
|
||||
Some Marvell SoCs have an On-Die Message Interrupt (ODMI) controller
|
||||
which can be used by on-board peripheral for MSI interrupts.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : The value here should contain "marvell,odmi-controller".
|
||||
|
||||
- interrupt,controller : Identifies the node as an interrupt controller.
|
||||
|
||||
- msi-controller : Identifies the node as an MSI controller.
|
||||
|
||||
- marvell,odmi-frames : Number of ODMI frames available. Each frame
|
||||
provides a number of events.
|
||||
|
||||
- reg : List of register definitions, one for each
|
||||
ODMI frame.
|
||||
|
||||
- marvell,spi-base : List of GIC base SPI interrupts, one for each
|
||||
ODMI frame. Those SPI interrupts are 0-based,
|
||||
i.e marvell,spi-base = <128> will use SPI #96.
|
||||
See Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
|
||||
for details about the GIC Device Tree binding.
|
||||
|
||||
- interrupt-parent : Reference to the parent interrupt controller.
|
||||
|
||||
Example:
|
||||
|
||||
odmi: odmi@300000 {
|
||||
compatible = "marvell,odmi-controller";
|
||||
interrupt-controller;
|
||||
msi-controller;
|
||||
marvell,odmi-frames = <4>;
|
||||
reg = <0x300000 0x4000>,
|
||||
<0x304000 0x4000>,
|
||||
<0x308000 0x4000>,
|
||||
<0x30C000 0x4000>;
|
||||
marvell,spi-base = <128>, <136>, <144>, <152>;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
Sigma Designs SMP86xx/SMP87xx secondary interrupt controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "sigma,smp8642-intc"
|
||||
- reg: physical address of MMIO region
|
||||
- ranges: address space mapping of child nodes
|
||||
- interrupt-parent: phandle of parent interrupt controller
|
||||
- interrupt-controller: boolean
|
||||
- #address-cells: should be <1>
|
||||
- #size-cells: should be <1>
|
||||
|
||||
One child node per control block with properties:
|
||||
- reg: address of registers for this control block
|
||||
- interrupt-controller: boolean
|
||||
- #interrupt-cells: should be <2>, interrupt index and flags per interrupts.txt
|
||||
- interrupts: interrupt spec of primary interrupt controller
|
||||
|
||||
Example:
|
||||
|
||||
interrupt-controller@6e000 {
|
||||
compatible = "sigma,smp8642-intc";
|
||||
reg = <0x6e000 0x400>;
|
||||
ranges = <0x0 0x6e000 0x400>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupt-controller;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
irq0: interrupt-controller@0 {
|
||||
reg = <0x000 0x100>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
irq1: interrupt-controller@100 {
|
||||
reg = <0x100 0x100>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
irq2: interrupt-controller@300 {
|
||||
reg = <0x300 0x100>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
|
@ -26,90 +26,6 @@
|
|||
#include "common.h"
|
||||
#include "machtypes.h"
|
||||
|
||||
static void __init ath79_misc_intc_domain_init(
|
||||
struct device_node *node, int irq);
|
||||
|
||||
static void ath79_misc_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_domain *domain = irq_desc_get_handler_data(desc);
|
||||
void __iomem *base = domain->host_data;
|
||||
u32 pending;
|
||||
|
||||
pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
|
||||
if (!pending) {
|
||||
spurious_interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
while (pending) {
|
||||
int bit = __ffs(pending);
|
||||
|
||||
generic_handle_irq(irq_linear_revmap(domain, bit));
|
||||
pending &= ~BIT(bit);
|
||||
}
|
||||
}
|
||||
|
||||
static void ar71xx_misc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
void __iomem *base = irq_data_get_irq_chip_data(d);
|
||||
unsigned int irq = d->hwirq;
|
||||
u32 t;
|
||||
|
||||
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
|
||||
/* flush write */
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void ar71xx_misc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
void __iomem *base = irq_data_get_irq_chip_data(d);
|
||||
unsigned int irq = d->hwirq;
|
||||
u32 t;
|
||||
|
||||
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
|
||||
/* flush write */
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void ar724x_misc_irq_ack(struct irq_data *d)
|
||||
{
|
||||
void __iomem *base = irq_data_get_irq_chip_data(d);
|
||||
unsigned int irq = d->hwirq;
|
||||
u32 t;
|
||||
|
||||
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
|
||||
/* flush write */
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
}
|
||||
|
||||
static struct irq_chip ath79_misc_irq_chip = {
|
||||
.name = "MISC",
|
||||
.irq_unmask = ar71xx_misc_irq_unmask,
|
||||
.irq_mask = ar71xx_misc_irq_mask,
|
||||
};
|
||||
|
||||
static void __init ath79_misc_irq_init(void)
|
||||
{
|
||||
if (soc_is_ar71xx() || soc_is_ar913x())
|
||||
ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
|
||||
else if (soc_is_ar724x() ||
|
||||
soc_is_ar933x() ||
|
||||
soc_is_ar934x() ||
|
||||
soc_is_qca955x())
|
||||
ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
|
||||
else
|
||||
BUG();
|
||||
|
||||
ath79_misc_intc_domain_init(NULL, ATH79_CPU_IRQ(6));
|
||||
}
|
||||
|
||||
static void ar934x_ip2_irq_dispatch(struct irq_desc *desc)
|
||||
{
|
||||
|
@ -212,142 +128,12 @@ static void qca955x_irq_init(void)
|
|||
irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch);
|
||||
}
|
||||
|
||||
/*
|
||||
* The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
|
||||
* these devices typically allocate coherent DMA memory, however the
|
||||
* DMA controller may still have some unsynchronized data in the FIFO.
|
||||
* Issue a flush in the handlers to ensure that the driver sees
|
||||
* the update.
|
||||
*
|
||||
* This array map the interrupt lines to the DDR write buffer channels.
|
||||
*/
|
||||
|
||||
static unsigned irq_wb_chan[8] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
asmlinkage void plat_irq_dispatch(void)
|
||||
{
|
||||
unsigned long pending;
|
||||
int irq;
|
||||
|
||||
pending = read_c0_status() & read_c0_cause() & ST0_IM;
|
||||
|
||||
if (!pending) {
|
||||
spurious_interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
pending >>= CAUSEB_IP;
|
||||
while (pending) {
|
||||
irq = fls(pending) - 1;
|
||||
if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
|
||||
ath79_ddr_wb_flush(irq_wb_chan[irq]);
|
||||
do_IRQ(MIPS_CPU_IRQ_BASE + irq);
|
||||
pending &= ~BIT(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq);
|
||||
irq_set_chip_data(irq, d->host_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops misc_irq_domain_ops = {
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
.map = misc_map,
|
||||
};
|
||||
|
||||
static void __init ath79_misc_intc_domain_init(
|
||||
struct device_node *node, int irq)
|
||||
{
|
||||
void __iomem *base = ath79_reset_base;
|
||||
struct irq_domain *domain;
|
||||
|
||||
domain = irq_domain_add_legacy(node, ATH79_MISC_IRQ_COUNT,
|
||||
ATH79_MISC_IRQ_BASE, 0, &misc_irq_domain_ops, base);
|
||||
if (!domain)
|
||||
panic("Failed to add MISC irqdomain");
|
||||
|
||||
/* Disable and clear all interrupts */
|
||||
__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
|
||||
irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain);
|
||||
}
|
||||
|
||||
static int __init ath79_misc_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
int irq;
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (!irq)
|
||||
panic("Failed to get MISC IRQ");
|
||||
|
||||
ath79_misc_intc_domain_init(node, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ar7100_misc_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
|
||||
return ath79_misc_intc_of_init(node, parent);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc",
|
||||
ar7100_misc_intc_of_init);
|
||||
|
||||
static int __init ar7240_misc_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
|
||||
return ath79_misc_intc_of_init(node, parent);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc",
|
||||
ar7240_misc_intc_of_init);
|
||||
|
||||
static int __init ar79_cpu_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
int err, i, count;
|
||||
|
||||
/* Fill the irq_wb_chan table */
|
||||
count = of_count_phandle_with_args(
|
||||
node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells");
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct of_phandle_args args;
|
||||
u32 irq = i;
|
||||
|
||||
of_property_read_u32_index(
|
||||
node, "qca,ddr-wb-channel-interrupts", i, &irq);
|
||||
if (irq >= ARRAY_SIZE(irq_wb_chan))
|
||||
continue;
|
||||
|
||||
err = of_parse_phandle_with_args(
|
||||
node, "qca,ddr-wb-channels",
|
||||
"#qca,ddr-wb-channel-cells",
|
||||
i, &args);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq_wb_chan[irq] = args.args[0];
|
||||
pr_info("IRQ: Set flush channel of IRQ%d to %d\n",
|
||||
irq, args.args[0]);
|
||||
}
|
||||
|
||||
return mips_cpu_irq_of_init(node, parent);
|
||||
}
|
||||
IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
|
||||
ar79_cpu_intc_of_init);
|
||||
|
||||
void __init arch_init_irq(void)
|
||||
{
|
||||
unsigned irq_wb_chan2 = -1;
|
||||
unsigned irq_wb_chan3 = -1;
|
||||
bool misc_is_ar71xx;
|
||||
|
||||
if (mips_machtype == ATH79_MACH_GENERIC_OF) {
|
||||
irqchip_init();
|
||||
return;
|
||||
|
@ -355,14 +141,26 @@ void __init arch_init_irq(void)
|
|||
|
||||
if (soc_is_ar71xx() || soc_is_ar724x() ||
|
||||
soc_is_ar913x() || soc_is_ar933x()) {
|
||||
irq_wb_chan[2] = 3;
|
||||
irq_wb_chan[3] = 2;
|
||||
irq_wb_chan2 = 3;
|
||||
irq_wb_chan3 = 2;
|
||||
} else if (soc_is_ar934x()) {
|
||||
irq_wb_chan[3] = 2;
|
||||
irq_wb_chan3 = 2;
|
||||
}
|
||||
|
||||
mips_cpu_irq_init();
|
||||
ath79_misc_irq_init();
|
||||
ath79_cpu_irq_init(irq_wb_chan2, irq_wb_chan3);
|
||||
|
||||
if (soc_is_ar71xx() || soc_is_ar913x())
|
||||
misc_is_ar71xx = true;
|
||||
else if (soc_is_ar724x() ||
|
||||
soc_is_ar933x() ||
|
||||
soc_is_ar934x() ||
|
||||
soc_is_qca955x())
|
||||
misc_is_ar71xx = false;
|
||||
else
|
||||
BUG();
|
||||
ath79_misc_irq_init(
|
||||
ath79_reset_base + AR71XX_RESET_REG_MISC_INT_STATUS,
|
||||
ATH79_CPU_IRQ(6), ATH79_MISC_IRQ_BASE, misc_is_ar71xx);
|
||||
|
||||
if (soc_is_ar934x())
|
||||
ar934x_ip2_irq_init();
|
||||
|
|
|
@ -144,4 +144,8 @@ static inline u32 ath79_reset_rr(unsigned reg)
|
|||
void ath79_device_reset_set(u32 mask);
|
||||
void ath79_device_reset_clear(u32 mask);
|
||||
|
||||
void ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3);
|
||||
void ath79_misc_irq_init(void __iomem *regs, int irq,
|
||||
int irq_base, bool is_ar71xx);
|
||||
|
||||
#endif /* __ASM_MACH_ATH79_H */
|
||||
|
|
|
@ -161,6 +161,11 @@ config ST_IRQCHIP
|
|||
help
|
||||
Enables SysCfg Controlled IRQs on STi based platforms.
|
||||
|
||||
config TANGO_IRQ
|
||||
bool
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_CHIP
|
||||
|
||||
config TB10X_IRQC
|
||||
bool
|
||||
select IRQ_DOMAIN
|
||||
|
@ -229,3 +234,7 @@ config IRQ_MXS
|
|||
def_bool y if MACH_ASM9260 || ARCH_MXS
|
||||
select IRQ_DOMAIN
|
||||
select STMP_DEVICE
|
||||
|
||||
config MVEBU_ODMI
|
||||
bool
|
||||
select GENERIC_MSI_IRQ_DOMAIN
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
obj-$(CONFIG_IRQCHIP) += irqchip.o
|
||||
|
||||
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_EXYNOS) += exynos-combiner.o
|
||||
|
@ -40,6 +42,7 @@ obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
|
|||
obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
|
||||
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
|
||||
obj-$(CONFIG_ST_IRQCHIP) += irq-st.o
|
||||
obj-$(CONFIG_TANGO_IRQ) += irq-tango.o
|
||||
obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
|
||||
obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o
|
||||
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
|
||||
|
@ -60,3 +63,4 @@ obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o
|
|||
obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
|
||||
obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
|
||||
obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
|
||||
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Atheros AR71xx/AR724x/AR913x specific interrupt handling
|
||||
*
|
||||
* Copyright (C) 2015 Alban Bedel <albeu@free.fr>
|
||||
* Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
|
||||
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
*
|
||||
* Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/irq_cpu.h>
|
||||
#include <asm/mach-ath79/ath79.h>
|
||||
|
||||
/*
|
||||
* The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
|
||||
* these devices typically allocate coherent DMA memory, however the
|
||||
* DMA controller may still have some unsynchronized data in the FIFO.
|
||||
* Issue a flush in the handlers to ensure that the driver sees
|
||||
* the update.
|
||||
*
|
||||
* This array map the interrupt lines to the DDR write buffer channels.
|
||||
*/
|
||||
|
||||
static unsigned irq_wb_chan[8] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
asmlinkage void plat_irq_dispatch(void)
|
||||
{
|
||||
unsigned long pending;
|
||||
int irq;
|
||||
|
||||
pending = read_c0_status() & read_c0_cause() & ST0_IM;
|
||||
|
||||
if (!pending) {
|
||||
spurious_interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
pending >>= CAUSEB_IP;
|
||||
while (pending) {
|
||||
irq = fls(pending) - 1;
|
||||
if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
|
||||
ath79_ddr_wb_flush(irq_wb_chan[irq]);
|
||||
do_IRQ(MIPS_CPU_IRQ_BASE + irq);
|
||||
pending &= ~BIT(irq);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init ar79_cpu_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
int err, i, count;
|
||||
|
||||
/* Fill the irq_wb_chan table */
|
||||
count = of_count_phandle_with_args(
|
||||
node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells");
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct of_phandle_args args;
|
||||
u32 irq = i;
|
||||
|
||||
of_property_read_u32_index(
|
||||
node, "qca,ddr-wb-channel-interrupts", i, &irq);
|
||||
if (irq >= ARRAY_SIZE(irq_wb_chan))
|
||||
continue;
|
||||
|
||||
err = of_parse_phandle_with_args(
|
||||
node, "qca,ddr-wb-channels",
|
||||
"#qca,ddr-wb-channel-cells",
|
||||
i, &args);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq_wb_chan[irq] = args.args[0];
|
||||
}
|
||||
|
||||
return mips_cpu_irq_of_init(node, parent);
|
||||
}
|
||||
IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
|
||||
ar79_cpu_intc_of_init);
|
||||
|
||||
void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3)
|
||||
{
|
||||
irq_wb_chan[2] = irq_wb_chan2;
|
||||
irq_wb_chan[3] = irq_wb_chan3;
|
||||
mips_cpu_irq_init();
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Atheros AR71xx/AR724x/AR913x MISC interrupt controller
|
||||
*
|
||||
* Copyright (C) 2015 Alban Bedel <albeu@free.fr>
|
||||
* Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
|
||||
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
*
|
||||
* Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#define AR71XX_RESET_REG_MISC_INT_STATUS 0
|
||||
#define AR71XX_RESET_REG_MISC_INT_ENABLE 4
|
||||
|
||||
#define ATH79_MISC_IRQ_COUNT 32
|
||||
|
||||
static void ath79_misc_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_domain *domain = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
void __iomem *base = domain->host_data;
|
||||
u32 pending;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
|
||||
if (!pending) {
|
||||
spurious_interrupt();
|
||||
chained_irq_exit(chip, desc);
|
||||
return;
|
||||
}
|
||||
|
||||
while (pending) {
|
||||
int bit = __ffs(pending);
|
||||
|
||||
generic_handle_irq(irq_linear_revmap(domain, bit));
|
||||
pending &= ~BIT(bit);
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void ar71xx_misc_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
void __iomem *base = irq_data_get_irq_chip_data(d);
|
||||
unsigned int irq = d->hwirq;
|
||||
u32 t;
|
||||
|
||||
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
__raw_writel(t | BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
|
||||
/* flush write */
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void ar71xx_misc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
void __iomem *base = irq_data_get_irq_chip_data(d);
|
||||
unsigned int irq = d->hwirq;
|
||||
u32 t;
|
||||
|
||||
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
__raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
|
||||
/* flush write */
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
}
|
||||
|
||||
static void ar724x_misc_irq_ack(struct irq_data *d)
|
||||
{
|
||||
void __iomem *base = irq_data_get_irq_chip_data(d);
|
||||
unsigned int irq = d->hwirq;
|
||||
u32 t;
|
||||
|
||||
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
__raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
|
||||
/* flush write */
|
||||
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
}
|
||||
|
||||
static struct irq_chip ath79_misc_irq_chip = {
|
||||
.name = "MISC",
|
||||
.irq_unmask = ar71xx_misc_irq_unmask,
|
||||
.irq_mask = ar71xx_misc_irq_mask,
|
||||
};
|
||||
|
||||
static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq);
|
||||
irq_set_chip_data(irq, d->host_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops misc_irq_domain_ops = {
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
.map = misc_map,
|
||||
};
|
||||
|
||||
static void __init ath79_misc_intc_domain_init(
|
||||
struct irq_domain *domain, int irq)
|
||||
{
|
||||
void __iomem *base = domain->host_data;
|
||||
|
||||
/* Disable and clear all interrupts */
|
||||
__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
|
||||
__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
|
||||
|
||||
irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain);
|
||||
}
|
||||
|
||||
static int __init ath79_misc_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (!irq) {
|
||||
pr_err("Failed to get MISC IRQ\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("Failed to get MISC IRQ registers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
domain = irq_domain_add_linear(node, ATH79_MISC_IRQ_COUNT,
|
||||
&misc_irq_domain_ops, base);
|
||||
if (!domain) {
|
||||
pr_err("Failed to add MISC irqdomain\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ath79_misc_intc_domain_init(domain, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ar7100_misc_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
|
||||
return ath79_misc_intc_of_init(node, parent);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc",
|
||||
ar7100_misc_intc_of_init);
|
||||
|
||||
static int __init ar7240_misc_intc_of_init(
|
||||
struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
|
||||
return ath79_misc_intc_of_init(node, parent);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc",
|
||||
ar7240_misc_intc_of_init);
|
||||
|
||||
void __init ath79_misc_irq_init(void __iomem *regs, int irq,
|
||||
int irq_base, bool is_ar71xx)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (is_ar71xx)
|
||||
ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
|
||||
else
|
||||
ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
|
||||
|
||||
domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT,
|
||||
irq_base, 0, &misc_irq_domain_ops, regs);
|
||||
if (!domain)
|
||||
panic("Failed to create MISC irqdomain");
|
||||
|
||||
ath79_misc_intc_domain_init(domain, irq);
|
||||
}
|
|
@ -92,18 +92,6 @@ static struct msi_domain_info gicv2m_msi_domain_info = {
|
|||
.chip = &gicv2m_msi_irq_chip,
|
||||
};
|
||||
|
||||
static int gicv2m_set_affinity(struct irq_data *irq_data,
|
||||
const struct cpumask *mask, bool force)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = irq_chip_set_affinity_parent(irq_data, mask, force);
|
||||
if (ret == IRQ_SET_MASK_OK)
|
||||
ret = IRQ_SET_MASK_OK_DONE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
|
||||
|
@ -122,7 +110,7 @@ static struct irq_chip gicv2m_irq_chip = {
|
|||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_set_affinity = gicv2m_set_affinity,
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
.irq_compose_msi_msg = gicv2m_compose_msi_msg,
|
||||
};
|
||||
|
||||
|
|
|
@ -319,7 +319,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
|
|||
writel_relaxed(val | bit, reg);
|
||||
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
|
||||
return IRQ_SET_MASK_OK;
|
||||
return IRQ_SET_MASK_OK_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Marvell
|
||||
*
|
||||
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "GIC-ODMI: " fmt
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
#define GICP_ODMIN_SET 0x40
|
||||
#define GICP_ODMI_INT_NUM_SHIFT 12
|
||||
#define GICP_ODMIN_GM_EP_R0 0x110
|
||||
#define GICP_ODMIN_GM_EP_R1 0x114
|
||||
#define GICP_ODMIN_GM_EA_R0 0x108
|
||||
#define GICP_ODMIN_GM_EA_R1 0x118
|
||||
|
||||
/*
|
||||
* We don't support the group events, so we simply have 8 interrupts
|
||||
* per frame.
|
||||
*/
|
||||
#define NODMIS_SHIFT 3
|
||||
#define NODMIS_PER_FRAME (1 << NODMIS_SHIFT)
|
||||
#define NODMIS_MASK (NODMIS_PER_FRAME - 1)
|
||||
|
||||
struct odmi_data {
|
||||
struct resource res;
|
||||
void __iomem *base;
|
||||
unsigned int spi_base;
|
||||
};
|
||||
|
||||
static struct odmi_data *odmis;
|
||||
static unsigned long *odmis_bm;
|
||||
static unsigned int odmis_count;
|
||||
|
||||
/* Protects odmis_bm */
|
||||
static DEFINE_SPINLOCK(odmis_bm_lock);
|
||||
|
||||
static void odmi_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
|
||||
{
|
||||
struct odmi_data *odmi;
|
||||
phys_addr_t addr;
|
||||
unsigned int odmin;
|
||||
|
||||
if (WARN_ON(d->hwirq >= odmis_count * NODMIS_PER_FRAME))
|
||||
return;
|
||||
|
||||
odmi = &odmis[d->hwirq >> NODMIS_SHIFT];
|
||||
odmin = d->hwirq & NODMIS_MASK;
|
||||
|
||||
addr = odmi->res.start + GICP_ODMIN_SET;
|
||||
|
||||
msg->address_hi = upper_32_bits(addr);
|
||||
msg->address_lo = lower_32_bits(addr);
|
||||
msg->data = odmin << GICP_ODMI_INT_NUM_SHIFT;
|
||||
}
|
||||
|
||||
static struct irq_chip odmi_irq_chip = {
|
||||
.name = "ODMI",
|
||||
.irq_mask = irq_chip_mask_parent,
|
||||
.irq_unmask = irq_chip_unmask_parent,
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
.irq_compose_msi_msg = odmi_compose_msi_msg,
|
||||
};
|
||||
|
||||
static int odmi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct odmi_data *odmi = NULL;
|
||||
struct irq_fwspec fwspec;
|
||||
struct irq_data *d;
|
||||
unsigned int hwirq, odmin;
|
||||
int ret;
|
||||
|
||||
spin_lock(&odmis_bm_lock);
|
||||
hwirq = find_first_zero_bit(odmis_bm, NODMIS_PER_FRAME * odmis_count);
|
||||
if (hwirq >= NODMIS_PER_FRAME * odmis_count) {
|
||||
spin_unlock(&odmis_bm_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
__set_bit(hwirq, odmis_bm);
|
||||
spin_unlock(&odmis_bm_lock);
|
||||
|
||||
odmi = &odmis[hwirq >> NODMIS_SHIFT];
|
||||
odmin = hwirq & NODMIS_MASK;
|
||||
|
||||
fwspec.fwnode = domain->parent->fwnode;
|
||||
fwspec.param_count = 3;
|
||||
fwspec.param[0] = GIC_SPI;
|
||||
fwspec.param[1] = odmi->spi_base - 32 + odmin;
|
||||
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
|
||||
|
||||
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
|
||||
if (ret) {
|
||||
pr_err("Cannot allocate parent IRQ\n");
|
||||
spin_lock(&odmis_bm_lock);
|
||||
__clear_bit(odmin, odmis_bm);
|
||||
spin_unlock(&odmis_bm_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure the interrupt line to be edge */
|
||||
d = irq_domain_get_irq_data(domain->parent, virq);
|
||||
d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
|
||||
|
||||
irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
||||
&odmi_irq_chip, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void odmi_irq_domain_free(struct irq_domain *domain,
|
||||
unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
|
||||
if (d->hwirq >= odmis_count * NODMIS_PER_FRAME) {
|
||||
pr_err("Failed to teardown msi. Invalid hwirq %lu\n", d->hwirq);
|
||||
return;
|
||||
}
|
||||
|
||||
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
|
||||
|
||||
/* Actually free the MSI */
|
||||
spin_lock(&odmis_bm_lock);
|
||||
__clear_bit(d->hwirq, odmis_bm);
|
||||
spin_unlock(&odmis_bm_lock);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops odmi_domain_ops = {
|
||||
.alloc = odmi_irq_domain_alloc,
|
||||
.free = odmi_irq_domain_free,
|
||||
};
|
||||
|
||||
static struct irq_chip odmi_msi_irq_chip = {
|
||||
.name = "ODMI",
|
||||
};
|
||||
|
||||
static struct msi_domain_ops odmi_msi_ops = {
|
||||
};
|
||||
|
||||
static struct msi_domain_info odmi_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
|
||||
.ops = &odmi_msi_ops,
|
||||
.chip = &odmi_msi_irq_chip,
|
||||
};
|
||||
|
||||
static int __init mvebu_odmi_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *inner_domain, *plat_domain;
|
||||
int ret, i;
|
||||
|
||||
if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
|
||||
return -EINVAL;
|
||||
|
||||
odmis = kcalloc(odmis_count, sizeof(struct odmi_data), GFP_KERNEL);
|
||||
if (!odmis)
|
||||
return -ENOMEM;
|
||||
|
||||
odmis_bm = kcalloc(BITS_TO_LONGS(odmis_count * NODMIS_PER_FRAME),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
if (!odmis_bm) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
for (i = 0; i < odmis_count; i++) {
|
||||
struct odmi_data *odmi = &odmis[i];
|
||||
|
||||
ret = of_address_to_resource(node, i, &odmi->res);
|
||||
if (ret)
|
||||
goto err_unmap;
|
||||
|
||||
odmi->base = of_io_request_and_map(node, i, "odmi");
|
||||
if (IS_ERR(odmi->base)) {
|
||||
ret = PTR_ERR(odmi->base);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_index(node, "marvell,spi-base",
|
||||
i, &odmi->spi_base)) {
|
||||
ret = -EINVAL;
|
||||
goto err_unmap;
|
||||
}
|
||||
}
|
||||
|
||||
inner_domain = irq_domain_create_linear(of_node_to_fwnode(node),
|
||||
odmis_count * NODMIS_PER_FRAME,
|
||||
&odmi_domain_ops, NULL);
|
||||
if (!inner_domain) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
inner_domain->parent = irq_find_host(parent);
|
||||
|
||||
plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
|
||||
&odmi_msi_domain_info,
|
||||
inner_domain);
|
||||
if (!plat_domain) {
|
||||
ret = -ENOMEM;
|
||||
goto err_remove_inner;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_inner:
|
||||
irq_domain_remove(inner_domain);
|
||||
err_unmap:
|
||||
for (i = 0; i < odmis_count; i++) {
|
||||
struct odmi_data *odmi = &odmis[i];
|
||||
|
||||
if (odmi->base && !IS_ERR(odmi->base))
|
||||
iounmap(odmis[i].base);
|
||||
}
|
||||
kfree(odmis_bm);
|
||||
err_alloc:
|
||||
kfree(odmis);
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(mvebu_odmi, "marvell,odmi-controller", mvebu_odmi_init);
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Mans Rullgard <mans@mansr.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define IRQ0_CTL_BASE 0x0000
|
||||
#define IRQ1_CTL_BASE 0x0100
|
||||
#define EDGE_CTL_BASE 0x0200
|
||||
#define IRQ2_CTL_BASE 0x0300
|
||||
|
||||
#define IRQ_CTL_HI 0x18
|
||||
#define EDGE_CTL_HI 0x20
|
||||
|
||||
#define IRQ_STATUS 0x00
|
||||
#define IRQ_RAWSTAT 0x04
|
||||
#define IRQ_EN_SET 0x08
|
||||
#define IRQ_EN_CLR 0x0c
|
||||
#define IRQ_SOFT_SET 0x10
|
||||
#define IRQ_SOFT_CLR 0x14
|
||||
|
||||
#define EDGE_STATUS 0x00
|
||||
#define EDGE_RAWSTAT 0x04
|
||||
#define EDGE_CFG_RISE 0x08
|
||||
#define EDGE_CFG_FALL 0x0c
|
||||
#define EDGE_CFG_RISE_SET 0x10
|
||||
#define EDGE_CFG_RISE_CLR 0x14
|
||||
#define EDGE_CFG_FALL_SET 0x18
|
||||
#define EDGE_CFG_FALL_CLR 0x1c
|
||||
|
||||
struct tangox_irq_chip {
|
||||
void __iomem *base;
|
||||
unsigned long ctl;
|
||||
};
|
||||
|
||||
static inline u32 intc_readl(struct tangox_irq_chip *chip, int reg)
|
||||
{
|
||||
return readl_relaxed(chip->base + reg);
|
||||
}
|
||||
|
||||
static inline void intc_writel(struct tangox_irq_chip *chip, int reg, u32 val)
|
||||
{
|
||||
writel_relaxed(val, chip->base + reg);
|
||||
}
|
||||
|
||||
static void tangox_dispatch_irqs(struct irq_domain *dom, unsigned int status,
|
||||
int base)
|
||||
{
|
||||
unsigned int hwirq;
|
||||
unsigned int virq;
|
||||
|
||||
while (status) {
|
||||
hwirq = __ffs(status);
|
||||
virq = irq_find_mapping(dom, base + hwirq);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
status &= ~BIT(hwirq);
|
||||
}
|
||||
}
|
||||
|
||||
static void tangox_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_domain *dom = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *host_chip = irq_desc_get_chip(desc);
|
||||
struct tangox_irq_chip *chip = dom->host_data;
|
||||
unsigned int status_lo, status_hi;
|
||||
|
||||
chained_irq_enter(host_chip, desc);
|
||||
|
||||
status_lo = intc_readl(chip, chip->ctl + IRQ_STATUS);
|
||||
status_hi = intc_readl(chip, chip->ctl + IRQ_CTL_HI + IRQ_STATUS);
|
||||
|
||||
tangox_dispatch_irqs(dom, status_lo, 0);
|
||||
tangox_dispatch_irqs(dom, status_hi, 32);
|
||||
|
||||
chained_irq_exit(host_chip, desc);
|
||||
}
|
||||
|
||||
static int tangox_irq_set_type(struct irq_data *d, unsigned int flow_type)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct tangox_irq_chip *chip = gc->domain->host_data;
|
||||
struct irq_chip_regs *regs = &gc->chip_types[0].regs;
|
||||
|
||||
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
intc_writel(chip, regs->type + EDGE_CFG_RISE_SET, d->mask);
|
||||
intc_writel(chip, regs->type + EDGE_CFG_FALL_CLR, d->mask);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
intc_writel(chip, regs->type + EDGE_CFG_RISE_CLR, d->mask);
|
||||
intc_writel(chip, regs->type + EDGE_CFG_FALL_SET, d->mask);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
intc_writel(chip, regs->type + EDGE_CFG_RISE_CLR, d->mask);
|
||||
intc_writel(chip, regs->type + EDGE_CFG_FALL_CLR, d->mask);
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
intc_writel(chip, regs->type + EDGE_CFG_RISE_SET, d->mask);
|
||||
intc_writel(chip, regs->type + EDGE_CFG_FALL_SET, d->mask);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("Invalid trigger mode %x for IRQ %d\n",
|
||||
flow_type, d->irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return irq_setup_alt_chip(d, flow_type);
|
||||
}
|
||||
|
||||
static void __init tangox_irq_init_chip(struct irq_chip_generic *gc,
|
||||
unsigned long ctl_offs,
|
||||
unsigned long edge_offs)
|
||||
{
|
||||
struct tangox_irq_chip *chip = gc->domain->host_data;
|
||||
struct irq_chip_type *ct = gc->chip_types;
|
||||
unsigned long ctl_base = chip->ctl + ctl_offs;
|
||||
unsigned long edge_base = EDGE_CTL_BASE + edge_offs;
|
||||
int i;
|
||||
|
||||
gc->reg_base = chip->base;
|
||||
gc->unused = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ct[i].chip.irq_ack = irq_gc_ack_set_bit;
|
||||
ct[i].chip.irq_mask = irq_gc_mask_disable_reg;
|
||||
ct[i].chip.irq_mask_ack = irq_gc_mask_disable_reg_and_ack;
|
||||
ct[i].chip.irq_unmask = irq_gc_unmask_enable_reg;
|
||||
ct[i].chip.irq_set_type = tangox_irq_set_type;
|
||||
ct[i].chip.name = gc->domain->name;
|
||||
|
||||
ct[i].regs.enable = ctl_base + IRQ_EN_SET;
|
||||
ct[i].regs.disable = ctl_base + IRQ_EN_CLR;
|
||||
ct[i].regs.ack = edge_base + EDGE_RAWSTAT;
|
||||
ct[i].regs.type = edge_base;
|
||||
}
|
||||
|
||||
ct[0].type = IRQ_TYPE_LEVEL_MASK;
|
||||
ct[0].handler = handle_level_irq;
|
||||
|
||||
ct[1].type = IRQ_TYPE_EDGE_BOTH;
|
||||
ct[1].handler = handle_edge_irq;
|
||||
|
||||
intc_writel(chip, ct->regs.disable, 0xffffffff);
|
||||
intc_writel(chip, ct->regs.ack, 0xffffffff);
|
||||
}
|
||||
|
||||
static void __init tangox_irq_domain_init(struct irq_domain *dom)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
gc = irq_get_domain_generic_chip(dom, i * 32);
|
||||
tangox_irq_init_chip(gc, i * IRQ_CTL_HI, i * EDGE_CTL_HI);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init tangox_irq_init(void __iomem *base, struct resource *baseres,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct tangox_irq_chip *chip;
|
||||
struct irq_domain *dom;
|
||||
struct resource res;
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (!irq)
|
||||
panic("%s: failed to get IRQ", node->name);
|
||||
|
||||
err = of_address_to_resource(node, 0, &res);
|
||||
if (err)
|
||||
panic("%s: failed to get address", node->name);
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
chip->ctl = res.start - baseres->start;
|
||||
chip->base = base;
|
||||
|
||||
dom = irq_domain_add_linear(node, 64, &irq_generic_chip_ops, chip);
|
||||
if (!dom)
|
||||
panic("%s: failed to create irqdomain", node->name);
|
||||
|
||||
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);
|
||||
|
||||
tangox_irq_domain_init(dom);
|
||||
|
||||
irq_set_chained_handler(irq, tangox_irq_handler);
|
||||
irq_set_handler_data(irq, dom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init tangox_of_irq_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct device_node *c;
|
||||
struct resource res;
|
||||
void __iomem *base;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base)
|
||||
panic("%s: of_iomap failed", node->name);
|
||||
|
||||
of_address_to_resource(node, 0, &res);
|
||||
|
||||
for_each_child_of_node(node, c)
|
||||
tangox_irq_init(base, &res, c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
IRQCHIP_DECLARE(tangox_intc, "sigma,smp8642-intc", tangox_of_irq_init);
|
Загрузка…
Ссылка в новой задаче