Skip to content

Commit 4b51773

Browse files
author
Zoe Kaute
committed
drivers: crc: Add hardware-based CRC driver
Adds support for hardware-based CRC-8-CCITT and CRC-32-IEEE calculations. Signed-off-by: Zoe Kaute <zoe.kaute@brillpower.com>
1 parent b2a061d commit 4b51773

File tree

11 files changed

+490
-0
lines changed

11 files changed

+490
-0
lines changed

MAINTAINERS.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,22 @@ Release Notes:
11001100
tests:
11011101
- drivers.counter
11021102

1103+
"Drivers: CRC":
1104+
status: maintained
1105+
maintainers:
1106+
- zkat2
1107+
files:
1108+
- drivers/crc/
1109+
- dts/bindings/crc/
1110+
- include/zephyr/drivers/crc.h
1111+
- samples/drivers/crc/
1112+
- tests/drivers/crc/
1113+
- doc/hardware/peripherals/crc.rst
1114+
labels:
1115+
- "area: CRC"
1116+
tests:
1117+
- drivers.crc
1118+
11031119
"Drivers: Crypto":
11041120
status: maintained
11051121
maintainers:

doc/hardware/peripherals/crc.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.. _crc_api:
2+
3+
Cyclic Redundancy Check (CRC)
4+
#############################
5+
6+
Overview
7+
********
8+
The Cyclic Redundancy Check (CRC) API provides functions for configuring and computing CRC values
9+
on hardware.
10+
11+
Configuration Options
12+
*********************
13+
14+
Related configuration options:
15+
16+
* :kconfig:option:`CRC_INIT_PRIORITY`
17+
18+
API Reference
19+
*************
20+
21+
.. doxygengroup:: crc_interface

doc/hardware/peripherals/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Peripherals
2020
charger.rst
2121
coredump.rst
2222
counter.rst
23+
crc.rst
2324
dac.rst
2425
dma.rst
2526
display/index.rst

drivers/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_subdirectory_ifdef(CONFIG_CLOCK_CONTROL clock_control)
2626
add_subdirectory_ifdef(CONFIG_CONSOLE console)
2727
add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump)
2828
add_subdirectory_ifdef(CONFIG_COUNTER counter)
29+
add_subdirectory_ifdef(CONFIG_CRC_HW crc)
2930
add_subdirectory_ifdef(CONFIG_CRYPTO crypto)
3031
add_subdirectory_ifdef(CONFIG_DAC dac)
3132
add_subdirectory_ifdef(CONFIG_DAI dai)

drivers/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ source "drivers/clock_control/Kconfig"
1818
source "drivers/console/Kconfig"
1919
source "drivers/coredump/Kconfig"
2020
source "drivers/counter/Kconfig"
21+
source "drivers/crc/Kconfig"
2122
source "drivers/crypto/Kconfig"
2223
source "drivers/dac/Kconfig"
2324
source "drivers/dai/Kconfig"

drivers/crc/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) 2024 Brill Power Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
zephyr_library_sources_ifdef(CONFIG_CRC_STM32 crc_stm32.c)

drivers/crc/Kconfig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) 2024 Brill Power Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
menuconfig CRC_HW
5+
bool "CRC HW acceleration"
6+
7+
if CRC_HW
8+
9+
module = CRC_HW
10+
module-str = CRC_HW
11+
source "subsys/logging/Kconfig.template.log_config"
12+
13+
config CRC_INIT_PRIORITY
14+
int "CRC init priority"
15+
default KERNEL_INIT_PRIORITY_DEVICE
16+
help
17+
CRC driver device initialization priority.
18+
19+
source "drivers/crc/Kconfig.stm32"
20+
21+
endif # CRC_HW

drivers/crc/Kconfig.stm32

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2024 Brill Power Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config CRC_STM32
5+
bool
6+
default y
7+
depends on DT_HAS_ST_STM32_CRC_ENABLED
8+
help
9+
Enable the driver implementation for STM32 microcontrollers

