xref: /optee_os/core/kernel/dt_driver.c (revision ef20efc4448ce8a83ed420834b47b07e379545bb)
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