1/* linux/arch/arm/plat-s5p/irq-eint.c 2 * 3 * Copyright (c) 2010 Samsung Electronics Co., Ltd. 4 * http://www.samsung.com 5 * 6 * S5P - IRQ EINT support 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11*/ 12 13#include <linux/kernel.h> 14#include <linux/interrupt.h> 15#include <linux/irq.h> 16#include <linux/io.h> 17#include <linux/sysdev.h> 18#include <linux/gpio.h> 19 20#include <asm/hardware/vic.h> 21 22#include <plat/regs-irqtype.h> 23 24#include <mach/map.h> 25#include <plat/cpu.h> 26#include <plat/pm.h> 27 28#include <plat/gpio-cfg.h> 29#include <mach/regs-gpio.h> 30 31static inline void s5p_irq_eint_mask(unsigned int irq) 32{ 33 u32 mask; 34 35 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(irq))); 36 mask |= eint_irq_to_bit(irq); 37 __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(irq))); 38} 39 40static void s5p_irq_eint_unmask(unsigned int irq) 41{ 42 u32 mask; 43 44 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(irq))); 45 mask &= ~(eint_irq_to_bit(irq)); 46 __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(irq))); 47} 48 49static inline void s5p_irq_eint_ack(unsigned int irq) 50{ 51 __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); 52} 53 54static void s5p_irq_eint_maskack(unsigned int irq) 55{ 56 /* compiler should in-line these */ 57 s5p_irq_eint_mask(irq); 58 s5p_irq_eint_ack(irq); 59} 60 61static int s5p_irq_eint_set_type(unsigned int irq, unsigned int type) 62{ 63 int offs = EINT_OFFSET(irq); 64 int shift; 65 u32 ctrl, mask; 66 u32 newvalue = 0; 67 68 switch (type) { 69 case IRQ_TYPE_EDGE_RISING: 70 newvalue = S5P_EXTINT_RISEEDGE; 71 break; 72 73 case IRQ_TYPE_EDGE_FALLING: 74 newvalue = S5P_EXTINT_FALLEDGE; 75 break; 76 77 case IRQ_TYPE_EDGE_BOTH: 78 newvalue = S5P_EXTINT_BOTHEDGE; 79 break; 80 81 case IRQ_TYPE_LEVEL_LOW: 82 newvalue = S5P_EXTINT_LOWLEV; 83 break; 84 85 case IRQ_TYPE_LEVEL_HIGH: 86 newvalue = S5P_EXTINT_HILEV; 87 break; 88 89 default: 90 printk(KERN_ERR "No such irq type %d", type); 91 return -EINVAL; 92 } 93 94 shift = (offs & 0x7) * 4; 95 mask = 0x7 << shift; 96 97 ctrl = __raw_readl(S5P_EINT_CON(EINT_REG_NR(irq))); 98 ctrl &= ~mask; 99 ctrl |= newvalue << shift; 100 __raw_writel(ctrl, S5P_EINT_CON(EINT_REG_NR(irq))); 101 102 if ((0 <= offs) && (offs < 8)) 103 s3c_gpio_cfgpin(EINT_GPIO_0(offs & 0x7), EINT_MODE); 104 105 else if ((8 <= offs) && (offs < 16)) 106 s3c_gpio_cfgpin(EINT_GPIO_1(offs & 0x7), EINT_MODE); 107 108 else if ((16 <= offs) && (offs < 24)) 109 s3c_gpio_cfgpin(EINT_GPIO_2(offs & 0x7), EINT_MODE); 110 111 else if ((24 <= offs) && (offs < 32)) 112 s3c_gpio_cfgpin(EINT_GPIO_3(offs & 0x7), EINT_MODE); 113 114 else 115 printk(KERN_ERR "No such irq number %d", offs); 116 117 return 0; 118} 119 120static struct irq_chip s5p_irq_eint = { 121 .name = "s5p-eint", 122 .mask = s5p_irq_eint_mask, 123 .unmask = s5p_irq_eint_unmask, 124 .mask_ack = s5p_irq_eint_maskack, 125 .ack = s5p_irq_eint_ack, 126 .set_type = s5p_irq_eint_set_type, 127#ifdef CONFIG_PM 128 .set_wake = s3c_irqext_wake, 129#endif 130}; 131 132/* s5p_irq_demux_eint 133 * 134 * This function demuxes the IRQ from the group0 external interrupts, 135 * from EINTs 16 to 31. It is designed to be inlined into the specific 136 * handler s5p_irq_demux_eintX_Y. 137 * 138 * Each EINT pend/mask registers handle eight of them. 139 */ 140static inline void s5p_irq_demux_eint(unsigned int start) 141{ 142 u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start))); 143 u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start))); 144 unsigned int irq; 145 146 status &= ~mask; 147 status &= 0xff; 148 149 while (status) { 150 irq = fls(status) - 1; 151 generic_handle_irq(irq + start); 152 status &= ~(1 << irq); 153 } 154} 155 156static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) 157{ 158 s5p_irq_demux_eint(IRQ_EINT(16)); 159 s5p_irq_demux_eint(IRQ_EINT(24)); 160} 161 162static inline void s5p_irq_vic_eint_mask(unsigned int irq) 163{ 164 void __iomem *base = get_irq_chip_data(irq); 165 166 s5p_irq_eint_mask(irq); 167 writel(1 << EINT_OFFSET(irq), base + VIC_INT_ENABLE_CLEAR); 168} 169 170static void s5p_irq_vic_eint_unmask(unsigned int irq) 171{ 172 void __iomem *base = get_irq_chip_data(irq); 173 174 s5p_irq_eint_unmask(irq); 175 writel(1 << EINT_OFFSET(irq), base + VIC_INT_ENABLE); 176} 177 178static inline void s5p_irq_vic_eint_ack(unsigned int irq) 179{ 180 __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); 181} 182 183static void s5p_irq_vic_eint_maskack(unsigned int irq) 184{ 185 s5p_irq_vic_eint_mask(irq); 186 s5p_irq_vic_eint_ack(irq); 187} 188 189static struct irq_chip s5p_irq_vic_eint = { 190 .name = "s5p_vic_eint", 191 .mask = s5p_irq_vic_eint_mask, 192 .unmask = s5p_irq_vic_eint_unmask, 193 .mask_ack = s5p_irq_vic_eint_maskack, 194 .ack = s5p_irq_vic_eint_ack, 195 .set_type = s5p_irq_eint_set_type, 196#ifdef CONFIG_PM 197 .set_wake = s3c_irqext_wake, 198#endif 199}; 200 201int __init s5p_init_irq_eint(void) 202{ 203 int irq; 204 205 for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++) 206 set_irq_chip(irq, &s5p_irq_vic_eint); 207 208 for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) { 209 set_irq_chip(irq, &s5p_irq_eint); 210 set_irq_handler(irq, handle_level_irq); 211 set_irq_flags(irq, IRQF_VALID); 212 } 213 214 set_irq_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31); 215 return 0; 216} 217 218arch_initcall(s5p_init_irq_eint); 219