diff options
Diffstat (limited to '')
-rw-r--r-- | arch/sh/kernel/cpu/irq/Makefile | 6 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/irq/imask.c | 85 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/irq/intc-sh5.c | 197 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/irq/ipr.c | 83 |
4 files changed, 371 insertions, 0 deletions
diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile new file mode 100644 index 000000000..3f8e79402 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Linux/SuperH CPU-specific IRQ handlers. +# +obj-$(CONFIG_SUPERH32) += imask.o +obj-$(CONFIG_CPU_SH5) += intc-sh5.o +obj-$(CONFIG_CPU_HAS_IPR_IRQ) += ipr.o diff --git a/arch/sh/kernel/cpu/irq/imask.c b/arch/sh/kernel/cpu/irq/imask.c new file mode 100644 index 000000000..572585c3f --- /dev/null +++ b/arch/sh/kernel/cpu/irq/imask.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch/sh/kernel/cpu/irq/imask.c + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * + * Simple interrupt handling using IMASK of SR register. + * + */ +/* NOTE: Will not work on level 15 */ +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/spinlock.h> +#include <linux/cache.h> +#include <linux/irq.h> +#include <linux/bitmap.h> +#include <asm/irq.h> + +/* Bitmap of IRQ masked */ +#define IMASK_PRIORITY 15 + +static DECLARE_BITMAP(imask_mask, IMASK_PRIORITY); +static int interrupt_priority; + +static inline void set_interrupt_registers(int ip) +{ + unsigned long __dummy; + + asm volatile( +#ifdef CONFIG_CPU_HAS_SR_RB + "ldc %2, r6_bank\n\t" +#endif + "stc sr, %0\n\t" + "and #0xf0, %0\n\t" + "shlr2 %0\n\t" + "cmp/eq #0x3c, %0\n\t" + "bt/s 1f ! CLI-ed\n\t" + " stc sr, %0\n\t" + "and %1, %0\n\t" + "or %2, %0\n\t" + "ldc %0, sr\n" + "1:" + : "=&z" (__dummy) + : "r" (~0xf0), "r" (ip << 4) + : "t"); +} + +static void mask_imask_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + + clear_bit(irq, imask_mask); + if (interrupt_priority < IMASK_PRIORITY - irq) + interrupt_priority = IMASK_PRIORITY - irq; + set_interrupt_registers(interrupt_priority); +} + +static void unmask_imask_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + + set_bit(irq, imask_mask); + interrupt_priority = IMASK_PRIORITY - + find_first_zero_bit(imask_mask, IMASK_PRIORITY); + set_interrupt_registers(interrupt_priority); +} + +static struct irq_chip imask_irq_chip = { + .name = "SR.IMASK", + .irq_mask = mask_imask_irq, + .irq_unmask = unmask_imask_irq, + .irq_mask_ack = mask_imask_irq, +}; + +void make_imask_irq(unsigned int irq) +{ + irq_set_chip_and_handler_name(irq, &imask_irq_chip, handle_level_irq, + "level"); +} diff --git a/arch/sh/kernel/cpu/irq/intc-sh5.c b/arch/sh/kernel/cpu/irq/intc-sh5.c new file mode 100644 index 000000000..9e056a3a0 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/intc-sh5.c @@ -0,0 +1,197 @@ +/* + * arch/sh/kernel/cpu/irq/intc-sh5.c + * + * Interrupt Controller support for SH5 INTC. + * + * Copyright (C) 2000, 2001 Paolo Alberelli + * Copyright (C) 2003 Paul Mundt + * + * Per-interrupt selective. IRLM=0 (Fixed priority) is not + * supported being useless without a cascaded interrupt + * controller. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <cpu/irq.h> +#include <asm/page.h> + +/* + * Maybe the generic Peripheral block could move to a more + * generic include file. INTC Block will be defined here + * and only here to make INTC self-contained in a single + * file. + */ +#define INTC_BLOCK_OFFSET 0x01000000 + +/* Base */ +#define INTC_BASE PHYS_PERIPHERAL_BLOCK + \ + INTC_BLOCK_OFFSET + +/* Address */ +#define INTC_ICR_SET (intc_virt + 0x0) +#define INTC_ICR_CLEAR (intc_virt + 0x8) +#define INTC_INTPRI_0 (intc_virt + 0x10) +#define INTC_INTSRC_0 (intc_virt + 0x50) +#define INTC_INTSRC_1 (intc_virt + 0x58) +#define INTC_INTREQ_0 (intc_virt + 0x60) +#define INTC_INTREQ_1 (intc_virt + 0x68) +#define INTC_INTENB_0 (intc_virt + 0x70) +#define INTC_INTENB_1 (intc_virt + 0x78) +#define INTC_INTDSB_0 (intc_virt + 0x80) +#define INTC_INTDSB_1 (intc_virt + 0x88) + +#define INTC_ICR_IRLM 0x1 +#define INTC_INTPRI_PREGS 8 /* 8 Priority Registers */ +#define INTC_INTPRI_PPREG 8 /* 8 Priorities per Register */ + + +/* + * Mapper between the vector ordinal and the IRQ number + * passed to kernel/device drivers. + */ +int intc_evt_to_irq[(0xE20/0x20)+1] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x000 - 0x0E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x100 - 0x1E0 */ + 0, 0, 0, 0, 0, 1, 0, 0, /* 0x200 - 0x2E0 */ + 2, 0, 0, 3, 0, 0, 0, -1, /* 0x300 - 0x3E0 */ + 32, 33, 34, 35, 36, 37, 38, -1, /* 0x400 - 0x4E0 */ + -1, -1, -1, 63, -1, -1, -1, -1, /* 0x500 - 0x5E0 */ + -1, -1, 18, 19, 20, 21, 22, -1, /* 0x600 - 0x6E0 */ + 39, 40, 41, 42, -1, -1, -1, -1, /* 0x700 - 0x7E0 */ + 4, 5, 6, 7, -1, -1, -1, -1, /* 0x800 - 0x8E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x900 - 0x9E0 */ + 12, 13, 14, 15, 16, 17, -1, -1, /* 0xA00 - 0xAE0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB00 - 0xBE0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC00 - 0xCE0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD00 - 0xDE0 */ + -1, -1 /* 0xE00 - 0xE20 */ +}; + +static unsigned long intc_virt; +static int irlm; /* IRL mode */ + +static void enable_intc_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + unsigned long reg; + unsigned long bitmask; + + if ((irq <= IRQ_IRL3) && (irlm == NO_PRIORITY)) + printk("Trying to use straight IRL0-3 with an encoding platform.\n"); + + if (irq < 32) { + reg = INTC_INTENB_0; + bitmask = 1 << irq; + } else { + reg = INTC_INTENB_1; + bitmask = 1 << (irq - 32); + } + + __raw_writel(bitmask, reg); +} + +static void disable_intc_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + unsigned long reg; + unsigned long bitmask; + + if (irq < 32) { + reg = INTC_INTDSB_0; + bitmask = 1 << irq; + } else { + reg = INTC_INTDSB_1; + bitmask = 1 << (irq - 32); + } + + __raw_writel(bitmask, reg); +} + +static struct irq_chip intc_irq_type = { + .name = "INTC", + .irq_enable = enable_intc_irq, + .irq_disable = disable_intc_irq, +}; + +void __init plat_irq_setup(void) +{ + unsigned long long __dummy0, __dummy1=~0x00000000100000f0; + unsigned long reg; + int i; + + intc_virt = (unsigned long)ioremap_nocache(INTC_BASE, 1024); + if (!intc_virt) { + panic("Unable to remap INTC\n"); + } + + + /* Set default: per-line enable/disable, priority driven ack/eoi */ + for (i = 0; i < NR_INTC_IRQS; i++) + irq_set_chip_and_handler(i, &intc_irq_type, handle_level_irq); + + + /* Disable all interrupts and set all priorities to 0 to avoid trouble */ + __raw_writel(-1, INTC_INTDSB_0); + __raw_writel(-1, INTC_INTDSB_1); + + for (reg = INTC_INTPRI_0, i = 0; i < INTC_INTPRI_PREGS; i++, reg += 8) + __raw_writel( NO_PRIORITY, reg); + + +#ifdef CONFIG_SH_CAYMAN + { + unsigned long data; + + /* Set IRLM */ + /* If all the priorities are set to 'no priority', then + * assume we are using encoded mode. + */ + irlm = platform_int_priority[IRQ_IRL0] + + platform_int_priority[IRQ_IRL1] + + platform_int_priority[IRQ_IRL2] + + platform_int_priority[IRQ_IRL3]; + if (irlm == NO_PRIORITY) { + /* IRLM = 0 */ + reg = INTC_ICR_CLEAR; + i = IRQ_INTA; + printk("Trying to use encoded IRL0-3. IRLs unsupported.\n"); + } else { + /* IRLM = 1 */ + reg = INTC_ICR_SET; + i = IRQ_IRL0; + } + __raw_writel(INTC_ICR_IRLM, reg); + + /* Set interrupt priorities according to platform description */ + for (data = 0, reg = INTC_INTPRI_0; i < NR_INTC_IRQS; i++) { + data |= platform_int_priority[i] << + ((i % INTC_INTPRI_PPREG) * 4); + if ((i % INTC_INTPRI_PPREG) == (INTC_INTPRI_PPREG - 1)) { + /* Upon the 7th, set Priority Register */ + __raw_writel(data, reg); + data = 0; + reg += 8; + } + } + } +#endif + + /* + * And now let interrupts come in. + * sti() is not enough, we need to + * lower priority, too. + */ + __asm__ __volatile__("getcon " __SR ", %0\n\t" + "and %0, %1, %0\n\t" + "putcon %0, " __SR "\n\t" + : "=&r" (__dummy0) + : "r" (__dummy1)); +} diff --git a/arch/sh/kernel/cpu/irq/ipr.c b/arch/sh/kernel/cpu/irq/ipr.c new file mode 100644 index 000000000..5de6dff5c --- /dev/null +++ b/arch/sh/kernel/cpu/irq/ipr.c @@ -0,0 +1,83 @@ +/* + * Interrupt handling for IPR-based IRQ. + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> + * Copyright (C) 2006 Paul Mundt + * + * Supported system: + * On-chip supporting modules (TMU, RTC, etc.). + * On-chip supporting modules for SH7709/SH7709A/SH7729. + * Hitachi SolutionEngine external I/O: + * MS7709SE01, MS7709ASE01, and MS7750SE01 + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/topology.h> + +static inline struct ipr_desc *get_ipr_desc(struct irq_data *data) +{ + struct irq_chip *chip = irq_data_get_irq_chip(data); + return container_of(chip, struct ipr_desc, chip); +} + +static void disable_ipr_irq(struct irq_data *data) +{ + struct ipr_data *p = irq_data_get_irq_chip_data(data); + unsigned long addr = get_ipr_desc(data)->ipr_offsets[p->ipr_idx]; + /* Set the priority in IPR to 0 */ + __raw_writew(__raw_readw(addr) & (0xffff ^ (0xf << p->shift)), addr); + (void)__raw_readw(addr); /* Read back to flush write posting */ +} + +static void enable_ipr_irq(struct irq_data *data) +{ + struct ipr_data *p = irq_data_get_irq_chip_data(data); + unsigned long addr = get_ipr_desc(data)->ipr_offsets[p->ipr_idx]; + /* Set priority in IPR back to original value */ + __raw_writew(__raw_readw(addr) | (p->priority << p->shift), addr); +} + +/* + * The shift value is now the number of bits to shift, not the number of + * bits/4. This is to make it easier to read the value directly from the + * datasheets. The IPR address is calculated using the ipr_offset table. + */ +void register_ipr_controller(struct ipr_desc *desc) +{ + int i; + + desc->chip.irq_mask = disable_ipr_irq; + desc->chip.irq_unmask = enable_ipr_irq; + + for (i = 0; i < desc->nr_irqs; i++) { + struct ipr_data *p = desc->ipr_data + i; + int res; + + BUG_ON(p->ipr_idx >= desc->nr_offsets); + BUG_ON(!desc->ipr_offsets[p->ipr_idx]); + + res = irq_alloc_desc_at(p->irq, numa_node_id()); + if (unlikely(res != p->irq && res != -EEXIST)) { + printk(KERN_INFO "can not get irq_desc for %d\n", + p->irq); + continue; + } + + disable_irq_nosync(p->irq); + irq_set_chip_and_handler_name(p->irq, &desc->chip, + handle_level_irq, "level"); + irq_set_chip_data(p->irq, p); + disable_ipr_irq(irq_get_irq_data(p->irq)); + } +} +EXPORT_SYMBOL(register_ipr_controller); |