Linux reset子系統

来源:https://www.cnblogs.com/jianhua1992/archive/2023/07/19/17396492.html
-Advertisement-
Play Games

文章代碼分析基於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:

  1. 首先,提供描述系統中reset資源的方法(參考provider的介紹),這樣consumer可以基於這種描述,在自己的dts node中引用所需的reset信號。

  2. 然後,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. 
  1. 最後,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子系統及驅動實例


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 瞭解一下線程模型還是很有必要的,如果不清楚語言層面上的線程在操作系統層面怎麼映射使用,在使用過程中就會不清不楚,可能會踩一些坑 ...
  • ## 引言 **yield**關鍵字是 C# 中的一種語言特性,用於在枚舉器中簡化迭代器的實現。它使得開發人員可以通過定義自己的迭代器來簡化代碼,而不必手動實現 IEnumerable 和 IEnumerator 介面。 使用 `yield` 關鍵字,可以將迭代器中的值一次一個地返回,而不必使用一個 ...
  • # 基於Avalonia 11.0.0+ReactiveUI 的跨平臺項目開發1-通用框架 ### Avalonia簡介: Avalonia是.NET的一個跨平臺UI框架,提供了一個靈活的樣式系統,支持廣泛的操作系統,如Windows、Linux、macOS,並對Android、iOS和WebAss ...
  • 本文介紹了C#面向對象編程語言的三個基礎特征:封裝、繼承和多態。其中,封裝可以通過public、private、protected、internal等關鍵字來實現,目的是保護程式內部數據的完整性;繼承可以幫助實現基類的內容復用,但也要結合場景使用;多態是指相同類型對象調用相同方法卻表現出不同行為,使... ...
  • ###內置日誌用法: 1. 引入**Microsoft.Extensions.Logging**命名空間:用於定義和實現日誌記錄功能 2. ILogger介面 a. 內置日誌系統的主要介面,用於記錄日誌信息,可以通過一來註入將ILogger實例註入到記錄日誌的類中,並使用它來記錄不同級別的日誌消息 ...
  • # 什麼?博客園主題比我的個人博客好看?😮 # 夢開始的地方🛸 最近逛博客園,發現我的園子還挺好看,但是還不夠好看,所以通過我百度發現SimpleMemory主題還可以繼續添加新的東西,當然這些東西不一定非得用SimpleMemory主題才行,但是搭配SimpleMemory主題是真的好看呀(比 ...
  • 前段時間小編檢查同事代碼,發現居然寫的太複雜看不太懂,代碼命名不規範,重覆冗長代碼一堆,這時候就可以通過重構來改進代碼的質量。代碼重構是提高代碼質量和可維護性的關鍵過程,它旨在通過優化代碼結構和設計來提高代碼的可讀性、可理解性和可擴展性。本文講述在C#中重構代碼的幾個案例,供大家參考。 **1、命名 ...
  • ## 一:背景 ### 1. 講故事 前幾天寫了一篇 `如何洞察 .NET程式 非托管句柄泄露` 的文章,文中使用 WinDbg 的 `!htrace` 命令實現了句柄泄露的洞察,在文末我也說了,WinDbg 是以侵入式的方式解決了這個問題,在生產環境中大多數情況下是不能走附加進程的模式,所以這也是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...