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