diff options
author | Thomas Abraham <thomas.ab@samsung.com> | 2013-03-26 11:12:33 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2013-04-09 04:16:53 -0400 |
commit | 8dc3568d5527bfdb61afadea9284814a705b64ff (patch) | |
tree | 16d5bbaa5acff47e0a8fba2d3ca9d1233856109e | |
parent | f9819739427e6049d2d318743a4a25817018afd4 (diff) |
pinctrl: exynos5440: add gpio interrupt support
Exynos5440 supports gpio interrupts on gpios 16 to 23. The eight interrupt lines
originating from the pin-controller are connected to the gic. Add irq-chip support
for these interrupts.
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/pinctrl/pinctrl-exynos5440.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 7126b7db86c5..6038503ed929 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c | |||
@@ -20,6 +20,9 @@ | |||
20 | #include <linux/pinctrl/pinctrl.h> | 20 | #include <linux/pinctrl/pinctrl.h> |
21 | #include <linux/pinctrl/pinmux.h> | 21 | #include <linux/pinctrl/pinmux.h> |
22 | #include <linux/pinctrl/pinconf.h> | 22 | #include <linux/pinctrl/pinconf.h> |
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/irqdomain.h> | ||
25 | #include <linux/of_irq.h> | ||
23 | #include "core.h" | 26 | #include "core.h" |
24 | 27 | ||
25 | /* EXYNOS5440 GPIO and Pinctrl register offsets */ | 28 | /* EXYNOS5440 GPIO and Pinctrl register offsets */ |
@@ -37,6 +40,7 @@ | |||
37 | #define GPIO_DS1 0x2C | 40 | #define GPIO_DS1 0x2C |
38 | 41 | ||
39 | #define EXYNOS5440_MAX_PINS 23 | 42 | #define EXYNOS5440_MAX_PINS 23 |
43 | #define EXYNOS5440_MAX_GPIO_INT 8 | ||
40 | #define PIN_NAME_LENGTH 10 | 44 | #define PIN_NAME_LENGTH 10 |
41 | 45 | ||
42 | #define GROUP_SUFFIX "-grp" | 46 | #define GROUP_SUFFIX "-grp" |
@@ -109,6 +113,7 @@ struct exynos5440_pmx_func { | |||
109 | struct exynos5440_pinctrl_priv_data { | 113 | struct exynos5440_pinctrl_priv_data { |
110 | void __iomem *reg_base; | 114 | void __iomem *reg_base; |
111 | struct gpio_chip *gc; | 115 | struct gpio_chip *gc; |
116 | struct irq_domain *irq_domain; | ||
112 | 117 | ||
113 | const struct exynos5440_pin_group *pin_groups; | 118 | const struct exynos5440_pin_group *pin_groups; |
114 | unsigned int nr_groups; | 119 | unsigned int nr_groups; |
@@ -116,6 +121,16 @@ struct exynos5440_pinctrl_priv_data { | |||
116 | unsigned int nr_functions; | 121 | unsigned int nr_functions; |
117 | }; | 122 | }; |
118 | 123 | ||
124 | /** | ||
125 | * struct exynos5440_gpio_intr_data: private data for gpio interrupts. | ||
126 | * @priv: driver's private runtime data. | ||
127 | * @gpio_int: gpio interrupt number. | ||
128 | */ | ||
129 | struct exynos5440_gpio_intr_data { | ||
130 | struct exynos5440_pinctrl_priv_data *priv; | ||
131 | unsigned int gpio_int; | ||
132 | }; | ||
133 | |||
119 | /* list of all possible config options supported */ | 134 | /* list of all possible config options supported */ |
120 | static struct pin_config { | 135 | static struct pin_config { |
121 | char *prop_cfg; | 136 | char *prop_cfg; |
@@ -598,6 +613,22 @@ static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offse | |||
598 | return 0; | 613 | return 0; |
599 | } | 614 | } |
600 | 615 | ||
616 | /* gpiolib gpio_to_irq callback function */ | ||
617 | static int exynos5440_gpio_to_irq(struct gpio_chip *gc, unsigned offset) | ||
618 | { | ||
619 | struct exynos5440_pinctrl_priv_data *priv = dev_get_drvdata(gc->dev); | ||
620 | unsigned int virq; | ||
621 | |||
622 | if (offset < 16 || offset > 23) | ||
623 | return -ENXIO; | ||
624 | |||
625 | if (!priv->irq_domain) | ||
626 | return -ENXIO; | ||
627 | |||
628 | virq = irq_create_mapping(priv->irq_domain, offset - 16); | ||
629 | return virq ? : -ENXIO; | ||
630 | } | ||
631 | |||
601 | /* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */ | 632 | /* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */ |
602 | static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, | 633 | static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, |
603 | struct device_node *cfg_np, unsigned int **pin_list, | 634 | struct device_node *cfg_np, unsigned int **pin_list, |
@@ -821,6 +852,7 @@ static int exynos5440_gpiolib_register(struct platform_device *pdev, | |||
821 | gc->get = exynos5440_gpio_get; | 852 | gc->get = exynos5440_gpio_get; |
822 | gc->direction_input = exynos5440_gpio_direction_input; | 853 | gc->direction_input = exynos5440_gpio_direction_input; |
823 | gc->direction_output = exynos5440_gpio_direction_output; | 854 | gc->direction_output = exynos5440_gpio_direction_output; |
855 | gc->to_irq = exynos5440_gpio_to_irq; | ||
824 | gc->label = "gpiolib-exynos5440"; | 856 | gc->label = "gpiolib-exynos5440"; |
825 | gc->owner = THIS_MODULE; | 857 | gc->owner = THIS_MODULE; |
826 | ret = gpiochip_add(gc); | 858 | ret = gpiochip_add(gc); |
@@ -845,6 +877,110 @@ static int exynos5440_gpiolib_unregister(struct platform_device *pdev, | |||
845 | return 0; | 877 | return 0; |
846 | } | 878 | } |
847 | 879 | ||
880 | static void exynos5440_gpio_irq_unmask(struct irq_data *irqd) | ||
881 | { | ||
882 | struct exynos5440_pinctrl_priv_data *d; | ||
883 | unsigned long gpio_int; | ||
884 | |||
885 | d = irq_data_get_irq_chip_data(irqd); | ||
886 | gpio_int = readl(d->reg_base + GPIO_INT); | ||
887 | gpio_int |= 1 << irqd->hwirq; | ||
888 | writel(gpio_int, d->reg_base + GPIO_INT); | ||
889 | } | ||
890 | |||
891 | static void exynos5440_gpio_irq_mask(struct irq_data *irqd) | ||
892 | { | ||
893 | struct exynos5440_pinctrl_priv_data *d; | ||
894 | unsigned long gpio_int; | ||
895 | |||
896 | d = irq_data_get_irq_chip_data(irqd); | ||
897 | gpio_int = readl(d->reg_base + GPIO_INT); | ||
898 | gpio_int &= ~(1 << irqd->hwirq); | ||
899 | writel(gpio_int, d->reg_base + GPIO_INT); | ||
900 | } | ||
901 | |||
902 | /* irq_chip for gpio interrupts */ | ||
903 | static struct irq_chip exynos5440_gpio_irq_chip = { | ||
904 | .name = "exynos5440_gpio_irq_chip", | ||
905 | .irq_unmask = exynos5440_gpio_irq_unmask, | ||
906 | .irq_mask = exynos5440_gpio_irq_mask, | ||
907 | }; | ||
908 | |||
909 | /* interrupt handler for GPIO interrupts 0..7 */ | ||
910 | static irqreturn_t exynos5440_gpio_irq(int irq, void *data) | ||
911 | { | ||
912 | struct exynos5440_gpio_intr_data *intd = data; | ||
913 | struct exynos5440_pinctrl_priv_data *d = intd->priv; | ||
914 | int virq; | ||
915 | |||
916 | virq = irq_linear_revmap(d->irq_domain, intd->gpio_int); | ||
917 | if (!virq) | ||
918 | return IRQ_NONE; | ||
919 | generic_handle_irq(virq); | ||
920 | return IRQ_HANDLED; | ||
921 | } | ||
922 | |||
923 | static int exynos5440_gpio_irq_map(struct irq_domain *h, unsigned int virq, | ||
924 | irq_hw_number_t hw) | ||
925 | { | ||
926 | struct exynos5440_pinctrl_priv_data *d = h->host_data; | ||
927 | |||
928 | irq_set_chip_data(virq, d); | ||
929 | irq_set_chip_and_handler(virq, &exynos5440_gpio_irq_chip, | ||
930 | handle_level_irq); | ||
931 | set_irq_flags(virq, IRQF_VALID); | ||
932 | return 0; | ||
933 | } | ||
934 | |||
935 | /* irq domain callbacks for gpio interrupt controller */ | ||
936 | static const struct irq_domain_ops exynos5440_gpio_irqd_ops = { | ||
937 | .map = exynos5440_gpio_irq_map, | ||
938 | .xlate = irq_domain_xlate_twocell, | ||
939 | }; | ||
940 | |||
941 | /* setup handling of gpio interrupts */ | ||
942 | static int exynos5440_gpio_irq_init(struct platform_device *pdev, | ||
943 | struct exynos5440_pinctrl_priv_data *priv) | ||
944 | { | ||
945 | struct device *dev = &pdev->dev; | ||
946 | struct exynos5440_gpio_intr_data *intd; | ||
947 | int i, irq, ret; | ||
948 | |||
949 | intd = devm_kzalloc(dev, sizeof(*intd) * EXYNOS5440_MAX_GPIO_INT, | ||
950 | GFP_KERNEL); | ||
951 | if (!intd) { | ||
952 | dev_err(dev, "failed to allocate memory for gpio intr data\n"); | ||
953 | return -ENOMEM; | ||
954 | } | ||
955 | |||
956 | for (i = 0; i < EXYNOS5440_MAX_GPIO_INT; i++) { | ||
957 | irq = irq_of_parse_and_map(dev->of_node, i); | ||
958 | if (irq <= 0) { | ||
959 | dev_err(dev, "irq parsing failed\n"); | ||
960 | return -EINVAL; | ||
961 | } | ||
962 | |||
963 | intd->gpio_int = i; | ||
964 | intd->priv = priv; | ||
965 | ret = devm_request_irq(dev, irq, exynos5440_gpio_irq, | ||
966 | 0, dev_name(dev), intd++); | ||
967 | if (ret) { | ||
968 | dev_err(dev, "irq request failed\n"); | ||
969 | return -ENXIO; | ||
970 | } | ||
971 | } | ||
972 | |||
973 | priv->irq_domain = irq_domain_add_linear(dev->of_node, | ||
974 | EXYNOS5440_MAX_GPIO_INT, | ||
975 | &exynos5440_gpio_irqd_ops, priv); | ||
976 | if (!priv->irq_domain) { | ||
977 | dev_err(dev, "failed to create irq domain\n"); | ||
978 | return -ENXIO; | ||
979 | } | ||
980 | |||
981 | return 0; | ||
982 | } | ||
983 | |||
848 | static int exynos5440_pinctrl_probe(struct platform_device *pdev) | 984 | static int exynos5440_pinctrl_probe(struct platform_device *pdev) |
849 | { | 985 | { |
850 | struct device *dev = &pdev->dev; | 986 | struct device *dev = &pdev->dev; |
@@ -883,6 +1019,12 @@ static int exynos5440_pinctrl_probe(struct platform_device *pdev) | |||
883 | return ret; | 1019 | return ret; |
884 | } | 1020 | } |
885 | 1021 | ||
1022 | ret = exynos5440_gpio_irq_init(pdev, priv); | ||
1023 | if (ret) { | ||
1024 | dev_err(dev, "failed to setup gpio interrupts\n"); | ||
1025 | return ret; | ||
1026 | } | ||
1027 | |||
886 | platform_set_drvdata(pdev, priv); | 1028 | platform_set_drvdata(pdev, priv); |
887 | dev_info(dev, "EXYNOS5440 pinctrl driver registered\n"); | 1029 | dev_info(dev, "EXYNOS5440 pinctrl driver registered\n"); |
888 | return 0; | 1030 | return 0; |