Blame view

drivers/rtc/rtc-s35390a.c 10.5 KB
c46288b09   Byron Bradley   rtc: add support ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * Seiko Instruments S-35390A RTC Driver
   *
   * Copyright (c) 2007 Byron Bradley
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/module.h>
  #include <linux/rtc.h>
  #include <linux/i2c.h>
  #include <linux/bitrev.h>
  #include <linux/bcd.h>
  #include <linux/slab.h>
  
  #define S35390A_CMD_STATUS1	0
  #define S35390A_CMD_STATUS2	1
  #define S35390A_CMD_TIME1	2
542dd33a4   Michael Langer   drivers/rtc/rtc-s...
22
23
  #define S35390A_CMD_TIME2	3
  #define S35390A_CMD_INT2_REG1	5
c46288b09   Byron Bradley   rtc: add support ...
24
25
26
27
28
29
30
31
  
  #define S35390A_BYTE_YEAR	0
  #define S35390A_BYTE_MONTH	1
  #define S35390A_BYTE_DAY	2
  #define S35390A_BYTE_WDAY	3
  #define S35390A_BYTE_HOURS	4
  #define S35390A_BYTE_MINS	5
  #define S35390A_BYTE_SECS	6
542dd33a4   Michael Langer   drivers/rtc/rtc-s...
32
33
34
  #define S35390A_ALRM_BYTE_WDAY	0
  #define S35390A_ALRM_BYTE_HOURS	1
  #define S35390A_ALRM_BYTE_MINS	2
c46288b09   Byron Bradley   rtc: add support ...
35
36
37
38
39
  #define S35390A_FLAG_POC	0x01
  #define S35390A_FLAG_BLD	0x02
  #define S35390A_FLAG_24H	0x40
  #define S35390A_FLAG_RESET	0x80
  #define S35390A_FLAG_TEST	0x01
542dd33a4   Michael Langer   drivers/rtc/rtc-s...
40
41
42
43
44
45
  #define S35390A_INT2_MODE_MASK		0xF0
  
  #define S35390A_INT2_MODE_NOINTR	0x00
  #define S35390A_INT2_MODE_FREQ		0x10
  #define S35390A_INT2_MODE_ALARM		0x40
  #define S35390A_INT2_MODE_PMIN_EDG	0x20
3760f7367   Jean Delvare   i2c: Convert most...
46
47
48
49
50
  static const struct i2c_device_id s35390a_id[] = {
  	{ "s35390a", 0 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, s35390a_id);
c46288b09   Byron Bradley   rtc: add support ...
51
52
53
54
55
56
57
58
59
60
  struct s35390a {
  	struct i2c_client *client[8];
  	struct rtc_device *rtc;
  	int twentyfourhour;
  };
  
  static int s35390a_set_reg(struct s35390a *s35390a, int reg, char *buf, int len)
  {
  	struct i2c_client *client = s35390a->client[reg];
  	struct i2c_msg msg[] = {
65659f639   Shubhrajyoti D   drivers/rtc/rtc-s...
61
62
63
64
65
  		{
  			.addr = client->addr,
  			.len = len,
  			.buf = buf
  		},
c46288b09   Byron Bradley   rtc: add support ...
66
67
68
69
70
71
72
73
74
75
76
77
  	};
  
  	if ((i2c_transfer(client->adapter, msg, 1)) != 1)
  		return -EIO;
  
  	return 0;
  }
  
  static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
  {
  	struct i2c_client *client = s35390a->client[reg];
  	struct i2c_msg msg[] = {
65659f639   Shubhrajyoti D   drivers/rtc/rtc-s...
78
79
80
81
82
83
  		{
  			.addr = client->addr,
  			.flags = I2C_M_RD,
  			.len = len,
  			.buf = buf
  		},
c46288b09   Byron Bradley   rtc: add support ...
84
85
86
87
88
89
90
91
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
120
121
122
123
  	};
  
  	if ((i2c_transfer(client->adapter, msg, 1)) != 1)
  		return -EIO;
  
  	return 0;
  }
  
  static int s35390a_reset(struct s35390a *s35390a)
  {
  	char buf[1];
  
  	if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0)
  		return -EIO;
  
  	if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD)))
  		return 0;
  
  	buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H);
  	buf[0] &= 0xf0;
  	return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
  }
  
  static int s35390a_disable_test_mode(struct s35390a *s35390a)
  {
  	char buf[1];
  
  	if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)) < 0)
  		return -EIO;
  
  	if (!(buf[0] & S35390A_FLAG_TEST))
  		return 0;
  
  	buf[0] &= ~S35390A_FLAG_TEST;
  	return s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf));
  }
  
  static char s35390a_hr2reg(struct s35390a *s35390a, int hour)
  {
  	if (s35390a->twentyfourhour)
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
124
  		return bin2bcd(hour);
c46288b09   Byron Bradley   rtc: add support ...
125
126
  
  	if (hour < 12)
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
127
  		return bin2bcd(hour);
c46288b09   Byron Bradley   rtc: add support ...
128

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
129
  	return 0x40 | bin2bcd(hour - 12);
c46288b09   Byron Bradley   rtc: add support ...
130
131
132
133
134
135
136
  }
  
  static int s35390a_reg2hr(struct s35390a *s35390a, char reg)
  {
  	unsigned hour;
  
  	if (s35390a->twentyfourhour)
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
137
  		return bcd2bin(reg & 0x3f);
c46288b09   Byron Bradley   rtc: add support ...
138

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
139
  	hour = bcd2bin(reg & 0x3f);
c46288b09   Byron Bradley   rtc: add support ...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  	if (reg & 0x40)
  		hour += 12;
  
  	return hour;
  }
  
  static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
  {
  	struct s35390a	*s35390a = i2c_get_clientdata(client);
  	int i, err;
  	char buf[7];
  
  	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, "
  		"mon=%d, year=%d, wday=%d
  ", __func__, tm->tm_sec,
  		tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
  		tm->tm_wday);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
157
158
159
160
  	buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100);
  	buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1);
  	buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday);
  	buf[S35390A_BYTE_WDAY] = bin2bcd(tm->tm_wday);
c46288b09   Byron Bradley   rtc: add support ...
161
  	buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
162
163
  	buf[S35390A_BYTE_MINS] = bin2bcd(tm->tm_min);
  	buf[S35390A_BYTE_SECS] = bin2bcd(tm->tm_sec);
c46288b09   Byron Bradley   rtc: add support ...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  
  	/* This chip expects the bits of each byte to be in reverse order */
  	for (i = 0; i < 7; ++i)
  		buf[i] = bitrev8(buf[i]);
  
  	err = s35390a_set_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
  
  	return err;
  }
  
  static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
  {
  	struct s35390a *s35390a = i2c_get_clientdata(client);
  	char buf[7];
  	int i, err;
  
  	err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
  	if (err < 0)
  		return err;
  
  	/* This chip returns the bits of each byte in reverse order */
  	for (i = 0; i < 7; ++i)
  		buf[i] = bitrev8(buf[i]);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
187
188
  	tm->tm_sec = bcd2bin(buf[S35390A_BYTE_SECS]);
  	tm->tm_min = bcd2bin(buf[S35390A_BYTE_MINS]);
c46288b09   Byron Bradley   rtc: add support ...
189
  	tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
190
191
192
193
  	tm->tm_wday = bcd2bin(buf[S35390A_BYTE_WDAY]);
  	tm->tm_mday = bcd2bin(buf[S35390A_BYTE_DAY]);
  	tm->tm_mon = bcd2bin(buf[S35390A_BYTE_MONTH]) - 1;
  	tm->tm_year = bcd2bin(buf[S35390A_BYTE_YEAR]) + 100;
c46288b09   Byron Bradley   rtc: add support ...
194
195
196
197
198
199
200
201
202
  
  	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, "
  		"mon=%d, year=%d, wday=%d
  ", __func__, tm->tm_sec,
  		tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
  		tm->tm_wday);
  
  	return rtc_valid_tm(tm);
  }
