Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92

  • 设备部分
  • 驱动部分
  • 改动部分
  • 参考

在网上的其他branch上找到一份RX8010SJ的驱动,但是有点问题,先贴代码,再说明修改部分。

设备部分:

比较简单,确定I2C端口以及设备地址就行。
&i2c1 {
status = “okay”;
rx8010: rx8010@32 {
compatible = “epson,rx8010”;
reg = <0x32>;
status = “okay”;
};
};

驱动部分:

此驱动已经可以读写时间,alarm部分因为没有需求,所以硬件没接,也未做测试。

/** Driver for the Epson RTC module RX-8010 SJ** Copyright(C) Timesys Corporation 2015* Copyright(C) General Electric Company 2015** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.**/#include <linux/bcd.h>
#include <linux/bitops.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtc.h>#define RX8010_SEC     0x10
#define RX8010_MIN     0x11
#define RX8010_HOUR    0x12
#define RX8010_WDAY    0x13
#define RX8010_MDAY    0x14
#define RX8010_MONTH   0x15
#define RX8010_YEAR    0x16
#define RX8010_YEAR    0x16
#define RX8010_RESV17  0x17
#define RX8010_ALMIN   0x18
#define RX8010_ALHOUR  0x19
#define RX8010_ALWDAY  0x1A
#define RX8010_TCOUNT0 0x1B
#define RX8010_TCOUNT1 0x1C
#define RX8010_EXT     0x1D
#define RX8010_FLAG    0x1E
#define RX8010_CTRL    0x1F
/* 0x20 to 0x2F are user registers */
#define RX8010_RESV30  0x30
#define RX8010_RESV31  0x31
#define RX8010_IRQ     0x32#define RX8010_EXT_WADA  BIT(3)#define RX8010_FLAG_VLF  BIT(1)
#define RX8010_FLAG_AF   BIT(3)
#define RX8010_FLAG_TF   BIT(4)
#define RX8010_FLAG_UF   BIT(5)#define RX8010_CTRL_AIE  BIT(3)
#define RX8010_CTRL_UIE  BIT(5)
#define RX8010_CTRL_STOP BIT(6)
#define RX8010_CTRL_TEST BIT(7)#define RX8010_ALARM_AE  BIT(7)static const struct i2c_device_id rx8010_id[] = {{ "rx8010", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, rx8010_id);static const struct of_device_id rx8010_of_match[] = {{ .compatible = "epson,rx8010" },{ }
};
MODULE_DEVICE_TABLE(of, rx8010_of_match);struct rx8010_data {struct i2c_client *client;struct rtc_device *rtc;u8 ctrlreg;
};static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
{struct i2c_client *client = dev_id;struct rx8010_data *rx8010 = i2c_get_clientdata(client);int flagreg;mutex_lock(&rx8010->rtc->ops_lock);flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);if (flagreg <= 0) {mutex_unlock(&rx8010->rtc->ops_lock);return IRQ_NONE;}if (flagreg & RX8010_FLAG_VLF)dev_warn(&client->dev, "Frequency stop detected\n");if (flagreg & RX8010_FLAG_TF) {flagreg &= ~RX8010_FLAG_TF;rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF);}if (flagreg & RX8010_FLAG_AF) {flagreg &= ~RX8010_FLAG_AF;rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF);}if (flagreg & RX8010_FLAG_UF) {flagreg &= ~RX8010_FLAG_UF;rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF);}i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);mutex_unlock(&rx8010->rtc->ops_lock);return IRQ_HANDLED;
}static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
{struct rx8010_data *rx8010 = dev_get_drvdata(dev);u8 date[7];int flagreg;int err;flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);if (flagreg < 0)return flagreg;if (flagreg & RX8010_FLAG_VLF) {dev_warn(dev, "Frequency stop detected\n");return -EINVAL;}err = i2c_smbus_read_i2c_block_data(rx8010->client, RX8010_SEC,7, date);if (err != 7)return err < 0 ? err : -EIO;dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f);dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f);dt->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f);dt->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f);dt->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1;dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100;dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f);return rtc_valid_tm(dt);
}static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
{struct rx8010_data *rx8010 = dev_get_drvdata(dev);u8 date[7];int ctrl, flagreg;int ret;if ((dt->tm_year < 100) || (dt->tm_year > 199))return -EINVAL;/* set STOP bit before changing clock/calendar */ctrl = i2c_smbus_read_byte_data(rx8010->client, RX8010_CTRL);if (ctrl < 0)return ctrl;rx8010->ctrlreg = ctrl | RX8010_CTRL_STOP;ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,rx8010->ctrlreg);if (ret < 0)return ret;date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec);date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min);date[RX8010_HOUR - RX8010_SEC] = bin2bcd(dt->tm_hour);date[RX8010_MDAY - RX8010_SEC] = bin2bcd(dt->tm_mday);date[RX8010_MONTH - RX8010_SEC] = bin2bcd(dt->tm_mon + 1);date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100);date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday);ret = i2c_smbus_write_i2c_block_data(rx8010->client,RX8010_SEC, 7, date);if (ret < 0)return ret;/* clear STOP bit after changing clock/calendar */ctrl = i2c_smbus_read_byte_data(rx8010->client, RX8010_CTRL);if (ctrl < 0)return ctrl;rx8010->ctrlreg = ctrl & ~RX8010_CTRL_STOP;ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,rx8010->ctrlreg);if (ret < 0)return ret;flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);if (flagreg < 0) {return flagreg;}if (flagreg & RX8010_FLAG_VLF)ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG,flagreg & ~RX8010_FLAG_VLF);return 0;
}static int rx8010_init_client(struct i2c_client *client)
{struct rx8010_data *rx8010 = i2c_get_clientdata(client);u8 ctrl[2];int need_clear = 0, err = 0;u8 flag;flag = i2c_smbus_read_byte_data(client, RX8010_FLAG);if (err < 0)return err;flag &= ~(RX8010_FLAG_VLF);err = i2c_smbus_write_byte_data(client, RX8010_FLAG, flag);/* Initialize reserved registers as specified in datasheet */err = i2c_smbus_write_byte_data(client, RX8010_RESV17, 0xD8);if (err < 0)return err;err = i2c_smbus_write_byte_data(client, RX8010_RESV30, 0x00);if (err < 0)return err;err = i2c_smbus_write_byte_data(client, RX8010_RESV31, 0x08);if (err < 0)return err;err = i2c_smbus_write_byte_data(client, RX8010_IRQ, 0x00);if (err < 0)return err;err = i2c_smbus_read_i2c_block_data(rx8010->client, RX8010_FLAG,2, ctrl);if (err != 2)return err < 0 ? err : -EIO;if (ctrl[0] & RX8010_FLAG_VLF)dev_warn(&client->dev, "Frequency stop was detected\n");if (ctrl[0] & RX8010_FLAG_AF) {dev_warn(&client->dev, "Alarm was detected\n");need_clear = 1;}if (ctrl[0] & RX8010_FLAG_TF)need_clear = 1;if (ctrl[0] & RX8010_FLAG_UF)need_clear = 1;if (need_clear) {ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF);err = i2c_smbus_write_byte_data(client, RX8010_FLAG, ctrl[0]);if (err < 0)return err;}rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST);return err;
}static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{struct rx8010_data *rx8010 = dev_get_drvdata(dev);struct i2c_client *client = rx8010->client;u8 alarmvals[3];int flagreg;int err;err = i2c_smbus_read_i2c_block_data(client, RX8010_ALMIN, 3, alarmvals);if (err != 3)return err < 0 ? err : -EIO;flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);if (flagreg < 0)return flagreg;t->time.tm_sec = 0;t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);if (!(alarmvals[2] & RX8010_ALARM_AE))t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f);t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;return err;
}static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{struct i2c_client *client = to_i2c_client(dev);struct rx8010_data *rx8010 = dev_get_drvdata(dev);u8 alarmvals[3];int extreg, flagreg;int err;flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);if (flagreg < 0) {return flagreg;}if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) {rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,rx8010->ctrlreg);if (err < 0) {return err;}}flagreg &= ~RX8010_FLAG_AF;err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);if (err < 0)return err;alarmvals[0] = bin2bcd(t->time.tm_min);alarmvals[1] = bin2bcd(t->time.tm_hour);alarmvals[2] = bin2bcd(t->time.tm_mday);err = i2c_smbus_write_i2c_block_data(rx8010->client, RX8010_ALMIN,2, alarmvals);if (err < 0)return err;extreg = i2c_smbus_read_byte_data(client, RX8010_EXT);if (extreg < 0)return extreg;extreg |= RX8010_EXT_WADA;err = i2c_smbus_write_byte_data(rx8010->client, RX8010_EXT, extreg);if (err < 0)return err;if (alarmvals[2] == 0)alarmvals[2] |= RX8010_ALARM_AE;err = i2c_smbus_write_byte_data(rx8010->client, RX8010_ALWDAY,alarmvals[2]);if (err < 0)return err;if (t->enabled) {if (rx8010->rtc->uie_rtctimer.enabled)rx8010->ctrlreg |= RX8010_CTRL_UIE;if (rx8010->rtc->aie_timer.enabled)rx8010->ctrlreg |=(RX8010_CTRL_AIE | RX8010_CTRL_UIE);err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,rx8010->ctrlreg);if (err < 0)return err;}return 0;
}static int rx8010_alarm_irq_enable(struct device *dev,unsigned int enabled)
{struct i2c_client *client = to_i2c_client(dev);struct rx8010_data *rx8010 = dev_get_drvdata(dev);int flagreg;u8 ctrl;int err;ctrl = rx8010->ctrlreg;if (enabled) {if (rx8010->rtc->uie_rtctimer.enabled)ctrl |= RX8010_CTRL_UIE;if (rx8010->rtc->aie_timer.enabled)ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE);} else {if (!rx8010->rtc->uie_rtctimer.enabled)ctrl &= ~RX8010_CTRL_UIE;if (!rx8010->rtc->aie_timer.enabled)ctrl &= ~RX8010_CTRL_AIE;}flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);if (flagreg < 0)return flagreg;flagreg &= ~RX8010_FLAG_AF;err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);if (err < 0)return err;if (ctrl != rx8010->ctrlreg) {rx8010->ctrlreg = ctrl;err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,rx8010->ctrlreg);if (err < 0)return err;}return 0;
}static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{struct i2c_client *client = to_i2c_client(dev);struct rx8010_data *rx8010 = dev_get_drvdata(dev);int ret, tmp;int flagreg;switch (cmd) {case RTC_VL_READ:flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);if (flagreg < 0)return flagreg;tmp = !!(flagreg & RX8010_FLAG_VLF);if (copy_to_user((void __user *)arg, &tmp, sizeof(int)))return -EFAULT;return 0;case RTC_VL_CLR:flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);if (flagreg < 0) {return flagreg;}flagreg &= ~RX8010_FLAG_VLF;ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);if (ret < 0)return ret;return 0;default:return -ENOIOCTLCMD;}
}static struct rtc_class_ops rx8010_rtc_ops = {.read_time = rx8010_get_time,.set_time = rx8010_set_time,.ioctl = rx8010_ioctl,
};static int rx8010_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);struct rx8010_data *rx8010;int err = 0;if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA| I2C_FUNC_SMBUS_I2C_BLOCK)) {dev_err(&adapter->dev, "doesn't support required functionality\n");return -EIO;}rx8010 = devm_kzalloc(&client->dev, sizeof(struct rx8010_data),GFP_KERNEL);if (!rx8010)return -ENOMEM;rx8010->client = client;i2c_set_clientdata(client, rx8010);err = rx8010_init_client(client);if (err)return err;if (client->irq > 0) {dev_info(&client->dev, "IRQ %d supplied\n", client->irq);err = devm_request_threaded_irq(&client->dev, client->irq, NULL,rx8010_irq_1_handler,IRQF_TRIGGER_LOW | IRQF_ONESHOT,"rx8010", client);if (err) {dev_err(&client->dev, "unable to request IRQ\n");client->irq = 0;} else {rx8010_rtc_ops.read_alarm = rx8010_read_alarm;rx8010_rtc_ops.set_alarm = rx8010_set_alarm;rx8010_rtc_ops.alarm_irq_enable = rx8010_alarm_irq_enable;}}rx8010_rtc_ops.read_alarm = rx8010_read_alarm;rx8010_rtc_ops.set_alarm = rx8010_set_alarm;rx8010_rtc_ops.alarm_irq_enable = rx8010_alarm_irq_enable;device_init_wakeup(&client->dev, 1);rx8010->rtc = devm_rtc_device_register(&client->dev, client->name,&rx8010_rtc_ops, THIS_MODULE);if (IS_ERR(rx8010->rtc)) {dev_err(&client->dev, "unable to register the class device\n");return PTR_ERR(rx8010->rtc);}rx8010->rtc->max_user_freq = 1;return err;
}static struct i2c_driver rx8010_driver = {.driver = {.name = "rtc-rx8010",.of_match_table = of_match_ptr(rx8010_of_match),},.probe      = rx8010_probe,.id_table   = rx8010_id,
};module_i2c_driver(rx8010_driver);MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
MODULE_DESCRIPTION("Epson RX8010SJ RTC driver");
MODULE_LICENSE("GPL v2");

