.. | .. |
---|
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 | }; |
---|