diff --git a/boards/x86/qemu_x86/qemu_x86_64.yaml b/boards/x86/qemu_x86/qemu_x86_64.yaml index c62479ad3306..d4c8787f2e54 100644 --- a/boards/x86/qemu_x86/qemu_x86_64.yaml +++ b/boards/x86/qemu_x86/qemu_x86_64.yaml @@ -10,6 +10,7 @@ supported: - can - smp - smbus + - rtc testing: default: true ignore_tags: diff --git a/drivers/rtc/rtc_mc146818.c b/drivers/rtc/rtc_mc146818.c index f34b76d66a14..6ecdfafa6346 100644 --- a/drivers/rtc/rtc_mc146818.c +++ b/drivers/rtc/rtc_mc146818.c @@ -51,7 +51,6 @@ #define RTC_UIP RTC_REG_A #define RTC_DATA RTC_REG_B #define RTC_FLAG RTC_REG_C -#define RTC_ALARM_MDAY RTC_REG_D /* Alarm don't case state */ #define RTC_ALARM_DC 0xFF @@ -114,10 +113,13 @@ #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; - uint16_t alarms_count; - uint16_t mask; bool alarm_pending; rtc_alarm_callback cb; void *cb_data; @@ -194,31 +196,15 @@ static int rtc_mc146818_set_time(const struct device *dev, const struct rtc_time year = (1900 + timeptr->tm_year) % 100; cent = (1900 + timeptr->tm_year) / 100; - if (!(rtc_read(RTC_DATA) & RTC_DMODE_BIT)) { - rtc_write(RTC_SEC, (uint8_t)bin2bcd(timeptr->tm_sec)); - rtc_write(RTC_MIN, (uint8_t)bin2bcd(timeptr->tm_min)); - rtc_write(RTC_HOUR, (uint8_t)bin2bcd(timeptr->tm_hour)); - rtc_write(RTC_WDAY, (uint8_t)bin2bcd(timeptr->tm_wday)); - rtc_write(RTC_MDAY, (uint8_t)bin2bcd(timeptr->tm_mday)); - rtc_write(RTC_MONTH, (uint8_t)bin2bcd(timeptr->tm_mon + 1)); - rtc_write(RTC_YEAR, (uint8_t)bin2bcd(year)); - rtc_write(RTC_CENTURY, (uint8_t)bin2bcd(cent)); - } else { - 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); - } - - if (timeptr->tm_isdst == 1) { - value |= RTC_DSE_BIT; - } else { - value &= (~RTC_DSE_BIT); - } + 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; @@ -243,7 +229,7 @@ static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *tim goto out; } - if (!(rtc_read(RTC_ALARM_MDAY) & RTC_VRT_BIT)) { + if (!(rtc_read(RTC_REG_D) & RTC_VRT_BIT)) { ret = -ENODATA; goto out; } @@ -251,6 +237,7 @@ static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *tim 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; @@ -260,27 +247,11 @@ static int rtc_mc146818_get_time(const struct device *dev, struct rtc_time *tim timeptr->tm_min = rtc_read(RTC_MIN); timeptr->tm_sec = rtc_read(RTC_SEC); - if (!(rtc_read(RTC_DATA) & RTC_DMODE_BIT)) { - year = bcd2bin(year); - cent = bcd2bin(cent); - timeptr->tm_mon = bcd2bin(timeptr->tm_mon); - timeptr->tm_mday = bcd2bin(timeptr->tm_mday); - timeptr->tm_wday = bcd2bin(timeptr->tm_wday); - timeptr->tm_hour = bcd2bin(timeptr->tm_hour); - timeptr->tm_min = bcd2bin(timeptr->tm_min); - timeptr->tm_sec = bcd2bin(timeptr->tm_sec); - } - timeptr->tm_year = 100 * (int)cent + year - 1900; timeptr->tm_nsec = 0; timeptr->tm_yday = 0; value = rtc_read(RTC_DATA); - if (value & RTC_DSE_BIT) { - timeptr->tm_isdst = 1; - } else { - timeptr->tm_isdst = -1; - } /* Check time valid */ if (!rtc_mc146818_validate_time(timeptr)) { @@ -311,51 +282,34 @@ static bool rtc_mc146818_validate_alarm(const struct rtc_time *timeptr, uint32_t return false; } - if ((mask & RTC_ALARM_TIME_MASK_MONTH) && - (timeptr->tm_mon + 1 < MIN_WDAY || timeptr->tm_mon + 1 > MAX_WDAY)) { - return false; - } - - if ((mask & RTC_ALARM_TIME_MASK_MONTHDAY) && - (timeptr->tm_mday < MIN_MDAY || timeptr->tm_mday > MAX_MDAY)) { - return false; - } - - if ((mask & RTC_ALARM_TIME_MASK_YEAR) && - (timeptr->tm_year - 70 < MIN_YEAR_DIFF || timeptr->tm_year - 70 > MAX_YEAR_DIFF)) { - return false; - } - return true; } static int rtc_mc146818_alarm_get_supported_fields(const struct device *dev, uint16_t id, - uint16_t *mask) + uint16_t *mask) { - struct rtc_mc146818_data * const dev_data = dev->data; + ARG_UNUSED(dev); - if (dev_data->alarms_count <= id) { + if (id != 0) { return -EINVAL; } (*mask) = (RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE - | RTC_ALARM_TIME_MASK_HOUR - | RTC_ALARM_TIME_MASK_MONTHDAY - | RTC_ALARM_TIME_MASK_MONTH); + | 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) + 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 (dev_data->alarms_count <= id) { + if (id != 0) { ret = -EINVAL; goto out; } @@ -371,53 +325,21 @@ static int rtc_mc146818_alarm_set_time(const struct device *dev, uint16_t id, ui goto out; } - dev_data->mask = mask; - - if (!(rtc_read(RTC_DATA) & RTC_DMODE_BIT)) { - if (mask & RTC_ALARM_TIME_MASK_SECOND) { - rtc_write(RTC_ALARM_SEC, bin2bcd(timeptr->tm_sec)); - } else { - rtc_write(RTC_ALARM_SEC, bin2bcd(RTC_ALARM_DC)); - } - - if (mask & RTC_ALARM_TIME_MASK_MINUTE) { - rtc_write(RTC_ALARM_MIN, bin2bcd(timeptr->tm_min)); - } else { - rtc_write(RTC_ALARM_SEC, bin2bcd(RTC_ALARM_DC)); - } - - if (mask & RTC_ALARM_TIME_MASK_HOUR) { - rtc_write(RTC_ALARM_HOUR, bin2bcd(timeptr->tm_hour)); - } else { - rtc_write(RTC_ALARM_SEC, bin2bcd(RTC_ALARM_DC)); - } - + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + rtc_write(RTC_ALARM_SEC, timeptr->tm_sec); } else { - 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_ALARM_SEC, RTC_ALARM_DC); } - if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { - rtc_write(RTC_ALARM_MDAY, rtc_read(RTC_REG_D) | - timeptr->tm_mday); + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + rtc_write(RTC_ALARM_MIN, timeptr->tm_min); } else { - rtc_write(RTC_ALARM_SEC, rtc_read(RTC_REG_D) & - (~RTC_MDAY_ALARM)); + 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); @@ -431,11 +353,12 @@ static int rtc_mc146818_alarm_get_time(const struct device *dev, uint16_t id, ui 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 (dev_data->alarms_count <= id) { + if (id != 0) { ret = -EINVAL; goto out; } @@ -445,23 +368,24 @@ static int rtc_mc146818_alarm_get_time(const struct device *dev, uint16_t id, ui goto out; } - timeptr->tm_sec = rtc_read(RTC_ALARM_SEC); - timeptr->tm_min = rtc_read(RTC_ALARM_MIN); - timeptr->tm_hour = rtc_read(RTC_ALARM_HOUR); - timeptr->tm_mday = (rtc_read(RTC_ALARM_MDAY) & RTC_MDAY_ALARM); + (*mask) = 0; - if (!(rtc_read(RTC_DATA) & RTC_DMODE_BIT)) { - timeptr->tm_sec = bcd2bin(timeptr->tm_sec); - timeptr->tm_min = bcd2bin(timeptr->tm_min); - timeptr->tm_hour = bcd2bin(timeptr->tm_hour); + value = rtc_read(RTC_ALARM_SEC); + if (value <= MAX_SEC) { + timeptr->tm_sec = value; + (*mask) |= RTC_ALARM_TIME_MASK_SECOND; } - (*mask) = dev_data->mask; + value = rtc_read(RTC_ALARM_MIN); + if (value <= MAX_SEC) { + timeptr->tm_min = value; + (*mask) |= RTC_ALARM_TIME_MASK_MINUTE; + } - /* Check time valid */ - if (!rtc_mc146818_validate_alarm(timeptr, (*mask))) { - ret = -ENODATA; - goto out; + value = rtc_read(RTC_ALARM_HOUR); + if (value <= MAX_SEC) { + timeptr->tm_hour = value; + (*mask) |= RTC_ALARM_TIME_MASK_HOUR; } ret = 0; @@ -475,7 +399,7 @@ static int rtc_mc146818_alarm_set_callback(const struct device *dev, uint16_t id { struct rtc_mc146818_data * const dev_data = dev->data; - if (dev_data->alarms_count <= id) { + if (id != 0) { return -EINVAL; } @@ -501,14 +425,13 @@ 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 (dev_data->alarms_count <= id) { + if (id != 0) { return -EINVAL; } k_spinlock_key_t key = k_spin_lock(&dev_data->lock); - ret = (dev_data->alarm_pending == true) ? 1 : 0; - + ret = dev_data->alarm_pending ? 1 : 0; dev_data->alarm_pending = false; k_spin_unlock(&dev_data->lock, key); @@ -545,26 +468,31 @@ static int rtc_mc146818_update_set_callback(const struct device *dev, 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 (rtc_read(RTC_FLAG) & RTC_AF_BIT) { + 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 (rtc_read(RTC_FLAG) & RTC_UEF_BIT) { + if (regc & RTC_UEF_BIT) { if (dev_data->update_cb) { dev_data->update_cb(dev, dev_data->update_cb_data); } } #endif - } struct rtc_driver_api rtc_mc146818_driver_api = { @@ -583,26 +511,33 @@ struct rtc_driver_api rtc_mc146818_driver_api = { #endif /* CONFIG_RTC_UPDATE */ }; -static int rtc_mc146818_init(const struct device *dev) -{ - IRQ_CONNECT(DT_INST_IRQN(0), - DT_INST_IRQ(0, priority), - rtc_mc146818_isr, NULL, - DT_INST_IRQ(0, sense)); - irq_enable(DT_INST_IRQN(0)); - - return 0; -} +#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(n) \ - static struct rtc_mc146818_data rtc_data_##n = { \ - .alarms_count = DT_INST_PROP(n, alarms_count), \ - .mask = 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(n, &rtc_mc146818_init, NULL, &rtc_data_##n, \ - NULL, POST_KERNEL, \ - CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ - &rtc_mc146818_driver_api); \ + 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) diff --git a/dts/bindings/rtc/motorola,mc146818.yaml b/dts/bindings/rtc/motorola,mc146818.yaml index 11fd987ab586..7974c1d68c3b 100644 --- a/dts/bindings/rtc/motorola,mc146818.yaml +++ b/dts/bindings/rtc/motorola,mc146818.yaml @@ -7,3 +7,13 @@ description: Motorola MC146818 compatible Real Timer Clock compatible: "motorola,mc146818" include: rtc-device.yaml + +properties: + clock-frequency: + type: int + default: 32768 + enum: + - 4194304 + - 1048576 + - 32768 + description: Frequency of the input-clock in Hertz (Hz) diff --git a/tests/drivers/rtc/rtc_api/boards/qemu_x86.conf b/tests/drivers/rtc/rtc_api/boards/qemu_x86.conf new file mode 100644 index 000000000000..d56b7a15053a --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/qemu_x86.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2023 Bjarki Arge Andreasen +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_RTC_ALARM=y +CONFIG_RTC_UPDATE=y diff --git a/tests/drivers/rtc/rtc_api/boards/qemu_x86.overlay b/tests/drivers/rtc/rtc_api/boards/qemu_x86.overlay new file mode 100644 index 000000000000..78218008f631 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/qemu_x86.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023 Bjarki Arge Andreasen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The RTC IRQ is not routed to the IOAPIC if the legacy + * IRQ bit is set. The IRQ is required for alarm + * operation and the update callback. + */ +&hpet { + no-legacy-irq; +}; + +&rtc { + status = "okay"; +}; diff --git a/tests/drivers/rtc/rtc_api/boards/qemu_x86_64.conf b/tests/drivers/rtc/rtc_api/boards/qemu_x86_64.conf new file mode 100644 index 000000000000..d56b7a15053a --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/qemu_x86_64.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2023 Bjarki Arge Andreasen +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_RTC_ALARM=y +CONFIG_RTC_UPDATE=y diff --git a/tests/drivers/rtc/rtc_api/boards/qemu_x86_64.overlay b/tests/drivers/rtc/rtc_api/boards/qemu_x86_64.overlay new file mode 100644 index 000000000000..78218008f631 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/qemu_x86_64.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023 Bjarki Arge Andreasen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The RTC IRQ is not routed to the IOAPIC if the legacy + * IRQ bit is set. The IRQ is required for alarm + * operation and the update callback. + */ +&hpet { + no-legacy-irq; +}; + +&rtc { + status = "okay"; +}; diff --git a/tests/drivers/rtc/rtc_api/src/test_update_callback.c b/tests/drivers/rtc/rtc_api/src/test_update_callback.c index 6cddd3b045e5..2f98b515c876 100644 --- a/tests/drivers/rtc/rtc_api/src/test_update_callback.c +++ b/tests/drivers/rtc/rtc_api/src/test_update_callback.c @@ -10,32 +10,46 @@ #include static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc)); -static atomic_t callback_called_counter; -static atomic_t callback_test_user_data_address; +static uint32_t callback_called_counter; +static void *callback_test_user_data_address; static uint32_t test_user_data = 0x1234; +static struct k_spinlock lock; static void test_rtc_update_callback_handler(const struct device *dev, void *user_data) { - atomic_inc(&callback_called_counter); + k_spinlock_key_t key = k_spin_lock(&lock); - atomic_set(&callback_test_user_data_address, (uint32_t)user_data); + callback_called_counter++; + callback_test_user_data_address = user_data; + + k_spin_unlock(&lock, key); } ZTEST(rtc_api, test_update_callback) { int ret; + k_spinlock_key_t key; uint32_t counter; - uint32_t address; + void *address; ret = rtc_update_set_callback(rtc, NULL, NULL); zassert_ok(ret, "Failed to clear and disable update callback"); - atomic_set(&callback_called_counter, 0); + key = k_spin_lock(&lock); + + callback_called_counter = 0; + address = callback_test_user_data_address; + + k_spin_unlock(&lock, key); k_msleep(5000); - counter = atomic_get(&callback_called_counter); + key = k_spin_lock(&lock); + + counter = callback_called_counter; + + k_spin_unlock(&lock, key); zassert_equal(counter, 0, "Update callback should not have been called"); @@ -45,11 +59,14 @@ ZTEST(rtc_api, test_update_callback) k_msleep(10000); - counter = atomic_get(&callback_called_counter); + key = k_spin_lock(&lock); + + counter = callback_called_counter; + address = callback_test_user_data_address; - address = atomic_get(&callback_test_user_data_address); + k_spin_unlock(&lock, key); zassert_true(counter < 12 && counter > 8, "Invalid update callback called counter"); - zassert_equal(address, (uint32_t)(&test_user_data), "Incorrect user data"); + zassert_equal(address, ((void *)&test_user_data), "Incorrect user data"); } diff --git a/tests/drivers/rtc/rtc_api/testcase.yaml b/tests/drivers/rtc/rtc_api/testcase.yaml index 56a5fe4d4360..7da5c991a49a 100644 --- a/tests/drivers/rtc/rtc_api/testcase.yaml +++ b/tests/drivers/rtc/rtc_api/testcase.yaml @@ -9,3 +9,5 @@ tests: - api filter: dt_alias_exists("rtc") depends_on: rtc + platform_exclude: + - qemu_x86_64