]>
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> | |
ddc7fd25 | 21 | #include <linux/irq.h> |
1da177e4 LT |
22 | |
23 | #include <asm/traps.h> | |
1da177e4 LT |
24 | #include <asm/macintosh.h> |
25 | #include <asm/macints.h> | |
26 | #include <asm/mac_psc.h> | |
27 | ||
28 | #define DEBUG_PSC | |
29 | ||
1da177e4 | 30 | volatile __u8 *psc; |
a77cdaaf | 31 | EXPORT_SYMBOL_GPL(psc); |
1da177e4 | 32 | |
1da177e4 LT |
33 | /* |
34 | * Debugging dump, used in various places to see what's going on. | |
35 | */ | |
36 | ||
8dfbdf4a | 37 | static void psc_debug_dump(void) |
1da177e4 LT |
38 | { |
39 | int i; | |
40 | ||
70bc53b4 GU |
41 | if (!psc) |
42 | return; | |
43 | ||
1da177e4 LT |
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 | ||
a4df02a2 | 58 | static __init void psc_dma_die_die_die(void) |
1da177e4 LT |
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; | |
1da177e4 LT |
85 | return; |
86 | } | |
87 | ||
88 | /* | |
89 | * The PSC is always at the same spot, but using psc | |
a34f0b31 | 90 | * keeps things consistent with the psc_xxxx functions. |
1da177e4 LT |
91 | */ |
92 | ||
93 | psc = (void *) PSC_BASE; | |
1da177e4 LT |
94 | |
95 | printk("PSC detected at %p\n", psc); | |
96 | ||
97 | psc_dma_die_die_die(); | |
98 | ||
99 | #ifdef DEBUG_PSC | |
100 | psc_debug_dump(); | |
101 | #endif | |
102 | /* | |
103 | * Mask and clear all possible interrupts | |
104 | */ | |
105 | ||
106 | for (i = 0x30 ; i < 0x70 ; i += 0x10) { | |
107 | psc_write_byte(pIERbase + i, 0x0F); | |
108 | psc_write_byte(pIFRbase + i, 0x0F); | |
109 | } | |
110 | } | |
111 | ||
112 | /* | |
9145db56 | 113 | * PSC interrupt handler. It's a lot like the VIA interrupt handler. |
1da177e4 LT |
114 | */ |
115 | ||
bd0b9ac4 | 116 | static void psc_irq(struct irq_desc *desc) |
1da177e4 | 117 | { |
9145db56 | 118 | unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc); |
625b86ad | 119 | unsigned int irq = irq_desc_get_irq(desc); |
9145db56 GU |
120 | int pIFR = pIFRbase + offset; |
121 | int pIER = pIERbase + offset; | |
122 | int irq_num; | |
123 | unsigned char irq_bit, events; | |
1da177e4 | 124 | |
9145db56 GU |
125 | events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF; |
126 | if (!events) | |
127 | return; | |
128 | ||
129 | irq_num = irq << 3; | |
130 | irq_bit = 1; | |
131 | do { | |
132 | if (events & irq_bit) { | |
133 | psc_write_byte(pIFR, irq_bit); | |
134 | generic_handle_irq(irq_num); | |
135 | } | |
136 | irq_num++; | |
137 | irq_bit <<= 1; | |
138 | } while (events >= irq_bit); | |
139 | } | |
9145db56 GU |
140 | |
141 | /* | |
142 | * Register the PSC interrupt dispatchers for autovector interrupts 3-6. | |
143 | */ | |
144 | ||
145 | void __init psc_register_interrupts(void) | |
146 | { | |
09c5cb94 TG |
147 | irq_set_chained_handler_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30); |
148 | irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40); | |
149 | irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50); | |
150 | irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (void *)0x60); | |
9145db56 | 151 | } |
1da177e4 LT |
152 | |
153 | void psc_irq_enable(int irq) { | |
154 | int irq_src = IRQ_SRC(irq); | |
155 | int irq_idx = IRQ_IDX(irq); | |
156 | int pIER = pIERbase + (irq_src << 4); | |
157 | ||
1da177e4 LT |
158 | psc_write_byte(pIER, (1 << irq_idx) | 0x80); |
159 | } | |
160 | ||
161 | void psc_irq_disable(int irq) { | |
162 | int irq_src = IRQ_SRC(irq); | |
163 | int irq_idx = IRQ_IDX(irq); | |
164 | int pIER = pIERbase + (irq_src << 4); | |
165 | ||
1da177e4 LT |
166 | psc_write_byte(pIER, 1 << irq_idx); |
167 | } |