summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2018-05-17 19:49:03 -0700
committerYe Li <ye.li@nxp.com>2020-04-26 23:24:09 -0700
commitae2fcb5240e4ebfeb530590f4abfd66e74f24437 (patch)
tree350d672c88f80e0ee51cb2b9b87469befacd98ba /arch
parent9a0b3af21234b5b6d61e2898868fde725b33863c (diff)
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 <ye.li@nxp.com> Acked-by : Robin Gong <yibin.gong@nxp.com> (cherry picked from commit 37efd38e5306680047ab9545f271716fea15fa95) (cherry picked from commit ec6a694c1430f023401286862902bd14ca9acf87)
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-imx/imx8/fdt.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/imx8/fdt.c b/arch/arm/mach-imx/imx8/fdt.c
index 2ceae87695..6824b08459 100644
--- a/arch/arm/mach-imx/imx8/fdt.c
+++ b/arch/arm/mach-imx/imx8/fdt.c
@@ -9,9 +9,17 @@
#include <dm/ofnode.h>
#include <fdt_support.h>
#include <linux/libfdt.h>
+#include <malloc.h>
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;
+};
+
static bool check_owned_resource(sc_rsrc_t rsrc_id)
{
bool owned;
@@ -41,6 +49,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)
{
/*
@@ -292,6 +534,7 @@ int ft_system_setup(void *blob, bd_t *bd)
update_fdt_with_owned_resources(blob);
+ update_fdt_edma_nodes(blob);
if (is_imx8qm()) {
ret = config_smmu_fdt(blob);
if (ret)