linux/drivers/watchdog/rave-sp-wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3/*
   4 * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE
   5 * Supervisory Processor(SP) MCU
   6 *
   7 * Copyright (C) 2017 Zodiac Inflight Innovation
   8 *
   9 */
  10
  11#include <linux/delay.h>
  12#include <linux/kernel.h>
  13#include <linux/mfd/rave-sp.h>
  14#include <linux/module.h>
  15#include <linux/nvmem-consumer.h>
  16#include <linux/of_device.h>
  17#include <linux/platform_device.h>
  18#include <linux/reboot.h>
  19#include <linux/slab.h>
  20#include <linux/watchdog.h>
  21
  22enum {
  23        RAVE_SP_RESET_BYTE = 1,
  24        RAVE_SP_RESET_REASON_NORMAL = 0,
  25        RAVE_SP_RESET_DELAY_MS = 500,
  26};
  27
  28/**
  29 * struct rave_sp_wdt_variant - RAVE SP watchdog variant
  30 *
  31 * @max_timeout:        Largest possible watchdog timeout setting
  32 * @min_timeout:        Smallest possible watchdog timeout setting
  33 *
  34 * @configure:          Function to send configuration command
  35 * @restart:            Function to send "restart" command
  36 */
  37struct rave_sp_wdt_variant {
  38        unsigned int max_timeout;
  39        unsigned int min_timeout;
  40
  41        int (*configure)(struct watchdog_device *, bool);
  42        int (*restart)(struct watchdog_device *);
  43};
  44
  45/**
  46 * struct rave_sp_wdt - RAVE SP watchdog
  47 *
  48 * @wdd:                Underlying watchdog device
  49 * @sp:                 Pointer to parent RAVE SP device
  50 * @variant:            Device specific variant information
  51 * @reboot_notifier:    Reboot notifier implementing machine reset
  52 */
  53struct rave_sp_wdt {
  54        struct watchdog_device wdd;
  55        struct rave_sp *sp;
  56        const struct rave_sp_wdt_variant *variant;
  57        struct notifier_block reboot_notifier;
  58};
  59
  60static struct rave_sp_wdt *to_rave_sp_wdt(struct watchdog_device *wdd)
  61{
  62        return container_of(wdd, struct rave_sp_wdt, wdd);
  63}
  64
  65static int rave_sp_wdt_exec(struct watchdog_device *wdd, void *data,
  66                            size_t data_size)
  67{
  68        return rave_sp_exec(to_rave_sp_wdt(wdd)->sp,
  69                            data, data_size, NULL, 0);
  70}
  71
  72static int rave_sp_wdt_legacy_configure(struct watchdog_device *wdd, bool on)
  73{
  74        u8 cmd[] = {
  75                [0] = RAVE_SP_CMD_SW_WDT,
  76                [1] = 0,
  77                [2] = 0,
  78                [3] = on,
  79                [4] = on ? wdd->timeout : 0,
  80        };
  81
  82        return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
  83}
  84
  85static int rave_sp_wdt_rdu_configure(struct watchdog_device *wdd, bool on)
  86{
  87        u8 cmd[] = {
  88                [0] = RAVE_SP_CMD_SW_WDT,
  89                [1] = 0,
  90                [2] = on,
  91                [3] = (u8)wdd->timeout,
  92                [4] = (u8)(wdd->timeout >> 8),
  93        };
  94
  95        return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
  96}
  97
  98/**
  99 * rave_sp_wdt_configure - Configure watchdog device
 100 *
 101 * @wdd:        Device to configure
 102 * @on:         Desired state of the watchdog timer (ON/OFF)
 103 *
 104 * This function configures two aspects of the watchdog timer:
 105 *
 106 *  - Wheither it is ON or OFF
 107 *  - Its timeout duration
 108 *
 109 * with first aspect specified via function argument and second via
 110 * the value of 'wdd->timeout'.
 111 */
 112static int rave_sp_wdt_configure(struct watchdog_device *wdd, bool on)
 113{
 114        return to_rave_sp_wdt(wdd)->variant->configure(wdd, on);
 115}
 116
 117static int rave_sp_wdt_legacy_restart(struct watchdog_device *wdd)
 118{
 119        u8 cmd[] = {
 120                [0] = RAVE_SP_CMD_RESET,
 121                [1] = 0,
 122                [2] = RAVE_SP_RESET_BYTE
 123        };
 124
 125        return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
 126}
 127
 128static int rave_sp_wdt_rdu_restart(struct watchdog_device *wdd)
 129{
 130        u8 cmd[] = {
 131                [0] = RAVE_SP_CMD_RESET,
 132                [1] = 0,
 133                [2] = RAVE_SP_RESET_BYTE,
 134                [3] = RAVE_SP_RESET_REASON_NORMAL
 135        };
 136
 137        return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
 138}
 139
 140static int rave_sp_wdt_reboot_notifier(struct notifier_block *nb,
 141                                       unsigned long action, void *data)
 142{
 143        /*
 144         * Restart handler is called in atomic context which means we
 145         * can't communicate to SP via UART. Luckily for use SP will
 146         * wait 500ms before actually resetting us, so we ask it to do
 147         * so here and let the rest of the system go on wrapping
 148         * things up.
 149         */
 150        if (action == SYS_DOWN || action == SYS_HALT) {
 151                struct rave_sp_wdt *sp_wd =
 152                        container_of(nb, struct rave_sp_wdt, reboot_notifier);
 153
 154                const int ret = sp_wd->variant->restart(&sp_wd->wdd);
 155
 156                if (ret < 0)
 157                        dev_err(sp_wd->wdd.parent,
 158                                "Failed to issue restart command (%d)", ret);
 159                return NOTIFY_OK;
 160        }
 161
 162        return NOTIFY_DONE;
 163}
 164
 165static int rave_sp_wdt_restart(struct watchdog_device *wdd,
 166                               unsigned long action, void *data)
 167{
 168        /*
 169         * The actual work was done by reboot notifier above. SP
 170         * firmware waits 500 ms before issuing reset, so let's hang
 171         * here for twice that delay and hopefuly we'd never reach
 172         * the return statement.
 173         */
 174        mdelay(2 * RAVE_SP_RESET_DELAY_MS);
 175
 176        return -EIO;
 177}
 178
 179static int rave_sp_wdt_start(struct watchdog_device *wdd)
 180{
 181        int ret;
 182
 183        ret = rave_sp_wdt_configure(wdd, true);
 184        if (!ret)
 185                set_bit(WDOG_HW_RUNNING, &wdd->status);
 186
 187        return ret;
 188}
 189
 190static int rave_sp_wdt_stop(struct watchdog_device *wdd)
 191{
 192        return rave_sp_wdt_configure(wdd, false);
 193}
 194
 195static int rave_sp_wdt_set_timeout(struct watchdog_device *wdd,
 196                                   unsigned int timeout)
 197{
 198        wdd->timeout = timeout;
 199
 200        return rave_sp_wdt_configure(wdd, watchdog_active(wdd));
 201}
 202
 203static int rave_sp_wdt_ping(struct watchdog_device *wdd)
 204{
 205        u8 cmd[] = {
 206                [0] = RAVE_SP_CMD_PET_WDT,
 207                [1] = 0,
 208        };
 209
 210        return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
 211}
 212
 213static const struct watchdog_info rave_sp_wdt_info = {
 214        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 215        .identity = "RAVE SP Watchdog",
 216};
 217
 218static const struct watchdog_ops rave_sp_wdt_ops = {
 219        .owner = THIS_MODULE,
 220        .start = rave_sp_wdt_start,
 221        .stop = rave_sp_wdt_stop,
 222        .ping = rave_sp_wdt_ping,
 223        .set_timeout = rave_sp_wdt_set_timeout,
 224        .restart = rave_sp_wdt_restart,
 225};
 226
 227static const struct rave_sp_wdt_variant rave_sp_wdt_legacy = {
 228        .max_timeout = 255,
 229        .min_timeout = 1,
 230        .configure = rave_sp_wdt_legacy_configure,
 231        .restart   = rave_sp_wdt_legacy_restart,
 232};
 233
 234static const struct rave_sp_wdt_variant rave_sp_wdt_rdu = {
 235        .max_timeout = 180,
 236        .min_timeout = 60,
 237        .configure = rave_sp_wdt_rdu_configure,
 238        .restart   = rave_sp_wdt_rdu_restart,
 239};
 240
 241static const struct of_device_id rave_sp_wdt_of_match[] = {
 242        {
 243                .compatible = "zii,rave-sp-watchdog-legacy",
 244                .data = &rave_sp_wdt_legacy,
 245        },
 246        {
 247                .compatible = "zii,rave-sp-watchdog",
 248                .data = &rave_sp_wdt_rdu,
 249        },
 250        { /* sentinel */ }
 251};
 252
 253static int rave_sp_wdt_probe(struct platform_device *pdev)
 254{
 255        struct device *dev = &pdev->dev;
 256        struct watchdog_device *wdd;
 257        struct rave_sp_wdt *sp_wd;
 258        struct nvmem_cell *cell;
 259        __le16 timeout = 0;
 260        int ret;
 261
 262        sp_wd = devm_kzalloc(dev, sizeof(*sp_wd), GFP_KERNEL);
 263        if (!sp_wd)
 264                return -ENOMEM;
 265
 266        sp_wd->variant = of_device_get_match_data(dev);
 267        sp_wd->sp      = dev_get_drvdata(dev->parent);
 268
 269        wdd              = &sp_wd->wdd;
 270        wdd->parent      = dev;
 271        wdd->info        = &rave_sp_wdt_info;
 272        wdd->ops         = &rave_sp_wdt_ops;
 273        wdd->min_timeout = sp_wd->variant->min_timeout;
 274        wdd->max_timeout = sp_wd->variant->max_timeout;
 275        wdd->status      = WATCHDOG_NOWAYOUT_INIT_STATUS;
 276        wdd->timeout     = 60;
 277
 278        cell = nvmem_cell_get(dev, "wdt-timeout");
 279        if (!IS_ERR(cell)) {
 280                size_t len;
 281                void *value = nvmem_cell_read(cell, &len);
 282
 283                if (!IS_ERR(value)) {
 284                        memcpy(&timeout, value, min(len, sizeof(timeout)));
 285                        kfree(value);
 286                }
 287                nvmem_cell_put(cell);
 288        }
 289        watchdog_init_timeout(wdd, le16_to_cpu(timeout), dev);
 290        watchdog_set_restart_priority(wdd, 255);
 291        watchdog_stop_on_unregister(wdd);
 292
 293        sp_wd->reboot_notifier.notifier_call = rave_sp_wdt_reboot_notifier;
 294        ret = devm_register_reboot_notifier(dev, &sp_wd->reboot_notifier);
 295        if (ret) {
 296                dev_err(dev, "Failed to register reboot notifier\n");
 297                return ret;
 298        }
 299
 300        /*
 301         * We don't know if watchdog is running now. To be sure, let's
 302         * start it and depend on watchdog core to ping it
 303         */
 304        wdd->max_hw_heartbeat_ms = wdd->max_timeout * 1000;
 305        ret = rave_sp_wdt_start(wdd);
 306        if (ret) {
 307                dev_err(dev, "Watchdog didn't start\n");
 308                return ret;
 309        }
 310
 311        ret = devm_watchdog_register_device(dev, wdd);
 312        if (ret) {
 313                rave_sp_wdt_stop(wdd);
 314                return ret;
 315        }
 316
 317        return 0;
 318}
 319
 320static struct platform_driver rave_sp_wdt_driver = {
 321        .probe = rave_sp_wdt_probe,
 322        .driver = {
 323                .name = KBUILD_MODNAME,
 324                .of_match_table = rave_sp_wdt_of_match,
 325        },
 326};
 327
 328module_platform_driver(rave_sp_wdt_driver);
 329
 330MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match);
 331MODULE_LICENSE("GPL");
 332MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
 333MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
 334MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
 335MODULE_DESCRIPTION("RAVE SP Watchdog driver");
 336MODULE_ALIAS("platform:rave-sp-watchdog");
 337