uboot/drivers/watchdog/cortina_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2020 Cortina-Access
   4 *
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <hang.h>
  10#include <asm/io.h>
  11#include <wdt.h>
  12#include <linux/bitops.h>
  13
  14#define CA_WDT_CTRL             0x00
  15#define CA_WDT_PS               0x04
  16#define CA_WDT_DIV              0x08
  17#define CA_WDT_LD               0x0C
  18#define CA_WDT_LOADE            0x10
  19#define CA_WDT_CNT              0x14
  20#define CA_WDT_IE               0x18
  21#define CA_WDT_INT              0x1C
  22#define CA_WDT_STAT             0x20
  23
  24/* CA_WDT_CTRL */
  25#define CTL_WDT_EN              BIT(0)
  26#define CTL_WDT_RSTEN           BIT(1)
  27#define CTL_WDT_CLK_SEL         BIT(2)
  28/* CA_WDT_LOADE */
  29#define WDT_UPD                 BIT(0)
  30#define WDT_UPD_PS              BIT(1)
  31
  32/* Global config */
  33#define WDT_RESET_SUB           BIT(4)
  34#define WDT_RESET_ALL_BLOCK     BIT(6)
  35#define WDT_RESET_REMAP         BIT(7)
  36#define WDT_EXT_RESET           BIT(8)
  37#define WDT_RESET_DEFAULT       (WDT_EXT_RESET | WDT_RESET_REMAP | \
  38                                 WDT_RESET_ALL_BLOCK | WDT_RESET_SUB)
  39
  40struct ca_wdt_priv {
  41        void __iomem *base;
  42        void __iomem *global_config;
  43};
  44
  45static void cortina_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
  46{
  47        struct ca_wdt_priv *priv = dev_get_priv(dev);
  48
  49        /* Prescale using millisecond unit */
  50        writel(CORTINA_PER_IO_FREQ / 1000, priv->base + CA_WDT_PS);
  51
  52        /* Millisecond */
  53        writel(1, priv->base + CA_WDT_DIV);
  54
  55        writel(timeout_ms, priv->base + CA_WDT_LD);
  56        writel(WDT_UPD | WDT_UPD_PS, priv->base + CA_WDT_LOADE);
  57}
  58
  59static int cortina_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
  60{
  61        struct ca_wdt_priv *priv = dev_get_priv(dev);
  62
  63        cortina_wdt_set_timeout(dev, timeout);
  64
  65        /* WDT Reset option */
  66        setbits_32(priv->global_config, WDT_RESET_DEFAULT);
  67
  68        /* Enable WDT */
  69        setbits_32(priv->base, CTL_WDT_EN | CTL_WDT_RSTEN | CTL_WDT_CLK_SEL);
  70
  71        return 0;
  72}
  73
  74static int cortina_wdt_stop(struct udevice *dev)
  75{
  76        struct ca_wdt_priv *priv = dev_get_priv(dev);
  77
  78        /* Disable WDT */
  79        writel(0, priv->base);
  80
  81        return 0;
  82}
  83
  84static int cortina_wdt_reset(struct udevice *dev)
  85{
  86        struct ca_wdt_priv *priv = dev_get_priv(dev);
  87
  88        /* Reload WDT counter */
  89        writel(WDT_UPD, priv->base + CA_WDT_LOADE);
  90
  91        return 0;
  92}
  93
  94static int cortina_wdt_expire_now(struct udevice *dev, ulong flags)
  95{
  96        /* Set 1ms timeout to reset system */
  97        cortina_wdt_set_timeout(dev, 1);
  98        hang();
  99
 100        return 0;
 101}
 102
 103static int cortina_wdt_probe(struct udevice *dev)
 104{
 105        struct ca_wdt_priv *priv = dev_get_priv(dev);
 106
 107        priv->base = dev_remap_addr_index(dev, 0);
 108        if (!priv->base)
 109                return -ENOENT;
 110
 111        priv->global_config = dev_remap_addr_index(dev, 1);
 112        if (!priv->global_config)
 113                return -ENOENT;
 114
 115        /* Stop WDT */
 116        cortina_wdt_stop(dev);
 117
 118        return 0;
 119}
 120
 121static const struct wdt_ops cortina_wdt_ops = {
 122        .start = cortina_wdt_start,
 123        .reset = cortina_wdt_reset,
 124        .stop = cortina_wdt_stop,
 125        .expire_now = cortina_wdt_expire_now,
 126};
 127
 128static const struct udevice_id cortina_wdt_ids[] = {
 129        {.compatible = "cortina,ca-wdt"},
 130        {}
 131};
 132
 133U_BOOT_DRIVER(cortina_wdt) = {
 134        .name = "cortina_wdt",
 135        .id = UCLASS_WDT,
 136        .probe = cortina_wdt_probe,
 137        .of_match = cortina_wdt_ids,
 138        .ops = &cortina_wdt_ops,
 139};
 140