- 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:
Tom Rini 2020-09-30 09:21:43 -04:00
commit 01114adfc1
33 changed files with 359 additions and 213 deletions

View File

@ -155,10 +155,6 @@ config 64BIT
config SIFIVE_CLINT config SIFIVE_CLINT
bool bool
depends on RISCV_MMODE || SPL_RISCV_MMODE depends on RISCV_MMODE || SPL_RISCV_MMODE
select REGMAP
select SYSCON
select SPL_REGMAP if SPL
select SPL_SYSCON if SPL
help help
The SiFive CLINT block holds memory-mapped control and status registers The SiFive CLINT block holds memory-mapped control and status registers
associated with software and timer interrupts. associated with software and timer interrupts.
@ -177,22 +173,10 @@ config ANDES_PLIC
config ANDES_PLMT config ANDES_PLMT
bool bool
depends on RISCV_MMODE || SPL_RISCV_MMODE depends on RISCV_MMODE || SPL_RISCV_MMODE
select REGMAP
select SYSCON
select SPL_REGMAP if SPL
select SPL_SYSCON if SPL
help help
The Andes PLMT block holds memory-mapped mtime register The Andes PLMT block holds memory-mapped mtime register
associated with timer tick. 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 config SYS_MALLOC_F_LEN
default 0x1000 default 0x1000

View File

@ -3,7 +3,7 @@ config RISCV_NDS
select ARCH_EARLY_INIT_R select ARCH_EARLY_INIT_R
imply CPU imply CPU
imply CPU_RISCV 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_PLIC if (RISCV_MMODE || SPL_RISCV_MMODE)
imply ANDES_PLMT if (RISCV_MMODE || SPL_RISCV_MMODE) imply ANDES_PLMT if (RISCV_MMODE || SPL_RISCV_MMODE)
imply SPL_CPU_SUPPORT imply SPL_CPU_SUPPORT

View File

