ext: libmetal: Update libmetal to snapshot for bug fixes

Pull in libmetal SHA 59a10acbb0bb684c1a75488f11878cb984170c81 to get
some build fixes related to newlib.

Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
This commit is contained in:
Kumar Gala 2019-03-07 13:18:51 -06:00 committed by Kumar Gala
parent d60dfdf481
commit 8108d16f9f
76 changed files with 1844 additions and 1140 deletions

View File

@ -11,12 +11,14 @@ env:
- ZEPHYR_TOOLCHAIN_VARIANT=zephyr
- ZEPHYR_SDK_INSTALL_DIR=/opt/zephyr-sdk
- ZEPHYR_BASE=$TRAVIS_BUILD_DIR/deps/zephyr
- ZEPHYR_SDK_VERSION=0.9.3
- ZEPHYR_SDK_VERSION=0.9.5
- ZEPHYR_SDK_DOWNLOAD_FOLDER=https://github.com/zephyrproject-rtos/meta-zephyr-sdk/releases/download/$ZEPHYR_SDK_VERSION
- ZEPHYR_SDK_SETUP_BINARY=zephyr-sdk-$ZEPHYR_SDK_VERSION-setup.run
- ZEPHYR_SDK_DOWNLOAD_URL=$ZEPHYR_SDK_DOWNLOAD_FOLDER/$ZEPHYR_SDK_SETUP_BINARY
- FREERTOS_ZIP_URL=https://cfhcable.dl.sourceforge.net/project/freertos/FreeRTOS/V10.0.1/FreeRTOSv10.0.1.zip
- GCC_ARM_COMPILER_PACKAGE=gcc-arm-embedded_7-2018q2-1~trusty1_amd64.deb
- CC=gcc-4.9
- CXX=g++-4.9
matrix:
fast_finish: true
@ -34,8 +36,16 @@ cache:
directories:
- $ZEPHYR_SDK_INSTALL_DIR
- /usr/local/bin
- $HOME/bin/cmake
before_install:
- if [ ! -f $HOME/bin/cmake/cmake-3.13.1-Linux-x86_64/bin/cmake ]; then
mkdir -p $HOME/bin/cmake && cd $HOME/bin/cmake &&
wget https://github.com/Kitware/CMake/releases/download/v3.13.1/cmake-3.13.1-Linux-x86_64.sh &&
yes | sh cmake-3.13.1-Linux-x86_64.sh | cat &&
cd -;
fi &&
export PATH=$HOME/bin/cmake/cmake-3.13.1-Linux-x86_64/bin:$PATH
- if [[ "$TARGET" == "zephyr" ]]; then
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test &&
sudo apt-get update -qq &&
@ -43,8 +53,9 @@ before_install:
sudo pip3 install pyelftools;
fi
- if [[ "$TARGET" == "linux" ]]; then
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test &&
sudo apt-get update -qq &&
sudo apt-get install libsysfs-dev libhugetlbfs-dev make gcc;
sudo apt-get install libsysfs-dev libhugetlbfs-dev make gcc-4.9;
fi
# This is to kick start CI on generic platform. Will need to have a proper way to get the required packages
- if [[ "$TARGET" == "generic" || "$TARGET" == "freertos" ]]; then
@ -77,7 +88,7 @@ script:
- if [[ "$TARGET" == "zephyr" ]]; then
mkdir -p ../libmetal/build-zephyr &&
cd ../libmetal/build-zephyr &&
cmake .. -DWITH_ZEPHYR=on -DBOARD=qemu_cortex_m3 -DWITH_TESTS=on &&
cmake .. -DWITH_ZEPHYR=on -DBOARD=qemu_cortex_m3 &&
make VERBOSE=1;
fi
- if [[ "$TARGET" == "linux" ]]; then

View File

@ -16,11 +16,13 @@ collect (PROJECT_LIB_HEADERS device.h)
collect (PROJECT_LIB_HEADERS dma.h)
collect (PROJECT_LIB_HEADERS io.h)
collect (PROJECT_LIB_HEADERS irq.h)
collect (PROJECT_LIB_HEADERS irq_controller.h)
collect (PROJECT_LIB_HEADERS list.h)
collect (PROJECT_LIB_HEADERS log.h)
collect (PROJECT_LIB_HEADERS mutex.h)
collect (PROJECT_LIB_HEADERS shmem.h)
collect (PROJECT_LIB_HEADERS sleep.h)
collect (PROJECT_LIB_HEADERS softirq.h)
collect (PROJECT_LIB_HEADERS spinlock.h)
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_HEADERS time.h)
@ -31,8 +33,10 @@ collect (PROJECT_LIB_SOURCES dma.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 log.c)
collect (PROJECT_LIB_SOURCES shmem.c)
collect (PROJECT_LIB_SOURCES softirq.c)
collect (PROJECT_LIB_SOURCES version.c)
add_subdirectory (compiler)
@ -62,7 +66,7 @@ endif (WITH_DEFAULT_LOGGER)
if (WITH_ZEPHYR)
zephyr_library_named(metal)
add_dependencies(metal ${OFFSETS_H_TARGET})
add_dependencies(metal offsets_h)
zephyr_library_sources(${_sources})
zephyr_include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
else (WITH_ZEPHYR)

View File

@ -16,7 +16,10 @@
#if defined(HAVE_STDATOMIC_H) && !defined(__STDC_NO_ATOMICS__) && \
!defined(__cplusplus)
# include <stdint.h>
# include <stdatomic.h>
#elif defined(__cplusplus)
# include <atomic>
#elif defined(__GNUC__)
# include <metal/compiler/gcc/atomic.h>
#else

View File

@ -20,6 +20,9 @@ extern "C" {
#define metal_align(n) __attribute__((aligned(n)))
#define metal_weak __attribute__((weak))
#define METAL_PACKED_BEGIN
#define METAL_PACKED_END __attribute__((__packed__))
#ifdef __cplusplus
}
#endif

View File

@ -20,6 +20,9 @@ extern "C" {
#define metal_align(n) __attribute__((aligned(n)))
#define metal_weak __attribute__((weak))
#define METAL_PACKED_BEGIN __packed
#define METAL_PACKED_END
#ifdef __cplusplus
}
#endif

View File

