Commit c23be918 authored by Palmer Dabbelt's avatar Palmer Dabbelt

Merge patch series "Add non-coherent DMA support for AX45MP"

Prabhakar <prabhakar.csengg@gmail.com> says:

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

non-coherent DMA support for AX45MP
====================================

On the Andes AX45MP core, cache coherency is a specification option so it
may not be supported. In this case DMA will fail. To get around with this
issue this patch series does the below:

1] Andes alternative ports is implemented as errata which checks if the
IOCP is missing and only then applies to CMO errata. One vendor specific
SBI EXT (ANDES_SBI_EXT_IOCP_SW_WORKAROUND) is implemented as part of
errata.

Below are the configs which Andes port provides (and are selected by
RZ/Five):
      - ERRATA_ANDES
      - ERRATA_ANDES_CMO

OpenSBI patch supporting ANDES_SBI_EXT_IOCP_SW_WORKAROUND SBI is now
part v1.3 release.

2] Andes AX45MP core has a Programmable Physical Memory Attributes (PMA)
block that allows dynamic adjustment of memory attributes in the runtime.
It contains a configurable amount of PMA entries implemented as CSR
registers to control the attributes of memory locations in interest.
OpenSBI configures the PMA regions as required and creates a reserve memory
node and propagates it to the higher boot stack.

Currently OpenSBI (upstream) configures the required PMA region and passes
this a shared DMA pool to Linux.

    reserved-memory {
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        pma_resv0@58000000 {
            compatible = "shared-dma-pool";
            reg = <0x0 0x58000000 0x0 0x08000000>;
            no-map;
            linux,dma-default;
        };
    };

The above shared DMA pool gets appended to Linux DTB so the DMA memory
requests go through this region.

3] We provide callbacks to synchronize specific content between memory and
cache.

4] RZ/Five SoC selects the below configs
        - AX45MP_L2_CACHE
        - DMA_GLOBAL_POOL
        - ERRATA_ANDES
        - ERRATA_ANDES_CMO

----------x---------------------x--------------------x---------------x----

* b4-shazam-merge:
  soc: renesas: Kconfig: Select the required configs for RZ/Five SoC
  cache: Add L2 cache management for Andes AX45MP RISC-V core
  dt-bindings: cache: andestech,ax45mp-cache: Add DT binding documentation for L2 cache controller
  riscv: mm: dma-noncoherent: nonstandard cache operations support
  riscv: errata: Add Andes alternative ports
  riscv: asm: vendorid_list: Add Andes Technology to the vendors list

