Blame view
drivers/watchdog/pm8916_wdt.c
5.98 KB
969c0acc0 watchdog: Add pm8... |
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 |
// SPDX-License-Identifier: GPL-2.0 #include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/property.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/watchdog.h> #define PON_INT_RT_STS 0x10 #define PMIC_WD_BARK_STS_BIT BIT(6) #define PON_PMIC_WD_RESET_S1_TIMER 0x54 #define PON_PMIC_WD_RESET_S2_TIMER 0x55 #define PON_PMIC_WD_RESET_S2_CTL 0x56 #define RESET_TYPE_WARM 0x01 #define RESET_TYPE_SHUTDOWN 0x04 #define RESET_TYPE_HARD 0x07 #define PON_PMIC_WD_RESET_S2_CTL2 0x57 #define S2_RESET_EN_BIT BIT(7) #define PON_PMIC_WD_RESET_PET 0x58 #define WATCHDOG_PET_BIT BIT(0) #define PM8916_WDT_DEFAULT_TIMEOUT 32 #define PM8916_WDT_MIN_TIMEOUT 1 #define PM8916_WDT_MAX_TIMEOUT 127 struct pm8916_wdt { struct regmap *regmap; struct watchdog_device wdev; u32 baseaddr; }; static int pm8916_wdt_start(struct watchdog_device *wdev) { struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); return regmap_update_bits(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2, S2_RESET_EN_BIT, S2_RESET_EN_BIT); } static int pm8916_wdt_stop(struct watchdog_device *wdev) { struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); return regmap_update_bits(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2, S2_RESET_EN_BIT, 0); } static int pm8916_wdt_ping(struct watchdog_device *wdev) { struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); return regmap_update_bits(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_PET, WATCHDOG_PET_BIT, WATCHDOG_PET_BIT); } static int pm8916_wdt_configure_timers(struct watchdog_device *wdev) { struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); int err; err = regmap_write(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S1_TIMER, wdev->timeout - wdev->pretimeout); if (err) return err; return regmap_write(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_TIMER, wdev->pretimeout); } static int pm8916_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) { wdev->timeout = timeout; return pm8916_wdt_configure_timers(wdev); } static int pm8916_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int pretimeout) { wdev->pretimeout = pretimeout; return pm8916_wdt_configure_timers(wdev); } static irqreturn_t pm8916_wdt_isr(int irq, void *arg) { struct pm8916_wdt *wdt = arg; int err, sts; err = regmap_read(wdt->regmap, wdt->baseaddr + PON_INT_RT_STS, &sts); if (err) return IRQ_HANDLED; if (sts & PMIC_WD_BARK_STS_BIT) watchdog_notify_pretimeout(&wdt->wdev); return IRQ_HANDLED; } static const struct watchdog_info pm8916_wdt_ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "QCOM PM8916 PON WDT", }; static const struct watchdog_info pm8916_wdt_pt_ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT, .identity = "QCOM PM8916 PON WDT", }; static const struct watchdog_ops pm8916_wdt_ops = { .owner = THIS_MODULE, .start = pm8916_wdt_start, .stop = pm8916_wdt_stop, .ping = pm8916_wdt_ping, .set_timeout = pm8916_wdt_set_timeout, .set_pretimeout = pm8916_wdt_set_pretimeout, }; static int pm8916_wdt_probe(struct platform_device *pdev) { |
9723a82a9 watchdog: pm8916_... |
135 |
struct device *dev = &pdev->dev; |
969c0acc0 watchdog: Add pm8... |
136 137 138 |
struct pm8916_wdt *wdt; struct device *parent; int err, irq; |
9723a82a9 watchdog: pm8916_... |
139 |
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); |
969c0acc0 watchdog: Add pm8... |
140 141 |
if (!wdt) return -ENOMEM; |
9723a82a9 watchdog: pm8916_... |
142 |
parent = dev->parent; |
969c0acc0 watchdog: Add pm8... |
143 144 145 146 147 148 149 150 151 |
/* * The pm8916-pon-wdt is a child of the pon device, which is a child * of the pm8916 mfd device. We want access to the pm8916 registers. * Retrieve regmap from pm8916 (parent->parent) and base address * from pm8916-pon (pon). */ wdt->regmap = dev_get_regmap(parent->parent, NULL); if (!wdt->regmap) { |
9723a82a9 watchdog: pm8916_... |
152 153 |
dev_err(dev, "failed to locate regmap "); |
969c0acc0 watchdog: Add pm8... |
154 155 156 157 158 |
return -ENODEV; } err = device_property_read_u32(parent, "reg", &wdt->baseaddr); if (err) { |
9723a82a9 watchdog: pm8916_... |
159 160 |
dev_err(dev, "failed to get pm8916-pon address "); |
969c0acc0 watchdog: Add pm8... |
161 162 163 164 165 |
return err; } irq = platform_get_irq(pdev, 0); if (irq > 0) { |
1993f1d7c watchdog: pm8916_... |
166 167 168 169 170 171 172 173 174 175 176 |
err = devm_request_irq(dev, irq, pm8916_wdt_isr, 0, "pm8916_wdt", wdt); if (err) return err; wdt->wdev.info = &pm8916_wdt_pt_ident; } else { if (irq == -EPROBE_DEFER) return -EPROBE_DEFER; wdt->wdev.info = &pm8916_wdt_ident; |
969c0acc0 watchdog: Add pm8... |
177 178 179 180 181 182 183 |
} /* Configure watchdog to hard-reset mode */ err = regmap_write(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL, RESET_TYPE_HARD); if (err) { |
9723a82a9 watchdog: pm8916_... |
184 185 |
dev_err(dev, "failed configure watchdog "); |
969c0acc0 watchdog: Add pm8... |
186 187 |
return err; } |
969c0acc0 watchdog: Add pm8... |
188 |
wdt->wdev.ops = &pm8916_wdt_ops, |
9723a82a9 watchdog: pm8916_... |
189 |
wdt->wdev.parent = dev; |
969c0acc0 watchdog: Add pm8... |
190 191 192 193 194 |
wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT; wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT; wdt->wdev.timeout = PM8916_WDT_DEFAULT_TIMEOUT; wdt->wdev.pretimeout = 0; watchdog_set_drvdata(&wdt->wdev, wdt); |
b1413e6ed watchdog: pm8916_... |
195 |
platform_set_drvdata(pdev, wdt); |
969c0acc0 watchdog: Add pm8... |
196 |
|
9723a82a9 watchdog: pm8916_... |
197 |
watchdog_init_timeout(&wdt->wdev, 0, dev); |
969c0acc0 watchdog: Add pm8... |
198 |
pm8916_wdt_configure_timers(&wdt->wdev); |
9723a82a9 watchdog: pm8916_... |
199 |
return devm_watchdog_register_device(dev, &wdt->wdev); |
969c0acc0 watchdog: Add pm8... |
200 |
} |
b1413e6ed watchdog: pm8916_... |
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
static int __maybe_unused pm8916_wdt_suspend(struct device *dev) { struct pm8916_wdt *wdt = dev_get_drvdata(dev); if (watchdog_active(&wdt->wdev)) return pm8916_wdt_stop(&wdt->wdev); return 0; } static int __maybe_unused pm8916_wdt_resume(struct device *dev) { struct pm8916_wdt *wdt = dev_get_drvdata(dev); if (watchdog_active(&wdt->wdev)) return pm8916_wdt_start(&wdt->wdev); return 0; } static SIMPLE_DEV_PM_OPS(pm8916_wdt_pm_ops, pm8916_wdt_suspend, pm8916_wdt_resume); |
969c0acc0 watchdog: Add pm8... |
223 224 225 226 227 228 229 230 231 232 233 |
static const struct of_device_id pm8916_wdt_id_table[] = { { .compatible = "qcom,pm8916-wdt" }, { } }; MODULE_DEVICE_TABLE(of, pm8916_wdt_id_table); static struct platform_driver pm8916_wdt_driver = { .probe = pm8916_wdt_probe, .driver = { .name = "pm8916-wdt", .of_match_table = of_match_ptr(pm8916_wdt_id_table), |
b1413e6ed watchdog: pm8916_... |
234 |
.pm = &pm8916_wdt_pm_ops, |
969c0acc0 watchdog: Add pm8... |
235 236 237 238 239 240 241 |
}, }; module_platform_driver(pm8916_wdt_driver); MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>"); MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver"); MODULE_LICENSE("GPL v2"); |