rtc: ds1307: Fix incorrect clock reset for DS13xx
authorCallum Sinclair <callum.sinclair@alliedtelesis.co.nz>
Tue, 10 Aug 2021 02:51:15 +0000 (14:51 +1200)
committerTom Rini <trini@konsulko.com>
Wed, 1 Sep 2021 23:25:37 +0000 (19:25 -0400)
The ds1307 driver also supports the DS1339 and DS1340.
However, in ds1307_rtc_reset the register writes assume that the chip
is a DS1307. This is evident in the writing of bits SQWE, RS1, RS0 to
the control register. While this applies correctly to the DS1307, on a
DS1340 the control register doesn't contain those bits (instead, the
register is used for clock calibration). By writing these bits the
clock calibration will be changed and the chip can become
non-functional after a reset call.

Signed-off-by: Callum Sinclair <callum.sinclair@alliedtelesis.co.nz>
drivers/rtc/ds1307.c

index 2015ce9..3be97c9 100644 (file)
@@ -43,11 +43,21 @@ enum ds_type {
 
 #define RTC_SEC_BIT_CH         0x80    /* Clock Halt (in Register 0)   */
 
+/* DS1307-specific bits */
 #define RTC_CTL_BIT_RS0                0x01    /* Rate select 0                */
 #define RTC_CTL_BIT_RS1                0x02    /* Rate select 1                */
 #define RTC_CTL_BIT_SQWE       0x10    /* Square Wave Enable           */
 #define RTC_CTL_BIT_OUT                0x80    /* Output Control               */
 
+/* DS1337-specific bits */
+#define DS1337_CTL_BIT_RS1     0x08    /* Rate select 1                */
+#define DS1337_CTL_BIT_RS2     0x10    /* Rate select 2                */
+#define DS1337_CTL_BIT_EOSC    0x80    /* Enable Oscillator            */
+
+/* DS1340-specific bits */
+#define DS1340_SEC_BIT_EOSC    0x80    /* Enable Oscillator            */
+#define DS1340_CTL_BIT_OUT     0x80    /* Output Control               */
+
 /* MCP7941X-specific bits */
 #define MCP7941X_BIT_ST                0x80
 #define MCP7941X_BIT_VBATEN    0x08
@@ -261,9 +271,25 @@ read_rtc:
                                         buf[RTC_SEC_REG_ADDR]);
                        return -1;
                }
-       }
-
-       if (type == m41t11) {
+       } else if (type == ds_1337) {
+               if (buf[RTC_CTL_REG_ADDR] & DS1337_CTL_BIT_EOSC) {
+                       printf("### Warning: RTC oscillator has stopped\n");
+                       /* clear the not oscillator enable (~EOSC) flag */
+                       buf[RTC_CTL_REG_ADDR] &= ~DS1337_CTL_BIT_EOSC;
+                       dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR,
+                                        buf[RTC_CTL_REG_ADDR]);
+                       return -1;
+               }
+       } else if (type == ds_1340) {
+               if (buf[RTC_SEC_REG_ADDR] & DS1340_SEC_BIT_EOSC) {
+                       printf("### Warning: RTC oscillator has stopped\n");
+                       /* clear the not oscillator enable (~EOSC) flag */
+                       buf[RTC_SEC_REG_ADDR] &= ~DS1340_SEC_BIT_EOSC;
+                       dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR,
+                                        buf[RTC_SEC_REG_ADDR]);
+                       return -1;
+               }
+       } else if (type == m41t11) {
                /* clock halted?  turn it on, so clock can tick. */
                if (buf[RTC_SEC_REG_ADDR] & RTC_SEC_BIT_CH) {
                        buf[RTC_SEC_REG_ADDR] &= ~RTC_SEC_BIT_CH;
@@ -273,9 +299,7 @@ read_rtc:
                                         buf[RTC_SEC_REG_ADDR]);
                        goto read_rtc;
                }
-       }
-
-       if (type == mcp794xx) {
+       } else if (type == mcp794xx) {
                /* make sure that the backup battery is enabled */
                if (!(buf[RTC_DAY_REG_ADDR] & MCP7941X_BIT_VBATEN)) {
                        dm_i2c_reg_write(dev, RTC_DAY_REG_ADDR,
@@ -314,18 +338,37 @@ read_rtc:
 static int ds1307_rtc_reset(struct udevice *dev)
 {
        int ret;
+       enum ds_type type = dev_get_driver_data(dev);
 
-       /* clear Clock Halt */
+       /*
+        * reset clock/oscillator in the seconds register:
+        * on DS1307 bit 7 enables Clock Halt (CH),
+        * on DS1340 bit 7 disables the oscillator (not EOSC)
+        * on MCP794xx bit 7 enables Start Oscillator (ST)
+        */
        ret = dm_i2c_reg_write(dev, RTC_SEC_REG_ADDR, 0x00);
        if (ret < 0)
                return ret;
-       ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR,
-                              RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 |
-                              RTC_CTL_BIT_RS0);
-       if (ret < 0)
-               return ret;
 
-       return 0;
+       if (type == ds_1307) {
+               /* Write control register in order to enable square-wave
+                * output (SQWE) and set a default rate of 32.768kHz (RS1|RS0).
+                */
+               ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR,
+                                      RTC_CTL_BIT_SQWE | RTC_CTL_BIT_RS1 |
+                                      RTC_CTL_BIT_RS0);
+       } else if (type == ds_1337) {
+               /* Write control register in order to enable oscillator output
+                * (not EOSC) and set a default rate of 32.768kHz (RS2|RS1).
+                */
+               ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR,
+                                      DS1337_CTL_BIT_RS2 | DS1337_CTL_BIT_RS1);
+       } else if (type == ds_1340 || type == mcp794xx || type == m41t11) {
+               /* Reset clock calibration, frequency test and output level. */
+               ret = dm_i2c_reg_write(dev, RTC_CTL_REG_ADDR, 0x00);
+       }
+
+       return ret;
 }
 
 static int ds1307_probe(struct udevice *dev)