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