Blame view

drivers/watchdog/da9052_wdt.c 4.59 KB
2e62c4988   Marcus Folkesson   watchdog: add SPD...
1
  // SPDX-License-Identifier: GPL-2.0+
664a0d786   Ashish Jangam   Watchdog: DA9052/...
2
3
4
5
6
7
8
  /*
   * System monitoring driver for DA9052 PMICs.
   *
   * Copyright(c) 2012 Dialog Semiconductor Ltd.
   *
   * Author: Anthony Olech <Anthony.Olech@diasemi.com>
   *
664a0d786   Ashish Jangam   Watchdog: DA9052/...
9
10
11
12
13
14
15
16
17
18
19
   */
  
  #include <linux/module.h>
  #include <linux/delay.h>
  #include <linux/uaccess.h>
  #include <linux/platform_device.h>
  #include <linux/time.h>
  #include <linux/watchdog.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/jiffies.h>
664a0d786   Ashish Jangam   Watchdog: DA9052/...
20
21
22
23
24
25
26
27
28
29
  
  #include <linux/mfd/da9052/reg.h>
  #include <linux/mfd/da9052/da9052.h>
  
  #define DA9052_DEF_TIMEOUT	4
  #define DA9052_TWDMIN		256
  
  struct da9052_wdt_data {
  	struct watchdog_device wdt;
  	struct da9052 *da9052;
664a0d786   Ashish Jangam   Watchdog: DA9052/...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  	unsigned long jpast;
  };
  
  static const struct {
  	u8 reg_val;
  	int time;  /* Seconds */
  } da9052_wdt_maps[] = {
  	{ 1, 2 },
  	{ 2, 4 },
  	{ 3, 8 },
  	{ 4, 16 },
  	{ 5, 32 },
  	{ 5, 33 },  /* Actual time  32.768s so included both 32s and 33s */
  	{ 6, 65 },
  	{ 6, 66 },  /* Actual time 65.536s so include both, 65s and 66s */
  	{ 7, 131 },
  };
664a0d786   Ashish Jangam   Watchdog: DA9052/...
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
  static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev,
  				  unsigned int timeout)
  {
  	struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct da9052 *da9052 = driver_data->da9052;
  	int ret, i;
  
  	/*
  	 * Disable the Watchdog timer before setting
  	 * new time out.
  	 */
  	ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
  				DA9052_CONTROLD_TWDSCALE, 0);
  	if (ret < 0) {
  		dev_err(da9052->dev, "Failed to disable watchdog bit, %d
  ",
  			ret);
  		return ret;
  	}
  	if (timeout) {
  		/*
  		 * To change the timeout, da9052 needs to
  		 * be disabled for at least 150 us.
  		 */
  		udelay(150);
  
  		/* Set the desired timeout */
  		for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++)
  			if (da9052_wdt_maps[i].time == timeout)
  				break;
  
  		if (i == ARRAY_SIZE(da9052_wdt_maps))
  			ret = -EINVAL;
  		else
  			ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
  						DA9052_CONTROLD_TWDSCALE,
  						da9052_wdt_maps[i].reg_val);
  		if (ret < 0) {
  			dev_err(da9052->dev,
  				"Failed to update timescale bit, %d
  ", ret);
  			return ret;
  		}
  
  		wdt_dev->timeout = timeout;
  		driver_data->jpast = jiffies;
  	}
  
  	return 0;
  }
664a0d786   Ashish Jangam   Watchdog: DA9052/...
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
  static int da9052_wdt_start(struct watchdog_device *wdt_dev)
  {
  	return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
  }
  
  static int da9052_wdt_stop(struct watchdog_device *wdt_dev)
  {
  	return da9052_wdt_set_timeout(wdt_dev, 0);
  }
  
  static int da9052_wdt_ping(struct watchdog_device *wdt_dev)
  {
  	struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);
  	struct da9052 *da9052 = driver_data->da9052;
  	unsigned long msec, jnow = jiffies;
  	int ret;
  
  	/*
  	 * We have a minimum time for watchdog window called TWDMIN. A write
  	 * to the watchdog before this elapsed time should cause an error.
  	 */
  	msec = (jnow - driver_data->jpast) * 1000/HZ;
  	if (msec < DA9052_TWDMIN)
  		mdelay(msec);
  
  	/* Reset the watchdog timer */
  	ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
  				DA9052_CONTROLD_WATCHDOG, 1 << 7);
  	if (ret < 0)
