linux/drivers/mfd/arizona-irq.c
<<
>>
Prefs
   1/*
   2 * Arizona interrupt support
   3 *
   4 * Copyright 2012 Wolfson Microelectronics plc
   5 *
   6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/gpio.h>
  15#include <linux/interrupt.h>
  16#include <linux/irq.h>
  17#include <linux/irqdomain.h>
  18#include <linux/module.h>
  19#include <linux/pm_runtime.h>
  20#include <linux/regmap.h>
  21#include <linux/regulator/consumer.h>
  22#include <linux/slab.h>
  23
  24#include <linux/mfd/arizona/core.h>
  25#include <linux/mfd/arizona/registers.h>
  26
  27#include "arizona.h"
  28
  29#define ARIZONA_AOD_IRQ_INDEX 0
  30#define ARIZONA_MAIN_IRQ_INDEX 1
  31
  32static int arizona_map_irq(struct arizona *arizona, int irq)
  33{
  34        int ret;
  35
  36        if (arizona->aod_irq_chip) {
  37                ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
  38                if (ret >= 0)
  39                        return ret;
  40        }
  41
  42        return regmap_irq_get_virq(arizona->irq_chip, irq);
  43}
  44
  45int arizona_request_irq(struct arizona *arizona, int irq, char *name,
  46                           irq_handler_t handler, void *data)
  47{
  48        irq = arizona_map_irq(arizona, irq);
  49        if (irq < 0)
  50                return irq;
  51
  52        return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT,
  53                                    name, data);
  54}
  55EXPORT_SYMBOL_GPL(arizona_request_irq);
  56
  57void arizona_free_irq(struct arizona *arizona, int irq, void *data)
  58{
  59        irq = arizona_map_irq(arizona, irq);
  60        if (irq < 0)
  61                return;
  62
  63        free_irq(irq, data);
  64}
  65EXPORT_SYMBOL_GPL(arizona_free_irq);
  66
  67int arizona_set_irq_wake(struct arizona *arizona, int irq, int on)
  68{
  69        irq = arizona_map_irq(arizona, irq);
  70        if (irq < 0)
  71                return irq;
  72
  73        return irq_set_irq_wake(irq, on);
  74}
  75EXPORT_SYMBOL_GPL(arizona_set_irq_wake);
  76
  77static irqreturn_t arizona_boot_done(int irq, void *data)
  78{
  79        struct arizona *arizona = data;
  80
  81        dev_dbg(arizona->dev, "Boot done\n");
  82
  83        return IRQ_HANDLED;
  84}
  85
  86static irqreturn_t arizona_ctrlif_err(int irq, void *data)
  87{
  88        struct arizona *arizona = data;
  89
  90        /*
  91         * For pretty much all potential sources a register cache sync
  92         * won't help, we've just got a software bug somewhere.
  93         */
  94        dev_err(arizona->dev, "Control interface error\n");
  95
  96        return IRQ_HANDLED;
  97}
  98
  99static irqreturn_t arizona_irq_thread(int irq, void *data)
 100{
 101        struct arizona *arizona = data;
 102        bool poll;
 103        unsigned int val;
 104        int ret;
 105
 106        ret = pm_runtime_get_sync(arizona->dev);
 107        if (ret < 0) {
 108                dev_err(arizona->dev, "Failed to resume device: %d\n", ret);
 109                return IRQ_NONE;
 110        }
 111
 112        do {
 113                poll = false;
 114
 115                if (arizona->aod_irq_chip) {
 116                        /*
 117                         * Check the AOD status register to determine whether
 118                         * the nested IRQ handler should be called.
 119                         */
 120                        ret = regmap_read(arizona->regmap,
 121                                          ARIZONA_AOD_IRQ1, &val);
 122                        if (ret)
 123                                dev_warn(arizona->dev,
 124                                        "Failed to read AOD IRQ1 %d\n", ret);
 125                        else if (val)
 126                                handle_nested_irq(
 127                                        irq_find_mapping(arizona->virq, 0));
 128                }
 129
 130                /*
 131                 * Check if one of the main interrupts is asserted and only
 132                 * check that domain if it is.
 133                 */
 134                ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS,
 135                                  &val);
 136                if (ret == 0 && val & ARIZONA_IRQ1_STS) {
 137                        handle_nested_irq(irq_find_mapping(arizona->virq, 1));
 138                } else if (ret != 0) {
 139                        dev_err(arizona->dev,
 140                                "Failed to read main IRQ status: %d\n", ret);
 141                }
 142
 143                /*
 144                 * Poll the IRQ pin status to see if we're really done
 145                 * if the interrupt controller can't do it for us.
 146                 */
 147                if (!arizona->pdata.irq_gpio) {
 148                        break;
 149                } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_RISING &&
 150                           gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
 151                        poll = true;
 152                } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_FALLING &&
 153                           !gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
 154                        poll = true;
 155                }
 156        } while (poll);
 157
 158        pm_runtime_mark_last_busy(arizona->dev);
 159        pm_runtime_put_autosuspend(arizona->dev);
 160
 161        return IRQ_HANDLED;
 162}
 163
 164static void arizona_irq_enable(struct irq_data *data)
 165{
 166}
 167
 168static void arizona_irq_disable(struct irq_data *data)
 169{
 170}
 171
 172static int arizona_irq_set_wake(struct irq_data *data, unsigned int on)
 173{
 174        struct arizona *arizona = irq_data_get_irq_chip_data(data);
 175
 176        return irq_set_irq_wake(arizona->irq, on);
 177}
 178
 179static struct irq_chip arizona_irq_chip = {
 180        .name                   = "arizona",
 181        .irq_disable            = arizona_irq_disable,
 182        .irq_enable             = arizona_irq_enable,
 183        .irq_set_wake           = arizona_irq_set_wake,
 184};
 185
 186static struct lock_class_key arizona_irq_lock_class;
 187static struct lock_class_key arizona_irq_request_class;
 188
 189static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
 190                              irq_hw_number_t hw)
 191{
 192        struct arizona *data = h->host_data;
 193
 194        irq_set_chip_data(virq, data);
 195        irq_set_lockdep_class(virq, &arizona_irq_lock_class,
 196                &arizona_irq_request_class);
 197        irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_simple_irq);
 198        irq_set_nested_thread(virq, 1);
 199        irq_set_noprobe(virq);
 200
 201        return 0;
 202}
 203
 204static const struct irq_domain_ops arizona_domain_ops = {
 205        .map    = arizona_irq_map,
 206        .xlate  = irq_domain_xlate_twocell,
 207};
 208
 209int arizona_irq_init(struct arizona *arizona)
 210{
 211        int flags = IRQF_ONESHOT;
 212        int ret;
 213        const struct regmap_irq_chip *aod, *irq;
 214        struct irq_data *irq_data;
 215        unsigned int virq;
 216
 217        arizona->ctrlif_error = true;
 218
 219        switch (arizona->type) {
 220#ifdef CONFIG_MFD_WM5102
 221        case WM5102:
 222                aod = &wm5102_aod;
 223                irq = &wm5102_irq;
 224
 225                arizona->ctrlif_error = false;
 226                break;
 227#endif
 228#ifdef CONFIG_MFD_WM5110
 229        case WM5110:
 230        case WM8280:
 231                aod = &wm5110_aod;
 232
 233                switch (arizona->rev) {
 234                case 0 ... 2:
 235                        irq = &wm5110_irq;
 236                        break;
 237                default:
 238                        irq = &wm5110_revd_irq;
 239                        break;
 240                }
 241
 242                arizona->ctrlif_error = false;
 243                break;
 244#endif
 245#ifdef CONFIG_MFD_CS47L24
 246        case WM1831:
 247        case CS47L24:
 248                aod = NULL;
 249                irq = &cs47l24_irq;
 250
 251                arizona->ctrlif_error = false;
 252                break;
 253#endif
 254#ifdef CONFIG_MFD_WM8997
 255        case WM8997:
 256                aod = &wm8997_aod;
 257                irq = &wm8997_irq;
 258
 259                arizona->ctrlif_error = false;
 260                break;
 261#endif
 262#ifdef CONFIG_MFD_WM8998
 263        case WM8998:
 264        case WM1814:
 265                aod = &wm8998_aod;
 266                irq = &wm8998_irq;
 267
 268                arizona->ctrlif_error = false;
 269                break;
 270#endif
 271        default:
 272                BUG_ON("Unknown Arizona class device" == NULL);
 273                return -EINVAL;
 274        }
 275
 276        /* Disable all wake sources by default */
 277        regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0);
 278
 279        /* Read the flags from the interrupt controller if not specified */
 280        if (!arizona->pdata.irq_flags) {
 281                irq_data = irq_get_irq_data(arizona->irq);
 282                if (!irq_data) {
 283                        dev_err(arizona->dev, "Invalid IRQ: %d\n",
 284                                arizona->irq);
 285                        return -EINVAL;
 286                }
 287
 288                arizona->pdata.irq_flags = irqd_get_trigger_type(irq_data);
 289                switch (arizona->pdata.irq_flags) {
 290                case IRQF_TRIGGER_LOW:
 291                case IRQF_TRIGGER_HIGH:
 292                case IRQF_TRIGGER_RISING:
 293                case IRQF_TRIGGER_FALLING:
 294                        break;
 295
 296                case IRQ_TYPE_NONE:
 297                default:
 298                        /* Device default */
 299                        arizona->pdata.irq_flags = IRQF_TRIGGER_LOW;
 300                        break;
 301                }
 302        }
 303
 304        if (arizona->pdata.irq_flags & (IRQF_TRIGGER_HIGH |
 305                                        IRQF_TRIGGER_RISING)) {
 306                ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,
 307                                         ARIZONA_IRQ_POL, 0);
 308                if (ret != 0) {
 309                        dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n",
 310                                ret);
 311                        goto err;
 312                }
 313        }
 314
 315        flags |= arizona->pdata.irq_flags;
 316
 317        /* Allocate a virtual IRQ domain to distribute to the regmap domains */
 318        arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
 319                                              arizona);
 320        if (!arizona->virq) {
 321                dev_err(arizona->dev, "Failed to add core IRQ domain\n");
 322                ret = -EINVAL;
 323                goto err;
 324        }
 325
 326        if (aod) {
 327                virq = irq_create_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX);
 328                if (!virq) {
 329                        dev_err(arizona->dev, "Failed to map AOD IRQs\n");
 330                        ret = -EINVAL;
 331                        goto err_domain;
 332                }
 333
 334                ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT,
 335                                          0, aod, &arizona->aod_irq_chip);
 336                if (ret != 0) {
 337                        dev_err(arizona->dev,
 338                                "Failed to add AOD IRQs: %d\n", ret);
 339                        goto err_map_aod;
 340                }
 341        }
 342
 343        virq = irq_create_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX);
 344        if (!virq) {
 345                dev_err(arizona->dev, "Failed to map main IRQs\n");
 346                ret = -EINVAL;
 347                goto err_aod;
 348        }
 349
 350        ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT,
 351                                  0, irq, &arizona->irq_chip);
 352        if (ret != 0) {
 353                dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret);
 354                goto err_map_main_irq;
 355        }
 356
 357        /* Used to emulate edge trigger and to work around broken pinmux */
 358        if (arizona->pdata.irq_gpio) {
 359                if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) {
 360                        dev_warn(arizona->dev, "IRQ %d is not GPIO %d (%d)\n",
 361                                 arizona->irq, arizona->pdata.irq_gpio,
 362                                 gpio_to_irq(arizona->pdata.irq_gpio));
 363                        arizona->irq = gpio_to_irq(arizona->pdata.irq_gpio);
 364                }
 365
 366                ret = devm_gpio_request_one(arizona->dev,
 367                                            arizona->pdata.irq_gpio,
 368                                            GPIOF_IN, "arizona IRQ");
 369                if (ret != 0) {
 370                        dev_err(arizona->dev,
 371                                "Failed to request IRQ GPIO %d:: %d\n",
 372                                arizona->pdata.irq_gpio, ret);
 373                        arizona->pdata.irq_gpio = 0;
 374                }
 375        }
 376
 377        ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
 378                                   flags, "arizona", arizona);
 379
 380        if (ret != 0) {
 381                dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n",
 382                        arizona->irq, ret);
 383                goto err_main_irq;
 384        }
 385
 386        /* Make sure the boot done IRQ is unmasked for resumes */
 387        ret = arizona_request_irq(arizona, ARIZONA_IRQ_BOOT_DONE, "Boot done",
 388                                  arizona_boot_done, arizona);
 389        if (ret != 0) {
 390                dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
 391                        arizona->irq, ret);
 392                goto err_boot_done;
 393        }
 394
 395        /* Handle control interface errors in the core */
 396        if (arizona->ctrlif_error) {
 397                ret = arizona_request_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR,
 398                                          "Control interface error",
 399                                          arizona_ctrlif_err, arizona);
 400                if (ret != 0) {
 401                        dev_err(arizona->dev,
 402                                "Failed to request CTRLIF_ERR %d: %d\n",
 403                                arizona->irq, ret);
 404                        goto err_ctrlif;
 405                }
 406        }
 407
 408        return 0;
 409
 410err_ctrlif:
 411        arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
 412err_boot_done:
 413        free_irq(arizona->irq, arizona);
 414err_main_irq:
 415        regmap_del_irq_chip(irq_find_mapping(arizona->virq,
 416                                             ARIZONA_MAIN_IRQ_INDEX),
 417                            arizona->irq_chip);
 418err_map_main_irq:
 419        irq_dispose_mapping(irq_find_mapping(arizona->virq,
 420                                             ARIZONA_MAIN_IRQ_INDEX));
 421err_aod:
 422        regmap_del_irq_chip(irq_find_mapping(arizona->virq,
 423                                             ARIZONA_AOD_IRQ_INDEX),
 424                            arizona->aod_irq_chip);
 425err_map_aod:
 426        irq_dispose_mapping(irq_find_mapping(arizona->virq,
 427                                             ARIZONA_AOD_IRQ_INDEX));
 428err_domain:
 429        irq_domain_remove(arizona->virq);
 430err:
 431        return ret;
 432}
 433
 434int arizona_irq_exit(struct arizona *arizona)
 435{
 436        unsigned int virq;
 437
 438        if (arizona->ctrlif_error)
 439                arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona);
 440        arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
 441
 442        virq = irq_find_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX);
 443        regmap_del_irq_chip(virq, arizona->irq_chip);
 444        irq_dispose_mapping(virq);
 445
 446        virq = irq_find_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX);
 447        regmap_del_irq_chip(virq, arizona->aod_irq_chip);
 448        irq_dispose_mapping(virq);
 449
 450        irq_domain_remove(arizona->virq);
 451
 452        free_irq(arizona->irq, arizona);
 453
 454        return 0;
 455}
 456