一、使用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反而比第一次写的复杂一点,但是他也有他的好处,就是使用驱动分离,完全分离的那种,兼容性上好很多。