2440 RTC

在Linux 2.6.29版本下实现了RTC的功能,但没有测试过。

 

在linux 2.6.30版本下,之用字符设备实现RTC。

代码如下:

#include <asm/io.h>
//#include <asm/arch/regs-rtc.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
//#include <linxu/devfs_fs_kernel.h>
#include <linux/module.h>

 

static int major;

 

#define DEVICE_NAME "RTC"
#define DEVICE_MAJOR major
#define DEVICE_MINOR 0
#define DEVICE_IRQ IRQ_TICK

 

//寄存器定义

#define RTCCON 0x57000040
#define TICNT  0x57000044
#define RTCSEC 0x57000070
#define RTCMIN 0x57000074
#define RTCHOUR 0x57000078

 

//寄存器虚拟地址

static void *rtccon;
static void *ticnt;
static void *rtcsec;
static void *rtcmin;
static void *rtchour;

 

static int flag = 0;
static char time[8];

 

 

static int num_to_BCD(int num)
{
 int i,j,r;
 i = num/10;
 j = num%10;
 r = (i<<4)|j;
 return r;
}

 

static int num_from_BCD(int num)

 int i,j,r;
 i = (num & 0xf0)>>4;
 j = (num &0x0f);
 r = i*10+j;
 return r;
}

 

 

static void set_time(int hour, int min, int sec)
{
 time[0] = hour/10 +'0';
 time[1] = hour%10 +'0';
 time[2] = ':';
 time[3] = min/10 +'0';
 time[4] = min%10 +'0';
 time[5] = ':';
 time[6] = sec/10 +'0';
 time[7] = sec%10 +'0';
}

 

 

static DECLARE_WAIT_QUEUE_HEAD(wq);

 

 

static irqreturn_t RTC_interrupt(void)

 flag++;
 wake_up_interruptible(&wq);
 return IRQ_HANDLED;
}

 

//1s我们只能读一次时间,这是由于RTC_read()会阻塞自己,直到1s中断到来,唤醒它阻塞的队列

static int RTC_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
 int hour, min, sec;
 wait_event_interruptible(wq, flag);            // 在此阻塞,直到有中断产生(flag发生变化)
 hour = num_from_BCD(ioread32(rtchour));
 min = num_from_BCD(ioread32(rtcmin));
 sec = num_from_BCD(ioread32(rtcsec));
 set_time(hour,min,sec);
 copy_to_user(buff, time, sizeof(time));
 flag = 0;
 return 0;
}

 

 

static int RTC_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
 int hour, min, sec;
 int tmp;
 copy_from_user(time, buff, sizeof(time));
 hour = time[1] -'0' + (time[0]-'0')*10;
 min = time[4] -'0' + (time[3] -'0')*10;
 sec = time[7] - '0' + (time[6] -'0')*10;
 tmp = ioread32(ticnt) & (~(1<<7));
 iowrite32(tmp, ticnt);
 iowrite32(0x01, rtccon);
 iowrite32(num_to_BCD(hour), rtchour);
 iowrite32(num_to_BCD(min), rtcmin);
 iowrite32(num_to_BCD(sec), rtcsec);
 iowrite32(0x00, rtccon);
 tmp = ioread32(ticnt) |(1<<7);
 iowrite32(tmp, ticnt);
 return 0;
}

 

static int RTC_ioctlr(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
 switch(cmd)
 {
 case RTC_CMD_READ:
  RTC_read(filp, (char*)arg, 8, 0);     // 调用read()函数实现ioctl中的读命令
  break;
 case RTC_CMD_WRITE:
  RTC_write(filp, (char*)arg, 8, 0);    // 这里的arg是不能有“&”符的,因为我们在传递参数的时候传的是地址
  break;
 default:
  break;
 }
 return 0;
}

 

static int RTC_open(struct inode *inode, struct file *filp)
{
 int ret;

 ret = request_irq(DEVICE_IRQ, RTC_interrupt, IRQF_DISABLED, DEVICE_NAME, NULL);
    if(ret < 0)
 {
  printk("request irq failed/n");
  return ret;
 }

 

 // 寄存器单独映射,并不是我们经常见到的映射寄存器的整个区域

 rtccon = ioremap(RTCCON,4);
 ticnt = ioremap(TICNT, 4);
 rtcmin = ioremap(RTCMIN,4);
 rtcsec = ioremap(RTCSEC, 4);
 rtchour = ioremap(RTCHOUR, 4);

 

 //初始时间为12:30:00(在此也可以看出2440的RTC的时间在寄存器中是以BCD码存在的)
 iowrite32(0x01, rtccon);
 iowrite32(0, rtcsec);
 iowrite32((3<<4)|0, rtcmin);
 iowrite32((1<<4)|2, rtchour);
 iowrite32(0x00, rtccon);
 iowrite32(127|(1<<7), ticnt);


 return 0;
}

 

