Blame view

drivers/watchdog/stpmic1_wdt.c 3.51 KB
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
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
  // SPDX-License-Identifier: GPL-2.0
  // Copyright (C) STMicroelectronics 2018
  // Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
  
  #include <linux/kernel.h>
  #include <linux/mfd/stpmic1.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/of.h>
  #include <linux/regmap.h>
  #include <linux/slab.h>
  #include <linux/watchdog.h>
  
  /* WATCHDOG CONTROL REGISTER bit */
  #define WDT_START		BIT(0)
  #define WDT_PING		BIT(1)
  #define WDT_START_MASK		BIT(0)
  #define WDT_PING_MASK		BIT(1)
  #define WDT_STOP		0
  
  #define PMIC_WDT_MIN_TIMEOUT 1
  #define PMIC_WDT_MAX_TIMEOUT 256
  #define PMIC_WDT_DEFAULT_TIMEOUT 30
  
  static bool nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, bool, 0);
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  
  struct stpmic1_wdt {
  	struct stpmic1 *pmic;
  	struct watchdog_device wdtdev;
  };
  
  static int pmic_wdt_start(struct watchdog_device *wdd)
  {
  	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
  
  	return regmap_update_bits(wdt->pmic->regmap,
  				  WCHDG_CR, WDT_START_MASK, WDT_START);
  }
  
  static int pmic_wdt_stop(struct watchdog_device *wdd)
  {
  	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
  
  	return regmap_update_bits(wdt->pmic->regmap,
  				  WCHDG_CR, WDT_START_MASK, WDT_STOP);
  }
  
  static int pmic_wdt_ping(struct watchdog_device *wdd)
  {
  	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
  
  	return regmap_update_bits(wdt->pmic->regmap,
  				  WCHDG_CR, WDT_PING_MASK, WDT_PING);
  }
  
  static int pmic_wdt_set_timeout(struct watchdog_device *wdd,
  				unsigned int timeout)
  {
  	struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd);
  
  	wdd->timeout = timeout;
  	/* timeout is equal to register value + 1 */
  	return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1);
  }
  
  static const struct watchdog_info pmic_watchdog_info = {
  	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  	.identity = "STPMIC1 PMIC Watchdog",
  };
  
  static const struct watchdog_ops pmic_watchdog_ops = {
  	.owner = THIS_MODULE,
  	.start = pmic_wdt_start,
  	.stop = pmic_wdt_stop,
  	.ping = pmic_wdt_ping,
  	.set_timeout = pmic_wdt_set_timeout,
  };
  
  static int pmic_wdt_probe(struct platform_device *pdev)
  {
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
84
  	struct device *dev = &pdev->dev;
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
85
86
87
  	int ret;
  	struct stpmic1 *pmic;
  	struct stpmic1_wdt *wdt;
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
88
  	if (!dev->parent)
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
89
  		return -EINVAL;
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
90
  	pmic = dev_get_drvdata(dev->parent);
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
91
92
  	if (!pmic)
  		return -EINVAL;
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
93
  	wdt = devm_kzalloc(dev, sizeof(struct stpmic1_wdt), GFP_KERNEL);
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
94
95
96
97
98
99
100
101
102
  	if (!wdt)
  		return -ENOMEM;
  
  	wdt->pmic = pmic;
  
  	wdt->wdtdev.info = &pmic_watchdog_info;
  	wdt->wdtdev.ops = &pmic_watchdog_ops;
  	wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT;
  	wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT;
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
103
  	wdt->wdtdev.parent = dev;
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
104
105
  
  	wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT;
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
106
  	watchdog_init_timeout(&wdt->wdtdev, 0, dev);
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
107
108
109
  
  	watchdog_set_nowayout(&wdt->wdtdev, nowayout);
  	watchdog_set_drvdata(&wdt->wdtdev, wdt);
d07c4ad8b   Guenter Roeck   watchdog: stpmic1...
110
  	ret = devm_watchdog_register_device(dev, &wdt->wdtdev);
28804c2c8   Pascal PAILLET-LME   watchdog: stpmic1...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  	if (ret)
  		return ret;
  
  	dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed
  ");
  	return 0;
  }
  
  static const struct of_device_id of_pmic_wdt_match[] = {
  	{ .compatible = "st,stpmic1-wdt" },
  	{ },
  };
  
  MODULE_DEVICE_TABLE(of, of_pmic_wdt_match);
  
  static struct platform_driver stpmic1_wdt_driver = {
  	.probe = pmic_wdt_probe,
  	.driver = {
  		.name = "stpmic1-wdt",
  		.of_match_table = of_pmic_wdt_match,
  	},
  };
  module_platform_driver(stpmic1_wdt_driver);
  
  MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device");
  MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
  MODULE_LICENSE("GPL v2");