IC - reset子系统

简介

  • 复杂的IC(SOC)内部有多个拥有独立功能的硬件模块,例如:cpu cores,tpu cores以及wdt,usb,mmc等各种外设。
  • 出于IC稳定性(异常后可恢复)等方面的考虑,SOC中会设计专门的reset电路,负责各个硬件模块的复位,在内部为硬件模块设计复位信号(reset signals),软件可通过寄存器控制硬件模块的复位。

SOC 示例

  • SOC(xx181x)的reset系统,比较复杂,设计了三个层级,主要功能:
  1. 外设模块的复位。
  2. CPU、TPU核的复位。
  3. 系统整体的复位。

使用

  • Reset系统实现比较复杂,但是暴露给用户使用的方式比较简单,一般是提供多个寄存器,每个寄存器以1 bit控制一个ip的复位,因此对于某个ip的复位只有两个操作:
  1. 复位,往控制该ip的特定bit写0
  2. 解复位,往控制该ip的特定bit写1
  • 先复位,延时一会,解复位,ip就能够复位成功。
  • SOC(xx181x)也提供了另外一套寄存器,同样以1 bit控制一个ip的复位,不同的是:复位时只需要往该bit写0,不需要再解复位(往该bit写1),ip就能复位成功。

适配

  1. dts配置
  • 需要在dts中配置reset-controller
/ {
    ...
    rst: reset-controller {
        compatible = "xxxxxx,reset";
        reg = <0x0 0x03003000 0x0 0x10>;
    };
    ...
}
  • 其它ip reset配置
#include 

/{
    ...
    watchdog0: wd@0x3010000 {
        ...
        resets = <&rst RST_WDT>;
        reset-names = "rst_wdt";
    };
    ...
}
  • 说明:
  1. xxx-resets.h是uboot和linux中,各个ip的reset id定义,如下:
#define RST_MAINRST_AP          0
#define RST_SECONDRST_AP        1
#define RST_DDR                 2
#define RST_H264C               3
#define RST_WDT                 4
  1. <&rst RST_WDT> 中的RST_WDT是引用头文件中的reset id,填具体的数字也可以,如下:
resets = <&rst 4>;

uboot

  • reset id定义
file: u-boot-2021.10/include/dt-bindings/reset/xxx-resets.h
  • 驱动实现
file: u-boot-2021.10/drivers/reset/reset-xxx.c
  • 驱动中reset接口使用
  • uboot下有一套和linux类似的接口,使用略。

Linux

reset-controller驱动实现

  • reset id定义
//file: linux_5.10/include/dt-bindings/reset/xxx-resets.h
  • 驱动接口定义
struct reset_control_ops{
    int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);//复位+解复位
    int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);//复位
    int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);//解复位
    int (*status)(struct reset_controller_dev *rcdev, unsigned long id);//复位状态查询
}
  • 驱动核心接口(assert、deassert)实现
//file: linux_5.10/drivers/reset/reset-xxx.c

static int xx_reset_assert(struct reset_controller_dev *rcdev,
                              unsigned long id)
{
    // 将reset id转换为对应的 寄存器和bit,对该bit写0。
    // soc xx181x实现
    int bank = id / 32;
    int offset = id % 32;
    
    spin_lock_irqsave(&data->lock, flags);
    // 将该bit置 0
    reg = readl(data->membase + (bank * 4));
    writel(reg & ~BIT(offset), data->membase + (bank * 4));

    spin_unlock_irqrestore(&data->lock, flags);

    return 0;
}

static int xx_reset_deassert(struct reset_controller_dev *rcdev,
                                unsigned long id)
{
    // 将reset id转换为对应的 寄存器和bit,对该bit写1。
    // soc xx181x实现同上,置0改为置1
    return 0;
}

static const struct reset_control_ops bm_reset_ops = {
    .assert         = xxx_reset_assert,
    .deassert       = xxx_reset_deassert,
};

reset framework core(provider)

  • reset 主逻辑以及API实现
// file: drivers/reset/core.c
struct reset_control {
    struct reset_controller_dev *rcdev;
    struct list_head list;
    unsigned int id;
    struct kref refcnt;
    bool shared;
    bool array;
    atomic_t deassert_count;
    atomic_t triggered_count;
};
...
  • consumer使用的头文件(接口定义)
// file: include/linux/reset.h

// 获取reset handle
struct reset_control *devm_reset_control_get(struct device *dev, const char *id);

devm_reset_control_get_exclusive:查找并获取reset_control的独占引用(不能与其他驱动程序共享)。
devm_reset_control_get_shared:查找并获取reset_control的共享引用(可以与另一个驱动程序共享)。
devm_reset_control_get:与 devm_reset_control_get_exclusive 相同。
devm_reset_control_get_optional:与 devm_reset_control_get 相同,只是引用名称是可选的。
devm_reset_control_get_optional_exclusive:与devm_reset_control_get_exclusive 相同,只是引用名称是可选的。
devm_reset_control_get_optional_shared:与devm_reset_control_get_shared 相同,只是引用名称是可选

// reset 操作
int reset_control_reset(struct reset_control *rstc); //复位 + 解复位操作
int reset_control_assert(struct reset_control *rstc); //复位
int reset_control_deassert(struct reset_control *rstc); //解复位

其它驱动(consumer)使用

  • 示例
  struct reset_control *reset;
  int ret; 

  // 1. get handle
  reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
  if (IS_ERR(reset)) {
    …
  } 

  // 2. 复位
  ret = reset_control_assert(reset);
  if (ret) {
    …
  }
  
  udelay(2);
  // 3. 解复位
  reset_control_deassert(reset);
  • 也可以根据dts中的reset-name获取handle
reset = devm_reset_control_get(&pdev->dev, "rst_wdt");
if (IS_ERR(reset)) {
  …
} 

你可能感兴趣的:(嵌入式,reset,Linux)