@ -72,6 +72,17 @@ static int riscv_cpu_probe(void)
return 0; 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 arch_cpu_init_dm(void)
{ {
int ret; int ret;
@ -111,6 +122,15 @@ int arch_cpu_init_dm(void)
ret = riscv_init_ipi(); ret = riscv_init_ipi();
if (ret) if (ret)
return 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 #endif
return 0; return 0;

View File

@ -10,7 +10,7 @@ config SIFIVE_FU540
select SPL_RAM if SPL select SPL_RAM if SPL
imply CPU imply CPU
imply CPU_RISCV 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 SIFIVE_CLINT if (RISCV_MMODE || SPL_RISCV_MMODE)
imply CMD_CPU imply CMD_CPU
imply SPL_CPU_SUPPORT imply SPL_CPU_SUPPORT

View File

@ -7,7 +7,7 @@ config GENERIC_RISCV
select ARCH_EARLY_INIT_R select ARCH_EARLY_INIT_R
imply CPU imply CPU
imply CPU_RISCV 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 SIFIVE_CLINT if (RISCV_MMODE || SPL_RISCV_MMODE)
imply CMD_CPU imply CMD_CPU
imply SPL_CPU_SUPPORT imply SPL_CPU_SUPPORT

View File

@ -43,14 +43,32 @@ _start:
csrr a0, CSR_MHARTID csrr a0, CSR_MHARTID
#endif #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 tp, a0
mv s1, a1 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 la t0, trap_entry
csrw MODE_PREFIX(tvec), t0 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 csrw MODE_PREFIX(ie), zero
#if CONFIG_IS_ENABLED(SMP) #if CONFIG_IS_ENABLED(SMP)
@ -65,8 +83,6 @@ _start:
#else #else
li t0, SIE_SSIE li t0, SIE_SSIE
#endif #endif
/* Clear any pending IPIs */
csrc MODE_PREFIX(ip), t0
csrs MODE_PREFIX(ie), t0 csrs MODE_PREFIX(ie), t0
#endif #endif
@ -87,10 +103,10 @@ call_board_init_f_0:
jal board_init_f_alloc_reserve jal board_init_f_alloc_reserve
/* /*
* Set global data pointer here for all harts, uninitialized at this * Save global data pointer for later. We don't set it here because it
* point. * is not initialized yet.
*/ */
mv gp, a0 mv s0, a0
/* setup stack */ /* setup stack */
#if CONFIG_IS_ENABLED(SMP) #if CONFIG_IS_ENABLED(SMP)
@ -111,6 +127,14 @@ call_board_init_f_0:
amoswap.w s2, t1, 0(t0) amoswap.w s2, t1, 0(t0)
bnez s2, wait_for_gd_init bnez s2, wait_for_gd_init
#else #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 bnez tp, secondary_hart_loop
#endif #endif
@ -127,16 +151,21 @@ call_board_init_f_0:
#ifndef CONFIG_XIP #ifndef CONFIG_XIP
la t0, available_harts_lock la t0, available_harts_lock
fence rw, w amoswap.w.rl zero, zero, 0(t0)
amoswap.w zero, zero, 0(t0)
wait_for_gd_init: wait_for_gd_init:
la t0, available_harts_lock la t0, available_harts_lock
li t1, 1 li t1, 1
1: amoswap.w t1, t1, 0(t0) 1: amoswap.w.aq t1, t1, 0(t0)
fence r, rw
bnez t1, 1b 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 */ /* register available harts in the available_harts mask */
li t1, 1 li t1, 1
sll t1, t1, tp sll t1, t1, tp
@ -144,8 +173,7 @@ wait_for_gd_init:
or t2, t2, t1 or t2, t2, t1
SREG t2, GD_AVAILABLE_HARTS(gp) SREG t2, GD_AVAILABLE_HARTS(gp)
fence rw, w amoswap.w.rl zero, zero, 0(t0)
amoswap.w zero, zero, 0(t0)
/* /*
* Continue on hart lottery winner, others branch to * Continue on hart lottery winner, others branch to
@ -395,6 +423,10 @@ secondary_hart_relocate:
mv gp, a2 mv gp, a2
#endif #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: secondary_hart_loop:
wfi wfi

View File

@ -55,9 +55,13 @@
reg = <0x0 0x10070000 0x0 0x1000>; reg = <0x0 0x10070000 0x0 0x1000>;
fuse-count = <0x1000>; fuse-count = <0x1000>;
}; };
clint@2000000 { clint: clint@2000000 {
compatible = "riscv,clint0"; 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>; reg = <0x0 0x2000000 0x0 0xc0000>;
u-boot,dm-spl; u-boot,dm-spl;
}; };

View File

@ -34,6 +34,10 @@
}; };
&clint {
clocks = <&rtcclk>;
};
&qspi0 { &qspi0 {
u-boot,dm-spl; u-boot,dm-spl;

View File

@ -17,6 +17,8 @@
compatible = "kendryte,k210"; compatible = "kendryte,k210";
aliases { aliases {
cpu0 = &cpu0;
cpu1 = &cpu1;
dma0 = &dmac0; dma0 = &dmac0;
gpio0 = &gpio0; gpio0 = &gpio0;
gpio1 = &gpio1_0; gpio1 = &gpio1_0;
@ -126,14 +128,13 @@
read-only; read-only;
}; };
clint0: interrupt-controller@2000000 { clint0: clint@2000000 {
#interrupt-cells = <1>; #interrupt-cells = <1>;
compatible = "kendryte,k210-clint", "riscv,clint0"; compatible = "kendryte,k210-clint", "riscv,clint0";
reg = <0x2000000 0xC000>; reg = <0x2000000 0xC000>;
interrupt-controller;
interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>,
<&cpu1_intc 3>, <&cpu1_intc 7>; <&cpu1_intc 3>, <&cpu1_intc 7>;
clocks = <&sysclk K210_CLK_CPU>; clocks = <&sysclk K210_CLK_CLINT>;
}; };
plic0: interrupt-controller@C000000 { plic0: interrupt-controller@C000000 {

View File

@ -24,9 +24,6 @@ struct arch_global_data {
#ifdef CONFIG_ANDES_PLIC #ifdef CONFIG_ANDES_PLIC
void __iomem *plic; /* plic base address */ void __iomem *plic; /* plic base address */
#endif #endif
#ifdef CONFIG_ANDES_PLMT
void __iomem *plmt; /* plmt base address */
#endif
#if CONFIG_IS_ENABLED(SMP) #if CONFIG_IS_ENABLED(SMP)
struct ipi_data ipi[CONFIG_NR_CPUS]; struct ipi_data ipi[CONFIG_NR_CPUS];
#endif #endif

View File

@ -18,14 +18,21 @@
* IPI data structure. The hart ID is inserted by the hart handling the IPI and * IPI data structure. The hart ID is inserted by the hart handling the IPI and
* calling the function. * 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 * @addr: Address of function
* @arg0: First argument of function * @arg0: First argument of function
* @arg1: Second argument of function * @arg1: Second argument of function
* @valid: Whether this IPI is valid
*/ */
struct ipi_data { struct ipi_data {
ulong addr; ulong addr;
ulong arg0; ulong arg0;
ulong arg1; ulong arg1;
unsigned int valid;
}; };
/** /**

View File

@ -7,13 +7,13 @@
#define _ASM_SYSCON_H #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 { enum {
RISCV_NONE, RISCV_NONE,
RISCV_SYSCON_CLINT, /* Core Local Interruptor (CLINT) */ RISCV_SYSCON_CLINT, /* Core Local Interruptor (CLINT) */
RISCV_SYSCON_PLIC, /* Platform Level Interrupt Controller (PLIC) */ RISCV_SYSCON_PLIC, /* Platform Level Interrupt Controller (PLIC) */
RISCV_SYSCON_PLMT, /* Platform Level Machine Timer (PLMT) */
}; };
#endif /* _ASM_SYSCON_H */ #endif /* _ASM_SYSCON_H */

