关闭

mini2440 LED驱动 (用ioremap实现访问CPU寄存器)

356人阅读 评论(0) 收藏 举报

I/O 内存访问流程:

1. request_mem_region()  申请IO内存

2.ioremap() 将物理地址映射到虚拟地址

3.ioread8() 、ioread16()、ioread32()、iowrite8()、iowrite16()、iowrite32() 读写

4.iounmap() 释放虚拟内存

5.release_mem_region() 释放IO内存

注意:

1、2 在模块初始化或打开设备时调用

4、5 在模块卸载或关闭设备时调用

request_mem_region()不是必须使用,但建议使用。任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再次申请该资源就会失败。

 

 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/ioport.h>

#define DEVICE_NAME "led_decly"

#define LED1_ON   ~(1<<5) //低电平点亮LED
#define LED2_ON   ~(1<<6)
#define LED3_ON   ~(1<<7)
#define LED4_ON   ~(1<<8)

#define LED1_OFF   (1<<5)
#define LED2_OFF   (1<<6)
#define LED3_OFF   (1<<7)
#define LED4_OFF   (1<<8)

#define GPBCON 0x56000010 //寄存器地址(物理地址)
#define GPBDAT 0x56000014
static volatile unsigned long *gpbcon_addr; //经过ioremap映射后的虚拟地址
static volatile unsigned long *gpbdat_addr;

static void Led_port_init(void)
{
   //设置GPB5-GPB8为输出端口
   *gpbcon_addr &= ~((3<<10)|(3<<12)|(3<<14)|(3<<16)); 
   *gpbcon_addr |= (1<<10)|(1<<12)|(1<<14)|(1<<16);
   
   //全亮
   *gpbdat_addr &= LED1_ON & LED2_ON & LED3_ON & LED4_ON;
}


static void led_turn_on(unsigned int led_nu)
{
 switch (led_nu)
 {
  case 1:
   *gpbdat_addr &= LED1_ON;
   break;
  case 2:
   *gpbdat_addr &= LED2_ON;
   break;
  case 3:
   *gpbdat_addr &= LED3_ON;
   break;
  case 4:
   *gpbdat_addr &= LED4_ON;
   break;
  default:
   break;
 }
}

static void led_turn_off(unsigned int led_nu)
{
 switch (led_nu)
 {
  case 1:
   *gpbdat_addr |= LED1_OFF;
   break;
  case 2:
   *gpbdat_addr |= LED2_OFF;
   break;
  case 3:
   *gpbdat_addr |= LED3_OFF;
   break;
  case 4:
   *gpbdat_addr |= LED4_OFF;
   break;
  default:
   break;
 }
}

static int led_open(struct inode * inode , struct file * filp)
{
 return 0;
}

static int led_release(struct inode * inode, struct file *filp)
{
 return 0;
}

static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
 int ret = 0;
 switch (cmd)
 {
  case 0:
   led_turn_off(arg);
   break;
  case 1:
   led_turn_on(arg);
   break;
  default:
   ret = -EINVAL;
   break;
 }
 return ret;
}

static const struct file_operations led_fops =
{
 .owner = THIS_MODULE,
 .open = led_open,
 .release = led_release,
 .ioctl = led_ioctl,
};

static struct miscdevice led_dev =
{
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &led_fops,
};

static int __init led_init(void)

 int ret;
 
 //申请IO内存,不是必须的。
 if (!request_mem_region(GPBCON, 8, "leds"))
 {
  ret = -EBUSY;
  goto request_mem_failed;
 }
 
 gpbcon_addr = ioremap(GPBCON, 4);//将物理地址映射为虚拟地址
 if (NULL == gpbcon_addr)
 {
  ret = -EIO;
  printk("gpbcon remap failed\n");
  goto con_map_failed;
 }
 gpbdat_addr = ioremap(GPBDAT, 4);
 if (NULL == gpbdat_addr)
 {
  ret = -EIO;
  printk("gpbdat remap failed\n");
  goto dat_map_failed;
 }
 printk("gpbcon_addr remap on %p\n", gpbcon_addr);
 printk("gpbdat_addr remap on %p\n", gpbdat_addr);
 
 Led_port_init();

 ret = misc_register(&led_dev);
 if (ret)
 {
  printk("misc_register failed\n");
  goto failed;
 }
 
 printk("leds init\n");
 return 0;
 
 failed:
  iounmap(gpbdat_addr);
 dat_map_failed:
  iounmap(gpbcon_addr);
 con_map_failed:
  release_mem_region(GPBCON, 8);
 request_mem_failed:
  return ret;
}

static void __exit led_exit(void)
{
 iounmap(gpbdat_addr); //取消映射
 iounmap(gpbcon_addr);
 release_mem_region(GPBCON, 8); //释放I/O内存
 misc_deregister(&led_dev);
 
 printk("leds exit\n");
}

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Decly");
module_init(led_init);
module_exit(led_exit);

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:148324次
    • 积分:1784
    • 等级:
    • 排名:千里之外
    • 原创:15篇
    • 转载:156篇
    • 译文:0篇
    • 评论:9条
    文章分类
    最新评论