531 lines
16 KiB
C
Raw Normal View History

/****************************************************************************
* arch/risc-v/src/mpfs/mpfs_opensbi.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 <errno.h>
#include <stdint.h>
#include <riscv_arch.h>
#include <hardware/mpfs_plic.h>
#include <hardware/mpfs_memorymap.h>
#include <hardware/mpfs_clint.h>
#include <hardware/mpfs_sysreg.h>
/* OpenSBI will also define NULL. Undefine NULL in order to avoid warning:
* 'warning: "NULL" redefined'
*/
#ifdef NULL
#undef NULL
#endif
#include <sbi/sbi_types.h>
#include <sbi/riscv_atomic.h>
#include <sbi/riscv_asm.h>
#include <sbi/riscv_io.h>
#include <sbi/riscv_encoding.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_domain.h>
#include <sbi/sbi_timer.h>
#include <sbi/sbi_init.h>
#include <sbi/sbi_scratch.h>
#include <sbi_utils/irqchip/plic.h>
#include <sbi_utils/ipi/aclint_mswi.h>
#include <sbi_utils/timer/aclint_mtimer.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MPFS_SYS_CLK 1000000000
#define MPFS_MAX_NUM_HARTS 5
#define MPFS_HART_COUNT 5
#define MPFS_ACLINT_MSWI_ADDR MPFS_CLINT_MSIP0
#define MPFS_ACLINT_MTIMER_ADDR MPFS_CLINT_MTIMECMP0
#define MPFS_PMP_DEFAULT_ADDR 0xfffffffff
#define MPFS_PMP_DEFAULT_PERM 0x000000009f
#define UBOOT_LOAD_ADDR 0x80200000 /* We expect u-boot here */
/* The following define is not accessible with assember. Make sure it's in
* sync with the assembler usage in mpfs_opensbi_utils.S.
*/
#if SBI_PLATFORM_DEFAULT_HART_STACK_SIZE != 8192
# error "Fix define in file mpfs_opensbi_utils.S"
#endif
#define MPFS_SYSREG_SOFT_RESET_CR (MPFS_SYSREG_BASE + \
MPFS_SYSREG_SOFT_RESET_CR_OFFSET)
#define MPFS_SYSREG_SUBBLK_CLOCK_CR (MPFS_SYSREG_BASE + \
MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET)
/****************************************************************************
* Private Types
****************************************************************************/
struct sbi_scratch_holder_s
{
union
{
struct sbi_scratch scratch;
unsigned long buffer[SBI_SCRATCH_SIZE / sizeof(uintptr_t)];
};
};
typedef struct sbi_scratch_holder_s sbi_scratch_holder_t;
/* Linker provided region start / end addresses */
extern const uint64_t __mpfs_nuttx_start;
extern const uint64_t __mpfs_nuttx_end;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void mpfs_console_putc(char ch);
static int mpfs_early_init(bool cold_boot);
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);
/****************************************************************************
* Extern Function Declarations
****************************************************************************/
/* riscv_internal.h cannot be included due to a number of redefinition
* conflicts. Thus, define the riscv_lowputc() with the extern definition.
*/
extern void riscv_lowputc(char ch);
/****************************************************************************
* Private Data
****************************************************************************/
static bool mpfs_console_ready = false;
static struct plic_data mpfs_plic =
{
.addr = MPFS_PLIC_BASE,
.num_src = MPFS_HART_COUNT,
};
static struct sbi_console_device mpfs_console =
{
.name = "mpfs_uart",
.console_putc = mpfs_console_putc,
.console_getc = NULL,
};
static struct aclint_mtimer_data mpfs_mtimer =
{
.mtime_freq = MPFS_SYS_CLK,
.mtime_addr = MPFS_ACLINT_MTIMER_ADDR + ACLINT_DEFAULT_MTIME_OFFSET,
.mtime_size = ACLINT_DEFAULT_MTIME_SIZE,
.mtimecmp_addr = MPFS_ACLINT_MTIMER_ADDR + ACLINT_DEFAULT_MTIMECMP_OFFSET,
.mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE,
.first_hartid = 0,
.hart_count = MPFS_HART_COUNT,
.has_64bit_mmio = TRUE,
};
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,
};
static struct aclint_mswi_data mpfs_mswi =
{
.addr = MPFS_ACLINT_MSWI_ADDR,
.size = ACLINT_MSWI_SIZE,
.first_hartid = 0,
.hart_count = MPFS_HART_COUNT,
};
const struct sbi_platform platform =
{
.opensbi_version = OPENSBI_VERSION,
.platform_version = SBI_PLATFORM_VERSION(0x0, 0x01),
.name = "Microchip PolarFire(R) SoC",
.features = SBI_PLATFORM_DEFAULT_FEATURES,
.hart_count = MPFS_HART_COUNT,
.hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE,
.platform_ops_addr = (unsigned long)&platform_ops,
.firmware_context = 0
};
/* This must go into l2_scratchpad region, starting at 0x0a000000. */
static sbi_scratch_holder_t g_scratches[MPFS_MAX_NUM_HARTS] \
__attribute__((section(".l2_scratchpad")));
/* These stacks are used in the mpfs_opensbi_utils.S */
uint8_t g_hart_stacks[SBI_PLATFORM_DEFAULT_HART_STACK_SIZE * \
MPFS_HART_COUNT] \
__attribute__((section(".ddrstorage")));
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mpfs_hart_to_scratch
*
* Description:
* Returns the scratch area start for the given hart.
*
* Input Parameters:
* hartid (0,1..5)
*
* Returned Value:
* The scratch area in l2_scratchpad.
*
****************************************************************************/
static unsigned long mpfs_hart_to_scratch(int hartid)
{
DEBUGASSERT(hartid < MPFS_MAX_NUM_HARTS);
return (unsigned long)(&g_scratches[hartid].scratch);
}
/****************************************************************************
* Name: mpfs_irqchip_init
*
* Description:
* Sets the interrupt priorities via the plic_cold_irqchip_init() call.
* Also this provides the proper PLIC base address for further irq
* property handling such as threshold levels.
*
* Input Parameters:
* cold_boot - True, if this is the hart doing the real boot
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int mpfs_irqchip_init(bool cold_boot)
{
int rc;
uint32_t hartid = current_hartid();
if (cold_boot)
{
rc = plic_cold_irqchip_init(&mpfs_plic);
if (rc)
{
return rc;
}
}
return plic_warm_irqchip_init(&mpfs_plic, (hartid) ? (2 * hartid - 1) : 0,
(hartid) ? (2 * hartid) : -1);
}
/****************************************************************************
* Name: mpfs_console_putc
*
* Description:
* Sets the interrupt priorities via the plic_cold_irqchip_init() call.
* Also this provides the proper PLIC base address for further irq
* property handling such as threshold levels.
*
* Input Parameters:
* ch - Character to be printed out
*
* Returned Value:
* None
*
****************************************************************************/
static void mpfs_console_putc(char ch)
{
#ifdef CONFIG_DEBUG_FEATURES
if (mpfs_console_ready)
{
riscv_lowputc(ch);
}
#endif
}
/****************************************************************************
* Name: mpfs_opensbi_console_init
*
* Description:
* Initializes the console for OpenSBI usage. OpenSBI expects this
* function to be present.
*
* Input Parameters:
* None
*
* Returned Value:
* Always zero indicating a success.
*
****************************************************************************/
static int mpfs_opensbi_console_init(void)
{
mpfs_console_ready = true;
return 0;
}
/****************************************************************************
* Name: mpfs_ipi_init
*
* Description:
* Initializes the IPI for OpenSBI usage. Also adds the regions into
* OpenSBI domains.
*
* Input Parameters:
* cold_boot - Indicates the primary boot hart
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int mpfs_ipi_init(bool cold_boot)
{
int rc;
if (cold_boot)
{
rc = aclint_mswi_cold_init(&mpfs_mswi);
if (rc)
{
return rc;
}
}
return aclint_mswi_warm_init();
}
/****************************************************************************
* Name: mpfs_timer_init
*
* Description:
* Initializes the clint timer interface. Commands such as "csrr a0, time"
* (reading the CSR time register) will cause an illegal instruction
* exception, because the hardware has no support for it. That command is
* emulated via the CLINT timer in the OpenSBI trap handler.
*
* Input Parameters:
* cold_boot - If set, indicates the primary boot hart
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int mpfs_timer_init(bool cold_boot)
{
int rc;
if (cold_boot)
{
rc = aclint_mtimer_cold_init(&mpfs_mtimer, NULL);
if (rc)
{
return rc;
}
}
return aclint_mtimer_warm_init();
}
/****************************************************************************
* Name: mpfs_early_init
*
* Description:
* Initializes the clint timer interface. Commands such as "csrr a0, time"
* (reading the CSR time register) will cause an illegal instruction
* exception, because the hardware has no support for it. That command is
* emulated via the CLINT timer in the OpenSBI trap handler.
*
* Input Parameters:
* cold_boot - If set, indicates the primary boot hart
*
* Returned Value:
* Zero (OK) is returned on success.
*
****************************************************************************/
static int mpfs_early_init(bool cold_boot)
{
/* We expect that e51 has terminated the following irqs with
* up_disable_irq():
* 1. MPFS_IRQ_MMC_MAIN
* 2. MPFS_IRQ_MTIMER
*
* U-boot will reuse eMMC and loads the kernel from there. OpenSBI will
* use CLINT timer. Upstream u-boot doesn't turn the clocks on itsef.
*/
if (!cold_boot)
{
return 0;
}
/* Explicitly reset eMMC */
modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_MMC);
modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_MMC, 0);
/* There are other clocks that need to be enabled for the Linux kernel to
* run. For now, turn on all the clocks.
*/
putreg32(0x0, MPFS_SYSREG_SOFT_RESET_CR);
putreg32(0x7fffffff, MPFS_SYSREG_SUBBLK_CLOCK_CR);
return 0;
}
/****************************************************************************
* Name: mpfs_opensbi_scratch_setup
*
* Description:
* Initializes the scratch area per hart. The scratch area is used to save
* and restore registers (see mpfs_exception_opensbi), and to send and
* reveice messages to other harts via the IPI mechanism.
*
* Input Parameters:
* hartid - hart number to be prepared
*
* Returned Value:
* None
*
****************************************************************************/
static void mpfs_opensbi_scratch_setup(uint32_t hartid)
{
DEBUGASSERT(hartid < MPFS_MAX_NUM_HARTS);
g_scratches[hartid].scratch.options = SBI_SCRATCH_DEBUG_PRINTS;
g_scratches[hartid].scratch.hartid_to_scratch =
(unsigned long)mpfs_hart_to_scratch;
g_scratches[hartid].scratch.platform_addr = (unsigned long)&platform;
/* Our FW area in l2lim section. OpenSBI needs to be aware of it in order
* to protect the area. However, we set the PMP values already and lock
* them so that OpenSBI has no chance override then.
*/
g_scratches[hartid].scratch.fw_start = (unsigned long)&__mpfs_nuttx_start;
g_scratches[hartid].scratch.fw_size = (unsigned long)&__mpfs_nuttx_end -
(unsigned long)&__mpfs_nuttx_start;
DEBUGASSERT(g_scratches[hartid].scratch.fw_size > 0);
}
/****************************************************************************
* Name: mpfs_opensbi_pmp_setup
*
* Description:
* Initializes the PMP registers in a known default state. All harts need
* to set these registers.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static void mpfs_opensbi_pmp_setup(void)
{
/* All access granted */
csr_write(pmpaddr0, MPFS_PMP_DEFAULT_ADDR);
csr_write(pmpcfg0, MPFS_PMP_DEFAULT_PERM);
csr_write(pmpcfg2, 0);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mpfs_opensbi_setup
*
* Description:
* Calls the necessary OpenSBI init functions:
* - Sets up the PMP registers (to avoid OpenSBI overriding them)
* - Sets up the OpenSBI console
* - Sets up the mscratch registers
* - Sets up the firmware to be run (should be already at .next_addr)
* - Calls the sbi_init() that will not return
*
* Input Parameters:
* None
*
* Returned Value:
* None - this will never return
*
****************************************************************************/
void __attribute__((noreturn)) mpfs_opensbi_setup(void)
{
uint32_t hartid = current_hartid();
mpfs_opensbi_pmp_setup();
sbi_console_set_device(&mpfs_console);
mpfs_opensbi_scratch_setup(hartid);
csr_write(mscratch, &g_scratches[hartid].scratch);
g_scratches[hartid].scratch.next_mode = PRV_S;
g_scratches[hartid].scratch.next_addr = UBOOT_LOAD_ADDR;
g_scratches[hartid].scratch.next_arg1 = 0;
sbi_init(&g_scratches[hartid].scratch);
/* Will never get here */
DEBUGPANIC();
}