Browse Source

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>
pull/83667/head
Gergo Vari 11 months ago committed by Benjamin Cabé
parent
commit
2759adaf1a
  1. 1
      drivers/mfd/CMakeLists.txt
  2. 1
      drivers/mfd/Kconfig
  3. 10
      drivers/mfd/Kconfig.ds3231
  4. 77
      drivers/mfd/mfd_ds3231.c
  5. 1
      drivers/rtc/CMakeLists.txt
  6. 1
      drivers/rtc/Kconfig
  7. 23
      drivers/rtc/Kconfig.ds3231
  8. 859
      drivers/rtc/rtc_ds3231.c
  9. 1
      drivers/sensor/maxim/CMakeLists.txt
  10. 1
      drivers/sensor/maxim/Kconfig
  11. 7
      drivers/sensor/maxim/ds3231/CMakeLists.txt
  12. 22
      drivers/sensor/maxim/ds3231/Kconfig
  13. 272
      drivers/sensor/maxim/ds3231/ds3231.c
  14. 17
      drivers/sensor/maxim/ds3231/ds3231.h
  15. 0
      dts/bindings/counter/maxim,ds3231.yaml
  16. 34
      dts/bindings/mfd/maxim,ds3231-mfd.yaml
  17. 35
      dts/bindings/rtc/maxim,ds3231-rtc.yaml
  18. 10
      dts/bindings/sensor/maxim,ds3231-sensor.yaml
  19. 39
      include/zephyr/drivers/mfd/ds3231.h
  20. 109
      include/zephyr/drivers/rtc/rtc_ds3231.h

1
drivers/mfd/CMakeLists.txt

@ -20,3 +20,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c) @@ -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)

1
drivers/mfd/Kconfig

@ -23,6 +23,7 @@ source "drivers/mfd/Kconfig.adp5585" @@ -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

@ -0,0 +1,10 @@ @@ -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

@ -0,0 +1,77 @@ @@ -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)

1
drivers/rtc/CMakeLists.txt

@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_RTC_RV8263 rtc_rv8263.c) @@ -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)

1
drivers/rtc/Kconfig

@ -44,6 +44,7 @@ config RTC_SHELL @@ -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

@ -0,0 +1,23 @@ @@ -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

@ -0,0 +1,859 @@ @@ -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)

1
drivers/sensor/maxim/CMakeLists.txt

@ -12,4 +12,5 @@ add_subdirectory_ifdef(CONFIG_MAX31865 max31865) @@ -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

1
drivers/sensor/maxim/Kconfig

@ -3,6 +3,7 @@ @@ -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

@ -0,0 +1,7 @@ @@ -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

@ -0,0 +1,22 @@ @@ -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

@ -0,0 +1,272 @@ @@ -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

@ -0,0 +1,17 @@ @@ -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

0
dts/bindings/rtc/maxim,ds3231.yaml → dts/bindings/counter/maxim,ds3231.yaml

34
dts/bindings/mfd/maxim,ds3231-mfd.yaml

@ -0,0 +1,34 @@ @@ -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

@ -0,0 +1,35 @@ @@ -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

@ -0,0 +1,10 @@ @@ -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

@ -0,0 +1,39 @@ @@ -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

@ -0,0 +1,109 @@ @@ -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…
Cancel
Save