1/* 2 * linux/arch/m68k/amiga/cia.c - CIA support 3 * 4 * Copyright (C) 1996 Roman Zippel 5 * 6 * The concept of some functions bases on the original Amiga OS function 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file COPYING in the main directory of this archive 10 * for more details. 11 */ 12 13#include <linux/types.h> 14#include <linux/kernel.h> 15#include <linux/sched.h> 16#include <linux/errno.h> 17#include <linux/kernel_stat.h> 18#include <linux/init.h> 19#include <linux/seq_file.h> 20#include <linux/interrupt.h> 21 22#include <asm/irq.h> 23#include <asm/amigahw.h> 24#include <asm/amigaints.h> 25 26struct ciabase { 27 volatile struct CIA *cia; 28 unsigned char icr_mask, icr_data; 29 unsigned short int_mask; 30 int handler_irq, cia_irq, server_irq; 31 char *name; 32} ciaa_base = { 33 .cia = &ciaa, 34 .int_mask = IF_PORTS, 35 .handler_irq = IRQ_AMIGA_PORTS, 36 .cia_irq = IRQ_AMIGA_CIAA, 37 .name = "CIAA" 38}, ciab_base = { 39 .cia = &ciab, 40 .int_mask = IF_EXTER, 41 .handler_irq = IRQ_AMIGA_EXTER, 42 .cia_irq = IRQ_AMIGA_CIAB, 43 .name = "CIAB" 44}; 45 46/* 47 * Cause or clear CIA interrupts, return old interrupt status. 48 */ 49 50unsigned char cia_set_irq(struct ciabase *base, unsigned char mask) 51{ 52 unsigned char old; 53 54 old = (base->icr_data |= base->cia->icr); 55 if (mask & CIA_ICR_SETCLR) 56 base->icr_data |= mask; 57 else 58 base->icr_data &= ~mask; 59 if (base->icr_data & base->icr_mask) 60 amiga_custom.intreq = IF_SETCLR | base->int_mask; 61 return old & base->icr_mask; 62} 63 64/* 65 * Enable or disable CIA interrupts, return old interrupt mask, 66 */ 67 68unsigned char cia_able_irq(struct ciabase *base, unsigned char mask) 69{ 70 unsigned char old; 71 72 old = base->icr_mask; 73 base->icr_data |= base->cia->icr; 74 base->cia->icr = mask; 75 if (mask & CIA_ICR_SETCLR) 76 base->icr_mask |= mask; 77 else 78 base->icr_mask &= ~mask; 79 base->icr_mask &= CIA_ICR_ALL; 80 if (base->icr_data & base->icr_mask) 81 amiga_custom.intreq = IF_SETCLR | base->int_mask; 82 return old; 83} 84 85static irqreturn_t cia_handler(int irq, void *dev_id) 86{ 87 struct ciabase *base = (struct ciabase *)dev_id; 88 int mach_irq; 89 unsigned char ints; 90 91 mach_irq = base->cia_irq; 92 ints = cia_set_irq(base, CIA_ICR_ALL); 93 amiga_custom.intreq = base->int_mask; 94 for (; ints; mach_irq++, ints >>= 1) { 95 if (ints & 1) 96 m68k_handle_int(mach_irq); 97 } 98 return IRQ_HANDLED; 99} 100 101static void cia_enable_irq(unsigned int irq) 102{ 103 unsigned char mask; 104 105 if (irq >= IRQ_AMIGA_CIAB) { 106 mask = 1 << (irq - IRQ_AMIGA_CIAB); 107 cia_set_irq(&ciab_base, mask); 108 cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask); 109 } else { 110 mask = 1 << (irq - IRQ_AMIGA_CIAA); 111 cia_set_irq(&ciaa_base, mask); 112 cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask); 113 } 114} 115 116static void cia_disable_irq(unsigned int irq) 117{ 118 if (irq >= IRQ_AMIGA_CIAB) 119 cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB)); 120 else 121 cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA)); 122} 123 124static struct irq_controller cia_irq_controller = { 125 .name = "cia", 126 .lock = __SPIN_LOCK_UNLOCKED(cia_irq_controller.lock), 127 .enable = cia_enable_irq, 128 .disable = cia_disable_irq, 129}; 130 131/* 132 * Override auto irq 2 & 6 and use them as general chain 133 * for external interrupts, we link the CIA interrupt sources 134 * into this chain. 135 */ 136 137static void auto_enable_irq(unsigned int irq) 138{ 139 switch (irq) { 140 case IRQ_AUTO_2: 141 amiga_custom.intena = IF_SETCLR | IF_PORTS; 142 break; 143 case IRQ_AUTO_6: 144 amiga_custom.intena = IF_SETCLR | IF_EXTER; 145 break; 146 } 147} 148 149static void auto_disable_irq(unsigned int irq) 150{ 151 switch (irq) { 152 case IRQ_AUTO_2: 153 amiga_custom.intena = IF_PORTS; 154 break; 155 case IRQ_AUTO_6: 156 amiga_custom.intena = IF_EXTER; 157 break; 158 } 159} 160 161static struct irq_controller auto_irq_controller = { 162 .name = "auto", 163 .lock = __SPIN_LOCK_UNLOCKED(auto_irq_controller.lock), 164 .enable = auto_enable_irq, 165 .disable = auto_disable_irq, 166}; 167 168void __init cia_init_IRQ(struct ciabase *base) 169{ 170 m68k_setup_irq_controller(&cia_irq_controller, base->cia_irq, CIA_IRQS); 171 172 /* clear any pending interrupt and turn off all interrupts */ 173 cia_set_irq(base, CIA_ICR_ALL); 174 cia_able_irq(base, CIA_ICR_ALL); 175 176 /* override auto int and install CIA handler */ 177 m68k_setup_irq_controller(&auto_irq_controller, base->handler_irq, 1); 178 m68k_irq_startup(base->handler_irq); 179 request_irq(base->handler_irq, cia_handler, IRQF_SHARED, base->name, base); 180} 181