Kyungmin Park | b0d5217 | 2009-11-17 08:41:16 +0100 | [diff] [blame] | 1 | /* |
| 2 | * linux/arch/arm/plat-s5pc1xx/irq-eint.c |
| 3 | * |
| 4 | * Copyright 2009 Samsung Electronics Co. |
| 5 | * Byungho Min <bhmin@samsung.com> |
| 6 | * Kyungin Park <kyungmin.park@samsung.com> |
| 7 | * |
| 8 | * Based on plat-s3c64xx/irq-eint.c |
| 9 | * |
| 10 | * S5PC1XX - Interrupt handling for IRQ_EINT(x) |
| 11 | * |
| 12 | * This program is free software; you can redistribute it and/or modify |
| 13 | * it under the terms of the GNU General Public License version 2 as |
| 14 | * published by the Free Software Foundation. |
| 15 | */ |
| 16 | |
| 17 | #include <linux/kernel.h> |
| 18 | #include <linux/interrupt.h> |
| 19 | #include <linux/irq.h> |
| 20 | #include <linux/io.h> |
| 21 | #include <linux/sysdev.h> |
| 22 | #include <linux/pm.h> |
| 23 | #include <linux/gpio.h> |
| 24 | |
| 25 | #include <asm/hardware/vic.h> |
| 26 | |
| 27 | #include <mach/map.h> |
| 28 | |
| 29 | #include <plat/gpio-cfg.h> |
| 30 | #include <plat/gpio-ext.h> |
| 31 | #include <plat/pm.h> |
| 32 | #include <plat/regs-gpio.h> |
| 33 | #include <plat/regs-irqtype.h> |
| 34 | |
| 35 | /* |
| 36 | * bank is a group of external interrupt |
| 37 | * bank0 means EINT0 ... EINT7 |
| 38 | * bank1 means EINT8 ... EINT15 |
| 39 | * bank2 means EINT16 ... EINT23 |
| 40 | * bank3 means EINT24 ... EINT31 |
| 41 | */ |
| 42 | |
| 43 | static inline int s3c_get_eint(unsigned int irq) |
| 44 | { |
| 45 | int real; |
| 46 | |
| 47 | if (irq < IRQ_EINT16_31) |
| 48 | real = (irq - IRQ_EINT0); |
| 49 | else |
| 50 | real = (irq - S3C_IRQ_EINT_BASE) + IRQ_EINT16_31 - IRQ_EINT0; |
| 51 | |
| 52 | return real; |
| 53 | } |
| 54 | |
| 55 | static inline int s3c_get_bank(unsigned int irq) |
| 56 | { |
| 57 | return s3c_get_eint(irq) >> 3; |
| 58 | } |
| 59 | |
| 60 | static inline int s3c_eint_to_bit(unsigned int irq) |
| 61 | { |
| 62 | int real, bit; |
| 63 | |
| 64 | real = s3c_get_eint(irq); |
| 65 | bit = 1 << (real & (8 - 1)); |
| 66 | |
| 67 | return bit; |
| 68 | } |
| 69 | |
| 70 | static inline void s3c_irq_eint_mask(unsigned int irq) |
| 71 | { |
| 72 | u32 mask; |
| 73 | u32 bank = s3c_get_bank(irq); |
| 74 | |
| 75 | mask = __raw_readl(S5PC1XX_WKUP_INT_MASK(bank)); |
| 76 | mask |= s3c_eint_to_bit(irq); |
| 77 | __raw_writel(mask, S5PC1XX_WKUP_INT_MASK(bank)); |
| 78 | } |
| 79 | |
| 80 | static void s3c_irq_eint_unmask(unsigned int irq) |
| 81 | { |
| 82 | u32 mask; |
| 83 | u32 bank = s3c_get_bank(irq); |
| 84 | |
| 85 | mask = __raw_readl(S5PC1XX_WKUP_INT_MASK(bank)); |
| 86 | mask &= ~(s3c_eint_to_bit(irq)); |
| 87 | __raw_writel(mask, S5PC1XX_WKUP_INT_MASK(bank)); |
| 88 | } |
| 89 | |
| 90 | static inline void s3c_irq_eint_ack(unsigned int irq) |
| 91 | { |
| 92 | u32 bank = s3c_get_bank(irq); |
| 93 | |
| 94 | __raw_writel(s3c_eint_to_bit(irq), S5PC1XX_WKUP_INT_PEND(bank)); |
| 95 | } |
| 96 | |
| 97 | static void s3c_irq_eint_maskack(unsigned int irq) |
| 98 | { |
| 99 | /* compiler should in-line these */ |
| 100 | s3c_irq_eint_mask(irq); |
| 101 | s3c_irq_eint_ack(irq); |
| 102 | } |
| 103 | |
| 104 | static int s3c_irq_eint_set_type(unsigned int irq, unsigned int type) |
| 105 | { |
| 106 | u32 bank = s3c_get_bank(irq); |
| 107 | int real = s3c_get_eint(irq); |
| 108 | int gpio, shift, sfn; |
| 109 | u32 ctrl, con = 0; |
| 110 | |
| 111 | switch (type) { |
| 112 | case IRQ_TYPE_NONE: |
| 113 | printk(KERN_WARNING "No edge setting!\n"); |
| 114 | break; |
| 115 | |
| 116 | case IRQ_TYPE_EDGE_RISING: |
| 117 | con = S5PC1XX_WKUP_INT_RISEEDGE; |
| 118 | break; |
| 119 | |
| 120 | case IRQ_TYPE_EDGE_FALLING: |
| 121 | con = S5PC1XX_WKUP_INT_FALLEDGE; |
| 122 | break; |
| 123 | |
| 124 | case IRQ_TYPE_EDGE_BOTH: |
| 125 | con = S5PC1XX_WKUP_INT_BOTHEDGE; |
| 126 | break; |
| 127 | |
| 128 | case IRQ_TYPE_LEVEL_LOW: |
| 129 | con = S5PC1XX_WKUP_INT_LOWLEV; |
| 130 | break; |
| 131 | |
| 132 | case IRQ_TYPE_LEVEL_HIGH: |
| 133 | con = S5PC1XX_WKUP_INT_HILEV; |
| 134 | break; |
| 135 | |
| 136 | default: |
| 137 | printk(KERN_ERR "No such irq type %d", type); |
| 138 | return -EINVAL; |
| 139 | } |
| 140 | |
| 141 | gpio = real & (8 - 1); |
| 142 | shift = gpio << 2; |
| 143 | |
| 144 | ctrl = __raw_readl(S5PC1XX_WKUP_INT_CON(bank)); |
| 145 | ctrl &= ~(0x7 << shift); |
| 146 | ctrl |= con << shift; |
| 147 | __raw_writel(ctrl, S5PC1XX_WKUP_INT_CON(bank)); |
| 148 | |
| 149 | switch (real) { |
| 150 | case 0 ... 7: |
| 151 | gpio = S5PC100_GPH0(gpio); |
| 152 | break; |
| 153 | case 8 ... 15: |
| 154 | gpio = S5PC100_GPH1(gpio); |
| 155 | break; |
| 156 | case 16 ... 23: |
| 157 | gpio = S5PC100_GPH2(gpio); |
| 158 | break; |
| 159 | case 24 ... 31: |
| 160 | gpio = S5PC100_GPH3(gpio); |
| 161 | break; |
| 162 | default: |
| 163 | return -EINVAL; |
| 164 | } |
| 165 | |
| 166 | sfn = S3C_GPIO_SFN(0x2); |
| 167 | s3c_gpio_cfgpin(gpio, sfn); |
| 168 | |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | static struct irq_chip s3c_irq_eint = { |
| 173 | .name = "EINT", |
| 174 | .mask = s3c_irq_eint_mask, |
| 175 | .unmask = s3c_irq_eint_unmask, |
| 176 | .mask_ack = s3c_irq_eint_maskack, |
| 177 | .ack = s3c_irq_eint_ack, |
| 178 | .set_type = s3c_irq_eint_set_type, |
| 179 | .set_wake = s3c_irqext_wake, |
| 180 | }; |
| 181 | |
| 182 | /* s3c_irq_demux_eint |
| 183 | * |
| 184 | * This function demuxes the IRQ from external interrupts, |
| 185 | * from IRQ_EINT(16) to IRQ_EINT(31). It is designed to be inlined into |
| 186 | * the specific handlers s3c_irq_demux_eintX_Y. |
| 187 | */ |
| 188 | static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end) |
| 189 | { |
| 190 | u32 status = __raw_readl(S5PC1XX_WKUP_INT_PEND((start >> 3))); |
| 191 | u32 mask = __raw_readl(S5PC1XX_WKUP_INT_MASK((start >> 3))); |
| 192 | unsigned int irq; |
| 193 | |
| 194 | status &= ~mask; |
| 195 | status &= (1 << (end - start + 1)) - 1; |
| 196 | |
| 197 | for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) { |
| 198 | if (status & 1) |
| 199 | generic_handle_irq(irq); |
| 200 | |
| 201 | status >>= 1; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | static void s3c_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) |
| 206 | { |
| 207 | s3c_irq_demux_eint(16, 23); |
| 208 | s3c_irq_demux_eint(24, 31); |
| 209 | } |
| 210 | |
| 211 | /* |
| 212 | * Handle EINT0 ... EINT15 at VIC directly |
| 213 | */ |
| 214 | static void s3c_irq_vic_eint_mask(unsigned int irq) |
| 215 | { |
| 216 | void __iomem *base = get_irq_chip_data(irq); |
| 217 | unsigned int real; |
| 218 | |
| 219 | s3c_irq_eint_mask(irq); |
| 220 | real = s3c_get_eint(irq); |
| 221 | writel(1 << real, base + VIC_INT_ENABLE_CLEAR); |
| 222 | } |
| 223 | |
| 224 | static void s3c_irq_vic_eint_unmask(unsigned int irq) |
| 225 | { |
| 226 | void __iomem *base = get_irq_chip_data(irq); |
| 227 | unsigned int real; |
| 228 | |
| 229 | s3c_irq_eint_unmask(irq); |
| 230 | real = s3c_get_eint(irq); |
| 231 | writel(1 << real, base + VIC_INT_ENABLE); |
| 232 | } |
| 233 | |
| 234 | static inline void s3c_irq_vic_eint_ack(unsigned int irq) |
| 235 | { |
| 236 | u32 bit; |
| 237 | u32 bank = s3c_get_bank(irq); |
| 238 | |
| 239 | bit = s3c_eint_to_bit(irq); |
| 240 | __raw_writel(bit, S5PC1XX_WKUP_INT_PEND(bank)); |
| 241 | } |
| 242 | |
| 243 | static void s3c_irq_vic_eint_maskack(unsigned int irq) |
| 244 | { |
| 245 | /* compiler should in-line these */ |
| 246 | s3c_irq_vic_eint_mask(irq); |
| 247 | s3c_irq_vic_eint_ack(irq); |
| 248 | } |
| 249 | |
| 250 | static struct irq_chip s3c_irq_vic_eint = { |
| 251 | .name = "EINT", |
| 252 | .mask = s3c_irq_vic_eint_mask, |
| 253 | .unmask = s3c_irq_vic_eint_unmask, |
| 254 | .mask_ack = s3c_irq_vic_eint_maskack, |
| 255 | .ack = s3c_irq_vic_eint_ack, |
| 256 | .set_type = s3c_irq_eint_set_type, |
| 257 | .set_wake = s3c_irqext_wake, |
| 258 | }; |
| 259 | |
| 260 | static int __init s5pc1xx_init_irq_eint(void) |
| 261 | { |
| 262 | int irq; |
| 263 | |
| 264 | for (irq = IRQ_EINT0; irq <= IRQ_EINT15; irq++) { |
| 265 | set_irq_chip(irq, &s3c_irq_vic_eint); |
| 266 | set_irq_handler(irq, handle_level_irq); |
| 267 | set_irq_flags(irq, IRQF_VALID); |
| 268 | } |
| 269 | |
| 270 | for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) { |
| 271 | set_irq_chip(irq, &s3c_irq_eint); |
| 272 | set_irq_handler(irq, handle_level_irq); |
| 273 | set_irq_flags(irq, IRQF_VALID); |
| 274 | } |
| 275 | |
| 276 | set_irq_chained_handler(IRQ_EINT16_31, s3c_irq_demux_eint16_31); |
| 277 | |
| 278 | return 0; |
| 279 | } |
| 280 | |
| 281 | arch_initcall(s5pc1xx_init_irq_eint); |