Skip to content

Commit f1b6403

Browse files
committed
rtc: add subsecond support to stm32 driver
Add support for RTC_ALARM_TIME_MASK_NSEC on STM32 series that implement the RTC_ALRMASSR/RTC_ALRMBSSR subsecond alarm registers. Infer whether NSEC matching was requested from the programmed MASKSS field in the alarm subsecond register to avoid storing additional alarm state. Signed-off-by: Jon Ringle <jringle@gridpoint.com>
1 parent 351db5b commit f1b6403

1 file changed

Lines changed: 175 additions & 6 deletions

File tree

drivers/rtc/rtc_ll_stm32.c

Lines changed: 175 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ LOG_MODULE_REGISTER(rtc_stm32, CONFIG_RTC_LOG_LEVEL);
5353
#define HW_SUBSECOND_SUPPORT 1
5454
#endif
5555

56+
/* STM32 devices that support subsecond alarms (require RTC_ALRMASSR/RTC_ALRMBSSR registers) */
57+
#if defined(CONFIG_SOC_SERIES_STM32F1X) || defined(CONFIG_SOC_SERIES_STM32F2X) \
58+
|| defined(CONFIG_SOC_SERIES_STM32L1X) || defined(CONFIG_SOC_SERIES_STM32C0X)
59+
/* STM32F1, STM32F2, STM32L1, and STM32C0 series do not support subsecond alarms */
60+
#define HW_SUBSECOND_ALARM_SUPPORT (0)
61+
#else
62+
/* STM32F3, STM32F4, STM32L0, STM32L4, STM32H7, STM32G0, STM32G4, STM32WL5, STM32WB, STM32U5 support subsecond alarms */
63+
#define HW_SUBSECOND_ALARM_SUPPORT (1)
64+
#endif
65+
5666
/* RTC start time: 1st, Jan, 2000 */
5767
#define RTC_YEAR_REF 2000
5868
/* struct tm start time: 1st, Jan, 1900 */
@@ -94,7 +104,8 @@ LOG_MODULE_REGISTER(rtc_stm32, CONFIG_RTC_LOG_LEVEL);
94104
#define RTC_STM32_SUPPORTED_ALARM_FIELDS \
95105
(RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE \
96106
| RTC_ALARM_TIME_MASK_HOUR | RTC_ALARM_TIME_MASK_WEEKDAY \
97-
| RTC_ALARM_TIME_MASK_MONTHDAY)
107+
| RTC_ALARM_TIME_MASK_MONTHDAY \
108+
| (HW_SUBSECOND_ALARM_SUPPORT ? RTC_ALARM_TIME_MASK_NSEC : 0))
98109

99110
#define RTC_STM32_EXTI_LINE_NUM DT_INST_PROP_OR(0, alrm_exti_line, 0)
100111

@@ -701,15 +712,56 @@ static int rtc_stm32_get_time(const struct device *dev, struct rtc_time *timeptr
701712
return 0;
702713
}
703714

