Add support for Zephyr

Most of the basic SoC drivers are copied from the official SERV
source code and have only been adapted. This commit also adds
device tree files and other definitions to support the Trashernet
hardware.

A preliminary driver (polling-based) for the Trashernet Ethernet
module is also included.
This commit is contained in:
Markus Koch 2024-12-02 13:28:44 +01:00
parent b8f273c9b6
commit d75c373da7
32 changed files with 982 additions and 0 deletions

9
zephyr/west.yml Normal file
View File

@ -0,0 +1,9 @@
manifest:
remotes:
- name: zephyrproject-rtos
url-base: https://github.com/zephyrproject-rtos
projects:
- name: zephyr
remote: zephyrproject-rtos
revision: v3.7.0
import: true

View File

@ -0,0 +1,4 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
add_subdirectory(drivers)

8
zephyr/zephyr/Kconfig Normal file
View File

@ -0,0 +1,8 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
menu "SweRVolf"
rsource "drivers/Kconfig"
endmenu

View File

@ -0,0 +1,8 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
#
# SPDX-License-Identifier: Apache-2.0
config BOARD_TRASHERNET_SOC
bool "Trashernet SoC"
depends on SOC_RISCV32_SERVANT

View File

@ -0,0 +1,13 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
#
# SPDX-License-Identifier: Apache-2.0
config BOARD
default "trashernet_soc"
depends on BOARD_TRASHERNET_SOC
config ETH_TRASHERNET
bool
default y
depends on NET_L2_ETHERNET

View File

@ -0,0 +1,17 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
#
# SPDX-License-Identifier: Apache-2.0
identifier: trashernet_soc
name: Trashernet SoC
type: mcu
arch: riscv32
toolchain:
- zephyr
ram: 32
vendor: notsyncing.net
testing:
ignore_tags:
- net
- bluetooth

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
* Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
/dts-v1/;
#include <serv.dtsi>
/ {
chosen {
zephyr,sram = &ram;
zephyr,console = &uart0;
};
ram: memory@0 {
compatible = "mmio-sram";
reg = <0x40000000 0x800000>;
};
soc {
compatible = "olofk,serv";
#address-cells = <1>;
#size-cells = <1>;
uart0: serial@0 {
reg = <0x0 0x1>;
compatible = "trashernet,serial";
};
eth0: ethernet@0 {
reg = <0x0 0x1>;
compatible = "notsyncing,trashernet";
local-mac-address = [10 b1 7d df 6f db];
};
};
};

View File

@ -0,0 +1,13 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
#
# SPDX-License-Identifier: Apache-2.0
CONFIG_BOARD_TRASHERNET_SOC=y
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_TRASHERNET=y
CONFIG_NET_L2_ETHERNET=y
CONFIG_UART_CONSOLE=y
CONFIG_XIP=n

View File

@ -0,0 +1,7 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
add_subdirectory_ifdef(CONFIG_SERIAL_HAS_DRIVER serial)
add_subdirectory_ifdef(CONFIG_SYS_CLOCK_EXISTS timer)
add_subdirectory_ifdef(CONFIG_ETH_TRASHERNET trashernet)

View File

@ -0,0 +1,12 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
menu "Device Drivers"
rsource "serial/Kconfig"
rsource "timer/Kconfig"
rsource "trashernet/Kconfig"
endmenu

View File

@ -0,0 +1,7 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
# SPDX-License-Identifier: Apache-2.0
set(ZEPHYR_CURRENT_LIBRARY drivers__serial)
zephyr_library_sources_ifdef(CONFIG_UART_TRASHERNET uart_trashernet.c)

View File

