linux reset框架

文章參考蝸殼科技

1 前言

Linux kernel爲了方便設備驅動的編寫,抽象出一個簡單的軟件框架----reset framework,爲reset的provider提供統一的reset資源管理手段,併爲reset的consumer(各個硬件模塊)提供便捷、統一的復位控制API。reset framework可以分爲consumer和provider。provider也就是具體reset動作的提供者,實現了某個模塊的具體的reset過程,provider是reset框架代碼主要部分;consumer也就是reset framework的使用者,就如在某個外設模塊的驅動中,我們需要調用reset函數,驅動模塊就是一個consumer

2 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);

3 provider

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狀態的生效和失效。

4 代碼實例

有了上述的基本知識之後,我們就可以來完成一個reset framework了

4.1 設備樹

首先是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。

4.2 consumer

接下來我們看看代碼中如何使用這些信息的
在驅動的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 );

}

4.3 provider

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章