bsps/shared: Add Abracom EOZ9 RTC driver

The EOZ9 RTC has a similar register interface like the MCP7940M (and
quite some other I2C RTCs). This commit:

* Extracts the generic parts from MCP7940M and moves it into a generic
  i2c-rtc driver.
* Uses the new i2c-rtc for the MCP7940M.
* Uses the new i2c-rtc for the new Abracom EOZ9.
This commit is contained in:
Christian Mauderer
2024-07-31 15:22:06 +02:00
committed by Amar Takhar
parent acf7c725ca
commit 3b053f6919
7 changed files with 787 additions and 324 deletions

View File

@@ -0,0 +1,156 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup shared_tod_abeoz9_rtc
*
* @brief This file provides the interfaces of @ref shared_tod_abeoz9_rtc.
*/
/*
* Copyright (C) 2024 embedded brains GmbH & Co. KG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <dev/i2c/i2c.h>
#include <libchip/abeoz9-rtc.h>
#include <stdint.h>
#define REG_ABEOZ9_CONTROL_1 0x00u
#define ABEOZ9_CONTROL_1_WE (0x1u << 0)
#define ABEOZ9_CONTROL_1_TE (0x1u << 1)
#define ABEOZ9_CONTROL_1_TAR (0x1u << 2)
#define ABEOZ9_CONTROL_1_EERE (0x1u << 3)
#define ABEOZ9_CONTROL_1_SROn (0x1u << 4)
#define ABEOZ9_CONTROL_1_TD0 (0x1u << 5)
#define ABEOZ9_CONTROL_1_TD1 (0x1u << 6)
#define ABEOZ9_CONTROL_1_ClkInt (0x1u << 7)
#define REG_ABEOZ9_CONTROL_INT 0x01u
#define ABEOZ9_CONTROL_INT_AIE (0x1u << 0)
#define ABEOZ9_CONTROL_INT_TIE (0x1u << 1)
#define ABEOZ9_CONTROL_INT_V1IE (0x1u << 2)
#define ABEOZ9_CONTROL_INT_V2IE (0x1u << 3)
#define ABEOZ9_CONTROL_INT_SRIE (0x1u << 4)
#define REG_ABEOZ9_CONTROL_INT_FLAG 0x02u
#define ABEOZ9_CONTROL_INT_FLAG_AF (0x1u << 0)
#define ABEOZ9_CONTROL_INT_FLAG_TF (0x1u << 1)
#define ABEOZ9_CONTROL_INT_FLAG_V1IF (0x1u << 2)
#define ABEOZ9_CONTROL_INT_FLAG_V2IF (0x1u << 3)
#define ABEOZ9_CONTROL_INT_FLAG_SRF (0x1u << 4)
#define REG_ABEOZ9_CONTROL_STATUS 0x03u
#define ABEOZ9_CONTROL_STATUS_V1F (0x1u << 2)
#define ABEOZ9_CONTROL_STATUS_V2F (0x1u << 3)
#define ABEOZ9_CONTROL_STATUS_SR (0x1u << 4)
#define ABEOZ9_CONTROL_STATUS_PON (0x1u << 5)
#define ABEOZ9_CONTROL_STATUS_EEBusy (0x1u << 7)
#define REG_ABEOZ9_CONTROL_RESET 0x04u
#define ABEOZ9_CONTROL_RESET_SysR (0x1u << 1)
#define REG_ABEOZ9_CLOCK_SEC 0x08u
#define REG_ABEOZ9_CLOCK_MIN 0x09u
#define REG_ABEOZ9_CLOCK_HOUR 0x0au
#define REG_ABEOZ9_CLOCK_WKDAY 0x0bu
#define REG_ABEOZ9_CLOCK_DATE 0x0cu
#define REG_ABEOZ9_CLOCK_MTH 0x0du
#define REG_ABEOZ9_CLOCK_YEAR 0x0eu
#define CLOCK_LEN (REG_ABEOZ9_CLOCK_YEAR + 1 - REG_ABEOZ9_CLOCK_SEC)
#define REG_ABEOZ9_ALARM_SEC 0x10u
#define REG_ABEOZ9_ALARM_MIN 0x11u
#define REG_ABEOZ9_ALARM_HOUR 0x12u
#define REG_ABEOZ9_ALARM_WKDAY 0x13u
#define REG_ABEOZ9_ALARM_DATE 0x14u
#define REG_ABEOZ9_ALARM_MTH 0x15u
#define REG_ABEOZ9_ALARM_YEAR 0x16u
#define ALARM_LEN (REG_ABEOZ9_ALARM_YEAR + 1 - REG_ABEOZ9_ALARM_SEC)
int abeoz9_rtc_hw_init(struct i2c_rtc_base *base)
{
uint8_t reg;
ssize_t rv;
struct abeoz9_rtc *ctx =
RTEMS_CONTAINER_OF(base, struct abeoz9_rtc, base);
/*
* Check PON bit. If that is set, we are starting with an uninitialized chip.
*/
rv = i2c_rtc_read(&ctx->base, REG_ABEOZ9_CONTROL_STATUS, &reg, 1);
if (rv == 0) {
if ((reg & (ABEOZ9_CONTROL_STATUS_PON | ABEOZ9_CONTROL_STATUS_SR)) != 0) {
/*
* First power up after a reset or a self recovery reset. Init state is
* like follows (from the data sheet):
*
* - CLKOUT is selected at CLKOUT pin, default frequency is 32.768 kHz
* defined in register EEPROM Control
* - Timer and Timer Auto-Reload mode are disabled; Timer Source Clock
* frequency is set to 32Hz
* - Self Recovery function is enabled
* - Automatic EEPROM Refresh every hour is enabled
* - 24 hour mode is selected, no Alarm is set
* - All Interrupts are disabled
* - At Power-On Reset, "PON" Flag is set = "1" and has to be cleared by
* writing = "0"
* - At Self-Recovery Reset or System Reset, "SR" Flag is set = "1" and
* has to be cleared by writing = "0".
* - Values in the clock page are undefined.
*
* That's a quite sensible default. Only a well defined value for the time
* and the PON / SR flags have to be set.
*/
uint8_t clock[CLOCK_LEN] = {0, 0, 0, 1, 1, 1, 1};
rv = i2c_rtc_write(&ctx->base, REG_ABEOZ9_CLOCK_SEC, clock, CLOCK_LEN);
if (rv == 0) {
reg &= ~(ABEOZ9_CONTROL_STATUS_PON | ABEOZ9_CONTROL_STATUS_SR);
rv = i2c_rtc_write(&ctx->base, REG_ABEOZ9_CONTROL_STATUS, &reg, 1);
}
} else {
/*
* RTC already active. Disable all alarms and similar functionality.
* Otherwise leave it in default state.
*/
uint8_t alarm[ALARM_LEN] = {0, 0, 0, 0, 0, 0, 0};
reg = 0;
rv = i2c_rtc_write(&ctx->base, REG_ABEOZ9_CONTROL_INT, &reg, 1);
reg = 0;
rv = i2c_rtc_write(&ctx->base, REG_ABEOZ9_ALARM_SEC, alarm, ALARM_LEN);
}
}
return rv;
}

