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 Dec 24, 2023
1 parent f8f5d85 commit 897f8e5
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 4 deletions.
211 changes: 208 additions & 3 deletions drivers/rtc/rtc_rpi_pico.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#include <zephyr/logging/log.h>
#include <zephyr/spinlock.h>

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

#define DT_DRV_COMPAT raspberrypi_pico_rtc

Expand All @@ -20,22 +22,73 @@

/* 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 @@ -99,12 +152,164 @@ 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

Check failure on line 185 in drivers/rtc/rtc_rpi_pico.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

C99_COMMENTS

drivers/rtc/rtc_rpi_pico.c:185 do not use C99 // comments
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

Check failure on line 209 in drivers/rtc/rtc_rpi_pico.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

C99_COMMENTS

drivers/rtc/rtc_rpi_pico.c:209 do not use C99 // comments
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

Check failure on line 235 in drivers/rtc/rtc_rpi_pico.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

C99_COMMENTS

drivers/rtc/rtc_rpi_pico.c:235 do not use C99 // comments
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 897f8e5

Please sign in to comment.