以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
1、访问寄存器的方式
之前对寄存器的操作,都是先定义指向寄存器的指针,然后再解引用来对寄存器进行操作。这是因为ARM体系中,内存和IO是统一编址的。但是其他体系(比如X86)不是统一编址的,因此这样操作的代码不具有可移植性!
2、内核提供的寄存器读写接口
内核提供了寄存器读写接口,这些接口具有移植性。
定义在/arch/arm/inluce/asm/Io.h文件中。
3、代码实践
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h> // arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>#define MYMAJOR 200
#define MYNAME "testchar"#define GPJ0CON S5PV210_GPJ0CON
#define GPJ0DAT S5PV210_GPJ0DAT#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)#define GPJ0CON_PA 0xe0200240
#define GPJ0DAT_PA 0xe0200244#define S5P_GPJ0REG(x) (x)
#define S5P_GPJ0CON S5P_GPJ0REG(0)
#define S5P_GPJ0DAT S5P_GPJ0REG(4)unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;static void __iomem *baseaddr; // 寄存器的虚拟地址的基地址//**********省略代码// 模块安装函数
static int __init chrdev_init(void)
{ printk(KERN_INFO "chrdev_init helloworld init\n");mymajor = register_chrdev(0, MYNAME, &test_fops);if (mymajor < 0){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);/* // 使用动态映射的方式来操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))return -EINVAL;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))return -EINVAL;pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
*/
// *pGPJ0CON = 0x11111111;
// *pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮// 测试1:用2次ioremap得到的动态映射虚拟地址来操作,测试成功
// writel(0x11111111, pGPJ0CON);
// writel(((0<<3) | (0<<4) | (0<<5)), pGPJ0DAT);// 测试2:用静态映射的虚拟地址来操作,测试成功
// writel(0x11111111, GPJ0CON);
// writel(((0<<3) | (0<<4) | (0<<5)), GPJ0DAT);// 测试3:用1次ioremap映射多个寄存器得到虚拟地址,测试成功if (!request_mem_region(GPJ0CON_PA, 8, "GPJ0BASE"))return -EINVAL;baseaddr = ioremap(GPJ0CON_PA, 8);writel(0x11111111, baseaddr + S5P_GPJ0CON);writel(((0<<3) | (0<<4) | (0<<5)), baseaddr + S5P_GPJ0DAT);return 0;
}// 模块卸载载函数
static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");//*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));//writel(((1<<3) | (1<<4) | (1<<5)), pGPJ0DAT); //writel(((1<<3) | (1<<4) | (1<<5)), GPJ0DAT); writel(((1<<3) | (1<<4) | (1<<5)), baseaddr + S5P_GPJ0DAT); /* // 解除映射iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);
*/iounmap(baseaddr);release_mem_region(baseaddr, 8);// 在module_exit宏调用的函数中去注销字符设备驱动unregister_chrdev(mymajor, MYNAME);// rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息