经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Linux/Shell » 查看文章
Linux reset子系统 - BSP-路人甲
来源:cnblogs  作者:BSP-路人甲  时间:2023/7/19 9:23:51  对本文有异议
文章代码分析基于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决定):

  1. device {
  2. resets = <&rst 20>;
  3. reset-names = "reset";
  4. };
  5. This represents a device with a single reset signal named "reset".
  6. bus {
  7. resets = <&rst 10> <&rst 11> <&rst 12> <&rst 11>;
  8. reset-names = "i2s1", "i2s2", "dma", "mixer";
  9. };
  10. 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
  1. static inline int __must_check device_reset(struct device *dev)
  • 如果需要更为复杂的控制(例如有多个reset信号、需要控制处于reset状态的长度的等),可以使用稍微复杂的API
  1. /* 通过reset_control_get或者devm_reset_control_get获得reset句柄 */
  2. struct reset_control *reset_control_get(struct device *dev, const char *id);
  3. struct reset_control *devm_reset_control_get(struct device *dev, const char *id);
  4. /* 通过reset_control_put释放reset句柄 */
  5. void reset_control_put(struct reset_control *rstc);
  6. /* 通过reset_control_reset进行复位,或者通过reset_control_assert使设备处于复位生效状态,通过reset_control_deassert使复位失效 */
  7. int reset_control_reset(struct reset_control *rstc); /先复位,延迟一会,然后解复位
  8. int reset_control_assert(struct reset_control *rstc); //复位
  9. 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的抽象也很简单:

  1. /**
  2. * struct reset_controller_dev - reset controller entity that might
  3. * provide multiple reset controls
  4. * @ops: a pointer to device specific struct reset_control_ops
  5. * @owner: kernel module of the reset controller driver
  6. * @list: internal list of reset controller devices
  7. * @reset_control_head: head of internal list of requested reset controls
  8. * @dev: corresponding driver model device struct
  9. * @of_node: corresponding device tree node as phandle target
  10. * @of_reset_n_cells: number of cells in reset line specifiers
  11. * @of_xlate: translation function to translate from specifier as found in the
  12. * device tree to id as given to the reset control ops, defaults
  13. * to :c:func:`of_reset_simple_xlate`.
  14. * @nr_resets: number of reset controls in this reset controller device
  15. */
  16. struct reset_controller_dev {
  17. const struct reset_control_ops *ops;//ops提供reset操作的实现,基本上是reset provider的所有工作量。
  18. struct module *owner;
  19. struct list_head list;////全局链表,复位控制器注册后挂载到全局链表
  20. struct list_head reset_control_head;////各个模块复位的链表头
  21. struct device *dev;
  22. struct device_node *of_node;
  23. int of_reset_n_cells;////用于解析consumer device dts node中的“resets = <>; ”节点,指示dts中引用时,需要几个参数
  24. int (*of_xlate)(struct reset_controller_dev *rcdev,
  25. const struct of_phandle_args *reset_spec);//用于解析consumer device dts node中的“resets = <>; ”节点
  26. unsigned int nr_resets;//该reset controller所控制的reset信号的个数
  27. };

struct reset_control_ops也比较单纯,如下:

  1. /**
  2. * struct reset_control_ops - reset controller driver callbacks
  3. *
  4. * @reset: for self-deasserting resets, does all necessary
  5. * things to reset the device
  6. * @assert: manually assert the reset line, if supported
  7. * @deassert: manually deassert the reset line, if supported
  8. * @status: return the status of the reset line, if supported
  9. */
  10. struct reset_control_ops {
  11. int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); //控制设备完成一次完整的复位过程
  12. int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); //控制设备reset状态的生效
  13. int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);//控制设备reset状态的失效。
  14. int (*status)(struct reset_controller_dev *rcdev, unsigned long id); //复位状态查询
  15. };

5. reset驱动的设备树描述总结

