risc-v/mpfs: amend OpenSBI to utilize IHC

Linux kernel uses M-mode trap for handling Inter-Hart Communication (IHC).
This patch provides all the required functionalities for this purpose.
Previously, HSS bootloader was required. Now, NuttX is run as the
bootloader providing OpenSBI vendor extensions instead. This setup has
been tested on the following configuration:

 - Hart 0 has NuttX in bootloader mode with OpenSBI
 - Hart 1 unused
 - Hart 2 has NuttX configured at 0xa2000000
 - Hart 3 has U-boot / Linux kernel (at 0x80200000)
 - Hart 4 has U-boot / Linux kernel (at 0x80200000)

Upon startup, NuttX on hart 0 will initialize SD-card driver, loads
the hart 2 NuttX from the SD-card and loads the U-boot to 0x80200000.
Also the nuttx.sbi -binary is loaded from SD-card into address 0x80000000,
which is also marked as reserved area in the Linux kernel device tree (for
the chuck 0x80000000 - 0x80200000).

Hart 2 NuttX waits until Linux kernel (IHC master) is started. After the
initial handshake, RPMsg / virtIO bus along with the IHC may be used for
proper AMP mode.

Signed-off-by: Eero Nurkkala <eero.nurkkala@offcode.fi>
This commit is contained in:
Eero Nurkkala 2022-05-20 07:12:54 +03:00 committed by Xiang Xiao
parent 84bcb075d7
commit 3ea7d4bab4
5 changed files with 577 additions and 303 deletions

View File

@ -272,59 +272,23 @@
#define MPIE_MASK (1 << 2)
#define ACK_INT_MASK (1 << 3)
#define IHC_MAX_MESSAGE_SIZE 4
#define IHC_MAX_MESSAGE_SIZE 2
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;
#define SBI_EXT_IHC_CTX_INIT 0
#define SBI_EXT_IHC_SEND 1
#define SBI_EXT_IHC_RECEIVE 2
typedef union ihca_ip_msg_avail_stat_t_
enum ihc_channel_e
{
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;
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 */
};
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;
typedef enum ihc_channel_e ihc_channel_t;
#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_IHC_H */

View File

