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 158c0c44c9SEtienne Carriere struct dt_driver_prov_list dt_driver_provider_list = 168c0c44c9SEtienne Carriere SLIST_HEAD_INITIALIZER(dt_driver_provider_list); 173fd340e5SEtienne Carriere 183fd340e5SEtienne Carriere /* 193fd340e5SEtienne Carriere * Driver provider registering API functions 203fd340e5SEtienne Carriere */ 213fd340e5SEtienne Carriere 223fd340e5SEtienne Carriere TEE_Result dt_driver_register_provider(const void *fdt, int nodeoffset, 233fd340e5SEtienne Carriere get_of_device_func get_of_device, 243fd340e5SEtienne Carriere void *priv, enum dt_driver_type type) 253fd340e5SEtienne Carriere { 263fd340e5SEtienne Carriere struct dt_driver_provider *prv = NULL; 273fd340e5SEtienne Carriere int provider_cells = 0; 283fd340e5SEtienne Carriere uint32_t phandle = 0; 293fd340e5SEtienne Carriere 303fd340e5SEtienne Carriere provider_cells = fdt_get_dt_driver_cells(fdt, nodeoffset, type); 313fd340e5SEtienne Carriere if (provider_cells < 0) { 323fd340e5SEtienne Carriere DMSG("Failed to find provider cells: %d", provider_cells); 333fd340e5SEtienne Carriere return TEE_ERROR_GENERIC; 343fd340e5SEtienne Carriere } 353fd340e5SEtienne Carriere 363fd340e5SEtienne Carriere phandle = fdt_get_phandle(fdt, nodeoffset); 373fd340e5SEtienne Carriere if (!phandle || phandle == (uint32_t)-1) { 383fd340e5SEtienne Carriere DMSG("Failed to find provide phandle"); 393fd340e5SEtienne Carriere return TEE_ERROR_GENERIC; 403fd340e5SEtienne Carriere } 413fd340e5SEtienne Carriere 423fd340e5SEtienne Carriere prv = calloc(1, sizeof(*prv)); 433fd340e5SEtienne Carriere if (!prv) 443fd340e5SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 453fd340e5SEtienne Carriere 463fd340e5SEtienne Carriere prv->nodeoffset = nodeoffset; 473fd340e5SEtienne Carriere prv->type = type; 483fd340e5SEtienne Carriere prv->provider_cells = provider_cells; 493fd340e5SEtienne Carriere prv->phandle = phandle; 503fd340e5SEtienne Carriere prv->get_of_device = get_of_device; 513fd340e5SEtienne Carriere prv->priv_data = priv; 523fd340e5SEtienne Carriere 533fd340e5SEtienne Carriere SLIST_INSERT_HEAD(&dt_driver_provider_list, prv, link); 543fd340e5SEtienne Carriere 553fd340e5SEtienne Carriere return TEE_SUCCESS; 563fd340e5SEtienne Carriere } 573fd340e5SEtienne Carriere 583fd340e5SEtienne Carriere /* Release driver provider references once all dt_drivers are initialized */ 593fd340e5SEtienne Carriere static TEE_Result dt_driver_release_provider(void) 603fd340e5SEtienne Carriere { 613fd340e5SEtienne Carriere struct dt_driver_provider *prv = NULL; 623fd340e5SEtienne Carriere 633fd340e5SEtienne Carriere while (!SLIST_EMPTY(&dt_driver_provider_list)) { 643fd340e5SEtienne Carriere prv = SLIST_FIRST(&dt_driver_provider_list); 653fd340e5SEtienne Carriere SLIST_REMOVE_HEAD(&dt_driver_provider_list, link); 663fd340e5SEtienne Carriere free(prv); 673fd340e5SEtienne Carriere } 683fd340e5SEtienne Carriere 693fd340e5SEtienne Carriere return TEE_SUCCESS; 703fd340e5SEtienne Carriere } 713fd340e5SEtienne Carriere 723fd340e5SEtienne Carriere driver_init_late(dt_driver_release_provider); 733fd340e5SEtienne Carriere 743fd340e5SEtienne Carriere /* 753fd340e5SEtienne Carriere * Helper functions for dt_drivers querying driver provider information 763fd340e5SEtienne Carriere */ 773fd340e5SEtienne Carriere 783fd340e5SEtienne Carriere int fdt_get_dt_driver_cells(const void *fdt, int nodeoffset, 793fd340e5SEtienne Carriere enum dt_driver_type type) 803fd340e5SEtienne Carriere { 813fd340e5SEtienne Carriere const char *cells_name = NULL; 823fd340e5SEtienne Carriere const fdt32_t *c = NULL; 833fd340e5SEtienne Carriere int len = 0; 843fd340e5SEtienne Carriere 853fd340e5SEtienne Carriere switch (type) { 863fd340e5SEtienne Carriere case DT_DRIVER_CLK: 873fd340e5SEtienne Carriere cells_name = "#clock-cells"; 883fd340e5SEtienne Carriere break; 893fd340e5SEtienne Carriere default: 903fd340e5SEtienne Carriere panic(); 913fd340e5SEtienne Carriere } 923fd340e5SEtienne Carriere 933fd340e5SEtienne Carriere c = fdt_getprop(fdt, nodeoffset, cells_name, &len); 943fd340e5SEtienne Carriere if (!c) 953fd340e5SEtienne Carriere return len; 963fd340e5SEtienne Carriere 973fd340e5SEtienne Carriere if (len != sizeof(*c)) 983fd340e5SEtienne Carriere return -FDT_ERR_BADNCELLS; 993fd340e5SEtienne Carriere 1003fd340e5SEtienne Carriere return fdt32_to_cpu(*c); 1013fd340e5SEtienne Carriere } 1023fd340e5SEtienne Carriere 1033fd340e5SEtienne Carriere unsigned int dt_driver_provider_cells(struct dt_driver_provider *prv) 1043fd340e5SEtienne Carriere { 1053fd340e5SEtienne Carriere return prv->provider_cells; 1063fd340e5SEtienne Carriere } 107f498c404SEtienne Carriere 108f498c404SEtienne Carriere struct dt_driver_provider *dt_driver_get_provider_by_node(int nodeoffset) 109f498c404SEtienne Carriere { 110f498c404SEtienne Carriere struct dt_driver_provider *prv = NULL; 111f498c404SEtienne Carriere 112f498c404SEtienne Carriere SLIST_FOREACH(prv, &dt_driver_provider_list, link) 113f498c404SEtienne Carriere if (prv->nodeoffset == nodeoffset) 114f498c404SEtienne Carriere return prv; 115f498c404SEtienne Carriere 116f498c404SEtienne Carriere return NULL; 117f498c404SEtienne Carriere } 118f498c404SEtienne Carriere 119f498c404SEtienne Carriere struct dt_driver_provider *dt_driver_get_provider_by_phandle(uint32_t phandle) 120f498c404SEtienne Carriere { 121f498c404SEtienne Carriere struct dt_driver_provider *prv = NULL; 122f498c404SEtienne Carriere 123f498c404SEtienne Carriere SLIST_FOREACH(prv, &dt_driver_provider_list, link) 124f498c404SEtienne Carriere if (prv->phandle == phandle) 125f498c404SEtienne Carriere return prv; 126f498c404SEtienne Carriere 127f498c404SEtienne Carriere return NULL; 128f498c404SEtienne Carriere } 129a22e85b2SEtienne Carriere 130a22e85b2SEtienne Carriere static void *device_from_provider_prop(struct dt_driver_provider *prv, 131a22e85b2SEtienne Carriere const uint32_t *prop) 132a22e85b2SEtienne Carriere { 133a22e85b2SEtienne Carriere struct dt_driver_phandle_args *pargs = NULL; 134a22e85b2SEtienne Carriere unsigned int n = 0; 135a22e85b2SEtienne Carriere void *device = NULL; 136a22e85b2SEtienne Carriere 137a22e85b2SEtienne Carriere pargs = calloc(1, prv->provider_cells * sizeof(uint32_t *) + 138a22e85b2SEtienne Carriere sizeof(*pargs)); 139a22e85b2SEtienne Carriere if (!pargs) 140a22e85b2SEtienne Carriere return NULL; 141a22e85b2SEtienne Carriere 142a22e85b2SEtienne Carriere pargs->args_count = prv->provider_cells; 143a22e85b2SEtienne Carriere for (n = 0; n < prv->provider_cells; n++) 144a22e85b2SEtienne Carriere pargs->args[n] = fdt32_to_cpu(prop[n + 1]); 145a22e85b2SEtienne Carriere 146a22e85b2SEtienne Carriere device = prv->get_of_device(pargs, prv->priv_data); 147a22e85b2SEtienne Carriere 148a22e85b2SEtienne Carriere free(pargs); 149a22e85b2SEtienne Carriere 150a22e85b2SEtienne Carriere return device; 151a22e85b2SEtienne Carriere } 152a22e85b2SEtienne Carriere 153a22e85b2SEtienne Carriere void *dt_driver_device_from_node_idx_prop(const char *prop_name, 154a22e85b2SEtienne Carriere const void *fdt, int nodeoffset, 155a22e85b2SEtienne Carriere unsigned int prop_idx) 156a22e85b2SEtienne Carriere { 157a22e85b2SEtienne Carriere int len = 0; 158a22e85b2SEtienne Carriere int idx = 0; 159a22e85b2SEtienne Carriere int idx32 = 0; 160a22e85b2SEtienne Carriere int prv_cells = 0; 161a22e85b2SEtienne Carriere uint32_t phandle = 0; 162a22e85b2SEtienne Carriere const uint32_t *prop = NULL; 163a22e85b2SEtienne Carriere struct dt_driver_provider *prv = NULL; 164a22e85b2SEtienne Carriere 165a22e85b2SEtienne Carriere prop = fdt_getprop(fdt, nodeoffset, prop_name, &len); 166a22e85b2SEtienne Carriere if (!prop) 167a22e85b2SEtienne Carriere return NULL; 168a22e85b2SEtienne Carriere 169a22e85b2SEtienne Carriere while (idx < len) { 170a22e85b2SEtienne Carriere idx32 = idx / sizeof(uint32_t); 171a22e85b2SEtienne Carriere phandle = fdt32_to_cpu(prop[idx32]); 172a22e85b2SEtienne Carriere 173a22e85b2SEtienne Carriere prv = dt_driver_get_provider_by_phandle(phandle); 174a22e85b2SEtienne Carriere if (!prv) 175a22e85b2SEtienne Carriere return NULL; 176a22e85b2SEtienne Carriere 177a22e85b2SEtienne Carriere prv_cells = dt_driver_provider_cells(prv); 178a22e85b2SEtienne Carriere if (prop_idx) { 179a22e85b2SEtienne Carriere prop_idx--; 180a22e85b2SEtienne Carriere idx += sizeof(phandle) + prv_cells * sizeof(uint32_t); 181a22e85b2SEtienne Carriere continue; 182a22e85b2SEtienne Carriere } 183a22e85b2SEtienne Carriere 184a22e85b2SEtienne Carriere return device_from_provider_prop(prv, prop + idx32); 185a22e85b2SEtienne Carriere } 186a22e85b2SEtienne Carriere 187a22e85b2SEtienne Carriere return NULL; 188a22e85b2SEtienne Carriere } 189*ef20efc4SEtienne Carriere 190*ef20efc4SEtienne Carriere /* Lookup a compatible driver, possibly of a specific @type, for the FDT node */ 191*ef20efc4SEtienne Carriere static TEE_Result probe_device_by_compat(const void *fdt, int node, 192*ef20efc4SEtienne Carriere const char *compat, 193*ef20efc4SEtienne Carriere enum dt_driver_type type) 194*ef20efc4SEtienne Carriere { 195*ef20efc4SEtienne Carriere const struct dt_driver *drv = NULL; 196*ef20efc4SEtienne Carriere const struct dt_device_match *dm = NULL; 197*ef20efc4SEtienne Carriere 198*ef20efc4SEtienne Carriere for_each_dt_driver(drv) { 199*ef20efc4SEtienne Carriere if (drv->type != type) 200*ef20efc4SEtienne Carriere continue; 201*ef20efc4SEtienne Carriere 202*ef20efc4SEtienne Carriere for (dm = drv->match_table; dm && dm->compatible; dm++) 203*ef20efc4SEtienne Carriere if (strcmp(dm->compatible, compat) == 0) 204*ef20efc4SEtienne Carriere return drv->probe(fdt, node, dm->compat_data); 205*ef20efc4SEtienne Carriere } 206*ef20efc4SEtienne Carriere 207*ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 208*ef20efc4SEtienne Carriere } 209*ef20efc4SEtienne Carriere 210*ef20efc4SEtienne Carriere TEE_Result dt_driver_probe_device_by_node(const void *fdt, int nodeoffset, 211*ef20efc4SEtienne Carriere enum dt_driver_type type) 212*ef20efc4SEtienne Carriere { 213*ef20efc4SEtienne Carriere int idx = 0; 214*ef20efc4SEtienne Carriere int len = 0; 215*ef20efc4SEtienne Carriere int count = 0; 216*ef20efc4SEtienne Carriere const char *compat = NULL; 217*ef20efc4SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 218*ef20efc4SEtienne Carriere 219*ef20efc4SEtienne Carriere count = fdt_stringlist_count(fdt, nodeoffset, "compatible"); 220*ef20efc4SEtienne Carriere if (count < 0) 221*ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 222*ef20efc4SEtienne Carriere 223*ef20efc4SEtienne Carriere for (idx = 0; idx < count; idx++) { 224*ef20efc4SEtienne Carriere compat = fdt_stringlist_get(fdt, nodeoffset, "compatible", 225*ef20efc4SEtienne Carriere idx, &len); 226*ef20efc4SEtienne Carriere if (!compat) 227*ef20efc4SEtienne Carriere return TEE_ERROR_GENERIC; 228*ef20efc4SEtienne Carriere 229*ef20efc4SEtienne Carriere res = probe_device_by_compat(fdt, nodeoffset, compat, type); 230*ef20efc4SEtienne Carriere 231*ef20efc4SEtienne Carriere if (res != TEE_ERROR_ITEM_NOT_FOUND) 232*ef20efc4SEtienne Carriere return res; 233*ef20efc4SEtienne Carriere } 234*ef20efc4SEtienne Carriere 235*ef20efc4SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 236*ef20efc4SEtienne Carriere } 237