162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * devtree.c - convenience functions for device tree manipulation 462306a36Sopenharmony_ci * Copyright 2007 David Gibson, IBM Corporation. 562306a36Sopenharmony_ci * Copyright (c) 2007 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: David Gibson <david@gibson.dropbear.id.au> 862306a36Sopenharmony_ci * Scott Wood <scottwood@freescale.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <stdarg.h> 1162306a36Sopenharmony_ci#include <stddef.h> 1262306a36Sopenharmony_ci#include "types.h" 1362306a36Sopenharmony_ci#include "string.h" 1462306a36Sopenharmony_ci#include "stdio.h" 1562306a36Sopenharmony_ci#include "ops.h" 1662306a36Sopenharmony_ci#include "of.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_civoid dt_fixup_memory(u64 start, u64 size) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci void *root, *memory; 2162306a36Sopenharmony_ci int naddr, nsize, i; 2262306a36Sopenharmony_ci u32 memreg[4]; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci root = finddevice("/"); 2562306a36Sopenharmony_ci if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0) 2662306a36Sopenharmony_ci naddr = 2; 2762306a36Sopenharmony_ci else 2862306a36Sopenharmony_ci naddr = be32_to_cpu(naddr); 2962306a36Sopenharmony_ci if (naddr < 1 || naddr > 2) 3062306a36Sopenharmony_ci fatal("Can't cope with #address-cells == %d in /\n\r", naddr); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0) 3362306a36Sopenharmony_ci nsize = 1; 3462306a36Sopenharmony_ci else 3562306a36Sopenharmony_ci nsize = be32_to_cpu(nsize); 3662306a36Sopenharmony_ci if (nsize < 1 || nsize > 2) 3762306a36Sopenharmony_ci fatal("Can't cope with #size-cells == %d in /\n\r", nsize); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci i = 0; 4062306a36Sopenharmony_ci if (naddr == 2) 4162306a36Sopenharmony_ci memreg[i++] = cpu_to_be32(start >> 32); 4262306a36Sopenharmony_ci memreg[i++] = cpu_to_be32(start & 0xffffffff); 4362306a36Sopenharmony_ci if (nsize == 2) 4462306a36Sopenharmony_ci memreg[i++] = cpu_to_be32(size >> 32); 4562306a36Sopenharmony_ci memreg[i++] = cpu_to_be32(size & 0xffffffff); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci memory = finddevice("/memory"); 4862306a36Sopenharmony_ci if (! memory) { 4962306a36Sopenharmony_ci memory = create_node(NULL, "memory"); 5062306a36Sopenharmony_ci setprop_str(memory, "device_type", "memory"); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci printf("Memory <- <0x%x", be32_to_cpu(memreg[0])); 5462306a36Sopenharmony_ci for (i = 1; i < (naddr + nsize); i++) 5562306a36Sopenharmony_ci printf(" 0x%x", be32_to_cpu(memreg[i])); 5662306a36Sopenharmony_ci printf("> (%ldMB)\n\r", (unsigned long)(size >> 20)); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32)); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define MHZ(x) ((x + 500000) / 1000000) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_civoid dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci void *devp = NULL; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu)); 6862306a36Sopenharmony_ci printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb)); 6962306a36Sopenharmony_ci if (bus > 0) 7062306a36Sopenharmony_ci printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus)); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci while ((devp = find_node_by_devtype(devp, "cpu"))) { 7362306a36Sopenharmony_ci setprop_val(devp, "clock-frequency", cpu_to_be32(cpu)); 7462306a36Sopenharmony_ci setprop_val(devp, "timebase-frequency", cpu_to_be32(tb)); 7562306a36Sopenharmony_ci if (bus > 0) 7662306a36Sopenharmony_ci setprop_val(devp, "bus-frequency", cpu_to_be32(bus)); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci timebase_period_ns = 1000000000 / tb; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_civoid dt_fixup_clock(const char *path, u32 freq) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci void *devp = finddevice(path); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (devp) { 8762306a36Sopenharmony_ci printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq)); 8862306a36Sopenharmony_ci setprop_val(devp, "clock-frequency", cpu_to_be32(freq)); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid dt_fixup_mac_address_by_alias(const char *alias, const u8 *addr) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci void *devp = find_node_by_alias(alias); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (devp) { 9762306a36Sopenharmony_ci printf("%s: local-mac-address <-" 9862306a36Sopenharmony_ci " %02x:%02x:%02x:%02x:%02x:%02x\n\r", alias, 9962306a36Sopenharmony_ci addr[0], addr[1], addr[2], 10062306a36Sopenharmony_ci addr[3], addr[4], addr[5]); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci setprop(devp, "local-mac-address", addr, 6); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid dt_fixup_mac_address(u32 index, const u8 *addr) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci void *devp = find_node_by_prop_value(NULL, "linux,network-index", 10962306a36Sopenharmony_ci (void*)&index, sizeof(index)); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (devp) { 11262306a36Sopenharmony_ci printf("ENET%d: local-mac-address <-" 11362306a36Sopenharmony_ci " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index, 11462306a36Sopenharmony_ci addr[0], addr[1], addr[2], 11562306a36Sopenharmony_ci addr[3], addr[4], addr[5]); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci setprop(devp, "local-mac-address", addr, 6); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid __dt_fixup_mac_addresses(u32 startindex, ...) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci va_list ap; 12462306a36Sopenharmony_ci u32 index = startindex; 12562306a36Sopenharmony_ci const u8 *addr; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci va_start(ap, startindex); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci while ((addr = va_arg(ap, const u8 *))) 13062306a36Sopenharmony_ci dt_fixup_mac_address(index++, addr); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci va_end(ap); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define MAX_ADDR_CELLS 4 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_civoid dt_get_reg_format(void *node, u32 *naddr, u32 *nsize) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci if (getprop(node, "#address-cells", naddr, 4) != 4) 14062306a36Sopenharmony_ci *naddr = 2; 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci *naddr = be32_to_cpu(*naddr); 14362306a36Sopenharmony_ci if (getprop(node, "#size-cells", nsize, 4) != 4) 14462306a36Sopenharmony_ci *nsize = 1; 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci *nsize = be32_to_cpu(*nsize); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void copy_val(u32 *dest, u32 *src, int naddr) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int pad = MAX_ADDR_CELLS - naddr; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci memset(dest, 0, pad * 4); 15462306a36Sopenharmony_ci memcpy(dest + pad, src, naddr * 4); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int sub_reg(u32 *reg, u32 *sub) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int i, borrow = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) { 16262306a36Sopenharmony_ci int prev_borrow = borrow; 16362306a36Sopenharmony_ci borrow = reg[i] < sub[i] + prev_borrow; 16462306a36Sopenharmony_ci reg[i] -= sub[i] + prev_borrow; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return !borrow; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int add_reg(u32 *reg, u32 *add, int naddr) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci int i, carry = 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) { 17562306a36Sopenharmony_ci u64 tmp = (u64)be32_to_cpu(reg[i]) + be32_to_cpu(add[i]) + carry; 17662306a36Sopenharmony_ci carry = tmp >> 32; 17762306a36Sopenharmony_ci reg[i] = cpu_to_be32((u32)tmp); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return !carry; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* It is assumed that if the first byte of reg fits in a 18462306a36Sopenharmony_ci * range, then the whole reg block fits. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic int compare_reg(u32 *reg, u32 *range, u32 *rangesize) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int i; 18962306a36Sopenharmony_ci u32 end; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci for (i = 0; i < MAX_ADDR_CELLS; i++) { 19262306a36Sopenharmony_ci if (be32_to_cpu(reg[i]) < be32_to_cpu(range[i])) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci if (be32_to_cpu(reg[i]) > be32_to_cpu(range[i])) 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < MAX_ADDR_CELLS; i++) { 19962306a36Sopenharmony_ci end = be32_to_cpu(range[i]) + be32_to_cpu(rangesize[i]); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (be32_to_cpu(reg[i]) < end) 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci if (be32_to_cpu(reg[i]) > end) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return reg[i] != end; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* reg must be MAX_ADDR_CELLS */ 21162306a36Sopenharmony_cistatic int find_range(u32 *reg, u32 *ranges, int nregaddr, 21262306a36Sopenharmony_ci int naddr, int nsize, int buflen) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci int nrange = nregaddr + naddr + nsize; 21562306a36Sopenharmony_ci int i; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci for (i = 0; i + nrange <= buflen; i += nrange) { 21862306a36Sopenharmony_ci u32 range_addr[MAX_ADDR_CELLS]; 21962306a36Sopenharmony_ci u32 range_size[MAX_ADDR_CELLS]; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci copy_val(range_addr, ranges + i, nregaddr); 22262306a36Sopenharmony_ci copy_val(range_size, ranges + i + nregaddr + naddr, nsize); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (compare_reg(reg, range_addr, range_size)) 22562306a36Sopenharmony_ci return i; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return -1; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* Currently only generic buses without special encodings are supported. 23262306a36Sopenharmony_ci * In particular, PCI is not supported. Also, only the beginning of the 23362306a36Sopenharmony_ci * reg block is tracked; size is ignored except in ranges. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cistatic u32 prop_buf[MAX_PROP_LEN / 4]; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int dt_xlate(void *node, int res, int reglen, unsigned long *addr, 23862306a36Sopenharmony_ci unsigned long *size) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci u32 last_addr[MAX_ADDR_CELLS]; 24162306a36Sopenharmony_ci u32 this_addr[MAX_ADDR_CELLS]; 24262306a36Sopenharmony_ci void *parent; 24362306a36Sopenharmony_ci u64 ret_addr, ret_size; 24462306a36Sopenharmony_ci u32 naddr, nsize, prev_naddr, prev_nsize; 24562306a36Sopenharmony_ci int buflen, offset; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci parent = get_parent(node); 24862306a36Sopenharmony_ci if (!parent) 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci dt_get_reg_format(parent, &naddr, &nsize); 25262306a36Sopenharmony_ci if (nsize > 2) 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci offset = (naddr + nsize) * res; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (reglen < offset + naddr + nsize || 25862306a36Sopenharmony_ci MAX_PROP_LEN < (offset + naddr + nsize) * 4) 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci copy_val(last_addr, prop_buf + offset, naddr); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret_size = be32_to_cpu(prop_buf[offset + naddr]); 26462306a36Sopenharmony_ci if (nsize == 2) { 26562306a36Sopenharmony_ci ret_size <<= 32; 26662306a36Sopenharmony_ci ret_size |= be32_to_cpu(prop_buf[offset + naddr + 1]); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci for (;;) { 27062306a36Sopenharmony_ci prev_naddr = naddr; 27162306a36Sopenharmony_ci prev_nsize = nsize; 27262306a36Sopenharmony_ci node = parent; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci parent = get_parent(node); 27562306a36Sopenharmony_ci if (!parent) 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci dt_get_reg_format(parent, &naddr, &nsize); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci buflen = getprop(node, "ranges", prop_buf, 28162306a36Sopenharmony_ci sizeof(prop_buf)); 28262306a36Sopenharmony_ci if (buflen == 0) 28362306a36Sopenharmony_ci continue; 28462306a36Sopenharmony_ci if (buflen < 0 || buflen > sizeof(prop_buf)) 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci offset = find_range(last_addr, prop_buf, prev_naddr, 28862306a36Sopenharmony_ci naddr, prev_nsize, buflen / 4); 28962306a36Sopenharmony_ci if (offset < 0) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci copy_val(this_addr, prop_buf + offset, prev_naddr); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!sub_reg(last_addr, this_addr)) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci copy_val(this_addr, prop_buf + offset + prev_naddr, naddr); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!add_reg(last_addr, this_addr, naddr)) 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (naddr > 2) 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ret_addr = ((u64)be32_to_cpu(last_addr[2]) << 32) | be32_to_cpu(last_addr[3]); 30762306a36Sopenharmony_ci if (sizeof(void *) == 4 && 30862306a36Sopenharmony_ci (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL || 30962306a36Sopenharmony_ci ret_addr + ret_size > 0x100000000ULL)) 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci *addr = ret_addr; 31362306a36Sopenharmony_ci if (size) 31462306a36Sopenharmony_ci *size = ret_size; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 1; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciint dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int reglen; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci reglen = getprop(node, "reg", prop_buf, sizeof(prop_buf)) / 4; 32462306a36Sopenharmony_ci return dt_xlate(node, res, reglen, addr, size); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciint dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (buflen > sizeof(prop_buf)) 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci memcpy(prop_buf, buf, buflen); 33462306a36Sopenharmony_ci return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciint dt_is_compatible(void *node, const char *compat) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci char *buf = (char *)prop_buf; 34062306a36Sopenharmony_ci int len, pos; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci len = getprop(node, "compatible", buf, MAX_PROP_LEN); 34362306a36Sopenharmony_ci if (len < 0) 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (pos = 0; pos < len; pos++) { 34762306a36Sopenharmony_ci if (!strcmp(buf + pos, compat)) 34862306a36Sopenharmony_ci return 1; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pos += strnlen(&buf[pos], len - pos); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciint dt_get_virtual_reg(void *node, void **addr, int nres) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned long xaddr; 35962306a36Sopenharmony_ci int n, i; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci n = getprop(node, "virtual-reg", addr, nres * 4); 36262306a36Sopenharmony_ci if (n > 0) { 36362306a36Sopenharmony_ci for (i = 0; i < n/4; i ++) 36462306a36Sopenharmony_ci ((u32 *)addr)[i] = be32_to_cpu(((u32 *)addr)[i]); 36562306a36Sopenharmony_ci return n / 4; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci for (n = 0; n < nres; n++) { 36962306a36Sopenharmony_ci if (!dt_xlate_reg(node, n, &xaddr, NULL)) 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci addr[n] = (void *)xaddr; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return n; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 378