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 pm8916_wdt
*wdt
;
136 struct device
*parent
;
139 wdt
= devm_kzalloc(&pdev
->dev
, sizeof(*wdt
), GFP_KERNEL
);
143 parent
= pdev
->dev
.parent
;
146 * The pm8916-pon-wdt is a child of the pon device, which is a child
147 * of the pm8916 mfd device. We want access to the pm8916 registers.
148 * Retrieve regmap from pm8916 (parent->parent) and base address
149 * from pm8916-pon (pon).
151 wdt
->regmap
= dev_get_regmap(parent
->parent
, NULL
);
153 dev_err(&pdev
->dev
, "failed to locate regmap\n");
157 err
= device_property_read_u32(parent
, "reg", &wdt
->baseaddr
);
159 dev_err(&pdev
->dev
, "failed to get pm8916-pon address\n");
163 irq
= platform_get_irq(pdev
, 0);
165 if (devm_request_irq(&pdev
->dev
, irq
, pm8916_wdt_isr
, 0,
170 /* Configure watchdog to hard-reset mode */
171 err
= regmap_write(wdt
->regmap
,
172 wdt
->baseaddr
+ PON_PMIC_WD_RESET_S2_CTL
,
175 dev_err(&pdev
->dev
, "failed configure watchdog\n");
179 wdt
->wdev
.info
= (irq
> 0) ? &pm8916_wdt_pt_ident
: &pm8916_wdt_ident
,
180 wdt
->wdev
.ops
= &pm8916_wdt_ops
,
181 wdt
->wdev
.parent
= &pdev
->dev
;
182 wdt
->wdev
.min_timeout
= PM8916_WDT_MIN_TIMEOUT
;
183 wdt
->wdev
.max_timeout
= PM8916_WDT_MAX_TIMEOUT
;
184 wdt
->wdev
.timeout
= PM8916_WDT_DEFAULT_TIMEOUT
;
185 wdt
->wdev
.pretimeout
= 0;
186 watchdog_set_drvdata(&wdt
->wdev
, wdt
);
188 watchdog_init_timeout(&wdt
->wdev
, 0, &pdev
->dev
);
189 pm8916_wdt_configure_timers(&wdt
->wdev
);
191 return devm_watchdog_register_device(&pdev
->dev
, &wdt
->wdev
);
194 static const struct of_device_id pm8916_wdt_id_table
[] = {
195 { .compatible
= "qcom,pm8916-wdt" },
198 MODULE_DEVICE_TABLE(of
, pm8916_wdt_id_table
);
200 static struct platform_driver pm8916_wdt_driver
= {
201 .probe
= pm8916_wdt_probe
,
203 .name
= "pm8916-wdt",
204 .of_match_table
= of_match_ptr(pm8916_wdt_id_table
),
207 module_platform_driver(pm8916_wdt_driver
);
209 MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
210 MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
211 MODULE_LICENSE("GPL v2");