@ -46,7 +46,6 @@
#include <nuttx/list.h>
#include <arch/board/board.h>
#include "hardware/mpfs_sysreg.h"
#include "hardware/mpfs_ihc.h"
#include "riscv_internal.h"
@ -115,22 +114,30 @@ struct mpfs_rptun_dev_s
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
struct mpfs_queue_table_s
{
void *data;
};
enum mpfs_irq_type_e
{
MP_IRQ = 0x0,
ACK_IRQ = 0x1,
};
struct mpfs_ihc_msg_s
{
uint32_t msg[IHC_MAX_MESSAGE_SIZE];
};
/* Used to store information for the remote via ecall (eg. Linux) */
struct ihc_sbi_rx_msg_s
{
uint8_t irq_type;
struct mpfs_ihc_msg_s ihc_msg;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@ -158,13 +165,14 @@ static int mpfs_rptun_register_callback(struct rptun_dev_s *dev,
* 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.
* has been found out. This is not needed if NuttX is the bootloader, not
* HSS.
*/
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_queue_table_s 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;
@ -222,6 +230,9 @@ static const struct rptun_ops_s g_mpfs_rptun_ops =
* acking to a message or not.
*
* Input Parameters:
* mhartid - My hart id. If remote owns multiple harts, this is the
* mhartid base on the context, not necessarily the actual
* mhartid.
* is_ack - Boolean that is set true if an ack has been found
*
* Returned Value:
@ -229,9 +240,9 @@ static const struct rptun_ops_s g_mpfs_rptun_ops =
*
****************************************************************************/
static uint32_t mpfs_ihc_parse_incoming_hartid(bool *is_ack)
static uint32_t mpfs_ihc_parse_incoming_hartid(uint32_t mhartid,
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));
@ -331,228 +342,6 @@ static uint32_t mpfs_ihc_context_to_remote_hart_id(ihc_channel_t channel)
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_sig, or lets the mpfs_rptun_thread() run as it
* waits for the g_mpfs_rx_sig. 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)
{
/* Received the ack */
nxsem_post(&g_mpfs_ack_sig);
}
else
{
g_vq_idx = (message[0] >> 16);
DEBUGASSERT(g_vq_idx < VRINGS);
nxsem_post(&g_mpfs_rx_sig);
}
}
/****************************************************************************
* 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)
{
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)
{
/* 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);
while (rhartid < MPFS_NUM_HARTS)
{
putreg32(0, MPFS_IHC_CTRL(hart_to_configure, rhartid));
rhartid++;
}
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
*
@ -615,6 +404,348 @@ static uint32_t mpfs_ihc_context_to_local_hart_id(ihc_channel_t channel)
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_sig, or lets the mpfs_rptun_thread() run as it
* waits for the g_mpfs_rx_sig. 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)
{
/* Received the ack */
nxsem_post(&g_mpfs_ack_sig);
}
else
{
g_vq_idx = (message[0] >> 16);
DEBUGASSERT(g_vq_idx < VRINGS);
nxsem_post(&g_mpfs_rx_sig);
}
}
/****************************************************************************
* Name: mpfs_ihc_message_present_handler
*
* Description:
* This function fills up a structure that gets into the remote end, such
* as Linux kernel. The structure may contain data from the
* MPFS_IHC_MSG_IN -register, or in case of an ack, just the irq_type.
*
* Input Parameters:
* message - Pointer for storing data, must not be NULL
* mhartid - The primary hart id of a set of hartids. Not necessarily
* the absolute mhartid if multiple harts are incorporated
* within the remote (eg. Linux kernel used on 2 harts).
* rhartid - Remote hart id
* is_ack - Boolean indicating an ack message
*
* Returned Value:
* None
*
****************************************************************************/
static void mpfs_ihc_message_present_handler(uint32_t *message,
uint32_t mhartid,
uint32_t rhartid,
bool is_ack)
{
struct ihc_sbi_rx_msg_s *msg;
uintptr_t message_ihc = (uintptr_t)MPFS_IHC_MSG_IN(mhartid, rhartid);
uint32_t message_size_ihc = getreg32(MPFS_IHC_MSG_SIZE(mhartid, rhartid));
msg = (struct ihc_sbi_rx_msg_s *)message;
if (is_ack)
{
msg->irq_type = ACK_IRQ;
/* msg->ihc_msg content doesn't matter here */
}
else
{
msg->irq_type = MP_IRQ;
msg->ihc_msg = *(struct mpfs_ihc_msg_s *)message_ihc;
}
DEBUGASSERT(sizeof(msg->ihc_msg) >= message_size_ihc);
}
/****************************************************************************
* 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.
* mhartid - Context hart id, not necessarily the absolute mhartid but
* rather, the primary hartid of the set of harts.
* is_ack - Boolean indicating an ack message
* msg - For storing data, could be NULL
*
* Returned Value:
* None
*
****************************************************************************/
static void mpfs_ihc_rx_message(ihc_channel_t channel, uint32_t mhartid,
bool is_ack, uint32_t *msg)
{
uint32_t rhartid = mpfs_ihc_context_to_remote_hart_id(channel);
uint32_t ctrl_reg = getreg32(MPFS_IHC_CTRL(mhartid, rhartid));
if (is_ack)
{
if (mhartid == CONTEXTB_HARTID)
{
uintptr_t msg_in = MPFS_IHC_MSG_IN(mhartid, rhartid);
DEBUGASSERT(msg == NULL);
mpfs_ihc_rx_handler((uint32_t *)msg_in, is_ack);
}
else
{
/* This path is meant for the OpenSBI vendor extension only */
DEBUGASSERT(msg != NULL);
mpfs_ihc_message_present_handler(msg, mhartid, rhartid, is_ack);
}
}
else if (MP_MESSAGE_PRESENT == (ctrl_reg & MP_MASK))
{
/* Check if we have a message */
if (mhartid == CONTEXTB_HARTID)
{
uintptr_t msg_in = MPFS_IHC_MSG_IN(mhartid, rhartid);
DEBUGASSERT(msg == NULL);
mpfs_ihc_rx_handler((uint32_t *)msg_in, is_ack);
}
else
{
/* This path is meant for the OpenSBI vendor extension only */
DEBUGASSERT(msg != NULL);
mpfs_ihc_message_present_handler(msg, 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(mhartid, &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, mhartid, is_ack, NULL);
if (is_ack)
{
/* 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_sbi_message_present_indirect_isr
*
* Description:
* This is used by OpenSBI. This is handled as an OpenSBI extension.
* The S-mode kernel uses this in its extended OpenSBI vendor call.
*
* Input Parameters:
* channel - Enum that describes the channel used.
* msg - The msg pointer from sbi_trap_regs->a1 register for data
* exchange.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_MPFS_OPENSBI
void mpfs_ihc_sbi_message_present_indirect_isr(ihc_channel_t channel,
uint32_t *msg)
{
bool is_ack;
uint32_t mhartid = mpfs_ihc_context_to_local_hart_id(channel);
uint32_t origin_hart = mpfs_ihc_parse_incoming_hartid(mhartid, &is_ack);
if (origin_hart != UNDEFINED_HART_ID)
{
/* Process incoming packet */
mpfs_ihc_rx_message(origin_hart, mhartid, is_ack, msg);
if (is_ack)
{
/* Clear the ack */
modifyreg32(MPFS_IHC_CTRL(mhartid, origin_hart), ACK_CLR, 0);
}
}
}
#endif
/****************************************************************************
* 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);
while (rhartid < MPFS_NUM_HARTS)
{
putreg32(0, MPFS_IHC_CTRL(hart_to_configure, rhartid));
rhartid++;
}
#ifdef CONFIG_MPFS_OPENSBI
/* With OpenSBI we act the opposite direction */
g_connected_harts = (HSS_HART_MASK | (1 << CONTEXTB_HARTID));
g_connected_hart_ints = IHCIA_CONTEXTA_INTS;
#else
g_connected_harts = ihcia_remote_harts[hart_to_configure];
g_connected_hart_ints = ihcia_remote_hart_ints[hart_to_configure];
#endif
}
/****************************************************************************
* 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_tx_message
*
@ -676,7 +807,12 @@ static int mpfs_ihc_tx_message(ihc_channel_t channel, uint32_t *message)
/* Wait for the ACK to arrive to maintain the logic */
nxsem_wait_uninterruptible(&g_mpfs_ack_sig);
if (mhartid == CONTEXTB_HARTID)
{
/* Only applicable for the CONTEXTB_HART */
nxsem_wait_uninterruptible(&g_mpfs_ack_sig);
}
}
return OK;
@ -1117,7 +1253,7 @@ static void mpfs_rpmsg_device_created(struct rpmsg_device *rdev, void *priv_)
static int mpfs_rptun_thread(int argc, char *argv[])
{
struct mpfs_queue_table *info;
struct mpfs_queue_table_s *info;
while (1)
{
@ -1134,6 +1270,66 @@ static int mpfs_rptun_thread(int argc, char *argv[])
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mpfs_ihc_sbi_ecall_handler
*
* Description:
* This is sbi_platform_operations / vendor_ext_provider ecall handler.
* Related Linux ecalls end up here.
*
* Input Parameters:
* funcid - SBI_EXT_IHC_CTX_INIT, SBI_EXT_IHC_SEND or
* SBI_EXT_IHC_RECEIVE. Others are invalid.
* remote_channel - The remote we're communicating with
* message_ptr - Local storage for data exchange
*
* Returned Value:
* OK on success, a negated error code otherwise
*
****************************************************************************/
#ifdef CONFIG_MPFS_OPENSBI
int mpfs_ihc_sbi_ecall_handler(unsigned long funcid, uint32_t remote_channel,
uint32_t *message_ptr)
{
uint32_t mhartid;
uint32_t rhartid;
int result = OK;
/* Check the channel is bound to a valid context */
if ((remote_channel < IHC_CHANNEL_TO_CONTEXTA) ||
(remote_channel > IHC_CHANNEL_TO_CONTEXTB))
{
return -EINVAL;
}
switch (funcid)
{
case SBI_EXT_IHC_CTX_INIT:
mhartid = mpfs_ihc_context_to_local_hart_id(remote_channel);
rhartid = mpfs_ihc_context_to_remote_hart_id(remote_channel);
mpfs_ihc_local_context_init(mhartid);
mpfs_ihc_local_remote_config(mhartid, rhartid);
break;
case SBI_EXT_IHC_SEND:
result = mpfs_ihc_tx_message(remote_channel, message_ptr);
break;
case SBI_EXT_IHC_RECEIVE:
mpfs_ihc_sbi_message_present_indirect_isr(remote_channel,
message_ptr);
break;
default:
result = -ENOTSUP;
}
return result;
}
#endif
/****************************************************************************
* Name: mpfs_ihc_init
*

View File

@ -27,9 +27,6 @@
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdbool.h>
#include "chip.h"
/****************************************************************************
* Public Function Prototypes
@ -65,6 +62,27 @@ extern "C"
int mpfs_ihc_init(void);
/****************************************************************************
* Name: mpfs_ihc_sbi_ecall_handler
*
* Description:
* This is sbi_platform_operations / vendor_ext_provider ecall handler.
* Related Linux ecalls end up here.
*
* Input Parameters:
* funcid - SBI_EXT_IHC_CTX_INIT, SBI_EXT_IHC_SEND or
* SBI_EXT_IHC_RECEIVE. Others are invalid.
* remote_channel - The remote we're communicating with
* message_ptr - Local storage for data exchange
*
* Returned Value:
* OK on success, a negated error code otherwise
*
****************************************************************************/
int mpfs_ihc_sbi_ecall_handler(unsigned long funcid, uint32_t remote_channel,
uint32_t *message_ptr);
#undef EXTERN
#if defined(__cplusplus)
}

View File

@ -26,6 +26,9 @@
#include <hardware/mpfs_memorymap.h>
#include <hardware/mpfs_clint.h>
#include <hardware/mpfs_sysreg.h>
#ifdef CONFIG_MPFS_IHC
#include <hardware/mpfs_ihc.h>
#endif
#include <sbi/riscv_io.h>
#include <sbi/riscv_encoding.h>
@ -33,10 +36,15 @@
#include <sbi/sbi_platform.h>
#include <sbi/sbi_init.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_trap.h>
#include <sbi_utils/irqchip/plic.h>
#include <sbi_utils/ipi/aclint_mswi.h>
#include <sbi_utils/timer/aclint_mtimer.h>
#ifdef CONFIG_MPFS_IHC
#include <mpfs_ihc.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -64,6 +72,10 @@
#define MPFS_SYSREG_SUBBLK_CLOCK_CR (MPFS_SYSREG_BASE + \
MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET)
#define MICROCHIP_TECHNOLOGY_MVENDOR_ID 0x029
#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \
MICROCHIP_TECHNOLOGY_MVENDOR_ID)
/****************************************************************************
* Private Types
****************************************************************************/
@ -94,6 +106,13 @@ static int mpfs_opensbi_console_init(void);
static int mpfs_irqchip_init(bool cold_boot);
static int mpfs_ipi_init(bool cold_boot);
static int mpfs_timer_init(bool cold_boot);
#ifdef CONFIG_MPFS_IHC
static int mpfs_opensbi_vendor_ext_check(long extid);
static int mpfs_opensbi_ecall_handler(long extid, long funcid,
const struct sbi_trap_regs *regs,
unsigned long *out_val,
struct sbi_trap_info *out_trap);
#endif
/****************************************************************************
* Extern Function Declarations
@ -138,14 +157,18 @@ static struct aclint_mtimer_data mpfs_mtimer =
static const struct sbi_platform_operations platform_ops =
{
.console_init = mpfs_opensbi_console_init,
.early_init = mpfs_early_init,
.irqchip_init = mpfs_irqchip_init,
.irqchip_exit = NULL,
.ipi_init = mpfs_ipi_init,
.ipi_exit = NULL,
.timer_init = mpfs_timer_init,
.timer_exit = NULL,
.console_init = mpfs_opensbi_console_init,
.early_init = mpfs_early_init,
.irqchip_init = mpfs_irqchip_init,
.irqchip_exit = NULL,
.ipi_init = mpfs_ipi_init,
.ipi_exit = NULL,
.timer_init = mpfs_timer_init,
.timer_exit = NULL,
#ifdef CONFIG_MPFS_IHC
.vendor_ext_check = mpfs_opensbi_vendor_ext_check,
.vendor_ext_provider = mpfs_opensbi_ecall_handler,
#endif
};
static struct aclint_mswi_data mpfs_mswi =
@ -499,6 +522,73 @@ static void mpfs_opensbi_pmp_setup(void)
csr_write(pmpcfg2, 0);
}
/****************************************************************************
* Name: mpfs_opensbi_vendor_ext_check
*
* Description:
* Used by the OpenSBI in vendor probe to check the vendor ID.
*
* Input Parameters:
* extid - Vendor ID to be checked
*
* Returned Value:
* 1 on match, zero in case of no match
*
****************************************************************************/
#ifdef CONFIG_MPFS_IHC
static int mpfs_opensbi_vendor_ext_check(long extid)
{
return (SBI_EXT_MICROCHIP_TECHNOLOGY == extid);
}
/****************************************************************************
* Name: mpfs_opensbi_ecall_handler
*
* Description:
* Used by the S-mode kernel such as Linux to perform M-mode ecall actions
* related to Inter-Hart Communication (IHC).
*
* Input Parameters:
* extid - Vendor ID
* funcid - One of the valid functions
* sbi_trap_regs - SBI trap registers
* out_val - Error code location
* out_trap - Trap registers such as epc, unused
*
* Returned Value:
* 0 always
*
****************************************************************************/
static int mpfs_opensbi_ecall_handler(long extid, long funcid,
const struct sbi_trap_regs *regs,
unsigned long *out_val,
struct sbi_trap_info *out_trap)
{
uint32_t remote_channel = (uint32_t)regs->a0;
uint32_t *message_ptr = (uint32_t *)regs->a1;
int result = 0;
switch (funcid)
{
case SBI_EXT_IHC_CTX_INIT:
case SBI_EXT_IHC_SEND:
case SBI_EXT_IHC_RECEIVE:
result = mpfs_ihc_sbi_ecall_handler(funcid, remote_channel,
message_ptr);
break;
default:
result = SBI_ENOTSUPP;
}
*out_val = result;
return 0;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/

View File

@ -48,7 +48,11 @@
int mpfs_board_ihc_init(void)
{
int ret;
int ret = 0;
/* With OpenSBI, initilization comes via mpfs_opensbi.c, not here */
#ifndef CONFIG_MPFS_OPENSBI
ret = mpfs_ihc_init();
@ -58,5 +62,7 @@ int mpfs_board_ihc_init(void)
ret);
}
#endif
return ret;
}