diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 9f907892caf..cbd6a5652d8 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -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" diff --git a/components/drivers/hwspinlock/Kconfig b/components/drivers/hwspinlock/Kconfig new file mode 100644 index 00000000000..de0d13c948e --- /dev/null +++ b/components/drivers/hwspinlock/Kconfig @@ -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 diff --git a/components/drivers/hwspinlock/SConscript b/components/drivers/hwspinlock/SConscript new file mode 100755 index 00000000000..81ed9adcca6 --- /dev/null +++ b/components/drivers/hwspinlock/SConscript @@ -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') diff --git a/components/drivers/hwspinlock/hwspinlock.c b/components/drivers/hwspinlock/hwspinlock.c new file mode 100644 index 00000000000..92a968f6614 --- /dev/null +++ b/components/drivers/hwspinlock/hwspinlock.c @@ -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 + +#define DBG_TAG "rtdm.hwspinlock" +#define DBG_LVL DBG_INFO +#include + +#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); +} diff --git a/components/drivers/hwspinlock/hwspinlock_dm.h b/components/drivers/hwspinlock/hwspinlock_dm.h new file mode 100644 index 00000000000..f2b6b348f2b --- /dev/null +++ b/components/drivers/hwspinlock/hwspinlock_dm.h @@ -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 +#include +#include +#include +#include + +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__ */ diff --git a/components/drivers/include/drivers/hwspinlock.h b/components/drivers/include/drivers/hwspinlock.h new file mode 100644 index 00000000000..33acbe0b1cf --- /dev/null +++ b/components/drivers/include/drivers/hwspinlock.h @@ -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 + +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__ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index fd51ea7db4d..126e41fd826 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -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