赞
踩
文章参考蜗壳科技
Linux kernel为了方便设备驱动的编写,抽象出一个简单的软件框架----reset framework,为reset的provider提供统一的reset资源管理手段,并为reset的consumer(各个硬件模块)提供便捷、统一的复位控制API。reset framework可以分为consumer和provider。provider也就是具体reset动作的提供者,实现了某个模块的具体的reset过程,provider是reset框架代码主要部分;consumer也就是reset framework的使用者,就如在某个外设模块的驱动中,我们需要调用reset函数,驱动模块就是一个consumer
从某一个硬件模块的驱动设计者来看,他的要求很简单:我只是想复位我的硬件,而不想知道到底用什么手段才能复位(例如控制哪个寄存器的哪个bit位,等等)。
这个要求其实体现了软件设计(甚至是任何设计)中的一个最最质朴的设计理念:封装和抽象。对设备驱动来说,它期望看到是“reset”这个通用概念,用这个通用概念去发号施令的话,这个驱动就具备了通用性和可移植性(无论在周围的环境如何变化,“reset”本身不会变化)。而至于怎么reset,是通过寄存器A的bit m,还是寄存器B的bit n,则是平台维护者需要关心的事情(就是本文的reset provider)。
看到这样的要求,Linux kernel说:OK,于是reset framework出场,提供了如下的机制(基于device tree):
1)首先,提供描述系统中reset资源的方法(参考下面第3章的介绍),这样consumer可以基于这种描述在自己的dts node中引用所需的reset信号。
2)然后,consumer设备在自己的dts node中使用“resets”、“reset-names”等关键字声明所需的reset的资源,例如[1](“resets”字段的具体格式由reset provider决定”):
device {
resets = <&rst 20>;
reset-names = "reset";
};
3)最后,consumer driver在需要的时候,可以调用下面的API复位自己(具体可参考“include/linux/reset.h“):
3-a)只有一个reset信号的话,可以使用最简单的device_reset API
int device_reset(struct device *dev);
3-b)如果需要更为复杂的控制(例如有多个reset信号、需要控制处于reset状态的长度的等),可以使用稍微复杂的API
/* 通过reset_control_get或者devm_reset_control_get获得reset句柄 */
struct reset_control *reset_control_get(struct device *dev, const char *id);
void reset_control_put(struct reset_control *rstc);
struct reset_control *devm_reset_control_get(struct device *dev, const char *id);
/* 通过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);
kernel为reset provider提供的API位于“include/linux/reset-controller.h”中,很简单,无非就是:创建并填充reset controller设备(struct reset_controller_dev),并调用相应的接口(reset_controller_register/reset_controller_unregister)注册或者注销之。
reset controller的抽象也很简单:
struct reset_controller_dev {
struct reset_control_ops *ops;
struct module *owner;
struct list_head list;
struct device_node *of_node;
int of_reset_n_cells;
int (*of_xlate)(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec);
unsigned int nr_resets;
};
ops提供reset操作的实现,基本上是reset provider的所有工作量。
of_xlate和of_reset_n_cells用于解析consumer device dts node中的“resets = ; ”节点,如果reset controller比较简单(仅仅是线性的索引),可以不实现,使用reset framework提供的简单版本----of_reset_simple_xlate即可。
nr_resets,该reset controller所控制的reset信号的个数。
其它字段内部使用,provider不需要关心。
struct reset_control_ops也比较单纯,如下:
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);
};
reset可控制设备完成一次完整的复位过程。
assert和deassert分别控制设备reset状态的生效和失效。
有了上述的基本知识之后,我们就可以来完成一个reset framework了
首先是device tree中需要对reset进行定义
rst: reset-controller { compatible = "arch64,a10-reset"; #reset-cells = <2>; reg = <0x0 0x91000000 0x0 0x1000>; }; i2c0: i2c@0xA1006000 { compatible = "arch64,a10-i2c"; reg = <0 0xA1006000 0 0x100>; interrupt-parent = <&gic>; interrupts = <0 32 4>; clock-frequency = <24000000>; resets = <&rst 0x50 11>; reset-names = "i2c0"; status = "disabled"; };
上面就是一个简单的reset framework 的device tree。
1-5行是reset-controller的内容,第二行是reset controller的匹配字符串,第四行是reset controller的寄存器和大小.
6-15行则是i2c0的device tree描述,其中13行表示的i2c0的reset的名称,便于在驱动中获取,第12行则表示i2c0使用的reset controller ,寄存器的偏移以及那个bit。
接下来我们看看代码中如何使用这些信息的
在驱动的probe代码中,驱动需要获取reset的信息
i2c_dev->i2c_rst =
devm_reset_control_get(i2c_dev->dev, "i2c0");
i2c_rst是一个reset_control的结构体
struct reset_control *cnn_rst;
对于provider来讲,使用reset framework很简单
static int i2c_reset_assert(struct reset_control *rstc) { int rc = 0; rc = reset_control_assert(rstc); if (rc < 0) { pr_err("%s: failed\n", __func__); return rc; } return rc; } static int i2c_reset_assert(struct reset_control *rstc) { int rc = 0; rc = reset_control_assert(rstc); if (rc < 0) { pr_err("%s: failed\n", __func__); return rc; } return rc; } static int i2c_hw_reset(struct i2c_dev *i2c_dev) { i2c_reset_assert(i2c_dev->i2c_rst ); udelay(1); i2c_reset_release(i2c_dev->i2c_rst ); }
reset framework的主要工作由provider来完成,将provider也作为一个驱动来完成,
static const struct of_device_id a10_reset_dt_ids[] = { { .compatible = "hobot,a10-reset", }, { }, }; static struct platform_driver a10_reset_driver = { .probe = a10_reset_probe, .driver = { .name = KBUILD_MODNAME, .of_match_table = a10_reset_dt_ids, }, }; static int __init a10_reset_init(void) { return platform_driver_register(&a10_reset_driver); }
首先将reset驱动作为一个platform设备驱动进行注册。
static int a10_reset_probe(struct platform_device *pdev) { struct a10_reset_data *data; struct resource *res; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; u32 modrst_offset; /* * The binding was mainlined without the required property. * Do not continue, when we encounter an old DT. */ if (!of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) { dev_err(&pdev->dev, "%s missing #reset-cells property\n", pdev->dev.of_node->full_name); return -EINVAL; } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(data->membase)) return PTR_ERR(data->membase); spin_lock_init(&data->lock); data->rcdev.owner = THIS_MODULE; data->rcdev.nr_resets = a10_MAX_NR_RESETS; data->rcdev.ops = &a10_reset_ops; data->rcdev.of_node = pdev->dev.of_node; data->rcdev.of_xlate = a10_reset_of_xlate; data->rcdev.of_reset_n_cells = 2; return devm_reset_controller_register(dev, &data->rcdev); }
接下来是驱动的probe代码,获取设备树中的信息。接下来最重要的就是实现第32行的a10_reset_ops结构体
static int a10_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) { void __iomem *regaddr; uint32_t reg_val, offset; unsigned long flags; u8 bit; struct a10_reset_data *data = to_a10_reset_data(rcdev); if (rcdev == NULL || id < 0) return -EINVAL; spin_lock_irqsave(&data->lock, flags); offset = (id & RESET_REG_OFFSET_MASK) >> RESET_REG_OFFSET_SHIFT; regaddr = data->membase + offset; reg_val = readl(regaddr); bit = (id & RESET_REG_BIT_MASK); reg_val |= BIT(bit); writel(reg_val, regaddr); spin_unlock_irqrestore(&data->lock, flags); return 0; } static int a10_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) { void __iomem *regaddr; uint32_t reg_val, offset; unsigned long flags; u8 bit; struct a10_reset_data *data = to_a10_reset_data(rcdev); if (rcdev == NULL || id < 0) return -EINVAL; spin_lock_irqsave(&data->lock, flags); offset = (id & RESET_REG_OFFSET_MASK) >> RESET_REG_OFFSET_SHIFT; regaddr = data->membase + offset; reg_val = readl(regaddr); bit = (id & RESET_REG_BIT_MASK); reg_val &= ~(BIT(bit)); writel(reg_val, regaddr); spin_unlock_irqrestore(&data->lock, flags); return 0; } static int a10_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { return 0; } static const struct reset_control_ops a10_reset_ops = { .assert = a10_reset_assert, .deassert = a10_reset_deassert, .status = a10_reset_status, };
assert和deassert就是对设备树中指定的寄存器和bit位进行操作,以实现模块的reset
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。