189c049a0   Guenter Roeck   watchdog: da9052_...
126
  		return ret;
664a0d786   Ashish Jangam   Watchdog: DA9052/...
127
128
129
130
131
  
  	/*
  	 * FIXME: Reset the watchdog core, in general PMIC
  	 * is supposed to do this
  	 */
189c049a0   Guenter Roeck   watchdog: da9052_...
132
133
  	return da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
  				 DA9052_CONTROLD_WATCHDOG, 0 << 7);
664a0d786   Ashish Jangam   Watchdog: DA9052/...
134
  }
6c368932f   Bhumika Goyal   watchdog: constif...
135
  static const struct watchdog_info da9052_wdt_info = {
664a0d786   Ashish Jangam   Watchdog: DA9052/...
136
137
138
139
140
141
142
143
144
145
  	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  	.identity	= "DA9052 Watchdog",
  };
  
  static const struct watchdog_ops da9052_wdt_ops = {
  	.owner = THIS_MODULE,
  	.start = da9052_wdt_start,
  	.stop = da9052_wdt_stop,
  	.ping = da9052_wdt_ping,
  	.set_timeout = da9052_wdt_set_timeout,
664a0d786   Ashish Jangam   Watchdog: DA9052/...
146
  };
2d991a164   Bill Pemberton   watchdog: remove ...
147
  static int da9052_wdt_probe(struct platform_device *pdev)
664a0d786   Ashish Jangam   Watchdog: DA9052/...
148
  {
f7e29623e   Guenter Roeck   watchdog: da9052_...
149
150
  	struct device *dev = &pdev->dev;
  	struct da9052 *da9052 = dev_get_drvdata(dev->parent);
664a0d786   Ashish Jangam   Watchdog: DA9052/...
151
152
153
  	struct da9052_wdt_data *driver_data;
  	struct watchdog_device *da9052_wdt;
  	int ret;
f7e29623e   Guenter Roeck   watchdog: da9052_...
154
  	driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL);
189c049a0   Guenter Roeck   watchdog: da9052_...
155
156
  	if (!driver_data)
  		return -ENOMEM;
664a0d786   Ashish Jangam   Watchdog: DA9052/...
157
158
159
160
161
162
163
  	driver_data->da9052 = da9052;
  
  	da9052_wdt = &driver_data->wdt;
  
  	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
  	da9052_wdt->info = &da9052_wdt_info;
  	da9052_wdt->ops = &da9052_wdt_ops;
f7e29623e   Guenter Roeck   watchdog: da9052_...
164
  	da9052_wdt->parent = dev;
664a0d786   Ashish Jangam   Watchdog: DA9052/...
165
  	watchdog_set_drvdata(da9052_wdt, driver_data);
664a0d786   Ashish Jangam   Watchdog: DA9052/...
166
167
168
  	ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,
  				DA9052_CONTROLD_TWDSCALE, 0);
  	if (ret < 0) {
f7e29623e   Guenter Roeck   watchdog: da9052_...
169
170
  		dev_err(dev, "Failed to disable watchdog bits, %d
  ", ret);
189c049a0   Guenter Roeck   watchdog: da9052_...
171
  		return ret;
664a0d786   Ashish Jangam   Watchdog: DA9052/...
172
  	}
60415f701   Wolfram Sang   watchdog: da9052_...
173
  	return devm_watchdog_register_device(dev, &driver_data->wdt);
664a0d786   Ashish Jangam   Watchdog: DA9052/...
174
  }
664a0d786   Ashish Jangam   Watchdog: DA9052/...
175
176
  static struct platform_driver da9052_wdt_driver = {
  	.probe = da9052_wdt_probe,
664a0d786   Ashish Jangam   Watchdog: DA9052/...
177
178
179
180
181
182
183
184
185
186
187
  	.driver = {
  		.name	= "da9052-watchdog",
  	},
  };
  
  module_platform_driver(da9052_wdt_driver);
  
  MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
  MODULE_DESCRIPTION("DA9052 SM Device Driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:da9052-watchdog");