Skip to content

Commit

Permalink
drivers: rtc: rpi_pico: Add alarm support to RPi Pico RTC driver
Browse files Browse the repository at this point in the history
This adds support for the alarm functionality of the RPi Pico RTC.

Signed-off-by: Andrew Featherstone <[email protected]>
  • Loading branch information
ajf58 committed Feb 25, 2024
1 parent 1d0c23c commit 9da61ef
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 4 deletions.
216 changes: 213 additions & 3 deletions drivers/rtc/rtc_rpi_pico.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
#include <zephyr/logging/log.h>
#include <zephyr/spinlock.h>

#include <hardware/irq.h>
#include <hardware/rtc.h>
#include <hardware/regs/rtc.h>

#include "rtc_utils.h"

#define DT_DRV_COMPAT raspberrypi_pico_rtc

Expand All @@ -20,22 +24,72 @@

/* struct tm start time: 1st, Jan, 1900 */
#define TM_YEAR_REF 1900

#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;
}
Expand Down Expand Up @@ -100,12 +154,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_tm(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);
2 changes: 2 additions & 0 deletions dts/arm/rpi_pico/rp2040.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -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";
};
};
Expand Down
2 changes: 1 addition & 1 deletion dts/bindings/rtc/raspberrypi,pico-rtc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 9da61ef

Please sign in to comment.