Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions arch/riscv/Kconfig.errata
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,21 @@ config ERRATA_THEAD_GHOSTWRITE

If you don't know what to do here, say "Y".

config ERRATA_THEAD_WRITE_ONCE
bool "Apply T-Head WRITE_ONCE errata"
depends on ERRATA_THEAD
default y
help
The early version of T-Head C9xx cores of sg2042 & th1520 have a store
merge buffer delay problem. The store merge buffer could improve the
store queue performance by merging multi-store requests, but when there
are no continued store requests, the prior single store request would be
waiting in the store queue for a long time. That would cause signifi-
cant problems for communication between multi-cores. Appending a
fence w.o could immediately flush the store merge buffer and let other
cores see the write result.

This will apply the WRITE_ONCE errata to handle the non-standard beh-
avior via appending a fence w.o instruction for WRITE_ONCE().

endmenu # "CPU errata selection"
20 changes: 20 additions & 0 deletions arch/riscv/errata/thead/errata.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,23 @@ static bool errata_probe_ghostwrite(unsigned int stage,
return true;
}

static bool errata_probe_write_once(unsigned int stage,
unsigned long arch_id, unsigned long impid)
{
if (!IS_ENABLED(CONFIG_ERRATA_THEAD_WRITE_ONCE))
return false;

/* target-c9xx cores report arch_id and impid as 0 */
if (arch_id != 0 || impid != 0)
return false;

if (stage == RISCV_ALTERNATIVES_BOOT ||
stage == RISCV_ALTERNATIVES_MODULE)
return true;

return false;
}

static u32 thead_errata_probe(unsigned int stage,
unsigned long archid, unsigned long impid)
{
Expand All @@ -183,6 +200,9 @@ static u32 thead_errata_probe(unsigned int stage,

errata_probe_ghostwrite(stage, archid, impid);

if (errata_probe_write_once(stage, archid, impid))
cpu_req_errata |= BIT(ERRATA_THEAD_WRITE_ONCE);

return cpu_req_errata;
}

Expand Down
3 changes: 2 additions & 1 deletion arch/riscv/include/asm/errata_list_vendors.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
#define ERRATA_THEAD_MAE 0
#define ERRATA_THEAD_PMU 1
#define ERRATA_THEAD_GHOSTWRITE 2
#define ERRATA_THEAD_NUMBER 3
#define ERRATA_THEAD_WRITE_ONCE 3
#define ERRATA_THEAD_NUMBER 4
#endif

#ifdef CONFIG_ERRATA_MIPS
Expand Down
34 changes: 34 additions & 0 deletions arch/riscv/include/asm/rwonce.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __ASM_RWONCE_H
#define __ASM_RWONCE_H

#include <linux/compiler_types.h>
#include <asm/alternative-macros.h>
#include <asm/vendorid_list.h>
#include <asm/errata_list_vendors.h>

#if defined(CONFIG_ERRATA_THEAD_WRITE_ONCE) && !defined(NO_ALTERNATIVE)

#define write_once_fence() \
do { \
asm volatile(ALTERNATIVE( \
"nop", \
"fence w, o", \
THEAD_VENDOR_ID, \
ERRATA_THEAD_WRITE_ONCE, \
CONFIG_ERRATA_THEAD_WRITE_ONCE) \
: : : "memory"); \
} while (0)

#define __WRITE_ONCE(x, val) \
do { \
*(volatile typeof(x) *)&(x) = (val); \
write_once_fence(); \
} while (0)

#endif /* defined(CONFIG_ERRATA_THEAD_WRITE_ONCE) && !defined(NO_ALTERNATIVE) */

#include <asm-generic/rwonce.h>

#endif /* __ASM_RWONCE_H */
2 changes: 2 additions & 0 deletions include/asm-generic/rwonce.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@
__READ_ONCE(x); \
})

#ifndef __WRITE_ONCE
#define __WRITE_ONCE(x, val) \
do { \
*(volatile typeof(x) *)&(x) = (val); \
} while (0)
#endif

#define WRITE_ONCE(x, val) \
do { \
Expand Down
Loading