18c0c44c9SEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause 28c0c44c9SEtienne Carriere /* 38c0c44c9SEtienne Carriere * Copyright (c) 2021, Linaro Limited 48c0c44c9SEtienne Carriere * Copyright (c) 2021, Bootlin 58c0c44c9SEtienne Carriere */ 68c0c44c9SEtienne Carriere 73fd340e5SEtienne Carriere #include <initcall.h> 83fd340e5SEtienne Carriere #include <kernel/dt.h> 98c0c44c9SEtienne Carriere #include <kernel/dt_driver.h> 103fd340e5SEtienne Carriere #include <libfdt.h> 113fd340e5SEtienne Carriere #include <malloc.h> 128c0c44c9SEtienne Carriere #include <sys/queue.h> 133fd340e5SEtienne Carriere #include <tee_api_types.h> 148c0c44c9SEtienne Carriere 15*704f6eddSEtienne Carriere /* 16*704f6eddSEtienne Carriere * struct dt_driver_provider - DT related info on probed device 17*704f6eddSEtienne Carriere * 18*704f6eddSEtienne Carriere * Saves information on the probed device so that device 19*704f6eddSEtienne Carriere * drivers can get resources from DT phandle and related arguments. 20*704f6eddSEtienne Carriere * 21*704f6eddSEtienne Carriere * @nodeoffset: Node offset of device referenced in the FDT 22*704f6eddSEtienne Carriere * @type: One of DT_DRIVER_* or DT_DRIVER_NOTYPE. 23*704f6eddSEtienne Carriere * @provider_cells: Cells count in the FDT used by the driver's references 24*704f6eddSEtienne Carriere * @get_of_device: Function to get driver's device ref from phandle data 25*704f6eddSEtienne Carriere * @priv_data: Driver private data passed as @get_of_device argument 26*704f6eddSEtienne Carriere * @link: Reference in DT driver providers list 27*704f6eddSEtienne Carriere */ 28*704f6eddSEtienne Carriere struct dt_driver_provider { 29*704f6eddSEtienne Carriere int nodeoffset; 30*704f6eddSEtienne Carriere enum dt_driver_type type; 31*704f6eddSEtienne Carriere unsigned int provider_cells; 32*704f6eddSEtienne Carriere uint32_t phandle; 33*704f6eddSEtienne Carriere get_of_device_func get_of_device; 34*704f6eddSEtienne Carriere void *priv_data; 35*704f6eddSEtienne Carriere SLIST_ENTRY(dt_driver_provider) link; 36*704f6eddSEtienne Carriere }; 37*704f6eddSEtienne Carriere 38*704f6eddSEtienne Carriere static SLIST_HEAD(, dt_driver_provider) dt_driver_provider_list = 398c0c44c9SEtienne Carriere SLIST_HEAD_INITIALIZER(dt_driver_provider_list); 403fd340e5SEtienne Carriere 413fd340e5SEtienne Carriere /* 423fd340e5SEtienne Carriere * Driver provider registering API functions 433fd340e5SEtienne Carriere */ 443fd340e5SEtienne Carriere 453fd340e5SEtienne Carriere TEE_Result dt_driver_register_provider(const void *fdt, int nodeoffset, 463fd340e5SEtienne Carriere get_of_device_func get_of_device, 473fd340e5SEtienne Carriere void *priv, enum dt_driver_type type) 483fd340e5SEtienne Carriere { 493fd340e5SEtienne Carriere struct dt_driver_provider *prv = NULL; 503fd340e5SEtienne Carriere int provider_cells = 0; 513fd340e5SEtienne Carriere uint32_t phandle = 0; 523fd340e5SEtienne Carriere 533fd340e5SEtienne Carriere provider_cells = fdt_get_dt_driver_cells(fdt, nodeoffset, type); 543fd340e5SEtienne Carriere if (provider_cells < 0) { 553fd340e5SEtienne Carriere DMSG("Failed to find provider cells: %d", provider_cells); 563fd340e5SEtienne Carriere return TEE_ERROR_GENERIC; 573fd340e5SEtienne Carriere } 583fd340e5SEtienne Carriere 593fd340e5SEtienne Carriere phandle = fdt_get_phandle(fdt, nodeoffset); 603fd340e5SEtienne Carriere if (!phandle || phandle == (uint32_t)-1) { 613fd340e5SEtienne Carriere DMSG("Failed to find provide phandle"); 623fd340e5SEtienne Carriere return TEE_ERROR_GENERIC; 633fd340e5SEtienne Carriere } 643fd340e5SEtienne Carriere 653fd340e5SEtienne Carriere prv = calloc(1, sizeof(*prv)); 663fd340e5SEtienne Carriere if (!prv) 673fd340e5SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 683fd340e5SEtienne Carriere 693fd340e5SEtienne Carriere prv->nodeoffset = nodeoffset; 703fd340e5SEtienne Carriere prv->type = type; 713fd340e5SEtienne Carriere prv->provider_cells = provider_cells; 723fd340e5SEtienne Carriere prv->phandle = phandle; 733fd340e5SEtienne Carriere prv->get_of_device = get_of_device; 743fd340e5SEtienne Carriere prv->priv_data = priv; 753fd340e5SEtienne Carriere 763fd340e5SEtienne Carriere SLIST_INSERT_HEAD(&dt_driver_provider_list, prv, link); 773fd340e5SEtienne Carriere 783fd340e5SEtienne Carriere return TEE_SUCCESS; 793fd340e5SEtienne Carriere } 803fd340e5SEtienne Carriere 813fd340e5SEtienne Carriere /* Release driver provider references once all dt_drivers are initialized */ 823fd340e5SEtienne Carriere static TEE_Result dt_driver_release_provider(void) 833fd340e5SEtienne Carriere { 843fd340e5SEtienne Carriere struct dt_driver_provider *prv = NULL; 853fd340e5SEtienne Carriere 863fd340e5SEtienne Carriere while (!SLIST_EMPTY(&dt_driver_provider_list)) { 873fd340e5SEtienne Carriere prv = SLIST_FIRST(&dt_driver_provider_list); 883fd340e5SEtienne Carriere SLIST_REMOVE_HEAD(&dt_driver_provider_list, link); 893fd340e5SEtienne Carriere free(prv); 903fd340e5SEtienne Carriere } 913fd340e5SEtienne Carriere 923fd340e5SEtienne Carriere return TEE_SUCCESS; 933fd340e5SEtienne Carriere } 943fd340e5SEtienne Carriere 953fd340e5SEtienne Carriere driver_init_late(dt_driver_release_provider); 963fd340e5SEtienne Carriere 973fd340e5SEtienne Carriere /* 983fd340e5SEtienne Carriere * Helper functions for dt_drivers querying driver provider information 993fd340e5SEtienne Carriere */ 1003fd340e5SEtienne Carriere 1013fd340e5SEtienne Carriere int fdt_get_dt_driver_cells(const void *fdt, int nodeoffset, 1023fd340e5SEtienne Carriere enum dt_driver_type type) 1033fd340e5SEtienne Carriere { 1043fd340e5SEtienne Carriere const char *cells_name = NULL; 1053fd340e5SEtienne Carriere const fdt32_t *c = NULL; 1063fd340e5SEtienne Carriere int len = 0; 1073fd340e5SEtienne Carriere 1083fd340e5SEtienne Carriere switch (type) { 1093fd340e5SEtienne Carriere case DT_DRIVER_CLK: 1103fd340e5SEtienne Carriere cells_name = "#clock-cells"; 1113fd340e5SEtienne Carriere break; 1123fd340e5SEtienne Carriere default: 1133fd340e5SEtienne Carriere panic(); 1143fd340e5SEtienne Carriere } 1153fd340e5SEtienne Carriere 1163fd340e5SEtienne Carriere c = fdt_getprop(fdt, nodeoffset, cells_name, &len); 1173fd340e5SEtienne Carriere if (!c) 1183fd340e5SEtienne Carriere return len; 1193fd340e5SEtienne Carriere 1203fd340e5SEtienne Carriere if (len != sizeof(*c)) 1213fd340e5SEtienne Carriere return -FDT_ERR_BADNCELLS; 1223fd340e5SEtienne Carriere 1233fd340e5SEtienne Carriere return fdt32_to_cpu(*c); 1243fd340e5SEtienne Carriere } 1253fd340e5SEtienne Carriere 1263fd340e5SEtienne Carriere unsigned int dt_driver_provider_cells(struct dt_driver_provider *prv) 1273fd340e5SEtienne Carriere { 1283fd340e5SEtienne Carriere return prv->provider_cells; 1293fd340e5SEtienne Carriere } 130f498c404SEtienne Carriere 131f498c404SEtienne Carriere struct dt_driver_provider *dt_driver_get_provider_by_node(int nodeoffset) 132f498c404SEtienne Carriere { 133f498c404SEtienne Carriere struct dt_driver_provider *prv = NULL; 134f498c404SEtienne Carriere 135f498c404SEtienne Carriere SLIST_FOREACH(prv, &dt_driver_provider_list, link) 136f498c404SEtienne Carriere if (prv->nodeoffset == nodeoffset) 137f498c404SEtienne Carriere return prv; 138f498c404SEtienne Carriere 139f498c404SEtienne Carriere return NULL; 140f498c404SEtienne Carriere } 141f498c404SEtienne Carriere 142f498c404SEtienne Carriere struct dt_driver_provider *dt_driver_get_provider_by_phandle(uint32_t phandle) 143f498c404SEtienne Carriere { 144f498c404SEtienne Carriere struct dt_driver_provider *prv = NULL; 145f498c404SEtienne Carriere 146f498c404SEtienne Carriere SLIST_FOREACH(prv, &dt_driver_provider_list, link) 147f498c404SEtienne Carriere if (prv->phandle == phandle) 148f498c404SEtienne Carriere return prv; 149f498c404SEtienne Carriere 150f498c404SEtienne Carriere return NULL; 151f498c404SEtienne Carriere } 152a22e85b2SEtienne Carriere 153a22e85b2SEtienne Carriere static void *device_from_provider_prop(struct dt_driver_provider *prv, 154a22e85b2SEtienne Carriere const uint32_t *prop) 155a22e85b2SEtienne Carriere { 156a22e85b2SEtienne Carriere struct dt_driver_phandle_args *pargs = NULL; 157a22e85b2SEtienne Carriere unsigned int n = 0; 158a22e85b2SEtienne Carriere void *device = NULL; 159a22e85b2SEtienne Carriere 160a22e85b2SEtienne Carriere pargs = calloc(1, prv->provider_cells * sizeof(uint32_t *) + 161a22e85b2SEtienne Carriere sizeof(*pargs)); 162a22e85b2SEtienne Carriere if (!pargs) 163a22e85b2SEtienne Carriere return NULL; 164a22e85b2SEtienne Carriere 165a22e85b2SEtienne Carriere pargs->args_count = prv->provider_cells; 166a22e85b2SEtienne Carriere for (n = 0; n < prv->provider_cells; n++) 167a22e85b2SEtienne Carriere pargs->args[n] = fdt32_to_cpu(prop[n + 1]); 168a22e85b2SEtienne Carriere 169a22e85b2SEtienne Carriere device = prv->get_of_device(pargs, prv->priv_data); 170a22e85b2SEtienne Carriere 171a22e85b2SEtienne Carriere free(pargs); 172a22e85b2SEtienne Carriere 173a22e85b2SEtienne Carriere return device; 174a22e85b2SEtienne Carriere } 175a22e85b2SEtienne Carriere 176a22e85b2SEtienne Carriere void *dt_driver_device_from_node_idx_prop(const char *prop_name, 177a22e85b2SEtienne Carriere const void *fdt, int nodeoffset, 178a22e85b2SEtienne Carriere unsigned int prop_idx) 179a22e85b2SEtienne Carriere { 180a22e85b2SEtienne Carriere int len = 0; 181a22e85b2SEtienne Carriere int idx = 0; 182a22e85b2SEtienne Carriere int idx32 = 0; 183a22e85b2SEtienne Carriere int prv_cells = 0; 184a22e85b2SEtienne Carriere uint32_t phandle = 0; 185a22e85b2SEtienne Carriere const uint32_t *prop = NULL; 186a22e85b2SEtienne Carriere struct dt_driver_provider *prv = NULL; 187a22e85b2SEtienne Carriere 188a22e85b2SEtienne Carriere prop = fdt_getprop(fdt, nodeoffset, prop_name, &len); 189a22e85b2SEtienne Carriere if (!prop) 190a22e85b2SEtienne Carriere return NULL; 191a22e85b2SEtienne Carriere 192a22e85b2SEtienne Carriere while (idx < len) { 193a22e85b2SEtienne Carriere idx32 = idx / sizeof(uint32_t); 194a22e85b2SEtienne Carriere phandle = fdt32_to_cpu(prop[idx32]); 195a22e85b2SEtienne Carriere 196a22e85b2SEtienne Carriere prv = dt_driver_get_provider_by_phandle(phandle); 197a22e85b2SEtienne Carriere if (!prv) 198a22e85b2SEtienne Carriere return NULL; 199a22e85b2SEtienne Carriere 200a22e85b2SEtienne Carriere prv_cells = dt_driver_provider_cells(prv); 201a22e85b2SEtienne Carriere if (prop_idx) { 202a22e85b2SEtienne Carriere prop_idx--; 203a22e85b2SEtienne Carriere idx += sizeof(phandle) + prv_cells * sizeof(uint32_t); 204a22e85b2SEtienne Carriere continue; 205a22e85b2SEtienne Carriere } 206a22e85b2SEtienne Carriere 207a22e85b2SEtienne Carriere return device_from_provider_prop(prv, prop + idx32); 208a22e85b2SEtienne Carriere } 209a22e85b2SEtienne Carriere 210a22e85b2SEtienne Carriere return NULL; 211a22e85b2SEtienne Carriere } 212ef20efc4SEtienne Carriere 213ef20efc4SEtienne Carriere /* Lookup a compatible driver, possibly of a specific @type, for the FDT node */ 214ef20efc4SEtienne Carriere static TEE_Result probe_device_by_compat(const void *fdt, int node, 215ef20efc4SEtienne Carriere const char *compat, 216ef20efc4SEtienne Carriere enum dt_driver_type type) 217ef20efc4SEtienne Carriere { 218ef20efc4SEtienne Carriere const struct dt_driver *drv = NULL; 219ef20efc4SEtienne Carriere const struct dt_device_match *dm = NULL; 220ef20efc4SEtienne Carriere 221ef20efc4SEtienne Carriere for_each_dt_driver(drv) { 222ef20efc4SEtienne Carriere if (drv->type != type) 223ef20efc4SEtienne Carriere continue; 224ef20efc4SEtienne Carriere 225ef20efc4SEtienne Carriere for (dm = drv->match_table; dm && dm->compatible; dm++) 226ef20efc4SEtienne Carriere if (strcmp(dm->compatible, compat) == 0) 227ef20efc4SEtienne Carriere return drv->probe(fdt, node, dm->compat_data); 228ef20efc4SEtienne Carriere } 229ef20efc4SEtienne Carriere 230ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 231ef20efc4SEtienne Carriere } 232ef20efc4SEtienne Carriere 233ef20efc4SEtienne Carriere TEE_Result dt_driver_probe_device_by_node(const void *fdt, int nodeoffset, 234ef20efc4SEtienne Carriere enum dt_driver_type type) 235ef20efc4SEtienne Carriere { 236ef20efc4SEtienne Carriere int idx = 0; 237ef20efc4SEtienne Carriere int len = 0; 238ef20efc4SEtienne Carriere int count = 0; 239ef20efc4SEtienne Carriere const char *compat = NULL; 240ef20efc4SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 241ef20efc4SEtienne Carriere 242ef20efc4SEtienne Carriere count = fdt_stringlist_count(fdt, nodeoffset, "compatible"); 243ef20efc4SEtienne Carriere if (count < 0) 244ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 245ef20efc4SEtienne Carriere 246ef20efc4SEtienne Carriere for (idx = 0; idx < count; idx++) { 247ef20efc4SEtienne Carriere compat = fdt_stringlist_get(fdt, nodeoffset, "compatible", 248ef20efc4SEtienne Carriere idx, &len); 249ef20efc4SEtienne Carriere if (!compat) 250ef20efc4SEtienne Carriere return TEE_ERROR_GENERIC; 251ef20efc4SEtienne Carriere 252ef20efc4SEtienne Carriere res = probe_device_by_compat(fdt, nodeoffset, compat, type); 253ef20efc4SEtienne Carriere 254ef20efc4SEtienne Carriere if (res != TEE_ERROR_ITEM_NOT_FOUND) 255ef20efc4SEtienne Carriere return res; 256ef20efc4SEtienne Carriere } 257ef20efc4SEtienne Carriere 258ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 259ef20efc4SEtienne Carriere } 260