diff options
author | Herbert Valerio Riedel <hvr@gnu.org> | 2006-07-14 03:24:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-15 00:53:53 -0400 |
commit | 7e56a7dcbb974d9725d80e50d70c6eed7f71110b (patch) | |
tree | 67a7bd07bef6d81be075f49ce7eaba67a0b4687d /drivers/rtc | |
parent | bef317e364f065717819fbbe7965d4401820286c (diff) |
[PATCH] RTC subsystem, Add ISL1208 support
Add support for the I2C-attached Intersil ISL1208 RTC chip.
[akpm@osdl.org: cleanups, fixlets]
Signed-off-by: Herbert Valerio Riedel <hvr@gnu.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-isl1208.c | 591 |
3 files changed, 602 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f5b9f187a930..7ff1d88094b6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -121,6 +121,16 @@ config RTC_DRV_DS1553 | |||
121 | This driver can also be built as a module. If so, the module | 121 | This driver can also be built as a module. If so, the module |
122 | will be called rtc-ds1553. | 122 | will be called rtc-ds1553. |
123 | 123 | ||
124 | config RTC_DRV_ISL1208 | ||
125 | tristate "Intersil 1208" | ||
126 | depends on RTC_CLASS && I2C | ||
127 | help | ||
128 | If you say yes here you get support for the | ||
129 | Intersil 1208 RTC chip. | ||
130 | |||
131 | This driver can also be built as a module. If so, the module | ||
132 | will be called rtc-isl1208. | ||
133 | |||
124 | config RTC_DRV_DS1672 | 134 | config RTC_DRV_DS1672 |
125 | tristate "Dallas/Maxim DS1672" | 135 | tristate "Dallas/Maxim DS1672" |
126 | depends on RTC_CLASS && I2C | 136 | depends on RTC_CLASS && I2C |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 54220714ff49..bbcfb09d81d9 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -12,6 +12,7 @@ obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o | |||
12 | obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o | 12 | obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o |
13 | 13 | ||
14 | obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o | 14 | obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o |
15 | obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o | ||
15 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o | 16 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o |
16 | obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o | 17 | obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o |
17 | obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o | 18 | obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o |
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c new file mode 100644 index 000000000000..f324d0a635d4 --- /dev/null +++ b/drivers/rtc/rtc-isl1208.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /* | ||
2 | * Intersil ISL1208 rtc class driver | ||
3 | * | ||
4 | * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/bcd.h> | ||
16 | #include <linux/rtc.h> | ||
17 | |||
18 | #define DRV_NAME "isl1208" | ||
19 | #define DRV_VERSION "0.2" | ||
20 | |||
21 | /* Register map */ | ||
22 | /* rtc section */ | ||
23 | #define ISL1208_REG_SC 0x00 | ||
24 | #define ISL1208_REG_MN 0x01 | ||
25 | #define ISL1208_REG_HR 0x02 | ||
26 | #define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */ | ||
27 | #define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ | ||
28 | #define ISL1208_REG_DT 0x03 | ||
29 | #define ISL1208_REG_MO 0x04 | ||
30 | #define ISL1208_REG_YR 0x05 | ||
31 | #define ISL1208_REG_DW 0x06 | ||
32 | #define ISL1208_RTC_SECTION_LEN 7 | ||
33 | |||
34 | /* control/status section */ | ||
35 | #define ISL1208_REG_SR 0x07 | ||
36 | #define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ | ||
37 | #define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ | ||
38 | #define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ | ||
39 | #define ISL1208_REG_SR_ALM (1<<2) /* alarm */ | ||
40 | #define ISL1208_REG_SR_BAT (1<<1) /* battery */ | ||
41 | #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ | ||
42 | #define ISL1208_REG_INT 0x08 | ||
43 | #define ISL1208_REG_09 0x09 /* reserved */ | ||
44 | #define ISL1208_REG_ATR 0x0a | ||
45 | #define ISL1208_REG_DTR 0x0b | ||
46 | |||
47 | /* alarm section */ | ||
48 | #define ISL1208_REG_SCA 0x0c | ||
49 | #define ISL1208_REG_MNA 0x0d | ||
50 | #define ISL1208_REG_HRA 0x0e | ||
51 | #define ISL1208_REG_DTA 0x0f | ||
52 | #define ISL1208_REG_MOA 0x10 | ||
53 | #define ISL1208_REG_DWA 0x11 | ||
54 | #define ISL1208_ALARM_SECTION_LEN 6 | ||
55 | |||
56 | /* user section */ | ||
57 | #define ISL1208_REG_USR1 0x12 | ||
58 | #define ISL1208_REG_USR2 0x13 | ||
59 | #define ISL1208_USR_SECTION_LEN 2 | ||
60 | |||
61 | /* i2c configuration */ | ||
62 | #define ISL1208_I2C_ADDR 0xde | ||
63 | |||
64 | static unsigned short normal_i2c[] = { | ||
65 | ISL1208_I2C_ADDR>>1, I2C_CLIENT_END | ||
66 | }; | ||
67 | I2C_CLIENT_INSMOD; /* defines addr_data */ | ||
68 | |||
69 | static int isl1208_attach_adapter(struct i2c_adapter *adapter); | ||
70 | static int isl1208_detach_client(struct i2c_client *client); | ||
71 | |||
72 | static struct i2c_driver isl1208_driver = { | ||
73 | .driver = { | ||
74 | .name = DRV_NAME, | ||
75 | }, | ||
76 | .id = I2C_DRIVERID_ISL1208, | ||
77 | .attach_adapter = &isl1208_attach_adapter, | ||
78 | .detach_client = &isl1208_detach_client, | ||
79 | }; | ||
80 | |||
81 | /* block read */ | ||
82 | static int | ||
83 | isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], | ||
84 | unsigned len) | ||
85 | { | ||
86 | u8 reg_addr[1] = { reg }; | ||
87 | struct i2c_msg msgs[2] = { | ||
88 | { client->addr, client->flags, sizeof(reg_addr), reg_addr }, | ||
89 | { client->addr, client->flags | I2C_M_RD, len, buf } | ||
90 | }; | ||
91 | int ret; | ||
92 | |||
93 | BUG_ON(len == 0); | ||
94 | BUG_ON(reg > ISL1208_REG_USR2); | ||
95 | BUG_ON(reg + len > ISL1208_REG_USR2 + 1); | ||
96 | |||
97 | ret = i2c_transfer(client->adapter, msgs, 2); | ||
98 | if (ret > 0) | ||
99 | ret = 0; | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | /* block write */ | ||
104 | static int | ||
105 | isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], | ||
106 | unsigned len) | ||
107 | { | ||
108 | u8 i2c_buf[ISL1208_REG_USR2 + 2]; | ||
109 | struct i2c_msg msgs[1] = { | ||
110 | { client->addr, client->flags, len + 1, i2c_buf } | ||
111 | }; | ||
112 | int ret; | ||
113 | |||
114 | BUG_ON(len == 0); | ||
115 | BUG_ON(reg > ISL1208_REG_USR2); | ||
116 | BUG_ON(reg + len > ISL1208_REG_USR2 + 1); | ||
117 | |||
118 | i2c_buf[0] = reg; | ||
119 | memcpy(&i2c_buf[1], &buf[0], len); | ||
120 | |||
121 | ret = i2c_transfer(client->adapter, msgs, 1); | ||
122 | if (ret > 0) | ||
123 | ret = 0; | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | /* simple check to see wether we have a isl1208 */ | ||
128 | static int isl1208_i2c_validate_client(struct i2c_client *client) | ||
129 | { | ||
130 | u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; | ||
131 | u8 zero_mask[ISL1208_RTC_SECTION_LEN] = { | ||
132 | 0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8 | ||
133 | }; | ||
134 | int i; | ||
135 | int ret; | ||
136 | |||
137 | ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); | ||
138 | if (ret < 0) | ||
139 | return ret; | ||
140 | |||
141 | for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) { | ||
142 | if (regs[i] & zero_mask[i]) /* check if bits are cleared */ | ||
143 | return -ENODEV; | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static int isl1208_i2c_get_sr(struct i2c_client *client) | ||
150 | { | ||
151 | return i2c_smbus_read_byte_data(client, ISL1208_REG_SR) == -1 ? -EIO:0; | ||
152 | } | ||
153 | |||
154 | static int isl1208_i2c_get_atr(struct i2c_client *client) | ||
155 | { | ||
156 | int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR); | ||
157 | |||
158 | if (atr < 0) | ||
159 | return -EIO; | ||
160 | |||
161 | /* The 6bit value in the ATR register controls the load | ||
162 | * capacitance C_load * in steps of 0.25pF | ||
163 | * | ||
164 | * bit (1<<5) of the ATR register is inverted | ||
165 | * | ||
166 | * C_load(ATR=0x20) = 4.50pF | ||
167 | * C_load(ATR=0x00) = 12.50pF | ||
168 | * C_load(ATR=0x1f) = 20.25pF | ||
169 | * | ||
170 | */ | ||
171 | |||
172 | atr &= 0x3f; /* mask out lsb */ | ||
173 | atr ^= 1<<5; /* invert 6th bit */ | ||
174 | atr += 2*9; /* add offset of 4.5pF; unit[atr] = 0.25pF */ | ||
175 | |||
176 | return atr; | ||
177 | } | ||
178 | |||
179 | static int isl1208_i2c_get_dtr(struct i2c_client *client) | ||
180 | { | ||
181 | int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR); | ||
182 | |||
183 | if (dtr < 0) | ||
184 | return -EIO; | ||
185 | |||
186 | /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ | ||
187 | dtr = ((dtr & 0x3) * 20) * (dtr & (1<<2) ? -1 : 1); | ||
188 | |||
189 | return dtr; | ||
190 | } | ||
191 | |||
192 | static int isl1208_i2c_get_usr(struct i2c_client *client) | ||
193 | { | ||
194 | u8 buf[ISL1208_USR_SECTION_LEN] = { 0, }; | ||
195 | int ret; | ||
196 | |||
197 | ret = isl1208_i2c_read_regs (client, ISL1208_REG_USR1, buf, | ||
198 | ISL1208_USR_SECTION_LEN); | ||
199 | if (ret < 0) | ||
200 | return ret; | ||
201 | |||
202 | return (buf[1] << 8) | buf[0]; | ||
203 | } | ||
204 | |||
205 | static int isl1208_i2c_set_usr(struct i2c_client *client, u16 usr) | ||
206 | { | ||
207 | u8 buf[ISL1208_USR_SECTION_LEN]; | ||
208 | |||
209 | buf[0] = usr & 0xff; | ||
210 | buf[1] = (usr >> 8) & 0xff; | ||
211 | |||
212 | return isl1208_i2c_set_regs (client, ISL1208_REG_USR1, buf, | ||
213 | ISL1208_USR_SECTION_LEN); | ||
214 | } | ||
215 | |||
216 | static int isl1208_rtc_proc(struct device *dev, struct seq_file *seq) | ||
217 | { | ||
218 | struct i2c_client *const client = to_i2c_client(dev); | ||
219 | int sr, dtr, atr, usr; | ||
220 | |||
221 | sr = isl1208_i2c_get_sr(client); | ||
222 | if (sr < 0) { | ||
223 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | ||
224 | return sr; | ||
225 | } | ||
226 | |||
227 | seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n", | ||
228 | (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "", | ||
229 | (sr & ISL1208_REG_SR_BAT) ? " BAT" : "", | ||
230 | (sr & ISL1208_REG_SR_ALM) ? " ALM" : "", | ||
231 | (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "", | ||
232 | (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "", | ||
233 | (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", | ||
234 | sr); | ||
235 | |||
236 | seq_printf(seq, "batt_status\t: %s\n", | ||
237 | (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); | ||
238 | |||
239 | dtr = isl1208_i2c_get_dtr(client); | ||
240 | if (dtr >= 0 -1) | ||
241 | seq_printf(seq, "digital_trim\t: %d ppm\n", dtr); | ||
242 | |||
243 | atr = isl1208_i2c_get_atr(client); | ||
244 | if (atr >= 0) | ||
245 | seq_printf(seq, "analog_trim\t: %d.%.2d pF\n", | ||
246 | atr>>2, (atr&0x3)*25); | ||
247 | |||
248 | usr = isl1208_i2c_get_usr(client); | ||
249 | if (usr >= 0) | ||
250 | seq_printf(seq, "user_data\t: 0x%.4x\n", usr); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | |||
256 | static int isl1208_i2c_read_time(struct i2c_client *client, | ||
257 | struct rtc_time *tm) | ||
258 | { | ||
259 | int sr; | ||
260 | u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; | ||
261 | |||
262 | sr = isl1208_i2c_get_sr(client); | ||
263 | if (sr < 0) { | ||
264 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | ||
265 | return -EIO; | ||
266 | } | ||
267 | |||
268 | sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); | ||
269 | if (sr < 0) { | ||
270 | dev_err(&client->dev, "%s: reading RTC section failed\n", | ||
271 | __func__); | ||
272 | return sr; | ||
273 | } | ||
274 | |||
275 | tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SC]); | ||
276 | tm->tm_min = BCD2BIN(regs[ISL1208_REG_MN]); | ||
277 | { /* HR field has a more complex interpretation */ | ||
278 | const u8 _hr = regs[ISL1208_REG_HR]; | ||
279 | if (_hr & ISL1208_REG_HR_MIL) /* 24h format */ | ||
280 | tm->tm_hour = BCD2BIN(_hr & 0x3f); | ||
281 | else { // 12h format | ||
282 | tm->tm_hour = BCD2BIN(_hr & 0x1f); | ||
283 | if (_hr & ISL1208_REG_HR_PM) /* PM flag set */ | ||
284 | tm->tm_hour += 12; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DT]); | ||
289 | tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */ | ||
290 | tm->tm_year = BCD2BIN(regs[ISL1208_REG_YR]) + 100; | ||
291 | tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DW]); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int isl1208_i2c_read_alarm(struct i2c_client *client, | ||
297 | struct rtc_wkalrm *alarm) | ||
298 | { | ||
299 | struct rtc_time *const tm = &alarm->time; | ||
300 | u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; | ||
301 | int sr; | ||
302 | |||
303 | sr = isl1208_i2c_get_sr(client); | ||
304 | if (sr < 0) { | ||
305 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | ||
306 | return sr; | ||
307 | } | ||
308 | |||
309 | sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs, | ||
310 | ISL1208_ALARM_SECTION_LEN); | ||
311 | if (sr < 0) { | ||
312 | dev_err(&client->dev, "%s: reading alarm section failed\n", | ||
313 | __func__); | ||
314 | return sr; | ||
315 | } | ||
316 | |||
317 | /* MSB of each alarm register is an enable bit */ | ||
318 | tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SCA-ISL1208_REG_SCA] & 0x7f); | ||
319 | tm->tm_min = BCD2BIN(regs[ISL1208_REG_MNA-ISL1208_REG_SCA] & 0x7f); | ||
320 | tm->tm_hour = BCD2BIN(regs[ISL1208_REG_HRA-ISL1208_REG_SCA] & 0x3f); | ||
321 | tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DTA-ISL1208_REG_SCA] & 0x3f); | ||
322 | tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MOA-ISL1208_REG_SCA] & 0x1f)-1; | ||
323 | tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DWA-ISL1208_REG_SCA] & 0x03); | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static int isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
329 | { | ||
330 | return isl1208_i2c_read_time(to_i2c_client(dev), tm); | ||
331 | } | ||
332 | |||
333 | static int isl1208_i2c_set_time(struct i2c_client *client, | ||
334 | struct rtc_time const *tm) | ||
335 | { | ||
336 | int sr; | ||
337 | u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; | ||
338 | |||
339 | regs[ISL1208_REG_SC] = BIN2BCD(tm->tm_sec); | ||
340 | regs[ISL1208_REG_MN] = BIN2BCD(tm->tm_min); | ||
341 | regs[ISL1208_REG_HR] = BIN2BCD(tm->tm_hour) | ISL1208_REG_HR_MIL; | ||
342 | |||
343 | regs[ISL1208_REG_DT] = BIN2BCD(tm->tm_mday); | ||
344 | regs[ISL1208_REG_MO] = BIN2BCD(tm->tm_mon + 1); | ||
345 | regs[ISL1208_REG_YR] = BIN2BCD(tm->tm_year - 100); | ||
346 | |||
347 | regs[ISL1208_REG_DW] = BIN2BCD(tm->tm_wday & 7); | ||
348 | |||
349 | sr = isl1208_i2c_get_sr(client); | ||
350 | if (sr < 0) { | ||
351 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | ||
352 | return sr; | ||
353 | } | ||
354 | |||
355 | /* set WRTC */ | ||
356 | sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR, | ||
357 | sr | ISL1208_REG_SR_WRTC); | ||
358 | if (sr < 0) { | ||
359 | dev_err(&client->dev, "%s: writing SR failed\n", __func__); | ||
360 | return sr; | ||
361 | } | ||
362 | |||
363 | /* write RTC registers */ | ||
364 | sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); | ||
365 | if (sr < 0) { | ||
366 | dev_err(&client->dev, "%s: writing RTC section failed\n", | ||
367 | __func__); | ||
368 | return sr; | ||
369 | } | ||
370 | |||
371 | /* clear WRTC again */ | ||
372 | sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR, | ||
373 | sr & ~ISL1208_REG_SR_WRTC); | ||
374 | if (sr < 0) { | ||
375 | dev_err(&client->dev, "%s: writing SR failed\n", __func__); | ||
376 | return sr; | ||
377 | } | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | |||
383 | static int isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
384 | { | ||
385 | return isl1208_i2c_set_time(to_i2c_client(dev), tm); | ||
386 | } | ||
387 | |||
388 | static int isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
389 | { | ||
390 | return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); | ||
391 | } | ||
392 | |||
393 | static struct rtc_class_ops isl1208_rtc_ops = { | ||
394 | .proc = isl1208_rtc_proc, | ||
395 | .read_time = isl1208_rtc_read_time, | ||
396 | .set_time = isl1208_rtc_set_time, | ||
397 | .read_alarm = isl1208_rtc_read_alarm, | ||
398 | //.set_alarm = isl1208_rtc_set_alarm, | ||
399 | }; | ||
400 | |||
401 | /* sysfs interface */ | ||
402 | |||
403 | static ssize_t isl1208_sysfs_show_atrim(struct device *dev, | ||
404 | struct device_attribute *attr, | ||
405 | char *buf) | ||
406 | { | ||
407 | int atr; | ||
408 | |||
409 | atr = isl1208_i2c_get_atr(to_i2c_client(dev)); | ||
410 | if (atr < 0) | ||
411 | return atr; | ||
412 | |||
413 | return sprintf(buf, "%d.%.2d pF\n", atr>>2, (atr&0x3)*25); | ||
414 | } | ||
415 | static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL); | ||
416 | |||
417 | static ssize_t isl1208_sysfs_show_dtrim(struct device *dev, | ||
418 | struct device_attribute *attr, | ||
419 | char *buf) | ||
420 | { | ||
421 | int dtr; | ||
422 | |||
423 | dtr = isl1208_i2c_get_dtr(to_i2c_client(dev)); | ||
424 | if (dtr < 0) | ||
425 | return dtr; | ||
426 | |||
427 | return sprintf(buf, "%d ppm\n", dtr); | ||
428 | } | ||
429 | static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); | ||
430 | |||
431 | static ssize_t isl1208_sysfs_show_usr(struct device *dev, | ||
432 | struct device_attribute *attr, | ||
433 | char *buf) | ||
434 | { | ||
435 | int usr; | ||
436 | |||
437 | usr = isl1208_i2c_get_usr(to_i2c_client(dev)); | ||
438 | if (usr < 0) | ||
439 | return usr; | ||
440 | |||
441 | return sprintf(buf, "0x%.4x\n", usr); | ||
442 | } | ||
443 | |||
444 | static ssize_t isl1208_sysfs_store_usr(struct device *dev, | ||
445 | struct device_attribute *attr, | ||
446 | const char *buf, size_t count) | ||
447 | { | ||
448 | int usr = -1; | ||
449 | |||
450 | if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) { | ||
451 | if (sscanf(buf, "%x", &usr) != 1) | ||
452 | return -EINVAL; | ||
453 | } else { | ||
454 | if (sscanf(buf, "%d", &usr) != 1) | ||
455 | return -EINVAL; | ||
456 | } | ||
457 | |||
458 | if (usr < 0 || usr > 0xffff) | ||
459 | return -EINVAL; | ||
460 | |||
461 | return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count; | ||
462 | } | ||
463 | static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr, | ||
464 | isl1208_sysfs_store_usr); | ||
465 | |||
466 | static int | ||
467 | isl1208_probe(struct i2c_adapter *adapter, int addr, int kind) | ||
468 | { | ||
469 | int rc = 0; | ||
470 | struct i2c_client *new_client = NULL; | ||
471 | struct rtc_device *rtc = NULL; | ||
472 | |||
473 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { | ||
474 | rc = -ENODEV; | ||
475 | goto failout; | ||
476 | } | ||
477 | |||
478 | new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); | ||
479 | if (new_client == NULL) { | ||
480 | rc = -ENOMEM; | ||
481 | goto failout; | ||
482 | } | ||
483 | |||
484 | new_client->addr = addr; | ||
485 | new_client->adapter = adapter; | ||
486 | new_client->driver = &isl1208_driver; | ||
487 | new_client->flags = 0; | ||
488 | strcpy(new_client->name, DRV_NAME); | ||
489 | |||
490 | if (kind < 0) { | ||
491 | rc = isl1208_i2c_validate_client(new_client); | ||
492 | if (rc < 0) | ||
493 | goto failout; | ||
494 | } | ||
495 | |||
496 | rc = i2c_attach_client(new_client); | ||
497 | if (rc < 0) | ||
498 | goto failout; | ||
499 | |||
500 | dev_info(&new_client->dev, | ||
501 | "chip found, driver version " DRV_VERSION "\n"); | ||
502 | |||
503 | rtc = rtc_device_register(isl1208_driver.driver.name, | ||
504 | &new_client->dev, | ||
505 | &isl1208_rtc_ops, THIS_MODULE); | ||
506 | |||
507 | if (IS_ERR(rtc)) { | ||
508 | rc = PTR_ERR(rtc); | ||
509 | goto failout_detach; | ||
510 | } | ||
511 | |||
512 | i2c_set_clientdata(new_client, rtc); | ||
513 | |||
514 | rc = isl1208_i2c_get_sr(new_client); | ||
515 | if (rc < 0) { | ||
516 | dev_err(&new_client->dev, "reading status failed\n"); | ||
517 | goto failout_unregister; | ||
518 | } | ||
519 | |||
520 | if (rc & ISL1208_REG_SR_RTCF) | ||
521 | dev_warn(&new_client->dev, "rtc power failure detected, " | ||
522 | "please set clock.\n"); | ||
523 | |||
524 | rc = device_create_file(&new_client->dev, &dev_attr_atrim); | ||
525 | if (rc < 0) | ||
526 | goto failout_unregister; | ||
527 | rc = device_create_file(&new_client->dev, &dev_attr_dtrim); | ||
528 | if (rc < 0) | ||
529 | goto failout_atrim; | ||
530 | rc = device_create_file(&new_client->dev, &dev_attr_usr); | ||
531 | if (rc < 0) | ||
532 | goto failout_dtrim; | ||
533 | |||
534 | return 0; | ||
535 | |||
536 | failout_dtrim: | ||
537 | device_remove_file(&new_client->dev, &dev_attr_dtrim); | ||
538 | failout_atrim: | ||
539 | device_remove_file(&new_client->dev, &dev_attr_atrim); | ||
540 | failout_unregister: | ||
541 | rtc_device_unregister(rtc); | ||
542 | failout_detach: | ||
543 | i2c_detach_client(new_client); | ||
544 | failout: | ||
545 | kfree(new_client); | ||
546 | return rc; | ||
547 | } | ||
548 | |||
549 | static int | ||
550 | isl1208_attach_adapter (struct i2c_adapter *adapter) | ||
551 | { | ||
552 | return i2c_probe(adapter, &addr_data, isl1208_probe); | ||
553 | } | ||
554 | |||
555 | static int | ||
556 | isl1208_detach_client(struct i2c_client *client) | ||
557 | { | ||
558 | int rc; | ||
559 | struct rtc_device *const rtc = i2c_get_clientdata(client); | ||
560 | |||
561 | if (rtc) | ||
562 | rtc_device_unregister(rtc); /* do we need to kfree? */ | ||
563 | |||
564 | rc = i2c_detach_client(client); | ||
565 | if (rc) | ||
566 | return rc; | ||
567 | |||
568 | kfree(client); | ||
569 | |||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | /* module management */ | ||
574 | |||
575 | static int __init isl1208_init(void) | ||
576 | { | ||
577 | return i2c_add_driver(&isl1208_driver); | ||
578 | } | ||
579 | |||
580 | static void __exit isl1208_exit(void) | ||
581 | { | ||
582 | i2c_del_driver(&isl1208_driver); | ||
583 | } | ||
584 | |||
585 | MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); | ||
586 | MODULE_DESCRIPTION("Intersil ISL1208 RTC driver"); | ||
587 | MODULE_LICENSE("GPL"); | ||
588 | MODULE_VERSION(DRV_VERSION); | ||
589 | |||
590 | module_init(isl1208_init); | ||
591 | module_exit(isl1208_exit); | ||