1 /* 2 * DA9052 interrupt support 3 * 4 * Author: Fabio Estevam <fabio.estevam@freescale.com> 5 * Based on arizona-irq.c, which is: 6 * 7 * Copyright 2012 Wolfson Microelectronics plc 8 * 9 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 */ 15 16 #include <linux/device.h> 17 #include <linux/delay.h> 18 #include <linux/input.h> 19 #include <linux/interrupt.h> 20 #include <linux/irq.h> 21 #include <linux/irqdomain.h> 22 #include <linux/slab.h> 23 #include <linux/module.h> 24 25 #include <linux/mfd/da9052/da9052.h> 26 #include <linux/mfd/da9052/reg.h> 27 28 #define DA9052_NUM_IRQ_REGS 4 29 #define DA9052_IRQ_MASK_POS_1 0x01 30 #define DA9052_IRQ_MASK_POS_2 0x02 31 #define DA9052_IRQ_MASK_POS_3 0x04 32 #define DA9052_IRQ_MASK_POS_4 0x08 33 #define DA9052_IRQ_MASK_POS_5 0x10 34 #define DA9052_IRQ_MASK_POS_6 0x20 35 #define DA9052_IRQ_MASK_POS_7 0x40 36 #define DA9052_IRQ_MASK_POS_8 0x80 37 38 static const struct regmap_irq da9052_irqs[] = { 39 [DA9052_IRQ_DCIN] = { 40 .reg_offset = 0, 41 .mask = DA9052_IRQ_MASK_POS_1, 42 }, 43 [DA9052_IRQ_VBUS] = { 44 .reg_offset = 0, 45 .mask = DA9052_IRQ_MASK_POS_2, 46 }, 47 [DA9052_IRQ_DCINREM] = { 48 .reg_offset = 0, 49 .mask = DA9052_IRQ_MASK_POS_3, 50 }, 51 [DA9052_IRQ_VBUSREM] = { 52 .reg_offset = 0, 53 .mask = DA9052_IRQ_MASK_POS_4, 54 }, 55 [DA9052_IRQ_VDDLOW] = { 56 .reg_offset = 0, 57 .mask = DA9052_IRQ_MASK_POS_5, 58 }, 59 [DA9052_IRQ_ALARM] = { 60 .reg_offset = 0, 61 .mask = DA9052_IRQ_MASK_POS_6, 62 }, 63 [DA9052_IRQ_SEQRDY] = { 64 .reg_offset = 0, 65 .mask = DA9052_IRQ_MASK_POS_7, 66 }, 67 [DA9052_IRQ_COMP1V2] = { 68 .reg_offset = 0, 69 .mask = DA9052_IRQ_MASK_POS_8, 70 }, 71 [DA9052_IRQ_NONKEY] = { 72 .reg_offset = 1, 73 .mask = DA9052_IRQ_MASK_POS_1, 74 }, 75 [DA9052_IRQ_IDFLOAT] = { 76 .reg_offset = 1, 77 .mask = DA9052_IRQ_MASK_POS_2, 78 }, 79 [DA9052_IRQ_IDGND] = { 80 .reg_offset = 1, 81 .mask = DA9052_IRQ_MASK_POS_3, 82 }, 83 [DA9052_IRQ_CHGEND] = { 84 .reg_offset = 1, 85 .mask = DA9052_IRQ_MASK_POS_4, 86 }, 87 [DA9052_IRQ_TBAT] = { 88 .reg_offset = 1, 89 .mask = DA9052_IRQ_MASK_POS_5, 90 }, 91 [DA9052_IRQ_ADC_EOM] = { 92 .reg_offset = 1, 93 .mask = DA9052_IRQ_MASK_POS_6, 94 }, 95 [DA9052_IRQ_PENDOWN] = { 96 .reg_offset = 1, 97 .mask = DA9052_IRQ_MASK_POS_7, 98 }, 99 [DA9052_IRQ_TSIREADY] = { 100 .reg_offset = 1, 101 .mask = DA9052_IRQ_MASK_POS_8, 102 }, 103 [DA9052_IRQ_GPI0] = { 104 .reg_offset = 2, 105 .mask = DA9052_IRQ_MASK_POS_1, 106 }, 107 [DA9052_IRQ_GPI1] = { 108 .reg_offset = 2, 109 .mask = DA9052_IRQ_MASK_POS_2, 110 }, 111 [DA9052_IRQ_GPI2] = { 112 .reg_offset = 2, 113 .mask = DA9052_IRQ_MASK_POS_3, 114 }, 115 [DA9052_IRQ_GPI3] = { 116 .reg_offset = 2, 117 .mask = DA9052_IRQ_MASK_POS_4, 118 }, 119 [DA9052_IRQ_GPI4] = { 120 .reg_offset = 2, 121 .mask = DA9052_IRQ_MASK_POS_5, 122 }, 123 [DA9052_IRQ_GPI5] = { 124 .reg_offset = 2, 125 .mask = DA9052_IRQ_MASK_POS_6, 126 }, 127 [DA9052_IRQ_GPI6] = { 128 .reg_offset = 2, 129 .mask = DA9052_IRQ_MASK_POS_7, 130 }, 131 [DA9052_IRQ_GPI7] = { 132 .reg_offset = 2, 133 .mask = DA9052_IRQ_MASK_POS_8, 134 }, 135 [DA9052_IRQ_GPI8] = { 136 .reg_offset = 3, 137 .mask = DA9052_IRQ_MASK_POS_1, 138 }, 139 [DA9052_IRQ_GPI9] = { 140 .reg_offset = 3, 141 .mask = DA9052_IRQ_MASK_POS_2, 142 }, 143 [DA9052_IRQ_GPI10] = { 144 .reg_offset = 3, 145 .mask = DA9052_IRQ_MASK_POS_3, 146 }, 147 [DA9052_IRQ_GPI11] = { 148 .reg_offset = 3, 149 .mask = DA9052_IRQ_MASK_POS_4, 150 }, 151 [DA9052_IRQ_GPI12] = { 152 .reg_offset = 3, 153 .mask = DA9052_IRQ_MASK_POS_5, 154 }, 155 [DA9052_IRQ_GPI13] = { 156 .reg_offset = 3, 157 .mask = DA9052_IRQ_MASK_POS_6, 158 }, 159 [DA9052_IRQ_GPI14] = { 160 .reg_offset = 3, 161 .mask = DA9052_IRQ_MASK_POS_7, 162 }, 163 [DA9052_IRQ_GPI15] = { 164 .reg_offset = 3, 165 .mask = DA9052_IRQ_MASK_POS_8, 166 }, 167 }; 168 169 static const struct regmap_irq_chip da9052_regmap_irq_chip = { 170 .name = "da9052_irq", 171 .status_base = DA9052_EVENT_A_REG, 172 .mask_base = DA9052_IRQ_MASK_A_REG, 173 .ack_base = DA9052_EVENT_A_REG, 174 .num_regs = DA9052_NUM_IRQ_REGS, 175 .irqs = da9052_irqs, 176 .num_irqs = ARRAY_SIZE(da9052_irqs), 177 }; 178 179 static int da9052_map_irq(struct da9052 *da9052, int irq) 180 { 181 return regmap_irq_get_virq(da9052->irq_data, irq); 182 } 183 184 int da9052_enable_irq(struct da9052 *da9052, int irq) 185 { 186 irq = da9052_map_irq(da9052, irq); 187 if (irq < 0) 188 return irq; 189 190 enable_irq(irq); 191 192 return 0; 193 } 194 EXPORT_SYMBOL_GPL(da9052_enable_irq); 195 196 int da9052_disable_irq(struct da9052 *da9052, int irq) 197 { 198 irq = da9052_map_irq(da9052, irq); 199 if (irq < 0) 200 return irq; 201 202 disable_irq(irq); 203 204 return 0; 205 } 206 EXPORT_SYMBOL_GPL(da9052_disable_irq); 207 208 int da9052_disable_irq_nosync(struct da9052 *da9052, int irq) 209 { 210 irq = da9052_map_irq(da9052, irq); 211 if (irq < 0) 212 return irq; 213 214 disable_irq_nosync(irq); 215 216 return 0; 217 } 218 EXPORT_SYMBOL_GPL(da9052_disable_irq_nosync); 219 220 int da9052_request_irq(struct da9052 *da9052, int irq, char *name, 221 irq_handler_t handler, void *data) 222 { 223 irq = da9052_map_irq(da9052, irq); 224 if (irq < 0) 225 return irq; 226 227 return request_threaded_irq(irq, NULL, handler, 228 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 229 name, data); 230 } 231 EXPORT_SYMBOL_GPL(da9052_request_irq); 232 233 void da9052_free_irq(struct da9052 *da9052, int irq, void *data) 234 { 235 irq = da9052_map_irq(da9052, irq); 236 if (irq < 0) 237 return; 238 239 free_irq(irq, data); 240 } 241 EXPORT_SYMBOL_GPL(da9052_free_irq); 242 243 static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data) 244 { 245 struct da9052 *da9052 = irq_data; 246 247 complete(&da9052->done); 248 249 return IRQ_HANDLED; 250 } 251 252 int da9052_irq_init(struct da9052 *da9052) 253 { 254 int ret; 255 256 ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, 257 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 258 -1, &da9052_regmap_irq_chip, 259 &da9052->irq_data); 260 if (ret < 0) { 261 dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret); 262 goto regmap_err; 263 } 264 265 enable_irq_wake(da9052->chip_irq); 266 267 ret = da9052_request_irq(da9052, DA9052_IRQ_ADC_EOM, "adc-irq", 268 da9052_auxadc_irq, da9052); 269 270 if (ret != 0) { 271 dev_err(da9052->dev, "DA9052_IRQ_ADC_EOM failed: %d\n", ret); 272 goto request_irq_err; 273 } 274 275 return 0; 276 277 request_irq_err: 278 regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); 279 regmap_err: 280 return ret; 281 282 } 283 284 int da9052_irq_exit(struct da9052 *da9052) 285 { 286 da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM, da9052); 287 regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); 288 289 return 0; 290 } 291