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