设备驱动-14.userspace进行IP的寄存器读写

发布时间 2023-08-27 16:46:58作者: fuzidage

1 定义寄存器

以键盘keyscan为例,定义一个IP寄存器描述头文件,IOCRREG, IOCWREG定义了两个ioctl命令,用来读写寄存器。struct msg用来存放寄存器地址和值。
image
image

/* Keyscan register: addr + offset + mask */
#define keyscan_top_keyscan_config1			0x0
#define keyscan_top_keyscan_config2			0x4
#define keyscan_top_keyscan_config3			0x8
#define keyscan_top_keyscan_config4			0xc

#define keyscan_top_reg_fifo_count				0x20
#define keyscan_top_reg_fifo_count_OFFSET			0
#define keyscan_top_reg_fifo_count_MASK				0xf
#define keyscan_top_reg_fifo_not_empty				0x20
#define keyscan_top_reg_fifo_not_empty_OFFSET			4
#define keyscan_top_reg_fifo_not_empty_MASK			0x10
struct msg {
	long unsigned int addr;
	unsigned int data;
};
#define IOC_MAGIC 'k'
#define IOCRREG _IOR(IOC_MAGIC, 1, struct msg)
#define IOCWREG _IOW(IOC_MAGIC, 2, struct msg)

2 驱动代码

image

static long keyscan_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct msg m;
	memset(&m, 0, sizeof(m));
	void __iomem* base = dev->base;
	switch (cmd) {
	case IOCRREG:
		if (copy_from_user(&m, (struct msg __user *)arg, sizeof(m)))
			return -EFAULT;
		m.data = readl(base + m.addr);
		printk(KERN_DEBUG "base_addr:0x%lx, offset:0x%lx, read data: 0x%x \n", base, m.addr, m.data);
		if (copy_to_user((struct msg __user *)arg, &m, sizeof(m)))
			return -EFAULT;
		break;
	case IOCWREG:
		if (copy_from_user(&m, (struct msg __user *)arg, sizeof(m)))
			return -EFAULT;
		printk(KERN_DEBUG "base_addr:0x%lx, offset:0x%lx, write data: 0x%x \n", base, m.addr, m.data);
		writel(m.data, base + m.addr);
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

驱动先定义IP base addr,然后透过ioctl进行arg参数接收,确定msg中的addr和data, 接收cmd调用writel, readl进行读写。

3 用户态代码

image

static inline void clrsetbits_32(long unsigned int addr, unsigned int clear, unsigned int set) {
	int ret;
	struct msg m;
	memset(&m, 0, sizeof(m));
	m.addr = addr;
	ret = ioctl(fd, IOCRREG, &m);
	if (ret) {
		perror("ioctl: read error!");
		return;
	}
	m.data = (m.data & (~clear)) | set;
	ret = ioctl(fd, IOCWREG, &m);
	if (ret) {
		perror("ioctl: write error");
		return;
	}
	return;
}
static unsigned int read_reg(long unsigned int addr) {
	int ret;
	struct msg m;
	memset(&m, 0, sizeof(m));
	ret = ioctl(fd, IOCRREG, &m);
	if (ret) {
		perror("ioctl: read reg error!");
		return -1;
	}
	return m.data;
}

首先实现基础读写函数进行寄存器读写,reg_read函数传入addr即可得到val, clrsetbits_32需要先读,在写入val.

#define KEYSCAN_MASK(REG_NAME) keyscan_top_##REG_NAME##_MASK
#define KEYSCAN_OFFSET(REG_NAME) keyscan_top_##REG_NAME##_OFFSET
#define KEYSCAN_SET(REG_NAME, VAL)                                             \
	clrsetbits_32(keyscan_top_##REG_NAME, KEYSCAN_MASK(REG_NAME),          \
			  (VAL) << KEYSCAN_OFFSET(REG_NAME))
#define KEYSCAN_GET(REG_NAME)                                                  \
	((read_reg(keyscan_top_##REG_NAME) & KEYSCAN_MASK(REG_NAME)) >>        \
	 KEYSCAN_OFFSET(REG_NAME))

image

例如,当调用

KEYSCAN_GET(reg_fifo_count); //表示read_reg(keycan_top_reg_fifo_count) & keycan_top_reg_fifo_count_MASK >> keycan_top_reg_fifo_count_OFFSET

reg_fifo表示IP的某一个寄存器,count表示位域,因此对OFFSET定义成位域在该寄存器的偏移量(count位域是bit[3:0])定义为0,因此MASK定义为0xf.
同理not_empty也是一个位域,bit[4],OFFSET定义成4,MASK定义成0x10,来屏蔽除bit[4]的其他bit.

KEYSCAN_GET(reg_fifo_count);最终就获取到了reg_fifo寄存器的count位域的内容。
KEYSCAN_GET(reg_fifo_not_empty);最终就获取到了reg_fifo寄存器的not_empty位域的内容

又例如,当调用

KEYSCAN_SET(reg_enable, 1);//表示clrsetbits_32(keycan_top_reg_enable, keycan_top_reg_enable_MASK, 1 << keycan_top_reg_enable_OFFSET

image

#define keyscan_top_reg_row_mask			0x0
#define keyscan_top_reg_row_mask_OFFSET			0
#define keyscan_top_reg_row_mask_MASK			0xff
#define keyscan_top_reg_col_mask			0x0
#define keyscan_top_reg_col_mask_OFFSET			8
#define keyscan_top_reg_col_mask_MASK			0xff00
#define keyscan_top_reg_enable				0x00
#define keyscan_top_reg_enable_OFFSET			16
#define keyscan_top_reg_enable_MASK			0x10000

可以看到offset定义为16,mask定义为0x10000,用来屏蔽除bit[16]的其他位。clrsetbits_32会先读出该寄存器,然后对该位set1, mask掉其他位,再次写入该寄存器。