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