View File

@@ -0,0 +1,317 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup shared_tod_i2c_rtc
*
* @brief This file provides the implementation of @ref shared_tod_i2c_rtc.
*/
/*
* Copyright (C) 2023-2024 embedded brains GmbH & Co. KG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <dev/i2c/i2c.h>
#include <libchip/i2c-rtc.h>
#include <rtems/score/sysstate.h>
#include <rtems/score/todimpl.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define REG_RTCSEC 0x00u
#define RTCSEC_SECBCD_SHIFT 0u
#define RTCSEC_SECBCD_MASK (0x7fu << RTCSEC_SECBCD_SHIFT)
#define RTCSEC_SECBCD(x) (((x) << RTCSEC_SECBCD_SHIFT) & RTCSEC_SECBCD_MASK)
#define RTCSEC_SECBCD_GET(x) (((x) & RTCSEC_SECBCD_MASK) >> RTCSEC_SECBCD_SHIFT)
#define REG_RTCMIN 0x01u
#define RTCMIN_MINBCD_SHIFT 0u
#define RTCMIN_MINBCD_MASK (0x7fu << RTCMIN_MINBCD_SHIFT)
#define RTCMIN_MINBCD(x) (((x) << RTCMIN_MINBCD_SHIFT) & RTCMIN_MINBCD_MASK)
#define RTCMIN_MINBCD_GET(x) (((x) & RTCMIN_MINBCD_MASK) >> RTCMIN_MINBCD_SHIFT)
#define REG_RTCHOUR 0x02u
#define RTCHOUR_HRBCD12_SHIFT 0u
#define RTCHOUR_HRBCD12_MASK (0x1fu << RTCHOUR_HRBCD12_SHIFT)
#define RTCHOUR_HRBCD12(x) (((x) << RTCHOUR_HRBCD12_SHIFT) & RTCHOUR_HRBCD12_MASK)
#define RTCHOUR_HRBCD12_GET(x) (((x) & RTCHOUR_HRBCD12_MASK) >> RTCHOUR_HRBCD12_SHIFT)
#define RTCHOUR_HRBCD24_SHIFT 0u
#define RTCHOUR_HRBCD24_MASK (0x3fu << RTCHOUR_HRBCD24_SHIFT)
#define RTCHOUR_HRBCD24(x) (((x) << RTCHOUR_HRBCD24_SHIFT) & RTCHOUR_HRBCD24_MASK)
#define RTCHOUR_HRBCD24_GET(x) (((x) & RTCHOUR_HRBCD24_MASK) >> RTCHOUR_HRBCD24_SHIFT)
#define RTCHOUR_AMPM (0x01u << 5)
#define RTCHOUR_1224 (0x01u << 6)
#define REG_RTCWKDAY 0x03u
#define RTCWKDAY_WKDAY_SHIFT 0u
#define RTCWKDAY_WKDAY_MASK (0x7u << RTCWKDAY_WKDAY_SHIFT)
#define RTCWKDAY_WKDAY(x) (((x) << RTCWKDAY_WKDAY_SHIFT) & RTCWKDAY_WKDAY_MASK)
#define RTCWKDAY_WKDAY_GET(x) (((x) & RTCWKDAY_WKDAY_MASK) >> RTCWKDAY_WKDAY_SHIFT)
#define REG_RTCDATE 0x04u
#define RTCDATE_DATEBCD_SHIFT 0u
#define RTCDATE_DATEBCD_MASK (0x3fu << RTCDATE_DATEBCD_SHIFT)
#define RTCDATE_DATEBCD(x) (((x) << RTCDATE_DATEBCD_SHIFT) & RTCDATE_DATEBCD_MASK)
#define RTCDATE_DATEBCD_GET(x) (((x) & RTCDATE_DATEBCD_MASK) >> RTCDATE_DATEBCD_SHIFT)
#define REG_RTCMTH 0x05u
#define RTCMTH_MTHBCD_SHIFT 0u
#define RTCMTH_MTHBCD_MASK (0x1fu << RTCMTH_MTHBCD_SHIFT)
#define RTCMTH_MTHBCD(x) (((x) << RTCMTH_MTHBCD_SHIFT) & RTCMTH_MTHBCD_MASK)
#define RTCMTH_MTHBCD_GET(x) (((x) & RTCMTH_MTHBCD_MASK) >> RTCMTH_MTHBCD_SHIFT)
#define REG_RTCYEAR 0x06u
#define RTCYEAR_YRBCD_SHIFT 0u
#define RTCYEAR_YRBCD_MASK (0xffu << RTCYEAR_YRBCD_SHIFT)
#define RTCYEAR_YRBCD(x) (((x) << RTCYEAR_YRBCD_SHIFT) & RTCYEAR_YRBCD_MASK)
#define RTCYEAR_YRBCD_GET(x) (((x) & RTCYEAR_YRBCD_MASK) >> RTCYEAR_YRBCD_SHIFT)
static inline uint8_t bcd_to_bin(uint8_t bcd)
{
uint8_t bin;
bin = bcd & 0x0f;
bin += ((bcd >> 4) & 0x0f) * 10;
return bin;
}
static inline uint8_t bin_to_bcd(uint8_t bin)
{
uint8_t bcd;
bcd = bin % 10;
bcd |= (bin / 10) << 4;
return bcd;
}
static struct i2c_rtc_base *i2c_rtc_get_context(int minor)
{
return (struct i2c_rtc_base *) RTC_Table[minor].pDeviceParams;
}
int i2c_rtc_read(
struct i2c_rtc_base *ctx,
uint8_t addr,
uint8_t *buf,
size_t len
)
{
int fd;
int rv;
struct i2c_msg msgs[] = {{
.addr = ctx->i2c_addr,
.flags = 0,
.buf = &addr,
.len = 1,
}, {
.addr = ctx->i2c_addr,
.flags = I2C_M_RD,
.buf = buf,
.len = len,
}};
struct i2c_rdwr_ioctl_data payload = {
.msgs = msgs,
.nmsgs = sizeof(msgs)/sizeof(msgs[0]),
};
fd = open(ctx->i2c_bus_path, O_RDWR);
if (fd < 0) {
return fd;
}
rv = ioctl(fd, I2C_RDWR, &payload);
close(fd);
return rv;
}
int i2c_rtc_write(
struct i2c_rtc_base *ctx,
uint8_t addr,
const uint8_t *buf,
size_t len
)
{
int fd;
int rv;
uint8_t writebuf[len + 1];
struct i2c_msg msgs[] = {{
.addr = ctx->i2c_addr,
.flags = 0,
.buf = writebuf,
.len = len + 1,
}};
struct i2c_rdwr_ioctl_data payload = {
.msgs = msgs,
.nmsgs = sizeof(msgs)/sizeof(msgs[0]),
};
writebuf[0] = addr;
memcpy(&writebuf[1], buf, len);
fd = open(ctx->i2c_bus_path, O_RDWR);
if (fd < 0) {
return fd;
}
rv = ioctl(fd, I2C_RDWR, &payload);
close(fd);
return rv;
}
static int i2c_rtc_initialize_once(struct i2c_rtc_base *ctx)
{
int rv;
if (ctx->initialized || ctx->hw_init == NULL) {
return 0;
}
rv = ctx->hw_init(ctx);
if (rv == 0) {
ctx->initialized = true;
}
return rv;
}
static int i2c_rtc_get_time(int minor, rtems_time_of_day *time)
{
int rv = 0;
uint8_t buf[REG_RTCYEAR + 1];
struct i2c_rtc_base *ctx = i2c_rtc_get_context(minor);
if (!_System_state_Is_up(_System_state_Get())) {
return -1;
}
rtems_mutex_lock(&ctx->mutex);
rv = i2c_rtc_initialize_once(ctx);
if (rv == 0) {
rv = i2c_rtc_read(ctx, REG_RTCSEC + ctx->clock_offset, buf, sizeof(buf));
}
if (rv == 0) {
unsigned year = bcd_to_bin(RTCYEAR_YRBCD_GET(buf[REG_RTCYEAR])) +
(TOD_BASE_YEAR / 100 * 100);
if (year < TOD_BASE_YEAR) {
year += 100;
}
time->year = year;
time->month = bcd_to_bin(RTCMTH_MTHBCD_GET(buf[REG_RTCMTH]));
time->day = bcd_to_bin(RTCDATE_DATEBCD_GET(buf[REG_RTCDATE]));
time->hour = bcd_to_bin(RTCHOUR_HRBCD24_GET(buf[REG_RTCHOUR]));
time->minute = bcd_to_bin(RTCMIN_MINBCD_GET(buf[REG_RTCMIN]));
time->second = bcd_to_bin(RTCSEC_SECBCD_GET(buf[REG_RTCSEC]));
time->ticks = 0;
}
rtems_mutex_unlock(&ctx->mutex);
return rv;
}
static int i2c_rtc_set_time(int minor, const rtems_time_of_day *time)
{
int rv = 0;
uint8_t buf[REG_RTCYEAR + 1];
struct i2c_rtc_base *ctx = i2c_rtc_get_context(minor);
if (!_System_state_Is_up(_System_state_Get())) {
return -1;
}
rtems_mutex_lock(&ctx->mutex);
rv = i2c_rtc_initialize_once(ctx);
if (rv == 0) {
rv = i2c_rtc_read(ctx, REG_RTCSEC + ctx->clock_offset, buf, sizeof(buf));
}
if (rv == 0) {
/* Make sure weekday is not 0 (out of range). Otherwise it's not used. */
if (RTCWKDAY_WKDAY_GET(buf[REG_RTCWKDAY]) < 1) {
buf[REG_RTCWKDAY] &= ~RTCWKDAY_WKDAY_MASK;
buf[REG_RTCWKDAY] |= RTCWKDAY_WKDAY(1);
}
buf[REG_RTCYEAR] &= ~RTCYEAR_YRBCD_MASK;
buf[REG_RTCYEAR] |= RTCYEAR_YRBCD(bin_to_bcd(time->year % 100));
buf[REG_RTCMTH] &= ~RTCMTH_MTHBCD_MASK;
buf[REG_RTCMTH] |= RTCMTH_MTHBCD(bin_to_bcd(time->month));
buf[REG_RTCDATE] &= ~RTCDATE_DATEBCD_MASK;
buf[REG_RTCDATE] |= RTCDATE_DATEBCD(bin_to_bcd(time->day));
buf[REG_RTCHOUR] &= ~(RTCHOUR_HRBCD24_MASK | RTCHOUR_1224);
buf[REG_RTCHOUR] |= RTCHOUR_HRBCD24(bin_to_bcd(time->hour));
buf[REG_RTCMIN] &= ~RTCMIN_MINBCD_MASK;
buf[REG_RTCMIN] |= RTCMIN_MINBCD(bin_to_bcd(time->minute));
buf[REG_RTCSEC] &= ~RTCSEC_SECBCD_MASK;
buf[REG_RTCSEC] |= RTCSEC_SECBCD(bin_to_bcd(time->second));
rv = i2c_rtc_write(ctx, REG_RTCSEC + ctx->clock_offset, buf, sizeof(buf));
}
rtems_mutex_unlock(&ctx->mutex);
return rv;
}
static void i2c_rtc_init(int minor)
{
(void) minor;
}
bool i2c_rtc_probe(int minor)
{
return true;
}
const rtc_fns i2c_rtc_fns = {
.deviceInitialize = i2c_rtc_init,
.deviceGetTime = i2c_rtc_get_time,
.deviceSetTime = i2c_rtc_set_time,
};

