diff --git a/drivers/rtc/rtc_rpi_pico.c b/drivers/rtc/rtc_rpi_pico.c index f628d2e9708ef6..84a0d2a4dec29f 100644 --- a/drivers/rtc/rtc_rpi_pico.c +++ b/drivers/rtc/rtc_rpi_pico.c @@ -11,7 +11,11 @@ #include #include +#include #include +#include + +#include "rtc_utils.h" #define DT_DRV_COMPAT raspberrypi_pico_rtc @@ -22,21 +26,72 @@ #define TM_YEAR_REF 1900 /* See section 4.8.1 of the RP2040 datasheet. */ #define RP2040_RTC_YEAR_MAX 4095 +#ifdef CONFIG_RTC_ALARM +static int rtc_rpi_pico_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, + struct rtc_time *timeptr); +#endif struct rtc_rpi_pico_data { struct k_spinlock lock; + +#ifdef CONFIG_RTC_ALARM + struct rtc_time alarm_time; + uint16_t alarm_mask; + rtc_alarm_callback alarm_callback; + void *alarm_user_data; + bool alarm_pending; +#endif /* CONFIG_RTC_ALARM */ }; +static struct rtc_rpi_pico_data rtc_data; + LOG_MODULE_REGISTER(rtc_rpi, CONFIG_RTC_LOG_LEVEL); +#ifdef CONFIG_RTC_ALARM +static void rtc_rpi_isr(const struct device *dev) +{ + struct rtc_rpi_pico_data *data = dev->data; + + rtc_alarm_callback callback; + void *user_data; + + rtc_disable_alarm(); + + K_SPINLOCK(&data->lock) { + callback = data->alarm_callback; + user_data = data->alarm_user_data; + } + + if (callback != NULL) { + callback(dev, 0, user_data); + } else { + data->alarm_pending = true; + } + /* re-enable the alarm. */ + rtc_enable_alarm(); +} +#endif + static int rtc_rpi_pico_init(const struct device *dev) { int ret; +#ifdef CONFIG_RTC_ALARM + struct rtc_rpi_pico_data *data = dev->data; +#endif ret = clock_control_on(CLK_DRV, CLK_ID); if (ret < 0) { return ret; } +#ifdef CONFIG_RTC_ALARM + data->alarm_mask = 0; + data->alarm_callback = NULL; + data->alarm_pending = false; + + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtc_rpi_isr, DEVICE_DT_INST_GET(0), + 0); + irq_enable(DT_INST_IRQN(0)); +#endif rtc_init(); return 0; } @@ -101,12 +156,168 @@ static int rtc_rpi_pico_get_time(const struct device *dev, struct rtc_time *time return err; } +#if defined(CONFIG_RTC_ALARM) +static int rtc_rpi_pico_alarm_get_supported_fields(const struct device *dev, uint16_t id, + uint16_t *supported_fields) +{ + ARG_UNUSED(dev); + + if (id != 0) { + return -EINVAL; + } + *supported_fields = RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | + RTC_ALARM_TIME_MASK_HOUR | RTC_ALARM_TIME_MASK_WEEKDAY | + RTC_ALARM_TIME_MASK_MONTHDAY | RTC_ALARM_TIME_MASK_MONTH | + RTC_ALARM_TIME_MASK_YEAR; + + return 0; +} + +static int rtc_rpi_pico_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, + const struct rtc_time *alarm) +{ + struct rtc_rpi_pico_data *data = dev->data; + int err = 0; + uint16_t mask_available; + + (void)rtc_rpi_pico_alarm_get_supported_fields(NULL, 0, &mask_available); + + if (mask & ~mask_available) { + return -EINVAL; + } + + if (!rtc_utils_validate_rtc_time(alarm, mask)) { + return -EINVAL; + } + + LOG_INF("Setting alarm"); + + rtc_disable_alarm(); + if (mask == 0) { + /* Disable the alarm */ + data->alarm_mask = 0; + } + k_spinlock_key_t key = k_spin_lock(&data->lock); + + /* Clear before updating. */ + rtc_hw->irq_setup_0 = 0; + rtc_hw->irq_setup_1 = 0; + + /* Set the match enable bits for things we care about */ + if (mask & RTC_ALARM_TIME_MASK_YEAR) { + hw_set_bits(&rtc_hw->irq_setup_0, + RTC_IRQ_SETUP_0_YEAR_ENA_BITS | + ((alarm->tm_year + TM_YEAR_REF) << RTC_IRQ_SETUP_0_YEAR_LSB)); + } + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + hw_set_bits(&rtc_hw->irq_setup_0, + RTC_IRQ_SETUP_0_MONTH_ENA_BITS | + (alarm->tm_mon << RTC_IRQ_SETUP_0_MONTH_LSB)); + } + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + hw_set_bits(&rtc_hw->irq_setup_0, + RTC_IRQ_SETUP_0_DAY_ENA_BITS | + ((alarm->tm_mday + 1) << RTC_IRQ_SETUP_0_DAY_LSB)); + } + if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { + hw_set_bits(&rtc_hw->irq_setup_1, + RTC_IRQ_SETUP_1_DOTW_ENA_BITS | + (alarm->tm_wday << RTC_IRQ_SETUP_1_DOTW_LSB)); + } + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + hw_set_bits(&rtc_hw->irq_setup_1, + RTC_IRQ_SETUP_1_HOUR_ENA_BITS | + (alarm->tm_hour << RTC_IRQ_SETUP_1_HOUR_LSB)); + } + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + hw_set_bits(&rtc_hw->irq_setup_1, + RTC_IRQ_SETUP_1_MIN_ENA_BITS | + (alarm->tm_min << RTC_IRQ_SETUP_1_MIN_LSB)); + } + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + hw_set_bits(&rtc_hw->irq_setup_1, + RTC_IRQ_SETUP_1_SEC_ENA_BITS | + (alarm->tm_sec << RTC_IRQ_SETUP_1_SEC_LSB)); + } + data->alarm_time = *alarm; + data->alarm_mask = mask; + k_spin_unlock(&data->lock, key); + + /* Enable the IRQ at the peri */ + rtc_hw->inte = RTC_INTE_RTC_BITS; + + rtc_enable_alarm(); + + return err; +} + +static int rtc_rpi_pico_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, + struct rtc_time *timeptr) +{ + struct rtc_rpi_pico_data *data = dev->data; + + if (id != 0) { + return -EINVAL; + } + + K_SPINLOCK(&data->lock) { + *timeptr = data->alarm_time; + *mask = data->alarm_mask; + } + + return 0; +} + +static int rtc_rpi_pico_alarm_is_pending(const struct device *dev, uint16_t id) +{ + struct rtc_rpi_pico_data *data = dev->data; + int ret = 0; + + if (id != 0) { + return -EINVAL; + } + + K_SPINLOCK(&data->lock) { + ret = data->alarm_pending ? 1 : 0; + data->alarm_pending = false; + } + + return ret; +} + +static int rtc_rpi_pico_alarm_set_callback(const struct device *dev, uint16_t id, + rtc_alarm_callback callback, void *user_data) +{ + struct rtc_rpi_pico_data *data = dev->data; + + if (id != 0) { + return -EINVAL; + } + + K_SPINLOCK(&data->lock) { + data->alarm_callback = callback; + data->alarm_user_data = user_data; + if ((callback == NULL) && (user_data == NULL)) { + rtc_disable_alarm(); + } + } + + return 0; +} + +#endif /* CONFIG_RTC_ALARM */ + static const struct rtc_driver_api rtc_rpi_pico_driver_api = { .set_time = rtc_rpi_pico_set_time, .get_time = rtc_rpi_pico_get_time, +#if defined(CONFIG_RTC_ALARM) + .alarm_get_supported_fields = rtc_rpi_pico_alarm_get_supported_fields, + .alarm_set_time = rtc_rpi_pico_alarm_set_time, + .alarm_get_time = rtc_rpi_pico_alarm_get_time, + .alarm_is_pending = rtc_rpi_pico_alarm_is_pending, + .alarm_set_callback = rtc_rpi_pico_alarm_set_callback, +#endif /* CONFIG_RTC_ALARM */ }; -static struct rtc_rpi_pico_data rtc_data; - DEVICE_DT_INST_DEFINE(0, &rtc_rpi_pico_init, NULL, &rtc_data, NULL, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, &rtc_rpi_pico_driver_api); diff --git a/dts/arm/rpi_pico/rp2040.dtsi b/dts/arm/rpi_pico/rp2040.dtsi index 4635aabd7526a6..e04af802277018 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -404,7 +404,9 @@ compatible = "raspberrypi,pico-rtc"; reg = <0x4005c000 DT_SIZE_K(4)>; interrupts = <25 RPI_PICO_DEFAULT_IRQ_PRIORITY>; + interrupt-names = "rtc"; resets = <&reset RPI_PICO_RESETS_RESET_RTC>; + alarms-count = <1>; status = "disabled"; }; }; diff --git a/dts/bindings/rtc/raspberrypi,pico-rtc.yaml b/dts/bindings/rtc/raspberrypi,pico-rtc.yaml index b947a9fc6d60c6..cba7a708af8b36 100644 --- a/dts/bindings/rtc/raspberrypi,pico-rtc.yaml +++ b/dts/bindings/rtc/raspberrypi,pico-rtc.yaml @@ -5,7 +5,7 @@ description: RaspberryPi Pico RTC compatible: "raspberrypi,pico-rtc" -include: [rtc.yaml, reset-device.yaml] +include: [rtc-device.yaml, reset-device.yaml] properties: reg: diff --git a/tests/drivers/rtc/rtc_api/src/test_alarm.c b/tests/drivers/rtc/rtc_api/src/test_alarm.c index 4382208910852d..fcaf187dbf6a6d 100644 --- a/tests/drivers/rtc/rtc_api/src/test_alarm.c +++ b/tests/drivers/rtc/rtc_api/src/test_alarm.c @@ -55,21 +55,25 @@ ZTEST(rtc_api, test_alarm) zassert_ok(ret, "Failed to get supported alarm fields"); - int *fields[] = { - &alarm_time_set.tm_sec, &alarm_time_set.tm_min, &alarm_time_set.tm_hour, - &alarm_time_set.tm_mday, &alarm_time_set.tm_mon, &alarm_time_set.tm_year, - &alarm_time_set.tm_wday, &alarm_time_set.tm_yday, &alarm_time_set.tm_nsec}; + alarm_time_set = (struct rtc_time) { + .tm_sec = 70, + .tm_min = 70, + .tm_hour = 25, + .tm_mday = 35, + .tm_mon = 15, + .tm_year = 8000, + .tm_wday = 8, + .tm_yday = 370, + .tm_nsec = INT32_MAX, + }; uint16_t masks[] = {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_YEAR, RTC_ALARM_TIME_MASK_WEEKDAY, RTC_ALARM_TIME_MASK_YEARDAY, RTC_ALARM_TIME_MASK_NSEC}; - int bad_values[] = {70, 70, 25, 35, 15, 8000, 8, 370, INT32_MAX}; - - ARRAY_FOR_EACH(fields, j) + ARRAY_FOR_EACH(masks, j) { if (masks[j] & alarm_time_mask_supported) { - *fields[j] = bad_values[j]; ret = rtc_alarm_set_time(rtc, i, masks[j], &alarm_time_set); zassert_equal( -EINVAL, ret,