typedef uint64_t dmclk_frequency_t;Represents clock frequency in Hertz (Hz). Uses 64-bit unsigned integer to support high-frequency clocks.
typedef uint64_t dmclk_time_us_t;Represents time in microseconds (µs). Used for delay operations.
typedef enum
{
dmclk_source_unkown = 0, /**< Unknown clock source */
dmclk_source_internal, /**< Internal RC oscillator */
dmclk_source_external, /**< External crystal or oscillator */
dmclk_source_hibernation, /**< Low-power hibernation clock source */
} dmclk_source_t;Enumerates available clock sources.
typedef enum
{
dmclk_ioctl_cmd_get_frequency = DMDRVI_IOCTL_CUSTOM_BASE, /**< Get current clock frequency */
dmclk_ioctl_cmd_set_source, /**< Set clock source */
dmclk_ioctl_cmd_get_source, /**< Get clock source */
dmclk_ioctl_cmd_set_tolerance, /**< Set frequency tolerance */
dmclk_ioctl_cmd_get_tolerance, /**< Get frequency tolerance */
dmclk_ioctl_cmd_set_oscillator_frequency, /**< Set oscillator frequency */
dmclk_ioctl_cmd_get_oscillator_frequency, /**< Get oscillator frequency */
dmclk_ioctl_cmd_set_target_frequency, /**< Set target frequency */
dmclk_ioctl_cmd_get_target_frequency, /**< Get target frequency */
dmclk_ioctl_cmd_reconfigure, /**< Reconfigure clock with current settings */
dmclk_ioctl_cmd_get_domain_frequency, /**< Get a named peripheral clock domain's frequency; arg = dmclk_domain_query_t* */
dmclk_ioctl_cmd_max
} dmclk_ioctl_cmd_t;IOCTL commands for clock device control. They are numbered from
DMDRVI_IOCTL_CUSTOM_BASE so they do not collide with the standard
DMDRVI_IOCTL_* commands; any command outside this range (e.g. the
DMDRVI_IOCTL_BLOCK_GET_INFO / DMDRVI_IOCTL_MONITOR_GET_POLICY probes sent by
dmdevfs) is answered with -ENOTTY.
dmdrvi_context_t dmclk_dmdrvi_create(dmini_context_t config, const dmdrvi_dev_num_t* dev_num);Creates and initializes a new DMCLK device context.
Parameters:
config: DMINI configuration context with clock parametersdev_num: Device number structure (reserved for future use)
Returns:
- Valid context handle on success
- NULL on failure
Configuration Requirements:
The config parameter must contain a [dmclk] section with:
source: Clock source string ("internal", "external", "hibernation")target_frequency: Target frequency in Hztolerance: Acceptable frequency deviation in Hzoscillator_frequency: Oscillator frequency (required for external/hibernation sources)
Example:
dmini_context_t config = dmini_load("config.ini");
dmdrvi_dev_num_t dev_num = {0};
dmdrvi_context_t ctx = dmclk_dmdrvi_create(config, &dev_num);
if (ctx == NULL) {
// Handle error
}void dmclk_dmdrvi_free(dmdrvi_context_t context);Frees resources associated with a DMCLK device context.
Parameters:
context: Device context to free
Note: Always call this function when done with a context to prevent memory leaks.
void* dmclk_dmdrvi_open(dmdrvi_context_t context, int flags);Opens a clock device handle for operations.
Parameters:
context: Device context fromdmclk_dmdrvi_createflags: Open flags (DMDRVI_O_RDONLY supported, DMDRVI_O_WRONLY not supported)
Returns:
- Valid device handle on success
- NULL on failure
Example:
void* handle = dmclk_dmdrvi_open(ctx, DMDRVI_O_RDONLY);
if (handle == NULL) {
// Handle error
}void dmclk_dmdrvi_close(dmdrvi_context_t context, void* handle);Closes a device handle.
Parameters:
context: Device contexthandle: Device handle to close
size_t dmclk_dmdrvi_read(dmdrvi_context_t context, void* handle, void* buffer, size_t size);Reads current clock configuration as a formatted string.
Parameters:
context: Device contexthandle: Device handlebuffer: Buffer to receive datasize: Size of buffer in bytes
Returns: Number of bytes written to buffer
Output Format:
frequency=<current_freq>;source=<source_name>;oscillator_frequency=<osc_freq>
Example:
char buffer[256];
size_t bytes = dmclk_dmdrvi_read(ctx, handle, buffer, sizeof(buffer));
// buffer contains: "frequency=84000000;source=external;oscillator_frequency=8000000"size_t dmclk_dmdrvi_write(dmdrvi_context_t context, void* handle, const void* buffer, size_t size);Write operation - not implemented.
Returns: 0 (operation not supported)
int dmclk_dmdrvi_ioctl(dmdrvi_context_t context, void* handle, int command, void* arg);Performs control operations on the clock device.
Parameters:
context: Device contexthandle: Device handlecommand: IOCTL command fromdmclk_ioctl_cmd_targ: Command-specific argument (pointer to data)
Returns:
- 0 on success
- Negative error code on failure
Gets the current actual clock frequency.
dmclk_frequency_t freq;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_frequency, &freq);Gets the current clock source.
dmclk_source_t source;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_source, &source);Gets the frequency tolerance.
dmclk_frequency_t tolerance;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_tolerance, &tolerance);Gets the oscillator frequency.
dmclk_frequency_t osc_freq;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_oscillator_frequency, &osc_freq);Gets the target frequency.
dmclk_frequency_t target;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_target_frequency, &target);All set commands automatically trigger a clock reconfiguration after updating the parameter.
Sets the clock source.
dmclk_source_t source = dmclk_source_external;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_source, &source);Sets the frequency tolerance.
dmclk_frequency_t tolerance = 1000; // ±1 kHz
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_tolerance, &tolerance);Sets the oscillator frequency.
dmclk_frequency_t osc_freq = 8000000; // 8 MHz
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_oscillator_frequency, &osc_freq);Sets the target frequency.
dmclk_frequency_t target = 100000000; // 100 MHz
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_target_frequency, &target);Reconfigures the clock with current settings without changing any parameters.
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_reconfigure, NULL);int dmclk_dmdrvi_flush(dmdrvi_context_t context, void* handle);Flushes device buffers - no operation for clock device.
Returns: 0
int dmclk_dmdrvi_stat(dmdrvi_context_t context, void* handle, dmdrvi_stat_t* stat);Gets device statistics.
Parameters:
context: Device contexthandle: Device handlestat: Pointer to stat structure to fill
Returns:
- 0 on success
- Negative error code on failure
Stat Fields:
size: Size of the read data stringmode: File permissions (0444 - read-only)
The port layer implements hardware-specific clock configuration. These functions are called internally by the core module.
int dmclk_port_configure_internal(dmclk_frequency_t target_freq, dmclk_frequency_t tolerance);Configures the system clock using the internal oscillator.
int dmclk_port_configure_external(dmclk_frequency_t target_freq, dmclk_frequency_t tolerance, dmclk_frequency_t oscillator_freq);Configures the system clock using an external crystal or oscillator.
int dmclk_port_configure_hibernatation(dmclk_frequency_t target_freq, dmclk_frequency_t tolerance, dmclk_frequency_t oscillator_freq);Configures the system clock using the hibernation clock source.
Note: Function name contains a typo (should be "hibernation") but is kept for API compatibility.
void dmclk_port_delay_us(dmclk_time_us_t time_us);Delays execution for the specified number of microseconds.
dmclk_frequency_t dmclk_port_get_current_frequency(void);Returns the current system clock frequency in Hz.
The module uses standard errno error codes:
| Code | Description |
|---|---|
| 0 | Success |
| -EINVAL | Invalid parameter or configuration |
| -ENOMEM | Memory allocation failure |
Error messages are logged using the DMOD logging system (DMOD_LOG_ERROR, DMOD_LOG_INFO).
The DMCLK module is not inherently thread-safe. If multiple threads need to access the same device context, external synchronization must be provided by the application.
#include "dmclk.h"
#include "dmdrvi.h"
#include "dmini.h"
#include <stdio.h>
int main(void)
{
// Load configuration file
dmini_context_t config = dmini_load("config.ini");
if (config == NULL) {
printf("Failed to load config\n");
return -1;
}
// Create clock device
dmdrvi_dev_num_t dev_num = {0};
dmdrvi_context_t ctx = dmclk_dmdrvi_create(config, &dev_num);
if (ctx == NULL) {
printf("Failed to create clock device\n");
dmini_free(config);
return -1;
}
// Open device
void* handle = dmclk_dmdrvi_open(ctx, DMDRVI_O_RDONLY);
if (handle == NULL) {
printf("Failed to open device\n");
dmclk_dmdrvi_free(ctx);
dmini_free(config);
return -1;
}
// Read current configuration
char buffer[256];
dmclk_dmdrvi_read(ctx, handle, buffer, sizeof(buffer));
printf("Current config: %s\n", buffer);
// Get actual frequency
dmclk_frequency_t freq;
dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_frequency, &freq);
printf("Actual frequency: %lu Hz\n", freq);
// Change to 100 MHz
dmclk_frequency_t new_freq = 100000000;
if (dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_target_frequency, &new_freq) == 0) {
dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_frequency, &freq);
printf("New frequency: %lu Hz\n", freq);
}
// Cleanup
dmclk_dmdrvi_close(ctx, handle);
dmclk_dmdrvi_free(ctx);
dmini_free(config);
return 0;
}dmclk_port.h provides clock domains named for their consumers, including
dmclk_domain_sai1 and dmclk_domain_sai2. Existing SDIO/USB/RNG enum values
and API signatures are unchanged.
dmclk_frequency_t actual_hz;
int rc = dmclk_port_acquire_domain(dmclk_domain_sai2,
12288000, 3000, &actual_hz);
if (rc == 0) {
/* Configure SAI's own dividers using actual_hz, then enable SAI.
Later stop SAI and disable its peripheral clock gate. */
rc = dmclk_port_release_domain(dmclk_domain_sai2);
}The frequency is the peripheral's input clock, not the sample rate or MCLK output. Tolerance is absolute Hz; zero requires an exact frequency. The output pointer is mandatory and remains unchanged on failure. Frequencies read from the STM32F7 SAI port are rounded to the nearest Hz; acquisition checks the unrounded rational frequency against the requested tolerance.
Each successful acquire holds one reference and requires one release.
Compatible requests share the clock without interruption. Conflicting requests
return -EBUSY; an unreachable frequency returns -ERANGE, unsupported domains
return -ENOTSUP, invalid arguments or an unmatched release return -EINVAL.
Hardware readiness timeouts return -ETIMEDOUT. A failed release keeps its
reference so the caller can retry. Concurrent clock mutations return -EBUSY.
System-clock configuration and port deinitialization return -EBUSY while any
domain is reserved. SDIO/USB/RNG reservations borrow their existing frequency;
they do not retune the main PLL. After the final release the legacy configuration
paths are available again.
STM32F7 can configure an idle PLLSAI or PLLI2S with the existing oscillator and PLLM, or borrow an already-running compatible source. The port never retunes a running auxiliary PLL (including PLLSAI used by LTDC). Keep the affected SAI peripheral clock gate disabled during initial acquisition and final release. The port preserves unrelated muxes/dividers and restores its previous settings after the last release. Unknown HSE frequency, unavailable input sources, or unachievable accuracy can prevent acquisition. External SAI_CKIN is unsupported.
STM32F4 currently returns -ENOTSUP for SAI reservations because the existing
family-wide port does not distinguish the F4 variants' different SAI routing.
Existing clock configuration and CLK48 domain queries remain supported.