]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/rtc/rtc-ds1307.c
rtc-ds1307: oscillator restart for ds13{37,38,39,40}
[linux-2.6-omap-h63xx.git] / drivers / rtc / rtc-ds1307.c
index 75e30c6a7d5d71a4f69d46f95bfad6e02a726983..5306a1a5b873a54cb0937eb569e16057dbe83698 100644 (file)
@@ -52,6 +52,7 @@ I2C_CLIENT_INSMOD;
 /* RTC registers don't differ much, except for the century flag */
 #define DS1307_REG_SECS                0x00    /* 00-59 */
 #      define DS1307_BIT_CH            0x80
+#      define DS1340_BIT_nEOSC         0x80
 #define DS1307_REG_MIN         0x01    /* 00-59 */
 #define DS1307_REG_HOUR                0x02    /* 00-23, or 1-12{am,pm} */
 #      define DS1340_BIT_CENTURY_EN    0x80    /* in REG_HOUR */
@@ -68,7 +69,7 @@ I2C_CLIENT_INSMOD;
  */
 #define DS1307_REG_CONTROL     0x07            /* or ds1338 */
 #      define DS1307_BIT_OUT           0x80
-#      define DS1338_BIT_STOP          0x20
+#      define DS1338_BIT_OSF           0x20
 #      define DS1307_BIT_SQWE          0x10
 #      define DS1307_BIT_RS1           0x02
 #      define DS1307_BIT_RS0           0x01
@@ -84,8 +85,8 @@ I2C_CLIENT_INSMOD;
 #      define DS1340_BIT_FT            0x40
 #      define DS1340_BIT_CALIB_SIGN    0x20
 #      define DS1340_M_CALIBRATION     0x1f
-#define DS1338_REG_FLAG                0x09
-#      define DS1338_BIT_OSF           0x80
+#define DS1340_REG_FLAG                0x09
+#      define DS1340_BIT_OSF           0x80
 #define DS1337_REG_STATUS      0x0f
 #      define DS1337_BIT_OSF           0x80
 #      define DS1337_BIT_A2I           0x02
@@ -215,11 +216,18 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
        tmp = t->tm_year - 100;
        buf[DS1307_REG_YEAR] = BIN2BCD(tmp);
 
-       if (ds1307->type == ds_1337)
+       switch (ds1307->type) {
+       case ds_1337:
+       case ds_1339:
                buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
-       else if (ds1307->type == ds_1340)
+               break;
+       case ds_1340:
                buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
                                | DS1340_BIT_CENTURY;
+               break;
+       default:
+               break;
+       }
 
        ds1307->msg[1].flags = 0;
        ds1307->msg[1].len = 8;
@@ -296,11 +304,10 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
        switch (ds1307->type) {
        case ds_1337:
        case ds_1339:
-               ds1307->type = ds_1337;
-
                ds1307->reg_addr = DS1337_REG_CONTROL;
                ds1307->msg[1].len = 2;
 
+               /* get registers that the "rtc" read below won't read... */
                tmp = i2c_transfer(adapter, ds1307->msg, 2);
                if (tmp != 2) {
                        pr_debug("read error %d\n", tmp);
@@ -311,13 +318,16 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
                ds1307->reg_addr = 0;
                ds1307->msg[1].len = sizeof(ds1307->regs);
 
-               /* oscillator is off; need to turn it on */
-               if ((ds1307->regs[0] & DS1337_BIT_nEOSC)
-                               || (ds1307->regs[1] & DS1337_BIT_OSF)) {
-no_osc_start:
-                       printk(KERN_ERR "no %s oscillator code\n",
-                               chip->name);
-                       goto exit_free;
+               /* oscillator off?  turn it on, so clock can tick. */
+               if (ds1307->regs[0] & DS1337_BIT_nEOSC)
+                       i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
+                               ds1307->regs[0] & ~DS1337_BIT_nEOSC);
+
+               /* oscillator fault?  clear flag, and warn */
+               if (ds1307->regs[1] & DS1337_BIT_OSF) {
+                       i2c_smbus_write_byte_data(client, DS1337_REG_STATUS,
+                               ds1307->regs[1] & ~DS1337_BIT_OSF);
+                       dev_warn(&client->dev, "SET TIME!\n");
                }
                break;
        default:
@@ -340,20 +350,33 @@ read_rtc:
         */
        tmp = ds1307->regs[DS1307_REG_SECS];
        switch (ds1307->type) {
+       case ds_1340:
+               /* FIXME read register with DS1340_BIT_OSF, use that to
+                * trigger the "set time" warning (*after* restarting the
+                * oscillator!) instead of this weaker ds1307/m41t00 test.
+                */
        case ds_1307:
-       case ds_1338:
        case m41t00:
+               /* clock halted?  turn it on, so clock can tick. */
                if (tmp & DS1307_BIT_CH) {
-                       i2c_smbus_write_byte_data(client, 0, 0);
-                       dev_warn(&client->dev,
-                               "oscillator started; SET TIME!\n");
+                       i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+                       dev_warn(&client->dev, "SET TIME!\n");
                        goto read_rtc;
                }
                break;
-       case ds_1340:
-               /* FIXME write code to start the oscillator */
+       case ds_1338:
+               /* clock halted?  turn it on, so clock can tick. */
                if (tmp & DS1307_BIT_CH)
-                       goto no_osc_start;
+                       i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+
+               /* oscillator fault?  clear flag, and warn */
+               if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) {
+                       i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL,
+                                       ds1307->regs[DS1337_REG_CONTROL]
+                                       & ~DS1338_BIT_OSF);
+                       dev_warn(&client->dev, "SET TIME!\n");
+                       goto read_rtc;
+               }
                break;
        default:
                break;
@@ -380,6 +403,7 @@ read_rtc:
         *
         * REVISIT forcing 24 hour mode can prevent multi-master
         * configs from sharing this RTC ... don't do this.
+        * The clock needs to be reset after changing it, too...
         */
        tmp = ds1307->regs[DS1307_REG_HOUR];
        if (tmp & (1 << 6)) {