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#include <linux/irq.h> 22 23#include <asm/irq.h> 24#include <asm/amigahw.h> 25#include <asm/amigaints.h> 26 27struct ciabase { 28 volatile struct CIA *cia; 29 unsigned char icr_mask, icr_data; 30 unsigned short int_mask; 31 int handler_irq, cia_irq, server_irq; 32 char *name; 33} ciaa_base = { 34 .cia = &ciaa, 35 .int_mask = IF_PORTS, 36 .handler_irq = IRQ_AMIGA_PORTS, 37 .cia_irq = IRQ_AMIGA_CIAA, 38 .name = "CIAA" 39}, ciab_base = { 40 .cia = &ciab, 41 .int_mask = IF_EXTER, 42 .handler_irq = IRQ_AMIGA_EXTER, 43 .cia_irq = IRQ_AMIGA_CIAB, 44 .name = "CIAB" 45}; 46 47/* 48 * Cause or clear CIA interrupts, return old interrupt status. 49 */ 50 51unsigned char cia_set_irq(struct ciabase *base, unsigned char mask) 52{ 53 unsigned char old; 54 55 old = (base->icr_data |= base->cia->icr); 56 if (mask & CIA_ICR_SETCLR) 57 base->icr_data |= mask; 58 else 59 base->icr_data &= ~mask; 60 if (base->icr_data & base->icr_mask) 61 amiga_custom.intreq = IF_SETCLR | base->int_mask; 62 return old & base->icr_mask; 63} 64 65/* 66 * Enable or disable CIA interrupts, return old interrupt mask, 67 */ 68 69unsigned char cia_able_irq(struct ciabase *base, unsigned char mask) 70{ 71 unsigned char old; 72 73 old = base->icr_mask; 74 base->icr_data |= base->cia->icr; 75 base->cia->icr = mask; 76 if (mask & CIA_ICR_SETCLR) 77 base->icr_mask |= mask; 78 else 79 base->icr_mask &= ~mask; 80 base->icr_mask &= CIA_ICR_ALL; 81 if (base->icr_data & base->icr_mask) 82 amiga_custom.intreq = IF_SETCLR | base->int_mask; 83 return old; 84} 85 86static irqreturn_t cia_handler(int irq, void *dev_id) 87{ 88 struct ciabase *base = dev_id; 89 int mach_irq; 90 unsigned char ints; 91 92 mach_irq = base->cia_irq; 93 ints = cia_set_irq(base, CIA_ICR_ALL); 94 amiga_custom.intreq = base->int_mask; 95 for (; ints; mach_irq++, ints >>= 1) { 96 if (ints & 1) 97 generic_handle_irq(mach_irq); 98 } 99 return IRQ_HANDLED; 100} 101 102static void cia_irq_enable(struct irq_data *data) 103{ 104 unsigned int irq = data->irq; 105 unsigned char mask; 106 107 if (irq >= IRQ_AMIGA_CIAB) { 108 mask = 1 << (irq - IRQ_AMIGA_CIAB); 109 cia_set_irq(&ciab_base, mask); 110 cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask); 111 } else { 112 mask = 1 << (irq - IRQ_AMIGA_CIAA); 113 cia_set_irq(&ciaa_base, mask); 114 cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask); 115 } 116} 117 118static void cia_irq_disable(struct irq_data *data) 119{ 120 unsigned int irq = data->irq; 121 122 if (irq >= IRQ_AMIGA_CIAB) 123 cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB)); 124 else 125 cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA)); 126} 127 128static struct irq_chip cia_irq_chip = { 129 .name = "cia", 130 .irq_enable = cia_irq_enable, 131 .irq_disable = cia_irq_disable, 132}; 133 134/* 135 * Override auto irq 2 & 6 and use them as general chain 136 * for external interrupts, we link the CIA interrupt sources 137 * into this chain. 138 */ 139 140static void auto_irq_enable(struct irq_data *data) 141{ 142 switch (data->irq) { 143 case IRQ_AUTO_2: 144 amiga_custom.intena = IF_SETCLR | IF_PORTS; 145 break; 146 case IRQ_AUTO_6: 147 amiga_custom.intena = IF_SETCLR | IF_EXTER; 148 break; 149 } 150} 151 152static void auto_irq_disable(struct irq_data *data) 153{ 154 switch (data->irq) { 155 case IRQ_AUTO_2: 156 amiga_custom.intena = IF_PORTS; 157 break; 158 case IRQ_AUTO_6: 159 amiga_custom.intena = IF_EXTER; 160 break; 161 } 162} 163 164static struct irq_chip auto_irq_chip = { 165 .name = "auto", 166 .irq_enable = auto_irq_enable, 167 .irq_disable = auto_irq_disable, 168}; 169 170void __init cia_init_IRQ(struct ciabase *base) 171{ 172 m68k_setup_irq_controller(&cia_irq_chip, handle_simple_irq, 173 base->cia_irq, CIA_IRQS); 174 175 /* clear any pending interrupt and turn off all interrupts */ 176 cia_set_irq(base, CIA_ICR_ALL); 177 cia_able_irq(base, CIA_ICR_ALL); 178 179 /* override auto int and install CIA handler */ 180 m68k_setup_irq_controller(&auto_irq_chip, handle_simple_irq, 181 base->handler_irq, 1); 182 m68k_irq_startup_irq(base->handler_irq); 183 if (request_irq(base->handler_irq, cia_handler, IRQF_SHARED, 184 base->name, base)) 185 pr_err("Couldn't register %s interrupt\n", base->name); 186} 187