diff options
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 66 |
1 files changed, 45 insertions, 21 deletions
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 75e30c6a7d5d..5306a1a5b873 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -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)) { |