Blame view

drivers/rtc/rtc-ds1374.c 10.3 KB
bf4994d78   Scott Wood   rtc: RTC class dr...
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
  /*
   * 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>.
   *
   * 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
   * recommened in .../Documentation/i2c/writing-clients section
   * "Sending and receiving", using SMBus level communication is preferred.
   */
  
  #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: ...
27
  #include <linux/slab.h>
bc96ba741   Mark Brown   rtc: convert DS13...
28
  #include <linux/pm.h>
bf4994d78   Scott Wood   rtc: RTC class dr...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  
  #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 */
  #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...
45
  static const struct i2c_device_id ds1374_id[] = {
f2eb43271   Jean Delvare   rtc-ds1374: renam...
46
  	{ "ds1374", 0 },
3760f7367   Jean Delvare   i2c: Convert most...
47
48
49
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ds1374_id);
bf4994d78   Scott Wood   rtc: RTC class dr...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  struct ds1374 {
  	struct i2c_client *client;
  	struct rtc_device *rtc;
  	struct work_struct work;
  
  	/* 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...
66
  			   int reg, int nbytes)
bf4994d78   Scott Wood   rtc: RTC class dr...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  {
  	u8 buf[4];
  	int ret;
  	int i;
  
  	if (nbytes > 4) {
  		WARN_ON(1);
  		return -EINVAL;
  	}
  
  	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...
91
  			    int reg, int nbytes)
bf4994d78   Scott Wood   rtc: RTC class dr...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  {
  	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...
120
121
  			 "oscillator discontinuity flagged, time unreliable
  ");
bf4994d78   Scott Wood   rtc: RTC class dr...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
165
166
167
168
169
170
171
172
173
174
  
  	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)
  		rtc_time_to_tm(itime, time);
  
  	return ret;
  }
  
  static int ds1374_set_time(struct device *dev, struct rtc_time *time)
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	unsigned long itime;
  
  	rtc_tm_to_time(time, &itime);
  	return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
  }
  
  /* 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: ...
175
  	if (client->irq <= 0)
bf4994d78   Scott Wood   rtc: RTC class dr...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  		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;
  
  	rtc_time_to_tm(now + cur_alarm, &alarm->time);
  	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: ...
213
  	if (client->irq <= 0)
bf4994d78   Scott Wood   rtc: RTC class dr...
214
215
216
217
218
219
220
221
  		return -EINVAL;
  
  	ret = ds1374_read_time(dev, &now);
  	if (ret < 0)
  		return ret;
  
  	rtc_tm_to_time(&alarm->time, &new_alarm);
  	rtc_tm_to_time(&now, &itime);
bf4994d78   Scott Wood   rtc: RTC class dr...
222
223
224
225
226
227
  	/* 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 ...
228
  	if (time_before_eq(new_alarm, itime))
bf4994d78   Scott Wood   rtc: RTC class dr...
229
  		new_alarm = 1;
fa7af8b1b   Roel Kluin   rtc: test before ...
230
231
  	else
  		new_alarm -= itime;
bf4994d78   Scott Wood   rtc: RTC class dr...
232
233
234
235
236
237
238
239
240
241
242
243
244
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
275
276
277
278
279
280
281
282
  
  	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;
  }
  
  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 ...
283
  		goto unlock;
bf4994d78   Scott Wood   rtc: RTC class dr...
284
285
286
287
288
289
290
291
292
293
294
  
  	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...
295
  		rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
bf4994d78   Scott Wood   rtc: RTC class dr...
296
297
298
299
300
  	}
  
  out:
  	if (!ds1374->exiting)
  		enable_irq(client->irq);
28df30e61   Jiri Slaby   rtc: ds1374, fix ...
301
  unlock:
bf4994d78   Scott Wood   rtc: RTC class dr...
302
303
  	mutex_unlock(&ds1374->mutex);
  }
16380c153   John Stultz   RTC: Convert rtc ...
304
  static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
bf4994d78   Scott Wood   rtc: RTC class dr...
305
306
307
  {
  	struct i2c_client *client = to_i2c_client(dev);
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
16380c153   John Stultz   RTC: Convert rtc ...
308
  	int ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
309
310
  
  	mutex_lock(&ds1374->mutex);
16380c153   John Stultz   RTC: Convert rtc ...
311
312
313
  	ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
  	if (ret < 0)
  		goto out;
bf4994d78   Scott Wood   rtc: RTC class dr...
314

16380c153   John Stultz   RTC: Convert rtc ...
315
  	if (enabled) {
bf4994d78   Scott Wood   rtc: RTC class dr...
316
317
  		ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
  		ret &= ~DS1374_REG_CR_WDALM;
16380c153   John Stultz   RTC: Convert rtc ...
318
319
  	} else {
  		ret &= ~DS1374_REG_CR_WACE;
bf4994d78   Scott Wood   rtc: RTC class dr...
320
  	}
16380c153   John Stultz   RTC: Convert rtc ...
321
  	ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
bf4994d78   Scott Wood   rtc: RTC class dr...
322
323
324
325
326
327
328
329
330
331
332
  
  out:
  	mutex_unlock(&ds1374->mutex);
  	return ret;
  }
  
  static const struct rtc_class_ops ds1374_rtc_ops = {
  	.read_time = ds1374_read_time,
  	.set_time = ds1374_set_time,
  	.read_alarm = ds1374_read_alarm,
  	.set_alarm = ds1374_set_alarm,
16380c153   John Stultz   RTC: Convert rtc ...
333
  	.alarm_irq_enable = ds1374_alarm_irq_enable,
bf4994d78   Scott Wood   rtc: RTC class dr...
334
  };
d2653e927   Jean Delvare   i2c: Add support ...
335
336
  static int ds1374_probe(struct i2c_client *client,
  			const struct i2c_device_id *id)
bf4994d78   Scott Wood   rtc: RTC class dr...
337
338
339
  {
  	struct ds1374 *ds1374;
  	int ret;
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
340
  	ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
bf4994d78   Scott Wood   rtc: RTC class dr...
341
342
343
344
345
346
347
348
349
350
351
  	if (!ds1374)
  		return -ENOMEM;
  
  	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...
352
  		return ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
353

b42f93173   Anton Vorontsov   rtc: rtc-ds1374: ...
354
  	if (client->irq > 0) {
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
355
  		ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
adc7b9b68   Sachin Kamat   drivers/rtc/rtc-d...
356
  					"ds1374", client);
bf4994d78   Scott Wood   rtc: RTC class dr...
357
358
359
  		if (ret) {
  			dev_err(&client->dev, "unable to request IRQ
  ");
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
360
  			return ret;
bf4994d78   Scott Wood   rtc: RTC class dr...
361
  		}
26b3c01f7   Anton Vorontsov   rtc: set wakeup c...
362
363
  
  		device_set_wakeup_capable(&client->dev, 1);
bf4994d78   Scott Wood   rtc: RTC class dr...
364
  	}
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
365
  	ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
adc7b9b68   Sachin Kamat   drivers/rtc/rtc-d...
366
  						&ds1374_rtc_ops, THIS_MODULE);
bf4994d78   Scott Wood   rtc: RTC class dr...
367
  	if (IS_ERR(ds1374->rtc)) {
bf4994d78   Scott Wood   rtc: RTC class dr...
368
369
  		dev_err(&client->dev, "unable to register the class device
  ");
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
370
  		return PTR_ERR(ds1374->rtc);
bf4994d78   Scott Wood   rtc: RTC class dr...
371
372
373
  	}
  
  	return 0;
bf4994d78   Scott Wood   rtc: RTC class dr...
374
  }
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
375
  static int ds1374_remove(struct i2c_client *client)
bf4994d78   Scott Wood   rtc: RTC class dr...
376
377
  {
  	struct ds1374 *ds1374 = i2c_get_clientdata(client);
b42f93173   Anton Vorontsov   rtc: rtc-ds1374: ...
378
  	if (client->irq > 0) {
bf4994d78   Scott Wood   rtc: RTC class dr...
379
380
381
  		mutex_lock(&ds1374->mutex);
  		ds1374->exiting = 1;
  		mutex_unlock(&ds1374->mutex);
d1a966396   Sachin Kamat   drivers/rtc/rtc-d...
382
  		devm_free_irq(&client->dev, client->irq, client);
9db8995be   Tejun Heo   rtc: don't use fl...
383
  		cancel_work_sync(&ds1374->work);
bf4994d78   Scott Wood   rtc: RTC class dr...
384
  	}
bf4994d78   Scott Wood   rtc: RTC class dr...
385
386
  	return 0;
  }
8b80ef647   Jingoo Han   drivers/rtc/rtc-d...
387
  #ifdef CONFIG_PM_SLEEP
bc96ba741   Mark Brown   rtc: convert DS13...
388
  static int ds1374_suspend(struct device *dev)
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
389
  {
bc96ba741   Mark Brown   rtc: convert DS13...
390
  	struct i2c_client *client = to_i2c_client(dev);
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
391
392
393
394
  	if (client->irq >= 0 && device_may_wakeup(&client->dev))
  		enable_irq_wake(client->irq);
  	return 0;
  }
bc96ba741   Mark Brown   rtc: convert DS13...
395
  static int ds1374_resume(struct device *dev)
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
396
  {
bc96ba741   Mark Brown   rtc: convert DS13...
397
  	struct i2c_client *client = to_i2c_client(dev);
986e36a5b   Marc Pignat   rtc: DS1374 wakeu...
398
399
400
401
  	if (client->irq >= 0 && device_may_wakeup(&client->dev))
  		disable_irq_wake(client->irq);
  	return 0;
  }
8b80ef647   Jingoo Han   drivers/rtc/rtc-d...
402
  #endif
bc96ba741   Mark Brown   rtc: convert DS13...
403
404
  
  static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
bf4994d78   Scott Wood   rtc: RTC class dr...
405
406
407
408
  static struct i2c_driver ds1374_driver = {
  	.driver = {
  		.name = "rtc-ds1374",
  		.owner = THIS_MODULE,
8b80ef647   Jingoo Han   drivers/rtc/rtc-d...
409
  		.pm = &ds1374_pm,
bf4994d78   Scott Wood   rtc: RTC class dr...
410
411
  	},
  	.probe = ds1374_probe,
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
412
  	.remove = ds1374_remove,
3760f7367   Jean Delvare   i2c: Convert most...
413
  	.id_table = ds1374_id,
bf4994d78   Scott Wood   rtc: RTC class dr...
414
  };
0abc92011   Axel Lin   rtc: convert rtc ...
415
  module_i2c_driver(ds1374_driver);
bf4994d78   Scott Wood   rtc: RTC class dr...
416
417
418
419
  
  MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
  MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
  MODULE_LICENSE("GPL");