diff options
author | Stephen Boyd <sboyd@codeaurora.org> | 2014-02-26 13:59:21 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2014-03-19 04:58:29 -0400 |
commit | dc1a95ccaa1158948bbc6648d6dadc534a30ed92 (patch) | |
tree | dd83329603148ec2f223f41eb102381ac13c272a /drivers/mfd | |
parent | cced3548babc6d5338261f1b43ead62d93448567 (diff) |
mfd: pm8921: Migrate to irqdomains
Convert this driver to use irqdomains so that the PMIC's child
devices can be converted to devicetree.
Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 1 | ||||
-rw-r--r-- | drivers/mfd/pm8921-core.c | 198 |
2 files changed, 76 insertions, 123 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 650c90f814ff..833d2c884437 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -479,6 +479,7 @@ config MFD_PM8XXX | |||
479 | config MFD_PM8921_CORE | 479 | config MFD_PM8921_CORE |
480 | tristate "Qualcomm PM8921 PMIC chip" | 480 | tristate "Qualcomm PM8921 PMIC chip" |
481 | depends on (ARCH_MSM || HEXAGON) | 481 | depends on (ARCH_MSM || HEXAGON) |
482 | select IRQ_DOMAIN | ||
482 | select MFD_CORE | 483 | select MFD_CORE |
483 | select MFD_PM8XXX | 484 | select MFD_PM8XXX |
484 | help | 485 | help |
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c index 9ddc31f7a71d..c25e7dae150b 100644 --- a/drivers/mfd/pm8921-core.c +++ b/drivers/mfd/pm8921-core.c | |||
@@ -17,15 +17,15 @@ | |||
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | #include <linux/irqchip/chained_irq.h> | 18 | #include <linux/irqchip/chained_irq.h> |
19 | #include <linux/irq.h> | 19 | #include <linux/irq.h> |
20 | #include <linux/irqdomain.h> | ||
20 | #include <linux/module.h> | 21 | #include <linux/module.h> |
21 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
22 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
23 | #include <linux/err.h> | 24 | #include <linux/err.h> |
24 | #include <linux/ssbi.h> | 25 | #include <linux/ssbi.h> |
26 | #include <linux/of_platform.h> | ||
25 | #include <linux/mfd/core.h> | 27 | #include <linux/mfd/core.h> |
26 | #include <linux/mfd/pm8xxx/pm8921.h> | ||
27 | #include <linux/mfd/pm8xxx/core.h> | 28 | #include <linux/mfd/pm8xxx/core.h> |
28 | #include <linux/mfd/pm8xxx/irq.h> | ||
29 | 29 | ||
30 | #define SSBI_REG_ADDR_IRQ_BASE 0x1BB | 30 | #define SSBI_REG_ADDR_IRQ_BASE 0x1BB |
31 | 31 | ||
@@ -53,11 +53,12 @@ | |||
53 | #define REG_HWREV 0x002 /* PMIC4 revision */ | 53 | #define REG_HWREV 0x002 /* PMIC4 revision */ |
54 | #define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */ | 54 | #define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */ |
55 | 55 | ||
56 | #define PM8921_NR_IRQS 256 | ||
57 | |||
56 | struct pm_irq_chip { | 58 | struct pm_irq_chip { |
57 | struct device *dev; | 59 | struct device *dev; |
58 | spinlock_t pm_irq_lock; | 60 | spinlock_t pm_irq_lock; |
59 | unsigned int devirq; | 61 | struct irq_domain *irqdomain; |
60 | unsigned int irq_base; | ||
61 | unsigned int num_irqs; | 62 | unsigned int num_irqs; |
62 | unsigned int num_blocks; | 63 | unsigned int num_blocks; |
63 | unsigned int num_masters; | 64 | unsigned int num_masters; |
@@ -138,7 +139,7 @@ static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block) | |||
138 | for (i = 0; i < 8; i++) { | 139 | for (i = 0; i < 8; i++) { |
139 | if (bits & (1 << i)) { | 140 | if (bits & (1 << i)) { |
140 | pmirq = block * 8 + i; | 141 | pmirq = block * 8 + i; |
141 | irq = pmirq + chip->irq_base; | 142 | irq = irq_find_mapping(chip->irqdomain, pmirq); |
142 | generic_handle_irq(irq); | 143 | generic_handle_irq(irq); |
143 | } | 144 | } |
144 | } | 145 | } |
@@ -197,12 +198,11 @@ static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc) | |||
197 | static void pm8xxx_irq_mask_ack(struct irq_data *d) | 198 | static void pm8xxx_irq_mask_ack(struct irq_data *d) |
198 | { | 199 | { |
199 | struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); | 200 | struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); |
200 | unsigned int pmirq = d->irq - chip->irq_base; | 201 | unsigned int pmirq = irqd_to_hwirq(d); |
201 | int master, irq_bit; | 202 | int irq_bit; |
202 | u8 block, config; | 203 | u8 block, config; |
203 | 204 | ||
204 | block = pmirq / 8; | 205 | block = pmirq / 8; |
205 | master = block / 8; | ||
206 | irq_bit = pmirq % 8; | 206 | irq_bit = pmirq % 8; |
207 | 207 | ||
208 | config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; | 208 | config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR; |
@@ -212,12 +212,11 @@ static void pm8xxx_irq_mask_ack(struct irq_data *d) | |||
212 | static void pm8xxx_irq_unmask(struct irq_data *d) | 212 | static void pm8xxx_irq_unmask(struct irq_data *d) |
213 | { | 213 | { |
214 | struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); | 214 | struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); |
215 | unsigned int pmirq = d->irq - chip->irq_base; | 215 | unsigned int pmirq = irqd_to_hwirq(d); |
216 | int master, irq_bit; | 216 | int irq_bit; |
217 | u8 block, config; | 217 | u8 block, config; |
218 | 218 | ||
219 | block = pmirq / 8; | 219 | block = pmirq / 8; |
220 | master = block / 8; | ||
221 | irq_bit = pmirq % 8; | 220 | irq_bit = pmirq % 8; |
222 | 221 | ||
223 | config = chip->config[pmirq]; | 222 | config = chip->config[pmirq]; |
@@ -227,12 +226,11 @@ static void pm8xxx_irq_unmask(struct irq_data *d) | |||
227 | static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) | 226 | static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) |
228 | { | 227 | { |
229 | struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); | 228 | struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); |
230 | unsigned int pmirq = d->irq - chip->irq_base; | 229 | unsigned int pmirq = irqd_to_hwirq(d); |
231 | int master, irq_bit; | 230 | int irq_bit; |
232 | u8 block, config; | 231 | u8 block, config; |
233 | 232 | ||
234 | block = pmirq / 8; | 233 | block = pmirq / 8; |
235 | master = block / 8; | ||
236 | irq_bit = pmirq % 8; | 234 | irq_bit = pmirq % 8; |
237 | 235 | ||
238 | chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) | 236 | chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT) |
@@ -287,12 +285,9 @@ static int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq) | |||
287 | int pmirq, rc; | 285 | int pmirq, rc; |
288 | u8 block, bits, bit; | 286 | u8 block, bits, bit; |
289 | unsigned long flags; | 287 | unsigned long flags; |
288 | struct irq_data *irq_data = irq_get_irq_data(irq); | ||
290 | 289 | ||
291 | if (chip == NULL || irq < chip->irq_base || | 290 | pmirq = irq_data->hwirq; |
292 | irq >= chip->irq_base + chip->num_irqs) | ||
293 | return -EINVAL; | ||
294 | |||
295 | pmirq = irq - chip->irq_base; | ||
296 | 291 | ||
297 | block = pmirq / 8; | 292 | block = pmirq / 8; |
298 | bit = pmirq % 8; | 293 | bit = pmirq % 8; |
@@ -321,67 +316,29 @@ bail_out: | |||
321 | return rc; | 316 | return rc; |
322 | } | 317 | } |
323 | 318 | ||
324 | static struct pm_irq_chip *pm8xxx_irq_init(struct device *dev, | 319 | static struct lock_class_key pm8xxx_irq_lock_class; |
325 | const struct pm8xxx_irq_platform_data *pdata) | ||
326 | { | ||
327 | struct pm_irq_chip *chip; | ||
328 | int devirq, rc; | ||
329 | unsigned int pmirq; | ||
330 | |||
331 | if (!pdata) { | ||
332 | pr_err("No platform data\n"); | ||
333 | return ERR_PTR(-EINVAL); | ||
334 | } | ||
335 | 320 | ||
336 | devirq = pdata->devirq; | 321 | static int pm8xxx_irq_domain_map(struct irq_domain *d, unsigned int irq, |
337 | if (devirq < 0) { | 322 | irq_hw_number_t hwirq) |
338 | pr_err("missing devirq\n"); | 323 | { |
339 | rc = devirq; | 324 | struct pm_irq_chip *chip = d->host_data; |
340 | return ERR_PTR(-EINVAL); | ||
341 | } | ||
342 | |||
343 | chip = kzalloc(sizeof(struct pm_irq_chip) | ||
344 | + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL); | ||
345 | if (!chip) { | ||
346 | pr_err("Cannot alloc pm_irq_chip struct\n"); | ||
347 | return ERR_PTR(-EINVAL); | ||
348 | } | ||
349 | |||
350 | chip->dev = dev; | ||
351 | chip->devirq = devirq; | ||
352 | chip->irq_base = pdata->irq_base; | ||
353 | chip->num_irqs = pdata->irq_cdata.nirqs; | ||
354 | chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); | ||
355 | chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); | ||
356 | spin_lock_init(&chip->pm_irq_lock); | ||
357 | 325 | ||
358 | for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) { | 326 | irq_set_lockdep_class(irq, &pm8xxx_irq_lock_class); |
359 | irq_set_chip_and_handler(chip->irq_base + pmirq, | 327 | irq_set_chip_and_handler(irq, &pm8xxx_irq_chip, handle_level_irq); |
360 | &pm8xxx_irq_chip, | 328 | irq_set_chip_data(irq, chip); |
361 | handle_level_irq); | ||
362 | irq_set_chip_data(chip->irq_base + pmirq, chip); | ||
363 | #ifdef CONFIG_ARM | 329 | #ifdef CONFIG_ARM |
364 | set_irq_flags(chip->irq_base + pmirq, IRQF_VALID); | 330 | set_irq_flags(irq, IRQF_VALID); |
365 | #else | 331 | #else |
366 | irq_set_noprobe(chip->irq_base + pmirq); | 332 | irq_set_noprobe(irq); |
367 | #endif | 333 | #endif |
368 | } | ||
369 | |||
370 | irq_set_irq_type(devirq, pdata->irq_trigger_flag); | ||
371 | irq_set_handler_data(devirq, chip); | ||
372 | irq_set_chained_handler(devirq, pm8xxx_irq_handler); | ||
373 | irq_set_irq_wake(devirq, 1); | ||
374 | |||
375 | return chip; | ||
376 | } | ||
377 | |||
378 | static int pm8xxx_irq_exit(struct pm_irq_chip *chip) | ||
379 | { | ||
380 | irq_set_chained_handler(chip->devirq, NULL); | ||
381 | kfree(chip); | ||
382 | return 0; | 334 | return 0; |
383 | } | 335 | } |
384 | 336 | ||
337 | static const struct irq_domain_ops pm8xxx_irq_domain_ops = { | ||
338 | .xlate = irq_domain_xlate_twocell, | ||
339 | .map = pm8xxx_irq_domain_map, | ||
340 | }; | ||
341 | |||
385 | static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) | 342 | static int pm8921_readb(const struct device *dev, u16 addr, u8 *val) |
386 | { | 343 | { |
387 | const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); | 344 | const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev); |
@@ -432,42 +389,19 @@ static struct pm8xxx_drvdata pm8921_drvdata = { | |||
432 | .pmic_read_irq_stat = pm8921_read_irq_stat, | 389 | .pmic_read_irq_stat = pm8921_read_irq_stat, |
433 | }; | 390 | }; |
434 | 391 | ||
435 | static int pm8921_add_subdevices(const struct pm8921_platform_data | ||
436 | *pdata, | ||
437 | struct pm8921 *pmic, | ||
438 | u32 rev) | ||
439 | { | ||
440 | int ret = 0, irq_base = 0; | ||
441 | struct pm_irq_chip *irq_chip; | ||
442 | |||
443 | if (pdata->irq_pdata) { | ||
444 | pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS; | ||
445 | pdata->irq_pdata->irq_cdata.rev = rev; | ||
446 | irq_base = pdata->irq_pdata->irq_base; | ||
447 | irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata); | ||
448 | |||
449 | if (IS_ERR(irq_chip)) { | ||
450 | pr_err("Failed to init interrupts ret=%ld\n", | ||
451 | PTR_ERR(irq_chip)); | ||
452 | return PTR_ERR(irq_chip); | ||
453 | } | ||
454 | pmic->irq_chip = irq_chip; | ||
455 | } | ||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | static int pm8921_probe(struct platform_device *pdev) | 392 | static int pm8921_probe(struct platform_device *pdev) |
460 | { | 393 | { |
461 | const struct pm8921_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
462 | struct pm8921 *pmic; | 394 | struct pm8921 *pmic; |
463 | int rc; | 395 | int rc; |
464 | u8 val; | 396 | u8 val; |
397 | unsigned int irq; | ||
465 | u32 rev; | 398 | u32 rev; |
399 | struct pm_irq_chip *chip; | ||
400 | unsigned int nirqs = PM8921_NR_IRQS; | ||
466 | 401 | ||
467 | if (!pdata) { | 402 | irq = platform_get_irq(pdev, 0); |
468 | pr_err("missing platform data\n"); | 403 | if (irq < 0) |
469 | return -EINVAL; | 404 | return irq; |
470 | } | ||
471 | 405 | ||
472 | pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL); | 406 | pmic = devm_kzalloc(&pdev->dev, sizeof(struct pm8921), GFP_KERNEL); |
473 | if (!pmic) { | 407 | if (!pmic) { |
@@ -498,37 +432,55 @@ static int pm8921_probe(struct platform_device *pdev) | |||
498 | pm8921_drvdata.pm_chip_data = pmic; | 432 | pm8921_drvdata.pm_chip_data = pmic; |
499 | platform_set_drvdata(pdev, &pm8921_drvdata); | 433 | platform_set_drvdata(pdev, &pm8921_drvdata); |
500 | 434 | ||
501 | rc = pm8921_add_subdevices(pdata, pmic, rev); | 435 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip) + |
436 | sizeof(chip->config[0]) * nirqs, | ||
437 | GFP_KERNEL); | ||
438 | if (!chip) | ||
439 | return -ENOMEM; | ||
440 | |||
441 | pmic->irq_chip = chip; | ||
442 | chip->dev = &pdev->dev; | ||
443 | chip->num_irqs = nirqs; | ||
444 | chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8); | ||
445 | chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8); | ||
446 | spin_lock_init(&chip->pm_irq_lock); | ||
447 | |||
448 | chip->irqdomain = irq_domain_add_linear(pdev->dev.of_node, nirqs, | ||
449 | &pm8xxx_irq_domain_ops, | ||
450 | chip); | ||
451 | if (!chip->irqdomain) | ||
452 | return -ENODEV; | ||
453 | |||
454 | irq_set_handler_data(irq, chip); | ||
455 | irq_set_chained_handler(irq, pm8xxx_irq_handler); | ||
456 | irq_set_irq_wake(irq, 1); | ||
457 | |||
458 | rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); | ||
502 | if (rc) { | 459 | if (rc) { |
503 | pr_err("Cannot add subdevices rc=%d\n", rc); | 460 | irq_set_chained_handler(irq, NULL); |
504 | goto err; | 461 | irq_set_handler_data(irq, NULL); |
462 | irq_domain_remove(chip->irqdomain); | ||
505 | } | 463 | } |
506 | 464 | ||
507 | /* gpio might not work if no irq device is found */ | 465 | return rc; |
508 | WARN_ON(pmic->irq_chip == NULL); | 466 | } |
509 | 467 | ||
468 | static int pm8921_remove_child(struct device *dev, void *unused) | ||
469 | { | ||
470 | platform_device_unregister(to_platform_device(dev)); | ||
510 | return 0; | 471 | return 0; |
511 | |||
512 | err: | ||
513 | mfd_remove_devices(pmic->dev); | ||
514 | return rc; | ||
515 | } | 472 | } |
516 | 473 | ||
517 | static int pm8921_remove(struct platform_device *pdev) | 474 | static int pm8921_remove(struct platform_device *pdev) |
518 | { | 475 | { |
519 | struct pm8xxx_drvdata *drvdata; | 476 | int irq = platform_get_irq(pdev, 0); |
520 | struct pm8921 *pmic = NULL; | 477 | struct pm8921 *pmic = pm8921_drvdata.pm_chip_data; |
521 | 478 | struct pm_irq_chip *chip = pmic->irq_chip; | |
522 | drvdata = platform_get_drvdata(pdev); | 479 | |
523 | if (drvdata) | 480 | device_for_each_child(&pdev->dev, NULL, pm8921_remove_child); |
524 | pmic = drvdata->pm_chip_data; | 481 | irq_set_chained_handler(irq, NULL); |
525 | if (pmic) { | 482 | irq_set_handler_data(irq, NULL); |
526 | mfd_remove_devices(pmic->dev); | 483 | irq_domain_remove(chip->irqdomain); |
527 | if (pmic->irq_chip) { | ||
528 | pm8xxx_irq_exit(pmic->irq_chip); | ||
529 | pmic->irq_chip = NULL; | ||
530 | } | ||
531 | } | ||
532 | 484 | ||
533 | return 0; | 485 | return 0; |
534 | } | 486 | } |