Browse Source

Implement functionality to read time from hwclock

Helmut Pozimski 5 years ago
parent
commit
4e4e5159c8
1 changed files with 89 additions and 16 deletions
  1. 89 16
      rtc-ds13307.c

+ 89 - 16
rtc-ds13307.c

@@ -11,26 +11,37 @@
 #include <linux/module.h>
 #include <linux/rtc.h>
 #include <linux/i2c.h>
+#include <linux/bcd.h>
 
 #define M_NAME "rtc-ds13307" /* Module name */
 
 #define DEVICE_DS1307 1
 #define DEVICE_DS1337 2
 
-#define DS1307_SEC 0x00
+#define COMMON_SEC 0x00
+#define COMMON_MIN 0x01
+#define COMMON_HOUR 0x02
+#define COMMON_DAY 0x03
+#define COMMON_DATE 0x04
+#define COMMON_MONTH 0x05
+#define COMMON_YEAR 0x06
+#define COMMON_HIGH_BIT 0x80
+
 #define DS1307_MAX_ADDR 0x3F
 
+#define DS1337_HOUR 0x02
+#define DS1337_DATE 0x03
+
 #define DS1337_CTL 0x0E
 #define DS1337_STAT 0x0F
 
 static int model_detected;
 
-static const struct rtc_class_ops ds13307_rtc_ops = {
-};
+static struct i2c_driver ds13307_driver;
 
