mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-04 01:49:36 +00:00
![Linus Torvalds](/assets/img/avatar_default.png)
This is a rather large set of patches for device drivers that for one reason or another the subsystem maintainer preferred to get merged through the arm-soc tree. There are both new drivers as well as existing drivers that are getting converted from platform-specific code into standalone drivers using the appropriate subsystem specific interfaces. In particular, we can now have pinctrl, clk, clksource and irqchip drivers in one file per driver, without the need to call into platform specific interface, or to get called from platform specific code, as long as all information about the hardware is provided through a device tree. Most of the drivers we touch this time are for clocksource. Since now most of them are part of drivers/clocksource, I expect that we won't have to touch these again from arm-soc and can let the clocksource maintainers take care of these in the future. Another larger part of this series is specific to the exynos platform, which is seeing some significant effort in upstreaming and modernization of its device drivers this time around, which unfortunately is also the cause for the churn and a lot of the merge conflicts. There is one new subsystem that gets merged as part of this series: the reset controller interface, which is a very simple interface for taking devices on the SoC out of reset or back into reset. Patches to use this interface on i.MX follow later in this merge window, and we are going to have other platforms (at least tegra and sirf) get converted in 3.11. This will let us get rid of platform specific callbacks in a number of platform independent device drivers. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRhKUsAAoJEIwa5zzehBx3Ug4P/RqEen15hxS/NY8SIVRAU5c0 G9ZiSPcLmvXGR/t1RZFeLWKaKOYRb2oW1EbXrlkddprkmg85RuQE/KMpCgzPPhVC Yrs8UaagMGblaLOjwavVjin/CUXZokRdMfsQoIyMGOezmVGFnv4d4Kt64IOf35DF 24vDv/QO0BAI9k6m6WLqlWvSshb0IkW8r2LneRLnMEAVop7b1xkOxz0sR6l0LWfV 6JAMXyTjJMg0t8uCVW/QyNdxcxINHhV4SYcNkzF3EZ7ol50OiJsT9fg0XW759+Wb vlX6Xuehg+CBOg+g3ZOZuR8JOEkOhAGRSzuJkk/TmLCCxc+ghnuYz8HArxh6GMHK KaxvogLIi0ZsD94A/BZIKkDtOLWlzdz2HBrYo9PTz8zrOz/gXhwQ3zq0jPccC5E0 S+YYiobCBXepknF9301ti7wGD9VDzI8nmqOKG6tEBrD3xuO+RoBv+z4pBugN4/1C DlB19gOz60G5kniziL+wlmWER2qXmYrQZqS+s6+B2XoyoETC0Yij3Rck5vyC6qIK A2sni+Y9rzNOB9nzmnISP/UiGUffCy8AV4DZJjMSl0XkF4cpOXqRVGZ2nGB4tR5q GTOETcDCo5dvMDKX7Wfrz40CQzO39tnPCddg3OIS93ZwMpCeykIlb1FVL7RcsyF7 3uikzYHlDo3C5pvtJ5TS =ZWk9 -----END PGP SIGNATURE----- Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM SoC driver changes from Olof Johansson: "This is a rather large set of patches for device drivers that for one reason or another the subsystem maintainer preferred to get merged through the arm-soc tree. There are both new drivers as well as existing drivers that are getting converted from platform-specific code into standalone drivers using the appropriate subsystem specific interfaces. In particular, we can now have pinctrl, clk, clksource and irqchip drivers in one file per driver, without the need to call into platform specific interface, or to get called from platform specific code, as long as all information about the hardware is provided through a device tree. Most of the drivers we touch this time are for clocksource. Since now most of them are part of drivers/clocksource, I expect that we won't have to touch these again from arm-soc and can let the clocksource maintainers take care of these in the future. Another larger part of this series is specific to the exynos platform, which is seeing some significant effort in upstreaming and modernization of its device drivers this time around, which unfortunately is also the cause for the churn and a lot of the merge conflicts. There is one new subsystem that gets merged as part of this series: the reset controller interface, which is a very simple interface for taking devices on the SoC out of reset or back into reset. Patches to use this interface on i.MX follow later in this merge window, and we are going to have other platforms (at least tegra and sirf) get converted in 3.11. This will let us get rid of platform specific callbacks in a number of platform independent device drivers." * tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (256 commits) irqchip: s3c24xx: add missing __init annotations ARM: dts: Disable the RTC by default on exynos5 clk: exynos5250: Fix parent clock for sclk_mmc{0,1,2,3} ARM: exynos: restore mach/regs-clock.h for exynos5 clocksource: exynos_mct: fix build error on non-DT pinctrl: vt8500: wmt: Fix checking return value of pinctrl_register() irqchip: vt8500: Convert arch-vt8500 to new irqchip infrastructure reset: NULL deref on allocation failure reset: Add reset controller API dt: describe base reset signal binding ARM: EXYNOS: Add arm-pmu DT binding for exynos421x ARM: EXYNOS: Add arm-pmu DT binding for exynos5250 ARM: EXYNOS: Enable PMUs for exynos4 irqchip: exynos-combiner: Correct combined IRQs for exynos4 irqchip: exynos-combiner: Add set_irq_affinity function for combiner_irq ARM: EXYNOS: fix compilation error introduced due to common clock migration clk: exynos5250: Fix divider values for sclk_mmc{0,1,2,3} clk: exynos4: export clocks required for fimc-is clk: samsung: Fix compilation error clk: tegra: fix enum tegra114_clk to match binding ...
1356 lines
42 KiB
C
1356 lines
42 KiB
C
/*
|
|
* S3C24XX IRQ handling
|
|
*
|
|
* Copyright (c) 2003-2004 Simtec Electronics
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
* Copyright (c) 2012 Heiko Stuebner <heiko@sntech.de>
|
|
*
|
|
* 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/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/io.h>
|
|
#include <linux/err.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/device.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/irqchip/chained_irq.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
|
|
#include <asm/exception.h>
|
|
#include <asm/mach/irq.h>
|
|
|
|
#include <mach/regs-irq.h>
|
|
#include <mach/regs-gpio.h>
|
|
|
|
#include <plat/cpu.h>
|
|
#include <plat/regs-irqtype.h>
|
|
#include <plat/pm.h>
|
|
|
|
#include "irqchip.h"
|
|
|
|
#define S3C_IRQTYPE_NONE 0
|
|
#define S3C_IRQTYPE_EINT 1
|
|
#define S3C_IRQTYPE_EDGE 2
|
|
#define S3C_IRQTYPE_LEVEL 3
|
|
|
|
struct s3c_irq_data {
|
|
unsigned int type;
|
|
unsigned long offset;
|
|
unsigned long parent_irq;
|
|
|
|
/* data gets filled during init */
|
|
struct s3c_irq_intc *intc;
|
|
unsigned long sub_bits;
|
|
struct s3c_irq_intc *sub_intc;
|
|
};
|
|
|
|
/*
|
|
* Sructure holding the controller data
|
|
* @reg_pending register holding pending irqs
|
|
* @reg_intpnd special register intpnd in main intc
|
|
* @reg_mask mask register
|
|
* @domain irq_domain of the controller
|
|
* @parent parent controller for ext and sub irqs
|
|
* @irqs irq-data, always s3c_irq_data[32]
|
|
*/
|
|
struct s3c_irq_intc {
|
|
void __iomem *reg_pending;
|
|
void __iomem *reg_intpnd;
|
|
void __iomem *reg_mask;
|
|
struct irq_domain *domain;
|
|
struct s3c_irq_intc *parent;
|
|
struct s3c_irq_data *irqs;
|
|
};
|
|
|
|
/*
|
|
* Array holding pointers to the global controller structs
|
|
* [0] ... main_intc
|
|
* [1] ... sub_intc
|
|
* [2] ... main_intc2 on s3c2416
|
|
*/
|
|
static struct s3c_irq_intc *s3c_intc[3];
|
|
|
|
static void s3c_irq_mask(struct irq_data *data)
|
|
{
|
|
struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data);
|
|
struct s3c_irq_intc *intc = irq_data->intc;
|
|
struct s3c_irq_intc *parent_intc = intc->parent;
|
|
struct s3c_irq_data *parent_data;
|
|
unsigned long mask;
|
|
unsigned int irqno;
|
|
|
|
mask = __raw_readl(intc->reg_mask);
|
|
mask |= (1UL << irq_data->offset);
|
|
__raw_writel(mask, intc->reg_mask);
|
|
|
|
if (parent_intc) {
|
|
parent_data = &parent_intc->irqs[irq_data->parent_irq];
|
|
|
|
/* check to see if we need to mask the parent IRQ
|
|
* The parent_irq is always in main_intc, so the hwirq
|
|
* for find_mapping does not need an offset in any case.
|
|
*/
|
|
if ((mask & parent_data->sub_bits) == parent_data->sub_bits) {
|
|
irqno = irq_find_mapping(parent_intc->domain,
|
|
irq_data->parent_irq);
|
|
s3c_irq_mask(irq_get_irq_data(irqno));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void s3c_irq_unmask(struct irq_data *data)
|
|
{
|
|
struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data);
|
|
struct s3c_irq_intc *intc = irq_data->intc;
|
|
struct s3c_irq_intc *parent_intc = intc->parent;
|
|
unsigned long mask;
|
|
unsigned int irqno;
|
|
|
|
mask = __raw_readl(intc->reg_mask);
|
|
mask &= ~(1UL << irq_data->offset);
|
|
__raw_writel(mask, intc->reg_mask);
|
|
|
|
if (parent_intc) {
|
|
irqno = irq_find_mapping(parent_intc->domain,
|
|
irq_data->parent_irq);
|
|
s3c_irq_unmask(irq_get_irq_data(irqno));
|
|
}
|
|
}
|
|
|
|
static inline void s3c_irq_ack(struct irq_data *data)
|
|
{
|
|
struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data);
|
|
struct s3c_irq_intc *intc = irq_data->intc;
|
|
unsigned long bitval = 1UL << irq_data->offset;
|
|
|
|
__raw_writel(bitval, intc->reg_pending);
|
|
if (intc->reg_intpnd)
|
|
__raw_writel(bitval, intc->reg_intpnd);
|
|
}
|
|
|
|
static int s3c_irq_type(struct irq_data *data, unsigned int type)
|
|
{
|
|
switch (type) {
|
|
case IRQ_TYPE_NONE:
|
|
break;
|
|
case IRQ_TYPE_EDGE_RISING:
|
|
case IRQ_TYPE_EDGE_FALLING:
|
|
case IRQ_TYPE_EDGE_BOTH:
|
|
irq_set_handler(data->irq, handle_edge_irq);
|
|
break;
|
|
case IRQ_TYPE_LEVEL_LOW:
|
|
case IRQ_TYPE_LEVEL_HIGH:
|
|
irq_set_handler(data->irq, handle_level_irq);
|
|
break;
|
|
default:
|
|
pr_err("No such irq type %d", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s3c_irqext_type_set(void __iomem *gpcon_reg,
|
|
void __iomem *extint_reg,
|
|
unsigned long gpcon_offset,
|
|
unsigned long extint_offset,
|
|
unsigned int type)
|
|
{
|
|
unsigned long newvalue = 0, value;
|
|
|
|
/* Set the GPIO to external interrupt mode */
|
|
value = __raw_readl(gpcon_reg);
|
|
value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
|
|
__raw_writel(value, gpcon_reg);
|
|
|
|
/* Set the external interrupt to pointed trigger type */
|
|
switch (type)
|
|
{
|
|
case IRQ_TYPE_NONE:
|
|
pr_warn("No edge setting!\n");
|
|
break;
|
|
|
|
case IRQ_TYPE_EDGE_RISING:
|
|
newvalue = S3C2410_EXTINT_RISEEDGE;
|
|
break;
|
|
|
|
case IRQ_TYPE_EDGE_FALLING:
|
|
newvalue = S3C2410_EXTINT_FALLEDGE;
|
|
break;
|
|
|
|
case IRQ_TYPE_EDGE_BOTH:
|
|
newvalue = S3C2410_EXTINT_BOTHEDGE;
|
|
break;
|
|
|
|
case IRQ_TYPE_LEVEL_LOW:
|
|
newvalue = S3C2410_EXTINT_LOWLEV;
|
|
break;
|
|
|
|
case IRQ_TYPE_LEVEL_HIGH:
|
|
newvalue = S3C2410_EXTINT_HILEV;
|
|
break;
|
|
|
|
default:
|
|
pr_err("No such irq type %d", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
value = __raw_readl(extint_reg);
|
|
value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
|
|
__raw_writel(value, extint_reg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s3c_irqext_type(struct irq_data *data, unsigned int type)
|
|
{
|
|
void __iomem *extint_reg;
|
|
void __iomem *gpcon_reg;
|
|
unsigned long gpcon_offset, extint_offset;
|
|
|
|
if ((data->hwirq >= 4) && (data->hwirq <= 7)) {
|
|
gpcon_reg = S3C2410_GPFCON;
|
|
extint_reg = S3C24XX_EXTINT0;
|
|
gpcon_offset = (data->hwirq) * 2;
|
|
extint_offset = (data->hwirq) * 4;
|
|
} else if ((data->hwirq >= 8) && (data->hwirq <= 15)) {
|
|
gpcon_reg = S3C2410_GPGCON;
|
|
extint_reg = S3C24XX_EXTINT1;
|
|
gpcon_offset = (data->hwirq - 8) * 2;
|
|
extint_offset = (data->hwirq - 8) * 4;
|
|
} else if ((data->hwirq >= 16) && (data->hwirq <= 23)) {
|
|
gpcon_reg = S3C2410_GPGCON;
|
|
extint_reg = S3C24XX_EXTINT2;
|
|
gpcon_offset = (data->hwirq - 8) * 2;
|
|
extint_offset = (data->hwirq - 16) * 4;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset,
|
|
extint_offset, type);
|
|
}
|
|
|
|
static int s3c_irqext0_type(struct irq_data *data, unsigned int type)
|
|
{
|
|
void __iomem *extint_reg;
|
|
void __iomem *gpcon_reg;
|
|
unsigned long gpcon_offset, extint_offset;
|
|
|
|
if ((data->hwirq >= 0) && (data->hwirq <= 3)) {
|
|
gpcon_reg = S3C2410_GPFCON;
|
|
extint_reg = S3C24XX_EXTINT0;
|
|
gpcon_offset = (data->hwirq) * 2;
|
|
extint_offset = (data->hwirq) * 4;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset,
|
|
extint_offset, type);
|
|
}
|
|
|
|
static struct irq_chip s3c_irq_chip = {
|
|
.name = "s3c",
|
|
.irq_ack = s3c_irq_ack,
|
|
.irq_mask = s3c_irq_mask,
|
|
.irq_unmask = s3c_irq_unmask,
|
|
.irq_set_type = s3c_irq_type,
|
|
.irq_set_wake = s3c_irq_wake
|
|
};
|
|
|
|
static struct irq_chip s3c_irq_level_chip = {
|
|
.name = "s3c-level",
|
|
.irq_mask = s3c_irq_mask,
|
|
.irq_unmask = s3c_irq_unmask,
|
|
.irq_ack = s3c_irq_ack,
|
|
.irq_set_type = s3c_irq_type,
|
|
};
|
|
|
|
static struct irq_chip s3c_irqext_chip = {
|
|
.name = "s3c-ext",
|
|
.irq_mask = s3c_irq_mask,
|
|
.irq_unmask = s3c_irq_unmask,
|
|
.irq_ack = s3c_irq_ack,
|
|
.irq_set_type = s3c_irqext_type,
|
|
.irq_set_wake = s3c_irqext_wake
|
|
};
|
|
|
|
static struct irq_chip s3c_irq_eint0t4 = {
|
|
.name = "s3c-ext0",
|
|
.irq_ack = s3c_irq_ack,
|
|
.irq_mask = s3c_irq_mask,
|
|
.irq_unmask = s3c_irq_unmask,
|
|
.irq_set_wake = s3c_irq_wake,
|
|
.irq_set_type = s3c_irqext0_type,
|
|
};
|
|
|
|
static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc)
|
|
{
|
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
|
|
struct s3c_irq_intc *intc = irq_data->intc;
|
|
struct s3c_irq_intc *sub_intc = irq_data->sub_intc;
|
|
unsigned long src;
|
|
unsigned long msk;
|
|
unsigned int n;
|
|
unsigned int offset;
|
|
|
|
/* we're using individual domains for the non-dt case
|
|
* and one big domain for the dt case where the subintc
|
|
* starts at hwirq number 32.
|
|
*/
|
|
offset = (intc->domain->of_node) ? 32 : 0;
|
|
|
|
chained_irq_enter(chip, desc);
|
|
|
|
src = __raw_readl(sub_intc->reg_pending);
|
|
msk = __raw_readl(sub_intc->reg_mask);
|
|
|
|
src &= ~msk;
|
|
src &= irq_data->sub_bits;
|
|
|
|
while (src) {
|
|
n = __ffs(src);
|
|
src &= ~(1 << n);
|
|
irq = irq_find_mapping(sub_intc->domain, offset + n);
|
|
generic_handle_irq(irq);
|
|
}
|
|
|
|
chained_irq_exit(chip, desc);
|
|
}
|
|
|
|
static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
|
|
struct pt_regs *regs, int intc_offset)
|
|
{
|
|
int pnd;
|
|
int offset;
|
|
int irq;
|
|
|
|
pnd = __raw_readl(intc->reg_intpnd);
|
|
if (!pnd)
|
|
return false;
|
|
|
|
/* non-dt machines use individual domains */
|
|
if (!intc->domain->of_node)
|
|
intc_offset = 0;
|
|
|
|
/* We have a problem that the INTOFFSET register does not always
|
|
* show one interrupt. Occasionally we get two interrupts through
|
|
* the prioritiser, and this causes the INTOFFSET register to show
|
|
* what looks like the logical-or of the two interrupt numbers.
|
|
*
|
|
* Thanks to Klaus, Shannon, et al for helping to debug this problem
|
|
*/
|
|
offset = __raw_readl(intc->reg_intpnd + 4);
|
|
|
|
/* Find the bit manually, when the offset is wrong.
|
|
* The pending register only ever contains the one bit of the next
|
|
* interrupt to handle.
|
|
*/
|
|
if (!(pnd & (1 << offset)))
|
|
offset = __ffs(pnd);
|
|
|
|
irq = irq_find_mapping(intc->domain, intc_offset + offset);
|
|
handle_IRQ(irq, regs);
|
|
return true;
|
|
}
|
|
|
|
asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs)
|
|
{
|
|
do {
|
|
if (likely(s3c_intc[0]))
|
|
if (s3c24xx_handle_intc(s3c_intc[0], regs, 0))
|
|
continue;
|
|
|
|
if (s3c_intc[2])
|
|
if (s3c24xx_handle_intc(s3c_intc[2], regs, 64))
|
|
continue;
|
|
|
|
break;
|
|
} while (1);
|
|
}
|
|
|
|
#ifdef CONFIG_FIQ
|
|
/**
|
|
* s3c24xx_set_fiq - set the FIQ routing
|
|
* @irq: IRQ number to route to FIQ on processor.
|
|
* @on: Whether to route @irq to the FIQ, or to remove the FIQ routing.
|
|
*
|
|
* Change the state of the IRQ to FIQ routing depending on @irq and @on. If
|
|
* @on is true, the @irq is checked to see if it can be routed and the
|
|
* interrupt controller updated to route the IRQ. If @on is false, the FIQ
|
|
* routing is cleared, regardless of which @irq is specified.
|
|
*/
|
|
int s3c24xx_set_fiq(unsigned int irq, bool on)
|
|
{
|
|
u32 intmod;
|
|
unsigned offs;
|
|
|
|
if (on) {
|
|
offs = irq - FIQ_START;
|
|
if (offs > 31)
|
|
return -EINVAL;
|
|
|
|
intmod = 1 << offs;
|
|
} else {
|
|
intmod = 0;
|
|
}
|
|
|
|
__raw_writel(intmod, S3C2410_INTMOD);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(s3c24xx_set_fiq);
|
|
#endif
|
|
|
|
static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
|
|
irq_hw_number_t hw)
|
|
{
|
|
struct s3c_irq_intc *intc = h->host_data;
|
|
struct s3c_irq_data *irq_data = &intc->irqs[hw];
|
|
struct s3c_irq_intc *parent_intc;
|
|
struct s3c_irq_data *parent_irq_data;
|
|
unsigned int irqno;
|
|
|
|
/* attach controller pointer to irq_data */
|
|
irq_data->intc = intc;
|
|
irq_data->offset = hw;
|
|
|
|
parent_intc = intc->parent;
|
|
|
|
/* set handler and flags */
|
|
switch (irq_data->type) {
|
|
case S3C_IRQTYPE_NONE:
|
|
return 0;
|
|
case S3C_IRQTYPE_EINT:
|
|
/* On the S3C2412, the EINT0to3 have a parent irq
|
|
* but need the s3c_irq_eint0t4 chip
|
|
*/
|
|
if (parent_intc && (!soc_is_s3c2412() || hw >= 4))
|
|
irq_set_chip_and_handler(virq, &s3c_irqext_chip,
|
|
handle_edge_irq);
|
|
else
|
|
irq_set_chip_and_handler(virq, &s3c_irq_eint0t4,
|
|
handle_edge_irq);
|
|
break;
|
|
case S3C_IRQTYPE_EDGE:
|
|
if (parent_intc || intc->reg_pending == S3C2416_SRCPND2)
|
|
irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
|
|
handle_edge_irq);
|
|
else
|
|
irq_set_chip_and_handler(virq, &s3c_irq_chip,
|
|
handle_edge_irq);
|
|
break;
|
|
case S3C_IRQTYPE_LEVEL:
|
|
if (parent_intc)
|
|
irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
|
|
handle_level_irq);
|
|
else
|
|
irq_set_chip_and_handler(virq, &s3c_irq_chip,
|
|
handle_level_irq);
|
|
break;
|
|
default:
|
|
pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
irq_set_chip_data(virq, irq_data);
|
|
|
|
set_irq_flags(virq, IRQF_VALID);
|
|
|
|
if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) {
|
|
if (irq_data->parent_irq > 31) {
|
|
pr_err("irq-s3c24xx: parent irq %lu is out of range\n",
|
|
irq_data->parent_irq);
|
|
goto err;
|
|
}
|
|
|
|
parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
|
|
parent_irq_data->sub_intc = intc;
|
|
parent_irq_data->sub_bits |= (1UL << hw);
|
|
|
|
/* attach the demuxer to the parent irq */
|
|
irqno = irq_find_mapping(parent_intc->domain,
|
|
irq_data->parent_irq);
|
|
if (!irqno) {
|
|
pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n",
|
|
irq_data->parent_irq);
|
|
goto err;
|
|
}
|
|
irq_set_chained_handler(irqno, s3c_irq_demux);
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
set_irq_flags(virq, 0);
|
|
|
|
/* the only error can result from bad mapping data*/
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct irq_domain_ops s3c24xx_irq_ops = {
|
|
.map = s3c24xx_irq_map,
|
|
.xlate = irq_domain_xlate_twocell,
|
|
};
|
|
|
|
static void s3c24xx_clear_intc(struct s3c_irq_intc *intc)
|
|
{
|
|
void __iomem *reg_source;
|
|
unsigned long pend;
|
|
unsigned long last;
|
|
int i;
|
|
|
|
/* if intpnd is set, read the next pending irq from there */
|
|
reg_source = intc->reg_intpnd ? intc->reg_intpnd : intc->reg_pending;
|
|
|
|
last = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
pend = __raw_readl(reg_source);
|
|
|
|
if (pend == 0 || pend == last)
|
|
break;
|
|
|
|
__raw_writel(pend, intc->reg_pending);
|
|
if (intc->reg_intpnd)
|
|
__raw_writel(pend, intc->reg_intpnd);
|
|
|
|
pr_info("irq: clearing pending status %08x\n", (int)pend);
|
|
last = pend;
|
|
}
|
|
}
|
|
|
|
static struct s3c_irq_intc * __init s3c24xx_init_intc(struct device_node *np,
|
|
struct s3c_irq_data *irq_data,
|
|
struct s3c_irq_intc *parent,
|
|
unsigned long address)
|
|
{
|
|
struct s3c_irq_intc *intc;
|
|
void __iomem *base = (void *)0xf6000000; /* static mapping */
|
|
int irq_num;
|
|
int irq_start;
|
|
int ret;
|
|
|
|
intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
|
|
if (!intc)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
intc->irqs = irq_data;
|
|
|
|
if (parent)
|
|
intc->parent = parent;
|
|
|
|
/* select the correct data for the controller.
|
|
* Need to hard code the irq num start and offset
|
|
* to preserve the static mapping for now
|
|
*/
|
|
switch (address) {
|
|
case 0x4a000000:
|
|
pr_debug("irq: found main intc\n");
|
|
intc->reg_pending = base;
|
|
intc->reg_mask = base + 0x08;
|
|
intc->reg_intpnd = base + 0x10;
|
|
irq_num = 32;
|
|
irq_start = S3C2410_IRQ(0);
|
|
break;
|
|
case 0x4a000018:
|
|
pr_debug("irq: found subintc\n");
|
|
intc->reg_pending = base + 0x18;
|
|
intc->reg_mask = base + 0x1c;
|
|
irq_num = 29;
|
|
irq_start = S3C2410_IRQSUB(0);
|
|
break;
|
|
case 0x4a000040:
|
|
pr_debug("irq: found intc2\n");
|
|
intc->reg_pending = base + 0x40;
|
|
intc->reg_mask = base + 0x48;
|
|
intc->reg_intpnd = base + 0x50;
|
|
irq_num = 8;
|
|
irq_start = S3C2416_IRQ(0);
|
|
break;
|
|
case 0x560000a4:
|
|
pr_debug("irq: found eintc\n");
|
|
base = (void *)0xfd000000;
|
|
|
|
intc->reg_mask = base + 0xa4;
|
|
intc->reg_pending = base + 0xa8;
|
|
irq_num = 24;
|
|
irq_start = S3C2410_IRQ(32);
|
|
break;
|
|
default:
|
|
pr_err("irq: unsupported controller address\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
/* now that all the data is complete, init the irq-domain */
|
|
s3c24xx_clear_intc(intc);
|
|
intc->domain = irq_domain_add_legacy(np, irq_num, irq_start,
|
|
0, &s3c24xx_irq_ops,
|
|
intc);
|
|
if (!intc->domain) {
|
|
pr_err("irq: could not create irq-domain\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
set_handle_irq(s3c24xx_handle_irq);
|
|
|
|
return intc;
|
|
|
|
err:
|
|
kfree(intc);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
static struct s3c_irq_data init_eint[32] = {
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */
|
|
};
|
|
|
|
#ifdef CONFIG_CPU_S3C2410
|
|
static struct s3c_irq_data init_s3c2410base[32] = {
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2410subint[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
|
|
};
|
|
|
|
void __init s3c2410_init_irq(void)
|
|
{
|
|
#ifdef CONFIG_FIQ
|
|
init_FIQ(FIQ_START);
|
|
#endif
|
|
|
|
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL,
|
|
0x4a000000);
|
|
if (IS_ERR(s3c_intc[0])) {
|
|
pr_err("irq: could not create main interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2410subint[0],
|
|
s3c_intc[0], 0x4a000018);
|
|
s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_CPU_S3C2412
|
|
static struct s3c_irq_data init_s3c2412base[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* SDI/CF */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2412eint[32] = {
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 0 }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 1 }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 2 }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 3 }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */
|
|
{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2412subint[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
|
|
{ .type = S3C_IRQTYPE_NONE, },
|
|
{ .type = S3C_IRQTYPE_NONE, },
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* SDI */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* CF */
|
|
};
|
|
|
|
void __init s3c2412_init_irq(void)
|
|
{
|
|
pr_info("S3C2412: IRQ Support\n");
|
|
|
|
#ifdef CONFIG_FIQ
|
|
init_FIQ(FIQ_START);
|
|
#endif
|
|
|
|
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL,
|
|
0x4a000000);
|
|
if (IS_ERR(s3c_intc[0])) {
|
|
pr_err("irq: could not create main interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
s3c24xx_init_intc(NULL, &init_s3c2412eint[0], s3c_intc[0], 0x560000a4);
|
|
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2412subint[0],
|
|
s3c_intc[0], 0x4a000018);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_CPU_S3C2416
|
|
static struct s3c_irq_data init_s3c2416base[32] = {
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* LCD */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* DMA */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */
|
|
{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* NAND */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
|
|
{ .type = S3C_IRQTYPE_NONE, },
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2416subint[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2416_second[32] = {
|
|
{ .type = S3C_IRQTYPE_EDGE }, /* 2D */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE }, /* PCM0 */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_EDGE }, /* I2S0 */
|
|
};
|
|
|
|
void __init s3c2416_init_irq(void)
|
|
{
|
|
pr_info("S3C2416: IRQ Support\n");
|
|
|
|
#ifdef CONFIG_FIQ
|
|
init_FIQ(FIQ_START);
|
|
#endif
|
|
|
|
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL,
|
|
0x4a000000);
|
|
if (IS_ERR(s3c_intc[0])) {
|
|
pr_err("irq: could not create main interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
|
|
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2416subint[0],
|
|
s3c_intc[0], 0x4a000018);
|
|
|
|
s3c_intc[2] = s3c24xx_init_intc(NULL, &init_s3c2416_second[0],
|
|
NULL, 0x4a000040);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_CPU_S3C2440
|
|
static struct s3c_irq_data init_s3c2440base[32] = {
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2440subint[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */
|
|
};
|
|
|
|
void __init s3c2440_init_irq(void)
|
|
{
|
|
pr_info("S3C2440: IRQ Support\n");
|
|
|
|
#ifdef CONFIG_FIQ
|
|
init_FIQ(FIQ_START);
|
|
#endif
|
|
|
|
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL,
|
|
0x4a000000);
|
|
if (IS_ERR(s3c_intc[0])) {
|
|
pr_err("irq: could not create main interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
|
|
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0],
|
|
s3c_intc[0], 0x4a000018);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_CPU_S3C2442
|
|
static struct s3c_irq_data init_s3c2442base[32] = {
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
|
|
};
|
|
|
|
static struct s3c_irq_data init_s3c2442subint[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */
|
|
};
|
|
|
|
void __init s3c2442_init_irq(void)
|
|
{
|
|
pr_info("S3C2442: IRQ Support\n");
|
|
|
|
#ifdef CONFIG_FIQ
|
|
init_FIQ(FIQ_START);
|
|
#endif
|
|
|
|
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL,
|
|
0x4a000000);
|
|
if (IS_ERR(s3c_intc[0])) {
|
|
pr_err("irq: could not create main interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
|
|
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2442subint[0],
|
|
s3c_intc[0], 0x4a000018);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_CPU_S3C2443
|
|
static struct s3c_irq_data init_s3c2443base[32] = {
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
|
|
{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* LCD */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* DMA */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* CFON */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* NAND */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
|
|
{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
|
|
};
|
|
|
|
|
|
static struct s3c_irq_data init_s3c2443subint[32] = {
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
|
|
{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */
|
|
{ .type = S3C_IRQTYPE_NONE }, /* reserved */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD1 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */
|
|
{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */
|
|
};
|
|
|
|
void __init s3c2443_init_irq(void)
|
|
{
|
|
pr_info("S3C2443: IRQ Support\n");
|
|
|
|
#ifdef CONFIG_FIQ
|
|
init_FIQ(FIQ_START);
|
|
#endif
|
|
|
|
s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL,
|
|
0x4a000000);
|
|
if (IS_ERR(s3c_intc[0])) {
|
|
pr_err("irq: could not create main interrupt controller\n");
|
|
return;
|
|
}
|
|
|
|
s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
|
|
s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0],
|
|
s3c_intc[0], 0x4a000018);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
|
|
irq_hw_number_t hw)
|
|
{
|
|
unsigned int ctrl_num = hw / 32;
|
|
unsigned int intc_hw = hw % 32;
|
|
struct s3c_irq_intc *intc = s3c_intc[ctrl_num];
|
|
struct s3c_irq_intc *parent_intc = intc->parent;
|
|
struct s3c_irq_data *irq_data = &intc->irqs[intc_hw];
|
|
|
|
/* attach controller pointer to irq_data */
|
|
irq_data->intc = intc;
|
|
irq_data->offset = intc_hw;
|
|
|
|
if (!parent_intc)
|
|
irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);
|
|
else
|
|
irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
|
|
handle_edge_irq);
|
|
|
|
irq_set_chip_data(virq, irq_data);
|
|
|
|
set_irq_flags(virq, IRQF_VALID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Translate our of irq notation
|
|
* format: <ctrl_num ctrl_irq parent_irq type>
|
|
*/
|
|
static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
|
|
const u32 *intspec, unsigned int intsize,
|
|
irq_hw_number_t *out_hwirq, unsigned int *out_type)
|
|
{
|
|
struct s3c_irq_intc *intc;
|
|
struct s3c_irq_intc *parent_intc;
|
|
struct s3c_irq_data *irq_data;
|
|
struct s3c_irq_data *parent_irq_data;
|
|
int irqno;
|
|
|
|
if (WARN_ON(intsize < 4))
|
|
return -EINVAL;
|
|
|
|
if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {
|
|
pr_err("controller number %d invalid\n", intspec[0]);
|
|
return -EINVAL;
|
|
}
|
|
intc = s3c_intc[intspec[0]];
|
|
|
|
*out_hwirq = intspec[0] * 32 + intspec[2];
|
|
*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
|
|
|
|
parent_intc = intc->parent;
|
|
if (parent_intc) {
|
|
irq_data = &intc->irqs[intspec[2]];
|
|
irq_data->parent_irq = intspec[1];
|
|
parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
|
|
parent_irq_data->sub_intc = intc;
|
|
parent_irq_data->sub_bits |= (1UL << intspec[2]);
|
|
|
|
/* parent_intc is always s3c_intc[0], so no offset */
|
|
irqno = irq_create_mapping(parent_intc->domain, intspec[1]);
|
|
if (irqno < 0) {
|
|
pr_err("irq: could not map parent interrupt\n");
|
|
return irqno;
|
|
}
|
|
|
|
irq_set_chained_handler(irqno, s3c_irq_demux);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct irq_domain_ops s3c24xx_irq_ops_of = {
|
|
.map = s3c24xx_irq_map_of,
|
|
.xlate = s3c24xx_irq_xlate_of,
|
|
};
|
|
|
|
struct s3c24xx_irq_of_ctrl {
|
|
char *name;
|
|
unsigned long offset;
|
|
struct s3c_irq_intc **handle;
|
|
struct s3c_irq_intc **parent;
|
|
struct irq_domain_ops *ops;
|
|
};
|
|
|
|
static int __init s3c_init_intc_of(struct device_node *np,
|
|
struct device_node *interrupt_parent,
|
|
struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
|
|
{
|
|
struct s3c_irq_intc *intc;
|
|
struct s3c24xx_irq_of_ctrl *ctrl;
|
|
struct irq_domain *domain;
|
|
void __iomem *reg_base;
|
|
int i;
|
|
|
|
reg_base = of_iomap(np, 0);
|
|
if (!reg_base) {
|
|
pr_err("irq-s3c24xx: could not map irq registers\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
domain = irq_domain_add_linear(np, num_ctrl * 32,
|
|
&s3c24xx_irq_ops_of, NULL);
|
|
if (!domain) {
|
|
pr_err("irq: could not create irq-domain\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < num_ctrl; i++) {
|
|
ctrl = &s3c_ctrl[i];
|
|
|
|
pr_debug("irq: found controller %s\n", ctrl->name);
|
|
|
|
intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
|
|
if (!intc)
|
|
return -ENOMEM;
|
|
|
|
intc->domain = domain;
|
|
intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32,
|
|
GFP_KERNEL);
|
|
if (!intc->irqs) {
|
|
kfree(intc);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (ctrl->parent) {
|
|
intc->reg_pending = reg_base + ctrl->offset;
|
|
intc->reg_mask = reg_base + ctrl->offset + 0x4;
|
|
|
|
if (*(ctrl->parent)) {
|
|
intc->parent = *(ctrl->parent);
|
|
} else {
|
|
pr_warn("irq: parent of %s missing\n",
|
|
ctrl->name);
|
|
kfree(intc->irqs);
|
|
kfree(intc);
|
|
continue;
|
|
}
|
|
} else {
|
|
intc->reg_pending = reg_base + ctrl->offset;
|
|
intc->reg_mask = reg_base + ctrl->offset + 0x08;
|
|
intc->reg_intpnd = reg_base + ctrl->offset + 0x10;
|
|
}
|
|
|
|
s3c24xx_clear_intc(intc);
|
|
s3c_intc[i] = intc;
|
|
}
|
|
|
|
set_handle_irq(s3c24xx_handle_irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
|
|
{
|
|
.name = "intc",
|
|
.offset = 0,
|
|
}, {
|
|
.name = "subintc",
|
|
.offset = 0x18,
|
|
.parent = &s3c_intc[0],
|
|
}
|
|
};
|
|
|
|
int __init s3c2410_init_intc_of(struct device_node *np,
|
|
struct device_node *interrupt_parent,
|
|
struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl)
|
|
{
|
|
return s3c_init_intc_of(np, interrupt_parent,
|
|
s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
|
|
}
|
|
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
|
|
|
|
static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = {
|
|
{
|
|
.name = "intc",
|
|
.offset = 0,
|
|
}, {
|
|
.name = "subintc",
|
|
.offset = 0x18,
|
|
.parent = &s3c_intc[0],
|
|
}, {
|
|
.name = "intc2",
|
|
.offset = 0x40,
|
|
}
|
|
};
|
|
|
|
int __init s3c2416_init_intc_of(struct device_node *np,
|
|
struct device_node *interrupt_parent,
|
|
struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl)
|
|
{
|
|
return s3c_init_intc_of(np, interrupt_parent,
|
|
s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl));
|
|
}
|
|
IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of);
|
|
#endif
|