Link: https://lore.kernel.org/r/20230818135723.80612-1-prabhakar.mahadev-lad.rj@bp.renesas.comSigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parents 7f215d00 484861e0
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright (C) 2023 Renesas Electronics Corp.
%YAML 1.2
---
$id: http://devicetree.org/schemas/cache/andestech,ax45mp-cache.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Andestech AX45MP L2 Cache Controller
maintainers:
- Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
description:
A level-2 cache (L2C) is used to improve the system performance by providing
a large amount of cache line entries and reasonable access delays. The L2C
is shared between cores, and a non-inclusive non-exclusive policy is used.
select:
properties:
compatible:
contains:
enum:
- andestech,ax45mp-cache
required:
- compatible
properties:
compatible:
items:
- const: andestech,ax45mp-cache
- const: cache
reg:
maxItems: 1
interrupts:
maxItems: 1
cache-line-size:
const: 64
cache-level:
const: 2
cache-sets:
const: 1024
cache-size:
enum: [131072, 262144, 524288, 1048576, 2097152]
cache-unified: true
next-level-cache: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
- cache-line-size
- cache-level
- cache-sets
- cache-size
- cache-unified
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
cache-controller@2010000 {
compatible = "andestech,ax45mp-cache", "cache";
reg = <0x13400000 0x100000>;
interrupts = <508 IRQ_TYPE_LEVEL_HIGH>;
cache-line-size = <64>;
cache-level = <2>;
cache-sets = <1024>;
cache-size = <262144>;
cache-unified;
};
......@@ -20340,6 +20340,13 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
F: drivers/staging/
STANDALONE CACHE CONTROLLER DRIVERS
M: Conor Dooley <conor@kernel.org>
L: linux-riscv@lists.infradead.org
S: Maintained
T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/
F: drivers/cache
STARFIRE/DURALAN NETWORK DRIVER
M: Ion Badulescu <ionut@badula.org>
S: Odd Fixes
......
......@@ -275,6 +275,13 @@ config RISCV_DMA_NONCOHERENT
select DMA_BOUNCE_UNALIGNED_KMALLOC if SWIOTLB
select DMA_DIRECT_REMAP
config RISCV_NONSTANDARD_CACHE_OPS
bool
depends on RISCV_DMA_NONCOHERENT
help
This enables function pointer support for non-standard noncoherent
systems to handle cache management.
config AS_HAS_INSN
def_bool $(as-instr,.insn r 51$(comma) 0$(comma) 0$(comma) t0$(comma) t0$(comma) zero)
......
menu "CPU errata selection"
config ERRATA_ANDES
bool "Andes AX45MP errata"
depends on RISCV_ALTERNATIVE
help
All Andes errata Kconfig depend on this Kconfig. Disabling
this Kconfig will disable all Andes errata. Please say "Y"
here if your platform uses Andes CPU cores.
Otherwise, please say "N" here to avoid unnecessary overhead.
config ERRATA_ANDES_CMO
bool "Apply Andes cache management errata"
depends on ERRATA_ANDES && MMU && ARCH_R9A07G043
select RISCV_DMA_NONCOHERENT
default y
help
This will apply the cache management errata to handle the
non-standard handling on non-coherent operations on Andes cores.
If you don't know what to do here, say "Y".
config ERRATA_SIFIVE
bool "SiFive errata"
depends on RISCV_ALTERNATIVE
......
......@@ -2,5 +2,6 @@ ifdef CONFIG_RELOCATABLE
KBUILD_CFLAGS += -fno-pie
endif
obj-$(CONFIG_ERRATA_ANDES) += andes/
obj-$(CONFIG_ERRATA_SIFIVE) += sifive/
obj-$(CONFIG_ERRATA_THEAD) += thead/
// SPDX-License-Identifier: GPL-2.0-only
/*
* Erratas to be applied for Andes CPU cores
*
* Copyright (C) 2023 Renesas Electronics Corporation.
*
* Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
*/
#include <linux/memory.h>
#include <linux/module.h>
#include <asm/alternative.h>
#include <asm/cacheflush.h>
#include <asm/errata_list.h>
#include <asm/patch.h>
#include <asm/processor.h>
#include <asm/sbi.h>
#include <asm/vendorid_list.h>
#define ANDESTECH_AX45MP_MARCHID 0x8000000000008a45UL
#define ANDESTECH_AX45MP_MIMPID 0x500UL
#define ANDESTECH_SBI_EXT_ANDES 0x0900031E
#define ANDES_SBI_EXT_IOCP_SW_WORKAROUND 1
static long ax45mp_iocp_sw_workaround(void)
{
struct sbiret ret;
/*
* ANDES_SBI_EXT_IOCP_SW_WORKAROUND SBI EXT checks if the IOCP is missing and
* cache is controllable only then CMO will be applied to the platform.
*/
ret = sbi_ecall(ANDESTECH_SBI_EXT_ANDES, ANDES_SBI_EXT_IOCP_SW_WORKAROUND,
0, 0, 0, 0, 0, 0);
return ret.error ? 0 : ret.value;
}
static bool errata_probe_iocp(unsigned int stage, unsigned long arch_id, unsigned long impid)
{
if (!IS_ENABLED(CONFIG_ERRATA_ANDES_CMO))
return false;
if (arch_id != ANDESTECH_AX45MP_MARCHID || impid != ANDESTECH_AX45MP_MIMPID)
return false;
if (!ax45mp_iocp_sw_workaround())
return false;
/* Set this just to make core cbo code happy */
riscv_cbom_block_size = 1;
riscv_noncoherent_supported();
return true;
}
void __init_or_module andes_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
unsigned long archid, unsigned long impid,
unsigned int stage)
{
errata_probe_iocp(stage, archid, impid);
/* we have nothing to patch here ATM so just return back */
}
......@@ -45,6 +45,9 @@ struct alt_entry {
u32 patch_id; /* The patch ID (erratum ID or cpufeature ID) */
};
void andes_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
unsigned long archid, unsigned long impid,
unsigned int stage);
void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
unsigned long archid, unsigned long impid,
unsigned int stage);
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2023 Renesas Electronics Corp.
*/
#ifndef __ASM_DMA_NONCOHERENT_H
#define __ASM_DMA_NONCOHERENT_H
#include <linux/dma-direct.h>
/*
* struct riscv_nonstd_cache_ops - Structure for non-standard CMO function pointers
*
* @wback: Function pointer for cache writeback
* @inv: Function pointer for invalidating cache
* @wback_inv: Function pointer for flushing the cache (writeback + invalidating)
*/
struct riscv_nonstd_cache_ops {
void (*wback)(phys_addr_t paddr, size_t size);
void (*inv)(phys_addr_t paddr, size_t size);
void (*wback_inv)(phys_addr_t paddr, size_t size);
};
extern struct riscv_nonstd_cache_ops noncoherent_cache_ops;
void riscv_noncoherent_register_cache_ops(const struct riscv_nonstd_cache_ops *ops);
#endif /* __ASM_DMA_NONCOHERENT_H */
......@@ -11,6 +11,11 @@
#include <asm/hwcap.h>
#include <asm/vendorid_list.h>
#ifdef CONFIG_ERRATA_ANDES
#define ERRATA_ANDESTECH_NO_IOCP 0
#define ERRATA_ANDESTECH_NUMBER 1
#endif
#ifdef CONFIG_ERRATA_SIFIVE
#define ERRATA_SIFIVE_CIP_453 0
#define ERRATA_SIFIVE_CIP_1200 1
......
......@@ -5,6 +5,7 @@
#ifndef ASM_VENDOR_LIST_H
#define ASM_VENDOR_LIST_H
#define ANDESTECH_VENDOR_ID 0x31e
#define SIFIVE_VENDOR_ID 0x489
#define THEAD_VENDOR_ID 0x5b7
......
......@@ -42,6 +42,11 @@ static void riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info
#endif
switch (cpu_mfr_info->vendor_id) {
#ifdef CONFIG_ERRATA_ANDES
case ANDESTECH_VENDOR_ID:
cpu_mfr_info->patch_func = andes_errata_patch_func;
break;
#endif
#ifdef CONFIG_ERRATA_SIFIVE
case SIFIVE_VENDOR_ID:
cpu_mfr_info->patch_func = sifive_errata_patch_func;
......
......@@ -9,15 +9,28 @@
#include <linux/dma-map-ops.h>
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <asm/dma-noncoherent.h>
static bool noncoherent_supported __ro_after_init;
int dma_cache_alignment __ro_after_init = ARCH_DMA_MINALIGN;
EXPORT_SYMBOL_GPL(dma_cache_alignment);
struct riscv_nonstd_cache_ops noncoherent_cache_ops __ro_after_init = {
.wback = NULL,
.inv = NULL,
.wback_inv = NULL,
};
static inline void arch_dma_cache_wback(phys_addr_t paddr, size_t size)
{
void *vaddr = phys_to_virt(paddr);
#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
if (unlikely(noncoherent_cache_ops.wback)) {
noncoherent_cache_ops.wback(paddr, size);
return;
}
#endif
ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size);
}
......@@ -25,6 +38,13 @@ static inline void arch_dma_cache_inv(phys_addr_t paddr, size_t size)
{
void *vaddr = phys_to_virt(paddr);
#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
if (unlikely(noncoherent_cache_ops.inv)) {
noncoherent_cache_ops.inv(paddr, size);
return;
}
#endif
ALT_CMO_OP(inval, vaddr, size, riscv_cbom_block_size);
}
......@@ -32,6 +52,13 @@ static inline void arch_dma_cache_wback_inv(phys_addr_t paddr, size_t size)
{
void *vaddr = phys_to_virt(paddr);
#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
if (unlikely(noncoherent_cache_ops.wback_inv)) {
noncoherent_cache_ops.wback_inv(paddr, size);
return;
}
#endif
ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size);
}
......@@ -97,6 +124,13 @@ void arch_dma_prep_coherent(struct page *page, size_t size)
{
void *flush_addr = page_address(page);
#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
if (unlikely(noncoherent_cache_ops.wback_inv)) {
noncoherent_cache_ops.wback_inv(page_to_phys(page), size);
return;
}
#endif
ALT_CMO_OP(flush, flush_addr, size, riscv_cbom_block_size);
}
......@@ -128,3 +162,12 @@ void __init riscv_set_dma_cache_alignment(void)
if (!noncoherent_supported)
dma_cache_alignment = 1;
}
void riscv_noncoherent_register_cache_ops(const struct riscv_nonstd_cache_ops *ops)
{
if (!ops)
return;
noncoherent_cache_ops = *ops;
}
EXPORT_SYMBOL_GPL(riscv_noncoherent_register_cache_ops);
......@@ -7,15 +7,28 @@
#include <linux/libnvdimm.h>
#include <asm/cacheflush.h>
#include <asm/dma-noncoherent.h>
void arch_wb_cache_pmem(void *addr, size_t size)
{
#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
if (unlikely(noncoherent_cache_ops.wback)) {
noncoherent_cache_ops.wback(virt_to_phys(addr), size);
return;
}
#endif
ALT_CMO_OP(clean, addr, size, riscv_cbom_block_size);
}
EXPORT_SYMBOL_GPL(arch_wb_cache_pmem);
void arch_invalidate_pmem(void *addr, size_t size)
{
#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS
if (unlikely(noncoherent_cache_ops.inv)) {
noncoherent_cache_ops.inv(virt_to_phys(addr), size);
return;
}
#endif
ALT_CMO_OP(inval, addr, size, riscv_cbom_block_size);
}
EXPORT_SYMBOL_GPL(arch_invalidate_pmem);
......@@ -15,6 +15,8 @@ source "drivers/base/Kconfig"
source "drivers/bus/Kconfig"
source "drivers/cache/Kconfig"
source "drivers/connector/Kconfig"
source "drivers/firmware/Kconfig"
......
......@@ -11,6 +11,7 @@ ifdef building_out_of_srctree
MAKEFLAGS += --include-dir=$(srctree)
endif
obj-y += cache/
obj-y += irqchip/
obj-y += bus/
......
# SPDX-License-Identifier: GPL-2.0
menu "Cache Drivers"
config AX45MP_L2_CACHE
bool "Andes Technology AX45MP L2 Cache controller"
depends on RISCV_DMA_NONCOHERENT
select RISCV_NONSTANDARD_CACHE_OPS
help
Support for the L2 cache controller on Andes Technology AX45MP platforms.
endmenu
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_AX45MP_L2_CACHE) += ax45mp_cache.o
// SPDX-License-Identifier: GPL-2.0
/*
* non-coherent cache functions for Andes AX45MP
*
* Copyright (C) 2023 Renesas Electronics Corp.
*/
#include <linux/cacheflush.h>
#include <linux/cacheinfo.h>
#include <linux/dma-direction.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <asm/dma-noncoherent.h>
/* L2 cache registers */
#define AX45MP_L2C_REG_CTL_OFFSET 0x8
#define AX45MP_L2C_REG_C0_CMD_OFFSET 0x40
#define AX45MP_L2C_REG_C0_ACC_OFFSET 0x48
#define AX45MP_L2C_REG_STATUS_OFFSET 0x80
/* D-cache operation */
#define AX45MP_CCTL_L1D_VA_INVAL 0 /* Invalidate an L1 cache entry */
#define AX45MP_CCTL_L1D_VA_WB 1 /* Write-back an L1 cache entry */
/* L2 CCTL status */
#define AX45MP_CCTL_L2_STATUS_IDLE 0
/* L2 CCTL status cores mask */
#define AX45MP_CCTL_L2_STATUS_C0_MASK 0xf
/* L2 cache operation */
#define AX45MP_CCTL_L2_PA_INVAL 0x8 /* Invalidate an L2 cache entry */
#define AX45MP_CCTL_L2_PA_WB 0x9 /* Write-back an L2 cache entry */
#define AX45MP_L2C_REG_PER_CORE_OFFSET 0x10
#define AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET 4
#define AX45MP_L2C_REG_CN_CMD_OFFSET(n) \
(AX45MP_L2C_REG_C0_CMD_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET))
#define AX45MP_L2C_REG_CN_ACC_OFFSET(n) \
(AX45MP_L2C_REG_C0_ACC_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET))
#define AX45MP_CCTL_L2_STATUS_CN_MASK(n) \
(AX45MP_CCTL_L2_STATUS_C0_MASK << ((n) * AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET))
#define AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM 0x80b
#define AX45MP_CCTL_REG_UCCTLCOMMAND_NUM 0x80c
#define AX45MP_CACHE_LINE_SIZE 64
struct ax45mp_priv {
void __iomem *l2c_base;
u32 ax45mp_cache_line_size;
};
static struct ax45mp_priv ax45mp_priv;
/* L2 Cache operations */
static inline uint32_t ax45mp_cpu_l2c_get_cctl_status(void)
{
return readl(ax45mp_priv.l2c_base + AX45MP_L2C_REG_STATUS_OFFSET);
}
static void ax45mp_cpu_cache_operation(unsigned long start, unsigned long end,
unsigned int l1_op, unsigned int l2_op)
{
unsigned long line_size = ax45mp_priv.ax45mp_cache_line_size;
void __iomem *base = ax45mp_priv.l2c_base;
int mhartid = smp_processor_id();
unsigned long pa;
while (end > start) {
csr_write(AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM, start);
csr_write(AX45MP_CCTL_REG_UCCTLCOMMAND_NUM, l1_op);
pa = virt_to_phys((void *)start);
writel(pa, base + AX45MP_L2C_REG_CN_ACC_OFFSET(mhartid));
writel(l2_op, base + AX45MP_L2C_REG_CN_CMD_OFFSET(mhartid));
while ((ax45mp_cpu_l2c_get_cctl_status() &
AX45MP_CCTL_L2_STATUS_CN_MASK(mhartid)) !=
AX45MP_CCTL_L2_STATUS_IDLE)
;
start += line_size;
}
}
/* Write-back L1 and L2 cache entry */
static inline void ax45mp_cpu_dcache_wb_range(unsigned long start, unsigned long end)
{
ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_WB,
AX45MP_CCTL_L2_PA_WB);
}
/* Invalidate the L1 and L2 cache entry */
static inline void ax45mp_cpu_dcache_inval_range(unsigned long start, unsigned long end)
{
ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_INVAL,
AX45MP_CCTL_L2_PA_INVAL);
}
static void ax45mp_dma_cache_inv(phys_addr_t paddr, size_t size)
{
unsigned long start = (unsigned long)phys_to_virt(paddr);
unsigned long end = start + size;
unsigned long line_size;
unsigned long flags;
if (unlikely(start == end))
return;
line_size = ax45mp_priv.ax45mp_cache_line_size;
start = start & (~(line_size - 1));
end = ((end + line_size - 1) & (~(line_size - 1)));
local_irq_save(flags);
ax45mp_cpu_dcache_inval_range(start, end);
local_irq_restore(flags);
}
static void ax45mp_dma_cache_wback(phys_addr_t paddr, size_t size)
{
unsigned long start = (unsigned long)phys_to_virt(paddr);
unsigned long end = start + size;
unsigned long line_size;
unsigned long flags;
line_size = ax45mp_priv.ax45mp_cache_line_size;
start = start & (~(line_size - 1));
local_irq_save(flags);
ax45mp_cpu_dcache_wb_range(start, end);
local_irq_restore(flags);
}
static void ax45mp_dma_cache_wback_inv(phys_addr_t paddr, size_t size)
{
ax45mp_dma_cache_wback(paddr, size);
ax45mp_dma_cache_inv(paddr, size);
}
static int ax45mp_get_l2_line_size(struct device_node *np)
{
int ret;
ret = of_property_read_u32(np, "cache-line-size", &ax45mp_priv.ax45mp_cache_line_size);
if (ret) {
pr_err("Failed to get cache-line-size, defaulting to 64 bytes\n");
return ret;
}
if (ax45mp_priv.ax45mp_cache_line_size != AX45MP_CACHE_LINE_SIZE) {
pr_err("Expected cache-line-size to be 64 bytes (found:%u)\n",
ax45mp_priv.ax45mp_cache_line_size);
return -EINVAL;
}
return 0;
}
static const struct riscv_nonstd_cache_ops ax45mp_cmo_ops __initdata = {
.wback = &ax45mp_dma_cache_wback,
.inv = &ax45mp_dma_cache_inv,
.wback_inv = &ax45mp_dma_cache_wback_inv,
};
static const struct of_device_id ax45mp_cache_ids[] = {
{ .compatible = "andestech,ax45mp-cache" },
{ /* sentinel */ }
};
static int __init ax45mp_cache_init(void)
{
struct device_node *np;
struct resource res;
int ret;
np = of_find_matching_node(NULL, ax45mp_cache_ids);
if (!of_device_is_available(np))
return -ENODEV;
ret = of_address_to_resource(np, 0, &res);
if (ret)
return ret;
/*
* If IOCP is present on the Andes AX45MP core riscv_cbom_block_size
* will be 0 for sure, so we can definitely rely on it. If
* riscv_cbom_block_size = 0 we don't need to handle CMO using SW any
* more so we just return success here and only if its being set we
* continue further in the probe path.
*/
if (!riscv_cbom_block_size)
return 0;
ax45mp_priv.l2c_base = ioremap(res.start, resource_size(&res));
if (!ax45mp_priv.l2c_base)
return -ENOMEM;
ret = ax45mp_get_l2_line_size(np);
if (ret) {
iounmap(ax45mp_priv.l2c_base);
return ret;
}
riscv_noncoherent_register_cache_ops(&ax45mp_cmo_ops);
return 0;
}
early_initcall(ax45mp_cache_init);
......@@ -334,6 +334,10 @@ if RISCV
config ARCH_R9A07G043
bool "RISC-V Platform support for RZ/Five"
select ARCH_RZG2L
select AX45MP_L2_CACHE
select DMA_GLOBAL_POOL
select ERRATA_ANDES
select ERRATA_ANDES_CMO
help
This enables support for the Renesas RZ/Five SoC.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment