Blame view

drivers/rtc/rtc-isl1208.c 17 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>
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
17
18
19
20
21
  /* 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 ...
22
23
  #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...
24
25
26
27
28
29
30
31
  #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 ...
32
33
34
35
36
37
  #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...
38
  #define ISL1208_REG_INT 0x08
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
39
40
  #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 ...
41
  #define ISL1208_REG_09  0x09	/* reserved */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  #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 ...
58
  static struct i2c_driver isl1208_driver;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
59
60
61
62
  
  /* block read */
  static int
  isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
63
  		      unsigned len)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
64
65
66
  {
  	u8 reg_addr[1] = { reg };
  	struct i2c_msg msgs[2] = {
885ccbb35   Shubhrajyoti D   drivers/rtc/rtc-i...
67
68
69
70
71
72
73
74
75
76
77
  		{
  			.addr = client->addr,
  			.len = sizeof(reg_addr),
  			.buf = reg_addr
  		},
  		{
  			.addr = client->addr,
  			.flags = I2C_M_RD,
  			.len = len,
  			.buf = buf
  		}
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
78
79
  	};
  	int ret;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
80
81
82
83
84
85
86
87
88
89
90
91
  	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 ...
92
  		     unsigned len)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
93
94
95
  {
  	u8 i2c_buf[ISL1208_REG_USR2 + 2];
  	struct i2c_msg msgs[1] = {
885ccbb35   Shubhrajyoti D   drivers/rtc/rtc-i...
96
97
98
99
100
  		{
  			.addr = client->addr,
  			.len = len + 1,
  			.buf = i2c_buf
  		}
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
101
102
  	};
  	int ret;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
103
104
105
106
107
108
109
110
111
112
113
  	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;
  }
48fc7f7e7   Adam Buchbinder   Fix misspellings ...
114
  /* simple check to see whether we have a isl1208 */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
115
116
  static int
  isl1208_i2c_validate_client(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
117
118
119
120
121
122
123
124
125
126
127
128
129
  {
  	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 ...
130
  		if (regs[i] & zero_mask[i])	/* check if bits are cleared */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
131
132
133
134
135
  			return -ENODEV;
  	}
  
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
136
137
  static int
  isl1208_i2c_get_sr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
138
  {
c91fd91d6   Sachin Kamat   drivers/rtc/rtc-i...
139
  	return i2c_smbus_read_byte_data(client, ISL1208_REG_SR);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
140
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
141
142
  static int
  isl1208_i2c_get_atr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
143
144
  {
  	int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
145
  	if (atr < 0)
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
146
  		return atr;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
147
148
149
150
151
152
153
154
155
156
157
  
  	/* 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 ...
158
159
160
  	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...
161
162
163
  
  	return atr;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
164
165
  static int
  isl1208_i2c_get_dtr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
166
167
  {
  	int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
168
169
170
171
  	if (dtr < 0)
  		return -EIO;
  
  	/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
172
  	dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
173
174
175
  
  	return dtr;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
176
177
  static int
  isl1208_i2c_get_usr(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
178
179
180
  {
  	u8 buf[ISL1208_USR_SECTION_LEN] = { 0, };
  	int ret;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
181
182
  	ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1, buf,
  				    ISL1208_USR_SECTION_LEN);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
183
184
185
186
187
  	if (ret < 0)
  		return ret;
  
  	return (buf[1] << 8) | buf[0];
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
188
189
  static int
  isl1208_i2c_set_usr(struct i2c_client *client, u16 usr)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
190
191
192
193
194
  {
  	u8 buf[ISL1208_USR_SECTION_LEN];
  
  	buf[0] = usr & 0xff;
  	buf[1] = (usr >> 8) & 0xff;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
195
196
  	return isl1208_i2c_set_regs(client, ISL1208_REG_USR1, buf,
  				    ISL1208_USR_SECTION_LEN);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
197
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
198
  static int
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  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 ...
225
  isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  {
  	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 ...
244
  		   (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", sr);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
245
246
247
248
249
250
  
  	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 ...
251
  	if (dtr >= 0 - 1)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
252
253
254
255
256
257
258
  		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 ...
259
  			   atr >> 2, (atr & 0x3) * 25);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
260
261
262
263
264
265
266
267
  
  	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 ...
268
269
  static int
  isl1208_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  {
  	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...
288
289
  	tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]);
  	tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
290
291
292
  
  	/* HR field has a more complex interpretation */
  	{
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
293
  		const u8 _hr = regs[ISL1208_REG_HR];
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
294
  		if (_hr & ISL1208_REG_HR_MIL)	/* 24h format */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
295
  			tm->tm_hour = bcd2bin(_hr & 0x3f);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
296
297
  		else {
  			/* 12h format */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
298
  			tm->tm_hour = bcd2bin(_hr & 0x1f);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
299
  			if (_hr & ISL1208_REG_HR_PM)	/* PM flag set */
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
300
301
302
  				tm->tm_hour += 12;
  		}
  	}
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
303
304
305
306
  	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...
307
308
309
  
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
310
311
  static int
  isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
312
313
314
  {
  	struct rtc_time *const tm = &alarm->time;
  	u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
315
  	int icr, yr, sr = isl1208_i2c_get_sr(client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
316

7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
317
318
319
320
321
322
323
  	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 ...
324
  				   ISL1208_ALARM_SECTION_LEN);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
325
326
327
328
329
330
331
332
  	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...
333
334
335
336
  	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 ...
337
  	tm->tm_mon =
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
338
339
  		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...
340

cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
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
  	/* 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;
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
367
368
369
370
371
372
  	struct rtc_time rtc_tm;
  	int err, enable;
  
  	err = isl1208_i2c_read_time(client, &rtc_tm);
  	if (err)
  		return err;
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
373
374
  
  	/* If the alarm time is before the current time disable the alarm */
f118db1ef   Xunlei Pang   rtc: isl1208: Rep...
375
  	if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0)
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
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
  		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...
403
404
  	return 0;
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
405
406
  static int
  isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
407
408
409
  {
  	return isl1208_i2c_read_time(to_i2c_client(dev), tm);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
410
411
  static int
  isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
412
413
414
  {
  	int sr;
  	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
cc6c2ca30   Chris Elston   rtc: rtc-isl1208:...
415
416
417
418
419
420
  	/* 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...
421
422
423
  	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...
424

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
425
426
427
  	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...
428

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
429
  	regs[ISL1208_REG_DW] = bin2bcd(tm->tm_wday & 7);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
430
431
432
433
434
435
436
437
438
  
  	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 ...
439
  	sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  				       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 ...
457
  	sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
458
459
460
461
462
463
464
465
466
  				       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 ...
467
468
  static int
  isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
469
470
471
  {
  	return isl1208_i2c_set_time(to_i2c_client(dev), tm);
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
472
473
  static int
  isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
474
475
476
  {
  	return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
  }
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
477
478
479
480
481
482
483
484
485
486
487
  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;
72fca4a4b   Jan Luebbe   drivers/rtc/rtc-i...
488
  	struct rtc_device *rtc = i2c_get_clientdata(client);
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  	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!
  ");
72fca4a4b   Jan Luebbe   drivers/rtc/rtc-i...
512
  		rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
  		/* 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 ...
531
  static const struct rtc_class_ops isl1208_rtc_ops = {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
532
533
534
535
  	.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...
536
  	.set_alarm = isl1208_rtc_set_alarm,
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
537
538
539
  };
  
  /* sysfs interface */
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
540
541
542
  static ssize_t
  isl1208_sysfs_show_atrim(struct device *dev,
  			 struct device_attribute *attr, char *buf)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
543
  {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
544
  	int atr = isl1208_i2c_get_atr(to_i2c_client(dev));
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
545
546
  	if (atr < 0)
  		return atr;
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
547
548
  	return sprintf(buf, "%d.%.2d pF
  ", atr >> 2, (atr & 0x3) * 25);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
549
  }
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
550

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

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

7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
597
598
  static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
  		   isl1208_sysfs_store_usr);
e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
599
600
601
602
603
604
  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 ...
605

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

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

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
619
620
  	if (isl1208_i2c_validate_client(client) < 0)
  		return -ENODEV;
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
621
  	if (client->irq > 0) {
adce8c14c   Sachin Kamat   drivers/rtc/rtc-i...
622
623
  		rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
  					       isl1208_rtc_interrupt,
7a96f2874   Fabio Estevam   rtc: isl1208: Pas...
624
  					       IRQF_SHARED | IRQF_ONESHOT,
adce8c14c   Sachin Kamat   drivers/rtc/rtc-i...
625
626
  					       isl1208_driver.driver.name,
  					       client);
cf044f0ed   Ryan Mallon   drivers/rtc/rtc-i...
627
628
629
630
631
632
633
634
635
636
637
  		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;
  		}
  	}
adce8c14c   Sachin Kamat   drivers/rtc/rtc-i...
638
639
  	rtc = devm_rtc_device_register(&client->dev, isl1208_driver.driver.name,
  				  &isl1208_rtc_ops,
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
640
  				  THIS_MODULE);
adce8c14c   Sachin Kamat   drivers/rtc/rtc-i...
641
642
  	if (IS_ERR(rtc))
  		return PTR_ERR(rtc);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
643

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

9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
646
  	rc = isl1208_i2c_get_sr(client);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
647
  	if (rc < 0) {
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
648
649
  		dev_err(&client->dev, "reading status failed
  ");
adce8c14c   Sachin Kamat   drivers/rtc/rtc-i...
650
  		return rc;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
651
652
653
  	}
  
  	if (rc & ISL1208_REG_SR_RTCF)
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
654
  		dev_warn(&client->dev, "rtc power failure detected, "
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
655
656
  			 "please set clock.
  ");
e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
657
  	rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
658
  	if (rc)
adce8c14c   Sachin Kamat   drivers/rtc/rtc-i...
659
  		return rc;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
660
661
  
  	return 0;
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
662
663
664
  }
  
  static int
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
665
  isl1208_remove(struct i2c_client *client)
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
666
  {
e17ab5cbe   H Hartley Sweeten   rtc-isl1208: use ...
667
  	sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
668
669
670
  
  	return 0;
  }
3760f7367   Jean Delvare   i2c: Convert most...
671
672
  static const struct i2c_device_id isl1208_id[] = {
  	{ "isl1208", 0 },
5fa44f869   Ben Gardner   drivers/rtc/rtc-i...
673
  	{ "isl1218", 0 },
3760f7367   Jean Delvare   i2c: Convert most...
674
675
676
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, isl1208_id);
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
677
678
679
680
681
682
  static struct i2c_driver isl1208_driver = {
  	.driver = {
  		   .name = "rtc-isl1208",
  		   },
  	.probe = isl1208_probe,
  	.remove = isl1208_remove,
3760f7367   Jean Delvare   i2c: Convert most...
683
  	.id_table = isl1208_id,
9edae7bcd   Alessandro Zummo   rtc-isl1208: new ...
684
  };
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
685

0abc92011   Axel Lin   rtc: convert rtc ...
686
  module_i2c_driver(isl1208_driver);
7e56a7dcb   Herbert Valerio Riedel   [PATCH] RTC subsy...
687
688
689
690
  
  MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
  MODULE_DESCRIPTION("Intersil ISL1208 RTC driver");
  MODULE_LICENSE("GPL");