1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Cadence WDT driver - Used by Xilinx Zynq 4 * Reference: Linux kernel Cadence watchdog driver. 5 * 6 * Author(s): Shreenidhi Shedi <yesshedi@gmail.com> 7 */ 8 9 #include <common.h> 10 #include <dm.h> 11 #include <wdt.h> 12 #include <clk.h> 13 #include <linux/io.h> 14 15 DECLARE_GLOBAL_DATA_PTR; 16 17 struct cdns_regs { 18 u32 zmr; /* WD Zero mode register, offset - 0x0 */ 19 u32 ccr; /* Counter Control Register offset - 0x4 */ 20 u32 restart; /* Restart key register, offset - 0x8 */ 21 u32 status; /* Status Register, offset - 0xC */ 22 }; 23 24 struct cdns_wdt_priv { 25 bool rst; 26 u32 timeout; 27 struct cdns_regs *regs; 28 }; 29 30 #define CDNS_WDT_DEFAULT_TIMEOUT 10 31 32 /* Supports 1 - 516 sec */ 33 #define CDNS_WDT_MIN_TIMEOUT 1 34 #define CDNS_WDT_MAX_TIMEOUT 516 35 36 /* Restart key */ 37 #define CDNS_WDT_RESTART_KEY 0x00001999 38 39 /* Counter register access key */ 40 #define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 41 42 /* Counter value divisor */ 43 #define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 44 45 /* Clock prescaler value and selection */ 46 #define CDNS_WDT_PRESCALE_64 64 47 #define CDNS_WDT_PRESCALE_512 512 48 #define CDNS_WDT_PRESCALE_4096 4096 49 #define CDNS_WDT_PRESCALE_SELECT_64 1 50 #define CDNS_WDT_PRESCALE_SELECT_512 2 51 #define CDNS_WDT_PRESCALE_SELECT_4096 3 52 53 /* Input clock frequency */ 54 #define CDNS_WDT_CLK_75MHZ 75000000 55 56 /* Counter maximum value */ 57 #define CDNS_WDT_COUNTER_MAX 0xFFF 58 59 /********************* Register Map **********************************/ 60 61 /* 62 * Zero Mode Register - This register controls how the time out is indicated 63 * and also contains the access code to allow writes to the register (0xABC). 64 */ 65 #define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ 66 #define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ 67 #define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ 68 #define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ 69 #define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ 70 71 /* 72 * Counter Control register - This register controls how fast the timer runs 73 * and the reset value and also contains the access code to allow writes to 74 * the register. 75 */ 76 #define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ 77 78 /* Write access to Registers */ 79 static inline void cdns_wdt_writereg(u32 *addr, u32 val) 80 { 81 writel(val, addr); 82 } 83 84 /** 85 * cdns_wdt_reset - Reload the watchdog timer (i.e. pat the watchdog). 86 * 87 * @dev: Watchdog device 88 * 89 * Write the restart key value (0x00001999) to the restart register. 90 * 91 * Return: Always 0 92 */ 93 static int cdns_wdt_reset(struct udevice *dev) 94 { 95 struct cdns_wdt_priv *priv = dev_get_priv(dev); 96 97 debug("%s\n", __func__); 98 99 cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY); 100 101 return 0; 102 } 103 104 /** 105 * cdns_wdt_start - Enable and start the watchdog. 106 * 107 * @dev: Watchdog device 108 * @timeout: Timeout value 109 * @flags: Driver flags 110 * 111 * The counter value is calculated according to the formula: 112 * count = (timeout * clock) / prescaler + 1. 113 * 114 * The calculated count is divided by 0x1000 to obtain the field value 115 * to write to counter control register. 116 * 117 * Clears the contents of prescaler and counter reset value. Sets the 118 * prescaler to 4096 and the calculated count and access key 119 * to write to CCR Register. 120 * 121 * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) 122 * or Interrupt signal(IRQEN) with a specified cycles and the access 123 * key to write to ZMR Register. 124 * 125 * Return: Upon success 0, failure -1. 126 */ 127 static int cdns_wdt_start(struct udevice *dev, u64 timeout, ulong flags) 128 { 129 ulong clk_f; 130 u32 count, prescaler, ctrl_clksel, data = 0; 131 struct clk clock; 132 struct cdns_wdt_priv *priv = dev_get_priv(dev); 133 134 if (clk_get_by_index(dev, 0, &clock) < 0) { 135 dev_err(dev, "failed to get clock\n"); 136 return -1; 137 } 138 139 clk_f = clk_get_rate(&clock); 140 if (IS_ERR_VALUE(clk_f)) { 141 dev_err(dev, "failed to get rate\n"); 142 return -1; 143 } 144 145 if ((timeout < CDNS_WDT_MIN_TIMEOUT) || 146 (timeout > CDNS_WDT_MAX_TIMEOUT)) { 147 timeout = priv->timeout; 148 } 149 150 debug("%s: CLK_FREQ %ld, timeout %lld\n", __func__, clk_f, timeout); 151 152 if (clk_f <= CDNS_WDT_CLK_75MHZ) { 153 prescaler = CDNS_WDT_PRESCALE_512; 154 ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; 155 } else { 156 prescaler = CDNS_WDT_PRESCALE_4096; 157 ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; 158 } 159 160 /* 161 * Counter value divisor to obtain the value of 162 * counter reset to be written to control register. 163 */ 164 count = (timeout * (clk_f / prescaler)) / 165 CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; 166 167 if (count > CDNS_WDT_COUNTER_MAX) 168 count = CDNS_WDT_COUNTER_MAX; 169 170 cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL); 171 172 count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; 173 174 /* Write counter access key first to be able write to register */ 175 data = count | CDNS_WDT_REGISTER_ACCESS_KEY | ctrl_clksel; 176 cdns_wdt_writereg(&priv->regs->ccr, data); 177 178 data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | 179 CDNS_WDT_ZMR_ZKEY_VAL; 180 181 /* Reset on timeout if specified in device tree. */ 182 if (priv->rst) { 183 data |= CDNS_WDT_ZMR_RSTEN_MASK; 184 data &= ~CDNS_WDT_ZMR_IRQEN_MASK; 185 } else { 186 data &= ~CDNS_WDT_ZMR_RSTEN_MASK; 187 data |= CDNS_WDT_ZMR_IRQEN_MASK; 188 } 189 190 cdns_wdt_writereg(&priv->regs->zmr, data); 191 cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY); 192 193 return 0; 194 } 195 196 /** 197 * cdns_wdt_stop - Stop the watchdog. 198 * 199 * @dev: Watchdog device 200 * 201 * Read the contents of the ZMR register, clear the WDEN bit in the register 202 * and set the access key for successful write. 203 * 204 * Return: Always 0 205 */ 206 static int cdns_wdt_stop(struct udevice *dev) 207 { 208 struct cdns_wdt_priv *priv = dev_get_priv(dev); 209 210 cdns_wdt_writereg(&priv->regs->zmr, 211 CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); 212 213 return 0; 214 } 215 216 /** 217 * cdns_wdt_probe - Probe call for the device. 218 * 219 * @dev: Handle to the udevice structure. 220 * 221 * Return: Always 0. 222 */ 223 static int cdns_wdt_probe(struct udevice *dev) 224 { 225 debug("%s: Probing wdt%u\n", __func__, dev->seq); 226 227 cdns_wdt_stop(dev); 228 229 return 0; 230 } 231 232 static int cdns_wdt_ofdata_to_platdata(struct udevice *dev) 233 { 234 int node = dev_of_offset(dev); 235 struct cdns_wdt_priv *priv = dev_get_priv(dev); 236 237 priv->regs = devfdt_get_addr_ptr(dev); 238 if (IS_ERR(priv->regs)) 239 return PTR_ERR(priv->regs); 240 241 priv->timeout = fdtdec_get_int(gd->fdt_blob, node, "timeout-sec", 242 CDNS_WDT_DEFAULT_TIMEOUT); 243 244 priv->rst = fdtdec_get_bool(gd->fdt_blob, node, "reset-on-timeout"); 245 246 debug("%s: timeout %d, reset %d\n", __func__, priv->timeout, priv->rst); 247 248 return 0; 249 } 250 251 static const struct wdt_ops cdns_wdt_ops = { 252 .start = cdns_wdt_start, 253 .reset = cdns_wdt_reset, 254 .stop = cdns_wdt_stop, 255 }; 256 257 static const struct udevice_id cdns_wdt_ids[] = { 258 { .compatible = "cdns,wdt-r1p2" }, 259 {} 260 }; 261 262 U_BOOT_DRIVER(cdns_wdt) = { 263 .name = "cdns_wdt", 264 .id = UCLASS_WDT, 265 .of_match = cdns_wdt_ids, 266 .probe = cdns_wdt_probe, 267 .priv_auto_alloc_size = sizeof(struct cdns_wdt_priv), 268 .ofdata_to_platdata = cdns_wdt_ofdata_to_platdata, 269 .ops = &cdns_wdt_ops, 270 }; 271