aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
authorSebastian Reichel <sebastian.reichel@collabora.com>2020-09-02 19:31:39 +0200
committerStefano Babic <sbabic@denx.de>2020-11-01 15:58:47 +0100
commit8691198d1b859a89c3f1c404730134962136b713 (patch)
tree741c7f991edad49d9a15d2e0cd99905bcda2531d /drivers/rtc
parentcfe894967718bc669574eaafcb5b38f313567fe0 (diff)
downloadu-boot-8691198d1b859a89c3f1c404730134962136b713.zip
u-boot-8691198d1b859a89c3f1c404730134962136b713.tar.gz
u-boot-8691198d1b859a89c3f1c404730134962136b713.tar.bz2
rtc: m41t62: reset SQW in m41t62_rtc_reset
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.reichel@collabora.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/m41t62.c89
1 files changed, 83 insertions, 6 deletions
diff --git a/drivers/rtc/m41t62.c b/drivers/rtc/m41t62.c
index 94a6b52..85d6eb0 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 = {