18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Apple Peripheral System Controller (PSC)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	The PSC is used on the AV Macs to control IO functions not handled
68c2ecf20Sopenharmony_ci *	by the VIAs (Ethernet, DSP, SCC).
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * TO DO:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be
118c2ecf20Sopenharmony_ci * persisant interrupt conditions in those registers and I have no idea what
128c2ecf20Sopenharmony_ci * they are. Granted it doesn't affect since we're not enabling any interrupts
138c2ecf20Sopenharmony_ci * on those levels at the moment, but it would be nice to know. I have a feeling
148c2ecf20Sopenharmony_ci * they aren't actually interrupt lines but data lines (to the DSP?)
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/irq.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <asm/traps.h>
258c2ecf20Sopenharmony_ci#include <asm/macintosh.h>
268c2ecf20Sopenharmony_ci#include <asm/macints.h>
278c2ecf20Sopenharmony_ci#include <asm/mac_psc.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define DEBUG_PSC
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_civolatile __u8 *psc;
328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(psc);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * Debugging dump, used in various places to see what's going on.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void psc_debug_dump(void)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int	i;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!psc)
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
468c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "PSC #%d:  IFR = 0x%02X IER = 0x%02X\n",
478c2ecf20Sopenharmony_ci			i >> 4,
488c2ecf20Sopenharmony_ci			(int) psc_read_byte(pIFRbase + i),
498c2ecf20Sopenharmony_ci			(int) psc_read_byte(pIERbase + i));
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Try to kill all DMA channels on the PSC. Not sure how this his
558c2ecf20Sopenharmony_ci * supposed to work; this is code lifted from macmace.c and then
568c2ecf20Sopenharmony_ci * expanded to cover what I think are the other 7 channels.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic __init void psc_dma_die_die_die(void)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	int i;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	for (i = 0 ; i < 9 ; i++) {
648c2ecf20Sopenharmony_ci		psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800);
658c2ecf20Sopenharmony_ci		psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000);
668c2ecf20Sopenharmony_ci		psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100);
678c2ecf20Sopenharmony_ci		psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci * Initialize the PSC. For now this just involves shutting down all
738c2ecf20Sopenharmony_ci * interrupt sources using the IERs.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_civoid __init psc_init(void)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int i;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (macintosh_config->ident != MAC_MODEL_C660
818c2ecf20Sopenharmony_ci	 && macintosh_config->ident != MAC_MODEL_Q840)
828c2ecf20Sopenharmony_ci	{
838c2ecf20Sopenharmony_ci		psc = NULL;
848c2ecf20Sopenharmony_ci		return;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * The PSC is always at the same spot, but using psc
898c2ecf20Sopenharmony_ci	 * keeps things consistent with the psc_xxxx functions.
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	psc = (void *) PSC_BASE;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	pr_debug("PSC detected at %p\n", psc);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	psc_dma_die_die_die();
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#ifdef DEBUG_PSC
998c2ecf20Sopenharmony_ci	psc_debug_dump();
1008c2ecf20Sopenharmony_ci#endif
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * Mask and clear all possible interrupts
1038c2ecf20Sopenharmony_ci	 */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
1068c2ecf20Sopenharmony_ci		psc_write_byte(pIERbase + i, 0x0F);
1078c2ecf20Sopenharmony_ci		psc_write_byte(pIFRbase + i, 0x0F);
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci * PSC interrupt handler. It's a lot like the VIA interrupt handler.
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void psc_irq(struct irq_desc *desc)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
1188c2ecf20Sopenharmony_ci	unsigned int irq = irq_desc_get_irq(desc);
1198c2ecf20Sopenharmony_ci	int pIFR	= pIFRbase + offset;
1208c2ecf20Sopenharmony_ci	int pIER	= pIERbase + offset;
1218c2ecf20Sopenharmony_ci	int irq_num;
1228c2ecf20Sopenharmony_ci	unsigned char irq_bit, events;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
1258c2ecf20Sopenharmony_ci	if (!events)
1268c2ecf20Sopenharmony_ci		return;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	irq_num = irq << 3;
1298c2ecf20Sopenharmony_ci	irq_bit = 1;
1308c2ecf20Sopenharmony_ci	do {
1318c2ecf20Sopenharmony_ci		if (events & irq_bit) {
1328c2ecf20Sopenharmony_ci			psc_write_byte(pIFR, irq_bit);
1338c2ecf20Sopenharmony_ci			generic_handle_irq(irq_num);
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci		irq_num++;
1368c2ecf20Sopenharmony_ci		irq_bit <<= 1;
1378c2ecf20Sopenharmony_ci	} while (events >= irq_bit);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * Register the PSC interrupt dispatchers for autovector interrupts 3-6.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_civoid __init psc_register_interrupts(void)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30);
1478c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40);
1488c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50);
1498c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (void *)0x60);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_civoid psc_irq_enable(int irq) {
1538c2ecf20Sopenharmony_ci	int irq_src	= IRQ_SRC(irq);
1548c2ecf20Sopenharmony_ci	int irq_idx	= IRQ_IDX(irq);
1558c2ecf20Sopenharmony_ci	int pIER	= pIERbase + (irq_src << 4);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	psc_write_byte(pIER, (1 << irq_idx) | 0x80);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_civoid psc_irq_disable(int irq) {
1618c2ecf20Sopenharmony_ci	int irq_src	= IRQ_SRC(irq);
1628c2ecf20Sopenharmony_ci	int irq_idx	= IRQ_IDX(irq);
1638c2ecf20Sopenharmony_ci	int pIER	= pIERbase + (irq_src << 4);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	psc_write_byte(pIER, 1 << irq_idx);
1668c2ecf20Sopenharmony_ci}
167