xref: /optee_os/core/kernel/dt_driver.c (revision f498c4042931108699ecc3d692cd2250700f8756)
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 }
107*f498c404SEtienne Carriere 
108*f498c404SEtienne Carriere struct dt_driver_provider *dt_driver_get_provider_by_node(int nodeoffset)
109*f498c404SEtienne Carriere {
110*f498c404SEtienne Carriere 	struct dt_driver_provider *prv = NULL;
111*f498c404SEtienne Carriere 
112*f498c404SEtienne Carriere 	SLIST_FOREACH(prv, &dt_driver_provider_list, link)
113*f498c404SEtienne Carriere 		if (prv->nodeoffset == nodeoffset)
114*f498c404SEtienne Carriere 			return prv;
115*f498c404SEtienne Carriere 
116*f498c404SEtienne Carriere 	return NULL;
117*f498c404SEtienne Carriere }
118*f498c404SEtienne Carriere 
119*f498c404SEtienne Carriere struct dt_driver_provider *dt_driver_get_provider_by_phandle(uint32_t phandle)
120*f498c404SEtienne Carriere {
121*f498c404SEtienne Carriere 	struct dt_driver_provider *prv = NULL;
122*f498c404SEtienne Carriere 
123*f498c404SEtienne Carriere 	SLIST_FOREACH(prv, &dt_driver_provider_list, link)
124*f498c404SEtienne Carriere 		if (prv->phandle == phandle)
125*f498c404SEtienne Carriere 			return prv;
126*f498c404SEtienne Carriere 
127*f498c404SEtienne Carriere 	return NULL;
128*f498c404SEtienne Carriere }
129