irqchip/mxs: Prepare driver for hardware with different offsets
Alphascale asm9260 has similar functionality but different register offsets. To support asm9260 in the mxs driver we need to rework the hardcoded access mechanisms. - Define SET_REG and CLR_REG. These controllers support seperate CLR and SET offsets for each register. - Reimplement HW_ICOLL_INTERRUPT with SET_REG and CLR_REG to make it usable for both cases. - Instead of using icoll_base and adding the offsets at runtime, create a new data structure which contains base pointers to all required regitsters and use it. - Split out functionality, which is required for the init code of mxs and asm9260, into helper functions [ tglx: Massaged changelog and moved the return value change to the previous patch ] Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> Tested-by: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <kernel@pengutronix.de> Cc: marc.zyngier@arm.com Cc: jason@lakedaemon.net Link: http://lkml.kernel.org/r/1444677334-12242-5-git-send-email-linux@rempel-privat.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
e59a8451be
commit
25e34b4431
|
@ -28,18 +28,36 @@
|
|||
#include <linux/stmp_device.h>
|
||||
#include <asm/exception.h>
|
||||
|
||||
/*
|
||||
* this device provide 4 offsets for each register:
|
||||
* 0x0 - plain read write mode
|
||||
* 0x4 - set mode, OR logic.
|
||||
* 0x8 - clr mode, XOR logic.
|
||||
* 0xc - togle mode.
|
||||
*/
|
||||
#define SET_REG 4
|
||||
#define CLR_REG 8
|
||||
|
||||
#define HW_ICOLL_VECTOR 0x0000
|
||||
#define HW_ICOLL_LEVELACK 0x0010
|
||||
#define HW_ICOLL_CTRL 0x0020
|
||||
#define HW_ICOLL_STAT_OFFSET 0x0070
|
||||
#define HW_ICOLL_INTERRUPTn_SET(n) (0x0124 + (n) * 0x10)
|
||||
#define HW_ICOLL_INTERRUPTn_CLR(n) (0x0128 + (n) * 0x10)
|
||||
#define BM_ICOLL_INTERRUPTn_ENABLE 0x00000004
|
||||
#define HW_ICOLL_INTERRUPT0 0x0120
|
||||
#define HW_ICOLL_INTERRUPTn(n) ((n) * 0x10)
|
||||
#define BM_ICOLL_INTR_ENABLE BIT(2)
|
||||
#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1
|
||||
|
||||
#define ICOLL_NUM_IRQS 128
|
||||
|
||||
static void __iomem *icoll_base;
|
||||
struct icoll_priv {
|
||||
void __iomem *vector;
|
||||
void __iomem *levelack;
|
||||
void __iomem *ctrl;
|
||||
void __iomem *stat;
|
||||
void __iomem *intr;
|
||||
};
|
||||
|
||||
static struct icoll_priv icoll_priv;
|
||||
static struct irq_domain *icoll_domain;
|
||||
|
||||
static void icoll_ack_irq(struct irq_data *d)
|
||||
|
@ -50,19 +68,19 @@ static void icoll_ack_irq(struct irq_data *d)
|
|||
* BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
|
||||
*/
|
||||
__raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
|
||||
icoll_base + HW_ICOLL_LEVELACK);
|
||||
icoll_priv.levelack);
|
||||
}
|
||||
|
||||
static void icoll_mask_irq(struct irq_data *d)
|
||||
{
|
||||
__raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
|
||||
icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->hwirq));
|
||||
__raw_writel(BM_ICOLL_INTR_ENABLE,
|
||||
icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
|
||||
}
|
||||
|
||||
static void icoll_unmask_irq(struct irq_data *d)
|
||||
{
|
||||
__raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
|
||||
icoll_base + HW_ICOLL_INTERRUPTn_SET(d->hwirq));
|
||||
__raw_writel(BM_ICOLL_INTR_ENABLE,
|
||||
icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
|
||||
}
|
||||
|
||||
static struct irq_chip mxs_icoll_chip = {
|
||||
|
@ -75,8 +93,8 @@ asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
|
|||
{
|
||||
u32 irqnr;
|
||||
|
||||
irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET);
|
||||
__raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR);
|
||||
irqnr = __raw_readl(icoll_priv.stat);
|
||||
__raw_writel(irqnr, icoll_priv.vector);
|
||||
handle_domain_irq(icoll_domain, irqnr, regs);
|
||||
}
|
||||
|
||||
|
@ -93,23 +111,45 @@ static const struct irq_domain_ops icoll_irq_domain_ops = {
|
|||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static void __init icoll_add_domain(struct device_node *np,
|
||||
int num)
|
||||
{
|
||||
icoll_domain = irq_domain_add_linear(np, num,
|
||||
&icoll_irq_domain_ops, NULL);
|
||||
|
||||
if (!icoll_domain)
|
||||
panic("%s: unable to create irq domain", np->full_name);
|
||||
}
|
||||
|
||||
static void __iomem * __init icoll_init_iobase(struct device_node *np)
|
||||
{
|
||||
void __iomem *icoll_base;
|
||||
|
||||
icoll_base = of_io_request_and_map(np, 0, np->name);
|
||||
if (!icoll_base)
|
||||
panic("%s: unable to map resource", np->full_name);
|
||||
return icoll_base;
|
||||
}
|
||||
|
||||
static int __init icoll_of_init(struct device_node *np,
|
||||
struct device_node *interrupt_parent)
|
||||
{
|
||||
icoll_base = of_iomap(np, 0);
|
||||
if (!icoll_base)
|
||||
panic("%s: unable to map resource", np->full_name);
|
||||
void __iomem *icoll_base;
|
||||
|
||||
icoll_base = icoll_init_iobase(np);
|
||||
icoll_priv.vector = icoll_base + HW_ICOLL_VECTOR;
|
||||
icoll_priv.levelack = icoll_base + HW_ICOLL_LEVELACK;
|
||||
icoll_priv.ctrl = icoll_base + HW_ICOLL_CTRL;
|
||||
icoll_priv.stat = icoll_base + HW_ICOLL_STAT_OFFSET;
|
||||
icoll_priv.intr = icoll_base + HW_ICOLL_INTERRUPT0;
|
||||
|
||||
/*
|
||||
* Interrupt Collector reset, which initializes the priority
|
||||
* for each irq to level 0.
|
||||
*/
|
||||
stmp_reset_block(icoll_base + HW_ICOLL_CTRL);
|
||||
stmp_reset_block(icoll_priv.ctrl);
|
||||
|
||||
icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS,
|
||||
&icoll_irq_domain_ops, NULL);
|
||||
if (!icoll_domain)
|
||||
panic("%s: unable to create irqdomain", np->full_name);
|
||||
icoll_add_domain(np, ICOLL_NUM_IRQS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue