Skip to content

Commit f66b731

Browse files
Aziz Idomarcarlescufi
authored andcommitted
drivers: hwspinlock: implement sqn hwspinlock driver
When we lock an hwspinlock, we must write the CPU identifier to the hwspinlock register. If we want to unlock the locked hwspinlock, we have to rewrite the same CPU identifier. To define the CPU identifier, we use affinity 1 and affinity 2 fields of the MPIDR register. Signed-off-by: Aziz Idomar <[email protected]>
1 parent c3ac598 commit f66b731

File tree

5 files changed

+190
-0
lines changed

5 files changed

+190
-0
lines changed

drivers/hwspinlock/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
zephyr_library()
44

5+
zephyr_library_sources_ifdef(CONFIG_SQN_HWSPINLOCK sqn_hwspinlock.c)
56
zephyr_library_sources_ifdef(CONFIG_USERSPACE hwspinlock_handlers.c)

drivers/hwspinlock/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ config HWSPINLOCK_INIT_PRIORITY
1616
help
1717
HW spinlock driver device initialization priority.
1818

19+
source "drivers/hwspinlock/Kconfig.sqn"
20+
1921
endif

drivers/hwspinlock/Kconfig.sqn

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Sequans HW spinlock configuration
2+
3+
# Copyright (c) 2023 Sequans Communications.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config SQN_HWSPINLOCK
7+
bool "Sequans HW spinlock Driver"
8+
default y
9+
depends on DT_HAS_SQN_HWSPINLOCK_ENABLED
10+
help
11+
Enable HW spinlock for SQN
12+
13+
if SQN_HWSPINLOCK
14+
config SQN_HWSPINLOCK_RELAX_TIME
15+
int "Sequans HW spinlock relax time"
16+
default 50
17+
help
18+
Default HW spinlock relax time in microseconds.
19+
20+
endif #SQN_HWSPINLOCK

drivers/hwspinlock/sqn_hwspinlock.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright (c) 2023 Sequans Communications
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT sqn_hwspinlock
8+
9+
#include <zephyr/device.h>
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/sys/sys_io.h>
12+
#include <zephyr/drivers/hwspinlock.h>
13+
14+
#include <zephyr/sys/printk.h>
15+
#include <zephyr/logging/log.h>
16+
LOG_MODULE_REGISTER(sqn_hwspinlock);
17+
18+
struct sqn_hwspinlock_data {
19+
DEVICE_MMIO_RAM;
20+
};
21+
22+
struct sqn_hwspinlock_config {
23+
DEVICE_MMIO_ROM;
24+
uint32_t num_locks;
25+
};
26+
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+
}
31+
32+
/*
33+
* To define CPU id, we use the affinity2 and affinity1
34+
* fields of the MPIDR register.
35+
*/
36+
static uint8_t mpidr_to_cpuid(uint64_t mpidr_val)
37+
{
38+
uint8_t cpuid = ((mpidr_val >> 8) & 0x0F) | ((mpidr_val >> 12) & 0xF0);
39+
40+
return ++cpuid;
41+
}
42+
43+
static int sqn_hwspinlock_trylock(const struct device *dev, uint32_t id)
44+
{
45+
const struct sqn_hwspinlock_config *config = dev->config;
46+
uint8_t cpuid;
47+
48+
if (id > config->num_locks)
49+
return -EINVAL;
50+
51+
/*
52+
* If the register value is equal to cpuid, this means that the current
53+
* core has already locked the HW spinlock.
54+
* If not, we try to lock the HW spinlock by writing cpuid, then check
55+
* whether it is locked.
56+
*/
57+
58+
cpuid = mpidr_to_cpuid(read_mpidr_el1());
59+
if (sys_read8(get_lock_addr(dev, id)) == cpuid)
60+
return 0;
61+
62+
sys_write8(cpuid, get_lock_addr(dev, id));
63+
if (sys_read8(get_lock_addr(dev, id)) == cpuid)
64+
return 0;
65+
66+
return -EBUSY;
67+
}
68+
69+
static void sqn_hwspinlock_lock(const struct device *dev, uint32_t id)
70+
{
71+
const struct sqn_hwspinlock_config *config = dev->config;
72+
uint8_t cpuid;
73+
74+
if (id > config->num_locks) {
75+
LOG_ERR("unsupported hwspinlock id '%d'", id);
76+
return;
77+
}
78+
79+
/*
80+
* Writing cpuid is equivalent to trying to lock HW spinlock, after
81+
* which we check whether we've locked by reading the register value
82+
* and comparing it with cpuid.
83+
*/
84+
85+
cpuid = mpidr_to_cpuid(read_mpidr_el1());
86+
if (sys_read8(get_lock_addr(dev, id)) == 0) {
87+
sys_write8(cpuid, get_lock_addr(dev, id));
88+
}
89+
90+
while (sys_read8(get_lock_addr(dev, id)) != cpuid) {
91+
k_busy_wait(CONFIG_SQN_HWSPINLOCK_RELAX_TIME);
92+
sys_write8(cpuid, get_lock_addr(dev, id));
93+
}
94+
}
95+
96+
static void sqn_hwspinlock_unlock(const struct device *dev, uint32_t id)
97+
{
98+
const struct sqn_hwspinlock_config *config = dev->config;
99+
uint8_t cpuid;
100+
101+
if (id > config->num_locks) {
102+
LOG_ERR("unsupported hwspinlock id '%d'", id);
103+
return;
104+
}
105+
106+
/*
107+
* If the HW spinlock register value is equal to the cpuid and we write
108+
* the cpuid, then the register value will be 0. So to unlock the
109+
* hwspinlock, we write cpuid.
110+
*/
111+
112+
cpuid = mpidr_to_cpuid(read_mpidr_el1());
113+
sys_write8(cpuid, get_lock_addr(dev, id));
114+
}
115+
116+
static uint32_t sqn_hwspinlock_get_max_id(const struct device *dev)
117+
{
118+
const struct sqn_hwspinlock_config *config = dev->config;
119+
120+
return config->num_locks;
121+
}
122+
123+
static const struct hwspinlock_driver_api hwspinlock_api = {
124+
.trylock = sqn_hwspinlock_trylock,
125+
.lock = sqn_hwspinlock_lock,
126+
.unlock = sqn_hwspinlock_unlock,
127+
.get_max_id = sqn_hwspinlock_get_max_id,
128+
};
129+
130+
static int sqn_hwspinlock_init(const struct device *dev)
131+
{
132+
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
133+
134+
return 0;
135+
}
136+
137+
#define SQN_HWSPINLOCK_INIT(idx) \
138+
static struct sqn_hwspinlock_data sqn_hwspinlock##idx##_data; \
139+
static struct sqn_hwspinlock_config sqn_hwspinlock##idx##_config = { \
140+
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(idx)), \
141+
.num_locks = DT_INST_PROP(idx, num_locks), \
142+
}; \
143+
DEVICE_DT_INST_DEFINE(idx, \
144+
sqn_hwspinlock_init, \
145+
NULL, \
146+
&sqn_hwspinlock##idx##_data, \
147+
&sqn_hwspinlock##idx##_config, \
148+
PRE_KERNEL_1, CONFIG_HWSPINLOCK_INIT_PRIORITY, \
149+
&hwspinlock_api)
150+
151+
DT_INST_FOREACH_STATUS_OKAY(SQN_HWSPINLOCK_INIT);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright (c) 2023 Sequans Communications
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: SQN Hardware spinlocks
5+
6+
compatible: "sqn,hwspinlock"
7+
8+
include: base.yaml
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
num_locks:
15+
type: int
16+
required: true

0 commit comments

Comments
 (0)