Blame view

drivers/rtc/rtc-ds1374.c 14.3 KB
bf4994d78   Scott Wood   rtc: RTC class dr...
1
2
3
4
5
6
  /*
   * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
   *
   * Based on code by Randy Vinson <rvinson@mvista.com>,
   * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
   *
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
7
   * Copyright (C) 2014 Rose Technology
bf4994d78   Scott Wood   rtc: RTC class dr...
8
9
10
11
12
13
14
15
16
   * Copyright (C) 2006-2007 Freescale Semiconductor
   *
   * 2005 (c) MontaVista Software, Inc. This file is licensed under
   * the terms of the GNU General Public License version 2. This program
   * is licensed "as is" without any warranty of any kind, whether express
   * or implied.
   */
  /*
   * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
ccf988b66   Mauro Carvalho Chehab   docs: i2c: conver...
17
   * recommended in .../Documentation/i2c/writing-clients.rst section
bf4994d78   Scott Wood   rtc: RTC class dr...
18
19
   * "Sending and receiving", using SMBus level communication is preferred.
   */
a737e835e   Joe Perches   rtc: use more sta...
20
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
bf4994d78   Scott Wood   rtc: RTC class dr...
21
22
23
24
25
26
27
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/i2c.h>
  #include <linux/rtc.h>
  #include <linux/bcd.h>
  #include <linux/workqueue.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
28
  #include <linux/slab.h>
bc96ba741   Mark Brown   rtc: convert DS13...
29
  #include <linux/pm.h>
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
30
31
32
33
34
35
36
  #ifdef CONFIG_RTC_DRV_DS1374_WDT
  #include <linux/fs.h>
  #include <linux/ioctl.h>
  #include <linux/miscdevice.h>
  #include <linux/reboot.h>
  #include <linux/watchdog.h>
  #endif
bf4994d78   Scott Wood   rtc: RTC class dr...
37
38
39
40
41
42
43
44
45
46
  
  #define DS1374_REG_TOD0		0x00 /* Time of Day */
  #define DS1374_REG_TOD1		0x01
  #define DS1374_REG_TOD2		0x02
  #define DS1374_REG_TOD3		0x03
  #define DS1374_REG_WDALM0	0x04 /* Watchdog/Alarm */
  #define DS1374_REG_WDALM1	0x05
  #define DS1374_REG_WDALM2	0x06
  #define DS1374_REG_CR		0x07 /* Control */
  #define DS1374_REG_CR_AIE	0x01 /* Alarm Int. Enable */
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
47
  #define DS1374_REG_CR_WDSTR	0x08 /* 1=INT, 0=RST */
bf4994d78   Scott Wood   rtc: RTC class dr...
48
49
50
51
52
53
  #define DS1374_REG_CR_WDALM	0x20 /* 1=Watchdog, 0=Alarm */
  #define DS1374_REG_CR_WACE	0x40 /* WD/Alarm counter enable */
  #define DS1374_REG_SR		0x08 /* Status */
  #define DS1374_REG_SR_OSF	0x80 /* Oscillator Stop Flag */
  #define DS1374_REG_SR_AF	0x01 /* Alarm Flag */
  #define DS1374_REG_TCR		0x09 /* Trickle Charge */
3760f7367   Jean Delvare   i2c: Convert most...
54
  static const struct i2c_device_id ds1374_id[] = {
f2eb43271   Jean Delvare   rtc-ds1374: renam...
55
  	{ "ds1374", 0 },
3760f7367   Jean Delvare   i2c: Convert most...
56
57
58
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ds1374_id);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
59
60
61
62
63
64
65
  #ifdef CONFIG_OF
  static const struct of_device_id ds1374_of_match[] = {
  	{ .compatible = "dallas,ds1374" },
  	{ }
  };
  MODULE_DEVICE_TABLE(of, ds1374_of_match);
  #endif
bf4994d78   Scott Wood   rtc: RTC class dr...
66
67
68
69
  struct ds1374 {
  	struct i2c_client *client;
  	struct rtc_device *rtc;
  	struct work_struct work;
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
70
71
72
  #ifdef CONFIG_RTC_DRV_DS1374_WDT
  	struct watchdog_device wdt;
  #endif
bf4994d78   Scott Wood   rtc: RTC class dr...
73
74
75
76
77
78
79
80
81
82
83
  	/* The mutex protects alarm operations, and prevents a race
  	 * between the enable_irq() in the workqueue and the free_irq()
  	 * in the remove function.
  	 */
  	struct mutex mutex;
  	int exiting;
  };
  
  static struct i2c_driver ds1374_driver;
  
  static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
adc7b9b68   Sachin Kamat   drivers/rtc/rtc-d...
84
  			   int reg, int nbytes)
bf4994d78   Scott Wood   rtc: RTC class dr...
85
86
87
88
  {
  	u8 buf[4];
  	int ret;
  	int i;
01835fadf   Srikant Ritolia   rtc: ds1374: Merg...
89
  	if (WARN_ON(nbytes > 4))
bf4994d78   Scott Wood   rtc: RTC class dr...
90
  		return -EINVAL;
bf4994d78   Scott Wood   rtc: RTC class dr...
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
  	ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
  
  	if (ret < 0)
  		return ret;
  	if (ret < nbytes)
  		return -EIO;
  
  	for (i = nbytes - 1, *time = 0; i >= 0; i--)
  		*time = (*time << 8) | buf[i];
  
  	return 0;
  }
  
  static int ds1374_write_rtc(struct i2c_client *client, u32 time,
adc7b9b68   Sachin Kamat   drivers/rtc/rtc-d...
106
  			    int reg, int nbytes)
bf4994d78   Scott Wood   rtc: RTC class dr...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  {
  	u8 buf[4];
  	int i;
  
  	if (nbytes > 4) {
  		WARN_ON(1);
  		return -EINVAL;
  	}
  
  	for (i = 0; i < nbytes; i++) {
  		buf[i] = time & 0xff;
  		time >>= 8;
  	}
  
  	return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
  }
  
  static int ds1374_check_rtc_status(struct i2c_client *client)
  {
  	int ret = 0;
  	int control, stat;
  
  	stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
  	if (stat < 0)
  		return stat;
  
  	if (stat & DS1374_REG_SR_OSF)
  		dev_warn(&client->dev,
adc7b9b68   Sachin Kamat   drivers/rtc/rtc-d...
135
136
  			 "oscillator discontinuity flagged, time unreliable
  ");
bf4994d78   Scott Wood   rtc: RTC class dr...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  
  	stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
  
  	ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
  	if (ret < 0)
  		return ret;
  
  	/* If the alarm is pending, clear it before requesting
  	 * the interrupt, so an interrupt event isn't reported
  	 * before everything is initialized.
  	 */
  
  	control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (control < 0)
  		return control;
  
  	control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
  	return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
  }
  
  static int ds1374_read_time(struct device *dev, struct rtc_time *time)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	u32 itime;
  	int ret;
  
  	ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
  	if (!ret)
ca824be9b   Alexandre Belloni   rtc: ds1374: swit...
165
  		rtc_time64_to_tm(itime, time);
bf4994d78   Scott Wood   rtc: RTC class dr...
166
167
168
169
170
171
172
  
  	return ret;
  }
  
  static int ds1374_set_time(struct device *dev, struct rtc_time *time)
  {
  	struct i2c_client *client = to_i2c_client(dev);
ca824be9b   Alexandre Belloni   rtc: ds1374: swit...
173
  	unsigned long itime = rtc_tm_to_time64(time);
bf4994d78   Scott Wood   rtc: RTC class dr...
174

bf4994d78   Scott Wood   rtc: RTC class dr...
175
176
  	return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
  }
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
177
  #ifndef CONFIG_RTC_DRV_DS1374_WDT
bf4994d78   Scott Wood   rtc: RTC class dr...
178
179
180
181
182
183
184
185
186
187
188
  /* The ds1374 has a decrementer for an alarm, rather than a comparator.
   * If the time of day is changed, then the alarm will need to be
   * reset.
   */
  static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
  	u32 now, cur_alarm;
  	int cr, sr;
  	int ret = 0;
b42f93173   Anton Vorontsov   rtc: rtc-ds1374: ...
189
  	if (client->irq <= 0)
bf4994d78   Scott Wood   rtc: RTC class dr...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  		return -EINVAL;
  
  	mutex_lock(&ds1374->mutex);
  
  	cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (ret < 0)
  		goto out;
  
  	sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
  	if (ret < 0)
  		goto out;
  
  	ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
  	if (ret)
  		goto out;
  
  	ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
  	if (ret)
  		goto out;
