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