README.md 23 KB

Reset Controller Framework

Introduction

The Reset Controller Framework in RT-Thread provides a standardized interface for managing hardware reset signals in embedded systems. Reset controllers are essential for properly initializing, recovering, and managing the state of hardware peripherals and subsystems.

General Overview

Reset signals are fundamental to hardware management for:

  • Device Initialization: Bringing devices to a known state during boot
  • Error Recovery: Resetting devices when errors occur
  • Power Management: Resetting devices when entering/exiting low-power modes
  • Hot-plug Support: Reinitializing devices for hotplug scenarios
  • System Stability: Ensuring clean state transitions

Common reset controller types include:

  • Register-based: Single register controlling multiple resets
  • GPIO-based: Reset lines controlled by GPIO pins
  • Power Management IC (PMIC): Resets integrated in power management chips
  • System Control: Resets managed through system control interfaces (SCMI)

RT-Thread Implementation

The RT-Thread reset controller framework, located in components/drivers/reset/, provides:

  1. Consumer API: Simple interface for device drivers to control reset lines
  2. Provider API: Framework for implementing reset controller drivers
  3. Device Tree Integration: Automatic configuration from FDT
  4. Reset Operations: Assert, deassert, and pulse reset signals
  5. Array Support: Managing multiple reset lines for a device
  6. Status Query: Checking reset line state

Architecture:

┌─────────────────────────────────────────────────────────┐
│                  Consumer Drivers                        │
│    (UART, SPI, MMC, USB, Peripheral drivers)            │
└────────────────────┬────────────────────────────────────┘
                     │ Consumer API
                     │ (get, reset, assert, deassert)
┌────────────────────┴────────────────────────────────────┐
│          Reset Controller Framework                      │
│  - Reset Control Management                              │
│  - Array Support                                         │
│  - Status Tracking                                       │
└────────────────────┬────────────────────────────────────┘
                     │ Provider API
                     │ (ops callbacks)
┌────────────────────┴────────────────────────────────────┐
│        Reset Controller Drivers                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │   Simple    │  │    SCMI     │  │   Custom    │     │
│  │   Reset     │  │   Reset     │  │   Reset     │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└────────────────────┬────────────────────────────────────┘
                     │
┌────────────────────┴────────────────────────────────────┐
│         Hardware Reset Controllers                       │
│  (Reset registers, PMICs, System controllers)           │
└──────────────────────────────────────────────────────────┘

Kconfig Configuration

Main Configuration

menuconfig RT_USING_RESET
    bool "Using Reset Controller support"
    depends on RT_USING_DM
    depends on RT_USING_OFW
    default n

Location in menuconfig:

RT-Thread Components → Device Drivers → Using Reset Controller support

Dependencies:

  • RT_USING_DM: Must be enabled first
  • RT_USING_OFW: Device tree support required

Default: Disabled (opt-in feature)

Reset Driver Options

SCMI Reset Driver

config RT_RESET_SCMI
    bool "Reset driver controlled via ARM SCMI interface"
    depends on RT_USING_RESET
    depends on RT_FIRMWARE_ARM_SCMI
    default n

Supports reset controllers controlled through ARM System Control and Management Interface (SCMI).

Dependencies:

  • RT_FIRMWARE_ARM_SCMI: ARM SCMI firmware interface

Simple Reset Driver

config RT_RESET_SIMPLE
    bool "Simple Reset Controller Driver"
    depends on RT_USING_RESET
    default n

Supports basic register-based reset controllers with simple bit operations.

Vendor-Specific Options

if RT_USING_RESET
    osource "$(SOC_DM_RESET_DIR)/Kconfig"
endif

Allows SoC-specific reset drivers to add their own Kconfig options.

Device Tree Bindings

Reset Controller Properties

Reset controllers export resets using:

#reset-cells = <n>;              /* Number of cells in reset specifier */

The number of cells typically indicates how many values are needed to identify a specific reset line (usually 1).

Reset Consumer Properties

Devices reference reset lines using:

resets = <&rstc idx>;            /* Reset controller phandle and index */
reset-names = "name";            /* Reset line names */

Simple Reset Controller Example

soc {
    /* Reset controller node */
    rstc: reset-controller@1c20000 {
        compatible = "vendor,reset-controller", "reset-simple";
        reg = <0x1c20000 0x100>;
        #reset-cells = <1>;
    };
};

Consumer Usage Examples

