上篇講了Linux clock驅動,今天說說Linux的reset驅動。
時鐘和復位是兩個不同的驅動,但通常都是由負責clock驅動的人,把reset驅動完成。同樣,reset驅動也是由芯片廠商去完成的。
Linux reset子系統
reset子系統非常簡單,與clock子系統非常類似,但在驅動實現上,reset驅動更簡單。
因為clock驅動主要是時鐘的實現,涉及到固定時鐘、分頻、門控等一些時鐘的分級關系,需要弄清楚時鐘樹里每個時鐘的關系。
而reset驅動有點相當于clock驅動的門控,它只有復位和解復位兩個功能。
類似于clock子系統,reset子系統也分為了consumer
和provider
,結構體關系如下:
consumer :
reset API接口的使用者,內核提供了統一的reset接口:
devm_reset_control_get(struct device *dev, const char *id)//獲取reset句柄
reset_control_deassert(struct reset_control *rstc)//解復位
reset_control_assert(struct reset_control *rstc)//復位
reset_control_reset(struct reset_control *rstc)//先復位,延遲一會,然后解復位
struct reset_control結構體表示一個reset句柄,驅動中使用reset API,需要先獲取reset句柄
provider :
reset提供者,即reset驅動。struct reset_controller_dev
結構體代表一個reset
控制器,內部包含了reset操作函數集合struct reset_control_ops
,注冊reset驅動時,需要分配一個struct reset_controller_dev結構體,然后填充成員,最后將該結構體注冊。
struct reset_controller_dev{
const struct reset_control_ops *ops;//復位控制操作函數
struct list_head list;//全局鏈表,復位控制器注冊后掛載到全局鏈表
struct list_head reset_control_head;//各個模塊復位的鏈表頭
struct device *dev;
int of_reset_n_cells;//dts中引用時,需要幾個參數
//通過dts引用的參數,解析復位控制器中相應的參數
int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec);
unsigned int nr_resets;//復位設備個數
}
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);//解復位
int (*status)(struct reset_controller_dev *rcdev, unsigned long id);//復位狀態查詢
}
reset復位API說明
devm_reset_control_get
struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
- 作用 :獲取相應的reset句柄
- 參數 :
- dev:指向申請reset資源的設備句柄
- id:指向要申請的reset資源名(字符串),可以為NULL
- 返回 :
- 成功:返回reset句柄
- 失敗:返回NULL
reset_control_deassert
int reset_control_deassert(struct reset_control *rstc)
- 作用 :對傳入的reset資源進行解復位操作
- 參數 :
- rstc:指向申請reset資源的設備句柄
- 返回 :
- 成功:返回0
- 失敗:返回錯誤碼
reset_control_assert
int reset_control_assert(struct reset_control *rstc)
- 作用 :對傳入的reset資源進行復位操作。
參數和返回值與reset_control_deassert
相同
reset_control_reset
int reset_control_reset(struct reset_control *rstc)
- 作用:對傳入的reset資源先進行復位操作,然后等待5us,再進行解復位操作。
- 相當于執行了一遍
reset_control_assert
后,然后delay一會,再調用reset_control_deassert
reset API使用示例
基本步驟:
1、調用devm_reset_control_get()
獲取reset句柄
2、調用reset_control_assert()
進行復位操作
3、調用reset_control_deassert()
進行解復位操作
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、復位 */
ret = reset_control_assert(host-?>rstc);
if (ret) {
dev_err(&pdev-?>dev, "unable to reset_control_assert\\n");
return ret;
}
udelay(1);
/* 3、解復位 */
ret = reset_control_deassert(host-?>rstc);
if (ret) {
dev_err(&pdev-?>dev, "unable to reset_control_deassert\\n");
return ret;
}
}
......
}
reset驅動實例
類似于clock驅動,reset驅動也是編進內核的,在Linux啟動時,完成reset驅動的加載。
設備樹
reset:reset-controller{
compatible = "xx,xx-reset";
reg = 0x0 0xc0000000 0x0 0x1000?>;
#reset-cells = 1?>;
};
上述是一個reset控制器的節點,0xc0000000
是寄存器基址,0x1000
是映射大小。 #reset-cells
代表引用該reset時需要的cells個數。
例如,#reset-cells = <1>;
則正確引用為:
mmc:mmc@0x12345678{
......
resets = &reset 0?>;//0代表reset設備id,id是自定義的,但是不能超過reset驅動中指定的設備個數
......
};
驅動編寫
reset驅動編寫的基本步驟:
1、實現struct reset_control_ops
結構體中的.reset
、.assert
、.deassert
、.status
函數
2、分配struct reset_controller_dev
結構體,填充ops
、owner
、nr_resets
等成員內容
3、調用reset_controller_register
函數注冊reset設備
以下是從實際項目中分離出來的reset驅動代碼:
#include
#include
#include
#include
#include
#include
// 自定義芯片廠的結構體,保存寄存器基址等信息
struct xx_reset{
struct reset_controller_dev rcdev;
void __iomem *base;
//......
};
static int xx_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:先復位,延遲一會,然后解復位
return 0;
}
static int xx_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:復位
return 0;
}
static int xx_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:解復位
return 0;
}
static int xx_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
{
//操作寄存器:獲取復位狀態
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操作函數集合
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設備個數
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");