linux/drivers/mfd/tps65910-irq.c
<<
>>
Prefs
   1/*
   2 * tps65910-irq.c  --  TI TPS6591x
   3 *
   4 * Copyright 2010 Texas Instruments Inc.
   5 *
   6 * Author: Graeme Gregory <gg@slimlogic.co.uk>
   7 * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
   8 *
   9 *  This program is free software; you can redistribute it and/or modify it
  10 *  under  the terms of the GNU General  Public License as published by the
  11 *  Free Software Foundation;  either version 2 of the License, or (at your
  12 *  option) any later version.
  13 *
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/init.h>
  19#include <linux/bug.h>
  20#include <linux/device.h>
  21#include <linux/interrupt.h>
  22#include <linux/irq.h>
  23#include <linux/irqdomain.h>
  24#include <linux/gpio.h>
  25#include <linux/mfd/tps65910.h>
  26
  27/*
  28 * This is a threaded IRQ handler so can access I2C/SPI.  Since all
  29 * interrupts are clear on read the IRQ line will be reasserted and
  30 * the physical IRQ will be handled again if another interrupt is
  31 * asserted while we run - in the normal course of events this is a
  32 * rare occurrence so we save I2C/SPI reads.  We're also assuming that
  33 * it's rare to get lots of interrupts firing simultaneously so try to
  34 * minimise I/O.
  35 */
  36static irqreturn_t tps65910_irq(int irq, void *irq_data)
  37{
  38        struct tps65910 *tps65910 = irq_data;
  39        unsigned int reg;
  40        u32 irq_sts;
  41        u32 irq_mask;
  42        int i;
  43
  44        tps65910_reg_read(tps65910, TPS65910_INT_STS, &reg);
  45        irq_sts = reg;
  46        tps65910_reg_read(tps65910, TPS65910_INT_STS2, &reg);
  47        irq_sts |= reg << 8;
  48        switch (tps65910_chip_id(tps65910)) {
  49        case TPS65911:
  50                tps65910_reg_read(tps65910, TPS65910_INT_STS3, &reg);
  51                irq_sts |= reg << 16;
  52        }
  53
  54        tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
  55        irq_mask = reg;
  56        tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
  57        irq_mask |= reg << 8;
  58        switch (tps65910_chip_id(tps65910)) {
  59        case TPS65911:
  60                tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
  61                irq_mask |= reg << 16;
  62        }
  63
  64        irq_sts &= ~irq_mask;
  65
  66        if (!irq_sts)
  67                return IRQ_NONE;
  68
  69        for (i = 0; i < tps65910->irq_num; i++) {
  70
  71                if (!(irq_sts & (1 << i)))
  72                        continue;
  73
  74                handle_nested_irq(irq_find_mapping(tps65910->domain, i));
  75        }
  76
  77        /* Write the STS register back to clear IRQs we handled */
  78        reg = irq_sts & 0xFF;
  79        irq_sts >>= 8;
  80        tps65910_reg_write(tps65910, TPS65910_INT_STS, reg);
  81        reg = irq_sts & 0xFF;
  82        tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg);
  83        switch (tps65910_chip_id(tps65910)) {
  84        case TPS65911:
  85                reg = irq_sts >> 8;
  86                tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg);
  87        }
  88
  89        return IRQ_HANDLED;
  90}
  91
  92static void tps65910_irq_lock(struct irq_data *data)
  93{
  94        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
  95
  96        mutex_lock(&tps65910->irq_lock);
  97}
  98
  99static void tps65910_irq_sync_unlock(struct irq_data *data)
 100{
 101        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
 102        u32 reg_mask;
 103        unsigned int reg;
 104
 105        tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
 106        reg_mask = reg;
 107        tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
 108        reg_mask |= reg << 8;
 109        switch (tps65910_chip_id(tps65910)) {
 110        case TPS65911:
 111                tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
 112                reg_mask |= reg << 16;
 113        }
 114
 115        if (tps65910->irq_mask != reg_mask) {
 116                reg = tps65910->irq_mask & 0xFF;
 117                tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg);
 118                reg = tps65910->irq_mask >> 8 & 0xFF;
 119                tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg);
 120                switch (tps65910_chip_id(tps65910)) {
 121                case TPS65911:
 122                        reg = tps65910->irq_mask >> 16;
 123                        tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg);
 124                }
 125        }
 126        mutex_unlock(&tps65910->irq_lock);
 127}
 128
 129static void tps65910_irq_enable(struct irq_data *data)
 130{
 131        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
 132
 133        tps65910->irq_mask &= ~(1 << data->hwirq);
 134}
 135
 136static void tps65910_irq_disable(struct irq_data *data)
 137{
 138        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
 139
 140        tps65910->irq_mask |= (1 << data->hwirq);
 141}
 142
 143#ifdef CONFIG_PM_SLEEP
 144static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable)
 145{
 146        struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
 147        return irq_set_irq_wake(tps65910->chip_irq, enable);
 148}
 149#else
 150#define tps65910_irq_set_wake NULL
 151#endif
 152
 153static struct irq_chip tps65910_irq_chip = {
 154        .name = "tps65910",
 155        .irq_bus_lock = tps65910_irq_lock,
 156        .irq_bus_sync_unlock = tps65910_irq_sync_unlock,
 157        .irq_disable = tps65910_irq_disable,
 158        .irq_enable = tps65910_irq_enable,
 159        .irq_set_wake = tps65910_irq_set_wake,
 160};
 161
 162static int tps65910_irq_map(struct irq_domain *h, unsigned int virq,
 163                                irq_hw_number_t hw)
 164{
 165        struct tps65910 *tps65910 = h->host_data;
 166
 167        irq_set_chip_data(virq, tps65910);
 168        irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq);
 169        irq_set_nested_thread(virq, 1);
 170
 171        /* ARM needs us to explicitly flag the IRQ as valid
 172         * and will set them noprobe when we do so. */
 173#ifdef CONFIG_ARM
 174        set_irq_flags(virq, IRQF_VALID);
 175#else
 176        irq_set_noprobe(virq);
 177#endif
 178
 179        return 0;
 180}
 181
 182static struct irq_domain_ops tps65910_domain_ops = {
 183        .map    = tps65910_irq_map,
 184        .xlate  = irq_domain_xlate_twocell,
 185};
 186
 187int tps65910_irq_init(struct tps65910 *tps65910, int irq,
 188                    struct tps65910_platform_data *pdata)
 189{
 190        int ret;
 191        int flags = IRQF_ONESHOT;
 192
 193        if (!irq) {
 194                dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n");
 195                return -EINVAL;
 196        }
 197
 198        if (!pdata) {
 199                dev_warn(tps65910->dev, "No interrupt support, no pdata\n");
 200                return -EINVAL;
 201        }
 202
 203        switch (tps65910_chip_id(tps65910)) {
 204        case TPS65910:
 205                tps65910->irq_num = TPS65910_NUM_IRQ;
 206                break;
 207        case TPS65911:
 208                tps65910->irq_num = TPS65911_NUM_IRQ;
 209                break;
 210        }
 211
 212        if (pdata->irq_base > 0) {
 213                pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0,
 214                                        tps65910->irq_num, -1);
 215                if (pdata->irq_base < 0) {
 216                        dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n",
 217                                        pdata->irq_base);
 218                        return pdata->irq_base;
 219                }
 220        }
 221
 222        tps65910->irq_mask = 0xFFFFFF;
 223
 224        mutex_init(&tps65910->irq_lock);
 225        tps65910->chip_irq = irq;
 226        tps65910->irq_base = pdata->irq_base;
 227
 228        if (pdata->irq_base > 0)
 229                tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node,
 230                                        tps65910->irq_num,
 231                                        pdata->irq_base,
 232                                        0,
 233                                        &tps65910_domain_ops, tps65910);
 234        else
 235                tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node,
 236                                        tps65910->irq_num,
 237                                        &tps65910_domain_ops, tps65910);
 238
 239        if (!tps65910->domain) {
 240                dev_err(tps65910->dev, "Failed to create IRQ domain\n");
 241                return -ENOMEM;
 242        }
 243
 244        ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
 245                                   "tps65910", tps65910);
 246
 247        irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
 248
 249        if (ret != 0)
 250                dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret);
 251
 252        return ret;
 253}
 254
 255int tps65910_irq_exit(struct tps65910 *tps65910)
 256{
 257        if (tps65910->chip_irq)
 258                free_irq(tps65910->chip_irq, tps65910);
 259        return 0;
 260}
 261
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.