Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions boards/ambiq/apollo510_evb/apollo510_evb.dts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/*
* SPDX-FileCopyrightText: Copyright 2025 Ambiq Micro Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

/dts-v1/;
#include <ambiq/ambiq_apollo510.dtsi>
#include "apollo510_evb-pinctrl.dtsi"
Expand All @@ -19,6 +25,7 @@
ambiq,xo32m = &xo32m_xtal;
ambiq,xo32k = &xo32k_xtal;
ambiq,extrefclk = &extrefclk;
zephyr,crc = &crc32;
};

aliases {
Expand All @@ -30,6 +37,7 @@
sw1 = &button1;
pwm-led0 = &pwm_led0;
sdhc0 = &sdio0;
crc0 = &crc32;
};

sram0: memory@SSRAM_BASE_NAME {
Expand Down Expand Up @@ -299,3 +307,7 @@ zephyr_udc0: &usb {
&gpio192_223 {
status = "okay";
};

&crc32 {
status = "okay";
};
1 change: 1 addition & 0 deletions boards/ambiq/apollo510_evb/apollo510_evb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ supported:
- adc
- watchdog
- counter
- crc
- gpio
- spi
- i2c
Expand Down
1 change: 1 addition & 0 deletions drivers/crc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/crc.h)
zephyr_library()

# zephyr-keep-sorted-start
zephyr_library_sources_ifdef(CONFIG_CRC_AMBIQ crc_ambiq.c)
zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_NXP crc_nxp.c)
zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_NXP_LPC crc_nxp_lpc.c)
zephyr_library_sources_ifdef(CONFIG_CRC_DRIVER_RENESAS_RA crc_renesas_ra.c)
Expand Down
1 change: 1 addition & 0 deletions drivers/crc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ source "drivers/crc/Kconfig.sf32lb"
source "drivers/crc/Kconfig.silabs"
source "drivers/crc/Kconfig.stm32"
# zephyr-keep-sorted-stop
source "drivers/crc/Kconfig.ambiq"

config CRC_DRIVER_HAS_CRC4
bool
Expand Down
16 changes: 16 additions & 0 deletions drivers/crc/Kconfig.ambiq
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Ambiq CRC configuration options

# Copyright (c) 2025 Ambiq
# Author: Richard S Wheatley
# SPDX-License-Identifier: Apache-2.0

config CRC_AMBIQ
bool "Ambiq Apollo510 CRC support"
default y
depends on DT_HAS_AMBIQ_HW_CRC32_ENABLED
depends on SOC_SERIES_APOLLO5X
select CRC_DRIVER_HAS_CRC32_IEEE
help
Enable hardware-accelerated CRC32 IEEE support for Ambiq
Apollo510 SoCs. Uses the SECURITY CRC engine
with boundary-crossing protection for TCM and SSRAM regions.
279 changes: 279 additions & 0 deletions drivers/crc/crc_ambiq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*
* Copyright (c) 2025 Ambiq Micro, Inc.
* Author: Richard S Wheatley
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT ambiq_hw_crc32

#include <soc.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/drivers/crc.h>
#include <zephyr/kernel.h>
#include <zephyr/cache.h>
#include <zephyr/pm/policy.h>

LOG_MODULE_REGISTER(ambiq_hw_crc32, CONFIG_CRC_DRIVER_LOG_LEVEL);

/*
* This driver decomposes am_hal_crc() into begin / update / finish to fit
* the Zephyr CRC streaming API. The boundary-checking logic from
* am_hal_crc_find_next_boundary() is called directly so that each update()
* call correctly splits at TCM 4 KB and major memory-region boundaries.
*
* The SECURITY->RESULT register is the hardware CRC accumulator:
* - Seeded in begin() from ctx->seed
* - Left running across update() calls (am_hal_crc32 with
* g_bCrcInitialized = true skips re-seeding)
* - Read in finish() and returned via ctx->result
*/

struct crc_ambiq_data {
/*
* Binary semaphore (not k_mutex) because the Zephyr CRC API does not
* require begin() and finish() to run on the same thread. Mutexes are
* owned and can only be unlocked by their locking thread, so a begin
* in one context with a finish in another would fail. The semaphore
* allows cross-thread release.
*/
struct k_sem lock;
};

/*
* Release the resources acquired in crc_ambiq_begin(). Called both on the
* normal path from crc_ambiq_finish() and on hardware-error paths in
* crc_ambiq_update() so the caller never has to call finish() after a failure.
*/
static void crc_ambiq_release(const struct device *dev, struct crc_ctx *ctx)
{
struct crc_ambiq_data *data = dev->data;

am_hal_crc_finalize();
ctx->state = CRC_STATE_IDLE;
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_RAM, PM_ALL_SUBSTATES);
k_sem_give(&data->lock);
}

