Blame view

drivers/rtc/rtc-isl1208.c 17.5 KB
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * Intersil ISL1208 rtc class driver
   *
   * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org>
   *
   *  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/i2c.h>
  #include <linux/bcd.h>
  #include <linux/rtc.h>
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
17
  #define DRV_VERSION "0.3"
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
18
19
20
21
22
23
  
  /* Register map */
  /* rtc section */
  #define ISL1208_REG_SC  0x00
  #define ISL1208_REG_MN  0x01
  #define ISL1208_REG_HR  0x02
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
24
25
  #define ISL1208_REG_HR_MIL     (1<<7)	/* 24h/12h mode */
  #define ISL1208_REG_HR_PM      (1<<5)	/* PM/AM bit in 12h mode */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
26
27
28
29
30
31
32
33
  #define ISL1208_REG_DT  0x03
  #define ISL1208_REG_MO  0x04
  #define ISL1208_REG_YR  0x05
  #define ISL1208_REG_DW  0x06
  #define ISL1208_RTC_SECTION_LEN 7
  
  /* control/status section */
  #define ISL1208_REG_SR  0x07
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
34
35
36
37
38
39
  #define ISL1208_REG_SR_ARST    (1<<7)	/* auto reset */
  #define ISL1208_REG_SR_XTOSCB  (1<<6)	/* crystal oscillator */
  #define ISL1208_REG_SR_WRTC    (1<<4)	/* write rtc */
  #define ISL1208_REG_SR_ALM     (1<<2)	/* alarm */
  #define ISL1208_REG_SR_BAT     (1<<1)	/* battery */
  #define ISL1208_REG_SR_RTCF    (1<<0)	/* rtc fail */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
40
  #define ISL1208_REG_INT 0x08
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
41
42
  #define ISL1208_REG_INT_ALME   (1<<6)   /* alarm enable */
  #define ISL1208_REG_INT_IM     (1<<7)   /* interrupt/alarm mode */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
43
  #define ISL1208_REG_09  0x09	/* reserved */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  #define ISL1208_REG_ATR 0x0a
  #define ISL1208_REG_DTR 0x0b
  
  /* alarm section */
  #define ISL1208_REG_SCA 0x0c
  #define ISL1208_REG_MNA 0x0d
  #define ISL1208_REG_HRA 0x0e
  #define ISL1208_REG_DTA 0x0f
  #define ISL1208_REG_MOA 0x10
  #define ISL1208_REG_DWA 0x11
  #define ISL1208_ALARM_SECTION_LEN 6
  
  /* user section */
  #define ISL1208_REG_USR1 0x12
  #define ISL1208_REG_USR2 0x13
  #define ISL1208_USR_SECTION_LEN 2
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
60
  static struct i2c_driver isl1208_driver;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
61
62
63
64
  
  /* block read */
  static int
  isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
65
  		      unsigned len)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
66
67
68
  {
  	u8 reg_addr[1] = { reg };
  	struct i2c_msg msgs[2] = {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
69
70
71
  		{client->addr, 0, sizeof(reg_addr), reg_addr}
  		,
  		{client->addr, I2C_M_RD, len, buf}
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
72
73
  	};
  	int ret;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
74
75
76
77
78
79
80
81
82
83
84
85
  	BUG_ON(reg > ISL1208_REG_USR2);
  	BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
  
  	ret = i2c_transfer(client->adapter, msgs, 2);
  	if (ret > 0)
  		ret = 0;
  	return ret;
  }
  
  /* block write */
  static int
  isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
86
  		     unsigned len)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
87
88
89
  {
  	u8 i2c_buf[ISL1208_REG_USR2 + 2];
  	struct i2c_msg msgs[1] = {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
90
  		{client->addr, 0, len + 1, i2c_buf}
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
91
92
  	};
  	int ret;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
93
94
95
96
97
98
99
100
101
102
103
104
105
  	BUG_ON(reg > ISL1208_REG_USR2);
  	BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
  
  	i2c_buf[0] = reg;
  	memcpy(&i2c_buf[1], &buf[0], len);
  
  	ret = i2c_transfer(client->adapter, msgs, 1);
  	if (ret > 0)
  		ret = 0;
  	return ret;
  }
  
  /* simple check to see wether we have a isl1208 */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
