mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-01 10:42:11 +00:00
platform: cznic: turris-omnia-mcu: Add support for MCU watchdog
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>
This commit is contained in:
parent
90e700fd12
commit
ab89fb5fb9
@ -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
|
||||
|
@ -4,3 +4,4 @@ obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
|
||||
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
|
||||
|
@ -377,6 +377,10 @@ static int omnia_mcu_probe(struct i2c_client *client)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = omnia_mcu_register_watchdog(mcu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return omnia_mcu_register_gpiochip(mcu);
|
||||
}
|
||||
|
||||
|
130
drivers/platform/cznic/turris-omnia-mcu-watchdog.c
Normal file
130
drivers/platform/cznic/turris-omnia-mcu-watchdog.c
Normal file
@ -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;
|
||||
}
|
@ -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 struct i2c_client *client, void *cmd,
|
||||
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 omnia_mcu_poweroff_group;
|
||||
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user