lowermachine/source/linux_driver/fifo.c
Miaow 71f8e672d5 机器已经部署到淮安
8月底出差告一段落,运行一切正常
1. 添加了喷阀数据队列走空计数和自动补偿功能
2. 喷阀数据队列发生不可恢复的异常时向上位机报告错误
3. 取消双FIFO
2022-09-01 21:40:16 +08:00

355 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#define FIFO_CNT 1 /* 设备号个数 */
#define FIFO_NAME "fifo" /* 名字 */
#define FIFO_CMD_FUNCTION_CLEAR 1
#define FIFO_CMD_FUNCTION_PADDING 2
#define FIFO_CMD_GET_EMPTYCOUNT 3
/*
* 相关寄存器地址定义
*/
#define FIFO_REG_BASE 0x43C00000
#define FIFO_REG_0_OFFSET 0x00000000
#define FIFO_REG_1_OFFSET 0x00000004
#define FIFO_REG_2_OFFSET 0x00000008
#define FIFO_REG_3_OFFSET 0x0000000C
#define FIFO_REG_4_OFFSET 0x00000010
#define FIFO_REG_5_OFFSET 0x00000014
#define FIFO_REG_6_OFFSET 0x00000018
#define FIFO_REG_7_OFFSET 0x0000001C
#define FIFO_REG_8_OFFSET 0x00000020
#define FIFO_REG_9_OFFSET 0x00000024
#define FIFO_REG_10_OFFSET 0x00000028
#define FIFO_REG_11_OFFSET 0x0000002C
#define FIFO_REG_12_OFFSET 0x00000030 // {16'b0, almost_empty, empty, almost_full, full, data_count[11:0]};
#define FIFO_REG_13_OFFSET 0x00000034
#define FIFO_REG_14_OFFSET 0x00000038
#define FIFO_CR_CLR_MASK ((u32)(1 << 1)) // 清空队列 (Clear) 对该位写入1队列将清空同时队列输出为全0。注意不要写入除1以外的任何值。
#define FIFO_CR_WS_MASK ((u32)(1 << 0)) // 写入同步 (Write Synchronization) 对该位写入1FIFO_DATx的数据按字节小端序进入队列。 注意不要写入除1以外的任何值。
#define FIFO_SR_VLD_MASK ((u32)(1 << 16)) // 数据输出有效标志 (Valid) 0: 当前无有效输出,输出保持上一状态 1: 当前队列正在输出有效数据
#define FIFO_SR_AMEM_MASK ((u32)(1 << 15)) // 队列将空标志 (Almost Empty) 0: 队列没有被读空 1: 队列在一个读时钟周期后会被读空
#define FIFO_SR_EM_MASK ((u32)(1 << 14)) // 队列空标志 (Empty) 0: 队列中存在有效数据,没有被读空 1: 队列中已经没有有效数据
#define FIFO_SR_AMFU_MASK ((u32)(1 << 13)) // 队列将满标志 (Almost Full) 0: 队列没有被写满 1: 队列在一个写时钟周期后会被写满
#define FIFO_SR_FU_MASK ((u32)(1 << 12)) // 队列满标志 (Almost Full) 0: 队列中的有效数据小于FIFO数据深度 1: 队列中的有效数据达到FIFO数据深度
#define FIFO_SR_CNT_MASK ((u32)(0xFFF << 0)) // 队列数据数量 (Data Count) 该值指示队列中的数据数量 注意一个数据为384位宽
#define FIFO_ECR_CNT_MASK ((u32)0xFFFFFFFF) // 队列空读取累计次数
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *fifo_dat0_addr;
static void __iomem *fifo_dat1_addr;
static void __iomem *fifo_dat2_addr;
static void __iomem *fifo_dat3_addr;
static void __iomem *fifo_dat4_addr;
static void __iomem *fifo_dat5_addr;
static void __iomem *fifo_dat6_addr;
static void __iomem *fifo_dat7_addr;
static void __iomem *fifo_dat8_addr;
static void __iomem *fifo_dat9_addr;
static void __iomem *fifo_dat10_addr;
static void __iomem *fifo_dat11_addr;
static void __iomem *fifo_sr_addr;
static void __iomem *fifo_ecr_addr;
static void __iomem *fifo_cr_addr;
/* fifo设备结构体 */
struct fifo_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
static struct fifo_dev fifo; /* led设备 */
/*
* @description : 打开设备
* @param inode : 传递给驱动的inode
* @param - filp : 设备文件file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int fifo_open(struct inode *inode, struct file *filp)
{
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t fifo_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
u32 data = readl(fifo_sr_addr) & FIFO_SR_CNT_MASK;
copy_to_user(buf, &data, 4);
return cnt;
}
static u32 kern_buf_u32[8 * 4096];
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t fifo_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
int i;
if (cnt % 32 != 0 || cnt > sizeof(kern_buf_u32))
{
printk(KERN_ERR "cnt error, cnt=%d, sizeof=%d\r\n", cnt, (u32)sizeof(kern_buf_u32));
return -1;
}
ret = copy_from_user(kern_buf_u32, buf, cnt); // 得到应用层传递过来的数据
if (ret < 0)
{
printk(KERN_ERR "kernel write failed!\r\n");
return -EFAULT;
}
for (i = 0; i < (cnt / sizeof(u32)); i += 8)
{
writel(kern_buf_u32[i], fifo_dat0_addr);
writel(kern_buf_u32[i + 1], fifo_dat1_addr);
writel(kern_buf_u32[i + 2], fifo_dat2_addr);
writel(kern_buf_u32[i + 3], fifo_dat3_addr);
writel(kern_buf_u32[i + 4], fifo_dat4_addr);
writel(kern_buf_u32[i + 5], fifo_dat5_addr);
writel(kern_buf_u32[i + 6], fifo_dat6_addr);
writel(kern_buf_u32[i + 7], fifo_dat7_addr);
writel(0, fifo_dat8_addr);
writel(0, fifo_dat9_addr);
writel(0, fifo_dat10_addr);
writel(0, fifo_dat11_addr);
writel(FIFO_CR_WS_MASK, fifo_cr_addr);
}
return cnt;
}
/*
* @description : 关闭/释放设备
* @param filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int fifo_release(struct inode *inode, struct file *filp)
{
return 0;
}
static long fifo_ioctl(struct file *fp, unsigned int cmd, unsigned long tmp)
{
if (_IOC_TYPE(cmd) != 'D')
{
printk(KERN_ERR "IOC_TYPE or IOC_WRITE error: IOC_TYPE=%c, IOC_WRITE=%d\r\n", _IOC_TYPE(cmd), _IOC_DIR(cmd));
return -EINVAL;
}
if (_IOC_NR(cmd) == FIFO_CMD_GET_EMPTYCOUNT)
{
u32 empty_count = readl(fifo_ecr_addr) & FIFO_ECR_CNT_MASK;
printk("%d\r\n", empty_count);
if (copy_to_user((u32 *)tmp, &empty_count, 4) < 0)
{
printk(KERN_ERR "get empty count error\r\n");
return -EINVAL;
}
}
else if (_IOC_NR(cmd) == FIFO_CMD_FUNCTION_CLEAR)
{
// 清空队列
writel(FIFO_CR_CLR_MASK, fifo_cr_addr);
}
else if (_IOC_NR(cmd) == FIFO_CMD_FUNCTION_PADDING)
{
// 对队列中添加tmp个数的0元素
int i;
for (i = 0; i < tmp; i ++)
{
writel((u32)0, fifo_dat0_addr);
writel((u32)0, fifo_dat1_addr);
writel((u32)0, fifo_dat2_addr);
writel((u32)0, fifo_dat3_addr);
writel((u32)0, fifo_dat4_addr);
writel((u32)0, fifo_dat5_addr);
writel((u32)0, fifo_dat6_addr);
writel((u32)0, fifo_dat7_addr);
writel((u32)0, fifo_dat8_addr);
writel((u32)0, fifo_dat9_addr);
writel((u32)0, fifo_dat10_addr);
writel((u32)0, fifo_dat11_addr);
writel(FIFO_CR_WS_MASK, fifo_cr_addr);
}
}
return 0;
}
/* 设备操作函数 */
static struct file_operations fifo_fops = {
.owner = THIS_MODULE,
.open = fifo_open,
.read = fifo_read,
.write = fifo_write,
.release = fifo_release,
.unlocked_ioctl = fifo_ioctl,
};
static int __init fifo_init(void)
{
int ret;
/* 寄存器地址映射 */
fifo_dat0_addr = ioremap(FIFO_REG_BASE + FIFO_REG_0_OFFSET, 4);
fifo_dat1_addr = ioremap(FIFO_REG_BASE + FIFO_REG_1_OFFSET, 4);
fifo_dat2_addr = ioremap(FIFO_REG_BASE + FIFO_REG_2_OFFSET, 4);
fifo_dat3_addr = ioremap(FIFO_REG_BASE + FIFO_REG_3_OFFSET, 4);
fifo_dat4_addr = ioremap(FIFO_REG_BASE + FIFO_REG_4_OFFSET, 4);
fifo_dat5_addr = ioremap(FIFO_REG_BASE + FIFO_REG_5_OFFSET, 4);
fifo_dat6_addr = ioremap(FIFO_REG_BASE + FIFO_REG_6_OFFSET, 4);
fifo_dat7_addr = ioremap(FIFO_REG_BASE + FIFO_REG_7_OFFSET, 4);
fifo_dat8_addr = ioremap(FIFO_REG_BASE + FIFO_REG_8_OFFSET, 4);
fifo_dat9_addr = ioremap(FIFO_REG_BASE + FIFO_REG_9_OFFSET, 4);
fifo_dat10_addr = ioremap(FIFO_REG_BASE + FIFO_REG_10_OFFSET, 4);
fifo_dat11_addr = ioremap(FIFO_REG_BASE + FIFO_REG_11_OFFSET, 4);
fifo_sr_addr = ioremap(FIFO_REG_BASE + FIFO_REG_12_OFFSET, 4);
fifo_ecr_addr = ioremap(FIFO_REG_BASE + FIFO_REG_13_OFFSET, 4);
fifo_cr_addr = ioremap(FIFO_REG_BASE + FIFO_REG_14_OFFSET, 4);
/* 注册字符设备驱动 */
//(1)创建设备号
if (fifo.major)
{
fifo.devid = MKDEV(fifo.major, 0);
ret = register_chrdev_region(fifo.devid, FIFO_CNT, FIFO_NAME);
if (ret)
goto FAIL_REGISTER_CHR_DEV;
}
else
{
ret = alloc_chrdev_region(&fifo.devid, 0, FIFO_CNT, FIFO_NAME);
if (ret)
goto FAIL_REGISTER_CHR_DEV;
fifo.major = MAJOR(fifo.devid);
fifo.minor = MINOR(fifo.devid);
}
//(2)初始化cdev
fifo.cdev.owner = THIS_MODULE;
cdev_init(&fifo.cdev, &fifo_fops);
//(3)添加cdev
ret = cdev_add(&fifo.cdev, fifo.devid, FIFO_CNT);
if (ret)
goto FAIL_ADD_CDEV;
//(4)创建类
fifo.class = class_create(THIS_MODULE, FIFO_NAME);
if (IS_ERR(fifo.class))
{
ret = PTR_ERR(fifo.class);
goto FAIL_CREATE_CLASS;
}
//(5)创建设备
fifo.device = device_create(fifo.class, NULL, fifo.devid, NULL, FIFO_NAME);
if (IS_ERR(fifo.device))
{
ret = PTR_ERR(fifo.device);
goto FAIL_CREATE_DEV;
}
return 0;
FAIL_CREATE_DEV:
class_destroy(fifo.class);
FAIL_CREATE_CLASS:
cdev_del(&fifo.cdev);
FAIL_ADD_CDEV:
unregister_chrdev_region(fifo.devid, FIFO_CNT);
FAIL_REGISTER_CHR_DEV:
iounmap(fifo_dat0_addr);
iounmap(fifo_dat1_addr);
iounmap(fifo_dat2_addr);
iounmap(fifo_dat3_addr);
iounmap(fifo_dat4_addr);
iounmap(fifo_dat5_addr);
iounmap(fifo_dat6_addr);
iounmap(fifo_dat7_addr);
iounmap(fifo_dat8_addr);
iounmap(fifo_dat9_addr);
iounmap(fifo_dat10_addr);
iounmap(fifo_dat11_addr);
iounmap(fifo_sr_addr);
iounmap(fifo_ecr_addr);
iounmap(fifo_cr_addr);
return ret;
}
static void __exit fifo_exit(void)
{
//(1)注销设备
device_destroy(fifo.class, fifo.devid);
//(2)注销类
class_destroy(fifo.class);
//(3)删除cdev
cdev_del(&fifo.cdev);
//(4)注销设备号
unregister_chrdev_region(fifo.devid, FIFO_CNT);
//(5)取消内存映射
iounmap(fifo_dat0_addr);
iounmap(fifo_dat1_addr);
iounmap(fifo_dat2_addr);
iounmap(fifo_dat3_addr);
iounmap(fifo_dat4_addr);
iounmap(fifo_dat5_addr);
iounmap(fifo_dat6_addr);
iounmap(fifo_dat7_addr);
iounmap(fifo_dat8_addr);
iounmap(fifo_dat9_addr);
iounmap(fifo_dat10_addr);
iounmap(fifo_dat11_addr);
iounmap(fifo_sr_addr);
iounmap(fifo_ecr_addr);
iounmap(fifo_cr_addr);
}
/* 驱动模块入口和出口函数注册 */
module_init(fifo_init);
module_exit(fifo_exit);
MODULE_AUTHOR("Dingkun");
MODULE_DESCRIPTION("driver for hardware fifo in the platform");
MODULE_LICENSE("GPL");