| /* |
| * 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 <time.h> |
| |
| #include <drivers/counter.h> |
| #include <kernel.h> |
| #include <zephyr/types.h> |
| #include <sys/notify.h> |
| |
| #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 <syscalls/maxim_ds3231.h> |
| |
| #endif /* ZEPHYR_INCLUDE_DRIVERS_RTC_DS3231_H_ */ |