Linux Audio

Check our new training course

Loading...
v6.2
  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#define DEBUG_PSC
 30
 31volatile __u8 *psc;
 32EXPORT_SYMBOL_GPL(psc);
 33
 34/*
 35 * Debugging dump, used in various places to see what's going on.
 36 */
 37
 38static void psc_debug_dump(void)
 39{
 40	int	i;
 41
 42	if (!psc)
 43		return;
 44
 45	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
 46		printk(KERN_DEBUG "PSC #%d:  IFR = 0x%02X IER = 0x%02X\n",
 47			i >> 4,
 48			(int) psc_read_byte(pIFRbase + i),
 49			(int) psc_read_byte(pIERbase + i));
 50	}
 51}
 52
 53/*
 54 * Try to kill all DMA channels on the PSC. Not sure how this his
 55 * supposed to work; this is code lifted from macmace.c and then
 56 * expanded to cover what I think are the other 7 channels.
 57 */
 58
 59static __init void psc_dma_die_die_die(void)
 60{
 61	int i;
 62
 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}
 70
 71/*
 72 * Initialize the PSC. For now this just involves shutting down all
 73 * interrupt sources using the IERs.
 74 */
 75
 76void __init psc_init(void)
 77{
 78	int i;
 79
 80	if (macintosh_config->ident != MAC_MODEL_C660
 81	 && macintosh_config->ident != MAC_MODEL_Q840)
 82	{
 83		psc = NULL;
 84		return;
 85	}
 86
 87	/*
 88	 * The PSC is always at the same spot, but using psc
 89	 * keeps things consistent with the psc_xxxx functions.
 90	 */
 91
 92	psc = (void *) PSC_BASE;
 93
 94	pr_debug("PSC detected at %p\n", psc);
 95
 96	psc_dma_die_die_die();
 97
 98#ifdef DEBUG_PSC
 99	psc_debug_dump();
100#endif
101	/*
102	 * Mask and clear all possible interrupts
103	 */
104
105	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
106		psc_write_byte(pIERbase + i, 0x0F);
107		psc_write_byte(pIFRbase + i, 0x0F);
108	}
109}
110
111/*
112 * PSC interrupt handler. It's a lot like the VIA interrupt handler.
113 */
114
115static void psc_irq(struct irq_desc *desc)
116{
117	unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
118	unsigned int irq = irq_desc_get_irq(desc);
119	int pIFR	= pIFRbase + offset;
120	int pIER	= pIERbase + offset;
121	int irq_num;
122	unsigned char irq_bit, events;
123
124	events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
125	if (!events)
126		return;
127
128	irq_num = irq << 3;
129	irq_bit = 1;
130	do {
131		if (events & irq_bit) {
132			psc_write_byte(pIFR, irq_bit);
133			generic_handle_irq(irq_num);
134		}
135		irq_num++;
136		irq_bit <<= 1;
137	} while (events >= irq_bit);
138}
139
140/*
141 * Register the PSC interrupt dispatchers for autovector interrupts 3-6.
142 */
143
144void __init psc_register_interrupts(void)
145{
146	irq_set_chained_handler_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30);
147	irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40);
148	irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50);
149	irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (void *)0x60);
150}
151
152void psc_irq_enable(int irq) {
153	int irq_src	= IRQ_SRC(irq);
154	int irq_idx	= IRQ_IDX(irq);
155	int pIER	= pIERbase + (irq_src << 4);
156
157	psc_write_byte(pIER, (1 << irq_idx) | 0x80);
158}
159
160void psc_irq_disable(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	psc_write_byte(pIER, 1 << irq_idx);
166}
v4.17
 
  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#include <linux/irq.h>
 22
 23#include <asm/traps.h>
 24#include <asm/macintosh.h>
 25#include <asm/macints.h>
 26#include <asm/mac_psc.h>
 27
 28#define DEBUG_PSC
 29
 30volatile __u8 *psc;
 31EXPORT_SYMBOL_GPL(psc);
 32
 33/*
 34 * Debugging dump, used in various places to see what's going on.
 35 */
 36
 37static void psc_debug_dump(void)
 38{
 39	int	i;
 40
 41	if (!psc)
 42		return;
 43
 44	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
 45		printk(KERN_DEBUG "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
 58static __init void psc_dma_die_die_die(void)
 59{
 60	int i;
 61
 62	for (i = 0 ; i < 9 ; i++) {
 63		psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800);
 64		psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000);
 65		psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100);
 66		psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100);
 67	}
 68}
 69
 70/*
 71 * Initialize the PSC. For now this just involves shutting down all
 72 * interrupt sources using the IERs.
 73 */
 74
 75void __init psc_init(void)
 76{
 77	int i;
 78
 79	if (macintosh_config->ident != MAC_MODEL_C660
 80	 && macintosh_config->ident != MAC_MODEL_Q840)
 81	{
 82		psc = NULL;
 83		return;
 84	}
 85
 86	/*
 87	 * The PSC is always at the same spot, but using psc
 88	 * keeps things consistent with the psc_xxxx functions.
 89	 */
 90
 91	psc = (void *) PSC_BASE;
 92
 93	pr_debug("PSC detected at %p\n", psc);
 94
 95	psc_dma_die_die_die();
 96
 97#ifdef DEBUG_PSC
 98	psc_debug_dump();
 99#endif
100	/*
101	 * Mask and clear all possible interrupts
102	 */
103
104	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
105		psc_write_byte(pIERbase + i, 0x0F);
106		psc_write_byte(pIFRbase + i, 0x0F);
107	}
108}
109
110/*
111 * PSC interrupt handler. It's a lot like the VIA interrupt handler.
112 */
113
114static void psc_irq(struct irq_desc *desc)
115{
116	unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
117	unsigned int irq = irq_desc_get_irq(desc);
118	int pIFR	= pIFRbase + offset;
119	int pIER	= pIERbase + offset;
120	int irq_num;
121	unsigned char irq_bit, events;
122
123	events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
124	if (!events)
125		return;
126
127	irq_num = irq << 3;
128	irq_bit = 1;
129	do {
130		if (events & irq_bit) {
131			psc_write_byte(pIFR, irq_bit);
132			generic_handle_irq(irq_num);
133		}
134		irq_num++;
135		irq_bit <<= 1;
136	} while (events >= irq_bit);
137}
138
139/*
140 * Register the PSC interrupt dispatchers for autovector interrupts 3-6.
141 */
142
143void __init psc_register_interrupts(void)
144{
145	irq_set_chained_handler_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30);
146	irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40);
147	irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50);
148	irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (void *)0x60);
149}
150
151void psc_irq_enable(int irq) {
152	int irq_src	= IRQ_SRC(irq);
153	int irq_idx	= IRQ_IDX(irq);
154	int pIER	= pIERbase + (irq_src << 4);
155
156	psc_write_byte(pIER, (1 << irq_idx) | 0x80);
157}
158
159void psc_irq_disable(int irq) {
160	int irq_src	= IRQ_SRC(irq);
161	int irq_idx	= IRQ_IDX(irq);
162	int pIER	= pIERbase + (irq_src << 4);
163
164	psc_write_byte(pIER, 1 << irq_idx);
165}