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");
 |