rtc: rv3028: Mirror 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 as well (if something has
changed).

See: https://github.com/raspberrypi/linux/issues/2912

Signed-off-by: Phil Elwell <phil@raspberrypi.com>
This commit is contained in:
Phil Elwell
2020-05-15 16:28:32 +01:00
parent 355bbe0f81
commit 3af3e39f2a

View File

@@ -19,6 +19,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/rtc.h> #include <linux/rtc.h>
//#include "rtc-core.h"
#define RV3028_SEC 0x00 #define RV3028_SEC 0x00
#define RV3028_MIN 0x01 #define RV3028_MIN 0x01
@@ -80,7 +81,7 @@
#define RV3028_BACKUP_TCE BIT(5) #define RV3028_BACKUP_TCE BIT(5)
#define RV3028_BACKUP_TCR_MASK GENMASK(1,0) #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 #define OFFSET_STEP_PPT 953674
@@ -790,7 +791,8 @@ static int rv3028_probe(struct i2c_client *client)
struct rv3028_data *rv3028; struct rv3028_data *rv3028;
int ret, status; int ret, status;
u32 ohms; u32 ohms;
u8 bsm; u32 bsm;
u8 backup, backup_bits, backup_mask;
struct nvmem_config nvmem_cfg = { struct nvmem_config nvmem_cfg = {
.name = "rv3028_nvram", .name = "rv3028_nvram",
.word_size = 1, .word_size = 1,
@@ -857,16 +859,16 @@ static int rv3028_probe(struct i2c_client *client)
if (ret) if (ret)
return ret; return ret;
backup_bits = 0;
backup_mask = 0;
/* setup backup switchover mode */ /* setup backup switchover mode */
if (!device_property_read_u8(&client->dev, "backup-switchover-mode", if (!device_property_read_u32(&client->dev,
"backup-switchover-mode",
&bsm)) { &bsm)) {
if (bsm <= 3) { if (bsm <= 3) {
ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, backup_bits |= (u8)(bsm << 2);
RV3028_BACKUP_BSM_MASK, backup_mask |= RV3028_BACKUP_BSM_MASK;
(bsm & 0x03) << 2);
if (ret)
return ret;
} else { } else {
dev_warn(&client->dev, "invalid backup switchover mode value\n"); dev_warn(&client->dev, "invalid backup switchover mode value\n");
} }
@@ -882,13 +884,32 @@ static int rv3028_probe(struct i2c_client *client)
break; break;
if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { if (i < ARRAY_SIZE(rv3028_trickle_resistors)) {
ret = rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_TCE | backup_bits |= RV3028_BACKUP_TCE | i;
RV3028_BACKUP_TCR_MASK, RV3028_BACKUP_TCE | i); backup_mask |= RV3028_BACKUP_TCE |
RV3028_BACKUP_TCR_MASK;
} else {
dev_warn(&client->dev,
"invalid trickle resistor value\n");
}
}
if (backup_mask) {
ret = rv3028_eeprom_read((void *)(rv3028->regmap),
RV3028_BACKUP,
(void *)&backup, 1);
/* Write register and EEPROM if needed */
if (!ret && (backup & backup_mask) != backup_bits) {
backup = (backup & ~backup_mask) | backup_bits;
ret = rv3028_update_cfg(rv3028, RV3028_BACKUP,
backup_mask, backup_bits);
}
/* In the event of an EEPROM failure, just update the register */
if (ret)
ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
backup_mask, backup_bits);
if (ret) if (ret)
return ret; return ret;
} else {
dev_warn(&client->dev, "invalid trickle resistor value\n");
}
} }
ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group); ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group);