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 15704f6eddSEtienne Carriere /* 16704f6eddSEtienne Carriere * struct dt_driver_provider - DT related info on probed device 17704f6eddSEtienne Carriere * 18704f6eddSEtienne Carriere * Saves information on the probed device so that device 19704f6eddSEtienne Carriere * drivers can get resources from DT phandle and related arguments. 20704f6eddSEtienne Carriere * 21704f6eddSEtienne Carriere * @nodeoffset: Node offset of device referenced in the FDT 22704f6eddSEtienne Carriere * @type: One of DT_DRIVER_* or DT_DRIVER_NOTYPE. 23704f6eddSEtienne Carriere * @provider_cells: Cells count in the FDT used by the driver's references 24704f6eddSEtienne Carriere * @get_of_device: Function to get driver's device ref from phandle data 25704f6eddSEtienne Carriere * @priv_data: Driver private data passed as @get_of_device argument 26704f6eddSEtienne Carriere * @link: Reference in DT driver providers list 27704f6eddSEtienne Carriere */ 28704f6eddSEtienne Carriere struct dt_driver_provider { 29704f6eddSEtienne Carriere int nodeoffset; 30704f6eddSEtienne Carriere enum dt_driver_type type; 31704f6eddSEtienne Carriere unsigned int provider_cells; 32704f6eddSEtienne Carriere uint32_t phandle; 33704f6eddSEtienne Carriere get_of_device_func get_of_device; 34704f6eddSEtienne Carriere void *priv_data; 35704f6eddSEtienne Carriere SLIST_ENTRY(dt_driver_provider) link; 36704f6eddSEtienne Carriere }; 37704f6eddSEtienne Carriere 38704f6eddSEtienne 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, 154*d8b14b46SEtienne Carriere const uint32_t *prop, 155*d8b14b46SEtienne Carriere TEE_Result *res) 156a22e85b2SEtienne Carriere { 157a22e85b2SEtienne Carriere struct dt_driver_phandle_args *pargs = NULL; 158a22e85b2SEtienne Carriere unsigned int n = 0; 159a22e85b2SEtienne Carriere void *device = NULL; 160a22e85b2SEtienne Carriere 161a22e85b2SEtienne Carriere pargs = calloc(1, prv->provider_cells * sizeof(uint32_t *) + 162a22e85b2SEtienne Carriere sizeof(*pargs)); 163*d8b14b46SEtienne Carriere if (!pargs) { 164*d8b14b46SEtienne Carriere *res = TEE_ERROR_OUT_OF_MEMORY; 165a22e85b2SEtienne Carriere return NULL; 166*d8b14b46SEtienne Carriere } 167a22e85b2SEtienne Carriere 168a22e85b2SEtienne Carriere pargs->args_count = prv->provider_cells; 169a22e85b2SEtienne Carriere for (n = 0; n < prv->provider_cells; n++) 170a22e85b2SEtienne Carriere pargs->args[n] = fdt32_to_cpu(prop[n + 1]); 171a22e85b2SEtienne Carriere 172*d8b14b46SEtienne Carriere device = prv->get_of_device(pargs, prv->priv_data, res); 173a22e85b2SEtienne Carriere 174a22e85b2SEtienne Carriere free(pargs); 175a22e85b2SEtienne Carriere 176a22e85b2SEtienne Carriere return device; 177a22e85b2SEtienne Carriere } 178a22e85b2SEtienne Carriere 179a22e85b2SEtienne Carriere void *dt_driver_device_from_node_idx_prop(const char *prop_name, 180a22e85b2SEtienne Carriere const void *fdt, int nodeoffset, 181*d8b14b46SEtienne Carriere unsigned int prop_idx, 182*d8b14b46SEtienne Carriere TEE_Result *res) 183a22e85b2SEtienne Carriere { 184a22e85b2SEtienne Carriere int len = 0; 185a22e85b2SEtienne Carriere int idx = 0; 186a22e85b2SEtienne Carriere int idx32 = 0; 187a22e85b2SEtienne Carriere int prv_cells = 0; 188a22e85b2SEtienne Carriere uint32_t phandle = 0; 189a22e85b2SEtienne Carriere const uint32_t *prop = NULL; 190a22e85b2SEtienne Carriere struct dt_driver_provider *prv = NULL; 191a22e85b2SEtienne Carriere 192a22e85b2SEtienne Carriere prop = fdt_getprop(fdt, nodeoffset, prop_name, &len); 193*d8b14b46SEtienne Carriere if (!prop) { 194*d8b14b46SEtienne Carriere *res = TEE_ERROR_GENERIC; 195a22e85b2SEtienne Carriere return NULL; 196*d8b14b46SEtienne Carriere } 197a22e85b2SEtienne Carriere 198a22e85b2SEtienne Carriere while (idx < len) { 199a22e85b2SEtienne Carriere idx32 = idx / sizeof(uint32_t); 200a22e85b2SEtienne Carriere phandle = fdt32_to_cpu(prop[idx32]); 201a22e85b2SEtienne Carriere 202a22e85b2SEtienne Carriere prv = dt_driver_get_provider_by_phandle(phandle); 203*d8b14b46SEtienne Carriere if (!prv) { 204*d8b14b46SEtienne Carriere *res = TEE_ERROR_GENERIC; 205a22e85b2SEtienne Carriere return NULL; 206*d8b14b46SEtienne Carriere } 207a22e85b2SEtienne Carriere 208a22e85b2SEtienne Carriere prv_cells = dt_driver_provider_cells(prv); 209a22e85b2SEtienne Carriere if (prop_idx) { 210a22e85b2SEtienne Carriere prop_idx--; 211a22e85b2SEtienne Carriere idx += sizeof(phandle) + prv_cells * sizeof(uint32_t); 212a22e85b2SEtienne Carriere continue; 213a22e85b2SEtienne Carriere } 214a22e85b2SEtienne Carriere 215*d8b14b46SEtienne Carriere return device_from_provider_prop(prv, prop + idx32, res); 216a22e85b2SEtienne Carriere } 217a22e85b2SEtienne Carriere 218*d8b14b46SEtienne Carriere *res = TEE_ERROR_GENERIC; 219a22e85b2SEtienne Carriere return NULL; 220a22e85b2SEtienne Carriere } 221ef20efc4SEtienne Carriere 222ef20efc4SEtienne Carriere /* Lookup a compatible driver, possibly of a specific @type, for the FDT node */ 223ef20efc4SEtienne Carriere static TEE_Result probe_device_by_compat(const void *fdt, int node, 224ef20efc4SEtienne Carriere const char *compat, 225ef20efc4SEtienne Carriere enum dt_driver_type type) 226ef20efc4SEtienne Carriere { 227ef20efc4SEtienne Carriere const struct dt_driver *drv = NULL; 228ef20efc4SEtienne Carriere const struct dt_device_match *dm = NULL; 229ef20efc4SEtienne Carriere 230ef20efc4SEtienne Carriere for_each_dt_driver(drv) { 231ef20efc4SEtienne Carriere if (drv->type != type) 232ef20efc4SEtienne Carriere continue; 233ef20efc4SEtienne Carriere 234ef20efc4SEtienne Carriere for (dm = drv->match_table; dm && dm->compatible; dm++) 235ef20efc4SEtienne Carriere if (strcmp(dm->compatible, compat) == 0) 236ef20efc4SEtienne Carriere return drv->probe(fdt, node, dm->compat_data); 237ef20efc4SEtienne Carriere } 238ef20efc4SEtienne Carriere 239ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 240ef20efc4SEtienne Carriere } 241ef20efc4SEtienne Carriere 242ef20efc4SEtienne Carriere TEE_Result dt_driver_probe_device_by_node(const void *fdt, int nodeoffset, 243ef20efc4SEtienne Carriere enum dt_driver_type type) 244ef20efc4SEtienne Carriere { 245ef20efc4SEtienne Carriere int idx = 0; 246ef20efc4SEtienne Carriere int len = 0; 247ef20efc4SEtienne Carriere int count = 0; 248ef20efc4SEtienne Carriere const char *compat = NULL; 249ef20efc4SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 250ef20efc4SEtienne Carriere 251ef20efc4SEtienne Carriere count = fdt_stringlist_count(fdt, nodeoffset, "compatible"); 252ef20efc4SEtienne Carriere if (count < 0) 253ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 254ef20efc4SEtienne Carriere 255ef20efc4SEtienne Carriere for (idx = 0; idx < count; idx++) { 256ef20efc4SEtienne Carriere compat = fdt_stringlist_get(fdt, nodeoffset, "compatible", 257ef20efc4SEtienne Carriere idx, &len); 258ef20efc4SEtienne Carriere if (!compat) 259ef20efc4SEtienne Carriere return TEE_ERROR_GENERIC; 260ef20efc4SEtienne Carriere 261ef20efc4SEtienne Carriere res = probe_device_by_compat(fdt, nodeoffset, compat, type); 262ef20efc4SEtienne Carriere 263ef20efc4SEtienne Carriere if (res != TEE_ERROR_ITEM_NOT_FOUND) 264ef20efc4SEtienne Carriere return res; 265ef20efc4SEtienne Carriere } 266ef20efc4SEtienne Carriere 267ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 268ef20efc4SEtienne Carriere } 269