| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * linux/arch/sh/boards/ec3104/irq.c | 
|  | 3 | * EC3104 companion chip support | 
|  | 4 | * | 
|  | 5 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | 
|  | 6 | * | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include <asm/io.h> | 
|  | 10 | #include <asm/irq.h> | 
|  | 11 | #include <asm/ec3104/ec3104.h> | 
|  | 12 |  | 
|  | 13 | /* This is for debugging mostly;  here's the table that I intend to keep | 
|  | 14 | * in here: | 
|  | 15 | * | 
|  | 16 | *   index      function        base addr       power           interrupt bit | 
|  | 17 | *       0      power           b0ec0000        ---             00000001 (unused) | 
|  | 18 | *       1      irqs            b0ec1000        ---             00000002 (unused) | 
|  | 19 | *       2      ??              b0ec2000        b0ec0008        00000004 | 
|  | 20 | *       3      PS2 (1)         b0ec3000        b0ec000c        00000008 | 
|  | 21 | *       4      PS2 (2)         b0ec4000        b0ec0010        00000010 | 
|  | 22 | *       5      ??              b0ec5000        b0ec0014        00000020 | 
|  | 23 | *       6      I2C             b0ec6000        b0ec0018        00000040 | 
|  | 24 | *       7      serial (1)      b0ec7000        b0ec001c        00000080 | 
|  | 25 | *       8      serial (2)      b0ec8000        b0ec0020        00000100 | 
|  | 26 | *       9      serial (3)      b0ec9000        b0ec0024        00000200 | 
|  | 27 | *      10      serial (4)      b0eca000        b0ec0028        00000400 | 
|  | 28 | *      12      GPIO (1)        b0ecc000        b0ec0030 | 
|  | 29 | *      13      GPIO (2)        b0ecc000        b0ec0030 | 
|  | 30 | *      16      pcmcia (1)      b0ed0000        b0ec0040        00010000 | 
|  | 31 | *      17      pcmcia (2)      b0ed1000        b0ec0044        00020000 | 
|  | 32 | */ | 
|  | 33 |  | 
|  | 34 | /* I used the register names from another interrupt controller I worked with, | 
|  | 35 | * since it seems to be identical to the ec3104 except that all bits are | 
|  | 36 | * inverted: | 
|  | 37 | * | 
|  | 38 | * IRR: Interrupt Request Register (pending and enabled interrupts) | 
|  | 39 | * IMR: Interrupt Mask Register (which interrupts are enabled) | 
|  | 40 | * IPR: Interrupt Pending Register (pending interrupts, even disabled ones) | 
|  | 41 | * | 
|  | 42 | * 0 bits mean pending or enabled, 1 bits mean not pending or disabled.  all | 
|  | 43 | * IRQs seem to be level-triggered. | 
|  | 44 | */ | 
|  | 45 |  | 
|  | 46 | #define EC3104_IRR (EC3104_BASE + 0x1000) | 
|  | 47 | #define EC3104_IMR (EC3104_BASE + 0x1004) | 
|  | 48 | #define EC3104_IPR (EC3104_BASE + 0x1008) | 
|  | 49 |  | 
|  | 50 | #define ctrl_readl(addr) (*(volatile u32 *)(addr)) | 
|  | 51 | #define ctrl_writel(data,addr) (*(volatile u32 *)(addr) = (data)) | 
|  | 52 | #define ctrl_readb(addr) (*(volatile u8 *)(addr)) | 
|  | 53 |  | 
|  | 54 | static char *ec3104_name(unsigned index) | 
|  | 55 | { | 
|  | 56 | switch(index) { | 
|  | 57 | case 0: | 
|  | 58 | return "power management"; | 
|  | 59 | case 1: | 
|  | 60 | return "interrupts"; | 
|  | 61 | case 3: | 
|  | 62 | return "PS2 (1)"; | 
|  | 63 | case 4: | 
|  | 64 | return "PS2 (2)"; | 
|  | 65 | case 5: | 
|  | 66 | return "I2C (1)"; | 
|  | 67 | case 6: | 
|  | 68 | return "I2C (2)"; | 
|  | 69 | case 7: | 
|  | 70 | return "serial (1)"; | 
|  | 71 | case 8: | 
|  | 72 | return "serial (2)"; | 
|  | 73 | case 9: | 
|  | 74 | return "serial (3)"; | 
|  | 75 | case 10: | 
|  | 76 | return "serial (4)"; | 
|  | 77 | case 16: | 
|  | 78 | return "pcmcia (1)"; | 
|  | 79 | case 17: | 
|  | 80 | return "pcmcia (2)"; | 
|  | 81 | default: { | 
|  | 82 | static char buf[32]; | 
|  | 83 |  | 
|  | 84 | sprintf(buf, "unknown (%d)", index); | 
|  | 85 |  | 
|  | 86 | return buf; | 
|  | 87 | } | 
|  | 88 | } | 
|  | 89 | } | 
|  | 90 |  | 
|  | 91 | int get_pending_interrupts(char *buf) | 
|  | 92 | { | 
|  | 93 | u32 ipr; | 
|  | 94 | u32 bit; | 
|  | 95 | char *p = buf; | 
|  | 96 |  | 
|  | 97 | p += sprintf(p, "pending: ("); | 
|  | 98 |  | 
|  | 99 | ipr = ctrl_inl(EC3104_IPR); | 
|  | 100 |  | 
|  | 101 | for (bit = 1; bit < 32; bit++) | 
|  | 102 | if (!(ipr & (1<<bit))) | 
|  | 103 | p += sprintf(p, "%s ", ec3104_name(bit)); | 
|  | 104 |  | 
|  | 105 | p += sprintf(p, ")\n"); | 
|  | 106 |  | 
|  | 107 | return p - buf; | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | static inline u32 ec3104_irq2mask(unsigned int irq) | 
|  | 111 | { | 
|  | 112 | return (1 << (irq - EC3104_IRQBASE)); | 
|  | 113 | } | 
|  | 114 |  | 
|  | 115 | static inline void mask_ec3104_irq(unsigned int irq) | 
|  | 116 | { | 
|  | 117 | u32 mask; | 
|  | 118 |  | 
|  | 119 | mask = ctrl_readl(EC3104_IMR); | 
|  | 120 |  | 
|  | 121 | mask |= ec3104_irq2mask(irq); | 
|  | 122 |  | 
|  | 123 | ctrl_writel(mask, EC3104_IMR); | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | static inline void unmask_ec3104_irq(unsigned int irq) | 
|  | 127 | { | 
|  | 128 | u32 mask; | 
|  | 129 |  | 
|  | 130 | mask = ctrl_readl(EC3104_IMR); | 
|  | 131 |  | 
|  | 132 | mask &= ~ec3104_irq2mask(irq); | 
|  | 133 |  | 
|  | 134 | ctrl_writel(mask, EC3104_IMR); | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | static void disable_ec3104_irq(unsigned int irq) | 
|  | 138 | { | 
|  | 139 | mask_ec3104_irq(irq); | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | static void enable_ec3104_irq(unsigned int irq) | 
|  | 143 | { | 
|  | 144 | unmask_ec3104_irq(irq); | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 | static void mask_and_ack_ec3104_irq(unsigned int irq) | 
|  | 148 | { | 
|  | 149 | mask_ec3104_irq(irq); | 
|  | 150 | } | 
|  | 151 |  | 
|  | 152 | static void end_ec3104_irq(unsigned int irq) | 
|  | 153 | { | 
|  | 154 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | 
|  | 155 | unmask_ec3104_irq(irq); | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | static unsigned int startup_ec3104_irq(unsigned int irq) | 
|  | 159 | { | 
|  | 160 | unmask_ec3104_irq(irq); | 
|  | 161 |  | 
|  | 162 | return 0; | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | static void shutdown_ec3104_irq(unsigned int irq) | 
|  | 166 | { | 
|  | 167 | mask_ec3104_irq(irq); | 
|  | 168 |  | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | static struct hw_interrupt_type ec3104_int = { | 
|  | 172 | .typename       = "EC3104", | 
|  | 173 | .enable         = enable_ec3104_irq, | 
|  | 174 | .disable        = disable_ec3104_irq, | 
|  | 175 | .ack            = mask_and_ack_ec3104_irq, | 
|  | 176 | .end            = end_ec3104_irq, | 
|  | 177 | .startup        = startup_ec3104_irq, | 
|  | 178 | .shutdown       = shutdown_ec3104_irq, | 
|  | 179 | }; | 
|  | 180 |  | 
|  | 181 | /* Yuck.  the _demux API is ugly */ | 
|  | 182 | int ec3104_irq_demux(int irq) | 
|  | 183 | { | 
|  | 184 | if (irq == EC3104_IRQ) { | 
|  | 185 | unsigned int mask; | 
|  | 186 |  | 
|  | 187 | mask = ctrl_readl(EC3104_IRR); | 
|  | 188 |  | 
|  | 189 | if (mask == 0xffffffff) | 
|  | 190 | return EC3104_IRQ; | 
|  | 191 | else | 
|  | 192 | return EC3104_IRQBASE + ffz(mask); | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | return irq; | 
|  | 196 | } |