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> 87acb3a47SLudovic Barre #include <kernel/interrupt.h> 9bce4951cSJens Wiklander #include <kernel/linker.h> 109fe4c797SJerome Forissier #include <libfdt.h> 117ba16abbSJerome Forissier #include <mm/core_memprot.h> 127ba16abbSJerome Forissier #include <mm/core_mmu.h> 13a4f139d7SJerome Forissier #include <string.h> 147ba16abbSJerome Forissier #include <trace.h> 15a4f139d7SJerome Forissier 16a4f139d7SJerome Forissier const struct dt_driver *dt_find_compatible_driver(const void *fdt, int offs) 17a4f139d7SJerome Forissier { 18a4f139d7SJerome Forissier const struct dt_device_match *dm; 19a4f139d7SJerome Forissier const struct dt_driver *drv; 20a4f139d7SJerome Forissier 21db783ff8SEtienne Carriere for_each_dt_driver(drv) { 22db783ff8SEtienne Carriere for (dm = drv->match_table; dm; dm++) { 23db783ff8SEtienne Carriere if (!dm->compatible) { 24db783ff8SEtienne Carriere break; 25db783ff8SEtienne Carriere } 26a4f139d7SJerome Forissier if (!fdt_node_check_compatible(fdt, offs, 27db783ff8SEtienne Carriere dm->compatible)) { 28a4f139d7SJerome Forissier return drv; 29db783ff8SEtienne Carriere } 30db783ff8SEtienne Carriere } 31db783ff8SEtienne Carriere } 32a4f139d7SJerome Forissier 33a4f139d7SJerome Forissier return NULL; 34a4f139d7SJerome Forissier } 35a4f139d7SJerome Forissier 3650f3b323SPeng Fan bool dt_have_prop(const void *fdt, int offs, const char *propname) 3750f3b323SPeng Fan { 3850f3b323SPeng Fan const void *prop; 3950f3b323SPeng Fan 4050f3b323SPeng Fan prop = fdt_getprop(fdt, offs, propname, NULL); 4150f3b323SPeng Fan 4250f3b323SPeng Fan return prop; 4350f3b323SPeng Fan } 4450f3b323SPeng Fan 4595cdc5e0SCedric Neveux int dt_disable_status(void *fdt, int node) 4695cdc5e0SCedric Neveux { 4795cdc5e0SCedric Neveux const char *prop = NULL; 4895cdc5e0SCedric Neveux int len = 0; 4995cdc5e0SCedric Neveux 5095cdc5e0SCedric Neveux prop = fdt_getprop(fdt, node, "status", &len); 5195cdc5e0SCedric Neveux if (!prop) { 5295cdc5e0SCedric Neveux if (fdt_setprop_string(fdt, node, "status", "disabled")) 5395cdc5e0SCedric Neveux return -1; 5495cdc5e0SCedric Neveux } else { 5595cdc5e0SCedric Neveux /* 5695cdc5e0SCedric Neveux * Status is there, modify it. 5795cdc5e0SCedric Neveux * Ask to set "disabled" value to the property. The value 5895cdc5e0SCedric Neveux * will be automatically truncated with "len" size by the 5995cdc5e0SCedric Neveux * fdt_setprop_inplace function. 6095cdc5e0SCedric Neveux * Setting a value different from "ok" or "okay" will disable 6195cdc5e0SCedric Neveux * the property. 6295cdc5e0SCedric Neveux * Setting a truncated value of "disabled" with the original 6395cdc5e0SCedric Neveux * property "len" is preferred to not increase the DT size and 6495cdc5e0SCedric Neveux * losing time in recalculating the overall DT offsets. 6595cdc5e0SCedric Neveux * If original length of the status property is larger than 6695cdc5e0SCedric Neveux * "disabled", the property will start with "disabled" and be 6795cdc5e0SCedric Neveux * completed with the rest of the original property. 6895cdc5e0SCedric Neveux */ 6995cdc5e0SCedric Neveux if (fdt_setprop_inplace(fdt, node, "status", "disabled", len)) 7095cdc5e0SCedric Neveux return -1; 7195cdc5e0SCedric Neveux } 7295cdc5e0SCedric Neveux 7395cdc5e0SCedric Neveux return 0; 7495cdc5e0SCedric Neveux } 7595cdc5e0SCedric Neveux 7695cdc5e0SCedric Neveux int dt_enable_secure_status(void *fdt, int node) 7795cdc5e0SCedric Neveux { 7895cdc5e0SCedric Neveux if (dt_disable_status(fdt, node)) { 7995cdc5e0SCedric Neveux EMSG("Unable to disable Normal Status"); 8095cdc5e0SCedric Neveux return -1; 8195cdc5e0SCedric Neveux } 8295cdc5e0SCedric Neveux 8395cdc5e0SCedric Neveux if (fdt_setprop_string(fdt, node, "secure-status", "okay")) 8495cdc5e0SCedric Neveux return -1; 8595cdc5e0SCedric Neveux 8695cdc5e0SCedric Neveux return 0; 8795cdc5e0SCedric Neveux } 8895cdc5e0SCedric Neveux 897ba16abbSJerome Forissier int dt_map_dev(const void *fdt, int offs, vaddr_t *base, size_t *size) 907ba16abbSJerome Forissier { 917ba16abbSJerome Forissier enum teecore_memtypes mtype; 927ba16abbSJerome Forissier paddr_t pbase; 937ba16abbSJerome Forissier vaddr_t vbase; 94df7cecc0SLionel Debieve size_t sz; 957ba16abbSJerome Forissier int st; 967ba16abbSJerome Forissier 977ba16abbSJerome Forissier assert(cpu_mmu_enabled()); 987ba16abbSJerome Forissier 997ba16abbSJerome Forissier st = _fdt_get_status(fdt, offs); 1007ba16abbSJerome Forissier if (st == DT_STATUS_DISABLED) 1017ba16abbSJerome Forissier return -1; 1027ba16abbSJerome Forissier 1037ba16abbSJerome Forissier pbase = _fdt_reg_base_address(fdt, offs); 104c0cfb36cSEtienne Carriere if (pbase == DT_INFO_INVALID_REG) 1057ba16abbSJerome Forissier return -1; 1067ba16abbSJerome Forissier sz = _fdt_reg_size(fdt, offs); 107df7cecc0SLionel Debieve if (sz == DT_INFO_INVALID_REG_SIZE) 1087ba16abbSJerome Forissier return -1; 1097ba16abbSJerome Forissier 1107ba16abbSJerome Forissier if ((st & DT_STATUS_OK_SEC) && !(st & DT_STATUS_OK_NSEC)) 1117ba16abbSJerome Forissier mtype = MEM_AREA_IO_SEC; 1127ba16abbSJerome Forissier else 1137ba16abbSJerome Forissier mtype = MEM_AREA_IO_NSEC; 1147ba16abbSJerome Forissier 1157ba16abbSJerome Forissier /* Check if we have a mapping, create one if needed */ 116bc9618c0SAnton Rybakov vbase = (vaddr_t)core_mmu_add_mapping(mtype, pbase, sz); 117bc9618c0SAnton Rybakov if (!vbase) { 1187ba16abbSJerome Forissier EMSG("Failed to map %zu bytes at PA 0x%"PRIxPA, 11923b1daf4SPeng Fan (size_t)sz, pbase); 1207ba16abbSJerome Forissier return -1; 1217ba16abbSJerome Forissier } 1227ba16abbSJerome Forissier 1237ba16abbSJerome Forissier *base = vbase; 1247ba16abbSJerome Forissier *size = sz; 1257ba16abbSJerome Forissier return 0; 1267ba16abbSJerome Forissier } 1277ba16abbSJerome Forissier 1289fe4c797SJerome Forissier /* Read a physical address (n=1 or 2 cells) */ 1299fe4c797SJerome Forissier static paddr_t _fdt_read_paddr(const uint32_t *cell, int n) 1309fe4c797SJerome Forissier { 1319fe4c797SJerome Forissier paddr_t addr; 1329fe4c797SJerome Forissier 1339fe4c797SJerome Forissier if (n < 1 || n > 2) 1349fe4c797SJerome Forissier goto bad; 1359fe4c797SJerome Forissier 1369fe4c797SJerome Forissier addr = fdt32_to_cpu(*cell); 1379fe4c797SJerome Forissier cell++; 1389fe4c797SJerome Forissier if (n == 2) { 1399fe4c797SJerome Forissier #ifdef ARM32 1409fe4c797SJerome Forissier if (addr) { 1419fe4c797SJerome Forissier /* High order 32 bits can't be nonzero */ 1429fe4c797SJerome Forissier goto bad; 1439fe4c797SJerome Forissier } 1449fe4c797SJerome Forissier addr = fdt32_to_cpu(*cell); 1459fe4c797SJerome Forissier #else 1469fe4c797SJerome Forissier addr = (addr << 32) | fdt32_to_cpu(*cell); 1479fe4c797SJerome Forissier #endif 1489fe4c797SJerome Forissier } 1499fe4c797SJerome Forissier 1509fe4c797SJerome Forissier if (!addr) 1519fe4c797SJerome Forissier goto bad; 1529fe4c797SJerome Forissier 1539fe4c797SJerome Forissier return addr; 1549fe4c797SJerome Forissier bad: 155c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1569fe4c797SJerome Forissier 1579fe4c797SJerome Forissier } 1589fe4c797SJerome Forissier 1599fe4c797SJerome Forissier paddr_t _fdt_reg_base_address(const void *fdt, int offs) 1609fe4c797SJerome Forissier { 1619fe4c797SJerome Forissier const void *reg; 1629fe4c797SJerome Forissier int ncells; 1639fe4c797SJerome Forissier int len; 16434deb103SPeng Fan int parent; 16534deb103SPeng Fan 16634deb103SPeng Fan parent = fdt_parent_offset(fdt, offs); 16734deb103SPeng Fan if (parent < 0) 168c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1699fe4c797SJerome Forissier 1709fe4c797SJerome Forissier reg = fdt_getprop(fdt, offs, "reg", &len); 1719fe4c797SJerome Forissier if (!reg) 172c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1739fe4c797SJerome Forissier 17434deb103SPeng Fan ncells = fdt_address_cells(fdt, parent); 1759fe4c797SJerome Forissier if (ncells < 0) 176c0cfb36cSEtienne Carriere return DT_INFO_INVALID_REG; 1779fe4c797SJerome Forissier 1789fe4c797SJerome Forissier return _fdt_read_paddr(reg, ncells); 1799fe4c797SJerome Forissier } 1809fe4c797SJerome Forissier 181df7cecc0SLionel Debieve size_t _fdt_reg_size(const void *fdt, int offs) 1829fe4c797SJerome Forissier { 1839fe4c797SJerome Forissier const uint32_t *reg; 1849fe4c797SJerome Forissier uint32_t sz; 1859fe4c797SJerome Forissier int n; 1869fe4c797SJerome Forissier int len; 18734deb103SPeng Fan int parent; 18834deb103SPeng Fan 18934deb103SPeng Fan parent = fdt_parent_offset(fdt, offs); 19034deb103SPeng Fan if (parent < 0) 1911527e616SMarek Vasut return DT_INFO_INVALID_REG_SIZE; 1929fe4c797SJerome Forissier 1939fe4c797SJerome Forissier reg = (const uint32_t *)fdt_getprop(fdt, offs, "reg", &len); 1949fe4c797SJerome Forissier if (!reg) 195df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 1969fe4c797SJerome Forissier 19734deb103SPeng Fan n = fdt_address_cells(fdt, parent); 1989fe4c797SJerome Forissier if (n < 1 || n > 2) 199df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 2009fe4c797SJerome Forissier 2019fe4c797SJerome Forissier reg += n; 2029fe4c797SJerome Forissier 20334deb103SPeng Fan n = fdt_size_cells(fdt, parent); 2049fe4c797SJerome Forissier if (n < 1 || n > 2) 205df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 2069fe4c797SJerome Forissier 2079fe4c797SJerome Forissier sz = fdt32_to_cpu(*reg); 2089fe4c797SJerome Forissier if (n == 2) { 2099fe4c797SJerome Forissier if (sz) 210df7cecc0SLionel Debieve return DT_INFO_INVALID_REG_SIZE; 2119fe4c797SJerome Forissier reg++; 2129fe4c797SJerome Forissier sz = fdt32_to_cpu(*reg); 2139fe4c797SJerome Forissier } 2149fe4c797SJerome Forissier 2159fe4c797SJerome Forissier return sz; 2169fe4c797SJerome Forissier } 2179fe4c797SJerome Forissier 2189fe4c797SJerome Forissier static bool is_okay(const char *st, int len) 2199fe4c797SJerome Forissier { 2209fe4c797SJerome Forissier return !strncmp(st, "ok", len) || !strncmp(st, "okay", len); 2219fe4c797SJerome Forissier } 2229fe4c797SJerome Forissier 2239fe4c797SJerome Forissier int _fdt_get_status(const void *fdt, int offs) 2249fe4c797SJerome Forissier { 2259fe4c797SJerome Forissier const char *prop; 2269fe4c797SJerome Forissier int st = 0; 2279fe4c797SJerome Forissier int len; 2289fe4c797SJerome Forissier 2299fe4c797SJerome Forissier prop = fdt_getprop(fdt, offs, "status", &len); 2309fe4c797SJerome Forissier if (!prop || is_okay(prop, len)) { 2319fe4c797SJerome Forissier /* If status is not specified, it defaults to "okay" */ 2329fe4c797SJerome Forissier st |= DT_STATUS_OK_NSEC; 2339fe4c797SJerome Forissier } 2349fe4c797SJerome Forissier 2359fe4c797SJerome Forissier prop = fdt_getprop(fdt, offs, "secure-status", &len); 2369fe4c797SJerome Forissier if (!prop) { 2379fe4c797SJerome Forissier /* 2389fe4c797SJerome Forissier * When secure-status is not specified it defaults to the same 2399fe4c797SJerome Forissier * value as status 2409fe4c797SJerome Forissier */ 2419fe4c797SJerome Forissier if (st & DT_STATUS_OK_NSEC) 2429fe4c797SJerome Forissier st |= DT_STATUS_OK_SEC; 2439fe4c797SJerome Forissier } else { 2449fe4c797SJerome Forissier if (is_okay(prop, len)) 2459fe4c797SJerome Forissier st |= DT_STATUS_OK_SEC; 2469fe4c797SJerome Forissier } 2479fe4c797SJerome Forissier 2489fe4c797SJerome Forissier return st; 2499fe4c797SJerome Forissier } 250c0cfb36cSEtienne Carriere 251df45c114SEtienne Carriere void _fdt_fill_device_info(const void *fdt, struct dt_node_info *info, int offs) 252c0cfb36cSEtienne Carriere { 253c0cfb36cSEtienne Carriere struct dt_node_info dinfo = { 254c0cfb36cSEtienne Carriere .reg = DT_INFO_INVALID_REG, 25506fd21ddSLionel Debieve .reg_size = DT_INFO_INVALID_REG_SIZE, 256c0cfb36cSEtienne Carriere .clock = DT_INFO_INVALID_CLOCK, 257c0cfb36cSEtienne Carriere .reset = DT_INFO_INVALID_RESET, 2587acb3a47SLudovic Barre .interrupt = DT_INFO_INVALID_INTERRUPT, 259c0cfb36cSEtienne Carriere }; 260c0cfb36cSEtienne Carriere const fdt32_t *cuint; 261c0cfb36cSEtienne Carriere 262c0cfb36cSEtienne Carriere dinfo.reg = _fdt_reg_base_address(fdt, offs); 26306fd21ddSLionel Debieve dinfo.reg_size = _fdt_reg_size(fdt, offs); 264c0cfb36cSEtienne Carriere 265c0cfb36cSEtienne Carriere cuint = fdt_getprop(fdt, offs, "clocks", NULL); 266c0cfb36cSEtienne Carriere if (cuint) { 267c0cfb36cSEtienne Carriere cuint++; 268c0cfb36cSEtienne Carriere dinfo.clock = (int)fdt32_to_cpu(*cuint); 269c0cfb36cSEtienne Carriere } 270c0cfb36cSEtienne Carriere 271c0cfb36cSEtienne Carriere cuint = fdt_getprop(fdt, offs, "resets", NULL); 272c0cfb36cSEtienne Carriere if (cuint) { 273c0cfb36cSEtienne Carriere cuint++; 274c0cfb36cSEtienne Carriere dinfo.reset = (int)fdt32_to_cpu(*cuint); 275c0cfb36cSEtienne Carriere } 276c0cfb36cSEtienne Carriere 277702fe5a7SClément Léger dinfo.interrupt = dt_get_irq_type_prio(fdt, offs, &dinfo.type, 278702fe5a7SClément Léger &dinfo.prio); 2797acb3a47SLudovic Barre 280c0cfb36cSEtienne Carriere dinfo.status = _fdt_get_status(fdt, offs); 281c0cfb36cSEtienne Carriere 282c0cfb36cSEtienne Carriere *info = dinfo; 283c0cfb36cSEtienne Carriere } 284*876826f3SGabriel Fernandez 285*876826f3SGabriel Fernandez int _fdt_read_uint32_array(const void *fdt, int node, const char *prop_name, 286*876826f3SGabriel Fernandez uint32_t *array, size_t count) 287*876826f3SGabriel Fernandez { 288*876826f3SGabriel Fernandez const fdt32_t *cuint = NULL; 289*876826f3SGabriel Fernandez int len = 0; 290*876826f3SGabriel Fernandez uint32_t i = 0; 291*876826f3SGabriel Fernandez 292*876826f3SGabriel Fernandez cuint = fdt_getprop(fdt, node, prop_name, &len); 293*876826f3SGabriel Fernandez if (!cuint) 294*876826f3SGabriel Fernandez return -FDT_ERR_NOTFOUND; 295*876826f3SGabriel Fernandez 296*876826f3SGabriel Fernandez if ((uint32_t)len != (count * sizeof(uint32_t))) 297*876826f3SGabriel Fernandez return -FDT_ERR_BADLAYOUT; 298*876826f3SGabriel Fernandez 299*876826f3SGabriel Fernandez for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { 300*876826f3SGabriel Fernandez *array = fdt32_to_cpu(*cuint); 301*876826f3SGabriel Fernandez array++; 302*876826f3SGabriel Fernandez cuint++; 303*876826f3SGabriel Fernandez } 304*876826f3SGabriel Fernandez 305*876826f3SGabriel Fernandez return 0; 306*876826f3SGabriel Fernandez } 307*876826f3SGabriel Fernandez 308*876826f3SGabriel Fernandez int _fdt_read_uint32(const void *fdt, int node, const char *prop_name, 309*876826f3SGabriel Fernandez uint32_t *value) 310*876826f3SGabriel Fernandez { 311*876826f3SGabriel Fernandez return _fdt_read_uint32_array(fdt, node, prop_name, value, 1); 312*876826f3SGabriel Fernandez } 313*876826f3SGabriel Fernandez 314*876826f3SGabriel Fernandez uint32_t _fdt_read_uint32_default(const void *fdt, int node, 315*876826f3SGabriel Fernandez const char *prop_name, uint32_t dflt_value) 316*876826f3SGabriel Fernandez { 317*876826f3SGabriel Fernandez uint32_t value = 0; 318*876826f3SGabriel Fernandez 319*876826f3SGabriel Fernandez if (_fdt_read_uint32(fdt, node, prop_name, &value) < 0) 320*876826f3SGabriel Fernandez return dflt_value; 321*876826f3SGabriel Fernandez 322*876826f3SGabriel Fernandez return value; 323*876826f3SGabriel Fernandez } 324