linux/arch/arm/mach-msm/sirc.c
<<
>>
Prefs
   1/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 and
   5 * only version 2 as published by the Free Software Foundation.
   6 *
   7 * This program is distributed in the hope that it will be useful,
   8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10 * GNU General Public License for more details.
  11 *
  12 * You should have received a copy of the GNU General Public License
  13 * along with this program; if not, write to the Free Software
  14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15 * 02110-1301, USA.
  16 *
  17 */
  18
  19#include <linux/io.h>
  20#include <linux/irq.h>
  21#include <linux/interrupt.h>
  22#include <asm/irq.h>
  23
  24static unsigned int int_enable;
  25static unsigned int wake_enable;
  26
  27static struct sirc_regs_t sirc_regs = {
  28        .int_enable       = SPSS_SIRC_INT_ENABLE,
  29        .int_enable_clear = SPSS_SIRC_INT_ENABLE_CLEAR,
  30        .int_enable_set   = SPSS_SIRC_INT_ENABLE_SET,
  31        .int_type         = SPSS_SIRC_INT_TYPE,
  32        .int_polarity     = SPSS_SIRC_INT_POLARITY,
  33        .int_clear        = SPSS_SIRC_INT_CLEAR,
  34};
  35
  36static struct sirc_cascade_regs sirc_reg_table[] = {
  37        {
  38                .int_status  = SPSS_SIRC_IRQ_STATUS,
  39                .cascade_irq = INT_SIRC_0,
  40        }
  41};
  42
  43/* Mask off the given interrupt. Keep the int_enable mask in sync with
  44   the enable reg, so it can be restored after power collapse. */
  45static void sirc_irq_mask(struct irq_data *d)
  46{
  47        unsigned int mask;
  48
  49        mask = 1 << (d->irq - FIRST_SIRC_IRQ);
  50        writel(mask, sirc_regs.int_enable_clear);
  51        int_enable &= ~mask;
  52        return;
  53}
  54
  55/* Unmask the given interrupt. Keep the int_enable mask in sync with
  56   the enable reg, so it can be restored after power collapse. */
  57static void sirc_irq_unmask(struct irq_data *d)
  58{
  59        unsigned int mask;
  60
  61        mask = 1 << (d->irq - FIRST_SIRC_IRQ);
  62        writel(mask, sirc_regs.int_enable_set);
  63        int_enable |= mask;
  64        return;
  65}
  66
  67static void sirc_irq_ack(struct irq_data *d)
  68{
  69        unsigned int mask;
  70
  71        mask = 1 << (d->irq - FIRST_SIRC_IRQ);
  72        writel(mask, sirc_regs.int_clear);
  73        return;
  74}
  75
  76static int sirc_irq_set_wake(struct irq_data *d, unsigned int on)
  77{
  78        unsigned int mask;
  79
  80        /* Used to set the interrupt enable mask during power collapse. */
  81        mask = 1 << (d->irq - FIRST_SIRC_IRQ);
  82        if (on)
  83                wake_enable |= mask;
  84        else
  85                wake_enable &= ~mask;
  86
  87        return 0;
  88}
  89
  90static int sirc_irq_set_type(struct irq_data *d, unsigned int flow_type)
  91{
  92        unsigned int mask;
  93        unsigned int val;
  94
  95        mask = 1 << (d->irq - FIRST_SIRC_IRQ);
  96        val = readl(sirc_regs.int_polarity);
  97
  98        if (flow_type & (IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING))
  99                val |= mask;
 100        else
 101                val &= ~mask;
 102
 103        writel(val, sirc_regs.int_polarity);
 104
 105        val = readl(sirc_regs.int_type);
 106        if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
 107                val |= mask;
 108                __irq_set_handler_locked(d->irq, handle_edge_irq);
 109        } else {
 110                val &= ~mask;
 111                __irq_set_handler_locked(d->irq, handle_level_irq);
 112        }
 113
 114        writel(val, sirc_regs.int_type);
 115
 116        return 0;
 117}
 118
 119/* Finds the pending interrupt on the passed cascade irq and redrives it */
 120static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc)
 121{
 122        unsigned int reg = 0;
 123        unsigned int sirq;
 124        unsigned int status;
 125
 126        while ((reg < ARRAY_SIZE(sirc_reg_table)) &&
 127                (sirc_reg_table[reg].cascade_irq != irq))
 128                reg++;
 129
 130        status = readl(sirc_reg_table[reg].int_status);
 131        status &= SIRC_MASK;
 132        if (status == 0)
 133                return;
 134
 135        for (sirq = 0;
 136             (sirq < NR_SIRC_IRQS) && ((status & (1U << sirq)) == 0);
 137             sirq++)
 138                ;
 139        generic_handle_irq(sirq+FIRST_SIRC_IRQ);
 140
 141        desc->irq_data.chip->irq_ack(&desc->irq_data);
 142}
 143
 144static struct irq_chip sirc_irq_chip = {
 145        .name          = "sirc",
 146        .irq_ack       = sirc_irq_ack,
 147        .irq_mask      = sirc_irq_mask,
 148        .irq_unmask    = sirc_irq_unmask,
 149        .irq_set_wake  = sirc_irq_set_wake,
 150        .irq_set_type  = sirc_irq_set_type,
 151};
 152
 153void __init msm_init_sirc(void)
 154{
 155        int i;
 156
 157        int_enable = 0;
 158        wake_enable = 0;
 159
 160        for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) {
 161                irq_set_chip_and_handler(i, &sirc_irq_chip, handle_edge_irq);
 162                set_irq_flags(i, IRQF_VALID);
 163        }
 164
 165        for (i = 0; i < ARRAY_SIZE(sirc_reg_table); i++) {
 166                irq_set_chained_handler(sirc_reg_table[i].cascade_irq,
 167                                        sirc_irq_handler);
 168                irq_set_irq_wake(sirc_reg_table[i].cascade_irq, 1);
 169        }
 170        return;
 171}
 172
 173