建立工程,成功创建两个虚拟串口

This commit is contained in:
ranchuan
2023-06-21 18:00:56 +08:00
commit 3604192d8f
872 changed files with 428764 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
add_subdirectory (${PROJECT_SYSTEM})
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,24 @@
collect (PROJECT_LIB_HEADERS alloc.h)
collect (PROJECT_LIB_HEADERS assert.h)
collect (PROJECT_LIB_HEADERS cache.h)
collect (PROJECT_LIB_HEADERS condition.h)
collect (PROJECT_LIB_HEADERS io.h)
collect (PROJECT_LIB_HEADERS irq.h)
collect (PROJECT_LIB_HEADERS log.h)
collect (PROJECT_LIB_HEADERS mutex.h)
collect (PROJECT_LIB_HEADERS sleep.h)
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES condition.c)
collect (PROJECT_LIB_SOURCES device.c)
collect (PROJECT_LIB_SOURCES init.c)
collect (PROJECT_LIB_SOURCES io.c)
collect (PROJECT_LIB_SOURCES irq.c)
collect (PROJECT_LIB_SOURCES shmem.c)
collect (PROJECT_LIB_SOURCES time.c)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/alloc.c
* @brief FreeRTOS libmetal memory allocattion definitions.
*/
#ifndef __METAL_ALLOC__H__
#error "Include metal/alloc.h instead of metal/freertos/alloc.h"
#endif
#ifndef __METAL_FREERTOS_ALLOC__H__
#define __METAL_FREERTOS_ALLOC__H__
#include "FreeRTOS.h"
#ifdef __cplusplus
extern "C" {
#endif
static inline void *metal_allocate_memory(unsigned int size)
{
return (pvPortMalloc(size));
}
static inline void metal_free_memory(void *ptr)
{
vPortFree(ptr);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_ALLOC__H__ */

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file assert.h
* @brief FreeRTOS assertion support.
*/
#ifndef __METAL_ASSERT__H__
#error "Include metal/assert.h instead of metal/freertos/assert.h"
#endif
#ifndef __METAL_FREERTOS_ASSERT__H__
#define __METAL_FREERTOS_ASSERT__H__
#include <assert.h>
/**
* @brief Assertion macro for FreeRTOS applications.
* @param cond Condition to evaluate.
*/
#define metal_sys_assert(cond) assert(cond)
#endif /* __METAL_FREERTOS_ASSERT__H__ */

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/cache.h
* @brief FreeRTOS cache operation primitives for libmetal.
*/
#ifndef __METAL_CACHE__H__
#error "Include metal/cache.h instead of metal/freertos/cache.h"
#endif
#ifndef __METAL_FREERTOS_CACHE__H__
#define __METAL_FREERTOS_CACHE__H__
#ifdef __cplusplus
extern "C" {
#endif
extern void metal_machine_cache_flush(void *addr, unsigned int len);
extern void metal_machine_cache_invalidate(void *addr, unsigned int len);
static inline void __metal_cache_flush(void *addr, unsigned int len)
{
metal_machine_cache_flush(addr, len);
}
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
{
metal_machine_cache_invalidate(addr, len);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_CACHE__H__ */

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/condition.c
* @brief Generic libmetal condition variable handling.
*/
#include <metal/condition.h>
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
/* TODO: Implement condition variable for FreeRTOS */
(void)cv;
(void)m;
return 0;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/condition.h
* @brief Generic condition variable primitives for libmetal.
*/
#ifndef __METAL_CONDITION__H__
#error "Include metal/condition.h instead of metal/freertos/condition.h"
#endif
#ifndef __METAL_FREERTOS_CONDITION__H__
#define __METAL_FREERTOS_CONDITION__H__
#include <metal/atomic.h>
#ifdef __cplusplus
extern "C" {
#endif
struct metal_condition {
metal_mutex_t *m; /**< mutex.
The condition variable is attached to
this mutex when it is waiting.
It is also used to check correctness
in case there are multiple waiters. */
atomic_int v; /**< condition variable value. */
};
/** Static metal condition variable initialization. */
#define METAL_CONDITION_INIT { NULL, ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
/* TODO: Implement condition variable for FreeRTOS */
(void)cv;
return;
}
static inline int metal_condition_signal(struct metal_condition *cv)
{
/* TODO: Implement condition variable for FreeRTOS */
(void)cv;
return 0;
}
static inline int metal_condition_broadcast(struct metal_condition *cv)
{
/* TODO: Implement condition variable for FreeRTOS */
(void)cv;
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_CONDITION__H__ */

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/device.c
* @brief FreeRTOS device operations.
*/
#include <metal/device.h>
#include <metal/sys.h>
#include <metal/utilities.h>
int metal_generic_dev_sys_open(struct metal_device *dev)
{
struct metal_io_region *io;
unsigned i;
/* map I/O memory regions */
for (i = 0; i < dev->num_regions; i++) {
io = &dev->regions[i];
if (!io->size)
break;
metal_sys_io_mem_map(io);
}
return 0;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/init.c
* @brief FreeRTOS libmetal initialization.
*/
#include <metal/sys.h>
#include <metal/utilities.h>
#include <metal/device.h>
struct metal_state _metal;
int metal_sys_init(const struct metal_init_params *params)
{
metal_unused(params);
metal_bus_register(&metal_generic_bus);
return 0;
}
void metal_sys_finish(void)
{
metal_bus_unregister(&metal_generic_bus);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/io.c
* @brief FreeRTOS libmetal io operations
*/
#include <metal/io.h>
void metal_sys_io_mem_map(struct metal_io_region *io)
{
unsigned long p;
size_t psize;
void *va;
va = io->virt;
psize = io->size;
if (psize) {
if (psize >> io->page_shift)
psize = (size_t)1 << io->page_shift;
for (p = 0; p <= (io->size >> io->page_shift); p++) {
metal_machine_io_mem_map(va, io->physmap[p],
psize, io->mem_flags);
va += psize;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/io.h
* @brief FreeRTOS specific io definitions.
*/
#ifndef __METAL_IO__H__
#error "Include metal/io.h instead of metal/freertos/io.h"
#endif
#ifndef __METAL_FREEROTS_IO__H__
#define __METAL_FREEROTS_IO__H__
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
/**
* @brief memory mapping for an I/O region
*/
void metal_sys_io_mem_map(struct metal_io_region *io);
/**
* @brief memory mapping
*/
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags);
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREEROTS_IO__H__ */

View File

@@ -0,0 +1,279 @@
/*
* Copyright (c) 2016 - 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/irq.c
* @brief FreeRTOS libmetal irq definitions.
*/
#include <metal/errno.h>
#include <metal/irq.h>
#include <metal/sys.h>
#include <metal/log.h>
#include <metal/mutex.h>
#include <metal/list.h>
#include <metal/utilities.h>
#include <metal/alloc.h>
/** IRQ handlers descriptor structure */
struct metal_irq_hddesc {
metal_irq_handler hd; /**< irq handler */
void *drv_id; /**< id to identify the driver
of the irq handler */
struct metal_device *dev; /**< device identifier */
struct metal_list node; /**< node on irq handlers list */
};
/** IRQ descriptor structure */
struct metal_irq_desc {
int irq; /**< interrupt number */
struct metal_list hdls; /**< interrupt handlers */
struct metal_list node; /**< node on irqs list */
};
/** IRQ state structure */
struct metal_irqs_state {
struct metal_list irqs; /**< interrupt descriptors */
metal_mutex_t irq_lock; /**< access lock */
};
static struct metal_irqs_state _irqs = {
.irqs = METAL_INIT_LIST(_irqs.irqs),
.irq_lock = METAL_MUTEX_INIT(_irqs.irq_lock),
};
int metal_irq_register(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
struct metal_irq_desc *irq_p = NULL;
struct metal_irq_hddesc *hdl_p;
struct metal_list *node;
unsigned int irq_flags_save;
if (irq < 0) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d need to be a positive number\n",
__func__, irq);
return -EINVAL;
}
if ((drv_id == NULL) || (hd == NULL)) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n",
__func__, irq);
return -EINVAL;
}
/* Search for irq in list */
metal_mutex_acquire(&_irqs.irq_lock);
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if (irq_p->irq == irq) {
struct metal_list *h_node;
/* Check if drv_id already exist */
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
/* if drv_id already exist reject */
if ((hdl_p->drv_id == drv_id) &&
((dev == NULL) || (hdl_p->dev == dev))) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d already registered."
"Will not register again.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -EINVAL;
}
}
/* irq found and drv_id not used, get out of metal_list_for_each */
break;
}
}
/* Either need to add handler to an existing list or to a new one */
hdl_p = metal_allocate_memory(sizeof(struct metal_irq_hddesc));
if (hdl_p == NULL) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d cannot allocate mem for drv_id %d.\n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
hdl_p->hd = hd;
hdl_p->drv_id = drv_id;
hdl_p->dev = dev;
/* interrupt already registered, add handler to existing list*/
if ((irq_p != NULL) && (irq_p->irq == irq)) {
irq_flags_save = metal_irq_save_disable();
metal_list_add_tail(&irq_p->hdls, &hdl_p->node);
metal_irq_restore_enable(irq_flags_save);
metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
/* interrupt was not already registered, add */
irq_p = metal_allocate_memory(sizeof(struct metal_irq_desc));
if (irq_p == NULL) {
metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
irq_p->irq = irq;
metal_list_init(&irq_p->hdls);
metal_list_add_tail(&irq_p->hdls, &hdl_p->node);
irq_flags_save = metal_irq_save_disable();
metal_list_add_tail(&_irqs.irqs, &irq_p->node);
metal_irq_restore_enable(irq_flags_save);
metal_log(METAL_LOG_DEBUG, "%s: success, added irq %d\n", __func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
/* helper function for metal_irq_unregister() */
static void metal_irq_delete_node(struct metal_list *node, void *p_to_free)
{
unsigned int irq_flags_save;
irq_flags_save=metal_irq_save_disable();
metal_list_del(node);
metal_irq_restore_enable(irq_flags_save);
metal_free_memory(p_to_free);
}
int metal_irq_unregister(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
struct metal_irq_desc *irq_p;
struct metal_list *node;
if (irq < 0) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need to be a positive number\n",
__func__, irq);
return -EINVAL;
}
/* Search for irq in list */
metal_mutex_acquire(&_irqs.irq_lock);
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if (irq_p->irq == irq) {
struct metal_list *h_node, *h_prenode;
struct metal_irq_hddesc *hdl_p;
unsigned int delete_count = 0;
metal_log(METAL_LOG_DEBUG, "%s: found irq %d\n",
__func__, irq);
/* Search through handlers */
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
if (((hd == NULL) || (hdl_p->hd == hd)) &&
((drv_id == NULL) || (hdl_p->drv_id == drv_id)) &&
((dev == NULL) || (hdl_p->dev == dev))) {
metal_log(METAL_LOG_DEBUG,
"%s: unregister hd=%p drv_id=%p dev=%p\n",
__func__, hdl_p->hd, hdl_p->drv_id, hdl_p->dev);
h_prenode = h_node->prev;
metal_irq_delete_node(h_node, hdl_p);
delete_count++;
h_node = h_prenode;
}
}
/* we did not find any handler to delete */
if (!delete_count) {
metal_log(METAL_LOG_DEBUG, "%s: No matching entry\n",
__func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
}
/* if interrupt handlers list is empty, unregister interrupt */
if (metal_list_is_empty(&irq_p->hdls)) {
metal_log(METAL_LOG_DEBUG,
"%s: handlers list empty, unregister interrupt\n",
__func__);
metal_irq_delete_node(node, irq_p);
}
metal_log(METAL_LOG_DEBUG, "%s: success\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
}
metal_log(METAL_LOG_DEBUG, "%s: No matching IRQ entry\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
}
unsigned int metal_irq_save_disable(void)
{
return sys_irq_save_disable();
}
void metal_irq_restore_enable(unsigned int flags)
{
sys_irq_restore_enable(flags);
}
void metal_irq_enable(unsigned int vector)
{
sys_irq_enable(vector);
}
void metal_irq_disable(unsigned int vector)
{
sys_irq_disable(vector);
}
/**
* @brief default handler
*/
void metal_irq_isr(unsigned int vector)
{
struct metal_list *node;
struct metal_irq_desc *irq_p;
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if ((unsigned int)irq_p->irq == vector) {
struct metal_list *h_node;
struct metal_irq_hddesc *hdl_p;
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
(hdl_p->hd)(vector, hdl_p->drv_id);
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/irq.c
* @brief FreeRTOS libmetal irq definitions.
*/
#ifndef __METAL_IRQ__H__
#error "Include metal/irq.h instead of metal/freertos/irq.h"
#endif
#ifndef __METAL_FREERTOS_IRQ__H__
#define __METAL_FREERTOS_IRQ__H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief default interrupt handler
* @param[in] vector interrupt vector
*/
void metal_irq_isr(unsigned int vector);
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_IRQ__H__ */

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Linaro nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* @file freertos/log.h
* @brief FreeRTOS libmetal log handler definition.
*/
#ifndef __METAL_METAL_LOG__H__
#error "Include metal/log.h instead of metal/freertos/log.h"
#endif
#ifndef __METAL_FREERTOS_LOG__H__
#define __METAL_FREERTOS_LOG__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_LOG__H__ */

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/mutex.h
* @brief FreeRTOS mutex primitives for libmetal.
*/
#ifndef __METAL_MUTEX__H__
#error "Include metal/mutex.h instead of metal/freertos/mutex.h"
#endif
#ifndef __METAL_FREERTOS_MUTEX__H__
#define __METAL_FREERTOS_MUTEX__H__
#include <metal/atomic.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
atomic_int v;
} metal_mutex_t;
/*
* METAL_MUTEX_INIT - used for initializing an mutex elmenet in a static struct
* or global
*/
#define METAL_MUTEX_INIT(m) { ATOMIC_VAR_INIT(0) }
/*
* METAL_MUTEX_DEFINE - used for defining and initializing a global or
* static singleton mutex
*/
#define METAL_MUTEX_DEFINE(m) metal_mutex_t m = METAL_MUTEX_INIT(m)
static inline void __metal_mutex_init(metal_mutex_t *mutex)
{
atomic_store(&mutex->v, 0);
}
static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
{
(void)mutex;
}
static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
{
return 1 - atomic_flag_test_and_set(&mutex->v);
}
static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
{
while (atomic_flag_test_and_set(&mutex->v)) {
;
}
}
static inline void __metal_mutex_release(metal_mutex_t *mutex)
{
atomic_flag_clear(&mutex->v);
}
static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
{
return atomic_load(&mutex->v);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_MUTEX__H__ */

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/shmem.c
* @brief FreeRTOS libmetal shared memory handling.
*/
#include <metal/shmem.h>
int metal_shmem_open(const char *name, size_t size,
struct metal_io_region **io)
{
return metal_shmem_open_generic(name, size, io);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/sleep.h
* @brief FreeRTOS sleep primitives for libmetal.
*/
#ifndef __METAL_SLEEP__H__
#error "Include metal/sleep.h instead of metal/freertos/sleep.h"
#endif
#ifndef __METAL_FREERTOS_SLEEP__H__
#define __METAL_FREERTOS_SLEEP__H__
#include <FreeRTOS.h>
#include <task.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline int __metal_sleep_usec(unsigned int usec)
{
const TickType_t xDelay = usec / portTICK_PERIOD_MS;
vTaskDelay(xDelay);
return 0;
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_SLEEP__H__ */

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/sys.h
* @brief FreeRTOS system primitives for libmetal.
*/
#ifndef __METAL_SYS__H__
#error "Include metal/sys.h instead of metal/freertos/sys.h"
#endif
#ifndef __METAL_FREERTOS_SYS__H__
#define __METAL_FREERTOS_SYS__H__
#include "./@PROJECT_MACHINE@/sys.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef METAL_MAX_DEVICE_REGIONS
#define METAL_MAX_DEVICE_REGIONS 1
#endif
/** Structure for FreeRTOS libmetal runtime state. */
struct metal_state {
/** Common (system independent) data. */
struct metal_common_state common;
};
#ifdef METAL_INTERNAL
/**
* @brief restore interrupts to state before disable_global_interrupt()
*/
void sys_irq_restore_enable(unsigned int flags);
/**
* @brief disable all interrupts
*/
unsigned int sys_irq_save_disable(void);
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_SYS__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/template/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/io.h>
#include <metal/sys.h>
#include <metal/utilities.h>
#include <stdint.h>
void sys_irq_restore_enable(unsigned int flags)
{
metal_unused(flags);
/* Add implementation here */
}
unsigned int sys_irq_save_disable(void)
{
return 0;
/* Add implementation here */
}
void sys_irq_enable(unsigned int vector)
{
metal_unused(vector);
/* Add implementation here */
}
void sys_irq_disable(unsigned int vector)
{
metal_unused(vector);
/* Add implementation here */
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
metal_unused(addr);
metal_unused(len);
/* Add implementation here */
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
metal_unused(addr);
metal_unused(len);
/* Add implementation here */
}
void metal_generic_default_poll(void)
{
/* Add implementation here */
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
metal_unused(pa);
metal_unused(size);
metal_unused(flags);
/* Add implementation here */
return va;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/template/sys.h
* @brief freertos template system primitives for libmetal.
*/
#ifndef __METAL_FREERTOS_SYS__H__
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#ifndef __METAL_FREERTOS_TEMPLATE_SYS__H__
#define __METAL_FREERTOS_TEMPLATE_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
void sys_irq_enable(unsigned int vector);
void sys_irq_disable(unsigned int vector);
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_SYS__H__ */

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/time.c
* @brief freertos libmetal time handling.
*/
#include <FreeRTOS.h>
#include <task.h>
#include <metal/time.h>
unsigned long long metal_get_timestamp(void)
{
return (unsigned long long)xTaskGetTickCount();
}

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2014, Mentor Graphics Corporation
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/zynq7/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/compiler.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include "xil_cache.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "xscugic.h"
/* Translation table is 16K in size */
#define ARM_AR_MEM_TTB_SIZE 16*1024
/* Each TTB descriptor covers a 1MB region */
#define ARM_AR_MEM_TTB_SECT_SIZE 1024*1024
/* Mask off lower bits of addr */
#define ARM_AR_MEM_TTB_SECT_SIZE_MASK (~(ARM_AR_MEM_TTB_SECT_SIZE-1UL))
void sys_irq_restore_enable(unsigned int flags)
{
Xil_ExceptionEnableMask(~flags);
}
unsigned int sys_irq_save_disable(void)
{
unsigned int state = mfcpsr() & XIL_EXCEPTION_ALL;
if (XIL_EXCEPTION_ALL != state) {
Xil_ExceptionDisableMask(XIL_EXCEPTION_ALL);
}
return state;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheFlush();
else
Xil_DCacheFlushRange((intptr_t)addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheInvalidate();
else
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("wfi");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
unsigned int section_offset;
unsigned int ttb_addr;
if (!flags)
return va;
/* Ensure the virtual and physical addresses are aligned on a
section boundary */
pa &= ARM_AR_MEM_TTB_SECT_SIZE_MASK;
/* Loop through entire region of memory (one MMU section at a time).
Each section requires a TTB entry. */
for (section_offset = 0; section_offset < size;
section_offset += ARM_AR_MEM_TTB_SECT_SIZE) {
/* Calculate translation table entry for this memory section */
ttb_addr = (pa + section_offset);
/* Write translation table entry value to entry address */
Xil_SetTlbAttributes(ttb_addr, flags);
}
return va;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/zynq7/sys.h
* @brief freertos zynq7 system primitives for libmetal.
*/
#ifndef __METAL_FREERTOS_SYS__H__
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#include "xscugic.h"
#ifndef __METAL_FREERTOS_ZYNQ7_SYS__H__
#define __METAL_FREERTOS_ZYNQ7_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
static inline void sys_irq_disable(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_ZYNQ7_SYS__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/zynqmp_a53/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/compiler.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include "xil_cache.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "xreg_cortexa53.h"
#include "xscugic.h"
#define MB (1024 * 1024UL)
#define GB (1024 * 1024 * 1024UL)
void sys_irq_restore_enable(unsigned int flags)
{
Xil_ExceptionEnableMask(~flags);
}
unsigned int sys_irq_save_disable(void)
{
unsigned int state = mfcpsr() & XIL_EXCEPTION_ALL;
if (XIL_EXCEPTION_ALL != state) {
Xil_ExceptionDisableMask(XIL_EXCEPTION_ALL);
}
return state;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheFlush();
else
Xil_DCacheFlushRange((intptr_t)addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheInvalidate();
else
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("wfi");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
unsigned long section_offset;
unsigned long ttb_addr;
#if defined (__aarch64__)
unsigned long ttb_size = (pa < 4*GB) ? 2*MB : 1*GB;
#else
unsigned long ttb_size = 1*MB;
#endif
if (!flags)
return va;
/* Ensure alignement on a section boundary */
pa &= ~(ttb_size-1UL);
/* Loop through entire region of memory (one MMU section at a time).
Each section requires a TTB entry. */
for (section_offset = 0; section_offset < size; ) {
/* Calculate translation table entry for this memory section */
ttb_addr = (pa + section_offset);
/* Write translation table entry value to entry address */
Xil_SetTlbAttributes(ttb_addr, flags);
#if defined (__aarch64__)
/* recalculate if we started below 4GB and going above in 64bit mode */
if ( ttb_addr >= 4*GB ) {
ttb_size = 1*GB;
}
#endif
section_offset += ttb_size;
}
return va;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/zynqmp_a53/sys.h
* @brief freertos zynqmp_a53 system primitives for libmetal.
*/
#ifndef __METAL_FREERTOS_SYS__H__
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#include "xscugic.h"
#ifndef __METAL_FREERTOS_ZYNQMP_A53_SYS__H__
#define __METAL_FREERTOS_ZYNQMP_A53_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
static inline void sys_irq_disable(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_ZYNQMP_A53_SYS__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/zynqmp_r5/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/compiler.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include "xil_cache.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "xil_mpu.h"
#include "xreg_cortexr5.h"
#include "xscugic.h"
#define MPU_REGION_SIZE_MIN 0x20
void sys_irq_restore_enable(unsigned int flags)
{
Xil_ExceptionEnableMask(~flags);
}
unsigned int sys_irq_save_disable(void)
{
unsigned int state = mfcpsr() & XIL_EXCEPTION_ALL;
if (XIL_EXCEPTION_ALL != state) {
Xil_ExceptionDisableMask(XIL_EXCEPTION_ALL);
}
return state;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheFlush();
else
Xil_DCacheFlushRange((intptr_t)addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheInvalidate();
else
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("wfi");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
size_t rsize = MPU_REGION_SIZE_MIN;
metal_phys_addr_t base_pa;
if (!flags)
return va;
while(1) {
if (rsize < size) {
rsize <<= 1;
continue;
} else {
base_pa = pa & ~(rsize - 1);
if ((base_pa + rsize) < (pa + size)) {
rsize <<= 1;
continue;
}
break;
}
}
Xil_SetMPURegion(base_pa, rsize, flags);
return va;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/zynqmp_r5/sys.h
* @brief freertos zynqmp_r5 system primitives for libmetal.
*/
#ifndef __METAL_FREERTOS_SYS__H__
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#include "xscugic.h"
#ifndef __METAL_FREERTOS_ZYNQMP_R5_SYS__H__
#define __METAL_FREERTOS_ZYNQMP_R5_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
static inline void sys_irq_disable(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_ZYNQMP_R5_SYS__H__ */

View File

@@ -0,0 +1,24 @@
collect (PROJECT_LIB_HEADERS alloc.h)
collect (PROJECT_LIB_HEADERS assert.h)
collect (PROJECT_LIB_HEADERS cache.h)
collect (PROJECT_LIB_HEADERS condition.h)
collect (PROJECT_LIB_HEADERS io.h)
collect (PROJECT_LIB_HEADERS irq.h)
collect (PROJECT_LIB_HEADERS log.h)
collect (PROJECT_LIB_HEADERS mutex.h)
collect (PROJECT_LIB_HEADERS sleep.h)
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES condition.c)
collect (PROJECT_LIB_SOURCES device.c)
collect (PROJECT_LIB_SOURCES init.c)
collect (PROJECT_LIB_SOURCES io.c)
collect (PROJECT_LIB_SOURCES irq.c)
collect (PROJECT_LIB_SOURCES shmem.c)
collect (PROJECT_LIB_SOURCES time.c)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/alloc.c
* @brief generic libmetal memory allocattion definitions.
*/
#ifndef __METAL_ALLOC__H__
#error "Include metal/alloc.h instead of metal/generic/alloc.h"
#endif
#ifndef __METAL_GENERIC_ALLOC__H__
#define __METAL_GENERIC_ALLOC__H__
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void *metal_allocate_memory(unsigned int size)
{
return (malloc(size));
}
static inline void metal_free_memory(void *ptr)
{
free(ptr);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_ALLOC__H__ */

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file assert.h
* @brief Generic assertion support.
*/
#ifndef __METAL_ASSERT__H__
#error "Include metal/assert.h instead of metal/generic/assert.h"
#endif
#ifndef __METAL_GENERIC_ASSERT__H__
#define __METAL_GENERIC_ASSERT__H__
#include <assert.h>
/**
* @brief Assertion macro for bare-metal applications.
* @param cond Condition to evaluate.
*/
#define metal_sys_assert(cond) assert(cond)
#endif /* __METAL_GENERIC_ASSERT__H__ */

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/cache.h
* @brief generic cache operation primitives for libmetal.
*/
#ifndef __METAL_CACHE__H__
#error "Include metal/cache.h instead of metal/generic/cache.h"
#endif
#ifndef __METAL_GENERIC_CACHE__H__
#define __METAL_GENERIC_CACHE__H__
#ifdef __cplusplus
extern "C" {
#endif
extern void metal_machine_cache_flush(void *addr, unsigned int len);
extern void metal_machine_cache_invalidate(void *addr, unsigned int len);
static inline void __metal_cache_flush(void *addr, unsigned int len)
{
metal_machine_cache_flush(addr, len);
}
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
{
metal_machine_cache_invalidate(addr, len);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_CACHE__H__ */

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/condition.c
* @brief Generic libmetal condition variable handling.
*/
#include <metal/condition.h>
#include <metal/irq.h>
extern void metal_generic_default_poll(void);
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
metal_mutex_t *tmpm = 0;
int v;
unsigned int flags;
/* Check if the mutex has been acquired */
if (!cv || !m || !metal_mutex_is_acquired(m))
return -EINVAL;
if (!atomic_compare_exchange_strong(&cv->m->v, &tmpm->v, m->v)) {
if (m != tmpm)
return -EINVAL;
}
v = atomic_load(&cv->v);
/* Release the mutex first. */
metal_mutex_release(m);
do {
flags = metal_irq_save_disable();
if (atomic_load(&cv->v) != v) {
metal_irq_restore_enable(flags);
break;
}
metal_generic_default_poll();
metal_irq_restore_enable(flags);
} while(1);
/* Acquire the mutex again. */
metal_mutex_acquire(m);
return 0;
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/condition.h
* @brief Generic condition variable primitives for libmetal.
*/
#ifndef __METAL_CONDITION__H__
#error "Include metal/condition.h instead of metal/generic/condition.h"
#endif
#ifndef __METAL_GENERIC_CONDITION__H__
#define __METAL_GENERIC_CONDITION__H__
#include <unistd.h>
#include <metal/atomic.h>
#include <stdint.h>
#include <limits.h>
#include <metal/errno.h>
#ifdef __cplusplus
extern "C" {
#endif
struct metal_condition {
metal_mutex_t *m; /**< mutex.
The condition variable is attached to
this mutex when it is waiting.
It is also used to check correctness
in case there are multiple waiters. */
atomic_int v; /**< condition variable value. */
};
/** Static metal condition variable initialization. */
#define METAL_CONDITION_INIT { NULL, ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
cv->m = NULL;
atomic_init(&cv->v, 0);
}
static inline int metal_condition_signal(struct metal_condition *cv)
{
if (!cv)
return -EINVAL;
/** wake up waiters if there are any. */
atomic_fetch_add(&cv->v, 1);
return 0;
}
static inline int metal_condition_broadcast(struct metal_condition *cv)
{
return metal_condition_signal(cv);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_CONDITION__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Xilinx nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* @file generic/sys.c
* @brief machine specific system primitives implementation.
*/
#include "metal/io.h"
#include "metal/sys.h"
void sys_irq_restore_enable(unsigned int flags)
{
}
unsigned int sys_irq_save_disable(void)
{
return 0;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
(void)addr;
(void)len;
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
(void)addr;
(void)len;
}
/**
* @brief poll function until some event happens
*/
void __attribute__((weak)) metal_generic_default_poll(void)
{
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
(void)va;
(void)pa;
(void)size;
(void)flags;
return va;
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Xilinx nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* @file generic/mp1_m4/sys.h
* @brief generic mp1_m4 system primitives for libmetal.
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#ifndef __METAL_GENERIC_MP1_M4_SYS__H__
#define __METAL_GENERIC_MP1_M4_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(MAX_IRQS)
#define MAX_IRQS 8 /**< maximum number of irqs */
#endif
static inline void sys_irq_enable(unsigned int vector)
{
(void)vector;
}
static inline void sys_irq_disable(unsigned int vector)
{
(void)vector;
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_MP1_M4_SYS__H__ */

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/device.c
* @brief Generic libmetal device operations.
*/
#include <metal/device.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <metal/utilities.h>
int metal_generic_dev_sys_open(struct metal_device *dev)
{
struct metal_io_region *io;
unsigned i;
/* map I/O memory regions */
for (i = 0; i < dev->num_regions; i++) {
io = &dev->regions[i];
if (!io->size)
break;
metal_sys_io_mem_map(io);
}
return 0;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/init.c
* @brief Generic libmetal initialization.
*/
#include <metal/sys.h>
#include <metal/utilities.h>
#include <metal/device.h>
struct metal_state _metal;
int metal_sys_init(const struct metal_init_params *params)
{
metal_unused(params);
metal_bus_register(&metal_generic_bus);
return 0;
}
void metal_sys_finish(void)
{
metal_bus_unregister(&metal_generic_bus);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/io.c
* @brief Generic libmetal io operations
*/
#include <metal/io.h>
void metal_sys_io_mem_map(struct metal_io_region *io)
{
unsigned long p;
size_t psize;
size_t *va;
va = (size_t *)io->virt;
psize = io->size;
if (psize) {
if (psize >> io->page_shift)
psize = (size_t)1 << io->page_shift;
for (p = 0; p <= (io->size >> io->page_shift); p++) {
metal_machine_io_mem_map(va, io->physmap[p],
psize, io->mem_flags);
va += psize;
}
}
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/shmem.c
* @brief Generic libmetal shared memory handling.
*/
#include <metal/shmem.h>
int metal_shmem_open(const char *name, size_t size,
struct metal_io_region **io)
{
return metal_shmem_open_generic(name, size, io);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/io.h
* @brief Generic specific io definitions.
*/
#ifndef __METAL_IO__H__
#error "Include metal/io.h instead of metal/generic/io.h"
#endif
#ifndef __METAL_GENERIC_IO__H__
#define __METAL_GENERIC_IO__H__
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
/**
* @brief memory mapping for an I/O region
*/
void metal_sys_io_mem_map(struct metal_io_region *io);
/**
* @brief memory mapping
*/
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags);
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_IO__H__ */

View File

@@ -0,0 +1,279 @@
/*
* Copyright (c) 2016 - 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/irq.c
* @brief generic libmetal irq definitions.
*/
#include <metal/errno.h>
#include <metal/irq.h>
#include <metal/sys.h>
#include <metal/log.h>
#include <metal/mutex.h>
#include <metal/list.h>
#include <metal/utilities.h>
#include <metal/alloc.h>
/** IRQ handlers descriptor structure */
struct metal_irq_hddesc {
metal_irq_handler hd; /**< irq handler */
void *drv_id; /**< id to identify the driver
of the irq handler */
struct metal_device *dev; /**< device identifier */
struct metal_list node; /**< node on irq handlers list */
};
/** IRQ descriptor structure */
struct metal_irq_desc {
int irq; /**< interrupt number */
struct metal_list hdls; /**< interrupt handlers */
struct metal_list node; /**< node on irqs list */
};
/** IRQ state structure */
struct metal_irqs_state {
struct metal_list irqs; /**< interrupt descriptors */
metal_mutex_t irq_lock; /**< access lock */
};
static struct metal_irqs_state _irqs = {
.irqs = METAL_INIT_LIST(_irqs.irqs),
.irq_lock = METAL_MUTEX_INIT(_irqs.irq_lock),
};
int metal_irq_register(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
struct metal_irq_desc *irq_p = NULL;
struct metal_irq_hddesc *hdl_p;
struct metal_list *node;
unsigned int irq_flags_save;
if (irq < 0) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d need to be a positive number\n",
__func__, irq);
return -EINVAL;
}
if ((drv_id == NULL) || (hd == NULL)) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n",
__func__, irq);
return -EINVAL;
}
/* Search for irq in list */
metal_mutex_acquire(&_irqs.irq_lock);
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if (irq_p->irq == irq) {
struct metal_list *h_node;
/* Check if drv_id already exist */
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
/* if drv_id already exist reject */
if ((hdl_p->drv_id == drv_id) &&
((dev == NULL) || (hdl_p->dev == dev))) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d already registered."
"Will not register again.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -EINVAL;
}
}
/* irq found and drv_id not used, get out of metal_list_for_each */
break;
}
}
/* Either need to add handler to an existing list or to a new one */
hdl_p = metal_allocate_memory(sizeof(struct metal_irq_hddesc));
if (hdl_p == NULL) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d cannot allocate mem for drv_id %d.\n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
hdl_p->hd = hd;
hdl_p->drv_id = drv_id;
hdl_p->dev = dev;
/* interrupt already registered, add handler to existing list*/
if ((irq_p != NULL) && (irq_p->irq == irq)) {
irq_flags_save = metal_irq_save_disable();
metal_list_add_tail(&irq_p->hdls, &hdl_p->node);
metal_irq_restore_enable(irq_flags_save);
metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
/* interrupt was not already registered, add */
irq_p = metal_allocate_memory(sizeof(struct metal_irq_desc));
if (irq_p == NULL) {
metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
irq_p->irq = irq;
metal_list_init(&irq_p->hdls);
metal_list_add_tail(&irq_p->hdls, &hdl_p->node);
irq_flags_save = metal_irq_save_disable();
metal_list_add_tail(&_irqs.irqs, &irq_p->node);
metal_irq_restore_enable(irq_flags_save);
metal_log(METAL_LOG_DEBUG, "%s: success, added irq %d\n", __func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
/* helper function for metal_irq_unregister() */
static void metal_irq_delete_node(struct metal_list *node, void *p_to_free)
{
unsigned int irq_flags_save;
irq_flags_save=metal_irq_save_disable();
metal_list_del(node);
metal_irq_restore_enable(irq_flags_save);
metal_free_memory(p_to_free);
}
int metal_irq_unregister(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
struct metal_irq_desc *irq_p;
struct metal_list *node;
if (irq < 0) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need to be a positive number\n",
__func__, irq);
return -EINVAL;
}
/* Search for irq in list */
metal_mutex_acquire(&_irqs.irq_lock);
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if (irq_p->irq == irq) {
struct metal_list *h_node, *h_prenode;
struct metal_irq_hddesc *hdl_p;
unsigned int delete_count = 0;
metal_log(METAL_LOG_DEBUG, "%s: found irq %d\n",
__func__, irq);
/* Search through handlers */
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
if (((hd == NULL) || (hdl_p->hd == hd)) &&
((drv_id == NULL) || (hdl_p->drv_id == drv_id)) &&
((dev == NULL) || (hdl_p->dev == dev))) {
metal_log(METAL_LOG_DEBUG,
"%s: unregister hd=%p drv_id=%p dev=%p\n",
__func__, hdl_p->hd, hdl_p->drv_id, hdl_p->dev);
h_prenode = h_node->prev;
metal_irq_delete_node(h_node, hdl_p);
h_node = h_prenode;
delete_count++;
}
}
/* we did not find any handler to delete */
if (!delete_count) {
metal_log(METAL_LOG_DEBUG, "%s: No matching entry\n",
__func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
}
/* if interrupt handlers list is empty, unregister interrupt */
if (metal_list_is_empty(&irq_p->hdls)) {
metal_log(METAL_LOG_DEBUG,
"%s: handlers list empty, unregister interrupt\n",
__func__);
metal_irq_delete_node(node, irq_p);
}
metal_log(METAL_LOG_DEBUG, "%s: success\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
}
metal_log(METAL_LOG_DEBUG, "%s: No matching IRQ entry\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
}
unsigned int metal_irq_save_disable(void)
{
return sys_irq_save_disable();
}
void metal_irq_restore_enable(unsigned int flags)
{
sys_irq_restore_enable(flags);
}
void metal_irq_enable(unsigned int vector)
{
sys_irq_enable(vector);
}
void metal_irq_disable(unsigned int vector)
{
sys_irq_disable(vector);
}
/**
* @brief default handler
*/
void metal_irq_isr(unsigned int vector)
{
struct metal_list *node;
struct metal_irq_desc *irq_p;
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if ((unsigned int)irq_p->irq == vector) {
struct metal_list *h_node;
struct metal_irq_hddesc *hdl_p;
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
(hdl_p->hd)(vector, hdl_p->drv_id);
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/irq.c
* @brief Generic libmetal irq definitions.
*/
#ifndef __METAL_IRQ__H__
#error "Include metal/irq.h instead of metal/generic/irq.h"
#endif
#ifndef __METAL_GENERIC_IRQ__H__
#define __METAL_GENERIC_IRQ__H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief default interrupt handler
* @param[in] vector interrupt vector
*/
void metal_irq_isr(unsigned int vector);
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_IRQ__H__ */

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Linaro nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* @file generic/log.h
* @brief Generic libmetal log handler definition.
*/
#ifndef __METAL_METAL_LOG__H__
#error "Include metal/log.h instead of metal/generic/log.h"
#endif
#ifndef __METAL_GENERIC_LOG__H__
#define __METAL_GENERIC_LOG__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_LOG__H__ */

View File

@@ -0,0 +1,10 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
check_include_files(xintc.h HAS_XINTC)
if (HAS_XINTC)
add_definitions(-DHAS_XINTC)
endif(HAS_XINTC)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/microblaze_generic/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/assert.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include <xil_cache.h>
#include <xil_exception.h>
#ifdef HAS_XINTC
#include <xintc.h>
#include <xparameters.h>
#endif /* HAS_XINTC */
#define MSR_IE 0x2UL /* MicroBlaze status register interrupt enable mask */
#if (XPAR_MICROBLAZE_USE_MSR_INSTR != 0)
unsigned int sys_irq_save_disable(void)
{
unsigned int state;
asm volatile(" mfs %0, rmsr \n"
" msrclr r0, %1 \n"
: "=r"(state)
: "i"(MSR_IE)
: "memory");
return state &= MSR_IE;
}
#else /* XPAR_MICROBLAZE_USE_MSR_INSTR == 0 */
unsigned int sys_irq_save_disable(void)
{
unsigned int tmp, state;
asm volatile (" mfs %0, rmsr \n"
" andi %1, %0, %2 \n"
" mts rmsr, %1 \n"
: "=r"(state), "=r"(tmp)
: "i"(~MSR_IE)
: "memory");
return state &= MSR_IE;
}
#endif /* XPAR_MICROBLAZE_USE_MSR_INSTR */
void sys_irq_restore_enable(unsigned int flags)
{
unsigned int tmp;
asm volatile(" mfs %0, rmsr \n"
" or %0, %0, %1 \n"
" mts rmsr, %0 \n"
: "=r"(tmp)
: "r"(~flags)
: "memory");
}
static void sys_irq_change(unsigned int vector, int is_enable)
{
#ifdef HAS_XINTC
XIntc_Config *cfgptr;
unsigned int ier;
unsigned int mask;
mask = 1 >> ((vector%32)-1); /* set bit corresponding to interrupt */
mask = is_enable ? mask : ~mask; /* if disable then turn off bit */
cfgptr = XIntc_LookupConfig(vector/32);
Xil_AssertVoid(cfgptr != NULL);
Xil_AssertVoid(vector < XPAR_INTC_MAX_NUM_INTR_INPUTS);
ier = XIntc_In32(cfgptr->BaseAddress + XIN_IER_OFFSET);
XIntc_Out32(cfgptr->BaseAddress + XIN_IER_OFFSET,
(ier | mask));
#else
(void)vector;
(void)is_enable;
metal_assert(0);
#endif
}
void metal_weak sys_irq_enable(unsigned int vector)
{
sys_irq_change(vector, 1);
}
void metal_weak sys_irq_disable(unsigned int vector)
{
sys_irq_change(vector, 0);
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len){
Xil_DCacheFlush();
}
else{
Xil_DCacheFlushRange((intptr_t)addr, len);
}
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len){
Xil_DCacheInvalidate();
}
else {
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
}
/**
* @brief make microblaze wait
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("nop");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
(void)pa;
(void)size;
(void)flags;
return va;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/microblaze_generic/sys.h
* @brief generic microblaze system primitives for libmetal.
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#ifndef __METAL_GENERIC_MICROBLAZE_SYS__H__
#define __METAL_GENERIC_MICROBLAZE_SYS__H__
#include <metal/compiler.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
void metal_weak sys_irq_enable(unsigned int vector);
void metal_weak sys_irq_disable(unsigned int vector);
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_MICROBLAZE_SYS__H__ */

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/mutex.h
* @brief Generic mutex primitives for libmetal.
*/
#ifndef __METAL_MUTEX__H__
#error "Include metal/mutex.h instead of metal/generic/mutex.h"
#endif
#ifndef __METAL_GENERIC_MUTEX__H__
#define __METAL_GENERIC_MUTEX__H__
#include <metal/atomic.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
union{
atomic_int v;
atomic_flag w;
};
} metal_mutex_t;
/*
* METAL_MUTEX_INIT - used for initializing an mutex elmenet in a static struct
* or global
*/
#define METAL_MUTEX_INIT(m) { ATOMIC_VAR_INIT(0) }
/*
* METAL_MUTEX_DEFINE - used for defining and initializing a global or
* static singleton mutex
*/
#define METAL_MUTEX_DEFINE(m) metal_mutex_t m = METAL_MUTEX_INIT(m)
static inline void __metal_mutex_init(metal_mutex_t *mutex)
{
atomic_store(&mutex->v, 0);
}
static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
{
(void)mutex;
}
static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
{
return 1 - atomic_flag_test_and_set(&mutex->w);
}
static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
{
while (atomic_flag_test_and_set(&mutex->w)) {
;
}
}
static inline void __metal_mutex_release(metal_mutex_t *mutex)
{
atomic_flag_clear(&mutex->w);
}
static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
{
return atomic_load(&mutex->v);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_MUTEX__H__ */

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/sleep.h
* @brief Generic sleep primitives for libmetal.
*/
#ifndef __METAL_SLEEP__H__
#error "Include metal/sleep.h instead of metal/generic/sleep.h"
#endif
#ifndef __METAL_GENERIC_SLEEP__H__
#define __METAL_GENERIC_SLEEP__H__
#include <metal/utilities.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline int __metal_sleep_usec(unsigned int usec)
{
metal_unused(usec);
/* Fix me */
return 0;
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_SLEEP__H__ */

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/sys.h
* @brief Generic system primitives for libmetal.
*/
#ifndef __METAL_SYS__H__
#error "Include metal/sys.h instead of metal/generic/sys.h"
#endif
#ifndef __METAL_GENERIC_SYS__H__
#define __METAL_GENERIC_SYS__H__
#include <metal/errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "./@PROJECT_MACHINE@/sys.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef METAL_MAX_DEVICE_REGIONS
#define METAL_MAX_DEVICE_REGIONS 1
#endif
/** Structure of generic libmetal runtime state. */
struct metal_state {
/** Common (system independent) data. */
struct metal_common_state common;
};
#ifdef METAL_INTERNAL
/**
* @brief restore interrupts to state before disable_global_interrupt()
*/
void sys_irq_restore_enable(unsigned int flags);
/**
* @brief disable all interrupts
*/
unsigned int sys_irq_save_disable(void);
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_SYS__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/template/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/io.h>
#include <metal/sys.h>
#include <metal/utilities.h>
#include <stdint.h>
void sys_irq_restore_enable(unsigned int flags)
{
metal_unused(flags);
/* Add implementation here */
}
unsigned int sys_irq_save_disable(void)
{
return 0;
/* Add implementation here */
}
void sys_irq_enable(unsigned int vector)
{
metal_unused(vector);
/* Add implementation here */
}
void sys_irq_disable(unsigned int vector)
{
metal_unused(vector);
/* Add implementation here */
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
metal_unused(addr);
metal_unused(len);
/* Add implementation here */
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
metal_unused(addr);
metal_unused(len);
/* Add implementation here */
}
void metal_generic_default_poll(void)
{
/* Add implementation here */
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
metal_unused(pa);
metal_unused(size);
metal_unused(flags);
/* Add implementation here */
return va;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, Linaro Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/template/sys.h
* @brief generic template system primitives for libmetal.
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#ifndef __METAL_GENERIC_TEMPLATE_SYS__H__
#define __METAL_GENERIC_TEMPLATE_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
void sys_irq_enable(unsigned int vector);
void sys_irq_disable(unsigned int vector);
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_TEMPLATE_SYS__H__ */

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/time.c
* @brief Generic libmetal time handling.
*/
#include <metal/time.h>
unsigned long long metal_get_timestamp(void)
{
/* TODO: Implement timestamp for generic system */
return 0;
}

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2014, Mentor Graphics Corporation
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/zynq7/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/compiler.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include "xil_cache.h"
#include "xil_mmu.h"
#include "xil_exception.h"
#include "xscugic.h"
/* Each TTB descriptor covers a 1MB region */
#define ARM_AR_MEM_TTB_SECT_SIZE 1024*1024
/* Mask off lower bits of addr */
#define ARM_AR_MEM_TTB_SECT_SIZE_MASK (~(ARM_AR_MEM_TTB_SECT_SIZE-1UL))
void sys_irq_restore_enable(unsigned int flags)
{
Xil_ExceptionEnableMask(~flags);
}
unsigned int sys_irq_save_disable(void)
{
unsigned int state = mfcpsr() & XIL_EXCEPTION_ALL;
if (XIL_EXCEPTION_ALL != state) {
Xil_ExceptionDisableMask(XIL_EXCEPTION_ALL);
}
return state;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheFlush();
else
Xil_DCacheFlushRange((intptr_t)addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheInvalidate();
else
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("wfi");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
unsigned int section_offset;
unsigned int ttb_addr;
if (!flags)
return va;
/* Ensure the virtual and physical addresses are aligned on a
section boundary */
pa &= ARM_AR_MEM_TTB_SECT_SIZE_MASK;
/* Loop through entire region of memory (one MMU section at a time).
Each section requires a TTB entry. */
for (section_offset = 0; section_offset < size;
section_offset += ARM_AR_MEM_TTB_SECT_SIZE) {
/* Calculate translation table entry for this memory section */
ttb_addr = (pa + section_offset);
/* Write translation table entry value to entry address */
Xil_SetTlbAttributes(ttb_addr, flags);
}
return va;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/zynq7/sys.h
* @brief generic zynq7 system primitives for libmetal.
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include "xscugic.h"
#ifndef __METAL_GENERIC_ZYNQ7_SYS__H__
#define __METAL_GENERIC_ZYNQ7_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
static inline void sys_irq_disable(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_ZYNQ7_SYS__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/zynqmp_a53/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/compiler.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include "xil_cache.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "xreg_cortexa53.h"
#include "xscugic.h"
#define MB (1024 * 1024UL)
#define GB (1024 * 1024 * 1024UL)
void sys_irq_restore_enable(unsigned int flags)
{
Xil_ExceptionEnableMask(~flags);
}
unsigned int sys_irq_save_disable(void)
{
unsigned int state = mfcpsr() & XIL_EXCEPTION_ALL;
if (XIL_EXCEPTION_ALL != state) {
Xil_ExceptionDisableMask(XIL_EXCEPTION_ALL);
}
return state;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheFlush();
else
Xil_DCacheFlushRange((intptr_t)addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheInvalidate();
else
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("wfi");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
unsigned long section_offset;
unsigned long ttb_addr;
#if defined (__aarch64__)
unsigned long ttb_size = (pa < 4*GB) ? 2*MB : 1*GB;
#else
unsigned long ttb_size = 1*MB;
#endif
if (!flags)
return va;
/* Ensure alignement on a section boundary */
pa &= ~(ttb_size-1UL);
/* Loop through entire region of memory (one MMU section at a time).
Each section requires a TTB entry. */
for (section_offset = 0; section_offset < size; ) {
/* Calculate translation table entry for this memory section */
ttb_addr = (pa + section_offset);
/* Write translation table entry value to entry address */
Xil_SetTlbAttributes(ttb_addr, flags);
#if defined (__aarch64__)
/* recalculate if we started below 4GB and going above in 64bit mode */
if ( ttb_addr >= 4*GB ) {
ttb_size = 1*GB;
}
#endif
section_offset += ttb_size;
}
return va;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/zynqmp_a53/sys.h
* @brief generic zynqmp_a53 system primitives for libmetal.
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include "xscugic.h"
#ifndef __METAL_GENERIC_ZYNQMP_A53_SYS__H__
#define __METAL_GENERIC_ZYNQMP_A53_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
static inline void sys_irq_disable(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_ZYNQMP_A53_SYS__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/zynqmp_r5/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/compiler.h>
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
#include "xil_cache.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#include "xil_mpu.h"
#include "xreg_cortexr5.h"
#include "xscugic.h"
#define MPU_REGION_SIZE_MIN 0x20
void sys_irq_restore_enable(unsigned int flags)
{
Xil_ExceptionEnableMask(~flags);
}
unsigned int sys_irq_save_disable(void)
{
unsigned int state = mfcpsr() & XIL_EXCEPTION_ALL;
if (XIL_EXCEPTION_ALL != state) {
Xil_ExceptionDisableMask(XIL_EXCEPTION_ALL);
}
return state;
}
void metal_machine_cache_flush(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheFlush();
else
Xil_DCacheFlushRange((intptr_t)addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len)
{
if (!addr && !len)
Xil_DCacheInvalidate();
else
Xil_DCacheInvalidateRange((intptr_t)addr, len);
}
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
asm volatile("wfi");
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
size_t rsize = MPU_REGION_SIZE_MIN;
metal_phys_addr_t base_pa;
if (!flags)
return va;
while(1) {
if (rsize < size) {
rsize <<= 1;
continue;
} else {
base_pa = pa & ~(rsize - 1);
if ((base_pa + rsize) < (pa + size)) {
rsize <<= 1;
continue;
}
break;
}
}
Xil_SetMPURegion(base_pa, rsize, flags);
return va;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/zynqmp_r5/sys.h
* @brief generic zynqmp_r5 system primitives for libmetal.
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include "xscugic.h"
#ifndef __METAL_GENERIC_ZYNQMP_R5_SYS__H__
#define __METAL_GENERIC_ZYNQMP_R5_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
static inline void sys_irq_disable(unsigned int vector)
{
XScuGic_DisableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);
}
#endif /* METAL_INTERNAL */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_ZYNQMP_R5_SYS__H__ */

View File

@@ -0,0 +1,20 @@
collect (PROJECT_LIB_HEADERS alloc.h)
collect (PROJECT_LIB_HEADERS assert.h)
collect (PROJECT_LIB_HEADERS cache.h)
collect (PROJECT_LIB_HEADERS condition.h)
collect (PROJECT_LIB_HEADERS io.h)
collect (PROJECT_LIB_HEADERS irq.h)
collect (PROJECT_LIB_HEADERS log.h)
collect (PROJECT_LIB_HEADERS mutex.h)
collect (PROJECT_LIB_HEADERS sleep.h)
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES condition.c)
collect (PROJECT_LIB_SOURCES device.c)
collect (PROJECT_LIB_SOURCES init.c)
collect (PROJECT_LIB_SOURCES irq.c)
collect (PROJECT_LIB_SOURCES shmem.c)
collect (PROJECT_LIB_SOURCES time.c)
collect (PROJECT_LIB_SOURCES utilities.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/alloc.h
* @brief linux memory allocattion definitions.
*/
#ifndef __METAL_ALLOC__H__
#error "Include metal/alloc.h instead of metal/linux/alloc.h"
#endif
#ifndef __METAL_LINUX_ALLOC__H__
#define __METAL_LINUX_ALLOC__H__
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void *metal_allocate_memory(unsigned int size)
{
return (malloc(size));
}
static inline void metal_free_memory(void *ptr)
{
free(ptr);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_ALLOC__H__ */

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file assert.h
* @brief Linux assertion support.
*/
#ifndef __METAL_ASSERT__H__
#error "Include metal/assert.h instead of metal/linux/assert.h"
#endif
#ifndef __METAL_LINUX_ASSERT__H__
#define __METAL_LINUX_ASSERT__H__
#include <assert.h>
/**
* @brief Assertion macro for Linux-based applications.
* @param cond Condition to evaluate.
*/
#define metal_sys_assert(cond) assert(cond)
#endif /* __METAL_LINUX_ASSERT__H__ */

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/cache.h
* @brief Linux cache operation primitives for libmetal.
*/
#ifndef __METAL_CACHE__H__
#error "Include metal/cache.h instead of metal/linux/cache.h"
#endif
#ifndef __METAL_LINUX_CACHE__H__
#define __METAL_LINUX_CACHE__H__
#include <metal/utilities.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void __metal_cache_flush(void *addr, unsigned int len)
{
/** Do nothing.
* Do not flush cache from Linux userspace.
*/
metal_unused(addr);
metal_unused(len);
return;
}
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
{
/** Do nothing.
* Do not flush cache from Linux userspace.
*/
metal_unused(addr);
metal_unused(len);
return;
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_CACHE__H__ */

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/condition.c
* @brief Generic libmetal condition variable handling.
*/
#include <metal/condition.h>
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
metal_mutex_t *tmpm = 0;
int v = 0;
/* Check if the mutex has been acquired */
if (!cv || !m || !metal_mutex_is_acquired(m))
return -EINVAL;
if (!atomic_compare_exchange_strong(&cv->m, &tmpm, m)) {
if (m != tmpm)
return -EINVAL;
}
v = atomic_load(&cv->wakeups);
atomic_fetch_add(&cv->waiters, 1);
/* Release the mutex before sleeping. */
metal_mutex_release(m);
syscall(SYS_futex, &cv->wakeups, FUTEX_WAIT, v, NULL, NULL, 0);
atomic_fetch_sub(&cv->waiters, 1);
/* Acquire the mutex after it's waken up. */
metal_mutex_acquire(m);
return 0;
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/condition.h
* @brief Linux condition variable primitives for libmetal.
*/
#ifndef __METAL_CONDITION__H__
#error "Include metal/condition.h instead of metal/linux/condition.h"
#endif
#ifndef __METAL_LINUX_CONDITION__H__
#define __METAL_LINUX_CONDITION__H__
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <metal/atomic.h>
#include <stdint.h>
#include <limits.h>
#include <metal/errno.h>
#ifdef __cplusplus
extern "C" {
#endif
struct metal_condition {
metal_mutex_t *m; /**< mutex.
The condition variable is attached to
this mutex when it is waiting.
It is also used to check correctness
in case there are multiple waiters. */
atomic_int waiters; /**< number of waiters. */
atomic_int wakeups; /**< number of wakeups. */
};
/** Static metal condition variable initialization. */
#define METAL_CONDITION_INIT { NULL, ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
cv->m = NULL;
atomic_init(&cv->waiters, 0);
atomic_init(&cv->wakeups, 0);
}
static inline int metal_condition_signal(struct metal_condition *cv)
{
if (!cv)
return -EINVAL;
atomic_fetch_add(&cv->wakeups, 1);
if (atomic_load(&cv->waiters) > 0)
syscall(SYS_futex, &cv->wakeups, FUTEX_WAKE, 1, NULL, NULL, 0);
return 0;
}
static inline int metal_condition_broadcast(struct metal_condition *cv)
{
if (!cv)
return -EINVAL;
atomic_fetch_add(&cv->wakeups, 1);
if (atomic_load(&cv->waiters) > 0)
syscall(SYS_futex, &cv->wakeups, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
return 0;
}
#endif /* __METAL_LINUX_CONDITION__H__ */

View File

@@ -0,0 +1,669 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/device.c
* @brief Linux libmetal device operations.
*/
#include <metal/device.h>
#include <metal/sys.h>
#include <metal/utilities.h>
#include <metal/irq.h>
#define MAX_DRIVERS 64
struct linux_bus;
struct linux_device;
struct linux_driver {
const char *drv_name;
const char *mod_name;
const char *cls_name;
struct sysfs_driver *sdrv;
int (*dev_open)(struct linux_bus *lbus,
struct linux_device *ldev);
void (*dev_close)(struct linux_bus *lbus,
struct linux_device *ldev);
void (*dev_irq_ack)(struct linux_bus *lbus,
struct linux_device *ldev,
int irq);
int (*dev_dma_map)(struct linux_bus *lbus,
struct linux_device *ldev,
uint32_t dir,
struct metal_sg *sg_in,
int nents_in,
struct metal_sg *sg_out);
void (*dev_dma_unmap)(struct linux_bus *lbus,
struct linux_device *ldev,
uint32_t dir,
struct metal_sg *sg,
int nents);
};
struct linux_bus {
struct metal_bus bus;
const char *bus_name;
struct linux_driver drivers[MAX_DRIVERS];
struct sysfs_bus *sbus;
};
struct linux_device {
struct metal_device device;
char dev_name[PATH_MAX];
char dev_path[PATH_MAX];
char cls_path[PATH_MAX];
metal_phys_addr_t region_phys[METAL_MAX_DEVICE_REGIONS];
struct linux_driver *ldrv;
struct sysfs_device *sdev;
struct sysfs_attribute *override;
int fd;
};
static struct linux_bus *to_linux_bus(struct metal_bus *bus)
{
return metal_container_of(bus, struct linux_bus, bus);
}
static struct linux_device *to_linux_device(struct metal_device *device)
{
return metal_container_of(device, struct linux_device, device);
}
static int metal_uio_read_map_attr(struct linux_device *ldev, unsigned index,
const char *name, unsigned long *value)
{
const char *cls = ldev->cls_path;
struct sysfs_attribute *attr;
char path[SYSFS_PATH_MAX];
int result;
result = snprintf(path, sizeof(path), "%s/maps/map%u/%s", cls, index, name);
if (result >= (int)sizeof(path))
return -EOVERFLOW;
attr = sysfs_open_attribute(path);
if (!attr || sysfs_read_attribute(attr) != 0)
return -errno;
*value = strtoul(attr->value, NULL, 0);
return 0;
}
static int metal_uio_dev_bind(struct linux_device *ldev,
struct linux_driver *ldrv)
{
struct sysfs_attribute *attr;
int result;
if (strcmp(ldev->sdev->driver_name, ldrv->drv_name) == 0)
return 0;
if (strcmp(ldev->sdev->driver_name, SYSFS_UNKNOWN) != 0) {
metal_log(METAL_LOG_INFO, "device %s in use by driver %s\n",
ldev->dev_name, ldev->sdev->driver_name);
return -EBUSY;
}
attr = sysfs_get_device_attr(ldev->sdev, "driver_override");
if (!attr) {
metal_log(METAL_LOG_ERROR, "device %s has no override\n",
ldev->dev_name);
return -errno;
}
result = sysfs_write_attribute(attr, ldrv->drv_name,
strlen(ldrv->drv_name));
if (result) {
metal_log(METAL_LOG_ERROR, "failed to set override on %s\n",
ldev->dev_name);
return -errno;
}
ldev->override = attr;
attr = sysfs_get_driver_attr(ldrv->sdrv, "bind");
if (!attr) {
metal_log(METAL_LOG_ERROR, "driver %s has no bind\n", ldrv->drv_name);
return -ENOTSUP;
}
result = sysfs_write_attribute(attr, ldev->dev_name,
strlen(ldev->dev_name));
if (result) {
metal_log(METAL_LOG_ERROR, "failed to bind %s to %s\n",
ldev->dev_name, ldrv->drv_name);
return -errno;
}
metal_log(METAL_LOG_DEBUG, "bound device %s to driver %s\n",
ldev->dev_name, ldrv->drv_name);
return 0;
}
static int metal_uio_dev_open(struct linux_bus *lbus, struct linux_device *ldev)
{
char *instance, path[SYSFS_PATH_MAX];
struct linux_driver *ldrv = ldev->ldrv;
unsigned long *phys, offset=0, size=0;
struct metal_io_region *io;
struct dlist *dlist;
int result, i;
void *virt;
int irq_info;
ldev->fd = -1;
ldev->sdev = sysfs_open_device(lbus->bus_name, ldev->dev_name);
if (!ldev->sdev) {
metal_log(METAL_LOG_ERROR, "device %s:%s not found\n",
lbus->bus_name, ldev->dev_name);
return -ENODEV;
}
metal_log(METAL_LOG_DEBUG, "opened sysfs device %s:%s\n",
lbus->bus_name, ldev->dev_name);
result = metal_uio_dev_bind(ldev, ldrv);
if (result)
return result;
result = snprintf(path, sizeof(path), "%s/uio", ldev->sdev->path);
if (result >= (int)sizeof(path))
return -EOVERFLOW;
dlist = sysfs_open_directory_list(path);
if (!dlist) {
metal_log(METAL_LOG_ERROR, "failed to scan class path %s\n",
path);
return -errno;
}
dlist_for_each_data(dlist, instance, char) {
result = snprintf(ldev->cls_path, sizeof(ldev->cls_path),
"%s/%s", path, instance);
if (result >= (int)sizeof(ldev->cls_path))
return -EOVERFLOW;
result = snprintf(ldev->dev_path, sizeof(ldev->dev_path),
"/dev/%s", instance);
if (result >= (int)sizeof(ldev->dev_path))
return -EOVERFLOW;
break;
}
sysfs_close_list(dlist);
if (sysfs_path_is_dir(ldev->cls_path) != 0) {
metal_log(METAL_LOG_ERROR, "invalid device class path %s\n",
ldev->cls_path);
return -ENODEV;
}
i = 0;
do {
if (!access(ldev->dev_path, F_OK))
break;
usleep(10);
i++;
} while (i < 1000);
if (i >= 1000) {
metal_log(METAL_LOG_ERROR, "failed to open file %s, timeout.\n",
ldev->dev_path);
return -ENODEV;
}
result = metal_open(ldev->dev_path, 0);
if (result < 0) {
metal_log(METAL_LOG_ERROR, "failed to open device %s\n",
ldev->dev_path, strerror(-result));
return result;
}
ldev->fd = result;
metal_log(METAL_LOG_DEBUG, "opened %s:%s as %s\n",
lbus->bus_name, ldev->dev_name, ldev->dev_path);
for (i = 0, result = 0; !result && i < METAL_MAX_DEVICE_REGIONS; i++) {
phys = &ldev->region_phys[ldev->device.num_regions];
result = (result ? result :
metal_uio_read_map_attr(ldev, i, "offset", &offset));
result = (result ? result :
metal_uio_read_map_attr(ldev, i, "addr", phys));
result = (result ? result :
metal_uio_read_map_attr(ldev, i, "size", &size));
result = (result ? result :
metal_map(ldev->fd, offset, size, 0, 0, &virt));
if (!result) {
io = &ldev->device.regions[ldev->device.num_regions];
metal_io_init(io, virt, phys, size, -1, 0, NULL);
ldev->device.num_regions++;
}
}
irq_info = 1;
if (write(ldev->fd, &irq_info, sizeof(irq_info)) <= 0) {
metal_log(METAL_LOG_INFO,
"%s: No IRQ for device %s.\n",
__func__, ldev->dev_name);
ldev->device.irq_num = 0;
ldev->device.irq_info = (void *)-1;
} else {
ldev->device.irq_num = 1;
ldev->device.irq_info = (void *)(intptr_t)ldev->fd;
}
return 0;
}
static void metal_uio_dev_close(struct linux_bus *lbus,
struct linux_device *ldev)
{
(void)lbus;
if ((intptr_t)ldev->device.irq_info >= 0)
/* Normally this call would not be needed, and is added as precaution.
Also for uio there is only 1 interrupt associated to the fd/device,
we therefore do not need to specify a particular device */
metal_irq_unregister(ldev->fd, NULL, NULL, NULL);
if (ldev->override) {
sysfs_write_attribute(ldev->override, "", 1);
ldev->override = NULL;
}
if (ldev->sdev) {
sysfs_close_device(ldev->sdev);
ldev->sdev = NULL;
}
if (ldev->fd >= 0) {
close(ldev->fd);
}
}
static void metal_uio_dev_irq_ack(struct linux_bus *lbus,
struct linux_device *ldev,
int irq)
{
(void)lbus;
(void)irq;
int irq_info = 1;
unsigned int val;
int ret;
ret = read(ldev->fd, (void *)&val, sizeof(val));
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s, read uio irq fd %d failed: %d.\n",
__func__, ldev->fd, ret);
return;
}
ret = write(ldev->fd, &irq_info, sizeof(irq_info));
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s, write uio irq fd %d failed: %d.\n",
__func__, ldev->fd, errno);
}
}
static int metal_uio_dev_dma_map(struct linux_bus *lbus,
struct linux_device *ldev,
uint32_t dir,
struct metal_sg *sg_in,
int nents_in,
struct metal_sg *sg_out)
{
int i, j;
void *vaddr_sg_lo, *vaddr_sg_hi, *vaddr_lo, *vaddr_hi;
struct metal_io_region *io;
(void)lbus;
(void)dir;
/* Check if the the input virt address is MMIO address */
for (i = 0; i < nents_in; i++) {
vaddr_sg_lo = sg_in[i].virt;
vaddr_sg_hi = vaddr_sg_lo + sg_in[i].len;
for (j = 0, io = ldev->device.regions;
j < (int)ldev->device.num_regions; j++, io++) {
vaddr_lo = io->virt;
vaddr_hi = vaddr_lo + io->size;
if (vaddr_sg_lo >= vaddr_lo &&
vaddr_sg_hi <= vaddr_hi) {
break;
}
}
if (j == (int)ldev->device.num_regions) {
metal_log(METAL_LOG_WARNING,
"%s,%s: input address isn't MMIO addr: 0x%x,%d.\n",
__func__, ldev->dev_name, vaddr_sg_lo, sg_in[i].len);
return -EINVAL;
}
}
if (sg_out != sg_in)
memcpy(sg_out, sg_in, nents_in*(sizeof(struct metal_sg)));
return nents_in;
}
static void metal_uio_dev_dma_unmap(struct linux_bus *lbus,
struct linux_device *ldev,
uint32_t dir,
struct metal_sg *sg,
int nents)
{
(void) lbus;
(void) ldev;
(void) dir;
(void) sg;
(void) nents;
return;
}
static struct linux_bus linux_bus[] = {
{
.bus_name = "platform",
.drivers = {
{
.drv_name = "uio_pdrv_genirq",
.mod_name = "uio_pdrv_genirq",
.cls_name = "uio",
.dev_open = metal_uio_dev_open,
.dev_close = metal_uio_dev_close,
.dev_irq_ack = metal_uio_dev_irq_ack,
.dev_dma_map = metal_uio_dev_dma_map,
.dev_dma_unmap = metal_uio_dev_dma_unmap,
},
{
.drv_name = "uio_dmem_genirq",
.mod_name = "uio_dmem_genirq",
.cls_name = "uio",
.dev_open = metal_uio_dev_open,
.dev_close = metal_uio_dev_close,
.dev_irq_ack = metal_uio_dev_irq_ack,
.dev_dma_map = metal_uio_dev_dma_map,
.dev_dma_unmap = metal_uio_dev_dma_unmap,
},
{ 0 /* sentinel */ }
}
},
{
.bus_name = "pci",
.drivers = {
{
.drv_name = "vfio-pci",
.mod_name = "vfio-pci",
},
{
.drv_name = "uio_pci_generic",
.mod_name = "uio_pci_generic",
.cls_name = "uio",
.dev_open = metal_uio_dev_open,
.dev_close = metal_uio_dev_close,
.dev_irq_ack = metal_uio_dev_irq_ack,
.dev_dma_map = metal_uio_dev_dma_map,
.dev_dma_unmap = metal_uio_dev_dma_unmap,
},
{ 0 /* sentinel */ }
}
},
{
/* sentinel */
.bus_name = NULL,
},
};
#define for_each_linux_bus(lbus) \
for ((lbus) = linux_bus; (lbus)->bus_name; (lbus)++)
#define for_each_linux_driver(lbus, ldrv) \
for ((ldrv) = lbus->drivers; (ldrv)->drv_name; (ldrv)++)
static int metal_linux_dev_open(struct metal_bus *bus,
const char *dev_name,
struct metal_device **device)
{
struct linux_bus *lbus = to_linux_bus(bus);
struct linux_device *ldev = NULL;
struct linux_driver *ldrv;
int error;
ldev = malloc(sizeof(*ldev));
if (!ldev)
return -ENOMEM;
for_each_linux_driver(lbus, ldrv) {
/* Check if we have a viable driver. */
if (!ldrv->sdrv || !ldrv->dev_open)
continue;
/* Allocate a linux device if we haven't already. */
if (!ldev)
ldev = malloc(sizeof(*ldev));
if (!ldev)
return -ENOMEM;
/* Reset device data. */
memset(ldev, 0, sizeof(*ldev));
strncpy(ldev->dev_name, dev_name, sizeof(ldev->dev_name) - 1);
ldev->fd = -1;
ldev->ldrv = ldrv;
ldev->device.bus = bus;
/* Try and open the device. */
error = ldrv->dev_open(lbus, ldev);
if (error) {
ldrv->dev_close(lbus, ldev);
continue;
}
*device = &ldev->device;
(*device)->name = ldev->dev_name;
metal_list_add_tail(&bus->devices, &(*device)->node);
return 0;
}
if (ldev)
free(ldev);
return -ENODEV;
}
static void metal_linux_dev_close(struct metal_bus *bus,
struct metal_device *device)
{
struct linux_device *ldev = to_linux_device(device);
struct linux_bus *lbus = to_linux_bus(bus);
ldev->ldrv->dev_close(lbus, ldev);
metal_list_del(&device->node);
free(ldev);
}
static void metal_linux_bus_close(struct metal_bus *bus)
{
struct linux_bus *lbus = to_linux_bus(bus);
struct linux_driver *ldrv;
for_each_linux_driver(lbus, ldrv) {
if (ldrv->sdrv)
sysfs_close_driver(ldrv->sdrv);
ldrv->sdrv = NULL;
}
sysfs_close_bus(lbus->sbus);
lbus->sbus = NULL;
}
static void metal_linux_dev_irq_ack(struct metal_bus *bus,
struct metal_device *device,
int irq)
{
struct linux_device *ldev = to_linux_device(device);
struct linux_bus *lbus = to_linux_bus(bus);
return ldev->ldrv->dev_irq_ack(lbus, ldev, irq);
}
static int metal_linux_dev_dma_map(struct metal_bus *bus,
struct metal_device *device,
uint32_t dir,
struct metal_sg *sg_in,
int nents_in,
struct metal_sg *sg_out)
{
struct linux_device *ldev = to_linux_device(device);
struct linux_bus *lbus = to_linux_bus(bus);
return ldev->ldrv->dev_dma_map(lbus, ldev, dir, sg_in,
nents_in, sg_out);
}
static void metal_linux_dev_dma_unmap(struct metal_bus *bus,
struct metal_device *device,
uint32_t dir,
struct metal_sg *sg,
int nents)
{
struct linux_device *ldev = to_linux_device(device);
struct linux_bus *lbus = to_linux_bus(bus);
ldev->ldrv->dev_dma_unmap(lbus, ldev, dir, sg,
nents);
}
static const struct metal_bus_ops metal_linux_bus_ops = {
.bus_close = metal_linux_bus_close,
.dev_open = metal_linux_dev_open,
.dev_close = metal_linux_dev_close,
.dev_irq_ack = metal_linux_dev_irq_ack,
.dev_dma_map = metal_linux_dev_dma_map,
.dev_dma_unmap = metal_linux_dev_dma_unmap,
};
static int metal_linux_register_bus(struct linux_bus *lbus)
{
lbus->bus.name = lbus->bus_name;
lbus->bus.ops = metal_linux_bus_ops;
return metal_bus_register(&lbus->bus);
}
static int metal_linux_probe_driver(struct linux_bus *lbus,
struct linux_driver *ldrv)
{
char command[256];
int ret;
ldrv->sdrv = sysfs_open_driver(lbus->bus_name, ldrv->drv_name);
/* Try probing the module and then open the driver. */
if (!ldrv->sdrv) {
ret = snprintf(command, sizeof(command),
"modprobe %s > /dev/null 2>&1", ldrv->mod_name);
if (ret >= (int)sizeof(command))
return -EOVERFLOW;
ret = system(command);
if (ret < 0) {
metal_log(METAL_LOG_WARNING,
"%s: executing system command '%s' failed.\n",
__func__, command);
}
ldrv->sdrv = sysfs_open_driver(lbus->bus_name, ldrv->drv_name);
}
/* Try sudo probing the module and then open the driver. */
if (!ldrv->sdrv) {
ret = snprintf(command, sizeof(command),
"sudo modprobe %s > /dev/null 2>&1", ldrv->mod_name);
if (ret >= (int)sizeof(command))
return -EOVERFLOW;
ret = system(command);
if (ret < 0) {
metal_log(METAL_LOG_WARNING,
"%s: executing system command '%s' failed.\n",
__func__, command);
}
ldrv->sdrv = sysfs_open_driver(lbus->bus_name, ldrv->drv_name);
}
/* If all else fails... */
return ldrv->sdrv ? 0 : -ENODEV;
}
static int metal_linux_probe_bus(struct linux_bus *lbus)
{
struct linux_driver *ldrv;
int error = -ENODEV;
lbus->sbus = sysfs_open_bus(lbus->bus_name);
if (!lbus->sbus)
return -ENODEV;
for_each_linux_driver(lbus, ldrv) {
error = metal_linux_probe_driver(lbus, ldrv);
if (!error)
break;
}
if (error) {
sysfs_close_bus(lbus->sbus);
lbus->sbus = NULL;
return error;
}
error = metal_linux_register_bus(lbus);
if (error) {
sysfs_close_driver(ldrv->sdrv);
ldrv->sdrv = NULL;
sysfs_close_bus(lbus->sbus);
lbus->sbus = NULL;
}
return error;
}
int metal_linux_bus_init(void)
{
struct linux_bus *lbus;
int valid = 0;
for_each_linux_bus(lbus)
valid += metal_linux_probe_bus(lbus) ? 0 : 1;
return valid ? 0 : -ENODEV;
}
void metal_linux_bus_finish(void)
{
struct linux_bus *lbus;
struct metal_bus *bus;
for_each_linux_bus(lbus) {
if (metal_bus_find(lbus->bus_name, &bus) == 0)
metal_bus_unregister(bus);
}
}
int metal_generic_dev_sys_open(struct metal_device *dev)
{
(void)dev;
return 0;
}
int metal_linux_get_device_property(struct metal_device *device,
const char *property_name,
void *output, int len)
{
int fd = 0;
int status = 0;
const int flags = O_RDONLY;
const int mode = S_IRUSR | S_IRGRP | S_IROTH;
struct linux_device *ldev = to_linux_device(device);
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/of_node/%s",
ldev->sdev->path, property_name);
fd = open(path, flags, mode);
if (fd < 0)
return -errno;
status = read(fd, output, len);
return status < 0 ? -errno : 0;
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/init.c
* @brief Linux libmetal initialization.
*/
#include <sys/types.h>
#include <metal/sys.h>
#include <metal/utilities.h>
struct metal_state _metal;
extern int metal_linux_irq_init();
extern void metal_linux_irq_shutdown();
/** Sort function for page size array. */
static int metal_pagesize_compare(const void *_a, const void *_b)
{
const struct metal_page_size *a = _a, *b = _b;
long diff = a->page_size - b->page_size;
return metal_sign(diff);
}
static int metal_add_page_size(const char *path, int shift, int mmap_flags)
{
int index = _metal.num_page_sizes;
unsigned long size = 1UL << shift;
if (index >= MAX_PAGE_SIZES) {
metal_log(METAL_LOG_WARNING, "skipped page size %ld - overflow\n",
size);
return -EOVERFLOW;
}
if (!path || shift <= 0) {
metal_log(METAL_LOG_WARNING, "skipped page size %ld - invalid args\n",
size);
return -EINVAL;
}
_metal.page_sizes[index].page_shift = shift;
_metal.page_sizes[index].page_size = size;
_metal.page_sizes[index].mmap_flags = mmap_flags;
strncpy(_metal.page_sizes[index].path, path, PATH_MAX);
_metal.num_page_sizes ++;
metal_log(METAL_LOG_DEBUG, "added page size %ld @%s\n", size, path);
return 0;
}
static int metal_init_page_sizes(void)
{
const int max_sizes = MAX_PAGE_SIZES - 1;
long sizes[max_sizes];
/* Determine system page size. */
sizes[0] = getpagesize();
if (sizes[0] <= 0) {
metal_log(METAL_LOG_ERROR, "failed to get page size\n");
return -ENOSYS;
}
_metal.page_size = sizes[0];
_metal.page_shift = metal_log2(sizes[0]);
metal_add_page_size(_metal.tmp_path, _metal.page_shift, 0);
#ifdef HAVE_HUGETLBFS_H
#ifndef MAP_HUGE_SHIFT
/* System does not support multiple huge page sizes. */
sizes[0] = gethugepagesize();
if (sizes[0] > 0) {
metal_add_page_size(hugetlbfs_find_path(),
metal_log2(sizes[0]),
MAP_HUGETLB);
}
#else
if (gethugepagesize() >= 0) {
int i, count;
/* System supports multiple huge page sizes. */
count = gethugepagesizes(sizes, max_sizes);
for (i = 0; i < count; i++) {
int shift = metal_log2(sizes[i]);
if ((shift & MAP_HUGE_MASK) != shift)
continue;
metal_add_page_size(
hugetlbfs_find_path_for_size(sizes[i]),
shift, (MAP_HUGETLB |
(shift << MAP_HUGE_SHIFT)));
}
}
#endif
#endif
/* Finally sort the resulting array by size. */
qsort(_metal.page_sizes, _metal.num_page_sizes,
sizeof(struct metal_page_size), metal_pagesize_compare);
return 0;
}
int metal_sys_init(const struct metal_init_params *params)
{
static char sysfs_path[SYSFS_PATH_MAX];
const char *tmp_path;
unsigned int seed;
FILE* urandom;
int result;
/* Determine sysfs mount point. */
result = sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path));
if (result) {
metal_log(METAL_LOG_ERROR, "failed to get sysfs path (%s)\n",
strerror(-result));
return result;
}
_metal.sysfs_path = sysfs_path;
/* Find the temporary directory location. */
tmp_path = getenv("TMPDIR");
if (!tmp_path)
tmp_path = "/tmp";
_metal.tmp_path = tmp_path;
/* Initialize the pseudo-random number generator. */
urandom = fopen("/dev/urandom", "r");
if (!urandom) {
metal_log(METAL_LOG_ERROR, "failed to open /dev/urandom (%s)\n",
strerror(errno));
return -errno;
}
if (fread(&seed, 1, sizeof(seed), urandom) <= 0) {
metal_log(METAL_LOG_DEBUG, "Failed fread /dev/urandom\n");
}
fclose(urandom);
srand(seed);
result = metal_init_page_sizes();
if (result < 0)
return result;
result = metal_linux_bus_init();
if (result < 0)
return result;
result = open("/proc/self/pagemap", O_RDONLY | O_CLOEXEC);
if (result < 0) {
metal_log(METAL_LOG_DEBUG, "Failed pagemap open - %s\n",
strerror(errno));
}
_metal.pagemap_fd = result;
metal_unused(params);
/* Initialize IRQ handling */
metal_linux_irq_init();
return 0;
}
void metal_sys_finish(void)
{
/* Shutdown IRQ handling */
metal_linux_irq_shutdown();
metal_linux_bus_finish();
close(_metal.pagemap_fd);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/io.h
* @brief Linux specific io definitions.
*/
#ifndef __METAL_IO__H__
#error "Include metal/io.h instead of metal/linux/io.h"
#endif
#ifndef __METAL_LINUX_IO__H__
#define __METAL_LINUX_IO__H__
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
#define metal_sys_io_mem_map(...)
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_IO__H__ */

View File

@@ -0,0 +1,374 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/**
* @file linux/irq.c
* @brief Linux libmetal irq operations
*/
#include <pthread.h>
#include <sched.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/sys.h>
#include <metal/mutex.h>
#include <metal/list.h>
#include <metal/utilities.h>
#include <metal/alloc.h>
#include <sys/time.h>
#include <sys/eventfd.h>
#include <stdint.h>
#include <metal/errno.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#define MAX_IRQS FD_SETSIZE /**< maximum number of irqs */
#define METAL_IRQ_STOP 0xFFFFFFFF /**< stop interrupts handling thread */
/** IRQ handler descriptor structure */
struct metal_irq_hddesc {
metal_irq_handler hd; /**< irq handler */
struct metal_device *dev; /**< metal device */
void *drv_id; /**< id to identify the driver
of the irq handler*/
struct metal_list list; /**< handler list container */
};
struct metal_irqs_state {
struct metal_irq_hddesc hds[MAX_IRQS]; /**< irqs handlers descriptor */
signed char irq_reg_stat[MAX_IRQS]; /**< irqs registration statistics.
It restore how many handlers have
been registered for each IRQ. */
int irq_reg_fd; /**< irqs registration notification file
descriptor */
metal_mutex_t irq_lock; /**< irq handling lock */
unsigned int irq_state; /**< global irq handling state */
pthread_t irq_pthread; /**< irq handling thread id */
};
struct metal_irqs_state _irqs;
int metal_irq_register(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
uint64_t val = 1;
struct metal_irq_hddesc *hd_desc;
struct metal_list *h_node;
int ret;
if ((irq < 0) || (irq >= MAX_IRQS)) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d is larger than the max supported %d.\n",
__func__, irq, MAX_IRQS - 1);
return -EINVAL;
}
if ((drv_id == NULL) || (hd == NULL)) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n",
__func__, irq);
return -EINVAL;
}
metal_mutex_acquire(&_irqs.irq_lock);
if (_irqs.irq_state == METAL_IRQ_STOP) {
metal_log(METAL_LOG_ERROR,
"%s: failed. metal IRQ handling has stopped.\n",
__func__);
metal_mutex_release(&_irqs.irq_lock);
return -EINVAL;
}
metal_list_for_each(&_irqs.hds[irq].list, h_node) {
hd_desc = metal_container_of(h_node, struct metal_irq_hddesc, list);
/* if drv_id already exist reject */
if ((hd_desc->drv_id == drv_id) &&
((dev == NULL) || (hd_desc->dev == dev))) {
metal_log(METAL_LOG_ERROR, "%s: irq %d already registered."
"Will not register again.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -EINVAL;
}
/* drv_id not used, get out of metal_list_for_each */
break;
}
/* Add to the end */
hd_desc = metal_allocate_memory(sizeof(struct metal_irq_hddesc));
if (hd_desc == NULL) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d cannot allocate mem for drv_id %d.\n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
hd_desc->hd = hd;
hd_desc->drv_id = drv_id;
hd_desc->dev = dev;
metal_list_add_tail(&_irqs.hds[irq].list, &hd_desc->list);
_irqs.irq_reg_stat[irq]++;
metal_mutex_release(&_irqs.irq_lock);
ret = write(_irqs.irq_reg_fd, &val, sizeof(val));
if (ret < 0) {
metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", __func__, irq);
}
metal_log(METAL_LOG_DEBUG, "%s: registered IRQ %d\n", __func__, irq);
return 0;
}
int metal_irq_unregister(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
uint64_t val = 1;
struct metal_irq_hddesc *hd_desc;
struct metal_list *h_node;
int ret;
unsigned int delete_count = 0;
if ((irq < 0) || (irq >= MAX_IRQS)) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d is larger than the max supported %d.\n",
__func__, irq, MAX_IRQS);
return -EINVAL;
}
metal_mutex_acquire(&_irqs.irq_lock);
if (_irqs.irq_state == METAL_IRQ_STOP) {
metal_log(METAL_LOG_ERROR,
"%s: failed. metal IRQ handling has stopped.\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return -EINVAL;
}
if (!hd && !drv_id && !dev) {
if (0 == _irqs.irq_reg_stat[irq])
goto no_entry;
_irqs.irq_reg_stat[irq] = 0;
goto out;
}
/* Search through handlers */
metal_list_for_each(&_irqs.hds[irq].list, h_node) {
hd_desc = metal_container_of(h_node, struct metal_irq_hddesc, list);
if (((hd == NULL) || (hd_desc->hd == hd)) &&
((drv_id == NULL) || (hd_desc->drv_id == drv_id)) &&
((dev == NULL) || (hd_desc->dev == dev))) {
if (_irqs.irq_reg_stat[irq] > 0)
_irqs.irq_reg_stat[irq]--;
h_node = h_node->prev;
metal_list_del(h_node->next);
metal_free_memory(hd_desc);
delete_count++;
}
}
if (delete_count)
goto out;
no_entry:
metal_log(METAL_LOG_DEBUG, "%s: No matching entry.\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
out:
metal_mutex_release(&_irqs.irq_lock);
ret = write(_irqs.irq_reg_fd, &val, sizeof(val));
if (ret < 0) {
metal_log(METAL_LOG_DEBUG, "%s: write failed IRQ %d\n", __func__, irq);
}
metal_log(METAL_LOG_DEBUG, "%s: unregistered IRQ %d (%d)\n", __func__, irq, delete_count);
return 0;
}
unsigned int metal_irq_save_disable()
{
metal_mutex_acquire(&_irqs.irq_lock);
return 0;
}
void metal_irq_restore_enable(unsigned flags)
{
(void)flags;
metal_mutex_release(&_irqs.irq_lock);
}
void metal_irq_enable(unsigned int vector)
{
(void)vector;
}
void metal_irq_disable(unsigned int vector)
{
(void)vector;
}
/**
* @brief IRQ handler
* @param[in] args not used. required for pthread.
*/
static void *metal_linux_irq_handling(void *args)
{
struct sched_param param;
uint64_t val;
int ret;
int i, j, pfds_total;
struct pollfd *pfds;
(void) args;
pfds = (struct pollfd *)malloc(MAX_IRQS * sizeof(struct pollfd));
if (!pfds) {
metal_log(METAL_LOG_ERROR, "%s: failed to allocate irq fds mem.\n",
__func__);
return NULL;
}
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
/* Ignore the set scheduler error */
ret = sched_setscheduler(0, SCHED_FIFO, &param);
if (ret) {
metal_log(METAL_LOG_WARNING, "%s: Failed to set scheduler: %d.\n",
__func__, ret);
}
while (1) {
metal_mutex_acquire(&_irqs.irq_lock);
if (_irqs.irq_state == METAL_IRQ_STOP) {
/* Killing this IRQ handling thread */
metal_mutex_release(&_irqs.irq_lock);
break;
}
/* Get the fdset */
memset(pfds, 0, MAX_IRQS * sizeof(struct pollfd));
pfds[0].fd = _irqs.irq_reg_fd;
pfds[0].events = POLLIN;
for(i = 0, j = 1; i < MAX_IRQS && j < MAX_IRQS; i++) {
if (_irqs.irq_reg_stat[i] > 0) {
pfds[j].fd = i;
pfds[j].events = POLLIN;
j++;
}
}
metal_mutex_release(&_irqs.irq_lock);
/* Wait for interrupt */
ret = poll(pfds, j, -1);
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s: poll() failed: %s.\n",
__func__, strerror(errno));
break;
}
/* Waken up from interrupt */
pfds_total = j;
for (i = 0; i < pfds_total; i++) {
if ( (pfds[i].fd == _irqs.irq_reg_fd) &&
(pfds[i].revents & (POLLIN | POLLRDNORM))) {
/* IRQ registration change notification */
if (read(pfds[i].fd, (void*)&val, sizeof(uint64_t)) < 0)
metal_log(METAL_LOG_ERROR,
"%s, read irq fd %d failed.\n",
__func__, pfds[i].fd);
} else if ((pfds[i].revents & (POLLIN | POLLRDNORM))) {
struct metal_irq_hddesc *hd_desc; /**< irq handler descriptor */
struct metal_device *dev = NULL; /**< metal device IRQ belongs to */
int irq_handled = 0; /**< flag to indicate if irq is handled */
struct metal_list *h_node;
metal_list_for_each(&_irqs.hds[pfds[i].fd].list, h_node) {
hd_desc = metal_container_of(h_node, struct metal_irq_hddesc, list);
metal_mutex_acquire(&_irqs.irq_lock);
if (!dev)
dev = hd_desc->dev;
metal_mutex_release(&_irqs.irq_lock);
if ((hd_desc->hd)(pfds[i].fd, hd_desc->drv_id) == METAL_IRQ_HANDLED)
irq_handled = 1;
}
if (irq_handled) {
if (dev && dev->bus->ops.dev_irq_ack)
dev->bus->ops.dev_irq_ack(dev->bus, dev, i);
}
} else if (pfds[i].revents) {
metal_log(METAL_LOG_DEBUG,
"%s: poll unexpected. fd %d: %d\n",
__func__, pfds[i].fd, pfds[i].revents);
}
}
}
free(pfds);
return NULL;
}
/**
* @brief irq handling initialization
* @return 0 on sucess, non-zero on failure
*/
int metal_linux_irq_init()
{
int ret, irq;
memset(&_irqs, 0, sizeof(_irqs));
/* init handlers list for each interrupt in table */
for (irq=0; irq < MAX_IRQS; irq++) {
metal_list_init(&_irqs.hds[irq].list);
}
_irqs.irq_reg_fd = eventfd(0,0);
if (_irqs.irq_reg_fd < 0) {
metal_log(METAL_LOG_ERROR, "Failed to create eventfd for IRQ handling.\n");
return -EAGAIN;
}
metal_mutex_init(&_irqs.irq_lock);
ret = pthread_create(&_irqs.irq_pthread, NULL,
metal_linux_irq_handling, NULL);
if (ret != 0) {
metal_log(METAL_LOG_ERROR, "Failed to create IRQ thread: %d.\n", ret);
return -EAGAIN;
}
return 0;
}
/**
* @brief irq handling shutdown
*/
void metal_linux_irq_shutdown()
{
int ret;
uint64_t val = 1;
metal_log(METAL_LOG_DEBUG, "%s\n", __func__);
metal_mutex_acquire(&_irqs.irq_lock);
_irqs.irq_state = METAL_IRQ_STOP;
metal_mutex_release(&_irqs.irq_lock);
ret = write (_irqs.irq_reg_fd, &val, sizeof(val));
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "Failed to write.\n");
}
ret = pthread_join(_irqs.irq_pthread, NULL);
if (ret) {
metal_log(METAL_LOG_ERROR, "Failed to join IRQ thread: %d.\n", ret);
}
close(_irqs.irq_reg_fd);
metal_mutex_deinit(&_irqs.irq_lock);
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/irq.h
* @brief Linux libmetal irq definitions.
*/
#ifndef __METAL_IRQ__H__
#error "Include metal/irq.h instead of metal/linux/irq.h"
#endif
#ifndef __METAL_LINUX_IRQ__H__
#define __METAL_LINUX_IRQ__H__
#endif /* __METAL_LINUX_IRQ__H__ */

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Linaro nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* @file linux/log.h
* @brief Linux libmetal log handler definition.
*/
#ifndef __METAL_METAL_LOG__H__
#error "Include metal/log.h instead of metal/linux/log.h"
#endif
#ifndef __METAL_LINUX_LOG__H__
#define __METAL_LINUX_LOG__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_LOG__H__ */

View File

@@ -0,0 +1,98 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/mutex.h
* @brief Linux mutex primitives for libmetal.
*/
#ifndef __METAL_MUTEX__H__
#error "Include metal/mutex.h instead of metal/linux/mutex.h"
#endif
#ifndef __METAL_LINUX_MUTEX__H__
#define __METAL_LINUX_MUTEX__H__
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <metal/atomic.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
atomic_int v;
} metal_mutex_t;
/*
* METAL_MUTEX_INIT - used for initializing an mutex elmenet in a static struct
* or global
*/
#define METAL_MUTEX_INIT(m) { ATOMIC_VAR_INIT(0) }
/*
* METAL_MUTEX_DEFINE - used for defining and initializing a global or
* static singleton mutex
*/
#define METAL_MUTEX_DEFINE(m) metal_mutex_t m = METAL_MUTEX_INIT(m)
static inline int __metal_mutex_cmpxchg(metal_mutex_t *mutex,
int exp, int val)
{
atomic_compare_exchange_strong(&mutex->v, (int *)&exp, val);
return exp;
}
static inline void __metal_mutex_init(metal_mutex_t *mutex)
{
atomic_store(&mutex->v, 0);
}
static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
{
(void)mutex;
}
static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
{
int val = 0;
return atomic_compare_exchange_strong(&mutex->v, &val, 1);
}
static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
{
int c = 0;
if (atomic_compare_exchange_strong(&mutex->v, &c, 1))
return;
if (c != 2)
c = atomic_exchange(&mutex->v, 2);
while (c != 0) {
syscall(SYS_futex, &mutex->v, FUTEX_WAIT, 2, NULL, NULL, 0);
c = atomic_exchange(&mutex->v, 2);
}
}
static inline void __metal_mutex_release(metal_mutex_t *mutex)
{
if (atomic_fetch_sub(&mutex->v, 1) != 1) {
atomic_store(&mutex->v, 0);
syscall(SYS_futex, &mutex->v, FUTEX_WAKE, 1, NULL, NULL, 0);
}
}
static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
{
return atomic_load(&mutex->v);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_MUTEX__H__ */

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/shmem.c
* @brief Linux libmetal shared memory handling.
*/
#include <metal/shmem.h>
#include <metal/sys.h>
#include <metal/utilities.h>
struct metal_shmem {
struct metal_io_region io;
metal_phys_addr_t *phys;
};
static void metal_shmem_io_close(struct metal_io_region *io)
{
metal_unmap(io->virt, io->size);
free((void *)io->physmap);
}
static const struct metal_io_ops metal_shmem_io_ops = {
NULL, NULL, NULL, NULL, NULL, metal_shmem_io_close
};
static int metal_shmem_try_map(struct metal_page_size *ps, int fd, size_t size,
struct metal_io_region **result)
{
size_t pages, page, phys_size;
struct metal_io_region *io;
metal_phys_addr_t *phys;
uint8_t *virt;
void *mem;
int error;
size = metal_align_up(size, ps->page_size);
pages = size / ps->page_size;
error = metal_map(fd, 0, size, 1, ps->mmap_flags, &mem);
if (error) {
metal_log(METAL_LOG_ERROR, "failed to mmap shmem - %s\n",
strerror(-error));
return error;
}
error = metal_mlock(mem, size);
if (error) {
metal_log(METAL_LOG_WARNING, "failed to mlock shmem - %s\n",
strerror(-error));
}
phys_size = sizeof(*phys) * pages;
phys = malloc(phys_size);
if (!phys) {
metal_unmap(mem, size);
return -ENOMEM;
}
io = malloc(sizeof(*io));
if (!io) {
free(phys);
metal_unmap(mem, size);
return -ENOMEM;
}
if (_metal.pagemap_fd < 0) {
phys[0] = 0;
metal_log(METAL_LOG_WARNING,
"shmem - failed to get va2pa mapping. use offset as pa.\n");
metal_io_init(io, mem, phys, size, -1, 0, &metal_shmem_io_ops);
} else {
for (virt = mem, page = 0; page < pages; page++) {
size_t offset = page * ps->page_size;
error = metal_virt2phys(virt + offset, &phys[page]);
if (error < 0)
phys[page] = METAL_BAD_OFFSET;
}
metal_io_init(io, mem, phys, size, ps->page_shift, 0,
&metal_shmem_io_ops);
}
*result = io;
return 0;
}
int metal_shmem_open(const char *name, size_t size,
struct metal_io_region **result)
{
struct metal_page_size *ps;
int fd, error;
error = metal_shmem_open_generic(name, size, result);
if (!error)
return error;
error = metal_open(name, 1);
if (error < 0) {
metal_log(METAL_LOG_ERROR, "Failed to open shmem file :%s\n", name);
return error;
}
fd = error;
/* Iterate through page sizes in decreasing order. */
metal_for_each_page_size_down(ps) {
if (ps->page_size > 2 * size)
continue;
error = metal_shmem_try_map(ps, fd, size, result);
if (!error)
break;
}
close(fd);
return error;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/sleep.h
* @brief linux sleep primitives for libmetal.
*/
#ifndef __METAL_SLEEP__H__
#error "Include metal/sleep.h instead of metal/linux/sleep.h"
#endif
#ifndef __METAL_LINUX_SLEEP__H__
#define __METAL_LINUX_SLEEP__H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline int __metal_sleep_usec(unsigned int usec)
{
return usleep(usec);
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_SLEEP__H__ */

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/sys.h
* @brief Linux system primitives for libmetal.
*/
#ifndef __METAL_SYS__H__
#error "Include metal/sys.h instead of metal/linux/sys.h"
#endif
#ifndef __METAL_LINUX_SYS__H__
#define __METAL_LINUX_SYS__H__
#include <metal/errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/futex.h>
#include <sysfs/libsysfs.h>
#ifdef HAVE_HUGETLBFS_H
#include <hugetlbfs.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define METAL_INVALID_VADDR NULL
#define MAX_PAGE_SIZES 32
struct metal_device;
/** Structure of shared page or hugepage sized data. */
struct metal_page_size {
/** Page size. */
size_t page_size;
/** Page shift. */
unsigned long page_shift;
/** Path to hugetlbfs (or tmpfs) mount point. */
char path[PATH_MAX];
/** Flags to use for mmap. */
int mmap_flags;
};
/** Structure of linux specific libmetal runtime state. */
struct metal_state {
/** Common (system independent) data. */
struct metal_common_state common;
/** file descriptor for shared data. */
int data_fd;
/** system page size. */
unsigned long page_size;
/** system page shift. */
unsigned long page_shift;
/** sysfs mount point. */
const char *sysfs_path;
/** sysfs mount point. */
const char *tmp_path;
/** available page sizes. */
struct metal_page_size page_sizes[MAX_PAGE_SIZES];
/** number of available page sizes. */
int num_page_sizes;
/** File descriptor for /proc/self/pagemap (or -1). */
int pagemap_fd;
};
#ifdef METAL_INTERNAL
extern int metal_linux_bus_init(void);
extern void metal_linux_bus_finish(void);
extern int metal_open(const char *path, int shm);
extern int metal_open_unlinked(const char *path, int shm);
extern int metal_mktemp(char *template, int fifo);
extern int metal_mktemp_unlinked(char *template);
extern int metal_map(int fd, off_t offset, size_t size, int expand,
int flags, void **result);
extern int metal_unmap(void *mem, size_t size);
extern int metal_mlock(void *mem, size_t size);
extern void metal_randomize_string(char *template);
extern void metal_mktemp_template(char template[PATH_MAX],
const char *name);
extern int metal_virt2phys(void *addr, unsigned long *phys);
/**
* @brief Read a device tree property of a device
*
* @param[in] device metal_device of the intended DT node
* @param[in] property_name name of the property to be read
* @param[out] output output buffer to store read data
* @param[in] len number of bytes to be read
* @return 0 on success, or -errno on error.
*/
extern int metal_linux_get_device_property(struct metal_device *device,
const char *property_name,
void *output, int len);
#define metal_for_each_page_size_up(ps) \
for ((ps) = &_metal.page_sizes[0]; \
(ps) <= &_metal.page_sizes[_metal.num_page_sizes - 1]; \
(ps)++)
#define metal_for_each_page_size_down(ps) \
for ((ps) = &_metal.page_sizes[_metal.num_page_sizes - 1]; \
(ps) >= &_metal.page_sizes[0]; \
(ps)--)
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_LINUX_SYS__H__ */

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file linux/time.c
* @brief Linux libmetal time handling.
*/
#include <unistd.h>
#include <time.h>
#include <metal/time.h>
#define NS_PER_S (1000 * 1000 * 1000)
unsigned long long metal_get_timestamp(void)
{
unsigned long long t = 0;
struct timespec tp;
int r;
r = clock_gettime(CLOCK_MONOTONIC, &tp);
if (r == -1) {
metal_log(METAL_LOG_ERROR,"clock_gettime failed!\n");
return t;
} else {
t = tp.tv_sec * (NS_PER_S);
t += tp.tv_nsec;
}
return t;
}

View File

@@ -0,0 +1,288 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file utils.c
* @brief Linux libmetal utility functions.
*/
#include <metal/utilities.h>
#include <metal/sys.h>
/**
* @brief Open (or create) a file.
*
* This function opens or creates a file with read/write permissions and the
* O_CLOEXEC flag set.
*
* @param[in] path File path to open.
* @param[in] shm Open shared memory (via shm_open) if non-zero.
* @return File descriptor.
*/
int metal_open(const char *path, int shm)
{
const int flags = O_RDWR | O_CREAT | O_CLOEXEC;
const int mode = S_IRUSR | S_IWUSR;
int fd;
if (!path || !strlen(path))
return -EINVAL;
fd = shm ? shm_open(path, flags, mode) : open(path, flags, mode);
return fd < 0 ? -errno : fd;
}
/**
* @brief Open (or create) and unlink a file.
*
* This function opens or creates a file with read/write permissions and the
* O_CLOEXEC flag set. This file is then unlinked to ensure that it is removed
* when the last reference to the file is dropped. This ensures that libmetal
* shared maps are released appropriately once all users of the shared data
* exit.
*
* @param[in] path File path to open.
* @param[in] shm Open shared memory (via shm_open) if non-zero.
* @return File descriptor.
*/
int metal_open_unlinked(const char *path, int shm)
{
int fd, error;
fd = metal_open(path, shm);
if (fd < 0)
return fd;
error = shm ? shm_unlink(path) : unlink(path);
error = error < 0 ? -errno : 0;
if (error)
close(fd);
return error ? error : fd;
}
/**
* @brief Randomize a string.
*
* This function randomizes the contents of a string.
*
* @param[in] template String to be randomized.
*/
void metal_randomize_string(char *template)
{
const char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-=";
while (*template) {
*template++ = chars[rand() % 64];
}
}
/**
* @brief Create a file name template suitable for use with metal_mktemp.
*
* @param[out] template Template string to be filled in.
* @param[in] name Module/user identifier used as part of the
* generated template.
*/
void metal_mktemp_template(char template[PATH_MAX], const char *name)
{
snprintf(template, PATH_MAX, "%s/metal-%s-XXXXXX",
_metal.tmp_path, name);
}
/**
* @brief Create a temporary file or fifo.
*
* This function creates a temporary file or fifo, and sets the O_CLOEXEC flag
* on it.
*
* @param[in] template File name template (the last 6 characters must
* be XXXXXX per mkstemp requirements).
* @param[in] fifo Non-zero to create a FIFO instead of a regular
* file.
* @return File descriptor.
*/
int metal_mktemp(char *template, int fifo)
{
const int mode = S_IRUSR | S_IWUSR;
int result, len, flags;
char *suffix;
if (!template)
return -EINVAL;
len = strlen(template);
suffix = &template[len - 6];
if (len < 6 || strcmp(suffix, "XXXXXX")) {
metal_log(METAL_LOG_ERROR, "template %s has no trailing pattern\n",
template);
return -EINVAL;
}
flags = (fifo
? O_RDONLY | O_NONBLOCK | O_CLOEXEC
: O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC);
for (;;) {
metal_randomize_string(suffix);
if (fifo) {
result = mkfifo(template, mode);
if (result < 0) {
if (errno == EEXIST)
continue;
metal_log(METAL_LOG_ERROR, "mkfifo(%s) failed (%s)\n",
template, strerror(errno));
return -errno;
}
}
result = open(template, flags, mode);
if (result < 0) {
if (fifo)
unlink(template);
if (errno == EEXIST)
continue;
metal_log(METAL_LOG_ERROR, "open() failed (%s)\n",
strerror(errno));
return -errno;
}
return result;
}
}
/**
* @brief Open (or create) and unlink a temporary file.
*
* This function creates a temporary file, and sets the O_CLOEXEC flag on it.
* O_CLOEXEC flag set. This file is then unlinked to ensure that it is removed
* when the last reference to the file is dropped. This ensures that libmetal
* shared maps are released appropriately once all users of the shared data
* exit.
*
* @param[in] template File name template (the last 6 characters must
* be XXXXXX per mkstemp requirements).
* @return File descriptor.
*/
int metal_mktemp_unlinked(char *template)
{
int fd, error;
fd = metal_mktemp(template, 0);
if (fd < 0)
return fd;
error = unlink(template) < 0 ? -errno : 0;
if (error)
close(fd);
return error ? error : fd;
}
/**
* @brief Map a segment of a file/device.
*
* This function maps a segment of a file or device into the process address
* space, after optionally expanding the file if necessary. If required, the
* file is expanded to hold the requested map area. This is done under and
* advisory lock, and therefore the called must not have an advisory lock on
* the file being mmapped.
*
* @param[in] fd File descriptor to map.
* @param[in] offset Offset in file to map.
* @param[in] size Size of region to map.
* @param[in] expand Allow file expansion via ftruncate if non-zero.
* @param[in] flags Flags for mmap(), MAP_SHARED included implicitly.
* @param[out] result Returned pointer to new memory map.
* @return 0 on success, or -errno on error.
*/
int metal_map(int fd, off_t offset, size_t size, int expand, int flags,
void **result)
{
int prot = PROT_READ | PROT_WRITE, error;
void *mem;
flags |= MAP_SHARED;
if (fd < 0) {
fd = -1;
flags = MAP_PRIVATE | MAP_ANONYMOUS;
} else if (expand) {
off_t reqsize = offset + size;
struct stat stat;
error = flock(fd, LOCK_EX) < 0 ? -errno : 0;
if (!error)
error = fstat(fd, &stat);
if (!error && stat.st_size < reqsize)
error = ftruncate(fd, reqsize);
if (!error)
flock(fd, LOCK_UN);
if (error)
return -errno;
}
mem = mmap(NULL, size, prot, flags, fd, offset);
if (mem == MAP_FAILED)
return -errno;
*result = mem;
return 0;
}
/**
* @brief Unmap a segment of the process address space.
*
* This function unmaps a segment of the process address space.
*
* @param[in] mem Segment to unmap.
* @param[in] size Size of region to unmap.
* @return 0 on success, or -errno on error.
*/
int metal_unmap(void *mem, size_t size)
{
return munmap(mem, size) < 0 ? -errno : 0;
}
/**
* @brief Lock in a region of the process address space.
*
* @param[in] mem Pointer to start of region.
* @param[in] size Size of region.
* @return 0 on success, or -errno on error.
*/
int metal_mlock(void *mem, size_t size)
{
return mlock(mem, size) ? -errno : 0;
}
int metal_virt2phys(void *addr, unsigned long *phys)
{
off_t offset;
uint64_t entry;
int error;
if (_metal.pagemap_fd < 0)
return -ENOSYS;
offset = ((uintptr_t)addr >> _metal.page_shift) * sizeof(entry);
error = pread(_metal.pagemap_fd, &entry, sizeof(entry), offset);
if (error < 0) {
metal_log(METAL_LOG_ERROR, "failed pagemap pread (offset %llx) - %s\n",
(unsigned long long)offset, strerror(errno));
return -errno;
}
/* Check page present and not swapped. */
if ((entry >> 62) != 2) {
metal_log(METAL_LOG_ERROR, "pagemap page not present, %llx -> %llx\n",
(unsigned long long)offset, (unsigned long long)entry);
return -ENOENT;
}
*phys = (entry & ((1ULL << 54) - 1)) << _metal.page_shift;
return 0;
}

View File

@@ -0,0 +1,25 @@
collect (PROJECT_LIB_HEADERS alloc.h)
collect (PROJECT_LIB_HEADERS assert.h)
collect (PROJECT_LIB_HEADERS cache.h)
collect (PROJECT_LIB_HEADERS condition.h)
collect (PROJECT_LIB_HEADERS io.h)
collect (PROJECT_LIB_HEADERS irq.h)
collect (PROJECT_LIB_HEADERS log.h)
collect (PROJECT_LIB_HEADERS mutex.h)
collect (PROJECT_LIB_HEADERS sleep.h)
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES alloc.c)
collect (PROJECT_LIB_SOURCES condition.c)
collect (PROJECT_LIB_SOURCES device.c)
collect (PROJECT_LIB_SOURCES init.c)
collect (PROJECT_LIB_SOURCES irq.c)
collect (PROJECT_LIB_SOURCES log.c)
collect (PROJECT_LIB_SOURCES shmem.c)
collect (PROJECT_LIB_SOURCES time.c)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
add_subdirectory(${PROJECT_MACHINE})
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_MACHINE})
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/alloc.c
* @brief Zephyr libmetal memory allocation handling.
*/
#include <metal/alloc.h>
#include <metal/compiler.h>
#if (CONFIG_HEAP_MEM_POOL_SIZE <= 0)
void* metal_weak metal_zephyr_allocate_memory(unsigned int size)
{
(void)size;
return NULL;
}
void metal_weak metal_zephyr_free_memory(void *ptr)
{
(void)ptr;
}
#endif /* CONFIG_HEAP_MEM_POOL_SIZE */

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/alloc.h
* @brief zephyr libmetal memory allocattion definitions.
*/
#ifndef __METAL_ALLOC__H__
#error "Include metal/alloc.h instead of metal/zephyr/alloc.h"
#endif
#ifndef __METAL_ZEPHYR_ALLOC__H__
#define __METAL_ZEPHYR_ALLOC__H__
#include <kernel.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#if (CONFIG_HEAP_MEM_POOL_SIZE > 0)
static inline void *metal_allocate_memory(unsigned int size)
{
return k_malloc(size);
}
static inline void metal_free_memory(void *ptr)
{
k_free(ptr);
}
#else
void *metal_zephyr_allocate_memory(unsigned int size);
void metal_zephyr_free_memory(void *ptr);
static inline void *metal_allocate_memory(unsigned int size)
{
return metal_zephyr_allocate_memory(size);
}
static inline void metal_free_memory(void *ptr)
{
metal_zephyr_free_memory(ptr);
}
#endif /* CONFIG_HEAP_MEM_POOL_SIZE */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_ZEPHYR_ALLOC__H__ */

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file assert.h
* @brief Zephyr assertion support.
*/
#ifndef __METAL_ASSERT__H__
#error "Include metal/assert.h instead of metal/zephyr/assert.h"
#endif
#ifndef __METAL_ZEPHYR_ASSERT__H__
#define __METAL_ZEPHYR_ASSERT__H__
#include <zephyr.h>
/**
* @brief Assertion macro for Zephyr-based applications.
* @param cond Condition to evaluate.
*/
#define metal_sys_assert(cond) __ASSERT_NO_MSG(cond)
#endif /* __METAL_ZEPHYR_ASSERT__H__ */

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/cache.h
* @brief Zephyr cache operation primitives for libmetal.
*/
#ifndef __METAL_CACHE__H__
#error "Include metal/cache.h instead of metal/zephyr/cache.h"
#endif
#ifndef __METAL_ZEPHYR_CACHE__H__
#define __METAL_ZEPHYR_CACHE__H__
#include <cache.h>
#include <metal/utilities.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void __metal_cache_flush(void *addr, unsigned int len)
{
sys_cache_flush((vaddr_t) addr, len);
}
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
{
metal_unused(addr);
metal_unused(len);
return;
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_ZEPHYR_CACHE__H__ */

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/condition.c
* @brief Zephyr libmetal condition variable handling.
*/
#include <metal/condition.h>
#include <metal/irq.h>
extern void metal_generic_default_poll(void);
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
metal_mutex_t *tmpm = 0;
int v;
unsigned int flags;
/* Check if the mutex has been acquired */
if (!cv || !m || !metal_mutex_is_acquired(m))
return -EINVAL;
if (!atomic_compare_exchange_strong(&cv->m, &tmpm, m)) {
if (m != tmpm)
return -EINVAL;
}
v = atomic_load(&cv->v);
/* Release the mutex first. */
metal_mutex_release(m);
do {
flags = metal_irq_save_disable();
if (atomic_load(&cv->v) != v) {
metal_irq_restore_enable(flags);
break;
}
metal_generic_default_poll();
metal_irq_restore_enable(flags);
} while(1);
/* Acquire the mutex again. */
metal_mutex_acquire(m);
return 0;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/condition.h
* @brief Zephyr condition variable primitives for libmetal.
*/
#ifndef __METAL_CONDITION__H__
#error "Include metal/condition.h instead of metal/generic/condition.h"
#endif
#ifndef __METAL_ZEPHYR_CONDITION__H__
#define __METAL_ZEPHYR_CONDITION__H__
#include <metal/errno.h>
#include <metal/atomic.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct metal_condition {
metal_mutex_t *m; /**< mutex.
The condition variable is attached to
this mutex when it is waiting.
It is also used to check correctness
in case there are multiple waiters. */
atomic_int v; /**< condition variable value. */
};
/** Static metal condition variable initialization. */
#define METAL_CONDITION_INIT { NULL, ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
cv->m = NULL;
atomic_init(&cv->v, 0);
}
static inline int metal_condition_signal(struct metal_condition *cv)
{
if (!cv)
return -EINVAL;
/** wake up waiters if there are any. */
atomic_fetch_add(&cv->v, 1);
return 0;
}
static inline int metal_condition_broadcast(struct metal_condition *cv)
{
return metal_condition_signal(cv);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_ZEPHYR_CONDITION__H__ */

View File

@@ -0,0 +1,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/qemu_cortex_m3/sys.c
* @brief machine specific system primitives implementation.
*/
#include <metal/io.h>
#include <metal/sys.h>
#include <stdint.h>
/**
* @brief poll function until some event happens
*/
void metal_weak metal_generic_default_poll(void)
{
__asm__ __volatile__("wfi");
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/qemu_cortex_m3/sys.h
* @brief Zephyr QEMU Cortex M3 system primitives for libmetal.
*/
#ifndef __METAL_ZEPHYR_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#ifndef __METAL_ZEPHYR_CORTEXM_SYS__H__
#define __METAL_ZEPHYR_CORTEXM_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_ZEPHYR_CORTEXM_SYS__H__ */

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/device.c
* @brief Zephyr libmetal device definitions.
*/
#include <metal/device.h>
#include <metal/sys.h>
#include <metal/utilities.h>
int metal_generic_dev_sys_open(struct metal_device *dev)
{
metal_unused(dev);
/* Since Zephyr runs bare-metal there is no mapping that needs to be
* done of IO regions
*/
return 0;
}
struct metal_bus metal_generic_bus = {
.name = "generic",
.ops = {
.bus_close = NULL,
.dev_open = metal_generic_dev_open,
.dev_close = NULL,
.dev_irq_ack = NULL,
.dev_dma_map = NULL,
.dev_dma_unmap = NULL,
},
};

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/init.c
* @brief Zephyr libmetal initialization.
*/
#include <metal/device.h>
#include <metal/sys.h>
#include <metal/utilities.h>
struct metal_state _metal;
int metal_sys_init(const struct metal_init_params *params)
{
metal_bus_register(&metal_generic_bus);
return 0;
}
void metal_sys_finish(void)
{
metal_bus_unregister(&metal_generic_bus);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/io.h
* @brief Zephyr specific io definitions.
*/
#ifndef __METAL_IO__H__
#error "Include metal/io.h instead of metal/zephyr/io.h"
#endif
#ifndef __METAL_ZEPHYR_IO__H__
#define __METAL_ZEPHYR_IO__H__
#include <stdlib.h>
#include <metal/utilities.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef METAL_INTERNAL
/**
* @brief memory mapping for an I/O region
*/
static inline void metal_sys_io_mem_map(struct metal_io_region *io)
{
metal_unused(io);
}
/**
* @brief memory mapping
*/
static inline void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags)
{
metal_unused(pa);
metal_unused(size);
metal_unused(flags);
return va;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_ZEPHYR_IO__H__ */

View File

@@ -0,0 +1,280 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/irq.c
* @brief Zephyr libmetal irq definitions.
*/
#include <metal/errno.h>
#include <metal/irq.h>
#include <metal/sys.h>
#include <metal/log.h>
#include <metal/mutex.h>
#include <metal/list.h>
#include <metal/utilities.h>
#include <metal/alloc.h>
#include <irq.h>
/** IRQ handlers descriptor structure */
struct metal_irq_hddesc {
metal_irq_handler hd; /**< irq handler */
void *drv_id; /**< id to identify the driver
of the irq handler */
struct metal_device *dev; /**< device identifier */
struct metal_list node; /**< node on irq handlers list */
};
/** IRQ descriptor structure */
struct metal_irq_desc {
int irq; /**< interrupt number */
struct metal_list hdls; /**< interrupt handlers */
struct metal_list node; /**< node on irqs list */
};
/** IRQ state structure */
struct metal_irqs_state {
struct metal_list irqs; /**< interrupt descriptors */
metal_mutex_t irq_lock; /**< access lock */
};
static struct metal_irqs_state _irqs = {
.irqs = METAL_INIT_LIST(_irqs.irqs),
.irq_lock = METAL_MUTEX_INIT(_irqs.irq_lock),
};
int metal_irq_register(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
struct metal_irq_desc *irq_p = NULL;
struct metal_irq_hddesc *hdl_p;
struct metal_list *node;
unsigned int irq_flags_save;
if (irq < 0) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d need to be a positive number\n",
__func__, irq);
return -EINVAL;
}
if ((drv_id == NULL) || (hd == NULL)) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need drv_id and hd.\n",
__func__, irq);
return -EINVAL;
}
/* Search for irq in list */
metal_mutex_acquire(&_irqs.irq_lock);
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if (irq_p->irq == irq) {
struct metal_list *h_node;
/* Check if drv_id already exist */
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
/* if drv_id already exist reject */
if ((hdl_p->drv_id == drv_id) &&
((dev == NULL) || (hdl_p->dev == dev))) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d already registered."
"Will not register again.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -EINVAL;
}
}
/* irq found and drv_id not used, get out of metal_list_for_each */
break;
}
}
/* Either need to add handler to an existing list or to a new one */
hdl_p = metal_allocate_memory(sizeof(struct metal_irq_hddesc));
if (hdl_p == NULL) {
metal_log(METAL_LOG_ERROR,
"%s: irq %d cannot allocate mem for drv_id %d.\n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
hdl_p->hd = hd;
hdl_p->drv_id = drv_id;
hdl_p->dev = dev;
/* interrupt already registered, add handler to existing list*/
if ((irq_p != NULL) && (irq_p->irq == irq)) {
irq_flags_save = metal_irq_save_disable();
metal_list_add_tail(&irq_p->hdls, &hdl_p->node);
metal_irq_restore_enable(irq_flags_save);
metal_log(METAL_LOG_DEBUG, "%s: success, irq %d add drv_id %p \n",
__func__, irq, drv_id);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
/* interrupt was not already registered, add */
irq_p = metal_allocate_memory(sizeof(struct metal_irq_desc));
if (irq_p == NULL) {
metal_log(METAL_LOG_ERROR, "%s: irq %d cannot allocate mem.\n",
__func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return -ENOMEM;
}
irq_p->irq = irq;
metal_list_init(&irq_p->hdls);
metal_list_add_tail(&irq_p->hdls, &hdl_p->node);
irq_flags_save = metal_irq_save_disable();
metal_list_add_tail(&_irqs.irqs, &irq_p->node);
metal_irq_restore_enable(irq_flags_save);
metal_log(METAL_LOG_DEBUG, "%s: success, added irq %d\n", __func__, irq);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
/* helper function for metal_irq_unregister() */
static void metal_irq_delete_node(struct metal_list *node, void *p_to_free)
{
unsigned int irq_flags_save;
irq_flags_save=metal_irq_save_disable();
metal_list_del(node);
metal_irq_restore_enable(irq_flags_save);
metal_free_memory(p_to_free);
}
int metal_irq_unregister(int irq,
metal_irq_handler hd,
struct metal_device *dev,
void *drv_id)
{
struct metal_irq_desc *irq_p;
struct metal_list *node;
if (irq < 0) {
metal_log(METAL_LOG_ERROR, "%s: irq %d need to be a positive number\n",
__func__, irq);
return -EINVAL;
}
/* Search for irq in list */
metal_mutex_acquire(&_irqs.irq_lock);
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if (irq_p->irq == irq) {
struct metal_list *h_node, *h_prenode;
struct metal_irq_hddesc *hdl_p;
unsigned int delete_count = 0;
metal_log(METAL_LOG_DEBUG, "%s: found irq %d\n",
__func__, irq);
/* Search through handlers */
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
if (((hd == NULL) || (hdl_p->hd == hd)) &&
((drv_id == NULL) || (hdl_p->drv_id == drv_id)) &&
((dev == NULL) || (hdl_p->dev == dev))) {
metal_log(METAL_LOG_DEBUG,
"%s: unregister hd=%p drv_id=%p dev=%p\n",
__func__, hdl_p->hd, hdl_p->drv_id, hdl_p->dev);
h_prenode = h_node->prev;
metal_irq_delete_node(h_node, hdl_p);
h_node = h_prenode;
delete_count++;
}
}
/* we did not find any handler to delete */
if (!delete_count) {
metal_log(METAL_LOG_DEBUG, "%s: No matching entry\n",
__func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
}
/* if interrupt handlers list is empty, unregister interrupt */
if (metal_list_is_empty(&irq_p->hdls)) {
metal_log(METAL_LOG_DEBUG,
"%s: handlers list empty, unregister interrupt\n",
__func__);
metal_irq_delete_node(node, irq_p);
}
metal_log(METAL_LOG_DEBUG, "%s: success\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return 0;
}
}
metal_log(METAL_LOG_DEBUG, "%s: No matching IRQ entry\n", __func__);
metal_mutex_release(&_irqs.irq_lock);
return -ENOENT;
}
unsigned int metal_irq_save_disable(void)
{
return irq_lock();
}
void metal_irq_restore_enable(unsigned int flags)
{
irq_unlock(flags);
}
void metal_irq_enable(unsigned int vector)
{
irq_enable(vector);
}
void metal_irq_disable(unsigned int vector)
{
irq_disable(vector);
}
/**
* @brief default handler
*/
void metal_irq_isr(unsigned int vector)
{
struct metal_list *node;
struct metal_irq_desc *irq_p;
metal_list_for_each(&_irqs.irqs, node) {
irq_p = metal_container_of(node, struct metal_irq_desc, node);
if ((unsigned int)irq_p->irq == vector) {
struct metal_list *h_node;
struct metal_irq_hddesc *hdl_p;
metal_list_for_each(&irq_p->hdls, h_node) {
hdl_p = metal_container_of(h_node,
struct metal_irq_hddesc,
node);
(hdl_p->hd)(vector, hdl_p->drv_id);
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2017, Linaro Limited. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file zephyr/irq.c
* @brief Zephyr libmetal irq definitions.
*/
#ifndef __METAL_IRQ__H__
#error "Include metal/irq.h instead of metal/zephyr/irq.h"
#endif
#ifndef __METAL_ZEPHYR_IRQ__H__
#define __METAL_ZEPHYR_IRQ__H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief default interrupt handler
* @param[in] vector interrupt vector
*/
void metal_irq_isr(unsigned int vector);
#ifdef __cplusplus
}
#endif
#endif /* __METAL_ZEPHYR_IRQ__H__ */

Some files were not shown because too many files have changed in this diff Show More