summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-davinci/irq.c
blob: eadbb9d7541570c5a997fffb79d107dccec98cbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Copyright (C) 2006, 2019 Texas Instruments.
//
// Interrupt handler for DaVinci boards.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/irqdomain.h>

#include <mach/hardware.h>
#include <mach/cputype.h>
#include <mach/common.h>
#include <asm/mach/irq.h>
#include <asm/exception.h>

#include "irqs.h"

#define FIQ_REG0_OFFSET		0x0000
#define FIQ_REG1_OFFSET		0x0004
#define IRQ_REG0_OFFSET		0x0008
#define IRQ_REG1_OFFSET		0x000C
#define IRQ_IRQENTRY_OFFSET	0x0014
#define IRQ_ENT_REG0_OFFSET	0x0018
#define IRQ_ENT_REG1_OFFSET	0x001C
#define IRQ_INCTL_REG_OFFSET	0x0020
#define IRQ_EABASE_REG_OFFSET	0x0024
#define IRQ_INTPRI0_REG_OFFSET	0x0030
#define IRQ_INTPRI7_REG_OFFSET	0x004C

static void __iomem *davinci_intc_base;
static struct irq_domain *davinci_irq_domain;

static inline void davinci_irq_writel(unsigned long value, int offset)
{
	__raw_writel(value, davinci_intc_base + offset);
}

static inline unsigned long davinci_irq_readl(int offset)
{
	return readl_relaxed(davinci_intc_base + offset);
}

static __init void
davinci_irq_setup_gc(void __iomem *base,
		     unsigned int irq_start, unsigned int num)
{
	struct irq_chip_generic *gc;
	struct irq_chip_type *ct;

	gc = irq_get_domain_generic_chip(davinci_irq_domain, irq_start);
	gc->reg_base = base;
	gc->irq_base = irq_start;

	ct = gc->chip_types;
	ct->chip.irq_ack = irq_gc_ack_set_bit;
	ct->chip.irq_mask = irq_gc_mask_clr_bit;
	ct->chip.irq_unmask = irq_gc_mask_set_bit;

	ct->regs.ack = IRQ_REG0_OFFSET;
	ct->regs.mask = IRQ_ENT_REG0_OFFSET;
	irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
			       IRQ_NOREQUEST | IRQ_NOPROBE, 0);
}

static asmlinkage void __exception_irq_entry
davinci_handle_irq(struct pt_regs *regs)
{
	int irqnr = davinci_irq_readl(IRQ_IRQENTRY_OFFSET);

	/*
	 * Use the formula for entry vector index generation from section
	 * 8.3.3 of the manual.
	 */
	irqnr >>= 2;
	irqnr -= 1;

	handle_domain_irq(davinci_irq_domain, irqnr, regs);
}

/* ARM Interrupt Controller Initialization */
void __init davinci_irq_init(void)
{
	unsigned i, j;
	const u8 *davinci_def_priorities = davinci_soc_info.intc_irq_prios;
	int ret, irq_base;

	davinci_intc_base = ioremap(davinci_soc_info.intc_base, SZ_4K);
	if (WARN_ON(!davinci_intc_base))
		return;

	/* Clear all interrupt requests */
	davinci_irq_writel(~0x0, FIQ_REG0_OFFSET);
	davinci_irq_writel(~0x0, FIQ_REG1_OFFSET);
	davinci_irq_writel(~0x0, IRQ_REG0_OFFSET);
	davinci_irq_writel(~0x0, IRQ_REG1_OFFSET);

	/* Disable all interrupts */
	davinci_irq_writel(0x0, IRQ_ENT_REG0_OFFSET);
	davinci_irq_writel(0x0, IRQ_ENT_REG1_OFFSET);

	/* Interrupts disabled immediately, IRQ entry reflects all */
	davinci_irq_writel(0x0, IRQ_INCTL_REG_OFFSET);

	/* we don't use the hardware vector table, just its entry addresses */
	davinci_irq_writel(0, IRQ_EABASE_REG_OFFSET);

	/* Clear all interrupt requests */
	davinci_irq_writel(~0x0, FIQ_REG0_OFFSET);
	davinci_irq_writel(~0x0, FIQ_REG1_OFFSET);
	davinci_irq_writel(~0x0, IRQ_REG0_OFFSET);
	davinci_irq_writel(~0x0, IRQ_REG1_OFFSET);

	for (i = IRQ_INTPRI0_REG_OFFSET; i <= IRQ_INTPRI7_REG_OFFSET; i += 4) {
		u32		pri;

		for (j = 0, pri = 0; j < 32; j += 4, davinci_def_priorities++)
			pri |= (*davinci_def_priorities & 0x07) << j;
		davinci_irq_writel(pri, i);
	}

	irq_base = irq_alloc_descs(-1, 0, davinci_soc_info.intc_irq_num, 0);
	if (WARN_ON(irq_base < 0))
		return;

	davinci_irq_domain = irq_domain_add_legacy(NULL,
					davinci_soc_info.intc_irq_num,
					irq_base, 0, &irq_domain_simple_ops,
					NULL);
	if (WARN_ON(!davinci_irq_domain))
		return;

	ret = irq_alloc_domain_generic_chips(davinci_irq_domain, 32, 1,
					    "AINTC", handle_edge_irq,
					    IRQ_NOREQUEST | IRQ_NOPROBE, 0, 0);
	if (WARN_ON(ret))
		return;

	for (i = 0, j = 0; i < davinci_soc_info.intc_irq_num; i += 32, j += 0x04)
		davinci_irq_setup_gc(davinci_intc_base + j, irq_base + i, 32);

	irq_set_handler(DAVINCI_INTC_IRQ(IRQ_TINT1_TINT34), handle_level_irq);
	set_handle_irq(davinci_handle_irq);
}
OpenPOWER on IntegriCloud