(五)使用platform框架来驱动led(本地编写device方式)

一、使用platform框架来驱动led

1.1、关于platform

platform从我个人理解是驱动分离管理,platform三个方面:bus虚拟总线、driver设备驱动,device设备,bus总线将驱动蛇设备关联起来,最后形成驱动,bus虚拟总线是内核负责,本人是一个小白,暂时不研究那么深,只是理解如何运用platform

1.2、device文件

根据platform的特性,我们需要自行实现两个文件,分别是device和driver这两个驱动。
下面是device文件。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>

#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
#define REGISTER_LENGTH 4


static void led_release(struct device *dev)
{
    printk("led device release\r\n");
}

static struct resource led_resources[] = {
    [0] = {
        .start  = CCM_CCGR1_BASE,
        .end    = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [1] = {
        .start  = SW_MUX_GPIO1_IO03_BASE,
        .end    = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [2] = {
        .start  = SW_PAD_GPIO1_IO03_BASE,
        .end    = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [3] = {
        .start  = GPIO1_DR_BASE,
        .end    = (GPIO1_DR_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [4] = {
        .start  = GPIO1_GDIR_BASE,
        .end    = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },
};

static struct platform_device led_device = {
    .name   = "imx6ull-led",
    .id     = -1,
    .dev    = {
        .release    = & led_release,
    },
    .num_resources  = ARRAY_SIZE(led_resources),
    .resource       = led_resources,

};

static int __init platform_device_init(void)
{
    return platform_device_register(&led_device);
}

static void __exit platform_device_exit(void)
{
    platform_device_unregister(&led_device);
}

module_init(platform_device_init);
module_exit(platform_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gale");

该文件主要是记录设备的信息,比如led的GPIO的几个关键寄存器,方便后续驱动获取来实现驱动,其中最关键的一个地方是设备name这个属性,他的名字必须和后面写驱动的name属性的值一样,因为方便虚拟总线寻找设备和驱动进行匹配。

1.3、driver文件

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>

#define PLATFORM_NAME   "platform_led"
#define PLATFORM_CT     1

#define LED_ON  1
#define LED_OFF 0

static void __iomem *imx6u_ccm_ccgr1;
static void __iomem *sw_mux_gpio1_io03;
static void __iomem *sw_pad_gpio1_io03;
static void __iomem *gpio1_dr;
static void __iomem *gpio1_gdir;


struct platform_struct{
    int major;
    dev_t  devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
};

struct platform_struct platform_dev;

static int platform_open(struct inode *inode, struct file *file)
{
    file->private_data = &platform_dev;
    return 0;
}

static int platform_release(struct inode *inode, struct file *file)
{
	return 0;
}

ssize_t platform_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)
{
    unsigned char data_tmp;
    int ret = 0;
    int val = 0;

    ret  = copy_from_user(&data_tmp,data,1);

    if(ret < 0){
        printk("write data err\r\n");
        return -1;
    }

    if(data_tmp == LED_ON){

        val = readl(gpio1_dr);
        val &= ~(1 << 3);
        writel(val,gpio1_dr);

    }
    else if(data_tmp == LED_OFF){

        val = readl(gpio1_dr);
        val |= (1 << 3);
        writel(val,gpio1_dr);

    }

    return 0;
}

ssize_t platform_read(struct file *file, char __user * buf,size_t len, loff_t * ppos)
{
    return 0;
}

static const struct file_operations platform_fileops={
    .owner      = THIS_MODULE,
    .open       = platform_open,
    .write      = platform_write,
    .read       = platform_read,
    .release    = platform_release,
};


void led_init(void)
{
    unsigned int val = 0;

    val = readl(imx6u_ccm_ccgr1);

    val &= ~(3 << 26);
    val |= (3 << 26);

    writel(val,imx6u_ccm_ccgr1);

    writel(5,sw_mux_gpio1_io03);
    writel(0x10B0,sw_pad_gpio1_io03);

    val = readl(gpio1_gdir);

    val &= ~(1 << 3);
    val |= (1 << 3);

    writel(val,gpio1_gdir);

    val = readl(gpio1_dr);

    val |= (1 << 3);

    writel(val,gpio1_dr);
}

static int led_probe(struct platform_device *dev)
{
    int i;
    int ret = 0;
    struct resource *led_source[5];
    int res_size[5];

    printk("device and driver has matched!\r\n");

    for(i = 0;i < 5;i++){
        led_source[i] = platform_get_resource(dev,IORESOURCE_MEM,i);
        if(!led_source[i]){
            dev_err(&dev->dev,"No MEM resource for always on\n");
            return -ENXIO;
        }
        res_size[i] = resource_size(led_source[i]);
    }

    imx6u_ccm_ccgr1     = ioremap(led_source[0]->start,res_size[0]);
    sw_mux_gpio1_io03   = ioremap(led_source[1]->start,res_size[1]);
    sw_pad_gpio1_io03   = ioremap(led_source[2]->start,res_size[2]);
    gpio1_dr            = ioremap(led_source[3]->start,res_size[3]);
    gpio1_gdir          = ioremap(led_source[4]->start,res_size[4]);

    led_init();

    platform_dev.major = 0;

    if(platform_dev.major){
        platform_dev.devid  = MKDEV(platform_dev.major,0);
        ret = register_chrdev_region(platform_dev.devid,PLATFORM_CT,PLATFORM_NAME);
    }
    else{
        ret = alloc_chrdev_region(&platform_dev.devid,0,PLATFORM_CT,PLATFORM_NAME);
        platform_dev.major  = MAJOR(platform_dev.devid);
    }

    if(ret < 0){
        printk("chrdev err\r\n");
        goto CHRDEV_ERR;
    }
    printk("major num:%d\r\n",platform_dev.major);

    platform_dev.cdev.owner = THIS_MODULE;  
    cdev_init(&platform_dev.cdev,&platform_fileops);
    ret = cdev_add(&platform_dev.cdev,platform_dev.devid,PLATFORM_CT);

    if(ret < 0){
        printk("cdev err\r\n");
        goto CDEV_ERR;
    }

    platform_dev.class = class_create(THIS_MODULE,PLATFORM_NAME);
    if (IS_ERR(platform_dev.class)) {
		ret = PTR_ERR(platform_dev.class);
		goto CLASS_ERR;
	}

    platform_dev.device = device_create(platform_dev.class,NULL,platform_dev.devid,NULL,PLATFORM_NAME);
    if (IS_ERR(platform_dev.device)) {
		ret = PTR_ERR(platform_dev.device);
		goto DEVICE_ERR;
	}
    return 0;

DEVICE_ERR:
    class_destroy(platform_dev.class);

CLASS_ERR:
    cdev_del(&platform_dev.cdev); 

CDEV_ERR:
    unregister_chrdev_region(platform_dev.devid,PLATFORM_CT);

CHRDEV_ERR:
    return ret;
}

static int led_remove(struct platform_device *dev)
{
    device_destroy(platform_dev.class,platform_dev.devid);
    class_destroy(platform_dev.class);
    cdev_del(&platform_dev.cdev);
    unregister_chrdev_region(platform_dev.devid,PLATFORM_CT);

    iounmap(imx6u_ccm_ccgr1);
    iounmap(sw_mux_gpio1_io03);
    iounmap(sw_pad_gpio1_io03);
    iounmap(gpio1_dr);
    iounmap(gpio1_gdir);

    return 0;
}


static struct platform_driver led_driver = {
    .driver = {
        .name   = "imx6ull-led",
    },
    .probe  = led_probe,
    .remove = led_remove,
};

static int __init platform_driver_init(void)
{
    return platform_driver_register(&led_driver);
}

static void __exit platform_driver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(platform_driver_init);
module_exit(platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gale");

驱动代码和第一次驱动linux类似,在这里面,重点是led_probe、led_remove还有name这三个点,其中led_probe,led_remove分别是驱动和设备匹配上就会执行led_probe,当解除时候就会运行led_remove,方便自己在这两个函数做一些操作。

其中在驱动和设备中,目前这种方式没有采用设备树,所以使用名字匹配,所以驱动的name属性和设备的name这两个属性的名字必须要一致,否则无法进行匹配。

二、个人理解

其实这种方式对于使用gpio反而比第一次写的复杂一点,但是他也有他的好处,就是使用驱动分离,完全分离的那种,兼容性上好很多。

发布了29 篇原创文章 · 获赞 0 · 访问量 430

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104208521