Skip to content

Latest commit

 

History

History
494 lines (358 loc) · 13.5 KB

api-reference.md

File metadata and controls

494 lines (358 loc) · 13.5 KB

DMCLK API Reference

Types

dmclk_frequency_t

typedef uint64_t dmclk_frequency_t;

Represents clock frequency in Hertz (Hz). Uses 64-bit unsigned integer to support high-frequency clocks.

dmclk_time_us_t

typedef uint64_t dmclk_time_us_t;

Represents time in microseconds (µs). Used for delay operations.

dmclk_source_t

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.

dmclk_ioctl_cmd_t

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 Interface Functions

dmclk_dmdrvi_create

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 parameters
  • dev_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 Hz
  • tolerance: Acceptable frequency deviation in Hz
  • oscillator_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
}

dmclk_dmdrvi_free

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.

dmclk_dmdrvi_open

void* dmclk_dmdrvi_open(dmdrvi_context_t context, int flags);

Opens a clock device handle for operations.

Parameters:

  • context: Device context from dmclk_dmdrvi_create
  • flags: 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
}

dmclk_dmdrvi_close

void dmclk_dmdrvi_close(dmdrvi_context_t context, void* handle);

Closes a device handle.

Parameters:

  • context: Device context
  • handle: Device handle to close

dmclk_dmdrvi_read

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 context
  • handle: Device handle
  • buffer: Buffer to receive data
  • size: 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"

dmclk_dmdrvi_write

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)

dmclk_dmdrvi_ioctl

int dmclk_dmdrvi_ioctl(dmdrvi_context_t context, void* handle, int command, void* arg);

Performs control operations on the clock device.

Parameters:

  • context: Device context
  • handle: Device handle
  • command: IOCTL command from dmclk_ioctl_cmd_t
  • arg: Command-specific argument (pointer to data)

Returns:

  • 0 on success
  • Negative error code on failure

Get Commands

dmclk_ioctl_cmd_get_frequency

Gets the current actual clock frequency.

dmclk_frequency_t freq;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_frequency, &freq);
dmclk_ioctl_cmd_get_source

Gets the current clock source.

dmclk_source_t source;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_source, &source);
dmclk_ioctl_cmd_get_tolerance

Gets the frequency tolerance.

dmclk_frequency_t tolerance;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_tolerance, &tolerance);
dmclk_ioctl_cmd_get_oscillator_frequency

Gets the oscillator frequency.

dmclk_frequency_t osc_freq;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_oscillator_frequency, &osc_freq);
dmclk_ioctl_cmd_get_target_frequency

Gets the target frequency.

dmclk_frequency_t target;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_get_target_frequency, &target);

Set Commands

All set commands automatically trigger a clock reconfiguration after updating the parameter.

dmclk_ioctl_cmd_set_source

Sets the clock source.

dmclk_source_t source = dmclk_source_external;
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_source, &source);
dmclk_ioctl_cmd_set_tolerance

Sets the frequency tolerance.

dmclk_frequency_t tolerance = 1000; // ±1 kHz
int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_set_tolerance, &tolerance);
dmclk_ioctl_cmd_set_oscillator_frequency

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);
dmclk_ioctl_cmd_set_target_frequency

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);
dmclk_ioctl_cmd_reconfigure

Reconfigures the clock with current settings without changing any parameters.

int ret = dmclk_dmdrvi_ioctl(ctx, handle, dmclk_ioctl_cmd_reconfigure, NULL);

dmclk_dmdrvi_flush

int dmclk_dmdrvi_flush(dmdrvi_context_t context, void* handle);

Flushes device buffers - no operation for clock device.

Returns: 0

dmclk_dmdrvi_stat

int dmclk_dmdrvi_stat(dmdrvi_context_t context, void* handle, dmdrvi_stat_t* stat);

Gets device statistics.

Parameters:

  • context: Device context
  • handle: Device handle
  • stat: Pointer to stat structure to fill

Returns:

  • 0 on success
  • Negative error code on failure

Stat Fields:

  • size: Size of the read data string
  • mode: File permissions (0444 - read-only)

Port Layer API

The port layer implements hardware-specific clock configuration. These functions are called internally by the core module.

dmclk_port_configure_internal

int dmclk_port_configure_internal(dmclk_frequency_t target_freq, dmclk_frequency_t tolerance);

Configures the system clock using the internal oscillator.

dmclk_port_configure_external

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.

dmclk_port_configure_hibernatation

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.

dmclk_port_delay_us

void dmclk_port_delay_us(dmclk_time_us_t time_us);

Delays execution for the specified number of microseconds.

dmclk_port_get_current_frequency

dmclk_frequency_t dmclk_port_get_current_frequency(void);

Returns the current system clock frequency in Hz.

Error Codes

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).

Thread Safety

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.

Example: Complete Usage

#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;
}

Peripheral input clock reservations

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.