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的。

發佈了21 篇原創文章 · 獲贊 5 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章