@ -0,0 +1,13 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
# SPDX-License-Identifier: Apache-2.0
if SERIAL
menuconfig UART_TRASHERNET
bool "Trashernet serial driver"
select SERIAL_HAS_DRIVER
help
Enables the Trashernet serial driver
endif

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
* Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <inttypes.h>
#define UART0_BASE 0x81000000
#define UART0_SR (*((volatile uint32_t *) (UART0_BASE + 0x00)))
#define UART0_DR (*((volatile uint32_t *) (UART0_BASE + 0x04)))
#define UART0_SR_RX_DATA_EMPTY (1 << 0)
#define UART0_SR_TX_FULL (1 << 1)
#define DT_DRV_COMPAT trashernet_serial
static struct k_spinlock lock;
static void uart_trashernet_poll_out(const struct device *dev, unsigned char c)
{
ARG_UNUSED(dev);
k_spinlock_key_t key = k_spin_lock(&lock);
while (UART0_SR & UART0_SR_TX_FULL);
UART0_DR = c;
k_spin_unlock(&lock, key);
}
static int uart_trashernet_poll_in(const struct device *dev, unsigned char *c)
{
ARG_UNUSED(dev);
*c = UART0_DR;
return (UART0_SR & UART0_SR_RX_DATA_EMPTY) ? -1 : 0;
}
static int uart_trashernet_init(const struct device *dev)
{
ARG_UNUSED(dev);
return 0;
}
static const struct uart_driver_api uart_trashernet_driver_api = {
.poll_in = uart_trashernet_poll_in,
.poll_out = uart_trashernet_poll_out,
.err_check = NULL,
};
struct my_dev_data {
};
struct my_dev_cfg {
};
#define CREATE_MY_DEVICE(inst) \
static struct my_dev_data my_data_##inst = { \
}; \
static const struct my_dev_cfg my_cfg_##inst = { \
}; \
DEVICE_DT_INST_DEFINE(inst, \
uart_trashernet_init, \
NULL, \
&my_data_##inst, \
&my_cfg_##inst, \
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
&uart_trashernet_driver_api);
DT_INST_FOREACH_STATUS_OKAY(CREATE_MY_DEVICE)

View File

@ -0,0 +1,5 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
zephyr_library_sources_ifdef(CONFIG_SERV_TIMER serv_timer.c)

View File

@ -0,0 +1,9 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
config SERV_TIMER
bool "SERV Timer"
select TICKLESS_CAPABLE
help
This module implements a kernel device driver for the SERV
timer driver. It provides the standard "system clock driver" interfaces.

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
This is basically a 32-bit version of riscv_machine_timer.c for Zephyr
*/
#include <zephyr/drivers/timer/system_timer.h>
#include <zephyr/sys_clock.h>
#include <zephyr/spinlock.h>
#include <zephyr/device.h>
#include <soc.h>
#define CYC_PER_TICK ((uint32_t)((uint32_t)sys_clock_hw_cycles_per_sec() \
/ (uint32_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC))
#define MAX_CYC 0xffffffffu
#define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK)
#define MIN_DELAY 1000
#define TICKLESS IS_ENABLED(CONFIG_TICKLESS_KERNEL)
static struct k_spinlock lock;
static uint32_t last_count;
static ALWAYS_INLINE void set_mtimecmp(uint32_t time)
{
//printk("set_mtimecmp to %d @ %d\n", time, sys_read32(SERV_TIMER_BASE));
sys_write32(time, SERV_TIMER_BASE);
}
static ALWAYS_INLINE uint32_t mtime(void)
{
return sys_read32(SERV_TIMER_BASE);
}
static void timer_isr(void *arg)
{
//printk("timer isr\n");
ARG_UNUSED(arg);
k_spinlock_key_t key = k_spin_lock(&lock);
uint32_t now = mtime();
uint32_t dticks = ((now - last_count) / CYC_PER_TICK);
last_count += dticks * CYC_PER_TICK;
if (!TICKLESS) {
uint32_t next = last_count + CYC_PER_TICK;
if ((int32_t)(next - now) < MIN_DELAY) {
next += CYC_PER_TICK;
}
set_mtimecmp(next);
}
k_spin_unlock(&lock, key);
sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? dticks : 1);
}
int irq_testy()
{
printk("test isr\n");
return 0;
}
int sys_clock_driver_init()
{
IRQ_CONNECT(RISCV_MACHINE_TIMER_IRQ, 0, timer_isr, NULL, 0);
//IRQ_CONNECT(5, 0, irq_testy, NULL, 0);
last_count = mtime();
set_mtimecmp(last_count + (uint32_t)CYC_PER_TICK);
//irq_enable(5);
irq_enable(RISCV_MACHINE_TIMER_IRQ);
return 0;
}
void sys_clock_set_timeout(int32_t ticks, bool idle)
{
ARG_UNUSED(idle);
#if defined(CONFIG_TICKLESS_KERNEL)
/* RISCV has no idle handler yet, so if we try to spin on the
* logic below to reset the comparator, we'll always bump it
* forward to the "next tick" due to MIN_DELAY handling and
* the interrupt will never fire! Just rely on the fact that
* the OS gave us the proper timeout already.
*/
if (idle) {
return;
}
ticks = ticks == K_TICKS_FOREVER ? MAX_TICKS : ticks;
ticks = MAX(MIN(ticks - 1, (int32_t)MAX_TICKS), 0);
k_spinlock_key_t key = k_spin_lock(&lock);
uint32_t now = mtime();
uint32_t adj, cyc = ticks * CYC_PER_TICK;
/* Round up to next tick boundary. */
adj = (now - last_count) + (CYC_PER_TICK - 1);
if (cyc <= MAX_CYC - adj) {
cyc += adj;
} else {
cyc = MAX_CYC;
}
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK;
if ((int32_t)(cyc + last_count - now) < MIN_DELAY) {
cyc += CYC_PER_TICK;
}
set_mtimecmp(cyc + last_count);
k_spin_unlock(&lock, key);
#endif
}
uint32_t sys_clock_elapsed(void)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return 0;
}
k_spinlock_key_t key = k_spin_lock(&lock);
uint32_t ret = (mtime() - last_count) / CYC_PER_TICK;
k_spin_unlock(&lock, key);
return ret;
}
uint32_t sys_timer_cycle_get_32(void)
{
return mtime();
}
uint32_t sys_clock_cycle_get_32(void)
{
return mtime();
}
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);

