xref: /optee_os/core/kernel/dt.c (revision 1527e616e749e48b4c021b1ceb1b731c86c9cdbc)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
2a4f139d7SJerome Forissier /*
3a4f139d7SJerome Forissier  * Copyright (c) 2016, Linaro Limited
4a4f139d7SJerome Forissier  */
5a4f139d7SJerome Forissier 
67ba16abbSJerome Forissier #include <assert.h>
7a4f139d7SJerome Forissier #include <kernel/dt.h>
8bce4951cSJens Wiklander #include <kernel/linker.h>
99fe4c797SJerome Forissier #include <libfdt.h>
107ba16abbSJerome Forissier #include <mm/core_memprot.h>
117ba16abbSJerome Forissier #include <mm/core_mmu.h>
12a4f139d7SJerome Forissier #include <string.h>
137ba16abbSJerome Forissier #include <trace.h>
14a4f139d7SJerome Forissier 
15a4f139d7SJerome Forissier const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs)
16a4f139d7SJerome Forissier {
17a4f139d7SJerome Forissier 	const struct dt_device_match *dm;
18a4f139d7SJerome Forissier 	const struct dt_driver *drv;
19a4f139d7SJerome Forissier 
20db783ff8SEtienne Carriere 	for_each_dt_driver(drv) {
21db783ff8SEtienne Carriere 		for (dm = drv->match_table; dm; dm++) {
22db783ff8SEtienne Carriere 			if (!dm->compatible) {
23db783ff8SEtienne Carriere 				break;
24db783ff8SEtienne Carriere 			}
25a4f139d7SJerome Forissier 			if (!fdt_node_check_compatible(fdt, offs,
26db783ff8SEtienne Carriere 						       dm->compatible)) {
27a4f139d7SJerome Forissier 				return drv;
28db783ff8SEtienne Carriere 			}
29db783ff8SEtienne Carriere 		}
30db783ff8SEtienne Carriere 	}
31a4f139d7SJerome Forissier 
32a4f139d7SJerome Forissier 	return NULL;
33a4f139d7SJerome Forissier }
34a4f139d7SJerome Forissier 
35a4f139d7SJerome Forissier const struct dt_driver *__dt_driver_start(void)
36a4f139d7SJerome Forissier {
37a4f139d7SJerome Forissier 	return &__rodata_dtdrv_start;
38a4f139d7SJerome Forissier }
39a4f139d7SJerome Forissier 
40a4f139d7SJerome Forissier const struct dt_driver *__dt_driver_end(void)
41a4f139d7SJerome Forissier {
42a4f139d7SJerome Forissier 	return &__rodata_dtdrv_end;
43a4f139d7SJerome Forissier }
449fe4c797SJerome Forissier 
4550f3b323SPeng Fan bool dt_have_prop(const void *fdt, int offs, const char *propname)
4650f3b323SPeng Fan {
4750f3b323SPeng Fan 	const void *prop;
4850f3b323SPeng Fan 
4950f3b323SPeng Fan 	prop = fdt_getprop(fdt, offs, propname, NULL);
5050f3b323SPeng Fan 
5150f3b323SPeng Fan 	return prop;
5250f3b323SPeng Fan }
5350f3b323SPeng Fan 
5495cdc5e0SCedric Neveux int dt_get_irq(void *fdt, int node)
5595cdc5e0SCedric Neveux {
5695cdc5e0SCedric Neveux 	const uint32_t *int_prop = NULL;
5795cdc5e0SCedric Neveux 	int len_prop = 0;
5895cdc5e0SCedric Neveux 	int it_num = DT_INFO_INVALID_INTERRUPT;
5995cdc5e0SCedric Neveux 
6095cdc5e0SCedric Neveux 	/*
6195cdc5e0SCedric Neveux 	 * Interrupt property can be defined with at least 2x32 bits word
6295cdc5e0SCedric Neveux 	 *  - Type of interrupt
6395cdc5e0SCedric Neveux 	 *  - Interrupt Number
6495cdc5e0SCedric Neveux 	 */
6595cdc5e0SCedric Neveux 	int_prop = fdt_getprop(fdt, node, "interrupts", &len_prop);
6695cdc5e0SCedric Neveux 
6795cdc5e0SCedric Neveux 	if (!int_prop || len_prop < 2)
6895cdc5e0SCedric Neveux 		return it_num;
6995cdc5e0SCedric Neveux 
7095cdc5e0SCedric Neveux 	it_num = fdt32_to_cpu(int_prop[1]);
7195cdc5e0SCedric Neveux 
7295cdc5e0SCedric Neveux 	return it_num;
7395cdc5e0SCedric Neveux }
7495cdc5e0SCedric Neveux 
7595cdc5e0SCedric Neveux int dt_disable_status(void *fdt, int node)
7695cdc5e0SCedric Neveux {
7795cdc5e0SCedric Neveux 	const char *prop = NULL;
7895cdc5e0SCedric Neveux 	int len = 0;
7995cdc5e0SCedric Neveux 
8095cdc5e0SCedric Neveux 	prop = fdt_getprop(fdt, node, "status", &len);
8195cdc5e0SCedric Neveux 	if (!prop) {
8295cdc5e0SCedric Neveux 		if (fdt_setprop_string(fdt, node, "status", "disabled"))
8395cdc5e0SCedric Neveux 			return -1;
8495cdc5e0SCedric Neveux 	} else {
8595cdc5e0SCedric Neveux 		/*
8695cdc5e0SCedric Neveux 		 * Status is there, modify it.
8795cdc5e0SCedric Neveux 		 * Ask to set "disabled" value to the property. The value
8895cdc5e0SCedric Neveux 		 * will be automatically truncated with "len" size by the
8995cdc5e0SCedric Neveux 		 * fdt_setprop_inplace function.
9095cdc5e0SCedric Neveux 		 * Setting a value different from "ok" or "okay" will disable
9195cdc5e0SCedric Neveux 		 * the property.
9295cdc5e0SCedric Neveux 		 * Setting a truncated value of "disabled" with the original
9395cdc5e0SCedric Neveux 		 * property "len" is preferred to not increase the DT size and
9495cdc5e0SCedric Neveux 		 * losing time in recalculating the overall DT offsets.
9595cdc5e0SCedric Neveux 		 * If original length of the status property is larger than
9695cdc5e0SCedric Neveux 		 * "disabled", the property will start with "disabled" and be
9795cdc5e0SCedric Neveux 		 * completed with the rest of the original property.
9895cdc5e0SCedric Neveux 		 */
9995cdc5e0SCedric Neveux 		if (fdt_setprop_inplace(fdt, node, "status", "disabled", len))
10095cdc5e0SCedric Neveux 			return -1;
10195cdc5e0SCedric Neveux 	}
10295cdc5e0SCedric Neveux 
10395cdc5e0SCedric Neveux 	return 0;
10495cdc5e0SCedric Neveux }
10595cdc5e0SCedric Neveux 
10695cdc5e0SCedric Neveux int dt_enable_secure_status(void *fdt, int node)
10795cdc5e0SCedric Neveux {
10895cdc5e0SCedric Neveux 	if (dt_disable_status(fdt, node)) {
10995cdc5e0SCedric Neveux 		EMSG("Unable to disable Normal Status");
11095cdc5e0SCedric Neveux 		return -1;
11195cdc5e0SCedric Neveux 	}
11295cdc5e0SCedric Neveux 
11395cdc5e0SCedric Neveux 	if (fdt_setprop_string(fdt, node, "secure-status", "okay"))
11495cdc5e0SCedric Neveux 		return -1;
11595cdc5e0SCedric Neveux 
11695cdc5e0SCedric Neveux 	return 0;
11795cdc5e0SCedric Neveux }
11895cdc5e0SCedric Neveux 
1197ba16abbSJerome Forissier int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size)
1207ba16abbSJerome Forissier {
1217ba16abbSJerome Forissier 	enum teecore_memtypes mtype;
1227ba16abbSJerome Forissier 	paddr_t pbase;
1237ba16abbSJerome Forissier 	vaddr_t vbase;
1247ba16abbSJerome Forissier 	ssize_t sz;
1257ba16abbSJerome Forissier 	int st;
1267ba16abbSJerome Forissier 
1277ba16abbSJerome Forissier 	assert(cpu_mmu_enabled());
1287ba16abbSJerome Forissier 
1297ba16abbSJerome Forissier 	st = _fdt_get_status(fdt, offs);
1307ba16abbSJerome Forissier 	if (st == DT_STATUS_DISABLED)
1317ba16abbSJerome Forissier 		return -1;
1327ba16abbSJerome Forissier 
1337ba16abbSJerome Forissier 	pbase = _fdt_reg_base_address(fdt, offs);
134c0cfb36cSEtienne Carriere 	if (pbase == DT_INFO_INVALID_REG)
1357ba16abbSJerome Forissier 		return -1;
1367ba16abbSJerome Forissier 	sz = _fdt_reg_size(fdt, offs);
1377ba16abbSJerome Forissier 	if (sz < 0)
1387ba16abbSJerome Forissier 		return -1;
1397ba16abbSJerome Forissier 
1407ba16abbSJerome Forissier 	if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC))
1417ba16abbSJerome Forissier 		mtype = MEM_AREA_IO_SEC;
1427ba16abbSJerome Forissier 	else
1437ba16abbSJerome Forissier 		mtype = MEM_AREA_IO_NSEC;
1447ba16abbSJerome Forissier 
1457ba16abbSJerome Forissier 	/* Check if we have a mapping, create one if needed */
1467ba16abbSJerome Forissier 	if (!core_mmu_add_mapping(mtype, pbase, sz)) {
1477ba16abbSJerome Forissier 		EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA,
14823b1daf4SPeng Fan 		     (size_t)sz, pbase);
1497ba16abbSJerome Forissier 		return -1;
1507ba16abbSJerome Forissier 	}
1517ba16abbSJerome Forissier 	vbase = (vaddr_t)phys_to_virt(pbase, mtype);
1527ba16abbSJerome Forissier 	if (!vbase) {
1537ba16abbSJerome Forissier 		EMSG("Failed to get VA for PA 0x%"PRIxPA, pbase);
1547ba16abbSJerome Forissier 		return -1;
1557ba16abbSJerome Forissier 	}
1567ba16abbSJerome Forissier 
1577ba16abbSJerome Forissier 	*base = vbase;
1587ba16abbSJerome Forissier 	*size = sz;
1597ba16abbSJerome Forissier 	return 0;
1607ba16abbSJerome Forissier }
1617ba16abbSJerome Forissier 
1629fe4c797SJerome Forissier /* Read a physical address (n=1 or 2 cells) */
1639fe4c797SJerome Forissier static paddr_t _fdt_read_paddr(const uint32_t *cell, int n)
1649fe4c797SJerome Forissier {
1659fe4c797SJerome Forissier 	paddr_t addr;
1669fe4c797SJerome Forissier 
1679fe4c797SJerome Forissier 	if (n < 1 || n > 2)
1689fe4c797SJerome Forissier 		goto bad;
1699fe4c797SJerome Forissier 
1709fe4c797SJerome Forissier 	addr = fdt32_to_cpu(*cell);
1719fe4c797SJerome Forissier 	cell++;
1729fe4c797SJerome Forissier 	if (n == 2) {
1739fe4c797SJerome Forissier #ifdef ARM32
1749fe4c797SJerome Forissier 		if (addr) {
1759fe4c797SJerome Forissier 			/* High order 32 bits can't be nonzero */
1769fe4c797SJerome Forissier 			goto bad;
1779fe4c797SJerome Forissier 		}
1789fe4c797SJerome Forissier 		addr = fdt32_to_cpu(*cell);
1799fe4c797SJerome Forissier #else
1809fe4c797SJerome Forissier 		addr = (addr << 32) | fdt32_to_cpu(*cell);
1819fe4c797SJerome Forissier #endif
1829fe4c797SJerome Forissier 	}
1839fe4c797SJerome Forissier 
1849fe4c797SJerome Forissier 	if (!addr)
1859fe4c797SJerome Forissier 		goto bad;
1869fe4c797SJerome Forissier 
1879fe4c797SJerome Forissier 	return addr;
1889fe4c797SJerome Forissier bad:
189c0cfb36cSEtienne Carriere 	return DT_INFO_INVALID_REG;
1909fe4c797SJerome Forissier 
1919fe4c797SJerome Forissier }
1929fe4c797SJerome Forissier 
1939fe4c797SJerome Forissier paddr_t _fdt_reg_base_address(const void *fdt, int offs)
1949fe4c797SJerome Forissier {
1959fe4c797SJerome Forissier 	const void *reg;
1969fe4c797SJerome Forissier 	int ncells;
1979fe4c797SJerome Forissier 	int len;
19834deb103SPeng Fan 	int parent;
19934deb103SPeng Fan 
20034deb103SPeng Fan 	parent = fdt_parent_offset(fdt, offs);
20134deb103SPeng Fan 	if (parent < 0)
202c0cfb36cSEtienne Carriere 		return DT_INFO_INVALID_REG;
2039fe4c797SJerome Forissier 
2049fe4c797SJerome Forissier 	reg = fdt_getprop(fdt, offs, "reg", &len);
2059fe4c797SJerome Forissier 	if (!reg)
206c0cfb36cSEtienne Carriere 		return DT_INFO_INVALID_REG;
2079fe4c797SJerome Forissier 
20834deb103SPeng Fan 	ncells = fdt_address_cells(fdt, parent);
2099fe4c797SJerome Forissier 	if (ncells < 0)
210c0cfb36cSEtienne Carriere 		return DT_INFO_INVALID_REG;
2119fe4c797SJerome Forissier 
2129fe4c797SJerome Forissier 	return _fdt_read_paddr(reg, ncells);
2139fe4c797SJerome Forissier }
2149fe4c797SJerome Forissier 
2159fe4c797SJerome Forissier ssize_t _fdt_reg_size(const void *fdt, int offs)
2169fe4c797SJerome Forissier {
2179fe4c797SJerome Forissier 	const uint32_t *reg;
2189fe4c797SJerome Forissier 	uint32_t sz;
2199fe4c797SJerome Forissier 	int n;
2209fe4c797SJerome Forissier 	int len;
22134deb103SPeng Fan 	int parent;
22234deb103SPeng Fan 
22334deb103SPeng Fan 	parent = fdt_parent_offset(fdt, offs);
22434deb103SPeng Fan 	if (parent < 0)
225*1527e616SMarek Vasut 		return DT_INFO_INVALID_REG_SIZE;
2269fe4c797SJerome Forissier 
2279fe4c797SJerome Forissier 	reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len);
2289fe4c797SJerome Forissier 	if (!reg)
2299fe4c797SJerome Forissier 		return -1;
2309fe4c797SJerome Forissier 
23134deb103SPeng Fan 	n = fdt_address_cells(fdt, parent);
2329fe4c797SJerome Forissier 	if (n < 1 || n > 2)
2339fe4c797SJerome Forissier 		return -1;
2349fe4c797SJerome Forissier 
2359fe4c797SJerome Forissier 	reg += n;
2369fe4c797SJerome Forissier 
23734deb103SPeng Fan 	n = fdt_size_cells(fdt, parent);
2389fe4c797SJerome Forissier 	if (n < 1 || n > 2)
2399fe4c797SJerome Forissier 		return -1;
2409fe4c797SJerome Forissier 
2419fe4c797SJerome Forissier 	sz = fdt32_to_cpu(*reg);
2429fe4c797SJerome Forissier 	if (n == 2) {
2439fe4c797SJerome Forissier 		if (sz)
2449fe4c797SJerome Forissier 			return -1;
2459fe4c797SJerome Forissier 		reg++;
2469fe4c797SJerome Forissier 		sz = fdt32_to_cpu(*reg);
2479fe4c797SJerome Forissier 	}
2489fe4c797SJerome Forissier 
2499fe4c797SJerome Forissier 	return sz;
2509fe4c797SJerome Forissier }
2519fe4c797SJerome Forissier 
2529fe4c797SJerome Forissier static bool is_okay(const char *st, int len)
2539fe4c797SJerome Forissier {
2549fe4c797SJerome Forissier 	return !strncmp(st, "ok", len) || !strncmp(st, "okay", len);
2559fe4c797SJerome Forissier }
2569fe4c797SJerome Forissier 
2579fe4c797SJerome Forissier int _fdt_get_status(const void *fdt, int offs)
2589fe4c797SJerome Forissier {
2599fe4c797SJerome Forissier 	const char *prop;
2609fe4c797SJerome Forissier 	int st = 0;
2619fe4c797SJerome Forissier 	int len;
2629fe4c797SJerome Forissier 
2639fe4c797SJerome Forissier 	prop = fdt_getprop(fdt, offs, "status", &len);
2649fe4c797SJerome Forissier 	if (!prop || is_okay(prop, len)) {
2659fe4c797SJerome Forissier 		/* If status is not specified, it defaults to "okay" */
2669fe4c797SJerome Forissier 		st |= DT_STATUS_OK_NSEC;
2679fe4c797SJerome Forissier 	}
2689fe4c797SJerome Forissier 
2699fe4c797SJerome Forissier 	prop = fdt_getprop(fdt, offs, "secure-status", &len);
2709fe4c797SJerome Forissier 	if (!prop) {
2719fe4c797SJerome Forissier 		/*
2729fe4c797SJerome Forissier 		 * When secure-status is not specified it defaults to the same
2739fe4c797SJerome Forissier 		 * value as status
2749fe4c797SJerome Forissier 		 */
2759fe4c797SJerome Forissier 		if (st & DT_STATUS_OK_NSEC)
2769fe4c797SJerome Forissier 			st |= DT_STATUS_OK_SEC;
2779fe4c797SJerome Forissier 	} else {
2789fe4c797SJerome Forissier 		if (is_okay(prop, len))
2799fe4c797SJerome Forissier 			st |= DT_STATUS_OK_SEC;
2809fe4c797SJerome Forissier 	}
2819fe4c797SJerome Forissier 
2829fe4c797SJerome Forissier 	return st;
2839fe4c797SJerome Forissier }
284c0cfb36cSEtienne Carriere 
285c0cfb36cSEtienne Carriere void _fdt_fill_device_info(void *fdt, struct dt_node_info *info, int offs)
286c0cfb36cSEtienne Carriere {
287c0cfb36cSEtienne Carriere 	struct dt_node_info dinfo = {
288c0cfb36cSEtienne Carriere 		.reg = DT_INFO_INVALID_REG,
289c0cfb36cSEtienne Carriere 		.clock = DT_INFO_INVALID_CLOCK,
290c0cfb36cSEtienne Carriere 		.reset = DT_INFO_INVALID_RESET,
291c0cfb36cSEtienne Carriere 	};
292c0cfb36cSEtienne Carriere 	const fdt32_t *cuint;
293c0cfb36cSEtienne Carriere 
294c0cfb36cSEtienne Carriere 	dinfo.reg = _fdt_reg_base_address(fdt, offs);
295c0cfb36cSEtienne Carriere 
296c0cfb36cSEtienne Carriere 	cuint = fdt_getprop(fdt, offs, "clocks", NULL);
297c0cfb36cSEtienne Carriere 	if (cuint) {
298c0cfb36cSEtienne Carriere 		cuint++;
299c0cfb36cSEtienne Carriere 		dinfo.clock = (int)fdt32_to_cpu(*cuint);
300c0cfb36cSEtienne Carriere 	}
301c0cfb36cSEtienne Carriere 
302c0cfb36cSEtienne Carriere 	cuint = fdt_getprop(fdt, offs, "resets", NULL);
303c0cfb36cSEtienne Carriere 	if (cuint) {
304c0cfb36cSEtienne Carriere 		cuint++;
305c0cfb36cSEtienne Carriere 		dinfo.reset = (int)fdt32_to_cpu(*cuint);
306c0cfb36cSEtienne Carriere 	}
307c0cfb36cSEtienne Carriere 
308c0cfb36cSEtienne Carriere 	dinfo.status = _fdt_get_status(fdt, offs);
309c0cfb36cSEtienne Carriere 
310c0cfb36cSEtienne Carriere 	*info = dinfo;
311c0cfb36cSEtienne Carriere }
312