diff options
author | David Daney <david.daney@cavium.com> | 2012-07-05 12:12:39 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-07-23 08:54:53 -0400 |
commit | a0c16582b5b50792b0fd3e07d23c537936fafcb7 (patch) | |
tree | 0c063bc5c775115fdd2d6709971d61151b807ace /arch/mips | |
parent | 7ed1815296498e9d1bfa1f13e94b743364b14caf (diff) |
MIPS: Octeon: Setup irq_domains for interrupts.
Create two domains. One for the GPIO lines, and the other for on-chip
sources.
Signed-off-by: David Daney <david.daney@cavium.com>
Cc: linux-mips@linux-mips.org
Cc: devicetree-discuss@lists.ozlabs.org
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: linux-kernel@vger.kernel.org
Cc: David Daney <david.daney@cavium.com>
Patchwork: https://patchwork.linux-mips.org/patch/3936/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/cavium-octeon/octeon-irq.c | 215 |
1 files changed, 206 insertions, 9 deletions
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c index fac22a89f614..52610ce50dfc 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c | |||
@@ -3,14 +3,17 @@ | |||
3 | * License. See the file "COPYING" in the main directory of this archive | 3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. | 4 | * for more details. |
5 | * | 5 | * |
6 | * Copyright (C) 2004-2008, 2009, 2010, 2011 Cavium Networks | 6 | * Copyright (C) 2004-2012 Cavium, Inc. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/interrupt.h> | 9 | #include <linux/interrupt.h> |
10 | #include <linux/irqdomain.h> | ||
10 | #include <linux/bitops.h> | 11 | #include <linux/bitops.h> |
11 | #include <linux/percpu.h> | 12 | #include <linux/percpu.h> |
13 | #include <linux/slab.h> | ||
12 | #include <linux/irq.h> | 14 | #include <linux/irq.h> |
13 | #include <linux/smp.h> | 15 | #include <linux/smp.h> |
16 | #include <linux/of.h> | ||
14 | 17 | ||
15 | #include <asm/octeon/octeon.h> | 18 | #include <asm/octeon/octeon.h> |
16 | 19 | ||
@@ -42,9 +45,9 @@ struct octeon_core_chip_data { | |||
42 | 45 | ||
43 | static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; | 46 | static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; |
44 | 47 | ||
45 | static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit, | 48 | static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, |
46 | struct irq_chip *chip, | 49 | struct irq_chip *chip, |
47 | irq_flow_handler_t handler) | 50 | irq_flow_handler_t handler) |
48 | { | 51 | { |
49 | union octeon_ciu_chip_data cd; | 52 | union octeon_ciu_chip_data cd; |
50 | 53 | ||
@@ -847,6 +850,178 @@ static struct irq_chip octeon_irq_chip_ciu_wd = { | |||
847 | .irq_mask = octeon_irq_dummy_mask, | 850 | .irq_mask = octeon_irq_dummy_mask, |
848 | }; | 851 | }; |
849 | 852 | ||
853 | static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit) | ||
854 | { | ||
855 | bool edge = false; | ||
856 | |||
857 | if (line == 0) | ||
858 | switch (bit) { | ||
859 | case 48 ... 49: /* GMX DRP */ | ||
860 | case 50: /* IPD_DRP */ | ||
861 | case 52 ... 55: /* Timers */ | ||
862 | case 58: /* MPI */ | ||
863 | edge = true; | ||
864 | break; | ||
865 | default: | ||
866 | break; | ||
867 | } | ||
868 | else /* line == 1 */ | ||
869 | switch (bit) { | ||
870 | case 47: /* PTP */ | ||
871 | edge = true; | ||
872 | break; | ||
873 | default: | ||
874 | break; | ||
875 | } | ||
876 | return edge; | ||
877 | } | ||
878 | |||
879 | struct octeon_irq_gpio_domain_data { | ||
880 | unsigned int base_hwirq; | ||
881 | }; | ||
882 | |||
883 | static int octeon_irq_gpio_xlat(struct irq_domain *d, | ||
884 | struct device_node *node, | ||
885 | const u32 *intspec, | ||
886 | unsigned int intsize, | ||
887 | unsigned long *out_hwirq, | ||
888 | unsigned int *out_type) | ||
889 | { | ||
890 | unsigned int type; | ||
891 | unsigned int pin; | ||
892 | unsigned int trigger; | ||
893 | struct octeon_irq_gpio_domain_data *gpiod; | ||
894 | |||
895 | if (d->of_node != node) | ||
896 | return -EINVAL; | ||
897 | |||
898 | if (intsize < 2) | ||
899 | return -EINVAL; | ||
900 | |||
901 | pin = intspec[0]; | ||
902 | if (pin >= 16) | ||
903 | return -EINVAL; | ||
904 | |||
905 | trigger = intspec[1]; | ||
906 | |||
907 | switch (trigger) { | ||
908 | case 1: | ||
909 | type = IRQ_TYPE_EDGE_RISING; | ||
910 | break; | ||
911 | case 2: | ||
912 | type = IRQ_TYPE_EDGE_FALLING; | ||
913 | break; | ||
914 | case 4: | ||
915 | type = IRQ_TYPE_LEVEL_HIGH; | ||
916 | break; | ||
917 | case 8: | ||
918 | type = IRQ_TYPE_LEVEL_LOW; | ||
919 | break; | ||
920 | default: | ||
921 | pr_err("Error: (%s) Invalid irq trigger specification: %x\n", | ||
922 | node->name, | ||
923 | trigger); | ||
924 | type = IRQ_TYPE_LEVEL_LOW; | ||
925 | break; | ||
926 | } | ||
927 | *out_type = type; | ||
928 | gpiod = d->host_data; | ||
929 | *out_hwirq = gpiod->base_hwirq + pin; | ||
930 | |||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | static int octeon_irq_ciu_xlat(struct irq_domain *d, | ||
935 | struct device_node *node, | ||
936 | const u32 *intspec, | ||
937 | unsigned int intsize, | ||
938 | unsigned long *out_hwirq, | ||
939 | unsigned int *out_type) | ||
940 | { | ||
941 | unsigned int ciu, bit; | ||
942 | |||
943 | ciu = intspec[0]; | ||
944 | bit = intspec[1]; | ||
945 | |||
946 | if (ciu > 1 || bit > 63) | ||
947 | return -EINVAL; | ||
948 | |||
949 | /* These are the GPIO lines */ | ||
950 | if (ciu == 0 && bit >= 16 && bit < 32) | ||
951 | return -EINVAL; | ||
952 | |||
953 | *out_hwirq = (ciu << 6) | bit; | ||
954 | *out_type = 0; | ||
955 | |||
956 | return 0; | ||
957 | } | ||
958 | |||
959 | static struct irq_chip *octeon_irq_ciu_chip; | ||
960 | static struct irq_chip *octeon_irq_gpio_chip; | ||
961 | |||
962 | static bool octeon_irq_virq_in_range(unsigned int virq) | ||
963 | { | ||
964 | /* We cannot let it overflow the mapping array. */ | ||
965 | if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0]))) | ||
966 | return true; | ||
967 | |||
968 | WARN_ONCE(true, "virq out of range %u.\n", virq); | ||
969 | return false; | ||
970 | } | ||
971 | |||
972 | static int octeon_irq_ciu_map(struct irq_domain *d, | ||
973 | unsigned int virq, irq_hw_number_t hw) | ||
974 | { | ||
975 | unsigned int line = hw >> 6; | ||
976 | unsigned int bit = hw & 63; | ||
977 | |||
978 | if (!octeon_irq_virq_in_range(virq)) | ||
979 | return -EINVAL; | ||
980 | |||
981 | if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0) | ||
982 | return -EINVAL; | ||
983 | |||
984 | if (octeon_irq_ciu_is_edge(line, bit)) | ||
985 | octeon_irq_set_ciu_mapping(virq, line, bit, | ||
986 | octeon_irq_ciu_chip, | ||
987 | handle_edge_irq); | ||
988 | else | ||
989 | octeon_irq_set_ciu_mapping(virq, line, bit, | ||
990 | octeon_irq_ciu_chip, | ||
991 | handle_level_irq); | ||
992 | |||
993 | return 0; | ||
994 | } | ||
995 | |||
996 | static int octeon_irq_gpio_map(struct irq_domain *d, | ||
997 | unsigned int virq, irq_hw_number_t hw) | ||
998 | { | ||
999 | unsigned int line = hw >> 6; | ||
1000 | unsigned int bit = hw & 63; | ||
1001 | |||
1002 | if (!octeon_irq_virq_in_range(virq)) | ||
1003 | return -EINVAL; | ||
1004 | |||
1005 | if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0) | ||
1006 | return -EINVAL; | ||
1007 | |||
1008 | octeon_irq_set_ciu_mapping(virq, line, bit, | ||
1009 | octeon_irq_gpio_chip, | ||
1010 | octeon_irq_handle_gpio); | ||
1011 | |||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | static struct irq_domain_ops octeon_irq_domain_ciu_ops = { | ||
1016 | .map = octeon_irq_ciu_map, | ||
1017 | .xlate = octeon_irq_ciu_xlat, | ||
1018 | }; | ||
1019 | |||
1020 | static struct irq_domain_ops octeon_irq_domain_gpio_ops = { | ||
1021 | .map = octeon_irq_gpio_map, | ||
1022 | .xlate = octeon_irq_gpio_xlat, | ||
1023 | }; | ||
1024 | |||
850 | static void octeon_irq_ip2_v1(void) | 1025 | static void octeon_irq_ip2_v1(void) |
851 | { | 1026 | { |
852 | const unsigned long core_id = cvmx_get_core_num(); | 1027 | const unsigned long core_id = cvmx_get_core_num(); |
@@ -972,7 +1147,8 @@ static void __init octeon_irq_init_ciu(void) | |||
972 | struct irq_chip *chip; | 1147 | struct irq_chip *chip; |
973 | struct irq_chip *chip_mbox; | 1148 | struct irq_chip *chip_mbox; |
974 | struct irq_chip *chip_wd; | 1149 | struct irq_chip *chip_wd; |
975 | struct irq_chip *chip_gpio; | 1150 | struct device_node *gpio_node; |
1151 | struct device_node *ciu_node; | ||
976 | 1152 | ||
977 | octeon_irq_init_ciu_percpu(); | 1153 | octeon_irq_init_ciu_percpu(); |
978 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu; | 1154 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu; |
@@ -986,15 +1162,16 @@ static void __init octeon_irq_init_ciu(void) | |||
986 | chip = &octeon_irq_chip_ciu_v2; | 1162 | chip = &octeon_irq_chip_ciu_v2; |
987 | chip_mbox = &octeon_irq_chip_ciu_mbox_v2; | 1163 | chip_mbox = &octeon_irq_chip_ciu_mbox_v2; |
988 | chip_wd = &octeon_irq_chip_ciu_wd_v2; | 1164 | chip_wd = &octeon_irq_chip_ciu_wd_v2; |
989 | chip_gpio = &octeon_irq_chip_ciu_gpio_v2; | 1165 | octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2; |
990 | } else { | 1166 | } else { |
991 | octeon_irq_ip2 = octeon_irq_ip2_v1; | 1167 | octeon_irq_ip2 = octeon_irq_ip2_v1; |
992 | octeon_irq_ip3 = octeon_irq_ip3_v1; | 1168 | octeon_irq_ip3 = octeon_irq_ip3_v1; |
993 | chip = &octeon_irq_chip_ciu; | 1169 | chip = &octeon_irq_chip_ciu; |
994 | chip_mbox = &octeon_irq_chip_ciu_mbox; | 1170 | chip_mbox = &octeon_irq_chip_ciu_mbox; |
995 | chip_wd = &octeon_irq_chip_ciu_wd; | 1171 | chip_wd = &octeon_irq_chip_ciu_wd; |
996 | chip_gpio = &octeon_irq_chip_ciu_gpio; | 1172 | octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio; |
997 | } | 1173 | } |
1174 | octeon_irq_ciu_chip = chip; | ||
998 | octeon_irq_ip4 = octeon_irq_ip4_mask; | 1175 | octeon_irq_ip4 = octeon_irq_ip4_mask; |
999 | 1176 | ||
1000 | /* Mips internal */ | 1177 | /* Mips internal */ |
@@ -1003,8 +1180,6 @@ static void __init octeon_irq_init_ciu(void) | |||
1003 | /* CIU_0 */ | 1180 | /* CIU_0 */ |
1004 | for (i = 0; i < 16; i++) | 1181 | for (i = 0; i < 16; i++) |
1005 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq); | 1182 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq); |
1006 | for (i = 0; i < 16; i++) | ||
1007 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip_gpio, octeon_irq_handle_gpio); | ||
1008 | 1183 | ||
1009 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq); | 1184 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq); |
1010 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq); | 1185 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq); |
@@ -1035,6 +1210,28 @@ static void __init octeon_irq_init_ciu(void) | |||
1035 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq); | 1210 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq); |
1036 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq); | 1211 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq); |
1037 | 1212 | ||
1213 | gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio"); | ||
1214 | if (gpio_node) { | ||
1215 | struct octeon_irq_gpio_domain_data *gpiod; | ||
1216 | |||
1217 | gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL); | ||
1218 | if (gpiod) { | ||
1219 | /* gpio domain host_data is the base hwirq number. */ | ||
1220 | gpiod->base_hwirq = 16; | ||
1221 | irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod); | ||
1222 | of_node_put(gpio_node); | ||
1223 | } else | ||
1224 | pr_warn("Cannot allocate memory for GPIO irq_domain.\n"); | ||
1225 | } else | ||
1226 | pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n"); | ||
1227 | |||
1228 | ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu"); | ||
1229 | if (ciu_node) { | ||
1230 | irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL); | ||
1231 | of_node_put(ciu_node); | ||
1232 | } else | ||
1233 | pr_warn("Cannot find device node for cavium,octeon-3860-ciu.\n"); | ||
1234 | |||
1038 | /* Enable the CIU lines */ | 1235 | /* Enable the CIU lines */ |
1039 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | 1236 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); |
1040 | clear_c0_status(STATUSF_IP4); | 1237 | clear_c0_status(STATUSF_IP4); |