1 /* 2 * Copyright (C) 2019 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <asm/io.h> 8 #include <asm/utils.h> 9 #include <clk.h> 10 #include <dm.h> 11 #include <reset.h> 12 #include <wdt.h> 13 14 #define WDT_CR 0x00 15 #define WDT_TORR 0x04 16 #define WDT_CRR 0x0C 17 #define WDT_EN_MASK 0x01 18 #define WDT_MODE_MASK 0x02 19 #define WDT_CRR_RESTART_VAL 0x76 20 21 DECLARE_GLOBAL_DATA_PTR; 22 23 struct rockchip_wdt_priv { 24 void __iomem *base; 25 struct clk clk; 26 unsigned long rate; 27 struct reset_ctl rst; 28 }; 29 30 /* 31 * Set the watchdog time interval. 32 * Counter is 32 bit. 33 */ 34 static int rockchip_wdt_settimeout(u64 timeout, 35 struct rockchip_wdt_priv *priv) 36 { 37 signed int i; 38 39 /* calculate the timeout range value */ 40 i = log_2_n_round_up(timeout * priv->rate / 1000) - 16; 41 if (i > 15) 42 i = 15; 43 if (i < 0) 44 i = 0; 45 46 writel((i | (i << 4)), priv->base + WDT_TORR); 47 48 return 0; 49 } 50 51 static void rockchip_wdt_enable(struct rockchip_wdt_priv *priv) 52 { 53 u32 val = readl(priv->base + WDT_CR); 54 55 /* Disable interrupt mode; always perform system reset. */ 56 val &= ~WDT_MODE_MASK; 57 /* Enable watchdog. */ 58 val |= WDT_EN_MASK; 59 60 writel(val, priv->base + WDT_CR); 61 } 62 63 static unsigned int rockchip_wdt_is_enabled(struct rockchip_wdt_priv *priv) 64 { 65 unsigned long val; 66 67 val = readl(priv->base + WDT_CR); 68 69 return val & WDT_EN_MASK; 70 } 71 72 static int rockchip_wdt_reset(struct udevice *dev) 73 { 74 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 75 76 if (rockchip_wdt_is_enabled(priv)) 77 /* restart the watchdog counter */ 78 writel(WDT_CRR_RESTART_VAL, priv->base + WDT_CRR); 79 80 return 0; 81 } 82 83 static int rockchip_wdt_start(struct udevice *dev, u64 timeout, ulong flags) 84 { 85 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 86 87 printf("Rockchip watchdog timeout: %lld sec\n", timeout / 1000); 88 89 if (priv->rst.dev) 90 reset_deassert(&priv->rst); 91 92 rockchip_wdt_reset(dev); 93 rockchip_wdt_settimeout(timeout, priv); 94 rockchip_wdt_enable(priv); 95 rockchip_wdt_reset(dev); 96 97 return 0; 98 } 99 100 static int rockchip_wdt_stop(struct udevice *dev) 101 { 102 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 103 104 if (priv->rst.dev) { 105 reset_assert(&priv->rst); 106 reset_deassert(&priv->rst); 107 } 108 109 printf("Rockchip watchdog stop\n"); 110 111 return 0; 112 } 113 114 static const struct wdt_ops rockchip_wdt_ops = { 115 .start = rockchip_wdt_start, 116 .reset = rockchip_wdt_reset, 117 .stop = rockchip_wdt_stop, 118 }; 119 120 static int rockchip_wdt_ofdata_to_platdata(struct udevice *dev) 121 { 122 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 123 124 priv->base = dev_read_addr_ptr(dev); 125 if (!priv->base) 126 return -ENOENT; 127 128 return 0; 129 } 130 131 static int rockchip_wdt_probe(struct udevice *dev) 132 { 133 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 134 int ret; 135 136 ret = reset_get_by_name(dev, "reset", &priv->rst); 137 if (ret) { 138 pr_err("reset_get_by_name(reset) failed: %d\n", ret); 139 priv->rst.dev = NULL; 140 } 141 142 ret = clk_get_by_index(dev, 0, &priv->clk); 143 if (ret < 0) 144 return ret; 145 146 /* Need clk framework support */ 147 priv->rate = clk_get_rate(&priv->clk); 148 if (priv->rate < 0) 149 return -EINVAL; 150 151 return 0; 152 } 153 154 static const struct udevice_id rockchip_wdt_ids[] = { 155 { .compatible = "snps,dw-wdt" }, 156 {} 157 }; 158 159 U_BOOT_DRIVER(rockchip_wdt) = { 160 .name = "rockchip_wdt", 161 .id = UCLASS_WDT, 162 .of_match = rockchip_wdt_ids, 163 .probe = rockchip_wdt_probe, 164 .priv_auto_alloc_size = sizeof(struct rockchip_wdt_priv), 165 .ofdata_to_platdata = rockchip_wdt_ofdata_to_platdata, 166 .ops = &rockchip_wdt_ops, 167 }; 168