linux/drivers/mfd/twl6030-irq.c
<<
>>
Prefs
   1/*
   2 * twl6030-irq.c - TWL6030 irq support
   3 *
   4 * Copyright (C) 2005-2009 Texas Instruments, Inc.
   5 *
   6 * Modifications to defer interrupt handling to a kernel thread:
   7 * Copyright (C) 2006 MontaVista Software, Inc.
   8 *
   9 * Based on tlv320aic23.c:
  10 * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
  11 *
  12 * Code cleanup and modifications to IRQ handler.
  13 * by syed khasim <x0khasim@ti.com>
  14 *
  15 * TWL6030 specific code and IRQ handling changes by
  16 * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
  17 * Balaji T K <balajitk@ti.com>
  18 *
  19 * This program is free software; you can redistribute it and/or modify
  20 * it under the terms of the GNU General Public License as published by
  21 * the Free Software Foundation; either version 2 of the License, or
  22 * (at your option) any later version.
  23 *
  24 * This program is distributed in the hope that it will be useful,
  25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27 * GNU General Public License for more details.
  28 *
  29 * You should have received a copy of the GNU General Public License
  30 * along with this program; if not, write to the Free Software
  31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  32 */
  33
  34#include <linux/init.h>
  35#include <linux/export.h>
  36#include <linux/interrupt.h>
  37#include <linux/irq.h>
  38#include <linux/kthread.h>
  39#include <linux/i2c/twl.h>
  40#include <linux/platform_device.h>
  41#include <linux/suspend.h>
  42#include <linux/of.h>
  43#include <linux/irqdomain.h>
  44
  45#include "twl-core.h"
  46
  47/*
  48 * TWL6030 (unlike its predecessors, which had two level interrupt handling)
  49 * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
  50 * It exposes status bits saying who has raised an interrupt. There are
  51 * three mask registers that corresponds to these status registers, that
  52 * enables/disables these interrupts.
  53 *
  54 * We set up IRQs starting at a platform-specified base. An interrupt map table,
  55 * specifies mapping between interrupt number and the associated module.
  56 */
  57#define TWL6030_NR_IRQS    20
  58
  59static int twl6030_interrupt_mapping[24] = {
  60        PWR_INTR_OFFSET,        /* Bit 0        PWRON                   */
  61        PWR_INTR_OFFSET,        /* Bit 1        RPWRON                  */
  62        PWR_INTR_OFFSET,        /* Bit 2        BAT_VLOW                */
  63        RTC_INTR_OFFSET,        /* Bit 3        RTC_ALARM               */
  64        RTC_INTR_OFFSET,        /* Bit 4        RTC_PERIOD              */
  65        HOTDIE_INTR_OFFSET,     /* Bit 5        HOT_DIE                 */
  66        SMPSLDO_INTR_OFFSET,    /* Bit 6        VXXX_SHORT              */
  67        SMPSLDO_INTR_OFFSET,    /* Bit 7        VMMC_SHORT              */
  68
  69        SMPSLDO_INTR_OFFSET,    /* Bit 8        VUSIM_SHORT             */
  70        BATDETECT_INTR_OFFSET,  /* Bit 9        BAT                     */
  71        SIMDETECT_INTR_OFFSET,  /* Bit 10       SIM                     */
  72        MMCDETECT_INTR_OFFSET,  /* Bit 11       MMC                     */
  73        RSV_INTR_OFFSET,        /* Bit 12       Reserved                */
  74        MADC_INTR_OFFSET,       /* Bit 13       GPADC_RT_EOC            */
  75        MADC_INTR_OFFSET,       /* Bit 14       GPADC_SW_EOC            */
  76        GASGAUGE_INTR_OFFSET,   /* Bit 15       CC_AUTOCAL              */
  77
  78        USBOTG_INTR_OFFSET,     /* Bit 16       ID_WKUP                 */
  79        USBOTG_INTR_OFFSET,     /* Bit 17       VBUS_WKUP               */
  80        USBOTG_INTR_OFFSET,     /* Bit 18       ID                      */
  81        USB_PRES_INTR_OFFSET,   /* Bit 19       VBUS                    */
  82        CHARGER_INTR_OFFSET,    /* Bit 20       CHRG_CTRL               */
  83        CHARGERFAULT_INTR_OFFSET,       /* Bit 21       EXT_CHRG        */
  84        CHARGERFAULT_INTR_OFFSET,       /* Bit 22       INT_CHRG        */
  85        RSV_INTR_OFFSET,        /* Bit 23       Reserved                */
  86};
  87/*----------------------------------------------------------------------*/
  88
  89static unsigned twl6030_irq_base;
  90static int twl_irq;
  91static bool twl_irq_wake_enabled;
  92
  93static struct completion irq_event;
  94static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
  95
  96static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
  97                                   unsigned long pm_event, void *unused)
  98{
  99        int chained_wakeups;
 100
 101        switch (pm_event) {
 102        case PM_SUSPEND_PREPARE:
 103                chained_wakeups = atomic_read(&twl6030_wakeirqs);
 104
 105                if (chained_wakeups && !twl_irq_wake_enabled) {
 106                        if (enable_irq_wake(twl_irq))
 107                                pr_err("twl6030 IRQ wake enable failed\n");
 108                        else
 109                                twl_irq_wake_enabled = true;
 110                } else if (!chained_wakeups && twl_irq_wake_enabled) {
 111                        disable_irq_wake(twl_irq);
 112                        twl_irq_wake_enabled = false;
 113                }
 114
 115                disable_irq(twl_irq);
 116                break;
 117
 118        case PM_POST_SUSPEND:
 119                enable_irq(twl_irq);
 120                break;
 121
 122        default:
 123                break;
 124        }
 125
 126        return NOTIFY_DONE;
 127}
 128
 129static struct notifier_block twl6030_irq_pm_notifier_block = {
 130        .notifier_call = twl6030_irq_pm_notifier,
 131};
 132
 133/*
 134 * This thread processes interrupts reported by the Primary Interrupt Handler.
 135 */
 136static int twl6030_irq_thread(void *data)
 137{
 138        long irq = (long)data;
 139        static unsigned i2c_errors;
 140        static const unsigned max_i2c_errors = 100;
 141        int ret;
 142
 143        while (!kthread_should_stop()) {
 144                int i;
 145                union {
 146                u8 bytes[4];
 147                u32 int_sts;
 148                } sts;
 149
 150                /* Wait for IRQ, then read PIH irq status (also blocking) */
 151                wait_for_completion_interruptible(&irq_event);
 152
 153                /* read INT_STS_A, B and C in one shot using a burst read */
 154                ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
 155                                REG_INT_STS_A, 3);
 156                if (ret) {
 157                        pr_warning("twl6030: I2C error %d reading PIH ISR\n",
 158                                        ret);
 159                        if (++i2c_errors >= max_i2c_errors) {
 160                                printk(KERN_ERR "Maximum I2C error count"
 161                                                " exceeded.  Terminating %s.\n",
 162                                                __func__);
 163                                break;
 164                        }
 165                        complete(&irq_event);
 166                        continue;
 167                }
 168
 169
 170
 171                sts.bytes[3] = 0; /* Only 24 bits are valid*/
 172
 173                /*
 174                 * Since VBUS status bit is not reliable for VBUS disconnect
 175                 * use CHARGER VBUS detection status bit instead.
 176                 */
 177                if (sts.bytes[2] & 0x10)
 178                        sts.bytes[2] |= 0x08;
 179
 180                for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
 181                        local_irq_disable();
 182                        if (sts.int_sts & 0x1) {
 183                                int module_irq = twl6030_irq_base +
 184                                        twl6030_interrupt_mapping[i];
 185                                generic_handle_irq(module_irq);
 186
 187                        }
 188                local_irq_enable();
 189                }
 190
 191                /*
 192                 * NOTE:
 193                 * Simulation confirms that documentation is wrong w.r.t the
 194                 * interrupt status clear operation. A single *byte* write to
 195                 * any one of STS_A to STS_C register results in all three
 196                 * STS registers being reset. Since it does not matter which
 197                 * value is written, all three registers are cleared on a
 198                 * single byte write, so we just use 0x0 to clear.
 199                 */
 200                ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
 201                if (ret)
 202                        pr_warning("twl6030: I2C error in clearing PIH ISR\n");
 203
 204                enable_irq(irq);
 205        }
 206
 207        return 0;
 208}
 209
 210/*
 211 * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
 212 * This is a chained interrupt, so there is no desc->action method for it.
 213 * Now we need to query the interrupt controller in the twl6030 to determine
 214 * which module is generating the interrupt request.  However, we can't do i2c
 215 * transactions in interrupt context, so we must defer that work to a kernel
 216 * thread.  All we do here is acknowledge and mask the interrupt and wakeup
 217 * the kernel thread.
 218 */
 219static irqreturn_t handle_twl6030_pih(int irq, void *devid)
 220{
 221        disable_irq_nosync(irq);
 222        complete(devid);
 223        return IRQ_HANDLED;
 224}
 225
 226/*----------------------------------------------------------------------*/
 227
 228static inline void activate_irq(int irq)
 229{
 230#ifdef CONFIG_ARM
 231        /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
 232         * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
 233         */
 234        set_irq_flags(irq, IRQF_VALID);
 235#else
 236        /* same effect on other architectures */
 237        irq_set_noprobe(irq);
 238#endif
 239}
 240
 241static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
 242{
 243        if (on)
 244                atomic_inc(&twl6030_wakeirqs);
 245        else
 246                atomic_dec(&twl6030_wakeirqs);
 247
 248        return 0;
 249}
 250
 251int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
 252{
 253        int ret;
 254        u8 unmask_value;
 255        ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
 256                        REG_INT_STS_A + offset);
 257        unmask_value &= (~(bit_mask));
 258        ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
 259                        REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
 260        return ret;
 261}
 262EXPORT_SYMBOL(twl6030_interrupt_unmask);
 263
 264int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
 265{
 266        int ret;
 267        u8 mask_value;
 268        ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
 269                        REG_INT_STS_A + offset);
 270        mask_value |= (bit_mask);
 271        ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
 272                        REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
 273        return ret;
 274}
 275EXPORT_SYMBOL(twl6030_interrupt_mask);
 276
 277int twl6030_mmc_card_detect_config(void)
 278{
 279        int ret;
 280        u8 reg_val = 0;
 281
 282        /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
 283        twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
 284                                                REG_INT_MSK_LINE_B);
 285        twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
 286                                                REG_INT_MSK_STS_B);
 287        /*
 288         * Initially Configuring MMC_CTRL for receiving interrupts &
 289         * Card status on TWL6030 for MMC1
 290         */
 291        ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
 292        if (ret < 0) {
 293                pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
 294                return ret;
 295        }
 296        reg_val &= ~VMMC_AUTO_OFF;
 297        reg_val |= SW_FC;
 298        ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
 299        if (ret < 0) {
 300                pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
 301                return ret;
 302        }
 303
 304        /* Configuring PullUp-PullDown register */
 305        ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
 306                                                TWL6030_CFG_INPUT_PUPD3);
 307        if (ret < 0) {
 308                pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
 309                                                                        ret);
 310                return ret;
 311        }
 312        reg_val &= ~(MMC_PU | MMC_PD);
 313        ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
 314                                                TWL6030_CFG_INPUT_PUPD3);
 315        if (ret < 0) {
 316                pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
 317                                                                        ret);
 318                return ret;
 319        }
 320
 321        return twl6030_irq_base + MMCDETECT_INTR_OFFSET;
 322}
 323EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
 324
 325int twl6030_mmc_card_detect(struct device *dev, int slot)
 326{
 327        int ret = -EIO;
 328        u8 read_reg = 0;
 329        struct platform_device *pdev = to_platform_device(dev);
 330
 331        if (pdev->id) {
 332                /* TWL6030 provide's Card detect support for
 333                 * only MMC1 controller.
 334                 */
 335                pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__);
 336                return ret;
 337        }
 338        /*
 339         * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
 340         * 0 - Card not present ,1 - Card present
 341         */
 342        ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
 343                                                TWL6030_MMCCTRL);
 344        if (ret >= 0)
 345                ret = read_reg & STS_MMC;
 346        return ret;
 347}
 348EXPORT_SYMBOL(twl6030_mmc_card_detect);
 349
 350int twl6030_init_irq(struct device *dev, int irq_num)
 351{
 352        struct                  device_node *node = dev->of_node;
 353        int                     nr_irqs, irq_base, irq_end;
 354        struct task_struct      *task;
 355        static struct irq_chip  twl6030_irq_chip;
 356        int                     status = 0;
 357        int                     i;
 358        u8                      mask[4];
 359
 360        nr_irqs = TWL6030_NR_IRQS;
 361
 362        irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
 363        if (IS_ERR_VALUE(irq_base)) {
 364                dev_err(dev, "Fail to allocate IRQ descs\n");
 365                return irq_base;
 366        }
 367
 368        irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
 369                              &irq_domain_simple_ops, NULL);
 370
 371        irq_end = irq_base + nr_irqs;
 372
 373        mask[1] = 0xFF;
 374        mask[2] = 0xFF;
 375        mask[3] = 0xFF;
 376
 377        /* mask all int lines */
 378        twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);
 379        /* mask all int sts */
 380        twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);
 381        /* clear INT_STS_A,B,C */
 382        twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
 383
 384        twl6030_irq_base = irq_base;
 385
 386        /*
 387         * install an irq handler for each of the modules;
 388         * clone dummy irq_chip since PIH can't *do* anything
 389         */
 390        twl6030_irq_chip = dummy_irq_chip;
 391        twl6030_irq_chip.name = "twl6030";
 392        twl6030_irq_chip.irq_set_type = NULL;
 393        twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
 394
 395        for (i = irq_base; i < irq_end; i++) {
 396                irq_set_chip_and_handler(i, &twl6030_irq_chip,
 397                                         handle_simple_irq);
 398                irq_set_chip_data(i, (void *)irq_num);
 399                activate_irq(i);
 400        }
 401
 402        dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n",
 403                        irq_num, irq_base, irq_end);
 404
 405        /* install an irq handler to demultiplex the TWL6030 interrupt */
 406        init_completion(&irq_event);
 407
 408        status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH",
 409                             &irq_event);
 410        if (status < 0) {
 411                dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);
 412                goto fail_irq;
 413        }
 414
 415        task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
 416        if (IS_ERR(task)) {
 417                dev_err(dev, "could not create irq %d thread!\n", irq_num);
 418                status = PTR_ERR(task);
 419                goto fail_kthread;
 420        }
 421
 422        twl_irq = irq_num;
 423        register_pm_notifier(&twl6030_irq_pm_notifier_block);
 424        return irq_base;
 425
 426fail_kthread:
 427        free_irq(irq_num, &irq_event);
 428
 429fail_irq:
 430        for (i = irq_base; i < irq_end; i++)
 431                irq_set_chip_and_handler(i, NULL, NULL);
 432
 433        return status;
 434}
 435
 436int twl6030_exit_irq(void)
 437{
 438        unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
 439
 440        if (twl6030_irq_base) {
 441                pr_err("twl6030: can't yet clean up IRQs?\n");
 442                return -ENOSYS;
 443        }
 444        return 0;
 445}
 446
 447
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.