diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-29 16:25:00 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-29 16:25:00 +0100 |
commit | ac435075892e3e651c667b4a9f2267cf3ef1d5a2 (patch) | |
tree | ff45694dd545392cc6553af1219b2bd840974307 /drivers/irqchip/irq-csky-apb-intc.c | |
parent | Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net (diff) | |
parent | dt-bindings: interrupt-controller: C-SKY APB intc (diff) | |
download | linux-ac435075892e3e651c667b4a9f2267cf3ef1d5a2.tar.xz linux-ac435075892e3e651c667b4a9f2267cf3ef1d5a2.zip |
Merge tag 'csky-for-linus-4.20' of https://github.com/c-sky/csky-linux
Pull C-SKY architecture port from Guo Ren:
"This contains the Linux port for C-SKY(csky) based on linux-4.19
Release, which has been through 10 rounds of review on mailing list.
More information:
http://en.c-sky.com
The development repo:
https://github.com/c-sky/csky-linux
ABI Documentation:
https://github.com/c-sky/csky-doc
Here is the pre-built cross compiler for fast test from our CI:
https://gitlab.com/c-sky/buildroot/-/jobs/101608095/artifacts/file/output/images/csky_toolchain_qemu_csky_ck807f_4.18_glibc_defconfig_482b221e52908be1c9b2ccb444255e1562bb7025.tar.xz
We use buildroot as our CI-test enviornment. "LTP, Lmbench ..." will
be tested for every commit. See here for more details:
https://gitlab.com/c-sky/buildroot/pipelines
We'll continouslly improve csky subsystem in future"
Arnd acks, and adds the following notes:
"I did a thorough review of the ABI, which as usual mainly consists of
spotting any files that don't use the asm-generic ABI itself, and
having it changed to it matches exactly what we do on other new
architectures.
I also looked at every other patch and commented on maybe half of them
where I saw something that did not quite seem right. Others have
reviewed specific patches in greater depth. I'm sure that one could
fine more of the minor details, but as long as they are not ABI
relevant, they can be fixed later.
The only patch that is part of the ABI and that nobody reviewed is the
signal handling. This is one of the areas I never worked on in much
detail. I did not see anything wrong with it, but I also don't know
what the problems with the other architectures are here, and we seem
to be hitting issues occasionally, and we never managed to generalize
this enough for new architectures to have a trivial implementation.
I was originally hoping that we could have the 64-bit time_t
interfaces ready in time to completely drop the 32-bit ones, but that
did not happen. We might still remove them in the next merge window
depending on whether the libc upstream people prefer to keep them or
not.
One more general comment: I think this may well be the last new CPU
architecture we ever add to the kernel. Both nds32 and c-sky are made
by companies that also work on risc-v, and generally speaking risc-v
seems to be killing off any of the minor licensable instruction set
projects, just like ARM has mostly killed off the custom
vendor-specific instruction sets already.
If we add another architecture in the future, it may instead be
something like the LLVM bitcode or WebAssembly, who knows?"
To which Geert Uytterhoeven pipes in about another architecture still in
the pipeline: Kalray MPPA.
* tag 'csky-for-linus-4.20' of https://github.com/c-sky/csky-linux: (24 commits)
dt-bindings: interrupt-controller: C-SKY APB intc
irqchip: add C-SKY APB bus interrupt controller
dt-bindings: interrupt-controller: C-SKY SMP intc
irqchip: add C-SKY SMP interrupt controller
MAINTAINERS: Add csky
dt-bindings: Add vendor prefix for csky
dt-bindings: csky CPU Bindings
csky: Misc headers
csky: SMP support
csky: Debug and Ptrace GDB
csky: User access
csky: Library functions
csky: ELF and module probe
csky: Atomic operations
csky: IRQ handling
csky: VDSO and rt_sigreturn
csky: Process management and Signal
csky: MMU and page table management
csky: Cache and TLB routines
csky: System Call
...
Diffstat (limited to 'drivers/irqchip/irq-csky-apb-intc.c')
-rw-r--r-- | drivers/irqchip/irq-csky-apb-intc.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-csky-apb-intc.c b/drivers/irqchip/irq-csky-apb-intc.c new file mode 100644 index 000000000000..2543baba8b1f --- /dev/null +++ b/drivers/irqchip/irq-csky-apb-intc.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/irqdomain.h> +#include <linux/irqchip.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <asm/irq.h> + +#define INTC_IRQS 64 + +#define CK_INTC_ICR 0x00 +#define CK_INTC_PEN31_00 0x14 +#define CK_INTC_PEN63_32 0x2c +#define CK_INTC_NEN31_00 0x10 +#define CK_INTC_NEN63_32 0x28 +#define CK_INTC_SOURCE 0x40 +#define CK_INTC_DUAL_BASE 0x100 + +#define GX_INTC_PEN31_00 0x00 +#define GX_INTC_PEN63_32 0x04 +#define GX_INTC_NEN31_00 0x40 +#define GX_INTC_NEN63_32 0x44 +#define GX_INTC_NMASK31_00 0x50 +#define GX_INTC_NMASK63_32 0x54 +#define GX_INTC_SOURCE 0x60 + +static void __iomem *reg_base; +static struct irq_domain *root_domain; + +static int nr_irq = INTC_IRQS; + +/* + * When controller support pulse signal, the PEN_reg will hold on signal + * without software trigger. + * + * So, to support pulse signal we need to clear IFR_reg and the address of + * IFR_offset is NEN_offset - 8. + */ +static void irq_ck_mask_set_bit(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct irq_chip_type *ct = irq_data_get_chip_type(d); + unsigned long ifr = ct->regs.mask - 8; + u32 mask = d->mask; + + irq_gc_lock(gc); + *ct->mask_cache |= mask; + irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); + irq_reg_writel(gc, irq_reg_readl(gc, ifr) & ~mask, ifr); + irq_gc_unlock(gc); +} + +static void __init ck_set_gc(struct device_node *node, void __iomem *reg_base, + u32 mask_reg, u32 irq_base) +{ + struct irq_chip_generic *gc; + + gc = irq_get_domain_generic_chip(root_domain, irq_base); + gc->reg_base = reg_base; + gc->chip_types[0].regs.mask = mask_reg; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; + + if (of_find_property(node, "csky,support-pulse-signal", NULL)) + gc->chip_types[0].chip.irq_unmask = irq_ck_mask_set_bit; +} + +static inline u32 build_channel_val(u32 idx, u32 magic) +{ + u32 res; + + /* + * Set the same index for each channel + */ + res = idx | (idx << 8) | (idx << 16) | (idx << 24); + + /* + * Set the channel magic number in descending order. + * The magic is 0x00010203 for ck-intc + * The magic is 0x03020100 for gx6605s-intc + */ + return res | magic; +} + +static inline void setup_irq_channel(u32 magic, void __iomem *reg_addr) +{ + u32 i; + + /* Setup 64 channel slots */ + for (i = 0; i < INTC_IRQS; i += 4) + writel_relaxed(build_channel_val(i, magic), reg_addr + i); +} + +static int __init +ck_intc_init_comm(struct device_node *node, struct device_node *parent) +{ + int ret; + + if (parent) { + pr_err("C-SKY Intc not a root irq controller\n"); + return -EINVAL; + } + + reg_base = of_iomap(node, 0); + if (!reg_base) { + pr_err("C-SKY Intc unable to map: %p.\n", node); + return -EINVAL; + } + + root_domain = irq_domain_add_linear(node, nr_irq, + &irq_generic_chip_ops, NULL); + if (!root_domain) { + pr_err("C-SKY Intc irq_domain_add failed.\n"); + return -ENOMEM; + } + + ret = irq_alloc_domain_generic_chips(root_domain, 32, 1, + "csky_intc", handle_level_irq, + IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0, 0); + if (ret) { + pr_err("C-SKY Intc irq_alloc_gc failed.\n"); + return -ENOMEM; + } + + return 0; +} + +static inline bool handle_irq_perbit(struct pt_regs *regs, u32 hwirq, + u32 irq_base) +{ + u32 irq; + + if (hwirq == 0) + return 0; + + while (hwirq) { + irq = __ffs(hwirq); + hwirq &= ~BIT(irq); + handle_domain_irq(root_domain, irq_base + irq, regs); + } + + return 1; +} + +/* gx6605s 64 irqs interrupt controller */ +static void gx_irq_handler(struct pt_regs *regs) +{ + bool ret; + + do { + ret = handle_irq_perbit(regs, + readl_relaxed(reg_base + GX_INTC_PEN31_00), 0); + ret |= handle_irq_perbit(regs, + readl_relaxed(reg_base + GX_INTC_PEN63_32), 32); + } while (ret); +} + +static int __init +gx_intc_init(struct device_node *node, struct device_node *parent) +{ + int ret; + + ret = ck_intc_init_comm(node, parent); + if (ret) + return ret; + + /* + * Initial enable reg to disable all interrupts + */ + writel_relaxed(0x0, reg_base + GX_INTC_NEN31_00); + writel_relaxed(0x0, reg_base + GX_INTC_NEN63_32); + + /* + * Initial mask reg with all unmasked, because we only use enalbe reg + */ + writel_relaxed(0x0, reg_base + GX_INTC_NMASK31_00); + writel_relaxed(0x0, reg_base + GX_INTC_NMASK63_32); + + setup_irq_channel(0x03020100, reg_base + GX_INTC_SOURCE); + + ck_set_gc(node, reg_base, GX_INTC_NEN31_00, 0); + ck_set_gc(node, reg_base, GX_INTC_NEN63_32, 32); + + set_handle_irq(gx_irq_handler); + + return 0; +} +IRQCHIP_DECLARE(csky_gx6605s_intc, "csky,gx6605s-intc", gx_intc_init); + +/* + * C-SKY simple 64 irqs interrupt controller, dual-together could support 128 + * irqs. + */ +static void ck_irq_handler(struct pt_regs *regs) +{ + bool ret; + void __iomem *reg_pen_lo = reg_base + CK_INTC_PEN31_00; + void __iomem *reg_pen_hi = reg_base + CK_INTC_PEN63_32; + + do { + /* handle 0 - 31 irqs */ + ret = handle_irq_perbit(regs, readl_relaxed(reg_pen_lo), 0); + ret |= handle_irq_perbit(regs, readl_relaxed(reg_pen_hi), 32); + + if (nr_irq == INTC_IRQS) + continue; + + /* handle 64 - 127 irqs */ + ret |= handle_irq_perbit(regs, + readl_relaxed(reg_pen_lo + CK_INTC_DUAL_BASE), 64); + ret |= handle_irq_perbit(regs, + readl_relaxed(reg_pen_hi + CK_INTC_DUAL_BASE), 96); + } while (ret); +} + +static int __init +ck_intc_init(struct device_node *node, struct device_node *parent) +{ + int ret; + + ret = ck_intc_init_comm(node, parent); + if (ret) + return ret; + + /* Initial enable reg to disable all interrupts */ + writel_relaxed(0, reg_base + CK_INTC_NEN31_00); + writel_relaxed(0, reg_base + CK_INTC_NEN63_32); + + /* Enable irq intc */ + writel_relaxed(BIT(31), reg_base + CK_INTC_ICR); + + ck_set_gc(node, reg_base, CK_INTC_NEN31_00, 0); + ck_set_gc(node, reg_base, CK_INTC_NEN63_32, 32); + + setup_irq_channel(0x00010203, reg_base + CK_INTC_SOURCE); + + set_handle_irq(ck_irq_handler); + + return 0; +} +IRQCHIP_DECLARE(ck_intc, "csky,apb-intc", ck_intc_init); + +static int __init +ck_dual_intc_init(struct device_node *node, struct device_node *parent) +{ + int ret; + + /* dual-apb-intc up to 128 irq sources*/ + nr_irq = INTC_IRQS * 2; + + ret = ck_intc_init(node, parent); + if (ret) + return ret; + + /* Initial enable reg to disable all interrupts */ + writel_relaxed(0, reg_base + CK_INTC_NEN31_00 + CK_INTC_DUAL_BASE); + writel_relaxed(0, reg_base + CK_INTC_NEN63_32 + CK_INTC_DUAL_BASE); + + ck_set_gc(node, reg_base + CK_INTC_DUAL_BASE, CK_INTC_NEN31_00, 64); + ck_set_gc(node, reg_base + CK_INTC_DUAL_BASE, CK_INTC_NEN63_32, 96); + + setup_irq_channel(0x00010203, + reg_base + CK_INTC_SOURCE + CK_INTC_DUAL_BASE); + + return 0; +} +IRQCHIP_DECLARE(ck_dual_intc, "csky,dual-apb-intc", ck_dual_intc_init); |