Linux CommonClock Framework分析之四 gpio clk gate驱动实现

     前面我们已经完成了CCF子系统的分析,也说明了如何实现CCF驱动,本章为该专栏的最后一篇文章,

本章我们将实现一个虚拟的gpio clk gate驱动。本章大概分为如下几个章节:
一、 本次驱动开发涉及的知识点

 

二、clk provider driver的实现流程说明

 

三、gpio clk gate driver驱动设计

 

 

一、 本次驱动开发涉及的知识点

本次驱动实践主要涉及如下几个方面的知识点:

  1. Platform device、platform drvier
  2. Gpio 驱动使用
  3. Clk provider驱动开发流程

 

二、clk provider driver的实现流程说明

 

关于clk provider driver的开发流程,在上一篇文章中已经做了说明,此处再说明一下,主要实现流程包括如下两步:

  1. 完成clk的注册,主要是调用clk_register接口,完成上述章节一所述的内容;
  2. 完成该clk provider的map,这种map机制可以理解为定义了clk consumer与clk_provider的映射关系,即该clk provider可以给哪些clk consumer提供时钟(如针对非设备树模式,则定义了clk consumer的设备名称、clk consumer的时钟使用名称),而clk provider的map存在两种方式:
    1. 若linux不支持设备树机制,则通过调用接口clk_register_clkdev,完成这种映射操作(即完成下图中“非设备树模式下clk_core的map”)。
    2. 若linux支持设备树机制,则通过调用接口of_clk_add_provider,完成map操作(即完成下图中“设备树模式下clk_core的map”)

 

三、gpio clk gate driver驱动设计

本次我们主要实现gpio clk gate driver,本次实现的平台为ubuntu16.04,我们使用之前模拟的gpio chip

driver,提供gpio index,从而通过gpio 的值实现gpio clk gate driver。。另外针对clk gate,仅需要提供enable、disable、is_enabled接口即可。

 

相关数据结构


    结构体struct virt_gpio_clk_gate作为本次gpio clk gate driver的数据结构,主要包含clk provier device、gpio index描述信息;

struct virt_gpio_clk_gate

{

        struct gpio_desc *gpiod_clk_gate;

        struct clk_hw   hw;

};

       而结构体gpio_clk_gate_platform_data主要用于传递platform device for clk provider的enable gpio index信息,因为此次我们使用ubuntu16.04进行验证,因此使用该模式传递gpio 信息,包含gpio index、是否低有效等信息。

struct gpio_clk_gate_platform_data

{

        int enable_gpio_index;

        int active_low;

};

 

 

    gpio clk provider driver实现

        Gpio clk provider driver的实现如下,主要完成clk provider device注册、clk_ops的实现(enable、disable、is_enabled)等。如下即为clk provider driver的probe接口,主要调用clk_register完成clk provider的注册、并调用of_clk_add_provider/clk_register_clkdev实现clk provider的map。

static int virt_gpio_clk_gate_probe(struct platform_device *pdev)
{
        struct gpio_clk_gate_platform_data *pdata = (struct gpio_clk_gate_platform_data *)(pdev->dev.platform_data);
        struct virt_gpio_clk_gate * gpio_clk_gate_ptr = NULL;
        struct clk_init_data init_data;
        unsigned long gate_gpio_flag = 0;
        struct clk *clk;
        int ret = 0;
        #ifdef CONFIG_OF
        int en_gpio = 0;
        enum of_gpio_flags of_flags;
        #endif

        printk("%s %d\n", __FUNCTION__, __LINE__);
        gpio_clk_gate_ptr = devm_kzalloc(&pdev->dev, sizeof(struct virt_gpio_clk_gate), GFP_KERNEL);

        if (!gpio_clk_gate_ptr)
                return -ENOMEM;

        printk("%s %d\n", __FUNCTION__, __LINE__);
        memset(&init_data, 0, sizeof(init_data));

        init_data.num_parents = 0;
        init_data.ops = &virt_gpio_clk_gate_ops;
        init_data.name = pdev->name;
        init_data.flags = CLK_IS_ROOT;

        if(pdata != NULL)
        {
                gpio_clk_gate_ptr->gpiod_clk_gate = gpio_to_desc(pdata->enable_gpio_index);
                gpio_clk_gate_ptr->hw.init = &init_data;

                printk("%s %d\n", __FUNCTION__, __LINE__);
                if(pdata->active_low)
                        gate_gpio_flag = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_HIGH;
                else
                        gate_gpio_flag = GPIOF_OUT_INIT_LOW;
        }

        #ifdef CONFIG_OF
        if(pdev->dev.of_node != NULL)
        {
                en_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "enable-gpio", 0, &of_flags);
                if(en_gpio < 0)
                        return en_gpio;

                if(of_flags & OF_GPIO_ACTIVE_LOW)
                        gate_gpio_flag = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_HIGH;
                else
                        gate_gpio_flag = GPIOF_OUT_INIT_LOW;

        }
        #endif

        printk("%s %d gpio index=%d\n", __FUNCTION__, __LINE__, pdata->enable_gpio_index);
        ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio_index, gate_gpio_flag, pdev->name);
        if (ret)
        {
                printk("%s %d ret=%d\n", __FUNCTION__, __LINE__, ret);
                return ret;
        }

        printk("%s %d\n", __FUNCTION__, __LINE__);
        clk = devm_clk_register(&pdev->dev, &gpio_clk_gate_ptr->hw);
        if (IS_ERR(clk))
                return -EINVAL;

        printk("%s %d\n", __FUNCTION__, __LINE__);
        if(pdev->dev.of_node != NULL)
                return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
        else
                return clk_register_clkdev(clk, "virt_gpio_gate", NULL);

        printk("%s %d\n", __FUNCTION__, __LINE__);
}