542dd33a4   Michael Langer   drivers/rtc/rtc-s...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
  {
  	struct s35390a *s35390a = i2c_get_clientdata(client);
  	char buf[3], sts = 0;
  	int err, i;
  
  	dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\
  		"mon=%d, year=%d, wday=%d
  ", __func__, alm->time.tm_sec,
  		alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday,
  		alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday);
  
  	/* disable interrupt */
  	err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
  	if (err < 0)
  		return err;
  
  	/* clear pending interrupt, if any */
  	err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts));
  	if (err < 0)
  		return err;
  
  	if (alm->enabled)
  		sts = S35390A_INT2_MODE_ALARM;
  	else
  		sts = S35390A_INT2_MODE_NOINTR;
  
  	/* This chip expects the bits of each byte to be in reverse order */
  	sts = bitrev8(sts);
  
  	/* set interupt mode*/
  	err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
  	if (err < 0)
  		return err;
  
  	if (alm->time.tm_wday != -1)
  		buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80;
  
  	buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
  			alm->time.tm_hour) | 0x80;
  	buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80;
  
  	if (alm->time.tm_hour >= 12)
  		buf[S35390A_ALRM_BYTE_HOURS] |= 0x40;
  
  	for (i = 0; i < 3; ++i)
  		buf[i] = bitrev8(buf[i]);
  
  	err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf,
  								sizeof(buf));
  
  	return err;
  }
  
  static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
  {
  	struct s35390a *s35390a = i2c_get_clientdata(client);
  	char buf[3], sts;
  	int i, err;
  
  	err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
  	if (err < 0)
  		return err;
  
  	if (bitrev8(sts) != S35390A_INT2_MODE_ALARM)
  		return -EINVAL;
  
  	err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
  	if (err < 0)
  		return err;
  
  	/* This chip returns the bits of each byte in reverse order */
  	for (i = 0; i < 3; ++i) {
  		buf[i] = bitrev8(buf[i]);
  		buf[i] &= ~0x80;
  	}
  
  	alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]);
  	alm->time.tm_hour = s35390a_reg2hr(s35390a,
  						buf[S35390A_ALRM_BYTE_HOURS]);
  	alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]);
  
  	dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d
  ",
  			__func__, alm->time.tm_min, alm->time.tm_hour,
  			alm->time.tm_wday);
  
  	return 0;
  }
  
  static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	return s35390a_read_alarm(to_i2c_client(dev), alm);
  }
  
  static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	return s35390a_set_alarm(to_i2c_client(dev), alm);
  }
c46288b09   Byron Bradley   rtc: add support ...
302
303
304
305
306
307
308
309
310
311
312
313
314
  static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
  {
  	return s35390a_get_datetime(to_i2c_client(dev), tm);
  }
  
  static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
  {
  	return s35390a_set_datetime(to_i2c_client(dev), tm);
  }
  
  static const struct rtc_class_ops s35390a_rtc_ops = {
  	.read_time	= s35390a_rtc_read_time,
  	.set_time	= s35390a_rtc_set_time,
542dd33a4   Michael Langer   drivers/rtc/rtc-s...
315
316
  	.set_alarm	= s35390a_rtc_set_alarm,
  	.read_alarm	= s35390a_rtc_read_alarm,
c46288b09   Byron Bradley   rtc: add support ...
317
318
319
  };
  
  static struct i2c_driver s35390a_driver;
d2653e927   Jean Delvare   i2c: Add support ...
320
321
  static int s35390a_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id)
c46288b09   Byron Bradley   rtc: add support ...
322
323
324
325
326
327
328
329
330
331
332
  {
  	int err;
  	unsigned int i;
  	struct s35390a *s35390a;
  	struct rtc_time tm;
  	char buf[1];
  
  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
  		err = -ENODEV;
  		goto exit;
  	}
b4cd3d6a7   Jingoo Han   rtc: rtc-s35390a:...
333
334
  	s35390a = devm_kzalloc(&client->dev, sizeof(struct s35390a),
  				GFP_KERNEL);
