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