MLK-18393 imx8: Check the eDMA channels and update DTB
authorYe Li <ye.li@nxp.com>
Fri, 18 May 2018 02:49:03 +0000 (19:49 -0700)
committerYe Li <ye.li@nxp.com>
Fri, 24 May 2019 11:28:14 +0000 (04:28 -0700)
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 <ye.li@nxp.com>
Acked-by : Robin Gong <yibin.gong@nxp.com>

(cherry picked from commit 37efd38e5306680047ab9545f271716fea15fa95)

arch/arm/mach-imx/imx8/cpu.c

index b45fd01..1d97d58 100644 (file)
 
 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