1
1
/*
2
- * Copyright (c) 2023 Sequans Communications
2
+ * Copyright (c) 2025 Sequans Communications
3
3
*
4
4
* SPDX-License-Identifier: Apache-2.0
5
5
*/
6
6
7
7
#define DT_DRV_COMPAT sqn_hwspinlock
8
8
9
- #include <zephyr/device.h>
10
9
#include <zephyr/kernel.h>
11
10
#include <zephyr/sys/sys_io.h>
11
+ #include <zephyr/sys/__assert.h>
12
12
#include <zephyr/drivers/hwspinlock.h>
13
13
14
- #include <zephyr/sys/printk.h>
15
14
#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
+ */
17
28
18
29
struct sqn_hwspinlock_data {
19
30
DEVICE_MMIO_RAM ;
@@ -24,45 +35,30 @@ struct sqn_hwspinlock_config {
24
35
uint32_t num_locks ;
25
36
};
26
37
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)
31
40
32
41
/*
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 .
35
44
*/
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)
39
46
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 ));
41
50
}
42
51
43
52
static int sqn_hwspinlock_trylock (const struct device * dev , uint32_t id )
44
53
{
45
- const struct sqn_hwspinlock_config * config = dev -> config ;
46
- uint8_t cpuid ;
54
+ mem_addr_t lock_addr = get_lock_addr (dev , id );
47
55
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" );
63
59
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 ) {
66
62
return 0 ;
67
63
}
68
64
@@ -71,59 +67,37 @@ static int sqn_hwspinlock_trylock(const struct device *dev, uint32_t id)
71
67
72
68
static void sqn_hwspinlock_lock (const struct device * dev , uint32_t id )
73
69
{
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 );
87
71
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" );
92
75
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 ) {
94
78
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 );
96
80
}
97
81
}
98
82
99
83
static void sqn_hwspinlock_unlock (const struct device * dev , uint32_t id )
100
84
{
101
- const struct sqn_hwspinlock_config * config = dev -> config ;
102
- uint8_t cpuid ;
85
+ mem_addr_t lock_addr = get_lock_addr (dev , id );
103
86
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" );
114
90
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 );
117
93
}
118
94
119
95
static uint32_t sqn_hwspinlock_get_max_id (const struct device * dev )
120
96
{
121
- const struct sqn_hwspinlock_config * config = dev -> config ;
122
-
123
- return config -> num_locks ;
97
+ return DEV_CFG (dev )-> num_locks ;
124
98
}
125
99
126
- static DEVICE_API (hwspinlock , hwspinlock_api ) = {
100
+ static DEVICE_API (hwspinlock , sqn_hwspinlock_api ) = {
127
101
.trylock = sqn_hwspinlock_trylock ,
128
102
.lock = sqn_hwspinlock_lock ,
129
103
.unlock = sqn_hwspinlock_unlock ,
@@ -137,18 +111,14 @@ static int sqn_hwspinlock_init(const struct device *dev)
137
111
return 0 ;
138
112
}
139
113
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)
153
123
154
124
DT_INST_FOREACH_STATUS_OKAY (SQN_HWSPINLOCK_INIT );
0 commit comments