mirror of https://github.com/OpenIPC/firmware.git
227 lines
6.2 KiB
Diff
227 lines
6.2 KiB
Diff
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 <linux/platform_device.h>
|
|
+#include <linux/watchdog.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/mfd/axp2101.h>
|
|
+#include <linux/bitops.h>
|
|
+
|
|
+ /* 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 <fuyao@allwinnertech.com>");
|
|
+MODULE_LICENSE("GPL v2");
|