設備樹學習之(二)點燈

開發板:tiny4412SDK + S702 + 4GB Flash
要移植的內核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自帶的 U-Boot 2010.12
busybox版本:busybox 1.25

目標:
學習設備樹中GPIO控制器的使用,實現配置引腳爲輸出功能,寫簡單的字符設備驅動程序,實現點亮LED。

原理圖:
這裏寫圖片描述
tiny4412 核心板上有6顆LED,這裏我們只控制其中4顆,它們分別接在GPM4_0、GPM4_1、GPM4_2、GPM4_3 引腳。想要點亮LED,首先得配置引腳爲輸出功能,輸出低電平時LED點亮,高電平時,LED熄滅。重點在於設備樹中GPIO控制器資源的使用。

設備樹參考:

參考:Samsung GPIO and Pin Mux/Config controller
Example 1: A pin-controller node with pin groups.
    pinctrl_0: pinctrl@11400000 { 
        compatible = "samsung,exynos4210-pinctrl";
        reg = <0x11400000 0x1000>;
        interrupts = <0 47 0>;
        /* ... */
        uart0_data: uart0-data { 
            samsung,pins = "gpa0-0", "gpa0-1";
            samsung,pin-function = <2>;
            samsung,pin-pud = <0>;
            samsung,pin-drv = <0>; 
        };
Example 3: A uart client node that supports 'default' and 'flow-control' states.
    uart@13800000 { 
        compatible = "samsung,exynos4210-uart";
        reg = <0x13800000 0x100>;
        interrupts = <0 52 0>;
        pinctrl-names = "default", "flow-control;
        pinctrl-0 = <&uart0_data>;
        pinctrl-1 = <&uart0_data &uart0_fctl>; 
    };
  "samsung,pins" property of the child node. The following pin configuration properties are supported.
  - samsung,pin-val: Initial value of pin output buffer.
  - samsung,pin-pud: Pull up/down configuration.
  - samsung,pin-drv: Drive strength configuration.
  - samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
  - samsung,pin-drv-pdn: Drive strength configuration in power down mode.

設備樹:

&pinctrl@11000000 {
        led_demo: led{
                samsung,pins = "gpm4-0", "gpm4-1" ,"gpm4-2", "gpm4-3";
                samsung,pin-function = <0x1>;   //1爲輸出
                samsung,pin-pud = <0x0>;        //沒有上拉
                samsung,pin-drv = <0x0>;        //驅動強度?
        };
}; 

led_pin {
    compatible         = "tiny4412,led_demo";
    pinctrl-names = "led_demo";
    pinctrl-0 = <&led_demo>;
    tiny4412,int_gpio1 = <&gpm4 0 GPIO_ACTIVE_HIGH>;
    tiny4412,int_gpio2 = <&gpm4 1 GPIO_ACTIVE_HIGH>;
    tiny4412,int_gpio3 = <&gpm4 2 GPIO_ACTIVE_HIGH>;
    tiny4412,int_gpio4 = <&gpm4 3 GPIO_ACTIVE_HIGH>;
};

我們在 pinctrl 中增加了 led 節點,它代表了一種引腳功能,比如這裏設置的 gpm4_0、gpm4_1、gpm4_2、gpm4_3,引腳功能爲 0x01 輸出,無上拉等等。在其它地方,我們可以引用它,來表示引腳支持的功能。
下面我們增加了 led_pin 節點,它有一個屬性 pinctrl-names = “led_demo”,這是我們給引腳功能狀態起的名字,如果支持多種功能,可以是字符串列表的形式。字符串的個數要和下面 pinctrl-n 的個數對應,pinctrl-0 引用了我們前面定義的那個將引腳設置爲輸出功能的屬性。
在代碼中,我們可以用過 pinctrl-names 來獲得特定的引腳功能,並設置它。如果 pinctrl-names 爲 “default”,那麼這種功能狀態將設置爲默認的引腳狀態,代碼中無需處理。

代碼片段:

static int led_probe(struct platform_device *pdev) {

    struct device *dev = &pdev->dev;
    dev_t devid;
    struct pinctrl *pctrl;
    struct pinctrl_state *pstate;
    pctrl = devm_pinctrl_get(dev);
    if(pctrl == NULL)
    {
        printk("devm_pinctrl_get error\n");
    }
    pstate = pinctrl_lookup_state(pctrl, "led_demo");
    if(pstate == NULL)
    {
        printk("pinctrl_lookup_state error\n");
    }
    pinctrl_select_state(pctrl, pstate);//設置爲輸出模式 
    printk("enter %s\n",__func__);
    led1 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio1", 0);;
    led2 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio2", 0);;
    led3 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio3", 0);;
    led4 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio4", 0);;
    if(led1 <= 0)
    {
        printk("%s error\n",__func__);
        return -EINVAL;
    }
    else
    {
        printk("led1 %d\n",led1);
        printk("led2 %d\n",led2);
        printk("led3 %d\n",led3);
        printk("led4 %d\n",led4);
        devm_gpio_request_one(dev, led1, GPIOF_OUT_INIT_HIGH, "LED1");
        devm_gpio_request_one(dev, led2, GPIOF_OUT_INIT_HIGH, "LED2");
        devm_gpio_request_one(dev, led3, GPIOF_OUT_INIT_HIGH, "LED3");
        devm_gpio_request_one(dev, led4, GPIOF_OUT_INIT_HIGH, "LED4");
    }

完整代碼:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

#define LED_CNT   4

static int  major;
static struct cdev  led_cdev;   //內核中用cdev描述一個字符設備
static struct class *cls;
static int led1,led2,led3,led4;

static ssize_t led_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
    char buf;
    int minor = iminor(file->f_inode);

    printk("minor is %d\n",minor);
    printk("%s\n",__func__);
    if(count != 1){
        printk("count != 1\n"); 
        return 1;
    }
    if (copy_from_user(&buf, user_buf, count))
        return -EFAULT;

    printk("rcv %d\n",buf);
    if(buf == 0x01)
    {
        switch(minor){
        case 0:
            gpio_set_value(led1, 0);
            break;
        case 1:
            gpio_set_value(led2, 0);
            break;
        case 2:
            gpio_set_value(led3, 0);
            break;
        case 3:
            gpio_set_value(led4, 0);
            break;
        default:
            printk("%s rcv minor error\n",__func__);
        }                       
    }
    else if(buf == 0x0)
    {
        switch(minor){
        case 0:
            gpio_set_value(led1, 1);
            break;
        case 1:
            gpio_set_value(led2, 1);
            break;
        case 2:
            gpio_set_value(led3, 1);
            break;
        case 3:
            gpio_set_value(led4, 1);
            break;
        default:
            printk("%s rcv minor error\n",__func__);
        }       
    }
}
static int led_open(struct inode *inode, struct file *file)
{
    printk("led_open\n");
    return 0;
}

static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .write = led_write,
};

static int led_probe(struct platform_device *pdev) {

    struct device *dev = &pdev->dev;
    dev_t devid;
    struct pinctrl *pctrl;
    struct pinctrl_state *pstate;
    pctrl = devm_pinctrl_get(dev);
    if(pctrl == NULL)
    {
        printk("devm_pinctrl_get error\n");
    }
    pstate = pinctrl_lookup_state(pctrl, "led_demo");
    if(pstate == NULL)
    {
        printk("pinctrl_lookup_state error\n");
    }
    pinctrl_select_state(pctrl, pstate);//設置爲輸出模式 
    printk("enter %s\n",__func__);
    led1 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio1", 0);;
    led2 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio2", 0);;
    led3 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio3", 0);;
    led4 = of_get_named_gpio(dev->of_node, "tiny4412,int_gpio4", 0);;
    if(led1 <= 0)
    {
        printk("%s error\n",__func__);
        return -EINVAL;
    }
    else
    {
        printk("led1 %d\n",led1);
        printk("led2 %d\n",led2);
        printk("led3 %d\n",led3);
        printk("led4 %d\n",led4);
        devm_gpio_request_one(dev, led1, GPIOF_OUT_INIT_HIGH, "LED1");
        devm_gpio_request_one(dev, led2, GPIOF_OUT_INIT_HIGH, "LED2");
        devm_gpio_request_one(dev, led3, GPIOF_OUT_INIT_HIGH, "LED3");
        devm_gpio_request_one(dev, led4, GPIOF_OUT_INIT_HIGH, "LED4");
    }

    if(alloc_chrdev_region(&devid, 0, LED_CNT, "led") < 0)/* (major,0~1) 對應 hello_fops, (major, 2~255)都不對應hello_fops */
    {
        printk("%s ERROR\n",__func__);
        goto error;
    }
    major = MAJOR(devid);                     

    cdev_init(&led_cdev, &led_fops);        //綁定文件操作函數
    cdev_add(&led_cdev, devid, LED_CNT);    //註冊到內核

    cls = class_create(THIS_MODULE, "led"); //創建led類,向類中添加設備,mdev會幫我們創建設備節點
    device_create(cls, NULL, MKDEV(major, 0), NULL, "led0"); 
    device_create(cls, NULL, MKDEV(major, 1), NULL, "led1"); 
    device_create(cls, NULL, MKDEV(major, 2), NULL, "led2"); 
    device_create(cls, NULL, MKDEV(major, 3), NULL, "led3"); 

error:
    unregister_chrdev_region(MKDEV(major, 0), LED_CNT);
    return 0;
}

static int led_remove(struct platform_device *pdev) {

    printk("enter %s\n",__func__);
    device_destroy(cls, MKDEV(major, 0));
    device_destroy(cls, MKDEV(major, 1));
    device_destroy(cls, MKDEV(major, 2));
    device_destroy(cls, MKDEV(major, 3));
    class_destroy(cls);

    cdev_del(&led_cdev);
    unregister_chrdev_region(MKDEV(major, 0), LED_CNT);

    printk("%s enter.\n", __func__);
    return 0;
}

static const struct of_device_id led_dt_ids[] = {
    { .compatible = "tiny4412,led_demo", },
    {},
};

MODULE_DEVICE_TABLE(of, led_dt_ids);

static struct platform_driver led_driver = {
    .driver        = {
        .name      = "led_demo",
        .of_match_table    = of_match_ptr(led_dt_ids),
    },
    .probe         = led_probe,
    .remove        = led_remove,
};

static int led_init(void){
    int ret;
    printk("enter %s\n",__func__);
    ret = platform_driver_register(&led_driver);
    if (ret)
        printk(KERN_ERR "led demo: probe failed: %d\n", ret);

    return ret; 
}

static void led_exit(void)
{
    printk("enter %s\n",__func__);
    platform_driver_unregister(&led_driver);
}

module_init(led_init);
module_exit(led_exit);

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