root/drivers/mfd/arizona-irq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. arizona_map_irq
  2. arizona_request_irq
  3. arizona_free_irq
  4. arizona_set_irq_wake
  5. arizona_boot_done
  6. arizona_ctrlif_err
  7. arizona_irq_thread
  8. arizona_irq_enable
  9. arizona_irq_disable
  10. arizona_irq_set_wake
  11. arizona_irq_map
  12. arizona_irq_init
  13. arizona_irq_exit

   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 
  29 static 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 
  42 int 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 }
  52 EXPORT_SYMBOL_GPL(arizona_request_irq);
  53 
  54 void 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 }
  62 EXPORT_SYMBOL_GPL(arizona_free_irq);
  63 
  64 int 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 }
  72 EXPORT_SYMBOL_GPL(arizona_set_irq_wake);
  73 
  74 static 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 
  83 static 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 
  96 static 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_get_sync(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 
 161 static void arizona_irq_enable(struct irq_data *data)
 162 {
 163 }
 164 
 165 static void arizona_irq_disable(struct irq_data *data)
 166 {
 167 }
 168 
 169 static 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 
 176 static 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 
 183 static struct lock_class_key arizona_irq_lock_class;
 184 static struct lock_class_key arizona_irq_request_class;
 185 
 186 static 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 
 201 static const struct irq_domain_ops arizona_domain_ops = {
 202         .map    = arizona_irq_map,
 203         .xlate  = irq_domain_xlate_twocell,
 204 };
 205 
 206 int 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 
 407 err_ctrlif:
 408         arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona);
 409 err_boot_done:
 410         free_irq(arizona->irq, arizona);
 411 err_main_irq:
 412         regmap_del_irq_chip(irq_find_mapping(arizona->virq,
 413                                              ARIZONA_MAIN_IRQ_INDEX),
 414                             arizona->irq_chip);
 415 err_map_main_irq:
 416         irq_dispose_mapping(irq_find_mapping(arizona->virq,
 417                                              ARIZONA_MAIN_IRQ_INDEX));
 418 err_aod:
 419         regmap_del_irq_chip(irq_find_mapping(arizona->virq,
 420                                              ARIZONA_AOD_IRQ_INDEX),
 421                             arizona->aod_irq_chip);
 422 err_map_aod:
 423         irq_dispose_mapping(irq_find_mapping(arizona->virq,
 424                                              ARIZONA_AOD_IRQ_INDEX));
 425 err_domain:
 426         irq_domain_remove(arizona->virq);
 427 err:
 428         return ret;
 429 }
 430 
 431 int 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 }

/* [<][>][^][v][top][bottom][index][help] */