131 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * CZ.NIC's Turris Omnia MCU watchdog driver4 *5 * 2024 by Marek Behún <kabel@kernel.org>6 */7 8#include <linux/bitops.h>9#include <linux/device.h>10#include <linux/i2c.h>11#include <linux/moduleparam.h>12#include <linux/types.h>13#include <linux/units.h>14#include <linux/watchdog.h>15 16#include <linux/turris-omnia-mcu-interface.h>17#include "turris-omnia-mcu.h"18 19#define WATCHDOG_TIMEOUT 12020 21static unsigned int timeout;22module_param(timeout, int, 0);23MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");24 25static bool nowayout = WATCHDOG_NOWAYOUT;26module_param(nowayout, bool, 0);27MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="28 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");29 30static int omnia_wdt_start(struct watchdog_device *wdt)31{32 struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);33 34 return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 1);35}36 37static int omnia_wdt_stop(struct watchdog_device *wdt)38{39 struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);40 41 return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 0);42}43 44static int omnia_wdt_ping(struct watchdog_device *wdt)45{46 struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);47 48 return omnia_cmd_write_u8(mcu->client, OMNIA_CMD_SET_WATCHDOG_STATE, 1);49}50 51static int omnia_wdt_set_timeout(struct watchdog_device *wdt,52 unsigned int timeout)53{54 struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);55 56 return omnia_cmd_write_u16(mcu->client, OMNIA_CMD_SET_WDT_TIMEOUT,57 timeout * DECI);58}59 60static unsigned int omnia_wdt_get_timeleft(struct watchdog_device *wdt)61{62 struct omnia_mcu *mcu = watchdog_get_drvdata(wdt);63 u16 timeleft;64 int err;65 66 err = omnia_cmd_read_u16(mcu->client, OMNIA_CMD_GET_WDT_TIMELEFT,67 &timeleft);68 if (err) {69 dev_err(&mcu->client->dev, "Cannot get watchdog timeleft: %d\n",70 err);71 return 0;72 }73 74 return timeleft / DECI;75}76 77static const struct watchdog_info omnia_wdt_info = {78 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,79 .identity = "Turris Omnia MCU Watchdog",80};81 82static const struct watchdog_ops omnia_wdt_ops = {83 .owner = THIS_MODULE,84 .start = omnia_wdt_start,85 .stop = omnia_wdt_stop,86 .ping = omnia_wdt_ping,87 .set_timeout = omnia_wdt_set_timeout,88 .get_timeleft = omnia_wdt_get_timeleft,89};90 91int omnia_mcu_register_watchdog(struct omnia_mcu *mcu)92{93 struct device *dev = &mcu->client->dev;94 u8 state;95 int err;96 97 if (!(mcu->features & OMNIA_FEAT_WDT_PING))98 return 0;99 100 mcu->wdt.info = &omnia_wdt_info;101 mcu->wdt.ops = &omnia_wdt_ops;102 mcu->wdt.parent = dev;103 mcu->wdt.min_timeout = 1;104 mcu->wdt.max_timeout = 65535 / DECI;105 106 mcu->wdt.timeout = WATCHDOG_TIMEOUT;107 watchdog_init_timeout(&mcu->wdt, timeout, dev);108 109 watchdog_set_drvdata(&mcu->wdt, mcu);110 111 omnia_wdt_set_timeout(&mcu->wdt, mcu->wdt.timeout);112 113 err = omnia_cmd_read_u8(mcu->client, OMNIA_CMD_GET_WATCHDOG_STATE,114 &state);115 if (err)116 return dev_err_probe(dev, err,117 "Cannot get MCU watchdog state\n");118 119 if (state)120 set_bit(WDOG_HW_RUNNING, &mcu->wdt.status);121 122 watchdog_set_nowayout(&mcu->wdt, nowayout);123 watchdog_stop_on_reboot(&mcu->wdt);124 err = devm_watchdog_register_device(dev, &mcu->wdt);125 if (err)126 return dev_err_probe(dev, err,127 "Cannot register MCU watchdog\n");128 129 return 0;130}131