设备树驱动实例,基于 imx6ull alpha 的 led 灯实验

3 实例

不了解设备树的,可以看另外一篇文章:
https://blog.csdn.net/weixin_46640184/article/details/124271806

led 接在 gpio1 的 io03 引脚上。

.dts

在原有 .dts 文件上添加 led 节点

#define GROUP(x)        (x>>16)
#define PIN(x)          (x&0xffff)
#define GROUP_PIN(g,p)  ((g<<16) | p)

#define CCM_CCGR(x)             ((x>>16))
#define CCM_CG(x)               (x&0xffff)
#define CCM_CCGR_CG(ccgr,cg)    ((ccgr<<16) | cg)

#define SW_MUX(x)          (x&0xf)

#define SW_PAD(x)           (x&0xffff)

#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

led{
    #address-cells = <1>;
	#size-cells = <1>;
	compatible = "fsl,imx6ull-alpha-led";
	status = "okay";
	reg = < CCM_CCGR1_BASE			0x04
			SW_MUX_GPIO1_IO03_BASE	0x04
			SW_PAD_GPIO1_IO03_BASE	0x04
			GPIO1_DR_BASE			0x04
			GPIO1_GDIR_BASE			0x04>;

	led-pin = <GROUP_PIN(1,3)>;
	led-clock = <CCM_CCGR_CG(1,13)>;
	led-pad = <SW_PAD(0x10b0)>;
	led-mux = <SW_MUX(5)>;
};

led_resource.h

#define LED_NAME	"myled"

#define GROUP(x)        (x>>16)
#define PIN(x)          (x&0xffff)
#define GROUP_PIN(g,p)  ((g<<16) | p)

#define CCM_CCGR(x)             ((x>>16))
#define CCM_CG(x)               (x&0xffff)
#define CCM_CCGR_CG(ccgr,cg)    ((ccgr<<16) | cg)

#define LED_MAJOR(x)             ((x>>16))
#define LED_MINOR(x)             (x&0xffff)
#define MAJOR_MINOR(ma,mi)    ((ma<<16) | mi)

#define SW_MUX(x)          (x&0xf)

#define SW_PAD(x)           (x&0xffff)

#define LED_NUM(x)          (x&0xffff)


/*
 * pin:
 * bit[31:16] = group
 * bit[15:0] = which pin
 * clock:
 * bit[31:16] = CCM_CCGRx
 * bit[15:0]  = CGx
 * devno:
 * bit[31:16] = major
 * bit[15:0]  = minor
 */
struct led_resource{
    int pin;
    int clock;
    int mux;
    int pad;
    int num;
    int devno;
};

struct led_resource *get_led_resource(void);

struct dtsled_dev {
	struct cdev led_cdev;
	struct class *led_class;
	struct device *led_dev;	/* device */
	int major;				/* major number of device */
	int minor;				/* minor number of device */
	dev_t devid;				/* device id */
	struct device_node *nd;			/* device node */
};

led_drv.c

头文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/irq.h>

#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>

#include <linux/platform_device.h>
#include <linux/cdev.h>

#include "led_resource.h"

一些定义

#define LEDON		1
#define LEDOFF		0


/* 
address_base:
0 IMX6U_CCM_CCGR1_BASE 		size;
2 SW_MUX_GPIO1_IO03_BASE	size;
4 SW_PAD_GPIO1_IO03_BASE	size;
6 GPIO1_DR_BASE				size;
8 GPIO1_GDIR_BASE			size;
*/
static u32 regdata[14];


/* mapped register virtual address point */
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;

static unsigned int led_pin;
static unsigned int led_clock;
static unsigned int led_mux;
static unsigned int led_pad;

struct dtsled_dev dtsled;

led_open

/*
 * @decription  :   open device
 * @param inode :   transfer inode to driver
 * @param filp  :   device file
 * @return      :   0 success, otherwise failed
 */
 static int led_open(struct inode *node, struct file * filp)
{
	u32 val;
	/* initialization led */
    /* mapped register address */
#if 0
    IMX6U_CCM_CCGR1 = of_iomap(regdata[0], regdata[1]);
    SW_MUX_GPIO1_IO03 = of_iomap(regdata[2], regdata[3]);
    SW_PAD_GPIO1_IO03 = of_iomap(regdata[4], regdata[5]);
    GPIO1_DR = of_iomap(regdata[6], regdata[7]);
    GPIO1_GDIR = of_iomap(regdata[8], regdata[9]);
#else
	IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
	SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
	SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
	GPIO1_DR = of_iomap(dtsled.nd, 3);
	GPIO1_GDIR = of_iomap(dtsled.nd, 4);
#endif
	/* enable clock */
	val = readl(IMX6U_CCM_CCGR1);
	val |= (3<< (CCM_CG(led_clock))*2);
	writel(val,IMX6U_CCM_CCGR1);

	/* set gpio multipex */
	writel(SW_MUX(led_mux),SW_MUX_GPIO1_IO03);

	/* set gpio pad */
	writel(SW_PAD(led_pad),SW_PAD_GPIO1_IO03);

	/* set gpio to output model */
	val = readl(GPIO1_GDIR);
	val |= (1<<PIN(led_pin));
	writel(val,GPIO1_GDIR);

	/* default set led to dark */
	val = readl(GPIO1_DR);
	val |= (1<<PIN(led_pin));
	writel(val,GPIO1_DR);

	return 0;
}

