Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/m68k/mac/psc.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Apple Peripheral System Controller (PSC)
4
*
5
* The PSC is used on the AV Macs to control IO functions not handled
6
* by the VIAs (Ethernet, DSP, SCC).
7
*
8
* TO DO:
9
*
10
* Try to figure out what's going on in pIFR5 and pIFR6. There seem to be
11
* persisant interrupt conditions in those registers and I have no idea what
12
* they are. Granted it doesn't affect since we're not enabling any interrupts
13
* on those levels at the moment, but it would be nice to know. I have a feeling
14
* they aren't actually interrupt lines but data lines (to the DSP?)
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/traps.h>
25
#include <asm/macintosh.h>
26
#include <asm/macints.h>
27
#include <asm/mac_psc.h>
28
29
#include "mac.h"
30
31
#define DEBUG_PSC
32
33
volatile __u8 *psc;
34
EXPORT_SYMBOL_GPL(psc);
35
36
/*
37
* Debugging dump, used in various places to see what's going on.
38
*/
39
40
static void psc_debug_dump(void)
41
{
42
int i;
43
44
if (!psc)
45
return;
46
47
for (i = 0x30 ; i < 0x70 ; i += 0x10) {
48
printk(KERN_DEBUG "PSC #%d: IFR = 0x%02X IER = 0x%02X\n",
49
i >> 4,
50
(int) psc_read_byte(pIFRbase + i),
51
(int) psc_read_byte(pIERbase + i));
52
}
53
}
54
55
/*
56
* Try to kill all DMA channels on the PSC. Not sure how this his
57
* supposed to work; this is code lifted from macmace.c and then
58
* expanded to cover what I think are the other 7 channels.
59
*/
60
61
static __init void psc_dma_die_die_die(void)
62
{
63
int i;
64
65
for (i = 0 ; i < 9 ; i++) {
66
psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800);
67
psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000);
68
psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100);
69
psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100);
70
}
71
}
72
73
/*
74
* Initialize the PSC. For now this just involves shutting down all
75
* interrupt sources using the IERs.
76
*/
77
78
void __init psc_init(void)
79
{
80
int i;
81
82
if (macintosh_config->ident != MAC_MODEL_C660
83
&& macintosh_config->ident != MAC_MODEL_Q840)
84
{
85
psc = NULL;
86
return;
87
}
88
89
/*
90
* The PSC is always at the same spot, but using psc
91
* keeps things consistent with the psc_xxxx functions.
92
*/
93
94
psc = (void *) PSC_BASE;
95
96
pr_debug("PSC detected at %p\n", psc);
97
98
psc_dma_die_die_die();
99
100
#ifdef DEBUG_PSC
101
psc_debug_dump();
102
#endif
103
/*
104
* Mask and clear all possible interrupts
105
*/
106
107
for (i = 0x30 ; i < 0x70 ; i += 0x10) {
108
psc_write_byte(pIERbase + i, 0x0F);
109
psc_write_byte(pIFRbase + i, 0x0F);
110
}
111
}
112
113
/*
114
* PSC interrupt handler. It's a lot like the VIA interrupt handler.
115
*/
116
117
static void psc_irq(struct irq_desc *desc)
118
{
119
unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
120
unsigned int irq = irq_desc_get_irq(desc);
121
int pIFR = pIFRbase + offset;
122
int pIER = pIERbase + offset;
123
int irq_num;
124
unsigned char irq_bit, events;
125
126
events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
127
if (!events)
128
return;
129
130
irq_num = irq << 3;
131
irq_bit = 1;
132
do {
133
if (events & irq_bit) {
134
psc_write_byte(pIFR, irq_bit);
135
generic_handle_irq(irq_num);
136
}
137
irq_num++;
138
irq_bit <<= 1;
139
} while (events >= irq_bit);
140
}
141
142
/*
143
* Register the PSC interrupt dispatchers for autovector interrupts 3-6.
144
*/
145
146
void __init psc_register_interrupts(void)
147
{
148
irq_set_chained_handler_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30);
149
irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40);
150
irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50);
151
irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (void *)0x60);
152
}
153
154
void psc_irq_enable(int irq) {
155
int irq_src = IRQ_SRC(irq);
156
int irq_idx = IRQ_IDX(irq);
157
int pIER = pIERbase + (irq_src << 4);
158
159
psc_write_byte(pIER, (1 << irq_idx) | 0x80);
160
}
161
162
void psc_irq_disable(int irq) {
163
int irq_src = IRQ_SRC(irq);
164
int irq_idx = IRQ_IDX(irq);
165
int pIER = pIERbase + (irq_src << 4);
166
167
psc_write_byte(pIER, 1 << irq_idx);
168
}
169
170