/* Single reset consumer */
uart0: serial@1c28000 {
    compatible = "vendor,uart";
    reg = <0x1c28000 0x400>;
    interrupts = <0 0 4>;
    
    clocks = <&ccu 64>;
    resets = <&rstc 32>;         /* Reset index 32 */
    reset-names = "uart";
    
    status = "okay";
};

/* Multiple resets consumer */
usb0: usb@1c19000 {
    compatible = "vendor,usb-otg";
    reg = <0x1c19000 0x1000>;
    interrupts = <0 32 4>;
    
    clocks = <&ccu 24>, <&ccu 25>;
    clock-names = "ahb", "otg";
    
    resets = <&rstc 11>, <&rstc 12>;
    reset-names = "ahb", "phy";
    
    status = "okay";
};

/* Reset with shared line */
mmc0: mmc@1c0f000 {
    compatible = "vendor,mmc";
    reg = <0x1c0f000 0x1000>;
    
    resets = <&rstc 8>;
    /* No reset-names for single reset */
    
    status = "okay";
};

Application Layer API

Overview

The consumer API provides simple functions for device drivers to control reset lines. Reset operations are typically performed during device initialization and error recovery.

Getting and Releasing Reset Controls

rt_reset_control_get_by_name

struct rt_reset_control *rt_reset_control_get_by_name(struct rt_device *dev, 
                                                       const char *name);

Get a reset control by name.

Parameters:

  • dev: Device structure pointer
  • name: Reset line name (matches reset-names in device tree)

Returns:

  • Pointer to reset control on success
  • NULL on failure

Example:

struct rt_device *dev = &pdev->parent;
struct rt_reset_control *rst;

/* Get the "phy" reset line */
rst = rt_reset_control_get_by_name(dev, "phy");
if (!rst) {
    LOG_W("No phy reset line");
    /* Not fatal - some boards may not have it */
}

rt_reset_control_get_by_index

struct rt_reset_control *rt_reset_control_get_by_index(struct rt_device *dev, 
                                                        int index);

Get a reset control by index.

Parameters:

  • dev: Device structure pointer
  • index: Reset index (0-based) in resets property

Returns:

  • Pointer to reset control on success
  • NULL on failure

Example:

/* Get the first reset */
struct rt_reset_control *rst = rt_reset_control_get_by_index(dev, 0);

rt_reset_control_get_array

struct rt_reset_control *rt_reset_control_get_array(struct rt_device *dev);

Get all reset controls for a device as an array.

Parameters:

  • dev: Device structure pointer

Returns:

  • Pointer to reset control array on success
  • NULL on failure

Example:

struct rt_reset_control *rst_array = rt_reset_control_get_array(dev);
if (rst_array) {
    /* All resets can be controlled as a group */
}

rt_reset_control_get_count

rt_ssize_t rt_reset_control_get_count(struct rt_device *dev);

Get the number of reset lines for a device.

Parameters:

  • dev: Device structure pointer

Returns:

  • Number of reset lines
  • Negative error code on failure

rt_reset_control_put

void rt_reset_control_put(struct rt_reset_control *rstc);

Release a reset control.

Parameters:

  • rstc: Reset control pointer

Reset Operations

rt_reset_control_reset

rt_err_t rt_reset_control_reset(struct rt_reset_control *rstc);

Perform a reset pulse: assert then deassert.

Parameters:

  • rstc: Reset control pointer

Returns:

  • RT_EOK on success
  • Error code on failure

Notes:

  • This is the most common operation
  • Automatically asserts and then deasserts with appropriate delay
  • Safe to call even if reset control is NULL

Example:

/* Reset a peripheral during initialization */
ret = rt_reset_control_reset(rst);
if (ret != RT_EOK) {
    LOG_E("Failed to reset peripheral: %d", ret);
    return ret;
}

/* Wait for hardware to stabilize */
rt_thread_mdelay(10);

rt_reset_control_assert

rt_err_t rt_reset_control_assert(struct rt_reset_control *rstc);

Assert (activate) the reset signal.

Parameters:

  • rstc: Reset control pointer

Returns:

  • RT_EOK on success
  • Error code on failure

Notes:

  • Puts the device into reset state
  • Must be followed by deassert to restore operation
  • Safe to call even if reset control is NULL

Example:

/* Put device into reset before power off */
rt_reset_control_assert(rst);

rt_reset_control_deassert

rt_err_t rt_reset_control_deassert(struct rt_reset_control *rstc);

Deassert (deactivate) the reset signal.

Parameters:

  • rstc: Reset control pointer

Returns:

  • RT_EOK on success
  • Error code on failure

