1/* arch/arm/plat-s3c64xx/irq-eint.c 2 * 3 * Copyright 2008 Openmoko, Inc. 4 * Copyright 2008 Simtec Electronics 5 * Ben Dooks <ben@simtec.co.uk> 6 * http://armlinux.simtec.co.uk/ 7 * 8 * S3C64XX - Interrupt handling for IRQ_EINT(x) 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15#include <linux/kernel.h> 16#include <linux/interrupt.h> 17#include <linux/sysdev.h> 18#include <linux/gpio.h> 19#include <linux/irq.h> 20#include <linux/io.h> 21 22#include <asm/hardware/vic.h> 23 24#include <plat/regs-irqtype.h> 25#include <mach/regs-gpio.h> 26#include <plat/gpio-cfg.h> 27 28#include <mach/map.h> 29#include <plat/cpu.h> 30#include <plat/pm.h> 31 32#define eint_offset(irq) ((irq) - IRQ_EINT(0)) 33#define eint_irq_to_bit(irq) (1 << eint_offset(irq)) 34 35static inline void s3c_irq_eint_mask(unsigned int irq) 36{ 37 u32 mask; 38 39 mask = __raw_readl(S3C64XX_EINT0MASK); 40 mask |= eint_irq_to_bit(irq); 41 __raw_writel(mask, S3C64XX_EINT0MASK); 42} 43 44static void s3c_irq_eint_unmask(unsigned int irq) 45{ 46 u32 mask; 47 48 mask = __raw_readl(S3C64XX_EINT0MASK); 49 mask &= ~eint_irq_to_bit(irq); 50 __raw_writel(mask, S3C64XX_EINT0MASK); 51} 52 53static inline void s3c_irq_eint_ack(unsigned int irq) 54{ 55 __raw_writel(eint_irq_to_bit(irq), S3C64XX_EINT0PEND); 56} 57 58static void s3c_irq_eint_maskack(unsigned int irq) 59{ 60 /* compiler should in-line these */ 61 s3c_irq_eint_mask(irq); 62 s3c_irq_eint_ack(irq); 63} 64 65static int s3c_irq_eint_set_type(unsigned int irq, unsigned int type) 66{ 67 int offs = eint_offset(irq); 68 int pin, pin_val; 69 int shift; 70 u32 ctrl, mask; 71 u32 newvalue = 0; 72 void __iomem *reg; 73 74 if (offs > 27) 75 return -EINVAL; 76 77 if (offs <= 15) 78 reg = S3C64XX_EINT0CON0; 79 else 80 reg = S3C64XX_EINT0CON1; 81 82 switch (type) { 83 case IRQ_TYPE_NONE: 84 printk(KERN_WARNING "No edge setting!\n"); 85 break; 86 87 case IRQ_TYPE_EDGE_RISING: 88 newvalue = S3C2410_EXTINT_RISEEDGE; 89 break; 90 91 case IRQ_TYPE_EDGE_FALLING: 92 newvalue = S3C2410_EXTINT_FALLEDGE; 93 break; 94 95 case IRQ_TYPE_EDGE_BOTH: 96 newvalue = S3C2410_EXTINT_BOTHEDGE; 97 break; 98 99 case IRQ_TYPE_LEVEL_LOW: 100 newvalue = S3C2410_EXTINT_LOWLEV; 101 break; 102 103 case IRQ_TYPE_LEVEL_HIGH: 104 newvalue = S3C2410_EXTINT_HILEV; 105 break; 106 107 default: 108 printk(KERN_ERR "No such irq type %d", type); 109 return -1; 110 } 111 112 if (offs <= 15) 113 shift = (offs / 2) * 4; 114 else 115 shift = ((offs - 16) / 2) * 4; 116 mask = 0x7 << shift; 117 118 ctrl = __raw_readl(reg); 119 ctrl &= ~mask; 120 ctrl |= newvalue << shift; 121 __raw_writel(ctrl, reg); 122 123 /* set the GPIO pin appropriately */ 124 125 if (offs < 16) { 126 pin = S3C64XX_GPN(offs); 127 pin_val = S3C_GPIO_SFN(2); 128 } else if (offs < 23) { 129 pin = S3C64XX_GPL(offs + 8 - 16); 130 pin_val = S3C_GPIO_SFN(3); 131 } else { 132 pin = S3C64XX_GPM(offs - 23); 133 pin_val = S3C_GPIO_SFN(3); 134 } 135 136 s3c_gpio_cfgpin(pin, pin_val); 137 138 return 0; 139} 140 141static struct irq_chip s3c_irq_eint = { 142 .name = "s3c-eint", 143 .mask = s3c_irq_eint_mask, 144 .unmask = s3c_irq_eint_unmask, 145 .mask_ack = s3c_irq_eint_maskack, 146 .ack = s3c_irq_eint_ack, 147 .set_type = s3c_irq_eint_set_type, 148 .set_wake = s3c_irqext_wake, 149}; 150 151/* s3c_irq_demux_eint 152 * 153 * This function demuxes the IRQ from the group0 external interrupts, 154 * from IRQ_EINT(0) to IRQ_EINT(27). It is designed to be inlined into 155 * the specific handlers s3c_irq_demux_eintX_Y. 156 */ 157static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end) 158{ 159 u32 status = __raw_readl(S3C64XX_EINT0PEND); 160 u32 mask = __raw_readl(S3C64XX_EINT0MASK); 161 unsigned int irq; 162 163 status &= ~mask; 164 status >>= start; 165 status &= (1 << (end - start + 1)) - 1; 166 167 for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) { 168 if (status & 1) 169 generic_handle_irq(irq); 170 171 status >>= 1; 172 } 173} 174 175static void s3c_irq_demux_eint0_3(unsigned int irq, struct irq_desc *desc) 176{ 177 s3c_irq_demux_eint(0, 3); 178} 179 180static void s3c_irq_demux_eint4_11(unsigned int irq, struct irq_desc *desc) 181{ 182 s3c_irq_demux_eint(4, 11); 183} 184 185static void s3c_irq_demux_eint12_19(unsigned int irq, struct irq_desc *desc) 186{ 187 s3c_irq_demux_eint(12, 19); 188} 189 190static void s3c_irq_demux_eint20_27(unsigned int irq, struct irq_desc *desc) 191{ 192 s3c_irq_demux_eint(20, 27); 193} 194 195static int __init s3c64xx_init_irq_eint(void) 196{ 197 int irq; 198 199 for (irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) { 200 set_irq_chip(irq, &s3c_irq_eint); 201 set_irq_handler(irq, handle_level_irq); 202 set_irq_flags(irq, IRQF_VALID); 203 } 204 205 set_irq_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3); 206 set_irq_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11); 207 set_irq_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19); 208 set_irq_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27); 209 210 return 0; 211} 212 213arch_initcall(s3c64xx_init_irq_eint); 214