led_write

/*
 * @decription  :   led open/close
 * @param  sta :   LEDON(0) turn on led, LEDOFF(1) turn off led
 * @return      :   none
 */
void led_switch(u8 sta)
{
    u32 val = 0;
    if(sta == LEDON)
    {
        val = readl(GPIO1_DR);
        val &= ~(1<<PIN(led_pin));
        writel(val,GPIO1_DR); 
    }else if(sta == LEDOFF)
    {
        val = readl(GPIO1_DR);
        val |= (1<<PIN(led_pin));
        writel(val,GPIO1_DR);
    }
	printk("write led!\n");
}


static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt,loff_t *off)
{
	int err;
    unsigned char status;
	
    err = copy_from_user(&status,buf,cnt);/* get current led statu value */

	if(err < 0)
    {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    
    led_switch(status);

    return 0;
}

led_release

static int led_release(struct inode *node, struct file *filp)
{
	printk("iounmap!\n");
	/* cancel map */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
	
	return 0;
}

file_operations

static struct file_operations myled_oprs = {
	.owner		= THIS_MODULE,
	.open		= led_open,
	.write		= led_write,
	.release	= led_release,
};

led_probe

static int led_probe(struct platform_device *pdev)
{
	
	dtsled.led_dev = &pdev->dev;
	dtsled.nd = dtsled.led_dev->of_node;

	s32 err = 0;

	err = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
	if(err < 0 )
	{
		printk("reg property read failed!\n");
	}
	
	of_property_read_u32(dtsled.nd, "led-clock", &led_clock);
	
	of_property_read_u32(dtsled.nd, "led-pin", &led_pin);
	
	of_property_read_u32(dtsled.nd, "led-pad", &led_pad);
	
	of_property_read_u32(dtsled.nd, "led-mux", &led_mux);

/*
	if(major)
	{
		devid = MKDEV(major, 0);
		register_chrdev_region(devid, led_num, LED_NAME);
	}else
	{
		alloc_chrdev_region(&devid, 0, led_num, LED_NAME);
		major = MAJOR(devid);
		minor = MINOR(devid);
	}
*/
	alloc_chrdev_region(&dtsled.devid, 0, 1, LED_NAME);
	dtsled.major = MAJOR(dtsled.devid);
	dtsled.minor = MINOR(dtsled.devid);

	printk("myled : major = %d, minor = %d\n",dtsled.major,dtsled.minor);

	/* initialization cdev */
	dtsled.led_cdev.owner = THIS_MODULE;
	cdev_init(&dtsled.led_cdev, &myled_oprs);

	/* add a cdev */
	cdev_add(&dtsled.led_cdev, dtsled.devid, 1);

	/* create class */
	dtsled.led_class = class_create(THIS_MODULE, LED_NAME);
	if(IS_ERR(dtsled.led_class)){
		printk("class create error!\n");
		return PTR_ERR(dtsled.led_class);
	}

	/* create device */
	dtsled.led_dev = device_create(dtsled.led_class,NULL,dtsled.devid,NULL,LED_NAME);
	if(IS_ERR(dtsled.led_dev)){
		printk("device create error!\n");
		return PTR_ERR(dtsled.led_dev);
	}

	return 0;
}

led_remove

static int led_remove(struct platform_device *pdev)
{
	/* log out cdev, device, class */
	cdev_del(&dtsled.led_cdev);
	unregister_chrdev_region(dtsled.devid,1);
	device_destroy(dtsled.led_class,dtsled.devid);
	class_destroy(dtsled.led_class);
	
	return 0;
}

platform_driver

static const struct of_device_id of_match_leds[] = {
	{.compatible = "fsl,imx6ull-alpha-led", .data = NULL },
	{/* sentinel */}
};

struct platform_driver led_drv = {
	.probe 	= led_probe,
	.remove = led_remove,
	.driver = {
		.name = LED_NAME,
		.of_match_table = of_match_leds,
	}
};

myled_exit

static void myled_exit(void)
{
	platform_driver_unregister(&led_drv);
}

myled_init

static int myled_init(void)
{
	platform_driver_register(&led_drv);
	return 0;
}

注册

module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LUO");

测试

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>


int main(int argc,char *argv[])
{
    int fd;
    char status;

    if(argc != 3)
    {
        printf("Usage : %s <dev> <on | off>\n",argv[0]);
        return -1;
    }
    /* open file */
    fd = open(argv[1], O_RDWR);
    if(fd == -1)
    {
        printf("can't open file %s\n",argv[1]);
        return -1;
    }

    /* write file */
    if( strcmp(argv[2], "on") == 0)
    {
        status = 1;
        write(fd,&status, 1);
    }else
    {
        status = 0;
        write(fd,&status,1);
    }

    close(fd);

    return 0;
}

Makefile

KERNELDIR := /home/luo/linux/IMX6ULL/linux_alpha/linux-imx-rel_imx_4.1.15_2.1.0_ga_alpha
CURRENT_PATH := $(shell pwd)

obj-m += led_drv.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
	rm -rf modules.order
	rm -f ledtest
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值