Skip to content

Commit 2c86e65

Browse files
Fymytejhedberg
authored andcommitted
drivers: hwspinlock: sqn: use cluster id instead of core mpidr
Using the mpidr of the core to lock the hwspinlock meant it acted as a spinlock in the cluster itself, which is exactly the opposite of what we tried to achieve. Additionally, this tied the driver to the arm architecture, even though the HW was not. Signed-off-by: Pierrick Guillaume <[email protected]>
1 parent ddc3779 commit 2c86e65

File tree

3 files changed

+59
-85
lines changed

3 files changed

+59
-85
lines changed

drivers/hwspinlock/Kconfig.sqn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ config SQN_HWSPINLOCK
1111
Enable HW spinlock for SQN
1212

1313
if SQN_HWSPINLOCK
14+
1415
config SQN_HWSPINLOCK_RELAX_TIME
1516
int "Sequans HW spinlock relax time"
1617
default 50
Lines changed: 53 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
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

1829
struct 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

4352
static 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

7268
static 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

9983
static 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

11995
static 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

154124
DT_INST_FOREACH_STATUS_OKAY(SQN_HWSPINLOCK_INIT);
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# Copyright (c) 2023 Sequans Communications
1+
# Copyright (c) 2025 Sequans Communications
22
# SPDX-License-Identifier: Apache-2.0
33

44
description: SQN Hardware spinlocks
55

66
compatible: "sqn,hwspinlock"
77

8-
include: base.yaml
8+
include: [base.yaml, hwspinlock-controller.yaml]
99

1010
properties:
1111
reg:
@@ -14,3 +14,6 @@ properties:
1414
num-locks:
1515
type: int
1616
required: true
17+
18+
hwlock-cells:
19+
- id

0 commit comments

Comments
 (0)