工作上用到两个系统,一个是4418的android系统,一个是rk3399的android字体,都要移植国产大芯片:sd3078 rtc始终芯片的驱动,由于两个系统年代不一样,s5p4418的内核依然用到板级配置文件。rk3399的系统比较新,肯定用到设备树了,由于两个版本有点差异,现在做对比,写博客记录下来。先声明,这个两个现在一直用,非常稳定,当然博客尽量写得详细,有疏漏之处欢迎提出和指点。
下面是整个驱动最核心部分源码:
#define SD307X_REG_SECONDS 0x00
#define SD307X_REG_MINUTES 0x01
#define SD307X_REG_HOURS 0x02
#define SD307X_REG_AMPM 0x02
#define SD307X_REG_DAY 0x03
#define SD307X_REG_DATE 0x04
#define SD307X_REG_MONTH 0x05
#define SD307X_REG_CENTURY 0x05
#define SD307X_REG_YEAR 0x06
#define SD307X_REG_ALARM1 0x07 /* Alarm 1 BASE */
#define SD307X_REG_ALARM2 0x0B /* Alarm 2 BASE */
#define SD307X_REG_CR 0x0E /* Control register */
# define SD307X_REG_CR_nEOSC 0x80
# define SD307X_REG_CR_INTCN 0x04
# define SD307X_REG_CR_A2IE 0x02
# define SD307X_REG_CR_A1IE 0x01
#define SD307X_REG_SR 0x0F /* control/status register */
# define SD307X_REG_SR_OSF 0x80
# define SD307X_REG_SR_BSY 0x04
# define SD307X_REG_SR_A2F 0x02
# define SD307X_REG_SR_A1F 0x01
struct sd307x {
struct i2c_client *client;
struct rtc_device *rtc;
struct work_struct work;
/* The mutex protects alarm operations, and prevents a race
* between the enable_irq() in the workqueue and the free_irq()
* in the remove function.
*/
struct mutex mutex;
int exiting;
};
int sd307x_write_on(struct i2c_client *client)
{
int ret;
ret = i2c_smbus_write_byte_data(client, 0x10, 0x80);
if(0 == ret){
ret = i2c_smbus_write_byte_data(client, 0x0f, 0xff);
}
//printk("sd307x_write_on %s.\n", 0==ret? "success": "failed");
return ret;
}
int sd307x_write_off(struct i2c_client *client)
{
int ret;
ret = i2c_smbus_write_byte_data(client, 0x0f, 0x7b);
if(0 == ret){
ret = i2c_smbus_write_byte_data(client, 0x10, 0);
}
//printk("sd307x_write_off %s.\n", 0==ret? "success": "failed");
return ret;
}
static int sd307x_read_time(struct device *dev, struct rtc_time *time)
{
struct i2c_client *client = to_i2c_client(dev);
int ret;
u8 buf[7];
unsigned int year, month, day, hour, minute, second;
unsigned int week, twelve_hr, am_pm;
unsigned int century, add_century = 0;
//printk("sd307x_read_time.............\n");
ret = i2c_smbus_read_i2c_block_data(client, SD307X_REG_SECONDS, 7, buf);
if (ret < 0)
return ret;
if (ret < 7)
return -EIO;
second = buf[0];
minute = buf[1];
hour = buf[2];
week = buf[3];
day = buf[4];
month = buf[5];
year = buf[6];
/* Extract additional information for AM/PM and century */
twelve_hr = hour & 0x80; //0--12 1--24
am_pm = hour & 0x20; //0--am 1--pm
century = 1;//month & 0x80;
/* Write to rtc_time structure */
time->tm_sec = bcd2bin(second);
time->tm_min = bcd2bin(minute);
if (twelve_hr) {
/* Convert to 24 hr */
if (am_pm)
time->tm_hour = bcd2bin(hour & 0x1F) + 20;
else
time->tm_hour = bcd2bin(hour & 0x1F);
} else {
time->tm_hour = bcd2bin(hour);
}
/* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
time->tm_wday = bcd2bin(week) - 1;
time->tm_mday = bcd2bin(day);
/* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
time->tm_mon = bcd2bin(month & 0x7F) - 1;
if (century)
add_century = 100;
time->tm_year = bcd2bin(year) + add_century;
/*printk("%d-%02d-%02d week %d %02d:%02d:%02d.\n", time->tm_year, time->tm_mon, time->tm_mday, time->tm_wday,
time->tm_hour, time->tm_min, time->tm_sec);*/
return rtc_valid_tm(time);
}
static int sd307x_set_time(struct device *dev, struct rtc_time *time)
{
int ret;
struct i2c_client *client = to_i2c_client(dev);
u8 buf[7];
/* Extract time from rtc_time and load into ds3232*/
/*printk("%d-%02d-%02d week %d %02d:%02d:%02d.\n", time->tm_year, time->tm_mon, time->tm_mday, time->tm_wday,
time->tm_hour, time->tm_min, time->tm_sec);*/
buf[0] = bin2bcd(time->tm_sec);
buf[1] = bin2bcd(time->tm_min);
buf[2] = bin2bcd(time->tm_hour) | 0x80;
/* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
buf[3] = bin2bcd(time->tm_wday + 1);
buf[4] = bin2bcd(time->tm_mday); /* Date */
/* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
buf[5] = bin2bcd(time->tm_mon + 1);
if (time->tm_year >= 100) {
buf[5] |= 0x80;
buf[6] = bin2bcd(time->tm_year - 100);
} else {
buf[6] = bin2bcd(time->tm_year);
}
sd307x_write_on(client);
ret = i2c_smbus_write_i2c_block_data(client, SD307X_REG_SECONDS, 7, buf);
sd307x_write_off(client);
return ret;
}
/*
* DS3232 has two alarm, we only use alarm1
* According to linux specification, only support one-shot alarm
* no periodic alarm mode
*/
static int sd307x_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct i2c_client *client = to_i2c_client(dev);
struct sd307x *sd307x = i2c_get_clientdata(client);
int control, stat;
int ret;
u8 buf[4];
mutex_lock(&sd307x->mutex);
ret = i2c_smbus_read_byte_data(client, SD307X_REG_SR);
if (ret < 0)
goto out;
stat = ret;
ret = i2c_smbus_read_byte_data(client, SD307X_REG_CR);
if (ret < 0)
goto out;
control = ret;
ret = i2c_smbus_read_i2c_block_data(client, SD307X_REG_ALARM1, 4, buf);
if (ret < 0)
goto out;
alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F);
alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F);
alarm->time.tm_mon = -1;
alarm->time.tm_year = -1;
alarm->time.tm_wday = -1;
alarm->time.tm_yday = -1;
alarm->time.tm_isdst = -1;
alarm->enabled = !!(control & SD307X_REG_CR_A1IE);
alarm->pending = !!(stat & SD307X_REG_SR_A1F);
ret = 0;
out:
mutex_unlock(&sd307x->mutex);
return ret;
}
/*
* linux rtc-module does not support wday alarm
* and only 24h time mode supported indeed
*/
static int sd307x_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct i2c_client *client = to_i2c_client(dev);
struct sd307x *sd307x = i2c_get_clientdata(client);
int control, stat;
int ret;
u8 buf[4];
if (client->irq <= 0)
return -EINVAL;
mutex_lock(&sd307x->mutex);
buf[0] = bin2bcd(alarm->time.tm_sec);
buf[1] = bin2bcd(alarm->time.tm_min);
buf[2] = bin2bcd(alarm->time.tm_hour);
buf[3] = bin2bcd(alarm->time.tm_mday);
/* clear alarm interrupt enable bit */
ret = i2c_smbus_read_byte_data(client, SD307X_REG_CR);
if (ret < 0)
goto out;
control = ret;
control &= ~(SD307X_REG_CR_A1IE | SD307X_REG_CR_A2IE);
ret = i2c_smbus_write_byte_data(client, SD307X_REG_CR, control);
if (ret < 0)
goto out;
/* clear any pending alarm flag */
ret = i2c_smbus_read_byte_data(client, SD307X_REG_SR);
if (ret < 0)
goto out;
stat = ret;
stat &= ~(SD307X_REG_SR_A1F | SD307X_REG_SR_A2F);
ret = i2c_smbus_write_byte_data(client, SD307X_REG_SR, stat);
if (ret < 0)
goto out;
ret = i2c_smbus_write_i2c_block_data(client, SD307X_REG_ALARM1, 4, buf);
if (alarm->enabled) {
control |= SD307X_REG_CR_A1IE;
ret = i2c_smbus_write_byte_data(client, SD307X_REG_CR, control);
}
out:
mutex_unlock(&sd307x->mutex);
return ret;
}
static const struct rtc_class_ops sd307x_rtc_ops = {
.read_time = sd307x_read_time,
.set_time = sd307x_set_time,
.read_alarm = sd307x_read_alarm,
.set_alarm = sd307x_set_alarm,
//.alarm_irq_enable = sd307x_alarm_irq_enable,
};
static int __devinit sd307x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sd307x *sd307x;
int ret;
u8 buf_sec[7];
sd307x = kzalloc(sizeof(struct sd307x), GFP_KERNEL);
if (!sd307x)
return -ENOMEM;
sd307x->client = client;
i2c_set_clientdata(client, sd307x);
//INIT_WORK(&sd307x->work, sd307x_work);
mutex_init(&sd307x->mutex);
//ret = sd307x_check_rtc_status(client);
//if (ret)
// goto out_free;
/* cpu init code should really have flagged this device as
* being wake-capable; if it didn't, do that here.
*/
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev, 1);
sd307x->rtc = rtc_device_register(client->name, &client->dev,
&sd307x_rtc_ops, THIS_MODULE);
if (IS_ERR(sd307x->rtc)) {
ret = PTR_ERR(sd307x->rtc);
dev_err(&client->dev, "unable to register the class device\n");
goto out_irq;
}
/*if (client->irq >= 0) {
ret = request_irq(client->irq, sd307x_irq, 0,
"sd307x", client);
if (ret) {
dev_err(&client->dev, "unable to request IRQ\n");
goto out_free;
}
}*/
i2c_smbus_read_i2c_block_data(client, SD307X_REG_SECONDS, 7, buf_sec);
//printk("rtc-sd307x probe success.\n");
return 0;
out_irq:
if (client->irq >= 0)
free_irq(client->irq, client);
out_free:
kfree(sd307x);
return ret;
}
点击传送:https://download.csdn.net/download/mynameislinduan/12456323
这个是现成的直接可以编译的。带设备树,和不带设备树的,两个版本都有。