From: "Marek Behún" <kabel@kernel.org>
To: Gregory CLEMENT <gregory.clement@bootlin.com>,
Arnd Bergmann <arnd@arndb.de>,
soc@kernel.org, arm@kernel.org,
Wim Van Sebroeck <wim@linux-watchdog.org>,
Guenter Roeck <linux@roeck-us.net>,
linux-watchdog@vger.kernel.org
Cc: "Marek Behún" <kabel@kernel.org>
Subject: [PATCH v2 5/7] platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
Date: Tue, 19 Sep 2023 12:38:13 +0200 [thread overview]
Message-ID: <20230919103815.16818-6-kabel@kernel.org> (raw)
In-Reply-To: <20230919103815.16818-1-kabel@kernel.org>
Add support for the watchdog mechanism provided by the MCU.
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/platform/cznic/Kconfig | 2 +
drivers/platform/cznic/Makefile | 1 +
.../platform/cznic/turris-omnia-mcu-base.c | 4 +
.../cznic/turris-omnia-mcu-watchdog.c | 126 ++++++++++++++++++
drivers/platform/cznic/turris-omnia-mcu.h | 24 ++++
5 files changed, 157 insertions(+)
create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c
diff --git a/drivers/platform/cznic/Kconfig b/drivers/platform/cznic/Kconfig
index 0a752aa654fa..e2649cdecc38 100644
--- a/drivers/platform/cznic/Kconfig
+++ b/drivers/platform/cznic/Kconfig
@@ -20,6 +20,7 @@ config TURRIS_OMNIA_MCU
select GPIOLIB
select GPIOLIB_IRQCHIP
select RTC_CLASS
+ select WATCHDOG_CORE
help
Say Y here to add support for the features implemented by the
microcontroller on the CZ.NIC's Turris Omnia SOHO router.
@@ -27,6 +28,7 @@ config TURRIS_OMNIA_MCU
- board poweroff into true low power mode (with voltage regulators
disabled) and the ability to configure wake up from this mode (via
rtcwake)
+ - MCU watchdog
- GPIO pins
- to get front button press events (the front button can be
configured either to generate press events to the CPU or to change
diff --git a/drivers/platform/cznic/Makefile b/drivers/platform/cznic/Makefile
index 6f1470d1f673..a43997a12d74 100644
--- a/drivers/platform/cznic/Makefile
+++ b/drivers/platform/cznic/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
turris-omnia-mcu-objs := turris-omnia-mcu-base.o
turris-omnia-mcu-objs += turris-omnia-mcu-gpio.o
turris-omnia-mcu-objs += turris-omnia-mcu-sys-off-wakeup.o
+turris-omnia-mcu-objs += turris-omnia-mcu-watchdog.o
diff --git a/drivers/platform/cznic/turris-omnia-mcu-base.c b/drivers/platform/cznic/turris-omnia-mcu-base.c
index b507f81ebe0a..708265203cc1 100644
--- a/drivers/platform/cznic/turris-omnia-mcu-base.c
+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
@@ -230,6 +230,10 @@ static int omnia_mcu_probe(struct i2c_client *client)
if (ret)
return ret;
+ ret = omnia_mcu_register_watchdog(mcu);
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/drivers/platform/cznic/turris-omnia-mcu-watchdog.c b/drivers/platform/cznic/turris-omnia-mcu-watchdog.c
new file mode 100644
index 000000000000..2238b2167c45
--- /dev/null
+++ b/drivers/platform/cznic/turris-omnia-mcu-watchdog.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CZ.NIC's Turris Omnia MCU watchdog driver
+ *
+ * 2023 by Marek Behún <kabel@kernel.org>
+ */
+
+#include "turris-omnia-mcu.h"
+
+#define WATCHDOG_TIMEOUT 120
+
+static unsigned int timeout;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int omnia_wdt_start(struct watchdog_device *wdt)
+{
+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
+
+ /*
+ * The MCU also implements LED controller, and the LED driver may in
+ * some cases require more I2C bandwidth. In order to prevent stealing
+ * that bandwidth from the LED driver, configure minimum time between
+ * pings to 10% of the configured timeout (for 120 second timeout we
+ * will be pinging at most every 12 seconds).
+ */
+ mcu->wdt.min_hw_heartbeat_ms = 100 * mcu->wdt.timeout;
+
+ return omnia_cmd_write_u8(mcu->client, CMD_SET_WATCHDOG_STATE, 1);
+}
+
+static int omnia_wdt_stop(struct watchdog_device *wdt)
+{
+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
+
+ return omnia_cmd_write_u8(mcu->client, CMD_SET_WATCHDOG_STATE, 0);
+}
+
+static int omnia_wdt_ping(struct watchdog_device *wdt)
+{
+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
+
+ return omnia_cmd_write_u8(mcu->client, CMD_SET_WATCHDOG_STATE, 1);
+}
+
+static int omnia_wdt_set_timeout(struct watchdog_device *wdt,
+ unsigned int timeout)
+{
+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
+
+ return omnia_cmd_write_u16(mcu->client, CMD_SET_WDT_TIMEOUT,
+ timeout * 10);
+}
+
+static unsigned int omnia_wdt_get_timeleft(struct watchdog_device *wdt)
+{
+ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);
+ int ret;
+
+ ret = omnia_cmd_read_u16(mcu->client, CMD_GET_WDT_TIMELEFT);
+ if (ret < 0) {
+ dev_err(&mcu->client->dev, "Cannot get watchdog timeleft: %d\n",
+ ret);
+ return 0;
+ }
+
+ return ret / 10;
+}
+
+static const struct watchdog_info omnia_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Turris Omnia MCU Watchdog",
+};
+
+static const struct watchdog_ops omnia_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = omnia_wdt_start,
+ .stop = omnia_wdt_stop,
+ .ping = omnia_wdt_ping,
+ .set_timeout = omnia_wdt_set_timeout,
+ .get_timeleft = omnia_wdt_get_timeleft,
+};
+
+int omnia_mcu_register_watchdog(struct omnia_mcu *mcu)
+{
+ struct device *dev = &mcu->client->dev;
+ int ret;
+
+ if (!(mcu->features & FEAT_WDT_PING))
+ return 0;
+
+ mcu->wdt.info = &omnia_wdt_info;
+ mcu->wdt.ops = &omnia_wdt_ops;
+ mcu->wdt.parent = dev;
+ mcu->wdt.min_timeout = 1;
+ mcu->wdt.max_timeout = 6553; /* 65535 deciseconds */
+
+ mcu->wdt.timeout = WATCHDOG_TIMEOUT;
+ watchdog_init_timeout(&mcu->wdt, timeout, dev);
+
+ watchdog_set_drvdata(&mcu->wdt, mcu);
+
+ omnia_wdt_set_timeout(&mcu->wdt, mcu->wdt.timeout);
+
+ ret = omnia_cmd_read_u8(mcu->client, CMD_GET_WATCHDOG_STATE);
+ if (ret < 0) {
+ dev_err(dev, "Cannot get MCU watchdog state: %d\n", ret);
+ return ret;
+ }
+
+ if (ret)
+ set_bit(WDOG_HW_RUNNING, &mcu->wdt.status);
+
+ watchdog_set_nowayout(&mcu->wdt, nowayout);
+ watchdog_stop_on_reboot(&mcu->wdt);
+ ret = devm_watchdog_register_device(dev, &mcu->wdt);
+ if (ret)
+ dev_err(dev, "Cannot register MCU watchdog: %d\n", ret);
+
+ return ret;
+}
diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h
index c40ac07be5f5..89fe39f4255f 100644
--- a/drivers/platform/cznic/turris-omnia-mcu.h
+++ b/drivers/platform/cznic/turris-omnia-mcu.h
@@ -16,6 +16,7 @@
#include <linux/mutex.h>
#include <linux/rtc.h>
#include <linux/turris-omnia-mcu-interface.h>
+#include <linux/watchdog.h>
#include <linux/workqueue.h>
struct omnia_mcu {
@@ -36,6 +37,9 @@ struct omnia_mcu {
struct rtc_device *rtcdev;
u32 rtc_alarm;
bool front_button_poweron;
+
+ /* MCU watchdog */
+ struct watchdog_device wdt;
};
static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
@@ -46,6 +50,25 @@ static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
return ret < 0 ? ret : 0;
}
+static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd,
+ u8 val)
+{
+ u8 buf[2] = { cmd, val };
+
+ return omnia_cmd_write(client, buf, sizeof(buf));
+}
+
+static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd,
+ u16 val)
+{
+ u8 buf[3];
+
+ buf[0] = cmd;
+ put_unaligned_le16(val, &buf[1]);
+
+ return omnia_cmd_write(client, buf, sizeof(buf));
+}
+
static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd,
u32 val)
{
@@ -136,5 +159,6 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
void omnia_mcu_unregister_gpiochip(struct omnia_mcu *mcu);
int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
+int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
#endif /* __DRIVERS_PLATFORM_CZNIC_TURRIS_OMNIA_MCU_H */
--
2.41.0
next prev parent reply other threads:[~2023-09-19 10:38 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-19 10:38 [PATCH v2 0/7] Turris Omnia MCU driver Marek Behún
2023-09-19 10:38 ` [PATCH v2 1/7] dt-bindings: arm: add cznic,turris-omnia-mcu binding Marek Behún
2023-09-20 12:37 ` Krzysztof Kozlowski
2023-09-19 10:38 ` [PATCH v2 2/7] platform: cznic: Add preliminary support for Turris Omnia MCU Marek Behún
2023-09-19 12:29 ` Andy Shevchenko
2023-09-19 15:16 ` Marek Behún
2023-09-19 18:27 ` Andy Shevchenko
2023-09-20 14:19 ` Marek Behún
2023-09-20 14:47 ` Andy Shevchenko
2023-09-19 10:38 ` [PATCH v2 3/7] platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs Marek Behún
2023-09-19 13:00 ` Andy Shevchenko
2023-09-20 17:08 ` Marek Behún
2023-09-21 9:55 ` Andy Shevchenko
2023-09-21 20:25 ` Marek Behún
2023-09-22 14:18 ` Andy Shevchenko
2023-09-25 10:03 ` Marek Behún
2023-09-25 10:29 ` Andy Shevchenko
2023-09-21 18:42 ` Marek Behún
2023-09-22 14:14 ` Andy Shevchenko
2023-09-20 11:58 ` Linus Walleij
2023-09-20 13:36 ` Andy Shevchenko
2023-09-21 19:45 ` Marek Behún
2023-09-21 20:14 ` Marek Behún
2023-09-22 14:21 ` Andy Shevchenko
2023-09-19 10:38 ` [PATCH v2 4/7] platform: cznic: turris-omnia-mcu: Add support for poweroff and wakeup Marek Behún
2023-09-19 10:38 ` Marek Behún [this message]
2023-09-19 13:50 ` [PATCH v2 5/7] platform: cznic: turris-omnia-mcu: Add support for MCU watchdog Guenter Roeck
2023-09-22 11:46 ` Marek Behún
2023-09-19 10:38 ` [PATCH v2 6/7] ARM: dts: turris-omnia: Add MCU system-controller node Marek Behún
2023-09-19 10:38 ` [PATCH v2 7/7] ARM: dts: turris-omnia: Add GPIO key node for front button Marek Behún
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230919103815.16818-6-kabel@kernel.org \
--to=kabel@kernel.org \
--cc=arm@kernel.org \
--cc=arnd@arndb.de \
--cc=gregory.clement@bootlin.com \
--cc=linux-watchdog@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=soc@kernel.org \
--cc=wim@linux-watchdog.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox