diff -drupN a/drivers/watchdog/axp2xx_wdt.c b/drivers/watchdog/axp2xx_wdt.c --- a/drivers/watchdog/axp2xx_wdt.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/watchdog/axp2xx_wdt.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,222 @@ +/* + * driver/watchdog/axp2xx_wdt.c + * + * Copyright (C) 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(x) KBUILD_MODNAME ": " x + +#include +#include +#include +#include +#include +#include + + /* for AXP2101_MODULE_EN (0x18) */ +#define AXP2101_WATCHDOG_EN BIT(0) + + /* for AXP2101_WATCHDOG_CFG (0x19) */ +#define AXP2101_WATCHDOG_CLR BIT(3) + +#define AXP2101_WATCHDOG_RST_CFG_MASK GENMASK(5, 4) +#define AXP2101_WATCHDOG_RST_SHIFT 4 +#define AXP2101_WATCHDOG_RST_IRQ (0 << AXP2101_WATCHDOG_RST_SHIFT) +#define AXP2101_WATCHDOG_RST_IRQ_SYS (1 << AXP2101_WATCHDOG_RST_SHIFT) +#define AXP2101_WATCHDOG_RST_IRQ_SYS_PWROK (2 << AXP2101_WATCHDOG_RST_SHIFT) +#define AXP2101_WATCHDOG_RST_IRQ_SYS_PWRONOFF (3 << AXP2101_WATCHDOG_RST_SHIFT) + +#define AXP2101_WATCHDOG_TIMER_MASK GENMASK(2, 0) +#define AXP2101_WATCHDOG_TIMER_1S 0 +#define AXP2101_WATCHDOG_TIMER_2S 1 +#define AXP2101_WATCHDOG_TIMER_4S 2 +#define AXP2101_WATCHDOG_TIMER_8S 3 +#define AXP2101_WATCHDOG_TIMER_16S 4 +#define AXP2101_WATCHDOG_TIMER_32S 5 +#define AXP2101_WATCHDOG_TIMER_64S 6 +#define AXP2101_WATCHDOG_TIMER_128S 7 + +#define DEFAULT_HEART_BEAT_MS 4000 +#define DEFAULT_TIMEOUT 5 + +struct axp2xx_watchdog { + struct watchdog_device wd; + struct regmap *regmap; + struct axp20x_dev *axp20x; +}; + +static int axp2101_wdt_start(struct watchdog_device *wdt) +{ + struct axp2xx_watchdog *axp2xx_wdt = watchdog_get_drvdata(wdt); + struct regmap *regmap = axp2xx_wdt->regmap; + + /* ping */ + regmap_update_bits(regmap, AXP2101_WATCHDOG_CFG, AXP2101_WATCHDOG_CLR, + AXP2101_WATCHDOG_CLR); + regmap_update_bits(regmap, AXP2101_MODULE_EN, AXP2101_WATCHDOG_EN, + AXP2101_WATCHDOG_EN); + return 0; +} + +static int axp2101_wdt_stop(struct watchdog_device *wdt) +{ + struct axp2xx_watchdog *axp2xx_wdt = watchdog_get_drvdata(wdt); + struct regmap *regmap = axp2xx_wdt->regmap; + + regmap_update_bits(regmap, AXP2101_MODULE_EN, AXP2101_WATCHDOG_EN, 0); + + return 0; +} + +static int axp2101_wdt_ping(struct watchdog_device *wdt) +{ + struct axp2xx_watchdog *axp2xx_wdt = watchdog_get_drvdata(wdt); + struct regmap *regmap = axp2xx_wdt->regmap; + + regmap_update_bits(regmap, AXP2101_WATCHDOG_CFG, AXP2101_WATCHDOG_CLR, + AXP2101_WATCHDOG_CLR); + return 0; +} +int axp2101_wdt_set_timeout(struct watchdog_device *wdt, unsigned int sec) +{ + struct axp2xx_watchdog *axp2xx_wdt = watchdog_get_drvdata(wdt); + + axp2101_wdt_stop(&axp2xx_wdt->wd); + axp2xx_wdt->wd.timeout = sec; + axp2101_wdt_start(&axp2xx_wdt->wd); + return 0; +} + +static struct watchdog_ops axp2101_wdt_ops = { + .owner = THIS_MODULE, + .start = axp2101_wdt_start, + .stop = axp2101_wdt_stop, + .ping = axp2101_wdt_ping, + .set_timeout = axp2101_wdt_set_timeout, + +}; + +static struct watchdog_info axp2xx_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "axp2xx_wdt", +}; + +static int axp2xx_wdt_probe(struct platform_device *pd) +{ + int ret; + struct axp2xx_watchdog *axp2xx_wd; + struct axp20x_dev *axp20x = dev_get_drvdata(pd->dev.parent); + + axp2xx_wd = devm_kzalloc(&pd->dev, sizeof(struct axp2xx_watchdog), + GFP_KERNEL); + if (!axp2xx_wd) { + pr_err("can not request memory\n"); + return -ENOMEM; + } + + switch (axp20x->variant) { + case AXP2101_ID: + axp2xx_wd->wd.ops = &axp2101_wdt_ops; + regmap_update_bits(axp20x->regmap, AXP2101_WATCHDOG_CFG, + AXP2101_WATCHDOG_RST_CFG_MASK, + AXP2101_WATCHDOG_RST_IRQ_SYS_PWRONOFF); + regmap_update_bits(axp20x->regmap, AXP2101_WATCHDOG_CFG, + AXP2101_WATCHDOG_TIMER_MASK, + AXP2101_WATCHDOG_TIMER_4S); + break; + default: + return -ENOTSUPP; + } + + + axp2xx_wd->wd.parent = &pd->dev; + axp2xx_wd->wd.info = &axp2xx_wdt_info; + axp2xx_wd->wd.max_hw_heartbeat_ms = DEFAULT_HEART_BEAT_MS; + axp2xx_wd->regmap = axp20x->regmap; + axp2xx_wd->axp20x = axp20x; + + ret = watchdog_init_timeout(&axp2xx_wd->wd, DEFAULT_TIMEOUT, &pd->dev); + if (ret < 0) { + pr_err("axp2xx_wdt set error timeout\n"); + return ret; + } + + platform_set_drvdata(pd, axp2xx_wd); + watchdog_set_drvdata(&axp2xx_wd->wd, axp2xx_wd); + + ret = watchdog_register_device(&axp2xx_wd->wd); + if (ret) + pr_err("can not register axp2xx watchdog\n"); + + return ret; +} + +static int axp2xx_wdt_remove(struct platform_device *pd) +{ + struct axp2xx_watchdog *axp2xx_wdt = platform_get_drvdata(pd); + + watchdog_unregister_device(&axp2xx_wdt->wd); + + return 0; +} + + +#ifdef CONFIG_PM_SLEEP +static int axp2xx_suspend(struct platform_device *pd, pm_message_t state) +{ + struct axp2xx_watchdog *axp2xx_wdt = platform_get_drvdata(pd); + + if (watchdog_active(&axp2xx_wdt->wd)) + axp2101_wdt_stop(&axp2xx_wdt->wd); + + return 0; +} + +static int axp2xx_resume(struct platform_device *pd) +{ + struct axp2xx_watchdog *axp2xx_wdt = platform_get_drvdata(pd); + + if (watchdog_active(&axp2xx_wdt->wd)) + axp2101_wdt_start(&axp2xx_wdt->wd); + + return 0; +} +#endif + +static struct platform_driver axp2xx_wdt_driver = { + .probe = axp2xx_wdt_probe, + .remove = axp2xx_wdt_remove, +#if CONFIG_PM_SLEEP + .suspend = axp2xx_suspend, + .resume = axp2xx_resume, +#endif + .driver = { + .name = "axp2xx-watchdog", + }, +}; + +static int __init axp2xx_wdt_init(void) +{ + int ret; + + ret = platform_driver_register(&axp2xx_wdt_driver); + if (ret) + pr_err("register axp2xx wdt driver failed %d\n", ret); + + return ret; +} + +static void __exit apx2xx_wdt_exit(void) +{ + platform_driver_unregister(&axp2xx_wdt_driver); +} + +subsys_initcall(axp2xx_wdt_init); + +MODULE_DESCRIPTION("xpower axp2xx watchdog driver"); +MODULE_AUTHOR("F.Y "); +MODULE_LICENSE("GPL v2");