View File

@ -15,7 +15,6 @@ obj-$(CONFIG_SIFIVE_CLINT) += sifive_clint.o
obj-$(CONFIG_ANDES_PLIC) += andes_plic.o obj-$(CONFIG_ANDES_PLIC) += andes_plic.o
obj-$(CONFIG_ANDES_PLMT) += andes_plmt.o obj-$(CONFIG_ANDES_PLMT) += andes_plmt.o
else else
obj-$(CONFIG_RISCV_RDTIME) += rdtime.o
obj-$(CONFIG_SBI) += sbi.o obj-$(CONFIG_SBI) += sbi.o
obj-$(CONFIG_SBI_IPI) += sbi_ipi.o obj-$(CONFIG_SBI_IPI) += sbi_ipi.o
endif endif

View File

@ -41,53 +41,45 @@ static int enable_ipi(int hart)
return 0; return 0;
} }
static int init_plic(void) int riscv_init_ipi(void)
{ {
struct udevice *dev;
ofnode node;
int ret; int ret;
long *base = syscon_get_first_range(RISCV_SYSCON_PLIC);
ofnode node;
struct udevice *dev;
u32 reg; u32 reg;
if (IS_ERR(base))
return PTR_ERR(base);
gd->arch.plic = base;
ret = uclass_find_first_device(UCLASS_CPU, &dev); ret = uclass_find_first_device(UCLASS_CPU, &dev);
if (ret) if (ret)
return ret; return ret;
else if (!dev)
return -ENODEV;
if (dev) { ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) { const char *device_type;
const char *device_type;
device_type = ofnode_read_string(node, "device_type"); device_type = ofnode_read_string(node, "device_type");
if (!device_type) if (!device_type)
continue; continue;
if (strcmp(device_type, "cpu")) if (strcmp(device_type, "cpu"))
continue; continue;
/* skip if hart is marked as not available */ /* skip if hart is marked as not available */
if (!ofnode_is_available(node)) if (!ofnode_is_available(node))
continue; continue;
/* read hart ID of CPU */ /* read hart ID of CPU */
ret = ofnode_read_u32(node, "reg", &reg); ret = ofnode_read_u32(node, "reg", &reg);
if (ret == 0) if (ret == 0)
enable_ipi(reg); enable_ipi(reg);
}
return 0;
} }
return -ENODEV; return 0;
}
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) int riscv_send_ipi(int hart)

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0+
/* /*
* Copyright (C) 2019, Rick Chen <rick@andestech.com> * 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). * U-Boot syscon driver for Andes's Platform Level Machine Timer (PLMT).
* The PLMT block holds memory-mapped mtime register * The PLMT block holds memory-mapped mtime register
@ -9,46 +10,43 @@
#include <common.h> #include <common.h>
#include <dm.h> #include <dm.h>
#include <regmap.h> #include <timer.h>
#include <syscon.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/syscon.h>
#include <linux/err.h> #include <linux/err.h>
/* mtime register */ /* mtime register */
#define MTIME_REG(base) ((ulong)(base)) #define MTIME_REG(base) ((ulong)(base))
DECLARE_GLOBAL_DATA_PTR; static int andes_plmt_get_count(struct udevice *dev, u64 *count)
#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)
{ {
PLMT_BASE_GET(); *count = readq((void __iomem *)MTIME_REG(dev->priv));
*time = readq((void __iomem *)MTIME_REG(gd->arch.plmt));
return 0; 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[] = { static const struct udevice_id andes_plmt_ids[] = {
{ .compatible = "riscv,plmt0", .data = RISCV_SYSCON_PLMT }, { .compatible = "riscv,plmt0" },
{ } { }
}; };
U_BOOT_DRIVER(andes_plmt) = { U_BOOT_DRIVER(andes_plmt) = {
.name = "andes_plmt", .name = "andes_plmt",
.id = UCLASS_SYSCON, .id = UCLASS_TIMER,
.of_match = andes_plmt_ids, .of_match = andes_plmt_ids,
.ops = &andes_plmt_ops,
.probe = andes_plmt_probe,
.flags = DM_FLAG_PRE_RELOC, .flags = DM_FLAG_PRE_RELOC,
}; };

View File

@ -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", printf("EPC: " REG_FMT " RA: " REG_FMT " TVAL: " REG_FMT "\n",
epc, regs->ra, tval); 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", printf("EPC: " REG_FMT " RA: " REG_FMT " reloc adjusted\n\n",
epc - gd->reloc_off, regs->ra - gd->reloc_off); epc - gd->reloc_off, regs->ra - gd->reloc_off);

