mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-19 21:51:31 +00:00
Merge branch 'next' of https://gitlab.denx.de/u-boot/custodians/u-boot-riscv into next
- Disable CMD_IRQ for RISC-V. - Update sipeed/maix doc - Obtain reg of SiFive RAM via dev_read_addr_index() instead of regmap API. - Cleans up RISC-V timer drivers and converts them to DM. - Correctly handle IPIs already pending upon prior stage bootloader (on the K210)
This commit is contained in:
commit
01114adfc1
33 changed files with 359 additions and 213 deletions
|
@ -155,10 +155,6 @@ config 64BIT
|
|||
config SIFIVE_CLINT
|
||||
bool
|
||||
depends on RISCV_MMODE || SPL_RISCV_MMODE
|
||||
select REGMAP
|
||||
select SYSCON
|
||||
select SPL_REGMAP if SPL
|
||||
select SPL_SYSCON if SPL
|
||||
help
|
||||
The SiFive CLINT block holds memory-mapped control and status registers
|
||||
associated with software and timer interrupts.
|
||||
|
@ -177,22 +173,10 @@ config ANDES_PLIC
|
|||
config ANDES_PLMT
|
||||
bool
|
||||
depends on RISCV_MMODE || SPL_RISCV_MMODE
|
||||
select REGMAP
|
||||
select SYSCON
|
||||
select SPL_REGMAP if SPL
|
||||
select SPL_SYSCON if SPL
|
||||
help
|
||||
The Andes PLMT block holds memory-mapped mtime register
|
||||
associated with timer tick.
|
||||
|
||||
config RISCV_RDTIME
|
||||
bool
|
||||
default y if RISCV_SMODE || SPL_RISCV_SMODE
|
||||
help
|
||||
The provides the riscv_get_time() API that is implemented using the
|
||||
standard rdtime instruction. This is the case for S-mode U-Boot, and
|
||||
is useful for processors that support rdtime in M-mode too.
|
||||
|
||||
config SYS_MALLOC_F_LEN
|
||||
default 0x1000
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ config RISCV_NDS
|
|||
select ARCH_EARLY_INIT_R
|
||||
imply CPU
|
||||
imply CPU_RISCV
|
||||
imply RISCV_TIMER
|
||||
imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
|
||||
imply ANDES_PLIC if (RISCV_MMODE || SPL_RISCV_MMODE)
|
||||
imply ANDES_PLMT if (RISCV_MMODE || SPL_RISCV_MMODE)
|
||||
imply SPL_CPU_SUPPORT
|
||||
|
|
|
@ -72,6 +72,17 @@ static int riscv_cpu_probe(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called on secondary harts just after the IPI is init'd. Currently
|
||||
* there's nothing to do, since we just need to clear any existing IPIs, and
|
||||
* that is handled by the sending of an ipi itself.
|
||||
*/
|
||||
#if CONFIG_IS_ENABLED(SMP)
|
||||
static void dummy_pending_ipi_clear(ulong hart, ulong arg0, ulong arg1)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int arch_cpu_init_dm(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -111,6 +122,15 @@ int arch_cpu_init_dm(void)
|
|||
ret = riscv_init_ipi();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Clear all pending IPIs on secondary harts. We don't do anything on
|
||||
* the boot hart, since we never send an IPI to ourselves, and no
|
||||
* interrupts are enabled
|
||||
*/
|
||||
ret = smp_call_function((ulong)dummy_pending_ipi_clear, 0, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -10,7 +10,7 @@ config SIFIVE_FU540
|
|||
select SPL_RAM if SPL
|
||||
imply CPU
|
||||
imply CPU_RISCV
|
||||
imply RISCV_TIMER
|
||||
imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
|
||||
imply SIFIVE_CLINT if (RISCV_MMODE || SPL_RISCV_MMODE)
|
||||
imply CMD_CPU
|
||||
imply SPL_CPU_SUPPORT
|
||||
|
|
|
@ -7,7 +7,7 @@ config GENERIC_RISCV
|
|||
select ARCH_EARLY_INIT_R
|
||||
imply CPU
|
||||
imply CPU_RISCV
|
||||
imply RISCV_TIMER
|
||||
imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
|
||||
imply SIFIVE_CLINT if (RISCV_MMODE || SPL_RISCV_MMODE)
|
||||
imply CMD_CPU
|
||||
imply SPL_CPU_SUPPORT
|
||||
|
|
|
@ -43,14 +43,32 @@ _start:
|
|||
csrr a0, CSR_MHARTID
|
||||
#endif
|
||||
|
||||
/* save hart id and dtb pointer */
|
||||
/*
|
||||
* Save hart id and dtb pointer. The thread pointer register is not
|
||||
* modified by C code. It is used by secondary_hart_loop.
|
||||
*/
|
||||
mv tp, a0
|
||||
mv s1, a1
|
||||
|
||||
/*
|
||||
* Set the global data pointer to a known value in case we get a very
|
||||
* early trap. The global data pointer will be set its actual value only
|
||||
* after it has been initialized.
|
||||
*/
|
||||
mv gp, zero
|
||||
|
||||
/*
|
||||
* Set the trap handler. This must happen after initializing gp because
|
||||
* the handler may use it.
|
||||
*/
|
||||
la t0, trap_entry
|
||||
csrw MODE_PREFIX(tvec), t0
|
||||
|
||||
/* mask all interrupts */
|
||||
/*
|
||||
* Mask all interrupts. Interrupts are disabled globally (in m/sstatus)
|
||||
* for U-Boot, but we will need to read m/sip to determine if we get an
|
||||
* IPI
|
||||
*/
|
||||
csrw MODE_PREFIX(ie), zero
|
||||
|
||||
#if CONFIG_IS_ENABLED(SMP)
|
||||
|
@ -65,8 +83,6 @@ _start:
|
|||
#else
|
||||
li t0, SIE_SSIE
|
||||
#endif
|
||||
/* Clear any pending IPIs */
|
||||
csrc MODE_PREFIX(ip), t0
|
||||
csrs MODE_PREFIX(ie), t0
|
||||
#endif
|
||||
|
||||
|
@ -87,10 +103,10 @@ call_board_init_f_0:
|
|||
jal board_init_f_alloc_reserve
|
||||
|
||||
/*
|
||||
* Set global data pointer here for all harts, uninitialized at this
|
||||
* point.
|
||||
* Save global data pointer for later. We don't set it here because it
|
||||
* is not initialized yet.
|
||||
*/
|
||||
mv gp, a0
|
||||
mv s0, a0
|
||||
|
||||
/* setup stack */
|
||||
#if CONFIG_IS_ENABLED(SMP)
|
||||
|
@ -111,6 +127,14 @@ call_board_init_f_0:
|
|||
amoswap.w s2, t1, 0(t0)
|
||||
bnez s2, wait_for_gd_init
|
||||
#else
|
||||
/*
|
||||
* FIXME: gp is set before it is initialized. If an XIP U-Boot ever
|
||||
* encounters a pending IPI on boot it is liable to jump to whatever
|
||||
* memory happens to be in ipi_data.addr on boot. It may also run into
|
||||
* problems if it encounters an exception too early (because printf/puts
|
||||
* accesses gd).
|
||||
*/
|
||||
mv gp, s0
|
||||
bnez tp, secondary_hart_loop
|
||||
#endif
|
||||
|
||||
|
@ -127,16 +151,21 @@ call_board_init_f_0:
|
|||
|
||||
#ifndef CONFIG_XIP
|
||||
la t0, available_harts_lock
|
||||
fence rw, w
|
||||
amoswap.w zero, zero, 0(t0)
|
||||
amoswap.w.rl zero, zero, 0(t0)
|
||||
|
||||
wait_for_gd_init:
|
||||
la t0, available_harts_lock
|
||||
li t1, 1
|
||||
1: amoswap.w t1, t1, 0(t0)
|
||||
fence r, rw
|
||||
1: amoswap.w.aq t1, t1, 0(t0)
|
||||
bnez t1, 1b
|
||||
|
||||
/*
|
||||
* Set the global data pointer only when gd_t has been initialized.
|
||||
* This was already set by arch_setup_gd on the boot hart, but all other
|
||||
* harts' global data pointers gets set here.
|
||||
*/
|
||||
mv gp, s0
|
||||
|
||||
/* register available harts in the available_harts mask */
|
||||
li t1, 1
|
||||
sll t1, t1, tp
|
||||
|
@ -144,8 +173,7 @@ wait_for_gd_init:
|
|||
or t2, t2, t1
|
||||
SREG t2, GD_AVAILABLE_HARTS(gp)
|
||||
|
||||
fence rw, w
|
||||
amoswap.w zero, zero, 0(t0)
|
||||
amoswap.w.rl zero, zero, 0(t0)
|
||||
|
||||
/*
|
||||
* Continue on hart lottery winner, others branch to
|
||||
|
@ -395,6 +423,10 @@ secondary_hart_relocate:
|
|||
mv gp, a2
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Interrupts are disabled globally, but they can still be read from m/sip. The
|
||||
* wfi function will wake us up if we get an IPI, even if we do not trap.
|
||||
*/
|
||||
secondary_hart_loop:
|
||||
wfi
|
||||
|
||||
|
|
|
@ -55,9 +55,13 @@
|
|||
reg = <0x0 0x10070000 0x0 0x1000>;
|
||||
fuse-count = <0x1000>;
|
||||
};
|
||||
clint@2000000 {
|
||||
clint: clint@2000000 {
|
||||
compatible = "riscv,clint0";
|
||||
interrupts-extended = <&cpu0_intc 3 &cpu0_intc 7 &cpu1_intc 3 &cpu1_intc 7 &cpu2_intc 3 &cpu2_intc 7 &cpu3_intc 3 &cpu3_intc 7 &cpu4_intc 3 &cpu4_intc 7>;
|
||||
interrupts-extended = <&cpu0_intc 3 &cpu0_intc 7
|
||||
&cpu1_intc 3 &cpu1_intc 7
|
||||
&cpu2_intc 3 &cpu2_intc 7
|
||||
&cpu3_intc 3 &cpu3_intc 7
|
||||
&cpu4_intc 3 &cpu4_intc 7>;
|
||||
reg = <0x0 0x2000000 0x0 0xc0000>;
|
||||
u-boot,dm-spl;
|
||||
};
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
|
||||
};
|
||||
|
||||
&clint {
|
||||
clocks = <&rtcclk>;
|
||||
};
|
||||
|
||||
&qspi0 {
|
||||
u-boot,dm-spl;
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
compatible = "kendryte,k210";
|
||||
|
||||
aliases {
|
||||
cpu0 = &cpu0;
|
||||
cpu1 = &cpu1;
|
||||
dma0 = &dmac0;
|
||||
gpio0 = &gpio0;
|
||||
gpio1 = &gpio1_0;
|
||||
|
@ -126,14 +128,13 @@
|
|||
read-only;
|
||||
};
|
||||
|
||||
clint0: interrupt-controller@2000000 {
|
||||
clint0: clint@2000000 {
|
||||
#interrupt-cells = <1>;
|
||||
compatible = "kendryte,k210-clint", "riscv,clint0";
|
||||
reg = <0x2000000 0xC000>;
|
||||
interrupt-controller;
|
||||
interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>,
|
||||
<&cpu1_intc 3>, <&cpu1_intc 7>;
|
||||
clocks = <&sysclk K210_CLK_CPU>;
|
||||
clocks = <&sysclk K210_CLK_CLINT>;
|
||||
};
|
||||
|
||||
plic0: interrupt-controller@C000000 {
|
||||
|
|
|
@ -24,9 +24,6 @@ struct arch_global_data {
|
|||
#ifdef CONFIG_ANDES_PLIC
|
||||
void __iomem *plic; /* plic base address */
|
||||
#endif
|
||||
#ifdef CONFIG_ANDES_PLMT
|
||||
void __iomem *plmt; /* plmt base address */
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(SMP)
|
||||
struct ipi_data ipi[CONFIG_NR_CPUS];
|
||||
#endif
|
||||
|
|
|
@ -18,14 +18,21 @@
|
|||
* IPI data structure. The hart ID is inserted by the hart handling the IPI and
|
||||
* calling the function.
|
||||
*
|
||||
* @valid is used to determine whether a sent IPI originated from U-Boot. It is
|
||||
* initialized to zero by board_init_f_alloc_reserve. When U-Boot sends its
|
||||
* first IPI, it is set to 1. This prevents already-pending IPIs not sent by
|
||||
* U-Boot from being taken.
|
||||
*
|
||||
* @addr: Address of function
|
||||
* @arg0: First argument of function
|
||||
* @arg1: Second argument of function
|
||||
* @valid: Whether this IPI is valid
|
||||
*/
|
||||
struct ipi_data {
|
||||
ulong addr;
|
||||
ulong arg0;
|
||||
ulong arg1;
|
||||
unsigned int valid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
#define _ASM_SYSCON_H
|
||||
|
||||
/*
|
||||
* System controllers in a RISC-V system
|
||||
* System controllers in a RISC-V system. These should only be used for
|
||||
* identifying IPI controllers. Other devices should use DM to probe.
|
||||
*/
|
||||
enum {
|
||||
RISCV_NONE,
|
||||
RISCV_SYSCON_CLINT, /* Core Local Interruptor (CLINT) */
|
||||
RISCV_SYSCON_PLIC, /* Platform Level Interrupt Controller (PLIC) */
|
||||
RISCV_SYSCON_PLMT, /* Platform Level Machine Timer (PLMT) */
|
||||
};
|
||||
|
||||
#endif /* _ASM_SYSCON_H */
|
||||
|
|
|
@ -15,7 +15,6 @@ obj-$(CONFIG_SIFIVE_CLINT) += sifive_clint.o
|
|||
obj-$(CONFIG_ANDES_PLIC) += andes_plic.o
|
||||
obj-$(CONFIG_ANDES_PLMT) += andes_plmt.o
|
||||
else
|
||||
obj-$(CONFIG_RISCV_RDTIME) += rdtime.o
|
||||
obj-$(CONFIG_SBI) += sbi.o
|
||||
obj-$(CONFIG_SBI_IPI) += sbi_ipi.o
|
||||
endif
|
||||
|
|
|
@ -41,18 +41,24 @@ static int enable_ipi(int hart)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int init_plic(void)
|
||||
int riscv_init_ipi(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
ofnode node;
|
||||
int ret;
|
||||
long *base = syscon_get_first_range(RISCV_SYSCON_PLIC);
|
||||
ofnode node;
|
||||
struct udevice *dev;
|
||||
u32 reg;
|
||||
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
gd->arch.plic = base;
|
||||
|
||||
ret = uclass_find_first_device(UCLASS_CPU, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
else if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev) {
|
||||
ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
|
||||
const char *device_type;
|
||||
|
||||
|
@ -74,20 +80,6 @@ static int init_plic(void)
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int riscv_init_ipi(void)
|
||||
{
|
||||
long *ret = syscon_get_first_range(RISCV_SYSCON_PLIC);
|
||||
|
||||
if (IS_ERR(ret))
|
||||
return PTR_ERR(ret);
|
||||
gd->arch.plic = ret;
|
||||
|
||||
return init_plic();
|
||||
}
|
||||
|
||||
int riscv_send_ipi(int hart)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019, Rick Chen <rick@andestech.com>
|
||||
* Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
|
||||
*
|
||||
* U-Boot syscon driver for Andes's Platform Level Machine Timer (PLMT).
|
||||
* The PLMT block holds memory-mapped mtime register
|
||||
|
@ -9,46 +10,43 @@
|
|||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <timer.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/syscon.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/* mtime register */
|
||||
#define MTIME_REG(base) ((ulong)(base))
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define PLMT_BASE_GET(void) \
|
||||
do { \
|
||||
long *ret; \
|
||||
\
|
||||
if (!gd->arch.plmt) { \
|
||||
ret = syscon_get_first_range(RISCV_SYSCON_PLMT); \
|
||||
if (IS_ERR(ret)) \
|
||||
return PTR_ERR(ret); \
|
||||
gd->arch.plmt = ret; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int riscv_get_time(u64 *time)
|
||||
static int andes_plmt_get_count(struct udevice *dev, u64 *count)
|
||||
{
|
||||
PLMT_BASE_GET();
|
||||
|
||||
*time = readq((void __iomem *)MTIME_REG(gd->arch.plmt));
|
||||
*count = readq((void __iomem *)MTIME_REG(dev->priv));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct timer_ops andes_plmt_ops = {
|
||||
.get_count = andes_plmt_get_count,
|
||||
};
|
||||
|
||||
static int andes_plmt_probe(struct udevice *dev)
|
||||
{
|
||||
dev->priv = dev_read_addr_ptr(dev);
|
||||
if (!dev->priv)
|
||||
return -EINVAL;
|
||||
|
||||
return timer_timebase_fallback(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id andes_plmt_ids[] = {
|
||||
{ .compatible = "riscv,plmt0", .data = RISCV_SYSCON_PLMT },
|
||||
{ .compatible = "riscv,plmt0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(andes_plmt) = {
|
||||
.name = "andes_plmt",
|
||||
.id = UCLASS_SYSCON,
|
||||
.id = UCLASS_TIMER,
|
||||
.of_match = andes_plmt_ids,
|
||||
.ops = &andes_plmt_ops,
|
||||
.probe = andes_plmt_probe,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
|
|
|
@ -78,7 +78,8 @@ static void _exit_trap(ulong code, ulong epc, ulong tval, struct pt_regs *regs)
|
|||
|
||||
printf("EPC: " REG_FMT " RA: " REG_FMT " TVAL: " REG_FMT "\n",
|
||||
epc, regs->ra, tval);
|
||||
if (gd->flags & GD_FLG_RELOC)
|
||||
/* Print relocation adjustments, but only if gd is initialized */
|
||||
if (gd && gd->flags & GD_FLG_RELOC)
|
||||
printf("EPC: " REG_FMT " RA: " REG_FMT " reloc adjusted\n\n",
|
||||
epc - gd->reloc_off, regs->ra - gd->reloc_off);
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018, Anup Patel <anup@brainfault.org>
|
||||
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
|
||||
*
|
||||
* The riscv_get_time() API implementation that is using the
|
||||
* standard rdtime instruction.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
/* Implement the API required by RISC-V timer driver */
|
||||
int riscv_get_time(u64 *time)
|
||||
{
|
||||
#ifdef CONFIG_64BIT
|
||||
u64 n;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"rdtime %0"
|
||||
: "=r" (n));
|
||||
|
||||
*time = n;
|
||||
#else
|
||||
u32 lo, hi, tmp;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"1:\n"
|
||||
"rdtimeh %0\n"
|
||||
"rdtime %1\n"
|
||||
"rdtimeh %2\n"
|
||||
"bne %0, %2, 1b"
|
||||
: "=&r" (hi), "=&r" (lo), "=&r" (tmp));
|
||||
|
||||
*time = ((u64)hi << 32) | lo;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -8,9 +8,9 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <timer.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/syscon.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -24,35 +24,19 @@
|
|||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int riscv_get_time(u64 *time)
|
||||
{
|
||||
/* ensure timer register base has a sane value */
|
||||
riscv_init_ipi();
|
||||
|
||||
*time = readq((void __iomem *)MTIME_REG(gd->arch.clint));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int riscv_set_timecmp(int hart, u64 cmp)
|
||||
{
|
||||
/* ensure timer register base has a sane value */
|
||||
riscv_init_ipi();
|
||||
|
||||
writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int riscv_init_ipi(void)
|
||||
{
|
||||
if (!gd->arch.clint) {
|
||||
long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT);
|
||||
int ret;
|
||||
struct udevice *dev;
|
||||
|
||||
if (IS_ERR(ret))
|
||||
return PTR_ERR(ret);
|
||||
gd->arch.clint = ret;
|
||||
}
|
||||
ret = uclass_get_device_by_driver(UCLASS_TIMER,
|
||||
DM_GET_DRIVER(sifive_clint), &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gd->arch.clint = dev_read_addr_ptr(dev);
|
||||
if (!gd->arch.clint)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -78,14 +62,36 @@ int riscv_get_ipi(int hart, int *pending)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_clint_get_count(struct udevice *dev, u64 *count)
|
||||
{
|
||||
*count = readq((void __iomem *)MTIME_REG(dev->priv));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct timer_ops sifive_clint_ops = {
|
||||
.get_count = sifive_clint_get_count,
|
||||
};
|
||||
|
||||
static int sifive_clint_probe(struct udevice *dev)
|
||||
{
|
||||
dev->priv = dev_read_addr_ptr(dev);
|
||||
if (!dev->priv)
|
||||
return -EINVAL;
|
||||
|
||||
return timer_timebase_fallback(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id sifive_clint_ids[] = {
|
||||
{ .compatible = "riscv,clint0", .data = RISCV_SYSCON_CLINT },
|
||||
{ .compatible = "riscv,clint0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sifive_clint) = {
|
||||
.name = "sifive_clint",
|
||||
.id = UCLASS_SYSCON,
|
||||
.id = UCLASS_TIMER,
|
||||
.of_match = sifive_clint_ids,
|
||||
.probe = sifive_clint_probe,
|
||||
.ops = &sifive_clint_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
|
|
|
@ -54,6 +54,14 @@ static int send_ipi_many(struct ipi_data *ipi, int wait)
|
|||
gd->arch.ipi[reg].arg0 = ipi->arg0;
|
||||
gd->arch.ipi[reg].arg1 = ipi->arg1;
|
||||
|
||||
/*
|
||||
* Ensure valid only becomes set when the IPI parameters are
|
||||
* set. An IPI may already be pending on other harts, so we
|
||||
* need a way to signal that the IPI device has been
|
||||
* initialized, and that it is ok to call the function.
|
||||
*/
|
||||
__smp_store_release(&gd->arch.ipi[reg].valid, 1);
|
||||
|
||||
ret = riscv_send_ipi(reg);
|
||||
if (ret) {
|
||||
pr_err("Cannot send IPI to hart %d\n", reg);
|
||||
|
@ -81,7 +89,13 @@ void handle_ipi(ulong hart)
|
|||
if (hart >= CONFIG_NR_CPUS)
|
||||
return;
|
||||
|
||||
__smp_mb();
|
||||
/*
|
||||
* If valid is not set, then U-Boot has not requested the IPI. The
|
||||
* IPI device may not be initialized, so all we can do is wait for
|
||||
* U-Boot to initialize it and send an IPI
|
||||
*/
|
||||
if (!__smp_load_acquire(&gd->arch.ipi[hart].valid))
|
||||
return;
|
||||
|
||||
smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
|
||||
invalidate_icache_all();
|
||||
|
|
|
@ -533,7 +533,9 @@
|
|||
};
|
||||
|
||||
cpus {
|
||||
timebase-frequency = <2000000>;
|
||||
cpu-test1 {
|
||||
timebase-frequency = <3000000>;
|
||||
compatible = "sandbox,cpu_sandbox";
|
||||
u-boot,dm-pre-reloc;
|
||||
};
|
||||
|
@ -839,11 +841,16 @@
|
|||
0x58 8>;
|
||||
};
|
||||
|
||||
timer {
|
||||
timer@0 {
|
||||
compatible = "sandbox,timer";
|
||||
clock-frequency = <1000000>;
|
||||
};
|
||||
|
||||
timer@1 {
|
||||
compatible = "sandbox,timer";
|
||||
sandbox,timebase-frequency-fallback;
|
||||
};
|
||||
|
||||
tpm2 {
|
||||
compatible = "sandbox,tpm2";
|
||||
};
|
||||
|
|
11
arch/sandbox/include/asm/cpu.h
Normal file
11
arch/sandbox/include/asm/cpu.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __SANDBOX_CPU_H
|
||||
#define __SANDBOX_CPU_H
|
||||
|
||||
void cpu_sandbox_set_current(const char *name);
|
||||
|
||||
#endif /* __SANDBOX_CPU_H */
|
|
@ -2235,7 +2235,7 @@ config CMD_DIAG
|
|||
|
||||
config CMD_IRQ
|
||||
bool "irq - Show information about interrupts"
|
||||
depends on !ARM && !MIPS && !SH
|
||||
depends on !ARM && !MIPS && !RISCV && !SH
|
||||
help
|
||||
This enables two commands:
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ Sipeed MAIX BiT sipeed_maix_bitm_defconfig bit first
|
|||
Sipeed MAIX BiT with Mic sipeed_maix_bitm_defconfig bit_mic first
|
||||
Sipeed MAIXDUINO sipeed_maix_bitm_defconfig maixduino first
|
||||
Sipeed MAIX GO goE second
|
||||
Sipeed MAIX ONE DOCK goD first
|
||||
Sipeed MAIX ONE DOCK dan first
|
||||
======================== ========================== ========== ==========
|
||||
|
||||
Flashing causes a reboot of the device. Parameter -t specifies that the serial
|
||||
|
@ -285,11 +285,15 @@ Technical Details
|
|||
Boot Sequence
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
1. ``RESET`` pin is deasserted.
|
||||
1. ``RESET`` pin is deasserted. The pin is connected to the ``RESET`` button. It
|
||||
can also be set to low via either the ``DTR`` or the ``RTS`` line of the
|
||||
serial interface (depending on the board).
|
||||
2. Both harts begin executing at ``0x00001000``.
|
||||
3. Both harts jump to firmware at ``0x88000000``.
|
||||
4. One hart is chosen as a boot hart.
|
||||
5. Firmware reads value of pin ``IO_16`` (ISP).
|
||||
5. Firmware reads the value of pin ``IO_16`` (ISP). This pin is connected to the
|
||||
``BOOT`` button. The pin can equally be set to low via either the ``DTR`` or
|
||||
``RTS`` line of the serial interface (depending on the board).
|
||||
|
||||
* If the pin is low, enter ISP mode. This mode allows loading data to ram,
|
||||
writing it to flash, and booting from specific addresses.
|
||||
|
|
|
@ -646,6 +646,10 @@ static int k210_clk_probe(struct udevice *dev)
|
|||
REGISTER_GATE(K210_CLK_RTC, "rtc", in0);
|
||||
#undef REGISTER_GATE
|
||||
|
||||
/* The MTIME register in CLINT runs at one 50th the CPU clock speed */
|
||||
clk_dm(K210_CLK_CLINT,
|
||||
clk_register_fixed_factor(NULL, "clint", "cpu", 0, 1, 50));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
#include <dm.h>
|
||||
#include <cpu.h>
|
||||
|
||||
int cpu_sandbox_get_desc(const struct udevice *dev, char *buf, int size)
|
||||
static int cpu_sandbox_get_desc(const struct udevice *dev, char *buf, int size)
|
||||
{
|
||||
snprintf(buf, size, "LEG Inc. SuperMegaUltraTurbo CPU No. 1");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_sandbox_get_info(const struct udevice *dev, struct cpu_info *info)
|
||||
static int cpu_sandbox_get_info(const struct udevice *dev,
|
||||
struct cpu_info *info)
|
||||
{
|
||||
info->cpu_freq = 42 * 42 * 42 * 42 * 42;
|
||||
info->features = 0x42424242;
|
||||
|
@ -24,21 +25,29 @@ int cpu_sandbox_get_info(const struct udevice *dev, struct cpu_info *info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int cpu_sandbox_get_count(const struct udevice *dev)
|
||||
static int cpu_sandbox_get_count(const struct udevice *dev)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
int cpu_sandbox_get_vendor(const struct udevice *dev, char *buf, int size)
|
||||
static int cpu_sandbox_get_vendor(const struct udevice *dev, char *buf,
|
||||
int size)
|
||||
{
|
||||
snprintf(buf, size, "Languid Example Garbage Inc.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_sandbox_is_current(struct udevice *dev)
|
||||
static const char *cpu_current = "cpu-test1";
|
||||
|
||||
void cpu_sandbox_set_current(const char *name)
|
||||
{
|
||||
if (!strcmp(dev->name, "cpu-test1"))
|
||||
cpu_current = name;
|
||||
}
|
||||
|
||||
static int cpu_sandbox_is_current(struct udevice *dev)
|
||||
{
|
||||
if (!strcmp(dev->name, cpu_current))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -52,7 +61,22 @@ static const struct cpu_ops cpu_sandbox_ops = {
|
|||
.is_current = cpu_sandbox_is_current,
|
||||
};
|
||||
|
||||
int cpu_sandbox_probe(struct udevice *dev)
|
||||
static int cpu_sandbox_bind(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
|
||||
|
||||
/* first examine the property in current cpu node */
|
||||
ret = dev_read_u32(dev, "timebase-frequency", &plat->timebase_freq);
|
||||
/* if not found, then look at the parent /cpus node */
|
||||
if (ret)
|
||||
ret = dev_read_u32(dev->parent, "timebase-frequency",
|
||||
&plat->timebase_freq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpu_sandbox_probe(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -67,5 +91,6 @@ U_BOOT_DRIVER(cpu_sandbox) = {
|
|||
.id = UCLASS_CPU,
|
||||
.ops = &cpu_sandbox_ops,
|
||||
.of_match = cpu_sandbox_ids,
|
||||
.bind = cpu_sandbox_bind,
|
||||
.probe = cpu_sandbox_probe,
|
||||
};
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <fdtdec.h>
|
||||
#include <init.h>
|
||||
#include <ram.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
|
@ -339,17 +338,12 @@ static int fu540_ddr_probe(struct udevice *dev)
|
|||
priv->info.size = gd->ram_size;
|
||||
|
||||
#if defined(CONFIG_SPL_BUILD)
|
||||
struct regmap *map;
|
||||
int ret;
|
||||
u32 clock = 0;
|
||||
|
||||
debug("FU540 DDR probe\n");
|
||||
priv->dev = dev;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &map);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &priv->ddr_clk);
|
||||
if (ret) {
|
||||
debug("clk get failed %d\n", ret);
|
||||
|
@ -369,9 +363,14 @@ static int fu540_ddr_probe(struct udevice *dev)
|
|||
}
|
||||
|
||||
ret = clk_enable(&priv->ddr_clk);
|
||||
priv->ctl = regmap_get_range(map, 0);
|
||||
priv->phy = regmap_get_range(map, 1);
|
||||
priv->physical_filter_ctrl = regmap_get_range(map, 2);
|
||||
if (ret < 0) {
|
||||
debug("Could not enable DDR clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->ctl = (struct fu540_ddrctl *)dev_read_addr_index(dev, 0);
|
||||
priv->phy = (struct fu540_ddrphy *)dev_read_addr_index(dev, 1);
|
||||
priv->physical_filter_ctrl = (u32 *)dev_read_addr_index(dev, 2);
|
||||
|
||||
return fu540_ddr_setup(dev);
|
||||
#endif
|
||||
|
|
|
@ -146,8 +146,8 @@ config RISCV_TIMER
|
|||
bool "RISC-V timer support"
|
||||
depends on TIMER && RISCV
|
||||
help
|
||||
Select this to enable support for the timer as defined
|
||||
by the RISC-V privileged architecture spec.
|
||||
Select this to enable support for a generic RISC-V S-Mode timer
|
||||
driver.
|
||||
|
||||
config ROCKCHIP_TIMER
|
||||
bool "Rockchip timer support"
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2020, Sean Anderson <seanga2@gmail.com>
|
||||
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
|
||||
* Copyright (C) 2018, Anup Patel <anup@brainfault.org>
|
||||
* Copyright (C) 2012 Regents of the University of California
|
||||
*
|
||||
* RISC-V privileged architecture defined generic timer driver
|
||||
* RISC-V architecturally-defined generic timer driver
|
||||
*
|
||||
* This driver relies on RISC-V platform codes to provide the essential API
|
||||
* riscv_get_time() which is supposed to return the timer counter as defined
|
||||
* by the RISC-V privileged architecture spec.
|
||||
*
|
||||
* This driver can be used in both M-mode and S-mode U-Boot.
|
||||
* This driver provides generic timer support for S-mode U-Boot.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <timer.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/**
|
||||
* riscv_get_time() - get the timer counter
|
||||
*
|
||||
* Platform codes should provide this API in order to make this driver function.
|
||||
*
|
||||
* @time: the 64-bit timer count as defined by the RISC-V privileged
|
||||
* architecture spec.
|
||||
* @return: 0 on success, -ve on error.
|
||||
*/
|
||||
extern int riscv_get_time(u64 *time);
|
||||
#include <asm/csr.h>
|
||||
|
||||
static int riscv_timer_get_count(struct udevice *dev, u64 *count)
|
||||
{
|
||||
return riscv_get_time(count);
|
||||
if (IS_ENABLED(CONFIG_64BIT)) {
|
||||
*count = csr_read(CSR_TIME);
|
||||
} else {
|
||||
u32 hi, lo;
|
||||
|
||||
do {
|
||||
hi = csr_read(CSR_TIMEH);
|
||||
lo = csr_read(CSR_TIME);
|
||||
} while (hi != csr_read(CSR_TIMEH));
|
||||
|
||||
*count = ((u64)hi << 32) | lo;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int riscv_timer_probe(struct udevice *dev)
|
||||
|
|
|
@ -40,7 +40,9 @@ static int sandbox_timer_probe(struct udevice *dev)
|
|||
{
|
||||
struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (!uc_priv->clock_rate)
|
||||
if (dev_read_bool(dev, "sandbox,timebase-frequency-fallback"))
|
||||
return timer_timebase_fallback(dev);
|
||||
else if (!uc_priv->clock_rate)
|
||||
uc_priv->clock_rate = SANDBOX_TIMER_RATE;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu.h>
|
||||
#include <dm.h>
|
||||
#include <init.h>
|
||||
#include <dm/lists.h>
|
||||
|
@ -79,6 +80,36 @@ static int timer_post_probe(struct udevice *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: should be CONFIG_IS_ENABLED(CPU), but the SPL config has _SUPPORT on
|
||||
* the end...
|
||||
*/
|
||||
#if defined(CONFIG_CPU) || defined(CONFIG_SPL_CPU_SUPPORT)
|
||||
int timer_timebase_fallback(struct udevice *dev)
|
||||
{
|
||||
struct udevice *cpu;
|
||||
struct cpu_platdata *cpu_plat;
|
||||
struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
/* Did we get our clock rate from the device tree? */
|
||||
if (uc_priv->clock_rate)
|
||||
return 0;
|
||||
|
||||
/* Fall back to timebase-frequency */
|
||||
dev_dbg(dev, "missing clocks or clock-frequency property; falling back on timebase-frequency\n");
|
||||
cpu = cpu_get_current_dev();
|
||||
if (!cpu)
|
||||
return -ENODEV;
|
||||
|
||||
cpu_plat = dev_get_parent_platdata(cpu);
|
||||
if (!cpu_plat)
|
||||
return -ENODEV;
|
||||
|
||||
uc_priv->clock_rate = cpu_plat->timebase_freq;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
u64 timer_conv_64(u32 count)
|
||||
{
|
||||
/* increment tbh if tbl has rolled over */
|
||||
|
|
|
@ -55,5 +55,6 @@
|
|||
#define K210_CLK_OTP 43
|
||||
#define K210_CLK_RTC 44
|
||||
#define K210_CLK_ACLK 45
|
||||
#define K210_CLK_CLINT 46
|
||||
|
||||
#endif /* CLOCK_K210_SYSCTL_H */
|
||||
|
|
|
@ -15,6 +15,21 @@
|
|||
*/
|
||||
int dm_timer_init(void);
|
||||
|
||||
/**
|
||||
* timer_timebase_fallback() - Helper for timers using timebase fallback
|
||||
* @dev: A timer partially-probed timer device
|
||||
*
|
||||
* This is a helper function designed for timers which need to fall back on the
|
||||
* cpu's timebase. This function is designed to be called during the driver's
|
||||
* probe(). If there is a clocks or clock-frequency property in the timer's
|
||||
* binding, then it will be used. Otherwise, the timebase of the current cpu
|
||||
* will be used. This is initialized by the cpu driver, and usually gotten from
|
||||
* ``/cpus/timebase-frequency`` or ``/cpus/cpu@X/timebase-frequency``.
|
||||
*
|
||||
* Return: 0 if OK, or negative error code on failure
|
||||
*/
|
||||
int timer_timebase_fallback(struct udevice *dev);
|
||||
|
||||
/*
|
||||
* timer_conv_64 - convert 32-bit counter value to 64-bit
|
||||
*
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
#include <dm.h>
|
||||
#include <timer.h>
|
||||
#include <dm/test.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <test/test.h>
|
||||
#include <test/ut.h>
|
||||
#include <asm/cpu.h>
|
||||
|
||||
/*
|
||||
* Basic test of the timer uclass.
|
||||
|
@ -17,9 +19,32 @@ static int dm_test_timer_base(struct unit_test_state *uts)
|
|||
{
|
||||
struct udevice *dev;
|
||||
|
||||
ut_assertok(uclass_get_device(UCLASS_TIMER, 0, &dev));
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@0", &dev));
|
||||
ut_asserteq(1000000, timer_get_rate(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_timer_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
/*
|
||||
* Test of timebase fallback
|
||||
*/
|
||||
static int dm_test_timer_timebase_fallback(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
cpu_sandbox_set_current("cpu-test1");
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev));
|
||||
ut_asserteq(3000000, timer_get_rate(dev));
|
||||
ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
|
||||
|
||||
cpu_sandbox_set_current("cpu-test2");
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev));
|
||||
ut_asserteq(2000000, timer_get_rate(dev));
|
||||
|
||||
cpu_sandbox_set_current("cpu-test1");
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_timer_timebase_fallback,
|
||||
UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
|
Loading…
Add table
Reference in a new issue