This takes care of resetting the 32kHz square wave, which is used by some boards as clock source for the SoC.
Signed-off-by: Sebastian Reichel <sebastian.reic...@collabora.com> --- drivers/rtc/m41t62.c | 89 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/m41t62.c b/drivers/rtc/m41t62.c index 94a6b523aab3..85d6eb02593f 100644 --- a/drivers/rtc/m41t62.c +++ b/drivers/rtc/m41t62.c @@ -22,6 +22,7 @@ #include <log.h> #include <rtc.h> #include <i2c.h> +#include <linux/log2.h> #define M41T62_REG_SSEC 0 #define M41T62_REG_SEC 1 @@ -49,6 +50,11 @@ #define M41T62_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */ #define M41T62_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */ +#define M41T62_WDAY_SQW_FREQ_MASK 0xf0 +#define M41T62_WDAY_SQW_FREQ_SHIFT 4 + +#define M41T62_SQW_MAX_FREQ 32768 + #define M41T62_FEATURE_HT (1 << 0) #define M41T62_FEATURE_BL (1 << 1) @@ -139,21 +145,92 @@ static int m41t62_rtc_set(struct udevice *dev, const struct rtc_time *tm) return 0; } -static int m41t62_rtc_reset(struct udevice *dev) +static int m41t62_sqw_enable(struct udevice *dev, bool enable) +{ + u8 val; + int ret; + + ret = dm_i2c_read(dev, M41T62_REG_ALARM_MON, &val, sizeof(val)); + if (ret) + return ret; + + if (enable) + val |= M41T62_ALMON_SQWE; + else + val &= ~M41T62_ALMON_SQWE; + + return dm_i2c_write(dev, M41T62_REG_ALARM_MON, &val, sizeof(val)); +} + +static int m41t62_sqw_set_rate(struct udevice *dev, unsigned int rate) +{ + u8 val, newval, sqwrateval; + int ret; + + if (rate >= M41T62_SQW_MAX_FREQ) + sqwrateval = 1; + else if (rate >= M41T62_SQW_MAX_FREQ / 4) + sqwrateval = 2; + else if (rate) + sqwrateval = 15 - ilog2(rate); + + ret = dm_i2c_read(dev, M41T62_REG_WDAY, &val, sizeof(val)); + if (ret) + return ret; + + newval = val; + newval &= ~M41T62_WDAY_SQW_FREQ_MASK; + newval |= (sqwrateval << M41T62_WDAY_SQW_FREQ_SHIFT); + + /* + * Try to avoid writing unchanged values. Writing to this register + * will reset the internal counter pipeline and thus affect system + * time. + */ + if (newval == val) + return 0; + + return dm_i2c_write(dev, M41T62_REG_WDAY, &newval, sizeof(newval)); +} + +static int m41t62_rtc_clear_ht(struct udevice *dev) { u8 val; + int ret; /* * M41T82: Make sure HT (Halt Update) bit is cleared. * This bit is 0 in M41T62 so its save to clear it always. */ - int ret = dm_i2c_read(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); - + ret = dm_i2c_read(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + if (ret) + return ret; val &= ~M41T80_ALHOUR_HT; - ret |= dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val)); + if (ret) + return ret; + + return 0; +} - return ret; +static int m41t62_rtc_reset(struct udevice *dev) +{ + int ret; + + ret = m41t62_rtc_clear_ht(dev); + if (ret) + return ret; + + /* + * Some boards feed the square wave as clock input into + * the SoC. This enables a 32.768kHz square wave, which is + * also the hardware default after power-loss. + */ + ret = m41t62_sqw_set_rate(dev, 32768); + if (ret) + return ret; + return m41t62_sqw_enable(dev, true); } /* @@ -162,7 +239,7 @@ static int m41t62_rtc_reset(struct udevice *dev) */ static int m41t62_rtc_probe(struct udevice *dev) { - return m41t62_rtc_reset(dev); + return m41t62_rtc_clear_ht(dev); } static const struct rtc_ops m41t62_rtc_ops = { -- 2.28.0