1 /*
2  * Copyright (c) 2019-2020 Peter Bigot Consulting, LLC
3  * Copyright (c) 2021 Laird Connectivity
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #ifdef CONFIG_SOC_POSIX
9 #undef _POSIX_C_SOURCE
10 #define _POSIX_C_SOURCE 200809L /* Required for gmtime_r */
11 #endif
12 
13 #define DT_DRV_COMPAT microchip_mcp7940n
14 
15 #include <zephyr/device.h>
16 #include <zephyr/drivers/counter.h>
17 #include <zephyr/drivers/gpio.h>
18 #include <zephyr/drivers/i2c.h>
19 #include <zephyr/drivers/rtc/mcp7940n.h>
20 #include <zephyr/kernel.h>
21 #include <zephyr/logging/log.h>
22 #include <zephyr/sys/timeutil.h>
23 #include <zephyr/sys/util.h>
24 #include <time.h>
25 
26 LOG_MODULE_REGISTER(MCP7940N, CONFIG_COUNTER_LOG_LEVEL);
27 
28 /* Alarm channels */
29 #define ALARM0_ID			0
30 #define ALARM1_ID			1
31 
32 /* Size of block when writing whole struct */
33 #define RTC_TIME_REGISTERS_SIZE		sizeof(struct mcp7940n_time_registers)
34 #define RTC_ALARM_REGISTERS_SIZE	sizeof(struct mcp7940n_alarm_registers)
35 
36 /* Largest block size */
37 #define MAX_WRITE_SIZE                  (RTC_TIME_REGISTERS_SIZE)
38 
39 /* tm struct uses years since 1900 but unix time uses years since
40  * 1970. MCP7940N default year is '1' so the offset is 69
41  */
42 #define UNIX_YEAR_OFFSET		69
43 
44 /* Macro used to decode BCD to UNIX time to avoid potential copy and paste
45  * errors.
46  */
47 #define RTC_BCD_DECODE(reg_prefix) (reg_prefix##_one + reg_prefix##_ten * 10)
48 
49 struct mcp7940n_config {
50 	struct counter_config_info generic;
51 	struct i2c_dt_spec i2c;
52 	const struct gpio_dt_spec int_gpios;
53 };
54 
55 struct mcp7940n_data {
56 	const struct device *mcp7940n;
57 	struct k_sem lock;
58 	struct mcp7940n_time_registers registers;
59 	struct mcp7940n_alarm_registers alm0_registers;
60 	struct mcp7940n_alarm_registers alm1_registers;
61 
62 	struct k_work alarm_work;
63 	struct gpio_callback int_callback;
64 
65 	counter_alarm_callback_t counter_handler[2];
66 	uint32_t counter_ticks[2];
67 	void *alarm_user_data[2];
68 
69 	bool int_active_high;
70 };
71 
72 /** @brief Convert bcd time in device registers to UNIX time
73  *
74  * @param dev the MCP7940N device pointer.
75  *
76  * @retval returns unix time.
77  */
decode_rtc(const struct device * dev)78 static time_t decode_rtc(const struct device *dev)
79 {
80 	struct mcp7940n_data *data = dev->data;
81 	time_t time_unix = 0;
82 	struct tm time = { 0 };
83 
84 	time.tm_sec = RTC_BCD_DECODE(data->registers.rtc_sec.sec);
85 	time.tm_min = RTC_BCD_DECODE(data->registers.rtc_min.min);
86 	time.tm_hour = RTC_BCD_DECODE(data->registers.rtc_hours.hr);
87 	time.tm_mday = RTC_BCD_DECODE(data->registers.rtc_date.date);
88 	time.tm_wday = data->registers.rtc_weekday.weekday;
89 	/* tm struct starts months at 0, mcp7940n starts at 1 */
90 	time.tm_mon = RTC_BCD_DECODE(data->registers.rtc_month.month) - 1;
91 	/* tm struct uses years since 1900 but unix time uses years since 1970 */
92 	time.tm_year = RTC_BCD_DECODE(data->registers.rtc_year.year) +
93 		UNIX_YEAR_OFFSET;
94 
95 	time_unix = timeutil_timegm(&time);
96 
97 	LOG_DBG("Unix time is %d\n", (uint32_t)time_unix);
98 
99 	return time_unix;
100 }
101 
102 /** @brief Encode time struct tm into mcp7940n rtc registers
103  *
104  * @param dev the MCP7940N device pointer.
105  * @param time_buffer tm struct containing time to be encoded into mcp7940n
106  * registers.
107  *
108  * @retval return 0 on success, or a negative error code from invalid
109  * parameter.
110  */
encode_rtc(const struct device * dev,struct tm * time_buffer)111 static int encode_rtc(const struct device *dev, struct tm *time_buffer)
112 {
113 	struct mcp7940n_data *data = dev->data;
114 	uint8_t month;
115 	uint8_t year_since_epoch;
116 
117 	/* In a tm struct, months start at 0, mcp7940n starts with 1 */
118 	month = time_buffer->tm_mon + 1;
119 
120 	if (time_buffer->tm_year < UNIX_YEAR_OFFSET) {
121 		return -EINVAL;
122 	}
123 	year_since_epoch = time_buffer->tm_year - UNIX_YEAR_OFFSET;
124 
125 	/* Set external oscillator configuration bit */
126 	data->registers.rtc_sec.start_osc = 1;
127 
128 	data->registers.rtc_sec.sec_one = time_buffer->tm_sec % 10;
129 	data->registers.rtc_sec.sec_ten = time_buffer->tm_sec / 10;
130 	data->registers.rtc_min.min_one = time_buffer->tm_min % 10;
131 	data->registers.rtc_min.min_ten = time_buffer->tm_min / 10;
132 	data->registers.rtc_hours.hr_one = time_buffer->tm_hour % 10;
133 	data->registers.rtc_hours.hr_ten = time_buffer->tm_hour / 10;
134 	data->registers.rtc_weekday.weekday = time_buffer->tm_wday;
135 	data->registers.rtc_date.date_one = time_buffer->tm_mday % 10;
136 	data->registers.rtc_date.date_ten = time_buffer->tm_mday / 10;
137 	data->registers.rtc_month.month_one = month % 10;
138 	data->registers.rtc_month.month_ten = month / 10;
139 	data->registers.rtc_year.year_one = year_since_epoch % 10;
140 	data->registers.rtc_year.year_ten = year_since_epoch / 10;
141 
142 	return 0;
143 }
144 
145 /** @brief Encode time struct tm into mcp7940n alarm registers
146  *
147  * @param dev the MCP7940N device pointer.
148  * @param time_buffer tm struct containing time to be encoded into mcp7940n
149  * registers.
150  * @param alarm_id alarm ID, can be 0 or 1 for MCP7940N.
151  *
152  * @retval return 0 on success, or a negative error code from invalid
153  * parameter.
154  */
encode_alarm(const struct device * dev,struct tm * time_buffer,uint8_t alarm_id)155 static int encode_alarm(const struct device *dev, struct tm *time_buffer, uint8_t alarm_id)
156 {
157 	struct mcp7940n_data *data = dev->data;
158 	uint8_t month;
159 	struct mcp7940n_alarm_registers *alm_regs;
160 
161 	if (alarm_id == ALARM0_ID) {
162 		alm_regs = &data->alm0_registers;
163 	} else if (alarm_id == ALARM1_ID) {
164 		alm_regs = &data->alm1_registers;
165 	} else {
166 		return -EINVAL;
167 	}
168 	/* In a tm struct, months start at 0 */
169 	month = time_buffer->tm_mon + 1;
170 
171 	alm_regs->alm_sec.sec_one = time_buffer->tm_sec % 10;
172 	alm_regs->alm_sec.sec_ten = time_buffer->tm_sec / 10;
173 	alm_regs->alm_min.min_one = time_buffer->tm_min % 10;
174 	alm_regs->alm_min.min_ten = time_buffer->tm_min / 10;
175 	alm_regs->alm_hours.hr_one = time_buffer->tm_hour % 10;
176 	alm_regs->alm_hours.hr_ten = time_buffer->tm_hour / 10;
177 	alm_regs->alm_weekday.weekday = time_buffer->tm_wday;
178 	alm_regs->alm_date.date_one = time_buffer->tm_mday % 10;
179 	alm_regs->alm_date.date_ten = time_buffer->tm_mday / 10;
180 	alm_regs->alm_month.month_one = month % 10;
181 	alm_regs->alm_month.month_ten = month / 10;
182 
183 	return 0;
184 }
185 
186 /** @brief Reads single register from MCP7940N
187  *
188  * @param dev the MCP7940N device pointer.
189  * @param addr register address.
190  * @param val pointer to uint8_t that will contain register value if
191  * successful.
192  *
193  * @retval return 0 on success, or a negative error code from an I2C
194  * transaction.
195  */
read_register(const struct device * dev,uint8_t addr,uint8_t * val)196 static int read_register(const struct device *dev, uint8_t addr, uint8_t *val)
197 {
198 	const struct mcp7940n_config *cfg = dev->config;
199 
200 	int rc = i2c_write_read_dt(&cfg->i2c, &addr, sizeof(addr), val, 1);
201 
202 	return rc;
203 }
204 
205 /** @brief Read registers from device and populate mcp7940n_registers struct
206  *
207  * @param dev the MCP7940N device pointer.
208  * @param unix_time pointer to time_t value that will contain unix time if
209  * successful.
210  *
211  * @retval return 0 on success, or a negative error code from an I2C
212  * transaction.
213  */
read_time(const struct device * dev,time_t * unix_time)214 static int read_time(const struct device *dev, time_t *unix_time)
215 {
216 	struct mcp7940n_data *data = dev->data;
217 	const struct mcp7940n_config *cfg = dev->config;
218 	uint8_t addr = REG_RTC_SEC;
219 
220 	int rc = i2c_write_read_dt(&cfg->i2c, &addr, sizeof(addr), &data->registers,
221 				   RTC_TIME_REGISTERS_SIZE);
222 
223 	if (rc >= 0) {
224 		*unix_time = decode_rtc(dev);
225 	}
226 
227 	return rc;
228 }
229 
230 /** @brief Write a single register to MCP7940N
231  *
232  * @param dev the MCP7940N device pointer.
233  * @param addr register address.
234  * @param value Value that will be written to the register.
235  *
236  * @retval return 0 on success, or a negative error code from an I2C
237  * transaction or invalid parameter.
238  */
write_register(const struct device * dev,enum mcp7940n_register addr,uint8_t value)239 static int write_register(const struct device *dev, enum mcp7940n_register addr, uint8_t value)
240 {
241 	const struct mcp7940n_config *cfg = dev->config;
242 	int rc = 0;
243 
244 	uint8_t time_data[2] = {addr, value};
245 
246 	rc = i2c_write_dt(&cfg->i2c, time_data, sizeof(time_data));
247 
248 	return rc;
249 }
250 
251 /** @brief Write a full time struct to MCP7940N registers.
252  *
253  * @param dev the MCP7940N device pointer.
254  * @param addr first register address to write to, should be REG_RTC_SEC,
255  * REG_ALM0_SEC or REG_ALM0_SEC.
256  * @param size size of data struct that will be written.
257  *
258  * @retval return 0 on success, or a negative error code from an I2C
259  * transaction or invalid parameter.
260  */
write_data_block(const struct device * dev,enum mcp7940n_register addr,uint8_t size)261 static int write_data_block(const struct device *dev, enum mcp7940n_register addr, uint8_t size)
262 {
263 	struct mcp7940n_data *data = dev->data;
264 	const struct mcp7940n_config *cfg = dev->config;
265 	int rc = 0;
266 	uint8_t time_data[MAX_WRITE_SIZE + 1];
267 	uint8_t *write_block_start;
268 
269 	if (size > MAX_WRITE_SIZE) {
270 		return -EINVAL;
271 	}
272 
273 	if (addr >= REG_INVAL) {
274 		return -EINVAL;
275 	}
276 
277 	if (addr == REG_RTC_SEC) {
278 		write_block_start = (uint8_t *)&data->registers;
279 	} else if (addr == REG_ALM0_SEC) {
280 		write_block_start = (uint8_t *)&data->alm0_registers;
281 	} else if (addr == REG_ALM1_SEC) {
282 		write_block_start = (uint8_t *)&data->alm1_registers;
283 	} else {
284 		return -EINVAL;
285 	}
286 
287 	/* Load register address into first byte then fill in data values */
288 	time_data[0] = addr;
289 	memcpy(&time_data[1], write_block_start, size);
290 
291 	rc = i2c_write_dt(&cfg->i2c, time_data, size + 1);
292 
293 	return rc;
294 }
295 
296 /** @brief Sets the correct weekday.
297  *
298  * If the time is never set then the device defaults to 1st January 1970
299  * but with the wrong weekday set. This function ensures the weekday is
300  * correct in this case.
301  *
302  * @param dev the MCP7940N device pointer.
303  * @param unix_time pointer to unix time that will be used to work out the weekday
304  *
305  * @retval return 0 on success, or a negative error code from an I2C
306  * transaction or invalid parameter.
307  */
set_day_of_week(const struct device * dev,time_t * unix_time)308 static int set_day_of_week(const struct device *dev, time_t *unix_time)
309 {
310 	struct mcp7940n_data *data = dev->data;
311 	struct tm time_buffer = { 0 };
312 	int rc = 0;
313 
314 	if (gmtime_r(unix_time, &time_buffer) != NULL) {
315 		data->registers.rtc_weekday.weekday = time_buffer.tm_wday;
316 		rc = write_register(dev, REG_RTC_WDAY,
317 			*((uint8_t *)(&data->registers.rtc_weekday)));
318 	} else {
319 		rc = -EINVAL;
320 	}
321 
322 	return rc;
323 }
324 
325 /** @brief Checks the interrupt pending flag (IF) of a given alarm.
326  *
327  * A callback is fired if an IRQ is pending.
328  *
329  * @param dev the MCP7940N device pointer.
330  * @param alarm_id ID of alarm, can be 0 or 1 for MCP7940N.
331  */
mcp7940n_handle_interrupt(const struct device * dev,uint8_t alarm_id)332 static void mcp7940n_handle_interrupt(const struct device *dev, uint8_t alarm_id)
333 {
334 	struct mcp7940n_data *data = dev->data;
335 	uint8_t alarm_reg_address;
336 	struct mcp7940n_alarm_registers *alm_regs;
337 	counter_alarm_callback_t cb;
338 	uint32_t ticks = 0;
339 	bool fire_callback = false;
340 
341 	if (alarm_id == ALARM0_ID) {
342 		alarm_reg_address = REG_ALM0_WDAY;
343 		alm_regs = &data->alm0_registers;
344 	} else if (alarm_id == ALARM1_ID) {
345 		alarm_reg_address = REG_ALM1_WDAY;
346 		alm_regs = &data->alm1_registers;
347 	} else {
348 		return;
349 	}
350 
351 	k_sem_take(&data->lock, K_FOREVER);
352 
353 	/* Check if this alarm has a pending interrupt */
354 	read_register(dev, alarm_reg_address, (uint8_t *)&alm_regs->alm_weekday);
355 
356 	if (alm_regs->alm_weekday.alm_if) {
357 		/* Clear interrupt */
358 		alm_regs->alm_weekday.alm_if = 0;
359 		write_register(dev, alarm_reg_address,
360 			*((uint8_t *)(&alm_regs->alm_weekday)));
361 
362 		/* Fire callback */
363 		if (data->counter_handler[alarm_id]) {
364 			cb = data->counter_handler[alarm_id];
365 			ticks = data->counter_ticks[alarm_id];
366 			fire_callback = true;
367 		}
368 	}
369 
370 	k_sem_give(&data->lock);
371 
372 	if (fire_callback) {
373 		cb(data->mcp7940n, 0, ticks, data->alarm_user_data[alarm_id]);
374 	}
375 }
376 
mcp7940n_work_handler(struct k_work * work)377 static void mcp7940n_work_handler(struct k_work *work)
378 {
379 	struct mcp7940n_data *data =
380 		CONTAINER_OF(work, struct mcp7940n_data, alarm_work);
381 
382 	/* Check interrupt flags for both alarms */
383 	mcp7940n_handle_interrupt(data->mcp7940n, ALARM0_ID);
384 	mcp7940n_handle_interrupt(data->mcp7940n, ALARM1_ID);
385 }
386 
mcp7940n_init_cb(const struct device * dev,struct gpio_callback * gpio_cb,uint32_t pins)387 static void mcp7940n_init_cb(const struct device *dev,
388 				 struct gpio_callback *gpio_cb, uint32_t pins)
389 {
390 	struct mcp7940n_data *data =
391 		CONTAINER_OF(gpio_cb, struct mcp7940n_data, int_callback);
392 
393 	ARG_UNUSED(pins);
394 
395 	k_work_submit(&data->alarm_work);
396 }
397 
mcp7940n_rtc_set_time(const struct device * dev,time_t unix_time)398 int mcp7940n_rtc_set_time(const struct device *dev, time_t unix_time)
399 {
400 	struct mcp7940n_data *data = dev->data;
401 	struct tm time_buffer = { 0 };
402 	int rc = 0;
403 
404 	if (unix_time > UINT32_MAX) {
405 		LOG_ERR("Unix time must be 32-bit");
406 		return -EINVAL;
407 	}
408 
409 	k_sem_take(&data->lock, K_FOREVER);
410 
411 	/* Convert unix_time to civil time */
412 	gmtime_r(&unix_time, &time_buffer);
413 	LOG_DBG("Desired time is %d-%d-%d %d:%d:%d\n", (time_buffer.tm_year + 1900),
414 		(time_buffer.tm_mon + 1), time_buffer.tm_mday, time_buffer.tm_hour,
415 		time_buffer.tm_min, time_buffer.tm_sec);
416 
417 	/* Encode time */
418 	rc = encode_rtc(dev, &time_buffer);
419 	if (rc < 0) {
420 		goto out;
421 	}
422 
423 	/* Write to device */
424 	rc = write_data_block(dev, REG_RTC_SEC, RTC_TIME_REGISTERS_SIZE);
425 
426 out:
427 	k_sem_give(&data->lock);
428 
429 	return rc;
430 }
431 
mcp7940n_counter_start(const struct device * dev)432 static int mcp7940n_counter_start(const struct device *dev)
433 {
434 	struct mcp7940n_data *data = dev->data;
435 	int rc = 0;
436 
437 	k_sem_take(&data->lock, K_FOREVER);
438 
439 	/* Set start oscillator configuration bit */
440 	data->registers.rtc_sec.start_osc = 1;
441 	rc = write_register(dev, REG_RTC_SEC,
442 		*((uint8_t *)(&data->registers.rtc_sec)));
443 
444 	k_sem_give(&data->lock);
445 
446 	return rc;
447 }
448 
mcp7940n_counter_stop(const struct device * dev)449 static int mcp7940n_counter_stop(const struct device *dev)
450 {
451 	struct mcp7940n_data *data = dev->data;
452 	int rc = 0;
453 
454 	k_sem_take(&data->lock, K_FOREVER);
455 
456 	/* Clear start oscillator configuration bit */
457 	data->registers.rtc_sec.start_osc = 0;
458 	rc = write_register(dev, REG_RTC_SEC,
459 		*((uint8_t *)(&data->registers.rtc_sec)));
460 
461 	k_sem_give(&data->lock);
462 
463 	return rc;
464 }
465 
mcp7940n_counter_get_value(const struct device * dev,uint32_t * ticks)466 static int mcp7940n_counter_get_value(const struct device *dev,
467 				      uint32_t *ticks)
468 {
469 	struct mcp7940n_data *data = dev->data;
470 	time_t unix_time;
471 	int rc;
472 
473 	k_sem_take(&data->lock, K_FOREVER);
474 
475 	/* Get time */
476 	rc = read_time(dev, &unix_time);
477 
478 	/* Convert time to ticks */
479 	if (rc >= 0) {
480 		*ticks = unix_time;
481 	}
482 
483 	k_sem_give(&data->lock);
484 
485 	return rc;
486 }
487 
mcp7940n_counter_set_alarm(const struct device * dev,uint8_t alarm_id,const struct counter_alarm_cfg * alarm_cfg)488 static int mcp7940n_counter_set_alarm(const struct device *dev, uint8_t alarm_id,
489 				      const struct counter_alarm_cfg *alarm_cfg)
490 {
491 	struct mcp7940n_data *data = dev->data;
492 	uint32_t seconds_until_alarm;
493 	time_t current_time;
494 	time_t alarm_time;
495 	struct tm time_buffer = { 0 };
496 	uint8_t alarm_base_address;
497 	struct mcp7940n_alarm_registers *alm_regs;
498 	int rc = 0;
499 
500 	k_sem_take(&data->lock, K_FOREVER);
501 
502 	if (alarm_id == ALARM0_ID) {
503 		alarm_base_address = REG_ALM0_SEC;
504 		alm_regs = &data->alm0_registers;
505 	} else if (alarm_id == ALARM1_ID) {
506 		alarm_base_address = REG_ALM1_SEC;
507 		alm_regs = &data->alm1_registers;
508 	} else {
509 		rc = -EINVAL;
510 		goto out;
511 	}
512 
513 	/* Convert ticks to time */
514 	seconds_until_alarm = alarm_cfg->ticks;
515 
516 	/* Get current time and add alarm offset */
517 	rc = read_time(dev, &current_time);
518 	if (rc < 0) {
519 		goto out;
520 	}
521 
522 	alarm_time = current_time + seconds_until_alarm;
523 	gmtime_r(&alarm_time, &time_buffer);
524 
525 	/* Set alarm trigger mask and alarm enable flag */
526 	if (alarm_id == ALARM0_ID) {
527 		data->registers.rtc_control.alm0_en = 1;
528 	} else if (alarm_id == ALARM1_ID) {
529 		data->registers.rtc_control.alm1_en = 1;
530 	}
531 
532 	/* Set alarm to match with second, minute, hour, day of week, day of
533 	 * month and month
534 	 */
535 	alm_regs->alm_weekday.alm_msk = MCP7940N_ALARM_TRIGGER_ALL;
536 
537 	/* Write time to alarm registers */
538 	encode_alarm(dev, &time_buffer, alarm_id);
539 	rc = write_data_block(dev, alarm_base_address, RTC_ALARM_REGISTERS_SIZE);
540 	if (rc < 0) {
541 		goto out;
542 	}
543 
544 	/* Enable alarm */
545 	rc = write_register(dev, REG_RTC_CONTROL,
546 		*((uint8_t *)(&data->registers.rtc_control)));
547 	if (rc < 0) {
548 		goto out;
549 	}
550 
551 	/* Config user data and callback */
552 	data->counter_handler[alarm_id] = alarm_cfg->callback;
553 	data->counter_ticks[alarm_id] = current_time;
554 	data->alarm_user_data[alarm_id] = alarm_cfg->user_data;
555 
556 out:
557 	k_sem_give(&data->lock);
558 
559 	return rc;
560 }
561 
mcp7940n_counter_cancel_alarm(const struct device * dev,uint8_t alarm_id)562 static int mcp7940n_counter_cancel_alarm(const struct device *dev, uint8_t alarm_id)
563 {
564 	struct mcp7940n_data *data = dev->data;
565 	int rc = 0;
566 
567 	k_sem_take(&data->lock, K_FOREVER);
568 
569 	/* Clear alarm enable bit */
570 	if (alarm_id == ALARM0_ID) {
571 		data->registers.rtc_control.alm0_en = 0;
572 	} else if (alarm_id == ALARM1_ID) {
573 		data->registers.rtc_control.alm1_en = 0;
574 	} else {
575 		rc = -EINVAL;
576 		goto out;
577 	}
578 
579 	rc = write_register(dev, REG_RTC_CONTROL,
580 		*((uint8_t *)(&data->registers.rtc_control)));
581 
582 out:
583 	k_sem_give(&data->lock);
584 
585 	return rc;
586 }
587 
mcp7940n_counter_set_top_value(const struct device * dev,const struct counter_top_cfg * cfg)588 static int mcp7940n_counter_set_top_value(const struct device *dev,
589 					  const struct counter_top_cfg *cfg)
590 {
591 	return -ENOTSUP;
592 }
593 
594 /* This function can be used to poll the alarm interrupt flags if the MCU is
595  * not connected to the MC7940N MFP pin. It can also be used to check if an
596  * alarm was triggered while the MCU was in reset. This function will clear
597  * the interrupt flag
598  *
599  * Return bitmask of alarm interrupt flag (IF) where each IF is shifted by
600  * the alarm ID.
601  */
mcp7940n_counter_get_pending_int(const struct device * dev)602 static uint32_t mcp7940n_counter_get_pending_int(const struct device *dev)
603 {
604 	struct mcp7940n_data *data = dev->data;
605 	uint32_t interrupt_pending = 0;
606 	int rc;
607 
608 	k_sem_take(&data->lock, K_FOREVER);
609 
610 	/* Check interrupt flag for alarm 0 */
611 	rc = read_register(dev, REG_ALM0_WDAY,
612 		(uint8_t *)&data->alm0_registers.alm_weekday);
613 	if (rc < 0) {
614 		goto out;
615 	}
616 
617 	if (data->alm0_registers.alm_weekday.alm_if) {
618 		/* Clear interrupt */
619 		data->alm0_registers.alm_weekday.alm_if = 0;
620 		rc = write_register(dev, REG_ALM0_WDAY,
621 			*((uint8_t *)(&data->alm0_registers.alm_weekday)));
622 		if (rc < 0) {
623 			goto out;
624 		}
625 		interrupt_pending |= (1 << ALARM0_ID);
626 	}
627 
628 	/* Check interrupt flag for alarm 1 */
629 	rc = read_register(dev, REG_ALM1_WDAY,
630 		(uint8_t *)&data->alm1_registers.alm_weekday);
631 	if (rc < 0) {
632 		goto out;
633 	}
634 
635 	if (data->alm1_registers.alm_weekday.alm_if) {
636 		/* Clear interrupt */
637 		data->alm1_registers.alm_weekday.alm_if = 0;
638 		rc = write_register(dev, REG_ALM1_WDAY,
639 			*((uint8_t *)(&data->alm1_registers.alm_weekday)));
640 		if (rc < 0) {
641 			goto out;
642 		}
643 		interrupt_pending |= (1 << ALARM1_ID);
644 	}
645 
646 out:
647 	k_sem_give(&data->lock);
648 
649 	if (rc) {
650 		interrupt_pending = 0;
651 	}
652 	return (interrupt_pending);
653 }
654 
mcp7940n_counter_get_top_value(const struct device * dev)655 static uint32_t mcp7940n_counter_get_top_value(const struct device *dev)
656 {
657 	return UINT32_MAX;
658 }
659 
mcp7940n_init(const struct device * dev)660 static int mcp7940n_init(const struct device *dev)
661 {
662 	struct mcp7940n_data *data = dev->data;
663 	const struct mcp7940n_config *cfg = dev->config;
664 	int rc = 0;
665 	time_t unix_time = 0;
666 
667 	/* Initialize and take the lock */
668 	k_sem_init(&data->lock, 0, 1);
669 
670 	if (!device_is_ready(cfg->i2c.bus)) {
671 		LOG_ERR("I2C device %s is not ready", cfg->i2c.bus->name);
672 		rc = -ENODEV;
673 		goto out;
674 	}
675 
676 	rc = read_time(dev, &unix_time);
677 	if (rc < 0) {
678 		goto out;
679 	}
680 
681 	rc = set_day_of_week(dev, &unix_time);
682 	if (rc < 0) {
683 		goto out;
684 	}
685 
686 	/* Set 24-hour time */
687 	data->registers.rtc_hours.twelve_hr = false;
688 	rc = write_register(dev, REG_RTC_HOUR,
689 		*((uint8_t *)(&data->registers.rtc_hours)));
690 	if (rc < 0) {
691 		goto out;
692 	}
693 
694 	/* Configure alarm interrupt gpio */
695 	if (cfg->int_gpios.port != NULL) {
696 
697 		if (!gpio_is_ready_dt(&cfg->int_gpios)) {
698 			LOG_ERR("Port device %s is not ready",
699 				cfg->int_gpios.port->name);
700 			rc = -ENODEV;
701 			goto out;
702 		}
703 
704 		data->mcp7940n = dev;
705 		k_work_init(&data->alarm_work, mcp7940n_work_handler);
706 
707 		gpio_pin_configure_dt(&cfg->int_gpios, GPIO_INPUT);
708 
709 		gpio_pin_interrupt_configure_dt(&cfg->int_gpios,
710 						GPIO_INT_EDGE_TO_ACTIVE);
711 
712 		gpio_init_callback(&data->int_callback, mcp7940n_init_cb,
713 				   BIT(cfg->int_gpios.pin));
714 
715 		(void)gpio_add_callback(cfg->int_gpios.port, &data->int_callback);
716 
717 		/* Configure interrupt polarity */
718 		if ((cfg->int_gpios.dt_flags & GPIO_ACTIVE_LOW) == GPIO_ACTIVE_LOW) {
719 			data->int_active_high = false;
720 		} else {
721 			data->int_active_high = true;
722 		}
723 		data->alm0_registers.alm_weekday.alm_pol = data->int_active_high;
724 		data->alm1_registers.alm_weekday.alm_pol = data->int_active_high;
725 		rc = write_register(dev, REG_ALM0_WDAY,
726 				    *((uint8_t *)(&data->alm0_registers.alm_weekday)));
727 		rc = write_register(dev, REG_ALM1_WDAY,
728 				    *((uint8_t *)(&data->alm1_registers.alm_weekday)));
729 	}
730 out:
731 	k_sem_give(&data->lock);
732 
733 	return rc;
734 }
735 
736 static DEVICE_API(counter, mcp7940n_api) = {
737 	.start = mcp7940n_counter_start,
738 	.stop = mcp7940n_counter_stop,
739 	.get_value = mcp7940n_counter_get_value,
740 	.set_alarm = mcp7940n_counter_set_alarm,
741 	.cancel_alarm = mcp7940n_counter_cancel_alarm,
742 	.set_top_value = mcp7940n_counter_set_top_value,
743 	.get_pending_int = mcp7940n_counter_get_pending_int,
744 	.get_top_value = mcp7940n_counter_get_top_value,
745 };
746 
747 #define INST_DT_MCP7904N(index)                                                         \
748 											\
749 	static struct mcp7940n_data mcp7940n_data_##index;				\
750 											\
751 	static const struct mcp7940n_config mcp7940n_config_##index = {			\
752 		.generic = {								\
753 			.max_top_value = UINT32_MAX,					\
754 			.freq = 1,							\
755 			.flags = COUNTER_CONFIG_INFO_COUNT_UP,				\
756 			.channels = 2,							\
757 		},									\
758 		.i2c = I2C_DT_SPEC_INST_GET(index),					\
759 		.int_gpios = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {0}),		\
760 	};										\
761 											\
762 	DEVICE_DT_INST_DEFINE(index, mcp7940n_init, NULL,				\
763 		    &mcp7940n_data_##index,						\
764 		    &mcp7940n_config_##index,						\
765 		    POST_KERNEL,							\
766 		    CONFIG_COUNTER_INIT_PRIORITY,					\
767 		    &mcp7940n_api);
768 
769 DT_INST_FOREACH_STATUS_OKAY(INST_DT_MCP7904N);
770