| /* |
| * 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 DEVICE_API(bbram, 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) |