a5fd0d184a
The init infrastructure, found in `init.h`, is currently used by: - `SYS_INIT`: to call functions before `main` - `DEVICE_*`: to initialize devices They are all sorted according to an initialization level + a priority. `SYS_INIT` calls are really orthogonal to devices, however, the required function signature requires a `const struct device *dev` as a first argument. The only reason for that is because the same init machinery is used by devices, so we have something like: ```c struct init_entry { int (*init)(const struct device *dev); /* only set by DEVICE_*, otherwise NULL */ const struct device *dev; } ``` As a result, we end up with such weird/ugly pattern: ```c static int my_init(const struct device *dev) { /* always NULL! add ARG_UNUSED to avoid compiler warning */ ARG_UNUSED(dev); ... } ``` This is really a result of poor internals isolation. This patch proposes a to make init entries more flexible so that they can accept sytem initialization calls like this: ```c static int my_init(void) { ... } ``` This is achieved using a union: ```c union init_function { /* for SYS_INIT, used when init_entry.dev == NULL */ int (*sys)(void); /* for DEVICE*, used when init_entry.dev != NULL */ int (*dev)(const struct device *dev); }; struct init_entry { /* stores init function (either for SYS_INIT or DEVICE*) union init_function init_fn; /* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows * to know which union entry to call. */ const struct device *dev; } ``` This solution **does not increase ROM usage**, and allows to offer clean public APIs for both SYS_INIT and DEVICE*. Note that however, init machinery keeps a coupling with devices. **NOTE**: This is a breaking change! All `SYS_INIT` functions will need to be converted to the new signature. See the script offered in the following commit. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> init: convert SYS_INIT functions to the new signature Conversion scripted using scripts/utils/migrate_sys_init.py. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> manifest: update projects for SYS_INIT changes Update modules with updated SYS_INIT calls: - hal_ti - lvgl - sof - TraceRecorderSource Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: devicetree: devices: adjust test Adjust test according to the recently introduced SYS_INIT infrastructure. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: kernel: threads: adjust SYS_INIT call Adjust to the new signature: int (*init_fn)(void); Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
295 lines
6.9 KiB
C
295 lines
6.9 KiB
C
/*
|
|
* Copyright (c) 2021 EPAM Systems
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/arch/arm64/hypercall.h>
|
|
#include <zephyr/xen/console.h>
|
|
#include <zephyr/xen/events.h>
|
|
#include <zephyr/xen/generic.h>
|
|
#include <zephyr/xen/hvm.h>
|
|
#include <zephyr/xen/public/io/console.h>
|
|
#include <zephyr/xen/public/sched.h>
|
|
#include <zephyr/xen/public/xen.h>
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/device_mmio.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(uart_hvc_xen, CONFIG_UART_LOG_LEVEL);
|
|
|
|
static struct hvc_xen_data hvc_data = {0};
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static void hvc_uart_evtchn_cb(void *priv);
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
static int read_from_ring(const struct device *dev, char *str, int len)
|
|
{
|
|
int recv = 0;
|
|
struct hvc_xen_data *hvc_data = dev->data;
|
|
XENCONS_RING_IDX cons = hvc_data->intf->in_cons;
|
|
XENCONS_RING_IDX prod = hvc_data->intf->in_prod;
|
|
XENCONS_RING_IDX in_idx = 0;
|
|
|
|
compiler_barrier();
|
|
__ASSERT((prod - cons) <= sizeof(hvc_data->intf->in),
|
|
"Invalid input ring buffer");
|
|
|
|
while (cons != prod && recv < len) {
|
|
in_idx = MASK_XENCONS_IDX(cons, hvc_data->intf->in);
|
|
str[recv] = hvc_data->intf->in[in_idx];
|
|
recv++;
|
|
cons++;
|
|
}
|
|
|
|
compiler_barrier();
|
|
hvc_data->intf->in_cons = cons;
|
|
|
|
notify_evtchn(hvc_data->evtchn);
|
|
return recv;
|
|
}
|
|
|
|
static int write_to_ring(const struct device *dev, const char *str, int len)
|
|
{
|
|
int sent = 0;
|
|
struct hvc_xen_data *hvc_data = dev->data;
|
|
XENCONS_RING_IDX cons = hvc_data->intf->out_cons;
|
|
XENCONS_RING_IDX prod = hvc_data->intf->out_prod;
|
|
XENCONS_RING_IDX out_idx = 0;
|
|
|
|
compiler_barrier();
|
|
__ASSERT((prod - cons) <= sizeof(hvc_data->intf->out),
|
|
"Invalid output ring buffer");
|
|
|
|
while ((sent < len) && ((prod - cons) < sizeof(hvc_data->intf->out))) {
|
|
out_idx = MASK_XENCONS_IDX(prod, hvc_data->intf->out);
|
|
hvc_data->intf->out[out_idx] = str[sent];
|
|
prod++;
|
|
sent++;
|
|
}
|
|
|
|
compiler_barrier();
|
|
hvc_data->intf->out_prod = prod;
|
|
|
|
if (sent) {
|
|
notify_evtchn(hvc_data->evtchn);
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
static int xen_hvc_poll_in(const struct device *dev,
|
|
unsigned char *c)
|
|
{
|
|
int ret = 0;
|
|
char temp;
|
|
|
|
ret = read_from_ring(dev, &temp, sizeof(temp));
|
|
if (!ret) {
|
|
/* Char was not received */
|
|
return -1;
|
|
}
|
|
|
|
*c = temp;
|
|
return 0;
|
|
}
|
|
|
|
static void xen_hvc_poll_out(const struct device *dev,
|
|
unsigned char c)
|
|
{
|
|
/* Not a good solution (notifying HV every time), but needed for poll_out */
|
|
(void) write_to_ring(dev, &c, sizeof(c));
|
|
}
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static int xen_hvc_fifo_fill(const struct device *dev, const uint8_t *tx_data,
|
|
int len)
|
|
{
|
|
int ret = 0, sent = 0;
|
|
|
|
while (len) {
|
|
sent = write_to_ring(dev, tx_data, len);
|
|
|
|
ret += sent;
|
|
tx_data += sent;
|
|
len -= sent;
|
|
|
|
if (len) {
|
|
/* Need to be able to read it from another domain */
|
|
HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int xen_hvc_fifo_read(const struct device *dev, uint8_t *rx_data,
|
|
const int size)
|
|
{
|
|
return read_from_ring(dev, rx_data, size);
|
|
}
|
|
|
|
static void xen_hvc_irq_tx_enable(const struct device *dev)
|
|
{
|
|
/*
|
|
* Need to explicitly call UART callback on TX enabling to
|
|
* process available buffered TX actions, because no HV events
|
|
* will be generated on tx_enable.
|
|
*/
|
|
hvc_uart_evtchn_cb(dev->data);
|
|
}
|
|
|
|
static int xen_hvc_irq_tx_ready(const struct device *dev)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void xen_hvc_irq_rx_enable(const struct device *dev)
|
|
{
|
|
/*
|
|
* Need to explicitly call UART callback on RX enabling to
|
|
* process available buffered RX actions, because no HV events
|
|
* will be generated on rx_enable.
|
|
*/
|
|
hvc_uart_evtchn_cb(dev->data);
|
|
}
|
|
|
|
static int xen_hvc_irq_tx_complete(const struct device *dev)
|
|
{
|
|
/*
|
|
* TX is performed by copying in ring buffer by fifo_fill,
|
|
* so it will be always completed.
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
static int xen_hvc_irq_rx_ready(const struct device *dev)
|
|
{
|
|
struct hvc_xen_data *data = dev->data;
|
|
|
|
/* RX is ready only if data is available in ring buffer */
|
|
return (data->intf->in_prod != data->intf->in_cons);
|
|
}
|
|
|
|
static int xen_hvc_irq_is_pending(const struct device *dev)
|
|
{
|
|
return xen_hvc_irq_rx_ready(dev);
|
|
}
|
|
|
|
static int xen_hvc_irq_update(const struct device *dev)
|
|
{
|
|
/* Nothing needs to be updated before actual ISR */
|
|
return 1;
|
|
}
|
|
|
|
static void xen_hvc_irq_callback_set(const struct device *dev,
|
|
uart_irq_callback_user_data_t cb, void *user_data)
|
|
{
|
|
struct hvc_xen_data *data = dev->data;
|
|
|
|
data->irq_cb = cb;
|
|
data->irq_cb_data = user_data;
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
static const struct uart_driver_api xen_hvc_api = {
|
|
.poll_in = xen_hvc_poll_in,
|
|
.poll_out = xen_hvc_poll_out,
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
.fifo_fill = xen_hvc_fifo_fill,
|
|
.fifo_read = xen_hvc_fifo_read,
|
|
.irq_tx_enable = xen_hvc_irq_tx_enable,
|
|
.irq_tx_ready = xen_hvc_irq_tx_ready,
|
|
.irq_rx_enable = xen_hvc_irq_rx_enable,
|
|
.irq_tx_complete = xen_hvc_irq_tx_complete,
|
|
.irq_rx_ready = xen_hvc_irq_rx_ready,
|
|
.irq_is_pending = xen_hvc_irq_is_pending,
|
|
.irq_update = xen_hvc_irq_update,
|
|
.irq_callback_set = xen_hvc_irq_callback_set,
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
};
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
static void hvc_uart_evtchn_cb(void *priv)
|
|
{
|
|
struct hvc_xen_data *data = priv;
|
|
|
|
if (data->irq_cb) {
|
|
data->irq_cb(data->dev, data->irq_cb_data);
|
|
}
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
int xen_console_init(const struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
uint64_t console_pfn = 0;
|
|
uintptr_t console_addr = 0;
|
|
struct hvc_xen_data *data = dev->data;
|
|
|
|
data->dev = dev;
|
|
|
|
ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &data->evtchn);
|
|
if (ret) {
|
|
LOG_ERR("%s: failed to get Xen console evtchn, ret = %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &console_pfn);
|
|
if (ret) {
|
|
LOG_ERR("%s: failed to get Xen console PFN, ret = %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
console_addr = (uintptr_t) (console_pfn << XEN_PAGE_SHIFT);
|
|
device_map(DEVICE_MMIO_RAM_PTR(dev), console_addr, XEN_PAGE_SIZE,
|
|
K_MEM_CACHE_WB);
|
|
|
|
data->intf = (struct xencons_interface *) DEVICE_MMIO_GET(dev);
|
|
|
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
|
bind_event_channel(data->evtchn, hvc_uart_evtchn_cb, data);
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
|
|
|
LOG_INF("Xen HVC inited successfully\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEVICE_DT_DEFINE(DT_NODELABEL(xen_hvc), xen_console_init, NULL, &hvc_data,
|
|
NULL, PRE_KERNEL_1, CONFIG_XEN_HVC_INIT_PRIORITY,
|
|
&xen_hvc_api);
|
|
|
|
#ifdef CONFIG_XEN_EARLY_CONSOLEIO
|
|
extern void __printk_hook_install(int (*fn)(int));
|
|
extern void __stdout_hook_install(int (*fn)(int));
|
|
|
|
int xen_consoleio_putc(int c)
|
|
{
|
|
char symbol = (char) c;
|
|
|
|
HYPERVISOR_console_io(CONSOLEIO_write, sizeof(symbol), &symbol);
|
|
return c;
|
|
}
|
|
|
|
|
|
|
|
int consoleio_hooks_set(void)
|
|
{
|
|
|
|
/* Will be replaced with poll_in/poll_out by uart_console.c later on boot */
|
|
__stdout_hook_install(xen_consoleio_putc);
|
|
__printk_hook_install(xen_consoleio_putc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(consoleio_hooks_set, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
|
#endif /* CONFIG_XEN_EARLY_CONSOLEIO */
|