1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * An I2C driver for the Intersil ISL 12026 |
4 | * |
5 | * Copyright (c) 2018 Cavium, Inc. |
6 | */ |
7 | #include <linux/bcd.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/i2c.h> |
10 | #include <linux/module.h> |
11 | #include <linux/mutex.h> |
12 | #include <linux/nvmem-provider.h> |
13 | #include <linux/of.h> |
14 | #include <linux/rtc.h> |
15 | #include <linux/slab.h> |
16 | |
17 | /* register offsets */ |
18 | #define ISL12026_REG_PWR 0x14 |
19 | # define ISL12026_REG_PWR_BSW BIT(6) |
20 | # define ISL12026_REG_PWR_SBIB BIT(7) |
21 | #define ISL12026_REG_SC 0x30 |
22 | #define ISL12026_REG_HR 0x32 |
23 | # define ISL12026_REG_HR_MIL BIT(7) /* military or 24 hour time */ |
24 | #define ISL12026_REG_SR 0x3f |
25 | # define ISL12026_REG_SR_RTCF BIT(0) |
26 | # define ISL12026_REG_SR_WEL BIT(1) |
27 | # define ISL12026_REG_SR_RWEL BIT(2) |
28 | # define ISL12026_REG_SR_MBZ BIT(3) |
29 | # define ISL12026_REG_SR_OSCF BIT(4) |
30 | |
31 | /* The EEPROM array responds at i2c address 0x57 */ |
32 | #define ISL12026_EEPROM_ADDR 0x57 |
33 | |
34 | #define ISL12026_PAGESIZE 16 |
35 | #define ISL12026_NVMEM_WRITE_TIME 20 |
36 | |
37 | struct isl12026 { |
38 | struct rtc_device *rtc; |
39 | struct i2c_client *nvm_client; |
40 | }; |
41 | |
42 | static int isl12026_read_reg(struct i2c_client *client, int reg) |
43 | { |
44 | u8 addr[] = {0, reg}; |
45 | u8 val; |
46 | int ret; |
47 | |
48 | struct i2c_msg msgs[] = { |
49 | { |
50 | .addr = client->addr, |
51 | .flags = 0, |
52 | .len = sizeof(addr), |
53 | .buf = addr |
54 | }, { |
55 | .addr = client->addr, |
56 | .flags = I2C_M_RD, |
57 | .len = 1, |
58 | .buf = &val |
59 | } |
60 | }; |
61 | |
62 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
63 | if (ret != ARRAY_SIZE(msgs)) { |
64 | dev_err(&client->dev, "read reg error, ret=%d\n" , ret); |
65 | ret = ret < 0 ? ret : -EIO; |
66 | } else { |
67 | ret = val; |
68 | } |
69 | |
70 | return ret; |
71 | } |
72 | |
73 | static int isl12026_arm_write(struct i2c_client *client) |
74 | { |
75 | int ret; |
76 | u8 op[3]; |
77 | struct i2c_msg msg = { |
78 | .addr = client->addr, |
79 | .flags = 0, |
80 | .len = 1, |
81 | .buf = op |
82 | }; |
83 | |
84 | /* Set SR.WEL */ |
85 | op[0] = 0; |
86 | op[1] = ISL12026_REG_SR; |
87 | op[2] = ISL12026_REG_SR_WEL; |
88 | msg.len = 3; |
89 | ret = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
90 | if (ret != 1) { |
91 | dev_err(&client->dev, "write error SR.WEL, ret=%d\n" , ret); |
92 | ret = ret < 0 ? ret : -EIO; |
93 | goto out; |
94 | } |
95 | |
96 | /* Set SR.WEL and SR.RWEL */ |
97 | op[2] = ISL12026_REG_SR_WEL | ISL12026_REG_SR_RWEL; |
98 | msg.len = 3; |
99 | ret = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
100 | if (ret != 1) { |
101 | dev_err(&client->dev, |
102 | "write error SR.WEL|SR.RWEL, ret=%d\n" , ret); |
103 | ret = ret < 0 ? ret : -EIO; |
104 | goto out; |
105 | } else { |
106 | ret = 0; |
107 | } |
108 | out: |
109 | return ret; |
110 | } |
111 | |
112 | static int isl12026_disarm_write(struct i2c_client *client) |
113 | { |
114 | int ret; |
115 | u8 op[3] = {0, ISL12026_REG_SR, 0}; |
116 | struct i2c_msg msg = { |
117 | .addr = client->addr, |
118 | .flags = 0, |
119 | .len = sizeof(op), |
120 | .buf = op |
121 | }; |
122 | |
123 | ret = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
124 | if (ret != 1) { |
125 | dev_err(&client->dev, |
126 | "write error SR, ret=%d\n" , ret); |
127 | ret = ret < 0 ? ret : -EIO; |
128 | } else { |
129 | ret = 0; |
130 | } |
131 | |
132 | return ret; |
133 | } |
134 | |
135 | static int isl12026_write_reg(struct i2c_client *client, int reg, u8 val) |
136 | { |
137 | int ret; |
138 | u8 op[3] = {0, reg, val}; |
139 | struct i2c_msg msg = { |
140 | .addr = client->addr, |
141 | .flags = 0, |
142 | .len = sizeof(op), |
143 | .buf = op |
144 | }; |
145 | |
146 | ret = isl12026_arm_write(client); |
147 | if (ret) |
148 | return ret; |
149 | |
150 | ret = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
151 | if (ret != 1) { |
152 | dev_err(&client->dev, "write error CCR, ret=%d\n" , ret); |
153 | ret = ret < 0 ? ret : -EIO; |
154 | goto out; |
155 | } |
156 | |
157 | msleep(ISL12026_NVMEM_WRITE_TIME); |
158 | |
159 | ret = isl12026_disarm_write(client); |
160 | out: |
161 | return ret; |
162 | } |
163 | |
164 | static int isl12026_rtc_set_time(struct device *dev, struct rtc_time *tm) |
165 | { |
166 | struct i2c_client *client = to_i2c_client(dev); |
167 | int ret; |
168 | u8 op[10]; |
169 | struct i2c_msg msg = { |
170 | .addr = client->addr, |
171 | .flags = 0, |
172 | .len = sizeof(op), |
173 | .buf = op |
174 | }; |
175 | |
176 | ret = isl12026_arm_write(client); |
177 | if (ret) |
178 | return ret; |
179 | |
180 | /* Set the CCR registers */ |
181 | op[0] = 0; |
182 | op[1] = ISL12026_REG_SC; |
183 | op[2] = bin2bcd(tm->tm_sec); /* SC */ |
184 | op[3] = bin2bcd(tm->tm_min); /* MN */ |
185 | op[4] = bin2bcd(tm->tm_hour) | ISL12026_REG_HR_MIL; /* HR */ |
186 | op[5] = bin2bcd(tm->tm_mday); /* DT */ |
187 | op[6] = bin2bcd(tm->tm_mon + 1); /* MO */ |
188 | op[7] = bin2bcd(tm->tm_year % 100); /* YR */ |
189 | op[8] = bin2bcd(tm->tm_wday & 7); /* DW */ |
190 | op[9] = bin2bcd(tm->tm_year >= 100 ? 20 : 19); /* Y2K */ |
191 | ret = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
192 | if (ret != 1) { |
193 | dev_err(&client->dev, "write error CCR, ret=%d\n" , ret); |
194 | ret = ret < 0 ? ret : -EIO; |
195 | goto out; |
196 | } |
197 | |
198 | ret = isl12026_disarm_write(client); |
199 | out: |
200 | return ret; |
201 | } |
202 | |
203 | static int isl12026_rtc_read_time(struct device *dev, struct rtc_time *tm) |
204 | { |
205 | struct i2c_client *client = to_i2c_client(dev); |
206 | u8 ccr[8]; |
207 | u8 addr[2]; |
208 | u8 sr; |
209 | int ret; |
210 | struct i2c_msg msgs[] = { |
211 | { |
212 | .addr = client->addr, |
213 | .flags = 0, |
214 | .len = sizeof(addr), |
215 | .buf = addr |
216 | }, { |
217 | .addr = client->addr, |
218 | .flags = I2C_M_RD, |
219 | } |
220 | }; |
221 | |
222 | /* First, read SR */ |
223 | addr[0] = 0; |
224 | addr[1] = ISL12026_REG_SR; |
225 | msgs[1].len = 1; |
226 | msgs[1].buf = &sr; |
227 | |
228 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
229 | if (ret != ARRAY_SIZE(msgs)) { |
230 | dev_err(&client->dev, "read error, ret=%d\n" , ret); |
231 | ret = ret < 0 ? ret : -EIO; |
232 | goto out; |
233 | } |
234 | |
235 | if (sr & ISL12026_REG_SR_RTCF) |
236 | dev_warn(&client->dev, "Real-Time Clock Failure on read\n" ); |
237 | if (sr & ISL12026_REG_SR_OSCF) |
238 | dev_warn(&client->dev, "Oscillator Failure on read\n" ); |
239 | |
240 | /* Second, CCR regs */ |
241 | addr[0] = 0; |
242 | addr[1] = ISL12026_REG_SC; |
243 | msgs[1].len = sizeof(ccr); |
244 | msgs[1].buf = ccr; |
245 | |
246 | ret = i2c_transfer(adap: client->adapter, msgs, ARRAY_SIZE(msgs)); |
247 | if (ret != ARRAY_SIZE(msgs)) { |
248 | dev_err(&client->dev, "read error, ret=%d\n" , ret); |
249 | ret = ret < 0 ? ret : -EIO; |
250 | goto out; |
251 | } |
252 | |
253 | tm->tm_sec = bcd2bin(ccr[0] & 0x7F); |
254 | tm->tm_min = bcd2bin(ccr[1] & 0x7F); |
255 | if (ccr[2] & ISL12026_REG_HR_MIL) |
256 | tm->tm_hour = bcd2bin(ccr[2] & 0x3F); |
257 | else |
258 | tm->tm_hour = bcd2bin(ccr[2] & 0x1F) + |
259 | ((ccr[2] & 0x20) ? 12 : 0); |
260 | tm->tm_mday = bcd2bin(ccr[3] & 0x3F); |
261 | tm->tm_mon = bcd2bin(ccr[4] & 0x1F) - 1; |
262 | tm->tm_year = bcd2bin(ccr[5]); |
263 | if (bcd2bin(ccr[7]) == 20) |
264 | tm->tm_year += 100; |
265 | tm->tm_wday = ccr[6] & 0x07; |
266 | |
267 | ret = 0; |
268 | out: |
269 | return ret; |
270 | } |
271 | |
272 | static const struct rtc_class_ops isl12026_rtc_ops = { |
273 | .read_time = isl12026_rtc_read_time, |
274 | .set_time = isl12026_rtc_set_time, |
275 | }; |
276 | |
277 | static int isl12026_nvm_read(void *p, unsigned int offset, |
278 | void *val, size_t bytes) |
279 | { |
280 | struct isl12026 *priv = p; |
281 | int ret; |
282 | u8 addr[2]; |
283 | struct i2c_msg msgs[] = { |
284 | { |
285 | .addr = priv->nvm_client->addr, |
286 | .flags = 0, |
287 | .len = sizeof(addr), |
288 | .buf = addr |
289 | }, { |
290 | .addr = priv->nvm_client->addr, |
291 | .flags = I2C_M_RD, |
292 | .buf = val |
293 | } |
294 | }; |
295 | |
296 | /* |
297 | * offset and bytes checked and limited by nvmem core, so |
298 | * proceed without further checks. |
299 | */ |
300 | ret = mutex_lock_interruptible(&priv->rtc->ops_lock); |
301 | if (ret) |
302 | return ret; |
303 | |
304 | /* 2 bytes of address, most significant first */ |
305 | addr[0] = offset >> 8; |
306 | addr[1] = offset; |
307 | msgs[1].len = bytes; |
308 | ret = i2c_transfer(adap: priv->nvm_client->adapter, msgs, ARRAY_SIZE(msgs)); |
309 | |
310 | mutex_unlock(lock: &priv->rtc->ops_lock); |
311 | |
312 | if (ret != ARRAY_SIZE(msgs)) { |
313 | dev_err(&priv->nvm_client->dev, |
314 | "nvmem read error, ret=%d\n" , ret); |
315 | return ret < 0 ? ret : -EIO; |
316 | } |
317 | |
318 | return 0; |
319 | } |
320 | |
321 | static int isl12026_nvm_write(void *p, unsigned int offset, |
322 | void *val, size_t bytes) |
323 | { |
324 | struct isl12026 *priv = p; |
325 | int ret; |
326 | u8 *v = val; |
327 | size_t chunk_size, num_written; |
328 | u8 payload[ISL12026_PAGESIZE + 2]; /* page + 2 address bytes */ |
329 | struct i2c_msg msgs[] = { |
330 | { |
331 | .addr = priv->nvm_client->addr, |
332 | .flags = 0, |
333 | .buf = payload |
334 | } |
335 | }; |
336 | |
337 | /* |
338 | * offset and bytes checked and limited by nvmem core, so |
339 | * proceed without further checks. |
340 | */ |
341 | ret = mutex_lock_interruptible(&priv->rtc->ops_lock); |
342 | if (ret) |
343 | return ret; |
344 | |
345 | num_written = 0; |
346 | while (bytes) { |
347 | chunk_size = round_down(offset, ISL12026_PAGESIZE) + |
348 | ISL12026_PAGESIZE - offset; |
349 | chunk_size = min(bytes, chunk_size); |
350 | /* |
351 | * 2 bytes of address, most significant first, followed |
352 | * by page data bytes |
353 | */ |
354 | memcpy(payload + 2, v + num_written, chunk_size); |
355 | payload[0] = offset >> 8; |
356 | payload[1] = offset; |
357 | msgs[0].len = chunk_size + 2; |
358 | ret = i2c_transfer(adap: priv->nvm_client->adapter, |
359 | msgs, ARRAY_SIZE(msgs)); |
360 | if (ret != ARRAY_SIZE(msgs)) { |
361 | dev_err(&priv->nvm_client->dev, |
362 | "nvmem write error, ret=%d\n" , ret); |
363 | ret = ret < 0 ? ret : -EIO; |
364 | break; |
365 | } |
366 | ret = 0; |
367 | bytes -= chunk_size; |
368 | offset += chunk_size; |
369 | num_written += chunk_size; |
370 | msleep(ISL12026_NVMEM_WRITE_TIME); |
371 | } |
372 | |
373 | mutex_unlock(lock: &priv->rtc->ops_lock); |
374 | |
375 | return ret; |
376 | } |
377 | |
378 | static void isl12026_force_power_modes(struct i2c_client *client) |
379 | { |
380 | int ret; |
381 | int pwr, requested_pwr; |
382 | u32 bsw_val, sbib_val; |
383 | bool set_bsw, set_sbib; |
384 | |
385 | /* |
386 | * If we can read the of_property, set the specified value. |
387 | * If there is an error reading the of_property (likely |
388 | * because it does not exist), keep the current value. |
389 | */ |
390 | ret = of_property_read_u32(np: client->dev.of_node, |
391 | propname: "isil,pwr-bsw" , out_value: &bsw_val); |
392 | set_bsw = (ret == 0); |
393 | |
394 | ret = of_property_read_u32(np: client->dev.of_node, |
395 | propname: "isil,pwr-sbib" , out_value: &sbib_val); |
396 | set_sbib = (ret == 0); |
397 | |
398 | /* Check if PWR.BSW and/or PWR.SBIB need specified values */ |
399 | if (!set_bsw && !set_sbib) |
400 | return; |
401 | |
402 | pwr = isl12026_read_reg(client, ISL12026_REG_PWR); |
403 | if (pwr < 0) { |
404 | dev_warn(&client->dev, "Error: Failed to read PWR %d\n" , pwr); |
405 | return; |
406 | } |
407 | |
408 | requested_pwr = pwr; |
409 | |
410 | if (set_bsw) { |
411 | if (bsw_val) |
412 | requested_pwr |= ISL12026_REG_PWR_BSW; |
413 | else |
414 | requested_pwr &= ~ISL12026_REG_PWR_BSW; |
415 | } /* else keep current BSW */ |
416 | |
417 | if (set_sbib) { |
418 | if (sbib_val) |
419 | requested_pwr |= ISL12026_REG_PWR_SBIB; |
420 | else |
421 | requested_pwr &= ~ISL12026_REG_PWR_SBIB; |
422 | } /* else keep current SBIB */ |
423 | |
424 | if (pwr >= 0 && pwr != requested_pwr) { |
425 | dev_dbg(&client->dev, "PWR: %02x\n" , pwr); |
426 | dev_dbg(&client->dev, "Updating PWR to: %02x\n" , requested_pwr); |
427 | isl12026_write_reg(client, ISL12026_REG_PWR, val: requested_pwr); |
428 | } |
429 | } |
430 | |
431 | static int isl12026_probe(struct i2c_client *client) |
432 | { |
433 | struct isl12026 *priv; |
434 | int ret; |
435 | struct nvmem_config nvm_cfg = { |
436 | .name = "isl12026-" , |
437 | .base_dev = &client->dev, |
438 | .stride = 1, |
439 | .word_size = 1, |
440 | .size = 512, |
441 | .reg_read = isl12026_nvm_read, |
442 | .reg_write = isl12026_nvm_write, |
443 | }; |
444 | |
445 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) |
446 | return -ENODEV; |
447 | |
448 | priv = devm_kzalloc(dev: &client->dev, size: sizeof(*priv), GFP_KERNEL); |
449 | if (!priv) |
450 | return -ENOMEM; |
451 | |
452 | i2c_set_clientdata(client, data: priv); |
453 | |
454 | isl12026_force_power_modes(client); |
455 | |
456 | priv->nvm_client = i2c_new_dummy_device(adapter: client->adapter, ISL12026_EEPROM_ADDR); |
457 | if (IS_ERR(ptr: priv->nvm_client)) |
458 | return PTR_ERR(ptr: priv->nvm_client); |
459 | |
460 | priv->rtc = devm_rtc_allocate_device(dev: &client->dev); |
461 | ret = PTR_ERR_OR_ZERO(ptr: priv->rtc); |
462 | if (ret) |
463 | return ret; |
464 | |
465 | priv->rtc->ops = &isl12026_rtc_ops; |
466 | nvm_cfg.priv = priv; |
467 | ret = devm_rtc_nvmem_register(rtc: priv->rtc, nvmem_config: &nvm_cfg); |
468 | if (ret) |
469 | return ret; |
470 | |
471 | return devm_rtc_register_device(priv->rtc); |
472 | } |
473 | |
474 | static void isl12026_remove(struct i2c_client *client) |
475 | { |
476 | struct isl12026 *priv = i2c_get_clientdata(client); |
477 | |
478 | i2c_unregister_device(client: priv->nvm_client); |
479 | } |
480 | |
481 | static const struct of_device_id isl12026_dt_match[] = { |
482 | { .compatible = "isil,isl12026" }, |
483 | { } |
484 | }; |
485 | MODULE_DEVICE_TABLE(of, isl12026_dt_match); |
486 | |
487 | static struct i2c_driver isl12026_driver = { |
488 | .driver = { |
489 | .name = "rtc-isl12026" , |
490 | .of_match_table = isl12026_dt_match, |
491 | }, |
492 | .probe = isl12026_probe, |
493 | .remove = isl12026_remove, |
494 | }; |
495 | |
496 | module_i2c_driver(isl12026_driver); |
497 | |
498 | MODULE_DESCRIPTION("ISL 12026 RTC driver" ); |
499 | MODULE_LICENSE("GPL" ); |
500 | |