ca824be9b   Alexandre Belloni   rtc: ds1374: swit...
209
  	rtc_time64_to_tm(now + cur_alarm, &alarm->time);
bf4994d78   Scott Wood   rtc: RTC class dr...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  	alarm->enabled = !!(cr & DS1374_REG_CR_WACE);
  	alarm->pending = !!(sr & DS1374_REG_SR_AF);
  
  out:
  	mutex_unlock(&ds1374->mutex);
  	return ret;
  }
  
  static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
  	struct rtc_time now;
  	unsigned long new_alarm, itime;
  	int cr;
  	int ret = 0;
b42f93173   Anton Vorontsov   rtc: rtc-ds1374: ...
226
  	if (client->irq <= 0)
bf4994d78   Scott Wood   rtc: RTC class dr...
227
228
229
230
231
  		return -EINVAL;
  
  	ret = ds1374_read_time(dev, &now);
  	if (ret < 0)
  		return ret;
ca824be9b   Alexandre Belloni   rtc: ds1374: swit...
232
233
  	new_alarm = rtc_tm_to_time64(&alarm->time);
  	itime = rtc_tm_to_time64(&now);
bf4994d78   Scott Wood   rtc: RTC class dr...
234

bf4994d78   Scott Wood   rtc: RTC class dr...
235
236
237
238
239
240
  	/* This can happen due to races, in addition to dates that are
  	 * truly in the past.  To avoid requiring the caller to check for
  	 * races, dates in the past are assumed to be in the recent past
  	 * (i.e. not something that we'd rather the caller know about via
  	 * an error), and the alarm is set to go off as soon as possible.
  	 */
fa7af8b1b   Roel Kluin   rtc: test before ...
241
  	if (time_before_eq(new_alarm, itime))
bf4994d78   Scott Wood   rtc: RTC class dr...
242
  		new_alarm = 1;
fa7af8b1b   Roel Kluin   rtc: test before ...
243
244
  	else
  		new_alarm -= itime;
bf4994d78   Scott Wood   rtc: RTC class dr...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  
  	mutex_lock(&ds1374->mutex);
  
  	ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (ret < 0)
  		goto out;
  
  	/* Disable any existing alarm before setting the new one
  	 * (or lack thereof). */
  	cr &= ~DS1374_REG_CR_WACE;
  
  	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
  	if (ret < 0)
  		goto out;
  
  	ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
  	if (ret)
  		goto out;
  
  	if (alarm->enabled) {
  		cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
  		cr &= ~DS1374_REG_CR_WDALM;
  
  		ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
  	}
  
  out:
  	mutex_unlock(&ds1374->mutex);
  	return ret;
  }
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
275
  #endif
bf4994d78   Scott Wood   rtc: RTC class dr...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  
  static irqreturn_t ds1374_irq(int irq, void *dev_id)
  {
  	struct i2c_client *client = dev_id;
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
  
  	disable_irq_nosync(irq);
  	schedule_work(&ds1374->work);
  	return IRQ_HANDLED;
  }
  
  static void ds1374_work(struct work_struct *work)
  {
  	struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
  	struct i2c_client *client = ds1374->client;
  	int stat, control;
  
  	mutex_lock(&ds1374->mutex);
  
  	stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
  	if (stat < 0)
28df30e61   Jiri Slaby   rtc: ds1374, fix ...
297
  		goto unlock;
bf4994d78   Scott Wood   rtc: RTC class dr...
298
299
300
301
302
303
304
305
306
307
308
  
  	if (stat & DS1374_REG_SR_AF) {
  		stat &= ~DS1374_REG_SR_AF;
  		i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
  
  		control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  		if (control < 0)
  			goto out;
  
  		control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
  		i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
bf4994d78   Scott Wood   rtc: RTC class dr...
309
  		rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
bf4994d78   Scott Wood   rtc: RTC class dr...
310
311
312
313
314
  	}
  
  out:
  	if (!ds1374->exiting)
  		enable_irq(client->irq);
28df30e61   Jiri Slaby   rtc: ds1374, fix ...
315
  unlock:
bf4994d78   Scott Wood   rtc: RTC class dr...
316
317
  	mutex_unlock(&ds1374->mutex);
  }
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
318
  #ifndef CONFIG_RTC_DRV_DS1374_WDT
