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 reset_deassert(&priv->rst); 90 91 rockchip_wdt_reset(dev); 92 rockchip_wdt_settimeout(timeout, priv); 93 rockchip_wdt_enable(priv); 94 rockchip_wdt_reset(dev); 95 96 return 0; 97 } 98 99 static int rockchip_wdt_stop(struct udevice *dev) 100 { 101 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 102 103 reset_assert(&priv->rst); 104 reset_deassert(&priv->rst); 105 106 printf("Rockchip watchdog stop\n"); 107 108 return 0; 109 } 110 111 static const struct wdt_ops rockchip_wdt_ops = { 112 .start = rockchip_wdt_start, 113 .reset = rockchip_wdt_reset, 114 .stop = rockchip_wdt_stop, 115 }; 116 117 static int rockchip_wdt_ofdata_to_platdata(struct udevice *dev) 118 { 119 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 120 121 priv->base = dev_read_addr_ptr(dev); 122 if (!priv->base) 123 return -ENOENT; 124 125 return 0; 126 } 127 128 static int rockchip_wdt_probe(struct udevice *dev) 129 { 130 struct rockchip_wdt_priv *priv = dev_get_priv(dev); 131 int ret; 132 133 ret = reset_get_by_name(dev, "reset", &priv->rst); 134 if (ret) { 135 pr_err("reset_get_by_name(reset) failed: %d\n", ret); 136 return ret; 137 } 138 139 ret = clk_get_by_index(dev, 0, &priv->clk); 140 if (ret < 0) 141 return ret; 142 143 /* Need clk framework support */ 144 priv->rate = clk_get_rate(&priv->clk); 145 if (priv->rate < 0) 146 return -EINVAL; 147 148 return 0; 149 } 150 151 static const struct udevice_id rockchip_wdt_ids[] = { 152 { .compatible = "snps,dw-wdt" }, 153 {} 154 }; 155 156 U_BOOT_DRIVER(rockchip_wdt) = { 157 .name = "rockchip_wdt", 158 .id = UCLASS_WDT, 159 .of_match = rockchip_wdt_ids, 160 .probe = rockchip_wdt_probe, 161 .priv_auto_alloc_size = sizeof(struct rockchip_wdt_priv), 162 .ofdata_to_platdata = rockchip_wdt_ofdata_to_platdata, 163 .ops = &rockchip_wdt_ops, 164 }; 165