Notes:

  • Takes the device out of reset state
  • Should be called after assert
  • Safe to call even if reset control is NULL

Example:

/* Take device out of reset */
rt_reset_control_deassert(rst);

/* Wait for hardware to be ready */
rt_thread_mdelay(1);

rt_reset_control_status

int rt_reset_control_status(struct rt_reset_control *rstc);

Get the current status of a reset line.

Parameters:

  • rstc: Reset control pointer

Returns:

  • 1 if asserted (in reset)
  • 0 if deasserted (not in reset)
  • Negative error code on failure

Example:

int status = rt_reset_control_status(rst);
if (status > 0) {
    LOG_W("Device is currently in reset");
}

Complete Application Example

Example: USB Driver with Reset Control

#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/clk.h>
#include <drivers/reset.h>
#include <drivers/usb.h>

struct usb_device {
    void *base;
    int irq;
    struct rt_clk *ahb_clk;
    struct rt_clk *otg_clk;
    struct rt_reset_control *ahb_rst;
    struct rt_reset_control *phy_rst;
};

static rt_err_t usb_hw_init(struct usb_device *usb)
{
    rt_err_t ret;
    
    /* Enable clocks first */
    ret = rt_clk_prepare_enable(usb->ahb_clk);
    if (ret != RT_EOK) {
        LOG_E("Failed to enable AHB clock: %d", ret);
        return ret;
    }
    
    ret = rt_clk_prepare_enable(usb->otg_clk);
    if (ret != RT_EOK) {
        LOG_E("Failed to enable OTG clock: %d", ret);
        goto err_disable_ahb;
    }
    
    /* Deassert resets after clocks are enabled */
    ret = rt_reset_control_deassert(usb->ahb_rst);
    if (ret != RT_EOK) {
        LOG_E("Failed to deassert AHB reset: %d", ret);
        goto err_disable_otg;
    }
    
    ret = rt_reset_control_deassert(usb->phy_rst);
    if (ret != RT_EOK) {
        LOG_E("Failed to deassert PHY reset: %d", ret);
        goto err_assert_ahb;
    }
    
    /* Wait for hardware to stabilize */
    rt_thread_mdelay(10);
    
    return RT_EOK;
    
err_assert_ahb:
    rt_reset_control_assert(usb->ahb_rst);
err_disable_otg:
    rt_clk_disable_unprepare(usb->otg_clk);
err_disable_ahb:
    rt_clk_disable_unprepare(usb->ahb_clk);
    return ret;
}

static void usb_hw_deinit(struct usb_device *usb)
{
    /* Assert resets before disabling clocks */
    rt_reset_control_assert(usb->phy_rst);
    rt_reset_control_assert(usb->ahb_rst);
    
    /* Disable clocks */
    rt_clk_disable_unprepare(usb->otg_clk);
    rt_clk_disable_unprepare(usb->ahb_clk);
}

static rt_err_t usb_probe(struct rt_platform_device *pdev)
{
    rt_err_t ret;
    struct rt_device *dev = &pdev->parent;
    struct usb_device *usb;
    
    /* Allocate device structure */
    usb = rt_calloc(1, sizeof(*usb));
    if (!usb)
        return -RT_ENOMEM;
    
    /* Map MMIO region */
    usb->base = rt_dm_dev_iomap(dev, 0);
    if (!usb->base) {
        ret = -RT_ERROR;
        goto err_free;
    }
    
    /* Get IRQ */
    usb->irq = rt_dm_dev_get_irq(dev, 0);
    if (usb->irq < 0) {
        ret = usb->irq;
        goto err_unmap;
    }
    
    /* Get clocks */
    usb->ahb_clk = rt_clk_get_by_name(dev, "ahb");
    if (!usb->ahb_clk) {
        LOG_E("Failed to get AHB clock");
        ret = -RT_ERROR;
        goto err_unmap;
    }
    
    usb->otg_clk = rt_clk_get_by_name(dev, "otg");
    if (!usb->otg_clk) {
        LOG_E("Failed to get OTG clock");
        ret = -RT_ERROR;
        goto err_put_ahb_clk;
    }
    
    /* Get resets */
    usb->ahb_rst = rt_reset_control_get_by_name(dev, "ahb");
    if (!usb->ahb_rst) {
        LOG_W("No AHB reset line");
        /* Not fatal - some platforms may not have it */
    }
    
    usb->phy_rst = rt_reset_control_get_by_name(dev, "phy");
    if (!usb->phy_rst) {
        LOG_W("No PHY reset line");
        /* Not fatal */
    }
    
    /* Initialize hardware */
    ret = usb_hw_init(usb);
    if (ret != RT_EOK) {
        goto err_put_resets;
    }
    
    /* Register USB controller */
    /* ... */
    
    pdev->priv = usb;
    LOG_I("USB device initialized");
    
    return RT_EOK;
    
err_put_resets:
    if (usb->phy_rst)
        rt_reset_control_put(usb->phy_rst);
    if (usb->ahb_rst)
        rt_reset_control_put(usb->ahb_rst);
    rt_clk_put(usb->otg_clk);
err_put_ahb_clk:
    rt_clk_put(usb->ahb_clk);
err_unmap:
    rt_iounmap(usb->base);
err_free:
    rt_free(usb);
    return ret;
}

