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 irq_handler_t irq_list[CIA_IRQS]; 33 } ciaa_base = { 34 .cia = &ciaa, 35 .int_mask = IF_PORTS, 36 .handler_irq = IRQ_AMIGA_AUTO_2, 37 .cia_irq = IRQ_AMIGA_CIAA, 38 .server_irq = IRQ_AMIGA_PORTS, 39 .name = "CIAA handler" 40 }, ciab_base = { 41 .cia = &ciab, 42 .int_mask = IF_EXTER, 43 .handler_irq = IRQ_AMIGA_AUTO_6, 44 .cia_irq = IRQ_AMIGA_CIAB, 45 .server_irq = IRQ_AMIGA_EXTER, 46 .name = "CIAB handler" 47 }; 48 49 /* 50 * Cause or clear CIA interrupts, return old interrupt status. 51 */ 52 53 unsigned char cia_set_irq(struct ciabase *base, unsigned char mask) 54 { 55 unsigned char old; 56 57 old = (base->icr_data |= base->cia->icr); 58 if (mask & CIA_ICR_SETCLR) 59 base->icr_data |= mask; 60 else 61 base->icr_data &= ~mask; 62 if (base->icr_data & base->icr_mask) 63 custom.intreq = IF_SETCLR | base->int_mask; 64 return old & base->icr_mask; 65 } 66 67 /* 68 * Enable or disable CIA interrupts, return old interrupt mask, 69 * interrupts will only be enabled if a handler exists 70 */ 71 72 unsigned char cia_able_irq(struct ciabase *base, unsigned char mask) 73 { 74 unsigned char old, tmp; 75 int i; 76 77 old = base->icr_mask; 78 base->icr_data |= base->cia->icr; 79 base->cia->icr = mask; 80 if (mask & CIA_ICR_SETCLR) 81 base->icr_mask |= mask; 82 else 83 base->icr_mask &= ~mask; 84 base->icr_mask &= CIA_ICR_ALL; 85 for (i = 0, tmp = 1; i < CIA_IRQS; i++, tmp <<= 1) { 86 if ((tmp & base->icr_mask) && !base->irq_list[i].handler) { 87 base->icr_mask &= ~tmp; 88 base->cia->icr = tmp; 89 } 90 } 91 if (base->icr_data & base->icr_mask) 92 custom.intreq = IF_SETCLR | base->int_mask; 93 return old; 94 } 95 96 int cia_request_irq(struct ciabase *base, unsigned int irq, 97 irqreturn_t (*handler)(int, void *, struct pt_regs *), 98 unsigned long flags, const char *devname, void *dev_id) 99 { 100 unsigned char mask; 101 102 base->irq_list[irq].handler = handler; 103 base->irq_list[irq].flags = flags; 104 base->irq_list[irq].dev_id = dev_id; 105 base->irq_list[irq].devname = devname; 106 107 /* enable the interrupt */ 108 mask = 1 << irq; 109 cia_set_irq(base, mask); 110 cia_able_irq(base, CIA_ICR_SETCLR | mask); 111 return 0; 112 } 113 114 void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id) 115 { 116 if (base->irq_list[irq].dev_id != dev_id) 117 printk("%s: removing probably wrong IRQ %i from %s\n", 118 __FUNCTION__, base->cia_irq + irq, 119 base->irq_list[irq].devname); 120 121 base->irq_list[irq].handler = NULL; 122 base->irq_list[irq].flags = 0; 123 124 cia_able_irq(base, 1 << irq); 125 } 126 127 static irqreturn_t cia_handler(int irq, void *dev_id, struct pt_regs *fp) 128 { 129 struct ciabase *base = (struct ciabase *)dev_id; 130 int mach_irq, i; 131 unsigned char ints; 132 133 mach_irq = base->cia_irq; 134 irq = SYS_IRQS + mach_irq; 135 ints = cia_set_irq(base, CIA_ICR_ALL); 136 custom.intreq = base->int_mask; 137 for (i = 0; i < CIA_IRQS; i++, irq++, mach_irq++) { 138 if (ints & 1) { 139 kstat_cpu(0).irqs[irq]++; 140 base->irq_list[i].handler(mach_irq, base->irq_list[i].dev_id, fp); 141 } 142 ints >>= 1; 143 } 144 amiga_do_irq_list(base->server_irq, fp); 145 return IRQ_HANDLED; 146 } 147 148 void __init cia_init_IRQ(struct ciabase *base) 149 { 150 int i; 151 152 /* init isr handlers */ 153 for (i = 0; i < CIA_IRQS; i++) { 154 base->irq_list[i].handler = NULL; 155 base->irq_list[i].flags = 0; 156 } 157 158 /* clear any pending interrupt and turn off all interrupts */ 159 cia_set_irq(base, CIA_ICR_ALL); 160 cia_able_irq(base, CIA_ICR_ALL); 161 162 /* install CIA handler */ 163 request_irq(base->handler_irq, cia_handler, 0, base->name, base); 164 165 custom.intena = IF_SETCLR | base->int_mask; 166 } 167 168 int cia_get_irq_list(struct ciabase *base, struct seq_file *p) 169 { 170 int i, j; 171 172 j = base->cia_irq; 173 for (i = 0; i < CIA_IRQS; i++) { 174 seq_printf(p, "cia %2d: %10d ", j + i, 175 kstat_cpu(0).irqs[SYS_IRQS + j + i]); 176 seq_puts(p, " "); 177 seq_printf(p, "%s\n", base->irq_list[i].devname); 178 } 179 return 0; 180 } 181