Android系统RTC调试从驱动到应用(一)

软件开发平台:android P 源码。
硬件开发平台:nxp imx8m mini开发板,RTC 型号8025T。

本文记录在nxp 8m mini 硬件平台, android P 源码的软件平台上调试RTC8025T驱动,RTC的framework 层android 已经写好了即 AlarmManagerService , APP 应用层通过((AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE)) 提供的接口来读取和设置 RTC 。

一.RTC相关驱动调试
先来了解下 RTC 8025T 操作模式:
操作模式 :
1 ) 实时时钟模式
实时时 钟模式
该功能被用来设定和读取年,月,日,星期,时,分,秒 时间信息。年份为后两位数字表示,
任何可以被 4 整除的年份被当成闰年处理。(2000 年到 2099 年)
2 ) 固定周期的中断发生功能:
固定周期的中断发生功能
固定周期定时中断发生功能可以产生一个固定周期的中断事件,固定周期可在 244.14uS 到
4095 分钟之间的 任意时间设定。
3)定时更新中断功能:
该功能可以根据内部时钟的定时设定,每秒或每分钟产生一个中断事件。
当中断事件产生,UF 标志位的值变成 1 同时/INT 引脚变成低电平表示一个中断事件的产生。
4 ) 闹钟中断功能
闹钟 中断功能:
中断功能
该功能可以根据报警设定来产生一个中断。
硬件电路设计如下:
Android系统RTC调试从驱动到应用(一)_第1张图片
Nxp 平台有有完整的RTC驱动框架,所以只需要完善8025T相关的读写功能就可以了。
Android系统RTC调试从驱动到应用(一)_第2张图片
初始化8025T

static int rx8025_probe(struct i2c_client *client,
         const struct i2c_device_id *id)
{
   struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
   struct rx8025_data *rx8025;
   int err = 0;
   //可以看到RTC 驱动初始化的上下文 	
   dump_stack();

   rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL);
   
   rx8025->client = client;
   i2c_set_clientdata(client, rx8025);

   err = rx8025_init_client(client);
   
  // 将设备操作函数rx8025_rtc_ops 和设备关联起来
   rx8025->rtc = devm_rtc_device_register(&client->dev, client->name,
                 &rx8025_rtc_ops, THIS_MODULE);
   
   //irq?
   if (client->irq > 0) {
      
     //注册中断回调函数 rx8025_handle_irq
      err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
                  rx8025_handle_irq,
                  IRQF_ONESHOT,
                  "rx8025", client);
      
   }

   rx8025->rtc->max_user_freq = 1;

   /* the rx8025 alarm only supports a minute accuracy */
   rx8025->rtc->uie_unsupported = 1;

   err = rx8025_sysfs_register(&client->dev);
   printk("%s  end\r\n", __func__);/*Herber*/

   return err;
}



这里重点关注 rx8025_rtc_ops,实现这里的读写函数就可以了。
static const struct rtc_class_ops rx8025_rtc_ops = {
.read_time = rx8025_get_time,
.set_time = rx8025_set_time,
.read_alarm = rx8025_read_alarm,
.set_alarm = rx8025_set_alarm,
.alarm_irq_enable = rx8025_alarm_irq_enable,
};

Struct rtc_class_ops 定义在rtc.h

struct rtc_class_ops {
int (*ioctl)(struct device *, unsigned int, unsigned long);
int (*read_time)(struct device *, struct rtc_time *);
int (*set_time)(struct device *, struct rtc_time *);
int (*read_alarm)(struct device *, struct rtc_wkalrm *);
int (*set_alarm)(struct device *, struct rtc_wkalrm *);
int (*proc)(struct device *, struct seq_file *);
int (*set_mmss64)(struct device *, time64_t secs);
int (*set_mmss)(struct device *, unsigned long secs);
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
int (*read_offset)(struct device *, long *offset);
int (*set_offset)(struct device *, long offset);
};

这里分析下写入rtc寄存器

将dt的 tm_sec等,转成bcd码 写入RX8025_REG_SEC寄存器。

static int rx8025_set_time(struct device *dev, struct rtc_time *dt)
{
   struct rx8025_data *rx8025 = dev_get_drvdata(dev);
   u8 date[7];
   int ret;

   dump_stack();

   printk("%s( %d %d %d %d %d %d %d %d %d)\n",__func__,dt->tm_sec,dt->tm_min,dt->tm_hour ,dt->tm_mday, dt->tm_mon,dt->tm_year, dt->tm_wday,dt->tm_yday,  dt->tm_isdst );
   if ((dt->tm_year < 100) || (dt->tm_year > 199))
   {  
      printk("%s set Year Err %d === \r\n",__func__,dt->tm_year);
      return -EINVAL;
   }
   /*
    * Here the read-only bits are written as "0".  I'm not sure if that
    * is sound.
    */
   date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec);
   date[RX8025_REG_MIN] = bin2bcd(dt->tm_min);
   if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
      date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour);
   else
      date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0)
         | bin2bcd((dt->tm_hour + 11) % 12 + 1);

   date[RX8025_REG_WDAY] = bin2bcd(dt->tm_wday);
   date[RX8025_REG_MDAY] = bin2bcd(dt->tm_mday);
   date[RX8025_REG_MONTH] = bin2bcd(dt->tm_mon + 1);
   date[RX8025_REG_YEAR] = bin2bcd(dt->tm_year - 100);

   ret = rx8025_write_regs(rx8025->client, RX8025_REG_SEC, 7, date);
   if (ret < 0)
      return ret;

   return rx8025_reset_validity(rx8025->client);                 
}

