1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Window watchdog device driver for Xilinx Versal WWDT 4 * 5 * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/interrupt.h> 10 #include <linux/io.h> 11 #include <linux/ioport.h> 12 #include <linux/module.h> 13 #include <linux/of_device.h> 14 #include <linux/of_address.h> 15 #include <linux/watchdog.h> 16 17 /* Max timeout is calculated at 100MHz source clock */ 18 #define XWWDT_DEFAULT_TIMEOUT 42 19 #define XWWDT_MIN_TIMEOUT 1 20 21 /* Register offsets for the WWDT device */ 22 #define XWWDT_MWR_OFFSET 0x00 23 #define XWWDT_ESR_OFFSET 0x04 24 #define XWWDT_FCR_OFFSET 0x08 25 #define XWWDT_FWR_OFFSET 0x0c 26 #define XWWDT_SWR_OFFSET 0x10 27 28 /* Master Write Control Register Masks */ 29 #define XWWDT_MWR_MASK BIT(0) 30 31 /* Enable and Status Register Masks */ 32 #define XWWDT_ESR_WINT_MASK BIT(16) 33 #define XWWDT_ESR_WSW_MASK BIT(8) 34 #define XWWDT_ESR_WEN_MASK BIT(0) 35 36 #define XWWDT_CLOSE_WINDOW_PERCENT 50 37 38 static int wwdt_timeout; 39 static int closed_window_percent; 40 41 module_param(wwdt_timeout, int, 0); 42 MODULE_PARM_DESC(wwdt_timeout, 43 "Watchdog time in seconds. (default=" 44 __MODULE_STRING(XWWDT_DEFAULT_TIMEOUT) ")"); 45 module_param(closed_window_percent, int, 0); 46 MODULE_PARM_DESC(closed_window_percent, 47 "Watchdog closed window percentage. (default=" 48 __MODULE_STRING(XWWDT_CLOSE_WINDOW_PERCENT) ")"); 49 /** 50 * struct xwwdt_device - Watchdog device structure 51 * @base: base io address of WDT device 52 * @spinlock: spinlock for IO register access 53 * @xilinx_wwdt_wdd: watchdog device structure 54 * @freq: source clock frequency of WWDT 55 * @close_percent: Closed window percent 56 */ 57 struct xwwdt_device { 58 void __iomem *base; 59 spinlock_t spinlock; /* spinlock for register handling */ 60 struct watchdog_device xilinx_wwdt_wdd; 61 unsigned long freq; 62 u32 close_percent; 63 }; 64 65 static int xilinx_wwdt_start(struct watchdog_device *wdd) 66 { 67 struct xwwdt_device *xdev = watchdog_get_drvdata(wdd); 68 struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd; 69 u64 time_out, closed_timeout, open_timeout; 70 u32 control_status_reg; 71 72 /* Calculate timeout count */ 73 time_out = xdev->freq * wdd->timeout; 74 closed_timeout = (time_out * xdev->close_percent) / 100; 75 open_timeout = time_out - closed_timeout; 76 wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout; 77 78 spin_lock(&xdev->spinlock); 79 80 iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET); 81 iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET); 82 iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET); 83 iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET); 84 85 /* Enable the window watchdog timer */ 86 control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET); 87 control_status_reg |= XWWDT_ESR_WEN_MASK; 88 iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET); 89 90 spin_unlock(&xdev->spinlock); 91 92 dev_dbg(xilinx_wwdt_wdd->parent, "Watchdog Started!\n"); 93 94 return 0; 95 } 96 97 static int xilinx_wwdt_keepalive(struct watchdog_device *wdd) 98 { 99 struct xwwdt_device *xdev = watchdog_get_drvdata(wdd); 100 u32 control_status_reg; 101 102 spin_lock(&xdev->spinlock); 103 104 /* Enable write access control bit for the window watchdog */ 105 iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET); 106 107 /* Trigger restart kick to watchdog */ 108 control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET); 109 control_status_reg |= XWWDT_ESR_WSW_MASK; 110 iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET); 111 112 spin_unlock(&xdev->spinlock); 113 114 return 0; 115 } 116 117 static const struct watchdog_info xilinx_wwdt_ident = { 118 .options = WDIOF_KEEPALIVEPING | 119 WDIOF_SETTIMEOUT, 120 .firmware_version = 1, 121 .identity = "xlnx_window watchdog", 122 }; 123 124 static const struct watchdog_ops xilinx_wwdt_ops = { 125 .owner = THIS_MODULE, 126 .start = xilinx_wwdt_start, 127 .ping = xilinx_wwdt_keepalive, 128 }; 129 130 static int xwwdt_probe(struct platform_device *pdev) 131 { 132 struct watchdog_device *xilinx_wwdt_wdd; 133 struct device *dev = &pdev->dev; 134 struct xwwdt_device *xdev; 135 struct clk *clk; 136 int ret; 137 138 xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL); 139 if (!xdev) 140 return -ENOMEM; 141 142 xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd; 143 xilinx_wwdt_wdd->info = &xilinx_wwdt_ident; 144 xilinx_wwdt_wdd->ops = &xilinx_wwdt_ops; 145 xilinx_wwdt_wdd->parent = dev; 146 147 xdev->base = devm_platform_ioremap_resource(pdev, 0); 148 if (IS_ERR(xdev->base)) 149 return PTR_ERR(xdev->base); 150 151 clk = devm_clk_get_enabled(dev, NULL); 152 if (IS_ERR(clk)) 153 return PTR_ERR(clk); 154 155 xdev->freq = clk_get_rate(clk); 156 if (!xdev->freq) 157 return -EINVAL; 158 159 xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT; 160 xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT; 161 xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout; 162 163 if (closed_window_percent == 0 || closed_window_percent >= 100) 164 xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT; 165 else 166 xdev->close_percent = closed_window_percent; 167 168 watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev); 169 spin_lock_init(&xdev->spinlock); 170 watchdog_set_drvdata(xilinx_wwdt_wdd, xdev); 171 watchdog_set_nowayout(xilinx_wwdt_wdd, 1); 172 173 ret = devm_watchdog_register_device(dev, xilinx_wwdt_wdd); 174 if (ret) 175 return ret; 176 177 dev_info(dev, "Xilinx window watchdog Timer with timeout %ds\n", 178 xilinx_wwdt_wdd->timeout); 179 180 return 0; 181 } 182 183 static const struct of_device_id xwwdt_of_match[] = { 184 { .compatible = "xlnx,versal-wwdt", }, 185 {}, 186 }; 187 MODULE_DEVICE_TABLE(of, xwwdt_of_match); 188 189 static struct platform_driver xwwdt_driver = { 190 .probe = xwwdt_probe, 191 .driver = { 192 .name = "Xilinx window watchdog", 193 .of_match_table = xwwdt_of_match, 194 }, 195 }; 196 197 module_platform_driver(xwwdt_driver); 198 199 MODULE_AUTHOR("Neeli Srinivas <srinivas.neeli@amd.com>"); 200 MODULE_DESCRIPTION("Xilinx window watchdog driver"); 201 MODULE_LICENSE("GPL"); 202