* [PATCH v3 1/6] dt-bindings: rtc: Add ST m41t93
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
@ 2025-09-20 15:01 ` Akhilesh Patil
2025-09-22 20:27 ` Rob Herring (Arm)
2025-09-20 15:01 ` [PATCH v3 2/6] rtc: m41t93: add device tree support Akhilesh Patil
` (5 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Akhilesh Patil @ 2025-09-20 15:01 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
Document DT bindings for m41t93 rtc which supports time, date,
alarm, watchdog, square wave clock output provider, user sram
and 8 bit timer.
Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
---
.../devicetree/bindings/rtc/st,m41t93.yaml | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/st,m41t93.yaml
diff --git a/Documentation/devicetree/bindings/rtc/st,m41t93.yaml b/Documentation/devicetree/bindings/rtc/st,m41t93.yaml
new file mode 100644
index 000000000000..bdd995c5c1f4
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/st,m41t93.yaml
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/st,m41t93.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ST M41T93 RTC and compatible
+
+maintainers:
+ - Akhilesh Patil <akhilesh@ee.iitb.ac.in>
+
+description:
+ ST M41T93 is spi based Real Time Clock (RTC) with time, date,
+ alarm, watchdog, square wave clock output, 8 bit timer and
+ 7 bytes of user SRAM.
+
+properties:
+ compatible:
+ enum:
+ - st,m41t93
+
+ reg:
+ maxItems: 1
+
+ "#clock-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: rtc.yaml
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ rtc@0 {
+ compatible = "st,m41t93";
+ reg = <0>;
+ #clock-cells = <0>;
+ spi-max-frequency = <2000000>;
+ };
+ };
+
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v3 1/6] dt-bindings: rtc: Add ST m41t93
2025-09-20 15:01 ` [PATCH v3 1/6] dt-bindings: rtc: Add ST m41t93 Akhilesh Patil
@ 2025-09-22 20:27 ` Rob Herring (Arm)
0 siblings, 0 replies; 9+ messages in thread
From: Rob Herring (Arm) @ 2025-09-22 20:27 UTC (permalink / raw)
To: Akhilesh Patil
Cc: conor+dt, akhileshpatilvnit, alexandre.belloni, krzk+dt,
linux-rtc, linux-kernel, devicetree, skhan
On Sat, 20 Sep 2025 20:31:24 +0530, Akhilesh Patil wrote:
> Document DT bindings for m41t93 rtc which supports time, date,
> alarm, watchdog, square wave clock output provider, user sram
> and 8 bit timer.
>
> Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
> ---
> .../devicetree/bindings/rtc/st,m41t93.yaml | 50 +++++++++++++++++++
> 1 file changed, 50 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/st,m41t93.yaml
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 2/6] rtc: m41t93: add device tree support
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
2025-09-20 15:01 ` [PATCH v3 1/6] dt-bindings: rtc: Add ST m41t93 Akhilesh Patil
@ 2025-09-20 15:01 ` Akhilesh Patil
2025-09-20 15:02 ` [PATCH v3 3/6] rtc: m41t93: migrate to regmap api for register access Akhilesh Patil
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Akhilesh Patil @ 2025-09-20 15:01 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
Add device tree support for m41t93 rtc by adding of_match_table.
Define compatible string - "st,m41t93" which can be used to instantiate
this rtc device via DT node.
Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
---
drivers/rtc/rtc-m41t93.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index 9444cb5f5190..4e803ff0ce49 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -191,9 +191,16 @@ static int m41t93_probe(struct spi_device *spi)
return 0;
}
+static const struct of_device_id m41t93_dt_match[] = {
+ { .compatible = "st,m41t93" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, m41t93_dt_match);
+
static struct spi_driver m41t93_driver = {
.driver = {
.name = "rtc-m41t93",
+ .of_match_table = m41t93_dt_match,
},
.probe = m41t93_probe,
};
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 3/6] rtc: m41t93: migrate to regmap api for register access
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
2025-09-20 15:01 ` [PATCH v3 1/6] dt-bindings: rtc: Add ST m41t93 Akhilesh Patil
2025-09-20 15:01 ` [PATCH v3 2/6] rtc: m41t93: add device tree support Akhilesh Patil
@ 2025-09-20 15:02 ` Akhilesh Patil
2025-09-20 15:02 ` [PATCH v3 4/6] rtc: m41t93: Add alarm support Akhilesh Patil
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Akhilesh Patil @ 2025-09-20 15:02 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
Adapt driver to use regmap api with spi bus instead of direct spi
subsystem calls to access device registers. Simplify and standardize the
register interactions using more abstract and bus agnostic regmap api
to reduce code duplication and improve maintainability. Define spi regmap
config suitable for m41t93 spi bus protocol to achieve same transactions on
spi bus.
Tested on TI am62x sk board with m41t93 rtc chip connected over spi0.
Validated set and get time using hwclock tool and verified spi bus
transfers using logic analyzer.
Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
---
drivers/rtc/rtc-m41t93.c | 121 ++++++++++++++++++++++-----------------
1 file changed, 70 insertions(+), 51 deletions(-)
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index 4e803ff0ce49..ad862bf706b6 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#define M41T93_REG_SSEC 0
#define M41T93_REG_ST_SEC 1
@@ -31,23 +32,17 @@
#define M41T93_FLAG_BL (1 << 4)
#define M41T93_FLAG_HT (1 << 6)
-static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
-{
- u8 buf[2];
-
- /* MSB must be '1' to write */
- buf[0] = addr | 0x80;
- buf[1] = data;
-
- return spi_write(spi, buf, sizeof(buf));
-}
+struct m41t93_data {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+};
static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
{
- struct spi_device *spi = to_spi_device(dev);
- int tmp;
- u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */
- u8 * const data = &buf[1]; /* ptr to first data byte */
+ struct m41t93_data *m41t93 = dev_get_drvdata(dev);
+ int tmp, ret;
+ u8 buf[8] = {0}; /* 8 data bytes */
+ u8 * const data = &buf[0]; /* ptr to first data byte */
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
@@ -56,31 +51,31 @@ static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
tm->tm_mon, tm->tm_year, tm->tm_wday);
if (tm->tm_year < 100) {
- dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n");
+ dev_warn(dev, "unsupported date (before 2000-01-01).\n");
return -EINVAL;
}
- tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
- if (tmp < 0)
- return tmp;
+ ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp);
+ if (ret < 0)
+ return ret;
if (tmp & M41T93_FLAG_OF) {
- dev_warn(&spi->dev, "OF bit is set, resetting.\n");
- m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
+ dev_warn(dev, "OF bit is set, resetting.\n");
+ regmap_write(m41t93->regmap, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
- tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
- if (tmp < 0) {
- return tmp;
+ ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp);
+ if (ret < 0) {
+ return ret;
} else if (tmp & M41T93_FLAG_OF) {
/* OF cannot be immediately reset: oscillator has to be
* restarted. */
u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
- dev_warn(&spi->dev,
+ dev_warn(dev,
"OF bit is still set, kickstarting clock.\n");
- m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+ regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc);
reset_osc &= ~M41T93_FLAG_ST;
- m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+ regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc);
}
}
@@ -94,14 +89,13 @@ static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1);
data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100);
- return spi_write(spi, buf, sizeof(buf));
+ return regmap_bulk_write(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf));
}
static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
{
- struct spi_device *spi = to_spi_device(dev);
- const u8 start_addr = 0;
+ struct m41t93_data *m41t93 = dev_get_drvdata(dev);
u8 buf[8];
int century_after_1900;
int tmp;
@@ -113,32 +107,32 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
case after poweron. Time is valid after resetting HT bit.
2. oscillator fail bit (OF) is set: time is invalid.
*/
- tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
- if (tmp < 0)
- return tmp;
+ ret = regmap_read(m41t93->regmap, M41T93_REG_ALM_HOUR_HT, &tmp);
+ if (ret < 0)
+ return ret;
if (tmp & M41T93_FLAG_HT) {
- dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n");
- m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT,
- tmp & ~M41T93_FLAG_HT);
+ dev_dbg(dev, "HT bit is set, reenable clock update.\n");
+ regmap_write(m41t93->regmap, M41T93_REG_ALM_HOUR_HT,
+ tmp & ~M41T93_FLAG_HT);
}
- tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
- if (tmp < 0)
- return tmp;
+ ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp);
+ if (ret < 0)
+ return ret;
if (tmp & M41T93_FLAG_OF) {
ret = -EINVAL;
- dev_warn(&spi->dev, "OF bit is set, write time to restart.\n");
+ dev_warn(dev, "OF bit is set, write time to restart.\n");
}
if (tmp & M41T93_FLAG_BL)
- dev_warn(&spi->dev, "BL bit is set, replace battery.\n");
+ dev_warn(dev, "BL bit is set, replace battery.\n");
/* read actual time/date */
- tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf));
- if (tmp < 0)
- return tmp;
+ ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]);
tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]);
@@ -167,26 +161,51 @@ static const struct rtc_class_ops m41t93_rtc_ops = {
static struct spi_driver m41t93_driver;
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = 0x00,
+ .write_flag_mask = 0x80,
+ .zero_flag_mask = true,
+};
+
static int m41t93_probe(struct spi_device *spi)
{
- struct rtc_device *rtc;
- int res;
+ int res, ret;
+ struct m41t93_data *m41t93;
spi->bits_per_word = 8;
spi_setup(spi);
- res = spi_w8r8(spi, M41T93_REG_WDAY);
+ m41t93 = devm_kzalloc(&spi->dev, sizeof(struct m41t93_data), GFP_KERNEL);
+
+ if (!m41t93)
+ return -ENOMEM;
+
+ /* Set up regmap to access device registers*/
+ m41t93->regmap = devm_regmap_init_spi(spi, ®map_config);
+ if (IS_ERR(m41t93->regmap)) {
+ dev_err(&spi->dev, "regmap init failure\n");
+ return PTR_ERR(m41t93->regmap);
+ }
+
+ ret = regmap_read(m41t93->regmap, M41T93_REG_WDAY, &res);
+ if (ret < 0) {
+ dev_err(&spi->dev, "IO error\n");
+ return -EIO;
+ }
+
if (res < 0 || (res & 0xf8) != 0) {
dev_err(&spi->dev, "not found 0x%x.\n", res);
return -ENODEV;
}
- rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name,
- &m41t93_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
+ spi_set_drvdata(spi, m41t93);
- spi_set_drvdata(spi, rtc);
+ m41t93->rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name,
+ &m41t93_rtc_ops, THIS_MODULE);
+ if (IS_ERR(m41t93->rtc))
+ return PTR_ERR(m41t93->rtc);
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 4/6] rtc: m41t93: Add alarm support
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
` (2 preceding siblings ...)
2025-09-20 15:02 ` [PATCH v3 3/6] rtc: m41t93: migrate to regmap api for register access Akhilesh Patil
@ 2025-09-20 15:02 ` Akhilesh Patil
2025-09-20 15:03 ` [PATCH v3 5/6] rtc: m41t93: Add square wave clock provider support Akhilesh Patil
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Akhilesh Patil @ 2025-09-20 15:02 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
Implement alarm feature for rtc-m41t93 by adding necessary
callbacks - set_alarm, read_alarm and alarm_irq_enable.
Enable support to configure alarm 1 out of 2 alarms present in this rtc.
Support only alarm configuration in this commit. This commit does not
implement alarm irq handling.
Use selftests/rtc/rtctest for testing. Tested by observing IRQ pin
(pin 12 of SOX18 package) on logic analyzer going low after alarm
condition is met.
Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
---
drivers/rtc/rtc-m41t93.c | 105 +++++++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index ad862bf706b6..b86e7a12eba6 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -22,6 +22,14 @@
#define M41T93_REG_DAY 5
#define M41T93_REG_MON 6
#define M41T93_REG_YEAR 7
+#define M41T93_REG_AL1_MONTH 0xa
+#define M41T93_REG_AL1_DATE 0xb
+#define M41T93_REG_AL1_HOUR 0xc
+#define M41T93_REG_AL1_MIN 0xd
+#define M41T93_REG_AL1_SEC 0xe
+#define M41T93_BIT_A1IE BIT(7)
+#define M41T93_BIT_ABE BIT(5)
+#define M41T93_FLAG_AF1 BIT(6)
#define M41T93_REG_ALM_HOUR_HT 0xc
@@ -153,10 +161,107 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
return ret;
}
+static int m41t93_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct m41t93_data *m41t93 = dev_get_drvdata(dev);
+ int ret;
+ unsigned int val;
+ u8 alarm_vals[5] = {0};
+
+ ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_DATE, alarm_vals, 4);
+ if (ret)
+ return ret;
+
+ /* Set alarm values */
+ alarm_vals[0] = bin2bcd(alrm->time.tm_mon + 1) & 0x1f;
+ alarm_vals[1] = bin2bcd(alrm->time.tm_mday) & 0x3f;
+ alarm_vals[2] = bin2bcd(alrm->time.tm_hour) & 0x3f;
+ alarm_vals[3] = bin2bcd(alrm->time.tm_min) & 0x7f;
+ alarm_vals[4] = bin2bcd(alrm->time.tm_sec) & 0x7f;
+
+ if (alrm->enabled) {
+ /* Enable alarm IRQ generation */
+ alarm_vals[0] |= M41T93_BIT_A1IE | M41T93_BIT_ABE;
+ }
+
+ /* Preserve SQWE bit */
+ ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val);
+ if (ret)
+ return ret;
+
+ alarm_vals[0] |= val & 0x40;
+
+ ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_MONTH,
+ alarm_vals, sizeof(alarm_vals));
+ if (ret)
+ return ret;
+
+ /* Device address pointer is now at FLAG register, move it to other location
+ * to finish setting alarm, as recommended by the datasheet.
+ * We do read of AL1_MONTH register to achieve this.
+ */
+ ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val);
+ if (ret)
+ return ret;
+
+ if (bcd2bin(val & 0x1f) == (alrm->time.tm_mon & 0x1f))
+ dev_notice(dev, "Alarm set successfully\n");
+
+ return 0;
+}
+
+static int m41t93_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct m41t93_data *m41t93 = dev_get_drvdata(dev);
+ int ret;
+ unsigned int val;
+ u8 alarm_vals[5] = {0};
+
+ ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_AL1_MONTH,
+ alarm_vals, sizeof(alarm_vals));
+ if (ret)
+ return ret;
+
+ alrm->time.tm_mon = bcd2bin(alarm_vals[0] & 0x1f) - 1;
+ alrm->time.tm_mday = bcd2bin(alarm_vals[1] & 0x3f);
+ alrm->time.tm_hour = bcd2bin(alarm_vals[2] & 0x3f);
+ alrm->time.tm_min = bcd2bin(alarm_vals[3] & 0x7f);
+ alrm->time.tm_sec = bcd2bin(alarm_vals[4] & 0x7f);
+
+ alrm->enabled = !!(alarm_vals[0] & M41T93_BIT_A1IE);
+
+ ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &val);
+ if (ret)
+ return ret;
+
+ alrm->pending = (val & M41T93_FLAG_AF1) && alrm->enabled;
+
+ return 0;
+}
+
+static int m41t93_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct m41t93_data *m41t93 = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ val = enabled ? M41T93_BIT_A1IE | M41T93_BIT_ABE : 0;
+
+ ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH,
+ M41T93_BIT_A1IE | M41T93_BIT_ABE, val);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static const struct rtc_class_ops m41t93_rtc_ops = {
.read_time = m41t93_get_time,
.set_time = m41t93_set_time,
+ .set_alarm = m41t93_set_alarm,
+ .read_alarm = m41t93_get_alarm,
+ .alarm_irq_enable = m41t93_alarm_irq_enable,
};
static struct spi_driver m41t93_driver;
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 5/6] rtc: m41t93: Add square wave clock provider support
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
` (3 preceding siblings ...)
2025-09-20 15:02 ` [PATCH v3 4/6] rtc: m41t93: Add alarm support Akhilesh Patil
@ 2025-09-20 15:03 ` Akhilesh Patil
2025-09-20 15:03 ` [PATCH v3 6/6] rtc: m41t93: Add watchdog support Akhilesh Patil
2025-10-02 15:51 ` [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
6 siblings, 0 replies; 9+ messages in thread
From: Akhilesh Patil @ 2025-09-20 15:03 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
Implement support to configure square wave output (SQW) of m41t93 rtc
via common clock framework clock provider api. Add clock provider
callbacks to control output frequency ranging from 1Hz to 32KHz as
supported by this rtc chip.
Use clock framework debugfs interface or clock consumer DT node to test.
Tested by measuring various frequencies on pull-up connected SWQ(7) pin
of m41t93 rtc chip using logic analyzer.
Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
---
drivers/rtc/rtc-m41t93.c | 156 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 155 insertions(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index b86e7a12eba6..46703e32f212 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -13,6 +13,7 @@
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
+#include <linux/clk-provider.h>
#define M41T93_REG_SSEC 0
#define M41T93_REG_ST_SEC 1
@@ -30,7 +31,11 @@
#define M41T93_BIT_A1IE BIT(7)
#define M41T93_BIT_ABE BIT(5)
#define M41T93_FLAG_AF1 BIT(6)
-
+#define M41T93_SRAM_BASE 0x19
+#define M41T93_REG_SQW 0x13
+#define M41T93_SQW_RS_MASK 0xf0
+#define M41T93_SQW_RS_SHIFT 4
+#define M41T93_BIT_SQWE BIT(6)
#define M41T93_REG_ALM_HOUR_HT 0xc
#define M41T93_REG_FLAGS 0xf
@@ -43,6 +48,9 @@
struct m41t93_data {
struct rtc_device *rtc;
struct regmap *regmap;
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw clks;
+#endif
};
static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
@@ -264,6 +272,146 @@ static const struct rtc_class_ops m41t93_rtc_ops = {
.alarm_irq_enable = m41t93_alarm_irq_enable,
};
+#ifdef CONFIG_COMMON_CLK
+#define clk_sqw_to_m41t93_data(clk) \
+ container_of(clk, struct m41t93_data, clks)
+
+/* m41t93 RTC clock output support */
+static unsigned long m41t93_clk_rates[] = {
+ 0,
+ 32768, /* RS3:RS0 = 0b0001 */
+ 8192,
+ 4096,
+ 2048,
+ 1024,
+ 512,
+ 256,
+ 128,
+ 64,
+ 32,
+ 16,
+ 8,
+ 4,
+ 2,
+ 1, /* RS3:RS0 = 0b1111 */
+};
+
+static unsigned long m41t93_clk_sqw_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ int ret;
+ unsigned int rate_id;
+ struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
+
+ ret = regmap_read(m41t93->regmap, M41T93_REG_SQW, &rate_id);
+ if (ret)
+ return ret;
+
+ rate_id &= M41T93_SQW_RS_MASK;
+ rate_id >>= M41T93_SQW_RS_SHIFT;
+
+ return m41t93_clk_rates[rate_id];
+}
+
+static int m41t93_clk_sqw_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(m41t93_clk_rates); i++) {
+ if (req->rate >= m41t93_clk_rates[i]) {
+ req->rate = m41t93_clk_rates[i];
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int m41t93_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ int id, ret;
+ struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
+
+ for (id = 0; id < ARRAY_SIZE(m41t93_clk_rates); id++) {
+ if (m41t93_clk_rates[id] == rate)
+ break;
+ }
+
+ if (id >= ARRAY_SIZE(m41t93_clk_rates))
+ return -EINVAL;
+
+ ret = regmap_update_bits(m41t93->regmap, M41T93_REG_SQW,
+ M41T93_SQW_RS_MASK, id << M41T93_SQW_RS_SHIFT);
+
+ return ret;
+}
+
+static int m41t93_clk_sqw_prepare(struct clk_hw *hw)
+{
+ int ret;
+ struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
+
+ ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH,
+ M41T93_BIT_SQWE, M41T93_BIT_SQWE);
+
+ return ret;
+}
+
+static void m41t93_clk_sqw_unprepare(struct clk_hw *hw)
+{
+ struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
+
+ regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH,
+ M41T93_BIT_SQWE, 0);
+}
+
+static int m41t93_clk_sqw_is_prepared(struct clk_hw *hw)
+{
+ int ret;
+ struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw);
+ unsigned int status;
+
+ ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &status);
+ if (ret)
+ return ret;
+
+ return !!(status & M41T93_BIT_SQWE);
+}
+
+static const struct clk_ops m41t93_clk_sqw_ops = {
+ .prepare = m41t93_clk_sqw_prepare,
+ .unprepare = m41t93_clk_sqw_unprepare,
+ .is_prepared = m41t93_clk_sqw_is_prepared,
+ .recalc_rate = m41t93_clk_sqw_recalc_rate,
+ .set_rate = m41t93_clk_sqw_set_rate,
+ .determine_rate = m41t93_clk_sqw_determine_rate,
+};
+
+static int rtc_m41t93_clks_register(struct device *dev, struct m41t93_data *m41t93)
+{
+ struct device_node *node = dev->of_node;
+ struct clk *clk;
+ struct clk_init_data init = {0};
+
+ init.name = "m41t93_clk_sqw";
+ init.ops = &m41t93_clk_sqw_ops;
+
+ m41t93->clks.init = &init;
+
+ /* Register the clock with CCF */
+ clk = devm_clk_register(dev, &m41t93->clks);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (node)
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ return 0;
+}
+#endif
+
static struct spi_driver m41t93_driver;
static const struct regmap_config regmap_config = {
@@ -312,6 +460,12 @@ static int m41t93_probe(struct spi_device *spi)
if (IS_ERR(m41t93->rtc))
return PTR_ERR(m41t93->rtc);
+#ifdef CONFIG_COMMON_CLK
+ ret = rtc_m41t93_clks_register(&spi->dev, m41t93);
+ if (ret)
+ dev_warn(&spi->dev, "Unable to register clock\n");
+#endif
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 6/6] rtc: m41t93: Add watchdog support
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
` (4 preceding siblings ...)
2025-09-20 15:03 ` [PATCH v3 5/6] rtc: m41t93: Add square wave clock provider support Akhilesh Patil
@ 2025-09-20 15:03 ` Akhilesh Patil
2025-10-02 15:51 ` [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
6 siblings, 0 replies; 9+ messages in thread
From: Akhilesh Patil @ 2025-09-20 15:03 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
Implement watchdog feature for m41t93 rtc with 1s resolution.
Implement alarm only support (WDIOF_ALARMONLY) in this commit.
Define start, stop, ping, and set_timeout callbacks as needed
by the watchdog framework.
Use selftests/watchdog/watchdog-test kselftest for testing.
Observed IRQ pin(12) of rtc chip going low after late pinging
the watchdog.
Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
---
drivers/rtc/rtc-m41t93.c | 99 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index 46703e32f212..483fc6a5ad21 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -14,6 +14,7 @@
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/clk-provider.h>
+#include <linux/watchdog.h>
#define M41T93_REG_SSEC 0
#define M41T93_REG_ST_SEC 1
@@ -36,6 +37,10 @@
#define M41T93_SQW_RS_MASK 0xf0
#define M41T93_SQW_RS_SHIFT 4
#define M41T93_BIT_SQWE BIT(6)
+#define M41T93_REG_WATCHDOG 0x9
+#define M41T93_WDT_RB_MASK 0x3
+#define M41T93_WDT_BMB_MASK 0x7c
+#define M41T93_WDT_BMB_SHIFT 2
#define M41T93_REG_ALM_HOUR_HT 0xc
#define M41T93_REG_FLAGS 0xf
@@ -51,6 +56,9 @@ struct m41t93_data {
#ifdef CONFIG_COMMON_CLK
struct clk_hw clks;
#endif
+#ifdef CONFIG_WATCHDOG
+ struct watchdog_device wdd;
+#endif
};
static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
@@ -412,6 +420,92 @@ static int rtc_m41t93_clks_register(struct device *dev, struct m41t93_data *m41t
}
#endif
+#ifdef CONFIG_WATCHDOG
+static int m41t93_wdt_ping(struct watchdog_device *wdd)
+{
+ u8 resolution, mult;
+ u8 val = 0;
+ int ret;
+ struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd);
+
+ /* Resolution supported by hardware
+ * 0b00 : 1/16 seconds
+ * 0b01 : 1/4 second
+ * 0b10 : 1 second
+ * 0b11 : 4 seconds
+ */
+ resolution = 0x2; /* hardcode resolution to 1s */
+ mult = wdd->timeout;
+ val = resolution | (mult << M41T93_WDT_BMB_SHIFT & M41T93_WDT_BMB_MASK);
+
+ ret = regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG,
+ M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, val);
+
+ return ret;
+}
+
+static int m41t93_wdt_start(struct watchdog_device *wdd)
+{
+ return m41t93_wdt_ping(wdd);
+}
+
+static int m41t93_wdt_stop(struct watchdog_device *wdd)
+{
+ struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd);
+
+ /* Write 0 to watchdog register */
+ return regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG,
+ M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, 0);
+}
+
+static int m41t93_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_timeout)
+{
+ wdd->timeout = new_timeout;
+
+ return 0;
+}
+
+static const struct watchdog_info m41t93_wdt_info = {
+ .identity = "m41t93 rtc Watchdog",
+ .options = WDIOF_ALARMONLY | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops m41t93_watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = m41t93_wdt_start,
+ .stop = m41t93_wdt_stop,
+ .ping = m41t93_wdt_ping,
+ .set_timeout = m41t93_wdt_set_timeout,
+};
+
+static int m41t93_watchdog_register(struct device *dev, struct m41t93_data *m41t93)
+{
+ int ret;
+
+ m41t93->wdd.parent = dev;
+ m41t93->wdd.info = &m41t93_wdt_info;
+ m41t93->wdd.ops = &m41t93_watchdog_ops;
+ m41t93->wdd.min_timeout = 0;
+ m41t93->wdd.max_timeout = 10;
+ m41t93->wdd.timeout = 3; /* Default timeout is 3 sec */
+ m41t93->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+
+ watchdog_set_drvdata(&m41t93->wdd, m41t93);
+
+ ret = devm_watchdog_register_device(dev, &m41t93->wdd);
+ if (ret) {
+ dev_warn(dev, "Failed to register watchdog\n");
+ return ret;
+ }
+
+ /* Disable watchdog at start */
+ ret = m41t93_wdt_stop(&m41t93->wdd);
+
+ return ret;
+}
+#endif
+
static struct spi_driver m41t93_driver;
static const struct regmap_config regmap_config = {
@@ -465,6 +559,11 @@ static int m41t93_probe(struct spi_device *spi)
if (ret)
dev_warn(&spi->dev, "Unable to register clock\n");
#endif
+#ifdef CONFIG_WATCHDOG
+ ret = m41t93_watchdog_register(&spi->dev, m41t93);
+ if (ret)
+ dev_warn(&spi->dev, "Unable to register watchdog\n");
+#endif
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog
2025-09-20 15:00 [PATCH v3 0/6] rtc: m41t93: add new features alarm, clock out, watchdog Akhilesh Patil
` (5 preceding siblings ...)
2025-09-20 15:03 ` [PATCH v3 6/6] rtc: m41t93: Add watchdog support Akhilesh Patil
@ 2025-10-02 15:51 ` Akhilesh Patil
6 siblings, 0 replies; 9+ messages in thread
From: Akhilesh Patil @ 2025-10-02 15:51 UTC (permalink / raw)
To: alexandre.belloni, krzk+dt, robh, conor+dt
Cc: skhan, linux-rtc, devicetree, linux-kernel, akhileshpatilvnit
On Sat, Sep 20, 2025 at 08:30:59PM +0530, Akhilesh Patil wrote:
> This patch series adds following to m41t93 rtc driver.
>
> Functionalities:
> - Alarm support (support to configure alarm 1)
> - Square wave output support
> - Watchdog support
>
> Code improvements:
> this series migrates existing driver to use standard regmap interface
> for spi instead of direct spi calls and uses regmap for new features.
>
> Device tree support:
> Adds device tree support to the driver along with binding documentation.
>
> Testing:
> This patch series is validated on TI am62x board with m41t93 rtc chip
> connected to spi0 bus.
> regmap migration is additionally tested by observing spi transfers
> with the help of logic analyzer. Short summary of test flow is added in
> commit message of respective features.
>
> Datasheet:
> https://www.st.com/resource/en/datasheet/m41t93.pdf
>
> patch 4 to 6 depend on patch 3 (regmap patch)
>
> Signed-off-by: Akhilesh Patil <akhilesh@ee.iitb.ac.in>
> ---
Hi Alexandre, I would like to follow-up on this patch series for review
and feedback for rtc code. Let me know if any additional improvements needed.
I am willing to volunteer to support this driver in the kernel, in case
needed, as a reviewer/maintainer. I have this m41t93 RTC hardware with
me. Given we are in 6.18 merge window, looking forward for
this patch series.
Regards,
Akhilesh
> Changes in v3:
> - Address comments on bindings from Krzysztof and add myself
> as a maintainer.
> - Re-validation/testing on top of v6.17-rc6
> - Link to v2: https://lore.kernel.org/lkml/cover.1757510157.git.akhilesh@ee.iitb.ac.in/
>
> Changes in v2:
> - Address DTS and bindings coding style feedback from Krzysztof
> - Verify bindings using $ make dt_binding_check
> - Update example in binding documentation after testing.
> - Analyze and Fix build warnings as suggested by kernel test robot.
> - Drop patch 5 from series (device detect logic change).
> This will be taken separately. Focus on functionalities in this series.
> - Update commit messages with short test steps for each feature.
> - Link to v1: https://lore.kernel.org/lkml/cover.1756908788.git.akhilesh@ee.iitb.ac.in/
> ---
>
> Akhilesh Patil (6):
> dt-bindings: rtc: Add ST m41t93
> rtc: m41t93: add device tree support
> rtc: m41t93: migrate to regmap api for register access
> rtc: m41t93: Add alarm support
> rtc: m41t93: Add square wave clock provider support
> rtc: m41t93: Add watchdog support
>
> .../devicetree/bindings/rtc/st,m41t93.yaml | 50 ++
> drivers/rtc/rtc-m41t93.c | 488 ++++++++++++++++--
> 2 files changed, 486 insertions(+), 52 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/rtc/st,m41t93.yaml
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 9+ messages in thread