改动部分:

左边是原生驱动,右边是修改部分。
改动一:

寄存器地址0x31误写成0x32.

改动二:

前面硬件的文章有提过,开机需要检查VLF是否被置位,如果是表示rtc还不能正常工作,需要置成0 ,然后再初始化才能工作,这样也是和数据手册上描述的上电流程是一致的。

改动三:

由于驱动没用到irq,那么rx8010_probe()里的rx8010_rtc_ops结构体对应的read_alarm/set_alarm/alarm_irq_enable将不会被赋值,这将导致注册alarm驱动的时候失败。
alarmtimer.c

static int alarmtimer_rtc_add_device(struct device *dev, struct class_interface *class_intf)
{unsigned long flags;struct rtc_device *rtc = to_rtc_device(dev);if (rtcdev)return -EBUSY;/*如果set_alarm是空函数会注册失败*/if (!rtc->ops->set_alarm)return -1;if (!device_may_wakeup(rtc->dev.parent))return -1;spin_lock_irqsave(&rtcdev_lock, flags);if (!rtcdev) {/*最终得到一个全局的rtcdev*/rtcdev = rtc;/* hold a reference so it doesn't go away */get_device(dev);}spin_unlock_irqrestore(&rtcdev_lock, flags);return 0;
}

同样的 device_init_wakeup(&client->dev, 1); 是为了让device_may_wakeup(rtc->dev.parent) 不会返回失败。
这样才设置时间的时候就可以正确地得到rtc device了。
alarm-dev.c