View File

@ -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;
}

View File

@ -8,9 +8,9 @@
*/ */
#include <common.h> #include <common.h>
#include <clk.h>
#include <dm.h> #include <dm.h>
#include <regmap.h> #include <timer.h>
#include <syscon.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/syscon.h> #include <asm/syscon.h>
#include <linux/err.h> #include <linux/err.h>
@ -24,35 +24,19 @@
DECLARE_GLOBAL_DATA_PTR; 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) int riscv_init_ipi(void)
{ {
if (!gd->arch.clint) { int ret;
long *ret = syscon_get_first_range(RISCV_SYSCON_CLINT); struct udevice *dev;
if (IS_ERR(ret)) ret = uclass_get_device_by_driver(UCLASS_TIMER,
return PTR_ERR(ret); DM_GET_DRIVER(sifive_clint), &dev);
gd->arch.clint = ret; if (ret)
} return ret;
gd->arch.clint = dev_read_addr_ptr(dev);
if (!gd->arch.clint)
return -EINVAL;
return 0; return 0;
} }
@ -78,14 +62,36 @@ int riscv_get_ipi(int hart, int *pending)
return 0; 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[] = { static const struct udevice_id sifive_clint_ids[] = {
{ .compatible = "riscv,clint0", .data = RISCV_SYSCON_CLINT }, { .compatible = "riscv,clint0" },
{ } { }
}; };
U_BOOT_DRIVER(sifive_clint) = { U_BOOT_DRIVER(sifive_clint) = {
.name = "sifive_clint", .name = "sifive_clint",
.id = UCLASS_SYSCON, .id = UCLASS_TIMER,
.of_match = sifive_clint_ids, .of_match = sifive_clint_ids,
.probe = sifive_clint_probe,
.ops = &sifive_clint_ops,
.flags = DM_FLAG_PRE_RELOC, .flags = DM_FLAG_PRE_RELOC,
}; };

View File

