Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
7e56a7dc HVR |
2 | /* |
3 | * Intersil ISL1208 rtc class driver | |
4 | * | |
5 | * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org> | |
7e56a7dc HVR |
6 | */ |
7 | ||
7e56a7dc | 8 | #include <linux/bcd.h> |
4b3a6a3a AB |
9 | #include <linux/i2c.h> |
10 | #include <linux/module.h> | |
5909b87d | 11 | #include <linux/of_device.h> |
9ece7cd8 | 12 | #include <linux/of_irq.h> |
4b3a6a3a | 13 | #include <linux/rtc.h> |
7e56a7dc | 14 | |
7e56a7dc HVR |
15 | /* Register map */ |
16 | /* rtc section */ | |
17 | #define ISL1208_REG_SC 0x00 | |
18 | #define ISL1208_REG_MN 0x01 | |
19 | #define ISL1208_REG_HR 0x02 | |
9edae7bc AZ |
20 | #define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */ |
21 | #define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ | |
7e56a7dc HVR |
22 | #define ISL1208_REG_DT 0x03 |
23 | #define ISL1208_REG_MO 0x04 | |
24 | #define ISL1208_REG_YR 0x05 | |
25 | #define ISL1208_REG_DW 0x06 | |
26 | #define ISL1208_RTC_SECTION_LEN 7 | |
27 | ||
28 | /* control/status section */ | |
29 | #define ISL1208_REG_SR 0x07 | |
9edae7bc AZ |
30 | #define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ |
31 | #define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ | |
32 | #define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ | |
dd35bdb0 | 33 | #define ISL1208_REG_SR_EVT (1<<3) /* event */ |
9edae7bc AZ |
34 | #define ISL1208_REG_SR_ALM (1<<2) /* alarm */ |
35 | #define ISL1208_REG_SR_BAT (1<<1) /* battery */ | |
36 | #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ | |
7e56a7dc | 37 | #define ISL1208_REG_INT 0x08 |
cf044f0e RM |
38 | #define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ |
39 | #define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ | |
dd35bdb0 MG |
40 | #define ISL1219_REG_EV 0x09 |
41 | #define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */ | |
cfa30622 | 42 | #define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */ |
7e56a7dc HVR |
43 | #define ISL1208_REG_ATR 0x0a |
44 | #define ISL1208_REG_DTR 0x0b | |
45 | ||
46 | /* alarm section */ | |
47 | #define ISL1208_REG_SCA 0x0c | |
48 | #define ISL1208_REG_MNA 0x0d | |
49 | #define ISL1208_REG_HRA 0x0e | |
50 | #define ISL1208_REG_DTA 0x0f | |
51 | #define ISL1208_REG_MOA 0x10 | |
52 | #define ISL1208_REG_DWA 0x11 | |
53 | #define ISL1208_ALARM_SECTION_LEN 6 | |
54 | ||
55 | /* user section */ | |
56 | #define ISL1208_REG_USR1 0x12 | |
57 | #define ISL1208_REG_USR2 0x13 | |
58 | #define ISL1208_USR_SECTION_LEN 2 | |
59 | ||
dd35bdb0 MG |
60 | /* event section */ |
61 | #define ISL1219_REG_SCT 0x14 | |
62 | #define ISL1219_REG_MNT 0x15 | |
63 | #define ISL1219_REG_HRT 0x16 | |
64 | #define ISL1219_REG_DTT 0x17 | |
65 | #define ISL1219_REG_MOT 0x18 | |
66 | #define ISL1219_REG_YRT 0x19 | |
67 | #define ISL1219_EVT_SECTION_LEN 6 | |
68 | ||
9edae7bc | 69 | static struct i2c_driver isl1208_driver; |
7e56a7dc | 70 | |
dd35bdb0 | 71 | /* ISL1208 various variants */ |
5909b87d | 72 | enum isl1208_id { |
dd35bdb0 | 73 | TYPE_ISL1208 = 0, |
5909b87d | 74 | TYPE_ISL1209, |
dd35bdb0 MG |
75 | TYPE_ISL1218, |
76 | TYPE_ISL1219, | |
5909b87d | 77 | ISL_LAST_ID |
dd35bdb0 MG |
78 | }; |
79 | ||
5909b87d TP |
80 | /* Chip capabilities table */ |
81 | static const struct isl1208_config { | |
82 | const char name[8]; | |
83 | unsigned int nvmem_length; | |
84 | unsigned has_tamper:1; | |
85 | unsigned has_timestamp:1; | |
86 | } isl1208_configs[] = { | |
87 | [TYPE_ISL1208] = { "isl1208", 2, false, false }, | |
88 | [TYPE_ISL1209] = { "isl1209", 2, true, false }, | |
89 | [TYPE_ISL1218] = { "isl1218", 8, false, false }, | |
90 | [TYPE_ISL1219] = { "isl1219", 2, true, true }, | |
91 | }; | |
92 | ||
93 | static const struct i2c_device_id isl1208_id[] = { | |
94 | { "isl1208", TYPE_ISL1208 }, | |
95 | { "isl1209", TYPE_ISL1209 }, | |
96 | { "isl1218", TYPE_ISL1218 }, | |
97 | { "isl1219", TYPE_ISL1219 }, | |
98 | { } | |
99 | }; | |
100 | MODULE_DEVICE_TABLE(i2c, isl1208_id); | |
101 | ||
413b7841 | 102 | static const __maybe_unused struct of_device_id isl1208_of_match[] = { |
5909b87d TP |
103 | { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] }, |
104 | { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] }, | |
105 | { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] }, | |
106 | { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] }, | |
107 | { } | |
108 | }; | |
109 | MODULE_DEVICE_TABLE(of, isl1208_of_match); | |
110 | ||
ed3c52a0 TP |
111 | /* Device state */ |
112 | struct isl1208_state { | |
c3544f6f | 113 | struct nvmem_config nvmem_config; |
ed3c52a0 | 114 | struct rtc_device *rtc; |
5909b87d | 115 | const struct isl1208_config *config; |
ed3c52a0 TP |
116 | }; |
117 | ||
7e56a7dc HVR |
118 | /* block read */ |
119 | static int | |
120 | isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], | |
9edae7bc | 121 | unsigned len) |
7e56a7dc | 122 | { |
7e56a7dc HVR |
123 | int ret; |
124 | ||
dd35bdb0 MG |
125 | WARN_ON(reg > ISL1219_REG_YRT); |
126 | WARN_ON(reg + len > ISL1219_REG_YRT + 1); | |
7e56a7dc | 127 | |
facc23b8 TP |
128 | ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf); |
129 | return (ret < 0) ? ret : 0; | |
7e56a7dc HVR |
130 | } |
131 | ||
132 | /* block write */ | |
133 | static int | |
134 | isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], | |
9edae7bc | 135 | unsigned len) |
7e56a7dc | 136 | { |
7e56a7dc HVR |
137 | int ret; |
138 | ||
dd35bdb0 MG |
139 | WARN_ON(reg > ISL1219_REG_YRT); |
140 | WARN_ON(reg + len > ISL1219_REG_YRT + 1); | |
7e56a7dc | 141 | |
facc23b8 TP |
142 | ret = i2c_smbus_write_i2c_block_data(client, reg, len, buf); |
143 | return (ret < 0) ? ret : 0; | |
7e56a7dc HVR |
144 | } |
145 | ||
48fc7f7e | 146 | /* simple check to see whether we have a isl1208 */ |
9edae7bc AZ |
147 | static int |
148 | isl1208_i2c_validate_client(struct i2c_client *client) | |
7e56a7dc HVR |
149 | { |
150 | u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; | |
151 | u8 zero_mask[ISL1208_RTC_SECTION_LEN] = { | |
152 | 0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8 | |
153 | }; | |
154 | int i; | |
155 | int ret; | |
156 | ||
157 | ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); | |
158 | if (ret < 0) | |
159 | return ret; | |
160 | ||
161 | for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) { | |
9edae7bc | 162 | if (regs[i] & zero_mask[i]) /* check if bits are cleared */ |
7e56a7dc HVR |
163 | return -ENODEV; |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
9edae7bc AZ |
169 | static int |
170 | isl1208_i2c_get_sr(struct i2c_client *client) | |
7e56a7dc | 171 | { |
c91fd91d | 172 | return i2c_smbus_read_byte_data(client, ISL1208_REG_SR); |
7e56a7dc HVR |
173 | } |
174 | ||
9edae7bc AZ |
175 | static int |
176 | isl1208_i2c_get_atr(struct i2c_client *client) | |
7e56a7dc HVR |
177 | { |
178 | int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR); | |
7e56a7dc | 179 | if (atr < 0) |
9edae7bc | 180 | return atr; |
7e56a7dc HVR |
181 | |
182 | /* The 6bit value in the ATR register controls the load | |
183 | * capacitance C_load * in steps of 0.25pF | |
184 | * | |
185 | * bit (1<<5) of the ATR register is inverted | |
186 | * | |
187 | * C_load(ATR=0x20) = 4.50pF | |
188 | * C_load(ATR=0x00) = 12.50pF | |
189 | * C_load(ATR=0x1f) = 20.25pF | |
190 | * | |
191 | */ | |
192 | ||
9edae7bc AZ |
193 | atr &= 0x3f; /* mask out lsb */ |
194 | atr ^= 1 << 5; /* invert 6th bit */ | |
195 | atr += 2 * 9; /* add offset of 4.5pF; unit[atr] = 0.25pF */ | |
7e56a7dc HVR |
196 | |
197 | return atr; | |
198 | } | |
199 | ||
c8c97a4f | 200 | /* returns adjustment value + 100 */ |
9edae7bc AZ |
201 | static int |
202 | isl1208_i2c_get_dtr(struct i2c_client *client) | |
7e56a7dc HVR |
203 | { |
204 | int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR); | |
7e56a7dc HVR |
205 | if (dtr < 0) |
206 | return -EIO; | |
207 | ||
208 | /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ | |
9edae7bc | 209 | dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1); |
7e56a7dc | 210 | |
c8c97a4f | 211 | return dtr + 100; |
7e56a7dc HVR |
212 | } |
213 | ||
9edae7bc AZ |
214 | static int |
215 | isl1208_i2c_get_usr(struct i2c_client *client) | |
7e56a7dc HVR |
216 | { |
217 | u8 buf[ISL1208_USR_SECTION_LEN] = { 0, }; | |
218 | int ret; | |
219 | ||
9edae7bc AZ |
220 | ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1, buf, |
221 | ISL1208_USR_SECTION_LEN); | |
7e56a7dc HVR |
222 | if (ret < 0) |
223 | return ret; | |
224 | ||
225 | return (buf[1] << 8) | buf[0]; | |
226 | } | |
227 | ||
9edae7bc AZ |
228 | static int |
229 | isl1208_i2c_set_usr(struct i2c_client *client, u16 usr) | |
7e56a7dc HVR |
230 | { |
231 | u8 buf[ISL1208_USR_SECTION_LEN]; | |
232 | ||
233 | buf[0] = usr & 0xff; | |
234 | buf[1] = (usr >> 8) & 0xff; | |
235 | ||
9edae7bc AZ |
236 | return isl1208_i2c_set_regs(client, ISL1208_REG_USR1, buf, |
237 | ISL1208_USR_SECTION_LEN); | |
7e56a7dc HVR |
238 | } |
239 | ||
cf044f0e RM |
240 | static int |
241 | isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable) | |
242 | { | |
243 | int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); | |
244 | ||
245 | if (icr < 0) { | |
246 | dev_err(&client->dev, "%s: reading INT failed\n", __func__); | |
247 | return icr; | |
248 | } | |
249 | ||
250 | if (enable) | |
251 | icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM; | |
252 | else | |
253 | icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM); | |
254 | ||
255 | icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr); | |
256 | if (icr < 0) { | |
257 | dev_err(&client->dev, "%s: writing INT failed\n", __func__); | |
258 | return icr; | |
259 | } | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
9edae7bc AZ |
264 | static int |
265 | isl1208_rtc_proc(struct device *dev, struct seq_file *seq) | |
7e56a7dc HVR |
266 | { |
267 | struct i2c_client *const client = to_i2c_client(dev); | |
268 | int sr, dtr, atr, usr; | |
269 | ||
270 | sr = isl1208_i2c_get_sr(client); | |
271 | if (sr < 0) { | |
272 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | |
273 | return sr; | |
274 | } | |
275 | ||
276 | seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n", | |
277 | (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "", | |
278 | (sr & ISL1208_REG_SR_BAT) ? " BAT" : "", | |
279 | (sr & ISL1208_REG_SR_ALM) ? " ALM" : "", | |
280 | (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "", | |
281 | (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "", | |
9edae7bc | 282 | (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", sr); |
7e56a7dc HVR |
283 | |
284 | seq_printf(seq, "batt_status\t: %s\n", | |
285 | (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); | |
286 | ||
287 | dtr = isl1208_i2c_get_dtr(client); | |
c8c97a4f TP |
288 | if (dtr >= 0) |
289 | seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100); | |
7e56a7dc HVR |
290 | |
291 | atr = isl1208_i2c_get_atr(client); | |
292 | if (atr >= 0) | |
293 | seq_printf(seq, "analog_trim\t: %d.%.2d pF\n", | |
9edae7bc | 294 | atr >> 2, (atr & 0x3) * 25); |
7e56a7dc HVR |
295 | |
296 | usr = isl1208_i2c_get_usr(client); | |
297 | if (usr >= 0) | |
298 | seq_printf(seq, "user_data\t: 0x%.4x\n", usr); | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
9edae7bc AZ |
303 | static int |
304 | isl1208_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) | |
7e56a7dc HVR |
305 | { |
306 | int sr; | |
307 | u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; | |
308 | ||
309 | sr = isl1208_i2c_get_sr(client); | |
310 | if (sr < 0) { | |
311 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | |
312 | return -EIO; | |
313 | } | |
314 | ||
315 | sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); | |
316 | if (sr < 0) { | |
317 | dev_err(&client->dev, "%s: reading RTC section failed\n", | |
318 | __func__); | |
319 | return sr; | |
320 | } | |
321 | ||
fe20ba70 AB |
322 | tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]); |
323 | tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]); | |
9edae7bc AZ |
324 | |
325 | /* HR field has a more complex interpretation */ | |
326 | { | |
7e56a7dc | 327 | const u8 _hr = regs[ISL1208_REG_HR]; |
9edae7bc | 328 | if (_hr & ISL1208_REG_HR_MIL) /* 24h format */ |
fe20ba70 | 329 | tm->tm_hour = bcd2bin(_hr & 0x3f); |
9edae7bc AZ |
330 | else { |
331 | /* 12h format */ | |
fe20ba70 | 332 | tm->tm_hour = bcd2bin(_hr & 0x1f); |
9edae7bc | 333 | if (_hr & ISL1208_REG_HR_PM) /* PM flag set */ |
7e56a7dc HVR |
334 | tm->tm_hour += 12; |
335 | } | |
336 | } | |
337 | ||
fe20ba70 AB |
338 | tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]); |
339 | tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */ | |
340 | tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 100; | |
341 | tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]); | |
7e56a7dc HVR |
342 | |
343 | return 0; | |
344 | } | |
345 | ||
9edae7bc AZ |
346 | static int |
347 | isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) | |
7e56a7dc HVR |
348 | { |
349 | struct rtc_time *const tm = &alarm->time; | |
350 | u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; | |
cf044f0e | 351 | int icr, yr, sr = isl1208_i2c_get_sr(client); |
7e56a7dc | 352 | |
7e56a7dc HVR |
353 | if (sr < 0) { |
354 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | |
355 | return sr; | |
356 | } | |
357 | ||
358 | sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs, | |
9edae7bc | 359 | ISL1208_ALARM_SECTION_LEN); |
7e56a7dc HVR |
360 | if (sr < 0) { |
361 | dev_err(&client->dev, "%s: reading alarm section failed\n", | |
362 | __func__); | |
363 | return sr; | |
364 | } | |
365 | ||
366 | /* MSB of each alarm register is an enable bit */ | |
fe20ba70 AB |
367 | tm->tm_sec = bcd2bin(regs[ISL1208_REG_SCA - ISL1208_REG_SCA] & 0x7f); |
368 | tm->tm_min = bcd2bin(regs[ISL1208_REG_MNA - ISL1208_REG_SCA] & 0x7f); | |
369 | tm->tm_hour = bcd2bin(regs[ISL1208_REG_HRA - ISL1208_REG_SCA] & 0x3f); | |
370 | tm->tm_mday = bcd2bin(regs[ISL1208_REG_DTA - ISL1208_REG_SCA] & 0x3f); | |
9edae7bc | 371 | tm->tm_mon = |
fe20ba70 AB |
372 | bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1; |
373 | tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03); | |
7e56a7dc | 374 | |
cf044f0e RM |
375 | /* The alarm doesn't store the year so get it from the rtc section */ |
376 | yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR); | |
377 | if (yr < 0) { | |
378 | dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__); | |
379 | return yr; | |
380 | } | |
381 | tm->tm_year = bcd2bin(yr) + 100; | |
382 | ||
383 | icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); | |
384 | if (icr < 0) { | |
385 | dev_err(&client->dev, "%s: reading INT failed\n", __func__); | |
386 | return icr; | |
387 | } | |
388 | alarm->enabled = !!(icr & ISL1208_REG_INT_ALME); | |
389 | ||
390 | return 0; | |
391 | } | |
392 | ||
393 | static int | |
394 | isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) | |
395 | { | |
396 | struct rtc_time *alarm_tm = &alarm->time; | |
397 | u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; | |
398 | const int offs = ISL1208_REG_SCA; | |
cf044f0e RM |
399 | struct rtc_time rtc_tm; |
400 | int err, enable; | |
401 | ||
402 | err = isl1208_i2c_read_time(client, &rtc_tm); | |
cf044f0e RM |
403 | if (err) |
404 | return err; | |
405 | ||
406 | /* If the alarm time is before the current time disable the alarm */ | |
f118db1e | 407 | if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0) |
cf044f0e RM |
408 | enable = 0x00; |
409 | else | |
410 | enable = 0x80; | |
411 | ||
412 | /* Program the alarm and enable it for each setting */ | |
413 | regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable; | |
414 | regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable; | |
415 | regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) | | |
416 | ISL1208_REG_HR_MIL | enable; | |
417 | ||
418 | regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable; | |
419 | regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable; | |
420 | regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable; | |
421 | ||
422 | /* write ALARM registers */ | |
423 | err = isl1208_i2c_set_regs(client, offs, regs, | |
424 | ISL1208_ALARM_SECTION_LEN); | |
425 | if (err < 0) { | |
426 | dev_err(&client->dev, "%s: writing ALARM section failed\n", | |
427 | __func__); | |
428 | return err; | |
429 | } | |
430 | ||
431 | err = isl1208_rtc_toggle_alarm(client, enable); | |
432 | if (err) | |
433 | return err; | |
434 | ||
7e56a7dc HVR |
435 | return 0; |
436 | } | |
437 | ||
9edae7bc AZ |
438 | static int |
439 | isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
7e56a7dc HVR |
440 | { |
441 | return isl1208_i2c_read_time(to_i2c_client(dev), tm); | |
442 | } | |
443 | ||
9edae7bc AZ |
444 | static int |
445 | isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm) | |
7e56a7dc HVR |
446 | { |
447 | int sr; | |
448 | u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; | |
449 | ||
cc6c2ca3 CE |
450 | /* The clock has an 8 bit wide bcd-coded register (they never learn) |
451 | * for the year. tm_year is an offset from 1900 and we are interested | |
452 | * in the 2000-2099 range, so any value less than 100 is invalid. | |
453 | */ | |
454 | if (tm->tm_year < 100) | |
455 | return -EINVAL; | |
456 | ||
fe20ba70 AB |
457 | regs[ISL1208_REG_SC] = bin2bcd(tm->tm_sec); |
458 | regs[ISL1208_REG_MN] = bin2bcd(tm->tm_min); | |
459 | regs[ISL1208_REG_HR] = bin2bcd(tm->tm_hour) | ISL1208_REG_HR_MIL; | |
7e56a7dc | 460 | |
fe20ba70 AB |
461 | regs[ISL1208_REG_DT] = bin2bcd(tm->tm_mday); |
462 | regs[ISL1208_REG_MO] = bin2bcd(tm->tm_mon + 1); | |
463 | regs[ISL1208_REG_YR] = bin2bcd(tm->tm_year - 100); | |
7e56a7dc | 464 | |
fe20ba70 | 465 | regs[ISL1208_REG_DW] = bin2bcd(tm->tm_wday & 7); |
7e56a7dc HVR |
466 | |
467 | sr = isl1208_i2c_get_sr(client); | |
468 | if (sr < 0) { | |
469 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | |
470 | return sr; | |
471 | } | |
472 | ||
473 | /* set WRTC */ | |
9edae7bc | 474 | sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, |
7e56a7dc HVR |
475 | sr | ISL1208_REG_SR_WRTC); |
476 | if (sr < 0) { | |
477 | dev_err(&client->dev, "%s: writing SR failed\n", __func__); | |
478 | return sr; | |
479 | } | |
480 | ||
481 | /* write RTC registers */ | |
482 | sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); | |
483 | if (sr < 0) { | |
484 | dev_err(&client->dev, "%s: writing RTC section failed\n", | |
485 | __func__); | |
486 | return sr; | |
487 | } | |
488 | ||
489 | /* clear WRTC again */ | |
5b9fc795 DO |
490 | sr = isl1208_i2c_get_sr(client); |
491 | if (sr < 0) { | |
492 | dev_err(&client->dev, "%s: reading SR failed\n", __func__); | |
493 | return sr; | |
494 | } | |
9edae7bc | 495 | sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, |
7e56a7dc HVR |
496 | sr & ~ISL1208_REG_SR_WRTC); |
497 | if (sr < 0) { | |
498 | dev_err(&client->dev, "%s: writing SR failed\n", __func__); | |
499 | return sr; | |
500 | } | |
501 | ||
502 | return 0; | |
503 | } | |
504 | ||
505 | ||
9edae7bc AZ |
506 | static int |
507 | isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
7e56a7dc HVR |
508 | { |
509 | return isl1208_i2c_set_time(to_i2c_client(dev), tm); | |
510 | } | |
511 | ||
9edae7bc AZ |
512 | static int |
513 | isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | |
7e56a7dc HVR |
514 | { |
515 | return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); | |
516 | } | |
517 | ||
cf044f0e RM |
518 | static int |
519 | isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | |
520 | { | |
521 | return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); | |
522 | } | |
523 | ||
dd35bdb0 MG |
524 | static ssize_t timestamp0_store(struct device *dev, |
525 | struct device_attribute *attr, | |
526 | const char *buf, size_t count) | |
527 | { | |
1b4c794f | 528 | struct i2c_client *client = to_i2c_client(dev->parent); |
dd35bdb0 MG |
529 | int sr; |
530 | ||
531 | sr = isl1208_i2c_get_sr(client); | |
532 | if (sr < 0) { | |
533 | dev_err(dev, "%s: reading SR failed\n", __func__); | |
534 | return sr; | |
535 | } | |
536 | ||
537 | sr &= ~ISL1208_REG_SR_EVT; | |
538 | ||
539 | sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); | |
540 | if (sr < 0) | |
541 | dev_err(dev, "%s: writing SR failed\n", | |
542 | __func__); | |
543 | ||
544 | return count; | |
545 | }; | |
546 | ||
547 | static ssize_t timestamp0_show(struct device *dev, | |
548 | struct device_attribute *attr, char *buf) | |
549 | { | |
1b4c794f | 550 | struct i2c_client *client = to_i2c_client(dev->parent); |
dd35bdb0 MG |
551 | u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, }; |
552 | struct rtc_time tm; | |
553 | int sr; | |
554 | ||
555 | sr = isl1208_i2c_get_sr(client); | |
556 | if (sr < 0) { | |
557 | dev_err(dev, "%s: reading SR failed\n", __func__); | |
558 | return sr; | |
559 | } | |
560 | ||
561 | if (!(sr & ISL1208_REG_SR_EVT)) | |
562 | return 0; | |
563 | ||
564 | sr = isl1208_i2c_read_regs(client, ISL1219_REG_SCT, regs, | |
565 | ISL1219_EVT_SECTION_LEN); | |
566 | if (sr < 0) { | |
567 | dev_err(dev, "%s: reading event section failed\n", | |
568 | __func__); | |
569 | return 0; | |
570 | } | |
571 | ||
572 | /* MSB of each alarm register is an enable bit */ | |
573 | tm.tm_sec = bcd2bin(regs[ISL1219_REG_SCT - ISL1219_REG_SCT] & 0x7f); | |
574 | tm.tm_min = bcd2bin(regs[ISL1219_REG_MNT - ISL1219_REG_SCT] & 0x7f); | |
575 | tm.tm_hour = bcd2bin(regs[ISL1219_REG_HRT - ISL1219_REG_SCT] & 0x3f); | |
576 | tm.tm_mday = bcd2bin(regs[ISL1219_REG_DTT - ISL1219_REG_SCT] & 0x3f); | |
577 | tm.tm_mon = | |
578 | bcd2bin(regs[ISL1219_REG_MOT - ISL1219_REG_SCT] & 0x1f) - 1; | |
579 | tm.tm_year = bcd2bin(regs[ISL1219_REG_YRT - ISL1219_REG_SCT]) + 100; | |
580 | ||
581 | sr = rtc_valid_tm(&tm); | |
582 | if (sr) | |
583 | return sr; | |
584 | ||
585 | return sprintf(buf, "%llu\n", | |
586 | (unsigned long long)rtc_tm_to_time64(&tm)); | |
587 | }; | |
588 | ||
589 | static DEVICE_ATTR_RW(timestamp0); | |
590 | ||
cf044f0e RM |
591 | static irqreturn_t |
592 | isl1208_rtc_interrupt(int irq, void *data) | |
593 | { | |
594 | unsigned long timeout = jiffies + msecs_to_jiffies(1000); | |
595 | struct i2c_client *client = data; | |
ed3c52a0 | 596 | struct isl1208_state *isl1208 = i2c_get_clientdata(client); |
cf044f0e RM |
597 | int handled = 0, sr, err; |
598 | ||
599 | /* | |
600 | * I2C reads get NAK'ed if we read straight away after an interrupt? | |
601 | * Using a mdelay/msleep didn't seem to help either, so we work around | |
602 | * this by continually trying to read the register for a short time. | |
603 | */ | |
604 | while (1) { | |
605 | sr = isl1208_i2c_get_sr(client); | |
606 | if (sr >= 0) | |
607 | break; | |
608 | ||
609 | if (time_after(jiffies, timeout)) { | |
610 | dev_err(&client->dev, "%s: reading SR failed\n", | |
611 | __func__); | |
612 | return sr; | |
613 | } | |
614 | } | |
615 | ||
616 | if (sr & ISL1208_REG_SR_ALM) { | |
617 | dev_dbg(&client->dev, "alarm!\n"); | |
618 | ||
ed3c52a0 | 619 | rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF); |
72fca4a4 | 620 | |
cf044f0e RM |
621 | /* Clear the alarm */ |
622 | sr &= ~ISL1208_REG_SR_ALM; | |
623 | sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); | |
624 | if (sr < 0) | |
625 | dev_err(&client->dev, "%s: writing SR failed\n", | |
626 | __func__); | |
627 | else | |
628 | handled = 1; | |
629 | ||
630 | /* Disable the alarm */ | |
631 | err = isl1208_rtc_toggle_alarm(client, 0); | |
632 | if (err) | |
633 | return err; | |
634 | } | |
635 | ||
5909b87d | 636 | if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) { |
dd35bdb0 MG |
637 | dev_warn(&client->dev, "event detected"); |
638 | handled = 1; | |
5909b87d TP |
639 | if (isl1208->config->has_timestamp) |
640 | sysfs_notify(&isl1208->rtc->dev.kobj, NULL, | |
641 | dev_attr_timestamp0.attr.name); | |
dd35bdb0 MG |
642 | } |
643 | ||
cf044f0e RM |
644 | return handled ? IRQ_HANDLED : IRQ_NONE; |
645 | } | |
646 | ||
ff8371ac | 647 | static const struct rtc_class_ops isl1208_rtc_ops = { |
9edae7bc AZ |
648 | .proc = isl1208_rtc_proc, |
649 | .read_time = isl1208_rtc_read_time, | |
650 | .set_time = isl1208_rtc_set_time, | |
651 | .read_alarm = isl1208_rtc_read_alarm, | |
cf044f0e | 652 | .set_alarm = isl1208_rtc_set_alarm, |
7e56a7dc HVR |
653 | }; |
654 | ||
655 | /* sysfs interface */ | |
656 | ||
9edae7bc AZ |
657 | static ssize_t |
658 | isl1208_sysfs_show_atrim(struct device *dev, | |
659 | struct device_attribute *attr, char *buf) | |
7e56a7dc | 660 | { |
1b4c794f | 661 | int atr = isl1208_i2c_get_atr(to_i2c_client(dev->parent)); |
7e56a7dc HVR |
662 | if (atr < 0) |
663 | return atr; | |
664 | ||
9edae7bc | 665 | return sprintf(buf, "%d.%.2d pF\n", atr >> 2, (atr & 0x3) * 25); |
7e56a7dc | 666 | } |
9edae7bc | 667 | |
7e56a7dc HVR |
668 | static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL); |
669 | ||
9edae7bc AZ |
670 | static ssize_t |
671 | isl1208_sysfs_show_dtrim(struct device *dev, | |
672 | struct device_attribute *attr, char *buf) | |
7e56a7dc | 673 | { |
1b4c794f | 674 | int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev->parent)); |
7e56a7dc HVR |
675 | if (dtr < 0) |
676 | return dtr; | |
677 | ||
c8c97a4f | 678 | return sprintf(buf, "%d ppm\n", dtr - 100); |
7e56a7dc | 679 | } |
9edae7bc | 680 | |
7e56a7dc HVR |
681 | static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); |
682 | ||
9edae7bc AZ |
683 | static ssize_t |
684 | isl1208_sysfs_show_usr(struct device *dev, | |
685 | struct device_attribute *attr, char *buf) | |
7e56a7dc | 686 | { |
1b4c794f | 687 | int usr = isl1208_i2c_get_usr(to_i2c_client(dev->parent)); |
7e56a7dc HVR |
688 | if (usr < 0) |
689 | return usr; | |
690 | ||
691 | return sprintf(buf, "0x%.4x\n", usr); | |
692 | } | |
693 | ||
9edae7bc AZ |
694 | static ssize_t |
695 | isl1208_sysfs_store_usr(struct device *dev, | |
696 | struct device_attribute *attr, | |
697 | const char *buf, size_t count) | |
7e56a7dc HVR |
698 | { |
699 | int usr = -1; | |
700 | ||
701 | if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) { | |
702 | if (sscanf(buf, "%x", &usr) != 1) | |
703 | return -EINVAL; | |
704 | } else { | |
705 | if (sscanf(buf, "%d", &usr) != 1) | |
706 | return -EINVAL; | |
707 | } | |
708 | ||
709 | if (usr < 0 || usr > 0xffff) | |
710 | return -EINVAL; | |
711 | ||
1b4c794f AB |
712 | if (isl1208_i2c_set_usr(to_i2c_client(dev->parent), usr)) |
713 | return -EIO; | |
714 | ||
715 | return count; | |
7e56a7dc | 716 | } |
9edae7bc | 717 | |
7e56a7dc HVR |
718 | static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr, |
719 | isl1208_sysfs_store_usr); | |
720 | ||
e17ab5cb HS |
721 | static struct attribute *isl1208_rtc_attrs[] = { |
722 | &dev_attr_atrim.attr, | |
723 | &dev_attr_dtrim.attr, | |
724 | &dev_attr_usr.attr, | |
725 | NULL | |
726 | }; | |
9edae7bc | 727 | |
e17ab5cb HS |
728 | static const struct attribute_group isl1208_rtc_sysfs_files = { |
729 | .attrs = isl1208_rtc_attrs, | |
730 | }; | |
9edae7bc | 731 | |
dd35bdb0 MG |
732 | static struct attribute *isl1219_rtc_attrs[] = { |
733 | &dev_attr_timestamp0.attr, | |
734 | NULL | |
735 | }; | |
736 | ||
737 | static const struct attribute_group isl1219_rtc_sysfs_files = { | |
738 | .attrs = isl1219_rtc_attrs, | |
739 | }; | |
740 | ||
c3544f6f TP |
741 | static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf, |
742 | size_t count) | |
743 | { | |
744 | struct isl1208_state *isl1208 = priv; | |
745 | struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); | |
746 | int ret; | |
747 | ||
748 | /* nvmem sanitizes offset/count for us, but count==0 is possible */ | |
749 | if (!count) | |
750 | return count; | |
751 | ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf, | |
752 | count); | |
753 | return ret == 0 ? count : ret; | |
754 | } | |
755 | ||
756 | static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf, | |
757 | size_t count) | |
758 | { | |
759 | struct isl1208_state *isl1208 = priv; | |
760 | struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); | |
761 | int ret; | |
762 | ||
763 | /* nvmem sanitizes off/count for us, but count==0 is possible */ | |
764 | if (!count) | |
765 | return count; | |
766 | ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf, | |
767 | count); | |
768 | ||
769 | return ret == 0 ? count : ret; | |
770 | } | |
771 | ||
772 | static const struct nvmem_config isl1208_nvmem_config = { | |
773 | .name = "isl1208_nvram", | |
774 | .word_size = 1, | |
775 | .stride = 1, | |
776 | /* .size from chip specific config */ | |
777 | .reg_read = isl1208_nvmem_read, | |
778 | .reg_write = isl1208_nvmem_write, | |
779 | }; | |
780 | ||
9ece7cd8 DO |
781 | static int isl1208_setup_irq(struct i2c_client *client, int irq) |
782 | { | |
783 | int rc = devm_request_threaded_irq(&client->dev, irq, NULL, | |
784 | isl1208_rtc_interrupt, | |
785 | IRQF_SHARED | IRQF_ONESHOT, | |
786 | isl1208_driver.driver.name, | |
787 | client); | |
788 | if (!rc) { | |
789 | device_init_wakeup(&client->dev, 1); | |
790 | enable_irq_wake(irq); | |
791 | } else { | |
792 | dev_err(&client->dev, | |
793 | "Unable to request irq %d, no alarm support\n", | |
794 | irq); | |
795 | } | |
796 | return rc; | |
797 | } | |
798 | ||
9edae7bc | 799 | static int |
2611e6d7 | 800 | isl1208_probe(struct i2c_client *client) |
9edae7bc AZ |
801 | { |
802 | int rc = 0; | |
ed3c52a0 | 803 | struct isl1208_state *isl1208; |
9ece7cd8 | 804 | int evdet_irq = -1; |
7e56a7dc | 805 | |
9edae7bc AZ |
806 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) |
807 | return -ENODEV; | |
7e56a7dc | 808 | |
9edae7bc AZ |
809 | if (isl1208_i2c_validate_client(client) < 0) |
810 | return -ENODEV; | |
811 | ||
ed3c52a0 TP |
812 | /* Allocate driver state, point i2c client data to it */ |
813 | isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL); | |
814 | if (!isl1208) | |
815 | return -ENOMEM; | |
816 | i2c_set_clientdata(client, isl1208); | |
7e56a7dc | 817 | |
5909b87d TP |
818 | /* Determine which chip we have */ |
819 | if (client->dev.of_node) { | |
820 | isl1208->config = of_device_get_match_data(&client->dev); | |
821 | if (!isl1208->config) | |
822 | return -ENODEV; | |
823 | } else { | |
2611e6d7 UKK |
824 | const struct i2c_device_id *id = i2c_match_id(isl1208_id, client); |
825 | ||
5909b87d TP |
826 | if (id->driver_data >= ISL_LAST_ID) |
827 | return -ENODEV; | |
828 | isl1208->config = &isl1208_configs[id->driver_data]; | |
829 | } | |
830 | ||
ed3c52a0 TP |
831 | isl1208->rtc = devm_rtc_allocate_device(&client->dev); |
832 | if (IS_ERR(isl1208->rtc)) | |
833 | return PTR_ERR(isl1208->rtc); | |
236b7187 | 834 | |
ed3c52a0 | 835 | isl1208->rtc->ops = &isl1208_rtc_ops; |
7e56a7dc | 836 | |
c3544f6f TP |
837 | /* Setup nvmem configuration in driver state struct */ |
838 | isl1208->nvmem_config = isl1208_nvmem_config; | |
839 | isl1208->nvmem_config.size = isl1208->config->nvmem_length; | |
840 | isl1208->nvmem_config.priv = isl1208; | |
841 | ||
9edae7bc | 842 | rc = isl1208_i2c_get_sr(client); |
7e56a7dc | 843 | if (rc < 0) { |
9edae7bc | 844 | dev_err(&client->dev, "reading status failed\n"); |
adce8c14 | 845 | return rc; |
7e56a7dc HVR |
846 | } |
847 | ||
848 | if (rc & ISL1208_REG_SR_RTCF) | |
9edae7bc | 849 | dev_warn(&client->dev, "rtc power failure detected, " |
7e56a7dc HVR |
850 | "please set clock.\n"); |
851 | ||
5909b87d | 852 | if (isl1208->config->has_tamper) { |
cfa30622 DO |
853 | struct device_node *np = client->dev.of_node; |
854 | u32 evienb; | |
855 | ||
856 | rc = i2c_smbus_read_byte_data(client, ISL1219_REG_EV); | |
857 | if (rc < 0) { | |
858 | dev_err(&client->dev, "failed to read EV reg\n"); | |
859 | return rc; | |
860 | } | |
861 | rc |= ISL1219_REG_EV_EVEN; | |
862 | if (!of_property_read_u32(np, "isil,ev-evienb", &evienb)) { | |
863 | if (evienb) | |
864 | rc |= ISL1219_REG_EV_EVIENB; | |
865 | else | |
866 | rc &= ~ISL1219_REG_EV_EVIENB; | |
867 | } | |
868 | rc = i2c_smbus_write_byte_data(client, ISL1219_REG_EV, rc); | |
dd35bdb0 MG |
869 | if (rc < 0) { |
870 | dev_err(&client->dev, "could not enable tamper detection\n"); | |
871 | return rc; | |
872 | } | |
5909b87d TP |
873 | evdet_irq = of_irq_get_byname(np, "evdet"); |
874 | } | |
875 | if (isl1208->config->has_timestamp) { | |
ed3c52a0 | 876 | rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); |
dd35bdb0 MG |
877 | if (rc) |
878 | return rc; | |
879 | } | |
880 | ||
ed3c52a0 | 881 | rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); |
9edae7bc | 882 | if (rc) |
adce8c14 | 883 | return rc; |
7e56a7dc | 884 | |
2023c5c8 | 885 | if (client->irq > 0) { |
9ece7cd8 | 886 | rc = isl1208_setup_irq(client, client->irq); |
2023c5c8 QS |
887 | if (rc) |
888 | return rc; | |
889 | ||
890 | } else { | |
891 | clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features); | |
892 | } | |
9ece7cd8 DO |
893 | |
894 | if (evdet_irq > 0 && evdet_irq != client->irq) | |
895 | rc = isl1208_setup_irq(client, evdet_irq); | |
896 | if (rc) | |
897 | return rc; | |
9d327c2d | 898 | |
3a905c2d | 899 | rc = devm_rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config); |
c3544f6f TP |
900 | if (rc) |
901 | return rc; | |
902 | ||
fdcfd854 | 903 | return devm_rtc_register_device(isl1208->rtc); |
7e56a7dc HVR |
904 | } |
905 | ||
9edae7bc AZ |
906 | static struct i2c_driver isl1208_driver = { |
907 | .driver = { | |
03835504 JMC |
908 | .name = "rtc-isl1208", |
909 | .of_match_table = of_match_ptr(isl1208_of_match), | |
910 | }, | |
2611e6d7 | 911 | .probe_new = isl1208_probe, |
3760f736 | 912 | .id_table = isl1208_id, |
9edae7bc | 913 | }; |
7e56a7dc | 914 | |
0abc9201 | 915 | module_i2c_driver(isl1208_driver); |
7e56a7dc HVR |
916 | |
917 | MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); | |
918 | MODULE_DESCRIPTION("Intersil ISL1208 RTC driver"); | |
919 | MODULE_LICENSE("GPL"); |