From aa2f8e35326276c81f94919b8173e386a6f6968f Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 17 Nov 2025 11:32:50 +0100 Subject: [PATCH 1/3] drivers: rtc: convert tristate to bool As u-boot doesn't have any loadable modules, tristate doesn't make sense. Convert it to bool. Signed-off-by: Michael Walle Reviewed-by: Tom Rini --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index ed903999f06..202daa7bcd6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -162,7 +162,7 @@ config RTC_MAX313XX - CLKOUT generation config RTC_PCF8563 - tristate "Philips PCF8563" + bool "Philips PCF8563" help If you say yes here you get support for the Philips PCF8563 RTC and compatible chips. From 1c2a2253f7980355ecaec76e45a5901635f00621 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 17 Nov 2025 11:32:51 +0100 Subject: [PATCH 2/3] drivers: rtc: add PCF85063 support Add support for the Microcrystal RV8263 and compatible RTCs. The driver's name was taken from linux. It should work with any NXP PCF85063 compatible RTCs. It was tested with a RV8263. Signed-off-by: Michael Walle --- drivers/rtc/Kconfig | 8 +++ drivers/rtc/Makefile | 1 + drivers/rtc/pcf85063.c | 107 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 drivers/rtc/pcf85063.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 202daa7bcd6..f8421e3bf58 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -161,6 +161,14 @@ config RTC_MAX313XX - Temperature sensor - CLKOUT generation +config RTC_PCF85063 + bool "Enable PCF85063 driver" + depends on DM_I2C + depends on DM_RTC + help + If you say yes here you get support for the NXP PCF85063 RTC + and compatible chips. + config RTC_PCF8563 bool "Philips PCF8563" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index a4ede413cd1..9d84aa836a1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_RTC_MC146818) += mc146818.o obj-$(CONFIG_MCFRTC) += mcfrtc.o obj-$(CONFIG_RTC_MV) += mvrtc.o obj-$(CONFIG_RTC_MXS) += mxsrtc.o +obj-$(CONFIG_RTC_PCF85063) += pcf85063.o obj-$(CONFIG_RTC_PCF8563) += pcf8563.o obj-$(CONFIG_RTC_PCF2127) += pcf2127.o obj-$(CONFIG_RTC_PL031) += pl031.o diff --git a/drivers/rtc/pcf85063.c b/drivers/rtc/pcf85063.c new file mode 100644 index 00000000000..737d4547aca --- /dev/null +++ b/drivers/rtc/pcf85063.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCF85063 and compatible I2C RTC driver + * + * Copyright (c) 2025 Kontron Europe GmbH. + */ + +#include +#include +#include +#include + +#define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL1_SR 0x58 + +#define PCF85063_REG_SC 0x04 /* datetime */ +#define PCF85063_REG_SC_OS 0x80 + +static int pcf85063_get_time(struct udevice *dev, struct rtc_time *tm) +{ + u8 regs[7]; + int ret; + + ret = dm_i2c_read(dev, PCF85063_REG_SC, regs, sizeof(regs)); + if (ret) + return ret; + + if (regs[0] & PCF85063_REG_SC_OS) { + dev_err(dev, "Power loss detected, Invalid time\n"); + return -EINVAL; + } + + tm->tm_sec = bcd2bin(regs[0] & 0x7f); + tm->tm_min = bcd2bin(regs[1] & 0x7f); + tm->tm_hour = bcd2bin(regs[2] & 0x3f); + tm->tm_mday = bcd2bin(regs[3] & 0x3f); + tm->tm_wday = regs[4] & 0x07; + tm->tm_mon = bcd2bin(regs[5] & 0x1f) - 1; + tm->tm_year = bcd2bin(regs[6]) + 2000; + + return 0; +} + +static int pcf85063_set_time(struct udevice *dev, const struct rtc_time *tm) +{ + u8 regs[7]; + + if (tm->tm_year < 2000 || tm->tm_year > 2099) { + dev_err(dev, "Year must be between 2000 and 2099.\n"); + return -EINVAL; + } + + regs[0] = bin2bcd(tm->tm_sec); + regs[1] = bin2bcd(tm->tm_min); + regs[2] = bin2bcd(tm->tm_hour); + regs[3] = bin2bcd(tm->tm_mday); + regs[4] = tm->tm_wday; + regs[5] = bin2bcd(tm->tm_mon + 1); + regs[6] = bin2bcd(tm->tm_year % 100); + + return dm_i2c_write(dev, PCF85063_REG_SC, regs, sizeof(regs)); +} + +static int pcf85063_reset(struct udevice *dev) +{ + return dm_i2c_reg_write(dev, PCF85063_REG_CTRL1, PCF85063_REG_CTRL1_SR); +} + +static int pcf85063_read(struct udevice *dev, unsigned int offset, u8 *buf, + unsigned int len) +{ + return dm_i2c_read(dev, offset, buf, len); +} + +static int pcf85063_write(struct udevice *dev, unsigned int offset, + const u8 *buf, unsigned int len) +{ + return dm_i2c_write(dev, offset, buf, len); +} + +static const struct rtc_ops pcf85063_rtc_ops = { + .get = pcf85063_get_time, + .set = pcf85063_set_time, + .reset = pcf85063_reset, + .read = pcf85063_read, + .write = pcf85063_write, +}; + +static int pcf85063_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS); + + return 0; +} + +static const struct udevice_id pcf85063_of_id[] = { + { .compatible = "microcrystal,rv8263" }, + { } +}; + +U_BOOT_DRIVER(rtc_pcf85063) = { + .name = "rtc-pcf85063", + .id = UCLASS_RTC, + .probe = pcf85063_probe, + .of_match = pcf85063_of_id, + .ops = &pcf85063_rtc_ops, +}; From 788f2d3800e3d7e66a42aaffcb6cbc44390353d2 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 17 Nov 2025 11:32:52 +0100 Subject: [PATCH 3/3] drivers: rtc: add RV3032 support Add support for the Microcrystal RV3032 RTC. Signed-off-by: Michael Walle --- drivers/rtc/Kconfig | 7 +++ drivers/rtc/Makefile | 1 + drivers/rtc/rv3032.c | 121 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 drivers/rtc/rv3032.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f8421e3bf58..ef1663f3450 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -194,6 +194,13 @@ config RTC_RV3029 This driver supports reading and writing the RTC/calendar and the battery-baced SRAM section. +config RTC_RV3032 + bool "Enable RV3032 driver" + depends on DM_RTC + help + If you say yes here you get support for the Micro Crystal RV3032 + RTC. + config RTC_RV8803 bool "Enable RV8803 driver" depends on DM_RTC diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 9d84aa836a1..9df373d5148 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_RTC_PL031) += pl031.o obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o obj-$(CONFIG_RTC_RV3028) += rv3028.o obj-$(CONFIG_RTC_RV3029) += rv3029.o +obj-$(CONFIG_RTC_RV3032) += rv3032.o obj-$(CONFIG_RTC_RV8803) += rv8803.o obj-$(CONFIG_RTC_RX8025) += rx8025.o obj-$(CONFIG_RTC_RX8010SJ) += rx8010sj.o diff --git a/drivers/rtc/rv3032.c b/drivers/rtc/rv3032.c new file mode 100644 index 00000000000..87ff5204e73 --- /dev/null +++ b/drivers/rtc/rv3032.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Micro Crystal RV3032 I2C RTC driver + * + * Copyright (c) 2025 Kontron Europe GmbH. + */ + +#include +#include +#include +#include + +#define RV3032_REG_SEC 0x01 +#define RV3032_REG_STATUS 0x0d +#define RV3032_REG_STATUS_VLF BIT(0) +#define RV3032_REG_STATUS_PORF BIT(1) + +static int rv3032_get_time(struct udevice *dev, struct rtc_time *tm) +{ + int ret, status; + u8 regs[7]; + + status = dm_i2c_reg_read(dev, RV3032_REG_STATUS); + if (status < 0) + return status; + + if (status & (RV3032_REG_STATUS_PORF | RV3032_REG_STATUS_VLF)) { + dev_err(dev, "Power loss detected, Invalid time\n"); + return -EINVAL; + } + + ret = dm_i2c_read(dev, RV3032_REG_SEC, regs, sizeof(regs)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(regs[0] & 0x7f); + tm->tm_min = bcd2bin(regs[1] & 0x7f); + tm->tm_hour = bcd2bin(regs[2] & 0x3f); + tm->tm_mday = bcd2bin(regs[3] & 0x3f); + tm->tm_wday = regs[4] & 0x07; + tm->tm_mon = bcd2bin(regs[5] & 0x1f) - 1; + tm->tm_year = bcd2bin(regs[6]) + 2000; + + return 0; +} + +static int rv3032_set_time(struct udevice *dev, const struct rtc_time *tm) +{ + u8 regs[7]; + int ret; + + if (tm->tm_year < 2000 || tm->tm_year > 2099) { + dev_err(dev, "Year must be between 2000 and 2099.\n"); + return -EINVAL; + } + + regs[0] = bin2bcd(tm->tm_sec); + regs[1] = bin2bcd(tm->tm_min); + regs[2] = bin2bcd(tm->tm_hour); + regs[3] = bin2bcd(tm->tm_mday); + regs[4] = tm->tm_wday; + regs[5] = bin2bcd(tm->tm_mon + 1); + regs[6] = bin2bcd(tm->tm_year % 100); + + ret = dm_i2c_write(dev, RV3032_REG_SEC, regs, sizeof(regs)); + if (ret) + return ret; + + return dm_i2c_reg_clrset(dev, RV3032_REG_STATUS, + RV3032_REG_STATUS_PORF | RV3032_REG_STATUS_VLF, + 0); +} + +static int rv3032_read(struct udevice *dev, unsigned int offset, u8 *buf, + unsigned int len) +{ + return dm_i2c_read(dev, offset, buf, len); +} + +static int rv3032_write(struct udevice *dev, unsigned int offset, + const u8 *buf, unsigned int len) +{ + return dm_i2c_write(dev, offset, buf, len); +} + +static int rv3032_reset(struct udevice *dev) +{ + /* + * There is no reset, but the "date reset" command needs this op to + * actually set the default time + */ + return 0; +} + +static const struct rtc_ops rv3032_rtc_ops = { + .get = rv3032_get_time, + .set = rv3032_set_time, + .read = rv3032_read, + .write = rv3032_write, + .reset = rv3032_reset, +}; + +static int rv3032_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS); + + return 0; +} + +static const struct udevice_id rv3032_of_id[] = { + { .compatible = "microcrystal,rv3032" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv3032) = { + .name = "rtc-rv3032", + .id = UCLASS_RTC, + .probe = rv3032_probe, + .of_match = rv3032_of_id, + .ops = &rv3032_rtc_ops, +};