diff --git a/drivers/rtc/rtc_rpi_pico.c b/drivers/rtc/rtc_rpi_pico.c index 8c7075f3bc82f5e..3efd1859ced2aac 100644 --- a/drivers/rtc/rtc_rpi_pico.c +++ b/drivers/rtc/rtc_rpi_pico.c @@ -10,21 +10,70 @@ #include #include +#include #include +#include #define DT_DRV_COMPAT raspberrypi_pico_rtc /* struct tm start time: 1st, Jan, 1900 */ #define TM_YEAR_REF 1900 - +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 { 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_key_t key = k_spin_lock(&data->lock); + callback = data->alarm_callback; + user_data = data->alarm_user_data; + k_spin_unlock(&data->lock, key); + + 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) { + struct rtc_rpi_pico_data *data = dev->data; + +#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; } @@ -88,12 +137,162 @@ 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) +{ + 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; + + 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); + + // Only add to setup if it isn't -1 + rtc_hw->irq_setup_0 = + (mask & RTC_ALARM_TIME_MASK_YEAR + ? (((uint32_t)alarm->tm_year + TM_YEAR_REF) << RTC_IRQ_SETUP_0_YEAR_LSB) + : 0) | + (mask & RTC_ALARM_TIME_MASK_MONTH + ? (((uint32_t)alarm->tm_mon) << RTC_IRQ_SETUP_0_MONTH_LSB) + : 0) | + (mask & RTC_ALARM_TIME_MASK_MONTHDAY + ? (((uint32_t)alarm->tm_mday + 1) << RTC_IRQ_SETUP_0_DAY_LSB) + : 0); + rtc_hw->irq_setup_1 = (mask & RTC_ALARM_TIME_MASK_WEEKDAY + ? (((uint32_t)alarm->tm_wday) << RTC_IRQ_SETUP_1_DOTW_LSB) + : 0) | + (mask & RTC_ALARM_TIME_MASK_HOUR + ? (((uint32_t)alarm->tm_hour) << RTC_IRQ_SETUP_1_HOUR_LSB) + : 0) | + (mask & RTC_ALARM_TIME_MASK_MINUTE + ? (((uint32_t)alarm->tm_min) << RTC_IRQ_SETUP_1_MIN_LSB) + : 0) | + (mask & RTC_ALARM_TIME_MASK_SECOND + ? (((uint32_t)alarm->tm_sec) << RTC_IRQ_SETUP_1_SEC_LSB) + : 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); + } + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MONTH_ENA_BITS); + } + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_DAY_ENA_BITS); + } + if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { + hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_DOTW_ENA_BITS); + } + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_HOUR_ENA_BITS); + } + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_MIN_ENA_BITS); + } + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_SEC_ENA_BITS); + } + 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_key_t key = k_spin_lock(&data->lock); + *timeptr = data->alarm_time; + *mask = data->alarm_mask; + k_spin_unlock(&data->lock, key); + + 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_key_t key = k_spin_lock(&data->lock); + data->alarm_callback = callback; + data->alarm_user_data = user_data; + if ((callback == NULL) && (user_data == NULL)) { + rtc_disable_alarm(); + } + k_spin_unlock(&data->lock, key); + + 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 6e1a3eb1b5862d4..ad3ba181f4ad5f7 100644 --- a/dts/arm/rpi_pico/rp2040.dtsi +++ b/dts/arm/rpi_pico/rp2040.dtsi @@ -246,7 +246,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 b41532cb2b0c76f..aa37f25bda9290b 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: