/* * Copyright (c) 2019 Peter Bigot Consulting, LLC * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Real-time clock control based on the DS3231 counter API. * * The [Maxim * DS3231](https://www.maximintegrated.com/en/products/analog/real-time-clocks/DS3231.html) * is a high-precision real-time clock with temperature-compensated * crystal oscillator and support for configurable alarms. * * The core Zephyr API to this device is as a counter, with the * following limitations: * * counter_read() and counter_*_alarm() cannot be invoked from * interrupt context, as they require communication with the device * over an I2C bus. * * many other counter APIs, such as start/stop/set_top_value are not * supported as the clock is always running. * * two alarm channels are supported but are not equally capable: * channel 0 supports alarms at 1 s resolution, while channel 1 * supports alarms at 1 minute resolution. * * Most applications for this device will need to use the extended * functionality exposed by this header to access the real-time-clock * features. The majority of these functions must be invoked from * supervisor mode. */ #ifndef ZEPHYR_INCLUDE_DRIVERS_RTC_DS3231_H_ #define ZEPHYR_INCLUDE_DRIVERS_RTC_DS3231_H_ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** @brief Bit in ctrl or ctrl_stat associated with alarm 1. */ #define MAXIM_DS3231_ALARM1 BIT(0) /** @brief Bit in ctrl or ctrl_stat associated with alarm 2. */ #define MAXIM_DS3231_ALARM2 BIT(1) /* Constants corresponding to bits in the DS3231 control register at * 0x0E. * * See the datasheet for interpretation of these bits. */ /** @brief ctrl bit for alarm 1 interrupt enable. */ #define MAXIM_DS3231_REG_CTRL_A1IE MAXIM_DS3231_ALARM1 /** @brief ctrl bit for alarm 2 interrupt enable. */ #define MAXIM_DS3231_REG_CTRL_A2IE MAXIM_DS3231_ALARM2 /** @brief ctrl bit for ISQ functionality. * * When clear the ISW signal provides a square wave. When set the ISW * signal indicates alarm events. * * @note The driver expects to be able to control this bit. */ #define MAXIM_DS3231_REG_CTRL_INTCN BIT(2) /** @brief ctrl bit offset for square wave output frequency. * * @note The driver will control the content of this field. */ #define MAXIM_DS3231_REG_CTRL_RS_Pos 3 /** @brief ctrl mask to isolate RS bits. */ #define MAXIM_DS3231_REG_CTRL_RS_Msk (0x03 << MAXIM_DS3231_REG_CTRL_RS_Pos) /** @brief ctrl RS field value for 1 Hz square wave. */ #define MAXIM_DS3231_REG_CTRL_RS_1Hz 0x00 /** @brief ctrl RS field value for 1024 Hz square wave. */ #define MAXIM_DS3231_REG_CTRL_RS_1KiHz 0x01 /** @brief ctrl RS field value for 4096 Hz square wave. */ #define MAXIM_DS3231_REG_CTRL_RS_4KiHz 0x02 /** @brief ctrl RS field value for 8192 Hz square wave. */ #define MAXIM_DS3231_REG_CTRL_RS_8KiHz 0x03 /** @brief ctrl bit to write to trigger temperature conversion. */ #define MAXIM_DS3231_REG_CTRL_CONV BIT(5) /** @brief ctrl bit to write to enable square wave output in battery mode. */ #define MAXIM_DS3231_REG_CTRL_BBSQW BIT(6) /** @brief ctrl bit to write to disable the oscillator. */ #define MAXIM_DS3231_REG_CTRL_EOSCn BIT(7), /** @brief ctrl_stat bit indicating alarm1 has triggered. * * If an alarm callback handler is registered this bit is * cleared prior to invoking the callback with the flags * indicating which alarms are ready. */ #define MAXIM_DS3231_REG_STAT_A1F MAXIM_DS3231_ALARM1 /** @brief ctrl_stat bit indicating alarm2 has triggered. * * If an alarm callback handler is registered this bit is * cleared prior to invoking the callback with the flags * indicating which alarms are ready. */ #define MAXIM_DS3231_REG_STAT_A2F MAXIM_DS3231_ALARM2 /** @brief Flag indicating a temperature conversion is in progress. */ #define MAXIM_DS3231_REG_STAT_BSY BIT(2) /** @brief Set to enable 32 KiHz open drain signal. * * @note This is a control bit, though it is positioned within the * ctrl_stat register which otherwise contains status bits. */ #define MAXIM_DS3231_REG_STAT_EN32kHz BIT(3) /** @brief Flag indicating the oscillator has been off since last cleared. */ #define MAXIM_DS3231_REG_STAT_OSF BIT(7) /** @brief Control alarm behavior on match in seconds field. * * If clear the alarm fires only when the RTC seconds matches the * alarm seconds. * * If set the alarm seconds field is ignored and an alarm will be * triggered every second. The bits for IGNMN, IGNHR, and IGNDA must * all be set. * * This bit must be clear for the second alarm instance. * * Bit maps to A1M1 and is used in * maxim_ds3231_alarm_configuration::alarm_flags. */ #define MAXIM_DS3231_ALARM_FLAGS_IGNSE BIT(0) /** @brief Control alarm behavior on match in minutes field. * * If clear the alarm fires only when the RTC minutes matches the * alarm minutes. The bit for IGNSE must be clear. * * If set the alarm minutes field is ignored and alarms will be * triggered based on IGNSE. The bits for IGNHR and IGNDA must both be * set. * * Bit maps to A1M2 or A2M2 and is used in * maxim_ds3231_alarm_configuration::alarm_flags. */ #define MAXIM_DS3231_ALARM_FLAGS_IGNMN BIT(1) /** @brief Control alarm behavior on match in hours field. * * If clear the alarm fires only when the RTC hours matches the * alarm hours. The bits for IGNMN and IGNSE must be clear. * * If set the alarm hours field is ignored and alarms will be * triggered based on IGNMN and IGNSE. The bit for IGNDA must be set. * * Bit maps to A1M3 or A2M3 and is used in * maxim_ds3231_alarm_configuration::alarm_flags. */ #define MAXIM_DS3231_ALARM_FLAGS_IGNHR BIT(2) /** @brief Control alarm behavior on match in day/date field. * * If clear the alarm fires only when the RTC day/date matches the * alarm day/date, mediated by MAXIM_DS3231_ALARM_FLAGS_DAY. The bits * for IGNHR, IGNMN, and IGNSE must be clear * * If set the alarm day/date field is ignored and an alarm will be * triggered based on IGNHR, IGNMN, and IGNSE. * * Bit maps to A1M4 or A2M4 and is used in * maxim_ds3231_alarm_configuration::alarm_flags. */ #define MAXIM_DS3231_ALARM_FLAGS_IGNDA BIT(3) /** @brief Control match on day of week versus day of month * * Set the flag to match on day of week; clear it to match on day of * month. * * Bit maps to DY/DTn in corresponding * maxim_ds3231_alarm_configuration::alarm_flags. */ #define MAXIM_DS3231_ALARM_FLAGS_DOW BIT(4) /** @brief Indicates that the alarm should be disabled once it fires. * * Set the flag in the maxim_ds3231_alarm_configuration::alarm_flags * field to cause the alarm to be disabled when the interrupt fires, * prior to invoking the corresponding handler. * * Leave false to allow the alarm to remain enabled so it will fire * again on the next match. */ #define MAXIM_DS3231_ALARM_FLAGS_AUTODISABLE BIT(7) /** * @brief RTC DS3231 Driver-Specific API * @defgroup rtc_interface Real Time Clock interfaces * @ingroup io_interfaces * @{ */ /** @brief Signature for DS3231 alarm callbacks. * * The alarm callback is invoked from the system work queue thread. * At the point the callback is invoked the corresponding alarm flags * will have been cleared from the device status register. The * callback is permitted to invoke operations on the device. * * @param dev the device from which the callback originated * @param id the alarm id * @param syncclock the value from maxim_ds3231_read_syncclock() at the * time the alarm interrupt was processed. * @param user_data the corresponding parameter from * maxim_ds3231_alarm::user_data. */ typedef void (*maxim_ds3231_alarm_callback_handler_t)(const struct device *dev, uint8_t id, uint32_t syncclock, void *user_data); /** @brief Signature used to notify a user of the DS3231 that an * asynchronous operation has completed. * * Functions compatible with this type are subject to all the * constraints of #sys_notify_generic_callback. * * @param dev the DS3231 device pointer * * @param notify the notification structure provided in the call * * @param res the result of the operation. */ typedef void (*maxim_ds3231_notify_callback)(const struct device *dev, struct sys_notify *notify, int res); /** @brief Information defining the alarm configuration. * * DS3231 alarms can be set to fire at specific times or at the * rollover of minute, hour, day, or day of week. * * When an alarm is configured with a handler an interrupt will be * generated and the handler called from the system work queue. * * When an alarm is configured without a handler, or a persisted alarm * is present, alarms can be read using maxim_ds3231_check_alarms(). */ struct maxim_ds3231_alarm { /** @brief Time specification for an RTC alarm. * * Though specified as a UNIX time, the alarm parameters are * determined by converting to civil time and interpreting the * component hours, minutes, seconds, day-of-week, and * day-of-month fields, mediated by the corresponding #flags. * * The year and month are ignored, but be aware that gmtime() * determines day-of-week based on calendar date. Decoded * alarm times will fall within 1978-01 since 1978-01-01 * (first of month) was a Sunday (first of week). */ time_t time; /** @brief Handler to be invoked when alarms are signalled. * * If this is null the alarm will not be triggered by the * INTn/SQW GPIO. This is a "persisted" alarm from its role * in using the DS3231 to trigger a wake from deep sleep. The * application should use maxim_ds3231_check_alarms() to * determine whether such an alarm has been triggered. * * If this is not null the driver will monitor the ISW GPIO * for alarm signals and will invoke the handler with a * parameter carrying the value returned by * maxim_ds3231_check_alarms(). The corresponding status flags * will be cleared in the device before the handler is * invoked. * * The handler will be invoked from the system work queue. */ maxim_ds3231_alarm_callback_handler_t handler; /** @brief User-provided pointer passed to alarm callback. */ void *user_data; /** @brief Flags controlling configuration of the alarm alarm. * * See MAXIM_DS3231_ALARM_FLAGS_IGNSE and related constants. * * Note that as described the alarm mask fields require that * if a unit is not ignored, higher-precision units must also * not be ignored. For example, if match on hours is enabled, * match on minutes and seconds must also be enabled. Failure * to comply with this requirement will cause * maxim_ds3231_set_alarm() to return an error, leaving the * alarm configuration unchanged. */ uint8_t flags; }; /** @brief Register the RTC clock against system clocks. * * This captures the same instant in both the RTC time scale and a * stable system clock scale, allowing conversion between those * scales. */ struct maxim_ds3231_syncpoint { /** @brief Time from the DS3231. * * This maybe in UTC, TAI, or local offset depending on how * the RTC is maintained. */ struct timespec rtc; /** @brief Value of a local clock at the same instant as #rtc. * * This is captured from a stable monotonic system clock * running at between 1 kHz and 1 MHz, allowing for * microsecond to millisecond accuracy in synchronization. */ uint32_t syncclock; }; /** @brief Read the local synchronization clock. * * Synchronization aligns the DS3231 real-time clock with a stable * monotonic local clock which should have a frequency between 1 kHz * and 1 MHz and be itself synchronized with the primary system time * clock. The accuracy of the alignment and the maximum time between * synchronization updates is affected by the resolution of this * clock. * * On some systems the hardware clock from k_cycles_get_32() is * suitable, but on others that clock advances too quickly. The * frequency of the target-specific clock is provided by * maxim_ds3231_syncclock_frequency(). * * At this time the value is captured from `k_uptime_get_32()`; future * kernel extensions may make a higher-resolution clock available. * * @note This function is *isr-ok*. * * @param dev the DS3231 device pointer * * @return the current value of the synchronization clock. */ static inline uint32_t maxim_ds3231_read_syncclock(const struct device *dev) { return k_uptime_get_32(); } /** @brief Get the frequency of the synchronization clock. * * Provides the frequency of the clock used in maxim_ds3231_read_syncclock(). * * @param dev the DS3231 device pointer * * @return the frequency of the selected synchronization clock. */ static inline uint32_t maxim_ds3231_syncclock_frequency(const struct device *dev) { return 1000U; } /** * @brief Set and clear specific bits in the control register. * * @note This function assumes the device register cache is valid. It * will not read the register value, and it will write to the device * only if the value changes as a result of applying the set and clear * changes. * * @note Unlike maxim_ds3231_stat_update() the return value from this * function indicates the register value after changes were made. * That return value is cached for use in subsequent operations. * * @note This function is *supervisor*. * * @return the non-negative updated value of the register, or a * negative error code from an I2C transaction. */ int maxim_ds3231_ctrl_update(const struct device *dev, uint8_t set_bits, uint8_t clear_bits); /** * @brief Read the ctrl_stat register then set and clear bits in it. * * The content of the ctrl_stat register will be read, then the set * and clear bits applied and the result written back to the device * (regardless of whether there appears to be a change in value). * * OSF, A1F, and A2F will be written with 1s if the corresponding bits * do not appear in either @p set_bits or @p clear_bits. This ensures * that if any flag becomes set between the read and the write that * indicator will not be cleared. * * @note Unlike maxim_ds3231_ctrl_update() the return value from this * function indicates the register value before any changes were made. * * @note This function is *supervisor*. * * @param dev the DS3231 device pointer * * @param set_bits bits to be set when writing back. Setting bits * other than @ref MAXIM_DS3231_REG_STAT_EN32kHz will have no effect. * * @param clear_bits bits to be cleared when writing back. Include * the bits for the status flags you want to clear. * * @return the non-negative register value as originally read * (disregarding the effect of clears and sets), or a negative error * code from an I2C transaction. */ int maxim_ds3231_stat_update(const struct device *dev, uint8_t set_bits, uint8_t clear_bits); /** @brief Read a DS3231 alarm configuration. * * The alarm configuration data is read from the device and * reconstructed into the output parameter. * * @note This function is *supervisor*. * * @param dev the DS3231 device pointer. * * @param id the alarm index, which must be 0 (for the 1 s resolution * alarm) or 1 (for the 1 min resolution alarm). * * @param cfg a pointer to a structure into which the configured alarm * data will be stored. * * @return a non-negative value indicating successful conversion, or a * negative error code from an I2C transaction or invalid parameter. */ int maxim_ds3231_get_alarm(const struct device *dev, uint8_t id, struct maxim_ds3231_alarm *cfg); /** @brief Configure a DS3231 alarm. * * The alarm configuration is validated and stored into the device. * * To cancel an alarm use counter_cancel_channel_alarm(). * * @note This function is *supervisor*. * * @param dev the DS3231 device pointer. * * @param id 0 Analog to counter index. @c ALARM1 is 0 and has 1 s * resolution, @c ALARM2 is 1 and has 1 minute resolution. * * @param cfg a pointer to the desired alarm configuration. Both * alarms are configured; if only one is to change the application * must supply the existing configuration for the other. * * @return a non-negative value on success, or a negative error code * from an I2C transaction or an invalid parameter. */ int maxim_ds3231_set_alarm(const struct device *dev, uint8_t id, const struct maxim_ds3231_alarm *cfg); /** @brief Synchronize the RTC against the local clock. * * The RTC advances one tick per second with no access to sub-second * precision. Synchronizing clocks at sub-second resolution requires * enabling a 1pps signal then capturing the system clocks in a GPIO * callback. This function provides that operation. * * Synchronization is performed in asynchronously, and may take as * long as 1 s to complete; notification of completion is provided * through the @p notify parameter. * * Applications should use maxim_ds3231_get_syncpoint() to retrieve the * synchronization data collected by this operation. * * @note This function is *supervisor*. * * @param dev the DS3231 device pointer. * * @param notify pointer to the object used to specify asynchronous * function behavior and store completion information. * * @retval non-negative on success * @retval -EBUSY if a synchronization or set is currently in progress * @retval -EINVAL if notify is not provided * @retval -ENOTSUP if the required interrupt is not configured */ int maxim_ds3231_synchronize(const struct device *dev, struct sys_notify *notify); /** @brief Request to update the synchronization point. * * This is a variant of maxim_ds3231_synchronize() for use from user * threads. * * @param dev the DS3231 device pointer. * * @param signal pointer to a valid and ready-to-be-signalled * k_poll_signal. May be NULL to request a synchronization point be * collected without notifying when it has been updated. * * @retval non-negative on success * @retval -EBUSY if a synchronization or set is currently in progress * @retval -ENOTSUP if the required interrupt is not configured */ __syscall int maxim_ds3231_req_syncpoint(const struct device *dev, struct k_poll_signal *signal); /** @brief Retrieve the most recent synchronization point. * * This function returns the synchronization data last captured using * maxim_ds3231_synchronize(). * * @param dev the DS3231 device pointer. * * @param syncpoint where to store the synchronization data. * * @retval non-negative on success * @retval -ENOENT if no syncpoint has been captured */ __syscall int maxim_ds3231_get_syncpoint(const struct device *dev, struct maxim_ds3231_syncpoint *syncpoint); /** @brief Set the RTC to a time consistent with the provided * synchronization. * * The RTC advances one tick per second with no access to sub-second * precision, and setting the clock resets the internal countdown * chain. This function implements the magic necessary to set the * clock while retaining as much sub-second accuracy as possible. It * requires a synchronization point that pairs sub-second resolution * civil time with a local synchronization clock captured at the same * instant. The set operation may take as long as 1 second to * complete; notification of completion is provided through the @p * notify parameter. * * @note This function is *supervisor*. * * @param dev the DS3231 device pointer. * * @param syncpoint the structure providing the synchronization point. * * @param notify pointer to the object used to specify asynchronous * function behavior and store completion information. * * @retval non-negative on success * @retval -EINVAL if syncpoint or notify are null * @retval -ENOTSUP if the required interrupt signal is not configured * @retval -EBUSY if a synchronization or set is currently in progress */ int maxim_ds3231_set(const struct device *dev, const struct maxim_ds3231_syncpoint *syncpoint, struct sys_notify *notify); /** @brief Check for and clear flags indicating that an alarm has * fired. * * Returns a mask indicating alarms that are marked as having fired, * and clears from stat the flags that it found set. Alarms that have * been configured with a callback are not represented in the return * value. * * This API may be used when a persistent alarm has been programmed. * * @note This function is *supervisor*. * * @param dev the DS3231 device pointer. * * @return a non-negative value that may have MAXIM_DS3231_ALARM1 and/or * MAXIM_DS3231_ALARM2 set, or a negative error code. */ int maxim_ds3231_check_alarms(const struct device *dev); /** * @} */ #ifdef __cplusplus } #endif /* @todo this should be syscalls/drivers/rtc/maxim_ds3231.h */ #include #endif /* ZEPHYR_INCLUDE_DRIVERS_RTC_DS3231_H_ */