1 /* Copyright (c) 2014, The Linux Foundation. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 and 5 * only version 2 as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 */ 13 #include <linux/clk.h> 14 #include <linux/delay.h> 15 #include <linux/io.h> 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/of.h> 19 #include <linux/platform_device.h> 20 #include <linux/watchdog.h> 21 22 #define WDT_RST 0x38 23 #define WDT_EN 0x40 24 #define WDT_STS 0x44 25 #define WDT_BITE_TIME 0x5C 26 27 struct qcom_wdt { 28 struct watchdog_device wdd; 29 struct clk *clk; 30 unsigned long rate; 31 void __iomem *base; 32 }; 33 34 static inline 35 struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) 36 { 37 return container_of(wdd, struct qcom_wdt, wdd); 38 } 39 40 static int qcom_wdt_start(struct watchdog_device *wdd) 41 { 42 struct qcom_wdt *wdt = to_qcom_wdt(wdd); 43 44 writel(0, wdt->base + WDT_EN); 45 writel(1, wdt->base + WDT_RST); 46 writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); 47 writel(1, wdt->base + WDT_EN); 48 return 0; 49 } 50 51 static int qcom_wdt_stop(struct watchdog_device *wdd) 52 { 53 struct qcom_wdt *wdt = to_qcom_wdt(wdd); 54 55 writel(0, wdt->base + WDT_EN); 56 return 0; 57 } 58 59 static int qcom_wdt_ping(struct watchdog_device *wdd) 60 { 61 struct qcom_wdt *wdt = to_qcom_wdt(wdd); 62 63 writel(1, wdt->base + WDT_RST); 64 return 0; 65 } 66 67 static int qcom_wdt_set_timeout(struct watchdog_device *wdd, 68 unsigned int timeout) 69 { 70 wdd->timeout = timeout; 71 return qcom_wdt_start(wdd); 72 } 73 74 static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, 75 void *data) 76 { 77 struct qcom_wdt *wdt = to_qcom_wdt(wdd); 78 u32 timeout; 79 80 /* 81 * Trigger watchdog bite: 82 * Setup BITE_TIME to be 128ms, and enable WDT. 83 */ 84 timeout = 128 * wdt->rate / 1000; 85 86 writel(0, wdt->base + WDT_EN); 87 writel(1, wdt->base + WDT_RST); 88 writel(timeout, wdt->base + WDT_BITE_TIME); 89 writel(1, wdt->base + WDT_EN); 90 91 /* 92 * Actually make sure the above sequence hits hardware before sleeping. 93 */ 94 wmb(); 95 96 msleep(150); 97 return 0; 98 } 99 100 static const struct watchdog_ops qcom_wdt_ops = { 101 .start = qcom_wdt_start, 102 .stop = qcom_wdt_stop, 103 .ping = qcom_wdt_ping, 104 .set_timeout = qcom_wdt_set_timeout, 105 .restart = qcom_wdt_restart, 106 .owner = THIS_MODULE, 107 }; 108 109 static const struct watchdog_info qcom_wdt_info = { 110 .options = WDIOF_KEEPALIVEPING 111 | WDIOF_MAGICCLOSE 112 | WDIOF_SETTIMEOUT 113 | WDIOF_CARDRESET, 114 .identity = KBUILD_MODNAME, 115 }; 116 117 static int qcom_wdt_probe(struct platform_device *pdev) 118 { 119 struct qcom_wdt *wdt; 120 struct resource *res; 121 struct device_node *np = pdev->dev.of_node; 122 u32 percpu_offset; 123 int ret; 124 125 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 126 if (!wdt) 127 return -ENOMEM; 128 129 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 130 131 /* We use CPU0's DGT for the watchdog */ 132 if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) 133 percpu_offset = 0; 134 135 res->start += percpu_offset; 136 res->end += percpu_offset; 137 138 wdt->base = devm_ioremap_resource(&pdev->dev, res); 139 if (IS_ERR(wdt->base)) 140 return PTR_ERR(wdt->base); 141 142 wdt->clk = devm_clk_get(&pdev->dev, NULL); 143 if (IS_ERR(wdt->clk)) { 144 dev_err(&pdev->dev, "failed to get input clock\n"); 145 return PTR_ERR(wdt->clk); 146 } 147 148 ret = clk_prepare_enable(wdt->clk); 149 if (ret) { 150 dev_err(&pdev->dev, "failed to setup clock\n"); 151 return ret; 152 } 153 154 /* 155 * We use the clock rate to calculate the max timeout, so ensure it's 156 * not zero to avoid a divide-by-zero exception. 157 * 158 * WATCHDOG_CORE assumes units of seconds, if the WDT is clocked such 159 * that it would bite before a second elapses it's usefulness is 160 * limited. Bail if this is the case. 161 */ 162 wdt->rate = clk_get_rate(wdt->clk); 163 if (wdt->rate == 0 || 164 wdt->rate > 0x10000000U) { 165 dev_err(&pdev->dev, "invalid clock rate\n"); 166 ret = -EINVAL; 167 goto err_clk_unprepare; 168 } 169 170 wdt->wdd.info = &qcom_wdt_info; 171 wdt->wdd.ops = &qcom_wdt_ops; 172 wdt->wdd.min_timeout = 1; 173 wdt->wdd.max_timeout = 0x10000000U / wdt->rate; 174 wdt->wdd.parent = &pdev->dev; 175 176 if (readl(wdt->base + WDT_STS) & 1) 177 wdt->wdd.bootstatus = WDIOF_CARDRESET; 178 179 /* 180 * If 'timeout-sec' unspecified in devicetree, assume a 30 second 181 * default, unless the max timeout is less than 30 seconds, then use 182 * the max instead. 183 */ 184 wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U); 185 watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); 186 187 ret = watchdog_register_device(&wdt->wdd); 188 if (ret) { 189 dev_err(&pdev->dev, "failed to register watchdog\n"); 190 goto err_clk_unprepare; 191 } 192 193 platform_set_drvdata(pdev, wdt); 194 return 0; 195 196 err_clk_unprepare: 197 clk_disable_unprepare(wdt->clk); 198 return ret; 199 } 200 201 static int qcom_wdt_remove(struct platform_device *pdev) 202 { 203 struct qcom_wdt *wdt = platform_get_drvdata(pdev); 204 205 watchdog_unregister_device(&wdt->wdd); 206 clk_disable_unprepare(wdt->clk); 207 return 0; 208 } 209 210 static const struct of_device_id qcom_wdt_of_table[] = { 211 { .compatible = "qcom,kpss-timer" }, 212 { .compatible = "qcom,scss-timer" }, 213 { }, 214 }; 215 MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); 216 217 static struct platform_driver qcom_watchdog_driver = { 218 .probe = qcom_wdt_probe, 219 .remove = qcom_wdt_remove, 220 .driver = { 221 .name = KBUILD_MODNAME, 222 .of_match_table = qcom_wdt_of_table, 223 }, 224 }; 225 module_platform_driver(qcom_watchdog_driver); 226 227 MODULE_DESCRIPTION("QCOM KPSS Watchdog Driver"); 228 MODULE_LICENSE("GPL v2"); 229