drivers: mfd: rv3032: Add MFD driver for rv3032
Add MFD driver for managing rv3032 functionality which did not fit RTC api. That way are implemented part which otherwise will not be used: - RTC - counter/timer - temperature sensor Signed-off-by: Stoyan Bogdanov <sbogdanov@baylibre.com>
This commit is contained in:
committed by
Anas Nashif
parent
376747f2dd
commit
4d1713a07e
@@ -27,6 +27,7 @@ zephyr_library_sources_ifdef(CONFIG_MFD_NPM2100 mfd_npm2100.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_NPM6001 mfd_npm6001.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_PCA9422 mfd_pca9422.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_PF1550 mfd_pf1550.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_RV3032 mfd_rv3032.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_MFD_TLE9104 mfd_tle9104.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c)
|
||||
# zephyr-keep-sorted-stop
|
||||
|
||||
@@ -40,6 +40,7 @@ source "drivers/mfd/Kconfig.npm2100"
|
||||
source "drivers/mfd/Kconfig.npm6001"
|
||||
source "drivers/mfd/Kconfig.pca9422"
|
||||
source "drivers/mfd/Kconfig.pf1550"
|
||||
source "drivers/mfd/Kconfig.rv3032_mfd"
|
||||
source "drivers/mfd/Kconfig.tle9104"
|
||||
# zephyr-keep-sorted-stop
|
||||
|
||||
|
||||
19
drivers/mfd/Kconfig.rv3032_mfd
Normal file
19
drivers/mfd/Kconfig.rv3032_mfd
Normal file
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2025 Baylibre SAS
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config MFD_RV3032
|
||||
bool "MFD driver for RV3032"
|
||||
default y
|
||||
depends on DT_HAS_MICROCRYSTAL_RV3032_MFD_ENABLED
|
||||
depends on GPIO
|
||||
select I2C
|
||||
help
|
||||
Enable MFD driver for RV3032 RTC
|
||||
|
||||
config MFD_MICROCRYSTAL_RV3032_INIT_PRIORITY
|
||||
int "Init priority of rv3032 MFD"
|
||||
depends on MFD_RV3032
|
||||
default 50
|
||||
help
|
||||
Initialization priority for the Microcrystal rv3032c7 MFD driver.
|
||||
It must be proor the RTC, COUNTER and Sensor driver init priority.
|
||||
397
drivers/mfd/mfd_rv3032.c
Normal file
397
drivers/mfd/mfd_rv3032.c
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Baylibre SAS
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/drivers/mfd/rv3032.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(mfd_rv3032, CONFIG_MFD_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT microcrystal_rv3032_mfd
|
||||
|
||||
#define RV3032_BSM_DISABLED 0x0
|
||||
#define RV3032_BSM_DIRECT 0x1
|
||||
#define RV3032_BSM_LEVEL 0x2
|
||||
|
||||
#define RV3032_BSM_FROM_DT_INST(inst) \
|
||||
UTIL_CAT(RV3032_BSM_, DT_INST_STRING_UPPER_TOKEN(inst, backup_switch_mode))
|
||||
|
||||
#define RV3032_BACKUP_FROM_DT_INST(inst) (FIELD_PREP(0, RV3032_BSM_FROM_DT_INST(inst)))
|
||||
|
||||
|
||||
struct mfd_rv3032_child {
|
||||
const struct device *dev;
|
||||
child_isr_t child_isr;
|
||||
};
|
||||
|
||||
struct mfd_rv3032_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
struct gpio_dt_spec gpio_int;
|
||||
struct gpio_dt_spec gpio_evi;
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
uint8_t aon;
|
||||
uint8_t backup;
|
||||
};
|
||||
|
||||
struct mfd_rv3032_data {
|
||||
struct k_sem lock;
|
||||
struct k_work work;
|
||||
struct gpio_callback int_callback;
|
||||
const struct device *dev;
|
||||
struct mfd_rv3032_child children[RV3032_DEV_MAX];
|
||||
};
|
||||
|
||||
static void mfd_rv3032_lock_sem(const struct device *dev)
|
||||
{
|
||||
struct mfd_rv3032_data *data = dev->data;
|
||||
|
||||
(void)k_sem_take(&data->lock, K_FOREVER);
|
||||
|
||||
}
|
||||
|
||||
static void mfd_rv3032_unlock_sem(const struct device *dev)
|
||||
{
|
||||
struct mfd_rv3032_data *data = dev->data;
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
|
||||
}
|
||||
|
||||
static void mfd_rv3032_fire_child_callback(struct mfd_rv3032_data *data, enum child_dev child_idx)
|
||||
{
|
||||
struct mfd_rv3032_child *child = &data->children[child_idx];
|
||||
|
||||
if (child->child_isr != NULL) {
|
||||
child->child_isr(child->dev);
|
||||
} else {
|
||||
LOG_WRN("child_isr missing (%d)", child_idx);
|
||||
}
|
||||
}
|
||||
|
||||
static void mfd_rv3032_work_cb(struct k_work *work)
|
||||
{
|
||||
struct mfd_rv3032_data *data = CONTAINER_OF(work, struct mfd_rv3032_data, work);
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
ret = mfd_rv3032_read_reg8(data->dev, RV3032_REG_STATUS, &status);
|
||||
if (ret) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Voltage Low Flag */
|
||||
if (status & RV3032_STATUS_VLF) {
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_VLF);
|
||||
LOG_DBG("(STATUS) Voltage Low Flag (%x)\n", status);
|
||||
}
|
||||
|
||||
/* Power On Reset Flag */
|
||||
if (status & RV3032_STATUS_PORF) {
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_PORF);
|
||||
LOG_DBG("(STATUS) Power On Reset Flag (%x)\n", status);
|
||||
}
|
||||
|
||||
/* External Event Flag */
|
||||
if (status & RV3032_STATUS_EVF) {
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_EVF);
|
||||
LOG_DBG("(STATUS) External Event Flag (%x)\n", status);
|
||||
/* TODO : Implement EVI handling */
|
||||
}
|
||||
|
||||
/* Alarm RTC - RTC */
|
||||
if (status & RV3032_STATUS_AF) {
|
||||
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_AF);
|
||||
mfd_rv3032_fire_child_callback(data, RV3032_DEV_RTC_ALARM);
|
||||
LOG_DBG("(STATUS) Alarm RTC (%x)\n", status);
|
||||
}
|
||||
|
||||
/* Periodic counter - COUNTER*/
|
||||
if (status & RV3032_STATUS_TF) {
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_TF);
|
||||
mfd_rv3032_fire_child_callback(data, RV3032_DEV_COUNTER);
|
||||
LOG_DBG("(STATUS) Periodic counter (%x)\n", status);
|
||||
}
|
||||
|
||||
/* Periodic time update Flag - RTC */
|
||||
if (status & RV3032_STATUS_UF) {
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_UF);
|
||||
mfd_rv3032_fire_child_callback(data, RV3032_DEV_RTC_UPDATE);
|
||||
LOG_DBG("(STATUS) Periodic time update Flag (%x)\n", status);
|
||||
}
|
||||
|
||||
/* Temperature Low/High Flags - SENSORS */
|
||||
if ((status & RV3032_STATUS_TLF) || (status & RV3032_STATUS_THF)) {
|
||||
ret = mfd_rv3032_clear_status(data->dev, RV3032_STATUS_TLF | RV3032_STATUS_THF);
|
||||
mfd_rv3032_fire_child_callback(data, RV3032_DEV_SENSOR);
|
||||
LOG_DBG("(STATUS) Temperature Low/High Flag (%x)\n", status);
|
||||
}
|
||||
|
||||
/* Check if interrupt occurred between STATUS read/write */
|
||||
if (ret) {
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mfd_rv3032_isr(const struct device *port, struct gpio_callback *cb,
|
||||
gpio_port_pins_t pins)
|
||||
{
|
||||
struct mfd_rv3032_data *data = CONTAINER_OF(cb, struct mfd_rv3032_data, int_callback);
|
||||
|
||||
ARG_UNUSED(port);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
|
||||
void mfd_rv3032_set_irq_handler(const struct device *dev, const struct device *child_dev,
|
||||
enum child_dev child_idx, child_isr_t handler)
|
||||
{
|
||||
struct mfd_rv3032_data *data = dev->data;
|
||||
struct mfd_rv3032_child *child;
|
||||
|
||||
if ((child_idx <= RV3032_DEV_REG) || (child_idx >= RV3032_DEV_MAX)) {
|
||||
LOG_ERR("Not valid child IRQ idx [%d]\n", child_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((handler == NULL) || (child_dev == NULL)) {
|
||||
LOG_ERR("Child handler or dev pointer is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set child device pointer */
|
||||
child = &data->children[child_idx];
|
||||
|
||||
switch (child_idx) {
|
||||
case RV3032_DEV_RTC_ALARM:
|
||||
LOG_DBG("Add IRQ handler for (RTC ALARM)");
|
||||
break;
|
||||
case RV3032_DEV_RTC_UPDATE:
|
||||
LOG_DBG("Add IRQ handler for (RTC UPDATE)");
|
||||
break;
|
||||
case RV3032_DEV_COUNTER:
|
||||
LOG_DBG("Add IRQ handler for (COUNTER)");
|
||||
break;
|
||||
case RV3032_DEV_SENSOR:
|
||||
LOG_DBG("Add IRQ handler for (SENSOR)");
|
||||
break;
|
||||
case RV3032_DEV_REG:
|
||||
case RV3032_DEV_MAX:
|
||||
default:
|
||||
LOG_ERR("Invalid child_id, out of usable range");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG(" child_dev[%p] handler[%p] (%d)\n", child_dev, handler, child_idx);
|
||||
|
||||
/* Store the interrupt handler and device instance for child device */
|
||||
child->dev = child_dev;
|
||||
child->child_isr = handler;
|
||||
}
|
||||
|
||||
int mfd_rv3032_read_regs(const struct device *dev, uint8_t addr, void *buf, size_t len)
|
||||
{
|
||||
const struct mfd_rv3032_config *config = dev->config;
|
||||
int err = 0;
|
||||
|
||||
mfd_rv3032_lock_sem(dev);
|
||||
err = i2c_write_read_dt(&config->i2c, &addr, sizeof(addr), buf, len);
|
||||
mfd_rv3032_unlock_sem(dev);
|
||||
if (err) {
|
||||
LOG_ERR("failed to read reg addr 0x%02x, len %d (err %d)", addr, len, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mfd_rv3032_read_reg8(const struct device *dev, uint8_t addr, uint8_t *val)
|
||||
{
|
||||
return mfd_rv3032_read_regs(dev, addr, val, sizeof(*val));
|
||||
}
|
||||
|
||||
int mfd_rv3032_write_regs(const struct device *dev, uint8_t addr, void *buf, size_t len)
|
||||
{
|
||||
const struct mfd_rv3032_config *config = dev->config;
|
||||
uint8_t block[sizeof(addr) + len];
|
||||
int err = 0;
|
||||
|
||||
mfd_rv3032_lock_sem(dev);
|
||||
block[0] = addr;
|
||||
memcpy(&block[1], buf, len);
|
||||
err = i2c_write_dt(&config->i2c, block, sizeof(block));
|
||||
mfd_rv3032_unlock_sem(dev);
|
||||
if (err) {
|
||||
LOG_ERR("failed to write reg addr 0x%02x, len %d (err %d)", addr, len, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mfd_rv3032_write_reg8(const struct device *dev, uint8_t addr, uint8_t val)
|
||||
{
|
||||
return mfd_rv3032_write_regs(dev, addr, &val, sizeof(val));
|
||||
}
|
||||
|
||||
int mfd_rv3032_update_reg8(const struct device *dev, uint8_t addr, uint8_t mask, uint8_t val)
|
||||
{
|
||||
const struct mfd_rv3032_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
mfd_rv3032_lock_sem(dev);
|
||||
err = i2c_reg_update_byte_dt(&config->i2c, addr, mask, val);
|
||||
mfd_rv3032_unlock_sem(dev);
|
||||
if (err) {
|
||||
LOG_ERR("failed to update reg addr 0x%02x, mask 0x%02x, val 0x%02x (err %d)", addr,
|
||||
mask, val, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mfd_rv3032_update_status(const struct device *dev, uint8_t mask, uint8_t val)
|
||||
{
|
||||
const struct mfd_rv3032_config *config = dev->config;
|
||||
int err;
|
||||
uint8_t old_val, new_val;
|
||||
uint8_t addr = RV3032_REG_STATUS;
|
||||
|
||||
err = i2c_reg_read_byte_dt(&config->i2c, addr, &old_val);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
new_val = (old_val & ~mask) | (val & mask);
|
||||
if (new_val == old_val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = i2c_reg_write_byte_dt(&config->i2c, addr, new_val);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (new_val) {
|
||||
LOG_DBG("Pending event!");
|
||||
}
|
||||
|
||||
return new_val;
|
||||
}
|
||||
|
||||
static int mfd_rv3032_init(const struct device *dev)
|
||||
{
|
||||
struct mfd_rv3032_data *data = dev->data;
|
||||
const struct mfd_rv3032_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
|
||||
if (!i2c_is_ready_dt(&(config->i2c))) {
|
||||
LOG_ERR("I2C bus not ready.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Clean all pending alarms and interrupts if in AON or backup mode
|
||||
* in case of AON enabled, chip have uninterruptible power supply
|
||||
* so we act like in one of the active backup modes, otherwise
|
||||
* POR bit and interrupt bit are discarded and cleaned
|
||||
*/
|
||||
if ((!config->aon) && (config->backup == RV3032_BSM_DISABLED)) {
|
||||
uint8_t status;
|
||||
|
||||
/* Set to default all configuration register at once
|
||||
* According to datasheet they all have default 0 values
|
||||
* - CONTROL1
|
||||
* - CONTROL2
|
||||
* - CONTROL3
|
||||
* - Time Stamp Control
|
||||
* - Clock Interrupt Control
|
||||
* - EVI Control
|
||||
*/
|
||||
|
||||
/* Clean all IRQ (RTC, Update, Counter) */
|
||||
err = mfd_rv3032_read_reg8(dev, RV3032_REG_STATUS, &status);
|
||||
if (err) {
|
||||
LOG_ERR("Status register read failed after EEPROM refresh: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (status & RV3032_STATUS_PORF) {
|
||||
LOG_WRN("POR detected with AON and BACKUP disabled (MCU reset?)");
|
||||
}
|
||||
|
||||
status = 0;
|
||||
/* Clean all IRQs (RTC, Update, Counter) */
|
||||
err = mfd_rv3032_write_reg8(dev, RV3032_REG_STATUS, status);
|
||||
if (err) {
|
||||
LOG_ERR("Status register write failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t zero_buff[6] = {0};
|
||||
/* Clean all regs at once: CONTROL1, CONTROL2, CONTROL3, TIME STAMP CONTROL,
|
||||
* CLOCK INTERRUPT CONTROL, EVI CONTROL
|
||||
*/
|
||||
err = mfd_rv3032_write_regs(dev, RV3032_REG_CONTROL1, zero_buff, 6);
|
||||
if (err) {
|
||||
LOG_ERR("CONTROL register write failed after EEPROM refresh: %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (config->gpio_int.port != NULL) {
|
||||
if (!gpio_is_ready_dt(&config->gpio_int)) {
|
||||
LOG_ERR("GPIO not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = gpio_pin_configure_dt(&config->gpio_int, GPIO_INPUT);
|
||||
if (err) {
|
||||
LOG_ERR("failed to configure GPIO (err %d)", err);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = gpio_pin_interrupt_configure_dt(&config->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (err) {
|
||||
LOG_ERR("failed to enable GPIO interrupt (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->int_callback, mfd_rv3032_isr, BIT(config->gpio_int.pin));
|
||||
|
||||
err = gpio_add_callback_dt(&config->gpio_int, &data->int_callback);
|
||||
if (err) {
|
||||
LOG_ERR("failed to add GPIO callback (err %d)", err);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
data->work.handler = mfd_rv3032_work_cb;
|
||||
} else {
|
||||
LOG_DBG("No GPIO INT in use!");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MFD_RV3032_DEFINE(inst) \
|
||||
static const struct mfd_rv3032_config mfd_rv3032_config##inst = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
|
||||
.gpio_evi = GPIO_DT_SPEC_INST_GET_OR(inst, evi_gpios, {0}), \
|
||||
.backup = RV3032_BACKUP_FROM_DT_INST(inst), \
|
||||
.aon = DT_INST_PROP_OR(ints, always_on, 0), \
|
||||
}; \
|
||||
\
|
||||
static struct mfd_rv3032_data mfd_rv3032_data##inst; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, &mfd_rv3032_init, NULL, &mfd_rv3032_data##inst, \
|
||||
&mfd_rv3032_config##inst, POST_KERNEL, \
|
||||
CONFIG_MFD_MICROCRYSTAL_RV3032_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MFD_RV3032_DEFINE)
|
||||
78
dts/bindings/mfd/microcrystal,rv3032-mfd.yaml
Normal file
78
dts/bindings/mfd/microcrystal,rv3032-mfd.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
# Copyright (c) 2025 Baylibre SAS
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: |
|
||||
Microcrystal rv-3032-c7 I2C MFD
|
||||
|
||||
The following example displays the node layout
|
||||
with every possible partial driver included.
|
||||
|
||||
rv3032: rv3032c7@51 {
|
||||
compatible = "microcrystal,rv3032-mfd";
|
||||
reg = <0x51>;
|
||||
int-gpios = <&porta 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
evi-gpios = <&porta 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
backup-switch-mode = "disabled";
|
||||
always-on;
|
||||
status = "okay";
|
||||
|
||||
rtc0: rv3032_rtc {
|
||||
compatible = "microcrystal,rv3032";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
counter0: rv3032_counter {
|
||||
compatible = "microcrystal,rv3032-counter";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
temp0: rv3032_temp {
|
||||
compatible = "microcrystal,rv3032-temp";
|
||||
low-temp = <40>;
|
||||
high-temp = <105>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "microcrystal,rv3032-mfd"
|
||||
|
||||
include:
|
||||
- name: i2c-device.yaml
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
GPIO connected to the RV3028 INT interrupt output. This signal is open-drain, active low.
|
||||
|
||||
evi-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
GPIO connected to the RV3028 EVI event/interrupt input. This signal is open-drain, active low.
|
||||
|
||||
always-on:
|
||||
type: boolean
|
||||
description: |
|
||||
AON stand for - Always On. This is not part of rv-3032-c7 chip functionality but
|
||||
real use case where main power is kept always on and advanced backup-switch is not
|
||||
used as expected.
|
||||
- true : Driver act like power was not interrupted on next device init
|
||||
- false : Every boot and execution of driver init function is like fresh boot.
|
||||
alarms are cleaned and device is clean.
|
||||
|
||||
backup-switch-mode:
|
||||
type: string
|
||||
required: true
|
||||
enum:
|
||||
- disabled
|
||||
- direct
|
||||
- level
|
||||
description: |
|
||||
Automatic backup switchover function selection:
|
||||
- disabled: The switchover function is disabled - only one power supply available (VDD)
|
||||
- direct: Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to
|
||||
VBACKUP without requiring VDD to drop below VTH:LSM (2.0 V)
|
||||
- level: Level Switching Mode (LSM): when VDD < VTH:LSM (2.0 V) AND VBACKUP > VTH:LSM,
|
||||
switchover occurs from VDD to VBACKUP
|
||||
161
include/zephyr/drivers/mfd/rv3032.h
Normal file
161
include/zephyr/drivers/mfd/rv3032.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Baylibre SAS
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_RV3032_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_MFD_RV3032_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
|
||||
/* RV3032 RAM register addresses */
|
||||
#define RV3032_REG_100TH_SECONDS 0x00
|
||||
#define RV3032_REG_SECONDS 0x01
|
||||
#define RV3032_REG_MINUTES 0x02
|
||||
#define RV3032_REG_HOURS 0x03
|
||||
#define RV3032_REG_WEEKDAY 0x04
|
||||
#define RV3032_REG_DATE 0x05
|
||||
#define RV3032_REG_MONTH 0x06
|
||||
#define RV3032_REG_YEAR 0x07
|
||||
#define RV3032_REG_ALARM_MINUTES 0x08
|
||||
#define RV3032_REG_ALARM_HOURS 0x09
|
||||
#define RV3032_REG_ALARM_DATE 0x0A
|
||||
#define RV3032_REG_TIMER_VALUE_0 0x0B
|
||||
#define RV3032_REG_TIMER_VALUE_1 0x0C
|
||||
#define RV3032_REG_STATUS 0x0D
|
||||
#define RV3032_REG_TEMPERATURE_LSB 0x0E
|
||||
#define RV3032_REG_TEMPERATURE_MSB 0x0F
|
||||
#define RV3032_REG_CONTROL1 0x10
|
||||
#define RV3032_REG_CONTROL2 0x11
|
||||
#define RV3032_REG_CONTROL3 0x12
|
||||
#define RV3032_TS_CTRL 0x13
|
||||
#define RV3032_CLK_INT_MASK 0x14
|
||||
#define RV3032_EVI_CONTROL 0x15
|
||||
#define RV3032_REG_TEMP_LOW_THLD 0x16
|
||||
#define RV3032_REG_TEMP_HIGH_THLD 0x17
|
||||
|
||||
/* EEPROM register addresses */
|
||||
#define RV3032_REG_EEPROM_ADDRESS 0x3D
|
||||
#define RV3032_REG_EEPROM_DATA 0x3E
|
||||
#define RV3032_REG_EEPROM_COMMAND 0x3F
|
||||
#define RV3032_REG_EEPROM_PMU 0xC0
|
||||
#define RV3032_REG_EEPROM_RAM1 0x40
|
||||
#define RV3032_REG_EEPROM_OFFSET 0xC1
|
||||
#define RV3032_REG_EEPROM_CLKOUT1 0xC2
|
||||
#define RV3032_REG_EEPROM_CLKOUT2 0xC3
|
||||
#define RV3032_REG_EEPROM_TREF0 0xC4
|
||||
#define RV3032_REG_EEPROM_TREF1 0xC5
|
||||
|
||||
/* Registers masks and bits */
|
||||
#define RV3032_CONTROL1_TD GENMASK(1, 0)
|
||||
#define RV3032_CONTROL1_EERD BIT(2)
|
||||
#define RV3032_CONTROL1_TE BIT(3)
|
||||
#define RV3032_CONTROL1_USEL BIT(4)
|
||||
#define RV3032_CONTROL1_GP0 BIT(5)
|
||||
|
||||
#define RV3032_CONTROL2_STOP BIT(0)
|
||||
#define RV3032_CONTROL2_GP1 BIT(1)
|
||||
#define RV3032_CONTROL2_EIE BIT(2)
|
||||
#define RV3032_CONTROL2_AIE BIT(3)
|
||||
#define RV3032_CONTROL2_TIE BIT(4)
|
||||
#define RV3032_CONTROL2_UIE BIT(5)
|
||||
#define RV3032_CONTROL2_CLKIE BIT(6)
|
||||
|
||||
#define RV3032_CONTROL3_TLIE BIT(0)
|
||||
#define RV3032_CONTROL3_THIE BIT(1)
|
||||
#define RV3032_CONTROL3_TLE BIT(2)
|
||||
#define RV3032_CONTROL3_THE BIT(3)
|
||||
#define RV3032_CONTROL3_BSIE BIT(4)
|
||||
|
||||
#define RV3032_100TH_SECONDS_MASK GENMASK(7, 0)
|
||||
#define RV3032_SECONDS_MASK GENMASK(6, 0)
|
||||
#define RV3032_MINUTES_MASK GENMASK(6, 0)
|
||||
#define RV3032_HOURS_AMPM BIT(5)
|
||||
#define RV3032_HOURS_12H_MASK GENMASK(4, 0)
|
||||
#define RV3032_HOURS_24H_MASK GENMASK(5, 0)
|
||||
#define RV3032_DATE_MASK GENMASK(5, 0)
|
||||
#define RV3032_WEEKDAY_MASK GENMASK(2, 0)
|
||||
#define RV3032_MONTH_MASK GENMASK(4, 0)
|
||||
#define RV3032_YEAR_MASK GENMASK(7, 0)
|
||||
|
||||
#define RV3032_ALARM_MINUTES_AE_M BIT(7)
|
||||
#define RV3032_ALARM_MINUTES_MASK GENMASK(6, 0)
|
||||
#define RV3032_ALARM_HOURS_AE_H BIT(7)
|
||||
#define RV3032_ALARM_HOURS_AMPM BIT(5)
|
||||
#define RV3032_ALARM_HOURS_12H_MASK GENMASK(4, 0)
|
||||
#define RV3032_ALARM_HOURS_24H_MASK GENMASK(5, 0)
|
||||
#define RV3032_ALARM_DATE_AE_D BIT(7)
|
||||
#define RV3032_ALARM_DATE_MASK GENMASK(5, 0)
|
||||
|
||||
#define RV3032_STATUS_VLF BIT(0)
|
||||
#define RV3032_STATUS_PORF BIT(1)
|
||||
#define RV3032_STATUS_EVF BIT(2)
|
||||
#define RV3032_STATUS_AF BIT(3)
|
||||
#define RV3032_STATUS_TF BIT(4)
|
||||
#define RV3032_STATUS_UF BIT(5)
|
||||
#define RV3032_STATUS_TLF BIT(6)
|
||||
#define RV3032_STATUS_THF BIT(7)
|
||||
|
||||
#define RV3032_EEPROM_CLKOUT1_HFD_LOW GENMASK(7, 0)
|
||||
#define RV3032_TEMPERATURE_BSF BIT(0)
|
||||
#define RV3032_TEMPERATURE_CLKF BIT(1)
|
||||
#define RV3032_TEMPERATURE_EEBUSY BIT(2)
|
||||
#define RV3032_TEMPERATURE_EEF BIT(3)
|
||||
#define RV3032_TEMPERATURE_TEMP_LSB GENMASK(7, 4)
|
||||
|
||||
#define RV3032_EEPROM_PMU_NCLKE BIT(6)
|
||||
#define RV3032_EEPROM_PMU_BSM GENMASK(5, 4)
|
||||
#define RV3032_EEPROM_PMU_TCR GENMASK(3, 2)
|
||||
#define RV3032_EEPROM_PMU_TCM GENMASK(1, 0)
|
||||
|
||||
#define RV3032_EEPROM_CLKOUT2_OS BIT(7)
|
||||
#define RV3032_EEPROM_CLKOUT2_FD GENMASK(6, 5)
|
||||
#define RV3032_EEPROM_CLKOUT2_HFD_HIGH GENMASK(4, 0)
|
||||
|
||||
/* The RV3032 only supports two-digit years. Leap years are correctly handled from 2000 to 2099 */
|
||||
#define RV3032_YEAR_OFFSET (2000 - 1900)
|
||||
|
||||
/* The RV3032 enumerates months 1 to 12 */
|
||||
#define RV3032_MONTH_OFFSET 1
|
||||
|
||||
typedef void (*child_isr_t)(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Child device of rv3032
|
||||
*/
|
||||
enum child_dev {
|
||||
RV3032_DEV_REG = 0,
|
||||
RV3032_DEV_RTC_ALARM,
|
||||
RV3032_DEV_RTC_UPDATE,
|
||||
RV3032_DEV_COUNTER,
|
||||
RV3032_DEV_SENSOR,
|
||||
RV3032_DEV_MAX, /** Maximum number of child devices */
|
||||
};
|
||||
|
||||
int mfd_rv3032_read_regs(const struct device *dev, uint8_t addr, void *buf, size_t len);
|
||||
int mfd_rv3032_read_reg8(const struct device *dev, uint8_t addr, uint8_t *val);
|
||||
int mfd_rv3032_write_regs(const struct device *dev, uint8_t addr, void *buf, size_t len);
|
||||
int mfd_rv3032_write_reg8(const struct device *dev, uint8_t addr, uint8_t val);
|
||||
int mfd_rv3032_update_reg8(const struct device *dev, uint8_t addr, uint8_t mask, uint8_t val);
|
||||
void mfd_rv3032_set_irq_handler(const struct device *dev, const struct device *child_dev,
|
||||
enum child_dev child_idx, child_isr_t handler);
|
||||
|
||||
#define mfd_rv3032_set_status(dev, mask) mfd_rv3032_update_status(dev, mask, 0xff)
|
||||
#define mfd_rv3032_clear_status(dev, mask) mfd_rv3032_update_status(dev, mask, 0x00)
|
||||
|
||||
int mfd_rv3032_update_status(const struct device *dev, uint8_t mask, uint8_t val);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_RV3032_H_ */
|
||||
Reference in New Issue
Block a user