From ec6a694c1430f023401286862902bd14ca9acf87 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Thu, 17 May 2018 19:49:03 -0700 Subject: [PATCH] MLK-18393 imx8: Check the eDMA channels and update DTB Since M4 will arrange some eDMA channels to its partition, the A core can't use them. We have to remove these eDMA channels from DTB dynamically. Different like other resources, disabling the eDMA channels require to modify the edma nodes by removing relevant registers, interrupts configurations, and adjust dma channels number. This patch searches the edma nodes from kernel DTB, checks the channels by binding their registers base address with their resource IDs. Then update the reg, interrupts, interrupt-names and dma-channels properties. Signed-off-by: Ye Li Acked-by : Robin Gong (cherry picked from commit 37efd38e5306680047ab9545f271716fea15fa95) --- arch/arm/mach-imx/imx8/cpu.c | 243 +++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/arch/arm/mach-imx/imx8/cpu.c b/arch/arm/mach-imx/imx8/cpu.c index b45fd0161f..1d97d58bb8 100644 --- a/arch/arm/mach-imx/imx8/cpu.c +++ b/arch/arm/mach-imx/imx8/cpu.c @@ -32,6 +32,13 @@ DECLARE_GLOBAL_DATA_PTR; +struct edma_ch_map { + sc_rsrc_t ch_start_rsrc; + u32 ch_start_regs; + u32 ch_num; + const char* node_path; +}; + #define BT_PASSOVER_TAG 0x504F struct pass_over_info_t *get_pass_over_info(void) { @@ -617,6 +624,240 @@ static int disable_fdt_node(void *blob, int nodeoffset) return rc; } +static void fdt_edma_debug_int_array(u32 *array, int count, u32 stride) +{ +#ifdef DEBUG + int i; + for (i = 0; i < count; i++) { + printf("0x%x ", array[i]); + if (i % stride == stride - 1) + printf("\n"); + } + + printf("\n"); +#endif +} + +static void fdt_edma_debug_stringlist(const char *stringlist, int length) +{ +#ifdef DEBUG + int i = 0, len; + while (i < length) { + printf("%s\n", stringlist); + + len = strlen(stringlist) + 1; + i += len; + stringlist += len; + } + + printf("\n"); +#endif +} + +static void fdt_edma_swap_int_array(u32 *array, int count) +{ + int i; + for (i = 0; i < count; i++) { + array[i] = cpu_to_fdt32(array[i]); + } +} + +static int fdt_edma_update_int_array(u32 *array, int count, u32 *new_array, u32 stride, int *remove_array, int remove_count) +{ + int i = 0, j, curr = 0, new_cnt = 0; + + do { + if (remove_count && curr == remove_array[i]) { + i++; + remove_count--; + array += stride; + } else { + for (j = 0; j< stride; j++) { + *new_array = *array; + new_array++; + array++; + } + new_cnt+= j; + } + curr++; + } while ((curr * stride) < count); + + return new_cnt; +} + +static int fdt_edma_update_stringlist(const char *stringlist, int stringlist_count, char *newlist, int *remove_array, int remove_count) +{ + int i = 0, curr = 0, new_len = 0; + int length; + + debug("fdt_edma_update_stringlist, remove_cnt %d\n", remove_count); + + do { + if (remove_count && curr == remove_array[i]) { + debug("remove %s at %d\n", stringlist, remove_array[i]); + + length = strlen(stringlist) + 1; + stringlist += length; + i++; + remove_count--; + } else { + length = strlen(stringlist) + 1; + strcpy(newlist, stringlist); + + debug("copy %s, %s, curr %d, len %d\n", newlist, stringlist, curr, length); + + stringlist += length; + newlist += length; + new_len += length; + } + curr++; + } while (curr < stringlist_count); + + return new_len; +} + +static int fdt_edma_get_channel_id(u32 *regs, int index, struct edma_ch_map *edma) +{ + u32 ch_reg = regs[(index << 2) + 1]; + u32 ch_reg_size = regs[(index << 2) + 3]; + int ch_id = (ch_reg - edma->ch_start_regs) / ch_reg_size; + if (ch_id >= edma->ch_num) + return -1; + + return ch_id; +} + +static void update_fdt_edma_nodes(void *blob) +{ + struct edma_ch_map edma_qm[] = { + { SC_R_DMA_0_CH0, 0x5a200000, 32, "/dma-controller@5a1f0000"}, + { SC_R_DMA_1_CH0, 0x5aa00000, 32, "/dma-controller@5a9f0000"}, + { SC_R_DMA_2_CH0, 0x59200000, 5, "/dma-controller@591F0000"}, + { SC_R_DMA_2_CH5, 0x59250000, 27, "/dma-controller@591F0000"}, + { SC_R_DMA_3_CH0, 0x59a00000, 32, "/dma-controller@599F0000"}, + }; + + struct edma_ch_map edma_qxp[] = { + { SC_R_DMA_0_CH0, 0x59200000, 32, "/dma-controller@591F0000"}, + { SC_R_DMA_1_CH0, 0x59a00000, 32, "/dma-controller@599F0000"}, + { SC_R_DMA_2_CH0, 0x5a200000, 5, "/dma-controller@5a1f0000"}, + { SC_R_DMA_2_CH5, 0x5a250000, 27, "/dma-controller@5a1f0000"}, + { SC_R_DMA_3_CH0, 0x5aa00000, 32, "/dma-controller@5a9f0000"}, + }; + + u32 i, j, edma_size; + int nodeoff, ret; + struct edma_ch_map *edma_array; + + if (is_imx8qm()) { + edma_array = edma_qm; + edma_size = ARRAY_SIZE(edma_qm); + } else { + edma_array = edma_qxp; + edma_size = ARRAY_SIZE(edma_qxp); + } + + for (i = 0; i < edma_size; i++, edma_array++) { + u32 regs[128]; + u32 interrupts[96]; + u32 dma_channels; + int regs_count, interrupts_count, int_names_count; + + const char *list; + int list_len, newlist_len; + int remove[32]; + int remove_cnt = 0; + char * newlist; + + nodeoff = fdt_path_offset(blob, edma_array->node_path); + if (nodeoff < 0) + continue; /* Not found, skip it */ + + printf("%s, %d\n", edma_array->node_path, nodeoff); + + regs_count = fdtdec_get_int_array_count(blob, nodeoff, "reg", regs, 128); + debug("regs_count %d\n", regs_count); + if (regs_count < 0) + continue; + + interrupts_count = fdtdec_get_int_array_count(blob, nodeoff, "interrupts", interrupts, 96); + debug("interrupts_count %d\n", interrupts_count); + if (interrupts_count < 0) + continue; + + dma_channels = fdtdec_get_uint(blob, nodeoff, "dma-channels", 0); + if (dma_channels == 0) + continue; + + list = fdt_getprop(blob, nodeoff, "interrupt-names", &list_len); + if (!list) + continue; + + int_names_count = fdt_stringlist_count(blob, nodeoff, "interrupt-names"); + + fdt_edma_debug_int_array(regs, regs_count, 4); + fdt_edma_debug_int_array(interrupts, interrupts_count, 3); + fdt_edma_debug_stringlist(list, list_len); + + for (j = 0; j < (regs_count >> 2); j++) { + int ch_id = fdt_edma_get_channel_id(regs, j, edma_array); + if (ch_id < 0) + continue; + + if (!check_owned_resource(edma_array->ch_start_rsrc + ch_id)) { + printf("remove edma items %d\n", j); + + dma_channels--; + + remove[remove_cnt] = j; + remove_cnt++; + } + } + + if (remove_cnt > 0) { + u32 new_regs[128]; + u32 new_interrupts[96]; + + regs_count = fdt_edma_update_int_array(regs, regs_count, new_regs, 4, remove, remove_cnt); + interrupts_count = fdt_edma_update_int_array(interrupts, interrupts_count, new_interrupts, 3, remove, remove_cnt); + + fdt_edma_debug_int_array(new_regs, regs_count, 4); + fdt_edma_debug_int_array(new_interrupts, interrupts_count, 3); + + fdt_edma_swap_int_array(new_regs, regs_count); + fdt_edma_swap_int_array(new_interrupts, interrupts_count); + + /* malloc a new string list */ + newlist = (char *)malloc(list_len); + if (!newlist) { + printf("malloc new string list failed, len=%d\n", list_len); + continue; + } + + newlist_len = fdt_edma_update_stringlist(list, int_names_count, newlist, remove, remove_cnt); + fdt_edma_debug_stringlist(newlist, newlist_len); + + ret = fdt_setprop(blob, nodeoff, "reg", new_regs, regs_count * sizeof(u32)); + if (ret) + printf("fdt_setprop regs error %d\n", ret); + + ret = fdt_setprop(blob, nodeoff, "interrupts", new_interrupts, interrupts_count * sizeof(u32)); + if (ret) + printf("fdt_setprop interrupts error %d\n", ret); + + ret = fdt_setprop_u32(blob, nodeoff, "dma-channels", dma_channels); + if (ret) + printf("fdt_setprop_u32 dma-channels error %d\n", ret); + + ret = fdt_setprop(blob, nodeoff, "interrupt-names", newlist, newlist_len); + if (ret) + printf("fdt_setprop interrupt-names error %d\n", ret); + + free(newlist); + } + } +} + static void update_fdt_with_owned_resources(void *blob) { /* Traverses the fdt nodes, @@ -870,6 +1111,8 @@ int ft_system_setup(void *blob, bd_t *bd) #endif update_fdt_with_owned_resources(blob); + + update_fdt_edma_nodes(blob); #ifdef CONFIG_IMX_SMMU config_smmu_fdt(blob); #endif -- 2.17.1