1 /* 2 * ST's LPC Watchdog 3 * 4 * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved 5 * 6 * Author: David Paris <david.paris@st.com> for STMicroelectronics 7 * Lee Jones <lee.jones@linaro.org> for STMicroelectronics 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public Licence 11 * as published by the Free Software Foundation; either version 12 * 2 of the Licence, or (at your option) any later version. 13 */ 14 15 #include <linux/clk.h> 16 #include <linux/init.h> 17 #include <linux/io.h> 18 #include <linux/kernel.h> 19 #include <linux/mfd/syscon.h> 20 #include <linux/module.h> 21 #include <linux/of.h> 22 #include <linux/of_platform.h> 23 #include <linux/platform_device.h> 24 #include <linux/regmap.h> 25 #include <linux/watchdog.h> 26 27 #include <dt-bindings/mfd/st-lpc.h> 28 29 /* Low Power Alarm */ 30 #define LPC_LPA_LSB_OFF 0x410 31 #define LPC_LPA_START_OFF 0x418 32 33 /* LPC as WDT */ 34 #define LPC_WDT_OFF 0x510 35 36 static struct watchdog_device st_wdog_dev; 37 38 struct st_wdog_syscfg { 39 unsigned int reset_type_reg; 40 unsigned int reset_type_mask; 41 unsigned int enable_reg; 42 unsigned int enable_mask; 43 }; 44 45 struct st_wdog { 46 void __iomem *base; 47 struct device *dev; 48 struct regmap *regmap; 49 struct st_wdog_syscfg *syscfg; 50 struct clk *clk; 51 unsigned long clkrate; 52 bool warm_reset; 53 }; 54 55 static struct st_wdog_syscfg stih407_syscfg = { 56 .enable_reg = 0x204, 57 .enable_mask = BIT(19), 58 }; 59 60 static const struct of_device_id st_wdog_match[] = { 61 { 62 .compatible = "st,stih407-lpc", 63 .data = &stih407_syscfg, 64 }, 65 {}, 66 }; 67 MODULE_DEVICE_TABLE(of, st_wdog_match); 68 69 static void st_wdog_setup(struct st_wdog *st_wdog, bool enable) 70 { 71 /* Type of watchdog reset - 0: Cold 1: Warm */ 72 if (st_wdog->syscfg->reset_type_reg) 73 regmap_update_bits(st_wdog->regmap, 74 st_wdog->syscfg->reset_type_reg, 75 st_wdog->syscfg->reset_type_mask, 76 st_wdog->warm_reset); 77 78 /* Mask/unmask watchdog reset */ 79 regmap_update_bits(st_wdog->regmap, 80 st_wdog->syscfg->enable_reg, 81 st_wdog->syscfg->enable_mask, 82 enable ? 0 : st_wdog->syscfg->enable_mask); 83 } 84 85 static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout) 86 { 87 unsigned long clkrate = st_wdog->clkrate; 88 89 writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF); 90 writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF); 91 } 92 93 static int st_wdog_start(struct watchdog_device *wdd) 94 { 95 struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); 96 97 writel_relaxed(1, st_wdog->base + LPC_WDT_OFF); 98 99 return 0; 100 } 101 102 static int st_wdog_stop(struct watchdog_device *wdd) 103 { 104 struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); 105 106 writel_relaxed(0, st_wdog->base + LPC_WDT_OFF); 107 108 return 0; 109 } 110 111 static int st_wdog_set_timeout(struct watchdog_device *wdd, 112 unsigned int timeout) 113 { 114 struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); 115 116 wdd->timeout = timeout; 117 st_wdog_load_timer(st_wdog, timeout); 118 119 return 0; 120 } 121 122 static int st_wdog_keepalive(struct watchdog_device *wdd) 123 { 124 struct st_wdog *st_wdog = watchdog_get_drvdata(wdd); 125 126 st_wdog_load_timer(st_wdog, wdd->timeout); 127 128 return 0; 129 } 130 131 static const struct watchdog_info st_wdog_info = { 132 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 133 .identity = "ST LPC WDT", 134 }; 135 136 static const struct watchdog_ops st_wdog_ops = { 137 .owner = THIS_MODULE, 138 .start = st_wdog_start, 139 .stop = st_wdog_stop, 140 .ping = st_wdog_keepalive, 141 .set_timeout = st_wdog_set_timeout, 142 }; 143 144 static struct watchdog_device st_wdog_dev = { 145 .info = &st_wdog_info, 146 .ops = &st_wdog_ops, 147 }; 148 149 static int st_wdog_probe(struct platform_device *pdev) 150 { 151 const struct of_device_id *match; 152 struct device_node *np = pdev->dev.of_node; 153 struct st_wdog *st_wdog; 154 struct regmap *regmap; 155 struct resource *res; 156 struct clk *clk; 157 void __iomem *base; 158 uint32_t mode; 159 int ret; 160 161 ret = of_property_read_u32(np, "st,lpc-mode", &mode); 162 if (ret) { 163 dev_err(&pdev->dev, "An LPC mode must be provided\n"); 164 return -EINVAL; 165 } 166 167 /* LPC can either run as a Clocksource or in RTC or WDT mode */ 168 if (mode != ST_LPC_MODE_WDT) 169 return -ENODEV; 170 171 st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL); 172 if (!st_wdog) 173 return -ENOMEM; 174 175 match = of_match_device(st_wdog_match, &pdev->dev); 176 if (!match) { 177 dev_err(&pdev->dev, "Couldn't match device\n"); 178 return -ENODEV; 179 } 180 st_wdog->syscfg = (struct st_wdog_syscfg *)match->data; 181 182 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 183 base = devm_ioremap_resource(&pdev->dev, res); 184 if (IS_ERR(base)) 185 return PTR_ERR(base); 186 187 regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); 188 if (IS_ERR(regmap)) { 189 dev_err(&pdev->dev, "No syscfg phandle specified\n"); 190 return PTR_ERR(regmap); 191 } 192 193 clk = devm_clk_get(&pdev->dev, NULL); 194 if (IS_ERR(clk)) { 195 dev_err(&pdev->dev, "Unable to request clock\n"); 196 return PTR_ERR(clk); 197 } 198 199 st_wdog->dev = &pdev->dev; 200 st_wdog->base = base; 201 st_wdog->clk = clk; 202 st_wdog->regmap = regmap; 203 st_wdog->warm_reset = of_property_read_bool(np, "st,warm_reset"); 204 st_wdog->clkrate = clk_get_rate(st_wdog->clk); 205 206 if (!st_wdog->clkrate) { 207 dev_err(&pdev->dev, "Unable to fetch clock rate\n"); 208 return -EINVAL; 209 } 210 st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate; 211 st_wdog_dev.parent = &pdev->dev; 212 213 ret = clk_prepare_enable(clk); 214 if (ret) { 215 dev_err(&pdev->dev, "Unable to enable clock\n"); 216 return ret; 217 } 218 219 watchdog_set_drvdata(&st_wdog_dev, st_wdog); 220 watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT); 221 222 /* Init Watchdog timeout with value in DT */ 223 ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev); 224 if (ret) { 225 dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n"); 226 clk_disable_unprepare(clk); 227 return ret; 228 } 229 230 ret = watchdog_register_device(&st_wdog_dev); 231 if (ret) { 232 dev_err(&pdev->dev, "Unable to register watchdog\n"); 233 clk_disable_unprepare(clk); 234 return ret; 235 } 236 237 st_wdog_setup(st_wdog, true); 238 239 dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s", 240 st_wdog->warm_reset ? "warm" : "cold"); 241 242 return ret; 243 } 244 245 static int st_wdog_remove(struct platform_device *pdev) 246 { 247 struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); 248 249 st_wdog_setup(st_wdog, false); 250 watchdog_unregister_device(&st_wdog_dev); 251 clk_disable_unprepare(st_wdog->clk); 252 253 return 0; 254 } 255 256 #ifdef CONFIG_PM_SLEEP 257 static int st_wdog_suspend(struct device *dev) 258 { 259 struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); 260 261 if (watchdog_active(&st_wdog_dev)) 262 st_wdog_stop(&st_wdog_dev); 263 264 st_wdog_setup(st_wdog, false); 265 266 clk_disable(st_wdog->clk); 267 268 return 0; 269 } 270 271 static int st_wdog_resume(struct device *dev) 272 { 273 struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev); 274 int ret; 275 276 ret = clk_enable(st_wdog->clk); 277 if (ret) { 278 dev_err(dev, "Unable to re-enable clock\n"); 279 watchdog_unregister_device(&st_wdog_dev); 280 clk_unprepare(st_wdog->clk); 281 return ret; 282 } 283 284 st_wdog_setup(st_wdog, true); 285 286 if (watchdog_active(&st_wdog_dev)) { 287 st_wdog_load_timer(st_wdog, st_wdog_dev.timeout); 288 st_wdog_start(&st_wdog_dev); 289 } 290 291 return 0; 292 } 293 #endif 294 295 static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops, 296 st_wdog_suspend, 297 st_wdog_resume); 298 299 static struct platform_driver st_wdog_driver = { 300 .driver = { 301 .name = "st-lpc-wdt", 302 .pm = &st_wdog_pm_ops, 303 .of_match_table = st_wdog_match, 304 }, 305 .probe = st_wdog_probe, 306 .remove = st_wdog_remove, 307 }; 308 module_platform_driver(st_wdog_driver); 309 310 MODULE_AUTHOR("David Paris <david.paris@st.com>"); 311 MODULE_DESCRIPTION("ST LPC Watchdog Driver"); 312 MODULE_LICENSE("GPL"); 313