16380c153   John Stultz   RTC: Convert rtc ...
319
  static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
bf4994d78   Scott Wood   rtc: RTC class dr...
320
321
322
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
16380c153   John Stultz   RTC: Convert rtc ...
323
  	int ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
324
325
  
  	mutex_lock(&ds1374->mutex);
16380c153   John Stultz   RTC: Convert rtc ...
326
327
328
  	ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (ret < 0)
  		goto out;
bf4994d78   Scott Wood   rtc: RTC class dr...
329

16380c153   John Stultz   RTC: Convert rtc ...
330
  	if (enabled) {
bf4994d78   Scott Wood   rtc: RTC class dr...
331
332
  		ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
  		ret &= ~DS1374_REG_CR_WDALM;
16380c153   John Stultz   RTC: Convert rtc ...
333
334
  	} else {
  		ret &= ~DS1374_REG_CR_WACE;
bf4994d78   Scott Wood   rtc: RTC class dr...
335
  	}
16380c153   John Stultz   RTC: Convert rtc ...
336
  	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
bf4994d78   Scott Wood   rtc: RTC class dr...
337
338
339
340
341
  
  out:
  	mutex_unlock(&ds1374->mutex);
  	return ret;
  }
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
342
  #endif
bf4994d78   Scott Wood   rtc: RTC class dr...
343
344
345
346
  
  static const struct rtc_class_ops ds1374_rtc_ops = {
  	.read_time = ds1374_read_time,
  	.set_time = ds1374_set_time,
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
347
  #ifndef CONFIG_RTC_DRV_DS1374_WDT
bf4994d78   Scott Wood   rtc: RTC class dr...
348
349
  	.read_alarm = ds1374_read_alarm,
  	.set_alarm = ds1374_set_alarm,
16380c153   John Stultz   RTC: Convert rtc ...
350
  	.alarm_irq_enable = ds1374_alarm_irq_enable,
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
351
352
353
354
355
356
357
358
359
360
361
  #endif
  };
  
  #ifdef CONFIG_RTC_DRV_DS1374_WDT
  /*
   *****************************************************************************
   *
   * Watchdog Driver
   *
   *****************************************************************************
   */
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
362
  /* Default margin */
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
363
364
365
  #define TIMER_MARGIN_DEFAULT	32
  #define TIMER_MARGIN_MIN	1
  #define TIMER_MARGIN_MAX	4095 /* 24-bit value */
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
366

d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
367
  static int wdt_margin;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
368
369
  module_param(wdt_margin, int, 0);
  MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
370
371
372
373
  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)")");
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
374
  static const struct watchdog_info ds1374_wdt_info = {
3d6cfb36e   Alexandre Belloni   rtc: ds1374: remo...
375
  	.identity       = "DS1374 Watchdog",
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
376
377
378
  	.options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
  						WDIOF_MAGICCLOSE,
  };
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
379
  static int ds1374_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout)
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
380
  {
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
381
382
383
  	struct ds1374 *ds1374 = watchdog_get_drvdata(wdt);
  	struct i2c_client *client = ds1374->client;
  	int ret, cr;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
384

d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
385
386
387
388
389
  	wdt->timeout = timeout;
  
  	cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (cr < 0)
  		return cr;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
390
391
392
  
  	/* Disable any existing watchdog/alarm before setting the new one */
  	cr &= ~DS1374_REG_CR_WACE;
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
393
  	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
394
  	if (ret < 0)
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
395
  		return ret;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
396
397
  
  	/* Set new watchdog time */
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
398
399
400
401
  	timeout = timeout * 4096;
  	ret = ds1374_write_rtc(client, timeout, DS1374_REG_WDALM0, 3);
  	if (ret)
  		return ret;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
402
403
404
  
  	/* Enable watchdog timer */
  	cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
405
  	cr &= ~DS1374_REG_CR_WDSTR;/* for RST PIN */
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
406
  	cr &= ~DS1374_REG_CR_AIE;
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
407
  	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
408
  	if (ret < 0)
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
409
  		return ret;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
410
411
  
  	return 0;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
412
  }
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
413
414
415
  /*
   * Reload the watchdog timer.  (ie, pat the watchdog)
   */
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
416
  static int ds1374_wdt_start(struct watchdog_device *wdt)
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
417
  {
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
418
  	struct ds1374 *ds1374 = watchdog_get_drvdata(wdt);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
419
  	u32 val;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
420

d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
421
  	return ds1374_read_rtc(ds1374->client, &val, DS1374_REG_WDALM0, 3);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
422
  }
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
423
  static int ds1374_wdt_stop(struct watchdog_device *wdt)
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
424
  {
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
425
426
  	struct ds1374 *ds1374 = watchdog_get_drvdata(wdt);
  	struct i2c_client *client = ds1374->client;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
427
  	int cr;
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
428
429
430
  	cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (cr < 0)
  		return cr;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
431
432
  	/* Disable watchdog timer */
  	cr &= ~DS1374_REG_CR_WACE;
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
433
  	return i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
434
  }
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
435
436
437
438
439
  static const struct watchdog_ops ds1374_wdt_ops = {
  	.owner          = THIS_MODULE,
  	.start          = ds1374_wdt_start,
  	.stop           = ds1374_wdt_stop,
  	.set_timeout    = ds1374_wdt_settimeout,
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
440
  };
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
441
442
443
444
445
446
447
448
  #endif /*CONFIG_RTC_DRV_DS1374_WDT*/
  /*
   *****************************************************************************
   *
   *	Driver Interface
   *
   *****************************************************************************
   */
