blob: 5864b85e67d0caec66d1615d24e5ae790d8bb8d2 [file] [log] [blame]
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
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
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530105 rc = pm8xxx_writeb(chip->dev,
106 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700107 if (rc)
108 pr_err("Failed Configuring IRQ rc=%d\n", rc);
109
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530110 rc = pm8xxx_readb(chip->dev,
111 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), r);
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700112 if (rc)
113 pr_err("Failed reading IRQ rc=%d\n", rc);
114bail:
115 spin_unlock(&chip->pm_irq_lock);
116 return rc;
117}
118
119static int pm8xxx_write_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700120{
121 int rc;
122
123 spin_lock(&chip->pm_irq_lock);
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530124 rc = pm8xxx_writeb(chip->dev,
125 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), bp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700126 if (rc) {
127 pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
128 goto bail;
129 }
130
131 cp |= PM_IRQF_WRITE;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530132 rc = pm8xxx_writeb(chip->dev,
133 SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700134 if (rc)
135 pr_err("Failed Configuring IRQ rc=%d\n", rc);
136bail:
137 spin_unlock(&chip->pm_irq_lock);
138 return rc;
139}
140
141static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
142{
143 int pmirq, irq, i, ret = 0;
144 u8 bits;
145
146 ret = pm8xxx_read_block_irq(chip, block, &bits);
147 if (ret) {
148 pr_err("Failed reading %d block ret=%d", block, ret);
149 return ret;
150 }
151 if (!bits) {
152 pr_err("block bit set in master but no irqs: %d", block);
153 return 0;
154 }
155
156 /* Check IRQ bits */
157 for (i = 0; i < 8; i++) {
158 if (bits & (1 << i)) {
159 pmirq = block * 8 + i;
160 irq = pmirq + chip->irq_base;
161 generic_handle_irq(irq);
162 }
163 }
164 return 0;
165}
166
167static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
168{
169 u8 blockbits;
170 int block_number, i, ret = 0;
171
172 ret = pm8xxx_read_master_irq(chip, master, &blockbits);
173 if (ret) {
174 pr_err("Failed to read master %d ret=%d\n", master, ret);
175 return ret;
176 }
177 if (!blockbits) {
178 pr_err("master bit set in root but no blocks: %d", master);
179 return 0;
180 }
181
182 for (i = 0; i < 8; i++)
183 if (blockbits & (1 << i)) {
184 block_number = master * 8 + i; /* block # */
185 ret |= pm8xxx_irq_block_handler(chip, block_number);
186 }
187 return ret;
188}
189
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700190static irqreturn_t pm8xxx_irq_handler(int irq, void *data)
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700191{
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700192 struct pm_irq_chip *chip = data;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700193 u8 root;
194 int i, ret, masters = 0;
195
196 ret = pm8xxx_read_root_irq(chip, &root);
197 if (ret) {
198 pr_err("Can't read root status ret=%d\n", ret);
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700199 return IRQ_HANDLED;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700200 }
201
202 /* on pm8xxx series masters start from bit 1 of the root */
203 masters = root >> 1;
204
205 /* Read allowed masters for blocks. */
206 for (i = 0; i < chip->num_masters; i++)
207 if (masters & (1 << i))
208 pm8xxx_irq_master_handler(chip, i);
209
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700210 return IRQ_HANDLED;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700211}
212
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700213static void pm8xxx_irq_mask(struct irq_data *d)
214{
215 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
216 unsigned int pmirq = d->irq - chip->irq_base;
217 int master, irq_bit;
218 u8 block, config;
219
220 block = pmirq / 8;
221 master = block / 8;
222 irq_bit = pmirq % 8;
223
224 config = chip->config[pmirq] | PM_IRQF_MASK_ALL;
225 pm8xxx_write_config_irq(chip, block, config);
226}
227
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700228static void pm8xxx_irq_mask_ack(struct irq_data *d)
229{
230 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
231 unsigned int pmirq = d->irq - chip->irq_base;
232 int master, irq_bit;
233 u8 block, config;
234
235 block = pmirq / 8;
236 master = block / 8;
237 irq_bit = pmirq % 8;
238
239 config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700240 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700241}
242
243static void pm8xxx_irq_unmask(struct irq_data *d)
244{
245 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
246 unsigned int pmirq = d->irq - chip->irq_base;
247 int master, irq_bit;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700248 u8 block, config, hw_conf;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700249
250 block = pmirq / 8;
251 master = block / 8;
252 irq_bit = pmirq % 8;
253
254 config = chip->config[pmirq];
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700255 pm8xxx_read_config_irq(chip, block, config, &hw_conf);
256 /* check if it is masked */
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700257 if ((hw_conf & PM_IRQF_MASK_ALL) == PM_IRQF_MASK_ALL)
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700258 pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700259}
260
261static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
262{
263 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
264 unsigned int pmirq = d->irq - chip->irq_base;
265 int master, irq_bit;
266 u8 block, config;
267
268 block = pmirq / 8;
269 master = block / 8;
270 irq_bit = pmirq % 8;
271
272 chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
273 | PM_IRQF_MASK_ALL;
274 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
275 if (flow_type & IRQF_TRIGGER_RISING)
276 chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
277 if (flow_type & IRQF_TRIGGER_FALLING)
278 chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
279 } else {
280 chip->config[pmirq] |= PM_IRQF_LVL_SEL;
281
282 if (flow_type & IRQF_TRIGGER_HIGH)
283 chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
284 else
285 chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
286 }
287
288 config = chip->config[pmirq] | PM_IRQF_CLR;
Abhijeet Dharmapurikar930bf7b2011-07-25 12:23:58 -0700289 return pm8xxx_write_config_irq(chip, block, config);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700290}
291
292static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
293{
294 return 0;
295}
296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297static int pm8xxx_irq_read_line(struct irq_data *d)
298{
299 struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
300
301 return pm8xxx_get_irq_stat(chip, d->irq);
302}
303
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700304static struct irq_chip pm8xxx_irq_chip = {
305 .name = "pm8xxx",
Abhijeet Dharmapurikar32467392011-08-18 16:51:50 -0700306 .irq_mask = pm8xxx_irq_mask,
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700307 .irq_mask_ack = pm8xxx_irq_mask_ack,
308 .irq_unmask = pm8xxx_irq_unmask,
309 .irq_set_type = pm8xxx_irq_set_type,
310 .irq_set_wake = pm8xxx_irq_set_wake,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 .irq_read_line = pm8xxx_irq_read_line,
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700312 .flags = IRQCHIP_MASK_ON_SUSPEND,
313};
314
315/**
316 * pm8xxx_get_irq_stat - get the status of the irq line
317 * @chip: pointer to identify a pmic irq controller
318 * @irq: the irq number
319 *
320 * The pm8xxx gpio and mpp rely on the interrupt block to read
321 * the values on their pins. This function is to facilitate reading
322 * the status of a gpio or an mpp line. The caller has to convert the
323 * gpio number to irq number.
324 *
325 * RETURNS:
326 * an int indicating the value read on that line
327 */
328int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
329{
330 int pmirq, rc;
331 u8 block, bits, bit;
332 unsigned long flags;
333
334 if (chip == NULL || irq < chip->irq_base ||
335 irq >= chip->irq_base + chip->num_irqs)
336 return -EINVAL;
337
338 pmirq = irq - chip->irq_base;
339
340 block = pmirq / 8;
341 bit = pmirq % 8;
342
343 spin_lock_irqsave(&chip->pm_irq_lock, flags);
344
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530345 rc = pm8xxx_writeb(chip->dev,
346 SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), block);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700347 if (rc) {
348 pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
349 irq, pmirq, block, rc);
350 goto bail_out;
351 }
352
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530353 rc = pm8xxx_readb(chip->dev,
354 SSBI_REG_ADDR_IRQ_RT_STATUS(chip->base_addr), &bits);
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700355 if (rc) {
356 pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
357 irq, pmirq, block, rc);
358 goto bail_out;
359 }
360
361 rc = (bits & (1 << bit)) ? 1 : 0;
362
363bail_out:
364 spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
365
366 return rc;
367}
368EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
369
370struct pm_irq_chip * __devinit pm8xxx_irq_init(struct device *dev,
371 const struct pm8xxx_irq_platform_data *pdata)
372{
373 struct pm_irq_chip *chip;
374 int devirq, rc;
375 unsigned int pmirq;
376
377 if (!pdata) {
378 pr_err("No platform data\n");
379 return ERR_PTR(-EINVAL);
380 }
381
382 devirq = pdata->devirq;
383 if (devirq < 0) {
384 pr_err("missing devirq\n");
385 rc = devirq;
386 return ERR_PTR(-EINVAL);
387 }
388
389 chip = kzalloc(sizeof(struct pm_irq_chip)
390 + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
391 if (!chip) {
392 pr_err("Cannot alloc pm_irq_chip struct\n");
393 return ERR_PTR(-EINVAL);
394 }
395
396 chip->dev = dev;
397 chip->devirq = devirq;
398 chip->irq_base = pdata->irq_base;
399 chip->num_irqs = pdata->irq_cdata.nirqs;
Anirudh Ghayalca42c7de2011-11-21 10:42:07 +0530400 chip->base_addr = pdata->irq_cdata.base_addr;
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700401 chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
402 chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
403 spin_lock_init(&chip->pm_irq_lock);
404
405 for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
406 irq_set_chip_and_handler(chip->irq_base + pmirq,
407 &pm8xxx_irq_chip,
408 handle_level_irq);
409 irq_set_chip_data(chip->irq_base + pmirq, chip);
410#ifdef CONFIG_ARM
411 set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
412#else
413 irq_set_noprobe(chip->irq_base + pmirq);
414#endif
415 }
416
Jay Chokshi35d12f42011-09-23 17:46:00 -0700417 if (devirq != 0) {
418 rc = request_irq(devirq, pm8xxx_irq_handler,
419 pdata->irq_trigger_flag,
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700420 "pm8xxx_usr_irq", chip);
Jay Chokshi35d12f42011-09-23 17:46:00 -0700421 if (rc) {
422 pr_err("failed to request_irq for %d rc=%d\n",
423 devirq, rc);
424 } else {
425 irq_set_irq_wake(devirq, 1);
426 }
Abhijeet Dharmapurikar636585d2011-08-18 16:14:10 -0700427 }
Abhijeet Dharmapurikarc013f0a2011-04-05 14:40:53 -0700428
429 return chip;
430}
431
432int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
433{
434 irq_set_chained_handler(chip->devirq, NULL);
435 kfree(chip);
436 return 0;
437}