diff options
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 242 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic-v3.h | 1 |
2 files changed, 127 insertions, 116 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 5cfa29991eed..146189b1b587 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -56,13 +56,14 @@ struct its_collection { | |||
56 | }; | 56 | }; |
57 | 57 | ||
58 | /* | 58 | /* |
59 | * The ITS_BASER structure - contains memory information and cached | 59 | * The ITS_BASER structure - contains memory information, cached |
60 | * value of BASER register configuration. | 60 | * value of BASER register configuration and ITS page size. |
61 | */ | 61 | */ |
62 | struct its_baser { | 62 | struct its_baser { |
63 | void *base; | 63 | void *base; |
64 | u64 val; | 64 | u64 val; |
65 | u32 order; | 65 | u32 order; |
66 | u32 psz; | ||
66 | }; | 67 | }; |
67 | 68 | ||
68 | /* | 69 | /* |
@@ -840,6 +841,110 @@ static void its_write_baser(struct its_node *its, struct its_baser *baser, | |||
840 | baser->val = its_read_baser(its, baser); | 841 | baser->val = its_read_baser(its, baser); |
841 | } | 842 | } |
842 | 843 | ||
844 | static int its_setup_baser(struct its_node *its, struct its_baser *baser, | ||
845 | u64 cache, u64 shr, u32 psz, u32 order) | ||
846 | { | ||
847 | u64 val = its_read_baser(its, baser); | ||
848 | u64 esz = GITS_BASER_ENTRY_SIZE(val); | ||
849 | u64 type = GITS_BASER_TYPE(val); | ||
850 | u32 alloc_pages; | ||
851 | void *base; | ||
852 | u64 tmp; | ||
853 | |||
854 | retry_alloc_baser: | ||
855 | alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz); | ||
856 | if (alloc_pages > GITS_BASER_PAGES_MAX) { | ||
857 | pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n", | ||
858 | &its->phys_base, its_base_type_string[type], | ||
859 | alloc_pages, GITS_BASER_PAGES_MAX); | ||
860 | alloc_pages = GITS_BASER_PAGES_MAX; | ||
861 | order = get_order(GITS_BASER_PAGES_MAX * psz); | ||
862 | } | ||
863 | |||
864 | base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); | ||
865 | if (!base) | ||
866 | return -ENOMEM; | ||
867 | |||
868 | retry_baser: | ||
869 | val = (virt_to_phys(base) | | ||
870 | (type << GITS_BASER_TYPE_SHIFT) | | ||
871 | ((esz - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | | ||
872 | ((alloc_pages - 1) << GITS_BASER_PAGES_SHIFT) | | ||
873 | cache | | ||
874 | shr | | ||
875 | GITS_BASER_VALID); | ||
876 | |||
877 | switch (psz) { | ||
878 | case SZ_4K: | ||
879 | val |= GITS_BASER_PAGE_SIZE_4K; | ||
880 | break; | ||
881 | case SZ_16K: | ||
882 | val |= GITS_BASER_PAGE_SIZE_16K; | ||
883 | break; | ||
884 | case SZ_64K: | ||
885 | val |= GITS_BASER_PAGE_SIZE_64K; | ||
886 | break; | ||
887 | } | ||
888 | |||
889 | its_write_baser(its, baser, val); | ||
890 | tmp = baser->val; | ||
891 | |||
892 | if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { | ||
893 | /* | ||
894 | * Shareability didn't stick. Just use | ||
895 | * whatever the read reported, which is likely | ||
896 | * to be the only thing this redistributor | ||
897 | * supports. If that's zero, make it | ||
898 | * non-cacheable as well. | ||
899 | */ | ||
900 | shr = tmp & GITS_BASER_SHAREABILITY_MASK; | ||
901 | if (!shr) { | ||
902 | cache = GITS_BASER_nC; | ||
903 | __flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order)); | ||
904 | } | ||
905 | goto retry_baser; | ||
906 | } | ||
907 | |||
908 | if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { | ||
909 | /* | ||
910 | * Page size didn't stick. Let's try a smaller | ||
911 | * size and retry. If we reach 4K, then | ||
912 | * something is horribly wrong... | ||
913 | */ | ||
914 | free_pages((unsigned long)base, order); | ||
915 | baser->base = NULL; | ||
916 | |||
917 | switch (psz) { | ||
918 | case SZ_16K: | ||
919 | psz = SZ_4K; | ||
920 | goto retry_alloc_baser; | ||
921 | case SZ_64K: | ||
922 | psz = SZ_16K; | ||
923 | goto retry_alloc_baser; | ||
924 | } | ||
925 | } | ||
926 | |||
927 | if (val != tmp) { | ||
928 | pr_err("ITS@%pa: %s doesn't stick: %lx %lx\n", | ||
929 | &its->phys_base, its_base_type_string[type], | ||
930 | (unsigned long) val, (unsigned long) tmp); | ||
931 | free_pages((unsigned long)base, order); | ||
932 | return -ENXIO; | ||
933 | } | ||
934 | |||
935 | baser->order = order; | ||
936 | baser->base = base; | ||
937 | baser->psz = psz; | ||
938 | |||
939 | pr_info("ITS@%pa: allocated %d %s @%lx (psz %dK, shr %d)\n", | ||
940 | &its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / esz), | ||
941 | its_base_type_string[type], | ||
942 | (unsigned long)virt_to_phys(base), | ||
943 | psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); | ||
944 | |||
945 | return 0; | ||
946 | } | ||
947 | |||
843 | static void its_parse_baser_device(struct its_node *its, struct its_baser *baser, | 948 | static void its_parse_baser_device(struct its_node *its, struct its_baser *baser, |
844 | u32 *order) | 949 | u32 *order) |
845 | { | 950 | { |
@@ -879,25 +984,20 @@ static void its_free_tables(struct its_node *its) | |||
879 | 984 | ||
880 | static int its_alloc_tables(const char *node_name, struct its_node *its) | 985 | static int its_alloc_tables(const char *node_name, struct its_node *its) |
881 | { | 986 | { |
882 | int err; | 987 | u64 typer = readq_relaxed(its->base + GITS_TYPER); |
883 | int i; | 988 | u32 ids = GITS_TYPER_DEVBITS(typer); |
884 | int psz = SZ_64K; | ||
885 | u64 shr = GITS_BASER_InnerShareable; | 989 | u64 shr = GITS_BASER_InnerShareable; |
886 | u64 cache; | 990 | u64 cache = GITS_BASER_WaWb; |
887 | u64 typer; | 991 | u32 psz = SZ_64K; |
888 | u32 ids; | 992 | int err, i; |
889 | 993 | ||
890 | if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) { | 994 | if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) { |
891 | /* | 995 | /* |
892 | * erratum 22375: only alloc 8MB table size | 996 | * erratum 22375: only alloc 8MB table size |
893 | * erratum 24313: ignore memory access type | 997 | * erratum 24313: ignore memory access type |
894 | */ | 998 | */ |
895 | cache = 0; | 999 | cache = GITS_BASER_nCnB; |
896 | ids = 0x14; /* 20 bits, 8MB */ | 1000 | ids = 0x14; /* 20 bits, 8MB */ |
897 | } else { | ||
898 | cache = GITS_BASER_WaWb; | ||
899 | typer = readq_relaxed(its->base + GITS_TYPER); | ||
900 | ids = GITS_TYPER_DEVBITS(typer); | ||
901 | } | 1001 | } |
902 | 1002 | ||
903 | its->device_ids = ids; | 1003 | its->device_ids = ids; |
@@ -906,11 +1006,7 @@ static int its_alloc_tables(const char *node_name, struct its_node *its) | |||
906 | struct its_baser *baser = its->tables + i; | 1006 | struct its_baser *baser = its->tables + i; |
907 | u64 val = its_read_baser(its, baser); | 1007 | u64 val = its_read_baser(its, baser); |
908 | u64 type = GITS_BASER_TYPE(val); | 1008 | u64 type = GITS_BASER_TYPE(val); |
909 | u64 entry_size = GITS_BASER_ENTRY_SIZE(val); | 1009 | u32 order = get_order(psz); |
910 | int order = get_order(psz); | ||
911 | int alloc_pages; | ||
912 | u64 tmp; | ||
913 | void *base; | ||
914 | 1010 | ||
915 | if (type == GITS_BASER_TYPE_NONE) | 1011 | if (type == GITS_BASER_TYPE_NONE) |
916 | continue; | 1012 | continue; |
@@ -918,105 +1014,19 @@ static int its_alloc_tables(const char *node_name, struct its_node *its) | |||
918 | if (type == GITS_BASER_TYPE_DEVICE) | 1014 | if (type == GITS_BASER_TYPE_DEVICE) |
919 | its_parse_baser_device(its, baser, &order); | 1015 | its_parse_baser_device(its, baser, &order); |
920 | 1016 | ||
921 | retry_alloc_baser: | 1017 | err = its_setup_baser(its, baser, cache, shr, psz, order); |
922 | alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz); | 1018 | if (err < 0) { |
923 | if (alloc_pages > GITS_BASER_PAGES_MAX) { | 1019 | its_free_tables(its); |
924 | alloc_pages = GITS_BASER_PAGES_MAX; | 1020 | return err; |
925 | order = get_order(GITS_BASER_PAGES_MAX * psz); | ||
926 | pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n", | ||
927 | node_name, order, alloc_pages); | ||
928 | } | ||
929 | |||
930 | base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); | ||
931 | if (!base) { | ||
932 | err = -ENOMEM; | ||
933 | goto out_free; | ||
934 | } | ||
935 | |||
936 | its->tables[i].base = base; | ||
937 | its->tables[i].order = order; | ||
938 | |||
939 | retry_baser: | ||
940 | val = (virt_to_phys(base) | | ||
941 | (type << GITS_BASER_TYPE_SHIFT) | | ||
942 | ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | | ||
943 | cache | | ||
944 | shr | | ||
945 | GITS_BASER_VALID); | ||
946 | |||
947 | switch (psz) { | ||
948 | case SZ_4K: | ||
949 | val |= GITS_BASER_PAGE_SIZE_4K; | ||
950 | break; | ||
951 | case SZ_16K: | ||
952 | val |= GITS_BASER_PAGE_SIZE_16K; | ||
953 | break; | ||
954 | case SZ_64K: | ||
955 | val |= GITS_BASER_PAGE_SIZE_64K; | ||
956 | break; | ||
957 | } | ||
958 | |||
959 | val |= alloc_pages - 1; | ||
960 | |||
961 | its_write_baser_cache(its, baser, val); | ||
962 | tmp = baser->val; | ||
963 | |||
964 | if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { | ||
965 | /* | ||
966 | * Shareability didn't stick. Just use | ||
967 | * whatever the read reported, which is likely | ||
968 | * to be the only thing this redistributor | ||
969 | * supports. If that's zero, make it | ||
970 | * non-cacheable as well. | ||
971 | */ | ||
972 | shr = tmp & GITS_BASER_SHAREABILITY_MASK; | ||
973 | if (!shr) { | ||
974 | cache = GITS_BASER_nC; | ||
975 | __flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order)); | ||
976 | } | ||
977 | goto retry_baser; | ||
978 | } | ||
979 | |||
980 | if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { | ||
981 | /* | ||
982 | * Page size didn't stick. Let's try a smaller | ||
983 | * size and retry. If we reach 4K, then | ||
984 | * something is horribly wrong... | ||
985 | */ | ||
986 | free_pages((unsigned long)base, order); | ||
987 | its->tables[i].base = NULL; | ||
988 | |||
989 | switch (psz) { | ||
990 | case SZ_16K: | ||
991 | psz = SZ_4K; | ||
992 | goto retry_alloc_baser; | ||
993 | case SZ_64K: | ||
994 | psz = SZ_16K; | ||
995 | goto retry_alloc_baser; | ||
996 | } | ||
997 | } | ||
998 | |||
999 | if (val != tmp) { | ||
1000 | pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", | ||
1001 | node_name, i, | ||
1002 | (unsigned long) val, (unsigned long) tmp); | ||
1003 | err = -ENXIO; | ||
1004 | goto out_free; | ||
1005 | } | 1021 | } |
1006 | 1022 | ||
1007 | pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", | 1023 | /* Update settings which will be used for next BASERn */ |
1008 | (int)(PAGE_ORDER_TO_SIZE(order) / entry_size), | 1024 | psz = baser->psz; |
1009 | its_base_type_string[type], | 1025 | cache = baser->val & GITS_BASER_CACHEABILITY_MASK; |
1010 | (unsigned long)virt_to_phys(base), | 1026 | shr = baser->val & GITS_BASER_SHAREABILITY_MASK; |
1011 | psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); | ||
1012 | } | 1027 | } |
1013 | 1028 | ||
1014 | return 0; | 1029 | return 0; |
1015 | |||
1016 | out_free: | ||
1017 | its_free_tables(its); | ||
1018 | |||
1019 | return err; | ||
1020 | } | 1030 | } |
1021 | 1031 | ||
1022 | static int its_alloc_collections(struct its_node *its) | 1032 | static int its_alloc_collections(struct its_node *its) |
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index dc493e0f0ff7..01cf171ef97d 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h | |||
@@ -228,6 +228,7 @@ | |||
228 | #define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT) | 228 | #define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT) |
229 | #define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT) | 229 | #define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT) |
230 | #define GITS_BASER_PAGES_MAX 256 | 230 | #define GITS_BASER_PAGES_MAX 256 |
231 | #define GITS_BASER_PAGES_SHIFT (0) | ||
231 | 232 | ||
232 | #define GITS_BASER_TYPE_NONE 0 | 233 | #define GITS_BASER_TYPE_NONE 0 |
233 | #define GITS_BASER_TYPE_DEVICE 1 | 234 | #define GITS_BASER_TYPE_DEVICE 1 |