c46288b09   Byron Bradley   rtc: add support ...
335
336
337
338
339
340
341
342
343
344
345
  	if (!s35390a) {
  		err = -ENOMEM;
  		goto exit;
  	}
  
  	s35390a->client[0] = client;
  	i2c_set_clientdata(client, s35390a);
  
  	/* This chip uses multiple addresses, use dummy devices for them */
  	for (i = 1; i < 8; ++i) {
  		s35390a->client[i] = i2c_new_dummy(client->adapter,
60b129d7b   Jean Delvare   i2c: Match dummy ...
346
  					client->addr + i);
c46288b09   Byron Bradley   rtc: add support ...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  		if (!s35390a->client[i]) {
  			dev_err(&client->dev, "Address %02x unavailable
  ",
  						client->addr + i);
  			err = -EBUSY;
  			goto exit_dummy;
  		}
  	}
  
  	err = s35390a_reset(s35390a);
  	if (err < 0) {
  		dev_err(&client->dev, "error resetting chip
  ");
  		goto exit_dummy;
  	}
  
  	err = s35390a_disable_test_mode(s35390a);
  	if (err < 0) {
  		dev_err(&client->dev, "error disabling test mode
  ");
  		goto exit_dummy;
  	}
  
  	err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
  	if (err < 0) {
  		dev_err(&client->dev, "error checking 12/24 hour mode
  ");
  		goto exit_dummy;
  	}
  	if (buf[0] & S35390A_FLAG_24H)
  		s35390a->twentyfourhour = 1;
  	else
  		s35390a->twentyfourhour = 0;
  
  	if (s35390a_get_datetime(client, &tm) < 0)
  		dev_warn(&client->dev, "clock needs to be set
  ");
542dd33a4   Michael Langer   drivers/rtc/rtc-s...
384
  	device_set_wakeup_capable(&client->dev, 1);
b4cd3d6a7   Jingoo Han   rtc: rtc-s35390a:...
385
386
387
  	s35390a->rtc = devm_rtc_device_register(&client->dev,
  					s35390a_driver.driver.name,
  					&s35390a_rtc_ops, THIS_MODULE);
c46288b09   Byron Bradley   rtc: add support ...
388
389
390
391
392
393
394
395
396
397
398
  
  	if (IS_ERR(s35390a->rtc)) {
  		err = PTR_ERR(s35390a->rtc);
  		goto exit_dummy;
  	}
  	return 0;
  
  exit_dummy:
  	for (i = 1; i < 8; ++i)
  		if (s35390a->client[i])
  			i2c_unregister_device(s35390a->client[i]);
c46288b09   Byron Bradley   rtc: add support ...
399
400
401
402
403
404
405
406
  
  exit:
  	return err;
  }
  
  static int s35390a_remove(struct i2c_client *client)
  {
  	unsigned int i;
c46288b09   Byron Bradley   rtc: add support ...
407
  	struct s35390a *s35390a = i2c_get_clientdata(client);
b4cd3d6a7   Jingoo Han   rtc: rtc-s35390a:...
408

c46288b09   Byron Bradley   rtc: add support ...
409
410
411
  	for (i = 1; i < 8; ++i)
  		if (s35390a->client[i])
  			i2c_unregister_device(s35390a->client[i]);
c46288b09   Byron Bradley   rtc: add support ...
412
413
414
415
416
417
418
419
420
  	return 0;
  }
  
  static struct i2c_driver s35390a_driver = {
  	.driver		= {
  		.name	= "rtc-s35390a",
  	},
  	.probe		= s35390a_probe,
  	.remove		= s35390a_remove,
3760f7367   Jean Delvare   i2c: Convert most...
421
  	.id_table	= s35390a_id,
c46288b09   Byron Bradley   rtc: add support ...
422
  };
0abc92011   Axel Lin   rtc: convert rtc ...
423
  module_i2c_driver(s35390a_driver);
c46288b09   Byron Bradley   rtc: add support ...
424
425
426
427
  
  MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>");
  MODULE_DESCRIPTION("S35390A RTC driver");
  MODULE_LICENSE("GPL");