d2653e927   Jean Delvare   i2c: Add support ...
449
450
  static int ds1374_probe(struct i2c_client *client,
  			const struct i2c_device_id *id)
bf4994d78   Scott Wood   rtc: RTC class dr...
451
452
453
  {
  	struct ds1374 *ds1374;
  	int ret;
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
454
  	ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
bf4994d78   Scott Wood   rtc: RTC class dr...
455
456
  	if (!ds1374)
  		return -ENOMEM;
c11af8131   Alexandre Belloni   rtc: ds1374: fix ...
457
458
459
  	ds1374->rtc = devm_rtc_allocate_device(&client->dev);
  	if (IS_ERR(ds1374->rtc))
  		return PTR_ERR(ds1374->rtc);
bf4994d78   Scott Wood   rtc: RTC class dr...
460
461
462
463
464
465
466
467
  	ds1374->client = client;
  	i2c_set_clientdata(client, ds1374);
  
  	INIT_WORK(&ds1374->work, ds1374_work);
  	mutex_init(&ds1374->mutex);
  
  	ret = ds1374_check_rtc_status(client);
  	if (ret)
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
468
  		return ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
469

b42f93173   Anton Vorontsov   rtc: rtc-ds1374: ...
470
  	if (client->irq > 0) {
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
471
  		ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
adc7b9b68   Sachin Kamat   drivers/rtc/rtc-d...
472
  					"ds1374", client);
bf4994d78   Scott Wood   rtc: RTC class dr...
473
474
475
  		if (ret) {
  			dev_err(&client->dev, "unable to request IRQ
  ");
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
476
  			return ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
477
  		}
26b3c01f7   Anton Vorontsov   rtc: set wakeup c...
478
479
  
  		device_set_wakeup_capable(&client->dev, 1);
bf4994d78   Scott Wood   rtc: RTC class dr...
480
  	}
c11af8131   Alexandre Belloni   rtc: ds1374: fix ...
481
  	ds1374->rtc->ops = &ds1374_rtc_ops;
4136ff3a5   Alexandre Belloni   rtc: ds1374: set ...
482
  	ds1374->rtc->range_max = U32_MAX;
c11af8131   Alexandre Belloni   rtc: ds1374: fix ...
483
484
485
486
  
  	ret = rtc_register_device(ds1374->rtc);
  	if (ret)
  		return ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
487

920f91e50   Søren Andersen   drivers/rtc/rtc-d...
488
  #ifdef CONFIG_RTC_DRV_DS1374_WDT
d3de4beb1   Johnson CH Chen (陳昭勳)   rtc: ds1374: wdt:...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  	ds1374->wdt.info = &ds1374_wdt_info;
  	ds1374->wdt.ops = &ds1374_wdt_ops;
  	ds1374->wdt.timeout = TIMER_MARGIN_DEFAULT;
  	ds1374->wdt.min_timeout = TIMER_MARGIN_MIN;
  	ds1374->wdt.max_timeout = TIMER_MARGIN_MAX;
  
  	watchdog_init_timeout(&ds1374->wdt, wdt_margin, &client->dev);
  	watchdog_set_nowayout(&ds1374->wdt, nowayout);
  	watchdog_stop_on_reboot(&ds1374->wdt);
  	watchdog_stop_on_unregister(&ds1374->wdt);
  	watchdog_set_drvdata(&ds1374->wdt, ds1374);
  	ds1374_wdt_settimeout(&ds1374->wdt, ds1374->wdt.timeout);
  
  	ret = devm_watchdog_register_device(&client->dev, &ds1374->wdt);
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
503
504
  	if (ret)
  		return ret;
920f91e50   Søren Andersen   drivers/rtc/rtc-d...
505
  #endif
bf4994d78   Scott Wood   rtc: RTC class dr...
506
  	return 0;
bf4994d78   Scott Wood   rtc: RTC class dr...
507
  }
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
508
  static int ds1374_remove(struct i2c_client *client)
