Commit d49a0f3f14a763242b71244019d7881ee06e0658

Authored by Margarita Olaya
Committed by Samuel Ortiz
1 parent 36e52873c6

tps65912: irq: add interrupt controller

This module controls the interrupt handling for the tps65912.
The interrupt sources can be the following:

- GPIO
- PWRON signal
- PWRHOLD signal
- Temperature detection

Signed-off-by: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
Acked-by: Samuel Ortiz <sameo@linux.intel.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

Showing 4 changed files with 243 additions and 1 deletions Side-by-side Diff

drivers/mfd/Makefile
... ... @@ -36,7 +36,7 @@
36 36 obj-$(CONFIG_TPS6105X) += tps6105x.o
37 37 obj-$(CONFIG_TPS65010) += tps65010.o
38 38 obj-$(CONFIG_TPS6507X) += tps6507x.o
39   -tps65912-objs := tps65912-core.o
  39 +tps65912-objs := tps65912-core.o tps65912-irq.o
40 40 obj-$(CONFIG_MFD_TPS65912) += tps65912.o
41 41 obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
42 42 obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
drivers/mfd/tps65912-core.c
... ... @@ -124,8 +124,16 @@
124 124 int tps65912_device_init(struct tps65912 *tps65912)
125 125 {
126 126 struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
  127 + struct tps65912_platform_data *init_data;
127 128 int ret, dcdc_avs, value;
128 129  
  130 + init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
  131 + if (init_data == NULL)
  132 + return -ENOMEM;
  133 +
  134 + init_data->irq = pmic_plat_data->irq;
  135 + init_data->irq_base = pmic_plat_data->irq;
  136 +
129 137 mutex_init(&tps65912->io_mutex);
130 138 dev_set_drvdata(tps65912->dev, tps65912);
131 139  
132 140  
... ... @@ -145,9 +153,14 @@
145 153 if (ret < 0)
146 154 goto err;
147 155  
  156 + ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
  157 + if (ret < 0)
  158 + goto err;
  159 +
148 160 return ret;
149 161  
150 162 err:
  163 + kfree(init_data);
151 164 mfd_remove_devices(tps65912->dev);
152 165 kfree(tps65912);
153 166 return ret;
drivers/mfd/tps65912-irq.c
  1 +/*
  2 + * tps65912-irq.c -- TI TPS6591x
  3 + *
  4 + * Copyright 2011 Texas Instruments Inc.
  5 + *
  6 + * Author: Margarita Olaya <magi@slimlogic.co.uk>
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify it
  9 + * under the terms of the GNU General Public License as published by the
  10 + * Free Software Foundation; either version 2 of the License, or (at your
  11 + * option) any later version.
  12 + *
  13 + * This driver is based on wm8350 implementation.
  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/gpio.h>
  24 +#include <linux/mfd/tps65912.h>
  25 +
  26 +static inline int irq_to_tps65912_irq(struct tps65912 *tps65912,
  27 + int irq)
  28 +{
  29 + return irq - tps65912->irq_base;
  30 +}
  31 +
  32 +/*
  33 + * This is a threaded IRQ handler so can access I2C/SPI. Since the
  34 + * IRQ handler explicitly clears the IRQ it handles the IRQ line
  35 + * will be reasserted and the physical IRQ will be handled again if
  36 + * another interrupt is asserted while we run - in the normal course
  37 + * of events this is a rare occurrence so we save I2C/SPI reads. We're
  38 + * also assuming that it's rare to get lots of interrupts firing
  39 + * simultaneously so try to minimise I/O.
  40 + */
  41 +static irqreturn_t tps65912_irq(int irq, void *irq_data)
  42 +{
  43 + struct tps65912 *tps65912 = irq_data;
  44 + u32 irq_sts;
  45 + u32 irq_mask;
  46 + u8 reg;
  47 + int i;
  48 +
  49 +
  50 + tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
  51 + irq_sts = reg;
  52 + tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
  53 + irq_sts |= reg << 8;
  54 + tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
  55 + irq_sts |= reg << 16;
  56 + tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
  57 + irq_sts |= reg << 24;
  58 +
  59 + tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
  60 + irq_mask = reg;
  61 + tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
  62 + irq_mask |= reg << 8;
  63 + tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
  64 + irq_mask |= reg << 16;
  65 + tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
  66 + irq_mask |= reg << 24;
  67 +
  68 + irq_sts &= ~irq_mask;
  69 + if (!irq_sts)
  70 + return IRQ_NONE;
  71 +
  72 + for (i = 0; i < tps65912->irq_num; i++) {
  73 + if (!(irq_sts & (1 << i)))
  74 + continue;
  75 +
  76 + handle_nested_irq(tps65912->irq_base + i);
  77 + }
  78 +
  79 + /* Write the STS register back to clear IRQs we handled */
  80 + reg = irq_sts & 0xFF;
  81 + irq_sts >>= 8;
  82 + if (reg)
  83 + tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
  84 + reg = irq_sts & 0xFF;
  85 + irq_sts >>= 8;
  86 + if (reg)
  87 + tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
  88 + reg = irq_sts & 0xFF;
  89 + irq_sts >>= 8;
  90 + if (reg)
  91 + tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
  92 + reg = irq_sts & 0xFF;
  93 + if (reg)
  94 + tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
  95 +
  96 + return IRQ_HANDLED;
  97 +}
  98 +
  99 +static void tps65912_irq_lock(struct irq_data *data)
  100 +{
  101 + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
  102 +
  103 + mutex_lock(&tps65912->irq_lock);
  104 +}
  105 +
  106 +static void tps65912_irq_sync_unlock(struct irq_data *data)
  107 +{
  108 + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
  109 + u32 reg_mask;
  110 + u8 reg;
  111 +
  112 + tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
  113 + reg_mask = reg;
  114 + tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
  115 + reg_mask |= reg << 8;
  116 + tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
  117 + reg_mask |= reg << 16;
  118 + tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
  119 + reg_mask |= reg << 24;
  120 +
  121 + if (tps65912->irq_mask != reg_mask) {
  122 + reg = tps65912->irq_mask & 0xFF;
  123 + tps65912->write(tps65912, TPS65912_INT_MSK, 1, &reg);
  124 + reg = tps65912->irq_mask >> 8 & 0xFF;
  125 + tps65912->write(tps65912, TPS65912_INT_MSK2, 1, &reg);
  126 + reg = tps65912->irq_mask >> 16 & 0xFF;
  127 + tps65912->write(tps65912, TPS65912_INT_MSK3, 1, &reg);
  128 + reg = tps65912->irq_mask >> 24 & 0xFF;
  129 + tps65912->write(tps65912, TPS65912_INT_MSK4, 1, &reg);
  130 + }
  131 +
  132 + mutex_unlock(&tps65912->irq_lock);
  133 +}
  134 +
  135 +static void tps65912_irq_enable(struct irq_data *data)
  136 +{
  137 + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
  138 +
  139 + tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq));
  140 +}
  141 +
  142 +static void tps65912_irq_disable(struct irq_data *data)
  143 +{
  144 + struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
  145 +
  146 + tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq));
  147 +}
  148 +
  149 +static struct irq_chip tps65912_irq_chip = {
  150 + .name = "tps65912",
  151 + .irq_bus_lock = tps65912_irq_lock,
  152 + .irq_bus_sync_unlock = tps65912_irq_sync_unlock,
  153 + .irq_disable = tps65912_irq_disable,
  154 + .irq_enable = tps65912_irq_enable,
  155 +};
  156 +
  157 +int tps65912_irq_init(struct tps65912 *tps65912, int irq,
  158 + struct tps65912_platform_data *pdata)
  159 +{
  160 + int ret, cur_irq;
  161 + int flags = IRQF_ONESHOT;
  162 + u8 reg;
  163 +
  164 + if (!irq) {
  165 + dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n");
  166 + return 0;
  167 + }
  168 +
  169 + if (!pdata || !pdata->irq_base) {
  170 + dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n");
  171 + return 0;
  172 + }
  173 +
  174 + /* Clear unattended interrupts */
  175 + tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
  176 + tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
  177 + tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
  178 + tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
  179 + tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
  180 + tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
  181 + tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
  182 + tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
  183 +
  184 + /* Mask top level interrupts */
  185 + tps65912->irq_mask = 0xFFFFFFFF;
  186 +
  187 + mutex_init(&tps65912->irq_lock);
  188 + tps65912->chip_irq = irq;
  189 + tps65912->irq_base = pdata->irq_base;
  190 +
  191 + tps65912->irq_num = TPS65912_NUM_IRQ;
  192 +
  193 + /* Register with genirq */
  194 + for (cur_irq = tps65912->irq_base;
  195 + cur_irq < tps65912->irq_num + tps65912->irq_base;
  196 + cur_irq++) {
  197 + irq_set_chip_data(cur_irq, tps65912);
  198 + irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip,
  199 + handle_edge_irq);
  200 + irq_set_nested_thread(cur_irq, 1);
  201 + /* ARM needs us to explicitly flag the IRQ as valid
  202 + * and will set them noprobe when we do so. */
  203 +#ifdef CONFIG_ARM
  204 + set_irq_flags(cur_irq, IRQF_VALID);
  205 +#else
  206 + irq_set_noprobe(cur_irq);
  207 +#endif
  208 + }
  209 +
  210 + ret = request_threaded_irq(irq, NULL, tps65912_irq, flags,
  211 + "tps65912", tps65912);
  212 +
  213 + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
  214 + if (ret != 0)
  215 + dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret);
  216 +
  217 + return ret;
  218 +}
  219 +
  220 +int tps65912_irq_exit(struct tps65912 *tps65912)
  221 +{
  222 + free_irq(tps65912->chip_irq, tps65912);
  223 + return 0;
  224 +}
include/linux/mfd/tps65912.h
... ... @@ -273,6 +273,8 @@
273 273 int is_dcdc2_avs;
274 274 int is_dcdc3_avs;
275 275 int is_dcdc4_avs;
  276 + int irq;
  277 + int irq_base;
276 278 struct regulator_init_data *tps65912_pmic_init_data;
277 279 };
278 280  
... ... @@ -306,6 +308,7 @@
306 308 };
307 309  
308 310 struct tps65912_platform_data {
  311 + int irq;
309 312 int irq_base;
310 313 };
311 314  
... ... @@ -317,6 +320,8 @@
317 320 int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val);
318 321 int tps65912_device_init(struct tps65912 *tps65912);
319 322 void tps65912_device_exit(struct tps65912 *tps65912);
  323 +int tps65912_irq_init(struct tps65912 *tps65912, int irq,
  324 + struct tps65912_platform_data *pdata);
320 325  
321 326 #endif /* __LINUX_MFD_TPS65912_H */