~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to arch/m68k/mac/oss.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      OSS handling
 
3
 *      Written by Joshua M. Thompson (funaho@jurai.org)
 
4
 *
 
5
 *
 
6
 *      This chip is used in the IIfx in place of VIA #2. It acts like a fancy
 
7
 *      VIA chip with prorammable interrupt levels.
 
8
 *
 
9
 * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
 
10
 *                recent insights into OSS operational details.
 
11
 * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped
 
12
 *                to mostly match the A/UX interrupt scheme supported on the
 
13
 *                VIA side. Also added support for enabling the ISM irq again
 
14
 *                since we now have a functional IOP manager.
 
15
 */
 
16
 
 
17
#include <linux/types.h>
 
18
#include <linux/kernel.h>
 
19
#include <linux/mm.h>
 
20
#include <linux/delay.h>
 
21
#include <linux/init.h>
 
22
#include <linux/irq.h>
 
23
 
 
24
#include <asm/bootinfo.h>
 
25
#include <asm/macintosh.h>
 
26
#include <asm/macints.h>
 
27
#include <asm/mac_via.h>
 
28
#include <asm/mac_oss.h>
 
29
 
 
30
int oss_present;
 
31
volatile struct mac_oss *oss;
 
32
 
 
33
extern void via1_irq(unsigned int irq, struct irq_desc *desc);
 
34
 
 
35
/*
 
36
 * Initialize the OSS
 
37
 *
 
38
 * The OSS "detection" code is actually in via_init() which is always called
 
39
 * before us. Thus we can count on oss_present being valid on entry.
 
40
 */
 
41
 
 
42
void __init oss_init(void)
 
43
{
 
44
        int i;
 
45
 
 
46
        if (!oss_present) return;
 
47
 
 
48
        oss = (struct mac_oss *) OSS_BASE;
 
49
 
 
50
        /* Disable all interrupts. Unlike a VIA it looks like we    */
 
51
        /* do this by setting the source's interrupt level to zero. */
 
52
 
 
53
        for (i = 0; i <= OSS_NUM_SOURCES; i++) {
 
54
                oss->irq_level[i] = OSS_IRQLEV_DISABLED;
 
55
        }
 
56
        /* If we disable VIA1 here, we never really handle it... */
 
57
        oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
 
58
}
 
59
 
 
60
/*
 
61
 * Initialize OSS for Nubus access
 
62
 */
 
63
 
 
64
void __init oss_nubus_init(void)
 
65
{
 
66
}
 
67
 
 
68
/*
 
69
 * Handle miscellaneous OSS interrupts. Right now that's just sound
 
70
 * and SCSI; everything else is routed to its own autovector IRQ.
 
71
 */
 
72
 
 
73
static void oss_irq(unsigned int irq, struct irq_desc *desc)
 
74
{
 
75
        int events;
 
76
 
 
77
        events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI);
 
78
        if (!events)
 
79
                return;
 
80
 
 
81
#ifdef DEBUG_IRQS
 
82
        if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
 
83
                printk("oss_irq: irq %u events = 0x%04X\n", irq,
 
84
                        (int) oss->irq_pending);
 
85
        }
 
86
#endif
 
87
        /* FIXME: how do you clear a pending IRQ?    */
 
88
 
 
89
        if (events & OSS_IP_SOUND) {
 
90
                oss->irq_pending &= ~OSS_IP_SOUND;
 
91
                /* FIXME: call sound handler */
 
92
        } else if (events & OSS_IP_SCSI) {
 
93
                oss->irq_pending &= ~OSS_IP_SCSI;
 
94
                generic_handle_irq(IRQ_MAC_SCSI);
 
95
        } else {
 
96
                /* FIXME: error check here? */
 
97
        }
 
98
}
 
99
 
 
100
/*
 
101
 * Nubus IRQ handler, OSS style
 
102
 *
 
103
 * Unlike the VIA/RBV this is on its own autovector interrupt level.
 
104
 */
 
105
 
 
106
static void oss_nubus_irq(unsigned int irq, struct irq_desc *desc)
 
107
{
 
108
        int events, irq_bit, i;
 
109
 
 
110
        events = oss->irq_pending & OSS_IP_NUBUS;
 
111
        if (!events)
 
112
                return;
 
113
 
 
114
#ifdef DEBUG_NUBUS_INT
 
115
        if (console_loglevel > 7) {
 
116
                printk("oss_nubus_irq: events = 0x%04X\n", events);
 
117
        }
 
118
#endif
 
119
        /* There are only six slots on the OSS, not seven */
 
120
 
 
121
        i = 6;
 
122
        irq_bit = 0x40;
 
123
        do {
 
124
                --i;
 
125
                irq_bit >>= 1;
 
126
                if (events & irq_bit) {
 
127
                        oss->irq_pending &= ~irq_bit;
 
128
                        generic_handle_irq(NUBUS_SOURCE_BASE + i);
 
129
                }
 
130
        } while(events & (irq_bit - 1));
 
131
}
 
132
 
 
133
/*
 
134
 * Register the OSS and NuBus interrupt dispatchers.
 
135
 */
 
136
 
 
137
void __init oss_register_interrupts(void)
 
138
{
 
139
        irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_irq);
 
140
        irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq);
 
141
        irq_set_chained_handler(OSS_IRQLEV_SOUND, oss_irq);
 
142
        irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq);
 
143
}
 
