// SPDX-License-Identifier: GPL-2.0+ /* * Texas Instruments' K3 R5 Remoteproc driver * * Copyright (C) 2018-2020 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla * Suman Anna */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ti_sci_proc.h" /* * R5F's view of this address can either be for ATCM or BTCM with the other * at address 0x0 based on loczrama signal. */ #define K3_R5_TCM_DEV_ADDR 0x41010000 /* R5 TI-SCI Processor Configuration Flags */ #define PROC_BOOT_CFG_FLAG_R5_DBG_EN 0x00000001 #define PROC_BOOT_CFG_FLAG_R5_DBG_NIDEN 0x00000002 #define PROC_BOOT_CFG_FLAG_R5_LOCKSTEP 0x00000100 #define PROC_BOOT_CFG_FLAG_R5_TEINIT 0x00000200 #define PROC_BOOT_CFG_FLAG_R5_NMFI_EN 0x00000400 #define PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE 0x00000800 #define PROC_BOOT_CFG_FLAG_R5_BTCM_EN 0x00001000 #define PROC_BOOT_CFG_FLAG_R5_ATCM_EN 0x00002000 #define PROC_BOOT_CFG_FLAG_GEN_IGN_BOOTVECTOR 0x10000000 /* Available from J7200 SoCs onwards */ #define PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS 0x00004000 /* R5 TI-SCI Processor Control Flags */ #define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT 0x00000001 /* R5 TI-SCI Processor Status Flags */ #define PROC_BOOT_STATUS_FLAG_R5_WFE 0x00000001 #define PROC_BOOT_STATUS_FLAG_R5_WFI 0x00000002 #define PROC_BOOT_STATUS_FLAG_R5_CLK_GATED 0x00000004 #define PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED 0x00000100 #define NR_CORES 2 enum cluster_mode { CLUSTER_MODE_SPLIT = 0, CLUSTER_MODE_LOCKSTEP, }; /** * struct k3_r5f_ip_data - internal data structure used for IP variations * @tcm_is_double: flag to denote the larger unified TCMs in certain modes * @tcm_ecc_autoinit: flag to denote the auto-initialization of TCMs for ECC */ struct k3_r5f_ip_data { bool tcm_is_double; bool tcm_ecc_autoinit; }; /** * struct k3_r5_mem - internal memory structure * @cpu_addr: MPU virtual address of the memory region * @bus_addr: Bus address used to access the memory region * @dev_addr: Device address from remoteproc view * @size: Size of the memory region */ struct k3_r5f_mem { void __iomem *cpu_addr; phys_addr_t bus_addr; u32 dev_addr; size_t size; }; /** * struct k3_r5f_core - K3 R5 core structure * @dev: cached device pointer * @cluster: pointer to the parent cluster. * @reset: reset control handle * @tsp: TI-SCI processor control handle * @ipdata: cached pointer to R5F IP specific feature data * @mem: Array of available internal memories * @num_mem: Number of available memories * @atcm_enable: flag to control ATCM enablement * @btcm_enable: flag to control BTCM enablement * @loczrama: flag to dictate which TCM is at device address 0x0 * @in_use: flag to tell if the core is already in use. */ struct k3_r5f_core { struct udevice *dev; struct k3_r5f_cluster *cluster; struct reset_ctl reset; struct ti_sci_proc tsp; struct k3_r5f_ip_data *ipdata; struct k3_r5f_mem *mem; int num_mems; u32 atcm_enable; u32 btcm_enable; u32 loczrama; bool in_use; }; /** * struct k3_r5f_cluster - K3 R5F Cluster structure * @mode: Mode to configure the Cluster - Split or LockStep * @cores: Array of pointers to R5 cores within the cluster */ struct k3_r5f_cluster { enum cluster_mode mode; struct k3_r5f_core *cores[NR_CORES]; }; static bool is_primary_core(struct k3_r5f_core *core) { return core == core->cluster->cores[0]; } static int k3_r5f_proc_request(struct k3_r5f_core *core) { struct k3_r5f_cluster *cluster = core->cluster; int i, ret; if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { for (i = 0; i < NR_CORES; i++) { ret = ti_sci_proc_request(&cluster->cores[i]->tsp); if (ret) goto proc_release; } } else { ret = ti_sci_proc_request(&core->tsp); } return 0; proc_release: while (i >= 0) { ti_sci_proc_release(&cluster->cores[i]->tsp); i--; } return ret; } static void k3_r5f_proc_release(struct k3_r5f_core *core) { struct k3_r5f_cluster *cluster = core->cluster; int i; if (cluster->mode == CLUSTER_MODE_LOCKSTEP) for (i = 0; i < NR_CORES; i++) ti_sci_proc_release(&cluster->cores[i]->tsp); else ti_sci_proc_release(&core->tsp); } static int k3_r5f_lockstep_release(struct k3_r5f_cluster *cluster) { int ret, c; debug("%s\n", __func__); for (c = NR_CORES - 1; c >= 0; c--) { ret = ti_sci_proc_power_domain_on(&cluster->cores[c]->tsp); if (ret) goto unroll_module_reset; } /* deassert local reset on all applicable cores */ for (c = NR_CORES - 1; c >= 0; c--) { ret = reset_deassert(&cluster->cores[c]->reset); if (ret) goto unroll_local_reset; } return 0; unroll_local_reset: while (c < NR_CORES) { reset_assert(&cluster->cores[c]->reset); c++; } c = 0; unroll_module_reset: while (c < NR_CORES) { ti_sci_proc_power_domain_off(&cluster->cores[c]->tsp); c++; } return ret; } static int k3_r5f_split_release(struct k3_r5f_core *core) { int ret; dev_dbg(core->dev, "%s\n", __func__); ret = ti_sci_proc_power_domain_on(&core->tsp); if (ret) { dev_err(core->dev, "module-reset deassert failed, ret = %d\n", ret); return ret; } ret = reset_deassert(&core->reset); if (ret) { dev_err(core->dev, "local-reset deassert failed, ret = %d\n", ret); if (ti_sci_proc_power_domain_off(&core->tsp)) dev_warn(core->dev, "module-reset assert back failed\n"); } return ret; } static int k3_r5f_prepare(struct udevice *dev) { struct k3_r5f_core *core = dev_get_priv(dev); struct k3_r5f_cluster *cluster = core->cluster; int ret = 0; dev_dbg(dev, "%s\n", __func__); if (cluster->mode == CLUSTER_MODE_LOCKSTEP) ret = k3_r5f_lockstep_release(cluster); else ret = k3_r5f_split_release(core); if (ret) dev_err(dev, "Unable to enable cores for TCM loading %d\n", ret); return ret; } static int k3_r5f_core_sanity_check(struct k3_r5f_core *core) { struct k3_r5f_cluster *cluster = core->cluster; if (core->in_use) { dev_err(core->dev, "Invalid op: Trying to load/start on already running core %d\n", core->tsp.proc_id); return -EINVAL; } if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !cluster->cores[1]) { dev_err(core->dev, "Secondary core is not probed in this cluster\n"); return -EAGAIN; } if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !is_primary_core(core)) { dev_err(core->dev, "Invalid op: Trying to start secondary core %d in lockstep mode\n", core->tsp.proc_id); return -EINVAL; } if (cluster->mode == CLUSTER_MODE_SPLIT && !is_primary_core(core)) { if (!core->cluster->cores[0]->in_use) { dev_err(core->dev, "Invalid seq: Enable primary core before loading secondary core\n"); return -EINVAL; } } return 0; } /* Zero out TCMs so that ECC can be effective on all TCM addresses */ void k3_r5f_init_tcm_memories(struct k3_r5f_core *core, bool auto_inited) { if (core->ipdata->tcm_ecc_autoinit && auto_inited) return; if (core->atcm_enable) memset(core->mem[0].cpu_addr, 0x00, core->mem[0].size); if (core->btcm_enable) memset(core->mem[1].cpu_addr, 0x00, core->mem[1].size); } /** * k3_r5f_load() - Load up the Remote processor image * @dev: rproc device pointer * @addr: Address at which image is available * @size: size of the image * * Return: 0 if all goes good, else appropriate error message. */ static int k3_r5f_load(struct udevice *dev, ulong addr, ulong size) { struct k3_r5f_core *core = dev_get_priv(dev); u64 boot_vector; u32 ctrl, sts, cfg = 0; bool mem_auto_init; int ret; dev_dbg(dev, "%s addr = 0x%lx, size = 0x%lx\n", __func__, addr, size); ret = k3_r5f_core_sanity_check(core); if (ret) return ret; ret = k3_r5f_proc_request(core); if (ret) return ret; ret = ti_sci_proc_get_status(&core->tsp, &boot_vector, &cfg, &ctrl, &sts); if (ret) return ret; mem_auto_init = !(cfg & PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS); ret = k3_r5f_prepare(dev); if (ret) { dev_err(dev, "R5f prepare failed for core %d\n", core->tsp.proc_id); goto proc_release; } k3_r5f_init_tcm_memories(core, mem_auto_init); ret = rproc_elf_load_image(dev, addr, size); if (ret < 0) { dev_err(dev, "Loading elf failedi %d\n", ret); goto proc_release; } boot_vector = rproc_elf_get_boot_addr(dev, addr); dev_dbg(dev, "%s: Boot vector = 0x%llx\n", __func__, boot_vector); ret = ti_sci_proc_set_config(&core->tsp, boot_vector, 0, 0); proc_release: k3_r5f_proc_release(core); return ret; } static int k3_r5f_core_halt(struct k3_r5f_core *core) { int ret; ret = ti_sci_proc_set_control(&core->tsp, PROC_BOOT_CTRL_FLAG_R5_CORE_HALT, 0); if (ret) dev_err(core->dev, "Core %d failed to stop\n", core->tsp.proc_id); return ret; } static int k3_r5f_core_run(struct k3_r5f_core *core) { int ret; ret = ti_sci_proc_set_control(&core->tsp, 0, PROC_BOOT_CTRL_FLAG_R5_CORE_HALT); if (ret) { dev_err(core->dev, "Core %d failed to start\n", core->tsp.proc_id); return ret; } return 0; } /** * k3_r5f_start() - Start the remote processor * @dev: rproc device pointer * * Return: 0 if all went ok, else return appropriate error */ static int k3_r5f_start(struct udevice *dev) { struct k3_r5f_core *core = dev_get_priv(dev); struct k3_r5f_cluster *cluster = core->cluster; int ret, c; dev_dbg(dev, "%s\n", __func__); ret = k3_r5f_core_sanity_check(core); if (ret) return ret; ret = k3_r5f_proc_request(core); if (ret) return ret; if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { if (is_primary_core(core)) { for (c = NR_CORES - 1; c >= 0; c--) { ret = k3_r5f_core_run(cluster->cores[c]); if (ret) goto unroll_core_run; } } else { dev_err(dev, "Invalid op: Trying to start secondary core %d in lockstep mode\n", core->tsp.proc_id); ret = -EINVAL; goto proc_release; } } else { ret = k3_r5f_core_run(core); if (ret) goto proc_release; } core->in_use = true; k3_r5f_proc_release(core); return 0; unroll_core_run: while (c < NR_CORES) { k3_r5f_core_halt(cluster->cores[c]); c++; } proc_release: k3_r5f_proc_release(core); return ret; } static int k3_r5f_split_reset(struct k3_r5f_core *core) { int ret; dev_dbg(core->dev, "%s\n", __func__); if (reset_assert(&core->reset)) ret = -EINVAL; if (ti_sci_proc_power_domain_off(&core->tsp)) ret = -EINVAL; return ret; } static int k3_r5f_lockstep_reset(struct k3_r5f_cluster *cluster) { int ret = 0, c; debug("%s\n", __func__); for (c = 0; c < NR_CORES; c++) if (reset_assert(&cluster->cores[c]->reset)) ret = -EINVAL; /* disable PSC modules on all applicable cores */ for (c = 0; c < NR_CORES; c++) if (ti_sci_proc_power_domain_off(&cluster->cores[c]->tsp)) ret = -EINVAL; return ret; } static int k3_r5f_unprepare(struct udevice *dev) { struct k3_r5f_core *core = dev_get_priv(dev); struct k3_r5f_cluster *cluster = core->cluster; int ret; dev_dbg(dev, "%s\n", __func__); if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { if (is_primary_core(core)) ret = k3_r5f_lockstep_reset(cluster); } else { ret = k3_r5f_split_reset(core); } if (ret) dev_warn(dev, "Unable to enable cores for TCM loading %d\n", ret); return 0; } static int k3_r5f_stop(struct udevice *dev) { struct k3_r5f_core *core = dev_get_priv(dev); struct k3_r5f_cluster *cluster = core->cluster; int c, ret; dev_dbg(dev, "%s\n", __func__); ret = k3_r5f_proc_request(core); if (ret) return ret; core->in_use = false; if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { if (is_primary_core(core)) { for (c = 0; c < NR_CORES; c++) k3_r5f_core_halt(cluster->cores[c]); } else { dev_err(dev, "Invalid op: Trying to stop secondary core in lockstep mode\n"); ret = -EINVAL; goto proc_release; } } else { k3_r5f_core_halt(core); } ret = k3_r5f_unprepare(dev); proc_release: k3_r5f_proc_release(core); return ret; } static void *k3_r5f_da_to_va(struct udevice *dev, ulong da, ulong size) { struct k3_r5f_core *core = dev_get_priv(dev); void __iomem *va = NULL; phys_addr_t bus_addr; u32 dev_addr, offset; ulong mem_size; int i; dev_dbg(dev, "%s\n", __func__); if (size <= 0) return NULL; for (i = 0; i < core->num_mems; i++) { bus_addr = core->mem[i].bus_addr; dev_addr = core->mem[i].dev_addr; mem_size = core->mem[i].size; if (da >= bus_addr && (da + size) <= (bus_addr + mem_size)) { offset = da - bus_addr; va = core->mem[i].cpu_addr + offset; return (__force void *)va; } if (da >= dev_addr && (da + size) <= (dev_addr + mem_size)) { offset = da - dev_addr; va = core->mem[i].cpu_addr + offset; return (__force void *)va; } } /* Assume it is DDR region and return da */ return map_physmem(da, size, MAP_NOCACHE); } static int k3_r5f_init(struct udevice *dev) { return 0; } static int k3_r5f_reset(struct udevice *dev) { return 0; } static const struct dm_rproc_ops k3_r5f_rproc_ops = { .init = k3_r5f_init, .reset = k3_r5f_reset, .start = k3_r5f_start, .stop = k3_r5f_stop, .load = k3_r5f_load, .device_to_virt = k3_r5f_da_to_va, }; static int k3_r5f_rproc_configure(struct k3_r5f_core *core) { struct k3_r5f_cluster *cluster = core->cluster; u32 set_cfg = 0, clr_cfg = 0, cfg, ctrl, sts; bool lockstep_permitted; u64 boot_vec = 0; int ret; dev_dbg(core->dev, "%s\n", __func__); ret = ti_sci_proc_request(&core->tsp); if (ret < 0) return ret; /* Do not touch boot vector now. Load will take care of it. */ clr_cfg |= PROC_BOOT_CFG_FLAG_GEN_IGN_BOOTVECTOR; ret = ti_sci_proc_get_status(&core->tsp, &boot_vec, &cfg, &ctrl, &sts); if (ret) goto out; /* Sanity check for Lockstep mode */ lockstep_permitted = !!(sts & PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED); if (cluster->mode && is_primary_core(core) && !lockstep_permitted) { dev_err(core->dev, "LockStep mode not permitted on this device\n"); ret = -EINVAL; goto out; } /* Primary core only configuration */ if (is_primary_core(core)) { /* always enable ARM mode */ clr_cfg |= PROC_BOOT_CFG_FLAG_R5_TEINIT; if (cluster->mode == CLUSTER_MODE_LOCKSTEP) set_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; else if (lockstep_permitted) clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; } if (core->atcm_enable) set_cfg |= PROC_BOOT_CFG_FLAG_R5_ATCM_EN; else clr_cfg |= PROC_BOOT_CFG_FLAG_R5_ATCM_EN; if (core->btcm_enable) set_cfg |= PROC_BOOT_CFG_FLAG_R5_BTCM_EN; else clr_cfg |= PROC_BOOT_CFG_FLAG_R5_BTCM_EN; if (core->loczrama) set_cfg |= PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE; else clr_cfg |= PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE; ret = k3_r5f_core_halt(core); if (ret) goto out; ret = ti_sci_proc_set_config(&core->tsp, boot_vec, set_cfg, clr_cfg); out: ti_sci_proc_release(&core->tsp); return ret; } static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) { u32 ids[2]; int ret; dev_dbg(dev, "%s\n", __func__); tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci"); if (IS_ERR(tsp->sci)) { dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci)); return PTR_ERR(tsp->sci); } ret = dev_read_u32_array(dev, "ti,sci-proc-ids", ids, 2); if (ret) { dev_err(dev, "Proc IDs not populated %d\n", ret); return ret; } tsp->ops = &tsp->sci->ops.proc_ops; tsp->proc_id = ids[0]; tsp->host_id = ids[1]; tsp->dev_id = dev_read_u32_default(dev, "ti,sci-dev-id", TI_SCI_RESOURCE_NULL); if (tsp->dev_id == TI_SCI_RESOURCE_NULL) { dev_err(dev, "Device ID not populated %d\n", ret); return -ENODEV; } return 0; } static int k3_r5f_of_to_priv(struct k3_r5f_core *core) { int ret; dev_dbg(core->dev, "%s\n", __func__); core->atcm_enable = dev_read_u32_default(core->dev, "ti,atcm-enable", 0); core->btcm_enable = dev_read_u32_default(core->dev, "ti,btcm-enable", 1); core->loczrama = dev_read_u32_default(core->dev, "ti,loczrama", 1); ret = ti_sci_proc_of_to_priv(core->dev, &core->tsp); if (ret) return ret; ret = reset_get_by_index(core->dev, 0, &core->reset); if (ret) { dev_err(core->dev, "Reset lines not available: %d\n", ret); return ret; } core->ipdata = (struct k3_r5f_ip_data *)dev_get_driver_data(core->dev); return 0; } static int k3_r5f_core_of_get_memories(struct k3_r5f_core *core) { static const char * const mem_names[] = {"atcm", "btcm"}; struct udevice *dev = core->dev; int i; dev_dbg(dev, "%s\n", __func__); core->num_mems = ARRAY_SIZE(mem_names); core->mem = calloc(core->num_mems, sizeof(*core->mem)); if (!core->mem) return -ENOMEM; for (i = 0; i < core->num_mems; i++) { core->mem[i].bus_addr = dev_read_addr_size_name(dev, mem_names[i], (fdt_addr_t *)&core->mem[i].size); if (core->mem[i].bus_addr == FDT_ADDR_T_NONE) { dev_err(dev, "%s bus address not found\n", mem_names[i]); return -EINVAL; } core->mem[i].cpu_addr = map_physmem(core->mem[i].bus_addr, core->mem[i].size, MAP_NOCACHE); if (!strcmp(mem_names[i], "atcm")) { core->mem[i].dev_addr = core->loczrama ? 0 : K3_R5_TCM_DEV_ADDR; } else { core->mem[i].dev_addr = core->loczrama ? K3_R5_TCM_DEV_ADDR : 0; } dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da 0x%x\n", mem_names[i], &core->mem[i].bus_addr, core->mem[i].size, core->mem[i].cpu_addr, core->mem[i].dev_addr); } return 0; } /* * Each R5F core within a typical R5FSS instance has a total of 64 KB of TCMs, * split equally into two 32 KB banks between ATCM and BTCM. The TCMs from both * cores are usable in Split-mode, but only the Core0 TCMs can be used in * LockStep-mode. The newer revisions of the R5FSS IP maximizes these TCMs by * leveraging the Core1 TCMs as well in certain modes where they would have * otherwise been unusable (Eg: LockStep-mode on J7200 SoCs). This is done by * making a Core1 TCM visible immediately after the corresponding Core0 TCM. * The SoC memory map uses the larger 64 KB sizes for the Core0 TCMs, and the * dts representation reflects this increased size on supported SoCs. The Core0 * TCM sizes therefore have to be adjusted to only half the original size in * Split mode. */ static void k3_r5f_core_adjust_tcm_sizes(struct k3_r5f_core *core) { struct k3_r5f_cluster *cluster = core->cluster; if (cluster->mode == CLUSTER_MODE_LOCKSTEP) return; if (!core->ipdata->tcm_is_double) return; if (core == cluster->cores[0]) { core->mem[0].size /= 2; core->mem[1].size /= 2; dev_dbg(core->dev, "adjusted TCM sizes, ATCM = 0x%zx BTCM = 0x%zx\n", core->mem[0].size, core->mem[1].size); } } /** * k3_r5f_probe() - Basic probe * @dev: corresponding k3 remote processor device * * Return: 0 if all goes good, else appropriate error message. */ static int k3_r5f_probe(struct udevice *dev) { struct k3_r5f_cluster *cluster = dev_get_priv(dev->parent); struct k3_r5f_core *core = dev_get_priv(dev); bool r_state; int ret; dev_dbg(dev, "%s\n", __func__); core->dev = dev; ret = k3_r5f_of_to_priv(core); if (ret) return ret; core->cluster = cluster; /* Assume Primary core gets probed first */ if (!cluster->cores[0]) cluster->cores[0] = core; else cluster->cores[1] = core; ret = k3_r5f_core_of_get_memories(core); if (ret) { dev_err(dev, "Rproc getting internal memories failed\n"); return ret; } /* * The PM functionality is not supported by the firmware during * SPL execution with the separated DM firmware image. The following * piece of code is not compiled in that case. */ if (!IS_ENABLED(CONFIG_K3_DM_FW)) { ret = core->tsp.sci->ops.dev_ops.is_on(core->tsp.sci, core->tsp.dev_id, &r_state, &core->in_use); if (ret) return ret; if (core->in_use) { dev_info(dev, "Core %d is already in use. No rproc commands work\n", core->tsp.proc_id); return 0; } /* Make sure Local reset is asserted. Redundant? */ reset_assert(&core->reset); } ret = k3_r5f_rproc_configure(core); if (ret) { dev_err(dev, "rproc configure failed %d\n", ret); return ret; } k3_r5f_core_adjust_tcm_sizes(core); dev_dbg(dev, "Remoteproc successfully probed\n"); return 0; } static int k3_r5f_remove(struct udevice *dev) { struct k3_r5f_core *core = dev_get_priv(dev); free(core->mem); ti_sci_proc_release(&core->tsp); return 0; } static const struct k3_r5f_ip_data k3_data = { .tcm_is_double = false, .tcm_ecc_autoinit = false, }; static const struct k3_r5f_ip_data j7200_data = { .tcm_is_double = true, .tcm_ecc_autoinit = true, }; static const struct udevice_id k3_r5f_rproc_ids[] = { { .compatible = "ti,am654-r5f", .data = (ulong)&k3_data, }, { .compatible = "ti,j721e-r5f", .data = (ulong)&k3_data, }, { .compatible = "ti,j7200-r5f", .data = (ulong)&j7200_data, }, {} }; U_BOOT_DRIVER(k3_r5f_rproc) = { .name = "k3_r5f_rproc", .of_match = k3_r5f_rproc_ids, .id = UCLASS_REMOTEPROC, .ops = &k3_r5f_rproc_ops, .probe = k3_r5f_probe, .remove = k3_r5f_remove, .priv_auto = sizeof(struct k3_r5f_core), }; static int k3_r5f_cluster_probe(struct udevice *dev) { struct k3_r5f_cluster *cluster = dev_get_priv(dev); dev_dbg(dev, "%s\n", __func__); cluster->mode = dev_read_u32_default(dev, "ti,cluster-mode", CLUSTER_MODE_LOCKSTEP); if (device_get_child_count(dev) != 2) { dev_err(dev, "Invalid number of R5 cores"); return -EINVAL; } dev_dbg(dev, "%s: Cluster successfully probed in %s mode\n", __func__, cluster->mode ? "lockstep" : "split"); return 0; } static const struct udevice_id k3_r5fss_ids[] = { { .compatible = "ti,am654-r5fss"}, { .compatible = "ti,j721e-r5fss"}, { .compatible = "ti,j7200-r5fss"}, {} }; U_BOOT_DRIVER(k3_r5fss) = { .name = "k3_r5fss", .of_match = k3_r5fss_ids, .id = UCLASS_MISC, .probe = k3_r5f_cluster_probe, .priv_auto = sizeof(struct k3_r5f_cluster), .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, };