drivers: rtc: rpi_pico: Add alarm support to RPi Pico RTC driver
This adds support for the alarm functionality of the RPi Pico RTC. Signed-off-by: Andrew Featherstone <andrew.featherstone@gmail.com>
This commit is contained in:
parent
df8c89b528
commit
1c50ba489f
4 changed files with 228 additions and 11 deletions
|
@ -11,7 +11,11 @@
|
||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
#include <zephyr/spinlock.h>
|
#include <zephyr/spinlock.h>
|
||||||
|
|
||||||
|
#include <hardware/irq.h>
|
||||||
#include <hardware/rtc.h>
|
#include <hardware/rtc.h>
|
||||||
|
#include <hardware/regs/rtc.h>
|
||||||
|
|
||||||
|
#include "rtc_utils.h"
|
||||||
|
|
||||||
#define DT_DRV_COMPAT raspberrypi_pico_rtc
|
#define DT_DRV_COMPAT raspberrypi_pico_rtc
|
||||||
|
|
||||||
|
@ -22,21 +26,72 @@
|
||||||
#define TM_YEAR_REF 1900
|
#define TM_YEAR_REF 1900
|
||||||
/* See section 4.8.1 of the RP2040 datasheet. */
|
/* See section 4.8.1 of the RP2040 datasheet. */
|
||||||
#define RP2040_RTC_YEAR_MAX 4095
|
#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 rtc_rpi_pico_data {
|
||||||
struct k_spinlock lock;
|
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);
|
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)
|
static int rtc_rpi_pico_init(const struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
#ifdef CONFIG_RTC_ALARM
|
||||||
|
struct rtc_rpi_pico_data *data = dev->data;
|
||||||
|
#endif
|
||||||
|
|
||||||
ret = clock_control_on(CLK_DRV, CLK_ID);
|
ret = clock_control_on(CLK_DRV, CLK_ID);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
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();
|
rtc_init();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -101,12 +156,168 @@ static int rtc_rpi_pico_get_time(const struct device *dev, struct rtc_time *time
|
||||||
return err;
|
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 = {
|
static const struct rtc_driver_api rtc_rpi_pico_driver_api = {
|
||||||
.set_time = rtc_rpi_pico_set_time,
|
.set_time = rtc_rpi_pico_set_time,
|
||||||
.get_time = rtc_rpi_pico_get_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,
|
DEVICE_DT_INST_DEFINE(0, &rtc_rpi_pico_init, NULL, &rtc_data, NULL, POST_KERNEL,
|
||||||
CONFIG_RTC_INIT_PRIORITY, &rtc_rpi_pico_driver_api);
|
CONFIG_RTC_INIT_PRIORITY, &rtc_rpi_pico_driver_api);
|
||||||
|
|
|
@ -404,7 +404,9 @@
|
||||||
compatible = "raspberrypi,pico-rtc";
|
compatible = "raspberrypi,pico-rtc";
|
||||||
reg = <0x4005c000 DT_SIZE_K(4)>;
|
reg = <0x4005c000 DT_SIZE_K(4)>;
|
||||||
interrupts = <25 RPI_PICO_DEFAULT_IRQ_PRIORITY>;
|
interrupts = <25 RPI_PICO_DEFAULT_IRQ_PRIORITY>;
|
||||||
|
interrupt-names = "rtc";
|
||||||
resets = <&reset RPI_PICO_RESETS_RESET_RTC>;
|
resets = <&reset RPI_PICO_RESETS_RESET_RTC>;
|
||||||
|
alarms-count = <1>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ description: RaspberryPi Pico RTC
|
||||||
|
|
||||||
compatible: "raspberrypi,pico-rtc"
|
compatible: "raspberrypi,pico-rtc"
|
||||||
|
|
||||||
include: [rtc.yaml, reset-device.yaml]
|
include: [rtc-device.yaml, reset-device.yaml]
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
reg:
|
reg:
|
||||||
|
|
|
@ -55,21 +55,25 @@ ZTEST(rtc_api, test_alarm)
|
||||||
|
|
||||||
zassert_ok(ret, "Failed to get supported alarm fields");
|
zassert_ok(ret, "Failed to get supported alarm fields");
|
||||||
|
|
||||||
int *fields[] = {
|
alarm_time_set = (struct rtc_time) {
|
||||||
&alarm_time_set.tm_sec, &alarm_time_set.tm_min, &alarm_time_set.tm_hour,
|
.tm_sec = 70,
|
||||||
&alarm_time_set.tm_mday, &alarm_time_set.tm_mon, &alarm_time_set.tm_year,
|
.tm_min = 70,
|
||||||
&alarm_time_set.tm_wday, &alarm_time_set.tm_yday, &alarm_time_set.tm_nsec};
|
.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,
|
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_HOUR, RTC_ALARM_TIME_MASK_MONTHDAY,
|
||||||
RTC_ALARM_TIME_MASK_MONTH, RTC_ALARM_TIME_MASK_YEAR,
|
RTC_ALARM_TIME_MASK_MONTH, RTC_ALARM_TIME_MASK_YEAR,
|
||||||
RTC_ALARM_TIME_MASK_WEEKDAY, RTC_ALARM_TIME_MASK_YEARDAY,
|
RTC_ALARM_TIME_MASK_WEEKDAY, RTC_ALARM_TIME_MASK_YEARDAY,
|
||||||
RTC_ALARM_TIME_MASK_NSEC};
|
RTC_ALARM_TIME_MASK_NSEC};
|
||||||
int bad_values[] = {70, 70, 25, 35, 15, 8000, 8, 370, INT32_MAX};
|
ARRAY_FOR_EACH(masks, j)
|
||||||
|
|
||||||
ARRAY_FOR_EACH(fields, j)
|
|
||||||
{
|
{
|
||||||
if (masks[j] & alarm_time_mask_supported) {
|
if (masks[j] & alarm_time_mask_supported) {
|
||||||
*fields[j] = bad_values[j];
|
|
||||||
ret = rtc_alarm_set_time(rtc, i, masks[j], &alarm_time_set);
|
ret = rtc_alarm_set_time(rtc, i, masks[j], &alarm_time_set);
|
||||||
zassert_equal(
|
zassert_equal(
|
||||||
-EINVAL, ret,
|
-EINVAL, ret,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue