Am 4. März 2025 18:53:10 UTC schrieb Bernhard Beschow <shen...@gmail.com>:
>
>
>Am 23. Februar 2025 11:47:08 UTC schrieb Bernhard Beschow <shen...@gmail.com>:
>>The implementation just allows Linux to determine date and time.
>>
>>Signed-off-by: Bernhard Beschow <shen...@gmail.com>
>>---
>> MAINTAINERS | 2 +
>> hw/rtc/rs5c372.c | 236 +++++++++++++++++++++++++++++++++++++
>> tests/qtest/rs5c372-test.c | 43 +++++++
>> hw/rtc/Kconfig | 5 +
>> hw/rtc/meson.build | 1 +
>> hw/rtc/trace-events | 4 +
>> tests/qtest/meson.build | 1 +
>> 7 files changed, 292 insertions(+)
>> create mode 100644 hw/rtc/rs5c372.c
>> create mode 100644 tests/qtest/rs5c372-test.c
>
>Ping for just this patch. I'd like to have it merged for 10.0.
Ping^2 -- just few days left before soft freeze.
AFAICS no open issues and I'd really like to have this RTC merged for 10.0.
What is holding it back?
Best regards,
Bernhard
>
>Thanks,
>Bernhard
>
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index 489e426d85..2552cfd65c 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -828,10 +828,12 @@ F: hw/arm/imx8mp-evk.c
>> F: hw/arm/fsl-imx8mp.c
>> F: hw/misc/imx8mp_*.c
>> F: hw/pci-host/fsl_imx8m_phy.c
>>+F: hw/rtc/rs5c372.c
>> F: include/hw/arm/fsl-imx8mp.h
>> F: include/hw/misc/imx8mp_*.h
>> F: include/hw/pci-host/fsl_imx8m_phy.h
>> F: pc-bios/imx8mp*
>>+F: tests/qtest/rs5c372-test.c
>> F: docs/system/arm/imx8mp-evk.rst
>>
>> MPS2 / MPS3
>>diff --git a/hw/rtc/rs5c372.c b/hw/rtc/rs5c372.c
>>new file mode 100644
>>index 0000000000..5542f74085
>>--- /dev/null
>>+++ b/hw/rtc/rs5c372.c
>>@@ -0,0 +1,236 @@
>>+/*
>>+ * Ricoh RS5C372, R222x I2C RTC
>>+ *
>>+ * Copyright (c) 2025 Bernhard Beschow <shen...@gmail.com>
>>+ *
>>+ * Based on hw/rtc/ds1338.c
>>+ *
>>+ * SPDX-License-Identifier: GPL-2.0-or-later
>>+ */
>>+
>>+#include "qemu/osdep.h"
>>+#include "hw/i2c/i2c.h"
>>+#include "hw/qdev-properties.h"
>>+#include "hw/resettable.h"
>>+#include "migration/vmstate.h"
>>+#include "qemu/bcd.h"
>>+#include "qom/object.h"
>>+#include "system/rtc.h"
>>+#include "trace.h"
>>+
>>+#define NVRAM_SIZE 0x10
>>+
>>+/* Flags definitions */
>>+#define SECONDS_CH 0x80
>>+#define HOURS_PM 0x20
>>+#define CTRL2_24 0x20
>>+
>>+#define TYPE_RS5C372 "rs5c372"
>>+OBJECT_DECLARE_SIMPLE_TYPE(RS5C372State, RS5C372)
>>+
>>+struct RS5C372State {
>>+ I2CSlave parent_obj;
>>+
>>+ int64_t offset;
>>+ uint8_t wday_offset;
>>+ uint8_t nvram[NVRAM_SIZE];
>>+ uint8_t ptr;
>>+ uint8_t tx_format;
>>+ bool addr_byte;
>>+};
>>+
>>+static void capture_current_time(RS5C372State *s)
>>+{
>>+ /*
>>+ * Capture the current time into the secondary registers which will be
>>+ * actually read by the data transfer operation.
>>+ */
>>+ struct tm now;
>>+ qemu_get_timedate(&now, s->offset);
>>+ s->nvram[0] = to_bcd(now.tm_sec);
>>+ s->nvram[1] = to_bcd(now.tm_min);
>>+ if (s->nvram[0xf] & CTRL2_24) {
>>+ s->nvram[2] = to_bcd(now.tm_hour);
>>+ } else {
>>+ int tmp = now.tm_hour;
>>+ if (tmp % 12 == 0) {
>>+ tmp += 12;
>>+ }
>>+ if (tmp <= 12) {
>>+ s->nvram[2] = to_bcd(tmp);
>>+ } else {
>>+ s->nvram[2] = HOURS_PM | to_bcd(tmp - 12);
>>+ }
>>+ }
>>+ s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
>>+ s->nvram[4] = to_bcd(now.tm_mday);
>>+ s->nvram[5] = to_bcd(now.tm_mon + 1);
>>+ s->nvram[6] = to_bcd(now.tm_year - 100);
>>+}
>>+
>>+static void inc_regptr(RS5C372State *s)
>>+{
>>+ s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
>>+}
>>+
>>+static int rs5c372_event(I2CSlave *i2c, enum i2c_event event)
>>+{
>>+ RS5C372State *s = RS5C372(i2c);
>>+
>>+ switch (event) {
>>+ case I2C_START_RECV:
>>+ /*
>>+ * In h/w, capture happens on any START condition, not just a
>>+ * START_RECV, but there is no need to actually capture on
>>+ * START_SEND, because the guest can't get at that data
>>+ * without going through a START_RECV which would overwrite it.
>>+ */
>>+ capture_current_time(s);
>>+ s->ptr = 0xf;
>>+ break;
>>+ case I2C_START_SEND:
>>+ s->addr_byte = true;
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+static uint8_t rs5c372_recv(I2CSlave *i2c)
>>+{
>>+ RS5C372State *s = RS5C372(i2c);
>>+ uint8_t res;
>>+
>>+ res = s->nvram[s->ptr];
>>+
>>+ trace_rs5c372_recv(s->ptr, res);
>>+
>>+ inc_regptr(s);
>>+ return res;
>>+}
>>+
>>+static int rs5c372_send(I2CSlave *i2c, uint8_t data)
>>+{
>>+ RS5C372State *s = RS5C372(i2c);
>>+
>>+ if (s->addr_byte) {
>>+ s->ptr = data >> 4;
>>+ s->tx_format = data & 0xf;
>>+ s->addr_byte = false;
>>+ return 0;
>>+ }
>>+
>>+ trace_rs5c372_send(s->ptr, data);
>>+
>>+ if (s->ptr < 7) {
>>+ /* Time register. */
>>+ struct tm now;
>>+ qemu_get_timedate(&now, s->offset);
>>+ switch (s->ptr) {
>>+ case 0:
>>+ now.tm_sec = from_bcd(data & 0x7f);
>>+ break;
>>+ case 1:
>>+ now.tm_min = from_bcd(data & 0x7f);
>>+ break;
>>+ case 2:
>>+ if (s->nvram[0xf] & CTRL2_24) {
>>+ now.tm_hour = from_bcd(data & 0x3f);
>>+ } else {
>>+ int tmp = from_bcd(data & (HOURS_PM - 1));
>>+ if (data & HOURS_PM) {
>>+ tmp += 12;
>>+ }
>>+ if (tmp % 12 == 0) {
>>+ tmp -= 12;
>>+ }
>>+ now.tm_hour = tmp;
>>+ }
>>+ break;
>>+ case 3:
>>+ {
>>+ /*
>>+ * The day field is supposed to contain a value in the range
>>+ * 1-7. Otherwise behavior is undefined.
>>+ */
>>+ int user_wday = (data & 7) - 1;
>>+ s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
>>+ }
>>+ break;
>>+ case 4:
>>+ now.tm_mday = from_bcd(data & 0x3f);
>>+ break;
>>+ case 5:
>>+ now.tm_mon = from_bcd(data & 0x1f) - 1;
>>+ break;
>>+ case 6:
>>+ now.tm_year = from_bcd(data) + 100;
>>+ break;
>>+ }
>>+ s->offset = qemu_timedate_diff(&now);
>>+ } else {
>>+ s->nvram[s->ptr] = data;
>>+ }
>>+ inc_regptr(s);
>>+ return 0;
>>+}
>>+
>>+static void rs5c372_reset_hold(Object *obj, ResetType type)
>>+{
>>+ RS5C372State *s = RS5C372(obj);
>>+
>>+ /* The clock is running and synchronized with the host */
>>+ s->offset = 0;
>>+ s->wday_offset = 0;
>>+ memset(s->nvram, 0, NVRAM_SIZE);
>>+ s->ptr = 0;
>>+ s->addr_byte = false;
>>+}
>>+
>>+static const VMStateDescription rs5c372_vmstate = {
>>+ .name = "rs5c372",
>>+ .version_id = 1,
>>+ .minimum_version_id = 1,
>>+ .fields = (const VMStateField[]) {
>>+ VMSTATE_I2C_SLAVE(parent_obj, RS5C372State),
>>+ VMSTATE_INT64(offset, RS5C372State),
>>+ VMSTATE_UINT8_V(wday_offset, RS5C372State, 2),
>>+ VMSTATE_UINT8_ARRAY(nvram, RS5C372State, NVRAM_SIZE),
>>+ VMSTATE_UINT8(ptr, RS5C372State),
>>+ VMSTATE_UINT8(tx_format, RS5C372State),
>>+ VMSTATE_BOOL(addr_byte, RS5C372State),
>>+ VMSTATE_END_OF_LIST()
>>+ }
>>+};
>>+
>>+static void rs5c372_init(Object *obj)
>>+{
>>+ qdev_prop_set_uint8(DEVICE(obj), "address", 0x32);
>>+}
>>+
>>+static void rs5c372_class_init(ObjectClass *klass, void *data)
>>+{
>>+ DeviceClass *dc = DEVICE_CLASS(klass);
>>+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
>>+ ResettableClass *rc = RESETTABLE_CLASS(klass);
>>+
>>+ k->event = rs5c372_event;
>>+ k->recv = rs5c372_recv;
>>+ k->send = rs5c372_send;
>>+ dc->vmsd = &rs5c372_vmstate;
>>+ rc->phases.hold = rs5c372_reset_hold;
>>+}
>>+
>>+static const TypeInfo rs5c372_types[] = {
>>+ {
>>+ .name = TYPE_RS5C372,
>>+ .parent = TYPE_I2C_SLAVE,
>>+ .instance_size = sizeof(RS5C372State),
>>+ .instance_init = rs5c372_init,
>>+ .class_init = rs5c372_class_init,
>>+ },
>>+};
>>+
>>+DEFINE_TYPES(rs5c372_types)
>>diff --git a/tests/qtest/rs5c372-test.c b/tests/qtest/rs5c372-test.c
>>new file mode 100644
>>index 0000000000..0f6a9b68b9
>>--- /dev/null
>>+++ b/tests/qtest/rs5c372-test.c
>>@@ -0,0 +1,43 @@
>>+/*
>>+ * QTest testcase for the RS5C372 RTC
>>+ *
>>+ * Copyright (c) 2025 Bernhard Beschow <shen...@gmail.com>
>>+ *
>>+ * Based on ds1338-test.c
>>+ *
>>+ * SPDX-License-Identifier: GPL-2.0-or-later
>>+ */
>>+
>>+#include "qemu/osdep.h"
>>+#include "qemu/bcd.h"
>>+#include "libqos/i2c.h"
>>+
>>+#define RS5C372_ADDR 0x32
>>+
>>+static void rs5c372_read_date(void *obj, void *data, QGuestAllocator *alloc)
>>+{
>>+ QI2CDevice *i2cdev = obj;
>>+
>>+ uint8_t resp[0x10];
>>+ time_t now = time(NULL);
>>+ struct tm *utc = gmtime(&now);
>>+
>>+ i2c_read_block(i2cdev, 0, resp, sizeof(resp));
>>+
>>+ /* check retrieved time against local time */
>>+ g_assert_cmpuint(from_bcd(resp[5]), == , utc->tm_mday);
>>+ g_assert_cmpuint(from_bcd(resp[6]), == , 1 + utc->tm_mon);
>>+ g_assert_cmpuint(2000 + from_bcd(resp[7]), == , 1900 + utc->tm_year);
>>+}
>>+
>>+static void rs5c372_register_nodes(void)
>>+{
>>+ QOSGraphEdgeOptions opts = { };
>>+ add_qi2c_address(&opts, &(QI2CAddress) { RS5C372_ADDR });
>>+
>>+ qos_node_create_driver("rs5c372", i2c_device_create);
>>+ qos_node_consumes("rs5c372", "i2c-bus", &opts);
>>+ qos_add_test("read_date", "rs5c372", rs5c372_read_date, NULL);
>>+}
>>+
>>+libqos_init(rs5c372_register_nodes);
>>diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig
>>index 2fe04ec1d0..315b0e4ecc 100644
>>--- a/hw/rtc/Kconfig
>>+++ b/hw/rtc/Kconfig
>>@@ -26,3 +26,8 @@ config GOLDFISH_RTC
>>
>> config LS7A_RTC
>> bool
>>+
>>+config RS5C372_RTC
>>+ bool
>>+ depends on I2C
>>+ default y if I2C_DEVICES
>>diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build
>>index 8ecc2d792c..6c87864dc0 100644
>>--- a/hw/rtc/meson.build
>>+++ b/hw/rtc/meson.build
>>@@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true:
>>files('goldfish_rtc.c'))
>> system_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c'))
>> system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c'))
>> system_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c'))
>>+system_ss.add(when: 'CONFIG_RS5C372_RTC', if_true: files('rs5c372.c'))
>>diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events
>>index 8012afe102..b9f2852d35 100644
>>--- a/hw/rtc/trace-events
>>+++ b/hw/rtc/trace-events
>>@@ -35,3 +35,7 @@ m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem
>>write addr:0x%04x val
>> # goldfish_rtc.c
>> goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value
>> 0x%08" PRIx64
>> goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 "
>> value 0x%08" PRIx64
>>+
>>+# rs5c372.c
>>+rs5c372_recv(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] -> 0x%02" PRIx8
>>+rs5c372_send(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] <- 0x%02" PRIx8
>>diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>>index 8a6243382a..9e5380ba7a 100644
>>--- a/tests/qtest/meson.build
>>+++ b/tests/qtest/meson.build
>>@@ -298,6 +298,7 @@ qos_test_ss.add(
>> 'pca9552-test.c',
>> 'pci-test.c',
>> 'pcnet-test.c',
>>+ 'rs5c372-test.c',
>> 'sdhci-test.c',
>> 'spapr-phb-test.c',
>> 'tmp105-test.c',