drivers: rtc: maxim,ds3231: RTC driver
This is a squash of all the groundwork needed to get a functioning driver for the DS3231 with the RTC API. Signed-off-by: Gergo Vari <work@gergovari.com>
This commit is contained in:
parent
9d0627a4bc
commit
2759adaf1a
@ -20,3 +20,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801 mfd_ite_it8801.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_ITE_IT8801_ALTCTRL mfd_it8801_altctrl.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_AW9523B mfd_aw9523b.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_DS3231 mfd_ds3231.c)
|
||||
|
||||
@ -23,6 +23,7 @@ source "drivers/mfd/Kconfig.adp5585"
|
||||
source "drivers/mfd/Kconfig.axp192"
|
||||
source "drivers/mfd/Kconfig.aw9523b"
|
||||
source "drivers/mfd/Kconfig.bd8lb600fs"
|
||||
source "drivers/mfd/Kconfig.ds3231"
|
||||
source "drivers/mfd/Kconfig.max20335"
|
||||
source "drivers/mfd/Kconfig.max31790"
|
||||
source "drivers/mfd/Kconfig.nct38xx"
|
||||
|
||||
10
drivers/mfd/Kconfig.ds3231
Normal file
10
drivers/mfd/Kconfig.ds3231
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config MFD_DS3231
|
||||
bool "DS3231 multi-function device driver"
|
||||
default y
|
||||
depends on DT_HAS_MAXIM_DS3231_MFD_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable the Maxim DS3231 multi-function device driver
|
||||
77
drivers/mfd/mfd_ds3231.c
Normal file
77
drivers/mfd/mfd_ds3231.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/mfd/ds3231.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(mfd_ds3231, CONFIG_MFD_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT maxim_ds3231_mfd
|
||||
|
||||
struct mfd_ds3231_data {
|
||||
struct k_sem lock;
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
struct mfd_ds3231_conf {
|
||||
struct i2c_dt_spec i2c_bus;
|
||||
};
|
||||
|
||||
int mfd_ds3231_i2c_get_registers(const struct device *dev, uint8_t start_reg, uint8_t *buf,
|
||||
const size_t buf_size)
|
||||
{
|
||||
struct mfd_ds3231_data *data = dev->data;
|
||||
const struct mfd_ds3231_conf *config = dev->config;
|
||||
|
||||
/* FIXME: bad start_reg/buf_size values break i2c for that run */
|
||||
|
||||
(void)k_sem_take(&data->lock, K_FOREVER);
|
||||
int err = i2c_burst_read_dt(&config->i2c_bus, start_reg, buf, buf_size);
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mfd_ds3231_i2c_set_registers(const struct device *dev, uint8_t start_reg, const uint8_t *buf,
|
||||
const size_t buf_size)
|
||||
{
|
||||
struct mfd_ds3231_data *data = dev->data;
|
||||
const struct mfd_ds3231_conf *config = dev->config;
|
||||
|
||||
(void)k_sem_take(&data->lock, K_FOREVER);
|
||||
int err = i2c_burst_write_dt(&config->i2c_bus, start_reg, buf, buf_size);
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mfd_ds3231_init(const struct device *dev)
|
||||
{
|
||||
struct mfd_ds3231_data *data = dev->data;
|
||||
const struct mfd_ds3231_conf *config = (struct mfd_ds3231_conf *)(dev->config);
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
if (!i2c_is_ready_dt(&(config->i2c_bus))) {
|
||||
LOG_ERR("I2C bus not ready.");
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MFD_DS3231_DEFINE(inst) \
|
||||
static const struct mfd_ds3231_conf config##inst = {.i2c_bus = \
|
||||
I2C_DT_SPEC_INST_GET(inst)}; \
|
||||
static struct mfd_ds3231_data data##inst; \
|
||||
DEVICE_DT_INST_DEFINE(inst, &mfd_ds3231_init, NULL, &data##inst, &config##inst, \
|
||||
POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MFD_DS3231_DEFINE)
|
||||
@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_RTC_RV8263 rtc_rv8263.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RTC_AM1805 rtc_am1805.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RTC_AMBIQ rtc_ambiq.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RTC_DS1307 rtc_ds1307.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RTC_DS3231 rtc_ds3231.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RTC_EMUL rtc_emul.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RTC_INFINEON_CAT1 rtc_ifx_cat1.c)
|
||||
|
||||
@ -44,6 +44,7 @@ config RTC_SHELL
|
||||
source "drivers/rtc/Kconfig.am1805"
|
||||
source "drivers/rtc/Kconfig.ambiq"
|
||||
source "drivers/rtc/Kconfig.ds1307"
|
||||
source "drivers/rtc/Kconfig.ds3231"
|
||||
source "drivers/rtc/Kconfig.emul"
|
||||
source "drivers/rtc/Kconfig.fake"
|
||||
source "drivers/rtc/Kconfig.ifx_cat1"
|
||||
|
||||
23
drivers/rtc/Kconfig.ds3231
Normal file
23
drivers/rtc/Kconfig.ds3231
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2024, Gergo Vari <work@gergovari.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config RTC_DS3231
|
||||
bool "Maxim DS3231 RTC/TCXO"
|
||||
default y
|
||||
depends on DT_HAS_MAXIM_DS3231_MFD_ENABLED
|
||||
depends on DT_HAS_MAXIM_DS3231_RTC_ENABLED
|
||||
select I2C
|
||||
select MFD
|
||||
help
|
||||
Enable RTC driver based on Maxim DS3231 I2C device.
|
||||
|
||||
config RTC_DS3231_INIT_PRIORITY
|
||||
int "DS3231 RTC driver initialization priority"
|
||||
depends on RTC_DS3231
|
||||
default 86
|
||||
help
|
||||
Initialization priority for the DS3231 RTC driver. It must be
|
||||
greater than the I2C controller init priority and the mfd driver
|
||||
init priority.
|
||||
859
drivers/rtc/rtc_ds3231.c
Normal file
859
drivers/rtc/rtc_ds3231.c
Normal file
@ -0,0 +1,859 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
*/
|
||||
|
||||
/* TODO: implement user mode? */
|
||||
/* TODO: implement aging offset with calibration */
|
||||
/* TODO: handle century bit, external storage? */
|
||||
|
||||
#include <zephyr/drivers/mfd/ds3231.h>
|
||||
#include <zephyr/drivers/rtc/rtc_ds3231.h>
|
||||
|
||||
#include <zephyr/drivers/rtc.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(RTC_DS3231, CONFIG_RTC_LOG_LEVEL);
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#define DT_DRV_COMPAT maxim_ds3231_rtc
|
||||
|
||||
#ifdef CONFIG_RTC_ALARM
|
||||
#define ALARM_COUNT 2
|
||||
struct rtc_ds3231_alarm {
|
||||
rtc_alarm_callback cb;
|
||||
void *user_data;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
struct rtc_ds3231_update {
|
||||
rtc_update_callback cb;
|
||||
void *user_data;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct rtc_ds3231_data {
|
||||
#ifdef CONFIG_RTC_ALARM
|
||||
struct rtc_ds3231_alarm alarms[ALARM_COUNT];
|
||||
#endif
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
struct rtc_ds3231_update update;
|
||||
#endif
|
||||
struct k_sem lock;
|
||||
struct gpio_callback isw_cb_data;
|
||||
struct k_work work;
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
struct rtc_ds3231_conf {
|
||||
const struct device *mfd;
|
||||
struct gpio_dt_spec freq_32k_gpios;
|
||||
struct gpio_dt_spec isw_gpios;
|
||||
};
|
||||
|
||||
static int rtc_ds3231_modify_register(const struct device *dev, uint8_t reg, uint8_t *buf,
|
||||
const uint8_t bitmask)
|
||||
{
|
||||
int err;
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
if (bitmask != 255) {
|
||||
uint8_t og_buf = 0;
|
||||
|
||||
err = mfd_ds3231_i2c_get_registers(config->mfd, reg, &og_buf, 1);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
og_buf &= ~bitmask;
|
||||
*buf &= bitmask;
|
||||
og_buf |= *buf;
|
||||
*buf = og_buf;
|
||||
}
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = mfd_ds3231_i2c_set_registers(config->mfd, reg, buf, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
enum rtc_ds3231_freq {
|
||||
FREQ_1000,
|
||||
FREQ_1024,
|
||||
FREQ_4096,
|
||||
FREQ_8192
|
||||
};
|
||||
struct rtc_ds3231_ctrl {
|
||||
bool en_osc;
|
||||
|
||||
bool conv;
|
||||
|
||||
enum rtc_ds3231_freq sqw_freq;
|
||||
|
||||
bool intctrl;
|
||||
bool en_alarm_1;
|
||||
bool en_alarm_2;
|
||||
};
|
||||
static int rtc_ds3231_ctrl_to_buf(const struct rtc_ds3231_ctrl *ctrl, uint8_t *buf)
|
||||
{
|
||||
if (ctrl->en_alarm_1) {
|
||||
*buf |= DS3231_BITS_CTRL_ALARM_1_EN;
|
||||
}
|
||||
|
||||
if (ctrl->en_alarm_2) {
|
||||
*buf |= DS3231_BITS_CTRL_ALARM_2_EN;
|
||||
}
|
||||
|
||||
switch (ctrl->sqw_freq) {
|
||||
case FREQ_1000:
|
||||
break;
|
||||
case FREQ_1024:
|
||||
*buf |= DS3231_BITS_CTRL_RS1;
|
||||
break;
|
||||
case FREQ_4096:
|
||||
*buf |= DS3231_BITS_CTRL_RS2;
|
||||
break;
|
||||
case FREQ_8192:
|
||||
*buf |= DS3231_BITS_CTRL_RS1;
|
||||
*buf |= DS3231_BITS_CTRL_RS2;
|
||||
break;
|
||||
}
|
||||
if (ctrl->intctrl) {
|
||||
*buf |= DS3231_BITS_CTRL_INTCTRL;
|
||||
} else { /* enable sqw */
|
||||
*buf |= DS3231_BITS_CTRL_BBSQW;
|
||||
}
|
||||
|
||||
if (ctrl->conv) {
|
||||
*buf |= DS3231_BITS_CTRL_CONV;
|
||||
}
|
||||
|
||||
if (!ctrl->en_osc) { /* active low */
|
||||
*buf |= DS3231_BITS_CTRL_EOSC;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int rtc_ds3231_modify_ctrl(const struct device *dev, const struct rtc_ds3231_ctrl *ctrl,
|
||||
const uint8_t bitmask)
|
||||
{
|
||||
uint8_t reg = DS3231_REG_CTRL;
|
||||
uint8_t buf = 0;
|
||||
|
||||
int err = rtc_ds3231_ctrl_to_buf(ctrl, &buf);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rtc_ds3231_modify_register(dev, reg, &buf, bitmask);
|
||||
}
|
||||
|
||||
struct rtc_ds3231_ctrl_sts {
|
||||
bool osf;
|
||||
bool en_32khz;
|
||||
bool bsy;
|
||||
bool a1f;
|
||||
bool a2f;
|
||||
};
|
||||
static int rtc_ds3231_ctrl_sts_to_buf(const struct rtc_ds3231_ctrl_sts *ctrl, uint8_t *buf)
|
||||
{
|
||||
if (ctrl->a1f) {
|
||||
*buf |= DS3231_BITS_CTRL_STS_ALARM_1_FLAG;
|
||||
}
|
||||
if (ctrl->a2f) {
|
||||
*buf |= DS3231_BITS_CTRL_STS_ALARM_2_FLAG;
|
||||
}
|
||||
if (ctrl->osf) {
|
||||
*buf |= DS3231_BITS_CTRL_STS_OSF;
|
||||
}
|
||||
if (ctrl->en_32khz) {
|
||||
*buf |= DS3231_BITS_CTRL_STS_32_EN;
|
||||
}
|
||||
if (ctrl->bsy) {
|
||||
*buf |= DS3231_BITS_CTRL_STS_BSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int rtc_ds3231_modify_ctrl_sts(const struct device *dev,
|
||||
const struct rtc_ds3231_ctrl_sts *ctrl, const uint8_t bitmask)
|
||||
{
|
||||
const uint8_t reg = DS3231_REG_CTRL_STS;
|
||||
uint8_t buf = 0;
|
||||
|
||||
int err = rtc_ds3231_ctrl_sts_to_buf(ctrl, &buf);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rtc_ds3231_modify_register(dev, reg, &buf, bitmask);
|
||||
}
|
||||
static int rtc_ds3231_get_ctrl_sts(const struct device *dev, uint8_t *buf)
|
||||
{
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
return mfd_ds3231_i2c_get_registers(config->mfd, DS3231_REG_CTRL_STS, buf, 1);
|
||||
}
|
||||
|
||||
struct rtc_ds3231_settings {
|
||||
bool osc; /* bit 0 */
|
||||
bool intctrl_or_sqw; /* bit 1 */
|
||||
enum rtc_ds3231_freq freq_sqw; /* bit 2 */
|
||||
bool freq_32khz; /* bit 3 */
|
||||
bool alarm_1; /* bit 4 */
|
||||
bool alarm_2; /* bit 5 */
|
||||
};
|
||||
static int rtc_ds3231_modify_settings(const struct device *dev, struct rtc_ds3231_settings *conf,
|
||||
uint8_t mask)
|
||||
{
|
||||
struct rtc_ds3231_ctrl ctrl = {};
|
||||
uint8_t ctrl_mask = 0;
|
||||
|
||||
struct rtc_ds3231_ctrl_sts ctrl_sts = {};
|
||||
uint8_t ctrl_sts_mask = 0;
|
||||
|
||||
if (mask & DS3231_BITS_STS_OSC) {
|
||||
ctrl.en_osc = conf->osc;
|
||||
ctrl_mask |= DS3231_BITS_CTRL_EOSC;
|
||||
}
|
||||
if (mask & DS3231_BITS_STS_INTCTRL) {
|
||||
ctrl.intctrl = !conf->intctrl_or_sqw;
|
||||
ctrl_mask |= DS3231_BITS_CTRL_BBSQW;
|
||||
}
|
||||
if (mask & DS3231_BITS_STS_SQW) {
|
||||
ctrl.sqw_freq = conf->freq_sqw;
|
||||
ctrl_mask |= DS3231_BITS_CTRL_RS1;
|
||||
ctrl_mask |= DS3231_BITS_CTRL_RS2;
|
||||
}
|
||||
if (mask & DS3231_BITS_STS_32KHZ) {
|
||||
ctrl_sts.en_32khz = conf->freq_32khz;
|
||||
ctrl_sts_mask |= DS3231_BITS_CTRL_STS_32_EN;
|
||||
}
|
||||
if (mask & DS3231_BITS_STS_ALARM_1) {
|
||||
ctrl.en_alarm_1 = conf->alarm_1;
|
||||
ctrl_mask |= DS3231_BITS_CTRL_ALARM_1_EN;
|
||||
}
|
||||
if (mask & DS3231_BITS_STS_ALARM_2) {
|
||||
ctrl.en_alarm_2 = conf->alarm_2;
|
||||
ctrl_mask |= DS3231_BITS_CTRL_ALARM_2_EN;
|
||||
}
|
||||
|
||||
ctrl.conv = false;
|
||||
|
||||
int err = rtc_ds3231_modify_ctrl(dev, &ctrl, ctrl_mask);
|
||||
|
||||
if (err != 0) {
|
||||
LOG_ERR("Couldn't set control register.");
|
||||
return -EIO;
|
||||
}
|
||||
err = rtc_ds3231_modify_ctrl_sts(dev, &ctrl_sts, ctrl_sts_mask);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Couldn't set status register.");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_ds3231_rtc_time_to_buf(const struct rtc_time *tm, uint8_t *buf)
|
||||
{
|
||||
buf[0] = bin2bcd(tm->tm_sec) & DS3231_BITS_TIME_SECONDS;
|
||||
buf[1] = bin2bcd(tm->tm_min) & DS3231_BITS_TIME_MINUTES;
|
||||
buf[2] = bin2bcd(tm->tm_hour) & DS3231_BITS_TIME_HOURS;
|
||||
buf[3] = bin2bcd(tm->tm_wday) & DS3231_BITS_TIME_DAY_OF_WEEK;
|
||||
buf[4] = bin2bcd(tm->tm_mday) & DS3231_BITS_TIME_DATE;
|
||||
buf[5] = bin2bcd(tm->tm_mon) & DS3231_BITS_TIME_MONTH;
|
||||
|
||||
/* here modulo 100 returns the last two digits of the year,
|
||||
* as the DS3231 chip can only store year data for 0-99,
|
||||
* hitting that ceiling can be detected with the century bit.
|
||||
*/
|
||||
|
||||
/* TODO: figure out a way to store the WHOLE year, not just the last 2 digits. */
|
||||
buf[6] = bin2bcd((tm->tm_year % 100)) & DS3231_BITS_TIME_YEAR;
|
||||
return 0;
|
||||
}
|
||||
static int rtc_ds3231_set_time(const struct device *dev, const struct rtc_time *tm)
|
||||
{
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
int buf_size = 7;
|
||||
uint8_t buf[buf_size];
|
||||
int err = rtc_ds3231_rtc_time_to_buf(tm, buf);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return mfd_ds3231_i2c_set_registers(config->mfd, DS3231_REG_TIME_SECONDS, buf, buf_size);
|
||||
}
|
||||
|
||||
static void rtc_ds3231_reset_rtc_time(struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_sec = 0;
|
||||
tm->tm_min = 0;
|
||||
tm->tm_hour = 0;
|
||||
tm->tm_wday = 0;
|
||||
tm->tm_mday = 0;
|
||||
tm->tm_mon = 0;
|
||||
tm->tm_year = 0;
|
||||
tm->tm_nsec = 0;
|
||||
tm->tm_isdst = -1;
|
||||
tm->tm_yday = -1;
|
||||
}
|
||||
static int rtc_ds3231_buf_to_rtc_time(const uint8_t *buf, struct rtc_time *timeptr)
|
||||
{
|
||||
rtc_ds3231_reset_rtc_time(timeptr);
|
||||
|
||||
timeptr->tm_sec = bcd2bin(buf[0] & DS3231_BITS_TIME_SECONDS);
|
||||
timeptr->tm_min = bcd2bin(buf[1] & DS3231_BITS_TIME_MINUTES);
|
||||
|
||||
int hour = buf[2] & DS3231_BITS_TIME_HOURS;
|
||||
|
||||
if (hour & DS3231_BITS_TIME_12HR) {
|
||||
bool pm = hour & DS3231_BITS_TIME_PM;
|
||||
|
||||
hour &= ~DS3231_BITS_TIME_12HR;
|
||||
hour &= ~DS3231_BITS_TIME_PM;
|
||||
timeptr->tm_hour = bcd2bin(hour + 12 * pm);
|
||||
} else {
|
||||
timeptr->tm_hour = bcd2bin(hour);
|
||||
}
|
||||
|
||||
timeptr->tm_wday = bcd2bin(buf[3] & DS3231_BITS_TIME_DAY_OF_WEEK);
|
||||
timeptr->tm_mday = bcd2bin(buf[4] & DS3231_BITS_TIME_DATE);
|
||||
timeptr->tm_mon = bcd2bin(buf[5] & DS3231_BITS_TIME_MONTH);
|
||||
timeptr->tm_year = bcd2bin(buf[6] & DS3231_BITS_TIME_YEAR);
|
||||
|
||||
/* FIXME: we will always just set us to 20xx for year */
|
||||
timeptr->tm_year = timeptr->tm_year + 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int rtc_ds3231_get_time(const struct device *dev, struct rtc_time *timeptr)
|
||||
{
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
const size_t buf_size = 7;
|
||||
uint8_t buf[buf_size];
|
||||
int err = mfd_ds3231_i2c_get_registers(config->mfd, DS3231_REG_TIME_SECONDS, buf, buf_size);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rtc_ds3231_buf_to_rtc_time(buf, timeptr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RTC_ALARM
|
||||
struct rtc_ds3231_alarm_details {
|
||||
uint8_t start_reg;
|
||||
size_t buf_size;
|
||||
};
|
||||
static struct rtc_ds3231_alarm_details alarms[] = {{DS3231_REG_ALARM_1_SECONDS, 4},
|
||||
{DS3231_REG_ALARM_2_MINUTES, 3}};
|
||||
static int rtc_ds3231_alarm_get_supported_fields(const struct device *dev, u int16_t id,
|
||||
uint16_t *mask)
|
||||
{
|
||||
*mask = RTC_ALARM_TIME_MASK_MONTHDAY | RTC_ALARM_TIME_MASK_WEEKDAY |
|
||||
RTC_ALARM_TIME_MASK_HOUR | RTC_ALARM_TIME_MASK_MINUTE;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
*mask |= RTC_ALARM_TIME_MASK_SECOND;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_ds3231_rtc_time_to_alarm_buf(const struct rtc_time *tm, int id, const uint16_t mask,
|
||||
uint8_t *buf)
|
||||
{
|
||||
if ((mask & RTC_ALARM_TIME_MASK_WEEKDAY) && (mask & RTC_ALARM_TIME_MASK_MONTHDAY)) {
|
||||
LOG_ERR("rtc_time_to_alarm_buf: Mask is invalid (%d)!\n", mask);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (id < 0 || id >= ALARM_COUNT) {
|
||||
LOG_ERR("rtc_time_to_alarm_buf: Alarm ID is out of range (%d)!\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mask & RTC_ALARM_TIME_MASK_MINUTE) {
|
||||
buf[1] = bin2bcd(tm->tm_min) & DS3231_BITS_TIME_MINUTES;
|
||||
} else {
|
||||
buf[1] |= DS3231_BITS_ALARM_RATE;
|
||||
}
|
||||
|
||||
if (mask & RTC_ALARM_TIME_MASK_HOUR) {
|
||||
buf[2] = bin2bcd(tm->tm_hour) & DS3231_BITS_TIME_HOURS;
|
||||
} else {
|
||||
buf[2] |= DS3231_BITS_ALARM_RATE;
|
||||
}
|
||||
|
||||
if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
|
||||
buf[3] = bin2bcd(tm->tm_wday) & DS3231_BITS_TIME_DAY_OF_WEEK;
|
||||
buf[3] |= DS3231_BITS_ALARM_DATE_W_OR_M;
|
||||
} else if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
|
||||
buf[3] = bin2bcd(tm->tm_mday) & DS3231_BITS_TIME_DATE;
|
||||
} else {
|
||||
buf[3] |= DS3231_BITS_ALARM_RATE;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
if (mask & RTC_ALARM_TIME_MASK_SECOND) {
|
||||
buf[0] = bin2bcd(tm->tm_sec) & DS3231_BITS_TIME_SECONDS;
|
||||
} else {
|
||||
buf[0] |= DS3231_BITS_ALARM_RATE;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (mask & RTC_ALARM_TIME_MASK_SECOND) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
buf[i] = buf[i + 1];
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_ds3231_modify_alarm_time(const struct device *dev, int id, const struct rtc_time *tm,
|
||||
const uint8_t mask)
|
||||
{
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
if (id >= ALARM_COUNT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
struct rtc_ds3231_alarm_details details = alarms[id];
|
||||
uint8_t start_reg = details.start_reg;
|
||||
size_t buf_size = details.buf_size;
|
||||
|
||||
uint8_t buf[buf_size];
|
||||
int err = rtc_ds3231_rtc_time_to_alarm_buf(tm, id, mask, buf);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return mfd_ds3231_i2c_set_registers(config->mfd, start_reg, buf, buf_size);
|
||||
}
|
||||
|
||||
static int rtc_ds3231_modify_alarm_state(const struct device *dev, uint16_t id, bool state)
|
||||
{
|
||||
struct rtc_ds3231_settings conf;
|
||||
uint8_t mask = 0;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
conf.alarm_1 = state;
|
||||
mask = DS3231_BITS_STS_ALARM_1;
|
||||
break;
|
||||
case 1:
|
||||
conf.alarm_2 = state;
|
||||
mask = DS3231_BITS_STS_ALARM_2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return rtc_ds3231_modify_settings(dev, &conf, mask);
|
||||
}
|
||||
static int rtc_ds3231_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
|
||||
const struct rtc_time *timeptr)
|
||||
{
|
||||
if (mask == 0) {
|
||||
return rtc_ds3231_modify_alarm_state(dev, id, false);
|
||||
}
|
||||
|
||||
int err = rtc_ds3231_modify_alarm_state(dev, id, true);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rtc_ds3231_modify_alarm_time(dev, id, timeptr, mask);
|
||||
}
|
||||
|
||||
static int rtc_ds3231_alarm_buf_to_rtc_time(uint8_t *buf, int id, struct rtc_time *tm,
|
||||
uint16_t *mask)
|
||||
{
|
||||
rtc_ds3231_reset_rtc_time(tm);
|
||||
|
||||
if (id < 0 || id > 1) {
|
||||
return -EINVAL;
|
||||
} else if (id == 1) {
|
||||
/* shift to the right to match original func */
|
||||
for (int i = 3; i > 0; i--) {
|
||||
buf[i] = buf[i - 1];
|
||||
}
|
||||
buf[0] = 0;
|
||||
}
|
||||
|
||||
*mask = 0;
|
||||
if (!(buf[1] & DS3231_BITS_ALARM_RATE)) {
|
||||
tm->tm_min = bcd2bin(buf[1] & DS3231_BITS_TIME_MINUTES);
|
||||
*mask |= RTC_ALARM_TIME_MASK_MINUTE;
|
||||
}
|
||||
if (!(buf[2] & DS3231_BITS_ALARM_RATE)) {
|
||||
tm->tm_hour = bcd2bin(buf[2] & DS3231_BITS_TIME_HOURS);
|
||||
*mask |= RTC_ALARM_TIME_MASK_HOUR;
|
||||
}
|
||||
if (!(buf[3] & DS3231_BITS_ALARM_RATE)) {
|
||||
if (buf[3] & DS3231_BITS_ALARM_DATE_W_OR_M) {
|
||||
tm->tm_wday = bcd2bin(buf[3] & DS3231_BITS_TIME_DAY_OF_WEEK);
|
||||
*mask |= RTC_ALARM_TIME_MASK_WEEKDAY;
|
||||
} else {
|
||||
tm->tm_mday = bcd2bin(buf[3] & DS3231_BITS_TIME_DATE);
|
||||
*mask |= RTC_ALARM_TIME_MASK_MONTHDAY;
|
||||
}
|
||||
}
|
||||
if (!(buf[0] & DS3231_BITS_ALARM_RATE)) {
|
||||
tm->tm_sec = bcd2bin(buf[0] & DS3231_BITS_TIME_SECONDS);
|
||||
*mask |= RTC_ALARM_TIME_MASK_SECOND;
|
||||
}
|
||||
|
||||
if ((*mask & RTC_ALARM_TIME_MASK_WEEKDAY) && (*mask & RTC_ALARM_TIME_MASK_MONTHDAY)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int rtc_ds3231_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
|
||||
struct rtc_time *timeptr)
|
||||
{
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
if (id >= ALARM_COUNT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
struct rtc_ds3231_alarm_details details = alarms[id];
|
||||
uint8_t start_reg = details.start_reg;
|
||||
size_t buf_size = details.buf_size;
|
||||
|
||||
uint8_t buf[4];
|
||||
int err = mfd_ds3231_i2c_get_registers(config->mfd, start_reg, buf, buf_size);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rtc_ds3231_alarm_buf_to_rtc_time(buf, id, timeptr, mask);
|
||||
}
|
||||
|
||||
static int rtc_ds3231_alarm_is_pending(const struct device *dev, uint16_t id)
|
||||
{
|
||||
uint8_t buf;
|
||||
int err = rtc_ds3231_get_ctrl_sts(dev, &buf);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t mask = 0;
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
mask |= DS3231_BITS_CTRL_STS_ALARM_1_FLAG;
|
||||
break;
|
||||
case 1:
|
||||
mask |= DS3231_BITS_CTRL_STS_ALARM_2_FLAG;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bool state = buf & mask;
|
||||
|
||||
if (state) {
|
||||
const struct rtc_ds3231_ctrl_sts ctrl = {.a1f = false, .a2f = false};
|
||||
|
||||
err = rtc_ds3231_modify_ctrl_sts(dev, &ctrl, mask);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static int rtc_ds3231_get_alarm_states(const struct device *dev, bool *states)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
for (int i = 0; i < ALARM_COUNT; i++) {
|
||||
states[i] = rtc_ds3231_alarm_is_pending(dev, i);
|
||||
if (!(states[i] == 0 || states[i] == 1)) {
|
||||
states[i] = -EINVAL;
|
||||
err = -EINVAL;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rtc_ds3231_alarm_set_callback(const struct device *dev, uint16_t id,
|
||||
rtc_alarm_callback cb, void *user_data)
|
||||
{
|
||||
if (id < 0 || id >= ALARM_COUNT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct rtc_ds3231_data *data = dev->data;
|
||||
|
||||
data->alarms[id] = (struct rtc_ds3231_alarm){cb, user_data};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtc_ds3231_check_alarms(const struct device *dev)
|
||||
{
|
||||
struct rtc_ds3231_data *data = dev->data;
|
||||
|
||||
bool states[2];
|
||||
|
||||
rtc_ds3231_get_alarm_states(dev, states);
|
||||
|
||||
for (int i = 0; i < ALARM_COUNT; i++) {
|
||||
if (states[i]) {
|
||||
if (data->alarms[i].cb) {
|
||||
data->alarms[i].cb(dev, i, data->alarms[i].user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static int rtc_ds3231_init_alarms(struct rtc_ds3231_data *data)
|
||||
{
|
||||
data->alarms[0] = (struct rtc_ds3231_alarm){NULL, NULL};
|
||||
data->alarms[1] = (struct rtc_ds3231_alarm){NULL, NULL};
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
static int rtc_ds3231_init_update(struct rtc_ds3231_data *data)
|
||||
{
|
||||
data->update = (struct rtc_ds3231_update){NULL, NULL};
|
||||
return 0;
|
||||
}
|
||||
static int rtc_ds3231_update_set_callback(const struct device *dev, rtc_update_callback cb,
|
||||
void *user_data)
|
||||
{
|
||||
struct rtc_ds3231_data *data = dev->data;
|
||||
|
||||
data->update = (struct rtc_ds3231_update){cb, user_data};
|
||||
return 0;
|
||||
}
|
||||
static void rtc_ds3231_update_callback(const struct device *dev)
|
||||
{
|
||||
struct rtc_ds3231_data *data = dev->data;
|
||||
|
||||
if (data->update.cb) {
|
||||
data->update.cb(dev, data->update.user_data);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_RTC_UPDATE */
|
||||
|
||||
#if defined(CONFIG_RTC_UPDATE) || defined(CONFIG_RTC_ALARM)
|
||||
static void rtc_ds3231_isw_h(struct k_work *work)
|
||||
{
|
||||
struct rtc_ds3231_data *data = CONTAINER_OF(work, struct rtc_ds3231_data, work);
|
||||
const struct device *dev = data->dev;
|
||||
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
rtc_ds3231_update_callback(dev);
|
||||
#endif /* CONFIG_RTC_UPDATE */
|
||||
|
||||
#ifdef CONFIG_RTC_ALARM
|
||||
rtc_ds3231_check_alarms(dev);
|
||||
#endif /* CONFIG_RTC_ALARM */
|
||||
}
|
||||
static void rtc_ds3231_isw_isr(const struct device *port, struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
struct rtc_ds3231_data *data = CONTAINER_OF(cb, struct rtc_ds3231_data, isw_cb_data);
|
||||
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
static int rtc_ds3231_init_isw(const struct rtc_ds3231_conf *config, struct rtc_ds3231_data *data)
|
||||
{
|
||||
if (!gpio_is_ready_dt(&config->isw_gpios)) {
|
||||
LOG_ERR("ISW GPIO pin is not ready.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_work_init(&data->work, rtc_ds3231_isw_h);
|
||||
|
||||
int err = gpio_pin_configure_dt(&(config->isw_gpios), GPIO_INPUT);
|
||||
|
||||
if (err != 0) {
|
||||
LOG_ERR("Couldn't configure ISW GPIO pin.");
|
||||
return err;
|
||||
}
|
||||
err = gpio_pin_interrupt_configure_dt(&(config->isw_gpios), GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Couldn't configure ISW interrupt.");
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->isw_cb_data, rtc_ds3231_isw_isr, BIT((config->isw_gpios).pin));
|
||||
err = gpio_add_callback((config->isw_gpios).port, &data->isw_cb_data);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Couldn't add ISW interrupt callback.");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(CONFIG_RTC_UPDATE) || defined(CONFIG_RTC_ALARM) */
|
||||
|
||||
static DEVICE_API(rtc, driver_api) = {
|
||||
.set_time = rtc_ds3231_set_time,
|
||||
.get_time = rtc_ds3231_get_time,
|
||||
|
||||
#ifdef CONFIG_RTC_ALARM
|
||||
.alarm_get_supported_fields = rtc_ds3231_alarm_get_supported_fields,
|
||||
.alarm_set_time = rtc_ds3231_alarm_set_time,
|
||||
.alarm_get_time = rtc_ds3231_alarm_get_time,
|
||||
.alarm_is_pending = rtc_ds3231_alarm_is_pending,
|
||||
.alarm_set_callback = rtc_ds3231_alarm_set_callback,
|
||||
#endif /* CONFIG_RTC_ALARM */
|
||||
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
.update_set_callback = rtc_ds3231_update_set_callback,
|
||||
#endif /* CONFIG_RTC_UPDATE */
|
||||
|
||||
#ifdef CONFIG_RTC_CALIBRATION
|
||||
/*.set_calibration = set_calibration,
|
||||
* .get_calibration = get_calibration,
|
||||
*/
|
||||
#endif /* CONFIG_RTC_CALIBRATION */
|
||||
};
|
||||
|
||||
static int rtc_ds3231_init_settings(const struct device *dev, const struct rtc_ds3231_conf *config)
|
||||
{
|
||||
struct rtc_ds3231_settings conf = {
|
||||
.osc = true,
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
.intctrl_or_sqw = false,
|
||||
.freq_sqw = FREQ_1000,
|
||||
#else
|
||||
.intctrl_or_sqw = true,
|
||||
#endif
|
||||
.freq_32khz = config->freq_32k_gpios.port,
|
||||
};
|
||||
uint8_t mask = 255 & ~DS3231_BITS_STS_ALARM_1 & ~DS3231_BITS_STS_ALARM_2;
|
||||
int err = rtc_ds3231_modify_settings(dev, &conf, mask);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
static int rtc_ds3231_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_SUSPEND: {
|
||||
struct rtc_ds3231_settings conf = {.osc = true,
|
||||
.intctrl_or_sqw = false,
|
||||
.freq_sqw = FREQ_1000,
|
||||
.freq_32khz = false};
|
||||
uint8_t mask = 255 & ~DS3231_BITS_STS_ALARM_1 & ~DS3231_BITS_STS_ALARM_2;
|
||||
|
||||
err = rtc_ds3231_modify_settings(dev, &conf, mask);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PM_DEVICE_ACTION_RESUME: {
|
||||
/* TODO: trigger a temp CONV */
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
|
||||
err = rtc_ds3231_init_settings(dev, config);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
static int rtc_ds3231_init(const struct device *dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
const struct rtc_ds3231_conf *config = dev->config;
|
||||
struct rtc_ds3231_data *data = dev->data;
|
||||
|
||||
if (!device_is_ready(config->mfd)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RTC_ALARM
|
||||
err = rtc_ds3231_init_alarms(data);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to init alarms.");
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RTC_UPDATE
|
||||
err = rtc_ds3231_init_update(data);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to init update callback.");
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = rtc_ds3231_init_settings(dev, config);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to init settings.");
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_RTC_UPDATE) || defined(CONFIG_RTC_ALARM)
|
||||
data->dev = dev;
|
||||
err = rtc_ds3231_init_isw(config, data);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Initing ISW interrupt failed!");
|
||||
return err;
|
||||
}
|
||||
#endif /* defined(CONFIG_RTC_UPDATE) || defined(CONFIG_RTC_ALARM) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RTC_DS3231_DEFINE(inst) \
|
||||
static struct rtc_ds3231_data rtc_ds3231_data_##inst; \
|
||||
static const struct rtc_ds3231_conf rtc_ds3231_conf_##inst = { \
|
||||
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
|
||||
.isw_gpios = GPIO_DT_SPEC_INST_GET(inst, isw_gpios), \
|
||||
.freq_32k_gpios = GPIO_DT_SPEC_INST_GET_OR(inst, freq_32khz_gpios, {NULL})}; \
|
||||
PM_DEVICE_DT_INST_DEFINE(inst, rtc_ds3231_pm_action); \
|
||||
DEVICE_DT_INST_DEFINE(inst, &rtc_ds3231_init, PM_DEVICE_DT_INST_GET(inst), \
|
||||
&rtc_ds3231_data_##inst, &rtc_ds3231_conf_##inst, POST_KERNEL, \
|
||||
CONFIG_RTC_DS3231_INIT_PRIORITY, &driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(RTC_DS3231_DEFINE)
|
||||
@ -12,4 +12,5 @@ add_subdirectory_ifdef(CONFIG_MAX31865 max31865)
|
||||
add_subdirectory_ifdef(CONFIG_MAX31875 max31875)
|
||||
add_subdirectory_ifdef(CONFIG_MAX44009 max44009)
|
||||
add_subdirectory_ifdef(CONFIG_MAX6675 max6675)
|
||||
add_subdirectory_ifdef(CONFIG_SENSOR_DS3231 ds3231)
|
||||
# zephyr-keep-sorted-stop
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/sensor/maxim/ds18b20/Kconfig"
|
||||
source "drivers/sensor/maxim/ds3231/Kconfig"
|
||||
source "drivers/sensor/maxim/max17055/Kconfig"
|
||||
source "drivers/sensor/maxim/max17262/Kconfig"
|
||||
source "drivers/sensor/maxim/max30101/Kconfig"
|
||||
|
||||
7
drivers/sensor/maxim/ds3231/CMakeLists.txt
Normal file
7
drivers/sensor/maxim/ds3231/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(ds3231.c)
|
||||
22
drivers/sensor/maxim/ds3231/Kconfig
Normal file
22
drivers/sensor/maxim/ds3231/Kconfig
Normal file
@ -0,0 +1,22 @@
|
||||
# DS3231 temperature sensor configuration options
|
||||
|
||||
# Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config SENSOR_DS3231
|
||||
bool "DS3231 sensor"
|
||||
default y
|
||||
depends on DT_HAS_MAXIM_DS3231_MFD_ENABLED
|
||||
depends on DT_HAS_MAXIM_DS3231_SENSOR_ENABLED
|
||||
select I2C
|
||||
select MFD
|
||||
select RTIO_WORKQ if SENSOR_ASYNC_API
|
||||
help
|
||||
Enable driver for DS3231 I2C-based temperature sensor.
|
||||
|
||||
config SENSOR_DS3231_INIT_PRIORITY
|
||||
int "DS3231 sensor driver init priority"
|
||||
default 86
|
||||
help
|
||||
Init priority for the DS3231 sensor driver. It must be
|
||||
greater than MFD_INIT_PRIORITY.
|
||||
272
drivers/sensor/maxim/ds3231/ds3231.c
Normal file
272
drivers/sensor/maxim/ds3231/ds3231.c
Normal file
@ -0,0 +1,272 @@
|
||||
/* ds3231.c - Driver for Maxim DS3231 temperature sensor */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/init.h>
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/rtio/work.h>
|
||||
|
||||
#include <zephyr/drivers/mfd/ds3231.h>
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ds3231.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(SENSOR_DS3231, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define DT_DRV_COMPAT maxim_ds3231_sensor
|
||||
|
||||
struct sensor_ds3231_data {
|
||||
const struct device *dev;
|
||||
uint16_t raw_temp;
|
||||
};
|
||||
|
||||
struct sensor_ds3231_conf {
|
||||
const struct device *mfd;
|
||||
};
|
||||
|
||||
int sensor_ds3231_read_temp(const struct device *dev, uint16_t *raw_temp)
|
||||
{
|
||||
const struct sensor_ds3231_conf *config = dev->config;
|
||||
|
||||
uint8_t buf[2];
|
||||
int err = mfd_ds3231_i2c_get_registers(config->mfd, DS3231_REG_TEMP_MSB, buf, 2);
|
||||
*raw_temp = ((uint16_t)((buf[0]) << 2) | (buf[1] >> 6));
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fetch and Get (will be deprecated) */
|
||||
|
||||
int sensor_ds3231_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct sensor_ds3231_data *data = dev->data;
|
||||
int err = sensor_ds3231_read_temp(dev, &(data->raw_temp));
|
||||
|
||||
if (err != 0) {
|
||||
LOG_ERR("ds3231 sample fetch failed %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sensor_ds3231_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct sensor_ds3231_data *data = dev->data;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_AMBIENT_TEMP:
|
||||
const uint16_t raw_temp = data->raw_temp;
|
||||
|
||||
val->val1 = (int8_t)(raw_temp & GENMASK(8, 2)) >> 2;
|
||||
|
||||
uint8_t frac = raw_temp & 3;
|
||||
|
||||
val->val2 = (frac * 25) * pow(10, 4);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read and Decode */
|
||||
|
||||
struct sensor_ds3231_header {
|
||||
uint64_t timestamp;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct sensor_ds3231_edata {
|
||||
struct sensor_ds3231_header header;
|
||||
uint16_t raw_temp;
|
||||
};
|
||||
|
||||
void sensor_ds3231_submit_sync(struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
uint32_t min_buf_len = sizeof(struct sensor_ds3231_edata);
|
||||
int rc;
|
||||
uint8_t *buf;
|
||||
uint32_t buf_len;
|
||||
|
||||
const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
|
||||
const struct device *dev = cfg->sensor;
|
||||
const struct sensor_chan_spec *const channels = cfg->channels;
|
||||
|
||||
rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
|
||||
rtio_iodev_sqe_err(iodev_sqe, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sensor_ds3231_edata *edata;
|
||||
|
||||
edata = (struct sensor_ds3231_edata *)buf;
|
||||
|
||||
if (channels[0].chan_type != SENSOR_CHAN_AMBIENT_TEMP) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t raw_temp;
|
||||
|
||||
rc = sensor_ds3231_read_temp(dev, &raw_temp);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Failed to fetch samples");
|
||||
rtio_iodev_sqe_err(iodev_sqe, rc);
|
||||
return;
|
||||
}
|
||||
edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks());
|
||||
edata->raw_temp = raw_temp;
|
||||
|
||||
rtio_iodev_sqe_ok(iodev_sqe, 0);
|
||||
}
|
||||
|
||||
void sensor_ds3231_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
|
||||
{
|
||||
struct rtio_work_req *req = rtio_work_req_alloc();
|
||||
|
||||
if (req == NULL) {
|
||||
LOG_ERR("RTIO work item allocation failed."
|
||||
"Consider to increase CONFIG_RTIO_WORKQ_POOL_ITEMS.");
|
||||
rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: optimize with new bus shims
|
||||
* to avoid swapping execution contexts
|
||||
* for a small register read
|
||||
*/
|
||||
rtio_work_req_submit(req, iodev_sqe, sensor_ds3231_submit_sync);
|
||||
}
|
||||
|
||||
static int sensor_ds3231_decoder_get_frame_count(const uint8_t *buffer,
|
||||
struct sensor_chan_spec chan_spec,
|
||||
uint16_t *frame_count)
|
||||
{
|
||||
int err = -ENOTSUP;
|
||||
|
||||
if (chan_spec.chan_idx != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_AMBIENT_TEMP:
|
||||
*frame_count = 1;
|
||||
break;
|
||||
default:
|
||||
return err;
|
||||
}
|
||||
|
||||
if (*frame_count > 0) {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sensor_ds3231_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size,
|
||||
size_t *frame_size)
|
||||
{
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_AMBIENT_TEMP:
|
||||
*base_size = sizeof(struct sensor_q31_sample_data);
|
||||
*frame_size = sizeof(struct sensor_q31_sample_data);
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int sensor_ds3231_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
|
||||
uint32_t *fit, uint16_t max_count, void *data_out)
|
||||
{
|
||||
if (*fit != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sensor_q31_data *out = data_out;
|
||||
|
||||
out->header.reading_count = 1;
|
||||
|
||||
const struct sensor_ds3231_edata *edata = (const struct sensor_ds3231_edata *)buffer;
|
||||
|
||||
switch (chan_spec.chan_type) {
|
||||
case SENSOR_CHAN_AMBIENT_TEMP:
|
||||
out->header.base_timestamp_ns = edata->header.timestamp;
|
||||
const uint16_t raw_temp = edata->raw_temp;
|
||||
|
||||
out->shift = 8 - 1;
|
||||
out->readings[0].temperature = (q31_t)raw_temp << (32 - 10);
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*fit = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SENSOR_DECODER_API_DT_DEFINE() = {
|
||||
.get_frame_count = sensor_ds3231_decoder_get_frame_count,
|
||||
.get_size_info = sensor_ds3231_decoder_get_size_info,
|
||||
.decode = sensor_ds3231_decoder_decode,
|
||||
};
|
||||
|
||||
int sensor_ds3231_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
*decoder = &SENSOR_DECODER_NAME();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sensor_ds3231_init(const struct device *dev)
|
||||
{
|
||||
const struct sensor_ds3231_conf *config = dev->config;
|
||||
|
||||
if (!device_is_ready(config->mfd)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_API(sensor, driver_api) = {
|
||||
.sample_fetch = sensor_ds3231_sample_fetch,
|
||||
.channel_get = sensor_ds3231_channel_get,
|
||||
#ifdef CONFIG_SENSOR_ASYNC_API
|
||||
.submit = sensor_ds3231_submit,
|
||||
.get_decoder = sensor_ds3231_get_decoder,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define SENSOR_DS3231_DEFINE(inst) \
|
||||
static struct sensor_ds3231_data sensor_ds3231_data_##inst; \
|
||||
static const struct sensor_ds3231_conf sensor_ds3231_conf_##inst = { \
|
||||
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst))}; \
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, &sensor_ds3231_init, NULL, &sensor_ds3231_data_##inst, \
|
||||
&sensor_ds3231_conf_##inst, POST_KERNEL, \
|
||||
CONFIG_SENSOR_DS3231_INIT_PRIORITY, &driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SENSOR_DS3231_DEFINE)
|
||||
17
drivers/sensor/maxim/ds3231/ds3231.h
Normal file
17
drivers/sensor/maxim/ds3231/ds3231.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_DS3231_DS3231_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_DS3231_DS3231_H_
|
||||
|
||||
/* Temperature registers */
|
||||
#define DS3231_REG_TEMP_MSB 0x11
|
||||
#define DS3231_REG_TEMP_LSB 0x12
|
||||
|
||||
/* Temperature bitmasks */
|
||||
#define DS3231_BITS_TEMP_LSB GENMASK(7, 6) /* fractional portion */
|
||||
|
||||
#endif
|
||||
34
dts/bindings/mfd/maxim,ds3231-mfd.yaml
Normal file
34
dts/bindings/mfd/maxim,ds3231-mfd.yaml
Normal file
@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: |
|
||||
Maxim DS3231 I2C MFD
|
||||
|
||||
The following example displays the node layout
|
||||
with every possible partial driver included.
|
||||
|
||||
ds3231: ds3231@68 {
|
||||
compatible = "maxim,ds3231-mfd";
|
||||
reg = <0x68>;
|
||||
status = "okay";
|
||||
|
||||
ds3231_sensor: ds3231_sensor {
|
||||
compatible = "maxim,ds3231-sensor";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
ds3231_rtc: ds3231_rtc {
|
||||
compatible = "maxim,ds3231-rtc";
|
||||
status = "okay";
|
||||
|
||||
isw-gpios = <&gpio0 25 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
freq-32khz-gpios = <&gpio0 33 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "maxim,ds3231-mfd"
|
||||
|
||||
include:
|
||||
- name: i2c-device.yaml
|
||||
35
dts/bindings/rtc/maxim,ds3231-rtc.yaml
Normal file
35
dts/bindings/rtc/maxim,ds3231-rtc.yaml
Normal file
@ -0,0 +1,35 @@
|
||||
#
|
||||
# Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: Maxim DS3231 I2C RTC/TCXO
|
||||
|
||||
compatible: "maxim,ds3231-rtc"
|
||||
|
||||
include: [rtc-device.yaml]
|
||||
|
||||
properties:
|
||||
freq-32khz-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
|
||||
32 KiHz open drain output
|
||||
|
||||
The DS3231 defaults to providing a 32 KiHz square wave on this
|
||||
signal. The driver does not make use of this, but applications
|
||||
may want access.
|
||||
|
||||
isw-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
|
||||
interrupt/square wave open drain output
|
||||
|
||||
The DS3231 uses this signal to notify when an alarm has triggered,
|
||||
and also to produce a square wave aligned to the countdown chain.
|
||||
Both capabilities are used within the driver.
|
||||
|
||||
This signal must be present to support time set
|
||||
and read operations that preserve sub-second accuracy.
|
||||
10
dts/bindings/sensor/maxim,ds3231-sensor.yaml
Normal file
10
dts/bindings/sensor/maxim,ds3231-sensor.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: Maxim DS3231 I2C temperature sensor
|
||||
|
||||
compatible: "maxim,ds3231-sensor"
|
||||
|
||||
include: [sensor-device.yaml]
|
||||
39
include/zephyr/drivers/mfd/ds3231.h
Normal file
39
include/zephyr/drivers/mfd/ds3231.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_DS3231_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_MFD_DS3231_H_
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
|
||||
/**
|
||||
* @brief Get specified number of registers from an I2C device
|
||||
* starting at the given register address.
|
||||
*
|
||||
* @param dev ds3231 mfd device
|
||||
* @param start_reg The register address to start at.
|
||||
* @param buf The buffer array pointer to store results in.
|
||||
* @param buf_size The amount of register values to return.
|
||||
* @retval 0 on success
|
||||
* @retval -errno in case of any bus error
|
||||
*/
|
||||
int mfd_ds3231_i2c_get_registers(const struct device *dev, uint8_t start_reg, uint8_t *buf,
|
||||
const size_t buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set a register on an I2C device at the given register address.
|
||||
*
|
||||
* @param dev ds3231 mfd device
|
||||
* @param start_reg The register address to set.
|
||||
* @param buf The value to write to the given address.
|
||||
* @param buf_size The size of the buffer to be written to the given address.
|
||||
* @retval 0 on success
|
||||
* @retval -errno in case of any bus error
|
||||
*/
|
||||
int mfd_ds3231_i2c_set_registers(const struct device *dev, uint8_t start_reg, const uint8_t *buf,
|
||||
const size_t buf_size);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_DS3231_H_ */
|
||||
109
include/zephyr/drivers/rtc/rtc_ds3231.h
Normal file
109
include/zephyr/drivers/rtc/rtc_ds3231.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2024 Gergo Vari <work@gergovari.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* REGISTERS
|
||||
*/
|
||||
|
||||
/* Time registers */
|
||||
#define DS3231_REG_TIME_SECONDS 0x00
|
||||
#define DS3231_REG_TIME_MINUTES 0x01
|
||||
#define DS3231_REG_TIME_HOURS 0x02
|
||||
#define DS3231_REG_TIME_DAY_OF_WEEK 0x03
|
||||
#define DS3231_REG_TIME_DATE 0x04
|
||||
#define DS3231_REG_TIME_MONTH 0x05
|
||||
#define DS3231_REG_TIME_YEAR 0x06
|
||||
|
||||
/* Alarm 1 registers */
|
||||
#define DS3231_REG_ALARM_1_SECONDS 0x07
|
||||
#define DS3231_REG_ALARM_1_MINUTES 0x08
|
||||
#define DS3231_REG_ALARM_1_HOURS 0x09
|
||||
#define DS3231_REG_ALARM_1_DATE 0x0A
|
||||
|
||||
/* Alarm 2 registers */
|
||||
/* Alarm 2 has no seconds to set, it only has minute accuracy. */
|
||||
#define DS3231_REG_ALARM_2_MINUTES 0x0B
|
||||
#define DS3231_REG_ALARM_2_HOURS 0x0C
|
||||
#define DS3231_REG_ALARM_2_DATE 0x0D
|
||||
|
||||
/* Control registers */
|
||||
#define DS3231_REG_CTRL 0x0E
|
||||
#define DS3231_REG_CTRL_STS 0x0F
|
||||
|
||||
/* Aging offset register */
|
||||
#define DS3231_REG_AGING_OFFSET 0x10
|
||||
|
||||
/*
|
||||
* BITMASKS
|
||||
*/
|
||||
|
||||
/* Time bitmasks */
|
||||
#define DS3231_BITS_TIME_SECONDS GENMASK(6, 0)
|
||||
#define DS3231_BITS_TIME_MINUTES GENMASK(6, 0)
|
||||
#define DS3231_BITS_TIME_HOURS GENMASK(5, 0)
|
||||
#define DS3231_BITS_TIME_PM BIT(5)
|
||||
#define DS3231_BITS_TIME_12HR BIT(6)
|
||||
#define DS3231_BITS_TIME_DAY_OF_WEEK GENMASK(2, 0)
|
||||
#define DS3231_BITS_TIME_DATE GENMASK(5, 0)
|
||||
#define DS3231_BITS_TIME_MONTH GENMASK(4, 0)
|
||||
#define DS3231_BITS_TIME_CENTURY BIT(7)
|
||||
#define DS3231_BITS_TIME_YEAR GENMASK(7, 0)
|
||||
|
||||
/* Alarm bitmasks */
|
||||
/* All alarm bitmasks match with time other than date and the alarm rate bit. */
|
||||
#define DS3231_BITS_ALARM_RATE BIT(7)
|
||||
#define DS3231_BITS_ALARM_DATE_W_OR_M BIT(6)
|
||||
|
||||
#define DS3231_BITS_SIGN BIT(7)
|
||||
/* Control bitmasks */
|
||||
#define DS3231_BITS_CTRL_EOSC BIT(7) /* enable oscillator, active low */
|
||||
#define DS3231_BITS_CTRL_BBSQW BIT(6) /* enable battery-backed square-wave */
|
||||
|
||||
/* Setting the CONV bit to 1 forces the temperature sensor to
|
||||
* convert the temperature into digital code and
|
||||
* execute the TCXO algorithm to update
|
||||
* the capacitance array to the oscillator. This can only
|
||||
* happen when a conversion is not already in progress.
|
||||
* The user should check the status bit BSY before
|
||||
* forcing the controller to start a new TCXO execution.
|
||||
* A user-initiated temperature conversion
|
||||
* does not affect the internal 64-second update cycle.
|
||||
*/
|
||||
#define DS3231_BITS_CTRL_CONV BIT(6)
|
||||
|
||||
/* Rate selectors */
|
||||
/* RS2 | RS1 | SQW FREQ
|
||||
* 0 | 0 | 1Hz
|
||||
* 0 | 1 | 1.024kHz
|
||||
* 1 | 0 | 4.096kHz
|
||||
* 1 | 1 | 8.192kHz
|
||||
*/
|
||||
#define DS3231_BITS_CTRL_RS2 BIT(4)
|
||||
#define DS3231_BITS_CTRL_RS1 BIT(3)
|
||||
|
||||
#define DS3231_BITS_CTRL_INTCTRL BIT(2)
|
||||
#define DS3231_BITS_CTRL_ALARM_2_EN BIT(1)
|
||||
#define DS3231_BITS_CTRL_ALARM_1_EN BIT(0)
|
||||
|
||||
/* Control status bitmasks */
|
||||
/* For some reason you can access OSF in both control and control status registers. */
|
||||
#define DS3231_BITS_CTRL_STS_OSF BIT(7) /* oscillator stop flag */ /* read only */
|
||||
#define DS3231_BITS_CTRL_STS_32_EN BIT(3) /* 32kHz square-wave */
|
||||
/* set when TXCO is busy, see CONV flag: read only */
|
||||
#define DS3231_BITS_CTRL_STS_BSY BIT(2)
|
||||
#define DS3231_BITS_CTRL_STS_ALARM_2_FLAG BIT(1) /* can only be set to 0 */
|
||||
#define DS3231_BITS_CTRL_STS_ALARM_1_FLAG BIT(0) /* can only be set to 0 */
|
||||
|
||||
/* Aging offset bitmask */
|
||||
#define DS3231_BITS_DATA BIT(6, 0)
|
||||
|
||||
/* Settings bitmasks */
|
||||
#define DS3231_BITS_STS_OSC BIT(0)
|
||||
#define DS3231_BITS_STS_INTCTRL BIT(1)
|
||||
#define DS3231_BITS_STS_SQW BIT(2)
|
||||
#define DS3231_BITS_STS_32KHZ BIT(3)
|
||||
#define DS3231_BITS_STS_ALARM_1 BIT(4)
|
||||
#define DS3231_BITS_STS_ALARM_2 BIT(5)
|
||||
Loading…
Reference in New Issue
Block a user