Commit | Line | Data |
---|---|---|
7e56a7dc HVR |
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); |