106
107
  static int
  isl1208_i2c_validate_client(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
108
109
110
111
112
113
114
115
116
117
118
119
120
  {
  	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
  	u8 zero_mask[ISL1208_RTC_SECTION_LEN] = {
  		0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8
  	};
  	int i;
  	int ret;
  
  	ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
  	if (ret < 0)
  		return ret;
  
  	for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
121
  		if (regs[i] & zero_mask[i])	/* check if bits are cleared */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
122
123
124
125
126
  			return -ENODEV;
  	}
  
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
127
128
  static int
  isl1208_i2c_get_sr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
129
  {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
130
131
132
133
134
  	int sr = i2c_smbus_read_byte_data(client, ISL1208_REG_SR);
  	if (sr < 0)
  		return -EIO;
  
  	return sr;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
135
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
136
137
  static int
  isl1208_i2c_get_atr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
138
139
  {
  	int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
140
  	if (atr < 0)
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
141
  		return atr;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
142
143
144
145
146
147
148
149
150
151
152
  
  	/* The 6bit value in the ATR register controls the load
  	 * capacitance C_load * in steps of 0.25pF
  	 *
  	 * bit (1<<5) of the ATR register is inverted
  	 *
  	 * C_load(ATR=0x20) =  4.50pF
  	 * C_load(ATR=0x00) = 12.50pF
  	 * C_load(ATR=0x1f) = 20.25pF
  	 *
  	 */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
153
154
155
  	atr &= 0x3f;		/* mask out lsb */
  	atr ^= 1 << 5;		/* invert 6th bit */
  	atr += 2 * 9;		/* add offset of 4.5pF; unit[atr] = 0.25pF */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
156
157
158
  
  	return atr;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
159
160
  static int
  isl1208_i2c_get_dtr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
161
162
  {
  	int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
163
164
165
166
  	if (dtr < 0)
  		return -EIO;
  
  	/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
167
  	dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
168
169
170
  
  	return dtr;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
171
172
  static int
  isl1208_i2c_get_usr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
173
174
175
  {
  	u8 buf[ISL1208_USR_SECTION_LEN] = { 0, };
  	int ret;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
176
177
  	ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1, buf,
  				    ISL1208_USR_SECTION_LEN);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
178
179
180
181
182
  	if (ret < 0)
  		return ret;
  
  	return (buf[1] << 8) | buf[0];
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
183
184
  static int
  isl1208_i2c_set_usr(struct i2c_client *client, u16 usr)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
185
186
187
188
189
  {
  	u8 buf[ISL1208_USR_SECTION_LEN];
  
  	buf[0] = usr & 0xff;
  	buf[1] = (usr >> 8) & 0xff;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
190
191
  	return isl1208_i2c_set_regs(client, ISL1208_REG_USR1, buf,
  				    ISL1208_USR_SECTION_LEN);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
192
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
193
  static int
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable)
  {
  	int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
  
  	if (icr < 0) {
  		dev_err(&client->dev, "%s: reading INT failed
  ", __func__);
  		return icr;
  	}
  
  	if (enable)
  		icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM;
  	else
  		icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM);
  
  	icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr);
  	if (icr < 0) {
  		dev_err(&client->dev, "%s: writing INT failed
  ", __func__);
  		return icr;
  	}
  
  	return 0;
  }
  
  static int
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
220
  isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  {
  	struct i2c_client *const client = to_i2c_client(dev);
  	int sr, dtr, atr, usr;
  
  	sr = isl1208_i2c_get_sr(client);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: reading SR failed
  ", __func__);
  		return sr;
  	}
  
  	seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)
  ",
  		   (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "",
  		   (sr & ISL1208_REG_SR_BAT) ? " BAT" : "",
  		   (sr & ISL1208_REG_SR_ALM) ? " ALM" : "",
  		   (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "",
  		   (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "",
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
239
  		   (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", sr);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
240
241
242
243
244
245
  
  	seq_printf(seq, "batt_status\t: %s
  ",
  		   (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay");
  
  	dtr = isl1208_i2c_get_dtr(client);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
246
  	if (dtr >= 0 - 1)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
247
248
249
250
251
252
253
  		seq_printf(seq, "digital_trim\t: %d ppm
  ", dtr);
  
  	atr = isl1208_i2c_get_atr(client);
  	if (atr >= 0)
  		seq_printf(seq, "analog_trim\t: %d.%.2d pF
  ",
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
254
  			   atr >> 2, (atr & 0x3) * 25);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
255
256
257
258
259
260
261
262
  
  	usr = isl1208_i2c_get_usr(client);
  	if (usr >= 0)
  		seq_printf(seq, "user_data\t: 0x%.4x
  ", usr);
  
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
263
264
  static int
  isl1208_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  {
  	int sr;
  	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
  
  	sr = isl1208_i2c_get_sr(client);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: reading SR failed
  ", __func__);
  		return -EIO;
  	}
  
  	sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: reading RTC section failed
  ",
  			__func__);
  		return sr;
  	}
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
283
284
  	tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]);
  	tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
285
286
287
  
  	/* HR field has a more complex interpretation */
  	{
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
288
  		const u8 _hr = regs[ISL1208_REG_HR];
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
289
  		if (_hr & ISL1208_REG_HR_MIL)	/* 24h format */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
290
  			tm->tm_hour = bcd2bin(_hr & 0x3f);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
291
292
  		else {
  			/* 12h format */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
293
  			tm->tm_hour = bcd2bin(_hr & 0x1f);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
294
  			if (_hr & ISL1208_REG_HR_PM)	/* PM flag set */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
295
296
297
  				tm->tm_hour += 12;
  		}
  	}
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
298
299
300
301
  	tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]);
  	tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]) - 1;	/* rtc starts at 1 */
  	tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 100;
  	tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
302
303
304
  
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
305
306
  static int
  isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
307
308
309
  {
  	struct rtc_time *const tm = &alarm->time;
  	u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
310
  	int icr, yr, sr = isl1208_i2c_get_sr(client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
311

7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
312
313
314
315
316
317
318
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: reading SR failed
  ", __func__);
  		return sr;
  	}
  
  	sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs,
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
319
  				   ISL1208_ALARM_SECTION_LEN);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
320
321
322
323
324
325
326
327
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: reading alarm section failed
  ",
  			__func__);
  		return sr;
  	}
  
  	/* MSB of each alarm register is an enable bit */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
328
329
330
331
  	tm->tm_sec = bcd2bin(regs[ISL1208_REG_SCA - ISL1208_REG_SCA] & 0x7f);
  	tm->tm_min = bcd2bin(regs[ISL1208_REG_MNA - ISL1208_REG_SCA] & 0x7f);
  	tm->tm_hour = bcd2bin(regs[ISL1208_REG_HRA - ISL1208_REG_SCA] & 0x3f);
  	tm->tm_mday = bcd2bin(regs[ISL1208_REG_DTA - ISL1208_REG_SCA] & 0x3f);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
332
  	tm->tm_mon =
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
333
334
  		bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1;
  	tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
335

cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
336
337
338
339
340
341
342
343
344
345
346
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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  	/* The alarm doesn't store the year so get it from the rtc section */
  	yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR);
  	if (yr < 0) {
  		dev_err(&client->dev, "%s: reading RTC YR failed
  ", __func__);
  		return yr;
  	}
  	tm->tm_year = bcd2bin(yr) + 100;
  
  	icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
  	if (icr < 0) {
  		dev_err(&client->dev, "%s: reading INT failed
  ", __func__);
  		return icr;
  	}
  	alarm->enabled = !!(icr & ISL1208_REG_INT_ALME);
  
  	return 0;
  }
  
  static int
  isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
  {
  	struct rtc_time *alarm_tm = &alarm->time;
  	u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
  	const int offs = ISL1208_REG_SCA;
  	unsigned long rtc_secs, alarm_secs;
  	struct rtc_time rtc_tm;
  	int err, enable;
  
  	err = isl1208_i2c_read_time(client, &rtc_tm);
  	if (err)
  		return err;
  	err = rtc_tm_to_time(&rtc_tm, &rtc_secs);
  	if (err)
  		return err;
  	err = rtc_tm_to_time(alarm_tm, &alarm_secs);
  	if (err)
  		return err;
  
  	/* If the alarm time is before the current time disable the alarm */
  	if (!alarm->enabled || alarm_secs <= rtc_secs)
  		enable = 0x00;
  	else
  		enable = 0x80;
  
  	/* Program the alarm and enable it for each setting */
  	regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable;
  	regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable;
  	regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) |
  		ISL1208_REG_HR_MIL | enable;
  
  	regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable;
  	regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable;
  	regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable;
  
  	/* write ALARM registers */
  	err = isl1208_i2c_set_regs(client, offs, regs,
  				  ISL1208_ALARM_SECTION_LEN);
  	if (err < 0) {
  		dev_err(&client->dev, "%s: writing ALARM section failed
  ",
  			__func__);
  		return err;
  	}
  
  	err = isl1208_rtc_toggle_alarm(client, enable);
  	if (err)
  		return err;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
405
406
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
407
408
  static int
  isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
409
410
411
  {
  	return isl1208_i2c_read_time(to_i2c_client(dev), tm);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
412
413
  static int
  isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
414
415
416
  {
  	int sr;
  	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
cc6c2ca30   Chris Elston   rtc: rtc-isl1208:...
417
418
419
420
421
422
  	/* The clock has an 8 bit wide bcd-coded register (they never learn)
  	 * for the year. tm_year is an offset from 1900 and we are interested
  	 * in the 2000-2099 range, so any value less than 100 is invalid.
  	 */
  	if (tm->tm_year < 100)
  		return -EINVAL;
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
423
424
425
  	regs[ISL1208_REG_SC] = bin2bcd(tm->tm_sec);
  	regs[ISL1208_REG_MN] = bin2bcd(tm->tm_min);
  	regs[ISL1208_REG_HR] = bin2bcd(tm->tm_hour) | ISL1208_REG_HR_MIL;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
426

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
427
428
429
  	regs[ISL1208_REG_DT] = bin2bcd(tm->tm_mday);
  	regs[ISL1208_REG_MO] = bin2bcd(tm->tm_mon + 1);
  	regs[ISL1208_REG_YR] = bin2bcd(tm->tm_year - 100);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
430

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
431
  	regs[ISL1208_REG_DW] = bin2bcd(tm->tm_wday & 7);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
432
433
434
435
436
437
438
439
440
  
  	sr = isl1208_i2c_get_sr(client);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: reading SR failed
  ", __func__);
  		return sr;
  	}
  
  	/* set WRTC */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
441
  	sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
  				       sr | ISL1208_REG_SR_WRTC);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: writing SR failed
  ", __func__);
  		return sr;
  	}
  
  	/* write RTC registers */
  	sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: writing RTC section failed
  ",
  			__func__);
  		return sr;
  	}
  
  	/* clear WRTC again */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