144
 
 
145
/*
 
146
 * Enable an OSS interrupt
 
147
 *
 
148
 * It looks messy but it's rather straightforward. The switch() statement
 
149
 * just maps the machspec interrupt numbers to the right OSS interrupt
 
150
 * source (if the OSS handles that interrupt) and then sets the interrupt
 
151
 * level for that source to nonzero, thus enabling the interrupt.
 
152
 */
 
153
 
 
154
void oss_irq_enable(int irq) {
 
155
#ifdef DEBUG_IRQUSE
 
156
        printk("oss_irq_enable(%d)\n", irq);
 
157
#endif
 
158
        switch(irq) {
 
159
                case IRQ_MAC_SCC:
 
160
                        oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
 
161
                        break;
 
162
                case IRQ_MAC_ADB:
 
163
                        oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
 
164
                        break;
 
165
                case IRQ_MAC_SCSI:
 
166
                        oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
 
167
                        break;
 
168
                case IRQ_NUBUS_9:
 
169
                case IRQ_NUBUS_A:
 
170
                case IRQ_NUBUS_B:
 
171
                case IRQ_NUBUS_C:
 
172
                case IRQ_NUBUS_D:
 
173
                case IRQ_NUBUS_E:
 
174
                        irq -= NUBUS_SOURCE_BASE;
 
175
                        oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
 
176
                        break;
 
177
#ifdef DEBUG_IRQUSE
 
178
                default:
 
179
                        printk("%s unknown irq %d\n", __func__, irq);
 
180
                        break;
 
181
#endif
 
182
        }
 
183
}
 
184
 
 
185
/*
 
186
 * Disable an OSS interrupt
 
187
 *
 
188
 * Same as above except we set the source's interrupt level to zero,
 
189
 * to disable the interrupt.
 
190
 */
 
191
 
 
192
void oss_irq_disable(int irq) {
 
193
#ifdef DEBUG_IRQUSE
 
194
        printk("oss_irq_disable(%d)\n", irq);
 
195
#endif
 
196
        switch(irq) {
 
197
                case IRQ_MAC_SCC:
 
198
                        oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED;
 
199
                        break;
 
200
                case IRQ_MAC_ADB:
 
201
                        oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED;
 
202
                        break;
 
203
                case IRQ_MAC_SCSI:
 
204
                        oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
 
205
                        break;
 
206
                case IRQ_NUBUS_9:
 
207
                case IRQ_NUBUS_A:
 
208
                case IRQ_NUBUS_B:
 
209
                case IRQ_NUBUS_C:
 
210
                case IRQ_NUBUS_D:
 
211
                case IRQ_NUBUS_E:
 
212
                        irq -= NUBUS_SOURCE_BASE;
 
213
                        oss->irq_level[irq] = OSS_IRQLEV_DISABLED;
 
214
                        break;
 
215
#ifdef DEBUG_IRQUSE
 
216
                default:
 
217
                        printk("%s unknown irq %d\n", __func__, irq);
 
218
                        break;
 
219
#endif
 
220
        }
 
221
}
 
222
 
 
223
/*
 
224
 * Clear an OSS interrupt
 
225
 *
 
226
 * Not sure if this works or not but it's the only method I could
 
227
 * think of based on the contents of the mac_oss structure.
 
228
 */
 
229
 
 
230
void oss_irq_clear(int irq) {
 
231
        /* FIXME: how to do this on OSS? */
 
232
        switch(irq) {
 
233
                case IRQ_MAC_SCC:
 
234
                        oss->irq_pending &= ~OSS_IP_IOPSCC;
 
235
                        break;
 
236
                case IRQ_MAC_ADB:
 
237
                        oss->irq_pending &= ~OSS_IP_IOPISM;
 
238
                        break;
 
239
                case IRQ_MAC_SCSI:
 
240
                        oss->irq_pending &= ~OSS_IP_SCSI;
 
241
                        break;
 
242
                case IRQ_NUBUS_9:
 
243
                case IRQ_NUBUS_A:
 
244
                case IRQ_NUBUS_B:
 
245
                case IRQ_NUBUS_C:
 
246
                case IRQ_NUBUS_D:
 
247
                case IRQ_NUBUS_E:
 
248
                        irq -= NUBUS_SOURCE_BASE;
 
249
                        oss->irq_pending &= ~(1 << irq);
 
250
                        break;
 
251
        }
 
252
}
 
253
 
 
254
/*
 
255
 * Check to see if a specific OSS interrupt is pending
 
256
 */
 
257
 
 
258
int oss_irq_pending(int irq)
 
259
{
 
260
        switch(irq) {
 
261
                case IRQ_MAC_SCC:
 
262
                        return oss->irq_pending & OSS_IP_IOPSCC;
 
263
                        break;
 
264
                case IRQ_MAC_ADB:
 
265
                        return oss->irq_pending & OSS_IP_IOPISM;
 
266
                        break;
 
267
                case IRQ_MAC_SCSI:
 
268
                        return oss->irq_pending & OSS_IP_SCSI;
 
269
                        break;
 
270
                case IRQ_NUBUS_9:
 
271
                case IRQ_NUBUS_A:
 
272
                case IRQ_NUBUS_B:
 
273
                case IRQ_NUBUS_C:
 
274
                case IRQ_NUBUS_D:
 
275
                case IRQ_NUBUS_E:
 
276
                        irq -= NUBUS_SOURCE_BASE;
 
277
                        return oss->irq_pending & (1 << irq);
 
278
                        break;
 
279
        }
 
280
        return 0;
 
281
}