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