嵌入式开发记录-day44 pinctrl子系统实现led驱动

1、pinctrl子系统提供给驱动的API接口

  Linux官方参考文档:doc "Documentation/driver-api/pinctl.rst"

   例程:page1225  

#include <linux/pinctrl/consumer.h>

        struct foo_state {
        struct pinctrl *p;
        struct pinctrl_state *s;
        ...
        };

        foo_probe()
        {
                /* Allocate a state holder named "foo" etc */
                struct foo_state *foo = ...;

                foo->p = devm_pinctrl_get(&device);
                if (IS_ERR(foo->p)) {
                        /* FIXME: clean up "foo" here */
                        return PTR_ERR(foo->p);
                }

                foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT);
                if (IS_ERR(foo->s)) {
                        /* FIXME: clean up "foo" here */
                        return PTR_ERR(s);
                }

                ret = pinctrl_select_state(foo->s);
                if (ret < 0) {
                        /* FIXME: clean up "foo" here */
                        return ret;
                }
        }
View Code

  API介绍:page1261.

The semantics of the pinctrl APIs are:

- pinctrl_get() is called in process context to obtain a handle to all pinctrl
  information for a given client device. It will allocate a struct from the
  kernel memory to hold the pinmux state. All mapping table parsing or similar
  slow operations take place within this API.

- devm_pinctrl_get() is a variant of pinctrl_get() that causes pinctrl_put()
  to be called automatically on the retrieved pointer when the associated
  device is removed. It is recommended to use this function over plain
  pinctrl_get().

- pinctrl_lookup_state() is called in process context to obtain a handle to a
  specific state for a client device. This operation may be slow, too.

- pinctrl_select_state() programs pin controller hardware according to the
  definition of the state as given by the mapping table. In theory, this is a
  fast-path operation, since it only involved blasting some register settings
  into hardware. However, note that some pin controllers may have their
  registers on a slow/IRQ-based bus, so client devices should not assume they
  can call pinctrl_select_state() from non-blocking contexts.
- pinctrl_put() frees all information associated with a pinctrl handle.

- devm_pinctrl_put() is a variant of pinctrl_put() that may be used to
  explicitly destroy a pinctrl object returned by devm_pinctrl_get().
  However, use of this function will be rare, due to the automatic cleanup
  that will occur even without calling it.

  pinctrl_get() must be paired with a plain pinctrl_put().
  pinctrl_get() may not be paired with devm_pinctrl_put().
  devm_pinctrl_get() can optionally be paired with devm_pinctrl_put().
  devm_pinctrl_get() may not be paired with plain pinctrl_put().

Usually the pin control core handled the get/put pair and call out to the
device drivers bookkeeping operations, like checking available functions and
the associated pins, whereas select_state pass on to the pin controller
driver which takes care of activating and/or deactivating the mux setting by
quickly poking some registers.

The pins are allocated for your device when you issue the devm_pinctrl_get()
call, after this you should be able to see this in the debugfs listing of all
pins.
View Code

2、使用pinctrl完成初始化的步骤

  A:pinctrl设备树文件中,添加描述,注意在对应的节点下,可以搜索gpl2,在同一级下添加

arch/arm/boot/dts/exynos4412-pinctrl.dtsi  // 描述使用了哪一个pin脚
    leds_gpios1{
        leds_gpios1_on:leds_gpios1-on {
            samsung,pins = "gpl2-0" ;
                        samsung,pin-function = <1>;   
                        samsung,pin-val = <1>;
                        samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
        };
        leds_gpios1_off:leds_gpios1-off {
            samsung,pins = "gpl2-0" ;
                            samsung,pin-function = <1>;
                            samsung,pin-val = <0>;
                            samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
        };
    };
View Code

  B:设备树中添加引用

vim arch/arm/boot/dts/exynos4412-itop-elite.dts
    &leds_test_node {
        status = "okay";
        pinctrl-names = "itop-leds1-on","itop-leds1-off";
        pinctrl-0 = <&leds_gpios1_on>;
        pinctrl-1 = <&leds_gpios1_off>;
    };
View Code

  C:驱动中使用:
三步:获取一个pinctrl句柄,获取其中一个状态,选取其中一个状态设置

3、API常用的简介

功能:获取pinctrl句柄。---“devm机制”。自己不用管内存相关的操作;
    
    struct pinctrl *devm_pinctrl_get(struct device *dev)  
    @dev: the device to obtain the handle for
    参数:dev。
    返回值:错误码或者pinctrl的句柄。
    销毁句柄的函数:devm_pinctrl_put(struct pinctrl *p)。
    
    功能:获取引脚状态。
    struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)  
    参数p:pinctrl的句柄。
    参数name:配置的名字,用于pinctrl检索。
    返回:状态或者返回错误码。
    
    功能:设置状态。
    int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) 
    参数p:句柄。
    参数state:引脚状态。
    返回值:错误,返回错误码。
View Code

  例程也可以参考:Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt page341的Example 4
  pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
  pinctrl-names的值设置为“default”,就可以调用这个函数,直接初始化。
  include/linux/pinctrl/pinctrl-state.h:30:#define PINCTRL_STATE_DEFAULT "default"

4、驱动代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

#include <dt-bindings/pinctrl/samsung.h>

#include <linux/pinctrl/consumer.h>

#define DRIVER_NAME "leds_test"

//page1225  doc " Documentation/driver-api/pinctl.rst "
static int ledpinctrl_probe(struct platform_device * pdev)
{
    struct pinctrl *leds_pin_ctrl;
    struct pinctrl_state *leds_state;
    int ret;

    printk("ledpinctrl init\n");
    //1、 获取一个pinctrl句柄
    leds_pin_ctrl = devm_pinctrl_get(&pdev->dev); 
    if (IS_ERR(leds_pin_ctrl)) {   // 使用宏判断错误信息
        printk("leds_pin_ctrl,failed,%d\n",PTR_ERR(leds_pin_ctrl));
        return -1;
    }
    // 2、获取其中一个状态
    leds_state = pinctrl_lookup_state(leds_pin_ctrl,"itop-leds1-on");
    if (IS_ERR(leds_state)) {
        printk("leds_state,failed,%d\n",PTR_ERR(leds_state));
        return -1;
    }
    // 3、选取其中一个状态设置
    ret = pinctrl_select_state(leds_pin_ctrl, leds_state);
    if(ret<0){
        printk("pinctrl_select_state,failed\n");
        return -1;    
    }
    // 释放句柄
    devm_pinctrl_put(leds_pin_ctrl);
    
    printk("init ok\n");
    return 0;

}

static int ledpinctrl_remove(struct platform_device * pdev)
{
    printk(KERN_ALERT "Goodbye, curel world, this is remove\n");

    return 0;
}

static const struct of_device_id of_ledpinctrl_dt_match[] = {
    {.compatible = DRIVER_NAME},
    {},
};

MODULE_DEVICE_TABLE(of,of_ledpinctrl_dt_match);

static struct platform_driver ledpinctrl_driver = {
    .probe     = ledpinctrl_probe,
    .remove = ledpinctrl_remove,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = of_ledpinctrl_dt_match,
        },
};

static int ledpinctrl_init(void)
{
    printk(KERN_ALERT "Hello, world\n");
    return platform_driver_register(&ledpinctrl_driver);
    return 0;
}

static void ledpinctrl_exit(void)
{
    printk(KERN_ALERT "Goodbye, curel world\n");
    platform_driver_unregister(&ledpinctrl_driver);
}

module_init(ledpinctrl_init);
module_exit(ledpinctrl_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("rty");
MODULE_DESCRIPTION("topeet4412_regiter_dev_drv");

5、测试

  1、修改好设备树,并编译,烧写进内核;

    fastboot.exe flash dtb exynos4412-itop-elite.dtb

  2、将驱动编译,并加载,查看前后变化

[root@iTOP-4412]# insmod itop4412_of_pinctrl.ko
[ 65.022771] itop4412_of_pinctrl: loading out-of-tree module taints kernel.
[ 65.029143] Hello, world
[ 65.031465] ledpinctrl init
[ 65.033663] init ok

此时led灯亮,测试正常

注意:描述添加那块,需要添加到对应节点下面,会找不到对的描述管脚;

 

posted @ 2020-09-13 11:55  笑不出花的旦旦  阅读(143)  评论(0)    收藏  举报