/* * Copyright (c) 2023 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT motorola_mc146818 #include #include #include #include #include #include #include #include #include #define RTC_STD_INDEX (DT_INST_REG_ADDR_BY_IDX(0, 0)) #define RTC_STD_TARGET (DT_INST_REG_ADDR_BY_IDX(0, 1)) /* Time indices in RTC RAM */ #define RTC_SEC 0x00 #define RTC_MIN 0x02 #define RTC_HOUR 0x04 /* Day of week index in RTC RAM */ #define RTC_WDAY 0x06 /* Day of month index in RTC RAM */ #define RTC_MDAY 0x07 /* Month and year index in RTC RAM */ #define RTC_MONTH 0x08 #define RTC_YEAR 0x09 /* Y2K Bugfix */ #define RTC_CENTURY 0x32 /* Alarm time indices in RTC RAM */ #define RTC_ALARM_SEC 0x01 #define RTC_ALARM_MIN 0x03 #define RTC_ALARM_HOUR 0x05 /* Registers A-D indeces in RTC RAM */ #define RTC_REG_A 0x0A #define RTC_REG_B 0x0B #define RTC_REG_C 0x0C #define RTC_REG_D 0x0D #define RTC_UIP RTC_REG_A #define RTC_DATA RTC_REG_B #define RTC_FLAG RTC_REG_C /* Alarm don't case state */ #define RTC_ALARM_DC 0xFF /* Update In Progress bit in REG_A */ #define RTC_UIP_BIT BIT(7) /* Update Cycle Inhibit bit in REG_B */ #define RTC_UCI_BIT BIT(7) /* Periodic Interrupt Enable bit in REG_B */ #define RTC_PIE_BIT BIT(6) /* Alarm Interrupt Enable bit in REG_B */ #define RTC_AIE_BIT BIT(5) /* Update-ended Interrupt Enable bit in REG_B */ #define RTC_UIE_BIT BIT(4) /* Data mode bit in REG_B */ #define RTC_DMODE_BIT BIT(2) /* Hour Format bit in REG_B */ #define RTC_HFORMAT_BIT BIT(1) /* Daylight Savings Enable Format bit in REG_B */ #define RTC_DSE_BIT BIT(0) /* Interrupt Request Flag bit in REG_C */ #define RTC_IRF_BIT BIT(7) /* Periodic Flag bit in REG_C */ #define RTC_PF_BIT BIT(6) /* Alarm Flag bit in REG_C */ #define RTC_AF_BIT BIT(5) /* Update-end Flag bit in REG_C */ #define RTC_UEF_BIT BIT(4) /* VRT bit in REG_D */ #define RTC_VRT_BIT BIT(7) /* Month day Alarm bits in REG_D */ #define RTC_MDAY_ALARM BIT_MASK(5) /* Maximum and Minimum values of time */ #define MIN_SEC 0 #define MAX_SEC 59 #define MIN_MIN 0 #define MAX_MIN 59 #define MIN_HOUR 0 #define MAX_HOUR 23 #define MAX_WDAY 7 #define MIN_WDAY 1 #define MAX_MDAY 31 #define MIN_MDAY 1 #define MAX_MON 12 #define MIN_MON 1 #define MIN_YEAR_DIFF 0 /* YEAR - 1900 */ #define MAX_YEAR_DIFF 99 /* YEAR - 1999 */ /* Input clock frequency mapped to divider bits */ #define RTC_IN_CLK_DIV_BITS_4194304 (0) #define RTC_IN_CLK_DIV_BITS_1048576 (1 << 4) #define RTC_IN_CLK_DIV_BITS_32768 (2 << 4) struct rtc_mc146818_data { struct k_spinlock lock; bool alarm_pending; rtc_alarm_callback cb; void *cb_data; rtc_update_callback update_cb; void *update_cb_data; }; static uint8_t rtc_read(int reg) { uint8_t value; sys_out8(reg, RTC_STD_INDEX); value = sys_in8(RTC_STD_TARGET); return value; } static void rtc_write(int reg, uint8_t value) { sys_out8(reg, RTC_STD_INDEX); sys_out8(value, RTC_STD_TARGET); } static bool rtc_mc146818_validate_time(const struct rtc_time *timeptr) { if (timeptr->tm_sec < MIN_SEC || timeptr->tm_sec > MAX_SEC) { return false; } if (timeptr->tm_min < MIN_MIN || timeptr->tm_min > MAX_MIN) { return false; } if (timeptr->tm_hour < MIN_HOUR || timeptr->tm_hour > MAX_HOUR) { return false; } if (timeptr->tm_wday + 1 < MIN_WDAY || timeptr->tm_wday + 1 > MAX_WDAY) { return false; } if (timeptr->tm_mday < MIN_MDAY || timeptr->tm_mday > MAX_MDAY) { return false; } if (timeptr->tm_mon + 1 < MIN_MON || timeptr->tm_mon + 1 > MAX_MON) { return false; } if (timeptr->tm_year - 70 < MIN_YEAR_DIFF || timeptr->tm_year - 70 > MAX_YEAR_DIFF) { return false; } return true; } static int rtc_mc146818_set_time(const struct device *dev, const struct rtc_time *timeptr) { struct rtc_mc146818_data * const dev_data = dev->data; uint8_t value; int year; int cent; int ret; k_spinlock_key_t key = k_spin_lock(&dev_data->lock); if (timeptr == NULL) { ret = -EINVAL; goto out; } /* Check time valid */ if (!rtc_mc146818_validate_time(timeptr)) { ret = -EINVAL; goto out; } value = rtc_read(RTC_DATA); rtc_write(RTC_DATA, value | RTC_UCI_BIT); year = (1900 + timeptr->tm_year) % 100; cent = (1900 + timeptr->tm_year) / 100; rtc_write(RTC_SEC, (uint8_t)timeptr->tm_sec); rtc_write(RTC_MIN, (uint8_t)timeptr->tm_min); rtc_write(RTC_HOUR, (uint8_t)timeptr->tm_hour); rtc_write(RTC_WDAY, (uint8_t)timeptr->tm_wday); rtc_write(RTC_MDAY, (uint8_t)timeptr->tm_mday); rtc_write(RTC_MONTH, (uint8_t)timeptr->tm_mon + 1); rtc_write(RTC_YEAR, year); rtc_write(RTC_CENTURY, cent); value &= (~RTC_UCI_BIT); rtc_write(RTC_DATA, value); ret = 0; out: k_spin_unlock(&dev_data->lock, key); return ret; } static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *timeptr) { struct rtc_mc146818_data * const dev_data = dev->data; int ret; uint8_t cent; uint8_t year; uint8_t value; k_spinlock_key_t key = k_spin_lock(&dev_data->lock); /* Validate arguments */ if (timeptr == NULL) { ret = -EINVAL; goto out; } if (!(rtc_read(RTC_REG_D) & RTC_VRT_BIT)) { ret = -ENODATA; goto out; } while (rtc_read(RTC_UIP) & RTC_UIP_BIT) { continue; } cent = rtc_read(RTC_CENTURY); year = rtc_read(RTC_YEAR); timeptr->tm_mon = rtc_read(RTC_MONTH) - 1; timeptr->tm_mday = rtc_read(RTC_MDAY); timeptr->tm_wday = rtc_read(RTC_WDAY) - 1; timeptr->tm_hour = rtc_read(RTC_HOUR); timeptr->tm_min = rtc_read(RTC_MIN); timeptr->tm_sec = rtc_read(RTC_SEC); timeptr->tm_year = 100 * (int)cent + year - 1900; timeptr->tm_nsec = 0; timeptr->tm_yday = 0; value = rtc_read(RTC_DATA); /* Check time valid */ if (!rtc_mc146818_validate_time(timeptr)) { ret = -ENODATA; goto out; } ret = 0; out: k_spin_unlock(&dev_data->lock, key); return ret; } #if defined(CONFIG_RTC_ALARM) static bool rtc_mc146818_validate_alarm(const struct rtc_time *timeptr, uint32_t mask) { if ((mask & RTC_ALARM_TIME_MASK_SECOND) && (timeptr->tm_sec < MIN_SEC || timeptr->tm_sec > MAX_SEC)) { return false; } if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && (timeptr->tm_min < MIN_MIN || timeptr->tm_min > MAX_MIN)) { return false; } if ((mask & RTC_ALARM_TIME_MASK_HOUR) && (timeptr->tm_hour < MIN_HOUR || timeptr->tm_hour > MAX_HOUR)) { return false; } return true; } static int rtc_mc146818_alarm_get_supported_fields(const struct device *dev, uint16_t id, uint16_t *mask) { ARG_UNUSED(dev); if (id != 0) { return -EINVAL; } (*mask) = (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR); return 0; } static int rtc_mc146818_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, const struct rtc_time *timeptr) { struct rtc_mc146818_data * const dev_data = dev->data; int ret; k_spinlock_key_t key = k_spin_lock(&dev_data->lock); if (id != 0) { ret = -EINVAL; goto out; } if ((mask > 0) && (timeptr == NULL)) { ret = -EINVAL; goto out; } /* Check time valid */ if (!rtc_mc146818_validate_alarm(timeptr, mask)) { ret = -EINVAL; goto out; } if (mask & RTC_ALARM_TIME_MASK_SECOND) { rtc_write(RTC_ALARM_SEC, timeptr->tm_sec); } else { rtc_write(RTC_ALARM_SEC, RTC_ALARM_DC); } if (mask & RTC_ALARM_TIME_MASK_MINUTE) { rtc_write(RTC_ALARM_MIN, timeptr->tm_min); } else { rtc_write(RTC_ALARM_SEC, RTC_ALARM_DC); } if (mask & RTC_ALARM_TIME_MASK_HOUR) { rtc_write(RTC_ALARM_HOUR, timeptr->tm_hour); } else { rtc_write(RTC_ALARM_SEC, RTC_ALARM_DC); } rtc_write(RTC_DATA, rtc_read(RTC_DATA) | RTC_AIE_BIT); ret = 0; out: k_spin_unlock(&dev_data->lock, key); return ret; } static int rtc_mc146818_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, struct rtc_time *timeptr) { struct rtc_mc146818_data * const dev_data = dev->data; uint8_t value; int ret; k_spinlock_key_t key = k_spin_lock(&dev_data->lock); if (id != 0) { ret = -EINVAL; goto out; } if (timeptr == NULL) { ret = -EINVAL; goto out; } (*mask) = 0; value = rtc_read(RTC_ALARM_SEC); if (value <= MAX_SEC) { timeptr->tm_sec = value; (*mask) |= RTC_ALARM_TIME_MASK_SECOND; } value = rtc_read(RTC_ALARM_MIN); if (value <= MAX_SEC) { timeptr->tm_min = value; (*mask) |= RTC_ALARM_TIME_MASK_MINUTE; } value = rtc_read(RTC_ALARM_HOUR); if (value <= MAX_SEC) { timeptr->tm_hour = value; (*mask) |= RTC_ALARM_TIME_MASK_HOUR; } ret = 0; out: k_spin_unlock(&dev_data->lock, key); return ret; } static int rtc_mc146818_alarm_set_callback(const struct device *dev, uint16_t id, rtc_alarm_callback callback, void *user_data) { struct rtc_mc146818_data * const dev_data = dev->data; if (id != 0) { return -EINVAL; } k_spinlock_key_t key = k_spin_lock(&dev_data->lock); dev_data->cb = callback; dev_data->cb_data = user_data; if (callback != NULL) { /* Enable Alarm callback */ rtc_write(RTC_DATA, (rtc_read(RTC_DATA) | RTC_AIE_BIT)); } else { /* Disable Alarm callback */ rtc_write(RTC_DATA, (rtc_read(RTC_DATA) & (~RTC_AIE_BIT))); } k_spin_unlock(&dev_data->lock, key); return 0; } static int rtc_mc146818_alarm_is_pending(const struct device *dev, uint16_t id) { struct rtc_mc146818_data * const dev_data = dev->data; int ret; if (id != 0) { return -EINVAL; } k_spinlock_key_t key = k_spin_lock(&dev_data->lock); ret = dev_data->alarm_pending ? 1 : 0; dev_data->alarm_pending = false; k_spin_unlock(&dev_data->lock, key); return ret; } #endif /* CONFIG_RTC_ALARM */ #if defined(CONFIG_RTC_UPDATE) static int rtc_mc146818_update_set_callback(const struct device *dev, rtc_update_callback callback, void *user_data) { struct rtc_mc146818_data * const dev_data = dev->data; k_spinlock_key_t key = k_spin_lock(&dev_data->lock); dev_data->update_cb = callback; dev_data->update_cb_data = user_data; if (callback != NULL) { /* Enable update callback */ rtc_write(RTC_DATA, (rtc_read(RTC_DATA) | RTC_UIE_BIT)); } else { /* Disable update callback */ rtc_write(RTC_DATA, (rtc_read(RTC_DATA) & (~RTC_UIE_BIT))); } k_spin_unlock(&dev_data->lock, key); return 0; } #endif /* CONFIG_RTC_UPDATE */ static void rtc_mc146818_isr(const struct device *dev) { struct rtc_mc146818_data * const dev_data = dev->data; uint8_t regc; ARG_UNUSED(dev_data); /* Read register, which clears the register */ regc = rtc_read(RTC_FLAG); #if defined(CONFIG_RTC_ALARM) if (regc & RTC_AF_BIT) { if (dev_data->cb) { dev_data->cb(dev, 0, dev_data->cb_data); dev_data->alarm_pending = false; } else { dev_data->alarm_pending = true; } } #endif #if defined(CONFIG_RTC_UPDATE) if (regc & RTC_UEF_BIT) { if (dev_data->update_cb) { dev_data->update_cb(dev, dev_data->update_cb_data); } } #endif } static const struct rtc_driver_api rtc_mc146818_driver_api = { .set_time = rtc_mc146818_set_time, .get_time = rtc_mc146818_get_time, #if defined(CONFIG_RTC_ALARM) .alarm_get_supported_fields = rtc_mc146818_alarm_get_supported_fields, .alarm_set_time = rtc_mc146818_alarm_set_time, .alarm_get_time = rtc_mc146818_alarm_get_time, .alarm_is_pending = rtc_mc146818_alarm_is_pending, .alarm_set_callback = rtc_mc146818_alarm_set_callback, #endif /* CONFIG_RTC_ALARM */ #if defined(CONFIG_RTC_UPDATE) .update_set_callback = rtc_mc146818_update_set_callback, #endif /* CONFIG_RTC_UPDATE */ }; #define RTC_MC146818_INIT_FN_DEFINE(n) \ static int rtc_mc146818_init##n(const struct device *dev) \ { \ rtc_write(RTC_REG_A, \ _CONCAT(RTC_IN_CLK_DIV_BITS_, \ DT_INST_PROP(n, clock_frequency))); \ \ rtc_write(RTC_REG_B, RTC_DMODE_BIT | RTC_HFORMAT_BIT); \ \ IRQ_CONNECT(DT_INST_IRQN(0), \ DT_INST_IRQ(0, priority), \ rtc_mc146818_isr, DEVICE_DT_INST_GET(n), \ DT_INST_IRQ(0, sense)); \ \ irq_enable(DT_INST_IRQN(0)); \ \ return 0; \ } #define RTC_MC146818_DEV_CFG(inst) \ struct rtc_mc146818_data rtc_mc146818_data##inst; \ \ RTC_MC146818_INIT_FN_DEFINE(inst) \ \ DEVICE_DT_INST_DEFINE(inst, &rtc_mc146818_init##inst, NULL, \ &rtc_mc146818_data##inst, NULL, POST_KERNEL, \ CONFIG_RTC_INIT_PRIORITY, \ &rtc_mc146818_driver_api); \ DT_INST_FOREACH_STATUS_OKAY(RTC_MC146818_DEV_CFG)