linux/drivers/mfd/pm8xxx-irq.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 and
   6 * only version 2 as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#define pr_fmt(fmt)     "%s: " fmt, __func__
  15
  16#include <linux/err.h>
  17#include <linux/interrupt.h>
  18#include <linux/irq.h>
  19#include <linux/kernel.h>
  20#include <linux/mfd/pm8xxx/core.h>
  21#include <linux/mfd/pm8xxx/irq.h>
  22#include <linux/platform_device.h>
  23#include <linux/slab.h>
  24
  25/* PMIC8xxx IRQ */
  26
  27#define SSBI_REG_ADDR_IRQ_BASE          0x1BB
  28
  29#define SSBI_REG_ADDR_IRQ_ROOT          (SSBI_REG_ADDR_IRQ_BASE + 0)
  30#define SSBI_REG_ADDR_IRQ_M_STATUS1     (SSBI_REG_ADDR_IRQ_BASE + 1)
  31#define SSBI_REG_ADDR_IRQ_M_STATUS2     (SSBI_REG_ADDR_IRQ_BASE + 2)
  32#define SSBI_REG_ADDR_IRQ_M_STATUS3     (SSBI_REG_ADDR_IRQ_BASE + 3)
  33#define SSBI_REG_ADDR_IRQ_M_STATUS4     (SSBI_REG_ADDR_IRQ_BASE + 4)
  34#define SSBI_REG_ADDR_IRQ_BLK_SEL       (SSBI_REG_ADDR_IRQ_BASE + 5)
  35#define SSBI_REG_ADDR_IRQ_IT_STATUS     (SSBI_REG_ADDR_IRQ_BASE + 6)
  36#define SSBI_REG_ADDR_IRQ_CONFIG        (SSBI_REG_ADDR_IRQ_BASE + 7)
  37#define SSBI_REG_ADDR_IRQ_RT_STATUS     (SSBI_REG_ADDR_IRQ_BASE + 8)
  38
  39#define PM_IRQF_LVL_SEL                 0x01    /* level select */
  40#define PM_IRQF_MASK_FE                 0x02    /* mask falling edge */
  41#define PM_IRQF_MASK_RE                 0x04    /* mask rising edge */
  42#define PM_IRQF_CLR                     0x08    /* clear interrupt */
  43#define PM_IRQF_BITS_MASK               0x70
  44#define PM_IRQF_BITS_SHIFT              4
  45#define PM_IRQF_WRITE                   0x80
  46
  47#define PM_IRQF_MASK_ALL                (PM_IRQF_MASK_FE | \
  48                                        PM_IRQF_MASK_RE)
  49
  50struct pm_irq_chip {
  51        struct device           *dev;
  52        spinlock_t              pm_irq_lock;
  53        unsigned int            devirq;
  54        unsigned int            irq_base;
  55        unsigned int            num_irqs;
  56        unsigned int            num_blocks;
  57        unsigned int            num_masters;
  58        u8                      config[0];
  59};
  60
  61static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
  62{
  63        return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
  64}
  65
  66static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
  67{
  68        return pm8xxx_readb(chip->dev,
  69                        SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
  70}
  71
  72static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
  73{
  74        int     rc;
  75
  76        spin_lock(&chip->pm_irq_lock);
  77        rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
  78        if (rc) {
  79                pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
  80                goto bail;
  81        }
  82
  83        rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
  84        if (rc)
  85                pr_err("Failed Reading Status rc=%d\n", rc);
  86bail:
  87        spin_unlock(&chip->pm_irq_lock);
  88        return rc;
  89}
  90
  91static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
  92{
  93        int     rc;
  94
  95        spin_lock(&chip->pm_irq_lock);
  96        rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
  97        if (rc) {
  98                pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
  99                goto bail;
 100        }
 101
 102        cp |= PM_IRQF_WRITE;
 103        rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
 104        if (rc)
 105                pr_err("Failed Configuring IRQ rc=%d\n", rc);
 106bail:
 107        spin_unlock(&chip->pm_irq_lock);
 108        return rc;
 109}
 110
 111static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
 112{
 113        int pmirq, irq, i, ret = 0;
 114        u8 bits;
 115
 116        ret = pm8xxx_read_block_irq(chip, block, &bits);
 117        if (ret) {
 118                pr_err("Failed reading %d block ret=%d", block, ret);
 119                return ret;
 120        }
 121        if (!bits) {
 122                pr_err("block bit set in master but no irqs: %d", block);
 123                return 0;
 124        }
 125
 126        /* Check IRQ bits */
 127        for (i = 0; i < 8; i++) {
 128                if (bits & (1 << i)) {
 129                        pmirq = block * 8 + i;
 130                        irq = pmirq + chip->irq_base;
 131                        generic_handle_irq(irq);
 132                }
 133        }
 134        return 0;
 135}
 136
 137static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
 138{
 139        u8 blockbits;
 140        int block_number, i, ret = 0;
 141
 142        ret = pm8xxx_read_master_irq(chip, master, &blockbits);
 143        if (ret) {
 144                pr_err("Failed to read master %d ret=%d\n", master, ret);
 145                return ret;
 146        }
 147        if (!blockbits) {
 148                pr_err("master bit set in root but no blocks: %d", master);
 149                return 0;
 150        }
 151
 152        for (i = 0; i < 8; i++)
 153                if (blockbits & (1 << i)) {
 154                        block_number = master * 8 + i;  /* block # */
 155                        ret |= pm8xxx_irq_block_handler(chip, block_number);
 156                }
 157        return ret;
 158}
 159
 160static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
 161{
 162        struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
 163        struct irq_chip *irq_chip = irq_desc_get_chip(desc);
 164        u8      root;
 165        int     i, ret, masters = 0;
 166
 167        ret = pm8xxx_read_root_irq(chip, &root);
 168        if (ret) {
 169                pr_err("Can't read root status ret=%d\n", ret);
 170                return;
 171        }
 172
 173        /* on pm8xxx series masters start from bit 1 of the root */
 174        masters = root >> 1;
 175
 176        /* Read allowed masters for blocks. */
 177        for (i = 0; i < chip->num_masters; i++)
 178                if (masters & (1 << i))
 179                        pm8xxx_irq_master_handler(chip, i);
 180
 181        irq_chip->irq_ack(&desc->irq_data);
 182}
 183
 184static void pm8xxx_irq_mask_ack(struct irq_data *d)
 185{
 186        struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
 187        unsigned int pmirq = d->irq - chip->irq_base;
 188        int     master, irq_bit;
 189        u8      block, config;
 190
 191        block = pmirq / 8;
 192        master = block / 8;
 193        irq_bit = pmirq % 8;
 194
 195        config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
 196        pm8xxx_config_irq(chip, block, config);
 197}
 198
 199static void pm8xxx_irq_unmask(struct irq_data *d)
 200{
 201        struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
 202        unsigned int pmirq = d->irq - chip->irq_base;
 203        int     master, irq_bit;
 204        u8      block, config;
 205
 206        block = pmirq / 8;
 207        master = block / 8;
 208        irq_bit = pmirq % 8;
 209
 210        config = chip->config[pmirq];
 211        pm8xxx_config_irq(chip, block, config);
 212}
 213
 214static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
 215{
 216        struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
 217        unsigned int pmirq = d->irq - chip->irq_base;
 218        int master, irq_bit;
 219        u8 block, config;
 220
 221        block = pmirq / 8;
 222        master = block / 8;
 223        irq_bit  = pmirq % 8;
 224
 225        chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
 226                                                        | PM_IRQF_MASK_ALL;
 227        if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
 228                if (flow_type & IRQF_TRIGGER_RISING)
 229                        chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
 230                if (flow_type & IRQF_TRIGGER_FALLING)
 231                        chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
 232        } else {
 233                chip->config[pmirq] |= PM_IRQF_LVL_SEL;
 234
 235                if (flow_type & IRQF_TRIGGER_HIGH)
 236                        chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
 237                else
 238                        chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
 239        }
 240
 241        config = chip->config[pmirq] | PM_IRQF_CLR;
 242        return pm8xxx_config_irq(chip, block, config);
 243}
 244
 245static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
 246{
 247        return 0;
 248}
 249
 250static struct irq_chip pm8xxx_irq_chip = {
 251        .name           = "pm8xxx",
 252        .irq_mask_ack   = pm8xxx_irq_mask_ack,
 253        .irq_unmask     = pm8xxx_irq_unmask,
 254        .irq_set_type   = pm8xxx_irq_set_type,
 255        .irq_set_wake   = pm8xxx_irq_set_wake,
 256        .flags          = IRQCHIP_MASK_ON_SUSPEND,
 257};
 258
 259/**
 260 * pm8xxx_get_irq_stat - get the status of the irq line
 261 * @chip: pointer to identify a pmic irq controller
 262 * @irq: the irq number
 263 *
 264 * The pm8xxx gpio and mpp rely on the interrupt block to read
 265 * the values on their pins. This function is to facilitate reading
 266 * the status of a gpio or an mpp line. The caller has to convert the
 267 * gpio number to irq number.
 268 *
 269 * RETURNS:
 270 * an int indicating the value read on that line
 271 */
 272int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
 273{
 274        int pmirq, rc;
 275        u8  block, bits, bit;
 276        unsigned long flags;
 277
 278        if (chip == NULL || irq < chip->irq_base ||
 279                        irq >= chip->irq_base + chip->num_irqs)
 280                return -EINVAL;
 281
 282        pmirq = irq - chip->irq_base;
 283
 284        block = pmirq / 8;
 285        bit = pmirq % 8;
 286
 287        spin_lock_irqsave(&chip->pm_irq_lock, flags);
 288
 289        rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
 290        if (rc) {
 291                pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
 292                        irq, pmirq, block, rc);
 293                goto bail_out;
 294        }
 295
 296        rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
 297        if (rc) {
 298                pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
 299                        irq, pmirq, block, rc);
 300                goto bail_out;
 301        }
 302
 303        rc = (bits & (1 << bit)) ? 1 : 0;
 304
 305bail_out:
 306        spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
 307
 308        return rc;
 309}
 310EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
 311
 312struct pm_irq_chip *  pm8xxx_irq_init(struct device *dev,
 313                                const struct pm8xxx_irq_platform_data *pdata)
 314{
 315        struct pm_irq_chip  *chip;
 316        int devirq, rc;
 317        unsigned int pmirq;
 318
 319        if (!pdata) {
 320                pr_err("No platform data\n");
 321                return ERR_PTR(-EINVAL);
 322        }
 323
 324        devirq = pdata->devirq;
 325        if (devirq < 0) {
 326                pr_err("missing devirq\n");
 327                rc = devirq;
 328                return ERR_PTR(-EINVAL);
 329        }
 330
 331        chip = kzalloc(sizeof(struct pm_irq_chip)
 332                        + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
 333        if (!chip) {
 334                pr_err("Cannot alloc pm_irq_chip struct\n");
 335                return ERR_PTR(-EINVAL);
 336        }
 337
 338        chip->dev = dev;
 339        chip->devirq = devirq;
 340        chip->irq_base = pdata->irq_base;
 341        chip->num_irqs = pdata->irq_cdata.nirqs;
 342        chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
 343        chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
 344        spin_lock_init(&chip->pm_irq_lock);
 345
 346        for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
 347                irq_set_chip_and_handler(chip->irq_base + pmirq,
 348                                &pm8xxx_irq_chip,
 349                                handle_level_irq);
 350                irq_set_chip_data(chip->irq_base + pmirq, chip);
 351#ifdef CONFIG_ARM
 352                set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
 353#else
 354                irq_set_noprobe(chip->irq_base + pmirq);
 355#endif
 356        }
 357
 358        irq_set_irq_type(devirq, pdata->irq_trigger_flag);
 359        irq_set_handler_data(devirq, chip);
 360        irq_set_chained_handler(devirq, pm8xxx_irq_handler);
 361        set_irq_wake(devirq, 1);
 362
 363        return chip;
 364}
 365
 366int pm8xxx_irq_exit(struct pm_irq_chip *chip)
 367{
 368        irq_set_chained_handler(chip->devirq, NULL);
 369        kfree(chip);
 370        return 0;
 371}
 372
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.