linux/arch/arm/mach-s3c2412/irq.c
<<
>>
Prefs
   1/* linux/arch/arm/mach-s3c2412/irq.c
   2 *
   3 * Copyright (c) 2006 Simtec Electronics
   4 *      Ben Dooks <ben@simtec.co.uk>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19 *
  20*/
  21
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/interrupt.h>
  25#include <linux/ioport.h>
  26#include <linux/sysdev.h>
  27#include <linux/io.h>
  28
  29#include <mach/hardware.h>
  30#include <asm/irq.h>
  31
  32#include <asm/mach/irq.h>
  33
  34#include <mach/regs-irq.h>
  35#include <mach/regs-gpio.h>
  36#include <mach/regs-power.h>
  37
  38#include <plat/cpu.h>
  39#include <plat/irq.h>
  40#include <plat/pm.h>
  41
  42#define INTMSK(start, end) ((1 << ((end) + 1 - (start))) - 1)
  43#define INTMSK_SUB(start, end) (INTMSK(start, end) << ((start - S3C2410_IRQSUB(0))))
  44
  45/* the s3c2412 changes the behaviour of IRQ_EINT0 through IRQ_EINT3 by
  46 * having them turn up in both the INT* and the EINT* registers. Whilst
  47 * both show the status, they both now need to be acked when the IRQs
  48 * go off.
  49*/
  50
  51static void
  52s3c2412_irq_mask(unsigned int irqno)
  53{
  54        unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
  55        unsigned long mask;
  56
  57        mask = __raw_readl(S3C2410_INTMSK);
  58        __raw_writel(mask | bitval, S3C2410_INTMSK);
  59
  60        mask = __raw_readl(S3C2412_EINTMASK);
  61        __raw_writel(mask | bitval, S3C2412_EINTMASK);
  62}
  63
  64static inline void
  65s3c2412_irq_ack(unsigned int irqno)
  66{
  67        unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
  68
  69        __raw_writel(bitval, S3C2412_EINTPEND);
  70        __raw_writel(bitval, S3C2410_SRCPND);
  71        __raw_writel(bitval, S3C2410_INTPND);
  72}
  73
  74static inline void
  75s3c2412_irq_maskack(unsigned int irqno)
  76{
  77        unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
  78        unsigned long mask;
  79
  80        mask = __raw_readl(S3C2410_INTMSK);
  81        __raw_writel(mask|bitval, S3C2410_INTMSK);
  82
  83        mask = __raw_readl(S3C2412_EINTMASK);
  84        __raw_writel(mask | bitval, S3C2412_EINTMASK);
  85
  86        __raw_writel(bitval, S3C2412_EINTPEND);
  87        __raw_writel(bitval, S3C2410_SRCPND);
  88        __raw_writel(bitval, S3C2410_INTPND);
  89}
  90
  91static void
  92s3c2412_irq_unmask(unsigned int irqno)
  93{
  94        unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
  95        unsigned long mask;
  96
  97        mask = __raw_readl(S3C2412_EINTMASK);
  98        __raw_writel(mask & ~bitval, S3C2412_EINTMASK);
  99
 100        mask = __raw_readl(S3C2410_INTMSK);
 101        __raw_writel(mask & ~bitval, S3C2410_INTMSK);
 102}
 103
 104static struct irq_chip s3c2412_irq_eint0t4 = {
 105        .ack       = s3c2412_irq_ack,
 106        .mask      = s3c2412_irq_mask,
 107        .unmask    = s3c2412_irq_unmask,
 108        .set_wake  = s3c_irq_wake,
 109        .set_type  = s3c_irqext_type,
 110};
 111
 112#define INTBIT(x)       (1 << ((x) - S3C2410_IRQSUB(0)))
 113
 114/* CF and SDI sub interrupts */
 115
 116static void s3c2412_irq_demux_cfsdi(unsigned int irq, struct irq_desc *desc)
 117{
 118        unsigned int subsrc, submsk;
 119
 120        subsrc = __raw_readl(S3C2410_SUBSRCPND);
 121        submsk = __raw_readl(S3C2410_INTSUBMSK);
 122
 123        subsrc  &= ~submsk;
 124
 125        if (subsrc & INTBIT(IRQ_S3C2412_SDI))
 126                generic_handle_irq(IRQ_S3C2412_SDI);
 127
 128        if (subsrc & INTBIT(IRQ_S3C2412_CF))
 129                generic_handle_irq(IRQ_S3C2412_CF);
 130}
 131
 132#define INTMSK_CFSDI    (1UL << (IRQ_S3C2412_CFSDI - IRQ_EINT0))
 133#define SUBMSK_CFSDI    INTMSK_SUB(IRQ_S3C2412_SDI, IRQ_S3C2412_CF)
 134
 135static void s3c2412_irq_cfsdi_mask(unsigned int irqno)
 136{
 137        s3c_irqsub_mask(irqno, INTMSK_CFSDI, SUBMSK_CFSDI);
 138}
 139
 140static void s3c2412_irq_cfsdi_unmask(unsigned int irqno)
 141{
 142        s3c_irqsub_unmask(irqno, INTMSK_CFSDI);
 143}
 144
 145static void s3c2412_irq_cfsdi_ack(unsigned int irqno)
 146{
 147        s3c_irqsub_maskack(irqno, INTMSK_CFSDI, SUBMSK_CFSDI);
 148}
 149
 150static struct irq_chip s3c2412_irq_cfsdi = {
 151        .name           = "s3c2412-cfsdi",
 152        .ack            = s3c2412_irq_cfsdi_ack,
 153        .mask           = s3c2412_irq_cfsdi_mask,
 154        .unmask         = s3c2412_irq_cfsdi_unmask,
 155};
 156
 157static int s3c2412_irq_rtc_wake(unsigned int irqno, unsigned int state)
 158{
 159        unsigned long pwrcfg;
 160
 161        pwrcfg = __raw_readl(S3C2412_PWRCFG);
 162        if (state)
 163                pwrcfg &= ~S3C2412_PWRCFG_RTC_MASKIRQ;
 164        else
 165                pwrcfg |= S3C2412_PWRCFG_RTC_MASKIRQ;
 166        __raw_writel(pwrcfg, S3C2412_PWRCFG);
 167
 168        return s3c_irq_chip.set_wake(irqno, state);
 169}
 170
 171static struct irq_chip s3c2412_irq_rtc_chip;
 172
 173static int s3c2412_irq_add(struct sys_device *sysdev)
 174{
 175        unsigned int irqno;
 176
 177        for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
 178                set_irq_chip(irqno, &s3c2412_irq_eint0t4);
 179                set_irq_handler(irqno, handle_edge_irq);
 180                set_irq_flags(irqno, IRQF_VALID);
 181        }
 182
 183        /* add demux support for CF/SDI */
 184
 185        set_irq_chained_handler(IRQ_S3C2412_CFSDI, s3c2412_irq_demux_cfsdi);
 186
 187        for (irqno = IRQ_S3C2412_SDI; irqno <= IRQ_S3C2412_CF; irqno++) {
 188                set_irq_chip(irqno, &s3c2412_irq_cfsdi);
 189                set_irq_handler(irqno, handle_level_irq);
 190                set_irq_flags(irqno, IRQF_VALID);
 191        }
 192
 193        /* change RTC IRQ's set wake method */
 194
 195        s3c2412_irq_rtc_chip = s3c_irq_chip;
 196        s3c2412_irq_rtc_chip.set_wake = s3c2412_irq_rtc_wake;
 197
 198        set_irq_chip(IRQ_RTC, &s3c2412_irq_rtc_chip);
 199
 200        return 0;
 201}
 202
 203static struct sysdev_driver s3c2412_irq_driver = {
 204        .add            = s3c2412_irq_add,
 205        .suspend        = s3c24xx_irq_suspend,
 206        .resume         = s3c24xx_irq_resume,
 207};
 208
 209static int s3c2412_irq_init(void)
 210{
 211        return sysdev_driver_register(&s3c2412_sysclass, &s3c2412_irq_driver);
 212}
 213
 214arch_initcall(s3c2412_irq_init);
 215