1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public |
3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. |
5 | * |
6 | * Copyright (C) 1992 Linus Torvalds |
7 | * Copyright (C) 1994 - 2001, 2003, 07 Ralf Baechle |
8 | */ |
9 | #include <linux/clockchips.h> |
10 | #include <linux/i8253.h> |
11 | #include <linux/init.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/smp.h> |
15 | #include <linux/spinlock.h> |
16 | #include <linux/irq.h> |
17 | #include <linux/pgtable.h> |
18 | |
19 | #include <asm/irq_cpu.h> |
20 | #include <asm/i8259.h> |
21 | #include <asm/io.h> |
22 | #include <asm/jazz.h> |
23 | #include <asm/tlbmisc.h> |
24 | |
25 | static DEFINE_RAW_SPINLOCK(r4030_lock); |
26 | |
27 | static void enable_r4030_irq(struct irq_data *d) |
28 | { |
29 | unsigned int mask = 1 << (d->irq - JAZZ_IRQ_START); |
30 | unsigned long flags; |
31 | |
32 | raw_spin_lock_irqsave(&r4030_lock, flags); |
33 | mask |= r4030_read_reg16(JAZZ_IO_IRQ_ENABLE); |
34 | r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, mask); |
35 | raw_spin_unlock_irqrestore(&r4030_lock, flags); |
36 | } |
37 | |
38 | void disable_r4030_irq(struct irq_data *d) |
39 | { |
40 | unsigned int mask = ~(1 << (d->irq - JAZZ_IRQ_START)); |
41 | unsigned long flags; |
42 | |
43 | raw_spin_lock_irqsave(&r4030_lock, flags); |
44 | mask &= r4030_read_reg16(JAZZ_IO_IRQ_ENABLE); |
45 | r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, mask); |
46 | raw_spin_unlock_irqrestore(&r4030_lock, flags); |
47 | } |
48 | |
49 | static struct irq_chip r4030_irq_type = { |
50 | .name = "R4030" , |
51 | .irq_mask = disable_r4030_irq, |
52 | .irq_unmask = enable_r4030_irq, |
53 | }; |
54 | |
55 | void __init init_r4030_ints(void) |
56 | { |
57 | int i; |
58 | |
59 | for (i = JAZZ_IRQ_START; i <= JAZZ_IRQ_END; i++) |
60 | irq_set_chip_and_handler(irq: i, chip: &r4030_irq_type, handle: handle_level_irq); |
61 | |
62 | r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, 0); |
63 | r4030_read_reg16(JAZZ_IO_IRQ_SOURCE); /* clear pending IRQs */ |
64 | r4030_read_reg32(JAZZ_R4030_INVAL_ADDR); /* clear error bits */ |
65 | } |
66 | |
67 | /* |
68 | * On systems with i8259-style interrupt controllers we assume for |
69 | * driver compatibility reasons interrupts 0 - 15 to be the i8259 |
70 | * interrupts even if the hardware uses a different interrupt numbering. |
71 | */ |
72 | void __init arch_init_irq(void) |
73 | { |
74 | /* |
75 | * this is a hack to get back the still needed wired mapping |
76 | * killed by init_mm() |
77 | */ |
78 | |
79 | /* Map 0xe0000000 -> 0x0:800005C0, 0xe0010000 -> 0x1:30000580 */ |
80 | add_wired_entry(0x02000017, 0x03c00017, 0xe0000000, PM_64K); |
81 | /* Map 0xe2000000 -> 0x0:900005C0, 0xe3010000 -> 0x0:910005C0 */ |
82 | add_wired_entry(0x02400017, 0x02440017, 0xe2000000, PM_16M); |
83 | /* Map 0xe4000000 -> 0x0:600005C0, 0xe4100000 -> 400005C0 */ |
84 | add_wired_entry(0x01800017, 0x01000017, 0xe4000000, PM_4M); |
85 | |
86 | init_i8259_irqs(); /* Integrated i8259 */ |
87 | mips_cpu_irq_init(); |
88 | init_r4030_ints(); |
89 | |
90 | change_c0_status(ST0_IM, IE_IRQ2 | IE_IRQ1); |
91 | } |
92 | |
93 | asmlinkage void plat_irq_dispatch(void) |
94 | { |
95 | unsigned int pending = read_c0_cause() & read_c0_status(); |
96 | unsigned int irq; |
97 | |
98 | if (pending & IE_IRQ4) { |
99 | r4030_read_reg32(JAZZ_TIMER_REGISTER); |
100 | do_IRQ(JAZZ_TIMER_IRQ); |
101 | } else if (pending & IE_IRQ2) { |
102 | irq = *(volatile u8 *)JAZZ_EISA_IRQ_ACK; |
103 | do_IRQ(irq); |
104 | } else if (pending & IE_IRQ1) { |
105 | irq = *(volatile u8 *)JAZZ_IO_IRQ_SOURCE >> 2; |
106 | if (likely(irq > 0)) |
107 | do_IRQ(irq + JAZZ_IRQ_START - 1); |
108 | else |
109 | panic(fmt: "Unimplemented loc_no_irq handler" ); |
110 | } |
111 | } |
112 | |
113 | struct clock_event_device r4030_clockevent = { |
114 | .name = "r4030" , |
115 | .features = CLOCK_EVT_FEAT_PERIODIC, |
116 | .rating = 300, |
117 | .irq = JAZZ_TIMER_IRQ, |
118 | }; |
119 | |
120 | static irqreturn_t r4030_timer_interrupt(int irq, void *dev_id) |
121 | { |
122 | struct clock_event_device *cd = dev_id; |
123 | |
124 | cd->event_handler(cd); |
125 | return IRQ_HANDLED; |
126 | } |
127 | |
128 | void __init plat_time_init(void) |
129 | { |
130 | struct clock_event_device *cd = &r4030_clockevent; |
131 | unsigned int cpu = smp_processor_id(); |
132 | |
133 | BUG_ON(HZ != 100); |
134 | |
135 | cd->cpumask = cpumask_of(cpu); |
136 | clockevents_register_device(dev: cd); |
137 | if (request_irq(irq: JAZZ_TIMER_IRQ, handler: r4030_timer_interrupt, IRQF_TIMER, |
138 | name: "R4030 timer" , dev: cd)) |
139 | pr_err("Failed to register R4030 timer interrupt\n" ); |
140 | |
141 | /* |
142 | * Set clock to 100Hz. |
143 | * |
144 | * The R4030 timer receives an input clock of 1kHz which is divided by |
145 | * a programmable 4-bit divider. This makes it fairly inflexible. |
146 | */ |
147 | r4030_write_reg32(JAZZ_TIMER_INTERVAL, 9); |
148 | setup_pit_timer(); |
149 | } |
150 | |