715+
#if HW_SUBSECOND_ALARM_SUPPORT
716+
/* Subsecond conversion functions */
717+
718+
/**
719+
* @brief Convert nanoseconds to RTC subsecond register value
720+
* @param nsec Nanoseconds (0-999999999)
721+
* @param sync_prescaler RTC sync prescaler value
722+
* @return RTC subsecond register value
723+
*/
724+
static inline uint32_t rtc_stm32_nsec_to_subsecond(uint32_t nsec, uint32_t sync_prescaler)
725+
{
726+
/* Convert nanoseconds to RTC subsecond register value
727+
* Formula: rtc_subsecond = sync_prescaler - (nsec * (sync_prescaler + 1)) / 1000000000
728+
*/
729+
uint64_t temp = (uint64_t)nsec * (sync_prescaler + 1);
730+
return sync_prescaler - (uint32_t)(temp / 1000000000L);
731+
}
732+
733+
/**
734+
* @brief Convert RTC subsecond register value to nanoseconds
735+
* @param rtc_subsecond RTC subsecond register value
736+
* @param sync_prescaler RTC sync prescaler value
737+
* @return Nanoseconds (0-999999999)
738+
*/
739+
static inline uint32_t rtc_stm32_subsecond_to_nsec(uint32_t rtc_subsecond, uint32_t sync_prescaler)
740+
{
741+
/* Convert RTC subsecond register value to nanoseconds
742+
* Formula: nsec = ((sync_prescaler - rtc_subsecond) * 1000000000) / (sync_prescaler + 1)
743+
*/
744+
uint64_t temp = ((uint64_t)(sync_prescaler - rtc_subsecond)) * 1000000000L;
745+
return (uint32_t)(temp / (sync_prescaler + 1));
746+
}
747+
#endif /* HW_SUBSECOND_ALARM_SUPPORT */
748+
704749
#ifdef STM32_RTC_ALARM_ENABLED
705-
static void rtc_stm32_alarm_get_alrm_time(uint16_t id, struct rtc_time *timeptr)
750+
static inline void rtc_stm32_get_ll_alrm_time(uint16_t id, struct rtc_time *timeptr,
751+
uint32_t sync_prescaler)
706752
{
707753
if (id == RTC_STM32_ALRM_A) {
708754
timeptr->tm_sec = bcd2bin(LL_RTC_ALMA_GetSecond(RTC));
709755
timeptr->tm_min = bcd2bin(LL_RTC_ALMA_GetMinute(RTC));
710756
timeptr->tm_hour = bcd2bin(LL_RTC_ALMA_GetHour(RTC));
711757
timeptr->tm_wday = bcd2bin(LL_RTC_ALMA_GetWeekDay(RTC));
712758
timeptr->tm_mday = bcd2bin(LL_RTC_ALMA_GetDay(RTC));
759+
#if HW_SUBSECOND_ALARM_SUPPORT
760+
uint32_t rtc_subsecond = LL_RTC_ALMA_GetSubSecond(RTC);
761+
timeptr->tm_nsec = rtc_stm32_subsecond_to_nsec(rtc_subsecond, sync_prescaler);
762+
#else
763+
timeptr->tm_nsec = 0;
764+
#endif
713765
return;
714766
}
715767
#if RTC_STM32_ALARMS_COUNT > 1
@@ -719,6 +771,12 @@ static void rtc_stm32_alarm_get_alrm_time(uint16_t id, struct rtc_time *timeptr)
719771
timeptr->tm_hour = bcd2bin(LL_RTC_ALMB_GetHour(RTC));
720772
timeptr->tm_wday = bcd2bin(LL_RTC_ALMB_GetWeekDay(RTC));
721773
timeptr->tm_mday = bcd2bin(LL_RTC_ALMB_GetDay(RTC));
774+
#if HW_SUBSECOND_ALARM_SUPPORT
775+
uint32_t rtc_subsecond = LL_RTC_ALMB_GetSubSecond(RTC);
776+
timeptr->tm_nsec = rtc_stm32_subsecond_to_nsec(rtc_subsecond, sync_prescaler);
777+
#else
778+
timeptr->tm_nsec = 0;
779+
#endif
722780
}
723781
#endif /* RTC_STM32_ALARMS_COUNT > 1 */
724782
}
@@ -773,6 +831,90 @@ static inline uint16_t rtc_stm32_alarm_get_alrm_mask(uint16_t id)
773831
return zephyr_alarm_mask;
774832
}
775833

