[DM/FEATURE] Support HWSpinlock fo AMP mode

Hardware spinlock modules provide hardware assistance for
synchronization and mutual exclusion between heterogeneous processors
and those not operating under a single, shared operating system.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI 2024-11-29 16:13:55 +08:00
parent 03a9729eb8
commit 6f93530ba1
7 changed files with 475 additions and 0 deletions

View File

@ -23,6 +23,7 @@ rsource "hwcrypto/Kconfig"
rsource "wlan/Kconfig"
rsource "led/Kconfig"
rsource "mailbox/Kconfig"
rsource "hwspinlock/Kconfig"
rsource "phye/Kconfig"
rsource "ata/Kconfig"
rsource "block/Kconfig"

View File

@ -0,0 +1,15 @@
menuconfig RT_USING_HWSPINLOCK
bool "Using Hardware Spinlock device drivers"
depends on RT_USING_DM
depends on RT_USING_OFW
select RT_USING_ADT
select RT_USING_ADT_REF
default n
help
Hardware spinlock modules provide hardware assistance for
synchronization and mutual exclusion between heterogeneous processors
and those not operating under a single, shared operating system.
if RT_USING_HWSPINLOCK
osource "$(SOC_DM_HWSPINLOCK_DIR)/Kconfig"
endif

View File

@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_USING_HWSPINLOCK']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['hwspinlock.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@ -0,0 +1,311 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-23 GuEe-GUI first version
*/
#include <cpuport.h>
#define DBG_TAG "rtdm.hwspinlock"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "hwspinlock_dm.h"
static struct rt_spinlock hwspinlock_ops_lock = {};
static rt_list_t hwspinlock_bank_nodes = RT_LIST_OBJECT_INIT(hwspinlock_bank_nodes);
rt_err_t rt_hwspinlock_bank_register(struct rt_hwspinlock_bank *bank)
{
struct rt_hwspinlock *hwlock;
if (!bank || !bank->ops || bank->locks_nr <= 0 || !bank->dev)
{
return -RT_EINVAL;
}
rt_list_init(&bank->list);
rt_ref_init(&bank->ref);
hwlock = &bank->locks[0];
for (int i = 0; i < bank->locks_nr; ++i, ++hwlock)
{
rt_spin_lock_init(&hwlock->lock);
hwlock->used = RT_FALSE;
}
rt_spin_lock(&hwspinlock_ops_lock);
rt_list_insert_after(&hwspinlock_bank_nodes, &bank->list);
rt_spin_unlock(&hwspinlock_ops_lock);
rt_dm_dev_bind_fwdata(bank->dev, RT_NULL, bank);
return RT_EOK;
}
rt_err_t rt_hwspinlock_bank_unregister(struct rt_hwspinlock_bank *bank)
{
rt_err_t err;
if (!bank)
{
return -RT_EINVAL;
}
rt_spin_lock(&hwspinlock_ops_lock);
if (rt_ref_read(&bank->ref) == 1)
{
rt_dm_dev_unbind_fwdata(bank->dev, RT_NULL);
err = RT_EOK;
}
else
{
err = -RT_EBUSY;
}
rt_spin_unlock(&hwspinlock_ops_lock);
return err;
}
rt_err_t rt_hwspin_trylock_raw(struct rt_hwspinlock *hwlock,
rt_ubase_t *out_irq_level)
{
rt_err_t err;
if (rt_unlikely(!hwlock))
{
return -RT_EINVAL;
}
if (out_irq_level)
{
*out_irq_level = rt_spin_lock_irqsave(&hwlock->lock);
}
else
{
rt_spin_lock(&hwlock->lock);
}
err = hwlock->bank->ops->trylock(hwlock);
if (rt_unlikely(err))
{
if (out_irq_level)
{
rt_spin_unlock_irqrestore(&hwlock->lock, *out_irq_level);
}
else
{
rt_spin_unlock(&hwlock->lock);
}
}
rt_hw_dmb();
return err;
}
rt_err_t rt_hwspin_lock_timeout_raw(struct rt_hwspinlock *hwlock,
rt_uint32_t timeout_ms, rt_ubase_t *out_irq_level)
{
rt_err_t err;
rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond(timeout_ms);
for (;;)
{
err = rt_hwspin_trylock_raw(hwlock, out_irq_level);
if (err != -RT_EBUSY)
{
break;
}
if (timeout < rt_tick_get())
{
return -RT_ETIMEOUT;
}
if (hwlock->bank->ops->relax)
{
hwlock->bank->ops->relax(hwlock);
}
}
return err;
}
void rt_hwspin_unlock_raw(struct rt_hwspinlock *hwlock,
rt_ubase_t *out_irq_level)
{
if (rt_unlikely(!hwlock))
{
return;
}
rt_hw_dmb();
hwlock->bank->ops->unlock(hwlock);
if (out_irq_level)
{
rt_spin_unlock_irqrestore(&hwlock->lock, *out_irq_level);
}
else
{
rt_spin_unlock(&hwlock->lock);
}
}
static struct rt_hwspinlock *hwspinlock_get(struct rt_hwspinlock_bank *bank, int id)
{
struct rt_hwspinlock *hwlock = RT_NULL;
if (bank)
{
int offset = id - bank->base_id;
if (!bank->locks[offset].used)
{
hwlock = &bank->locks[offset];
}
}
else
{
rt_list_for_each_entry(bank, &hwspinlock_bank_nodes, list)
{
hwlock = rt_err_ptr(-RT_EBUSY);
for (int i = 0; i < bank->locks_nr; ++i)
{
if (!bank->locks[i].used)
{
hwlock = &bank->locks[i];
goto _found;
}
}
}
}
_found:
if (!rt_is_err_or_null(hwlock))
{
hwlock->used = RT_TRUE;
}
return hwlock;
}
struct rt_hwspinlock *rt_hwspinlock_get(void)
{
struct rt_hwspinlock *lock;
rt_spin_lock(&hwspinlock_ops_lock);
lock = hwspinlock_get(RT_NULL, -1);
rt_spin_unlock(&hwspinlock_ops_lock);
return lock;
}
struct rt_hwspinlock *rt_hwspinlock_get_by_index(struct rt_device *dev, int index)
{
return rt_ofw_get_hwspinlock_by_index(dev->ofw_node, index);
}
struct rt_hwspinlock *rt_hwspinlock_get_by_name(struct rt_device *dev, const char *name)
{
return rt_ofw_get_hwspinlock_by_name(dev->ofw_node, name);
}
static void hwspinlock_release(struct ref *r)
{
struct rt_hwspinlock_bank *bank = rt_container_of(r, struct rt_hwspinlock_bank, ref);
LOG_E("%s is release", rt_dm_dev_get_name(bank->dev));
(void)bank;
RT_ASSERT(0);
}
void rt_hwspinlock_put(struct rt_hwspinlock *hwlock)
{
if (hwlock)
{
rt_ref_put(&hwlock->bank->ref, &hwspinlock_release);
hwlock->used = RT_TRUE;
}
}
struct rt_hwspinlock *rt_ofw_get_hwspinlock_by_index(struct rt_ofw_node *np, int index)
{
rt_err_t err;
struct rt_ofw_node *bank_np;
struct rt_ofw_cell_args args;
struct rt_hwspinlock *lock;
struct rt_hwspinlock_bank *bank;
if (!np && index < 0)
{
return rt_err_ptr(-RT_EINVAL);
}
err = rt_ofw_parse_phandle_cells(np, "hwlocks", "#hwlock-cells", index, &args);
if (err)
{
return rt_err_ptr(err);
}
bank_np = args.data;
if (!rt_ofw_data(bank_np))
{
rt_platform_ofw_request(bank_np);
}
rt_spin_lock(&hwspinlock_ops_lock);
bank = rt_ofw_data(bank_np);
rt_ofw_node_put(bank_np);
if (!bank || args.args_count != 1)
{
lock = rt_err_ptr(-RT_ENOSYS);
}
else
{
lock = hwspinlock_get(bank, bank->base_id + args.args[0]);
}
rt_spin_unlock(&hwspinlock_ops_lock);
return lock;
}
struct rt_hwspinlock *rt_ofw_get_hwspinlock_by_name(struct rt_ofw_node *np, const char *name)
{
int index;
if (!np || !name)
{
return rt_err_ptr(-RT_EINVAL);
}
index = rt_ofw_prop_index_of_string(np, "hwlock-names", name);
if (index < 0)
{
return rt_err_ptr(index);
}
return rt_ofw_get_hwspinlock_by_index(np, index);
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-23 GuEe-GUI first version
*/
#ifndef __HWSPINLOCK_DM_H__
#define __HWSPINLOCK_DM_H__
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/misc.h>
#include <ref.h>
struct rt_hwspinlock
{
struct rt_hwspinlock_bank *bank;
struct rt_spinlock lock;
rt_bool_t used;
void *priv;
};
struct rt_hwspinlock_ops
{
rt_err_t (*trylock)(struct rt_hwspinlock *hwlock);
void (*unlock)(struct rt_hwspinlock *hwlock);
void (*relax)(struct rt_hwspinlock *hwlock);
};
struct rt_hwspinlock_bank
{
rt_list_t list;
struct rt_ref ref;
struct rt_device *dev;
const struct rt_hwspinlock_ops *ops;
int base_id;
rt_size_t locks_nr;
struct rt_hwspinlock locks[];
};
#define hwspinlock_bank_alloc(obj, locks_nr) \
rt_calloc(1, sizeof(typeof(*obj)) + sizeof(struct rt_hwspinlock) * (locks_nr))
rt_inline int hwspinlock_find_id(struct rt_hwspinlock *hwlock)
{
return hwlock->bank->base_id + (hwlock - &hwlock->bank->locks[0]);
}
#endif /* __HWSPINLOCK_DM_H__ */

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-23 GuEe-GUI first version
*/
#ifndef __HWSPINLOCK_H__
#define __HWSPINLOCK_H__
#include <rtdef.h>
struct rt_hwspinlock;
struct rt_hwspinlock_ops;
struct rt_hwspinlock_bank;
rt_err_t rt_hwspinlock_bank_register(struct rt_hwspinlock_bank *bank);
rt_err_t rt_hwspinlock_bank_unregister(struct rt_hwspinlock_bank *bank);
rt_err_t rt_hwspin_trylock_raw(struct rt_hwspinlock *hwlock,
rt_ubase_t *out_irq_level);
rt_err_t rt_hwspin_lock_timeout_raw(struct rt_hwspinlock *hwlock,
rt_uint32_t timeout_ms, rt_ubase_t *out_irq_level);
void rt_hwspin_unlock_raw(struct rt_hwspinlock *hwlock,
rt_ubase_t *out_irq_level);
rt_inline rt_err_t rt_hwspin_trylock(struct rt_hwspinlock *hwlock)
{
return rt_hwspin_trylock_raw(hwlock, RT_NULL);
}
rt_inline rt_err_t rt_hwspin_trylock_irqsave(struct rt_hwspinlock *hwlock,
rt_ubase_t *out_irq_level)
{
return rt_hwspin_trylock_raw(hwlock, out_irq_level);
}
rt_inline rt_err_t rt_hwspin_lock_timeout(struct rt_hwspinlock *hwlock,
rt_uint32_t timeout_ms)
{
return rt_hwspin_lock_timeout_raw(hwlock, timeout_ms, RT_NULL);
}
rt_inline rt_err_t rt_hwspin_lock_timeout_irqsave(struct rt_hwspinlock *hwlock,
rt_uint32_t timeout_ms, rt_ubase_t *out_level)
{
return rt_hwspin_lock_timeout_raw(hwlock, timeout_ms, out_level);
}
rt_inline void rt_hwspin_unlock(struct rt_hwspinlock *hwlock)
{
rt_hwspin_unlock_raw(hwlock, RT_NULL);
}
rt_inline void rt_hwspin_unlock_irqsave(struct rt_hwspinlock *hwlock,
rt_ubase_t *out_level)
{
rt_hwspin_unlock_raw(hwlock, out_level);
}
struct rt_hwspinlock *rt_hwspinlock_get(void);
struct rt_hwspinlock *rt_hwspinlock_get_by_index(struct rt_device *dev, int index);
struct rt_hwspinlock *rt_hwspinlock_get_by_name(struct rt_device *dev, const char *name);
void rt_hwspinlock_put(struct rt_hwspinlock *hwlock);
struct rt_hwspinlock *rt_ofw_get_hwspinlock_by_index(struct rt_ofw_node *np, int index);
struct rt_hwspinlock *rt_ofw_get_hwspinlock_by_name(struct rt_ofw_node *np, const char *name);
#endif /* __HWSPINLOCK_H__ */

View File

@ -67,6 +67,10 @@ extern "C" {
#include "drivers/dma.h"
#endif /* RT_USING_DMA */
#ifdef RT_USING_HWSPINLOCK
#include "drivers/hwspinlock.h"
#endif /* RT_USING_HWSPINLOCK */
#include "drivers/iio.h"
#ifdef RT_USING_NVME