blob: 2c97e255653ab608b3bfde03f2968aeb5440a11a [file] [log] [blame]
/*
* 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 <zephyr/drivers/counter.h>
#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <zephyr/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_ */