static int crc_ambiq_begin(const struct device *dev, struct crc_ctx *ctx)
{
struct crc_ambiq_data *data = dev->data;

if (ctx == NULL) {
return -EINVAL;
}

/*
* The Ambiq SECURITY engine only supports a single fixed configuration:
* type = CRC32_IEEE
* polynomial = 0x04C11DB7 (baked into silicon)
* reversed = CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT
* (hardware always produces reflected CRC32 IEEE)
*
* Reject anything else so callers get a visible error instead of a
* silently-wrong result. The seed is configurable via the RESULT
* register and is honored below.
*/
if (ctx->type != CRC32_IEEE) {
return -ENOTSUP;
}

if (ctx->polynomial != CRC32_IEEE_POLY) {
return -EINVAL;
}

if (ctx->reversed != (CRC_FLAG_REVERSE_INPUT | CRC_FLAG_REVERSE_OUTPUT)) {
return -EINVAL;
}

k_sem_take(&data->lock, K_FOREVER);

if (SECURITY->CTRL_b.ENABLE) {
k_sem_give(&data->lock);
return -EBUSY;
}

/*
* Prevent the system from entering deep sleep while the CRC
* session is active. The SECURITY peripheral lives in the
* CRYPTO power domain -- if it gets gated between begin() and
* finish(), the RESULT accumulator is lost.
*/
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_RAM, PM_ALL_SUBSTATES);

/*
* Seed the hardware accumulator from ctx->seed so callers can run
* incremental / chained CRC32 starting from a prior result.
*/
SECURITY->RESULT = ctx->seed;

/*
* Tell am_hal_crc32() NOT to re-seed on subsequent calls.
* This keeps the RESULT register accumulating across all chunks
* and across multiple update() calls.
*/
am_hal_crc_set_init();

ctx->state = CRC_STATE_IN_PROGRESS;
ctx->result = ctx->seed;

return 0;
}

static int crc_ambiq_update(const struct device *dev, struct crc_ctx *ctx, const void *buffer,

Check failure on line 124 in drivers/crc/crc_ambiq.c

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 39 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=zephyrproject-rtos_zephyr&issues=AZ6I-llioIfZp13FjkBV&open=AZ6I-llioIfZp13FjkBV&pullRequest=110366
size_t bufsize)
{
uint32_t addr;
uint32_t remaining;
uint32_t chunk;
uint32_t boundary;
uint32_t hal_status;

/* Basic validation first */
if (ctx == NULL) {
return -EINVAL;
}

/* If the session is active, any caller error should release resources */
if (buffer == NULL || bufsize == 0) {
if (ctx->state == CRC_STATE_IN_PROGRESS) {
crc_ambiq_release(dev, ctx);
}
return -EINVAL;
}

if (ctx->state != CRC_STATE_IN_PROGRESS) {
return -EINVAL;
}

if (bufsize & 0x3) {
crc_ambiq_release(dev, ctx);
return -EINVAL;
}

addr = (uint32_t)buffer;

/*
* The SECURITY CRC engine uses DMA which bypasses the CPU data cache.
* The DMA source buffer must be 32-byte (cache line) aligned to
* guarantee correct cache flush semantics.
*
* If the caller's buffer is already aligned, flush and feed it
* directly. Otherwise, copy through a local aligned staging buffer.
*
* In both paths the boundary-aware loop from am_hal_crc() splits at
* TCM 4 KB and major memory-region crossings. SECURITY->RESULT
* accumulates across all chunks because am_hal_crc_set_init() was
* called in begin().
*/
#define CRC_DMA_CHUNK_SIZE 256
if ((addr & 0x1F) == 0) {
/* Buffer is 32-byte aligned -- use it directly */
sys_cache_data_flush_range((void *)buffer, bufsize);

Check failure on line 173 in drivers/crc/crc_ambiq.c

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

cast from 'const void *' to 'void *' drops const qualifier

See more on https://sonarcloud.io/project/issues?id=zephyrproject-rtos_zephyr&issues=AZ6I-llioIfZp13FjkBU&open=AZ6I-llioIfZp13FjkBU&pullRequest=110366

remaining = (uint32_t)bufsize;

while (remaining > 0) {
boundary = am_hal_crc_find_next_boundary(addr, remaining);
chunk = (boundary < addr + remaining)
? (boundary - addr) : remaining;

if (chunk > 0) {
hal_status = am_hal_crc32(addr, chunk, &ctx->result);
if (hal_status != AM_HAL_STATUS_SUCCESS) {
crc_ambiq_release(dev, ctx);
return -EIO;
}
}
addr += chunk;
remaining -= chunk;
}
} else {
/* Buffer is not aligned -- copy through a local staging buffer */
static uint8_t dma_buf[CRC_DMA_CHUNK_SIZE] __aligned(32);
const uint8_t *src = (const uint8_t *)buffer;

remaining = (uint32_t)bufsize;

while (remaining > 0) {
uint32_t n = MIN(remaining, sizeof(dma_buf));

memcpy(dma_buf, src, n);
sys_cache_data_flush_range(dma_buf, n);

uint32_t local_addr = (uint32_t)dma_buf;
uint32_t left = n;

while (left > 0) {
boundary = am_hal_crc_find_next_boundary(
local_addr, left);
chunk = (boundary < local_addr + left)
? (boundary - local_addr) : left;

if (chunk > 0) {
hal_status = am_hal_crc32(local_addr,
chunk,
&ctx->result);
if (hal_status != AM_HAL_STATUS_SUCCESS) {

Check failure on line 218 in drivers/crc/crc_ambiq.c

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 4 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=zephyrproject-rtos_zephyr&issues=AZ6I-llioIfZp13FjkBW&open=AZ6I-llioIfZp13FjkBW&pullRequest=110366
crc_ambiq_release(dev, ctx);
return -EIO;
}
}
local_addr += chunk;
left -= chunk;
}

src += n;
remaining -= n;
}
}

return 0;
}

