diff options
| -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); |