下面是 读取rtc的寄存器

static int rx8025_get_time(struct device *dev, struct rtc_time *dt)
{
   struct rx8025_data *rx8025 = dev_get_drvdata(dev);
   u8 date[7];
   int err;
   dump_stack();


   //从RX8025_REG_SEC 读7位到 date
   err = rx8025_read_regs(rx8025->client, RX8025_REG_SEC, 7, date);
   printk("%s para 0x%02x,0x%02x,0x%02x=== \r\n",__func__,date[0],date[1],date[2]);/*Herber*/
   if (err){
      printk("%s S3,Err +++++\r\n",__func__);
      return err;
   }
   
   dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f);
   dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f);
   if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
      dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f);
   else
      dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12
         + (date[RX8025_REG_HOUR] & 0x20 ? 12 : 0);

   dt->tm_mday = bcd2bin(date[RX8025_REG_MDAY] & 0x3f);
   dt->tm_mon = bcd2bin(date[RX8025_REG_MONTH] & 0x1f) - 1;
   dt->tm_year = bcd2bin(date[RX8025_REG_YEAR]) + 100;

   dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
      dt->tm_sec, dt->tm_min, dt->tm_hour,
      dt->tm_mday, dt->tm_mon, dt->tm_year);

   return rtc_valid_tm(dt);
}

可以看到rtc 驱动只提供了简单的设定和读取年,月,日,星期,时,分,秒 时间信息的功能。

#define CFG_RTC_I2C_DEV              "/dev/rtc0"
 
#include 
int main(void)
{

   int fd_i2c,retval;

   struct rtc_time new, current;

   printf("==== RTC TIME operates ====\n");
   fd_i2c = open(CFG_RTC_I2C_DEV, O_RDWR);          
   if (fd_i2c < 0) {
      printf("Open i2c device %s failed!\n", CFG_RTC_I2C_DEV);
      return -1;
   }
  
   scanf("%d-%d-%d", &new.tm_mday, &new.tm_mon, &new.tm_year);
   printf("\r\n");

   scanf("%d:%d:%d", &new.tm_hour, &new.tm_min, &new.tm_sec);
   printf("\r\n");
  
   printf("Test will set RTC date/time to %d-%d-%d, %02d:%02d:%02d.\n",
   new.tm_mday, new.tm_mon + 1, new.tm_year + 1900,
   new.tm_hour, new.tm_min, new.tm_sec);        
   /* Write the new date in RTC */
  printf("renew data in RTC Module \r\n");
   retval = ioctl(fd_i2c, RTC_SET_TIME, &new);
   if (retval == -1) {
      printf("--RTC_SET_TIME ioctl Err--\r\n");
      //close(fd);  
      goto err;//exit(errno);
   }

   /* Read back */
   retval = ioctl(fd_i2c, RTC_RD_TIME, &current);
   if (retval == -1) {
      printf("---RTC_RD_TIME ioctl Err ---\r\n");
      goto err;exit(errno);
   }   
   
   printf("\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
      current.tm_mday, current.tm_mon + 1, current.tm_year + 1900,
      current.tm_hour, current.tm_min, current.tm_sec);

  close(fd_i2c);
   return 0;
err:
   if (fd_i2c)                               
      close(fd_i2c);                   
   return -1;       
             
}

以上代码可以测试 rtc 的读写功能.

再梳理下rtc驱动的流程,在内核启动时 初始化rtc即rx8025_probe, 再将rtc 的时间给了系统。

static int __init rtc_hctosys(void)
{
	int err = -ENODEV;
	struct rtc_time tm;
	struct timespec64 tv64 = {
		.tv_nsec = NSEC_PER_SEC >> 1,
	};
	struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
	err = rtc_read_time(rtc, &tm);
	tv64.tv_sec = rtc_tm_to_time64(&tm);
	err = do_settimeofday64(&tv64);
	dev_info(rtc->dev.parent,
		"setting system clock to "
		"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
		tm.tm_hour, tm.tm_min, tm.tm_sec,
		(long long) tv64.tv_sec);

	return err;
}

late_initcall(rtc_hctosys);

这样系统时间就和rtc 的时间对齐了。

你可能感兴趣的:(android开发,android)