/*
* 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 <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>
#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 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;
};
static void s3c_irq_mask(struct irq_data *data)
{
struct s3c_irq_intc *intc = data->domain->host_data;
struct s3c_irq_intc *parent_intc = intc->parent;
struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq];
struct s3c_irq_data *parent_data;
unsigned long mask;
unsigned int irqno;
mask = __raw_readl(intc->reg_mask);
mask |= (1UL << data->hwirq);
__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 */
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_intc *intc = data->domain->host_data;
struct s3c_irq_intc *parent_intc = intc->parent;
struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq];
unsigned long mask;
unsigned int irqno;
mask = __raw_readl(intc->reg_mask);
mask &= ~(1UL << data->hwirq);
__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_intc *intc = data->domain->host_data;
unsigned long bitval = 1UL << data->hwirq;
__raw_writel(bitval, intc->reg_pending);
if (intc->reg_intpnd)
__raw_writel(bitval, intc->reg_intpnd);
}
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;