uboot/drivers/watchdog/rti_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) Siemens AG, 2020
   4 *
   5 * Authors:
   6 *   Jan Kiszka <jan.kiszka@siemens.com>
   7 *
   8 * Derived from linux/drivers/watchdog/rti_wdt.c
   9 */
  10
  11#include <common.h>
  12#include <clk.h>
  13#include <dm.h>
  14#include <dm/device_compat.h>
  15#include <power-domain.h>
  16#include <wdt.h>
  17#include <asm/io.h>
  18#include <remoteproc.h>
  19
  20/* Timer register set definition */
  21#define RTIDWDCTRL              0x90
  22#define RTIDWDPRLD              0x94
  23#define RTIWDSTATUS             0x98
  24#define RTIWDKEY                0x9c
  25#define RTIDWDCNTR              0xa0
  26#define RTIWWDRXCTRL            0xa4
  27#define RTIWWDSIZECTRL          0xa8
  28
  29#define RTIWWDRX_NMI            0xa
  30
  31#define RTIWWDSIZE_50P          0x50
  32
  33#define WDENABLE_KEY            0xa98559da
  34
  35#define WDKEY_SEQ0              0xe51a
  36#define WDKEY_SEQ1              0xa35c
  37
  38#define WDT_PRELOAD_SHIFT       13
  39
  40#define WDT_PRELOAD_MAX         0xfff
  41
  42struct rti_wdt_priv {
  43        phys_addr_t regs;
  44        unsigned int clk_khz;
  45};
  46
  47#ifdef CONFIG_WDT_K3_RTI_LOAD_FW
  48#define RTI_WDT_FIT_PATH        "/fit-images/k3-rti-wdt-firmware"
  49
  50static int rti_wdt_load_fw(struct udevice *dev)
  51{
  52        struct udevice *rproc_dev;
  53        int primary_core, ret;
  54        u32 cluster_mode;
  55        ofnode node;
  56        u64 rti_wdt_fw;
  57        u32 rti_wdt_fw_size;
  58
  59        node = ofnode_path(RTI_WDT_FIT_PATH);
  60        if (!ofnode_valid(node))
  61                goto fit_error;
  62
  63        ret = ofnode_read_u64(node, "load", &rti_wdt_fw);
  64        if (ret)
  65                goto fit_error;
  66        ret = ofnode_read_u32(node, "size", &rti_wdt_fw_size);
  67        if (ret)
  68                goto fit_error;
  69
  70        node = ofnode_by_compatible(ofnode_null(), "ti,am654-r5fss");
  71        if (!ofnode_valid(node))
  72                goto dt_error;
  73
  74        ret = ofnode_read_u32(node, "ti,cluster-mode", &cluster_mode);
  75        if (ret)
  76                cluster_mode = 1;
  77
  78        node = ofnode_by_compatible(node, "ti,am654-r5f");
  79        if (!ofnode_valid(node))
  80                goto dt_error;
  81
  82        ret = uclass_get_device_by_ofnode(UCLASS_REMOTEPROC, node, &rproc_dev);
  83        if (ret)
  84                return ret;
  85
  86        primary_core = dev_seq(rproc_dev);
  87
  88        ret = rproc_dev_init(primary_core);
  89        if (ret)
  90                goto fw_error;
  91
  92        if (cluster_mode == 1) {
  93                ret = rproc_dev_init(primary_core + 1);
  94                if (ret)
  95                        goto fw_error;
  96        }
  97
  98        ret = rproc_load(primary_core, (ulong)rti_wdt_fw,
  99                         rti_wdt_fw_size);
 100        if (ret)
 101                goto fw_error;
 102
 103        ret = rproc_start(primary_core);
 104        if (ret)
 105                goto fw_error;
 106
 107        return 0;
 108
 109fit_error:
 110        dev_err(dev, "No loadable firmware found under %s\n", RTI_WDT_FIT_PATH);
 111        return -ENOENT;
 112
 113dt_error:
 114        dev_err(dev, "No compatible firmware target processor found\n");
 115        return -ENODEV;
 116
 117fw_error:
 118        dev_err(dev, "Failed to load watchdog firmware into remote processor %d\n",
 119                primary_core);
 120        return ret;
 121}
 122#else
 123static inline int rti_wdt_load_fw(struct udevice *dev)
 124{
 125        return 0;
 126}
 127#endif
 128
 129static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
 130{
 131        struct rti_wdt_priv *priv = dev_get_priv(dev);
 132        u32 timer_margin;
 133        int ret;
 134
 135        if (readl(priv->regs + RTIDWDCTRL) == WDENABLE_KEY)
 136                return -EBUSY;
 137
 138        ret = rti_wdt_load_fw(dev);
 139        if (ret < 0)
 140                return ret;
 141
 142        timer_margin = timeout_ms * priv->clk_khz / 1000;
 143        timer_margin >>= WDT_PRELOAD_SHIFT;
 144        if (timer_margin > WDT_PRELOAD_MAX)
 145                timer_margin = WDT_PRELOAD_MAX;
 146
 147        writel(timer_margin, priv->regs + RTIDWDPRLD);
 148        writel(RTIWWDRX_NMI, priv->regs + RTIWWDRXCTRL);
 149        writel(RTIWWDSIZE_50P, priv->regs + RTIWWDSIZECTRL);
 150
 151        readl(priv->regs + RTIWWDSIZECTRL);
 152
 153        writel(WDENABLE_KEY, priv->regs + RTIDWDCTRL);
 154
 155        return 0;
 156}
 157
 158static int rti_wdt_reset(struct udevice *dev)
 159{
 160        struct rti_wdt_priv *priv = dev_get_priv(dev);
 161        u32 prld;
 162
 163        /* Make sure we do not reset too early */
 164        prld = readl(priv->regs + RTIDWDPRLD) << WDT_PRELOAD_SHIFT;
 165        if (readl(priv->regs + RTIDWDCNTR) >= prld / 2)
 166                return -EPERM;
 167
 168        writel(WDKEY_SEQ0, priv->regs + RTIWDKEY);
 169        writel(WDKEY_SEQ1, priv->regs + RTIWDKEY);
 170
 171        return 0;
 172}
 173
 174static int rti_wdt_probe(struct udevice *dev)
 175{
 176        struct rti_wdt_priv *priv = dev_get_priv(dev);
 177        struct clk clk;
 178        int ret;
 179
 180        priv->regs = devfdt_get_addr(dev);
 181        if (!priv->regs)
 182                return -EINVAL;
 183
 184        ret = clk_get_by_index(dev, 0, &clk);
 185        if (ret)
 186                return ret;
 187
 188        priv->clk_khz = clk_get_rate(&clk);
 189
 190        return 0;
 191}
 192
 193static const struct wdt_ops rti_wdt_ops = {
 194        .start = rti_wdt_start,
 195        .reset = rti_wdt_reset,
 196};
 197
 198static const struct udevice_id rti_wdt_ids[] = {
 199        { .compatible = "ti,j7-rti-wdt" },
 200        { }
 201};
 202
 203U_BOOT_DRIVER(rti_wdt) = {
 204        .name = "rti_wdt",
 205        .id = UCLASS_WDT,
 206        .of_match = rti_wdt_ids,
 207        .ops = &rti_wdt_ops,
 208        .probe = rti_wdt_probe,
 209        .priv_auto      = sizeof(struct rti_wdt_priv),
 210        .flags = DM_FLAG_LEAVE_PD_ON,
 211};
 212