@ -14,7 +14,9 @@ void metal_io_init(struct metal_io_region *io, void *virt,
unsigned page_shift, unsigned int mem_flags,
const struct metal_io_ops *ops)
{
const struct metal_io_ops nops = {NULL, NULL, NULL, NULL, NULL, NULL};
const struct metal_io_ops nops = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
io->virt = virt;
io->physmap = physmap;
@ -37,7 +39,7 @@ int metal_io_block_read(struct metal_io_region *io, unsigned long offset,
unsigned char *dest = dst;
int retlen;
if (offset > io->size)
if (offset >= io->size)
return -ERANGE;
if ((offset + len) > io->size)
len = io->size - offset;
@ -74,7 +76,7 @@ int metal_io_block_write(struct metal_io_region *io, unsigned long offset,
const unsigned char *source = src;
int retlen;
if (offset > io->size)
if (offset >= io->size)
return -ERANGE;
if ((offset + len) > io->size)
len = io->size - offset;
@ -110,7 +112,7 @@ int metal_io_block_set(struct metal_io_region *io, unsigned long offset,
unsigned char *ptr = metal_io_virt(io, offset);
int retlen = len;
if (offset > io->size)
if (offset >= io->size)
return -ERANGE;
if ((offset + len) > io->size)
len = io->size - offset;
@ -123,7 +125,7 @@ int metal_io_block_set(struct metal_io_region *io, unsigned long offset,
unsigned int i;
for (i = 1; i < sizeof(int); i++)
cint |= ((unsigned int)value << (8 * i));
cint |= ((unsigned int)value << (CHAR_BIT * i));
for (; len && ((uintptr_t)ptr % sizeof(int)); ptr++, len--)
*(unsigned char *)ptr = (unsigned char) value;

View File

@ -62,6 +62,11 @@ struct metal_io_ops {
memory_order order,
int len);
void (*close)(struct metal_io_region *io);
metal_phys_addr_t
(*offset_to_phys)(struct metal_io_region *io,
unsigned long offset);
unsigned long (*phys_to_offset)(struct metal_io_region *io,
metal_phys_addr_t phys);
};
/** Libmetal I/O region structure. */
@ -126,7 +131,7 @@ static inline size_t metal_io_region_size(struct metal_io_region *io)
static inline void *
metal_io_virt(struct metal_io_region *io, unsigned long offset)
{
return (io->virt != METAL_BAD_VA && offset <= io->size
return (io->virt != METAL_BAD_VA && offset < io->size
? (uint8_t *)io->virt + offset
: NULL);
}
@ -154,12 +159,16 @@ metal_io_virt_to_offset(struct metal_io_region *io, void *virt)
static inline metal_phys_addr_t
metal_io_phys(struct metal_io_region *io, unsigned long offset)
{
unsigned long page = (io->page_shift >=
sizeof(offset) * CHAR_BIT ?
0 : offset >> io->page_shift);
return (io->physmap != NULL && offset <= io->size
? io->physmap[page] + (offset & io->page_mask)
: METAL_BAD_PHYS);
if (!io->ops.offset_to_phys) {
unsigned long page = (io->page_shift >=
sizeof(offset) * CHAR_BIT ?
0 : offset >> io->page_shift);
return (io->physmap != NULL && offset < io->size
? io->physmap[page] + (offset & io->page_mask)
: METAL_BAD_PHYS);
}
return io->ops.offset_to_phys(io, offset);
}
/**
@ -171,15 +180,19 @@ metal_io_phys(struct metal_io_region *io, unsigned long offset)
static inline unsigned long
metal_io_phys_to_offset(struct metal_io_region *io, metal_phys_addr_t phys)
{
unsigned long offset =
(io->page_mask == (metal_phys_addr_t)(-1) ?
phys - io->physmap[0] : phys & io->page_mask);
do {
if (metal_io_phys(io, offset) == phys)
return offset;
offset += io->page_mask + 1;
} while (offset < io->size);
return METAL_BAD_OFFSET;
if (!io->ops.phys_to_offset) {
unsigned long offset =
(io->page_mask == (metal_phys_addr_t)(-1) ?
phys - io->physmap[0] : phys & io->page_mask);
do {
if (metal_io_phys(io, offset) == phys)
return offset;
offset += io->page_mask + 1;
} while (offset < io->size);
return METAL_BAD_OFFSET;
}
return (*io->ops.phys_to_offset)(io, phys);
}
/**

138
libmetal/lib/irq.c Normal file
View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2019, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <errno.h>
#include <metal/irq.h>
#include <metal/irq_controller.h>
#include <metal/list.h>
#include <metal/utilities.h>
/** List of registered IRQ controller */
static METAL_DECLARE_LIST(irq_cntrs);
static int metal_irq_allocate(int irq_base, int irq_num)
{
struct metal_list *node;
struct metal_irq_controller *cntr;
int irq_tocheck = irq_base, irq_end_tocheck;
if (irq_num == 0) {
return METAL_IRQ_ANY;
}
if (irq_tocheck == METAL_IRQ_ANY) {
irq_tocheck = 0;
}
irq_end_tocheck = irq_tocheck + irq_num;
metal_list_for_each(&irq_cntrs, node) {
int cntr_irq_base, cntr_irq_end;
cntr = metal_container_of(node,
struct metal_irq_controller, node);
cntr_irq_base = cntr->irq_base;
cntr_irq_end = cntr_irq_base + cntr->irq_num;
if (irq_tocheck < cntr_irq_end &&
irq_end_tocheck > cntr_irq_base) {
if (irq_base != METAL_IRQ_ANY) {
/* IRQ has been allocated */
return METAL_IRQ_ANY;
}
irq_tocheck = cntr_irq_end;
irq_end_tocheck = irq_tocheck + irq_num;
}
}
return irq_tocheck;
}
int metal_irq_register_controller(struct metal_irq_controller *cntr)
{
int irq_base;
struct metal_list *node;
if (cntr == NULL) {
return -EINVAL;
}
metal_list_for_each(&irq_cntrs, node) {
if (node == &cntr->node) {
return 0;
}
}
/* Allocate IRQ numbers which are not yet used by any IRQ
* controllers.*/
irq_base = metal_irq_allocate(cntr->irq_base , cntr->irq_num);
if (irq_base == METAL_IRQ_ANY) {
return -EINVAL;
}
cntr->irq_base = irq_base;
metal_list_add_tail(&irq_cntrs, &cntr->node);
return 0;
}
static struct metal_irq_controller *metal_irq_get_controller(int irq)
{
struct metal_list *node;
struct metal_irq_controller *cntr;
metal_list_for_each(&irq_cntrs, node) {
int irq_base, irq_end;
cntr = (struct metal_irq_controller *)
metal_container_of(node, struct metal_irq_controller,
node);
irq_base = cntr->irq_base;
irq_end = irq_base + cntr->irq_num;
if (irq >= irq_base && irq < irq_end) {
return cntr;
}
}
return NULL;
}
static void _metal_irq_set_enable(int irq, unsigned int state)
{
struct metal_irq_controller *cntr;
cntr = metal_irq_get_controller(irq);
if (cntr == NULL) {
return;
}
cntr->irq_set_enable(cntr, irq, state);
}
int metal_irq_register(int irq,
metal_irq_handler irq_handler,
void *arg)
{
struct metal_irq_controller *cntr;
struct metal_irq *irq_data;
cntr = metal_irq_get_controller(irq);
if (cntr == NULL) {
return -EINVAL;
}
if (cntr->irq_register != NULL) {
return cntr->irq_register(cntr, irq, irq_handler, arg);
}
if (cntr->irqs == NULL) {
return -EINVAL;
}
irq_data = &cntr->irqs[irq - cntr->irq_base];
irq_data->hd = irq_handler;
irq_data->arg = arg;
return 0;
}
void metal_irq_enable(unsigned int vector)
{
_metal_irq_set_enable((int)vector, METAL_IRQ_ENABLE);
}
void metal_irq_disable(unsigned int vector)
{
_metal_irq_set_enable((int)vector, METAL_IRQ_DISABLE);
}

View File

@ -19,6 +19,7 @@ extern "C" {
/** \defgroup irq Interrupt Handling Interfaces
* @{ */
#include <metal/list.h>
#include <stdlib.h>
/** IRQ handled status */
@ -27,53 +28,39 @@ extern "C" {
/**
* @brief type of interrupt handler
* @param[in] irq interrupt id
* @param[in] priv private data
* @return irq handled status
* @param[in] irq interrupt id
* @param[in] arg argument to pass to the handler
* @return irq handled status
*/
typedef int (*metal_irq_handler) (int irq, void *priv);
struct metal_device;
typedef int (*metal_irq_handler) (int irq, void *arg);
/**
* @brief Register interrupt handler for driver ID/device.
* @brief Register interrupt handler for interrupt.
* Only allow single interrupt handler for a interrupt.
*
* If irq_handler is NULL, it will unregister interrupt
* handler from interrupt
*
* @param[in] irq interrupt id
* @param[in] irq_handler interrupt handler
* @param[in] dev metal device this irq belongs to (can be NULL).
* @param[in] drv_id driver id is a unique interrupt handler identifier.
* It can also be used for driver data.
* @param[in] arg arg is the argument pointing to the data which
* will be passed to the interrupt handler.
* @return 0 for success, non-zero on failure
*/
int metal_irq_register(int irq,
metal_irq_handler irq_handler,
struct metal_device *dev,
void *drv_id);
void *arg);
/**
* @brief Unregister interrupt handler for driver ID and/or device.
*
* If interrupt handler (hd), driver ID (drv_id) and device (dev)
* are NULL, unregister all handlers for this interrupt.
*
* If interrupt handler (hd), device (dev) or driver ID (drv_id),
* are not NULL, unregister handlers matching non NULL criterias.
* e.g: when call is made with drv_id and dev non NULL,
* all handlers matching both are unregistered.
*
* If interrupt is not found, or other criterias not matching,
* return -ENOENT
* @brief Unregister interrupt handler for interrupt.
*
* @param[in] irq interrupt id
* @param[in] irq_handler interrupt handler
* @param[in] dev metal device this irq belongs to
* @param[in] drv_id driver id. It can be used for driver data.
* @return 0 for success, non-zero on failure
*/
int metal_irq_unregister(int irq,
metal_irq_handler irq_handler,
struct metal_device *dev,
void *drv_id);
static inline
void metal_irq_unregister(int irq)
{
metal_irq_register(irq, 0, NULL);
}
/**
* @brief disable interrupts

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2016, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file irq.h
* @brief Interrupt handling primitives for libmetal.
*/
#ifndef __METAL_IRQ_CONTROLLER__H__
#define __METAL_IRQ_CONTROLLER__H__
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup irq Interrupt Handling Interfaces
* @{ */
#include <metal/irq.h>
#include <metal/list.h>
#include <stdlib.h>
/** IRQ ANY ID */
#define METAL_IRQ_ANY (-1)
/** IRQ state macro which will be passed to metal irq set state function
* to indicate which state the caller want the IRQ to change to.
*/
#define METAL_IRQ_DISABLE 0U
#define METAL_IRQ_ENABLE 1U
struct metal_irq_controller;
/**
* @brief type of interrupt controller to set irq enable
* @param[in] irq_cntr pointer to interrupt controller
* @param[in] irq interrupt id
* @param[in] enable IRQ state
*/
typedef void (*metal_irq_set_enable) (struct metal_irq_controller *irq_cntr,
int irq, unsigned int enable);
/**
* @brief type of controller specific registering interrupt function
* @param[in] irq_cntr pointer to interrupt controller
* @param[in] irq interrupt id
* @param[in] hd interrupt handler
* @param[in] arg argument which will be passed to the interrupt handler
* @return 0 for success, negative value for failure
*/
typedef int (*metal_cntr_irq_register) (struct metal_irq_controller *irq_cntr,
int irq, metal_irq_handler hd,
void *arg);
/** Libmetal interrupt structure */
struct metal_irq {
metal_irq_handler hd; /**< Interrupt handler */
void *arg; /**< Argument to pass to the interrupt handler */
};
/** Libmetal interrupt controller structure */
struct metal_irq_controller {
int irq_base; /**< Start of IRQ number of the range managed by
the IRQ controller */
int irq_num; /**< Number of IRQs managed by the IRQ controller */
void *arg; /**< Argument to pass to interrupt controller function */
metal_irq_set_enable irq_set_enable; /**< function to set IRQ eanble */
metal_cntr_irq_register irq_register; /**< function to register IRQ
handler */
struct metal_list node; /**< list node */
struct metal_irq *irqs; /**< Array of IRQs managed by the controller */
};
#define METAL_IRQ_CONTROLLER_DECLARE(_irq_controller, \
_irq_base, _irq_num, \
_arg, \
_irq_set_enable, \
_irq_register, \
_irqs) \
struct metal_irq_controller _irq_controller = { \
.irq_base = _irq_base, \
.irq_num = _irq_num, \
.arg = _arg, \
.irq_set_enable = _irq_set_enable, \
.irq_register = _irq_register, \
.irqs = _irqs,\
};
/**
* @brief metal_irq_register_controller
*
* Register IRQ controller
* This function will allocate IRQ ids if it was
* not predefined in the irq controller. There is no
* locking in the function, it is not supposed to be
* called by multiple threads.
*
* @param[in] cntr Interrupt controller to register
* @return 0 on success, or negative value for failure.
*/
int metal_irq_register_controller(struct metal_irq_controller *cntr);
/**
* @brief metal_irq_handle
*
* Call registered IRQ handler
*
* @param[in] irq_data metal IRQ structure
* @param[in] irq IRQ id which will be passed to handler
* @return IRQ handler status
*/
static inline
int metal_irq_handle(struct metal_irq *irq_data, int irq)
{
if (irq_data != NULL && irq_data->hd != NULL) {
return irq_data->hd(irq, irq_data->arg);
} else {
return METAL_IRQ_NOT_HANDLED;
}
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_IRQ__H__ */

View File

@ -0,0 +1,3 @@
collect (PROJECT_LIB_HEADERS cpu.h)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2017, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file cpu.h
* @brief CPU specific primitives
*/
#ifndef __METAL_CEVA_CPU__H__
#define __METAL_CEVA_CPU__H__
#define metal_cpu_yield()
#ifdef TEAKLITE4
/*
* The dummy implementation is enough here since
* tl42x don't support the out of order and multi core
*/
#define __sync_synchronize()
#endif
#endif /* __METAL_CEVA_CPU__H__ */

View File

@ -0,0 +1,3 @@
collect (PROJECT_LIB_HEADERS cpu.h)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file cpu.h
* @brief CPU specific primitives
*/
#ifndef __METAL_CSKY_CPU__H__
#define __METAL_CSKY_CPU__H__
#define metal_cpu_yield()
#endif /* __METAL_CSKY_CPU__H__ */

View File

@ -0,0 +1,3 @@
collect (PROJECT_LIB_HEADERS cpu.h)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file cpu.h
* @brief CPU specific primitives
*/
#ifndef __METAL_RISCV_CPU__H__
#define __METAL_RISCV_CPU__H__
#define metal_cpu_yield()
#endif /* __METAL_RISCV_CPU__H__ */

101
libmetal/lib/softirq.c Normal file
View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <errno.h>
#include <metal/atomic.h>
#include <metal/irq.h>
#include <metal/irq_controller.h>
#include <metal/log.h>
#include <metal/sys.h>
#include <metal/softirq.h>
#include <metal/utilities.h>
#include <string.h>
#define METAL_SOFTIRQ_NUM 64
#define METAL_SOFTIRQ_ARRAY_DECLARE(num) \
static const int metal_softirq_num = num; \
static struct metal_irq metal_softirqs[num]; \
static atomic_char metal_softirq_pending[num]; \
static atomic_char metal_softirq_enabled[num]; \
static int metal_softirq_avail = 0;
METAL_SOFTIRQ_ARRAY_DECLARE(METAL_SOFTIRQ_NUM)
static void metal_softirq_set_enable(struct metal_irq_controller *cntr,
int irq, unsigned int enable)
{
if (irq < cntr->irq_base ||
irq >= (cntr->irq_base + cntr->irq_num)) {
return;
}
irq -= cntr->irq_base;
if (enable == METAL_IRQ_ENABLE) {
atomic_store(&metal_softirq_enabled[irq], 1);
} else {
atomic_store(&metal_softirq_enabled[irq], 0);
}
}
static METAL_IRQ_CONTROLLER_DECLARE(metal_softirq_cntr,
METAL_IRQ_ANY, METAL_SOFTIRQ_NUM,
NULL,
metal_softirq_set_enable, NULL,
metal_softirqs)
void metal_softirq_set(int irq)
{
struct metal_irq_controller *cntr;
cntr = &metal_softirq_cntr;
if (irq < cntr->irq_base ||
irq >= (cntr->irq_base + cntr->irq_num)) {
return;
}
irq -= cntr->irq_base;
atomic_store(&metal_softirq_pending[irq], 1);
}
int metal_softirq_init()
{
return metal_irq_register_controller(&metal_softirq_cntr);
}
int metal_softirq_allocate(int num)
{
int irq_base;
if ((metal_softirq_avail + num) >= metal_softirq_num) {
metal_log(METAL_LOG_ERROR, "No %d available soft irqs.\r\n",
num);
return -EINVAL;
}
irq_base = metal_softirq_avail;
irq_base += metal_softirq_cntr.irq_base;
metal_softirq_avail += num;
return irq_base;
}
void metal_softirq_dispatch()
{
int i;
for (i = 0; i < metal_softirq_num; i++) {
struct metal_irq *irq;
char is_pending = 1;
if (atomic_load(&metal_softirq_enabled[i]) != 0 &&
atomic_compare_exchange_strong(&metal_softirq_pending[i],
&is_pending, 0)) {
irq = &metal_softirqs[i];
(void)metal_irq_handle(irq,
i + metal_softirq_cntr.irq_base);
}
}
}

68
libmetal/lib/softirq.h Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2019, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file softirq.h
* @brief Soft Interrupt handling primitives for libmetal.
*/
#ifndef __METAL_SOFTIRQ__H__
#define __METAL_SOFTIRQ__H__
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup soft irq Interrupt Handling Interfaces
* @{ */
#include <metal/irq.h>
/**
* @brief metal_softirq_init
*
* Initialize libmetal soft IRQs controller
*
* @return 0 on success, or negative value for failure
*/
int metal_softirq_init();
/**
* @brief metal_softirq_dispatch
*
* Dispatch the pending soft IRQs
*/
void metal_softirq_dispatch();
/**
* @brief metal_softirq_allocate
*
* Allocate soft IRQs
*
* This function doesn't have any locking, it is not supposed
* to be called by multiple threads.
*
* @param[in] num number of soft irqs requested
* @return soft irq base for success, or negative value for failure
*/
int metal_softirq_allocate(int num);
/**
* @brief metal_softirq_set
*
* Set soft IRQ to pending
*
* @param[in] irq soft IRQ ID to set
*/
void metal_softirq_set(int irq);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_SOFTIRQ__H__ */

View File

@ -22,11 +22,11 @@ extern "C" {
/** \defgroup spinlock Spinlock Interfaces
* @{ */
struct metal_spinlock {
atomic_int v;
atomic_flag v;
};
/** Static metal spinlock initialization. */
#define METAL_SPINLOCK_INIT {ATOMIC_VAR_INIT(0)}
#define METAL_SPINLOCK_INIT {ATOMIC_FLAG_INIT}
/**
* @brief Initialize a libmetal spinlock.
@ -34,7 +34,7 @@ struct metal_spinlock {
*/
static inline void metal_spinlock_init(struct metal_spinlock *slock)
{
atomic_store(&slock->v, 0);
atomic_flag_clear(&slock->v);
}
/**

View File

@ -18,220 +18,6 @@
#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();
@ -242,38 +28,3 @@ 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

@ -16,19 +16,4 @@
#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,5 @@
collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES irq.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2016 - 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/xlnx_common/irq.c
* @brief generic libmetal Xilinx irq controller definitions.
*/
#include <errno.h>
#include <metal/irq_controller.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>
#define MAX_IRQS XLNX_MAXIRQS
static struct metal_irq irqs[MAX_IRQS]; /**< Linux IRQs array */
static void metal_xlnx_irq_set_enable(struct metal_irq_controller *irq_cntr,
int irq, unsigned int state)
{
if (irq < irq_cntr->irq_base ||
irq >= irq_cntr->irq_base + irq_cntr->irq_num) {
metal_log(METAL_LOG_ERROR, "%s: invalid irq %d\n",
__func__, irq);
return;
} else if (state == METAL_IRQ_ENABLE) {
sys_irq_enable((unsigned int)irq);
} else {
sys_irq_disable((unsigned int)irq);
}
}
/**< Xilinx common platform IRQ controller */
static METAL_IRQ_CONTROLLER_DECLARE(xlnx_irq_cntr,
0, MAX_IRQS,
NULL,
metal_xlnx_irq_set_enable, NULL,
irqs)
/**
* @brief default handler
*/
void metal_xlnx_irq_isr(void *arg)
{
unsigned int vector;
vector = (uintptr_t)arg;
if (vector >= MAX_IRQS) {
return;
}
(void)metal_irq_handle(&irqs[vector], (int)vector);
}
int metal_xlnx_irq_init(void)
{
int ret;
ret = metal_irq_register_controller(&xlnx_irq_cntr);
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s: register irq controller failed.\n",
__func__);
return ret;
}
return 0;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file freertos/xlnx_common/sys.h
* @brief freertos Xilinx common 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_XLNX_COMMON_SYS__H__
#define __METAL_FREERTOS_XLNX_COMMON_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief metal_xlnx_irq_isr
*
* Xilinx interrupt ISR can be registered to the Xilinx embeddedsw
* IRQ controller driver.
*
* @param[in] arg input argument, interrupt vector id.
*/
void metal_xlnx_irq_isr(void *arg);
/**
* @brief metal_xlnx_irq_int
*
* Xilinx interrupt controller initialization. It will initialize
* the metal Xilinx IRQ controller data structure.
*
* @return 0 for success, or negative value for failure
*/
int metal_xlnx_irq_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __METAL_FREERTOS_XLNX_COMMON_SYS__H__ */

View File

@ -2,4 +2,5 @@ collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -13,6 +13,7 @@
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#include "xscugic.h"
#ifndef __METAL_FREERTOS_ZYNQ7_SYS__H__
@ -24,6 +25,8 @@ extern "C" {
#ifdef METAL_INTERNAL
#define XLNX_MAXIRQS XSCUGIC_MAX_NUM_INTR_INPUTS
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);

View File

@ -2,4 +2,5 @@ collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -13,6 +13,7 @@
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#include "xscugic.h"
#ifndef __METAL_FREERTOS_ZYNQMP_A53_SYS__H__
@ -24,6 +25,8 @@ extern "C" {
#ifdef METAL_INTERNAL
#define XLNX_MAXIRQS XSCUGIC_MAX_NUM_INTR_INPUTS
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);

View File

@ -2,4 +2,5 @@ collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -13,6 +13,7 @@
#error "Include metal/sys.h instead of metal/freertos/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#include "xscugic.h"
#ifndef __METAL_FREERTOS_ZYNQMP_R5_SYS__H__
@ -24,6 +25,8 @@ extern "C" {
#ifdef METAL_INTERNAL
#define XLNX_MAXIRQS XSCUGIC_MAX_NUM_INTR_INPUTS
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);

View File

@ -17,7 +17,7 @@ extern void metal_generic_default_poll(void);
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
metal_mutex_t *tmpm = 0;
uintptr_t tmpmptr = 0, mptr = (uintptr_t)m;
int v;
unsigned int flags;
@ -25,8 +25,8 @@ int metal_condition_wait(struct metal_condition *cv,
if (!cv || !m || !metal_mutex_is_acquired(m))
return -EINVAL;
if (!atomic_compare_exchange_strong(&cv->m, &tmpm, m)) {
if (m != tmpm)
if (!atomic_compare_exchange_strong(&cv->mptr, &tmpmptr, mptr)) {
if (tmpmptr != mptr)
return -EINVAL;
}

View File

@ -27,21 +27,21 @@ 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_uintptr_t mptr; /**< mutex pointer.
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) }
#define METAL_CONDITION_INIT { ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
cv->m = NULL;
atomic_init(&cv->mptr, 0);
atomic_init(&cv->v, 0);
}

View File

@ -18,220 +18,6 @@
#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();
@ -242,38 +28,3 @@ 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

@ -16,19 +16,4 @@
#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

@ -7,4 +7,5 @@ if (HAS_XINTC)
add_definitions(-DHAS_XINTC)
endif(HAS_XINTC)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -35,6 +35,17 @@ unsigned int sys_irq_save_disable(void)
return state &= MSR_IE;
}
void sys_irq_restore_enable(unsigned int flags)
{
unsigned int tmp;
if (flags)
asm volatile(" msrset %0, %1 \n"
: "=r"(tmp)
: "i"(MSR_IE)
: "memory");
}
#else /* XPAR_MICROBLAZE_USE_MSR_INSTR == 0 */
unsigned int sys_irq_save_disable(void)
{
@ -49,19 +60,20 @@ unsigned int sys_irq_save_disable(void)
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");
if (flags)
asm volatile(" mfs %0, rmsr \n"
" or %0, %0, %1 \n"
" mts rmsr, %0 \n"
: "=r"(tmp)
: "r"(flags)
: "memory");
}
#endif /* XPAR_MICROBLAZE_USE_MSR_INSTR */
static void sys_irq_change(unsigned int vector, int is_enable)
{

View File

@ -13,6 +13,8 @@
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#ifndef __METAL_GENERIC_MICROBLAZE_SYS__H__
#define __METAL_GENERIC_MICROBLAZE_SYS__H__
@ -24,6 +26,10 @@ extern "C" {
#ifdef METAL_INTERNAL
#ifndef XLNX_MAXIRQS
#define XLNX_MAXIRQS 32
#endif
void metal_weak sys_irq_enable(unsigned int vector);
void metal_weak sys_irq_disable(unsigned int vector);

View File

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

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2016 - 2017, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/xlnx_common/irq.c
* @brief generic libmetal Xilinx irq controller definitions.
*/
#include <errno.h>
#include <metal/irq_controller.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>
#define MAX_IRQS XLNX_MAXIRQS
static struct metal_irq irqs[MAX_IRQS]; /**< Linux IRQs array */
static void metal_xlnx_irq_set_enable(struct metal_irq_controller *irq_cntr,
int irq, unsigned int state)
{
if (irq < irq_cntr->irq_base ||
irq >= irq_cntr->irq_base + irq_cntr->irq_num) {
metal_log(METAL_LOG_ERROR, "%s: invalid irq %d\n",
__func__, irq);
return;
} else if (state == METAL_IRQ_ENABLE) {
sys_irq_enable((unsigned int)irq);
} else {
sys_irq_disable((unsigned int)irq);
}
}
/**< Xilinx common platform IRQ controller */
static METAL_IRQ_CONTROLLER_DECLARE(xlnx_irq_cntr,
0, MAX_IRQS,
NULL,
metal_xlnx_irq_set_enable, NULL,
irqs)
/**
* @brief default handler
*/
void metal_xlnx_irq_isr(void *arg)
{
unsigned int vector;
vector = (uintptr_t)arg;
if (vector >= MAX_IRQS) {
return;
}
(void)metal_irq_handle(&irqs[vector], (int)vector);
}
int metal_xlnx_irq_init(void)
{
int ret;
ret = metal_irq_register_controller(&xlnx_irq_cntr);
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s: register irq controller failed.\n",
__func__);
return ret;
}
return 0;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file generic/xlnx_common/sys.h
* @brief generic xlnx_common 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_XLNX_COMMON_SYS__H__
#define __METAL_GENERIC_XLNX_COMMON_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief metal_xlnx_irq_isr
*
* Xilinx interrupt ISR can be registered to the Xilinx embeddedsw
* IRQ controller driver.
*
* @param[in] arg input argument, interrupt vector id.
*/
void metal_xlnx_irq_isr(void *arg);
/**
* @brief metal_xlnx_irq_int
*
* Xilinx interrupt controller initialization. It will initialize
* the metal Xilinx IRQ controller data structure.
*
* @return 0 for success, or negative value for failure
*/
int metal_xlnx_irq_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __METAL_GENERIC_XLNX_COMMON_SYS__H__ */

View File

@ -2,4 +2,5 @@ collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -13,6 +13,7 @@
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#include "xscugic.h"
#ifndef __METAL_GENERIC_ZYNQ7_SYS__H__
@ -24,6 +25,8 @@ extern "C" {
#ifdef METAL_INTERNAL
#define XLNX_MAXIRQS XSCUGIC_MAX_NUM_INTR_INPUTS
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);

View File

@ -2,4 +2,5 @@ collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -13,6 +13,7 @@
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#include "xscugic.h"
#ifndef __METAL_GENERIC_ZYNQMP_A53_SYS__H__
@ -24,6 +25,8 @@ extern "C" {
#ifdef METAL_INTERNAL
#define XLNX_MAXIRQS XSCUGIC_MAX_NUM_INTR_INPUTS
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);

View File

@ -2,4 +2,5 @@ collect (PROJECT_LIB_HEADERS sys.h)
collect (PROJECT_LIB_SOURCES sys.c)
add_subdirectory(../xlnx_common ${CMAKE_CURRENT_BINARY_DIR}/../xlnx_common)
# vim: expandtab:ts=2:sw=2:smartindent

View File

@ -10,9 +10,10 @@
*/
#ifndef __METAL_GENERIC_SYS__H__
#error "Include metal/sys.h instead of metal/generic/@PROJECT_MACHINE@/sys.h"
#error "Include metal/sys.h instead of metal/system/generic/@PROJECT_MACHINE@/sys.h"
#endif
#include <metal/system/@PROJECT_SYSTEM@/xlnx_common/sys.h>
#include "xscugic.h"
#ifndef __METAL_GENERIC_ZYNQMP_R5_SYS__H__
@ -24,6 +25,8 @@ extern "C" {
#ifdef METAL_INTERNAL
#define XLNX_MAXIRQS XSCUGIC_MAX_NUM_INTR_INPUTS
static inline void sys_irq_enable(unsigned int vector)
{
XScuGic_EnableIntr(XPAR_SCUGIC_0_DIST_BASEADDR, vector);

View File

@ -14,15 +14,15 @@
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
metal_mutex_t *tmpm = 0;
uintptr_t tmpmptr = 0, mptr = (uintptr_t)m;
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)
if (!atomic_compare_exchange_strong(&cv->mptr, &tmpmptr, mptr)) {
if (tmpmptr != mptr)
return -EINVAL;
}

View File

@ -29,22 +29,23 @@ 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_uintptr_t mptr; /**< mutex pointer.
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) }
#define METAL_CONDITION_INIT { ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0), \
ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
cv->m = NULL;
atomic_init(&cv->mptr, 0);
atomic_init(&cv->waiters, 0);
atomic_init(&cv->wakeups, 0);
}

View File

@ -31,13 +31,13 @@ struct linux_driver {
void (*dev_irq_ack)(struct linux_bus *lbus,
struct linux_device *ldev,
int irq);
int (*dev_dma_map)(struct linux_bus *lbus,
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,
void (*dev_dma_unmap)(struct linux_bus *lbus,
struct linux_device *ldev,
uint32_t dir,
struct metal_sg *sg,
@ -156,6 +156,7 @@ static int metal_uio_dev_open(struct linux_bus *lbus, struct linux_device *ldev)
ldev->fd = -1;
ldev->device.irq_info = (void *)-1;
ldev->sdev = sysfs_open_device(lbus->bus_name, ldev->dev_name);
if (!ldev->sdev) {
@ -249,6 +250,7 @@ static int metal_uio_dev_open(struct linux_bus *lbus, struct linux_device *ldev)
} else {
ldev->device.irq_num = 1;
ldev->device.irq_info = (void *)(intptr_t)ldev->fd;
metal_linux_irq_register_dev(&ldev->device, ldev->fd);
}
return 0;
@ -259,12 +261,6 @@ static void metal_uio_dev_close(struct linux_bus *lbus,
{
(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;
@ -586,34 +582,32 @@ static int metal_linux_probe_driver(struct linux_bus *lbus,
return ldrv->sdrv ? 0 : -ENODEV;
}
static void metal_linux_bus_close(struct metal_bus *bus);
static int metal_linux_probe_bus(struct linux_bus *lbus)
{
struct linux_driver *ldrv;
int error = -ENODEV;
int ret, 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;
ret = metal_linux_probe_driver(lbus, ldrv);
/* Clear the error if any driver is available */
if (!ret)
error = ret;
}
if (error) {
sysfs_close_bus(lbus->sbus);
lbus->sbus = NULL;
metal_linux_bus_close(&lbus->bus);
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;
}
if (error)
metal_linux_bus_close(&lbus->bus);
return error;
}

View File

@ -13,6 +13,7 @@
#include <sched.h>
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/irq_controller.h>
#include <metal/sys.h>
#include <metal/mutex.h>
#include <metal/list.h>
@ -20,203 +21,93 @@
#include <metal/alloc.h>
#include <sys/time.h>
#include <sys/eventfd.h>
#include <sched.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <unistd.h>
#define MAX_IRQS FD_SETSIZE /**< maximum number of irqs */
#define METAL_IRQ_STOP 0xFFFFFFFF /**< stop interrupts handling thread */
#define MAX_IRQS (FD_SETSIZE - 1) /**< maximum number of irqs */
/** 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 */
};
static struct metal_device *irqs_devs[MAX_IRQS]; /**< Linux devices for IRQs */
static int irq_notify_fd; /**< irq handling state change notification file
descriptor */
static metal_mutex_t irq_lock; /**< irq handling lock */
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. */
static bool irq_handling_stop; /**< stop interrupts handling */
int irq_reg_fd; /**< irqs registration notification file
descriptor */
static pthread_t irq_pthread; /**< irq handling thread id */
metal_mutex_t irq_lock; /**< irq handling lock */
/**< Indicate which IRQ is enabled */
static unsigned long
irqs_enabled[metal_div_round_up(MAX_IRQS, METAL_BITS_PER_ULONG)];
unsigned int irq_state; /**< global irq handling state */
static struct metal_irq irqs[MAX_IRQS]; /**< Linux IRQs array */
pthread_t irq_pthread; /**< irq handling thread id */
};
/* Static functions */
static void metal_linux_irq_set_enable(struct metal_irq_controller *irq_cntr,
int irq, unsigned int state);
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;
}
/**< Linux IRQ controller */
static METAL_IRQ_CONTROLLER_DECLARE(linux_irq_cntr,
0, MAX_IRQS,
NULL,
metal_linux_irq_set_enable, NULL,
irqs)
unsigned int metal_irq_save_disable()
{
metal_mutex_acquire(&_irqs.irq_lock);
/* This is to avoid deadlock if it is called in ISR */
if (pthread_self() == irq_pthread)
return 0;
metal_mutex_acquire(&irq_lock);
return 0;
}
void metal_irq_restore_enable(unsigned flags)
{
(void)flags;
metal_mutex_release(&_irqs.irq_lock);
if (pthread_self() != irq_pthread)
metal_mutex_release(&irq_lock);
}
void metal_irq_enable(unsigned int vector)
static int metal_linux_irq_notify()
{
(void)vector;
uint64_t val = 1;
int ret;
ret = write(irq_notify_fd, &val, sizeof(val));
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s failed\n", __func__);
}
return ret;
}
void metal_irq_disable(unsigned int vector)
static void metal_linux_irq_set_enable(struct metal_irq_controller *irq_cntr,
int irq, unsigned int state)
{
(void)vector;
int offset, ret;
if (irq < irq_cntr->irq_base ||
irq >= irq_cntr->irq_base + irq_cntr->irq_num) {
metal_log(METAL_LOG_ERROR, "%s: invalid irq %d\n",
__func__, irq);
return;
}
offset = irq - linux_irq_cntr.irq_base;
metal_mutex_acquire(&irq_lock);
if (state == METAL_IRQ_ENABLE)
metal_bitmap_set_bit(irqs_enabled, offset);
else
metal_bitmap_clear_bit(irqs_enabled, offset);
metal_mutex_release(&irq_lock);
/* Notify IRQ thread that IRQ state has changed */
ret = metal_linux_irq_notify();
if (ret < 0) {
metal_log(METAL_LOG_ERROR, "%s: failed to notify set %d enable\n",
__func__, irq);
}
}
/**
@ -233,7 +124,7 @@ static void *metal_linux_irq_handling(void *args)
(void) args;
pfds = (struct pollfd *)malloc(MAX_IRQS * sizeof(struct pollfd));
pfds = (struct pollfd *)malloc(FD_SETSIZE * sizeof(struct pollfd));
if (!pfds) {
metal_log(METAL_LOG_ERROR, "%s: failed to allocate irq fds mem.\n",
__func__);
@ -244,30 +135,30 @@ static void *metal_linux_irq_handling(void *args)
/* 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);
metal_log(METAL_LOG_WARNING, "%s: Failed to set scheduler: %s.\n",
__func__, strerror(ret));
}
while (1) {
metal_mutex_acquire(&_irqs.irq_lock);
if (_irqs.irq_state == METAL_IRQ_STOP) {
metal_mutex_acquire(&irq_lock);
if (irq_handling_stop == true) {
/* Killing this IRQ handling thread */
metal_mutex_release(&_irqs.irq_lock);
metal_mutex_release(&irq_lock);
break;
}
/* Get the fdset */
memset(pfds, 0, MAX_IRQS * sizeof(struct pollfd));
pfds[0].fd = _irqs.irq_reg_fd;
pfds[0].fd = irq_notify_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++;
}
j = 1;
metal_bitmap_for_each_set_bit(irqs_enabled, i,
linux_irq_cntr.irq_num) {
pfds[j].fd = i;
pfds[j].events = POLLIN;
j++;
}
metal_mutex_release(&_irqs.irq_lock);
metal_mutex_release(&irq_lock);
/* Wait for interrupt */
ret = poll(pfds, j, -1);
if (ret < 0) {
@ -278,37 +169,32 @@ static void *metal_linux_irq_handling(void *args)
/* Waken up from interrupt */
pfds_total = j;
for (i = 0; i < pfds_total; i++) {
if ( (pfds[i].fd == _irqs.irq_reg_fd) &&
if ( (pfds[i].fd == irq_notify_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);
"%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;
struct metal_device *dev = NULL;
int irq_handled = 0;
int fd;
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;
}
fd = pfds[i].fd;
dev = irqs_devs[fd];
metal_mutex_acquire(&irq_lock);
if (metal_irq_handle(&irqs[fd], fd)
== 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);
dev->bus->ops.dev_irq_ack(dev->bus, dev, fd);
}
metal_mutex_release(&irq_lock);
} else if (pfds[i].revents) {
metal_log(METAL_LOG_DEBUG,
"%s: poll unexpected. fd %d: %d\n",
"%s: poll unexpected. fd %d: %d\n",
__func__, pfds[i].fd, pfds[i].revents);
}
}
@ -323,23 +209,25 @@ static void *metal_linux_irq_handling(void *args)
*/
int metal_linux_irq_init()
{
int ret, irq;
int ret;
memset(&_irqs, 0, sizeof(_irqs));
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) {
irq_notify_fd = eventfd(0,0);
if (irq_notify_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_mutex_init(&irq_lock);
irq_handling_stop = false;
ret = metal_irq_register_controller(&linux_irq_cntr);
if (ret < 0) {
metal_log(METAL_LOG_ERROR,
"Linux IRQ controller failed to register.\n");
return -EINVAL;
}
ret = pthread_create(&irq_pthread, NULL,
metal_linux_irq_handling, NULL);
if (ret != 0) {
metal_log(METAL_LOG_ERROR, "Failed to create IRQ thread: %d.\n", ret);
@ -355,20 +243,24 @@ int metal_linux_irq_init()
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);
irq_handling_stop = true;
metal_linux_irq_notify();
ret = pthread_join(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);
close(irq_notify_fd);
metal_mutex_deinit(&irq_lock);
}
void metal_linux_irq_register_dev(struct metal_device *dev, int irq)
{
if (irq > MAX_IRQS) {
metal_log(METAL_LOG_ERROR, "Failed to register device to irq %d\n",
irq);
return;
}
irqs_devs[irq] = dev;
}

View File

@ -14,6 +14,22 @@
#endif
#ifndef __METAL_LINUX_IRQ__H__
#ifdef METAL_INTERNAL
#include <metal/device.h>
/**
* @brief metal_linux_register_dev
*
* Metal Linux internal function to register metal device to a IRQ
* which is generated from the device.
*
* @param[in] dev pointer to metal device
* @param[in] irq interrupt id
*/
void metal_linux_irq_register_dev(struct metal_device *dev, int irq);
#endif /* METAL_INTERNAL */
#define __METAL_LINUX_IRQ__H__
#endif /* __METAL_LINUX_IRQ__H__ */

View File

@ -25,7 +25,7 @@ static void metal_shmem_io_close(struct metal_io_region *io)
}
static const struct metal_io_ops metal_shmem_io_ops = {
NULL, NULL, NULL, NULL, NULL, metal_shmem_io_close
NULL, NULL, NULL, NULL, NULL, metal_shmem_io_close, NULL, NULL
};
static int metal_shmem_try_map(struct metal_page_size *ps, int fd, size_t size,
@ -43,8 +43,9 @@ static int metal_shmem_try_map(struct metal_page_size *ps, int fd, size_t 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));
metal_log(METAL_LOG_WARNING,
"failed to mmap shmem %ld,0x%x - %s\n",
size, ps->mmap_flags, strerror(-error));
return error;
}

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 io.c)
collect (PROJECT_LIB_SOURCES irq.c)
collect (PROJECT_LIB_SOURCES shmem.c)
collect (PROJECT_LIB_SOURCES time.c)
# vim: expandtab:ts=2:sw=2:smartindent

View File

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

View File

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

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/cache.h
* @brief NuttX cache operation primitives for libmetal.
*/
#ifndef __METAL_CACHE__H__
#error "Include metal/cache.h instead of metal/nuttx/cache.h"
#endif
#ifndef __METAL_NUTTX_CACHE__H__
#define __METAL_NUTTX_CACHE__H__
#include <nuttx/arch.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void __metal_cache_flush(void *addr, unsigned int len)
{
up_clean_dcache((uintptr_t)addr, (uintptr_t)addr + len);
}
static inline void __metal_cache_invalidate(void *addr, unsigned int len)
{
up_invalidate_dcache((uintptr_t)addr, (uintptr_t)addr + len);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_CACHE__H__ */

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/condition.c
* @brief NuttX libmetal condition variable handling.
*/
#include <metal/condition.h>
#include <metal/irq.h>
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
unsigned int flags;
/* Check if the mutex has been acquired */
if (!cv || !m || !metal_mutex_is_acquired(m))
return -EINVAL;
flags = metal_irq_save_disable();
/* Release the mutex first. */
metal_mutex_release(m);
nxsem_wait_uninterruptible(&cv->cond.sem);
metal_irq_restore_enable(flags);
/* Acquire the mutex again. */
metal_mutex_acquire(m);
return 0;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/condition.h
* @brief NuttX condition variable primitives for libmetal.
*/
#ifndef __METAL_CONDITION__H__
#error "Include metal/condition.h instead of metal/nuttx/condition.h"
#endif
#ifndef __METAL_NUTTX_CONDITION__H__
#define __METAL_NUTTX_CONDITION__H__
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
struct metal_condition {
pthread_cond_t cond;
};
/** Static metal condition variable initialization. */
#define METAL_CONDITION_INIT {PTHREAD_COND_INITIALIZER}
static inline void metal_condition_init(struct metal_condition *cv)
{
pthread_cond_init(&cv->cond, NULL);
}
static inline int metal_condition_signal(struct metal_condition *cv)
{
return -pthread_cond_signal(&cv->cond);
}
static inline int metal_condition_broadcast(struct metal_condition *cv)
{
return -pthread_cond_broadcast(&cv->cond);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_CONDITION__H__ */

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/device.c
* @brief NuttX libmetal device definitions.
*/
#include <metal/device.h>
int metal_generic_dev_sys_open(struct metal_device *dev)
{
return 0;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/init.c
* @brief NuttX libmetal initialization.
*/
#include <metal/device.h>
#include <metal/irq.h>
#include <metal/sys.h>
struct metal_state _metal;
int metal_sys_init(const struct metal_init_params *params)
{
int ret = metal_cntr_irq_init();
if (ret >= 0)
ret = metal_bus_register(&metal_generic_bus);
return ret;
}
void metal_sys_finish(void)
{
metal_bus_unregister(&metal_generic_bus);
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <metal/cache.h>
#include <metal/io.h>
#include <nuttx/arch.h>
static uint64_t metal_io_read_(struct metal_io_region *io,
unsigned long offset,
memory_order order,
int width)
{
uint64_t value = 0;
metal_io_block_read(io, offset, &value, width);
return value;
}
static void metal_io_write_(struct metal_io_region *io,
unsigned long offset,
uint64_t value,
memory_order order,
int width)
{
metal_io_block_write(io, offset, &value, width);
}
static int metal_io_block_read_(struct metal_io_region *io,
unsigned long offset,
void *restrict dst,
memory_order order,
int len)
{
void *va = metal_io_virt(io, offset);
metal_cache_invalidate(va, len);
memcpy(dst, va, len);
return len;
}
static int metal_io_block_write_(struct metal_io_region *io,
unsigned long offset,
const void *restrict src,
memory_order order,
int len)
{
void *va = metal_io_virt(io, offset);
memcpy(va, src, len);
metal_cache_flush(va, len);
return len;
}
static void metal_io_block_set_(struct metal_io_region *io,
unsigned long offset,
unsigned char value,
memory_order order,
int len)
{
void *va = metal_io_virt(io, offset);
memset(va, value, len);
metal_cache_flush(va, len);
}
static void metal_io_close_(struct metal_io_region *io)
{
}
static metal_phys_addr_t metal_io_offset_to_phys_(struct metal_io_region *io,
unsigned long offset)
{
return up_addrenv_va_to_pa((char *)io->virt + offset);
}
static unsigned long metal_io_phys_to_offset_(struct metal_io_region *io,
metal_phys_addr_t phys)
{
return (char *)up_addrenv_pa_to_va(phys) - (char *)io->virt;
}
static metal_phys_addr_t metal_io_phys_start_ = 0;
static struct metal_io_region metal_io_region_ = {
.virt = NULL,
.physmap = &metal_io_phys_start_,
.size = (size_t)-1,
.page_shift = sizeof(metal_phys_addr_t) * CHAR_BIT,
.page_mask = (metal_phys_addr_t)-1,
.mem_flags = 0,
.ops = {
.read = metal_io_read_,
.write = metal_io_write_,
.block_read = metal_io_block_read_,
.block_write = metal_io_block_write_,
.block_set = metal_io_block_set_,
.close = metal_io_close_,
.offset_to_phys = metal_io_offset_to_phys_,
.phys_to_offset = metal_io_phys_to_offset_,
},
};
struct metal_io_ops *metal_io_get_ops(void)
{
return &metal_io_region_.ops;
}
struct metal_io_region *metal_io_get_region(void)
{
return &metal_io_region_;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/io.h
* @brief NuttX specific io definitions.
*/
#ifndef __METAL_IO__H__
#error "Include metal/io.h instead of metal/nuttx/io.h"
#endif
#ifndef __METAL_NUTTX_IO__H__
#define __METAL_NUTTX_IO__H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get the default global io ops.
* @return an io ops.
*/
struct metal_io_ops *metal_io_get_ops(void);
/**
* @brief Get the default global io region.
* @return an io region.
*/
struct metal_io_region *metal_io_get_region(void);
#ifdef METAL_INTERNAL
/**
* @brief memory mapping for an I/O region
*/
static inline void metal_sys_io_mem_map(struct metal_io_region *io)
{
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_IO__H__ */

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/irq.c
* @brief NuttX libmetal irq definitions.
*/
#include <errno.h>
#include <metal/irq_controller.h>
#include <metal/alloc.h>
#include <nuttx/irq.h>
unsigned int metal_irq_save_disable(void)
{
return up_irq_save();
}
void metal_irq_restore_enable(unsigned int flags)
{
up_irq_restore(flags);
}
/* Implement the default irq controller */
static void metal_cntr_irq_set_enable(struct metal_irq_controller *cntr,
int irq, unsigned int enable)
{
if (irq >= 0 && irq < cntr->irq_num) {
if (enable == METAL_IRQ_ENABLE)
up_enable_irq(irq);
else
up_disable_irq(irq);
}
}
static int metal_cntr_irq_handler(int irq, void *context, void *data)
{
if (context != NULL)
return metal_irq_handle(data, irq);
/* context == NULL mean unregister */
irqchain_detach(irq, metal_cntr_irq_handler, data);
sched_kfree(data);
return 0;
}
static int metal_cntr_irq_attach(struct metal_irq_controller *cntr,
int irq, metal_irq_handler hd, void *arg)
{
if (irq < 0 || irq >= cntr->irq_num)
return -EINVAL;
if (hd) {
struct metal_irq *data;
data = metal_allocate_memory(sizeof(*data));
if (data == NULL)
return -ENOMEM;
data->hd = hd;
data->arg = arg;
irq_attach(irq, metal_cntr_irq_handler, data);
} else {
unsigned int flags;
flags = metal_irq_save_disable();
irq_dispatch(irq, NULL); /* fake a irq request */
metal_irq_restore_enable(flags);
}
return 0;
}
int metal_cntr_irq_init(void)
{
static METAL_IRQ_CONTROLLER_DECLARE(metal_cntr_irq,
0, NR_IRQS,
NULL,
metal_cntr_irq_set_enable,
metal_cntr_irq_attach,
NULL)
return metal_irq_register_controller(&metal_cntr_irq);
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/irq.h
* @brief NuttX libmetal irq definitions.
*/
#ifndef __METAL_IRQ__H__
#error "Include metal/irq.h instead of metal/nuttx/irq.h"
#endif
#ifndef __METAL_NUTTX_IRQ__H__
#define __METAL_NUTTX_IRQ__H__
#ifdef __cplusplus
extern "C" {
#endif
int metal_cntr_irq_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_IRQ__H__ */

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/log.h
* @brief NuttX libmetal log handler definition.
*/
#ifndef __METAL_METAL_LOG__H__
#error "Include metal/log.h instead of metal/nuttx/log.h"
#endif
#ifndef __METAL_NUTTX_LOG__H__
#define __METAL_NUTTX_LOG__H__
#include <syslog.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_LOG__H__ */

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/mutex.h
* @brief NuttX mutex primitives for libmetal.
*/
#ifndef __METAL_MUTEX__H__
#error "Include metal/mutex.h instead of metal/nuttx/mutex.h"
#endif
#ifndef __METAL_NUTTX_MUTEX__H__
#define __METAL_NUTTX_MUTEX__H__
#include <nuttx/mutex.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef mutex_t metal_mutex_t;
/*
* METAL_MUTEX_INIT - used for initializing an mutex elmenet in a static struct
* or global
*/
#define METAL_MUTEX_INIT(m) MUTEX_INITIALIZER
/*
* METAL_MUTEX_DEFINE - used for defining and initializing a global or
* static singleton mutex
*/
#define METAL_MUTEX_DEFINE(m) metal_mutex_t m = MUTEX_INITIALIZER
static inline void __metal_mutex_init(metal_mutex_t *mutex)
{
nxmutex_init(mutex);
}
static inline void __metal_mutex_deinit(metal_mutex_t *mutex)
{
nxmutex_destroy(mutex);
}
static inline int __metal_mutex_try_acquire(metal_mutex_t *mutex)
{
return nxmutex_trylock(mutex);
}
static inline void __metal_mutex_acquire(metal_mutex_t *mutex)
{
nxmutex_lock(mutex);
}
static inline void __metal_mutex_release(metal_mutex_t *mutex)
{
nxmutex_unlock(mutex);
}
static inline int __metal_mutex_is_acquired(metal_mutex_t *mutex)
{
return nxmutex_is_locked(mutex);
}
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_MUTEX__H__ */

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/shmem.c
* @brief NuttX 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,36 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/sleep.h
* @brief NuttX sleep primitives for libmetal.
*/
#ifndef __METAL_SLEEP__H__
#error "Include metal/sleep.h instead of metal/nuttx/sleep.h"
#endif
#ifndef __METAL_NUTTX_SLEEP__H__
#define __METAL_NUTTX_SLEEP__H__
#include <nuttx/signal.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline int __metal_sleep_usec(unsigned int usec)
{
return nxsig_usleep(usec);
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_SLEEP__H__ */

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/sys.h
* @brief NuttX system primitives for libmetal.
*/
#ifndef __METAL_SYS__H__
#error "Include metal/sys.h instead of metal/nuttx/sys.h"
#endif
#ifndef __METAL_NUTTX_SYS__H__
#define __METAL_NUTTX_SYS__H__
#ifdef __cplusplus
extern "C" {
#endif
#define METAL_INIT_DEFAULTS \
{ \
.log_handler = (metal_log_handler)syslog, \
.log_level = METAL_LOG_INFO, \
}
/** Structure of nuttx libmetal runtime state. */
struct metal_state {
/** Common (system independent) data. */
struct metal_common_state common;
};
#ifdef __cplusplus
}
#endif
#endif /* __METAL_NUTTX_SYS__H__ */

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018, Pinecone Inc. and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* @file nuttx/time.c
* @brief NuttX libmetal time handling.
*/
#include <metal/time.h>
#include <nuttx/clock.h>
unsigned long long metal_get_timestamp(void)
{
unsigned long long t = 0;
struct timespec tp;
int r;
r = clock_systimespec(&tp);
if (!r) {
t = (unsigned long long)tp.tv_sec * NSEC_PER_SEC;
t += tp.tv_nsec;
}
return t;
}

View File

@ -17,17 +17,19 @@ extern void metal_generic_default_poll(void);
int metal_condition_wait(struct metal_condition *cv,
metal_mutex_t *m)
{
metal_mutex_t *tmpm = 0;
uintptr_t tmpmptr = 0, mptr = (uintptr_t)m;
int v;
unsigned int flags;
/* Check if the mutex has been acquired */
if (!cv || !m || !metal_mutex_is_acquired(m))
if (!cv || !m || !metal_mutex_is_acquired(m)) {
return -EINVAL;
}
if (!atomic_compare_exchange_strong(&cv->m, &tmpm, m)) {
if (m != tmpm)
if (!atomic_compare_exchange_strong(&cv->mptr, &tmpmptr, mptr)) {
if (tmpmptr != mptr) {
return -EINVAL;
}
}
v = atomic_load(&cv->v);

View File

@ -26,21 +26,21 @@ 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_uintptr_t mptr; /**< mutex pointer.
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) }
#define METAL_CONDITION_INIT { ATOMIC_VAR_INIT(0), ATOMIC_VAR_INIT(0) }
static inline void metal_condition_init(struct metal_condition *cv)
{
cv->m = NULL;
atomic_init(&cv->mptr, 0);
atomic_init(&cv->v, 0);
}

View File

@ -17,221 +17,6 @@
#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)
{
@ -243,38 +28,3 @@ 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

@ -20,13 +20,6 @@
extern "C" {
#endif
/**
* @brief default interrupt handler
* @param[in] vector interrupt vector
*/
void metal_irq_isr(unsigned int vector);
#ifdef __cplusplus
}
#endif

View File

@ -12,8 +12,10 @@
#include <metal/time.h>
#include <sys_clock.h>
extern volatile u64_t _sys_clock_tick_count;
unsigned long long metal_get_timestamp(void)
{
return (unsigned long long)z_tick_get();
return (unsigned long long)_sys_clock_tick_count;
}

View File

@ -13,6 +13,7 @@
#define __METAL_UTILITIES__H__
#include <stdint.h>
#include <limits.h>
#include <metal/assert.h>
#ifdef __cplusplus
@ -69,7 +70,7 @@ extern "C" {
#define metal_container_of(ptr, structure, member) \
(void *)((uintptr_t)(ptr) - metal_offset_of(structure, member))
#define METAL_BITS_PER_ULONG (8 * sizeof(unsigned long))
#define METAL_BITS_PER_ULONG (CHAR_BIT * sizeof(unsigned long))
#define metal_bit(bit) (1UL << (bit))
@ -83,8 +84,8 @@ static inline void metal_bitmap_set_bit(unsigned long *bitmap, int bit)
static inline int metal_bitmap_is_bit_set(unsigned long *bitmap, int bit)
{
return bitmap[bit / METAL_BITS_PER_ULONG] &
metal_bit(bit & (METAL_BITS_PER_ULONG - 1));
return ((bitmap[bit / METAL_BITS_PER_ULONG] &
metal_bit(bit & (METAL_BITS_PER_ULONG - 1))) == 0) ? 0 : 1;
}
static inline void metal_bitmap_clear_bit(unsigned long *bitmap, int bit)
@ -113,7 +114,7 @@ metal_bitmap_next_set_bit(unsigned long *bitmap, unsigned int start,
#define metal_bitmap_for_each_set_bit(bitmap, bit, max) \
for ((bit) = metal_bitmap_next_set_bit((bitmap), 0, (max)); \
(bit) < (max); \
(bit) = metal_bitmap_next_set_bit((bitmap), (bit), (max)))
(bit) = metal_bitmap_next_set_bit((bitmap), (bit + 1), (max)))
static inline unsigned int
metal_bitmap_next_clear_bit(unsigned long *bitmap, unsigned int start,
@ -130,7 +131,7 @@ metal_bitmap_next_clear_bit(unsigned long *bitmap, unsigned int start,
#define metal_bitmap_for_each_clear_bit(bitmap, bit, max) \
for ((bit) = metal_bitmap_next_clear_bit((bitmap), 0, (max)); \
(bit) < (max); \
(bit) = metal_bitmap_next_clear_bit((bitmap), (bit), (max)))
(bit) = metal_bitmap_next_clear_bit((bitmap), (bit + 1), (max)))
static inline unsigned long metal_log2(unsigned long in)
{