drivers/crc/crc_stm32.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* Copyright (c) 2024 Brill Power Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/logging/log.h>
8+
LOG_MODULE_REGISTER(crc_stm32, CONFIG_CRC_HW_LOG_LEVEL);
9+
10+
#include <errno.h>
11+
12+
#include <zephyr/device.h>
13+
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
14+
#include <zephyr/drivers/crc.h>
15+
#include <zephyr/sys/byteorder.h>
16+
17+
#include <soc.h>
18+
#include <stm32_ll_crc.h>
19+
20+
#define DT_DRV_COMPAT st_stm32_crc
21+
22+
struct crc_stm32_cfg {
23+
CRC_TypeDef *base;
24+
struct stm32_pclken pclken;
25+
};
26+
27+
struct crc_stm32_data {
28+
struct k_sem sem;
29+
};
30+
31+
static void crc_lock(const struct device *dev)
32+
{
33+
struct crc_stm32_data *data = (struct crc_stm32_data *)dev->data;
34+
35+
k_sem_take(&data->sem, K_FOREVER);
36+
}
37+
38+
static void crc_unlock(const struct device *dev)
39+
{
40+
struct crc_stm32_data *data = (struct crc_stm32_data *)dev->data;
41+
42+
k_sem_give(&data->sem);
43+
}
44+
45+
static int crc_stm32_begin(const struct device *dev, struct crc_ctx *ctx)
46+
{
47+
/* Attempt to take semaphore */
48+
crc_lock(dev);
49+
50+
/* Ensure ctx is not currently being updated */
51+
if (ctx->state == CRC_STATE_IN_PROGRESS) {
52+
crc_unlock(dev);
53+
return -EBUSY;
54+
}
55+
56+
ctx->state = CRC_STATE_IN_PROGRESS; /* Indicate calculation in progress */
57+
58+
const struct crc_stm32_cfg *cfg = dev->config;
59+
60+
LL_CRC_SetInputDataReverseMode(cfg->base, (ctx->flags & CRC_FLAG_REVERSE_INPUT)
61+
? LL_CRC_INDATA_REVERSE_WORD
62+
: LL_CRC_INDATA_REVERSE_NONE);
63+
LL_CRC_SetOutputDataReverseMode(cfg->base, (ctx->flags & CRC_FLAG_REVERSE_OUTPUT)
64+
? LL_CRC_OUTDATA_REVERSE_BIT
65+
: LL_CRC_INDATA_REVERSE_NONE);
66+
LL_CRC_ResetCRCCalculationUnit(cfg->base);
67+
68+
switch (ctx->type) {
69+
case CRC8_CCITT_HW: {
70+
uint8_t init_val = (uint8_t)(ctx->initial_value & 0xFF);
71+
uint8_t poly = (uint8_t)(ctx->polynomial & 0xFF);
72+
73+
LL_CRC_SetPolynomialSize(cfg->base, LL_CRC_POLYLENGTH_8B);
74+
LL_CRC_SetPolynomialCoef(cfg->base, poly);
75+
LL_CRC_SetInitialData(cfg->base, init_val);
76+
break;
77+
}
78+
79+
case CRC32_IEEE_HW: {
80+
uint32_t init_val = (uint32_t)(ctx->initial_value & 0xFFFFFFFF);
81+
uint32_t poly = (uint32_t)(ctx->polynomial & 0xFFFFFFFF);
82+
83+
LL_CRC_SetPolynomialSize(cfg->base, LL_CRC_POLYLENGTH_32B);
84+
LL_CRC_SetPolynomialCoef(cfg->base, poly);
85+
LL_CRC_SetInitialData(cfg->base, init_val);
86+
break;
87+
}
88+
89+
default:
90+
ctx->state = CRC_STATE_IDLE;
91+
crc_unlock(dev);
92+
return -ENOTSUP;
93+
}
94+
return 0;
95+
}
96+
97+
static int crc_stm32_update(const struct device *dev, struct crc_ctx *ctx, const void *buffer,
98+
size_t bufsize)
99+
{
100+
/* Ensure CRC calculation has been initialized by crc_begin() */
101+
if (ctx->state == CRC_STATE_IDLE) {
102+
return -EINVAL;
103+
}
104+
105+
const struct crc_stm32_cfg *cfg = dev->config;
106+
const uint8_t *buf = buffer;
107+
108+
switch (ctx->type) {
109+
case CRC8_CCITT_HW: {
110+
register uint32_t index = 0;
111+
112+
for (index = 0; index < bufsize; index++) {
113+
LL_CRC_FeedData8(cfg->base, buf[index]);
114+
}
115+
uint8_t result = LL_CRC_ReadData8(cfg->base);
116+
117+
ctx->result = (crc_result_t)result;
118+
break;
119+
}
120+
121+
case CRC32_IEEE_HW: {
122+
register uint32_t crc_data = 0;
123+
register uint32_t index = 0;
124+
125+
/* Compute the CRC of Data Buffer array*/
126+
for (index = 0; index < (bufsize / 4); index++) {
127+
crc_data = sys_get_le32(buf + 4 * index);
128+
LL_CRC_FeedData32(cfg->base, crc_data);
129+
}
130+
131+
/* Last bytes specific handling */
132+
if ((bufsize & 3) != 0) {
133+
if ((bufsize & 3) == 3) {
134+
LL_CRC_FeedData16(cfg->base, sys_get_le16(buf + 4 * index));
135+
LL_CRC_FeedData8(cfg->base, buf[4 * index + 2]);
136+
} else if ((bufsize & 3) == 2) {
137+
LL_CRC_FeedData16(cfg->base, sys_get_le16(buf + 4 * index));
138+
} else { /* ((bufsize & 3) == 1) */
139+
LL_CRC_FeedData8(cfg->base, buf[4 * index]);
140+
}
141+
}
142+
uint32_t result = (~LL_CRC_ReadData32(cfg->base));
143+
144+
ctx->result = (crc_result_t)result;
145+
break;
146+
}
147+
148+
default:
149+
ctx->state = CRC_STATE_IDLE;
150+
crc_unlock(dev);
151+
return -ENOTSUP;
152+
}
153+
154+
return 0;
155+
}
156+
157+
static int crc_stm32_finish(const struct device *dev, struct crc_ctx *ctx)
158+
{
159+
/* Ensure CRC calculation is in progress */
160+
if (ctx->state == CRC_STATE_IDLE) {
161+
return -EINVAL;
162+
}
163+
164+
ctx->state = CRC_STATE_IDLE; /* Indicate calculation done */
165+
crc_unlock(dev);
166+
167+
return 0;
168+
}
169+
170+
/**
171+
* @brief Called by Zephyr when instantiating device during boot
172+
* @param dev Pointer to the device structure for the driver instance.
173+
* @retval 0 On success
174+
* @retval -ENODEV Clock control device is not ready
175+
* @retval -EIO Fail to turn on appropriate clock for CRC instance
176+
*/
177+
static int crc_stm32_init(const struct device *dev)
178+
{
179+
const struct crc_stm32_cfg *cfg = dev->config;
180+
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
181+
182+
if (!device_is_ready(clk)) {
183+
LOG_ERR("CRC: Clock control device not ready");
184+
return -ENODEV;
185+
}
186+
187+
if (clock_control_on(clk, (clock_control_subsys_t *)&cfg->pclken) != 0) {
188+
LOG_ERR("CRC: Clock control device could not initialise");
189+
return -EIO;
190+
}
191+
192+
struct crc_stm32_data *data = dev->data;
193+
194+
k_sem_init(&data->sem, 1, 1);
195+
196+
return 0;
197+
}
198+
199+
static const struct crc_driver_api crc_stm32_driver_api = {
200+
.crc_begin = crc_stm32_begin,
201+
.crc_update = crc_stm32_update,
202+
.crc_finish = crc_stm32_finish,
203+
};
204+
205+
#define STM32_CRC_INIT(index) \
206+
\
207+
static const struct crc_stm32_cfg crc_stm32_cfg_##index = { \
208+
.base = (CRC_TypeDef *)DT_INST_REG_ADDR(index), \
209+
.pclken = \
210+
{ \
211+
.enr = DT_INST_CLOCKS_CELL(index, bits), \
212+
.bus = DT_INST_CLOCKS_CELL(index, bus), \
213+
}, \
214+
}; \
215+
\
216+
static struct crc_stm32_data crc_stm32_data_##index = {}; \
217+
\
218+
DEVICE_DT_INST_DEFINE(index, &crc_stm32_init, NULL, &crc_stm32_data_##index, \
219+
&crc_stm32_cfg_##index, POST_KERNEL, CONFIG_CRC_INIT_PRIORITY, \
220+
&crc_stm32_driver_api);
221+
222+
DT_INST_FOREACH_STATUS_OKAY(STM32_CRC_INIT)

dts/bindings/crc/st,stm32-crc.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2024 Brill Power Ltd.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: ST STM32 family CRC
5+
6+
compatible: "st,stm32-crc"
7+
8+
include: [base.yaml]
9+
10+
properties:
11+
reg:
12+
required: true
13+
14+
clocks:
15+
type: phandle-array
16+
required: true
17+
description: Clock information

0 commit comments

Comments
 (0)