1966cdc96SMark Brown /* 2966cdc96SMark Brown * Arizona interrupt support 3966cdc96SMark Brown * 4966cdc96SMark Brown * Copyright 2012 Wolfson Microelectronics plc 5966cdc96SMark Brown * 6966cdc96SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 7966cdc96SMark Brown * 8966cdc96SMark Brown * This program is free software; you can redistribute it and/or modify 9966cdc96SMark Brown * it under the terms of the GNU General Public License version 2 as 10966cdc96SMark Brown * published by the Free Software Foundation. 11966cdc96SMark Brown */ 12966cdc96SMark Brown 13966cdc96SMark Brown #include <linux/delay.h> 14966cdc96SMark Brown #include <linux/gpio.h> 15966cdc96SMark Brown #include <linux/interrupt.h> 16966cdc96SMark Brown #include <linux/irq.h> 17966cdc96SMark Brown #include <linux/irqdomain.h> 18966cdc96SMark Brown #include <linux/module.h> 19966cdc96SMark Brown #include <linux/pm_runtime.h> 20966cdc96SMark Brown #include <linux/regmap.h> 21966cdc96SMark Brown #include <linux/regulator/consumer.h> 22966cdc96SMark Brown #include <linux/slab.h> 23966cdc96SMark Brown 24966cdc96SMark Brown #include <linux/mfd/arizona/core.h> 25966cdc96SMark Brown #include <linux/mfd/arizona/registers.h> 26966cdc96SMark Brown 27966cdc96SMark Brown #include "arizona.h" 28966cdc96SMark Brown 29966cdc96SMark Brown static int arizona_map_irq(struct arizona *arizona, int irq) 30966cdc96SMark Brown { 31966cdc96SMark Brown int ret; 32966cdc96SMark Brown 33966cdc96SMark Brown ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq); 34966cdc96SMark Brown if (ret < 0) 35966cdc96SMark Brown ret = regmap_irq_get_virq(arizona->irq_chip, irq); 36966cdc96SMark Brown 37966cdc96SMark Brown return ret; 38966cdc96SMark Brown } 39966cdc96SMark Brown 40966cdc96SMark Brown int arizona_request_irq(struct arizona *arizona, int irq, char *name, 41966cdc96SMark Brown irq_handler_t handler, void *data) 42966cdc96SMark Brown { 43966cdc96SMark Brown irq = arizona_map_irq(arizona, irq); 44966cdc96SMark Brown if (irq < 0) 45966cdc96SMark Brown return irq; 46966cdc96SMark Brown 47966cdc96SMark Brown return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, 48966cdc96SMark Brown name, data); 49966cdc96SMark Brown } 50966cdc96SMark Brown EXPORT_SYMBOL_GPL(arizona_request_irq); 51966cdc96SMark Brown 52966cdc96SMark Brown void arizona_free_irq(struct arizona *arizona, int irq, void *data) 53966cdc96SMark Brown { 54966cdc96SMark Brown irq = arizona_map_irq(arizona, irq); 55966cdc96SMark Brown if (irq < 0) 56966cdc96SMark Brown return; 57966cdc96SMark Brown 58966cdc96SMark Brown free_irq(irq, data); 59966cdc96SMark Brown } 60966cdc96SMark Brown EXPORT_SYMBOL_GPL(arizona_free_irq); 61966cdc96SMark Brown 62966cdc96SMark Brown int arizona_set_irq_wake(struct arizona *arizona, int irq, int on) 63966cdc96SMark Brown { 64966cdc96SMark Brown irq = arizona_map_irq(arizona, irq); 65966cdc96SMark Brown if (irq < 0) 66966cdc96SMark Brown return irq; 67966cdc96SMark Brown 68966cdc96SMark Brown return irq_set_irq_wake(irq, on); 69966cdc96SMark Brown } 70966cdc96SMark Brown EXPORT_SYMBOL_GPL(arizona_set_irq_wake); 71966cdc96SMark Brown 72966cdc96SMark Brown static irqreturn_t arizona_boot_done(int irq, void *data) 73966cdc96SMark Brown { 74966cdc96SMark Brown struct arizona *arizona = data; 75966cdc96SMark Brown 76966cdc96SMark Brown dev_dbg(arizona->dev, "Boot done\n"); 77966cdc96SMark Brown 78966cdc96SMark Brown return IRQ_HANDLED; 79966cdc96SMark Brown } 80966cdc96SMark Brown 81966cdc96SMark Brown static irqreturn_t arizona_ctrlif_err(int irq, void *data) 82966cdc96SMark Brown { 83966cdc96SMark Brown struct arizona *arizona = data; 84966cdc96SMark Brown 85966cdc96SMark Brown /* 86966cdc96SMark Brown * For pretty much all potential sources a register cache sync 87966cdc96SMark Brown * won't help, we've just got a software bug somewhere. 88966cdc96SMark Brown */ 89966cdc96SMark Brown dev_err(arizona->dev, "Control interface error\n"); 90966cdc96SMark Brown 91966cdc96SMark Brown return IRQ_HANDLED; 92966cdc96SMark Brown } 93966cdc96SMark Brown 94966cdc96SMark Brown static irqreturn_t arizona_irq_thread(int irq, void *data) 95966cdc96SMark Brown { 96966cdc96SMark Brown struct arizona *arizona = data; 973092f805SMark Brown bool poll; 983080de4eSMark Brown unsigned int val; 99cdabc1c8SMark Brown int ret; 100966cdc96SMark Brown 101966cdc96SMark Brown ret = pm_runtime_get_sync(arizona->dev); 102966cdc96SMark Brown if (ret < 0) { 103966cdc96SMark Brown dev_err(arizona->dev, "Failed to resume device: %d\n", ret); 104966cdc96SMark Brown return IRQ_NONE; 105966cdc96SMark Brown } 106966cdc96SMark Brown 1073092f805SMark Brown do { 1083092f805SMark Brown poll = false; 1093092f805SMark Brown 1103080de4eSMark Brown /* Always handle the AoD domain */ 1113080de4eSMark Brown handle_nested_irq(irq_find_mapping(arizona->virq, 0)); 1123080de4eSMark Brown 1133080de4eSMark Brown /* 1143080de4eSMark Brown * Check if one of the main interrupts is asserted and only 1153080de4eSMark Brown * check that domain if it is. 1163080de4eSMark Brown */ 1173092f805SMark Brown ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, 1183092f805SMark Brown &val); 1193080de4eSMark Brown if (ret == 0 && val & ARIZONA_IRQ1_STS) { 1203080de4eSMark Brown handle_nested_irq(irq_find_mapping(arizona->virq, 1)); 1213080de4eSMark Brown } else if (ret != 0) { 1223092f805SMark Brown dev_err(arizona->dev, 1233092f805SMark Brown "Failed to read main IRQ status: %d\n", ret); 1243080de4eSMark Brown } 125966cdc96SMark Brown 1263092f805SMark Brown /* 1273092f805SMark Brown * Poll the IRQ pin status to see if we're really done 1283092f805SMark Brown * if the interrupt controller can't do it for us. 1293092f805SMark Brown */ 1303092f805SMark Brown if (!arizona->pdata.irq_gpio) { 1313092f805SMark Brown break; 1323092f805SMark Brown } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_RISING && 1333092f805SMark Brown gpio_get_value_cansleep(arizona->pdata.irq_gpio)) { 1343092f805SMark Brown poll = true; 1353092f805SMark Brown } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_FALLING && 1363092f805SMark Brown !gpio_get_value_cansleep(arizona->pdata.irq_gpio)) { 1373092f805SMark Brown poll = true; 1383092f805SMark Brown } 1393092f805SMark Brown } while (poll); 1403092f805SMark Brown 141966cdc96SMark Brown pm_runtime_mark_last_busy(arizona->dev); 142966cdc96SMark Brown pm_runtime_put_autosuspend(arizona->dev); 143966cdc96SMark Brown 144966cdc96SMark Brown return IRQ_HANDLED; 145966cdc96SMark Brown } 146966cdc96SMark Brown 147966cdc96SMark Brown static void arizona_irq_enable(struct irq_data *data) 148966cdc96SMark Brown { 149966cdc96SMark Brown } 150966cdc96SMark Brown 151966cdc96SMark Brown static void arizona_irq_disable(struct irq_data *data) 152966cdc96SMark Brown { 153966cdc96SMark Brown } 154966cdc96SMark Brown 155c38715feSCharles Keepax static int arizona_irq_set_wake(struct irq_data *data, unsigned int on) 156c38715feSCharles Keepax { 157c38715feSCharles Keepax struct arizona *arizona = irq_data_get_irq_chip_data(data); 158c38715feSCharles Keepax 159c38715feSCharles Keepax return irq_set_irq_wake(arizona->irq, on); 160c38715feSCharles Keepax } 161c38715feSCharles Keepax 162966cdc96SMark Brown static struct irq_chip arizona_irq_chip = { 163966cdc96SMark Brown .name = "arizona", 164966cdc96SMark Brown .irq_disable = arizona_irq_disable, 165966cdc96SMark Brown .irq_enable = arizona_irq_enable, 166c38715feSCharles Keepax .irq_set_wake = arizona_irq_set_wake, 167966cdc96SMark Brown }; 168966cdc96SMark Brown 169966cdc96SMark Brown static int arizona_irq_map(struct irq_domain *h, unsigned int virq, 170966cdc96SMark Brown irq_hw_number_t hw) 171966cdc96SMark Brown { 172966cdc96SMark Brown struct regmap_irq_chip_data *data = h->host_data; 173966cdc96SMark Brown 174966cdc96SMark Brown irq_set_chip_data(virq, data); 175cfeb35daSCharles Keepax irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_simple_irq); 176966cdc96SMark Brown irq_set_nested_thread(virq, 1); 177966cdc96SMark Brown 178966cdc96SMark Brown /* ARM needs us to explicitly flag the IRQ as valid 179966cdc96SMark Brown * and will set them noprobe when we do so. */ 180966cdc96SMark Brown #ifdef CONFIG_ARM 181966cdc96SMark Brown set_irq_flags(virq, IRQF_VALID); 182966cdc96SMark Brown #else 183966cdc96SMark Brown irq_set_noprobe(virq); 184966cdc96SMark Brown #endif 185966cdc96SMark Brown 186966cdc96SMark Brown return 0; 187966cdc96SMark Brown } 188966cdc96SMark Brown 1897ce7b26fSKrzysztof Kozlowski static const struct irq_domain_ops arizona_domain_ops = { 190966cdc96SMark Brown .map = arizona_irq_map, 191966cdc96SMark Brown .xlate = irq_domain_xlate_twocell, 192966cdc96SMark Brown }; 193966cdc96SMark Brown 194966cdc96SMark Brown int arizona_irq_init(struct arizona *arizona) 195966cdc96SMark Brown { 196966cdc96SMark Brown int flags = IRQF_ONESHOT; 197966cdc96SMark Brown int ret, i; 198966cdc96SMark Brown const struct regmap_irq_chip *aod, *irq; 19922c75fe7SMark Brown struct irq_data *irq_data; 200966cdc96SMark Brown 20130a2af3aSCharles Keepax arizona->ctrlif_error = true; 20230a2af3aSCharles Keepax 203966cdc96SMark Brown switch (arizona->type) { 204863df8d5SMark Brown #ifdef CONFIG_MFD_WM5102 205966cdc96SMark Brown case WM5102: 206966cdc96SMark Brown aod = &wm5102_aod; 207966cdc96SMark Brown irq = &wm5102_irq; 20892d80139SMark Brown 20930a2af3aSCharles Keepax arizona->ctrlif_error = false; 21092d80139SMark Brown break; 211863df8d5SMark Brown #endif 212e102befeSMark Brown #ifdef CONFIG_MFD_WM5110 213e102befeSMark Brown case WM5110: 214e5d4ef0dSRichard Fitzgerald case WM8280: 215e102befeSMark Brown aod = &wm5110_aod; 2163215501fSCharles Keepax 2173215501fSCharles Keepax switch (arizona->rev) { 2183215501fSCharles Keepax case 0 ... 2: 219e102befeSMark Brown irq = &wm5110_irq; 2203215501fSCharles Keepax break; 2213215501fSCharles Keepax default: 2223215501fSCharles Keepax irq = &wm5110_revd_irq; 2233215501fSCharles Keepax break; 2243215501fSCharles Keepax } 22592d80139SMark Brown 22630a2af3aSCharles Keepax arizona->ctrlif_error = false; 22792d80139SMark Brown break; 228e102befeSMark Brown #endif 229dc7d4863SCharles Keepax #ifdef CONFIG_MFD_WM8997 230dc7d4863SCharles Keepax case WM8997: 231dc7d4863SCharles Keepax aod = &wm8997_aod; 232dc7d4863SCharles Keepax irq = &wm8997_irq; 233dc7d4863SCharles Keepax 23430a2af3aSCharles Keepax arizona->ctrlif_error = false; 235dc7d4863SCharles Keepax break; 236dc7d4863SCharles Keepax #endif 237966cdc96SMark Brown default: 238966cdc96SMark Brown BUG_ON("Unknown Arizona class device" == NULL); 239966cdc96SMark Brown return -EINVAL; 240966cdc96SMark Brown } 241966cdc96SMark Brown 2421816cb34SMark Brown /* Disable all wake sources by default */ 2431816cb34SMark Brown regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0); 2441816cb34SMark Brown 24522c75fe7SMark Brown /* Read the flags from the interrupt controller if not specified */ 24622c75fe7SMark Brown if (!arizona->pdata.irq_flags) { 24722c75fe7SMark Brown irq_data = irq_get_irq_data(arizona->irq); 24822c75fe7SMark Brown if (!irq_data) { 24922c75fe7SMark Brown dev_err(arizona->dev, "Invalid IRQ: %d\n", 25022c75fe7SMark Brown arizona->irq); 25122c75fe7SMark Brown return -EINVAL; 25222c75fe7SMark Brown } 25322c75fe7SMark Brown 25422c75fe7SMark Brown arizona->pdata.irq_flags = irqd_get_trigger_type(irq_data); 25522c75fe7SMark Brown switch (arizona->pdata.irq_flags) { 25622c75fe7SMark Brown case IRQF_TRIGGER_LOW: 25722c75fe7SMark Brown case IRQF_TRIGGER_HIGH: 25822c75fe7SMark Brown case IRQF_TRIGGER_RISING: 25922c75fe7SMark Brown case IRQF_TRIGGER_FALLING: 26022c75fe7SMark Brown break; 26122c75fe7SMark Brown 26222c75fe7SMark Brown case IRQ_TYPE_NONE: 26322c75fe7SMark Brown default: 26422c75fe7SMark Brown /* Device default */ 265f8a0941fSMark Brown arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; 26622c75fe7SMark Brown break; 26722c75fe7SMark Brown } 26822c75fe7SMark Brown } 269f8a0941fSMark Brown 270f8a0941fSMark Brown if (arizona->pdata.irq_flags & (IRQF_TRIGGER_HIGH | 271f8a0941fSMark Brown IRQF_TRIGGER_RISING)) { 272966cdc96SMark Brown ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1, 273966cdc96SMark Brown ARIZONA_IRQ_POL, 0); 274966cdc96SMark Brown if (ret != 0) { 275966cdc96SMark Brown dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n", 276966cdc96SMark Brown ret); 277966cdc96SMark Brown goto err; 278966cdc96SMark Brown } 279966cdc96SMark Brown } 280966cdc96SMark Brown 281f8a0941fSMark Brown flags |= arizona->pdata.irq_flags; 282f8a0941fSMark Brown 283966cdc96SMark Brown /* Allocate a virtual IRQ domain to distribute to the regmap domains */ 284966cdc96SMark Brown arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops, 285966cdc96SMark Brown arizona); 286966cdc96SMark Brown if (!arizona->virq) { 287b7dea5dcSMark Brown dev_err(arizona->dev, "Failed to add core IRQ domain\n"); 288966cdc96SMark Brown ret = -EINVAL; 289966cdc96SMark Brown goto err; 290966cdc96SMark Brown } 291966cdc96SMark Brown 292966cdc96SMark Brown ret = regmap_add_irq_chip(arizona->regmap, 293966cdc96SMark Brown irq_create_mapping(arizona->virq, 0), 2942a3377eeSCharles Keepax IRQF_ONESHOT, 0, aod, 295966cdc96SMark Brown &arizona->aod_irq_chip); 296966cdc96SMark Brown if (ret != 0) { 297966cdc96SMark Brown dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret); 298966cdc96SMark Brown goto err_domain; 299966cdc96SMark Brown } 300966cdc96SMark Brown 301966cdc96SMark Brown ret = regmap_add_irq_chip(arizona->regmap, 302966cdc96SMark Brown irq_create_mapping(arizona->virq, 1), 3032a3377eeSCharles Keepax IRQF_ONESHOT, 0, irq, 304966cdc96SMark Brown &arizona->irq_chip); 305966cdc96SMark Brown if (ret != 0) { 306d1cb4cc9SCharles Keepax dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret); 307966cdc96SMark Brown goto err_aod; 308966cdc96SMark Brown } 309966cdc96SMark Brown 310966cdc96SMark Brown /* Make sure the boot done IRQ is unmasked for resumes */ 311966cdc96SMark Brown i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE); 312966cdc96SMark Brown ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT, 313966cdc96SMark Brown "Boot done", arizona); 314966cdc96SMark Brown if (ret != 0) { 315966cdc96SMark Brown dev_err(arizona->dev, "Failed to request boot done %d: %d\n", 316966cdc96SMark Brown arizona->irq, ret); 317966cdc96SMark Brown goto err_boot_done; 318966cdc96SMark Brown } 319966cdc96SMark Brown 320966cdc96SMark Brown /* Handle control interface errors in the core */ 32130a2af3aSCharles Keepax if (arizona->ctrlif_error) { 322966cdc96SMark Brown i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR); 32392d80139SMark Brown ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, 32492d80139SMark Brown IRQF_ONESHOT, 325966cdc96SMark Brown "Control interface error", arizona); 326966cdc96SMark Brown if (ret != 0) { 32792d80139SMark Brown dev_err(arizona->dev, 32892d80139SMark Brown "Failed to request CTRLIF_ERR %d: %d\n", 329966cdc96SMark Brown arizona->irq, ret); 330966cdc96SMark Brown goto err_ctrlif; 331966cdc96SMark Brown } 33292d80139SMark Brown } 333966cdc96SMark Brown 3343092f805SMark Brown /* Used to emulate edge trigger and to work around broken pinmux */ 3353092f805SMark Brown if (arizona->pdata.irq_gpio) { 3363092f805SMark Brown if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) { 3373092f805SMark Brown dev_warn(arizona->dev, "IRQ %d is not GPIO %d (%d)\n", 3383092f805SMark Brown arizona->irq, arizona->pdata.irq_gpio, 3393092f805SMark Brown gpio_to_irq(arizona->pdata.irq_gpio)); 3403092f805SMark Brown arizona->irq = gpio_to_irq(arizona->pdata.irq_gpio); 3413092f805SMark Brown } 3423092f805SMark Brown 3433092f805SMark Brown ret = devm_gpio_request_one(arizona->dev, 3443092f805SMark Brown arizona->pdata.irq_gpio, 3453092f805SMark Brown GPIOF_IN, "arizona IRQ"); 3463092f805SMark Brown if (ret != 0) { 3473092f805SMark Brown dev_err(arizona->dev, 3483092f805SMark Brown "Failed to request IRQ GPIO %d:: %d\n", 3493092f805SMark Brown arizona->pdata.irq_gpio, ret); 3503092f805SMark Brown arizona->pdata.irq_gpio = 0; 3513092f805SMark Brown } 3523092f805SMark Brown } 3533092f805SMark Brown 354966cdc96SMark Brown ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread, 355966cdc96SMark Brown flags, "arizona", arizona); 356966cdc96SMark Brown 357966cdc96SMark Brown if (ret != 0) { 3587994c664SMark Brown dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n", 359966cdc96SMark Brown arizona->irq, ret); 360966cdc96SMark Brown goto err_main_irq; 361966cdc96SMark Brown } 362966cdc96SMark Brown 363966cdc96SMark Brown return 0; 364966cdc96SMark Brown 365966cdc96SMark Brown err_main_irq: 36630a2af3aSCharles Keepax if (arizona->ctrlif_error) 36730a2af3aSCharles Keepax free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), 36830a2af3aSCharles Keepax arizona); 369966cdc96SMark Brown err_ctrlif: 370966cdc96SMark Brown free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); 371966cdc96SMark Brown err_boot_done: 372966cdc96SMark Brown regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1), 373966cdc96SMark Brown arizona->irq_chip); 374966cdc96SMark Brown err_aod: 375966cdc96SMark Brown regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0), 376966cdc96SMark Brown arizona->aod_irq_chip); 377966cdc96SMark Brown err_domain: 378966cdc96SMark Brown err: 379966cdc96SMark Brown return ret; 380966cdc96SMark Brown } 381966cdc96SMark Brown 382966cdc96SMark Brown int arizona_irq_exit(struct arizona *arizona) 383966cdc96SMark Brown { 38430a2af3aSCharles Keepax if (arizona->ctrlif_error) 38530a2af3aSCharles Keepax free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), 38630a2af3aSCharles Keepax arizona); 387966cdc96SMark Brown free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); 388966cdc96SMark Brown regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1), 389966cdc96SMark Brown arizona->irq_chip); 390966cdc96SMark Brown regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0), 391966cdc96SMark Brown arizona->aod_irq_chip); 392966cdc96SMark Brown free_irq(arizona->irq, arizona); 393966cdc96SMark Brown 394966cdc96SMark Brown return 0; 395966cdc96SMark Brown } 396