- 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
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -41,53 +41,45 @@ 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;
ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
const char *device_type;
device_type = ofnode_read_string(node, "device_type");
if (!device_type)
continue;
device_type = ofnode_read_string(node, "device_type");
if (!device_type)
continue;
if (strcmp(device_type, "cpu"))
continue;
if (strcmp(device_type, "cpu"))
continue;
/* skip if hart is marked as not available */
if (!ofnode_is_available(node))
continue;
/* skip if hart is marked as not available */
if (!ofnode_is_available(node))
continue;
/* read hart ID of CPU */
ret = ofnode_read_u32(node, "reg", &reg);
if (ret == 0)
enable_ipi(reg);
}
return 0;
/* read hart ID of CPU */
ret = ofnode_read_u32(node, "reg", &reg);
if (ret == 0)
enable_ipi(reg);
}
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();
return 0;
}
int riscv_send_ipi(int hart)

View File

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

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",
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);

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

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].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();

View File

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

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
bool "irq - Show information about interrupts"
depends on !ARM && !MIPS && !SH
depends on !ARM && !MIPS && !RISCV && !SH
help
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 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.

View File

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

View File

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

View File

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

View File

@ -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"

View File

@ -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)

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);
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;

View File

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

View File

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

View File

@ -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
*

View File

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