View File

@ -0,0 +1,5 @@
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
# SPDX-License-Identifier: Apache-2.0
zephyr_library_sources_ifdef(CONFIG_ETH_TRASHERNET trashernet.c)

View File

@ -0,0 +1,10 @@
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
# SPDX-License-Identifier: Apache-2.0
config ETH_TRASHERNET
bool "Trashernet 10MBit/s PHY driver"
depends on NETWORKING
depends on !TEST_NET
depends on NET_L2_ETHERNET
help
This module implements a kernel device driver for the Trashernet 10 MBit/s PHY.

View File

@ -0,0 +1,253 @@
/* TRASHERNET Stand-alone Ethernet Controller with SPI
*
* Copyright (c) 2016 Markus Koch <markus@notsyncing.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT notsyncing_trashernet
#define LOG_MODULE_NAME eth_trashernet
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
// TODO: Load this stuff from DT
#define TRASHERNET_BASE 0x82000000
#define TRASHERNET_SR (*((volatile uint32_t *) (TRASHERNET_BASE + 0x00)))
#define TRASHERNET_DR (*((volatile uint8_t *) (TRASHERNET_BASE + 0x04)))
#define TRASHERNET_SR_LINK_UP (1 << 0)
#define TRASHERNET_SR_RX_AVAILABLE (1 << 1)
#define TRASHERNET_SR_TX_NOTEMPTY (1 << 2)
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <string.h>
#include <errno.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/ethernet.h>
#define ETH_TRASHERNET_RX_THREAD_STACK_SIZE 800
#define ETH_TRASHERNET_RX_THREAD_PRIO 2
struct eth_trashernet_config {
int32_t timeout;
};
struct eth_trashernet_runtime {
struct net_if *iface;
K_KERNEL_STACK_MEMBER(thread_stack, ETH_TRASHERNET_RX_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem int_sem;
bool iface_initialized : 1;
uint8_t mac_address[6];
};
static void eth_trashernet_set_data(const struct device *dev, uint8_t *buffer, uint16_t length)
{
uint16_t i;
for (i=0; i < length; ++i) {
TRASHERNET_DR = buffer[i];
}
}
static int eth_trashernet_tx(const struct device *dev, struct net_pkt *pkt)
{
struct eth_trashernet_runtime *context = dev->data;
uint16_t len = net_pkt_get_len(pkt);
struct net_buf *frag;
LOG_DBG("TX %s: pkt %p (len %u)", dev->name, pkt, len);
while (TRASHERNET_SR & TRASHERNET_SR_TX_NOTEMPTY); // FIXME: To be fixed in HDL: We currently do not support TX packet separation, so we have to wait for the TX buffer to be completely empty before adding the next packet.
for (frag = pkt->frags; frag; frag = frag->frags) {
eth_trashernet_set_data(dev, frag->data, frag->len);
}
TRASHERNET_SR = TRASHERNET_SR_TX_NOTEMPTY; // Frame complete, TX now
//LOG_DBG("%s: Tx successful", dev->name);
return 0;
}
static void eth_trashernet_get_data(const struct device *dev, uint8_t *buffer, uint16_t length)
{
for (int x = 0; x < length; ++x) {
buffer[x] = TRASHERNET_DR;
}
}
static int eth_trashernet_rx(const struct device *dev)
{
struct eth_trashernet_runtime *context = dev->data;
const struct eth_trashernet_config *config = dev->config;
uint16_t frm_len;
uint16_t lengthfr;
struct net_buf *pkt_buf;
struct net_pkt *pkt;
frm_len = TRASHERNET_SR >> 16;
TRASHERNET_SR = (1 << 1); // Confirm that we've read the length
if (!frm_len) {
LOG_ERR("%s: Receive called without data available!", dev->name);
return 0;
}
//LOG_DBG("RX Data %d", frm_len);
pkt = net_pkt_rx_alloc_with_buffer(context->iface, frm_len,
AF_UNSPEC, 0, K_MSEC(config->timeout));
if (!pkt) {
LOG_ERR("%s: Could not allocate rx buffer", dev->name);
return 0;
}
pkt_buf = pkt->buffer;
lengthfr = frm_len;
do {
size_t frag_len;
uint8_t *data_ptr;
size_t spi_frame_len;
data_ptr = pkt_buf->data;
/* Review the space available for the new frag */
frag_len = net_buf_tailroom(pkt_buf);
if (frm_len > frag_len) {
spi_frame_len = frag_len;
} else {
spi_frame_len = frm_len;
}
eth_trashernet_get_data(dev, data_ptr, spi_frame_len);
net_buf_add(pkt_buf, spi_frame_len);
/* One fragment has been written via SPI */
frm_len -= spi_frame_len;
pkt_buf = pkt_buf->frags;
} while (frm_len > 0);
net_pkt_set_iface(pkt, context->iface);
LOG_DBG("%s: Received packet of length %u", dev->name, lengthfr);
if (net_recv_data(net_pkt_iface(pkt), pkt) < 0) {
net_pkt_unref(pkt);
}
return 0;
}
static void eth_trashernet_rx_thread(void *p1, void *p2, void *p3)
{
int status_last = 0;
int status = 0;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
const struct device *dev = p1;
struct eth_trashernet_runtime *context = dev->data;
while (true) {
//k_sem_take(&context->int_sem, K_FOREVER); // Set to wait for IRQ
//k_sem_give(&context->int_sem); // Set in IRQ
status = TRASHERNET_SR;
if ((status ^ status_last) & TRASHERNET_SR_LINK_UP) { // Link change
if (status & TRASHERNET_SR_LINK_UP) {
LOG_INF("%s: Link up", dev->name);
net_eth_carrier_on(context->iface);
} else {
LOG_INF("%s: Link down", dev->name);
if (context->iface_initialized) {
net_eth_carrier_off(context->iface);
}
}
}
if (status & TRASHERNET_SR_RX_AVAILABLE) { // New RX data
eth_trashernet_rx(dev);
} else {
k_msleep(10); // We poll
}
status_last = status;
}
}
static enum ethernet_hw_caps eth_trashernet_get_capabilities(const struct device *dev)
{
ARG_UNUSED(dev);
return ETHERNET_LINK_10BASE_T
#if defined(CONFIG_NET_VLAN)
| ETHERNET_HW_VLAN
#endif
;
}
static void eth_trashernet_iface_init(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
struct eth_trashernet_runtime *context = dev->data;
net_if_set_link_addr(iface, context->mac_address,
sizeof(context->mac_address),
NET_LINK_ETHERNET);
if (context->iface == NULL) {
context->iface = iface;
}
ethernet_init(iface);
net_if_carrier_off(iface);
context->iface_initialized = true;
}
static const struct ethernet_api api_funcs = {
.iface_api.init = eth_trashernet_iface_init,
.get_capabilities = eth_trashernet_get_capabilities,
.send = eth_trashernet_tx,
};
static int eth_trashernet_init(const struct device *dev)
{
const struct eth_trashernet_config *config = dev->config;
struct eth_trashernet_runtime *context = dev->data;
/* Start interruption-poll thread */
k_thread_create(&context->thread, context->thread_stack,
ETH_TRASHERNET_RX_THREAD_STACK_SIZE,
eth_trashernet_rx_thread,
(void *)dev, NULL, NULL,
K_PRIO_COOP(ETH_TRASHERNET_RX_THREAD_PRIO),
0, K_NO_WAIT);
LOG_INF("%s: Initialized", dev->name);
return 0;
}
#define TRASHERNET_DEFINE(inst) \
static struct eth_trashernet_runtime eth_trashernet_runtime_##inst = { \
.mac_address = DT_INST_PROP(inst, local_mac_address), \
.int_sem = Z_SEM_INITIALIZER((eth_trashernet_runtime_##inst).int_sem, 0, UINT_MAX), \
}; \
\
static const struct eth_trashernet_config eth_trashernet_config_##inst = { \
.timeout = 500, \
}; \
\
ETH_NET_DEVICE_DT_INST_DEFINE(inst, eth_trashernet_init, NULL, &eth_trashernet_runtime_##inst, \
&eth_trashernet_config_##inst, CONFIG_ETH_INIT_PRIORITY, \
&api_funcs, NET_ETH_MTU);
DT_INST_FOREACH_STATUS_OKAY(TRASHERNET_DEFINE);