static rt_err_t usb_remove(struct rt_platform_device *pdev)
{
    struct usb_device *usb = pdev->priv;
    
    /* Deinitialize hardware */
    usb_hw_deinit(usb);
    
    /* Release resources */
    if (usb->phy_rst)
        rt_reset_control_put(usb->phy_rst);
    if (usb->ahb_rst)
        rt_reset_control_put(usb->ahb_rst);
    rt_clk_put(usb->otg_clk);
    rt_clk_put(usb->ahb_clk);
    rt_iounmap(usb->base);
    rt_free(usb);
    
    return RT_EOK;
}

static const struct rt_ofw_node_id usb_ofw_ids[] = {
    { .compatible = "vendor,usb-otg" },
    { /* sentinel */ }
};

static struct rt_platform_driver usb_driver = {
    .name = "usb-otg",
    .ids = usb_ofw_ids,
    .probe = usb_probe,
    .remove = usb_remove,
};

RT_PLATFORM_DRIVER_EXPORT(usb_driver);

Driver Implementation Guide

Key Structures

rt_reset_controller

struct rt_reset_controller {
    struct rt_object parent;
    rt_list_t rstc_nodes;                    /* List of reset controls */
    
    const char *name;                        /* Controller name */
    const struct rt_reset_control_ops *ops;  /* Operations */
    
    struct rt_ofw_node *ofw_node;            /* Device tree node */
    void *priv;                              /* Private data */
    
    struct rt_spinlock spinlock;             /* Lock for thread safety */
};

rt_reset_control_ops

struct rt_reset_control_ops {
    /* Parse device tree arguments */
    rt_err_t (*ofw_parse)(struct rt_reset_control *rstc, 
                         struct rt_ofw_cell_args *args);
    
    /* Reset operations */
    rt_err_t (*reset)(struct rt_reset_control *rstc);
    rt_err_t (*assert)(struct rt_reset_control *rstc);
    rt_err_t (*deassert)(struct rt_reset_control *rstc);
    int      (*status)(struct rt_reset_control *rstc);
};

Example: Simple Reset Controller Driver

#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/reset.h>

#define RESET_ASSERT   0x0
#define RESET_DEASSERT 0x4
#define RESET_STATUS   0x8

struct simple_reset {
    struct rt_reset_controller rstcer;
    void *base;
    rt_uint32_t nr_resets;
};

static rt_err_t simple_reset_ofw_parse(struct rt_reset_control *rstc,
                                       struct rt_ofw_cell_args *args)
{
    /* args->args[0] contains reset index */
    if (args->args_count != 1) {
        return -RT_EINVAL;
    }
    
    rstc->id = args->args[0];
    return RT_EOK;
}

static rt_err_t simple_reset_assert(struct rt_reset_control *rstc)
{
    struct simple_reset *sr = rstc->rstcer->priv;
    rt_uint32_t mask = RT_BIT(rstc->id);
    
    /* Set bit to assert reset */
    writel(readl(sr->base + RESET_ASSERT) | mask, 
           sr->base + RESET_ASSERT);
    
    return RT_EOK;
}

static rt_err_t simple_reset_deassert(struct rt_reset_control *rstc)
{
    struct simple_reset *sr = rstc->rstcer->priv;
    rt_uint32_t mask = RT_BIT(rstc->id);
    
    /* Set bit to deassert reset */
    writel(readl(sr->base + RESET_DEASSERT) | mask,
           sr->base + RESET_DEASSERT);
    
    return RT_EOK;
}

static int simple_reset_status(struct rt_reset_control *rstc)
{
    struct simple_reset *sr = rstc->rstcer->priv;
    rt_uint32_t status = readl(sr->base + RESET_STATUS);
    
    /* Return 1 if in reset, 0 if not */
    return !!(status & RT_BIT(rstc->id));
}

