This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 77e36d1acc risc-v/mpfs: introduce IHC driver 77e36d1acc is described below commit 77e36d1acc03ab23be9a99252a66ef0eead0e759 Author: Eero Nurkkala <eero.nurkk...@offcode.fi> AuthorDate: Tue May 3 07:33:16 2022 +0300 risc-v/mpfs: introduce IHC driver This provides an example of Asymmetric Multiprocessing (AMP). The master from Linux sends pings that this NuttX echoes back. The system uses RPMsg from OpenAMP. The Inter-Hart Communication module is present in the vendor's software stack with the tag "2021.11". The software is present on github at the polarfire-soc project. The following conditions must be met: 1. FPGA programmed with 2021.11 software 2. HSS (Vendor bootloader) with 2021.11 software 3. U-boot and Linux kernel from 2011.11 software Currently the IHC works as a slave only on the hart number 4. On the NuttX side, this patch uses rptun that incorporates rpmsg and virtio. If it used only rpmsg and virtio, the future maintenance would likely be much heavier. Using rptun also simplifies many things. Upon success, the master side from Linux may issue an example test: root@icicle-kit-es-amp:/opt/microchip/amp/rpmsg-pingpong# ./rpmsg-pingpong However, the rpmsg-pingpong.c (compiled on target with gcc), may need to be modified as seen below to match the device id: - char *rpmsg_dev="virtio0.rpmsg-amp-demo-channel.-1.0"; + char *rpmsg_dev="virtio0.rpmsg-amp-demo-channel.-1.1024"; This work uses a separate linker script. Due to a bug yet unknown to date, a small NuttX, when loaded by the vendor HSS bootloader, will cause the Linux kernel to hang at boot. Thus, the binary size is increased with a section 'filler_area' whose only purpose is to increase the image size so that the Linux kernel will boot up. Signed-off-by: Eero Nurkkala <eero.nurkk...@offcode.fi> --- arch/risc-v/src/mpfs/Kconfig | 7 + arch/risc-v/src/mpfs/Make.defs | 4 + arch/risc-v/src/mpfs/hardware/mpfs_ihc.h | 252 +++++ arch/risc-v/src/mpfs/mpfs_ihc.c | 1276 +++++++++++++++++++++++ arch/risc-v/src/mpfs/mpfs_ihc.h | 74 ++ boards/risc-v/mpfs/common/src/Make.defs | 4 + boards/risc-v/mpfs/common/src/mpfs_ihc.c | 62 ++ boards/risc-v/mpfs/icicle/configs/ihc/defconfig | 101 ++ boards/risc-v/mpfs/icicle/scripts/Make.defs | 6 +- boards/risc-v/mpfs/icicle/scripts/ld-ihc.script | 124 +++ boards/risc-v/mpfs/icicle/src/board_config.h | 1 + boards/risc-v/mpfs/icicle/src/mpfs_bringup.c | 9 + 12 files changed, 1919 insertions(+), 1 deletion(-) diff --git a/arch/risc-v/src/mpfs/Kconfig b/arch/risc-v/src/mpfs/Kconfig index d269a8b828..6c8c554b0c 100755 --- a/arch/risc-v/src/mpfs/Kconfig +++ b/arch/risc-v/src/mpfs/Kconfig @@ -239,6 +239,13 @@ config MPFS_EMMCSD ---help--- Selects the MPFS eMMCSD driver. +config MPFS_IHC + bool "IHC slave" + depends on RPTUN + default n + ---help--- + Selects and enables the Inter-Hart-Communication (IHC) slave driver. + config MPFS_ETHMAC bool default n diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index 77c55914a4..92b6b9ccb2 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -143,3 +143,7 @@ endif ifeq ($(CONFIG_USBDEV),y) CHIP_CSRCS += mpfs_usb.c endif + +ifeq ($(CONFIG_MPFS_IHC),y) +CHIP_CSRCS += mpfs_ihc.c +endif diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_ihc.h b/arch/risc-v/src/mpfs/hardware/mpfs_ihc.h new file mode 100755 index 0000000000..280cd9cfe8 --- /dev/null +++ b/arch/risc-v/src/mpfs/hardware/mpfs_ihc.h @@ -0,0 +1,252 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/hardware/mpfs_ihc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_IHC_H +#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_IHC_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MPFS_NUM_HARTS 5 +#define UNDEFINED_HART_ID 99 + +/* My Hart 0 */ + +#define IHC_LOCAL_H0_REMOTE_H1 0x50000000 +#define IHC_LOCAL_H0_REMOTE_H2 0x50000100 +#define IHC_LOCAL_H0_REMOTE_H3 0x50000200 +#define IHC_LOCAL_H0_REMOTE_H4 0x50000300 +#define IHCIA_LOCAL_H0 0x50000400 + +/* My Hart 0 */ + +#define IHC_LOCAL_H1_REMOTE_H0 0x50000500 +#define IHC_LOCAL_H1_REMOTE_H2 0x50000600 +#define IHC_LOCAL_H1_REMOTE_H3 0x50000700 +#define IHC_LOCAL_H1_REMOTE_H4 0x50000800 +#define IHCIA_LOCAL_H1 0x50000900 + +/* My Hart 0 */ + +#define IHC_LOCAL_H2_REMOTE_H0 0x50000a00 +#define IHC_LsOCAL_H2_REMOTE_H1 0x50000b00 +#define IHC_LOCAL_H2_REMOTE_H3 0x50000c00 +#define IHC_LOCAL_H2_REMOTE_H4 0x50000d00 +#define IHCIA_LOCAL_H2 0x50000e00 + +/* My Hart 0 */ + +#define IHC_LOCAL_H3_REMOTE_H0 0x50000f00 +#define IHC_LOCAL_H3_REMOTE_H1 0x50001000 +#define IHC_LOCAL_H3_REMOTE_H2 0x50001100 +#define IHC_LOCAL_H3_REMOTE_H4 0x50001200 +#define IHCIA_LOCAL_H3 0x50001300 + +/* My Hart 0 */ + +#define IHC_LOCAL_H4_REMOTE_H0 0x50001400 +#define IHC_LOCAL_H4_REMOTE_H1 0x50001500 +#define IHC_LOCAL_H4_REMOTE_H2 0x50001600 +#define IHC_LOCAL_H4_REMOTE_H3 0x50001700 +#define IHCIA_LOCAL_H4 0x50001800 + +#define MPFS_IHC_VERSION_OFFSET 0x00 +#define MPFS_IHC_CTRL_OFFSET 0x04 +#define MPFS_IHC_LOCAL_HARTID_OFFSET 0x08 +#define MPFS_IHC_MSG_SIZE_OFFSET 0x0c +#define MPFS_IHC_MSG_UNUSED_OFFSET 0x10 +#define MPFS_IHC_MSG_IN_OFFSET 0x20 +#define MPFS_IHC_MSG_OUT_OFFSET 0x30 + +#define MPFS_IHC_INT_EN_OFFSET 0x04 +#define MPFS_IHC_MSG_AVAIL_OFFSET 0x08 + +#define MPFS_LOCAL_REMOTE_OFFSET(l, r) (0x500 * l + 0x100 * r) + +#define MPFS_IHC_VERSION(l, r) (IHC_LOCAL_H0_REMOTE_H1 + MPFS_IHC_VERSION_OFFSET + MPFS_LOCAL_REMOTE_OFFSET(l, r)) +#define MPFS_IHC_CTRL(l, r) (IHC_LOCAL_H0_REMOTE_H1 + MPFS_IHC_CTRL_OFFSET + MPFS_LOCAL_REMOTE_OFFSET(l, r)) +#define MPFS_IHC_LOCAL_HARTID(l, r) (IHC_LOCAL_H0_REMOTE_H1 + MPFS_IHC_LOCAL_HARTID_OFFSET + MPFS_LOCAL_REMOTE_OFFSET(l, r)) +#define MPFS_IHC_MSG_SIZE(l, r) (IHC_LOCAL_H0_REMOTE_H1 + MPFS_IHC_MSG_SIZE_OFFSET + MPFS_LOCAL_REMOTE_OFFSET(l, r)) +#define MPFS_IHC_MSG_IN(l, r) (IHC_LOCAL_H0_REMOTE_H1 + MPFS_IHC_MSG_IN_OFFSET + MPFS_LOCAL_REMOTE_OFFSET(l, r)) +#define MPFS_IHC_MSG_OUT(l, r) (IHC_LOCAL_H0_REMOTE_H1 + MPFS_IHC_MSG_OUT_OFFSET + MPFS_LOCAL_REMOTE_OFFSET(l, r)) + +#define MPFS_IHC_INT_EN(l) (IHCIA_LOCAL_H0 + MPFS_IHC_INT_EN_OFFSET + 0x500 * l) +#define MPFS_IHC_MSG_AVAIL(l) (IHCIA_LOCAL_H0 + MPFS_IHC_MSG_AVAIL_OFFSET + 0x500 * l) + +/* Hart mask defines */ + +#define HART0_ID 0 +#define HART1_ID 1 +#define HART2_ID 2 +#define HART3_ID 3 +#define HART4_ID 4 + +#define HART0_MASK 1 +#define HART1_MASK 2 +#define HART2_MASK 4 +#define HART3_MASK 8 +#define HART4_MASK 0x10 + +/* Monitor hart (HSS hart) used in our system */ + +#define HSS_HART_MASK HART0_MASK +#define HSS_HART_ID HART0_ID + +/* HSS_REMOTE_HARTS_MASK: This is used to define the harts the HSS is + * communicating with + */ + +#define HSS_REMOTE_HARTS_MASK (HART1_MASK | HART2_MASK | HART3_MASK | HART4_MASK) + +/* Contex A and B hart ID's used in this system. Context A is the master. */ + +#define CONTEXTA_HARTID 0x01 +#define CONTEXTB_HARTID 0x04 + +/* Define which harts are connected via comms channels to a particular hart + * user defined. + */ + +#define IHCIA_H0_REMOTE_HARTS ((~HSS_HART_MASK) & HSS_REMOTE_HARTS_MASK) + +/* HSS and Context B connected */ + +#define IHCIA_H1_REMOTE_HARTS (HSS_HART_MASK | (HART4_MASK)) +#define IHCIA_H2_REMOTE_HARTS (HSS_HART_MASK) +#define IHCIA_H3_REMOTE_HARTS (HSS_HART_MASK) + +/* HSS and Context A connected */ + +#define IHCIA_H4_REMOTE_HARTS (HSS_HART_MASK | (HART1_MASK)) + +#define HSS_HART_DEFAULT_INT_EN (0 << 0) + +#define HSS_HART_MP_INT_EN (1 << 0) +#define HSS_HART_ACK_INT_EN (1 << 1) + +#define HART1_MP_INT_EN (1 << 2) +#define HART1_ACK_INT_EN (1 << 3) + +#define HART2_MP_INT_EN (1 << 4) +#define HART2_ACK_INT_EN (1 << 5) + +#define HART3_MP_INT_EN (1 << 6) +#define HART3_ACK_INT_EN (1 << 7) + +#define HART4_MP_INT_EN (1 << 8) +#define HART4_ACK_INT_EN (1 << 9) + +/* Connected to all harts */ + +#define IHCIA_H0_REMOTE_HARTS_INTS HSS_HART_DEFAULT_INT_EN + +/* HSS and Context B connected */ + +#define IHCIA_H1_REMOTE_HARTS_INTS (HSS_HART_MP_INT_EN | \ + HSS_HART_ACK_INT_EN | \ + HART4_MP_INT_EN | \ + HART4_ACK_INT_EN) + +#define IHCIA_H2_REMOTE_HARTS_INTS HSS_HART_DEFAULT_INT_EN +#define IHCIA_H3_REMOTE_HARTS_INTS HSS_HART_DEFAULT_INT_EN + +/* HSS and Context A connected */ + +#define IHCIA_H4_REMOTE_HARTS_INTS (HSS_HART_MP_INT_EN | \ + HSS_HART_ACK_INT_EN | \ + HART1_MP_INT_EN | \ + HART1_ACK_INT_EN) + +/* MiV-IHCC register bit definitions */ + +#define RMP_MESSAGE_PRESENT (1 << 0) /* Remote side message present */ +#define MP_MESSAGE_PRESENT (1 << 1) /* Local side message present */ +#define MPIE_EN (1 << 2) /* Enable MP interrupt */ +#define ACK_INT (1 << 3) /* Incoming ACK */ +#define ACK_CLR (1 << 4) /* Clear ACK */ +#define ACKIE_EN (1 << 5) /* Enable Ack Interrupt */ + +/* Control register bit MASKS */ + +#define RMP_MASK (1 << 0) +#define MP_MASK (1 << 1) +#define MPIE_MASK (1 << 2) +#define ACK_INT_MASK (1 << 3) + +#define IHC_MAX_MESSAGE_SIZE 4 + +#define LIBERO_SETTING_CONTEXT_A_HART_EN 0x0000000eul /* Harts 1 to 3 */ +#define LIBERO_SETTING_CONTEXT_B_HART_EN 0x00000010ul /* Hart 4 */ + +typedef union ihca_ip_int_en_t_ +{ + uint32_t int_en; + struct + { + uint32_t mp_h0_en : 1; + uint32_t ack_h0_en : 1; + uint32_t mp_h1_en : 1; + uint32_t ack_h1_en : 1; + uint32_t mp_h2_en : 1; + uint32_t ack_h2_en : 1; + uint32_t mp_h3_en : 1; + uint32_t ack_h3_en : 1; + uint32_t mp_h4_en : 1; + uint32_t ack_h4_en : 1; + uint32_t reserved : 22; + } bitfield; +} ihca_ip_int_en_t; + +typedef union ihca_ip_msg_avail_stat_t_ +{ + uint32_t msg_avail; + struct + { + uint32_t mp_h0 : 1; + uint32_t ack_h0 : 1; + uint32_t mp_h1 : 1; + uint32_t ack_h1 : 1; + uint32_t mp_h2 : 1; + uint32_t ack_h2 : 1; + uint32_t mp_h3 : 1; + uint32_t ack_h3 : 1; + uint32_t mp_h4 : 1; + uint32_t ack_h4 : 1; + uint32_t reserved : 22; + } bitfield; +} ihca_ip_msg_avail_stat_t; + +typedef union +{ + uint32_t ctl_reg; + struct + { + uint32_t rpm :1; /* Remote message present */ + uint32_t mp :1; /* Message present */ + uint32_t mpie :1; /* Message present interrupt enable */ + uint32_t ack :1; + uint32_t clr_ack :1; + uint32_t ackie :1; /* Ack interrupt enable */ + uint32_t reserved :26; + } bitfield; +} miv_ihcc_ctl_reg_t; + +#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_IHC_H */ diff --git a/arch/risc-v/src/mpfs/mpfs_ihc.c b/arch/risc-v/src/mpfs/mpfs_ihc.c new file mode 100755 index 0000000000..6f31ea4bd6 --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_ihc.c @@ -0,0 +1,1276 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_ihc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <time.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/kthread.h> +#include <nuttx/semaphore.h> +#include <nuttx/spi/spi.h> + +#include <nuttx/rptun/openamp.h> +#include <nuttx/rptun/rptun.h> +#include <nuttx/drivers/addrenv.h> +#include <nuttx/list.h> + +#include <arch/board/board.h> + +#include "hardware/mpfs_sysreg.h" +#include "hardware/mpfs_ihc.h" +#include "riscv_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_MPFS_IHC_DEBUG +# define ihcerr _err +# define ihcwarn _warn +# define ihcinfo _info +#else +# define ihcerr(x...) +# define ihcwarn(x...) +# define ihcinfo(x...) +#endif + +/* Slave, IHC_CHANNEL_SIDE_A is master */ + +#define IHC_CHANNEL_SIDE_B + +/* This name is picked by the master */ + +#define MPFS_RPTUN_PING_EPT_NAME "rpmsg-amp-demo-channel" + +/* rptun initialization names */ + +#define MPFS_RPTUN_CPU_NAME "mpfs-hart4" +#define MPFS_RPTUN_SHMEM_NAME "mpfs-shmem" + +/* Vring configuration parameters */ + +#define VRING_SHMEM 0xa2410000 /* Vring shared memory start */ +#define VRING0_DESCRIPTORS 0xa2400000 /* Vring0 descriptor area */ +#define VRING1_DESCRIPTORS 0xa2408000 /* Vring1 descriptor area */ +#define VRINGS 0x02 /* Number of vrings */ +#define VRING_ALIGN 0x1000 /* Vring alignment */ +#define VRING_NR 256 /* Number of descriptors */ +#define VRING_SIZE 512 /* Size of one descriptor */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mpfs_rptun_shmem_s +{ + volatile uintptr_t base; + volatile unsigned int seqs; + volatile unsigned int seqm; + struct rptun_rsc_s rsc; + bool master_up; +}; + +struct mpfs_rptun_dev_s +{ + struct list_node node; + struct rptun_dev_s rptun; + rptun_callback_t callback; + void *arg; + bool master; + unsigned int seq; + struct mpfs_rptun_shmem_s *shmem; + struct simple_addrenv_s addrenv[VRINGS]; + char cpuname[RPMSG_NAME_SIZE + 1]; + char shmemname[RPMSG_NAME_SIZE + 1]; +}; + +typedef enum +{ + IHC_CHANNEL_TO_HART0 = 0x00, /* Your hart to hart 0 */ + IHC_CHANNEL_TO_HART1 = 0x01, /* Your hart to hart 1 */ + IHC_CHANNEL_TO_HART2 = 0x02, /* Your hart to hart 2 */ + IHC_CHANNEL_TO_HART3 = 0x03, /* Your hart to hart 3 */ + IHC_CHANNEL_TO_HART4 = 0x04, /* Your hart to hart 4 */ + ihc_channel_to_contexta = 0x05, /* Your hart to context A */ + ihc_channel_to_contextb = 0x06, /* Your hart to context B */ +} ihc_channel_t; + +struct mpfs_queue_table +{ + void *data; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static const char *mpfs_rptun_get_cpuname(struct rptun_dev_s *dev); +static const char *mpfs_rptun_get_firmware(struct rptun_dev_s *dev); +static const struct rptun_addrenv_s +*mpfs_rptun_get_addrenv(struct rptun_dev_s *dev); +static struct rptun_rsc_s *mpfs_rptun_get_resource(struct rptun_dev_s *dev); +static bool mpfs_rptun_is_autostart(struct rptun_dev_s *dev); +static bool mpfs_rptun_is_master(struct rptun_dev_s *dev); +static int mpfs_rptun_start(struct rptun_dev_s *dev); +static int mpfs_rptun_stop(struct rptun_dev_s *dev); +static int mpfs_rptun_notify(struct rptun_dev_s *dev, uint32_t notifyid); +static int mpfs_rptun_register_callback(struct rptun_dev_s *dev, + rptun_callback_t callback, + void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is a bug workaround. This increases the image size in purpose. The + * problem currently is, that the vendor based bootloader (HSS) in + * combination with u-boot / Linux kernel will not boot if this NuttX image + * is small. In the linker script we KEEP the filler_area section so that no + * compiler will optimize it away. This will be removed once the root cause + * has been found out. + */ + +uint8_t unused_filler[0x80000] __attribute__((section(".filler_area"))); + +static struct rpmsg_endpoint g_mpgs_echo_ping_ept; +static struct mpfs_queue_table g_mpfs_virtqueue_table[VRINGS]; +static struct mpfs_rptun_shmem_s g_shmem; +static struct rpmsg_device *g_mpfs_rpmsg_device; +static struct rpmsg_virtio_device *g_mpfs_virtio_device; + +static sem_t g_mpfs_ack_lock = SEM_INITIALIZER(1); +static sem_t g_mpfs_rx_lock = SEM_INITIALIZER(1); +static struct list_node g_dev_list = LIST_INITIAL_VALUE(g_dev_list); + +static uint32_t g_connected_hart_ints; +static uint32_t g_connected_harts; +static int g_vq_idx = 0; + +const uint32_t ihcia_remote_harts[MPFS_NUM_HARTS] = +{ + IHCIA_H0_REMOTE_HARTS, + IHCIA_H1_REMOTE_HARTS, + IHCIA_H2_REMOTE_HARTS, + IHCIA_H3_REMOTE_HARTS, + IHCIA_H4_REMOTE_HARTS +}; + +const uint32_t ihcia_remote_hart_ints[MPFS_NUM_HARTS] = +{ + IHCIA_H0_REMOTE_HARTS_INTS, + IHCIA_H1_REMOTE_HARTS_INTS, + IHCIA_H2_REMOTE_HARTS_INTS, + IHCIA_H3_REMOTE_HARTS_INTS, + IHCIA_H4_REMOTE_HARTS_INTS +}; + +static const struct rptun_ops_s g_mpfs_rptun_ops = +{ + .get_cpuname = mpfs_rptun_get_cpuname, + .get_firmware = mpfs_rptun_get_firmware, + .get_addrenv = mpfs_rptun_get_addrenv, + .get_resource = mpfs_rptun_get_resource, + .is_autostart = mpfs_rptun_is_autostart, + .is_master = mpfs_rptun_is_master, + .start = mpfs_rptun_start, + .stop = mpfs_rptun_stop, + .notify = mpfs_rptun_notify, + .register_callback = mpfs_rptun_register_callback, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_ihc_parse_incoming_hartid + * + * Description: + * This function determines the remote hart id and whether the remote is + * acking to a message or not. + * + * Input Parameters: + * is_ack - Boolean that is set true if an ack has been found + * + * Returned Value: + * Remote hart id + * + ****************************************************************************/ + +static uint32_t mpfs_ihc_parse_incoming_hartid(bool *is_ack) +{ + uint32_t mhartid = riscv_mhartid(); + uint32_t hart_id = 0; + uint32_t return_hart_id = UNDEFINED_HART_ID; + uint32_t msg_avail = getreg32(MPFS_IHC_MSG_AVAIL(mhartid)); + + while (hart_id < MPFS_NUM_HARTS) + { + if (g_connected_harts & (1 << hart_id)) + { + uint32_t test_int = (1 << ((hart_id * 2) + 1)); + + if (msg_avail & test_int) + { + if (g_connected_hart_ints & test_int) + { + return_hart_id = hart_id; + *is_ack = true; + break; + } + } + + test_int = (1 << (hart_id * 2)); + + if (msg_avail & test_int) + { + if (((g_connected_hart_ints & test_int) == test_int)) + { + return_hart_id = hart_id; + *is_ack = false; + break; + } + } + } + + hart_id++; + } + + return return_hart_id; +} + +/**************************************************************************** + * Name: mpfs_ihc_context_to_remote_hart_id + * + * Description: + * This function determines the remote hart id with the provided context + * handle. + * + * Input Parameters: + * channel - Enum that describes the channel used. + * + * Returned Value: + * Remote hart id + * + ****************************************************************************/ + +static uint32_t mpfs_ihc_context_to_remote_hart_id(ihc_channel_t channel) +{ + uint32_t harts_in_context = LIBERO_SETTING_CONTEXT_B_HART_EN; + uint32_t hart = UNDEFINED_HART_ID; + uint32_t hart_idx = 0; + + if (channel <= IHC_CHANNEL_TO_HART4) + { + hart = channel; + } + else + { + DEBUGASSERT(LIBERO_SETTING_CONTEXT_A_HART_EN > 0); + DEBUGASSERT(LIBERO_SETTING_CONTEXT_B_HART_EN > 0); + + /* Determine context we are in */ + + if (channel == ihc_channel_to_contexta) + { + harts_in_context = LIBERO_SETTING_CONTEXT_A_HART_EN; + } + else + { + harts_in_context = LIBERO_SETTING_CONTEXT_B_HART_EN; + } + + hart_idx = 0; + + while (hart_idx < MPFS_NUM_HARTS) + { + if (harts_in_context & (1 << hart_idx)) + { + hart = hart_idx; + break; + } + + hart_idx++; + } + } + + DEBUGASSERT(hart != UNDEFINED_HART_ID); + + return hart; +} + +/**************************************************************************** + * Name: mpfs_ihc_rx_handler + * + * Description: + * This handles the received information and either lets the vq to proceed + * via posting g_mpfs_ack_lock, or lets the mpfs_rptun_thread() run as it + * waits for the g_mpfs_rx_lock. virtqueue_notification() cannot be called + * from the interrupt context, thus the thread that will perform it. + * + * Input Parameters: + * message - Pointer to the incoming message + * is_ack - Boolean indicating whether an ack is received + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ihc_rx_handler(uint32_t *message, bool is_ack) +{ + if (is_ack == true) + { + /* Received the ack */ + + nxsem_post(&g_mpfs_ack_lock); + } + else + { + g_vq_idx = (message[0] >> 16); + + DEBUGASSERT(g_vq_idx < VRINGS); + + nxsem_post(&g_mpfs_rx_lock); + } +} + +/**************************************************************************** + * Name: mpfs_ihc_rx_message + * + * Description: + * This function determines the remote hart id with the provided context + * handle. + * + * Input Parameters: + * channel - Enum that describes the channel used. + * is_ack - Boolean indicating an ack message + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ihc_rx_message(ihc_channel_t channel, bool is_ack) +{ + uint64_t mhartid = riscv_mhartid(); + uint32_t rhartid = mpfs_ihc_context_to_remote_hart_id(channel); + uint32_t ctrl_reg = getreg32(MPFS_IHC_CTRL(mhartid, rhartid)); + + if (is_ack == true) + { + mpfs_ihc_rx_handler((uint32_t *)MPFS_IHC_MSG_IN(mhartid, rhartid), + is_ack); + } + else if (MP_MESSAGE_PRESENT == (ctrl_reg & MP_MASK)) + { + /* Check if we have a message */ + + mpfs_ihc_rx_handler((uint32_t *)MPFS_IHC_MSG_IN(mhartid, rhartid), + is_ack); + + /* Set MP to 0. Note this generates an interrupt on the other hart + * if it has RMPIE bit set in the control register + */ + + volatile uint32_t temp = getreg32(MPFS_IHC_CTRL(mhartid, rhartid)) & + ~MP_MASK; + + /* Check if ACKIE_EN is set */ + + if (temp & ACKIE_EN) + { + temp |= ACK_INT; + } + + putreg32(temp, MPFS_IHC_CTRL(mhartid, rhartid)); + } +} + +/**************************************************************************** + * Name: mpfs_ihc_message_present_isr + * + * Description: + * This is called from the interrupt handler. This figures out the actions + * based on the information retieved from the subsequent functions. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ihc_message_present_isr(void) +{ + uint64_t mhartid = riscv_mhartid(); + bool is_ack; + + /* Check all our channels */ + + uint32_t origin_hart = mpfs_ihc_parse_incoming_hartid(&is_ack); + + if (origin_hart != UNDEFINED_HART_ID) + { + /* This is used to declare the master is up and running */ + + g_shmem.master_up = true; + + /* Process incoming packet */ + + mpfs_ihc_rx_message(origin_hart, is_ack); + + if (is_ack == true) + { + /* Clear the ack */ + + modifyreg32(MPFS_IHC_CTRL(mhartid, origin_hart), + ACK_CLR, 0); + } + } +} + +/**************************************************************************** + * Name: mpfs_ihc_interrupt + * + * Description: + * This is the interrupt handler. + * + * Input Parameters: + * irq - unused + * context - context, unused + * arg - private data pointer, unused + * + * Returned Value: + * OK always + * + ****************************************************************************/ + +static int mpfs_ihc_interrupt(int irq, void *context, void *arg) +{ + /* The 1st proper interrupt indicates the master is up */ + + if (!g_shmem.master_up) + { + g_shmem.rsc.rpmsg_vdev.status |= VIRTIO_CONFIG_STATUS_DRIVER_OK; + } + + mpfs_ihc_message_present_isr(); + + return OK; +} + +/**************************************************************************** + * Name: mpfs_ihc_local_context_init + * + * Description: + * This initializes the local context by zeroing the CTRL register and + * applying proper values for the g_connected_harts and + * g_connected_hart_ints globals. These globals are used to map the harts + * and contexts properly. + * + * Input Parameters: + * hart_to_configure - Hart to be configured + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ihc_local_context_init(uint32_t hart_to_configure) +{ + uint32_t rhartid = 0; + + DEBUGASSERT(hart_to_configure < MPFS_NUM_HARTS); + + /* Configure the base addresses in this hart context */ + + while (rhartid < MPFS_NUM_HARTS) + { + if (rhartid != hart_to_configure) + { + putreg32(0, MPFS_IHC_CTRL(hart_to_configure, rhartid)); + } + + rhartid++; + } + + putreg32(0, MPFS_IHC_INT_EN(hart_to_configure)); + + g_connected_harts = ihcia_remote_harts[hart_to_configure]; + g_connected_hart_ints = ihcia_remote_hart_ints[hart_to_configure]; +} + +/**************************************************************************** + * Name: mpfs_ihc_local_remote_config + * + * Description: + * This enables the required interrupts via two registers. + * + * Input Parameters: + * hart_to_configure - Hart to be configured + * rhartid - The associated remote hart id + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ihc_local_remote_config(uint32_t hart_to_configure, + uint32_t rhartid) +{ + /* Set-up enables in concentrator */ + + putreg32(ihcia_remote_hart_ints[hart_to_configure], + MPFS_IHC_INT_EN(hart_to_configure)); + + modifyreg32(MPFS_IHC_CTRL(hart_to_configure, rhartid), 0, MPIE_EN | + ACKIE_EN); +} + +/**************************************************************************** + * Name: mpfs_ihc_context_to_local_hart_id + * + * Description: + * Maps the context to a local hart id. + * + * Input Parameters: + * channel - Enum that describes the channel used. + * + * Returned Value: + * Local hart id + * + ****************************************************************************/ + +static uint32_t mpfs_ihc_context_to_local_hart_id(ihc_channel_t channel) +{ + uint32_t hart = UNDEFINED_HART_ID; + uint32_t hart_idx = 0; + uint32_t harts_in_context = LIBERO_SETTING_CONTEXT_B_HART_EN; + uint64_t mhartid = riscv_mhartid(); + + /* If we are sending to a Context, assume we are a Context. + * i.e. HSS bootloader will not send directly to a context. + */ + + if (channel <= IHC_CHANNEL_TO_HART4) + { + hart = (uint32_t)mhartid; + } + else + { + if (channel == ihc_channel_to_contexta) + { + /* We are context B */ + + harts_in_context = LIBERO_SETTING_CONTEXT_B_HART_EN; + } + else + { + /* We are context A */ + + harts_in_context = LIBERO_SETTING_CONTEXT_A_HART_EN; + } + + hart_idx = 0; + while (hart_idx < MPFS_NUM_HARTS) + { + if (harts_in_context & (1 << hart_idx)) + { + hart = hart_idx; + break; + } + + hart_idx++; + } + } + + DEBUGASSERT(hart < MPFS_NUM_HARTS); + + return hart; +} + +/**************************************************************************** + * Name: mpfs_ihc_tx_message + * + * Description: + * Sends and notifies the remote hart of an incoming message. + * + * Input Parameters: + * channel - Enum that describes the channel used. + * message - Pointer to the message to be sent + * + * Returned Value: + * OK on success, busy in case of error + * + ****************************************************************************/ + +static int mpfs_ihc_tx_message(ihc_channel_t channel, uint32_t *message) +{ + uint32_t i; + uint32_t mhartid = mpfs_ihc_context_to_local_hart_id(channel); + uint32_t rhartid = mpfs_ihc_context_to_remote_hart_id(channel); + uint32_t message_size = getreg32(MPFS_IHC_MSG_SIZE(mhartid, rhartid)); + uint32_t ctrl_reg = getreg32(MPFS_IHC_CTRL(mhartid, rhartid)); + + DEBUGASSERT(message_size <= IHC_MAX_MESSAGE_SIZE); + + /* Check if the system is busy. All we can try is wait. */ + + if ((RMP_MESSAGE_PRESENT | ACK_INT) & ctrl_reg) + { + nxsig_usleep(100); + + /* Give it a one more try */ + + ctrl_reg = getreg32(MPFS_IHC_CTRL(mhartid, rhartid)); + } + + /* Return if RMP bit 1 indicating busy */ + + if (RMP_MESSAGE_PRESENT == (ctrl_reg & RMP_MASK)) + { + return -EBUSY; + } + else if (ACK_INT == (ctrl_reg & ACK_INT_MASK)) + { + return -EBUSY; + } + else + { + /* Fill the buffer */ + + for (i = 0; i < message_size; i++) + { + putreg32(message[i], MPFS_IHC_MSG_OUT(mhartid, rhartid) + i * 4); + } + + /* Set the MP bit. This will notify other of incoming hart message */ + + modifyreg32(MPFS_IHC_CTRL(mhartid, rhartid), 0, RMP_MESSAGE_PRESENT); + + /* Wait for the ACK to arrive to maintain the logic */ + + nxsem_wait_uninterruptible(&g_mpfs_ack_lock); + } + + return OK; +} + +/**************************************************************************** + * Name: mpfs_rptun_get_cpuname + * + * Description: + * Gets the mpfs rptun cpuname. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * Pointer to the cpu name string. + * + ****************************************************************************/ + +static const char *mpfs_rptun_get_cpuname(struct rptun_dev_s *dev) +{ + struct mpfs_rptun_dev_s *priv = container_of(dev, + struct mpfs_rptun_dev_s, + rptun); + return priv->cpuname; +} + +/**************************************************************************** + * Name: mpfs_rptun_get_firmware + * + * Description: + * Gets the mpfs rptun firmware. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * Always null, no associated firmware present + * + ****************************************************************************/ + +static const char *mpfs_rptun_get_firmware(struct rptun_dev_s *dev) +{ + return NULL; +} + +/**************************************************************************** + * Name: mpfs_rptun_get_addrenv + * + * Description: + * Gets the mpfs rptun addrenv. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * Always null, no associated addrenv present. + * + ****************************************************************************/ + +static const struct rptun_addrenv_s * +mpfs_rptun_get_addrenv(struct rptun_dev_s *dev) +{ + return NULL; +} + +/**************************************************************************** + * Name: mpfs_rptun_get_resource + * + * Description: + * Gets the mpfs rptun resource. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * The resource + * + ****************************************************************************/ + +static struct rptun_rsc_s * +mpfs_rptun_get_resource(struct rptun_dev_s *dev) +{ + struct mpfs_rptun_dev_s *priv = container_of(dev, + struct mpfs_rptun_dev_s, + rptun); + struct rptun_rsc_s *rsc; + + /* Only slave supported so far */ + + DEBUGASSERT(!priv->master); + + if (priv->shmem != NULL) + { + return &priv->shmem->rsc; + } + else + { + /* Perform initial setup */ + + priv->shmem = &g_shmem; + rsc = &priv->shmem->rsc; + + g_shmem.base = VRING_SHMEM; + g_shmem.seqm = 0; + g_shmem.seqs = 0; + + rsc->rsc_tbl_hdr.ver = 1; + rsc->rsc_tbl_hdr.num = 1; + rsc->offset[0] = offsetof(struct rptun_rsc_s, + rpmsg_vdev); + rsc->rpmsg_vdev.type = RSC_VDEV; + rsc->rpmsg_vdev.id = VIRTIO_ID_RPMSG; + rsc->rpmsg_vdev.dfeatures = 1 << VIRTIO_RPMSG_F_NS | + 1 << VIRTIO_RPMSG_F_ACK | + VIRTIO_RING_F_EVENT_IDX; + + rsc->rpmsg_vdev.gfeatures = 1 << VIRTIO_RPMSG_F_NS | + 1 << VIRTIO_RPMSG_F_ACK | + VIRTIO_RING_F_EVENT_IDX; + + /* Set to VIRTIO_CONFIG_STATUS_DRIVER_OK when master is up */ + + rsc->rpmsg_vdev.status = 0; + + rsc->rpmsg_vdev.config_len = sizeof(struct fw_rsc_config); + rsc->rpmsg_vdev.num_of_vrings = VRINGS; + rsc->rpmsg_vring0.align = VRING_ALIGN; + rsc->rpmsg_vring0.num = VRING_NR; + rsc->rpmsg_vring0.da = VRING0_DESCRIPTORS; + rsc->rpmsg_vring0.notifyid = 0; + rsc->rpmsg_vring1.align = VRING_ALIGN; + rsc->rpmsg_vring1.num = VRING_NR; + rsc->rpmsg_vring1.da = VRING1_DESCRIPTORS; + rsc->rpmsg_vring0.notifyid = 1; + rsc->config.rxbuf_size = VRING_SIZE; + rsc->config.txbuf_size = VRING_SIZE; + } + + return &priv->shmem->rsc; +} + +/**************************************************************************** + * Name: mpfs_rptun_is_autostart + * + * Description: + * Checks whether the rptun needs to autostart without explicit user + * start command. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * Always true + * + ****************************************************************************/ + +static bool mpfs_rptun_is_autostart(struct rptun_dev_s *dev) +{ + return true; +} + +/**************************************************************************** + * Name: mpfs_rptun_is_master + * + * Description: + * Checks whether we are master. Currently only slave mode is supported. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * True, if master, false if slave + * + ****************************************************************************/ + +static bool mpfs_rptun_is_master(struct rptun_dev_s *dev) +{ + struct mpfs_rptun_dev_s *priv = container_of(dev, + struct mpfs_rptun_dev_s, + rptun); + return priv->master; +} + +/**************************************************************************** + * Name: mpfs_rptun_start + * + * Description: + * Rptun start notifier. This is unused at the moment. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * Always zero. + * + ****************************************************************************/ + +static int mpfs_rptun_start(struct rptun_dev_s *dev) +{ + return 0; +} + +/**************************************************************************** + * Name: mpfs_rptun_stop + * + * Description: + * Rptun stop notifier. This is currently unused. + * + * Input Parameters: + * dev - Rptun device. + * + * Returned Value: + * Always zero. + * + ****************************************************************************/ + +static int mpfs_rptun_stop(struct rptun_dev_s *dev) +{ + return 0; +} + +/**************************************************************************** + * Name: mpfs_rptun_notify + * + * Description: + * This rptun notifier is used to indicate the remote hart that it has a + * message present. + * + * Input Parameters: + * dev - Rptun device. + * notifyid - rpmsg_vringX.notifyid, as declared in the resource. + * + * Returned Value: + * OK on success, busy on error + * + ****************************************************************************/ + +static int mpfs_rptun_notify(struct rptun_dev_s *dev, uint32_t notifyid) +{ + uint32_t tx_msg[IHC_MAX_MESSAGE_SIZE]; + + /* We're looking for the id, but currently it's just RPTUN_NOTIFY_ALL. It's + * OK, the remote end doesn't really care about the id, but that might + * change in the future. + */ + + if (notifyid == RPTUN_NOTIFY_ALL) + { + tx_msg[0] = 0; + tx_msg[1] = 0; + } + else + { + tx_msg[0] = (notifyid << 16); + tx_msg[1] = 0; + } + + return mpfs_ihc_tx_message(ihc_channel_to_contexta, tx_msg); +} + +/**************************************************************************** + * Name: mpfs_rptun_register_callback + * + * Description: + * This registers a callback for the rptun. + * + * Input Parameters: + * dev - Rptun device. + * callback - The callback rptun calls on receiving data + * + * Returned Value: + * Zero always + * + ****************************************************************************/ + +static int mpfs_rptun_register_callback(struct rptun_dev_s *dev, + rptun_callback_t callback, + void *arg) +{ + struct mpfs_rptun_dev_s *priv = container_of(dev, + struct mpfs_rptun_dev_s, + rptun); + + priv->callback = callback; + priv->arg = arg; + + return 0; +} + +/**************************************************************************** + * Name: mpfs_rptun_init + * + * Description: + * Initializes the rptun device. + * + * Input Parameters: + * shmemname - Shared mempory name + * cpuname - Local CPU name + * + * Returned Value: + * OK on success, negated errno on failure + * + ****************************************************************************/ + +static int mpfs_rptun_init(const char *shmemname, const char *cpuname) +{ + struct mpfs_rptun_dev_s *dev; + int ret; + + dev = kmm_zalloc(sizeof(*dev)); + if (dev == NULL) + { + return -ENOMEM; + } + +#ifdef IHC_CHANNEL_SIDE_B + dev->master = false; +#else + dev->master = true; +#endif + + dev->rptun.ops = &g_mpfs_rptun_ops; + strncpy(dev->cpuname, cpuname, RPMSG_NAME_SIZE); + strncpy(dev->shmemname, shmemname, RPMSG_NAME_SIZE); + list_add_tail(&g_dev_list, &dev->node); + + ret = rptun_initialize(&dev->rptun); + if (ret < 0) + { + kmm_free(dev); + } + + return ret; +} + +/**************************************************************************** + * Name: mpfs_echo_ping_ept_cb + * + * Description: + * Callback for the echo ping endpoint. This simply echoes all data it + * receives. + * + * Input Parameters: + * ept - Endpoint data + * data - Pointer to the data + * len - Length of the received data + * src - Source address + * priv - Handle t o private data + * + * Returned Value: + * OK always + * + ****************************************************************************/ + +static int mpfs_echo_ping_ept_cb(FAR struct rpmsg_endpoint *ept, + FAR void *data, size_t len, uint32_t src, + FAR void *priv) +{ + /* This is simply echoes the data back */ + + rpmsg_send(ept, data, len); + + return OK; +} + +/**************************************************************************** + * Name: mpfs_echo_ping_init + * + * Description: + * Initializes the rpmsg echo ping endpoint. + * + * Input Parameters: + * rdev - Rpmsg device + * ept - Endpoint data + + * Returned Value: + * OK on success, a negated errno otherwise + * + ****************************************************************************/ + +static int mpfs_echo_ping_init(FAR struct rpmsg_device *rdev, + FAR struct rpmsg_endpoint *ept) +{ + return rpmsg_create_ept(ept, rdev, MPFS_RPTUN_PING_EPT_NAME, + RPMSG_ADDR_ANY, 0, + mpfs_echo_ping_ept_cb, NULL); +} + +/**************************************************************************** + * Name: mpfs_rpmsg_device_created + * + * Description: + * Callback that is called when the underlying rpmsg device has been + * created. This is used to initialize the ping enpoint at the proper + * time. + * + * Input Parameters: + * rdev - Rpmsg device + * priv_ - Private data + + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_rpmsg_device_created(FAR struct rpmsg_device *rdev, + FAR void *priv_) +{ + struct rpmsg_virtio_device *vdev = container_of(rdev, + struct rpmsg_virtio_device, + rdev); + + g_mpfs_virtio_device = vdev; + g_mpfs_rpmsg_device = rdev; + + g_mpfs_virtqueue_table[0].data = g_mpfs_virtio_device->svq; + g_mpfs_virtqueue_table[1].data = g_mpfs_virtio_device->rvq; + + mpfs_echo_ping_init(rdev, &g_mpgs_echo_ping_ept); +} + +/**************************************************************************** + * Name: mpfs_rptun_thread + * + * Description: + * This is used to listen to the g_mpfs_rx_lock semaphore and then + * notifying the associated virtqueue. The virtqueue_notification() + * cannot be called from irq context. + * + * Input Parameters: + * argc - Argument count + * argv - Argument variables + + * Returned Value: + * 0 on exit + * + ****************************************************************************/ + +static int mpfs_rptun_thread(int argc, FAR char *argv[]) +{ + struct mpfs_queue_table *info; + + while (1) + { + nxsem_wait(&g_mpfs_rx_lock); + + info = &g_mpfs_virtqueue_table[g_vq_idx]; + virtqueue_notification((struct virtqueue *)info->data); + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_ihc_init + * + * Description: + * This initializes the Inter-Hart Communication (IHC) module. Rptun is + * used to simplify the integration of rpmsg and virtio. This function + * installs the proper interrupt handlers, installs a thread, and performs + * all the required initialization tasks. + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success, a nagated errno on error + * + ****************************************************************************/ + +int mpfs_ihc_init(void) +{ + uint64_t mhartid = riscv_mhartid(); + FAR char *argv[3]; + char arg1[19]; + uint32_t rhartid; + int ret; + + /* We're IHC channel side B (slave) */ + +#ifdef IHC_CHANNEL_SIDE_A + rhartid = CONTEXTB_HARTID; +#else + rhartid = CONTEXTA_HARTID; +#endif + + /* Initialize IHC FPGA module registers to a known state */ + + mpfs_ihc_local_context_init((uint32_t)mhartid); + mpfs_ihc_local_remote_config((uint32_t)mhartid, rhartid); + + /* Attach and enable the applicable irq */ + + ret = irq_attach(MPFS_IRQ_FABRIC_F2H_59, mpfs_ihc_interrupt, NULL); + if (ret == OK) + { + up_enable_irq(MPFS_IRQ_FABRIC_F2H_59); + } + else + { + ihcerr("ERROR: Not able to attach irq\n"); + return ret; + } + + /* Initialize and wait for the master. This will block until. */ + + ihcinfo("Waiting for the master online...\n"); + ret = mpfs_rptun_init(MPFS_RPTUN_SHMEM_NAME, MPFS_RPTUN_CPU_NAME); + ihcinfo("..master is online\n"); + + /* Register callback to notify when rpmsg device is ready */ + + ret = rpmsg_register_callback(NULL, mpfs_rpmsg_device_created, NULL, NULL); + if (ret < 0) + { + ihcerr("ERROR: Not able to register rpmsg callback\n"); + goto init_error; + } + + /* Thread initialization */ + + snprintf(arg1, sizeof(arg1), "0x%" PRIxPTR, + (uintptr_t)g_mpfs_virtqueue_table); + argv[0] = (void *)"mpfs_ihc_thread"; + argv[1] = arg1; + argv[2] = NULL; + + ret = kthread_create("mpfs-rptun", CONFIG_RPTUN_PRIORITY, + CONFIG_RPTUN_STACKSIZE, mpfs_rptun_thread, argv); + if (ret < 0) + { + ihcerr("ERROR: Not able to create a thread!\n"); + rpmsg_unregister_callback(NULL, mpfs_rpmsg_device_created, NULL, NULL); + goto init_error; + } + + return OK; + +init_error: + + up_disable_irq(MPFS_IRQ_FABRIC_F2H_59); + return ret; +} + +/**************************************************************************** + * Name: up_addrenv_va_to_pa + * + * Description: + * This is needed by openamp/libmetal/lib/system/nuttx/io.c:78. The + * physical memory is mapped as virtual. + * + * Input Parameters: + * va_ + * + * Returned Value: + * va + * + ****************************************************************************/ + +uintptr_t up_addrenv_va_to_pa(FAR void *va_) +{ + uintptr_t va = (uintptr_t)va_; + + return va; +} + +/**************************************************************************** + * Name: up_addrenv_pa_to_va + * + * Description: + * This is needed by openamp/libmetal/lib/system/nuttx/io.c. The + * physical memory is mapped as virtual. + * + * Input Parameters: + * pa + * + * Returned Value: + * pa + * + ****************************************************************************/ + +FAR void *up_addrenv_pa_to_va(uintptr_t pa) +{ + return (FAR void *)pa; +} diff --git a/arch/risc-v/src/mpfs/mpfs_ihc.h b/arch/risc-v/src/mpfs/mpfs_ihc.h new file mode 100755 index 0000000000..2cefad9001 --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_ihc.h @@ -0,0 +1,74 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_ihc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_MPFS_IHC_H +#define __ARCH_RISCV_SRC_MPFS_MPFS_IHC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <sys/types.h> +#include <stdbool.h> + +#include "chip.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: mpfs_ihc_init + * + * Description: + * This initializes the Inter-Hart Communication (IHC) module. Rptun is + * used to simplify the integration of rpmsg and virtio. This function + * installs the proper interrupt handlers, installs a thread, and performs + * all the required initialization tasks. + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success, a nagated errno on error + * + ****************************************************************************/ + +int mpfs_ihc_init(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_MPFS_MPFS_IHC_H */ diff --git a/boards/risc-v/mpfs/common/src/Make.defs b/boards/risc-v/mpfs/common/src/Make.defs index 6505f67246..886bd06efe 100755 --- a/boards/risc-v/mpfs/common/src/Make.defs +++ b/boards/risc-v/mpfs/common/src/Make.defs @@ -42,6 +42,10 @@ ifeq ($(CONFIG_USBDEV),y) CSRCS += mpfs_usb.c endif +ifeq ($(CONFIG_MPFS_IHC),y) +CSRCS += mpfs_ihc.c +endif + DEPPATH += --dep-path src VPATH += :src CFLAGS += $(shell $(INCDIR) "$(CC)" $(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src) diff --git a/boards/risc-v/mpfs/common/src/mpfs_ihc.c b/boards/risc-v/mpfs/common/src/mpfs_ihc.c new file mode 100644 index 0000000000..feda98f3f7 --- /dev/null +++ b/boards/risc-v/mpfs/common/src/mpfs_ihc.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * boards/risc-v/mpfs/common/src/mpfs_ihc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <debug.h> +#include <errno.h> + +#include "mpfs_ihc.h" +#include "board_config.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_board_ihc_init + * + * Description: + * Starts the Inter-Hart Communication (IHC) driver. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate any failure. + * + ****************************************************************************/ + +int mpfs_board_ihc_init(void) +{ + int ret; + + ret = mpfs_ihc_init(); + + if (ret != OK) + { + syslog(LOG_ERR, "ERROR: Failed to initialize the IHC driver: %d\n", + ret); + } + + return ret; +} diff --git a/boards/risc-v/mpfs/icicle/configs/ihc/defconfig b/boards/risc-v/mpfs/icicle/configs/ihc/defconfig new file mode 100644 index 0000000000..faa33f09bb --- /dev/null +++ b/boards/risc-v/mpfs/icicle/configs/ihc/defconfig @@ -0,0 +1,101 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +# CONFIG_DISABLE_OS_API is not set +# CONFIG_NSH_DISABLE_DATE is not set +# CONFIG_NSH_DISABLE_LOSMART is not set +# CONFIG_NSH_DISABLE_MB is not set +CONFIG_ARCH="risc-v" +CONFIG_ARCH_BOARD="icicle" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ICICLE_MPFS=y +CONFIG_ARCH_CHIP="mpfs" +CONFIG_ARCH_CHIP_MPFS250T_FCVG484=y +CONFIG_ARCH_CHIP_MPFS=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARDCTL_MKRD=y +CONFIG_BOARD_LOOPSPERMSEC=54000 +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_ERROR=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_WARN=y +CONFIG_DEV_ZERO=y +CONFIG_EXPERIMENTAL=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FS_FAT=y +CONFIG_FS_PROCFS=y +CONFIG_FS_ROMFS=y +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3072 +CONFIG_INTELHEX_BINARY=y +CONFIG_IOB_NBUFFERS=24 +CONFIG_IOB_NCHAINS=24 +CONFIG_LIBC_HOSTNAME="icicle" +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_MEMSET_64BIT=y +CONFIG_MEMSET_OPTSPEED=y +CONFIG_MM_CIRCBUF=y +CONFIG_MM_IOB=y +CONFIG_MPFS_IHC=y +CONFIG_MPFS_UART2=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_DISABLE_BASENAME=y +CONFIG_NSH_DISABLE_DF=y +CONFIG_NSH_DISABLE_DIRNAME=y +CONFIG_NSH_DISABLE_EXIT=y +CONFIG_NSH_DISABLE_EXPORT=y +CONFIG_NSH_DISABLE_FREE=y +CONFIG_NSH_DISABLE_GET=y +CONFIG_NSH_DISABLE_HEXDUMP=y +CONFIG_NSH_DISABLE_MKFATFS=y +CONFIG_NSH_DISABLE_MKRD=y +CONFIG_NSH_DISABLE_MOUNT=y +CONFIG_NSH_DISABLE_PUT=y +CONFIG_NSH_DISABLE_PWD=y +CONFIG_NSH_DISABLE_RM=y +CONFIG_NSH_DISABLE_RMDIR=y +CONFIG_NSH_DISABLE_SET=y +CONFIG_NSH_DISABLE_SLEEP=y +CONFIG_NSH_DISABLE_SOURCE=y +CONFIG_NSH_DISABLE_TEST=y +CONFIG_NSH_DISABLE_TIME=y +CONFIG_NSH_DISABLE_TRUNCATE=y +CONFIG_NSH_DISABLE_UMOUNT=y +CONFIG_NSH_DISABLE_UNAME=y +CONFIG_NSH_DISABLE_UNSET=y +CONFIG_NSH_DISABLE_USLEEP=y +CONFIG_NSH_DISABLE_WGET=y +CONFIG_NSH_DISABLE_XD=y +CONFIG_NSH_LINELEN=160 +CONFIG_OPENAMP=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=1048576 +CONFIG_RAM_START=0xa2200000 +CONFIG_RAW_BINARY=y +CONFIG_RPTUN=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_START_MONTH=4 +CONFIG_START_YEAR=2021 +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_TIME64=y +CONFIG_TASK_NAME_SIZE=20 +CONFIG_UART2_SERIAL_CONSOLE=y diff --git a/boards/risc-v/mpfs/icicle/scripts/Make.defs b/boards/risc-v/mpfs/icicle/scripts/Make.defs index 228f2f8032..72470207f9 100755 --- a/boards/risc-v/mpfs/icicle/scripts/Make.defs +++ b/boards/risc-v/mpfs/icicle/scripts/Make.defs @@ -37,7 +37,11 @@ else ifeq ($(CONFIG_BUILD_PROTECTED),y) else ifeq ($(CONFIG_BUILD_KERNEL),y) LDSCRIPT = ld-kernel.script else - LDSCRIPT = ld.script + ifeq ($(CONFIG_MPFS_IHC),y) + LDSCRIPT = ld-ihc.script + else + LDSCRIPT = ld.script + endif endif ifneq ($(LDMEMORY),) diff --git a/boards/risc-v/mpfs/icicle/scripts/ld-ihc.script b/boards/risc-v/mpfs/icicle/scripts/ld-ihc.script new file mode 100755 index 0000000000..52a0f6c55c --- /dev/null +++ b/boards/risc-v/mpfs/icicle/scripts/ld-ihc.script @@ -0,0 +1,124 @@ +/**************************************************************************** + * boards/risc-v/mpfs/icicle/scripts/ld.script + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +MEMORY +{ + progmem (rx) : ORIGIN = 0xa2000000, LENGTH = 1M /* w/ cache */ + filler (rx) : ORIGIN = 0xa2100000, LENGTH = 524k + sram (rwx) : ORIGIN = 0xa2200000, LENGTH = 1M /* w/ cache */ + shared (rwx) : ORIGIN = 0xa4000000, LENGTH = 256k +} + +OUTPUT_ARCH("riscv") + +__ksram_start = ORIGIN(sram); +__ksram_size = LENGTH(sram); +__ksram_end = ORIGIN(sram) + LENGTH(sram); + +ENTRY(_stext) +EXTERN(_vectors) +SECTIONS +{ + .filler_area : ALIGN(0x1000) + { + __filler_area_load = LOADADDR(.filler_area); + __filler_area_start = .; + __filler_area_vma_start = .; + *(.filler_area) + KEEP(*(.filler_area)) + . = ALIGN(0x1000); + __filler_area_end = .; + __filler_area_vma_end = .; + } > filler + + .amp_ihc : ALIGN(0x1000) + { + __amp_ihc_load = LOADADDR(.amp_ihc); + __amp_ihc_start = .; + __amp_ihc_vma_start = .; + *(.amp_ihc) + . = ALIGN(0x1000); + __amp_ihc_end = .; + __amp_ihc_vma_end = .; + } > shared + + .text : { + _stext = ABSOLUTE(.); + mpfs_head.o + *(.text .text.*) + *(.fixup) + *(.gnu.warning) + *(.rodata .rodata.* .srodata .srodata.*) + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + _etext = ABSOLUTE(.); + } > progmem + + .init_section : ALIGN(4) { + _sinit = ABSOLUTE(.); + KEEP(*(.init_array .init_array.*)) + _einit = ABSOLUTE(.); + } > progmem + + _eronly = ABSOLUTE(.); + + .data : ALIGN(4) { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.sdata .sdata.* .sdata2.*) + *(.gnu.linkonce.d.*) + *(.gnu.linkonce.s.*) + CONSTRUCTORS + . = ALIGN(4); + _edata = ABSOLUTE(.); + } > sram AT > progmem + + PROVIDE(__global_pointer$ = _sdata + ((_edata - _sdata) / 2)); + + .bss : ALIGN(4) { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.sbss .sbss.*) + *(.gnu.linkonce.b.*) + *(.gnu.linkonce.sb.*) + *(COMMON) + . = ALIGN(32); + _ebss = ABSOLUTE(.); + } > sram + + /* Stabs debugging sections. */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +} diff --git a/boards/risc-v/mpfs/icicle/src/board_config.h b/boards/risc-v/mpfs/icicle/src/board_config.h index b38fd34975..22fa26920c 100755 --- a/boards/risc-v/mpfs/icicle/src/board_config.h +++ b/boards/risc-v/mpfs/icicle/src/board_config.h @@ -47,6 +47,7 @@ int mpfs_bringup(void); int mpfs_board_spi_init(void); int mpfs_board_i2c_init(void); +int mpfs_board_ihc_init(void); int mpfs_board_emmcsd_init(void); int mpfs_board_usb_init(void); int mpfs_pwm_setup(void); diff --git a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c index 4a6a1e42aa..07a024a113 100755 --- a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c +++ b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c @@ -145,5 +145,14 @@ int mpfs_bringup(void) } #endif +#ifdef CONFIG_MPFS_IHC + ret = mpfs_board_ihc_init(); + + if (ret < 0) + { + syslog(LOG_ERR, "Failed to init IHC driver: %d\n", ret); + } +#endif + return ret; }