Blame view

drivers/rtc/rtc-s35390a.c 7.59 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
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  /*
   * 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
  
  #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
  
  #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
3760f7367   Jean Delvare   i2c: Convert most...
36
37
38
39
40
  static const struct i2c_device_id s35390a_id[] = {
  	{ "s35390a", 0 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, s35390a_id);
c46288b09   Byron Bradley   rtc: add support ...
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  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[] = {
  		{ client->addr, 0, len, buf },
  	};
  
  	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[] = {
  		{ client->addr, I2C_M_RD, len, buf },
  	};
  
  	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...
105
  		return bin2bcd(hour);
c46288b09   Byron Bradley   rtc: add support ...
106
107
  
  	if (hour < 12)
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
108
  		return bin2bcd(hour);
c46288b09   Byron Bradley   rtc: add support ...
109

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
110
  	return 0x40 | bin2bcd(hour - 12);
c46288b09   Byron Bradley   rtc: add support ...
111
112
113
114
115
116
117
  }
  
  static int s35390a_reg2hr(struct s35390a *s35390a, char reg)
  {
  	unsigned hour;
  
  	if (s35390a->twentyfourhour)
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
118
  		return bcd2bin(reg & 0x3f);
c46288b09   Byron Bradley   rtc: add support ...
119

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
120
  	hour = bcd2bin(reg & 0x3f);
c46288b09   Byron Bradley   rtc: add support ...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  	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...
138
139
140
141
  	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 ...
142
  	buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
143
144
  	buf[S35390A_BYTE_MINS] = bin2bcd(tm->tm_min);
  	buf[S35390A_BYTE_SECS] = bin2bcd(tm->tm_sec);
c46288b09   Byron Bradley   rtc: add support ...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  
  	/* 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...
168
169
  	tm->tm_sec = bcd2bin(buf[S35390A_BYTE_SECS]);
  	tm->tm_min = bcd2bin(buf[S35390A_BYTE_MINS]);
c46288b09   Byron Bradley   rtc: add support ...
170
  	tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]);
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
171
172
173
174
  	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 ...
175
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
  
  	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);
  }
  
  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,
  };
  
  static struct i2c_driver s35390a_driver;
d2653e927   Jean Delvare   i2c: Add support ...
201
202
  static int s35390a_probe(struct i2c_client *client,
  			 const struct i2c_device_id *id)
c46288b09   Byron Bradley   rtc: add support ...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  {
  	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;
  	}
  
  	s35390a = kzalloc(sizeof(struct s35390a), GFP_KERNEL);
  	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 ...
227
  					client->addr + i);
c46288b09   Byron Bradley   rtc: add support ...
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
  		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
  ");
  
  	s35390a->rtc = rtc_device_register(s35390a_driver.driver.name,
  				&client->dev, &s35390a_rtc_ops, THIS_MODULE);
  
  	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]);
  	kfree(s35390a);
c46288b09   Byron Bradley   rtc: add support ...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  
  exit:
  	return err;
  }
  
  static int s35390a_remove(struct i2c_client *client)
  {
  	unsigned int i;
  
  	struct s35390a *s35390a = i2c_get_clientdata(client);
  	for (i = 1; i < 8; ++i)
  		if (s35390a->client[i])
  			i2c_unregister_device(s35390a->client[i]);
  
  	rtc_device_unregister(s35390a->rtc);
  	kfree(s35390a);
c46288b09   Byron Bradley   rtc: add support ...
296
297
298
299
300
301
302
303
304
305
  
  	return 0;
  }
  
  static struct i2c_driver s35390a_driver = {
  	.driver		= {
  		.name	= "rtc-s35390a",
  	},
  	.probe		= s35390a_probe,
  	.remove		= s35390a_remove,
3760f7367   Jean Delvare   i2c: Convert most...
306
  	.id_table	= s35390a_id,
c46288b09   Byron Bradley   rtc: add support ...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
  };
  
  static int __init s35390a_rtc_init(void)
  {
  	return i2c_add_driver(&s35390a_driver);
  }
  
  static void __exit s35390a_rtc_exit(void)
  {
  	i2c_del_driver(&s35390a_driver);
  }
  
  MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>");
  MODULE_DESCRIPTION("S35390A RTC driver");
  MODULE_LICENSE("GPL");
  
  module_init(s35390a_rtc_init);
  module_exit(s35390a_rtc_exit);