834+
#if HW_SUBSECOND_ALARM_SUPPORT
835+
/* Subsecond register access functions */
836+
837+
/**
838+
* @brief Read RTC subsecond register (RTC_SSR)
839+
* @param rtc_subsecond Pointer to store the subsecond value
840+
* @return 0 on success, negative error code on failure
841+
*/
842+
static inline int rtc_stm32_read_subsecond(uint32_t *rtc_subsecond)
843+
{
844+
if (rtc_subsecond == NULL) {
845+
return -EINVAL;
846+
}
847+
848+
*rtc_subsecond = LL_RTC_TIME_GetSubSecond(RTC);
849+
return 0;
850+
}
851+
852+
/**
853+
* @brief Read alarm subsecond register
854+
* @param id Alarm ID (RTC_STM32_ALRM_A or RTC_STM32_ALRM_B)
855+
* @param rtc_subsecond Pointer to store the subsecond value
856+
* @return 0 on success, negative error code on failure
857+
*/
858+
static inline int rtc_stm32_read_alarm_subsecond(uint16_t id, uint32_t *rtc_subsecond)
859+
{
860+
if (rtc_subsecond == NULL) {
861+
return -EINVAL;
862+
}
863+
864+
if (id == RTC_STM32_ALRM_A) {
865+
*rtc_subsecond = LL_RTC_ALMA_GetSubSecond(RTC);
866+
} else if (id == RTC_STM32_ALRM_B) {
867+
*rtc_subsecond = LL_RTC_ALMB_GetSubSecond(RTC);
868+
} else {
869+
return -EINVAL;
870+
}
871+
872+
return 0;
873+
}
874+
875+
static inline uint32_t rtc_stm32_alarm_get_subsecond_mask(uint16_t id)
876+
{
877+
uint32_t reg;
878+
879+
if (id == RTC_STM32_ALRM_A) {
880+
reg = RTC->ALRMASSR;
881+
} else if (id == RTC_STM32_ALRM_B) {
882+
reg = RTC->ALRMBSSR;
883+
} else {
884+
return 0;
885+
}
886+
887+
/*
888+
* MASKSS bitfield position/width differs across STM32 series (4..6 bits),
889+
* but is consistently located starting at bit 24.
890+
*/
891+
return (reg >> 24) & 0x3F;
892+
}
893+
894+
/**
895+
* @brief Write alarm subsecond register
896+
* @param id Alarm ID (RTC_STM32_ALRM_A or RTC_STM32_ALRM_B)
897+
* @param rtc_subsecond Subsecond value to write
898+
* @return 0 on success, negative error code on failure
899+
*/
900+
static inline int rtc_stm32_write_alarm_subsecond(uint16_t id, uint32_t rtc_subsecond)
901+
{
902+
if (id == RTC_STM32_ALRM_A) {
903+
LL_RTC_ALMA_SetSubSecond(RTC, rtc_subsecond);
904+
/* Compare SS[14:0] (15 bits) */
905+
LL_RTC_ALMA_SetSubSecondMask(RTC, 15);
906+
} else if (id == RTC_STM32_ALRM_B) {
907+
LL_RTC_ALMB_SetSubSecond(RTC, rtc_subsecond);
908+
/* Compare SS[14:0] (15 bits) */
909+
LL_RTC_ALMB_SetSubSecondMask(RTC, 15);
910+
} else {
911+
return -EINVAL;
912+
}
913+
914+
return 0;
915+
}
916+
#endif /* HW_SUBSECOND_ALARM_SUPPORT */
917+
776918
static int rtc_stm32_alarm_get_supported_fields(const struct device *dev, uint16_t id,
777919
uint16_t *mask)
778920
{
@@ -795,6 +937,7 @@ static int rtc_stm32_alarm_get_time(const struct device *dev, uint16_t id, uint1
795937
struct rtc_time *timeptr)
796938
{
797939
struct rtc_stm32_data *data = dev->data;
940+
const struct rtc_stm32_config *cfg = dev->config;
798941
int err = 0;
799942

800943
if ((mask == NULL) || (timeptr == NULL)) {
@@ -812,12 +955,20 @@ static int rtc_stm32_alarm_get_time(const struct device *dev, uint16_t id, uint1
812955
}
813956

814957
memset(timeptr, -1, sizeof(struct rtc_time));
815-
rtc_stm32_alarm_get_alrm_time(id, timeptr);
958+
rtc_stm32_get_ll_alrm_time(id, timeptr, cfg->sync_prescaler);
816959
*mask = rtc_stm32_alarm_get_alrm_mask(id);
960+
#if HW_SUBSECOND_ALARM_SUPPORT
961+
if (rtc_stm32_alarm_get_subsecond_mask(id) != 0) {
962+
*mask |= RTC_ALARM_TIME_MASK_NSEC;
963+
} else {
964+
timeptr->tm_nsec = 0;
965+
}
966+
#else
967+
timeptr->tm_nsec = 0;
968+
#endif
817969

818-
LOG_DBG("get alarm: mday = %d, wday = %d, hour = %d, min = %d, sec = %d, "
819-
"mask = 0x%04x", timeptr->tm_mday, timeptr->tm_wday, timeptr->tm_hour,
820-
timeptr->tm_min, timeptr->tm_sec, *mask);
970+
LOG_DBG("get alarm: %d/%d %d:%d:%d.%d mask=0x%04x", timeptr->tm_mday, timeptr->tm_wday,
971+
timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, timeptr->tm_nsec, *mask);
821972

822973
unlock:
823974
k_spin_unlock(&data->lock, key);
@@ -829,6 +980,7 @@ static int rtc_stm32_alarm_set_time(const struct device *dev, uint16_t id, uint1
829980
const struct rtc_time *timeptr)
830981
{
831982
struct rtc_stm32_data *data = dev->data;
983+
const struct rtc_stm32_config *cfg = dev->config;
832984
struct rtc_stm32_alrm *p_rtc_alrm;
833985
int err = 0;
834986

@@ -917,6 +1069,23 @@ static int rtc_stm32_alarm_set_time(const struct device *dev, uint16_t id, uint1
9171069
/* Disable the write protection for RTC registers */
9181070
LL_RTC_DisableWriteProtection(RTC);
9191071

1072+
#if HW_SUBSECOND_ALARM_SUPPORT
1073+
/* Handle subsecond alarm setting if requested */
1074+
if (mask & RTC_ALARM_TIME_MASK_NSEC) {
1075+
uint32_t rtc_subsecond = rtc_stm32_nsec_to_subsecond(timeptr->tm_nsec, cfg->sync_prescaler);
1076+
rtc_stm32_write_alarm_subsecond(id, rtc_subsecond);
1077+
} else {
1078+
/* Disable subsecond comparison */
1079+
if (id == RTC_STM32_ALRM_A) {
1080+
LL_RTC_ALMA_SetSubSecond(RTC, 0);
1081+
LL_RTC_ALMA_SetSubSecondMask(RTC, 0);
1082+
} else if (id == RTC_STM32_ALRM_B) {
1083+
LL_RTC_ALMB_SetSubSecond(RTC, 0);
1084+
LL_RTC_ALMB_SetSubSecondMask(RTC, 0);
1085+
}
1086+
}
1087+
#endif /* HW_SUBSECOND_ALARM_SUPPORT */
1088+
9201089
/* Enable Alarm */
9211090
rtc_stm32_enable_alarm(RTC, id);
9221091
/* Clear Alarm flag */

0 commit comments

Comments
 (0)