SD3078的linux驱动andorid例子以及源码,实测稳定的版本,详细源码和说明和使用方法

linux/android rtc驱动(sd3078/sd307x)编写和说明记录

  • SD3078的linux驱动andorid例子以及源码
    • 驱动的核心源码:
    • 结束,如果需需要下载直接现成可用的源码,请到下面链接去下载:

SD3078的linux驱动andorid例子以及源码

工作上用到两个系统,一个是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

这个是现成的直接可以编译的。带设备树,和不带设备树的,两个版本都有。

你可能感兴趣的:(技术心得以及事项)