sdk-hwV1.3/lichee/linux-4.9/drivers/watchdog/axp2xx_wdt.c

223 lines
5.7 KiB
C

/*
* 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,
#ifdef 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");