459
  	sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
460
461
462
463
464
465
466
467
468
  				       sr & ~ISL1208_REG_SR_WRTC);
  	if (sr < 0) {
  		dev_err(&client->dev, "%s: writing SR failed
  ", __func__);
  		return sr;
  	}
  
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
469
470
  static int
  isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
471
472
473
  {
  	return isl1208_i2c_set_time(to_i2c_client(dev), tm);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
474
475
  static int
  isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
476
477
478
  {
  	return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
  }
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  static int
  isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
  {
  	return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
  }
  
  static irqreturn_t
  isl1208_rtc_interrupt(int irq, void *data)
  {
  	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
  	struct i2c_client *client = data;
  	int handled = 0, sr, err;
  
  	/*
  	 * I2C reads get NAK'ed if we read straight away after an interrupt?
  	 * Using a mdelay/msleep didn't seem to help either, so we work around
  	 * this by continually trying to read the register for a short time.
  	 */
  	while (1) {
  		sr = isl1208_i2c_get_sr(client);
  		if (sr >= 0)
  			break;
  
  		if (time_after(jiffies, timeout)) {
  			dev_err(&client->dev, "%s: reading SR failed
  ",
  				__func__);
  			return sr;
  		}
  	}
  
  	if (sr & ISL1208_REG_SR_ALM) {
  		dev_dbg(&client->dev, "alarm!
  ");
  
  		/* Clear the alarm */
  		sr &= ~ISL1208_REG_SR_ALM;
  		sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
  		if (sr < 0)
  			dev_err(&client->dev, "%s: writing SR failed
  ",
  				__func__);
  		else
  			handled = 1;
  
  		/* Disable the alarm */
  		err = isl1208_rtc_toggle_alarm(client, 0);
  		if (err)
  			return err;
  	}
  
  	return handled ? IRQ_HANDLED : IRQ_NONE;
  }
ff8371ac9   David Brownell   [PATCH] constify ...
532
  static const struct rtc_class_ops isl1208_rtc_ops = {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
533
534
535
536
  	.proc = isl1208_rtc_proc,
  	.read_time = isl1208_rtc_read_time,
  	.set_time = isl1208_rtc_set_time,
  	.read_alarm = isl1208_rtc_read_alarm,
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
537
  	.set_alarm = isl1208_rtc_set_alarm,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
538
539
540
  };
  
  /* sysfs interface */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
541
542
543
  static ssize_t
  isl1208_sysfs_show_atrim(struct device *dev,
  			 struct device_attribute *attr, char *buf)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
544
  {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
545
  	int atr = isl1208_i2c_get_atr(to_i2c_client(dev));
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
546
547
  	if (atr < 0)
  		return atr;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
548
549
  	return sprintf(buf, "%d.%.2d pF
  ", atr >> 2, (atr & 0x3) * 25);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
550
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
551

7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
552
  static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
553
554
555
  static ssize_t
  isl1208_sysfs_show_dtrim(struct device *dev,
  			 struct device_attribute *attr, char *buf)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
556
  {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
557
  	int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev));
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
558
559
560
561
562
563
  	if (dtr < 0)
  		return dtr;
  
  	return sprintf(buf, "%d ppm
  ", dtr);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
564

7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
565
  static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
566
567
568
  static ssize_t
  isl1208_sysfs_show_usr(struct device *dev,
  		       struct device_attribute *attr, char *buf)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
569
  {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
570
  	int usr = isl1208_i2c_get_usr(to_i2c_client(dev));
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
571
572
573
574
575
576
  	if (usr < 0)
  		return usr;
  
  	return sprintf(buf, "0x%.4x
  ", usr);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
577
578
579
580
  static ssize_t
  isl1208_sysfs_store_usr(struct device *dev,
  			struct device_attribute *attr,
  			const char *buf, size_t count)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
  {
  	int usr = -1;
  
  	if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {
  		if (sscanf(buf, "%x", &usr) != 1)
  			return -EINVAL;
  	} else {
  		if (sscanf(buf, "%d", &usr) != 1)
  			return -EINVAL;
  	}
  
  	if (usr < 0 || usr > 0xffff)
  		return -EINVAL;
  
  	return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
597

7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
598
599
  static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
  		   isl1208_sysfs_store_usr);
e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
600
601
602
603
604
605
  static struct attribute *isl1208_rtc_attrs[] = {
  	&dev_attr_atrim.attr,
  	&dev_attr_dtrim.attr,
  	&dev_attr_usr.attr,
  	NULL
  };
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
606

e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
607
608
609
  static const struct attribute_group isl1208_rtc_sysfs_files = {
  	.attrs	= isl1208_rtc_attrs,
  };
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
610
611
  
  static int
d2653e927   Jean Delvare   i2c: Add support ...
612
  isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
613
614
615
  {
  	int rc = 0;
  	struct rtc_device *rtc;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
616

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
617
618
  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
  		return -ENODEV;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
619

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
620
621
622
623
  	if (isl1208_i2c_validate_client(client) < 0)
  		return -ENODEV;
  
  	dev_info(&client->dev,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
624
625
  		 "chip found, driver version " DRV_VERSION "
  ");
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
  	if (client->irq > 0) {
  		rc = request_threaded_irq(client->irq, NULL,
  					  isl1208_rtc_interrupt,
  					  IRQF_SHARED,
  					  isl1208_driver.driver.name, client);
  		if (!rc) {
  			device_init_wakeup(&client->dev, 1);
  			enable_irq_wake(client->irq);
  		} else {
  			dev_err(&client->dev,
  				"Unable to request irq %d, no alarm support
  ",
  				client->irq);
  			client->irq = 0;
  		}
  	}
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
642
  	rtc = rtc_device_register(isl1208_driver.driver.name,
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
643
644
  				  &client->dev, &isl1208_rtc_ops,
  				  THIS_MODULE);
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
645
646
647
648
  	if (IS_ERR(rtc)) {
  		rc = PTR_ERR(rtc);
  		goto exit_free_irq;
  	}
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
649

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
650
  	i2c_set_clientdata(client, rtc);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
651

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
652
  	rc = isl1208_i2c_get_sr(client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
653
  	if (rc < 0) {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
654
655
656
  		dev_err(&client->dev, "reading status failed
  ");
  		goto exit_unregister;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
657
658
659
  	}
  
  	if (rc & ISL1208_REG_SR_RTCF)
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
660
  		dev_warn(&client->dev, "rtc power failure detected, "
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
661
662
  			 "please set clock.
  ");
e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
663
  	rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
664
665
  	if (rc)
  		goto exit_unregister;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
666
667
  
  	return 0;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
668
  exit_unregister:
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
669
  	rtc_device_unregister(rtc);
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
670
671
672
  exit_free_irq:
  	if (client->irq)
  		free_irq(client->irq, client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
673

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
674
  	return rc;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
675
676
677
  }
  
  static int
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
678
  isl1208_remove(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
679
  {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
680
  	struct rtc_device *rtc = i2c_get_clientdata(client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
681

e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
682
  	sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
683
  	rtc_device_unregister(rtc);
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
684
685
  	if (client->irq)
  		free_irq(client->irq, client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
686
687
688
  
  	return 0;
  }
3760f7367   Jean Delvare   i2c: Convert most...
689
690
691
692
693
  static const struct i2c_device_id isl1208_id[] = {
  	{ "isl1208", 0 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, isl1208_id);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
694
695
696
697
698
699
  static struct i2c_driver isl1208_driver = {
  	.driver = {
  		   .name = "rtc-isl1208",
  		   },
  	.probe = isl1208_probe,
  	.remove = isl1208_remove,
3760f7367   Jean Delvare   i2c: Convert most...
700
  	.id_table = isl1208_id,
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
701
  };
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
702

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
703
704
  static int __init
  isl1208_init(void)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
705
706
707
  {
  	return i2c_add_driver(&isl1208_driver);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
708
709
  static void __exit
  isl1208_exit(void)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
710
711
712
713
714
715
716
717
718
719
720
  {
  	i2c_del_driver(&isl1208_driver);
  }
  
  MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
  MODULE_DESCRIPTION("Intersil ISL1208 RTC driver");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(DRV_VERSION);
  
  module_init(isl1208_init);
  module_exit(isl1208_exit);