cia.c (4633B)
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 unsigned long flags; 92 93 /* Interrupts get disabled while the timer irq flag is cleared and 94 * the timer interrupt serviced. 95 */ 96 mach_irq = base->cia_irq; 97 local_irq_save(flags); 98 ints = cia_set_irq(base, CIA_ICR_ALL); 99 amiga_custom.intreq = base->int_mask; 100 if (ints & 1) 101 generic_handle_irq(mach_irq); 102 local_irq_restore(flags); 103 mach_irq++, ints >>= 1; 104 for (; ints; mach_irq++, ints >>= 1) { 105 if (ints & 1) 106 generic_handle_irq(mach_irq); 107 } 108 return IRQ_HANDLED; 109} 110 111static void cia_irq_enable(struct irq_data *data) 112{ 113 unsigned int irq = data->irq; 114 unsigned char mask; 115 116 if (irq >= IRQ_AMIGA_CIAB) { 117 mask = 1 << (irq - IRQ_AMIGA_CIAB); 118 cia_set_irq(&ciab_base, mask); 119 cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask); 120 } else { 121 mask = 1 << (irq - IRQ_AMIGA_CIAA); 122 cia_set_irq(&ciaa_base, mask); 123 cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask); 124 } 125} 126 127static void cia_irq_disable(struct irq_data *data) 128{ 129 unsigned int irq = data->irq; 130 131 if (irq >= IRQ_AMIGA_CIAB) 132 cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB)); 133 else 134 cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA)); 135} 136 137static struct irq_chip cia_irq_chip = { 138 .name = "cia", 139 .irq_enable = cia_irq_enable, 140 .irq_disable = cia_irq_disable, 141}; 142 143/* 144 * Override auto irq 2 & 6 and use them as general chain 145 * for external interrupts, we link the CIA interrupt sources 146 * into this chain. 147 */ 148 149static void auto_irq_enable(struct irq_data *data) 150{ 151 switch (data->irq) { 152 case IRQ_AUTO_2: 153 amiga_custom.intena = IF_SETCLR | IF_PORTS; 154 break; 155 case IRQ_AUTO_6: 156 amiga_custom.intena = IF_SETCLR | IF_EXTER; 157 break; 158 } 159} 160 161static void auto_irq_disable(struct irq_data *data) 162{ 163 switch (data->irq) { 164 case IRQ_AUTO_2: 165 amiga_custom.intena = IF_PORTS; 166 break; 167 case IRQ_AUTO_6: 168 amiga_custom.intena = IF_EXTER; 169 break; 170 } 171} 172 173static struct irq_chip auto_irq_chip = { 174 .name = "auto", 175 .irq_enable = auto_irq_enable, 176 .irq_disable = auto_irq_disable, 177}; 178 179void __init cia_init_IRQ(struct ciabase *base) 180{ 181 m68k_setup_irq_controller(&cia_irq_chip, handle_simple_irq, 182 base->cia_irq, CIA_IRQS); 183 184 /* clear any pending interrupt and turn off all interrupts */ 185 cia_set_irq(base, CIA_ICR_ALL); 186 cia_able_irq(base, CIA_ICR_ALL); 187 188 /* override auto int and install CIA handler */ 189 m68k_setup_irq_controller(&auto_irq_chip, handle_simple_irq, 190 base->handler_irq, 1); 191 m68k_irq_startup_irq(base->handler_irq); 192 if (request_irq(base->handler_irq, cia_handler, IRQF_SHARED, 193 base->name, base)) 194 pr_err("Couldn't register %s interrupt\n", base->name); 195}