bf4994d78   Scott Wood   rtc: RTC class dr...
509
510
  {
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
b42f93173   Anton Vorontsov   rtc: rtc-ds1374: ...
511
  	if (client->irq > 0) {
bf4994d78   Scott Wood   rtc: RTC class dr...
512
513
514
  		mutex_lock(&ds1374->mutex);
  		ds1374->exiting = 1;
  		mutex_unlock(&ds1374->mutex);
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
515
  		devm_free_irq(&client->dev, client->irq, client);
9db8995be   Tejun Heo   rtc: don't use fl...
516
  		cancel_work_sync(&ds1374->work);
bf4994d78   Scott Wood   rtc: RTC class dr...
517
  	}
bf4994d78   Scott Wood   rtc: RTC class dr...
518
519
  	return 0;
  }
8b80ef647   Jingoo Han   drivers/rtc/rtc-d...
520
  #ifdef CONFIG_PM_SLEEP
bc96ba741   Mark Brown   rtc: convert DS13...
521
  static int ds1374_suspend(struct device *dev)
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
522
  {
bc96ba741   Mark Brown   rtc: convert DS13...
523
  	struct i2c_client *client = to_i2c_client(dev);
0d9030a2c   Octavian Purdila   rtc: fix drivers ...
524
  	if (client->irq > 0 && device_may_wakeup(&client->dev))
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
525
526
527
  		enable_irq_wake(client->irq);
  	return 0;
  }
bc96ba741   Mark Brown   rtc: convert DS13...
528
  static int ds1374_resume(struct device *dev)
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
529
  {
bc96ba741   Mark Brown   rtc: convert DS13...
530
  	struct i2c_client *client = to_i2c_client(dev);
0d9030a2c   Octavian Purdila   rtc: fix drivers ...
531
  	if (client->irq > 0 && device_may_wakeup(&client->dev))
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
532
533
534
  		disable_irq_wake(client->irq);
  	return 0;
  }
8b80ef647   Jingoo Han   drivers/rtc/rtc-d...
535
  #endif
bc96ba741   Mark Brown   rtc: convert DS13...
536
537
  
  static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
bf4994d78   Scott Wood   rtc: RTC class dr...
538
539
540
  static struct i2c_driver ds1374_driver = {
  	.driver = {
  		.name = "rtc-ds1374",
abac12e11   Javier Martinez Canillas   rtc: ds1374: Set ...
541
  		.of_match_table = of_match_ptr(ds1374_of_match),
8b80ef647   Jingoo Han   drivers/rtc/rtc-d...
542
  		.pm = &ds1374_pm,
bf4994d78   Scott Wood   rtc: RTC class dr...
543
544
  	},
  	.probe = ds1374_probe,
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
545
  	.remove = ds1374_remove,
3760f7367   Jean Delvare   i2c: Convert most...
546
  	.id_table = ds1374_id,
bf4994d78   Scott Wood   rtc: RTC class dr...
547
  };
0abc92011   Axel Lin   rtc: convert rtc ...
548
  module_i2c_driver(ds1374_driver);
bf4994d78   Scott Wood   rtc: RTC class dr...
549
550
551
552
  
  MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
  MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
  MODULE_LICENSE("GPL");