diff options
Diffstat (limited to 'target/linux/mvebu/patches-6.6/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch')
-rw-r--r-- | target/linux/mvebu/patches-6.6/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-6.6/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch b/target/linux/mvebu/patches-6.6/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch new file mode 100644 index 0000000000..cf3f88bfcf --- /dev/null +++ b/target/linux/mvebu/patches-6.6/820-v6.11-05-platform-cznic-turris-omnia-mcu-Add-support-for-MCU-.patch @@ -0,0 +1,250 @@ +From 33ae4e4c86bc6ff298489fb8b743e2743dd0af6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 1 Jul 2024 13:30:07 +0200 +Subject: [PATCH 05/11] platform: cznic: turris-omnia-mcu: Add support for MCU + watchdog +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for the watchdog mechanism provided by the MCU. + +Signed-off-by: Marek BehĂșn <kabel@kernel.org> +Reviewed-by: Andy Shevchenko <andy@kernel.org> +Reviewed-by: Guenter Roeck <linux@roeck-us.net> +Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> +Link: https://lore.kernel.org/r/20240701113010.16447-6-kabel@kernel.org +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +--- + drivers/platform/cznic/Kconfig | 2 + + drivers/platform/cznic/Makefile | 1 + + .../platform/cznic/turris-omnia-mcu-base.c | 4 + + .../cznic/turris-omnia-mcu-watchdog.c | 130 ++++++++++++++++++ + drivers/platform/cznic/turris-omnia-mcu.h | 24 ++++ + 5 files changed, 161 insertions(+) + create mode 100644 drivers/platform/cznic/turris-omnia-mcu-watchdog.c + +--- a/drivers/platform/cznic/Kconfig ++++ b/drivers/platform/cznic/Kconfig +@@ -19,6 +19,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. +@@ -26,6 +27,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 +--- a/drivers/platform/cznic/Makefile ++++ b/drivers/platform/cznic/Makefile +@@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris + turris-omnia-mcu-y := turris-omnia-mcu-base.o + turris-omnia-mcu-y += turris-omnia-mcu-gpio.o + turris-omnia-mcu-y += turris-omnia-mcu-sys-off-wakeup.o ++turris-omnia-mcu-y += turris-omnia-mcu-watchdog.o +--- a/drivers/platform/cznic/turris-omnia-mcu-base.c ++++ b/drivers/platform/cznic/turris-omnia-mcu-base.c +@@ -376,6 +376,10 @@ static int omnia_mcu_probe(struct i2c_cl + if (err) + return err; + ++ err = omnia_mcu_register_watchdog(mcu); ++ if (err) ++ return err; ++ + return omnia_mcu_register_gpiochip(mcu); + } + +--- /dev/null ++++ b/drivers/platform/cznic/turris-omnia-mcu-watchdog.c +@@ -0,0 +1,130 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * CZ.NIC's Turris Omnia MCU watchdog driver ++ * ++ * 2024 by Marek BehĂșn <kabel@kernel.org> ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/device.h> ++#include <linux/i2c.h> ++#include <linux/moduleparam.h> ++#include <linux/types.h> ++#include <linux/units.h> ++#include <linux/watchdog.h> ++ ++#include <linux/turris-omnia-mcu-interface.h> ++#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); ++ ++ return omnia_cmd_write_u8(mcu->client, OMNIA_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, OMNIA_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, OMNIA_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, OMNIA_CMD_SET_WDT_TIMEOUT, ++ timeout * DECI); ++} ++ ++static unsigned int omnia_wdt_get_timeleft(struct watchdog_device *wdt) ++{ ++ struct omnia_mcu *mcu = watchdog_get_drvdata(wdt); ++ u16 timeleft; ++ int err; ++ ++ err = omnia_cmd_read_u16(mcu->client, OMNIA_CMD_GET_WDT_TIMELEFT, ++ &timeleft); ++ if (err) { ++ dev_err(&mcu->client->dev, "Cannot get watchdog timeleft: %d\n", ++ err); ++ return 0; ++ } ++ ++ return timeleft / DECI; ++} ++ ++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; ++ u8 state; ++ int err; ++ ++ if (!(mcu->features & OMNIA_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 = 65535 / DECI; ++ ++ 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); ++ ++ err = omnia_cmd_read_u8(mcu->client, OMNIA_CMD_GET_WATCHDOG_STATE, ++ &state); ++ if (err) ++ return dev_err_probe(dev, err, ++ "Cannot get MCU watchdog state\n"); ++ ++ if (state) ++ set_bit(WDOG_HW_RUNNING, &mcu->wdt.status); ++ ++ watchdog_set_nowayout(&mcu->wdt, nowayout); ++ watchdog_stop_on_reboot(&mcu->wdt); ++ err = devm_watchdog_register_device(dev, &mcu->wdt); ++ if (err) ++ return dev_err_probe(dev, err, ++ "Cannot register MCU watchdog\n"); ++ ++ return 0; ++} +--- a/drivers/platform/cznic/turris-omnia-mcu.h ++++ b/drivers/platform/cznic/turris-omnia-mcu.h +@@ -13,6 +13,7 @@ + #include <linux/if_ether.h> + #include <linux/mutex.h> + #include <linux/types.h> ++#include <linux/watchdog.h> + #include <linux/workqueue.h> + #include <asm/byteorder.h> + #include <asm/unaligned.h> +@@ -43,6 +44,9 @@ struct omnia_mcu { + struct rtc_device *rtcdev; + u32 rtc_alarm; + bool front_button_poweron; ++ ++ /* MCU watchdog */ ++ struct watchdog_device wdt; + }; + + int omnia_cmd_write_read(const struct i2c_client *client, +@@ -55,6 +59,25 @@ static inline int omnia_cmd_write(const + return omnia_cmd_write_read(client, cmd, len, NULL, 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) + { +@@ -158,5 +181,6 @@ extern const struct attribute_group omni + + int omnia_mcu_register_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 /* __TURRIS_OMNIA_MCU_H */ |