5.1 对于provider

  1. reset:reset-controller{
  2. compatible = "xx,xx-reset";
  3. reg = <0x0 0x30390000 0x0 0x10000>;
  4. #reset-cells = <1>;
  5. };

上述是一个reset控制器的节点,0x30390000 是寄存器基址,0x1000是映射大小。"#reset-cells"代表引用该reset时需要的cells个数。

5.2 对于consumer

例如,#reset-cells = <1>; 则正确引用为:

  1. mmc:mmc@0x12345678{
  2. ......
  3. resets = <&reset 0>;//0代表reset设备id,id是自定义的,但是不能超过reset驱动中指定的设备个数
  4. ......
  5. };

6. 开源reset驱动实例

6.1 实例1(比较容易理解)

设备树: arch/arm/boot/dts/imx7d.dtsi

  1. pcie: pcie@0x33800000 {
  2. compatible = "fsl,imx7d-pcie", "snps,dw-pcie";
  3. ....
  4. resets = <&src IMX7_RESET_PCIEPHY>,
  5. <&src IMX7_RESET_PCIE_CTRL_APPS_EN>;
  6. reset-names = "pciephy", "apps";
  7. status = "disabled";
  8. };

驱动代码: drivers/reset/reset-imx7.c

  1. ...
  2. struct imx7_src {
  3. struct reset_controller_dev rcdev;
  4. struct regmap *regmap;
  5. };
  6. enum imx7_src_registers {
  7. SRC_A7RCR0 = 0x0004,
  8. SRC_M4RCR = 0x000c,
  9. SRC_ERCR = 0x0014,
  10. SRC_HSICPHY_RCR = 0x001c,
  11. SRC_USBOPHY1_RCR = 0x0020,
  12. SRC_USBOPHY2_RCR = 0x0024,
  13. SRC_MIPIPHY_RCR = 0x0028,
  14. SRC_PCIEPHY_RCR = 0x002c,
  15. SRC_DDRC_RCR = 0x1000,
  16. };
  17. struct imx7_src_signal {
  18. unsigned int offset, bit;
  19. };
  20. static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = {
  21. [IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) },
  22. [IMX7_RESET_A7_CORE_POR_RESET1] = { SRC_A7RCR0, BIT(1) },
  23. [IMX7_RESET_A7_CORE_RESET0] = { SRC_A7RCR0, BIT(4) },
  24. [IMX7_RESET_A7_CORE_RESET1] = { SRC_A7RCR0, BIT(5) },
  25. [IMX7_RESET_A7_DBG_RESET0] = { SRC_A7RCR0, BIT(8) },
  26. [IMX7_RESET_A7_DBG_RESET1] = { SRC_A7RCR0, BIT(9) },
  27. ...
  28. };
  29. static struct imx7_src *to_imx7_src(struct reset_controller_dev *rcdev)
  30. {
  31. return container_of(rcdev, struct imx7_src, rcdev);
  32. }
  33. static int imx7_reset_set(struct reset_controller_dev *rcdev,
  34. unsigned long id, bool assert)
  35. {
  36. struct imx7_src *imx7src = to_imx7_src(rcdev);
  37. const struct imx7_src_signal *signal = &imx7_src_signals[id];
  38. unsigned int value = assert ? signal->bit : 0;
  39. switch (id) {
  40. case IMX7_RESET_PCIEPHY:
  41. /*
  42. * wait for more than 10us to release phy g_rst and
  43. * btnrst
  44. */
  45. if (!assert)
  46. udelay(10);
  47. break;
  48. case IMX7_RESET_PCIE_CTRL_APPS_EN:
  49. value = (assert) ? 0 : signal->bit;
  50. break;
  51. }
  52. return regmap_update_bits(imx7src->regmap,
  53. signal->offset, signal->bit, value);
  54. }
  55. static int imx7_reset_assert(struct reset_controller_dev *rcdev,
  56. unsigned long id)
  57. {
  58. return imx7_reset_set(rcdev, id, true);
  59. }
  60. static int imx7_reset_deassert(struct reset_controller_dev *rcdev,
  61. unsigned long id)
  62. {
  63. return imx7_reset_set(rcdev, id, false);
  64. }
  65. static const struct reset_control_ops imx7_reset_ops = {
  66. .assert = imx7_reset_assert,
  67. .deassert = imx7_reset_deassert,
  68. };
  69. static int imx7_reset_probe(struct platform_device *pdev)
  70. {
  71. struct imx7_src *imx7src;
  72. struct device *dev = &pdev->dev;
  73. struct regmap_config config = { .name = "src" };
  74. imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL);
  75. if (!imx7src)
  76. return -ENOMEM;
  77. imx7src->regmap = syscon_node_to_regmap(dev->of_node);
  78. if (IS_ERR(imx7src->regmap)) {
  79. dev_err(dev, "Unable to get imx7-src regmap");
  80. return PTR_ERR(imx7src->regmap);
  81. }
  82. regmap_attach_dev(dev, imx7src->regmap, &config);
  83. imx7src->rcdev.owner = THIS_MODULE;
  84. imx7src->rcdev.nr_resets = IMX7_RESET_NUM;
  85. imx7src->rcdev.ops = &imx7_reset_ops;
  86. imx7src->rcdev.of_node = dev->of_node;
  87. return devm_reset_controller_register(dev, &imx7src->rcdev);
  88. }
  89. static const struct of_device_id imx7_reset_dt_ids[] = {
  90. { .compatible = "fsl,imx7d-src", },
  91. { /* sentinel */ },
  92. };
  93. static struct platform_driver imx7_reset_driver = {
  94. .probe = imx7_reset_probe,
  95. .driver = {
  96. .name = KBUILD_MODNAME,
  97. .of_match_table = imx7_reset_dt_ids,
  98. },
  99. };
  100. builtin_platform_driver(imx7_reset_driver);

6.2 实例2(在gpio子系统中嵌套reset子系统)

设备树: arc/arm64/boot/dts/myzr/myimx8mm.dts

  1. &pcie0{
  2. pinctrl-names = "default";
  3. pinctrl-0 = <&pinctrl_i2c4_pcieclk>, <&pinctrl_gpio1_pciendis>, <&pinctrl_sd2_pciewake>, <&pinctrl_sai2_pcienrst>;
  4. disable-gpio = <&gpio1 5 GPIO_ACTIVE_LOW>;
  5. reset-gpio = <&gpio4 21 GPIO_ACTIVE_LOW>;
  6. ext_osc = <1>;
  7. status = "okay";
  8. };

驱动代码: drivers/reset/gpio-reset.c

  1. ...
  2. struct gpio_reset_data {
  3. struct reset_controller_dev rcdev;
  4. unsigned int gpio;
  5. bool active_low;
  6. s32 delay_us;
  7. s32 post_delay_ms;
  8. };
  9. static void gpio_reset_set(struct reset_controller_dev *rcdev, int asserted)
  10. {
  11. struct gpio_reset_data *drvdata = container_of(rcdev,
  12. struct gpio_reset_data, rcdev);
  13. int value = asserted;
  14. if (drvdata->active_low)
  15. value = !value;
  16. gpio_set_value_cansleep(drvdata->gpio, value);
  17. }
  18. static int gpio_reset(struct reset_controller_dev *rcdev, unsigned long id)
  19. {
  20. struct gpio_reset_data *drvdata = container_of(rcdev,
  21. struct gpio_reset_data, rcdev);
  22. if (drvdata->delay_us < 0)
  23. return -ENOSYS;
  24. gpio_reset_set(rcdev, 1);
  25. udelay(drvdata->delay_us);
  26. gpio_reset_set(rcdev, 0);
  27. if (drvdata->post_delay_ms < 0)
  28. return 0;
  29. msleep(drvdata->post_delay_ms);
  30. return 0;
  31. }
  32. static int gpio_reset_assert(struct reset_controller_dev *rcdev,
  33. unsigned long id)
  34. {
  35. gpio_reset_set(rcdev, 1);
  36. return 0;
  37. }
  38. static int gpio_reset_deassert(struct reset_controller_dev *rcdev,
  39. unsigned long id)
  40. {
  41. gpio_reset_set(rcdev, 0);
  42. return 0;
  43. }
  44. static struct reset_control_ops gpio_reset_ops = {
  45. .reset = gpio_reset,
  46. .assert = gpio_reset_assert,
  47. .deassert = gpio_reset_deassert,
  48. };
  49. static int of_gpio_reset_xlate(struct reset_controller_dev *rcdev,
  50. const struct of_phandle_args *reset_spec)
  51. {
  52. if (WARN_ON(reset_spec->args_count != 0))
  53. return -EINVAL;
  54. return 0;
  55. }
  56. static int gpio_reset_probe(struct platform_device *pdev)
  57. {
  58. ...
  59. drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
  60. ...
  61. drvdata->rcdev.of_node = np;
  62. drvdata->rcdev.owner = THIS_MODULE;
  63. drvdata->rcdev.nr_resets = 1; ////该reset controller所控制的reset信号的个数
  64. drvdata->rcdev.ops = &gpio_reset_ops; //ops提供reset操作的实现。
  65. drvdata->rcdev.of_xlate = of_gpio_reset_xlate;
  66. reset_controller_register(&drvdata->rcdev); //注册reset controller
  67. return 0;
  68. }
  69. static int gpio_reset_remove(struct platform_device *pdev)
  70. {
  71. struct gpio_reset_data *drvdata = platform_get_drvdata(pdev);
  72. reset_controller_unregister(&drvdata->rcdev);
  73. return 0;
  74. }
  75. static struct of_device_id gpio_reset_dt_ids[] = {
  76. { .compatible = "gpio-reset" },
  77. { }
  78. };
  79. #ifdef CONFIG_PM_SLEEP
  80. static int gpio_reset_suspend(struct device *dev)
  81. {
  82. pinctrl_pm_select_sleep_state(dev);
  83. return 0;
  84. }
  85. static int gpio_reset_resume(struct device *dev)
  86. {
  87. pinctrl_pm_select_default_state(dev);
  88. return 0;
  89. }
  90. #endif
  91. static const struct dev_pm_ops gpio_reset_pm_ops = {
  92. SET_LATE_SYSTEM_SLEEP_PM_OPS(gpio_reset_suspend, gpio_reset_resume)
  93. };
  94. static struct platform_driver gpio_reset_driver = {
  95. .probe = gpio_reset_probe,
  96. .remove = gpio_reset_remove,
  97. .driver = {
  98. .name = "gpio-reset",
  99. .owner = THIS_MODULE,
  100. .of_match_table = of_match_ptr(gpio_reset_dt_ids),
  101. .pm = &gpio_reset_pm_ops,
  102. },
  103. };
  104. static int __init gpio_reset_init(void)
  105. {
  106. return platform_driver_register(&gpio_reset_driver);
  107. }
  108. arch_initcall(gpio_reset_init);
  109. static void __exit gpio_reset_exit(void)
  110. {
  111. platform_driver_unregister(&gpio_reset_driver);
  112. }
  113. ...

7. reset驱动的实质

操作soc对应的reset寄存器,以实现内核IP的复位,或者操作gpio管脚的电平,间接复位接到该pin脚的从设备。

参考

[1] Documentation/devicetree/bindings/reset/reset.txt
[2] Linux reset framework
[2] Linux reset子系统及驱动实例

原文链接:https://www.cnblogs.com/jianhua1992/p/17396492.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号