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