static int RTC_close(struct inode *inode, struct file *filp)
{
 free_irq(DEVICE_IRQ, NULL);
 return 0;
}

 

static struct file_operations RTC_ops =
{

 .owner = THIS_MODULE,
 .open = RTC_open,
 .release = RTC_close,
 .read = RTC_read,
 .write = RTC_write,
 .ioctl = RTC_ioctlr,
};

 

int RTC_init(void)
{
 int ret;

// 在2.6.内核中很少看到register_chrdev来注册字符设备的,我们一般用cdev_init(),cdev_add()等函数来实现字符设备的注册

 ret = register_chrdev(0, DEVICE_NAME, &RTC_ops);
 if(ret < 0)
 {
  printk("register device failed/n");
  return ret;
 }
 else
  major = ret;
 printk("register device sucessfully %d/n",ret);

// devfs_mk_cdev是用来创建设备结点的,但在我用的2.6.29的内核中没有此函数,所以就没有创建设备结点,需要在插入RTC模块后

//手动创建设备结点
 //devfs_mk_cdev(MKDEV(DEVICE_MAJOR,DEVICE_MINOR),S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP,DEVICE_NAME);
 return 0;
}

 

void RTC_exit(void)
{
 unregister_chrdev(DEVICE_MAJOR, DEVICE_NAME);
 //devfs_remove(DEVICE_NAME);
 printk("Device has been unregister/n");
}


module_init(RTC_init);
module_exit(RTC_exit);

 

上面是驱动程序,没有创建设备结点,需要根据主设备号用mknod自己创建。也只有时间的控制,没有年月的设置。

 

下面是应用测试程序:

 

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>

 

#define RTC_CMD_READ 0x81
#define RTC_CMD_WRITE 0x82

 

static char time[8];
static int fd;

 

void int_to_char(int t[])
{
 time[0] = t[0]/10 + '0';
 time[1] = t[0]%10 + '0';
 time[3] = t[1]/10 + '0';
 time[4] = t[1]%10 + '0';
 time[6] = t[2]/10 + '0';
 time[7] = t[2]%10 + '0';
}

 

void sig_handle(void)
{
 int t[3];

 printf("input new time below/n");
 printf("hour:");
 scanf("%d",&t[0]);
 printf("min:");
 scanf("%d", &t[1]);
 printf("sec:");
 scanf("%d", &t[2]);

 int_to_char(t);
 //write(fd, time,sizeof(time));
 ioctl(fd, RTC_CMD_WRITE, time);

 

 

int main(void)
{
 int i;
 signal(SIGTSTP, (void*)sig_handle);
 fd = open("/dev/rtc0",O_RDWR);
 if(fd < 0)
 {
  printf("open rtc0 error/n");
  exit(1);
 }
 printf("open rtc0 ok/n");
 while(1)
 {
  ioctl(fd, RTC_CMD_READ, time);
  for(i=0; i<8; i++)
  {
   printf("%c",time[i]);
  }
  printf("press Ctrl+Z to reset time/n");
  printf("/n");
 }
 close(fd);
 return 0;
}
这些也是参考http://blog.163.com/hjw_vc/blog/static/114831035200982174353570/?fromdm&fromSearch&isFromSearchEngine=yes的文章做的。

调试过程中遇到的问题:

1)把RTC_ioctl()中的arg取地址了,致使在应用中不能正确打印时间信息。

2)在编译应用层程序时,刚开始我用gcc rtc_app.c编译,但最后运行不对,提示有错误,大概说是格式不对。

3)在进行时间设置时,如果我们设置24:xx:xx的话,则当到小时增加1时,我们的时间会变为25:00:00;正确的做法是:时间是不存在24点的,最多有23:xx:xx.

 

遗留的问题:

如何能在此驱动上实现hwclock的。

你可能感兴趣的:(2440 RTC)