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_INT_RT_STS 0x10
13 #define PMIC_WD_BARK_STS_BIT BIT(6)
15 #define PON_PMIC_WD_RESET_S1_TIMER 0x54
16 #define PON_PMIC_WD_RESET_S2_TIMER 0x55
18 #define PON_PMIC_WD_RESET_S2_CTL 0x56
19 #define RESET_TYPE_WARM 0x01
20 #define RESET_TYPE_SHUTDOWN 0x04
21 #define RESET_TYPE_HARD 0x07
23 #define PON_PMIC_WD_RESET_S2_CTL2 0x57
24 #define S2_RESET_EN_BIT BIT(7)
26 #define PON_PMIC_WD_RESET_PET 0x58
27 #define WATCHDOG_PET_BIT BIT(0)
29 #define PM8916_WDT_DEFAULT_TIMEOUT 32
30 #define PM8916_WDT_MIN_TIMEOUT 1
31 #define PM8916_WDT_MAX_TIMEOUT 127
34 struct regmap
*regmap
;
35 struct watchdog_device wdev
;
39 static int pm8916_wdt_start(struct watchdog_device
*wdev
)
41 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
43 return regmap_update_bits(wdt
->regmap
,
44 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL2
,
45 S2_RESET_EN_BIT
, S2_RESET_EN_BIT
);
48 static int pm8916_wdt_stop(struct watchdog_device
*wdev
)
50 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
52 return regmap_update_bits(wdt
->regmap
,
53 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL2
,
57 static int pm8916_wdt_ping(struct watchdog_device
*wdev
)
59 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
61 return regmap_update_bits(wdt
->regmap
,
62 wdt
->baseaddr
+ PON_PMIC_WD_RESET_PET
,
63 WATCHDOG_PET_BIT
, WATCHDOG_PET_BIT
);
66 static int pm8916_wdt_configure_timers(struct watchdog_device
*wdev
)
68 struct pm8916_wdt
*wdt
= watchdog_get_drvdata(wdev
);
71 err
= regmap_write(wdt
->regmap
,
72 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S1_TIMER
,
73 wdev
->timeout
- wdev
->pretimeout
);
77 return regmap_write(wdt
->regmap
,
78 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_TIMER
,
82 static int pm8916_wdt_set_timeout(struct watchdog_device
*wdev
,
85 wdev
->timeout
= timeout
;
87 return pm8916_wdt_configure_timers(wdev
);
90 static int pm8916_wdt_set_pretimeout(struct watchdog_device
*wdev
,
91 unsigned int pretimeout
)
93 wdev
->pretimeout
= pretimeout
;
95 return pm8916_wdt_configure_timers(wdev
);
98 static irqreturn_t
pm8916_wdt_isr(int irq
, void *arg
)
100 struct pm8916_wdt
*wdt
= arg
;
103 err
= regmap_read(wdt
->regmap
, wdt
->baseaddr
+ PON_INT_RT_STS
, &sts
);
107 if (sts
& PMIC_WD_BARK_STS_BIT
)
108 watchdog_notify_pretimeout(&wdt
->wdev
);
113 static const struct watchdog_info pm8916_wdt_ident
= {
114 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
115 .identity
= "QCOM PM8916 PON WDT",
118 static const struct watchdog_info pm8916_wdt_pt_ident
= {
119 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
|
121 .identity
= "QCOM PM8916 PON WDT",
124 static const struct watchdog_ops pm8916_wdt_ops
= {
125 .owner
= THIS_MODULE
,
126 .start
= pm8916_wdt_start
,
127 .stop
= pm8916_wdt_stop
,
128 .ping
= pm8916_wdt_ping
,
129 .set_timeout
= pm8916_wdt_set_timeout
,
130 .set_pretimeout
= pm8916_wdt_set_pretimeout
,
133 static int pm8916_wdt_probe(struct platform_device
*pdev
)
135 struct device
*dev
= &pdev
->dev
;
136 struct pm8916_wdt
*wdt
;
137 struct device
*parent
;
140 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
144 parent
= dev
->parent
;
147 * The pm8916-pon-wdt is a child of the pon device, which is a child
148 * of the pm8916 mfd device. We want access to the pm8916 registers.
149 * Retrieve regmap from pm8916 (parent->parent) and base address
150 * from pm8916-pon (pon).
152 wdt
->regmap
= dev_get_regmap(parent
->parent
, NULL
);
154 dev_err(dev
, "failed to locate regmap\n");
158 err
= device_property_read_u32(parent
, "reg", &wdt
->baseaddr
);
160 dev_err(dev
, "failed to get pm8916-pon address\n");
164 irq
= platform_get_irq(pdev
, 0);
166 err
= devm_request_irq(dev
, irq
, pm8916_wdt_isr
, 0,
171 wdt
->wdev
.info
= &pm8916_wdt_pt_ident
;
173 if (irq
== -EPROBE_DEFER
)
174 return -EPROBE_DEFER
;
176 wdt
->wdev
.info
= &pm8916_wdt_ident
;
179 /* Configure watchdog to hard-reset mode */
180 err
= regmap_write(wdt
->regmap
,
181 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL
,
184 dev_err(dev
, "failed configure watchdog\n");
188 wdt
->wdev
.ops
= &pm8916_wdt_ops
,
189 wdt
->wdev
.parent
= dev
;
190 wdt
->wdev
.min_timeout
= PM8916_WDT_MIN_TIMEOUT
;
191 wdt
->wdev
.max_timeout
= PM8916_WDT_MAX_TIMEOUT
;
192 wdt
->wdev
.timeout
= PM8916_WDT_DEFAULT_TIMEOUT
;
193 wdt
->wdev
.pretimeout
= 0;
194 watchdog_set_drvdata(&wdt
->wdev
, wdt
);
196 watchdog_init_timeout(&wdt
->wdev
, 0, dev
);
197 pm8916_wdt_configure_timers(&wdt
->wdev
);
199 return devm_watchdog_register_device(dev
, &wdt
->wdev
);
202 static const struct of_device_id pm8916_wdt_id_table
[] = {
203 { .compatible
= "qcom,pm8916-wdt" },
206 MODULE_DEVICE_TABLE(of
, pm8916_wdt_id_table
);
208 static struct platform_driver pm8916_wdt_driver
= {
209 .probe
= pm8916_wdt_probe
,
211 .name
= "pm8916-wdt",
212 .of_match_table
= of_match_ptr(pm8916_wdt_id_table
),
215 module_platform_driver(pm8916_wdt_driver
);
217 MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
218 MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
219 MODULE_LICENSE("GPL v2");