#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/*********************************************
作者:王志川
功能:orangepi_3_lts的led灯驱动文件,基于设备数
*********************************************/
// #define PL_CFGO (0x07022000+0x0000)//输入输出设置寄存器
// #define PL_DRVO (0x07022000+0x0014)//引脚驱动水平寄存器
// #define PL_PULLO (0x07022000+0x001C)//上下拉设置寄存器
// #define PL_DAT (0x07022000+0x0010)//输出高低电平设置寄存器
//定义映射后的虚拟地址指针
static void __iomem *PL_CFGO_PI;
static void __iomem *PL_DRVO_PI;
static void __iomem *PL_PULLO_PI;
static void __iomem *PL_DAT_PI;
#define led_CNT 1 /* 设备号个数 */
#define led_NAME "led" /* 设备名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* led 设备结构体 */
struct led_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
};
struct led_dev led; /* led 设备 */
//对相关寄存器(物理地址)读写需要将其转化为虚拟地址,然后再进行读写
/*
*功能:LED打开/关闭,设置引脚高低电平
*/
void led_switch(u8 sta)
{
u32 val = 0;
if(sta == LEDON){
val = readl(PL_DAT_PI);
val &= ~(0x1 << 7);//bit7清0
val |= 0x1<<7;//bit7写1
writel(val, PL_DAT_PI);
}
else if(sta == LEDOFF){
val = readl(PL_DAT_PI);
val &= ~(0x1 << 7);//bit7清0
val |= 0x0<<7;//bit7写0
writel(val, PL_DAT_PI);
}
}
/*
*功能:将物理地址映射到虚拟地址
*/
// void led_remap(void)
// {
// PL_CFGO_PI = ioremap(PL_CFGO, 4);
// PL_DRVO_PI = ioremap(PL_DRVO, 4);
// PL_PULLO_PI = ioremap(PL_PULLO, 4);
// PL_DAT_PI = ioremap(PL_DAT, 4);
// }
/*
*功能:取消物理地址到虚拟地址的映射
*/
void led_unmap(void)
{
iounmap(PL_CFGO_PI);
iounmap(PL_DRVO_PI);
iounmap(PL_PULLO_PI);
iounmap(PL_DAT_PI);
}
/*
*功能: 打开设备
* 参数:传递给驱动的 inode
* 参数: 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &led; /* 设置私有数据 */
return 0;
}
/*
* 功能: 从设备读取数据
* 参数: 要打开的设备文件(文件描述符)
* 参数: 返回给用户空间的数据缓冲区
* 参数: 要读取的数据长度
* 参数: 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*
* 功能: 向设备写数据
* 参数: 设备文件,表示打开的文件描述符
* 参数: 要写给设备写入的数据
* 参数: 要写入的数据长度
* 参数: 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk(KERN_EMERG"kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == LEDON) {
led_switch(LEDON); /* 打开 LED 灯 */
} else if(ledstat == LEDOFF) {
led_switch(LEDOFF); /* 关闭 LED 灯 */
}
return 0;
}
/*
* 功能: 关闭/释放设备
* 参数: 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 设备操作函数 */
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
//驱动出口函数
static int __init led_init(void)
{
u32 val = 0;
int ret;
u32 regdata[16];
const char*str;
struct property *proper;
//获取设备树中的属性数据
led.nd = of_find_node_by_path("/myleds");
if(led.nd == NULL) {
printk(KERN_EMERG"myleds node not find!\r\n");
goto fail_find_node;
} else {
printk(KERN_EMERG"myleds node find!\r\n");
}
//获取compatible属性内容
proper = of_find_property(led.nd, "compatible", NULL);
if(proper == NULL) {
printk(KERN_EMERG"compatible property find failed\r\n");
} else {
printk(KERN_EMERG"compatible = %s\r\n", (char*)proper->value);
}
//获取 status 属性内容
ret = of_property_read_string(led.nd, "status", &str);
if(ret < 0){
printk(KERN_EMERG"status read failed!\r\n");
} else {
printk(KERN_EMERG"status = %s\r\n",str);
}
/* 4、获取 reg 属性内容 */
ret = of_property_read_u32_array(led.nd, "reg", regdata, 8);
if(ret < 0) {
printk(KERN_EMERG"reg property read failed!\r\n");
} else {
u8 i = 0;
printk(KERN_EMERG"reg data:\r\n");
for(i = 0; i < 8; i++)
printk(KERN_EMERG"%#X ", regdata[i]);
printk(KERN_EMERG"\r\n");
}
//初始化 LED
//寄存器地址映射
PL_CFGO_PI = of_iomap(led.nd, 0);
PL_DRVO_PI = of_iomap(led.nd, 1);
PL_PULLO_PI = of_iomap(led.nd, 2);
PL_DAT_PI = of_iomap(led.nd, 3);
// PL_CFGO_PI = ioremap(regdata[1], 4);
// PL_DRVO_PI = ioremap(regdata[5], 4);
// PL_PULLO_PI = ioremap(regdata[9],4);
// PL_DAT_PI = ioremap(regdata[13], 4);
//设置PL7为输出
val = readl(PL_CFGO_PI);
val &= ~(0x7 << 28);//bit30:28清0
val |= 0x1 << 28;//bit30:28设置为001,output模式
writel(val, PL_CFGO_PI);
//设置PL7驱动能力为level3
val = readl(PL_DRVO_PI);
val &= ~(0x3 << 14);//bit15:14清0
val |= 0x3<< 14;//bit15:14置11,level3
writel(val, PL_DRVO_PI);
//设置PL7为下拉
val = readl(PL_PULLO_PI);
val &= ~(0x3 << 14);//bit15:14清0
val |= 0x2<< 14;//bit15:14置10,pull-down
writel(val, PL_PULLO_PI);
//初始化灯亮
val = readl(PL_DAT_PI);
val &= ~(0x1 << 7);//bit7清0
val |= 0x1<<7;//bit7写1
writel(val, PL_DAT_PI);
//注册字符驱动
//创建设备号
if (led.major) { /* 定义了设备号 */
led.devid = MKDEV(led.major, 0);
ret = register_chrdev_region(led.devid, led_CNT,led_NAME);
if(ret < 0) {
pr_err("cannot register %s char driver [ret=%d]\n",led_NAME, led_CNT);
goto fail_devid;
}
} else { /* 没有定义设备号 */
ret = alloc_chrdev_region(&led.devid, 0, led_CNT,led_NAME); /* 申请设备号 */
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n",led_NAME, ret);
goto fail_devid;
}
led.major = MAJOR(led.devid); /* 获取分配号的主设备号 */
led.minor = MINOR(led.devid); /* 获取分配号的次设备号 */
}
printk(KERN_EMERG"led major=%d,minor=%d\r\n",led.major,led.minor);
//初始化 cdev
led.cdev.owner = THIS_MODULE;
cdev_init(&led.cdev, &led_fops);
//添加一个 cdev
ret = cdev_add(&led.cdev, led.devid, led_CNT);
if(ret < 0)
goto del_unregister;
//创建类
led.class = class_create(THIS_MODULE, led_NAME);
if (IS_ERR(led.class)) {
go
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
本内容主要是基于orangepi_3_lts开发板写的一个LED灯内核驱动程序,本次驱动添加了设备树修改,该驱动程序同样的,主要通过写寄存器的方式来实现。本次分享的内容主要包括LED灯的内部驱动源码,用于编译ko文件的Makefile以及上层用于测试的APP文件,用于打开和关闭led等。 本文件适用于linux驱动的初学者学习入门。本次写驱动主要流程就是写寄存器,包括IO口做什么引脚用,引脚驱动水平,输入输出模式以及读写高低电平四个寄存器,对应的寄存器保存在设备树文件中,驱动文件通过读设备树得到相对应的寄存器地址,相应的寄存器在手册上可以找到,并且源码具有较为详细的注释,方便学习。驱动主要内容包括初始化file_operation结构体对应的一些函数,包括open,close等等,以及如何从物理地址映射到虚拟地址,操作相关寄存器也是操作映射得到的虚拟地址等等内容,源码每一部分具有较为详细的注释。 此外,我后期会不断更新各种基于香橙派的设备驱动源码,如果觉得对你学习有帮助不妨留个关注。
资源推荐
资源详情
资源评论
收起资源包目录
03_led_dts.zip (5个子文件)
03_led_dts
.vscode
c_cpp_properties.json 739B
Makefile 261B
led.c 9KB
ledApp.c 986B
ledApp 14KB
共 5 条
- 1
资源评论
得之坦然,失之淡然。
- 粉丝: 400
- 资源: 10
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功