static int virt_gpio_clk_gate_remove(struct platform_device *pdev)
{


        printk("%s %d\n", __FUNCTION__, __LINE__);
        return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id of_gpio_clk_gate_match[] = {
        { .compatible = "virt_gpio_clk", },
        {},
};
#endif
static struct platform_driver gpio_clk_gate_driver = {
        .probe          = virt_gpio_clk_gate_probe,
        .remove         = virt_gpio_clk_gate_remove,
        .driver         = {
                .name   = "virt_gpio_clk",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(of_gpio_clk_gate_match),
        },
};

module_platform_driver(gpio_clk_gate_driver);

MODULE_AUTHOR("jerry_chg");
MODULE_DESCRIPTION("gpio clk gate driver");
MODULE_LICENSE("GPL");
                       

该gpio clk gate provider 的clk_ops操作接口定义如下:

#define to_clk_gpio(_hw) container_of(_hw, struct virt_gpio_clk_gate, hw)

static int virt_gpio_clk_gate_enable(struct clk_hw *hw)
{
        struct virt_gpio_clk_gate *gpio_clk_gate_ptr = to_clk_gpio(hw);

        gpiod_set_value(gpio_clk_gate_ptr->gpiod_clk_gate, 1);
        return 0;
}

static void virt_gpio_clk_gate_disable(struct clk_hw *hw)
{
        struct virt_gpio_clk_gate *gpio_clk_gate_ptr = to_clk_gpio(hw);
        gpiod_set_value(gpio_clk_gate_ptr->gpiod_clk_gate, 0);
}

static int virt_gpio_clk_gate_is_enabled(struct clk_hw *hw)
{
        struct virt_gpio_clk_gate *gpio_clk_gate_ptr = to_clk_gpio(hw);

        return gpiod_get_value(gpio_clk_gate_ptr->gpiod_clk_gate);
}

static struct clk_ops virt_gpio_clk_gate_ops =
{
        .enable = virt_gpio_clk_gate_enable,
        .disable = virt_gpio_clk_gate_disable,
        .is_enabled = virt_gpio_clk_gate_is_enabled,

};

 gpio clk consumer driver实现

     该consumer driver驱动主要用于验证gpio clk provider driver是否生效,我们在probe接口完成clk的enable操作;而在remove接口完成clk 的disable操作。接口实现如下:

static int virt_gpio_clk_consumer_probe(struct platform_device *pdev)
{
        struct virt_gpio_consumer_data *pdata = (struct virt_gpio_consumer_data *)(pdev->dev.platform_data);
        struct virt_clk_consumer *consumer_ptr = NULL;

        printk("%s %d\n", __FUNCTION__, __LINE__);
        consumer_ptr = devm_kzalloc(&pdev->dev, sizeof(struct virt_clk_consumer), GFP_KERNEL);

        if (!consumer_ptr)
                return -ENOMEM;

        printk("%s %d\n", __FUNCTION__, __LINE__);
        consumer_ptr->clk = clk_get(&pdev->dev, pdata->con_id);
        if (IS_ERR(consumer_ptr->clk))
        {
                return PTR_ERR(consumer_ptr->clk);
        }
        printk("%s %d\n", __FUNCTION__, __LINE__);
        platform_set_drvdata(pdev, consumer_ptr);
        clk_prepare_enable(consumer_ptr->clk);
        return 0;
}

static int virt_gpio_clk_consumer_remove(struct platform_device *pdev)
{
        struct virt_clk_consumer *consumer_ptr = platform_get_drvdata(pdev);

        clk_disable_unprepare(consumer_ptr->clk);
        clk_put(consumer_ptr->clk);
        printk("%s %d\n", __FUNCTION__, __LINE__);
        return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id of_gpio_clk_consumer_match[] = {
        { .compatible = "virt_gpio_consumer", },
        {},
};
#endif
static struct platform_driver gpio_clk_gate_driver = {
        .probe          = virt_gpio_clk_consumer_probe,
        .remove         = virt_gpio_clk_consumer_remove,
        .driver         = {
                .name   = "virt_gpio_consumer",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(of_gpio_clk_consumer_match),
        },
};

module_platform_driver(gpio_clk_gate_driver);

MODULE_AUTHOR("jerry_chg");
MODULE_DESCRIPTION("gpio clk gate driver");
MODULE_LICENSE("GPL");

Virt gpio chip driver

该驱动使用的是gpio driver子系统专栏中实现的虚拟gpio驱动,本处不再细述,想要了解的童鞋请参考

之前的文章。

 

 

测试验证:

  • 加载virt gpio 驱动
    1. insmod virt_gpio_dev.ko;
    2. insmod virt_gpio.ko
  • 加载gpio clk gate 驱动
    1. insmod gpio_clk_platform.ko
    2. insmod gpio_clk_driver.ko
  • 加载clk consumer device驱动
    1. insmod gpio_comsumer_device.ko
  • 查看当前gpio的值:

 

  • 加载clk consumer driver驱动
    1. insmod gpio_comsumer_driver.ko
  • 查看当前gpio的值

此时gpio已经改变,clk已经使能;

 

  • 卸载clk consumer driver驱动
    1. insmod gpio_comsumer_driver.ko
  • 查看当前gpio的值

此时gpio已经修改为0,clk 已经关闭;

 

 

 

     以上就是本章的主要内容,我们主要实现一个gpio clk gate provider driver,至此我们基本上已完成CCF子系统的学习,本章的驱动代码链接如下:

https://gitee.com/jerry_chg/gpio_clk_gate.git

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页