View File

@@ -1,5 +1,13 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup shared_tod_mcp7940m_rtc
*
* @brief This file provides the interfaces of @ref shared_tod_mcp7940m_rtc.
*/
/*
* Copyright (C) 2023 embedded brains GmbH & Co. KG
*
@@ -26,16 +34,6 @@
*/
/*
* Note: This driver implements only the basic RTC functionality of the
* MCP7940M. It tries not to touch any register fields except for the basic
* date/time fields in the get/set time functions. That way it should be
* possible to re-use the driver for similar RTCs by just replacing the
* initialization function. Suggested method for that: Add a field to the struct
* mcp7940m_rtc with a function pointer that points to the initialization
* function.
*
* All flags that are considered MCP7940M specific have a MCP7940M in the name.
*
* Only 24 hour format is supported. If this driver is the only ones who write
* the RTC, that shouldn't be a problem.
*
@@ -43,78 +41,19 @@
* and therefore doesn't really matter.
*/
#include <dev/i2c/i2c.h>
#include <libchip/mcp7940m-rtc.h>
#include <rtems/score/sysstate.h>
#include <rtems/score/todimpl.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define REG_RTCSEC 0x00u
#define RTCSEC_SECBCD_SHIFT 0u
#define RTCSEC_SECBCD_MASK (0x7fu << RTCSEC_SECBCD_SHIFT)
#define RTCSEC_SECBCD(x) (((x) << RTCSEC_SECBCD_SHIFT) & RTCSEC_SECBCD_MASK)
#define RTCSEC_SECBCD_GET(x) (((x) & RTCSEC_SECBCD_MASK) >> RTCSEC_SECBCD_SHIFT)
#define REG_MCP7940M_RTCSEC 0x00u
#define REG_MCP7940M_RTCMIN 0x01u
#define REG_MCP7940M_RTCHOUR 0x02u
#define REG_MCP7940M_RTCWKDAY 0x03u
#define REG_MCP7940M_RTCDATE 0x04u
#define REG_MCP7940M_RTCMTH 0x05u
#define REG_MCP7940M_RTCYEAR 0x06u
#define MCP7940M_RTCSEC_ST (0x01u << 7)
#define REG_RTCMIN 0x01
#define RTCMIN_MINBCD_SHIFT 0u
#define RTCMIN_MINBCD_MASK (0x7fu << RTCMIN_MINBCD_SHIFT)
#define RTCMIN_MINBCD(x) (((x) << RTCMIN_MINBCD_SHIFT) & RTCMIN_MINBCD_MASK)
#define RTCMIN_MINBCD_GET(x) (((x) & RTCMIN_MINBCD_MASK) >> RTCMIN_MINBCD_SHIFT)
#define REG_RTCHOUR 0x02
#define RTCHOUR_HRBCD12_SHIFT 0u
#define RTCHOUR_HRBCD12_MASK (0x1fu << RTCHOUR_HRBCD12_SHIFT)
#define RTCHOUR_HRBCD12(x) (((x) << RTCHOUR_HRBCD12_SHIFT) & RTCHOUR_HRBCD12_MASK)
#define RTCHOUR_HRBCD12_GET(x) (((x) & RTCHOUR_HRBCD12_MASK) >> RTCHOUR_HRBCD12_SHIFT)
#define RTCHOUR_HRBCD24_SHIFT 0u
#define RTCHOUR_HRBCD24_MASK (0x3fu << RTCHOUR_HRBCD24_SHIFT)
#define RTCHOUR_HRBCD24(x) (((x) << RTCHOUR_HRBCD24_SHIFT) & RTCHOUR_HRBCD24_MASK)
#define RTCHOUR_HRBCD24_GET(x) (((x) & RTCHOUR_HRBCD24_MASK) >> RTCHOUR_HRBCD24_SHIFT)
#define RTCHOUR_AMPM (0x01u << 5)
#define RTCHOUR_1224 (0x01u << 6)
#define REG_RTCWKDAY 0x03
#define RTCWKDAY_WKDAY_SHIFT 0u
#define RTCWKDAY_WKDAY_MASK (0x7u << RTCWKDAY_WKDAY_SHIFT)
#define RTCWKDAY_WKDAY(x) (((x) << RTCWKDAY_WKDAY_SHIFT) & RTCWKDAY_WKDAY_MASK)
#define RTCWKDAY_WKDAY_GET(x) (((x) & RTCWKDAY_WKDAY_MASK) >> RTCWKDAY_WKDAY_SHIFT)
#define REG_RTCDATE 0x04
#define RTCDATE_DATEBCD_SHIFT 0u
#define RTCDATE_DATEBCD_MASK (0x3fu << RTCDATE_DATEBCD_SHIFT)
#define RTCDATE_DATEBCD(x) (((x) << RTCDATE_DATEBCD_SHIFT) & RTCDATE_DATEBCD_MASK)
#define RTCDATE_DATEBCD_GET(x) (((x) & RTCDATE_DATEBCD_MASK) >> RTCDATE_DATEBCD_SHIFT)
#define REG_RTCMTH 0x05
#define RTCMTH_MTHBCD_SHIFT 0u
#define RTCMTH_MTHBCD_MASK (0x1fu << RTCMTH_MTHBCD_SHIFT)
#define RTCMTH_MTHBCD(x) (((x) << RTCMTH_MTHBCD_SHIFT) & RTCMTH_MTHBCD_MASK)
#define RTCMTH_MTHBCD_GET(x) (((x) & RTCMTH_MTHBCD_MASK) >> RTCMTH_MTHBCD_SHIFT)
#define MCP7940M_RTCMTH_LPYR (0x01u << 5)
#define REG_RTCYEAR 0x06
#define RTCYEAR_YRBCD_SHIFT 0u
#define RTCYEAR_YRBCD_MASK (0xffu << RTCYEAR_YRBCD_SHIFT)
#define RTCYEAR_YRBCD(x) (((x) << RTCYEAR_YRBCD_SHIFT) & RTCYEAR_YRBCD_MASK)
#define RTCYEAR_YRBCD_GET(x) (((x) & RTCYEAR_YRBCD_MASK) >> RTCYEAR_YRBCD_SHIFT)
#define REG_MCP7940M_CONTROL 0x07
#define MCP7940M_CONTROL_OUT (0x1u << 7)
@@ -126,108 +65,12 @@
#define MCP7940M_CONTROL_SQWFS1 (0x1u << 1)
#define MCP7940M_CONTROL_SQWFS0 (0x1u << 0)
static inline uint8_t bcd_to_bin(uint8_t bcd)
{
uint8_t bin;
bin = bcd & 0x0f;
bin += ((bcd >> 4) & 0x0f) * 10;
return bin;
}
static inline uint8_t bin_to_bcd(uint8_t bin)
{
uint8_t bcd;
bcd = bin % 10;
bcd |= (bin / 10) << 4;
return bcd;
}
static struct mcp7940m_rtc *mcp7940m_get_context(int minor)
{
return (struct mcp7940m_rtc *) RTC_Table[minor].pDeviceParams;
}
static int mcp7940m_i2c_read(
struct mcp7940m_rtc *ctx,
uint8_t addr,
uint8_t *buf,
size_t len
)
{
int fd;
int rv;
struct i2c_msg msgs[] = {{
.addr = ctx->i2c_addr,
.flags = 0,
.buf = &addr,
.len = 1,
}, {
.addr = ctx->i2c_addr,
.flags = I2C_M_RD,
.buf = buf,
.len = len,
}};
struct i2c_rdwr_ioctl_data payload = {
.msgs = msgs,
.nmsgs = sizeof(msgs)/sizeof(msgs[0]),
};
fd = open(ctx->i2c_bus_path, O_RDWR);
if (fd < 0) {
return fd;
}
rv = ioctl(fd, I2C_RDWR, &payload);
close(fd);
return rv;
}
static int mcp7940m_i2c_write(
struct mcp7940m_rtc *ctx,
uint8_t addr,
const uint8_t *buf,
size_t len
)
{
int fd;
int rv;
uint8_t writebuf[len + 1];
struct i2c_msg msgs[] = {{
.addr = ctx->i2c_addr,
.flags = 0,
.buf = writebuf,
.len = len + 1,
}};
struct i2c_rdwr_ioctl_data payload = {
.msgs = msgs,
.nmsgs = sizeof(msgs)/sizeof(msgs[0]),
};
writebuf[0] = addr;
memcpy(&writebuf[1], buf, len);
fd = open(ctx->i2c_bus_path, O_RDWR);
if (fd < 0) {
return fd;
}
rv = ioctl(fd, I2C_RDWR, &payload);
close(fd);
return rv;
}
static int mcp7940m_initialize_once(struct mcp7940m_rtc *ctx)
int mcp7940m_hw_init(struct i2c_rtc_base *base)
{
uint8_t reg;
ssize_t rv;
if (ctx->initialized) {
return 0;
}
struct mcp7940m_rtc *ctx =
RTEMS_CONTAINER_OF(base, struct mcp7940m_rtc, base);
/*
* Make sure that all alarms and outputs are disabled. Enable or disable
@@ -240,122 +83,15 @@ static int mcp7940m_initialize_once(struct mcp7940m_rtc *ctx)
if (!ctx->crystal) {
reg |= MCP7940M_CONTROL_EXTOSC;
}
rv = mcp7940m_i2c_write(ctx, REG_MCP7940M_CONTROL, &reg, 1);
rv = i2c_rtc_write(&ctx->base, REG_MCP7940M_CONTROL, &reg, 1);
if (rv == 0 && ctx->crystal) {
rv = mcp7940m_i2c_read(ctx, REG_RTCSEC, &reg, 1);
rv = i2c_rtc_read(&ctx->base, REG_MCP7940M_RTCSEC, &reg, 1);
if (rv == 0 && (reg & MCP7940M_RTCSEC_ST) == 0) {
reg |= MCP7940M_RTCSEC_ST;
rv = mcp7940m_i2c_write(ctx, REG_RTCSEC, &reg, 1);
rv = i2c_rtc_write(&ctx->base, REG_MCP7940M_RTCSEC, &reg, 1);
}
}
ctx->initialized = true;
return rv;
}
static int mcp7940m_get_time(int minor, rtems_time_of_day *time)
{
int rv = 0;
uint8_t buf[REG_RTCYEAR + 1];
struct mcp7940m_rtc *ctx = mcp7940m_get_context(minor);
if (!_System_state_Is_up(_System_state_Get())) {
return -1;
}
rtems_mutex_lock(&ctx->mutex);
rv = mcp7940m_initialize_once(ctx);
if (rv == 0) {
rv = mcp7940m_i2c_read(ctx, REG_RTCSEC, buf, sizeof(buf));
}
if (rv == 0) {
unsigned year = bcd_to_bin(RTCYEAR_YRBCD_GET(buf[REG_RTCYEAR])) +
(TOD_BASE_YEAR / 100 * 100);
if (year < TOD_BASE_YEAR) {
year += 100;
}
time->year = year;
time->month = bcd_to_bin(RTCMTH_MTHBCD_GET(buf[REG_RTCMTH]));
time->day = bcd_to_bin(RTCDATE_DATEBCD_GET(buf[REG_RTCDATE]));
time->hour = bcd_to_bin(RTCHOUR_HRBCD24_GET(buf[REG_RTCHOUR]));
time->minute = bcd_to_bin(RTCMIN_MINBCD_GET(buf[REG_RTCMIN]));
time->second = bcd_to_bin(RTCSEC_SECBCD_GET(buf[REG_RTCSEC]));
time->ticks = 0;
}
rtems_mutex_unlock(&ctx->mutex);
return rv;
}
static int mcp7940m_set_time(int minor, const rtems_time_of_day *time)
{
int rv = 0;
uint8_t buf[REG_RTCYEAR + 1];
struct mcp7940m_rtc *ctx = mcp7940m_get_context(minor);
if (!_System_state_Is_up(_System_state_Get())) {
return -1;
}
rtems_mutex_lock(&ctx->mutex);
rv = mcp7940m_initialize_once(ctx);
if (rv == 0) {
rv = mcp7940m_i2c_read(ctx, REG_RTCSEC, buf, sizeof(buf));
}
if (rv == 0) {
/* Make sure weekday is not 0 (out of range). Otherwise it's not used. */
if (RTCWKDAY_WKDAY_GET(buf[REG_RTCWKDAY]) < 1) {
buf[REG_RTCWKDAY] &= ~RTCWKDAY_WKDAY_MASK;
buf[REG_RTCWKDAY] |= RTCWKDAY_WKDAY(1);
}
buf[REG_RTCYEAR] &= ~RTCYEAR_YRBCD_MASK;
buf[REG_RTCYEAR] |= RTCYEAR_YRBCD(bin_to_bcd(time->year % 100));
buf[REG_RTCMTH] &= ~RTCMTH_MTHBCD_MASK;
buf[REG_RTCMTH] |= RTCMTH_MTHBCD(bin_to_bcd(time->month));
buf[REG_RTCDATE] &= ~RTCDATE_DATEBCD_MASK;
buf[REG_RTCDATE] |= RTCDATE_DATEBCD(bin_to_bcd(time->day));
buf[REG_RTCHOUR] &= ~(RTCHOUR_HRBCD24_MASK | RTCHOUR_1224);
buf[REG_RTCHOUR] |= RTCHOUR_HRBCD24(bin_to_bcd(time->hour));
buf[REG_RTCMIN] &= ~RTCMIN_MINBCD_MASK;
buf[REG_RTCMIN] |= RTCMIN_MINBCD(bin_to_bcd(time->minute));
buf[REG_RTCSEC] &= ~RTCSEC_SECBCD_MASK;
buf[REG_RTCSEC] |= RTCSEC_SECBCD(bin_to_bcd(time->second));
rv = mcp7940m_i2c_write(ctx, REG_RTCSEC, buf, sizeof(buf));
}
rtems_mutex_unlock(&ctx->mutex);
return rv;
}
static void mcp7940m_init(int minor)
{
(void) minor;
}
bool rtc_mcp7940m_probe(int minor)
{
return true;
}
const rtc_fns rtc_mcp7940m_fns = {
.deviceInitialize = mcp7940m_init,
.deviceGetTime = mcp7940m_get_time,
.deviceSetTime = mcp7940m_set_time,
};