diff options
Diffstat (limited to 'drivers/irqchip/irq-stm32-exti.c')
-rw-r--r-- | drivers/irqchip/irq-stm32-exti.c | 233 |
1 files changed, 140 insertions, 93 deletions
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 7bd1d4cb2e19..e00f2fa27f00 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c | |||
@@ -14,8 +14,10 @@ | |||
14 | #include <linux/irqchip.h> | 14 | #include <linux/irqchip.h> |
15 | #include <linux/irqchip/chained_irq.h> | 15 | #include <linux/irqchip/chained_irq.h> |
16 | #include <linux/irqdomain.h> | 16 | #include <linux/irqdomain.h> |
17 | #include <linux/module.h> | ||
17 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
18 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
20 | #include <linux/of_platform.h> | ||
19 | #include <linux/syscore_ops.h> | 21 | #include <linux/syscore_ops.h> |
20 | 22 | ||
21 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 23 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
@@ -37,12 +39,6 @@ struct stm32_exti_bank { | |||
37 | 39 | ||
38 | #define UNDEF_REG ~0 | 40 | #define UNDEF_REG ~0 |
39 | 41 | ||
40 | enum stm32_exti_hwspinlock { | ||
41 | HWSPINLOCK_UNKNOWN, | ||
42 | HWSPINLOCK_NONE, | ||
43 | HWSPINLOCK_READY, | ||
44 | }; | ||
45 | |||
46 | struct stm32_desc_irq { | 42 | struct stm32_desc_irq { |
47 | u32 exti; | 43 | u32 exti; |
48 | u32 irq_parent; | 44 | u32 irq_parent; |
@@ -69,8 +65,6 @@ struct stm32_exti_host_data { | |||
69 | void __iomem *base; | 65 | void __iomem *base; |
70 | struct stm32_exti_chip_data *chips_data; | 66 | struct stm32_exti_chip_data *chips_data; |
71 | const struct stm32_exti_drv_data *drv_data; | 67 | const struct stm32_exti_drv_data *drv_data; |
72 | struct device_node *node; | ||
73 | enum stm32_exti_hwspinlock hwlock_state; | ||
74 | struct hwspinlock *hwlock; | 68 | struct hwspinlock *hwlock; |
75 | }; | 69 | }; |
76 | 70 | ||
@@ -285,49 +279,27 @@ static int stm32_exti_set_type(struct irq_data *d, | |||
285 | 279 | ||
286 | static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) | 280 | static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) |
287 | { | 281 | { |
288 | struct stm32_exti_host_data *host_data = chip_data->host_data; | 282 | int ret, timeout = 0; |
289 | struct hwspinlock *hwlock; | ||
290 | int id, ret = 0, timeout = 0; | ||
291 | |||
292 | /* first time, check for hwspinlock availability */ | ||
293 | if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { | ||
294 | id = of_hwspin_lock_get_id(host_data->node, 0); | ||
295 | if (id >= 0) { | ||
296 | hwlock = hwspin_lock_request_specific(id); | ||
297 | if (hwlock) { | ||
298 | /* found valid hwspinlock */ | ||
299 | host_data->hwlock_state = HWSPINLOCK_READY; | ||
300 | host_data->hwlock = hwlock; | ||
301 | pr_debug("%s hwspinlock = %d\n", __func__, id); | ||
302 | } else { | ||
303 | host_data->hwlock_state = HWSPINLOCK_NONE; | ||
304 | } | ||
305 | } else if (id != -EPROBE_DEFER) { | ||
306 | host_data->hwlock_state = HWSPINLOCK_NONE; | ||
307 | } else { | ||
308 | /* hwspinlock driver shall be ready at that stage */ | ||
309 | ret = -EPROBE_DEFER; | ||
310 | } | ||
311 | } | ||
312 | 283 | ||
313 | if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { | 284 | if (!chip_data->host_data->hwlock) |
314 | /* | 285 | return 0; |
315 | * Use the x_raw API since we are under spin_lock protection. | 286 | |
316 | * Do not use the x_timeout API because we are under irq_disable | 287 | /* |
317 | * mode (see __setup_irq()) | 288 | * Use the x_raw API since we are under spin_lock protection. |
318 | */ | 289 | * Do not use the x_timeout API because we are under irq_disable |
319 | do { | 290 | * mode (see __setup_irq()) |
320 | ret = hwspin_trylock_raw(host_data->hwlock); | 291 | */ |
321 | if (!ret) | 292 | do { |
322 | return 0; | 293 | ret = hwspin_trylock_raw(chip_data->host_data->hwlock); |
323 | 294 | if (!ret) | |
324 | udelay(HWSPNLCK_RETRY_DELAY); | 295 | return 0; |
325 | timeout += HWSPNLCK_RETRY_DELAY; | 296 | |
326 | } while (timeout < HWSPNLCK_TIMEOUT); | 297 | udelay(HWSPNLCK_RETRY_DELAY); |
327 | 298 | timeout += HWSPNLCK_RETRY_DELAY; | |
328 | if (ret == -EBUSY) | 299 | } while (timeout < HWSPNLCK_TIMEOUT); |
329 | ret = -ETIMEDOUT; | 300 | |
330 | } | 301 | if (ret == -EBUSY) |
302 | ret = -ETIMEDOUT; | ||
331 | 303 | ||
332 | if (ret) | 304 | if (ret) |
333 | pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); | 305 | pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); |
@@ -337,7 +309,7 @@ static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) | |||
337 | 309 | ||
338 | static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) | 310 | static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) |
339 | { | 311 | { |
340 | if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) | 312 | if (chip_data->host_data->hwlock) |
341 | hwspin_unlock_raw(chip_data->host_data->hwlock); | 313 | hwspin_unlock_raw(chip_data->host_data->hwlock); |
342 | } | 314 | } |
343 | 315 | ||
@@ -586,8 +558,7 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, | |||
586 | return -EINVAL; | 558 | return -EINVAL; |
587 | } | 559 | } |
588 | 560 | ||
589 | #ifdef CONFIG_PM | 561 | static int __maybe_unused stm32_exti_h_suspend(void) |
590 | static int stm32_exti_h_suspend(void) | ||
591 | { | 562 | { |
592 | struct stm32_exti_chip_data *chip_data; | 563 | struct stm32_exti_chip_data *chip_data; |
593 | int i; | 564 | int i; |
@@ -602,7 +573,7 @@ static int stm32_exti_h_suspend(void) | |||
602 | return 0; | 573 | return 0; |
603 | } | 574 | } |
604 | 575 | ||
605 | static void stm32_exti_h_resume(void) | 576 | static void __maybe_unused stm32_exti_h_resume(void) |
606 | { | 577 | { |
607 | struct stm32_exti_chip_data *chip_data; | 578 | struct stm32_exti_chip_data *chip_data; |
608 | int i; | 579 | int i; |
@@ -616,17 +587,22 @@ static void stm32_exti_h_resume(void) | |||
616 | } | 587 | } |
617 | 588 | ||
618 | static struct syscore_ops stm32_exti_h_syscore_ops = { | 589 | static struct syscore_ops stm32_exti_h_syscore_ops = { |
590 | #ifdef CONFIG_PM_SLEEP | ||
619 | .suspend = stm32_exti_h_suspend, | 591 | .suspend = stm32_exti_h_suspend, |
620 | .resume = stm32_exti_h_resume, | 592 | .resume = stm32_exti_h_resume, |
593 | #endif | ||
621 | }; | 594 | }; |
622 | 595 | ||
623 | static void stm32_exti_h_syscore_init(void) | 596 | static void stm32_exti_h_syscore_init(struct stm32_exti_host_data *host_data) |
624 | { | 597 | { |
598 | stm32_host_data = host_data; | ||
625 | register_syscore_ops(&stm32_exti_h_syscore_ops); | 599 | register_syscore_ops(&stm32_exti_h_syscore_ops); |
626 | } | 600 | } |
627 | #else | 601 | |
628 | static inline void stm32_exti_h_syscore_init(void) {} | 602 | static void stm32_exti_h_syscore_deinit(void) |
629 | #endif | 603 | { |
604 | unregister_syscore_ops(&stm32_exti_h_syscore_ops); | ||
605 | } | ||
630 | 606 | ||
631 | static struct irq_chip stm32_exti_h_chip = { | 607 | static struct irq_chip stm32_exti_h_chip = { |
632 | .name = "stm32-exti-h", | 608 | .name = "stm32-exti-h", |
@@ -683,8 +659,6 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, | |||
683 | return NULL; | 659 | return NULL; |
684 | 660 | ||
685 | host_data->drv_data = dd; | 661 | host_data->drv_data = dd; |
686 | host_data->node = node; | ||
687 | host_data->hwlock_state = HWSPINLOCK_UNKNOWN; | ||
688 | host_data->chips_data = kcalloc(dd->bank_nr, | 662 | host_data->chips_data = kcalloc(dd->bank_nr, |
689 | sizeof(struct stm32_exti_chip_data), | 663 | sizeof(struct stm32_exti_chip_data), |
690 | GFP_KERNEL); | 664 | GFP_KERNEL); |
@@ -711,7 +685,8 @@ free_host_data: | |||
711 | 685 | ||
712 | static struct | 686 | static struct |
713 | stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, | 687 | stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, |
714 | u32 bank_idx) | 688 | u32 bank_idx, |
689 | struct device_node *node) | ||
715 | { | 690 | { |
716 | const struct stm32_exti_bank *stm32_bank; | 691 | const struct stm32_exti_bank *stm32_bank; |
717 | struct stm32_exti_chip_data *chip_data; | 692 | struct stm32_exti_chip_data *chip_data; |
@@ -731,7 +706,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, | |||
731 | writel_relaxed(0, base + stm32_bank->imr_ofst); | 706 | writel_relaxed(0, base + stm32_bank->imr_ofst); |
732 | writel_relaxed(0, base + stm32_bank->emr_ofst); | 707 | writel_relaxed(0, base + stm32_bank->emr_ofst); |
733 | 708 | ||
734 | pr_info("%pOF: bank%d\n", h_data->node, bank_idx); | 709 | pr_info("%pOF: bank%d\n", node, bank_idx); |
735 | 710 | ||
736 | return chip_data; | 711 | return chip_data; |
737 | } | 712 | } |
@@ -771,7 +746,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, | |||
771 | struct stm32_exti_chip_data *chip_data; | 746 | struct stm32_exti_chip_data *chip_data; |
772 | 747 | ||
773 | stm32_bank = drv_data->exti_banks[i]; | 748 | stm32_bank = drv_data->exti_banks[i]; |
774 | chip_data = stm32_exti_chip_init(host_data, i); | 749 | chip_data = stm32_exti_chip_init(host_data, i, node); |
775 | 750 | ||
776 | gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); | 751 | gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); |
777 | 752 | ||
@@ -815,50 +790,130 @@ static const struct irq_domain_ops stm32_exti_h_domain_ops = { | |||
815 | .xlate = irq_domain_xlate_twocell, | 790 | .xlate = irq_domain_xlate_twocell, |
816 | }; | 791 | }; |
817 | 792 | ||
818 | static int | 793 | static void stm32_exti_remove_irq(void *data) |
819 | __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, | 794 | { |
820 | struct device_node *node, | 795 | struct irq_domain *domain = data; |
821 | struct device_node *parent) | 796 | |
797 | irq_domain_remove(domain); | ||
798 | } | ||
799 | |||
800 | static int stm32_exti_remove(struct platform_device *pdev) | ||
801 | { | ||
802 | stm32_exti_h_syscore_deinit(); | ||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | static int stm32_exti_probe(struct platform_device *pdev) | ||
822 | { | 807 | { |
808 | int ret, i; | ||
809 | struct device *dev = &pdev->dev; | ||
810 | struct device_node *np = dev->of_node; | ||
823 | struct irq_domain *parent_domain, *domain; | 811 | struct irq_domain *parent_domain, *domain; |
824 | struct stm32_exti_host_data *host_data; | 812 | struct stm32_exti_host_data *host_data; |
825 | int ret, i; | 813 | const struct stm32_exti_drv_data *drv_data; |
814 | struct resource *res; | ||
826 | 815 | ||
827 | parent_domain = irq_find_host(parent); | 816 | host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); |
828 | if (!parent_domain) { | 817 | if (!host_data) |
829 | pr_err("interrupt-parent not found\n"); | 818 | return -ENOMEM; |
830 | return -EINVAL; | 819 | |
820 | /* check for optional hwspinlock which may be not available yet */ | ||
821 | ret = of_hwspin_lock_get_id(np, 0); | ||
822 | if (ret == -EPROBE_DEFER) | ||
823 | /* hwspinlock framework not yet ready */ | ||
824 | return ret; | ||
825 | |||
826 | if (ret >= 0) { | ||
827 | host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); | ||
828 | if (!host_data->hwlock) { | ||
829 | dev_err(dev, "Failed to request hwspinlock\n"); | ||
830 | return -EINVAL; | ||
831 | } | ||
832 | } else if (ret != -ENOENT) { | ||
833 | /* note: ENOENT is a valid case (means 'no hwspinlock') */ | ||
834 | dev_err(dev, "Failed to get hwspinlock\n"); | ||
835 | return ret; | ||
831 | } | 836 | } |
832 | 837 | ||
833 | host_data = stm32_exti_host_init(drv_data, node); | 838 | /* initialize host_data */ |
834 | if (!host_data) | 839 | drv_data = of_device_get_match_data(dev); |
840 | if (!drv_data) { | ||
841 | dev_err(dev, "no of match data\n"); | ||
842 | return -ENODEV; | ||
843 | } | ||
844 | host_data->drv_data = drv_data; | ||
845 | |||
846 | host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, | ||
847 | sizeof(*host_data->chips_data), | ||
848 | GFP_KERNEL); | ||
849 | if (!host_data->chips_data) | ||
835 | return -ENOMEM; | 850 | return -ENOMEM; |
836 | 851 | ||
852 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
853 | host_data->base = devm_ioremap_resource(dev, res); | ||
854 | if (IS_ERR(host_data->base)) { | ||
855 | dev_err(dev, "Unable to map registers\n"); | ||
856 | return PTR_ERR(host_data->base); | ||
857 | } | ||
858 | |||
837 | for (i = 0; i < drv_data->bank_nr; i++) | 859 | for (i = 0; i < drv_data->bank_nr; i++) |
838 | stm32_exti_chip_init(host_data, i); | 860 | stm32_exti_chip_init(host_data, i, np); |
861 | |||
862 | parent_domain = irq_find_host(of_irq_find_parent(np)); | ||
863 | if (!parent_domain) { | ||
864 | dev_err(dev, "GIC interrupt-parent not found\n"); | ||
865 | return -EINVAL; | ||
866 | } | ||
839 | 867 | ||
840 | domain = irq_domain_add_hierarchy(parent_domain, 0, | 868 | domain = irq_domain_add_hierarchy(parent_domain, 0, |
841 | drv_data->bank_nr * IRQS_PER_BANK, | 869 | drv_data->bank_nr * IRQS_PER_BANK, |
842 | node, &stm32_exti_h_domain_ops, | 870 | np, &stm32_exti_h_domain_ops, |
843 | host_data); | 871 | host_data); |
844 | 872 | ||
845 | if (!domain) { | 873 | if (!domain) { |
846 | pr_err("%pOFn: Could not register exti domain.\n", node); | 874 | dev_err(dev, "Could not register exti domain\n"); |
847 | ret = -ENOMEM; | 875 | return -ENOMEM; |
848 | goto out_unmap; | ||
849 | } | 876 | } |
850 | 877 | ||
851 | stm32_exti_h_syscore_init(); | 878 | ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); |
879 | if (ret) | ||
880 | return ret; | ||
881 | |||
882 | stm32_exti_h_syscore_init(host_data); | ||
852 | 883 | ||
853 | return 0; | 884 | return 0; |
885 | } | ||
854 | 886 | ||
855 | out_unmap: | 887 | /* platform driver only for MP1 */ |
856 | iounmap(host_data->base); | 888 | static const struct of_device_id stm32_exti_ids[] = { |
857 | kfree(host_data->chips_data); | 889 | { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, |
858 | kfree(host_data); | 890 | {}, |
859 | return ret; | 891 | }; |
892 | MODULE_DEVICE_TABLE(of, stm32_exti_ids); | ||
893 | |||
894 | static struct platform_driver stm32_exti_driver = { | ||
895 | .probe = stm32_exti_probe, | ||
896 | .remove = stm32_exti_remove, | ||
897 | .driver = { | ||
898 | .name = "stm32_exti", | ||
899 | .of_match_table = stm32_exti_ids, | ||
900 | }, | ||
901 | }; | ||
902 | |||
903 | static int __init stm32_exti_arch_init(void) | ||
904 | { | ||
905 | return platform_driver_register(&stm32_exti_driver); | ||
860 | } | 906 | } |
861 | 907 | ||
908 | static void __exit stm32_exti_arch_exit(void) | ||
909 | { | ||
910 | return platform_driver_unregister(&stm32_exti_driver); | ||
911 | } | ||
912 | |||
913 | arch_initcall(stm32_exti_arch_init); | ||
914 | module_exit(stm32_exti_arch_exit); | ||
915 | |||
916 | /* no platform driver for F4 and H7 */ | ||
862 | static int __init stm32f4_exti_of_init(struct device_node *np, | 917 | static int __init stm32f4_exti_of_init(struct device_node *np, |
863 | struct device_node *parent) | 918 | struct device_node *parent) |
864 | { | 919 | { |
@@ -874,11 +929,3 @@ static int __init stm32h7_exti_of_init(struct device_node *np, | |||
874 | } | 929 | } |
875 | 930 | ||
876 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); | 931 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); |
877 | |||
878 | static int __init stm32mp1_exti_of_init(struct device_node *np, | ||
879 | struct device_node *parent) | ||
880 | { | ||
881 | return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); | ||
882 | } | ||
883 | |||
884 | IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); | ||