@ -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].arg0 = ipi->arg0;
gd->arch.ipi[reg].arg1 = ipi->arg1; 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); ret = riscv_send_ipi(reg);
if (ret) { if (ret) {
pr_err("Cannot send IPI to hart %d\n", reg); pr_err("Cannot send IPI to hart %d\n", reg);
@ -81,7 +89,13 @@ void handle_ipi(ulong hart)
if (hart >= CONFIG_NR_CPUS) if (hart >= CONFIG_NR_CPUS)
return; 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; smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
invalidate_icache_all(); invalidate_icache_all();

View File

@ -533,7 +533,9 @@
}; };
cpus { cpus {
timebase-frequency = <2000000>;
cpu-test1 { cpu-test1 {
timebase-frequency = <3000000>;
compatible = "sandbox,cpu_sandbox"; compatible = "sandbox,cpu_sandbox";
u-boot,dm-pre-reloc; u-boot,dm-pre-reloc;
}; };
@ -839,11 +841,16 @@
0x58 8>; 0x58 8>;
}; };
timer { timer@0 {
compatible = "sandbox,timer"; compatible = "sandbox,timer";
clock-frequency = <1000000>; clock-frequency = <1000000>;
}; };
timer@1 {
compatible = "sandbox,timer";
sandbox,timebase-frequency-fallback;
};
tpm2 { tpm2 {
compatible = "sandbox,tpm2"; compatible = "sandbox,tpm2";
}; };

View 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 */

View File

@ -2235,7 +2235,7 @@ config CMD_DIAG
config CMD_IRQ config CMD_IRQ
bool "irq - Show information about interrupts" bool "irq - Show information about interrupts"
depends on !ARM && !MIPS && !SH depends on !ARM && !MIPS && !RISCV && !SH
help help
This enables two commands: This enables two commands:

View File

@ -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 MAIX BiT with Mic sipeed_maix_bitm_defconfig bit_mic first
Sipeed MAIXDUINO sipeed_maix_bitm_defconfig maixduino first Sipeed MAIXDUINO sipeed_maix_bitm_defconfig maixduino first
Sipeed MAIX GO goE second 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 Flashing causes a reboot of the device. Parameter -t specifies that the serial
@ -285,11 +285,15 @@ Technical Details
Boot Sequence 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``. 2. Both harts begin executing at ``0x00001000``.
3. Both harts jump to firmware at ``0x88000000``. 3. Both harts jump to firmware at ``0x88000000``.
4. One hart is chosen as a boot hart. 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, * If the pin is low, enter ISP mode. This mode allows loading data to ram,
writing it to flash, and booting from specific addresses. writing it to flash, and booting from specific addresses.

View File

@ -646,6 +646,10 @@ static int k210_clk_probe(struct udevice *dev)
REGISTER_GATE(K210_CLK_RTC, "rtc", in0); REGISTER_GATE(K210_CLK_RTC, "rtc", in0);
#undef REGISTER_GATE #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; return 0;
} }

View File

@ -8,14 +8,15 @@
#include <dm.h> #include <dm.h>
#include <cpu.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"); snprintf(buf, size, "LEG Inc. SuperMegaUltraTurbo CPU No. 1");
return 0; 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->cpu_freq = 42 * 42 * 42 * 42 * 42;
info->features = 0x42424242; info->features = 0x42424242;
@ -24,21 +25,29 @@ int cpu_sandbox_get_info(const struct udevice *dev, struct cpu_info *info)
return 0; return 0;
} }
int cpu_sandbox_get_count(const struct udevice *dev) static int cpu_sandbox_get_count(const struct udevice *dev)
{ {
return 42; 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."); snprintf(buf, size, "Languid Example Garbage Inc.");
return 0; 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 1;
return 0; return 0;
@ -52,7 +61,22 @@ static const struct cpu_ops cpu_sandbox_ops = {
.is_current = cpu_sandbox_is_current, .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; return 0;
} }
@ -67,5 +91,6 @@ U_BOOT_DRIVER(cpu_sandbox) = {
.id = UCLASS_CPU, .id = UCLASS_CPU,
.ops = &cpu_sandbox_ops, .ops = &cpu_sandbox_ops,
.of_match = cpu_sandbox_ids, .of_match = cpu_sandbox_ids,
.bind = cpu_sandbox_bind,
.probe = cpu_sandbox_probe, .probe = cpu_sandbox_probe,
}; };

View File

