| .. | .. |
|---|
| 11 | 11 | #include <linux/platform_device.h> |
|---|
| 12 | 12 | #include <linux/uaccess.h> |
|---|
| 13 | 13 | #include <linux/slab.h> |
|---|
| 14 | +#include <linux/i2c.h> |
|---|
| 14 | 15 | #include <linux/delay.h> |
|---|
| 15 | 16 | #include <linux/jiffies.h> |
|---|
| 16 | 17 | #include <linux/mfd/da9062/registers.h> |
|---|
| 17 | 18 | #include <linux/mfd/da9062/core.h> |
|---|
| 19 | +#include <linux/property.h> |
|---|
| 18 | 20 | #include <linux/regmap.h> |
|---|
| 19 | 21 | #include <linux/of.h> |
|---|
| 20 | 22 | |
|---|
| .. | .. |
|---|
| 30 | 32 | struct da9062_watchdog { |
|---|
| 31 | 33 | struct da9062 *hw; |
|---|
| 32 | 34 | struct watchdog_device wdtdev; |
|---|
| 35 | + bool use_sw_pm; |
|---|
| 33 | 36 | }; |
|---|
| 37 | + |
|---|
| 38 | +static unsigned int da9062_wdt_read_timeout(struct da9062_watchdog *wdt) |
|---|
| 39 | +{ |
|---|
| 40 | + unsigned int val; |
|---|
| 41 | + |
|---|
| 42 | + regmap_read(wdt->hw->regmap, DA9062AA_CONTROL_D, &val); |
|---|
| 43 | + |
|---|
| 44 | + return wdt_timeout[val & DA9062AA_TWDSCALE_MASK]; |
|---|
| 45 | +} |
|---|
| 34 | 46 | |
|---|
| 35 | 47 | static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) |
|---|
| 36 | 48 | { |
|---|
| .. | .. |
|---|
| 46 | 58 | |
|---|
| 47 | 59 | static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) |
|---|
| 48 | 60 | { |
|---|
| 49 | | - int ret; |
|---|
| 50 | | - |
|---|
| 51 | | - ret = regmap_update_bits(wdt->hw->regmap, |
|---|
| 52 | | - DA9062AA_CONTROL_F, |
|---|
| 53 | | - DA9062AA_WATCHDOG_MASK, |
|---|
| 54 | | - DA9062AA_WATCHDOG_MASK); |
|---|
| 55 | | - |
|---|
| 56 | | - return ret; |
|---|
| 61 | + return regmap_update_bits(wdt->hw->regmap, DA9062AA_CONTROL_F, |
|---|
| 62 | + DA9062AA_WATCHDOG_MASK, |
|---|
| 63 | + DA9062AA_WATCHDOG_MASK); |
|---|
| 57 | 64 | } |
|---|
| 58 | 65 | |
|---|
| 59 | 66 | static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, |
|---|
| .. | .. |
|---|
| 140 | 147 | void *data) |
|---|
| 141 | 148 | { |
|---|
| 142 | 149 | struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); |
|---|
| 150 | + struct i2c_client *client = to_i2c_client(wdt->hw->dev); |
|---|
| 143 | 151 | int ret; |
|---|
| 144 | 152 | |
|---|
| 145 | | - ret = regmap_write(wdt->hw->regmap, |
|---|
| 146 | | - DA9062AA_CONTROL_F, |
|---|
| 147 | | - DA9062AA_SHUTDOWN_MASK); |
|---|
| 148 | | - if (ret) |
|---|
| 153 | + /* Don't use regmap because it is not atomic safe */ |
|---|
| 154 | + ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, |
|---|
| 155 | + DA9062AA_SHUTDOWN_MASK); |
|---|
| 156 | + if (ret < 0) |
|---|
| 149 | 157 | dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", |
|---|
| 150 | 158 | ret); |
|---|
| 151 | 159 | |
|---|
| .. | .. |
|---|
| 178 | 186 | |
|---|
| 179 | 187 | static int da9062_wdt_probe(struct platform_device *pdev) |
|---|
| 180 | 188 | { |
|---|
| 181 | | - int ret; |
|---|
| 189 | + struct device *dev = &pdev->dev; |
|---|
| 190 | + unsigned int timeout; |
|---|
| 182 | 191 | struct da9062 *chip; |
|---|
| 183 | 192 | struct da9062_watchdog *wdt; |
|---|
| 184 | 193 | |
|---|
| 185 | | - chip = dev_get_drvdata(pdev->dev.parent); |
|---|
| 194 | + chip = dev_get_drvdata(dev->parent); |
|---|
| 186 | 195 | if (!chip) |
|---|
| 187 | 196 | return -EINVAL; |
|---|
| 188 | 197 | |
|---|
| 189 | | - wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); |
|---|
| 198 | + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); |
|---|
| 190 | 199 | if (!wdt) |
|---|
| 191 | 200 | return -ENOMEM; |
|---|
| 201 | + |
|---|
| 202 | + wdt->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm"); |
|---|
| 192 | 203 | |
|---|
| 193 | 204 | wdt->hw = chip; |
|---|
| 194 | 205 | |
|---|
| .. | .. |
|---|
| 199 | 210 | wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS; |
|---|
| 200 | 211 | wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; |
|---|
| 201 | 212 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; |
|---|
| 202 | | - wdt->wdtdev.parent = &pdev->dev; |
|---|
| 213 | + wdt->wdtdev.parent = dev; |
|---|
| 203 | 214 | |
|---|
| 204 | 215 | watchdog_set_restart_priority(&wdt->wdtdev, 128); |
|---|
| 205 | 216 | |
|---|
| 206 | 217 | watchdog_set_drvdata(&wdt->wdtdev, wdt); |
|---|
| 218 | + dev_set_drvdata(dev, &wdt->wdtdev); |
|---|
| 207 | 219 | |
|---|
| 208 | | - ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); |
|---|
| 209 | | - if (ret < 0) { |
|---|
| 210 | | - dev_err(wdt->hw->dev, |
|---|
| 211 | | - "watchdog registration failed (%d)\n", ret); |
|---|
| 212 | | - return ret; |
|---|
| 220 | + timeout = da9062_wdt_read_timeout(wdt); |
|---|
| 221 | + if (timeout) |
|---|
| 222 | + wdt->wdtdev.timeout = timeout; |
|---|
| 223 | + |
|---|
| 224 | + /* Set timeout from DT value if available */ |
|---|
| 225 | + watchdog_init_timeout(&wdt->wdtdev, 0, dev); |
|---|
| 226 | + |
|---|
| 227 | + if (timeout) { |
|---|
| 228 | + da9062_wdt_set_timeout(&wdt->wdtdev, wdt->wdtdev.timeout); |
|---|
| 229 | + set_bit(WDOG_HW_RUNNING, &wdt->wdtdev.status); |
|---|
| 213 | 230 | } |
|---|
| 214 | 231 | |
|---|
| 215 | | - return da9062_wdt_ping(&wdt->wdtdev); |
|---|
| 232 | + return devm_watchdog_register_device(dev, &wdt->wdtdev); |
|---|
| 216 | 233 | } |
|---|
| 234 | + |
|---|
| 235 | +static int __maybe_unused da9062_wdt_suspend(struct device *dev) |
|---|
| 236 | +{ |
|---|
| 237 | + struct watchdog_device *wdd = dev_get_drvdata(dev); |
|---|
| 238 | + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); |
|---|
| 239 | + |
|---|
| 240 | + if (!wdt->use_sw_pm) |
|---|
| 241 | + return 0; |
|---|
| 242 | + |
|---|
| 243 | + if (watchdog_active(wdd)) |
|---|
| 244 | + return da9062_wdt_stop(wdd); |
|---|
| 245 | + |
|---|
| 246 | + return 0; |
|---|
| 247 | +} |
|---|
| 248 | + |
|---|
| 249 | +static int __maybe_unused da9062_wdt_resume(struct device *dev) |
|---|
| 250 | +{ |
|---|
| 251 | + struct watchdog_device *wdd = dev_get_drvdata(dev); |
|---|
| 252 | + struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); |
|---|
| 253 | + |
|---|
| 254 | + if (!wdt->use_sw_pm) |
|---|
| 255 | + return 0; |
|---|
| 256 | + |
|---|
| 257 | + if (watchdog_active(wdd)) |
|---|
| 258 | + return da9062_wdt_start(wdd); |
|---|
| 259 | + |
|---|
| 260 | + return 0; |
|---|
| 261 | +} |
|---|
| 262 | + |
|---|
| 263 | +static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops, |
|---|
| 264 | + da9062_wdt_suspend, da9062_wdt_resume); |
|---|
| 217 | 265 | |
|---|
| 218 | 266 | static struct platform_driver da9062_wdt_driver = { |
|---|
| 219 | 267 | .probe = da9062_wdt_probe, |
|---|
| 220 | 268 | .driver = { |
|---|
| 221 | 269 | .name = "da9062-watchdog", |
|---|
| 270 | + .pm = &da9062_wdt_pm_ops, |
|---|
| 222 | 271 | .of_match_table = da9062_compatible_id_table, |
|---|
| 223 | 272 | }, |
|---|
| 224 | 273 | }; |
|---|