mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-19 13:41: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
|
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,6 +34,10 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&clint {
|
||||||
|
clocks = <&rtcclk>;
|
||||||
|
};
|
||||||
|
|
||||||
&qspi0 {
|
&qspi0 {
|
||||||
u-boot,dm-spl;
|
u-boot,dm-spl;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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", ®);
|
ret = ofnode_read_u32(node, "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)
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 <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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
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
|
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:
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue