blob: 999b53f964aee8d48ea496da5f1ea3e714c3c8b3 [file] [log] [blame]
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -07001/*
Duy Truong790f06d2013-02-13 16:38:12 -08002 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
Steve Mucklef132c6c2012-06-06 18:30:57 -070016#include <linux/export.h>
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070017#include <linux/err.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/kernel.h>
21#include <linux/mfd/pm8xxx/core.h>
22#include <linux/mfd/pm8xxx/irq.h>
23#include <linux/platform_device.h>
24#include <linux/slab.h>
25
26/* PMIC8xxx IRQ */
27
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053028#define SSBI_REG_ADDR_IRQ_ROOT(base) (base + 0)
29#define SSBI_REG_ADDR_IRQ_M_STATUS1(base) (base + 1)
30#define SSBI_REG_ADDR_IRQ_M_STATUS2(base) (base + 2)
31#define SSBI_REG_ADDR_IRQ_M_STATUS3(base) (base + 3)
32#define SSBI_REG_ADDR_IRQ_M_STATUS4(base) (base + 4)
33#define SSBI_REG_ADDR_IRQ_BLK_SEL(base) (base + 5)
34#define SSBI_REG_ADDR_IRQ_IT_STATUS(base) (base + 6)
35#define SSBI_REG_ADDR_IRQ_CONFIG(base) (base + 7)
36#define SSBI_REG_ADDR_IRQ_RT_STATUS(base) (base + 8)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070037
38#define PM_IRQF_LVL_SEL 0x01 /* level select */
39#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
40#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
41#define PM_IRQF_CLR 0x08 /* clear interrupt */
42#define PM_IRQF_BITS_MASK 0x70
43#define PM_IRQF_BITS_SHIFT 4
44#define PM_IRQF_WRITE 0x80
45
46#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
47 PM_IRQF_MASK_RE)
48
49struct pm_irq_chip {
50 struct device *dev;
51 spinlock_t pm_irq_lock;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053052 unsigned int base_addr;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070053 unsigned int devirq;
54 unsigned int irq_base;
55 unsigned int num_irqs;
56 unsigned int num_blocks;
57 unsigned int num_masters;
58 u8 config[0];
59};
60
61static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
62{
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053063 return pm8xxx_readb(chip->dev,
64 SSBI_REG_ADDR_IRQ_ROOT(chip->base_addr), rp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070065}
66
67static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
68{
69 return pm8xxx_readb(chip->dev,
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053070 SSBI_REG_ADDR_IRQ_M_STATUS1(chip->base_addr) + m, bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070071}
72
73static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
74{
75 int rc;
76
77 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053078 rc = pm8xxx_writeb(chip->dev,
79 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070080 if (rc) {
81 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
82 goto bail;
83 }
84
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053085 rc = pm8xxx_readb(chip->dev,
86 SSBI_REG_ADDR_IRQ_IT_STATUS(chip->base_addr), ip);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070087 if (rc)
88 pr_err("Failed Reading Status rc=%d\n", rc);
89bail:
90 spin_unlock(&chip->pm_irq_lock);
91 return rc;
92}
93
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -070094static int pm8xxx_read_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp, u8 *r)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070095{
96 int rc;
97
98 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053099 rc = pm8xxx_writeb(chip->dev,
100 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700101 if (rc) {
102 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
103 goto bail;
104 }
105
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800106 cp &= ~PM_IRQF_WRITE;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530107 rc = pm8xxx_writeb(chip->dev,
108 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700109 if (rc)
110 pr_err("Failed Configuring IRQ rc=%d\n", rc);
111
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530112 rc = pm8xxx_readb(chip->dev,
113 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), r);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700114 if (rc)
115 pr_err("Failed reading IRQ rc=%d\n", rc);
116bail:
117 spin_unlock(&chip->pm_irq_lock);
118 return rc;
119}
120
121static int pm8xxx_write_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700122{
123 int rc;
124
125 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530126 rc = pm8xxx_writeb(chip->dev,
127 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700128 if (rc) {
129 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
130 goto bail;
131 }
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800132 /*
133 * Set the write bit here as this could be a unrequested irq
134 * whose PM_IRQF_WRITE bit is not set
135 */
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700136 cp |= PM_IRQF_WRITE;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530137 rc = pm8xxx_writeb(chip->dev,
138 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700139 if (rc)
140 pr_err("Failed Configuring IRQ rc=%d\n", rc);
141bail:
142 spin_unlock(&chip->pm_irq_lock);
143 return rc;
144}
145
146static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
147{
148 int pmirq, irq, i, ret = 0;
149 u8 bits;
150
151 ret = pm8xxx_read_block_irq(chip, block, &bits);
152 if (ret) {
153 pr_err("Failed reading %d block ret=%d", block, ret);
154 return ret;
155 }
156 if (!bits) {
157 pr_err("block bit set in master but no irqs: %d", block);
158 return 0;
159 }
160
161 /* Check IRQ bits */
162 for (i = 0; i < 8; i++) {
163 if (bits & (1 << i)) {
164 pmirq = block * 8 + i;
165 irq = pmirq + chip->irq_base;
166 generic_handle_irq(irq);
167 }
168 }
169 return 0;
170}
171
172static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
173{
174 u8 blockbits;
175 int block_number, i, ret = 0;
176
177 ret = pm8xxx_read_master_irq(chip, master, &blockbits);
178 if (ret) {
179 pr_err("Failed to read master %d ret=%d\n", master, ret);
180 return ret;
181 }
182 if (!blockbits) {
183 pr_err("master bit set in root but no blocks: %d", master);
184 return 0;
185 }
186
187 for (i = 0; i < 8; i++)
188 if (blockbits & (1 << i)) {
189 block_number = master * 8 + i; /* block # */
190 ret |= pm8xxx_irq_block_handler(chip, block_number);
191 }
192 return ret;
193}
194
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700195static irqreturn_t pm8xxx_irq_handler(int irq, void *data)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700196{
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700197 struct pm_irq_chip *chip = data;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700198 u8 root;
199 int i, ret, masters = 0;
200
201 ret = pm8xxx_read_root_irq(chip, &root);
202 if (ret) {
203 pr_err("Can't read root status ret=%d\n", ret);
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700204 return IRQ_HANDLED;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700205 }
206
207 /* on pm8xxx series masters start from bit 1 of the root */
208 masters = root >> 1;
209
210 /* Read allowed masters for blocks. */
211 for (i = 0; i < chip->num_masters; i++)
212 if (masters & (1 << i))
213 pm8xxx_irq_master_handler(chip, i);
214
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700215 return IRQ_HANDLED;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700216}
217
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700218static void pm8xxx_irq_mask(struct irq_data *d)
219{
220 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
221 unsigned int pmirq = d->irq - chip->irq_base;
222 int master, irq_bit;
223 u8 block, config;
224
225 block = pmirq / 8;
226 master = block / 8;
227 irq_bit = pmirq % 8;
228
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800229 if (chip->config[pmirq] == 0) {
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800230 pr_warn("masking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800231 chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
232 }
233
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700234 config = chip->config[pmirq] | PM_IRQF_MASK_ALL;
235 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700236}
237
238static void pm8xxx_irq_mask_ack(struct irq_data *d)
239{
240 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
241 unsigned int pmirq = d->irq - chip->irq_base;
242 int master, irq_bit;
243 u8 block, config;
244
245 block = pmirq / 8;
246 master = block / 8;
247 irq_bit = pmirq % 8;
248
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800249 if (chip->config[pmirq] == 0) {
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800250 pr_warn("mask acking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800251 chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
252 }
253
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700254 config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700255 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700256}
257
258static void pm8xxx_irq_unmask(struct irq_data *d)
259{
260 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
261 unsigned int pmirq = d->irq - chip->irq_base;
262 int master, irq_bit;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700263 u8 block, config, hw_conf;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700264
265 block = pmirq / 8;
266 master = block / 8;
267 irq_bit = pmirq % 8;
268
269 config = chip->config[pmirq];
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700270 pm8xxx_read_config_irq(chip, block, config, &hw_conf);
271 /* check if it is masked */
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700272 if ((hw_conf & PM_IRQF_MASK_ALL) == PM_IRQF_MASK_ALL)
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700273 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700274}
275
276static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
277{
278 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
279 unsigned int pmirq = d->irq - chip->irq_base;
280 int master, irq_bit;
281 u8 block, config;
282
283 block = pmirq / 8;
284 master = block / 8;
285 irq_bit = pmirq % 8;
286
287 chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
288 | PM_IRQF_MASK_ALL;
289 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
290 if (flow_type & IRQF_TRIGGER_RISING)
291 chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
292 if (flow_type & IRQF_TRIGGER_FALLING)
293 chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
294 } else {
295 chip->config[pmirq] |= PM_IRQF_LVL_SEL;
296
297 if (flow_type & IRQF_TRIGGER_HIGH)
298 chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
299 else
300 chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
301 }
302
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800303 /*
304 * The PM_IRQF_WRITE flag serves as an indication that this interrupt
305 * been requested
306 */
307 chip->config[pmirq] |= PM_IRQF_WRITE;
308
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700309 config = chip->config[pmirq] | PM_IRQF_CLR;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700310 return pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700311}
312
313static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
314{
315 return 0;
316}
317
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318static int pm8xxx_irq_read_line(struct irq_data *d)
319{
320 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
321
322 return pm8xxx_get_irq_stat(chip, d->irq);
323}
324
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700325static struct irq_chip pm8xxx_irq_chip = {
326 .name = "pm8xxx",
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700327 .irq_mask = pm8xxx_irq_mask,
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700328 .irq_mask_ack = pm8xxx_irq_mask_ack,
329 .irq_unmask = pm8xxx_irq_unmask,
330 .irq_set_type = pm8xxx_irq_set_type,
331 .irq_set_wake = pm8xxx_irq_set_wake,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332 .irq_read_line = pm8xxx_irq_read_line,
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700333 .flags = IRQCHIP_MASK_ON_SUSPEND,
334};
335
336/**
337 * pm8xxx_get_irq_stat - get the status of the irq line
338 * @chip: pointer to identify a pmic irq controller
339 * @irq: the irq number
340 *
341 * The pm8xxx gpio and mpp rely on the interrupt block to read
342 * the values on their pins. This function is to facilitate reading
343 * the status of a gpio or an mpp line. The caller has to convert the
344 * gpio number to irq number.
345 *
346 * RETURNS:
347 * an int indicating the value read on that line
348 */
349int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
350{
351 int pmirq, rc;
352 u8 block, bits, bit;
353 unsigned long flags;
354
355 if (chip == NULL || irq < chip->irq_base ||
356 irq >= chip->irq_base + chip->num_irqs)
357 return -EINVAL;
358
359 pmirq = irq - chip->irq_base;
360
361 block = pmirq / 8;
362 bit = pmirq % 8;
363
364 spin_lock_irqsave(&chip->pm_irq_lock, flags);
365
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530366 rc = pm8xxx_writeb(chip->dev,
367 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), block);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700368 if (rc) {
369 pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
370 irq, pmirq, block, rc);
371 goto bail_out;
372 }
373
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530374 rc = pm8xxx_readb(chip->dev,
375 SSBI_REG_ADDR_IRQ_RT_STATUS(chip->base_addr), &bits);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700376 if (rc) {
377 pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
378 irq, pmirq, block, rc);
379 goto bail_out;
380 }
381
382 rc = (bits & (1 << bit)) ? 1 : 0;
383
384bail_out:
385 spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
386
387 return rc;
388}
389EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
390
391struct pm_irq_chip * __devinit pm8xxx_irq_init(struct device *dev,
392 const struct pm8xxx_irq_platform_data *pdata)
393{
394 struct pm_irq_chip *chip;
395 int devirq, rc;
396 unsigned int pmirq;
397
398 if (!pdata) {
399 pr_err("No platform data\n");
400 return ERR_PTR(-EINVAL);
401 }
402
403 devirq = pdata->devirq;
404 if (devirq < 0) {
405 pr_err("missing devirq\n");
406 rc = devirq;
407 return ERR_PTR(-EINVAL);
408 }
409
410 chip = kzalloc(sizeof(struct pm_irq_chip)
411 + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
412 if (!chip) {
413 pr_err("Cannot alloc pm_irq_chip struct\n");
414 return ERR_PTR(-EINVAL);
415 }
416
417 chip->dev = dev;
418 chip->devirq = devirq;
419 chip->irq_base = pdata->irq_base;
420 chip->num_irqs = pdata->irq_cdata.nirqs;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530421 chip->base_addr = pdata->irq_cdata.base_addr;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700422 chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
423 chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
424 spin_lock_init(&chip->pm_irq_lock);
425
426 for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
427 irq_set_chip_and_handler(chip->irq_base + pmirq,
428 &pm8xxx_irq_chip,
429 handle_level_irq);
430 irq_set_chip_data(chip->irq_base + pmirq, chip);
431#ifdef CONFIG_ARM
432 set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
433#else
434 irq_set_noprobe(chip->irq_base + pmirq);
435#endif
436 }
437
Jay Chokshi35d12f42011-09-23 17:46:00 -0700438 if (devirq != 0) {
439 rc = request_irq(devirq, pm8xxx_irq_handler,
440 pdata->irq_trigger_flag,
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700441 "pm8xxx_usr_irq", chip);
Jay Chokshi35d12f42011-09-23 17:46:00 -0700442 if (rc) {
443 pr_err("failed to request_irq for %d rc=%d\n",
444 devirq, rc);
445 } else {
446 irq_set_irq_wake(devirq, 1);
447 }
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700448 }
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700449
450 return chip;
451}
452
Stephen Boyd7a05bd72012-02-21 01:18:46 -0800453int pm8xxx_irq_exit(struct pm_irq_chip *chip)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700454{
455 irq_set_chained_handler(chip->devirq, NULL);
456 kfree(chip);
457 return 0;
458}