static const struct rt_reset_control_ops simple_reset_ops = {
    .ofw_parse = simple_reset_ofw_parse,
    .assert = simple_reset_assert,
    .deassert = simple_reset_deassert,
    .status = simple_reset_status,
};

static rt_err_t simple_reset_probe(struct rt_platform_device *pdev)
{
    rt_err_t ret;
    rt_uint32_t nr_resets;
    struct rt_device *dev = &pdev->parent;
    struct simple_reset *sr;
    
    /* Allocate driver structure */
    sr = rt_calloc(1, sizeof(*sr));
    if (!sr)
        return -RT_ENOMEM;
    
    /* Map reset controller registers */
    sr->base = rt_dm_dev_iomap(dev, 0);
    if (!sr->base) {
        ret = -RT_ERROR;
        goto err_free;
    }
    
    /* Get number of reset lines */
    if (rt_dm_dev_prop_read_u32(dev, "reset-count", &nr_resets)) {
        nr_resets = 32;  /* Default */
    }
    sr->nr_resets = nr_resets;
    
    /* Initialize reset controller */
    sr->rstcer.ops = &simple_reset_ops;
    sr->rstcer.ofw_node = dev->ofw_node;
    sr->rstcer.priv = sr;
    
    /* Register with framework */
    ret = rt_reset_controller_register(&sr->rstcer);
    if (ret != RT_EOK) {
        LOG_E("Failed to register reset controller: %d", ret);
        goto err_unmap;
    }
    
    pdev->priv = sr;
    LOG_I("Simple reset controller registered: %u lines", nr_resets);
    
    return RT_EOK;
    
err_unmap:
    rt_iounmap(sr->base);
err_free:
    rt_free(sr);
    return ret;
}

static const struct rt_ofw_node_id simple_reset_ofw_ids[] = {
    { .compatible = "simple-reset" },
    { /* sentinel */ }
};

static struct rt_platform_driver simple_reset_driver = {
    .name = "simple-reset",
    .ids = simple_reset_ofw_ids,
    .probe = simple_reset_probe,
};

static int simple_reset_drv_register(void)
{
    rt_platform_driver_register(&simple_reset_driver);
    return 0;
}
INIT_SUBSYS_EXPORT(simple_reset_drv_register);

Best Practices

For Consumer Drivers

  1. Check return values: Reset operations can fail
  2. Order matters: Enable clocks before deasserting resets
  3. Handle missing resets: Not all platforms have reset lines (check for NULL)
  4. Wait after reset: Allow hardware time to stabilize
  5. Reset during errors: Use reset for error recovery
  6. Assert before power off: Put devices in reset before disabling power

Common Patterns

Initialization Sequence

/* Correct initialization order */
rt_clk_prepare_enable(clk);          /* 1. Enable clocks */
rt_reset_control_deassert(rst);      /* 2. Take out of reset */
rt_thread_mdelay(10);                 /* 3. Wait for stability */
/* Initialize hardware registers */

Cleanup Sequence

/* Correct cleanup order (reverse of init) */
/* Disable hardware */
rt_reset_control_assert(rst);         /* 1. Put into reset */
rt_clk_disable_unprepare(clk);        /* 2. Disable clocks */

Error Recovery

/* Reset device on error */
if (device_error_detected()) {
    rt_reset_control_reset(rst);      /* Pulse reset */
    rt_thread_mdelay(10);
    reinitialize_device();
}

Troubleshooting

Common Issues

  1. Reset not found

    • Check device tree: Ensure resets and reset-names properties exist
    • Check compatible string: Verify reset driver is loaded
    • Check Kconfig: Enable reset controller support
  2. Device doesn't work after reset

    • Check clock order: Clocks must be enabled before deassert
    • Check delay: Add delay after reset for hardware stabilization
    • Check reset polarity: Verify assert/deassert are correct
  3. System hangs during reset

    • Check clock status: Device clock must be running
    • Check dependencies: Some resets depend on others
    • Check power: Device power must be on

Performance Considerations

Memory Usage

  • Each reset controller: ~100 bytes
  • Each reset control: ~40 bytes
  • Minimal overhead

Timing

  • Reset operations: Usually fast (register writes)
  • May need delays: For hardware stabilization
  • No complex calculations

Related Modules

  • clk: Clock management, coordinate with resets
  • regulator: Power supply, coordinate with resets
  • pinctrl: Pin configuration, may need after reset
  • pmdomain: Power domain, higher-level control

References