]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Apple Peripheral System Controller (PSC) | |
3 | * | |
4 | * The PSC is used on the AV Macs to control IO functions not handled | |
5 | * by the VIAs (Ethernet, DSP, SCC). | |
6 | * | |
7 | * TO DO: | |
8 | * | |
9 | * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be | |
10 | * persisant interrupt conditions in those registers and I have no idea what | |
11 | * they are. Granted it doesn't affect since we're not enabling any interrupts | |
12 | * on those levels at the moment, but it would be nice to know. I have a feeling | |
13 | * they aren't actually interrupt lines but data lines (to the DSP?) | |
14 | */ | |
15 | ||
16 | #include <linux/types.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/init.h> | |
21 | ||
22 | #include <asm/traps.h> | |
23 | #include <asm/bootinfo.h> | |
24 | #include <asm/macintosh.h> | |
25 | #include <asm/macints.h> | |
26 | #include <asm/mac_psc.h> | |
27 | ||
28 | #define DEBUG_PSC | |
29 | ||
30 | int psc_present; | |
31 | volatile __u8 *psc; | |
32 | ||
33 | irqreturn_t psc_irq(int, void *, struct pt_regs *); | |
34 | ||
35 | /* | |
36 | * Debugging dump, used in various places to see what's going on. | |
37 | */ | |
38 | ||
39 | void psc_debug_dump(void) | |
40 | { | |
41 | int i; | |
42 | ||
43 | if (!psc_present) return; | |
44 | for (i = 0x30 ; i < 0x70 ; i += 0x10) { | |
45 | printk("PSC #%d: IFR = 0x%02X IER = 0x%02X\n", | |
46 | i >> 4, | |
47 | (int) psc_read_byte(pIFRbase + i), | |
48 | (int) psc_read_byte(pIERbase + i)); | |
49 | } | |
50 | } | |
51 | ||
52 | /* | |
53 | * Try to kill all DMA channels on the PSC. Not sure how this his | |
54 | * supposed to work; this is code lifted from macmace.c and then | |
55 | * expanded to cover what I think are the other 7 channels. | |
56 | */ | |
57 | ||
58 | void psc_dma_die_die_die(void) | |
59 | { | |
60 | int i; | |
61 | ||
62 | printk("Killing all PSC DMA channels..."); | |
63 | for (i = 0 ; i < 9 ; i++) { | |
64 | psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800); | |
65 | psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000); | |
66 | psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100); | |
67 | psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100); | |
68 | } | |
69 | printk("done!\n"); | |
70 | } | |
71 | ||
72 | /* | |
73 | * Initialize the PSC. For now this just involves shutting down all | |
74 | * interrupt sources using the IERs. | |
75 | */ | |
76 | ||
77 | void __init psc_init(void) | |
78 | { | |
79 | int i; | |
80 | ||
81 | if (macintosh_config->ident != MAC_MODEL_C660 | |
82 | && macintosh_config->ident != MAC_MODEL_Q840) | |
83 | { | |
84 | psc = NULL; | |
85 | psc_present = 0; | |
86 | return; | |
87 | } | |
88 | ||
89 | /* | |
90 | * The PSC is always at the same spot, but using psc | |
91 | * keeps things consisant with the psc_xxxx functions. | |
92 | */ | |
93 | ||
94 | psc = (void *) PSC_BASE; | |
95 | psc_present = 1; | |
96 | ||
97 | printk("PSC detected at %p\n", psc); | |
98 | ||
99 | psc_dma_die_die_die(); | |
100 | ||
101 | #ifdef DEBUG_PSC | |
102 | psc_debug_dump(); | |
103 | #endif | |
104 | /* | |
105 | * Mask and clear all possible interrupts | |
106 | */ | |
107 | ||
108 | for (i = 0x30 ; i < 0x70 ; i += 0x10) { | |
109 | psc_write_byte(pIERbase + i, 0x0F); | |
110 | psc_write_byte(pIFRbase + i, 0x0F); | |
111 | } | |
112 | } | |
113 | ||
114 | /* | |
115 | * Register the PSC interrupt dispatchers for autovector interrupts 3-6. | |
116 | */ | |
117 | ||
118 | void __init psc_register_interrupts(void) | |
119 | { | |
120 | cpu_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3", (void *) 0x30); | |
121 | cpu_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4", (void *) 0x40); | |
122 | cpu_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5", (void *) 0x50); | |
123 | cpu_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6", (void *) 0x60); | |
124 | } | |
125 | ||
126 | /* | |
127 | * PSC interrupt handler. It's a lot like the VIA interrupt handler. | |
128 | */ | |
129 | ||
130 | irqreturn_t psc_irq(int irq, void *dev_id, struct pt_regs *regs) | |
131 | { | |
132 | int pIFR = pIFRbase + ((int) dev_id); | |
133 | int pIER = pIERbase + ((int) dev_id); | |
134 | int base_irq; | |
135 | int irq_bit,i; | |
136 | unsigned char events; | |
137 | ||
138 | base_irq = irq << 3; | |
139 | ||
140 | #ifdef DEBUG_IRQS | |
141 | printk("psc_irq: irq %d pIFR = 0x%02X pIER = 0x%02X\n", | |
142 | irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER)); | |
143 | #endif | |
144 | ||
145 | events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF; | |
146 | if (!events) | |
147 | return IRQ_NONE; | |
148 | ||
149 | for (i = 0, irq_bit = 1 ; i < 4 ; i++, irq_bit <<= 1) { | |
150 | if (events & irq_bit) { | |
151 | psc_write_byte(pIER, irq_bit); | |
152 | mac_do_irq_list(base_irq + i, regs); | |
153 | psc_write_byte(pIFR, irq_bit); | |
154 | psc_write_byte(pIER, irq_bit | 0x80); | |
155 | } | |
156 | } | |
157 | return IRQ_HANDLED; | |
158 | } | |
159 | ||
160 | void psc_irq_enable(int irq) { | |
161 | int irq_src = IRQ_SRC(irq); | |
162 | int irq_idx = IRQ_IDX(irq); | |
163 | int pIER = pIERbase + (irq_src << 4); | |
164 | ||
165 | #ifdef DEBUG_IRQUSE | |
166 | printk("psc_irq_enable(%d)\n", irq); | |
167 | #endif | |
168 | psc_write_byte(pIER, (1 << irq_idx) | 0x80); | |
169 | } | |
170 | ||
171 | void psc_irq_disable(int irq) { | |
172 | int irq_src = IRQ_SRC(irq); | |
173 | int irq_idx = IRQ_IDX(irq); | |
174 | int pIER = pIERbase + (irq_src << 4); | |
175 | ||
176 | #ifdef DEBUG_IRQUSE | |
177 | printk("psc_irq_disable(%d)\n", irq); | |
178 | #endif | |
179 | psc_write_byte(pIER, 1 << irq_idx); | |
180 | } | |
181 | ||
182 | void psc_irq_clear(int irq) { | |
183 | int irq_src = IRQ_SRC(irq); | |
184 | int irq_idx = IRQ_IDX(irq); | |
185 | int pIFR = pIERbase + (irq_src << 4); | |
186 | ||
187 | psc_write_byte(pIFR, 1 << irq_idx); | |
188 | } | |
189 | ||
190 | int psc_irq_pending(int irq) | |
191 | { | |
192 | int irq_src = IRQ_SRC(irq); | |
193 | int irq_idx = IRQ_IDX(irq); | |
194 | int pIFR = pIERbase + (irq_src << 4); | |
195 | ||
196 | return psc_read_byte(pIFR) & (1 << irq_idx); | |
197 | } |