文章代碼分析基於linux-5.19.13,架構基於aarch64(ARM64)。 # 1. 前言 複雜IC內部有很多具有獨立功能的硬體模塊,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出於功耗、穩定性等方面的考慮,有些IC在內部為這些硬體模塊設計了複位信號(res ...
文章代碼分析基於linux-5.19.13,架構基於aarch64(ARM64)。
1. 前言
複雜IC內部有很多具有獨立功能的硬體模塊,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出於功耗、穩定性等方面的考慮,有些IC在內部為這些硬體模塊設計了複位信號(reset signals),軟體可通過寄存器(一般1個bit控制1個硬體)控制這些硬體模塊的複位狀態。
Linux kernel為了方便設備驅動的編寫,抽象出一個簡單的軟體框架----reset framework,為reset的provider提供統一的reset資源管理手段,併為reset的consumer(各個硬體模塊)提供便捷、統一的複位控制API。
2. 前言
reset子系統也分為了consumer和provider,結構體關係如下:
3. consumer
對於一個具體的硬體模塊,它的要求很簡單:複位我的硬體模塊,而不必關註具體複位的手段(例如控制哪個寄存器的哪個bit位,等等)。
Linux kernel基於device tree提供了對應的reset framework:
-
首先,提供描述系統中reset資源的方法(參考provider的介紹),這樣consumer可以基於這種描述,在自己的dts node中引用所需的reset信號。
-
然後,consumer設備在自己的dts node中使用“resets”、“reset-names”等關鍵字聲明所需的reset的資源,例如("resets"欄位的具體格式由reset provider決定):
device {
resets = <&rst 20>;
reset-names = "reset";
};
This represents a device with a single reset signal named "reset".
bus {
resets = <&rst 10> <&rst 11> <&rst 12> <&rst 11>;
reset-names = "i2s1", "i2s2", "dma", "mixer";
};
This represents a bus that controls the reset signal of each of four sub- ordinate devices. Consider for example a bus that fails to operate unless no child device has reset asserted.
- 最後,consumer driver在需要的時候,可以調用下麵的API複位自己(具體可參考"include\linux\reset.h"):
- 只有一個reset信號的話,可以使用最簡單的device_reset API
static inline int __must_check device_reset(struct device *dev)
- 如果需要更為複雜的控制(例如有多個reset信號、需要控制處於reset狀態的長度的等),可以使用稍微複雜的API
/* 通過reset_control_get或者devm_reset_control_get獲得reset句柄 */
struct reset_control *reset_control_get(struct device *dev, const char *id);
struct reset_control *devm_reset_control_get(struct device *dev, const char *id);
/* 通過reset_control_put釋放reset句柄 */
void reset_control_put(struct reset_control *rstc);
/* 通過reset_control_reset進行複位,或者通過reset_control_assert使設備處於複位生效狀態,通過reset_control_deassert使複位失效 */
int reset_control_reset(struct reset_control *rstc); /先複位,延遲一會,然後解複位
int reset_control_assert(struct reset_control *rstc); //複位
int reset_control_deassert(struct reset_control *rstc);//解複位
4. provider
kernel為reset provider提供的API位於"include/linux/reset-controller.h"中,很簡單,無非就是:創建並填充reset controller設備(struct reset_controller_dev),並調用相應的介面:
- reset_controller_register //註冊reset_controller
- reset_controller_unregister //註銷reset_controller
reset controller的抽象也很簡單:
/**
* struct reset_controller_dev - reset controller entity that might
* provide multiple reset controls
* @ops: a pointer to device specific struct reset_control_ops
* @owner: kernel module of the reset controller driver
* @list: internal list of reset controller devices
* @reset_control_head: head of internal list of requested reset controls
* @dev: corresponding driver model device struct
* @of_node: corresponding device tree node as phandle target
* @of_reset_n_cells: number of cells in reset line specifiers
* @of_xlate: translation function to translate from specifier as found in the
* device tree to id as given to the reset control ops, defaults
* to :c:func:`of_reset_simple_xlate`.
* @nr_resets: number of reset controls in this reset controller device
*/
struct reset_controller_dev {
const struct reset_control_ops *ops;//ops提供reset操作的實現,基本上是reset provider的所有工作量。
struct module *owner;
struct list_head list;////全局鏈表,複位控制器註冊後掛載到全局鏈表
struct list_head reset_control_head;////各個模塊複位的鏈表頭
struct device *dev;
struct device_node *of_node;
int of_reset_n_cells;////用於解析consumer device dts node中的“resets = <>; ”節點,指示dts中引用時,需要幾個參數
int (*of_xlate)(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec);//用於解析consumer device dts node中的“resets = <>; ”節點
unsigned int nr_resets;//該reset controller所控制的reset信號的個數
};
struct reset_control_ops也比較單純,如下:
/**
* struct reset_control_ops - reset controller driver callbacks
*
* @reset: for self-deasserting resets, does all necessary
* things to reset the device
* @assert: manually assert the reset line, if supported
* @deassert: manually deassert the reset line, if supported
* @status: return the status of the reset line, if supported
*/
struct reset_control_ops {
int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); //控制設備完成一次完整的複位過程
int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); //控制設備reset狀態的生效
int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);//控制設備reset狀態的失效。
int (*status)(struct reset_controller_dev *rcdev, unsigned long id); //複位狀態查詢
};
5. reset驅動的設備樹描述總結
5.1 對於provider
reset:reset-controller{
compatible = "xx,xx-reset";
reg = <0x0 0x30390000 0x0 0x10000>;
#reset-cells = <1>;
};
上述是一個reset控制器的節點,0x30390000 是寄存器基址,0x1000是映射大小。"#reset-cells"代表引用該reset時需要的cells個數。
5.2 對於consumer
例如,#reset-cells = <1>; 則正確引用為:
mmc:mmc@0x12345678{
......
resets = <&reset 0>;//0代表reset設備id,id是自定義的,但是不能超過reset驅動中指定的設備個數
......
};
6. 開源reset驅動實例
6.1 實例1(比較容易理解)
設備樹: arch/arm/boot/dts/imx7d.dtsi
pcie: pcie@0x33800000 {
compatible = "fsl,imx7d-pcie", "snps,dw-pcie";
....
resets = <&src IMX7_RESET_PCIEPHY>,
<&src IMX7_RESET_PCIE_CTRL_APPS_EN>;
reset-names = "pciephy", "apps";
status = "disabled";
};
驅動代碼: drivers/reset/reset-imx7.c
...
struct imx7_src {
struct reset_controller_dev rcdev;
struct regmap *regmap;
};
enum imx7_src_registers {
SRC_A7RCR0 = 0x0004,
SRC_M4RCR = 0x000c,
SRC_ERCR = 0x0014,
SRC_HSICPHY_RCR = 0x001c,
SRC_USBOPHY1_RCR = 0x0020,
SRC_USBOPHY2_RCR = 0x0024,
SRC_MIPIPHY_RCR = 0x0028,
SRC_PCIEPHY_RCR = 0x002c,
SRC_DDRC_RCR = 0x1000,
};
struct imx7_src_signal {
unsigned int offset, bit;
};
static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = {
[IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) },
[IMX7_RESET_A7_CORE_POR_RESET1] = { SRC_A7RCR0, BIT(1) },
[IMX7_RESET_A7_CORE_RESET0] = { SRC_A7RCR0, BIT(4) },
[IMX7_RESET_A7_CORE_RESET1] = { SRC_A7RCR0, BIT(5) },
[IMX7_RESET_A7_DBG_RESET0] = { SRC_A7RCR0, BIT(8) },
[IMX7_RESET_A7_DBG_RESET1] = { SRC_A7RCR0, BIT(9) },
...
};
static struct imx7_src *to_imx7_src(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct imx7_src, rcdev);
}
static int imx7_reset_set(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct imx7_src *imx7src = to_imx7_src(rcdev);
const struct imx7_src_signal *signal = &imx7_src_signals[id];
unsigned int value = assert ? signal->bit : 0;
switch (id) {
case IMX7_RESET_PCIEPHY:
/*
* wait for more than 10us to release phy g_rst and
* btnrst
*/
if (!assert)
udelay(10);
break;
case IMX7_RESET_PCIE_CTRL_APPS_EN:
value = (assert) ? 0 : signal->bit;
break;
}
return regmap_update_bits(imx7src->regmap,
signal->offset, signal->bit, value);
}
static int imx7_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return imx7_reset_set(rcdev, id, true);
}
static int imx7_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return imx7_reset_set(rcdev, id, false);
}
static const struct reset_control_ops imx7_reset_ops = {
.assert = imx7_reset_assert,
.deassert = imx7_reset_deassert,
};
static int imx7_reset_probe(struct platform_device *pdev)
{
struct imx7_src *imx7src;
struct device *dev = &pdev->dev;
struct regmap_config config = { .name = "src" };
imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL);
if (!imx7src)
return -ENOMEM;
imx7src->regmap = syscon_node_to_regmap(dev->of_node);
if (IS_ERR(imx7src->regmap)) {
dev_err(dev, "Unable to get imx7-src regmap");
return PTR_ERR(imx7src->regmap);
}
regmap_attach_dev(dev, imx7src->regmap, &config);
imx7src->rcdev.owner = THIS_MODULE;
imx7src->rcdev.nr_resets = IMX7_RESET_NUM;
imx7src->rcdev.ops = &imx7_reset_ops;
imx7src->rcdev.of_node = dev->of_node;
return devm_reset_controller_register(dev, &imx7src->rcdev);
}
static const struct of_device_id imx7_reset_dt_ids[] = {
{ .compatible = "fsl,imx7d-src", },
{ /* sentinel */ },
};
static struct platform_driver imx7_reset_driver = {
.probe = imx7_reset_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = imx7_reset_dt_ids,
},
};
builtin_platform_driver(imx7_reset_driver);
6.2 實例2(在gpio子系統中嵌套reset子系統)
設備樹: arc/arm64/boot/dts/myzr/myimx8mm.dts
&pcie0{
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c4_pcieclk>, <&pinctrl_gpio1_pciendis>, <&pinctrl_sd2_pciewake>, <&pinctrl_sai2_pcienrst>;
disable-gpio = <&gpio1 5 GPIO_ACTIVE_LOW>;
reset-gpio = <&gpio4 21 GPIO_ACTIVE_LOW>;
ext_osc = <1>;
status = "okay";
};
驅動代碼: drivers/reset/gpio-reset.c
...
struct gpio_reset_data {
struct reset_controller_dev rcdev;
unsigned int gpio;
bool active_low;
s32 delay_us;
s32 post_delay_ms;
};
static void gpio_reset_set(struct reset_controller_dev *rcdev, int asserted)
{
struct gpio_reset_data *drvdata = container_of(rcdev,
struct gpio_reset_data, rcdev);
int value = asserted;
if (drvdata->active_low)
value = !value;
gpio_set_value_cansleep(drvdata->gpio, value);
}
static int gpio_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
struct gpio_reset_data *drvdata = container_of(rcdev,
struct gpio_reset_data, rcdev);
if (drvdata->delay_us < 0)
return -ENOSYS;
gpio_reset_set(rcdev, 1);
udelay(drvdata->delay_us);
gpio_reset_set(rcdev, 0);
if (drvdata->post_delay_ms < 0)
return 0;
msleep(drvdata->post_delay_ms);
return 0;
}
static int gpio_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
gpio_reset_set(rcdev, 1);
return 0;
}
static int gpio_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
gpio_reset_set(rcdev, 0);
return 0;
}
static struct reset_control_ops gpio_reset_ops = {
.reset = gpio_reset,
.assert = gpio_reset_assert,
.deassert = gpio_reset_deassert,
};
static int of_gpio_reset_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
if (WARN_ON(reset_spec->args_count != 0))
return -EINVAL;
return 0;
}
static int gpio_reset_probe(struct platform_device *pdev)
{
...
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
...
drvdata->rcdev.of_node = np;
drvdata->rcdev.owner = THIS_MODULE;
drvdata->rcdev.nr_resets = 1; ////該reset controller所控制的reset信號的個數
drvdata->rcdev.ops = &gpio_reset_ops; //ops提供reset操作的實現。
drvdata->rcdev.of_xlate = of_gpio_reset_xlate;
reset_controller_register(&drvdata->rcdev); //註冊reset controller
return 0;
}
static int gpio_reset_remove(struct platform_device *pdev)
{
struct gpio_reset_data *drvdata = platform_get_drvdata(pdev);
reset_controller_unregister(&drvdata->rcdev);
return 0;
}
static struct of_device_id gpio_reset_dt_ids[] = {
{ .compatible = "gpio-reset" },
{ }
};
#ifdef CONFIG_PM_SLEEP
static int gpio_reset_suspend(struct device *dev)
{
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int gpio_reset_resume(struct device *dev)
{
pinctrl_pm_select_default_state(dev);
return 0;
}
#endif
static const struct dev_pm_ops gpio_reset_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(gpio_reset_suspend, gpio_reset_resume)
};
static struct platform_driver gpio_reset_driver = {
.probe = gpio_reset_probe,
.remove = gpio_reset_remove,
.driver = {
.name = "gpio-reset",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_reset_dt_ids),
.pm = &gpio_reset_pm_ops,
},
};
static int __init gpio_reset_init(void)
{
return platform_driver_register(&gpio_reset_driver);
}
arch_initcall(gpio_reset_init);
static void __exit gpio_reset_exit(void)
{
platform_driver_unregister(&gpio_reset_driver);
}
...
7. reset驅動的實質
操作soc對應的reset寄存器,以實現內核IP的複位,或者操作gpio管腳的電平,間接複位接到該pin腳的從設備。
參考
[1] Documentation/devicetree/bindings/reset/reset.txt
[2] Linux reset framework
[2] Linux reset子系統及驅動實例