11/*
2- * Copyright (c) 2023 Sequans Communications
2+ * Copyright (c) 2025 Sequans Communications
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
66
77#define DT_DRV_COMPAT sqn_hwspinlock
88
9- #include <zephyr/device.h>
109#include <zephyr/kernel.h>
1110#include <zephyr/sys/sys_io.h>
11+ #include <zephyr/sys/__assert.h>
1212#include <zephyr/drivers/hwspinlock.h>
1313
14- #include <zephyr/sys/printk.h>
1514#include <zephyr/logging/log.h>
16- LOG_MODULE_REGISTER (sqn_hwspinlock );
15+ LOG_MODULE_REGISTER (sqn_hwspinlock , CONFIG_HWSPINLOCK_INIT_PRIORITY );
16+
17+ #include <soc.h>
18+
19+ /*
20+ * HWSPINLOCK mechanism:
21+ * When a hwspinlock is unlocked, its register value is 0.
22+ * To claim the spinlock, the user write its cluster id.
23+ * To release it, the user write its cluster id again.
24+ * Trying to write another value does not update the value of the register.
25+ * To check that the user holds the lock, it reads the register and compare the value with its
26+ * cluster id.
27+ */
1728
1829struct sqn_hwspinlock_data {
1930 DEVICE_MMIO_RAM ;
@@ -24,45 +35,30 @@ struct sqn_hwspinlock_config {
2435 uint32_t num_locks ;
2536};
2637
27- static inline mem_addr_t get_lock_addr (const struct device * dev , uint32_t id )
28- {
29- return (mem_addr_t )(DEVICE_MMIO_GET (dev ) + id * sizeof (uint32_t ));
30- }
38+ #define DEV_DATA (dev ) ((struct sqn_hwspinlock_data *)dev->data)
39+ #define DEV_CFG (dev ) ((const struct sqn_hwspinlock_config *)dev->config)
3140
3241/*
33- * To define CPU id, we use the affinity2 and affinity1
34- * fields of the MPIDR register .
42+ * CURRENT_CLUSTER value starts at 0. Because 0 is a reserved value for the register, increment all
43+ * ids by 1 .
3544 */
36- static uint8_t mpidr_to_cpuid (uint64_t mpidr_val )
37- {
38- uint8_t cpuid = ((mpidr_val >> 8 ) & 0x0F ) | ((mpidr_val >> 12 ) & 0xF0 );
45+ #define HWSPINLOCK_CLUSTER_ID (CURRENT_CLUSTER + 1)
3946
40- return ++ cpuid ;
47+ static inline mem_addr_t get_lock_addr (const struct device * dev , uint32_t id )
48+ {
49+ return (mem_addr_t )(DEVICE_MMIO_GET (dev ) + id * sizeof (uint32_t ));
4150}
4251
4352static int sqn_hwspinlock_trylock (const struct device * dev , uint32_t id )
4453{
45- const struct sqn_hwspinlock_config * config = dev -> config ;
46- uint8_t cpuid ;
54+ mem_addr_t lock_addr = get_lock_addr (dev , id );
4755
48- if (id > config -> num_locks ) {
49- return - EINVAL ;
50- }
51-
52- /*
53- * If the register value is equal to cpuid, this means that the current
54- * core has already locked the HW spinlock.
55- * If not, we try to lock the HW spinlock by writing cpuid, then check
56- * whether it is locked.
57- */
58-
59- cpuid = mpidr_to_cpuid (read_mpidr_el1 ());
60- if (sys_read8 (get_lock_addr (dev , id )) == cpuid ) {
61- return 0 ;
62- }
56+ __ASSERT (id < DEV_CFG (dev )-> num_locks , "Invalid hwspinlock id" );
57+ __ASSERT (sys_read8 (lock_addr ) != HWSPINLOCK_CLUSTER_ID ,
58+ "Tried to lock hwspinlock already locked by this cluster" );
6359
64- sys_write8 (cpuid , get_lock_addr ( dev , id ) );
65- if (sys_read8 (get_lock_addr ( dev , id )) == cpuid ) {
60+ sys_write8 (HWSPINLOCK_CLUSTER_ID , lock_addr );
61+ if (sys_read8 (lock_addr ) == HWSPINLOCK_CLUSTER_ID ) {
6662 return 0 ;
6763 }
6864
@@ -71,59 +67,37 @@ static int sqn_hwspinlock_trylock(const struct device *dev, uint32_t id)
7167
7268static void sqn_hwspinlock_lock (const struct device * dev , uint32_t id )
7369{
74- const struct sqn_hwspinlock_config * config = dev -> config ;
75- uint8_t cpuid ;
76-
77- if (id > config -> num_locks ) {
78- LOG_ERR ("unsupported hwspinlock id '%d'" , id );
79- return ;
80- }
81-
82- /*
83- * Writing cpuid is equivalent to trying to lock HW spinlock, after
84- * which we check whether we've locked by reading the register value
85- * and comparing it with cpuid.
86- */
70+ mem_addr_t lock_addr = get_lock_addr (dev , id );
8771
88- cpuid = mpidr_to_cpuid (read_mpidr_el1 ());
89- if (sys_read8 (get_lock_addr (dev , id )) == 0 ) {
90- sys_write8 (cpuid , get_lock_addr (dev , id ));
91- }
72+ __ASSERT (id < DEV_CFG (dev )-> num_locks , "Invalid hwspinlock id" );
73+ __ASSERT (sys_read8 (lock_addr ) != HWSPINLOCK_CLUSTER_ID ,
74+ "Tried to lock hwspinlock already locked by this cluster" );
9275
93- while (sys_read8 (get_lock_addr (dev , id )) != cpuid ) {
76+ sys_write8 (HWSPINLOCK_CLUSTER_ID , lock_addr );
77+ while (sys_read8 (lock_addr ) != HWSPINLOCK_CLUSTER_ID ) {
9478 k_busy_wait (CONFIG_SQN_HWSPINLOCK_RELAX_TIME );
95- sys_write8 (cpuid , get_lock_addr ( dev , id ) );
79+ sys_write8 (HWSPINLOCK_CLUSTER_ID , lock_addr );
9680 }
9781}
9882
9983static void sqn_hwspinlock_unlock (const struct device * dev , uint32_t id )
10084{
101- const struct sqn_hwspinlock_config * config = dev -> config ;
102- uint8_t cpuid ;
85+ mem_addr_t lock_addr = get_lock_addr (dev , id );
10386
104- if (id > config -> num_locks ) {
105- LOG_ERR ("unsupported hwspinlock id '%d'" , id );
106- return ;
107- }
108-
109- /*
110- * If the HW spinlock register value is equal to the cpuid and we write
111- * the cpuid, then the register value will be 0. So to unlock the
112- * hwspinlock, we write cpuid.
113- */
87+ __ASSERT (id < DEV_CFG (dev )-> num_locks , "Invalid hwspinlock id" );
88+ __ASSERT (sys_read8 (lock_addr ) == HWSPINLOCK_CLUSTER_ID ,
89+ "Tried to unlock hwspinlock not locked by this cluster" );
11490
115- cpuid = mpidr_to_cpuid ( read_mpidr_el1 ());
116- sys_write8 (cpuid , get_lock_addr ( dev , id ) );
91+ /* Unlock by writing our own cluster id again */
92+ sys_write8 (HWSPINLOCK_CLUSTER_ID , lock_addr );
11793}
11894
11995static uint32_t sqn_hwspinlock_get_max_id (const struct device * dev )
12096{
121- const struct sqn_hwspinlock_config * config = dev -> config ;
122-
123- return config -> num_locks ;
97+ return DEV_CFG (dev )-> num_locks ;
12498}
12599
126- static DEVICE_API (hwspinlock , hwspinlock_api ) = {
100+ static DEVICE_API (hwspinlock , sqn_hwspinlock_api ) = {
127101 .trylock = sqn_hwspinlock_trylock ,
128102 .lock = sqn_hwspinlock_lock ,
129103 .unlock = sqn_hwspinlock_unlock ,
@@ -137,18 +111,14 @@ static int sqn_hwspinlock_init(const struct device *dev)
137111 return 0 ;
138112}
139113
140- #define SQN_HWSPINLOCK_INIT (idx ) \
141- static struct sqn_hwspinlock_data sqn_hwspinlock##idx##_data; \
142- static const struct sqn_hwspinlock_config sqn_hwspinlock##idx##_config = { \
143- DEVICE_MMIO_ROM_INIT(DT_DRV_INST(idx)), \
144- .num_locks = DT_INST_PROP(idx, num_locks), \
145- }; \
146- DEVICE_DT_INST_DEFINE(idx, \
147- sqn_hwspinlock_init, \
148- NULL, \
149- &sqn_hwspinlock##idx##_data, \
150- &sqn_hwspinlock##idx##_config, \
151- PRE_KERNEL_1, CONFIG_HWSPINLOCK_INIT_PRIORITY, \
152- &hwspinlock_api)
114+ #define SQN_HWSPINLOCK_INIT (idx ) \
115+ static struct sqn_hwspinlock_data sqn_hwspinlock##idx##_data; \
116+ static const struct sqn_hwspinlock_config sqn_hwspinlock##idx##_config = { \
117+ DEVICE_MMIO_ROM_INIT(DT_DRV_INST(idx)), \
118+ .num_locks = DT_INST_PROP(idx, num_locks), \
119+ }; \
120+ DEVICE_DT_INST_DEFINE(idx, sqn_hwspinlock_init, NULL, &sqn_hwspinlock##idx##_data, \
121+ &sqn_hwspinlock##idx##_config, PRE_KERNEL_1, \
122+ CONFIG_HWSPINLOCK_INIT_PRIORITY, &sqn_hwspinlock_api)
153123
154124DT_INST_FOREACH_STATUS_OKAY (SQN_HWSPINLOCK_INIT );
0 commit comments