summaryrefslogtreecommitdiff
path: root/components/drivers
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2025-12-09 19:53:55 +0800
committerR b b666 <[email protected]>2025-12-11 11:08:33 +0800
commitdd20176cbae0a4f5867829ce776e09245239d01c (patch)
tree958b6d2a2c6774e052f2ef27ea623b8f90efa9ac /components/drivers
parentd76356b94ec8e0ea45e7b1d5996a827e0f52be15 (diff)
[dm][hwspinlock] support hwspinlock
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 <[email protected]>
Diffstat (limited to 'components/drivers')
-rwxr-xr-xcomponents/drivers/Kconfig1
-rw-r--r--components/drivers/hwspinlock/Kconfig15
-rw-r--r--components/drivers/hwspinlock/SConscript15
-rw-r--r--components/drivers/hwspinlock/hwspinlock.c319
-rw-r--r--components/drivers/hwspinlock/hwspinlock_dm.h125
-rw-r--r--components/drivers/include/drivers/hwspinlock.h180
-rw-r--r--components/drivers/include/rtdevice.h4
7 files changed, 659 insertions, 0 deletions
diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig
index 9b0c2d652d..de082cd6ad 100755
--- a/components/drivers/Kconfig
+++ b/components/drivers/Kconfig
@@ -24,6 +24,7 @@ rsource "wlan/Kconfig"
rsource "led/Kconfig"
rsource "input/Kconfig"
rsource "mailbox/Kconfig"
+rsource "hwspinlock/Kconfig"
rsource "phye/Kconfig"
rsource "ata/Kconfig"
rsource "nvme/Kconfig"
diff --git a/components/drivers/hwspinlock/Kconfig b/components/drivers/hwspinlock/Kconfig
new file mode 100644
index 0000000000..de0d13c948
--- /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 100644
index 0000000000..81ed9adcca
--- /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 0000000000..1e8ca635da
--- /dev/null
+++ b/components/drivers/hwspinlock/hwspinlock.c
@@ -0,0 +1,319 @@
+/*
+ * 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 <rtthread.h>
+
+#include <cpuport.h>
+
+#define DBG_TAG "rtdm.hwspinlock"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "hwspinlock_dm.h"
+
+static RT_DEFINE_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)
+ {
+ hwlock->bank = bank;
+ hwlock->used = RT_FALSE;
+ rt_spin_lock_init(&hwlock->lock);
+ }
+
+ 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_list_remove(&bank->list);
+ 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 (!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 (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 (!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;
+ rt_ref_get(&hwlock->bank->ref);
+ }
+
+ 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 rt_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_spin_lock(&hwspinlock_ops_lock);
+ hwlock->used = RT_FALSE;
+ rt_spin_unlock(&hwspinlock_ops_lock);
+
+ rt_ref_put(&hwlock->bank->ref, &hwspinlock_release);
+ }
+}
+
+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 0000000000..521b5618b5
--- /dev/null
+++ b/components/drivers/hwspinlock/hwspinlock_dm.h
@@ -0,0 +1,125 @@
+/*
+ * 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>
+
+/**
+ * @brief Hardware spinlock instance
+ *
+ * Represents a single hardware spinlock managed by a bank. Each lock
+ * maintains a local software spinlock to protect internal state and a
+ * usage flag to indicate whether it has been allocated to clients.
+ */
+struct rt_hwspinlock
+{
+ struct rt_hwspinlock_bank *bank;
+ struct rt_spinlock lock; /**< Local software spinlock to guard structure access */
+
+ rt_bool_t used; /**< Indicates whether this lock is currently in use */
+ void *priv; /**< Driver-specific private data */
+};
+
+/**
+ * @brief Hardware spinlock operations
+ *
+ * A hardware spinlock driver must provide these low-level operations.
+ * trylock() performs a non-blocking acquire. relax() is called inside
+ * busy-wait loops to reduce power usage or yield the bus.
+ */
+struct rt_hwspinlock_ops
+{
+ /**
+ * @brief Attempt to acquire the hardware spinlock (non-blocking)
+ *
+ * Should immediately return RT_EOK on success or -RT_EBUSY if the lock
+ * is already taken by another CPU or hardware requester.
+ */
+ rt_err_t (*trylock)(struct rt_hwspinlock *hwlock);
+
+ /**
+ * @brief Release the hardware spinlock
+ */
+ void (*unlock)(struct rt_hwspinlock *hwlock);
+
+ /**
+ * @brief Relax operation called during spinning
+ *
+ * Typically implemented using power-saving instructions (e.g., WFE),
+ * yielding the CPU, or other hardware-supported wait mechanisms.
+ */
+ void (*relax)(struct rt_hwspinlock *hwlock);
+};
+
+/**
+ * @brief Hardware spinlock bank
+ *
+ * Represents a group of hardware spinlocks exported by the same device
+ * or hardware module. A bank is registered globally so that locks can be
+ * retrieved by index, name, or device tree bindings.
+ */
+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; /**< Global base ID used to calculate lock identifiers */
+ rt_size_t locks_nr; /**< Number of hardware spinlocks in this bank */
+
+ /**
+ * @brief Array of hardware spinlock instances
+ *
+ * Implemented as a flexible array member. Memory is allocated using
+ * hwspinlock_bank_alloc(), ensuring space for all lock structures.
+ */
+ struct rt_hwspinlock locks[];
+};
+
+/**
+ * @brief Allocate a hardware spinlock bank with a given number of locks
+ *
+ * The allocation includes both the bank structure and the flexible array
+ * of hardware spinlock objects.
+ *
+ * @param obj The object pointer used to store the bank
+ * @param locks_nr Number of hardware spinlocks to allocate
+ *
+ * @return Pointer to the allocated memory or NULL on failure
+ */
+#define hwspinlock_bank_alloc(obj, locks_nr) \
+ rt_calloc(1, sizeof(typeof(*obj)) + sizeof(struct rt_hwspinlock) * (locks_nr))
+
+/**
+ * @brief Compute the global hardware spinlock ID
+ *
+ * The global ID is calculated as:
+ * bank->base_id + index
+ * where the index is derived from the hardware spinlock's position inside
+ * the bank->locks array.
+ *
+ * @param hwlock Pointer to a hardware spinlock
+ *
+ * @return The global lock identifier
+ */
+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 0000000000..f89c6af517
--- /dev/null
+++ b/components/drivers/include/drivers/hwspinlock.h
@@ -0,0 +1,180 @@
+/*
+ * 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;
+
+/**
+ * @brief Register a hardware spinlock bank
+ *
+ * A bank represents a contiguous set of hardware spinlocks provided by
+ * a hardware module. This API installs the bank into the system-wide list
+ * so that each individual lock can be retrieved and used by clients.
+ *
+ * @param bank Pointer to the hardware spinlock bank
+ * @return RT_EOK on success, otherwise an error code
+ */
+rt_err_t rt_hwspinlock_bank_register(struct rt_hwspinlock_bank *bank);
+
+/**
+ * @brief Unregister a hardware spinlock bank
+ *
+ * Removes the bank from the global registry. All locks under the bank
+ * become unavailable. Caller must ensure no active users remain.
+ *
+ * @param bank Pointer to the hardware spinlock bank
+ * @return RT_EOK on success, otherwise an error code
+ */
+rt_err_t rt_hwspinlock_bank_unregister(struct rt_hwspinlock_bank *bank);
+
+/**
+ * @brief Try to acquire a hardware spinlock (raw interface)
+ *
+ * The raw interface allows hardware drivers to retrieve the IRQ level
+ * before acquiring the lock, enabling IRQ-safe usage scenarios.
+ *
+ * @param hwlock Pointer to the hardware spinlock
+ * @param out_irq_level If non-NULL, returns saved IRQ level
+ *
+ * @return RT_EOK if lock is acquired, -RT_EBUSY if lock is held by others
+ */
+rt_err_t rt_hwspin_trylock_raw(struct rt_hwspinlock *hwlock,
+ rt_ubase_t *out_irq_level);
+
+/**
+ * @brief Acquire a hardware spinlock with timeout (raw interface)
+ *
+ * Spins or relaxes according to the hardware implementation until the
+ * lock becomes available or timeout expires. IRQ level handling is optional.
+ *
+ * @param hwlock Pointer to the hardware spinlock
+ * @param timeout_ms Timeout in milliseconds
+ * @param out_irq_level If non-NULL, returns saved IRQ level
+ *
+ * @return RT_EOK on success, -RT_ETIMEOUT if timed out
+ */
+rt_err_t rt_hwspin_lock_timeout_raw(struct rt_hwspinlock *hwlock,
+ rt_uint32_t timeout_ms, rt_ubase_t *out_irq_level);
+
+/**
+ * @brief Release a hardware spinlock (raw interface)
+ *
+ * Restores IRQ state if @p out_irq_level is provided.
+ *
+ * @param hwlock Pointer to the hardware spinlock
+ * @param out_irq_level IRQ level saved during lock operation
+ */
+void rt_hwspin_unlock_raw(struct rt_hwspinlock *hwlock,
+ rt_ubase_t *out_irq_level);
+
+/**
+ * @brief Try to acquire a hardware spinlock (no IRQ management)
+ */
+rt_inline rt_err_t rt_hwspin_trylock(struct rt_hwspinlock *hwlock)
+{
+ return rt_hwspin_trylock_raw(hwlock, RT_NULL);
+}
+
+/**
+ * @brief Try to acquire a hardware spinlock with IRQ saving
+ */
+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);
+}
+
+/**
+ * @brief Acquire a hardware spinlock with timeout (no IRQ management)
+ */
+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);
+}
+
+/**
+ * @brief Acquire a hardware spinlock with timeout and IRQ saving
+ */
+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);
+}
+
+/**
+ * @brief Release a hardware spinlock (no IRQ management)
+ */
+rt_inline void rt_hwspin_unlock(struct rt_hwspinlock *hwlock)
+{
+ rt_hwspin_unlock_raw(hwlock, RT_NULL);
+}
+
+/**
+ * @brief Release a hardware spinlock and restore IRQ level
+ */
+rt_inline void rt_hwspin_unlock_irqsave(struct rt_hwspinlock *hwlock,
+ rt_ubase_t *out_level)
+{
+ rt_hwspin_unlock_raw(hwlock, out_level);
+}
+
+/**
+ * @brief Acquire a free hardware spinlock from any registered bank
+ *
+ * The returned lock must later be released via rt_hwspinlock_put().
+ *
+ * @return Pointer to a hardware spinlock, or NULL if none available
+ */
+struct rt_hwspinlock *rt_hwspinlock_get(void);
+
+/**
+ * @brief Get hardware spinlock by index from a specific device
+ *
+ * @param dev Device providing hardware spinlocks
+ * @param index Hardware spinlock index within device/bank
+ *
+ * @return Pointer to the hardware spinlock, or NULL if invalid index
+ */
+struct rt_hwspinlock *rt_hwspinlock_get_by_index(struct rt_device *dev, int index);
+
+/**
+ * @brief Get hardware spinlock by name from a specific device
+ *
+ * Names are assigned by device drivers and mapped to lock indices.
+ *
+ * @return Pointer to the hardware spinlock, or NULL if name not found
+ */
+struct rt_hwspinlock *rt_hwspinlock_get_by_name(struct rt_device *dev, const char *name);
+
+/**
+ * @brief Release hardware spinlock reference obtained from get()
+ *
+ * Does not unlock the spinlock; only decreases usage count.
+ */
+void rt_hwspinlock_put(struct rt_hwspinlock *hwlock);
+
+/**
+ * @brief Get hardware spinlock by index from device tree node
+ */
+struct rt_hwspinlock *rt_ofw_get_hwspinlock_by_index(struct rt_ofw_node *np, int index);
+
+/**
+ * @brief Get hardware spinlock by name from device tree node
+ */
+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 88a388b6f0..d55a41f30b 100644
--- a/components/drivers/include/rtdevice.h
+++ b/components/drivers/include/rtdevice.h
@@ -66,6 +66,10 @@ extern "C" {
#include "drivers/mailbox.h"
#endif /* RT_USING_MBOX */
+#ifdef RT_USING_HWSPINLOCK
+#include "drivers/hwspinlock.h"
+#endif /* RT_USING_HWSPINLOCK */
+
#ifdef RT_USING_BLK
#include "drivers/blk.h"
#endif /* RT_USING_BLK */