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

news/2024/11/25 19:37:54/

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


http://www.ppmy.cn/news/112505.html

相关文章

linux中的rx作用,linux – ifconfig输出中RX’错误’和’帧’的确切含义?

我看到(RHEL)节点出现网络问题(数据包丢失),这似乎也表现为ifconfig输出中’error’和’frame’字段的非零计数&#xff1a; eth2 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx ... RX packets:277593775 errors:1049 dropped:0 overruns:0 frame:536 在某处有详细描述’错误…

PCIe_TX/RX测试步骤

TX测试 一: GEN1、2、3采用自动化测试&#xff1a; 1. 点击infinum软件中Analyze->Automated Test Apps->D9050PCIC PCIExpress Gen5 Test App&#xff0c;进入自动化测试界面&#xff1b; 2. Set Up窗口&#xff1a;PCIE5.0->CEM-End Point Tests->Device Defi…

摄影小白入门相机选择(个人出发)

1.微单与卡片机 在产品质量上&#xff0c;相机的感光器件CMOS这些&#xff0c;可以一概认为&#xff0c;同价同质。 两者的区别主要在镜头的设计&#xff0c;黑卡被设计为不可更换镜头式无反相机&#xff0c;入门级别的一般搭配标准变焦镜头 如16-55这种焦距&#xff0c;旗舰级…

无线路由器wifi TX/RX率和实际吞吐量的区别

无意中发现无线路由器中 wifi TX/RX率都超过100M bit/s&#xff0c;但是电脑实际上10M bit/s都没有。网上查到两个解释&#xff1a; “The TX/RX rate is the rate that "the radios talk to each other" ! It is NOT the same as the speed of the data (Etherne…

dpdk 问题分析:ice 100G 网卡 rx_packets 与 rx_bytes 统计问题

问题描述 1. ipackets 减掉 imissed 统计的问题 ice 100G 网卡获取的 ipackets 会减掉 imissed 的报文&#xff0c;此时统计的是软件实际从网卡收包的 pps&#xff0c;但收包的 bps 中没有减掉这部分报文长度且硬件不能提供这部分信息用于计算&#xff0c;为此屏蔽 ipackets …

索尼rx1r人脸识别_RX1 vs RX1R 索尼黑卡新老机皇画面解析力内战

现在的相机市场开始往微单的方向转移,以往的卡片机市场开始缩小,但却开始往高端卡片机发展了。索尼的RX1就是这样一款一体化全幅数码相机,时隔半年,RX1的小改款RX1R也跟着登场。两者在硬件配置上没有什么不同,不过,两者的成像是否也完全一致呢?日本媒体近日对两台机器进…

Verilog 实现 UART RX 接收器

目录 1、简述 2、设计 3、实现 4、测试 1、简述 串口作为 CPU 最常使用的外设资源之一&#xff0c;常常出现在各种场合&#xff0c;既然最近在入坑 FPGA&#xff0c;那么先搞一个简单的串口接收机来玩玩&#xff1b; 串口相关的基本知识就不在这里重复议论了&#xff0c;…

索尼黑卡相机Sony Rx100-M3与手机互联APP相关问题

虽然题目里面有黑卡&#xff0c;但是这个APP针对具有互联功能的索尼相机都是通用的。 索尼相机拍照可以通过相机的WiFi热点直接发送给手机&#xff0c;方便手机再加工或上传社交网络。也可以使用该APP用手机遥控相机&#xff0c;还可以控制一些拍摄参数。 但是索尼这个APP做的…