static int alarm_set_rtc(struct timespec *ts)
{
......rtc_dev = alarmtimer_get_rtcdev();/*这样得到就不会为NULL了。*/if (rtc_dev) rv = rtc_set_time(rtc_dev, &new_rtc_tm);
......
}

参考:

https://raw.githubusercontent.com/torvalds/linux/master/drivers/rtc/rtc-rx8010.c

[RK3288][Android6.0] RTC模块RX8010SJ驱动添加及改动相关推荐

  1. [RK3288][Android6.0] RTC模块RX8010SJ硬件部分概要

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 背景 特性 框图 硬件连接 注意事项 上电过程 初始化过程 I2C地址 参考 背景: 系统自带的p ...

  2. [RK3288][Android6.0] 调试笔记 --- apk安装添加黑名单

    Platform: ROCKCHIP OS: Android 6.0 Kernel: 3.10.92 出于定制考虑,需要把系统一些不必要的apk移除,但是一个个找又太麻烦, 在不考虑存储空间的情况下可 ...

  3. [RK3288][Android6.0] DVP接口摄像头OV5640添加

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 DVP或者MIPI接口直接在camera board文件中改动就可以. cam_board_rk328 ...

  4. [RK3288][Android6.0] 调试笔记 --- WiFi芯片AP6356S添加

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 背景: RK3288 Android6.0平台对AP6356S的支持不是很完善,直接修改wifi ch ...

  5. [RK3288][Android6.0] 调试笔记 --- 电池电量一直显示100%

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 之前文章[RK3288][Android6.0] 调试笔记 - 伪电池驱动添加 阐述了如何添加一个 ...

  6. [RK3288][Android6.0] 设置中通过Sensor旋转显示画面小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 Settings -> Display有个选项控制旋转屏幕时内容是否跟着旋转 这个功能是通过 ...

  7. [RK3288][Android6.0] 调试笔记 --- touch无法获取坐标点

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 有网友遇到调试touch的时候能触发中断,但无法获取坐标点 具体可参考文章 [RK3288][An ...

  8. [RK3288][Android6.0] 调试笔记 --- AndroidTool低格无效问题

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 现象: 之前文章 [RK3288][Android6.0] 调试笔记 - AndroidTool两 ...

  9. [RK3288][Android6.0] 调试笔记 --- 设置中文为默认输入法

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 添加方法: 1. 添加第三方apk 路径: rk3288/device/rockchip/rk3288 ...

最新文章

  1. JSF实现“Hello World!”
  2. phpinfo.php ctf,这你不是你所常见的PHP文件包含漏洞(利用phpinfo)
  3. 7. Leetcode 611. 有效三角形的个数 (数组-双向双指针)
  4. C语言 int 转单精度浮点,单精度浮点数与十六进制转换 C语言程序 单片机也可用...
  5. [私]-optee的同步方法
  6. 真效率神器,UI稿智能转换成前端代码,准确率极高
  7. Apache Flink 进阶入门(二):Time 深度解析
  8. 举例说明string类和stringbuffer类的区别_String,StringBuilder,StringBuffer的区别
  9. 编程语言---自己常用网站整理
  10. VSCode每打开一次文件弹出一个git弹窗:-login -i rev-parse --show-toplevel
  11. XAMPP报错:mysqli::real_connect(): (HY000/1045): Access denied for user ‘pma‘@‘localhost‘
  12. 503.下一个更大元素II(力扣leetcode) 博主可答疑该问题
  13. Windows各个系统勒索补丁下载地址
  14. 中信证券java面试_中信证券面试问题分享
  15. BUUCTF Misc 神秘龙卷风
  16. 武汉大学:最美大学最美网络
  17. 改进YOLOv5系列:最新ConvNeXt结合YOLO | CVPR2022 多种搭配,即插即用 | Backbone主干CNN模型
  18. windows神器,让你的效率直线提升
  19. 解决win11不能拖拽图片进ppt/ps的问题
  20. androidjni开发!应聘高级Android工程师历程感言,快来收藏!

热门文章

  1. 基于MATLAB读取高光谱影像每个像素的光谱信息
  2. 浅谈模式 - 观察者模式
  3. 浅谈模式 - 代理模式
  4. C++11标准模板(STL)- 算法 - 集合操作(在已排序范围上)(std::set_difference)
  5. 鸟哥的Linux私房菜(服务器)- 第十六章、文件服务器之二: SAMBA 服务器
  6. paypal电汇使用心得
  7. 4星|《经济学人2017精选书评合辑》:书都比较新且没有中译本
  8. cesium——相机飞行定位原理
  9. 为何“看得懂单词却读不懂句子”
  10. 解决Jenkins设置管理员后登陆提示:Access Denied admin没有Overall/Read权限