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