diff options
Diffstat (limited to 'arch/m68k/mac/psc.c')
-rw-r--r-- | arch/m68k/mac/psc.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c new file mode 100644 index 000000000000..e72384e43a1e --- /dev/null +++ b/arch/m68k/mac/psc.c | |||
@@ -0,0 +1,197 @@ | |||
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 | } | ||