1 /* Copyright (c) 2024 Daniel Kampert
2 * Author: Daniel Kampert <DanielKampert@kampis-Elektroecke.de>
3 */
4
5 #include <zephyr/device.h>
6 #include <zephyr/devicetree.h>
7 #include <zephyr/drivers/i2c.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/rtc.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys/byteorder.h>
12 #include "rtc_utils.h"
13
14 #define RV8263C8_REGISTER_CONTROL_1 0x00
15 #define RV8263C8_REGISTER_CONTROL_2 0x01
16 #define RV8263C8_REGISTER_OFFSET 0x02
17 #define RV8263C8_REGISTER_RAM 0x03
18 #define RV8263C8_REGISTER_SECONDS 0x04
19 #define RV8263C8_REGISTER_MINUTES 0x05
20 #define RV8263C8_REGISTER_HOURS 0x06
21 #define RV8263C8_REGISTER_DATE 0x07
22 #define RV8263C8_REGISTER_WEEKDAY 0x08
23 #define RV8263C8_REGISTER_MONTH 0x09
24 #define RV8263C8_REGISTER_YEAR 0x0A
25 #define RV8263C8_REGISTER_SECONDS_ALARM 0x0B
26 #define RV8263C8_REGISTER_MINUTES_ALARM 0x0C
27 #define RV8263C8_REGISTER_HOURS_ALARM 0x0D
28 #define RV8263C8_REGISTER_DATE_ALARM 0x0E
29 #define RV8263C8_REGISTER_WEEKDAY_ALARM 0x0F
30 #define RV8263C8_REGISTER_TIMER_VALUE 0x10
31 #define RV8263C8_REGISTER_TIMER_MODE 0x11
32
33 #define RV8263_BM_FAST_MODE (0x01 << 7)
34 #define RV8263_BM_NORMAL_MODE (0x00 << 7)
35 #define RV8263C8_BM_24H_MODE_ENABLE (0x00 << 1)
36 #define RV8263C8_BM_24H_MODE_DISABLE (0x00 << 1)
37 #define RV8263C8_BM_CLOCK_ENABLE (0x00 << 5)
38 #define RV8263C8_BM_CLOCK_DISABLE (0x01 << 5)
39 #define RV8263C8_BM_ALARM_INT_ENABLE (0x01 << 7)
40 #define RV8263C8_BM_ALARM_INT_DISABLE (0x00 << 7)
41 #define RV8263C8_BM_MINUTE_INT_ENABLE (0x01 << 5)
42 #define RV8263C8_BM_MINUTE_INT_DISABLE (0x00 << 5)
43 #define RV8263C8_BM_HALF_MINUTE_INT_ENABLE (0x01 << 4)
44 #define RV8263C8_BM_HALF_MINUTE_INT_DISABLE (0x00 << 4)
45 #define RV8263C8_BM_ALARM_ENABLE (0x00 << 7)
46 #define RV8263C8_BM_ALARM_DISABLE (0x01 << 7)
47 #define RV8263C8_BM_AF (0x01 << 6)
48 #define RV8263C8_BM_TF (0x01 << 3)
49 #define RV8263_BM_MODE (0x01 << 7)
50 #define RV8263_BM_TD_1HZ (0x02 << 3)
51 #define RV8263_BM_TE_ENABLE (0x01 << 2)
52 #define RV8263_BM_TIE_ENABLE (0x01 << 1)
53 #define RV8263_BM_TI_TP_PULSE (0x01 << 0)
54 #define RV8263_BM_OS (0x01 << 7)
55 #define RV8263C8_BM_SOFTWARE_RESET (0x58)
56 #define RV8263C8_BM_REGISTER_OFFSET 0x7F
57 #define RV8263_YEAR_OFFSET (2000 - 1900)
58
59 #define SECONDS_BITS GENMASK(6, 0)
60 #define MINUTES_BITS GENMASK(7, 0)
61 #define HOURS_BITS GENMASK(5, 0)
62 #define DATE_BITS GENMASK(5, 0)
63 #define MONTHS_BITS GENMASK(4, 0)
64 #define WEEKDAY_BITS GENMASK(2, 0)
65 #define YEAR_BITS GENMASK(7, 0)
66 #define VALIDATE_24HR BIT(6)
67
68 #define MIN_SEC 0
69 #define MAX_SEC 59
70 #define MIN_MIN 0
71 #define MAX_MIN 59
72 #define MIN_HOUR 0
73 #define MAX_HOUR 23
74 #define MAX_WDAY 7
75 #define MIN_WDAY 1
76 #define MAX_MDAY 31
77 #define MIN_MDAY 1
78 #define MAX_MON 12
79 #define MIN_MON 1
80 #define MIN_YEAR_DIFF 0
81 #define MAX_YEAR_DIFF 99
82
83 #define DT_DRV_COMPAT microcrystal_rv_8263_c8
84
85 LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_RTC_LOG_LEVEL);
86
87 struct rv8263c8_config {
88 struct i2c_dt_spec i2c_bus;
89 uint32_t clkout;
90
91 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
92 struct gpio_dt_spec int_gpio;
93 #endif
94 };
95
96 struct rv8263c8_data {
97 struct k_sem lock;
98
99 #if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
100 const struct device *dev;
101 struct gpio_callback gpio_cb;
102 #endif
103
104 #if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
105 rtc_alarm_callback alarm_cb;
106 void *alarm_cb_data;
107 struct k_work alarm_work;
108 #endif
109
110 #if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
111 rtc_update_callback update_cb;
112 void *update_cb_data;
113 struct k_work update_work;
114 #endif
115 };
116
rv8263c8_update_disable_timer(const struct device * dev)117 static int rv8263c8_update_disable_timer(const struct device *dev)
118 {
119 int err;
120 uint8_t buf[2];
121 const struct rv8263c8_config *config = dev->config;
122
123 /* Value 0 disables the timer. */
124 buf[0] = RV8263C8_REGISTER_TIMER_VALUE;
125 buf[1] = 0;
126 err = i2c_write_dt(&config->i2c_bus, buf, 2);
127 if (err < 0) {
128 return err;
129 }
130
131 buf[0] = RV8263C8_REGISTER_TIMER_MODE;
132 return i2c_write_dt(&config->i2c_bus, buf, 2);
133 }
134
135 #if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
rv8263c8_gpio_callback_handler(const struct device * p_port,struct gpio_callback * p_cb,gpio_port_pins_t pins)136 static void rv8263c8_gpio_callback_handler(const struct device *p_port, struct gpio_callback *p_cb,
137 gpio_port_pins_t pins)
138 {
139 ARG_UNUSED(pins);
140 ARG_UNUSED(p_port);
141
142 struct rv8263c8_data *data = CONTAINER_OF(p_cb, struct rv8263c8_data, gpio_cb);
143
144 #if CONFIG_RTC_ALARM
145 k_work_submit(&data->alarm_work);
146 #endif
147
148 #if CONFIG_RTC_UPDATE
149 k_work_submit(&data->update_work);
150 #endif
151 }
152 #endif
153
154 #if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
rv8263c8_alarm_worker(struct k_work * p_work)155 static void rv8263c8_alarm_worker(struct k_work *p_work)
156 {
157 struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, alarm_work);
158 const struct rv8263c8_config *config = data->dev->config;
159
160 LOG_DBG("Process alarm worker from interrupt");
161
162 if (data->alarm_cb != NULL) {
163 uint8_t reg;
164
165 i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®);
166
167 if (reg & RV8263C8_BM_AF) {
168 reg &= ~RV8263C8_BM_AF;
169
170 LOG_DBG("Calling alarm callback");
171 data->alarm_cb(data->dev, 0, data->alarm_cb_data);
172
173 i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, reg);
174 }
175 }
176 }
177 #endif
178
179 #if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
rv8263c8_update_enable_timer(const struct device * dev)180 static int rv8263c8_update_enable_timer(const struct device *dev)
181 {
182 int err;
183 const struct rv8263c8_config *config = dev->config;
184 uint8_t buf[2];
185
186 /* Set the timer preload value for 1 second. */
187 buf[0] = RV8263C8_REGISTER_TIMER_VALUE;
188 buf[1] = 1;
189 err = i2c_write_dt(&config->i2c_bus, buf, 2);
190 if (err < 0) {
191 return err;
192 }
193
194 buf[0] = RV8263C8_REGISTER_TIMER_MODE;
195 buf[1] = RV8263_BM_TD_1HZ | RV8263_BM_TE_ENABLE | RV8263_BM_TIE_ENABLE |
196 RV8263_BM_TI_TP_PULSE;
197 return i2c_write_dt(&config->i2c_bus, buf, 2);
198 }
199
rv8263c8_update_worker(struct k_work * p_work)200 static void rv8263c8_update_worker(struct k_work *p_work)
201 {
202 uint8_t reg;
203 struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, update_work);
204 const struct rv8263c8_config *config = data->dev->config;
205
206 LOG_DBG("Process update worker from interrupt");
207
208 if (data->update_cb != NULL) {
209 i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®);
210
211 if (reg & RV8263C8_BM_TF) {
212 LOG_DBG("Calling update callback");
213 data->update_cb(data->dev, data->update_cb_data);
214 }
215 }
216
217 rv8263c8_update_enable_timer(data->dev);
218 i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_TF,
219 RV8263C8_BM_TF);
220 }
221 #endif
222
rv8263c8_time_set(const struct device * dev,const struct rtc_time * timeptr)223 static int rv8263c8_time_set(const struct device *dev, const struct rtc_time *timeptr)
224 {
225 uint8_t regs[8];
226 const struct rv8263c8_config *config = dev->config;
227
228 if (timeptr == NULL || (timeptr->tm_year < RV8263_YEAR_OFFSET)) {
229 LOG_ERR("invalid time");
230 return -EINVAL;
231 }
232
233 LOG_DBG("Set time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = "
234 "%u",
235 timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
236 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
237
238 regs[0] = RV8263C8_REGISTER_SECONDS;
239 regs[1] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS;
240 regs[2] = bin2bcd(timeptr->tm_min) & MINUTES_BITS;
241 regs[3] = bin2bcd(timeptr->tm_hour) & HOURS_BITS;
242 regs[4] = bin2bcd(timeptr->tm_mday) & DATE_BITS;
243 regs[5] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS;
244 regs[6] = bin2bcd(timeptr->tm_mon) & MONTHS_BITS;
245 regs[7] = bin2bcd(timeptr->tm_year - RV8263_YEAR_OFFSET) & YEAR_BITS;
246
247 return i2c_write_dt(&config->i2c_bus, regs, 8);
248 }
249
rv8263c8_time_get(const struct device * dev,struct rtc_time * timeptr)250 static int rv8263c8_time_get(const struct device *dev, struct rtc_time *timeptr)
251 {
252 int err;
253 uint8_t regs[7];
254 const struct rv8263c8_config *config = dev->config;
255
256 if (timeptr == NULL) {
257 return -EINVAL;
258 }
259
260 err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs));
261 if (err < 0) {
262 return err;
263 }
264
265 /* Return an error when the oscillator is stopped. */
266 if (regs[0] & RV8263_BM_OS) {
267 return -ENODATA;
268 }
269
270 timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS);
271 timeptr->tm_min = bcd2bin(regs[1] & MINUTES_BITS);
272 timeptr->tm_hour = bcd2bin(regs[2] & HOURS_BITS);
273 timeptr->tm_mday = bcd2bin(regs[3] & DATE_BITS);
274 timeptr->tm_wday = bcd2bin(regs[4] & WEEKDAY_BITS);
275 timeptr->tm_mon = bcd2bin(regs[5] & MONTHS_BITS);
276 timeptr->tm_year = bcd2bin(regs[6] & YEAR_BITS) + RV8263_YEAR_OFFSET;
277
278 /* Unused. */
279 timeptr->tm_nsec = 0;
280 timeptr->tm_isdst = -1;
281 timeptr->tm_yday = -1;
282
283 /* Validate the chip in 24hr mode. */
284 if (regs[2] & VALIDATE_24HR) {
285 return -ENODATA;
286 }
287
288 LOG_DBG("Get time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = "
289 "%u",
290 timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
291 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
292
293 return 0;
294 }
295
rv8263c8_init(const struct device * dev)296 static int rv8263c8_init(const struct device *dev)
297 {
298 int err;
299 int temp;
300 struct rv8263c8_data *data = dev->data;
301 const struct rv8263c8_config *config = dev->config;
302
303 #if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
304 if (config->int_gpio.port == NULL) {
305 return -EINVAL;
306 }
307 #endif
308
309 if (!i2c_is_ready_dt(&config->i2c_bus)) {
310 LOG_ERR("I2C bus not ready!");
311 return -ENODEV;
312 }
313
314 k_sem_init(&data->lock, 1, 1);
315
316 err = rv8263c8_update_disable_timer(dev);
317 if (err < 0) {
318 LOG_ERR("Error while disabling the timer! Error: %i", err);
319 return err;
320 }
321
322 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_1,
323 RV8263C8_BM_24H_MODE_DISABLE | RV8263C8_BM_CLOCK_ENABLE);
324 if (err < 0) {
325 LOG_ERR("Error while writing CONTROL_1! Error: %i", err);
326 return err;
327 }
328
329 temp = config->clkout;
330 LOG_DBG("Configure ClkOut: %u", temp);
331
332 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2,
333 RV8263C8_BM_AF | temp);
334 if (err < 0) {
335 LOG_ERR("Error while writing CONTROL_2! Error: %i", err);
336 return err;
337 }
338
339 LOG_DBG("Configure ClkOut: %u", temp);
340
341 #if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
342 uint8_t buf[2];
343
344 buf[0] = RV8263C8_REGISTER_TIMER_MODE;
345 buf[1] = 0;
346 err = i2c_write_dt(&config->i2c_bus, buf, 2);
347 if (err < 0) {
348 LOG_ERR("Error while writing CONTROL2! Error: %i", err);
349 return err;
350 }
351 #endif
352
353 #if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
354 if (!gpio_is_ready_dt(&config->int_gpio)) {
355 LOG_ERR("GPIO not ready!");
356 return err;
357 }
358
359 err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
360 if (err < 0) {
361 LOG_ERR("Failed to configure GPIO! Error: %u", err);
362 return err;
363 }
364
365 err = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_FALLING);
366 if (err < 0) {
367 LOG_ERR("Failed to configure interrupt! Error: %u", err);
368 return err;
369 }
370
371 gpio_init_callback(&data->gpio_cb, rv8263c8_gpio_callback_handler,
372 BIT(config->int_gpio.pin));
373
374 err = gpio_add_callback_dt(&config->int_gpio, &data->gpio_cb);
375 if (err < 0) {
376 LOG_ERR("Failed to add GPIO callback! Error: %u", err);
377 return err;
378 }
379 #endif
380
381 (void)k_sem_take(&data->lock, K_FOREVER);
382 #if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
383 data->alarm_work.handler = rv8263c8_alarm_worker;
384 #endif
385
386 #if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
387 data->update_work.handler = rv8263c8_update_worker;
388 #endif
389
390 #if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
391 data->dev = dev;
392 #endif
393 k_sem_give(&data->lock);
394
395 return 0;
396 }
397
398 #if CONFIG_RTC_ALARM
rv8263c8_alarm_get_supported_fields(const struct device * dev,uint16_t id,uint16_t * p_mask)399 static int rv8263c8_alarm_get_supported_fields(const struct device *dev, uint16_t id,
400 uint16_t *p_mask)
401 {
402 ARG_UNUSED(dev);
403 ARG_UNUSED(id);
404
405 (*p_mask) = (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE |
406 RTC_ALARM_TIME_MASK_HOUR | RTC_ALARM_TIME_MASK_MONTHDAY |
407 RTC_ALARM_TIME_MASK_WEEKDAY);
408
409 return 0;
410 }
411
rv8263c8_alarm_set_time(const struct device * dev,uint16_t id,uint16_t mask,const struct rtc_time * timeptr)412 static int rv8263c8_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
413 const struct rtc_time *timeptr)
414 {
415 int err;
416 uint8_t regs[5];
417 const struct rv8263c8_config *config = dev->config;
418
419 ARG_UNUSED(id);
420
421 if ((mask > 0) && (timeptr == NULL)) {
422 return -EINVAL;
423 }
424
425 /* Disable the alarm when mask is zero. */
426 if (mask == 0) {
427 return i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2,
428 RV8263C8_BM_ALARM_INT_ENABLE,
429 RV8263C8_BM_ALARM_INT_DISABLE);
430 }
431
432 if (!rtc_utils_validate_rtc_time(timeptr, mask)) {
433 LOG_ERR("Invalid mask!");
434 return -EINVAL;
435 }
436
437 regs[0] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS;
438 regs[1] = bin2bcd(timeptr->tm_min) & MINUTES_BITS;
439 regs[2] = bin2bcd(timeptr->tm_hour) & HOURS_BITS;
440 regs[3] = bin2bcd(timeptr->tm_mday) & DATE_BITS;
441 regs[4] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS;
442
443 if (mask & RTC_ALARM_TIME_MASK_SECOND) {
444 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM,
445 RV8263C8_BM_ALARM_ENABLE | regs[0]);
446 } else {
447 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM,
448 RV8263C8_BM_ALARM_DISABLE);
449 }
450
451 if (err < 0) {
452 LOG_ERR("Error while writing SECONDS alarm! Error: %i", err);
453 return err;
454 }
455
456 if (mask & RTC_ALARM_TIME_MASK_MINUTE) {
457 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM,
458 RV8263C8_BM_ALARM_ENABLE | regs[1]);
459 } else {
460 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM,
461 RV8263C8_BM_ALARM_DISABLE);
462 }
463
464 if (err < 0) {
465 LOG_ERR("Error while writing MINUTE alarm! Error: %i", err);
466 return err;
467 }
468
469 if (mask & RTC_ALARM_TIME_MASK_HOUR) {
470 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM,
471 RV8263C8_BM_ALARM_ENABLE | regs[2]);
472 } else {
473 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM,
474 RV8263C8_BM_ALARM_DISABLE);
475 }
476
477 if (err < 0) {
478 LOG_ERR("Error while writing HOUR alarm! Error: %i", err);
479 return err;
480 }
481
482 if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
483 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM,
484 RV8263C8_BM_ALARM_ENABLE | regs[3]);
485 } else {
486 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM,
487 RV8263C8_BM_ALARM_DISABLE);
488 }
489
490 if (err < 0) {
491 LOG_ERR("Error while writing MONTHDAY alarm! Error: %i", err);
492 return err;
493 }
494
495 if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
496 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM,
497 RV8263C8_BM_ALARM_ENABLE | regs[4]);
498 } else {
499 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM,
500 RV8263C8_BM_ALARM_DISABLE);
501 }
502
503 if (err < 0) {
504 LOG_ERR("Error while writing WEEKDAY alarm! Error: %i", err);
505 }
506
507 return err;
508 }
509
rv8263c8_alarm_get_time(const struct device * dev,uint16_t id,uint16_t * p_mask,struct rtc_time * timeptr)510 static int rv8263c8_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *p_mask,
511 struct rtc_time *timeptr)
512 {
513 int err;
514 const struct rv8263c8_config *config = dev->config;
515 uint8_t value[5];
516
517 ARG_UNUSED(id);
518
519 if (timeptr == NULL) {
520 return -EINVAL;
521 }
522
523 (*p_mask) = 0;
524
525 err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, value,
526 sizeof(value));
527 if (err < 0) {
528 LOG_ERR("Error while reading alarm! Error: %i", err);
529 return err;
530 }
531
532 if (value[0] <= MAX_SEC) {
533 timeptr->tm_sec = bcd2bin(value[0]) & SECONDS_BITS;
534 (*p_mask) |= RTC_ALARM_TIME_MASK_SECOND;
535 }
536
537 if (value[1] <= MAX_MIN) {
538 timeptr->tm_min = bcd2bin(value[1]) & MINUTES_BITS;
539 (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE;
540 }
541
542 if (value[2] <= MAX_HOUR) {
543 timeptr->tm_hour = bcd2bin(value[2]) & HOURS_BITS;
544 (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR;
545 }
546
547 if (value[3] <= MAX_MDAY) {
548 timeptr->tm_mday = bcd2bin(value[3]) & DATE_BITS;
549 (*p_mask) |= RTC_ALARM_TIME_MASK_MONTHDAY;
550 }
551
552 if (value[4] <= MAX_WDAY) {
553 timeptr->tm_wday = bcd2bin(value[4]) & WEEKDAY_BITS;
554 (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY;
555 }
556
557 return 0;
558 }
559
rv8263c8_alarm_set_callback(const struct device * dev,uint16_t id,rtc_alarm_callback callback,void * user_data)560 static int rv8263c8_alarm_set_callback(const struct device *dev, uint16_t id,
561 rtc_alarm_callback callback, void *user_data)
562 {
563 int err;
564 const struct rv8263c8_config *config = dev->config;
565
566 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
567 struct rv8263c8_data *data = dev->data;
568 #endif
569
570 ARG_UNUSED(id);
571
572 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
573 if (config->int_gpio.port == NULL) {
574 return -ENOTSUP;
575 }
576
577 (void)k_sem_take(&data->lock, K_FOREVER);
578 data->alarm_cb = callback;
579 data->alarm_cb_data = user_data;
580 k_sem_give(&data->lock);
581 #else
582 return -ENOTSUP;
583 #endif
584
585 if ((callback == NULL) && (user_data == NULL)) {
586 LOG_DBG("Disable alarm function");
587
588 err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2,
589 RV8263C8_BM_ALARM_INT_ENABLE,
590 RV8263C8_BM_ALARM_INT_DISABLE);
591 } else {
592 LOG_DBG("Enable alarm function");
593
594 err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2,
595 RV8263C8_BM_ALARM_INT_ENABLE | RV8263C8_BM_AF,
596 RV8263C8_BM_ALARM_INT_ENABLE);
597 }
598
599 if (err < 0) {
600 LOG_ERR("Error while writing CONTROL2! Error: %i", err);
601 }
602
603 return err;
604 }
605
rv8263c8_alarm_is_pending(const struct device * dev,uint16_t id)606 static int rv8263c8_alarm_is_pending(const struct device *dev, uint16_t id)
607 {
608 int err;
609 uint8_t reg;
610 const struct rv8263c8_config *config = dev->config;
611
612 ARG_UNUSED(id);
613
614 err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®);
615 if (err) {
616 return err;
617 }
618
619 if (reg & RV8263C8_BM_AF) {
620 reg &= ~RV8263C8_BM_AF;
621 err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, reg);
622 if (err) {
623 return err;
624 }
625
626 return 1;
627 }
628
629 return 0;
630 }
631 #endif
632
633 #if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
rv8263_update_callback(const struct device * dev,rtc_update_callback callback,void * user_data)634 int rv8263_update_callback(const struct device *dev, rtc_update_callback callback, void *user_data)
635 {
636 struct rv8263c8_data *const data = dev->data;
637
638 (void)k_sem_take(&data->lock, K_FOREVER);
639 data->update_cb = callback;
640 data->update_cb_data = user_data;
641 k_sem_give(&data->lock);
642
643 /* Disable the update callback. */
644 if ((callback == NULL) && (user_data == NULL)) {
645 return rv8263c8_update_disable_timer(dev);
646 }
647
648 return rv8263c8_update_enable_timer(dev);
649 }
650 #endif
651
652 #ifdef CONFIG_RTC_CALIBRATION
rv8263c8_calibration_set(const struct device * dev,int32_t calibration)653 int rv8263c8_calibration_set(const struct device *dev, int32_t calibration)
654 {
655 int8_t offset;
656 int32_t test_mode0;
657 int32_t test_mode1;
658 int32_t offset_ppm_mode0;
659 int32_t offset_ppm_mode1;
660 const struct rv8263c8_config *config = dev->config;
661
662 /* NOTE: The RTC API is using a PPB (Parts Per Billion) value. The RTC is using PPM.
663 * Here we calculate the offset when using MODE = 0.
664 * Formula from the application manual:
665 * Offset [ppm] = (calibration [ppb] / (4.34 [ppm] x 1000))
666 */
667 offset_ppm_mode0 = calibration / 4340;
668
669 /* Here we calculate the offset when using MODE = 1.
670 * Formula from the application manual:
671 * Offset [ppm] = (calibration [ppb] / (4.069 [ppm] x 1000))
672 */
673 offset_ppm_mode1 = calibration / 4069;
674
675 LOG_DBG("Offset Mode = 0: %i", offset_ppm_mode0);
676 LOG_DBG("Offset Mode = 1: %i", offset_ppm_mode1);
677
678 test_mode0 = offset_ppm_mode0 * 4340;
679 test_mode0 = calibration - test_mode0;
680 test_mode1 = offset_ppm_mode1 * 4069;
681 test_mode1 = calibration - test_mode1;
682
683 /* Compare the values and select the value with the smallest error. */
684 test_mode0 = test_mode0 < 0 ? -test_mode0 : test_mode0;
685 test_mode1 = test_mode1 < 0 ? -test_mode1 : test_mode1;
686 if (test_mode0 > test_mode1) {
687 LOG_DBG("Use fast mode (Mode = 1)");
688
689 /* Error with MODE = 1 is smaller -> Use MODE = 1. */
690 offset = RV8263_BM_FAST_MODE | (offset_ppm_mode1 & GENMASK(7, 0));
691 } else {
692 LOG_DBG("Use normal mode (Mode = 0)");
693
694 /* Error with MODE = 0 is smaller -> Use MODE = 0. */
695 offset = RV8263_BM_NORMAL_MODE | (offset_ppm_mode0 & GENMASK(7, 0));
696 }
697
698 LOG_DBG("Set offset value: %i", (offset & RV8263C8_BM_REGISTER_OFFSET));
699
700 return i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, offset);
701 }
702
rv8263c8_calibration_get(const struct device * dev,int32_t * calibration)703 int rv8263c8_calibration_get(const struct device *dev, int32_t *calibration)
704 {
705 int err;
706 int32_t temp;
707 int8_t offset;
708 const struct rv8263c8_config *config = dev->config;
709
710 if (calibration == NULL) {
711 return -EINVAL;
712 }
713
714 err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, &offset);
715 if (err) {
716 return err;
717 }
718
719 /* Convert the signed 7 bit into a signed 8 bit value. */
720 if (offset & (0x01 << 6)) {
721 temp = offset | (0x01 << 7);
722 } else {
723 temp = offset & (0x3F);
724 temp &= ~(0x01 << 7);
725 }
726
727 LOG_DBG("Read offset: %i", temp);
728
729 if (offset & RV8263_BM_FAST_MODE) {
730 temp = temp * 4340L;
731 } else {
732 temp = temp * 4069L;
733 }
734
735 *calibration = temp;
736
737 return 0;
738 }
739 #endif
740
741 static const struct rtc_driver_api rv8263c8_driver_api = {
742 .set_time = rv8263c8_time_set,
743 .get_time = rv8263c8_time_get,
744 #if CONFIG_RTC_ALARM
745 .alarm_get_supported_fields = rv8263c8_alarm_get_supported_fields,
746 .alarm_set_time = rv8263c8_alarm_set_time,
747 .alarm_get_time = rv8263c8_alarm_get_time,
748 .alarm_is_pending = rv8263c8_alarm_is_pending,
749 .alarm_set_callback = rv8263c8_alarm_set_callback,
750 #endif
751 #if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
752 .update_set_callback = rv8263_update_callback,
753 #endif
754 #ifdef CONFIG_RTC_CALIBRATION
755 .set_calibration = rv8263c8_calibration_set,
756 .get_calibration = rv8263c8_calibration_get,
757 #endif
758 };
759
760 #define RV8263_DEFINE(inst) \
761 static struct rv8263c8_data rv8263c8_data_##inst; \
762 static const struct rv8263c8_config rv8263c8_config_##inst = { \
763 .i2c_bus = I2C_DT_SPEC_INST_GET(inst), \
764 .clkout = DT_INST_ENUM_IDX(inst, clkout), \
765 IF_ENABLED(DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios), \
766 (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0})))}; \
767 DEVICE_DT_INST_DEFINE(inst, &rv8263c8_init, NULL, &rv8263c8_data_##inst, \
768 &rv8263c8_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, \
769 &rv8263c8_driver_api);
770
771 DT_INST_FOREACH_STATUS_OKAY(RV8263_DEFINE)
772