Skip to content

Add MFD Support for Motorola mc146818 RTC, Counter, and BBRAM #91751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions boards/intel/btl/intel_btl_s_crb.dts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
status = "okay";
};

&mfd {
status = "okay";
};

&rtc {
status = "okay";
};
1 change: 1 addition & 0 deletions drivers/bbram/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ zephyr_library_sources_ifdef(CONFIG_BBRAM_MICROCHIP_MCP7940N_EMUL bbram_microchi
zephyr_library_sources_ifdef(CONFIG_BBRAM_XEC bbram_xec.c)
zephyr_library_sources_ifdef(CONFIG_BBRAM_STM32 bbram_stm32.c)
zephyr_library_sources_ifdef(CONFIG_BBRAM_RTS5912 bbram_rts5912.c)
zephyr_library_sources_ifdef(CONFIG_BBRAM_MOTOROLA_MC146818 bbram_mc146818.c)
2 changes: 2 additions & 0 deletions drivers/bbram/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ source "drivers/bbram/Kconfig.stm32"

source "drivers/bbram/Kconfig.rts5912"

source "drivers/bbram/Kconfig.mc146818"

endif # BBRAM
12 changes: 12 additions & 0 deletions drivers/bbram/Kconfig.mc146818
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Intel SoC BBRAM configuration options

# Copyright (c) 2025 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

config BBRAM_MOTOROLA_MC146818
bool "BBRAM driver for x86 CMOS/RTC clock's bbram"
default y if !COUNTER
select MFD
depends on DT_HAS_MOTOROLA_MC146818_BBRAM_ENABLED
help
Enable driver for Motorola mc146818 BBRAM.
155 changes: 155 additions & 0 deletions drivers/bbram/bbram_mc146818.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2025 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/

/*
* Read and Write access to offset ranges 0x2A(42)-0x31(49) and 0xAA(170)-0xB1(177)
* are lockable through BIOS setting. To access the memory in those offsets,
* disable the Lock in BIOS through following steps.
* Intel Advanced Menu -> PCH-IO Configuration -> Security Configuration ->
* RTC Memory Lock -> Disable
*/

#define DT_DRV_COMPAT motorola_mc146818_bbram

#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/sys/util.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/bbram.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/spinlock.h>
#include <zephyr/drivers/mfd/mc146818.h>

#define MIN_SIZE 1 /* Minimum size to write */
#define MIN_OFFSET 0x0E /* Starting offset of memory */
#define MAX_STD 0x7F /* Last offset of Standard memory bank */
#define RTC_CENT 0x32 /* Offset for RTC Century Byte */

struct bbram_mc146818_config {
const struct device *mfd;
size_t mem_size;
};

struct bbram_mc146818_data {
struct k_spinlock lock;
};

static int bbram_mc146818_read(const struct device *dev, size_t offset,
size_t size, uint8_t *data)
{
const struct bbram_mc146818_config *config = dev->config;
struct bbram_mc146818_data *dev_data = dev->data;

if (size < MIN_SIZE || offset + size > config->mem_size
|| data == NULL) {
return -EFAULT;
}

offset += MIN_OFFSET;

k_spinlock_key_t key = k_spin_lock(&dev_data->lock);

for (size_t i = 0; i < size; i++) {
if (offset < MAX_STD) {
if (offset >= RTC_CENT) {

/* RTC_CENT byte is used to store Century data for the
* RTC time and date, so skipping read/write operation
* to this byte.
*/

*(data + i) = mfd_mc146818_std_read(config->mfd, offset+1);
} else {
*(data + i) = mfd_mc146818_std_read(config->mfd, offset);
}
} else {
*(data + i) = mfd_mc146818_ext_read(config->mfd, offset+1);
}
offset++;
}

k_spin_unlock(&dev_data->lock, key);
return 0;
}

static int bbram_mc146818_write(const struct device *dev, size_t offset,
size_t size, const uint8_t *data)
{
const struct bbram_mc146818_config *config = dev->config;
struct bbram_mc146818_data *dev_data = dev->data;

if (size < MIN_SIZE || offset + size > config->mem_size
|| data == NULL) {
return -EFAULT;
}

offset += MIN_OFFSET;

k_spinlock_key_t key = k_spin_lock(&dev_data->lock);

for (size_t i = 0; i < size; i++) {
if (offset < MAX_STD) {
if (offset >= RTC_CENT) {

/* RTC_CENT byte is used to store Century data for the
* RTC time and date, so skipping read/write operation
* to this byte.
*/

mfd_mc146818_std_write(config->mfd, offset+1, *(data + i));
} else {
mfd_mc146818_std_write(config->mfd, offset, *(data + i));
}
} else {
mfd_mc146818_ext_write(config->mfd, offset+1, *(data + i));
}
offset++;
}

k_spin_unlock(&dev_data->lock, key);
return 0;
}

static int bbram_mc146818_get_size(const struct device *dev, size_t *size)
{
const struct bbram_mc146818_config *config = dev->config;

*size = config->mem_size;

return 0;
}

static const struct bbram_driver_api bbram_mc146818_api = {
.read = bbram_mc146818_read,
.write = bbram_mc146818_write,
.get_size = bbram_mc146818_get_size,
};

static int bbram_mc146818_init(const struct device *dev)
{
const struct bbram_mc146818_config *config = dev->config;

if (!device_is_ready(config->mfd)) {
return -ENODEV;
}

return 0;
}

#define BBRAM_MC146818_DEV_CFG(n) \
static const struct bbram_mc146818_config bbram_config_##n = { \
.mfd = DEVICE_DT_GET(DT_INST_PARENT(n)), \
.mem_size = DT_INST_PROP(n, size), \
}; \
static struct bbram_mc146818_data bbram_data_##n; \
DEVICE_DT_INST_DEFINE(n, &bbram_mc146818_init, NULL, \
&bbram_data_##n, &bbram_config_##n, \
POST_KERNEL, \
UTIL_INC(CONFIG_MFD_MOTOROLA_MC146818_INIT_PRIORITY), \
&bbram_mc146818_api); \

