上篇講了Linux clock驅(qū)動,今天說說Linux的reset驅(qū)動。
時鐘和復(fù)位是兩個不同的驅(qū)動,但通常都是由負責(zé)clock驅(qū)動的人,把reset驅(qū)動完成。同樣,reset驅(qū)動也是由芯片廠商去完成的。
Linux reset子系統(tǒng)
reset子系統(tǒng)非常簡單,與clock子系統(tǒng)非常類似,但在驅(qū)動實現(xiàn)上,reset驅(qū)動更簡單。
因為clock驅(qū)動主要是時鐘的實現(xiàn),涉及到固定時鐘、分頻、門控等一些時鐘的分級關(guān)系,需要弄清楚時鐘樹里每個時鐘的關(guān)系。
而reset驅(qū)動有點相當于clock驅(qū)動的門控,它只有復(fù)位和解復(fù)位兩個功能。
類似于clock子系統(tǒng),reset子系統(tǒng)也分為了consumer
和provider
,結(jié)構(gòu)體關(guān)系如下:
consumer :
reset API接口的使用者,內(nèi)核提供了統(tǒng)一的reset接口:
devm_reset_control_get(struct device *dev, const char *id)//獲取reset句柄
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ù)位
struct reset_control結(jié)構(gòu)體表示一個reset句柄,驅(qū)動中使用reset API,需要先獲取reset句柄
provider :
reset提供者,即reset驅(qū)動。struct reset_controller_dev
結(jié)構(gòu)體代表一個reset
控制器,內(nèi)部包含了reset操作函數(shù)集合struct reset_control_ops
,注冊reset驅(qū)動時,需要分配一個struct reset_controller_dev結(jié)構(gòu)體,然后填充成員,最后將該結(jié)構(gòu)體注冊。
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ù)
}
struct reset_control_ops{
int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);//復(fù)位+解復(fù)位
int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);//復(fù)位
int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);//解復(fù)位
int (*status)(struct reset_controller_dev *rcdev, unsigned long id);//復(fù)位狀態(tài)查詢
}
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句柄
- 失?。悍祷豊ULL
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
reset API使用示例
基本步驟:
1、調(diào)用devm_reset_control_get()
獲取reset句柄
2、調(diào)用reset_control_assert()
進行復(fù)位操作
3、調(diào)用reset_control_deassert()
進行解復(fù)位操作
static int xx_probe(struct platform_device *pdev)
{
struct device_node* np = pdev-?>dev.of_node;
......
/* 1、獲取reset句柄 */
host-?>rstc = devm_reset_control_get(&pdev-?>dev, np-?>name);
if (IS_ERR(host-?>rstc)) {
dev_err(&pdev-?>dev, "No reset controller specified\\n");
return PTR_ERR(host-?>rstc);
}
if (host-?>rstc) {
/* 2、復(fù)位 */
ret = reset_control_assert(host-?>rstc);
if (ret) {
dev_err(&pdev-?>dev, "unable to reset_control_assert\\n");
return ret;
}
udelay(1);
/* 3、解復(fù)位 */
ret = reset_control_deassert(host-?>rstc);
if (ret) {
dev_err(&pdev-?>dev, "unable to reset_control_deassert\\n");
return ret;
}
}
......
}
reset驅(qū)動實例
類似于clock驅(qū)動,reset驅(qū)動也是編進內(nèi)核的,在Linux啟動時,完成reset驅(qū)動的加載。
設(shè)備樹
reset:reset-controller{
compatible = "xx,xx-reset";
reg = 0x0 0xc0000000 0x0 0x1000?>;
#reset-cells = 1?>;
};
上述是一個reset控制器的節(jié)點,0xc0000000
是寄存器基址,0x1000
是映射大小。 #reset-cells
代表引用該reset時需要的cells個數(shù)。
例如,#reset-cells = <1>;
則正確引用為:
mmc:mmc@0x12345678{
......
resets = &reset 0?>;//0代表reset設(shè)備id,id是自定義的,但是不能超過reset驅(qū)動中指定的設(shè)備個數(shù)
......
};
驅(qū)動編寫
reset驅(qū)動編寫的基本步驟:
1、實現(xiàn)struct reset_control_ops
結(jié)構(gòu)體中的.reset
、.assert
、.deassert
、.status
函數(shù)
2、分配struct reset_controller_dev
結(jié)構(gòu)體,填充ops
、owner
、nr_resets
等成員內(nèi)容
3、調(diào)用reset_controller_register
函數(shù)注冊reset設(shè)備
以下是從實際項目中分離出來的reset驅(qū)動代碼:
#include
#include
#include
#include
#include
#include
// 自定義芯片廠的結(jié)構(gòu)體,保存寄存器基址等信息
struct xx_reset{
struct reset_controller_dev rcdev;
void __iomem *base;
//......
};
static int xx_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:先復(fù)位,延遲一會,然后解復(fù)位
return 0;
}
static int xx_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:復(fù)位
return 0;
}
static int xx_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:解復(fù)位
return 0;
}
static int xx_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:獲取復(fù)位狀態(tài)
return 0;
}
static struct reset_control_ops xx_reset_ops = {
.rest = xx_rest,
.assert = xx_reset_asser,
.deassert = xx_reset_deassert,
.status = xx_rest_status,
};
static int xx_reset_probe(struct platform_device *pdev)
{
struct xx_reset *xx_reset;
struct resource *res;
xx_reset = devm_kzalloc(&pdev-?>dev, sizeof(*xx_reset), GFP_KERNEL);
if (!xx_reset)
return -ENOMEM;
platform_set_drvdata(pdev, xx_reset);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xx_reset-?>base = devm_ioremap_resource(&pdev-?>dev, res);//映射寄存器基址
if (IS_ERR(xx_reset-?>base))
return PTR_ERR(xx_reset-?>base);
xx_reset-?>rcdev.ops = &xx_reset_ops;//reset_ops操作函數(shù)集合
xx_reset-?>rcdev.owner = THIS_MODULE;
xx_reset-?>rcdev.of_node = pdev-?>dev.of_node;
xx_reset-?>rcdev.of_reset_n_cells = 1;
xx_reset-?>rcdev.nr_resets = BITS_PER_LONG;//reset設(shè)備個數(shù)
return reset_controller_register(&xx_reset-?>rcdev);//注冊reset controller
}
static int xx_reset_remove(struct platform_device *pdev)
{
struct xx_reste *xx_reset = platform_get_drvdata(pdev);
reset_controller_unregister(&xx_reset-?>rcdev);
return 0;
}
static const struct of_device_id ak_reset_of_match[]={
{.compatible = "xx,xx-reset"},
{},
};
MODULE_DEVICE_TABLE(of, xx_reset_of_match);
static struct platform_driver xx_reset_driver = {
.probe = xx_reset_probe,
.remove = xx_reset_remove,
.driver = {
.name = "xx-reset",
.of_match_table = ak_reset_of_match,
},
};
module_platorm_driver(xx_reset_driver);
MODULE_LICENSE("GPL");
MODULE_DESCPRIPTION("xx reset controller driver");
MODULE_AUTHOR("xx Microelectronic");
MODULE_VERSION("v1.0.00");