View File

@ -0,0 +1,14 @@
# Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
# SPDX-License-Identifier: Apache-2.0
description: Trashernet PHY
compatible: "notsyncing,trashernet"
include: [ethernet-controller.yaml]
properties:
full-duplex:
type: boolean
description: |
Optional feature flag - Enables full duplex reception and transmission.

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
* Copyright (c) 2024 Markus Koch <markus@notsyncing.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "olofk,servant";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "olofk,serv";
riscv,isa = "rv32i_zicsr";
reg = <0>;
device_type = "cpu";
};
};
};

5
zephyr/zephyr/module.yml Normal file
View File

@ -0,0 +1,5 @@
build:
settings:
board_root: zephyr
dts_root: zephyr
soc_root: zephyr

View File

@ -0,0 +1,8 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
zephyr_sources(
soc_irq.S
vector.S
irq.c
cpu_idle.c)

View File

@ -0,0 +1,26 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
if SOC_RISCV32_SERVANT
config SOC
string
default "servant"
config SYS_CLOCK_HW_CYCLES_PER_SEC
int
default 25000000
config RISCV_SOC_INTERRUPT_INIT
bool
default y
config NUM_IRQS
int
default 8
config SERV_TIMER
bool
default y
endif # SOC_RISCV32_SERVANT

