Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.16 312 lines 7.5 kB view raw
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 36static struct watchdog_device st_wdog_dev; 37 38struct 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 45struct 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 55static struct st_wdog_syscfg stih407_syscfg = { 56 .enable_reg = 0x204, 57 .enable_mask = BIT(19), 58}; 59 60static const struct of_device_id st_wdog_match[] = { 61 { 62 .compatible = "st,stih407-lpc", 63 .data = &stih407_syscfg, 64 }, 65 {}, 66}; 67MODULE_DEVICE_TABLE(of, st_wdog_match); 68 69static 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 85static 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 93static 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 102static 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 111static 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 122static 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 131static const struct watchdog_info st_wdog_info = { 132 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 133 .identity = "ST LPC WDT", 134}; 135 136static 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 144static struct watchdog_device st_wdog_dev = { 145 .info = &st_wdog_info, 146 .ops = &st_wdog_ops, 147}; 148 149static 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 245static 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 257static 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 271static 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 295static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops, 296 st_wdog_suspend, 297 st_wdog_resume); 298 299static 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}; 308module_platform_driver(st_wdog_driver); 309 310MODULE_AUTHOR("David Paris <david.paris@st.com>"); 311MODULE_DESCRIPTION("ST LPC Watchdog Driver"); 312MODULE_LICENSE("GPL");