1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/bitops.h>
3 #include <linux/interrupt.h>
4 #include <linux/kernel.h>
5 #include <linux/module.h>
7 #include <linux/property.h>
8 #include <linux/platform_device.h>
9 #include <linux/regmap.h>
10 #include <linux/watchdog.h>
12 #define PON_POFF_REASON1 0x0c
13 #define PON_POFF_REASON1_PMIC_WD BIT(2)
14 #define PON_POFF_REASON2 0x0d
15 #define PON_POFF_REASON2_UVLO BIT(5)
16 #define PON_POFF_REASON2_OTST3 BIT(6)
18 #define PON_INT_RT_STS 0x10
19 #define PMIC_WD_BARK_STS_BIT BIT(6)
21 #define PON_PMIC_WD_RESET_S1_TIMER 0x54
22 #define PON_PMIC_WD_RESET_S2_TIMER 0x55
24 #define PON_PMIC_WD_RESET_S2_CTL 0x56
25 #define RESET_TYPE_WARM 0x01
26 #define RESET_TYPE_SHUTDOWN 0x04
27 #define RESET_TYPE_HARD 0x07
29 #define PON_PMIC_WD_RESET_S2_CTL2 0x57
30 #define S2_RESET_EN_BIT BIT(7)
32 #define PON_PMIC_WD_RESET_PET 0x58
33 #define WATCHDOG_PET_BIT BIT(0)
35 #define PM8916_WDT_DEFAULT_TIMEOUT 32
36 #define PM8916_WDT_MIN_TIMEOUT 1
37 #define PM8916_WDT_MAX_TIMEOUT 127
40 struct regmap
*regmap
;
41 struct watchdog_device wdev
;
45 static int pm8916_wdt_start(struct watchdog_device
*wdev
)
47 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
49 return regmap_update_bits(wdt
->regmap
,
50 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL2
,
51 S2_RESET_EN_BIT
, S2_RESET_EN_BIT
);
54 static int pm8916_wdt_stop(struct watchdog_device
*wdev
)
56 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
58 return regmap_update_bits(wdt
->regmap
,
59 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL2
,
63 static int pm8916_wdt_ping(struct watchdog_device
*wdev
)
65 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
67 return regmap_write(wdt
->regmap
, wdt
->baseaddr
+ PON_PMIC_WD_RESET_PET
,
71 static int pm8916_wdt_configure_timers(struct watchdog_device
*wdev
)
73 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
76 err
= regmap_write(wdt
->regmap
,
77 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S1_TIMER
,
78 wdev
->timeout
- wdev
->pretimeout
);
82 return regmap_write(wdt
->regmap
,
83 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_TIMER
,
87 static int pm8916_wdt_set_timeout(struct watchdog_device
*wdev
,
90 wdev
->timeout
= timeout
;
92 return pm8916_wdt_configure_timers(wdev
);
95 static int pm8916_wdt_set_pretimeout(struct watchdog_device
*wdev
,
96 unsigned int pretimeout
)
98 wdev
->pretimeout
= pretimeout
;
100 return pm8916_wdt_configure_timers(wdev
);
103 static irqreturn_t
pm8916_wdt_isr(int irq
, void *arg
)
105 struct pm8916_wdt
*wdt
= arg
;
108 err
= regmap_read(wdt
->regmap
, wdt
->baseaddr
+ PON_INT_RT_STS
, &sts
);
112 if (sts
& PMIC_WD_BARK_STS_BIT
)
113 watchdog_notify_pretimeout(&wdt
->wdev
);
118 static const struct watchdog_info pm8916_wdt_ident
= {
119 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
|
120 WDIOF_OVERHEAT
| WDIOF_CARDRESET
| WDIOF_POWERUNDER
,
121 .identity
= "QCOM PM8916 PON WDT",
124 static const struct watchdog_info pm8916_wdt_pt_ident
= {
125 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
|
126 WDIOF_OVERHEAT
| WDIOF_CARDRESET
| WDIOF_POWERUNDER
|
128 .identity
= "QCOM PM8916 PON WDT",
131 static const struct watchdog_ops pm8916_wdt_ops
= {
132 .owner
= THIS_MODULE
,
133 .start
= pm8916_wdt_start
,
134 .stop
= pm8916_wdt_stop
,
135 .ping
= pm8916_wdt_ping
,
136 .set_timeout
= pm8916_wdt_set_timeout
,
137 .set_pretimeout
= pm8916_wdt_set_pretimeout
,
140 static int pm8916_wdt_probe(struct platform_device
*pdev
)
142 struct device
*dev
= &pdev
->dev
;
143 struct pm8916_wdt
*wdt
;
144 struct device
*parent
;
149 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
153 parent
= dev
->parent
;
156 * The pm8916-pon-wdt is a child of the pon device, which is a child
157 * of the pm8916 mfd device. We want access to the pm8916 registers.
158 * Retrieve regmap from pm8916 (parent->parent) and base address
159 * from pm8916-pon (pon).
161 wdt
->regmap
= dev_get_regmap(parent
->parent
, NULL
);
163 dev_err(dev
, "failed to locate regmap\n");
167 err
= device_property_read_u32(parent
, "reg", &wdt
->baseaddr
);
169 dev_err(dev
, "failed to get pm8916-pon address\n");
173 irq
= platform_get_irq(pdev
, 0);
175 err
= devm_request_irq(dev
, irq
, pm8916_wdt_isr
, 0,
180 wdt
->wdev
.info
= &pm8916_wdt_pt_ident
;
182 if (irq
== -EPROBE_DEFER
)
183 return -EPROBE_DEFER
;
185 wdt
->wdev
.info
= &pm8916_wdt_ident
;
188 err
= regmap_bulk_read(wdt
->regmap
, wdt
->baseaddr
+ PON_POFF_REASON1
,
189 &poff
, ARRAY_SIZE(poff
));
191 dev_err(dev
, "failed to read POFF reason: %d\n", err
);
195 dev_dbg(dev
, "POFF reason: %#x %#x\n", poff
[0], poff
[1]);
196 if (poff
[0] & PON_POFF_REASON1_PMIC_WD
)
197 wdt
->wdev
.bootstatus
|= WDIOF_CARDRESET
;
198 if (poff
[1] & PON_POFF_REASON2_UVLO
)
199 wdt
->wdev
.bootstatus
|= WDIOF_POWERUNDER
;
200 if (poff
[1] & PON_POFF_REASON2_OTST3
)
201 wdt
->wdev
.bootstatus
|= WDIOF_OVERHEAT
;
203 err
= regmap_read(wdt
->regmap
, wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL2
,
206 dev_err(dev
, "failed to check if watchdog is active: %d\n", err
);
209 if (val
& S2_RESET_EN_BIT
)
210 set_bit(WDOG_HW_RUNNING
, &wdt
->wdev
.status
);
212 /* Configure watchdog to hard-reset mode */
213 err
= regmap_write(wdt
->regmap
,
214 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL
,
217 dev_err(dev
, "failed configure watchdog\n");
221 wdt
->wdev
.ops
= &pm8916_wdt_ops
;
222 wdt
->wdev
.parent
= dev
;
223 wdt
->wdev
.min_timeout
= PM8916_WDT_MIN_TIMEOUT
;
224 wdt
->wdev
.max_timeout
= PM8916_WDT_MAX_TIMEOUT
;
225 wdt
->wdev
.timeout
= PM8916_WDT_DEFAULT_TIMEOUT
;
226 wdt
->wdev
.pretimeout
= 0;
227 watchdog_set_drvdata(&wdt
->wdev
, wdt
);
228 platform_set_drvdata(pdev
, wdt
);
230 watchdog_init_timeout(&wdt
->wdev
, 0, dev
);
231 pm8916_wdt_configure_timers(&wdt
->wdev
);
233 return devm_watchdog_register_device(dev
, &wdt
->wdev
);
236 static int __maybe_unused
pm8916_wdt_suspend(struct device
*dev
)
238 struct pm8916_wdt
*wdt
= dev_get_drvdata(dev
);
240 if (watchdog_active(&wdt
->wdev
))
241 return pm8916_wdt_stop(&wdt
->wdev
);
246 static int __maybe_unused
pm8916_wdt_resume(struct device
*dev
)
248 struct pm8916_wdt
*wdt
= dev_get_drvdata(dev
);
250 if (watchdog_active(&wdt
->wdev
))
251 return pm8916_wdt_start(&wdt
->wdev
);
256 static SIMPLE_DEV_PM_OPS(pm8916_wdt_pm_ops
, pm8916_wdt_suspend
,
259 static const struct of_device_id pm8916_wdt_id_table
[] = {
260 { .compatible
= "qcom,pm8916-wdt" },
263 MODULE_DEVICE_TABLE(of
, pm8916_wdt_id_table
);
265 static struct platform_driver pm8916_wdt_driver
= {
266 .probe
= pm8916_wdt_probe
,
268 .name
= "pm8916-wdt",
269 .of_match_table
= pm8916_wdt_id_table
,
270 .pm
= &pm8916_wdt_pm_ops
,
273 module_platform_driver(pm8916_wdt_driver
);
275 MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
276 MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
277 MODULE_LICENSE("GPL v2");