0001
0002 #include <linux/bitops.h>
0003 #include <linux/interrupt.h>
0004 #include <linux/kernel.h>
0005 #include <linux/module.h>
0006 #include <linux/of.h>
0007 #include <linux/property.h>
0008 #include <linux/platform_device.h>
0009 #include <linux/regmap.h>
0010 #include <linux/watchdog.h>
0011
0012 #define PON_POFF_REASON1 0x0c
0013 #define PON_POFF_REASON1_PMIC_WD BIT(2)
0014 #define PON_POFF_REASON2 0x0d
0015 #define PON_POFF_REASON2_UVLO BIT(5)
0016 #define PON_POFF_REASON2_OTST3 BIT(6)
0017
0018 #define PON_INT_RT_STS 0x10
0019 #define PMIC_WD_BARK_STS_BIT BIT(6)
0020
0021 #define PON_PMIC_WD_RESET_S1_TIMER 0x54
0022 #define PON_PMIC_WD_RESET_S2_TIMER 0x55
0023
0024 #define PON_PMIC_WD_RESET_S2_CTL 0x56
0025 #define RESET_TYPE_WARM 0x01
0026 #define RESET_TYPE_SHUTDOWN 0x04
0027 #define RESET_TYPE_HARD 0x07
0028
0029 #define PON_PMIC_WD_RESET_S2_CTL2 0x57
0030 #define S2_RESET_EN_BIT BIT(7)
0031
0032 #define PON_PMIC_WD_RESET_PET 0x58
0033 #define WATCHDOG_PET_BIT BIT(0)
0034
0035 #define PM8916_WDT_DEFAULT_TIMEOUT 32
0036 #define PM8916_WDT_MIN_TIMEOUT 1
0037 #define PM8916_WDT_MAX_TIMEOUT 127
0038
0039 struct pm8916_wdt {
0040 struct regmap *regmap;
0041 struct watchdog_device wdev;
0042 u32 baseaddr;
0043 };
0044
0045 static int pm8916_wdt_start(struct watchdog_device *wdev)
0046 {
0047 struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
0048
0049 return regmap_update_bits(wdt->regmap,
0050 wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
0051 S2_RESET_EN_BIT, S2_RESET_EN_BIT);
0052 }
0053
0054 static int pm8916_wdt_stop(struct watchdog_device *wdev)
0055 {
0056 struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
0057
0058 return regmap_update_bits(wdt->regmap,
0059 wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
0060 S2_RESET_EN_BIT, 0);
0061 }
0062
0063 static int pm8916_wdt_ping(struct watchdog_device *wdev)
0064 {
0065 struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
0066
0067 return regmap_write(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_PET,
0068 WATCHDOG_PET_BIT);
0069 }
0070
0071 static int pm8916_wdt_configure_timers(struct watchdog_device *wdev)
0072 {
0073 struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
0074 int err;
0075
0076 err = regmap_write(wdt->regmap,
0077 wdt->baseaddr + PON_PMIC_WD_RESET_S1_TIMER,
0078 wdev->timeout - wdev->pretimeout);
0079 if (err)
0080 return err;
0081
0082 return regmap_write(wdt->regmap,
0083 wdt->baseaddr + PON_PMIC_WD_RESET_S2_TIMER,
0084 wdev->pretimeout);
0085 }
0086
0087 static int pm8916_wdt_set_timeout(struct watchdog_device *wdev,
0088 unsigned int timeout)
0089 {
0090 wdev->timeout = timeout;
0091
0092 return pm8916_wdt_configure_timers(wdev);
0093 }
0094
0095 static int pm8916_wdt_set_pretimeout(struct watchdog_device *wdev,
0096 unsigned int pretimeout)
0097 {
0098 wdev->pretimeout = pretimeout;
0099
0100 return pm8916_wdt_configure_timers(wdev);
0101 }
0102
0103 static irqreturn_t pm8916_wdt_isr(int irq, void *arg)
0104 {
0105 struct pm8916_wdt *wdt = arg;
0106 int err, sts;
0107
0108 err = regmap_read(wdt->regmap, wdt->baseaddr + PON_INT_RT_STS, &sts);
0109 if (err)
0110 return IRQ_HANDLED;
0111
0112 if (sts & PMIC_WD_BARK_STS_BIT)
0113 watchdog_notify_pretimeout(&wdt->wdev);
0114
0115 return IRQ_HANDLED;
0116 }
0117
0118 static const struct watchdog_info pm8916_wdt_ident = {
0119 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
0120 WDIOF_OVERHEAT | WDIOF_CARDRESET | WDIOF_POWERUNDER,
0121 .identity = "QCOM PM8916 PON WDT",
0122 };
0123
0124 static const struct watchdog_info pm8916_wdt_pt_ident = {
0125 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
0126 WDIOF_OVERHEAT | WDIOF_CARDRESET | WDIOF_POWERUNDER |
0127 WDIOF_PRETIMEOUT,
0128 .identity = "QCOM PM8916 PON WDT",
0129 };
0130
0131 static const struct watchdog_ops pm8916_wdt_ops = {
0132 .owner = THIS_MODULE,
0133 .start = pm8916_wdt_start,
0134 .stop = pm8916_wdt_stop,
0135 .ping = pm8916_wdt_ping,
0136 .set_timeout = pm8916_wdt_set_timeout,
0137 .set_pretimeout = pm8916_wdt_set_pretimeout,
0138 };
0139
0140 static int pm8916_wdt_probe(struct platform_device *pdev)
0141 {
0142 struct device *dev = &pdev->dev;
0143 struct pm8916_wdt *wdt;
0144 struct device *parent;
0145 unsigned int val;
0146 int err, irq;
0147 u8 poff[2];
0148
0149 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0150 if (!wdt)
0151 return -ENOMEM;
0152
0153 parent = dev->parent;
0154
0155
0156
0157
0158
0159
0160
0161 wdt->regmap = dev_get_regmap(parent->parent, NULL);
0162 if (!wdt->regmap) {
0163 dev_err(dev, "failed to locate regmap\n");
0164 return -ENODEV;
0165 }
0166
0167 err = device_property_read_u32(parent, "reg", &wdt->baseaddr);
0168 if (err) {
0169 dev_err(dev, "failed to get pm8916-pon address\n");
0170 return err;
0171 }
0172
0173 irq = platform_get_irq(pdev, 0);
0174 if (irq > 0) {
0175 err = devm_request_irq(dev, irq, pm8916_wdt_isr, 0,
0176 "pm8916_wdt", wdt);
0177 if (err)
0178 return err;
0179
0180 wdt->wdev.info = &pm8916_wdt_pt_ident;
0181 } else {
0182 if (irq == -EPROBE_DEFER)
0183 return -EPROBE_DEFER;
0184
0185 wdt->wdev.info = &pm8916_wdt_ident;
0186 }
0187
0188 err = regmap_bulk_read(wdt->regmap, wdt->baseaddr + PON_POFF_REASON1,
0189 &poff, ARRAY_SIZE(poff));
0190 if (err) {
0191 dev_err(dev, "failed to read POFF reason: %d\n", err);
0192 return err;
0193 }
0194
0195 dev_dbg(dev, "POFF reason: %#x %#x\n", poff[0], poff[1]);
0196 if (poff[0] & PON_POFF_REASON1_PMIC_WD)
0197 wdt->wdev.bootstatus |= WDIOF_CARDRESET;
0198 if (poff[1] & PON_POFF_REASON2_UVLO)
0199 wdt->wdev.bootstatus |= WDIOF_POWERUNDER;
0200 if (poff[1] & PON_POFF_REASON2_OTST3)
0201 wdt->wdev.bootstatus |= WDIOF_OVERHEAT;
0202
0203 err = regmap_read(wdt->regmap, wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
0204 &val);
0205 if (err) {
0206 dev_err(dev, "failed to check if watchdog is active: %d\n", err);
0207 return err;
0208 }
0209 if (val & S2_RESET_EN_BIT)
0210 set_bit(WDOG_HW_RUNNING, &wdt->wdev.status);
0211
0212
0213 err = regmap_write(wdt->regmap,
0214 wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL,
0215 RESET_TYPE_HARD);
0216 if (err) {
0217 dev_err(dev, "failed configure watchdog\n");
0218 return err;
0219 }
0220
0221 wdt->wdev.ops = &pm8916_wdt_ops,
0222 wdt->wdev.parent = dev;
0223 wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT;
0224 wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT;
0225 wdt->wdev.timeout = PM8916_WDT_DEFAULT_TIMEOUT;
0226 wdt->wdev.pretimeout = 0;
0227 watchdog_set_drvdata(&wdt->wdev, wdt);
0228 platform_set_drvdata(pdev, wdt);
0229
0230 watchdog_init_timeout(&wdt->wdev, 0, dev);
0231 pm8916_wdt_configure_timers(&wdt->wdev);
0232
0233 return devm_watchdog_register_device(dev, &wdt->wdev);
0234 }
0235
0236 static int __maybe_unused pm8916_wdt_suspend(struct device *dev)
0237 {
0238 struct pm8916_wdt *wdt = dev_get_drvdata(dev);
0239
0240 if (watchdog_active(&wdt->wdev))
0241 return pm8916_wdt_stop(&wdt->wdev);
0242
0243 return 0;
0244 }
0245
0246 static int __maybe_unused pm8916_wdt_resume(struct device *dev)
0247 {
0248 struct pm8916_wdt *wdt = dev_get_drvdata(dev);
0249
0250 if (watchdog_active(&wdt->wdev))
0251 return pm8916_wdt_start(&wdt->wdev);
0252
0253 return 0;
0254 }
0255
0256 static SIMPLE_DEV_PM_OPS(pm8916_wdt_pm_ops, pm8916_wdt_suspend,
0257 pm8916_wdt_resume);
0258
0259 static const struct of_device_id pm8916_wdt_id_table[] = {
0260 { .compatible = "qcom,pm8916-wdt" },
0261 { }
0262 };
0263 MODULE_DEVICE_TABLE(of, pm8916_wdt_id_table);
0264
0265 static struct platform_driver pm8916_wdt_driver = {
0266 .probe = pm8916_wdt_probe,
0267 .driver = {
0268 .name = "pm8916-wdt",
0269 .of_match_table = of_match_ptr(pm8916_wdt_id_table),
0270 .pm = &pm8916_wdt_pm_ops,
0271 },
0272 };
0273 module_platform_driver(pm8916_wdt_driver);
0274
0275 MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
0276 MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
0277 MODULE_LICENSE("GPL v2");