赞
踩
复杂IC内部有很多具有独立功能的硬件模块,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出于功耗、稳定性等方面的考虑,有些IC在内部为这些硬件模块设计了复位信号(reset signals),软件可通过寄存器(一般1个bit控制1个硬件)控制这些硬件模块的复位状态。
Linux kernel为了方便设备驱动的编写,抽象出一个简单的软件框架----reset framework,为reset的provider提供统一的reset资源管理手段,并为reset的consumer(各个硬件模块)提供便捷、统一的复位控制API。
reset子系统也分为了consumer和provider,结构体关系如下:
对于一个具体的硬件模块,它的要求很简单:复位我的硬件模块,而不必关注具体复位的手段(例如控制哪个寄存器的哪个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. |
static inline int __must_check device_reset(struct device *dev) |
/* 通过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);//解复位 |
kernel为reset provider提供的API位于"include/linux/reset-controller.h"中,很简单,无非就是:创建并填充reset controller设备(struct reset_controller_dev),并调用相应的接口:
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); //复位状态查询 | |
}; |
reset:reset-controller{ | |
compatible = "xx,xx-reset"; | |
reg = <0x0 0x30390000 0x0 0x10000>; | |
#reset-cells = <1>; | |
}; |
上述是一个reset控制器的节点,0x30390000 是寄存器基址,0x1000是映射大小。"#reset-cells"代表引用该reset时需要的cells个数。
例如,#reset-cells = <1>; 则正确引用为:
mmc:mmc@0x12345678{ | |
...... | |
resets = <&reset 0>;//0代表reset设备id,id是自定义的,但是不能超过reset驱动中指定的设备个数 | |
...... | |
}; |
设备树: 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); | |
设备树: 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); | |
} | |
... |
操作soc对应的reset寄存器,以实现内核IP的复位,或者操作gpio管脚的电平,间接复位接到该pin脚的从设备。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。