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