linux/arch/arm/mach-s3c64xx/irq-eint.c
<<
>>
Prefs
   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)    ((u32)(1 << eint_offset(irq)))
  34
  35static inline void s3c_irq_eint_mask(struct irq_data *data)
  36{
  37        u32 mask;
  38
  39        mask = __raw_readl(S3C64XX_EINT0MASK);
  40        mask |= (u32)data->chip_data;
  41        __raw_writel(mask, S3C64XX_EINT0MASK);
  42}
  43
  44static void s3c_irq_eint_unmask(struct irq_data *data)
  45{
  46        u32 mask;
  47
  48        mask = __raw_readl(S3C64XX_EINT0MASK);
  49        mask &= ~((u32)data->chip_data);
  50        __raw_writel(mask, S3C64XX_EINT0MASK);
  51}
  52
  53static inline void s3c_irq_eint_ack(struct irq_data *data)
  54{
  55        __raw_writel((u32)data->chip_data, S3C64XX_EINT0PEND);
  56}
  57
  58static void s3c_irq_eint_maskack(struct irq_data *data)
  59{
  60        /* compiler should in-line these */
  61        s3c_irq_eint_mask(data);
  62        s3c_irq_eint_ack(data);
  63}
  64
  65static int s3c_irq_eint_set_type(struct irq_data *data, unsigned int type)
  66{
  67        int offs = eint_offset(data->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        .irq_mask       = s3c_irq_eint_mask,
 144        .irq_unmask     = s3c_irq_eint_unmask,
 145        .irq_mask_ack   = s3c_irq_eint_maskack,
 146        .irq_ack        = s3c_irq_eint_ack,
 147        .irq_set_type   = s3c_irq_eint_set_type,
 148        .irq_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_chip_data(irq, (void *)eint_irq_to_bit(irq));
 202                set_irq_handler(irq, handle_level_irq);
 203                set_irq_flags(irq, IRQF_VALID);
 204        }
 205
 206        set_irq_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3);
 207        set_irq_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
 208        set_irq_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19);
 209        set_irq_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27);
 210
 211        return 0;
 212}
 213
 214arch_initcall(s3c64xx_init_irq_eint);
 215