@ -11,7 +11,6 @@
#include <fdtdec.h> #include <fdtdec.h>
#include <init.h> #include <init.h>
#include <ram.h> #include <ram.h>
#include <regmap.h>
#include <syscon.h> #include <syscon.h>
#include <asm/io.h> #include <asm/io.h>
#include <clk.h> #include <clk.h>
@ -339,17 +338,12 @@ static int fu540_ddr_probe(struct udevice *dev)
priv->info.size = gd->ram_size; priv->info.size = gd->ram_size;
#if defined(CONFIG_SPL_BUILD) #if defined(CONFIG_SPL_BUILD)
struct regmap *map;
int ret; int ret;
u32 clock = 0; u32 clock = 0;
debug("FU540 DDR probe\n"); debug("FU540 DDR probe\n");
priv->dev = dev; 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); ret = clk_get_by_index(dev, 0, &priv->ddr_clk);
if (ret) { if (ret) {
debug("clk get failed %d\n", 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); ret = clk_enable(&priv->ddr_clk);
priv->ctl = regmap_get_range(map, 0); if (ret < 0) {
priv->phy = regmap_get_range(map, 1); debug("Could not enable DDR clock\n");
priv->physical_filter_ctrl = regmap_get_range(map, 2); 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); return fu540_ddr_setup(dev);
#endif #endif

View File

@ -146,8 +146,8 @@ config RISCV_TIMER
bool "RISC-V timer support" bool "RISC-V timer support"
depends on TIMER && RISCV depends on TIMER && RISCV
help help
Select this to enable support for the timer as defined Select this to enable support for a generic RISC-V S-Mode timer
by the RISC-V privileged architecture spec. driver.
config ROCKCHIP_TIMER config ROCKCHIP_TIMER
bool "Rockchip timer support" bool "Rockchip timer support"

View File

@ -1,36 +1,37 @@
// SPDX-License-Identifier: GPL-2.0+ // 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, 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 * This driver provides generic timer support for S-mode U-Boot.
* 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.
*/ */
#include <common.h> #include <common.h>
#include <dm.h> #include <dm.h>
#include <errno.h> #include <errno.h>
#include <timer.h> #include <timer.h>
#include <asm/io.h> #include <asm/csr.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);
static int riscv_timer_get_count(struct udevice *dev, u64 *count) 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) static int riscv_timer_probe(struct udevice *dev)

View File

@ -40,7 +40,9 @@ static int sandbox_timer_probe(struct udevice *dev)
{ {
struct timer_dev_priv *uc_priv = dev_get_uclass_priv(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; uc_priv->clock_rate = SANDBOX_TIMER_RATE;
return 0; return 0;

View File

@ -4,6 +4,7 @@
*/ */
#include <common.h> #include <common.h>
#include <cpu.h>
#include <dm.h> #include <dm.h>
#include <init.h> #include <init.h>
#include <dm/lists.h> #include <dm/lists.h>
@ -79,6 +80,36 @@ static int timer_post_probe(struct udevice *dev)
return 0; 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) u64 timer_conv_64(u32 count)
{ {
/* increment tbh if tbl has rolled over */ /* increment tbh if tbl has rolled over */

View File

@ -55,5 +55,6 @@
#define K210_CLK_OTP 43 #define K210_CLK_OTP 43
#define K210_CLK_RTC 44 #define K210_CLK_RTC 44
#define K210_CLK_ACLK 45 #define K210_CLK_ACLK 45
#define K210_CLK_CLINT 46
#endif /* CLOCK_K210_SYSCTL_H */ #endif /* CLOCK_K210_SYSCTL_H */

View File

@ -15,6 +15,21 @@
*/ */
int dm_timer_init(void); 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 * timer_conv_64 - convert 32-bit counter value to 64-bit
* *

View File

@ -7,8 +7,10 @@
#include <dm.h> #include <dm.h>
#include <timer.h> #include <timer.h>
#include <dm/test.h> #include <dm/test.h>
#include <dm/device-internal.h>
#include <test/test.h> #include <test/test.h>
#include <test/ut.h> #include <test/ut.h>
#include <asm/cpu.h>
/* /*
* Basic test of the timer uclass. * Basic test of the timer uclass.
@ -17,9 +19,32 @@ static int dm_test_timer_base(struct unit_test_state *uts)
{ {
struct udevice *dev; 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)); ut_asserteq(1000000, timer_get_rate(dev));
return 0; return 0;
} }
DM_TEST(dm_test_timer_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); 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);