1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) STMicroelectronics 2018
// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
#include <linux/kernel.h>
#include <linux/mfd/stpmic1.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
/* WATCHDOG CONTROL REGISTER bit */
#define WDT_START BIT(0)
#define WDT_PING BIT(1)
#define WDT_START_MASK BIT(0)
#define WDT_PING_MASK BIT(1)
#define WDT_STOP 0
#define PMIC_WDT_MIN_TIMEOUT 1
#define PMIC_WDT_MAX_TIMEOUT 256
#define PMIC_WDT_DEFAULT_TIMEOUT 30
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct stpmic1_wdt {
struct stpmic1 *pmic;
struct watchdog_device wdtdev;
};
static int pmic_wdt_start(struct watchdog_device *wdd)
{
struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
return regmap_update_bits(wdt->pmic->regmap,
WCHDG_CR, WDT_START_MASK, WDT_START);
}
static int pmic_wdt_stop(struct watchdog_device *wdd)
{
struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
return regmap_update_bits(wdt->pmic->regmap,
WCHDG_CR, WDT_START_MASK, WDT_STOP);
}
static int pmic_wdt_ping(struct watchdog_device *wdd)
{
struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
return regmap_update_bits(wdt->pmic->regmap,
WCHDG_CR, WDT_PING_MASK, WDT_PING);
}
static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
wdd->timeout = timeout;
/* timeout is equal to register value + 1 */
return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
}
static const struct watchdog_info pmic_watchdog_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "STPMIC1 PMIC Watchdog",
};
static const struct watchdog_ops pmic_watchdog_ops = {
.owner = THIS_MODULE,
.start = pmic_wdt_start,
.stop = pmic_wdt_stop,
.ping = pmic_wdt_ping,
.set_timeout = pmic_wdt_set_timeout,
};
static int pmic_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret;
struct stpmic1 *pmic;
struct stpmic1_wdt *wdt;
if (!dev->parent)
return -EINVAL;
pmic = dev_get_drvdata(dev->parent);
if (!pmic)
return -EINVAL;
wdt = devm_kzalloc(dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
wdt->pmic = pmic;
wdt->wdtdev.info = &pmic_watchdog_info;
wdt->wdtdev.ops = &pmic_watchdog_ops;
wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
wdt->wdtdev.parent = dev;
wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT;
watchdog_init_timeout(&wdt->wdtdev, 0, dev);
watchdog_set_nowayout(&wdt->wdtdev, nowayout);
watchdog_set_drvdata(&wdt->wdtdev, wdt);
ret = devm_watchdog_register_device(dev, &wdt->wdtdev);
if (ret)
return ret;
dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n");
return 0;
}
static const struct of_device_id of_pmic_wdt_match[] = {
{ .compatible = "st,stpmic1-wdt" },
{ },
};
MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
static struct platform_driver stpmic1_wdt_driver = {
.probe = pmic_wdt_probe,
.driver = {
.name = "stpmic1-wdt",
.of_match_table = of_pmic_wdt_match,
},
};
module_platform_driver(stpmic1_wdt_driver);
MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
MODULE_LICENSE("GPL v2");
|