View File

@ -0,0 +1,8 @@
# Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
# SPDX-License-Identifier: Apache-2.0
config SOC_RISCV32_SERVANT
bool "servant SoC"
select RISCV
select ATOMIC_OPERATIONS_C
select RISCV_ISA_EXT_ZICSR

View File

@ -0,0 +1,22 @@
#include <zephyr/irq.h>
#include <zephyr/tracing/tracing.h>
// Override arch_cpu_idle() and arch_cpu_atomic_idle() to prevent insertion
// of `wfi` instructions which lock up our system. This was introduced in
// Zephyr 3.6.0 with commit 5fb6e267f629dedb8382da6bcad8018b1bb8930a.
//
// This is probably a hardware bug in SERV. This issue is tracked as #131.
// https://github.com/olofk/serv/issues/131
void arch_cpu_idle(void)
{
sys_trace_idle();
irq_unlock(MSTATUS_IEN);
}
void arch_cpu_atomic_idle(unsigned int key)
{
sys_trace_idle();
irq_unlock(key);
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief interrupt management code for riscv SOCs supporting the riscv
privileged architecture specification
*/
#include <zephyr/irq.h>
void arch_irq_enable(unsigned int irq)
{
uint32_t mie;
/*
* CSR mie register is updated using atomic instruction csrrs
* (atomic read and set bits in CSR register)
*/
__asm__ volatile ("csrrs %0, mie, %1\n"
: "=r" (mie)
: "r" (1 << irq));
}
void arch_irq_disable(unsigned int irq)
{
uint32_t mie;
/*
* Use atomic instruction csrrc to disable device interrupt in mie CSR.
* (atomic read and clear bits in CSR register)
*/
__asm__ volatile ("csrrc %0, mie, %1\n"
: "=r" (mie)
: "r" (1 << irq));
};
int arch_irq_is_enabled(unsigned int irq)
{
uint32_t mie;
__asm__ volatile ("csrr %0, mie" : "=r" (mie));
return !!(mie & (1 << irq));
}
#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT)
void soc_interrupt_init(void)
{
/* ensure that all interrupts are disabled */
(void)irq_lock();
__asm__ volatile ("csrwi mie, 0\n"
"csrwi mip, 0\n");
}
#endif

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) 2018 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/arch/riscv/common/linker.ld>

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2020 Olof Kindgren <olof.kindgren@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __RISCV32_SERVANT_SOC_H_
#define __RISCV32_SERVANT_SOC_H_
#include <soc_common.h>
/* Timer configuration */
#define SERV_TIMER_BASE 0x80000000
#define SERV_TIMER_IRQ 7
#endif /* __RISCV32_SERVANT_SOC_H_ */

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file configuration macros for riscv SOCs supporting the riscv
* privileged architecture specification
*/
#ifndef __SOC_COMMON_H_
#define __SOC_COMMON_H_
/* IRQ numbers */
#define RISCV_MACHINE_SOFT_IRQ 3 /* Machine Software Interrupt */
#define RISCV_MACHINE_TIMER_IRQ 7 /* Machine Timer Interrupt */
#define RISCV_MACHINE_EXT_IRQ 11 /* Machine External Interrupt */
/* ECALL Exception numbers */
#define SOC_MCAUSE_ECALL_EXP 11 /* Machine ECALL instruction */
#define SOC_MCAUSE_USER_ECALL_EXP 8 /* User ECALL instruction */
/* SOC-specific MCAUSE bitfields */
#ifdef CONFIG_64BIT
/* Interrupt Mask */
#define SOC_MCAUSE_IRQ_MASK (1 << 63)
/* Exception code Mask */
#define SOC_MCAUSE_EXP_MASK 0x7FFFFFFFFFFFFFFF
#else
/* Interrupt Mask */
#define SOC_MCAUSE_IRQ_MASK (1 << 31)
/* Exception code Mask */
#define SOC_MCAUSE_EXP_MASK 0x7FFFFFFF
#endif
/* SOC-Specific EXIT ISR command */
#define SOC_ERET mret
#ifndef _ASMLANGUAGE
#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT)
void soc_interrupt_init(void);
#endif
#if defined(CONFIG_RISCV_HAS_PLIC)
void riscv_plic_irq_enable(uint32_t irq);
void riscv_plic_irq_disable(uint32_t irq);
int riscv_plic_irq_is_enabled(uint32_t irq);
void riscv_plic_set_priority(uint32_t irq, uint32_t priority);
int riscv_plic_get_irq(void);
#endif
#endif /* !_ASMLANGUAGE */
#endif /* __SOC_COMMON_H_ */

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* common interrupt management code for riscv SOCs supporting the riscv
* privileged architecture specification
*/
#include <offsets.h>
#include <zephyr/linker/sections.h>
#include <soc.h>
/* exports */
GTEXT(__soc_handle_irq)
/*
* SOC-specific function to handle pending IRQ number generating the interrupt.
* Exception number is given as parameter via register a0.
*/
SECTION_FUNC(exception.other, __soc_handle_irq)
/* Clear exception number from CSR mip register */
li t1, 1
sll t0, t1, a0
csrrc t1, mip, t0
/* Return */
jalr x0, ra
/*
* __soc_is_irq is defined as .weak to allow re-implementation by
* SOCs that does not truly follow the riscv privilege specification.
*/
WTEXT(__soc_is_irq)
/*
* SOC-specific function to determine if the exception is the result of a
* an interrupt or an exception
* return 1 (interrupt) or 0 (exception)
*
*/
SECTION_FUNC(exception.other, __soc_is_irq)
/* Read mcause and check if interrupt bit is set */
csrr t0, mcause
li t1, SOC_MCAUSE_IRQ_MASK
and t0, t0, t1
/* If interrupt bit is not set, return with 0 */
addi a0, x0, 0
beqz t0, not_interrupt
addi a0, a0, 1
not_interrupt:
/* return */
jalr x0, ra

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
* Contributors: 2018 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/toolchain.h>
/* exports */
GTEXT(__start)
/* imports */
GTEXT(__initialize)
GTEXT(_isr_wrapper)
SECTION_FUNC(vectors, __start)
.option norvc;
/*
* Set mtvec (Machine Trap-Vector Base-Address Register)
* to __isr_wrapper.
*/
la t0, _isr_wrapper
csrw mtvec, t0
/* Jump to __initialize */
tail __initialize