static int crc_ambiq_finish(const struct device *dev, struct crc_ctx *ctx)
{
if (ctx == NULL) {
return -EINVAL;
}

if (ctx->state != CRC_STATE_IN_PROGRESS) {
return -EINVAL;
}

crc_ambiq_release(dev, ctx);

return 0;
}

static int crc_ambiq_init(const struct device *dev)
{
struct crc_ambiq_data *data = dev->data;

/* Binary semaphore: initial count 1, max 1 (one outstanding CRC session) */
k_sem_init(&data->lock, 1, 1);

return 0;
}

/**
* @brief Ambiq CRC32 hardware driver API.
*
* Provides hardware-accelerated CRC32_IEEE (polynomial 0x04C11DB7) calculation using the
* SECURITY peripheral. The hardware produces reflected CRC32 with reversed input/output.
* Operations are protected by PM policy locks to prevent CRYPTO power domain gating during
* active CRC sessions.
*/
static DEVICE_API(crc, crc_ambiq_api) = {
.begin = crc_ambiq_begin,
.update = crc_ambiq_update,
.finish = crc_ambiq_finish,
};

#define CRC_AMBIQ_INIT(n) \
static struct crc_ambiq_data crc_ambiq_data_##n; \
DEVICE_DT_INST_DEFINE(n, crc_ambiq_init, NULL, &crc_ambiq_data_##n, NULL, POST_KERNEL, \
CONFIG_CRC_DRIVER_INIT_PRIORITY, &crc_ambiq_api);

DT_INST_FOREACH_STATUS_OKAY(CRC_AMBIQ_INIT)
6 changes: 6 additions & 0 deletions dts/arm/ambiq/ambiq_apollo510.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@
status = "disabled";
};

crc32: crc32@40030030 {
compatible = "ambiq,hw-crc32";
reg = <0x40030030 0x4>;
status = "disabled";
};

stimer0: stimer@STIMER_BASE_NAME {
compatible = "ambiq,stimer";
reg = <STIMER_REG_BASE STIMER_REG_SIZE>;
Expand Down
16 changes: 16 additions & 0 deletions dts/bindings/crc/ambiq,hw-crc32.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2025, Ambiq
# Author: Richard S Wheatley
# SPDX-License-Identifier: Apache-2.0

description: |
Hardware CRC32 Cyclic Redundancy Check Module.

This module implements a hardware-based CRC32 calculation.

compatible: "ambiq,hw-crc32"

include: base.yaml

properties:
reg:
required: true
2 changes: 2 additions & 0 deletions samples/drivers/crc/boards/apollo510_evb.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_SAMPLE_CRC_VARIANT_CRC8=n
CONFIG_SAMPLE_CRC_VARIANT_CRC32_IEEE=y
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ manifest:
groups:
- hal
- name: hal_ambiq
revision: c03d2f9902cfc3ff0d3f78ead538e9726251f7f9
revision: 72007192ac28e0e7484a44a0b9ff3a31d08a5b28
path: modules/hal/ambiq
groups:
- hal
Expand Down
Loading