之前的文章電源管理入門-1關(guān)機重啟詳解介紹了整機SoC的重啟也可以說是reset,那么子系統(tǒng)的reset,例如某個驅(qū)動(網(wǎng)卡、USB等)或者某個子系統(tǒng)(NPU、ISP等運行在獨立的M核或者R核上的AI系統(tǒng)),這些零碎模塊的reset就需要用另外一種機制,Linux提供了reset framework框架,我們可以使用這個框架對子系統(tǒng)reset,然后操作硬件CRU寄存器進行硬件的reset操作。
考慮到安全的因素對CRU寄存器的操作可以放在:
- ATF里面的BL31(通過SMC指令)
- 或者放到SCP里面(通過Linux-SCMI-》SCP)里面進行。
本小節(jié)先介紹下Linux里面的通用reset框架,下篇介紹arm-scmi到SCP進行CRU硬件操作的實現(xiàn)。
1. 簡介
復(fù)雜SoC內(nèi)部有很多具有獨立功能的硬件模塊,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出于功耗、穩(wěn)定性等方面的考慮,有些SoC在內(nèi)部為這些硬件模塊設(shè)計了復(fù)位信號(reset signals),軟件可通過寄存器(一般1個bit控制1個硬件)控制這些硬件模塊的復(fù)位狀態(tài)。
例如有3個軟件I2C/EMMC/IPC都有復(fù)位某個硬件模塊的需求,那么要寫三個復(fù)位操作代碼。
- 這些代碼可以進行抽象出來一個獨立的軟件框架-reset framework,
- 這樣軟件使用者(consumer:I2C/EMMC/IPC)直接使用硬件模塊的名字,就可以對硬件進行復(fù)位。
- 一個模塊硬件的復(fù)位實現(xiàn)為單獨的reset driver(provider),只用實現(xiàn)一次就可以了。
再次說明了,解決復(fù)雜問題的普遍方法就是抽象,而Linux內(nèi)核可以說是玩得一手好抽象,也是操作系統(tǒng)的必備技能。
2. consumer-驅(qū)動軟件
對于硬件驅(qū)動來的需求來說,就是復(fù)位某個硬件,在驅(qū)動代碼里面可以通過硬件的名字進行復(fù)位,這個名字對應(yīng)設(shè)置放在了dts文件中,例如:
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";
};
&rst:使用rst驅(qū)動,0x50:寄存器偏移,11:使用那個bit 進行復(fù)位的時候,在驅(qū)動軟件里面加上
i2c_dev->i2c_rst =
devm_reset_control_get(i2c_dev->dev, "i2c0");
static int i2c_reset_assert(struct reset_control *rstc)
{
int rc = 0;
rc = reset_control_assert(rstc);
if (rc < 0) {
pr_err("%s: failed
", __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
", __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 );
}
i2c_dev->i2c_rst是一個reset_control的結(jié)構(gòu)體
struct reset_control {
struct reset_controller_dev *rcdev;
struct list_head list;
unsigned int id;
struct kref refcnt;
bool acquired;
bool shared;
bool array;
atomic_t deassert_count;
atomic_t triggered_count;
};
上面i2c驅(qū)動作為consumer調(diào)用了reset framework提供的API函數(shù)(include/linux/reset.h),如下:
/* 通過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進行復(fù)位,或者通過reset_control_assert使設(shè)備處于復(fù)位生效狀態(tài),通過reset_control_deassert使復(fù)位失效 */
reset_control_deassert(struct reset_control *rstc)//解復(fù)位
reset_control_assert(struct reset_control *rstc)//復(fù)位
reset_control_reset(struct reset_control *rstc)//先復(fù)位,延遲一會,然后解復(fù)位
3. provider-reset驅(qū)動
3.1 整體介紹
reset驅(qū)動是一個獨立驅(qū)動,為其他驅(qū)動提供硬件復(fù)位的服務(wù)。首先在dts里面設(shè)置.compatible這樣驅(qū)動就可以加載了,如下定義了rst驅(qū)動:
rst: reset-controller {
compatible = "arch64,a10-reset";
#reset-cells = <2>;
reg = <0x0 0x91000000 0x0 0x1000>;
};
上述是一個reset控制器的節(jié)點,0x91000000是寄存器基址,0x1000是映射大小。#reset-cells代表引用該reset時需要的cells個數(shù)。
然后就是reset驅(qū)動的實現(xiàn),reset驅(qū)動編寫的基本步驟:
- 實現(xiàn)struct reset_control_ops結(jié)構(gòu)體中的.reset、.assert、.deassert、.status函數(shù)
- 分配struct reset_controller_dev結(jié)構(gòu)體,填充ops、owner、nr_resets等成員內(nèi)容
- 調(diào)用reset_controller_register函數(shù)注冊reset設(shè)備
首先定義platform_driver:
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);
}
系統(tǒng)初始化,dts中配置了此reset驅(qū)動,就會調(diào)用a10_reset_probe
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
",
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);
}
data->rcdev的定義如下:
struct reset_controller_dev{
const struct reset_control_ops *ops;//復(fù)位控制操作函數(shù)
struct list_head list;//全局鏈表,復(fù)位控制器注冊后掛載到全局鏈表
struct list_head reset_control_head;//各個模塊復(fù)位的鏈表頭
struct device *dev;int of_reset_n_cells;//dts中引用時,需要幾個參數(shù)
//通過dts引用的參數(shù),解析復(fù)位控制器中相應(yīng)的參數(shù)
int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec);unsigned int nr_resets;//復(fù)位設(shè)備個數(shù)
}
- ops提供reset操作的實現(xiàn),基本上是reset provider的所有工作量。
- of_xlate和of_reset_n_cells用于解析consumer device dts node中的“resets = ; ”節(jié)點,如果reset controller比較簡單(僅僅是線性的索引),可以不實現(xiàn),使用reset framework提供的簡單版本----of_reset_simple_xlate即可。
- nr_resets,該reset controller所控制的reset信號的個數(shù)。
a10_reset_ops定義了reset framework的回調(diào)函數(shù),對具體寄存器位進行操作
//reset可控制設(shè)備完成一次完整的復(fù)位過程。
//assert和deassert分別控制設(shè)備reset狀態(tài)的生效和失效。
static const struct reset_control_ops a10_reset_ops = {
.assert = a10_reset_assert,
.deassert = a10_reset_deassert,
.status = a10_reset_status,
};
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;
}
3.2 reset復(fù)位API說明
devm_reset_control_get
struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
?作用:獲取相應(yīng)的reset句柄
?參數(shù):
? dev:指向申請reset資源的設(shè)備句柄
? id:指向要申請的reset資源名(字符串),可以為NULL
?返回:
? 成功:返回reset句柄
? 失敗:返回NULL
reset_control_deassert
int reset_control_deassert(struct reset_control *rstc)
?作用:對傳入的reset資源進行解復(fù)位操作
?參數(shù):
? rstc:指向申請reset資源的設(shè)備句柄
?返回:
? 成功:返回0
? 失?。悍祷劐e誤碼
reset_control_assert
int reset_control_assert(struct reset_control *rstc)
?作用:對傳入的reset資源進行復(fù)位操作。
參數(shù)和返回值與reset_control_deassert相同
reset_control_reset
int reset_control_reset(struct reset_control *rstc)
?作用:對傳入的reset資源先進行復(fù)位操作,然后等待5us,再進行解復(fù)位操作。
?相當于執(zhí)行了一遍reset_control_assert后,然后delay一會,再調(diào)用reset_control_deassert
后記:
使用markdown寫中文發(fā)現(xiàn)段落行首空格實在不好搞,然后調(diào)研了很多牛人寫的中文博客發(fā)現(xiàn)行首不用空格的很多,咱們這里為了方便書寫,也不要行首空格了。畢竟工具是服務(wù)人的,規(guī)則都是在變化的。
后續(xù)文章先在稀土掘金首發(fā)(寫的快),然后復(fù)制過來,歡迎關(guān)注:https://juejin.cn/user/2052111227697336
電源管理,可能很多人不喜歡看,我分幾次多篇一塊發(fā)完。也歡迎大家把喜歡看的技術(shù)留言。
電源管理這個專欄其實比較小眾,大伙并不是那么愛看,我就先多寫幾篇存著,到時一塊推送,避免公共資源的浪費,節(jié)省點大家的時間。有時候我也劃開微信看看直播和視頻號,發(fā)現(xiàn)很多無腦的直播,比如河邊鋼筋磨石頭、在家轉(zhuǎn)大棍子,什么科目三,感覺這些都有人看,這么無腦,我就算寫點垃圾文字也比這強的吧,也有可能人看視頻就是為了無腦休息下。
-
控制器
+關(guān)注
關(guān)注
112文章
16332瀏覽量
177803 -
寄存器
+關(guān)注
關(guān)注
31文章
5336瀏覽量
120228 -
reset
+關(guān)注
關(guān)注
0文章
34瀏覽量
12877
原文標題:3. provider-reset驅(qū)動
文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論