blob: ac39e800e561bebcf8aa5df4e5801de0eab68670 [file] [log] [blame]
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -07001/*
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -08002 * Copyright (c) 2011-2012, Code Aurora Forum. 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
16#include <linux/err.h>
17#include <linux/interrupt.h>
18#include <linux/irq.h>
19#include <linux/kernel.h>
20#include <linux/mfd/pm8xxx/core.h>
21#include <linux/mfd/pm8xxx/irq.h>
22#include <linux/platform_device.h>
23#include <linux/slab.h>
24
25/* PMIC8xxx IRQ */
26
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053027#define SSBI_REG_ADDR_IRQ_ROOT(base) (base + 0)
28#define SSBI_REG_ADDR_IRQ_M_STATUS1(base) (base + 1)
29#define SSBI_REG_ADDR_IRQ_M_STATUS2(base) (base + 2)
30#define SSBI_REG_ADDR_IRQ_M_STATUS3(base) (base + 3)
31#define SSBI_REG_ADDR_IRQ_M_STATUS4(base) (base + 4)
32#define SSBI_REG_ADDR_IRQ_BLK_SEL(base) (base + 5)
33#define SSBI_REG_ADDR_IRQ_IT_STATUS(base) (base + 6)
34#define SSBI_REG_ADDR_IRQ_CONFIG(base) (base + 7)
35#define SSBI_REG_ADDR_IRQ_RT_STATUS(base) (base + 8)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070036
37#define PM_IRQF_LVL_SEL 0x01 /* level select */
38#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
39#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
40#define PM_IRQF_CLR 0x08 /* clear interrupt */
41#define PM_IRQF_BITS_MASK 0x70
42#define PM_IRQF_BITS_SHIFT 4
43#define PM_IRQF_WRITE 0x80
44
45#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
46 PM_IRQF_MASK_RE)
47
48struct pm_irq_chip {
49 struct device *dev;
50 spinlock_t pm_irq_lock;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053051 unsigned int base_addr;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070052 unsigned int devirq;
53 unsigned int irq_base;
54 unsigned int num_irqs;
55 unsigned int num_blocks;
56 unsigned int num_masters;
57 u8 config[0];
58};
59
60static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
61{
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053062 return pm8xxx_readb(chip->dev,
63 SSBI_REG_ADDR_IRQ_ROOT(chip->base_addr), rp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070064}
65
66static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
67{
68 return pm8xxx_readb(chip->dev,
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053069 SSBI_REG_ADDR_IRQ_M_STATUS1(chip->base_addr) + m, bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070070}
71
72static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
73{
74 int rc;
75
76 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053077 rc = pm8xxx_writeb(chip->dev,
78 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070079 if (rc) {
80 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
81 goto bail;
82 }
83
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053084 rc = pm8xxx_readb(chip->dev,
85 SSBI_REG_ADDR_IRQ_IT_STATUS(chip->base_addr), ip);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -070086 if (rc)
87 pr_err("Failed Reading Status rc=%d\n", rc);
88bail:
89 spin_unlock(&chip->pm_irq_lock);
90 return rc;
91}
92
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -070093static int pm8xxx_read_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp, u8 *r)
94{
95 int rc;
96
97 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +053098 rc = pm8xxx_writeb(chip->dev,
99 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700100 if (rc) {
101 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
102 goto bail;
103 }
104
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800105 cp &= ~PM_IRQF_WRITE;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530106 rc = pm8xxx_writeb(chip->dev,
107 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700108 if (rc)
109 pr_err("Failed Configuring IRQ rc=%d\n", rc);
110
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530111 rc = pm8xxx_readb(chip->dev,
112 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), r);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700113 if (rc)
114 pr_err("Failed reading IRQ rc=%d\n", rc);
115bail:
116 spin_unlock(&chip->pm_irq_lock);
117 return rc;
118}
119
120static int pm8xxx_write_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700121{
122 int rc;
123
124 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530125 rc = pm8xxx_writeb(chip->dev,
126 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700127 if (rc) {
128 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
129 goto bail;
130 }
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800131 /*
132 * Set the write bit here as this could be a unrequested irq
133 * whose PM_IRQF_WRITE bit is not set
134 */
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700135 cp |= PM_IRQF_WRITE;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530136 rc = pm8xxx_writeb(chip->dev,
137 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700138 if (rc)
139 pr_err("Failed Configuring IRQ rc=%d\n", rc);
140bail:
141 spin_unlock(&chip->pm_irq_lock);
142 return rc;
143}
144
145static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
146{
147 int pmirq, irq, i, ret = 0;
148 u8 bits;
149
150 ret = pm8xxx_read_block_irq(chip, block, &bits);
151 if (ret) {
152 pr_err("Failed reading %d block ret=%d", block, ret);
153 return ret;
154 }
155 if (!bits) {
156 pr_err("block bit set in master but no irqs: %d", block);
157 return 0;
158 }
159
160 /* Check IRQ bits */
161 for (i = 0; i < 8; i++) {
162 if (bits & (1 << i)) {
163 pmirq = block * 8 + i;
164 irq = pmirq + chip->irq_base;
165 generic_handle_irq(irq);
166 }
167 }
168 return 0;
169}
170
171static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
172{
173 u8 blockbits;
174 int block_number, i, ret = 0;
175
176 ret = pm8xxx_read_master_irq(chip, master, &blockbits);
177 if (ret) {
178 pr_err("Failed to read master %d ret=%d\n", master, ret);
179 return ret;
180 }
181 if (!blockbits) {
182 pr_err("master bit set in root but no blocks: %d", master);
183 return 0;
184 }
185
186 for (i = 0; i < 8; i++)
187 if (blockbits & (1 << i)) {
188 block_number = master * 8 + i; /* block # */
189 ret |= pm8xxx_irq_block_handler(chip, block_number);
190 }
191 return ret;
192}
193
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700194static irqreturn_t pm8xxx_irq_handler(int irq, void *data)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700195{
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700196 struct pm_irq_chip *chip = data;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700197 u8 root;
198 int i, ret, masters = 0;
199
200 ret = pm8xxx_read_root_irq(chip, &root);
201 if (ret) {
202 pr_err("Can't read root status ret=%d\n", ret);
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700203 return IRQ_HANDLED;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700204 }
205
206 /* on pm8xxx series masters start from bit 1 of the root */
207 masters = root >> 1;
208
209 /* Read allowed masters for blocks. */
210 for (i = 0; i < chip->num_masters; i++)
211 if (masters & (1 << i))
212 pm8xxx_irq_master_handler(chip, i);
213
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700214 return IRQ_HANDLED;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700215}
216
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700217static void pm8xxx_irq_mask(struct irq_data *d)
218{
219 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
220 unsigned int pmirq = d->irq - chip->irq_base;
221 int master, irq_bit;
222 u8 block, config;
223
224 block = pmirq / 8;
225 master = block / 8;
226 irq_bit = pmirq % 8;
227
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800228 if (chip->config[pmirq] == 0) {
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800229 pr_warn("masking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800230 chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
231 }
232
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700233 config = chip->config[pmirq] | PM_IRQF_MASK_ALL;
234 pm8xxx_write_config_irq(chip, block, config);
235}
236
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700237static void pm8xxx_irq_mask_ack(struct irq_data *d)
238{
239 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
240 unsigned int pmirq = d->irq - chip->irq_base;
241 int master, irq_bit;
242 u8 block, config;
243
244 block = pmirq / 8;
245 master = block / 8;
246 irq_bit = pmirq % 8;
247
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800248 if (chip->config[pmirq] == 0) {
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800249 pr_warn("mask acking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
Abhijeet Dharmapurikar9d10de32012-01-26 20:40:33 -0800250 chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
251 }
252
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700253 config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700254 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700255}
256
257static void pm8xxx_irq_unmask(struct irq_data *d)
258{
259 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
260 unsigned int pmirq = d->irq - chip->irq_base;
261 int master, irq_bit;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700262 u8 block, config, hw_conf;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700263
264 block = pmirq / 8;
265 master = block / 8;
266 irq_bit = pmirq % 8;
267
268 config = chip->config[pmirq];
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700269 pm8xxx_read_config_irq(chip, block, config, &hw_conf);
270 /* check if it is masked */
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700271 if ((hw_conf & PM_IRQF_MASK_ALL) == PM_IRQF_MASK_ALL)
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700272 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700273}
274
275static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
276{
277 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
278 unsigned int pmirq = d->irq - chip->irq_base;
279 int master, irq_bit;
280 u8 block, config;
281
282 block = pmirq / 8;
283 master = block / 8;
284 irq_bit = pmirq % 8;
285
286 chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
287 | PM_IRQF_MASK_ALL;
288 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
289 if (flow_type & IRQF_TRIGGER_RISING)
290 chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
291 if (flow_type & IRQF_TRIGGER_FALLING)
292 chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
293 } else {
294 chip->config[pmirq] |= PM_IRQF_LVL_SEL;
295
296 if (flow_type & IRQF_TRIGGER_HIGH)
297 chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
298 else
299 chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
300 }
301
Abhijeet Dharmapurikar442ec042012-02-13 10:44:01 -0800302 /*
303 * The PM_IRQF_WRITE flag serves as an indication that this interrupt
304 * been requested
305 */
306 chip->config[pmirq] |= PM_IRQF_WRITE;
307
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700308 config = chip->config[pmirq] | PM_IRQF_CLR;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700309 return pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700310}
311
312static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
313{
314 return 0;
315}
316
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317static int pm8xxx_irq_read_line(struct irq_data *d)
318{
319 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
320
321 return pm8xxx_get_irq_stat(chip, d->irq);
322}
323
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700324static struct irq_chip pm8xxx_irq_chip = {
325 .name = "pm8xxx",
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700326 .irq_mask = pm8xxx_irq_mask,
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700327 .irq_mask_ack = pm8xxx_irq_mask_ack,
328 .irq_unmask = pm8xxx_irq_unmask,
329 .irq_set_type = pm8xxx_irq_set_type,
330 .irq_set_wake = pm8xxx_irq_set_wake,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 .irq_read_line = pm8xxx_irq_read_line,
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700332 .flags = IRQCHIP_MASK_ON_SUSPEND,
333};
334
335/**
336 * pm8xxx_get_irq_stat - get the status of the irq line
337 * @chip: pointer to identify a pmic irq controller
338 * @irq: the irq number
339 *
340 * The pm8xxx gpio and mpp rely on the interrupt block to read
341 * the values on their pins. This function is to facilitate reading
342 * the status of a gpio or an mpp line. The caller has to convert the
343 * gpio number to irq number.
344 *
345 * RETURNS:
346 * an int indicating the value read on that line
347 */
348int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
349{
350 int pmirq, rc;
351 u8 block, bits, bit;
352 unsigned long flags;
353
354 if (chip == NULL || irq < chip->irq_base ||
355 irq >= chip->irq_base + chip->num_irqs)
356 return -EINVAL;
357
358 pmirq = irq - chip->irq_base;
359
360 block = pmirq / 8;
361 bit = pmirq % 8;
362
363 spin_lock_irqsave(&chip->pm_irq_lock, flags);
364
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530365 rc = pm8xxx_writeb(chip->dev,
366 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), block);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700367 if (rc) {
368 pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
369 irq, pmirq, block, rc);
370 goto bail_out;
371 }
372
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530373 rc = pm8xxx_readb(chip->dev,
374 SSBI_REG_ADDR_IRQ_RT_STATUS(chip->base_addr), &bits);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700375 if (rc) {
376 pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
377 irq, pmirq, block, rc);
378 goto bail_out;
379 }
380
381 rc = (bits & (1 << bit)) ? 1 : 0;
382
383bail_out:
384 spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
385
386 return rc;
387}
388EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
389
390struct pm_irq_chip * __devinit pm8xxx_irq_init(struct device *dev,
391 const struct pm8xxx_irq_platform_data *pdata)
392{
393 struct pm_irq_chip *chip;
394 int devirq, rc;
395 unsigned int pmirq;
396
397 if (!pdata) {
398 pr_err("No platform data\n");
399 return ERR_PTR(-EINVAL);
400 }
401
402 devirq = pdata->devirq;
403 if (devirq < 0) {
404 pr_err("missing devirq\n");
405 rc = devirq;
406 return ERR_PTR(-EINVAL);
407 }
408
409 chip = kzalloc(sizeof(struct pm_irq_chip)
410 + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
411 if (!chip) {
412 pr_err("Cannot alloc pm_irq_chip struct\n");
413 return ERR_PTR(-EINVAL);
414 }
415
416 chip->dev = dev;
417 chip->devirq = devirq;
418 chip->irq_base = pdata->irq_base;
419 chip->num_irqs = pdata->irq_cdata.nirqs;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530420 chip->base_addr = pdata->irq_cdata.base_addr;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700421 chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
422 chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
423 spin_lock_init(&chip->pm_irq_lock);
424
425 for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
426 irq_set_chip_and_handler(chip->irq_base + pmirq,
427 &pm8xxx_irq_chip,
428 handle_level_irq);
429 irq_set_chip_data(chip->irq_base + pmirq, chip);
430#ifdef CONFIG_ARM
431 set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
432#else
433 irq_set_noprobe(chip->irq_base + pmirq);
434#endif
435 }
436
Jay Chokshi35d12f42011-09-23 17:46:00 -0700437 if (devirq != 0) {
438 rc = request_irq(devirq, pm8xxx_irq_handler,
439 pdata->irq_trigger_flag,
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700440 "pm8xxx_usr_irq", chip);
Jay Chokshi35d12f42011-09-23 17:46:00 -0700441 if (rc) {
442 pr_err("failed to request_irq for %d rc=%d\n",
443 devirq, rc);
444 } else {
445 irq_set_irq_wake(devirq, 1);
446 }
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700447 }
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700448
449 return chip;
450}
451
Stephen Boyd7a05bd72012-02-21 01:18:46 -0800452int pm8xxx_irq_exit(struct pm_irq_chip *chip)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700453{
454 irq_set_chained_handler(chip->devirq, NULL);
455 kfree(chip);
456 return 0;
457}