Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * $Id: setup.c,v 1.4 2003/08/03 03:05:10 lethal Exp $ | |
3 | * | |
4 | * Setup and IRQ handling code for the HD64465 companion chip. | |
5 | * by Greg Banks <gbanks@pocketpenguins.com> | |
6 | * Copyright (c) 2000 PocketPenguins Inc | |
7 | * | |
8 | * Derived from setup_hd64461.c which bore the message: | |
9 | * Copyright (C) 2000 YAEGASHI Takeshi | |
10 | */ | |
11 | ||
12 | #include <linux/config.h> | |
13 | #include <linux/sched.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/param.h> | |
17 | #include <linux/ioport.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/irq.h> | |
21 | ||
22 | #include <asm/io.h> | |
23 | #include <asm/irq.h> | |
24 | ||
25 | #include <asm/hd64465/hd64465.h> | |
26 | ||
27 | static void disable_hd64465_irq(unsigned int irq) | |
28 | { | |
29 | unsigned long flags; | |
30 | unsigned short nimr; | |
31 | unsigned short mask = 1 << (irq - HD64465_IRQ_BASE); | |
32 | ||
33 | pr_debug("disable_hd64465_irq(%d): mask=%x\n", irq, mask); | |
34 | local_irq_save(flags); | |
35 | nimr = inw(HD64465_REG_NIMR); | |
36 | nimr |= mask; | |
37 | outw(nimr, HD64465_REG_NIMR); | |
38 | local_irq_restore(flags); | |
39 | } | |
40 | ||
41 | ||
42 | static void enable_hd64465_irq(unsigned int irq) | |
43 | { | |
44 | unsigned long flags; | |
45 | unsigned short nimr; | |
46 | unsigned short mask = 1 << (irq - HD64465_IRQ_BASE); | |
47 | ||
48 | pr_debug("enable_hd64465_irq(%d): mask=%x\n", irq, mask); | |
49 | local_irq_save(flags); | |
50 | nimr = inw(HD64465_REG_NIMR); | |
51 | nimr &= ~mask; | |
52 | outw(nimr, HD64465_REG_NIMR); | |
53 | local_irq_restore(flags); | |
54 | } | |
55 | ||
56 | ||
57 | static void mask_and_ack_hd64465(unsigned int irq) | |
58 | { | |
59 | disable_hd64465_irq(irq); | |
60 | } | |
61 | ||
62 | ||
63 | static void end_hd64465_irq(unsigned int irq) | |
64 | { | |
65 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | |
66 | enable_hd64465_irq(irq); | |
67 | } | |
68 | ||
69 | ||
70 | static unsigned int startup_hd64465_irq(unsigned int irq) | |
71 | { | |
72 | enable_hd64465_irq(irq); | |
73 | return 0; | |
74 | } | |
75 | ||
76 | ||
77 | static void shutdown_hd64465_irq(unsigned int irq) | |
78 | { | |
79 | disable_hd64465_irq(irq); | |
80 | } | |
81 | ||
82 | ||
83 | static struct hw_interrupt_type hd64465_irq_type = { | |
84 | .typename = "HD64465-IRQ", | |
85 | .startup = startup_hd64465_irq, | |
86 | .shutdown = shutdown_hd64465_irq, | |
87 | .enable = enable_hd64465_irq, | |
88 | .disable = disable_hd64465_irq, | |
89 | .ack = mask_and_ack_hd64465, | |
90 | .end = end_hd64465_irq, | |
91 | }; | |
92 | ||
93 | ||
94 | static irqreturn_t hd64465_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |
95 | { | |
96 | printk(KERN_INFO | |
97 | "HD64465: spurious interrupt, nirr: 0x%x nimr: 0x%x\n", | |
98 | inw(HD64465_REG_NIRR), inw(HD64465_REG_NIMR)); | |
99 | ||
100 | return IRQ_NONE; | |
101 | } | |
102 | ||
103 | ||
104 | /*====================================================*/ | |
105 | ||
106 | /* | |
107 | * Support for a secondary IRQ demux step. This is necessary | |
108 | * because the HD64465 presents a very thin interface to the | |
109 | * PCMCIA bus; a lot of features (such as remapping interrupts) | |
110 | * normally done in hardware by other PCMCIA host bridges is | |
111 | * instead done in software. | |
112 | */ | |
113 | static struct | |
114 | { | |
115 | int (*func)(int, void *); | |
116 | void *dev; | |
117 | } hd64465_demux[HD64465_IRQ_NUM]; | |
118 | ||
119 | void hd64465_register_irq_demux(int irq, | |
120 | int (*demux)(int irq, void *dev), void *dev) | |
121 | { | |
122 | hd64465_demux[irq - HD64465_IRQ_BASE].func = demux; | |
123 | hd64465_demux[irq - HD64465_IRQ_BASE].dev = dev; | |
124 | } | |
125 | EXPORT_SYMBOL(hd64465_register_irq_demux); | |
126 | ||
127 | void hd64465_unregister_irq_demux(int irq) | |
128 | { | |
129 | hd64465_demux[irq - HD64465_IRQ_BASE].func = 0; | |
130 | } | |
131 | EXPORT_SYMBOL(hd64465_unregister_irq_demux); | |
132 | ||
133 | ||
134 | ||
135 | int hd64465_irq_demux(int irq) | |
136 | { | |
137 | if (irq == CONFIG_HD64465_IRQ) { | |
138 | unsigned short i, bit; | |
139 | unsigned short nirr = inw(HD64465_REG_NIRR); | |
140 | unsigned short nimr = inw(HD64465_REG_NIMR); | |
141 | ||
142 | pr_debug("hd64465_irq_demux, nirr=%04x, nimr=%04x\n", nirr, nimr); | |
143 | nirr &= ~nimr; | |
144 | for (bit = 1, i = 0 ; i < HD64465_IRQ_NUM ; bit <<= 1, i++) | |
145 | if (nirr & bit) | |
146 | break; | |
147 | ||
148 | if (i < HD64465_IRQ_NUM) { | |
149 | irq = HD64465_IRQ_BASE + i; | |
150 | if (hd64465_demux[i].func != 0) | |
151 | irq = hd64465_demux[i].func(irq, hd64465_demux[i].dev); | |
152 | } | |
153 | } | |
154 | return irq; | |
155 | } | |
156 | ||
157 | static struct irqaction irq0 = { hd64465_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "HD64465", NULL, NULL}; | |
158 | ||
159 | ||
160 | static int __init setup_hd64465(void) | |
161 | { | |
162 | int i; | |
163 | unsigned short rev; | |
164 | unsigned short smscr; | |
165 | ||
166 | if (!MACH_HD64465) | |
167 | return 0; | |
168 | ||
169 | printk(KERN_INFO "HD64465 configured at 0x%x on irq %d(mapped into %d to %d)\n", | |
170 | CONFIG_HD64465_IOBASE, | |
171 | CONFIG_HD64465_IRQ, | |
172 | HD64465_IRQ_BASE, | |
173 | HD64465_IRQ_BASE+HD64465_IRQ_NUM-1); | |
174 | ||
175 | if (inw(HD64465_REG_SDID) != HD64465_SDID) { | |
176 | printk(KERN_ERR "HD64465 device ID not found, check base address\n"); | |
177 | } | |
178 | ||
179 | rev = inw(HD64465_REG_SRR); | |
180 | printk(KERN_INFO "HD64465 hardware revision %d.%d\n", (rev >> 8) & 0xff, rev & 0xff); | |
181 | ||
182 | outw(0xffff, HD64465_REG_NIMR); /* mask all interrupts */ | |
183 | ||
184 | for (i = 0; i < HD64465_IRQ_NUM ; i++) { | |
d1bef4ed | 185 | irq_desc[HD64465_IRQ_BASE + i].chip = &hd64465_irq_type; |
1da177e4 LT |
186 | } |
187 | ||
188 | setup_irq(CONFIG_HD64465_IRQ, &irq0); | |
189 | ||
190 | #ifdef CONFIG_SERIAL | |
191 | /* wake up the UART from STANDBY at this point */ | |
192 | smscr = inw(HD64465_REG_SMSCR); | |
193 | outw(smscr & (~HD64465_SMSCR_UARTST), HD64465_REG_SMSCR); | |
194 | ||
195 | /* remap IO ports for first ISA serial port to HD64465 UART */ | |
196 | hd64465_port_map(0x3f8, 8, CONFIG_HD64465_IOBASE + 0x8000, 1); | |
197 | #endif | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | module_init(setup_hd64465); |