Path: blob/master/arch/arm/mach-exynos4/irq-eint.c
10817 views
/* linux/arch/arm/mach-exynos4/irq-eint.c1*2* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.3* http://www.samsung.com4*5* EXYNOS4 - IRQ EINT support6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*/1112#include <linux/kernel.h>13#include <linux/interrupt.h>14#include <linux/irq.h>15#include <linux/io.h>16#include <linux/sysdev.h>17#include <linux/gpio.h>1819#include <plat/pm.h>20#include <plat/cpu.h>21#include <plat/gpio-cfg.h>2223#include <mach/regs-gpio.h>2425static DEFINE_SPINLOCK(eint_lock);2627static unsigned int eint0_15_data[16];2829static unsigned int exynos4_get_irq_nr(unsigned int number)30{31u32 ret = 0;3233switch (number) {34case 0 ... 3:35ret = (number + IRQ_EINT0);36break;37case 4 ... 7:38ret = (number + (IRQ_EINT4 - 4));39break;40case 8 ... 15:41ret = (number + (IRQ_EINT8 - 8));42break;43default:44printk(KERN_ERR "number available : %d\n", number);45}4647return ret;48}4950static inline void exynos4_irq_eint_mask(struct irq_data *data)51{52u32 mask;5354spin_lock(&eint_lock);55mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq)));56mask |= eint_irq_to_bit(data->irq);57__raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq)));58spin_unlock(&eint_lock);59}6061static void exynos4_irq_eint_unmask(struct irq_data *data)62{63u32 mask;6465spin_lock(&eint_lock);66mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq)));67mask &= ~(eint_irq_to_bit(data->irq));68__raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq)));69spin_unlock(&eint_lock);70}7172static inline void exynos4_irq_eint_ack(struct irq_data *data)73{74__raw_writel(eint_irq_to_bit(data->irq),75S5P_EINT_PEND(EINT_REG_NR(data->irq)));76}7778static void exynos4_irq_eint_maskack(struct irq_data *data)79{80exynos4_irq_eint_mask(data);81exynos4_irq_eint_ack(data);82}8384static int exynos4_irq_eint_set_type(struct irq_data *data, unsigned int type)85{86int offs = EINT_OFFSET(data->irq);87int shift;88u32 ctrl, mask;89u32 newvalue = 0;9091switch (type) {92case IRQ_TYPE_EDGE_RISING:93newvalue = S5P_IRQ_TYPE_EDGE_RISING;94break;9596case IRQ_TYPE_EDGE_FALLING:97newvalue = S5P_IRQ_TYPE_EDGE_FALLING;98break;99100case IRQ_TYPE_EDGE_BOTH:101newvalue = S5P_IRQ_TYPE_EDGE_BOTH;102break;103104case IRQ_TYPE_LEVEL_LOW:105newvalue = S5P_IRQ_TYPE_LEVEL_LOW;106break;107108case IRQ_TYPE_LEVEL_HIGH:109newvalue = S5P_IRQ_TYPE_LEVEL_HIGH;110break;111112default:113printk(KERN_ERR "No such irq type %d", type);114return -EINVAL;115}116117shift = (offs & 0x7) * 4;118mask = 0x7 << shift;119120spin_lock(&eint_lock);121ctrl = __raw_readl(S5P_EINT_CON(EINT_REG_NR(data->irq)));122ctrl &= ~mask;123ctrl |= newvalue << shift;124__raw_writel(ctrl, S5P_EINT_CON(EINT_REG_NR(data->irq)));125spin_unlock(&eint_lock);126127switch (offs) {128case 0 ... 7:129s3c_gpio_cfgpin(EINT_GPIO_0(offs & 0x7), EINT_MODE);130break;131case 8 ... 15:132s3c_gpio_cfgpin(EINT_GPIO_1(offs & 0x7), EINT_MODE);133break;134case 16 ... 23:135s3c_gpio_cfgpin(EINT_GPIO_2(offs & 0x7), EINT_MODE);136break;137case 24 ... 31:138s3c_gpio_cfgpin(EINT_GPIO_3(offs & 0x7), EINT_MODE);139break;140default:141printk(KERN_ERR "No such irq number %d", offs);142}143144return 0;145}146147static struct irq_chip exynos4_irq_eint = {148.name = "exynos4-eint",149.irq_mask = exynos4_irq_eint_mask,150.irq_unmask = exynos4_irq_eint_unmask,151.irq_mask_ack = exynos4_irq_eint_maskack,152.irq_ack = exynos4_irq_eint_ack,153.irq_set_type = exynos4_irq_eint_set_type,154#ifdef CONFIG_PM155.irq_set_wake = s3c_irqext_wake,156#endif157};158159/* exynos4_irq_demux_eint160*161* This function demuxes the IRQ from from EINTs 16 to 31.162* It is designed to be inlined into the specific handler163* s5p_irq_demux_eintX_Y.164*165* Each EINT pend/mask registers handle eight of them.166*/167static inline void exynos4_irq_demux_eint(unsigned int start)168{169unsigned int irq;170171u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start)));172u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start)));173174status &= ~mask;175status &= 0xff;176177while (status) {178irq = fls(status) - 1;179generic_handle_irq(irq + start);180status &= ~(1 << irq);181}182}183184static void exynos4_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)185{186exynos4_irq_demux_eint(IRQ_EINT(16));187exynos4_irq_demux_eint(IRQ_EINT(24));188}189190static void exynos4_irq_eint0_15(unsigned int irq, struct irq_desc *desc)191{192u32 *irq_data = irq_get_handler_data(irq);193struct irq_chip *chip = irq_get_chip(irq);194195chip->irq_mask(&desc->irq_data);196197if (chip->irq_ack)198chip->irq_ack(&desc->irq_data);199200generic_handle_irq(*irq_data);201202chip->irq_unmask(&desc->irq_data);203}204205int __init exynos4_init_irq_eint(void)206{207int irq;208209for (irq = 0 ; irq <= 31 ; irq++) {210irq_set_chip_and_handler(IRQ_EINT(irq), &exynos4_irq_eint,211handle_level_irq);212set_irq_flags(IRQ_EINT(irq), IRQF_VALID);213}214215irq_set_chained_handler(IRQ_EINT16_31, exynos4_irq_demux_eint16_31);216217for (irq = 0 ; irq <= 15 ; irq++) {218eint0_15_data[irq] = IRQ_EINT(irq);219220irq_set_handler_data(exynos4_get_irq_nr(irq),221&eint0_15_data[irq]);222irq_set_chained_handler(exynos4_get_irq_nr(irq),223exynos4_irq_eint0_15);224}225226return 0;227}228229arch_initcall(exynos4_init_irq_eint);230231232