DT_INST_FOREACH_STATUS_OKAY(BBRAM_MC146818_DEV_CFG)
1 change: 1 addition & 0 deletions drivers/counter/Kconfig.cmos
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
config COUNTER_CMOS
bool "Counter driver for x86 CMOS/RTC clock"
default y if !RTC
select MFD
depends on DT_HAS_MOTOROLA_MC146818_ENABLED
7 changes: 4 additions & 3 deletions drivers/counter/counter_cmos.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

/* The "CMOS" device is accessed via an address latch and data port. */

#define X86_CMOS_ADDR (DT_INST_REG_ADDR_BY_IDX(0, 0))
#define X86_CMOS_DATA (DT_INST_REG_ADDR_BY_IDX(0, 1))
#define X86_CMOS_ADDR (DT_REG_ADDR_BY_IDX(DT_INST_PARENT(0), 0))
#define X86_CMOS_DATA (DT_REG_ADDR_BY_IDX(DT_INST_PARENT(0), 1))

/*
* A snapshot of the RTC state, or at least the state we're
Expand Down Expand Up @@ -204,4 +204,5 @@ static DEVICE_API(counter, api) = {
};

DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &info, POST_KERNEL,
CONFIG_COUNTER_INIT_PRIORITY, &api);
UTIL_INC(CONFIG_MFD_MOTOROLA_MC146818_INIT_PRIORITY),
&api);
1 change: 1 addition & 0 deletions drivers/mfd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_DS3231 mfd_ds3231.c)
zephyr_library_sources_ifdef(CONFIG_MFD_MAX22017 mfd_max22017.c)
zephyr_library_sources_ifdef(CONFIG_MFD_MCHP_SAM_FLEXCOM mfd_mchp_sam_flexcom.c)
zephyr_library_sources_ifdef(CONFIG_MFD_PF1550 mfd_pf1550.c)
zephyr_library_sources_ifdef(CONFIG_MFD_MOTOROLA_MC146818 mfd_mc146818.c)
1 change: 1 addition & 0 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ source "drivers/mfd/Kconfig.pf1550"
source "drivers/mfd/Kconfig.lpflexcomm"
source "drivers/mfd/Kconfig.tle9104"
source "drivers/mfd/Kconfig.it8801"
source "drivers/mfd/Kconfig.mc146818"

endif # MFD
20 changes: 20 additions & 0 deletions drivers/mfd/Kconfig.mc146818
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Intel SoC RTC configuration options

# Copyright (c) 2025 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

config MFD_MOTOROLA_MC146818
bool "MOTOROLA MC146818 multi-function device driver"
default y
depends on DT_HAS_MOTOROLA_MC146818_MFD_ENABLED
help
common code required by bbram and rtc, which will likely be a locking mechanism
so both RTC and BBRAM can set/get regs safely and independently.

config MFD_MOTOROLA_MC146818_INIT_PRIORITY
int "Init priority of mc146818 MFD"
depends on MFD_MOTOROLA_MC146818
default 50
help
Initialization priority for the MOTOROLA MC146818 MFD driver.
It must be greater than RTC, COUNTER and BBRAM driver init priority.
86 changes: 86 additions & 0 deletions drivers/mfd/mfd_mc146818.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2025 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT motorola_mc146818_mfd

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/spinlock.h>
#include <zephyr/drivers/mfd/mc146818.h>
#include <zephyr/init.h>
#include <zephyr/sys/util.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/sys_io.h>

#define RTC_STD_INDEX (DT_INST_REG_ADDR_BY_IDX(0, 0))
#define RTC_STD_TARGET (DT_INST_REG_ADDR_BY_IDX(0, 1))
#define RTC_EXT_INDEX (DT_INST_REG_ADDR_BY_IDX(0, 2))
#define RTC_EXT_TARGET (DT_INST_REG_ADDR_BY_IDX(0, 3))

struct mfd_mc146818_data {
struct k_spinlock lock;
};

uint8_t mfd_mc146818_std_read(const struct device *dev, uint8_t offset)
{
struct mfd_mc146818_data *data = dev->data;
uint8_t value;

k_spinlock_key_t key = k_spin_lock(&data->lock);

sys_out8(offset, RTC_STD_INDEX);
value = sys_in8(RTC_STD_TARGET);

k_spin_unlock(&data->lock, key);
return value;
}

void mfd_mc146818_std_write(const struct device *dev, uint8_t offset, uint8_t value)
{
struct mfd_mc146818_data *data = dev->data;

k_spinlock_key_t key = k_spin_lock(&data->lock);

sys_out8(offset, RTC_STD_INDEX);
sys_out8(value, RTC_STD_TARGET);

k_spin_unlock(&data->lock, key);
}

uint8_t mfd_mc146818_ext_read(const struct device *dev, uint8_t offset)
{
struct mfd_mc146818_data *data = dev->data;
uint8_t value;

k_spinlock_key_t key = k_spin_lock(&data->lock);

sys_out8(offset, RTC_EXT_INDEX);
value = sys_in8(RTC_EXT_TARGET);

k_spin_unlock(&data->lock, key);
return value;
}

void mfd_mc146818_ext_write(const struct device *dev, uint8_t offset, uint8_t value)
{
struct mfd_mc146818_data *data = dev->data;

k_spinlock_key_t key = k_spin_lock(&data->lock);

sys_out8(offset, RTC_EXT_INDEX);
sys_out8(value, RTC_EXT_TARGET);

k_spin_unlock(&data->lock, key);
}

#define MFD_MC146818_DEFINE(inst) \
static struct mfd_mc146818_data data##inst; \
DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &data##inst, NULL, \
POST_KERNEL, \
CONFIG_MFD_MOTOROLA_MC146818_INIT_PRIORITY, \
NULL); \

DT_INST_FOREACH_STATUS_OKAY(MFD_MC146818_DEFINE)
1 change: 1 addition & 0 deletions drivers/rtc/Kconfig.mc146818
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
config RTC_MOTOROLA_MC146818
bool "RTC driver for x86 CMOS/RTC clock"
default y if !COUNTER
select MFD
depends on DT_HAS_MOTOROLA_MC146818_ENABLED
Loading