From ffc137a8949cd7859bdf139fa7a56b9dbdb4b2ce Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 15 May 2020 16:28:32 +0100 Subject: [PATCH] rtc: rv3028: Write BSM and TCE/TCR to EEPROM Periodically the RV3028 refreshes registers from the EEPROM. When this happens, some settings that have only been committed to registers are lost. Change the handling of backup-switchover-mode and trickle-resistor-ohms to write the EEPROM instead (if something has changed), on the understanding that registers will be refreshed afterwards. See: https://github.com/raspberrypi/linux/issues/2912 Signed-off-by: Phil Elwell --- drivers/rtc/rtc-rv3028.c | 60 ++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 17 deletions(-) --- a/drivers/rtc/rtc-rv3028.c +++ b/drivers/rtc/rtc-rv3028.c @@ -18,6 +18,7 @@ #include #include #include +//#include "rtc-core.h" #define RV3028_SEC 0x00 #define RV3028_MIN 0x01 @@ -73,7 +74,7 @@ #define RV3028_BACKUP_TCE BIT(5) #define RV3028_BACKUP_TCR_MASK GENMASK(1,0) -#define RV3028_BACKUP_BSM_MASK 0x0C +#define RV3028_BACKUP_BSM_MASK GENMASK(3,2) #define OFFSET_STEP_PPT 953674 @@ -601,7 +602,8 @@ static int rv3028_probe(struct i2c_clien struct rv3028_data *rv3028; int ret, status; u32 ohms; - u8 bsm; + u32 bsm; + u8 backup, backup_bits, backup_mask; struct nvmem_config nvmem_cfg = { .name = "rv3028_nvram", .word_size = 1, @@ -673,16 +675,17 @@ static int rv3028_probe(struct i2c_clien if (ret) return ret; + backup_bits = 0; + backup_mask = 0; + /* setup backup switchover mode */ - if (!device_property_read_u8(&client->dev, "backup-switchover-mode", - &bsm)) { + dev_dbg(&client->dev, "Checking RTC backup switchover-mode\n"); + if (!device_property_read_u32(&client->dev, + "backup-switchover-mode", + &bsm)) { if (bsm <= 3) { - ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, - RV3028_BACKUP_BSM_MASK, - (bsm & 0x03) << 2); - - if (ret) - return ret; + backup_bits |= (u8)(bsm << 2); + backup_mask |= RV3028_BACKUP_BSM_MASK; } else { dev_warn(&client->dev, "invalid backup switchover mode value\n"); } @@ -698,15 +701,38 @@ static int rv3028_probe(struct i2c_clien break; if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { - ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, - RV3028_BACKUP_TCE | - RV3028_BACKUP_TCR_MASK, - RV3028_BACKUP_TCE | i); - if (ret) - return ret; + backup_bits |= RV3028_BACKUP_TCE | i; + backup_mask |= RV3028_BACKUP_TCE | + RV3028_BACKUP_TCR_MASK; } else { - dev_warn(&client->dev, "invalid trickle resistor value\n"); + dev_warn(&client->dev, + "invalid trickle resistor value\n"); + } + } + + if (backup_mask) { + ret = rv3028_eeprom_read((void *)(rv3028->regmap), + RV3028_BACKUP, + (void *)&backup, 1); + if (!ret) { + /* Write EEPROM only if needed */ + if ((backup & backup_mask) != backup_bits) { + backup = (backup & ~backup_mask) | backup_bits; + dev_dbg(&client->dev, + "Backup register doesn't match: EEPROM write required\n"); + ret = rv3028_eeprom_write( + (void *)(rv3028->regmap), + RV3028_BACKUP, (void *)&backup, 1); + } } + + /* In the event of an EEPROM failure, update the register + instead. */ + if (ret) + ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, + backup_mask, backup_bits); + if (ret) + return ret; } ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group);