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