]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * $Id: setup.c,v 1.5 2004/03/16 00:07:50 lethal Exp $ | |
3 | * Copyright (C) 2000 YAEGASHI Takeshi | |
4 | * Hitachi HD64461 companion chip support | |
5 | */ | |
6 | ||
7 | #include <linux/config.h> | |
8 | #include <linux/sched.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/param.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/irq.h> | |
15 | ||
16 | #include <asm/io.h> | |
17 | #include <asm/irq.h> | |
18 | ||
19 | #include <asm/hd64461/hd64461.h> | |
20 | ||
21 | static void disable_hd64461_irq(unsigned int irq) | |
22 | { | |
23 | unsigned long flags; | |
24 | unsigned short nimr; | |
25 | unsigned short mask = 1 << (irq - HD64461_IRQBASE); | |
26 | ||
27 | local_irq_save(flags); | |
28 | nimr = inw(HD64461_NIMR); | |
29 | nimr |= mask; | |
30 | outw(nimr, HD64461_NIMR); | |
31 | local_irq_restore(flags); | |
32 | } | |
33 | ||
34 | static void enable_hd64461_irq(unsigned int irq) | |
35 | { | |
36 | unsigned long flags; | |
37 | unsigned short nimr; | |
38 | unsigned short mask = 1 << (irq - HD64461_IRQBASE); | |
39 | ||
40 | local_irq_save(flags); | |
41 | nimr = inw(HD64461_NIMR); | |
42 | nimr &= ~mask; | |
43 | outw(nimr, HD64461_NIMR); | |
44 | local_irq_restore(flags); | |
45 | } | |
46 | ||
47 | static void mask_and_ack_hd64461(unsigned int irq) | |
48 | { | |
49 | disable_hd64461_irq(irq); | |
50 | #ifdef CONFIG_HD64461_ENABLER | |
51 | if (irq == HD64461_IRQBASE + 13) | |
52 | outb(0x00, HD64461_PCC1CSCR); | |
53 | #endif | |
54 | } | |
55 | ||
56 | static void end_hd64461_irq(unsigned int irq) | |
57 | { | |
58 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | |
59 | enable_hd64461_irq(irq); | |
60 | } | |
61 | ||
62 | static unsigned int startup_hd64461_irq(unsigned int irq) | |
63 | { | |
64 | enable_hd64461_irq(irq); | |
65 | return 0; | |
66 | } | |
67 | ||
68 | static void shutdown_hd64461_irq(unsigned int irq) | |
69 | { | |
70 | disable_hd64461_irq(irq); | |
71 | } | |
72 | ||
73 | static struct hw_interrupt_type hd64461_irq_type = { | |
74 | .typename = "HD64461-IRQ", | |
75 | .startup = startup_hd64461_irq, | |
76 | .shutdown = shutdown_hd64461_irq, | |
77 | .enable = enable_hd64461_irq, | |
78 | .disable = disable_hd64461_irq, | |
79 | .ack = mask_and_ack_hd64461, | |
80 | .end = end_hd64461_irq, | |
81 | }; | |
82 | ||
83 | static irqreturn_t hd64461_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |
84 | { | |
85 | printk(KERN_INFO | |
86 | "HD64461: spurious interrupt, nirr: 0x%x nimr: 0x%x\n", | |
87 | inw(HD64461_NIRR), inw(HD64461_NIMR)); | |
88 | ||
89 | return IRQ_NONE; | |
90 | } | |
91 | ||
92 | static struct { | |
93 | int (*func) (int, void *); | |
94 | void *dev; | |
95 | } hd64461_demux[HD64461_IRQ_NUM]; | |
96 | ||
97 | void hd64461_register_irq_demux(int irq, | |
98 | int (*demux) (int irq, void *dev), void *dev) | |
99 | { | |
100 | hd64461_demux[irq - HD64461_IRQBASE].func = demux; | |
101 | hd64461_demux[irq - HD64461_IRQBASE].dev = dev; | |
102 | } | |
103 | ||
104 | EXPORT_SYMBOL(hd64461_register_irq_demux); | |
105 | ||
106 | void hd64461_unregister_irq_demux(int irq) | |
107 | { | |
108 | hd64461_demux[irq - HD64461_IRQBASE].func = 0; | |
109 | } | |
110 | ||
111 | EXPORT_SYMBOL(hd64461_unregister_irq_demux); | |
112 | ||
113 | int hd64461_irq_demux(int irq) | |
114 | { | |
115 | if (irq == CONFIG_HD64461_IRQ) { | |
116 | unsigned short bit; | |
117 | unsigned short nirr = inw(HD64461_NIRR); | |
118 | unsigned short nimr = inw(HD64461_NIMR); | |
119 | int i; | |
120 | ||
121 | nirr &= ~nimr; | |
122 | for (bit = 1, i = 0; i < 16; bit <<= 1, i++) | |
123 | if (nirr & bit) | |
124 | break; | |
125 | if (i == 16) | |
126 | irq = CONFIG_HD64461_IRQ; | |
127 | else { | |
128 | irq = HD64461_IRQBASE + i; | |
129 | if (hd64461_demux[i].func != 0) { | |
130 | irq = hd64461_demux[i].func(irq, hd64461_demux[i].dev); | |
131 | } | |
132 | } | |
133 | } | |
134 | return __irq_demux(irq); | |
135 | } | |
136 | ||
137 | static struct irqaction irq0 = { hd64461_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "HD64461", NULL, NULL }; | |
138 | ||
139 | int __init setup_hd64461(void) | |
140 | { | |
141 | int i; | |
142 | ||
143 | if (!MACH_HD64461) | |
144 | return 0; | |
145 | ||
146 | printk(KERN_INFO | |
147 | "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n", | |
148 | CONFIG_HD64461_IOBASE, CONFIG_HD64461_IRQ, HD64461_IRQBASE, | |
149 | HD64461_IRQBASE + 15); | |
150 | ||
151 | #if defined(CONFIG_CPU_SUBTYPE_SH7709) /* Should be at processor specific part.. */ | |
152 | outw(0x2240, INTC_ICR1); | |
153 | #endif | |
154 | outw(0xffff, HD64461_NIMR); | |
155 | ||
156 | for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) { | |
157 | irq_desc[i].handler = &hd64461_irq_type; | |
158 | } | |
159 | ||
160 | setup_irq(CONFIG_HD64461_IRQ, &irq0); | |
161 | ||
162 | #ifdef CONFIG_HD64461_ENABLER | |
163 | printk(KERN_INFO "HD64461: enabling PCMCIA devices\n"); | |
164 | outb(0x4c, HD64461_PCC1CSCIER); | |
165 | outb(0x00, HD64461_PCC1CSCR); | |
166 | #endif | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | module_init(setup_hd64461); |