123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /*
- * Copyright (c) 2006-2019, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2011-09-15 Bernard first version
- * 2019-07-28 zdzn add smp support
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <board.h>
- #include "cp15.h"
- #define DBG_TAG "libcpu.aarch64.cpu"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include <string.h>
- #include "cpu.h"
- #include "psci_api.h"
- void (*system_off)(void);
- #ifdef RT_USING_SMP
- #ifdef RT_USING_FDT
- #include "dtb_node.h"
- struct dtb_node *_cpu_node[RT_CPUS_NR];
- #endif /* RT_USING_FDT */
- #define MPIDR_AFF_MASK 0x000000FF00FFFFFFul
- #define REPORT_ERR(retval) LOG_E("got error code %d in %s(), %s:%d", (retval), __func__, __FILE__, __LINE__)
- #define CHECK_RETVAL(retval) if (retval) {REPORT_ERR(retval);}
- /**
- * cpu_ops_tbl contains cpu_ops_t for each cpu kernel observed,
- * given cpu logical id 'i', its cpu_ops_t is 'cpu_ops_tbl[i]'
- */
- struct cpu_ops_t *cpu_ops_tbl[RT_CPUS_NR];
- #ifdef RT_USING_SMART
- // _id_to_mpidr is a table translate logical id to mpid, which is a 64-bit value
- rt_uint64_t rt_cpu_mpidr_early[RT_CPUS_NR] rt_weak = {[0 ... RT_CPUS_NR - 1] = ID_ERROR};
- #else
- /* The more common mpidr_el1 table, redefine it in BSP if it is in other cases */
- rt_weak rt_uint64_t rt_cpu_mpidr_early[] =
- {
- [0] = 0x80000000,
- [1] = 0x80000001,
- [2] = 0x80000002,
- [3] = 0x80000003,
- [4] = 0x80000004,
- [5] = 0x80000005,
- [6] = 0x80000006,
- [7] = 0x80000007,
- [RT_CPUS_NR] = 0
- };
- #endif /* RT_USING_SMART */
- void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock)
- {
- lock->slock = 0;
- }
- #define TICKET_SHIFT 16
- void rt_hw_spin_lock(rt_hw_spinlock_t *lock)
- {
- unsigned int tmp;
- struct __arch_tickets lockval, newval;
- asm volatile(
- /* Atomically increment the next ticket. */
- " prfm pstl1strm, %3\n"
- "1: ldaxr %w0, %3\n"
- " add %w1, %w0, %w5\n"
- " stxr %w2, %w1, %3\n"
- " cbnz %w2, 1b\n"
- /* Did we get the lock? */
- " eor %w1, %w0, %w0, ror #16\n"
- " cbz %w1, 3f\n"
- /*
- * No: spin on the owner. Send a local event to avoid missing an
- * unlock before the exclusive load.
- */
- " sevl\n"
- "2: wfe\n"
- " ldaxrh %w2, %4\n"
- " eor %w1, %w2, %w0, lsr #16\n"
- " cbnz %w1, 2b\n"
- /* We got the lock. Critical section starts here. */
- "3:"
- : "=&r"(lockval), "=&r"(newval), "=&r"(tmp), "+Q"(*lock)
- : "Q"(lock->tickets.owner), "I"(1 << TICKET_SHIFT)
- : "memory");
- rt_hw_dmb();
- }
- void rt_hw_spin_unlock(rt_hw_spinlock_t *lock)
- {
- rt_hw_dmb();
- asm volatile(
- " stlrh %w1, %0\n"
- : "=Q"(lock->tickets.owner)
- : "r"(lock->tickets.owner + 1)
- : "memory");
- }
- static int _cpus_init_data_hardcoded(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[])
- {
- // load in cpu_hw_ids in cpuid_to_hwid,
- // cpu_ops to cpu_ops_tbl
- if (num_cpus > RT_CPUS_NR)
- {
- LOG_W("num_cpus (%d) greater than RT_CPUS_NR (%d)\n", num_cpus, RT_CPUS_NR);
- num_cpus = RT_CPUS_NR;
- }
- for (int i = 0; i < num_cpus; i++)
- {
- set_hwid(i, cpu_hw_ids[i]);
- cpu_ops_tbl[i] = cpu_ops[i];
- }
- return 0;
- }
- #ifdef RT_USING_FDT
- /** read ('size' * 4) bytes number from start, big-endian format */
- static rt_uint64_t _read_be_number(void *start, int size)
- {
- rt_uint64_t buf = 0;
- for (; size > 0; size--)
- {
- buf = (buf << 32) | fdt32_to_cpu(*(uint32_t *)start);
- start = (uint32_t *)start + 1;
- }
- return buf;
- }
- /** check device-type of the node, */
- static bool _node_is_cpu(struct dtb_node *node)
- {
- char *device_type = dtb_node_get_dtb_node_property_value(node, "device_type", NULL);
- if (device_type)
- {
- return !strcmp(device_type, "cpu");
- }
- return false;
- }
- static int _read_and_set_hwid(struct dtb_node *cpu, int *id_pool, int *pcpuid)
- {
- // size/address_cells is number of elements in reg array
- int size;
- static int address_cells, size_cells;
- if (!address_cells && !size_cells)
- dtb_node_get_dtb_node_cells(cpu, &address_cells, &size_cells);
- void *id_start = dtb_node_get_dtb_node_property_value(cpu, "reg", &size);
- rt_uint64_t mpid = _read_be_number(id_start, address_cells);
- *pcpuid = *id_pool;
- *id_pool = *id_pool + 1;
- set_hwid(*pcpuid, mpid);
- LOG_I("Using MPID 0x%lx as cpu %d", mpid, *pcpuid);
- // setting _cpu_node for cpu_init use
- _cpu_node[*pcpuid] = cpu;
- return 0;
- }
- static int _read_and_set_cpuops(struct dtb_node *cpu, int cpuid)
- {
- char *method = dtb_node_get_dtb_node_property_value(cpu, "enable-method", NULL);
- if (!method)
- {
- LOG_E("Cannot read method from cpu node");
- return -1;
- }
- struct cpu_ops_t *cpu_ops;
- if (!strcmp(method, cpu_ops_psci.method))
- {
- cpu_ops = &cpu_ops_psci;
- }
- else if (!strcmp(method, cpu_ops_spin_tbl.method))
- {
- cpu_ops = &cpu_ops_spin_tbl;
- }
- else
- {
- cpu_ops = RT_NULL;
- LOG_E("Not supported cpu_ops: %s", method);
- }
- cpu_ops_tbl[cpuid] = cpu_ops;
- LOG_D("Using boot method [%s] for cpu %d", cpu_ops->method, cpuid);
- return 0;
- }
- static int _cpus_init_data_fdt()
- {
- // cpuid_to_hwid and cpu_ops_tbl with fdt
- void *root = get_dtb_node_head();
- int id_pool = 0;
- int cpuid;
- struct dtb_node *cpus = dtb_node_get_dtb_node_by_path(root, "/cpus");
- // for each cpu node (device-type is cpu), read its mpid and set its cpuid_to_hwid
- for_each_node_child(cpus)
- {
- if (!_node_is_cpu(cpus))
- {
- continue;
- }
- if (id_pool > RT_CPUS_NR)
- {
- LOG_W("Reading more cpus from FDT than RT_CPUS_NR"
- "\n Parsing will not continue and only %d cpus will be used.", RT_CPUS_NR);
- break;
- }
- _read_and_set_hwid(cpus, &id_pool, &cpuid);
- _read_and_set_cpuops(cpus, cpuid);
- }
- return 0;
- }
- #endif /* RT_USING_FDT */
- /** init cpu with hardcoded infomation or parsing from FDT */
- static int _cpus_init(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[])
- {
- int retval;
- // first setup cpu_ops_tbl and cpuid_to_hwid
- if (num_cpus > 0)
- retval = _cpus_init_data_hardcoded(num_cpus, cpu_hw_ids, cpu_ops);
- else
- {
- retval = -1;
- #ifdef RT_USING_FDT
- retval = _cpus_init_data_fdt();
- #endif
- }
- if (retval)
- return retval;
- // using cpuid_to_hwid and cpu_ops_tbl to call method_init and cpu_init
- // assuming that cpuid 0 has already init
- for (int i = 1; i < RT_CPUS_NR; i++)
- {
- if (cpuid_to_hwid(i) == ID_ERROR)
- {
- LOG_E("Failed to find hardware id of CPU %d", i);
- continue;
- }
- if (cpu_ops_tbl[i] && cpu_ops_tbl[i]->cpu_init)
- {
- retval = cpu_ops_tbl[i]->cpu_init(i);
- CHECK_RETVAL(retval);
- }
- else
- {
- LOG_E("Failed to find cpu_init for cpu %d with cpu_ops[%p], cpu_ops->cpu_init[%p]"
- , cpuid_to_hwid(i), cpu_ops_tbl[i], cpu_ops_tbl[i] ? cpu_ops_tbl[i]->cpu_init : NULL);
- }
- }
- return 0;
- }
- static void _boot_secondary(void)
- {
- for (int i = 1; i < RT_CPUS_NR; i++)
- {
- int retval = -0xbad0; // mark no support operation
- if (cpu_ops_tbl[i] && cpu_ops_tbl[i]->cpu_boot)
- retval = cpu_ops_tbl[i]->cpu_boot(i);
- if (retval)
- {
- if (retval == -0xbad0)
- LOG_E("No cpu_ops was probed for CPU %d. Try to configure it or use fdt", i);
- else
- LOG_E("Failed to boot secondary CPU %d, error code %d", i, retval);
- } else {
- LOG_I("Secondary CPU %d booted", i);
- }
- }
- }
- rt_weak void rt_hw_secondary_cpu_up(void)
- {
- _boot_secondary();
- }
- /**
- * @brief boot cpu with hardcoded data
- *
- * @param num_cpus number of cpus
- * @param cpu_hw_ids each element represents a hwid of cpu[i]
- * @param cpu_ops each element represents a pointer to cpu_ops of cpu[i]
- * @return int 0 on success,
- */
- int rt_hw_cpu_boot_secondary(int num_cpus, rt_uint64_t *cpu_hw_ids, struct cpu_ops_t *cpu_ops[])
- {
- int retval = 0;
- if (num_cpus < 1 || !cpu_hw_ids || !cpu_ops)
- return -1;
- retval = _cpus_init(num_cpus, cpu_hw_ids, cpu_ops);
- CHECK_RETVAL(retval);
- return retval;
- }
- #define CPU_INIT_USING_FDT 0,0,0
- /**
- * @brief Initialize cpu infomation from fdt
- *
- * @return int
- */
- int rt_hw_cpu_init()
- {
- #ifdef RT_USING_FDT
- return _cpus_init(CPU_INIT_USING_FDT);
- #else
- LOG_E("CPU init failed since RT_USING_FDT was not defined");
- return -0xa; /* no fdt support */
- #endif /* RT_USING_FDT */
- }
- rt_weak void rt_hw_secondary_cpu_idle_exec(void)
- {
- asm volatile("wfe" ::
- : "memory", "cc");
- }
- #endif /*RT_USING_SMP*/
- /**
- * @addtogroup ARM CPU
- */
- /*@{*/
- const char *rt_hw_cpu_arch(void)
- {
- return "aarch64";
- }
- /** shutdown CPU */
- void rt_hw_cpu_shutdown(void)
- {
- rt_uint32_t level;
- rt_kprintf("shutdown...\n");
- if (system_off)
- system_off();
- LOG_E("system shutdown failed");
- level = rt_hw_interrupt_disable();
- while (level)
- {
- RT_ASSERT(0);
- }
- }
- MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_shutdown, shutdown, shutdown machine);
- #ifdef RT_USING_CPU_FFS
- /**
- * This function finds the first bit set (beginning with the least significant bit)
- * in value and return the index of that bit.
- *
- * Bits are numbered starting at 1 (the least significant bit). A return value of
- * zero from any of these functions means that the argument was zero.
- *
- * @return return the index of the first bit set. If value is 0, then this function
- * shall return 0.
- */
- int __rt_ffs(int value)
- {
- return __builtin_ffs(value);
- }
- #endif
- /*@}*/
|