-/* Reads a single byte via i2c, returns 0 on success */
-static int ds13307_read_single_byte(struct i2c_client *client,
-		unsigned char *addr, u8 *byte) {
+/* Reads a specified number of bytes via i2c, returns 0 on success */
+static int ds13307_read_bytes(struct i2c_client *client,
+		unsigned char *addr, u8 *byte, int length) {
 	int r;
 	struct i2c_msg msgs[] = {
 		{
@@ -41,7 +52,7 @@ static int ds13307_read_single_byte(struct i2c_client *client,
 		{
 			.addr = client->addr,
 			.flags = I2C_M_RD,
-			.len = 1,
+			.len = length,
 			.buf = byte
 		}
 	};
@@ -53,6 +64,57 @@ static int ds13307_read_single_byte(struct i2c_client *client,
 	}
 }
 
+
+static int ds13307_read_time(struct device *dev, struct rtc_time *time) {
+	struct i2c_client *client;
+	int r;
+	int h12 = 0;
+	u8 buf[8], stopbit;
+	unsigned char addr = COMMON_SEC;
+	client = to_i2c_client(dev);
+	r = ds13307_read_bytes(client, &addr, buf, 8);
+	if (model_detected == DEVICE_DS1337) {
+		addr = DS1337_STAT;
+		r = ds13307_read_bytes(client, &addr, &stopbit, 1);
+		stopbit = stopbit & COMMON_HIGH_BIT;
+	} else {
+		stopbit = buf[COMMON_SEC] & 0x40;
+	}
+	if (r) {
+		return -EIO;
+	}
+	if (stopbit) {
+		printk(KERN_ERR "%s: Oscillator stop bit is set, values read from rtc device cannot be trusted\n", M_NAME);
+		return -EINVAL;
+	}
+	if (buf[COMMON_HOUR] & 0x40) {
+		buf[COMMON_HOUR] = buf[COMMON_HOUR] ^ 0x40;
+		if (buf[COMMON_HOUR] & 0x20) {
+			buf[COMMON_HOUR] = buf[COMMON_HOUR] ^ 0x20;
+			h12 = 1;
+		}
+	}
+	if (model_detected == DEVICE_DS1337) {
+		buf[COMMON_MONTH] = buf[COMMON_MONTH] ^ COMMON_HIGH_BIT;
+	}
+
+	time->tm_sec = bcd2bin(buf[COMMON_SEC]);
+	time->tm_min = bcd2bin(buf[COMMON_MIN]);
+	time->tm_hour = bcd2bin(buf[COMMON_HOUR]);
+	if(h12) {
+		time->tm_hour += 12;
+	}
+	time->tm_mday = bcd2bin(buf[COMMON_DATE]);
+	time->tm_wday = bcd2bin(buf[COMMON_DAY]);
+	time->tm_mon = bcd2bin(buf[COMMON_MONTH]);
+	time->tm_year = bcd2bin(buf[COMMON_YEAR]) + 100;
+	return 0;
+}
+
+static const struct rtc_class_ops ds13307_rtc_ops = {
+	.read_time = ds13307_read_time,
+};
+
 /* Performs the device detection to distinguish between the
  * DS1307 and DS1337 chips. Returns the defined device ID or
  * -1 on failure
@@ -61,13 +123,13 @@ static int ds13307_detect_device(struct i2c_client *client) {
 	u8 data;
 	int result;
 	unsigned char addr = DS1307_MAX_ADDR;
-	result = ds13307_read_single_byte(client, &addr, &data);
+	result = ds13307_read_bytes(client, &addr, &data, 1);
 	if (!result) {
 		printk(KERN_INFO "%s: Detected device DS1307\n", M_NAME);
 		return DEVICE_DS1307;
 	}
         addr = DS1337_CTL;
-	result = ds13307_read_single_byte(client, &addr, &data);
+	result = ds13307_read_bytes(client, &addr, &data, 1);
 	if (!result) {	
 		printk(KERN_INFO "%s: Detected device DS1337\n", M_NAME);
  		return DEVICE_DS1337;
@@ -86,26 +148,30 @@ static int ds13307_start_oscillator(struct i2c_client *client) {
        	unsigned char addr;
 	char buf[2];
 	if (model_detected == DEVICE_DS1307) {
-		addr = DS1307_SEC;
+		addr = COMMON_SEC;
 	} else {
 		addr = DS1337_STAT;
 	}
-	r = ds13307_read_single_byte(client, &addr, &data);
-	if (data & 0x80) {
+	r = ds13307_read_bytes(client, &addr, &data, 1);
+	if (data & COMMON_HIGH_BIT) {
 		buf[0] = addr;
-		buf[1] = data ^ 0x80;
+		buf[1] = data ^ COMMON_HIGH_BIT;
 		v = i2c_master_send(client, buf, 2);
+		if (v == 2) {
+			printk(KERN_DEBUG "%s: oscillator stop bit successfully cleared\n", M_NAME);
+		}
 	} else {
 		v = 2;
 	}
 	if ((r<0) || (v!=2)) {
 		return -EIO;
-	}
+	} 
 	return 0;	
 }
 
 static int ds13307_probe(struct i2c_client *client,
 		const struct i2c_device_id *id) {
+	struct rtc_device *rtc;
 	model_detected = ds13307_detect_device(client);
 	if ((model_detected != DEVICE_DS1307) && (model_detected != DEVICE_DS1337)) {
 		return -EIO;
@@ -113,9 +179,15 @@ static int ds13307_probe(struct i2c_client *client,
 	if (ds13307_start_oscillator(client)) {
 		printk(KERN_ERR "%s: failed to initialize the oscillator\n", M_NAME);
 		return -EIO;
-	} else {
-		printk(KERN_DEBUG "%s: oscillator stop bit successfully cleared\n", M_NAME);
+	} 
+	rtc = devm_rtc_device_register(&client->dev, ds13307_driver.driver.name,
+		&ds13307_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(rtc)) {
+		return PTR_ERR(rtc);
 	}
+
+	i2c_set_clientdata(client,rtc);
 	return 0;
 }
 
@@ -134,6 +206,7 @@ static struct i2c_driver ds13307_driver = {
 	.driver = {
 		.name = "rtc-ds13307",
 		.of_match_table = of_match_ptr(ds13307_of_match),
+		.owner = THIS_MODULE
 	},
 	.id_table = ds13307_idtable,
 	.probe = ds13307_probe,