/* * Copyright (c) 2026 Justin Hibbits * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "iicbus_if.h" /* * Driver for the Richo rs5c372a RTC. The chip itself includes 2 alarm clocks * in addition to the clock component, but this driver offers only the RTC * component. * * Like many other RTCs, this reports the date and time in BCD. * * The `Hour' register uses bit 5 in a dual role: In 24-hour time, it's a part * of the first digit (0, 1, 2). In 12-hour time it denotes PM, so 12PM is * reported as 0x32, 1PM is 0x21, etc. */ #define RS5C372_REG_SEC 0x0 #define RS5C372_REG_MIN 0x1 #define RS5C372_REG_HOUR 0x2 #define HOUR_HR_M 0x1f #define HOUR_PM 0x20 #define RS5C372_REG_DOW 0x3 #define RS5C372_REG_DAY 0x4 #define RS5C372_REG_MON 0x5 #define RS5C372_REG_YEAR 0x6 #define RS5C372_REG_CTRL1 0xe #define RS5C372_REG_CTRL2 0xf #define CTRL_PM 0x20 static struct ofw_compat_data compat_data[] = { { "ricoh,rs5c372a", 1 }, { NULL, 0 } }; static int rs5c372a_gettime(device_t dev, struct timespec *ts) { struct bcd_clocktime ct = {}; uint8_t clock_regs[7]; int err; uint8_t ctrl2; bool is_12hr = true; err = iicdev_readfrom(dev, RS5C372_REG_CTRL2, &ctrl2, sizeof(ctrl2), IIC_WAIT); if (err != 0) return (err); err = iicdev_readfrom(dev, RS5C372_REG_SEC, clock_regs, sizeof(clock_regs), IIC_WAIT); if (err != 0) return (err); if (ctrl2 & CTRL_PM) is_12hr = false; ct.sec = clock_regs[RS5C372_REG_SEC]; ct.min = clock_regs[RS5C372_REG_MIN]; ct.hour = clock_regs[RS5C372_REG_HOUR]; ct.dow = clock_regs[RS5C372_REG_DOW]; ct.day = clock_regs[RS5C372_REG_DAY]; ct.mon = clock_regs[RS5C372_REG_MON]; ct.year = clock_regs[RS5C372_REG_YEAR]; if (is_12hr) { ct.ispm = ct.hour & HOUR_PM; ct.hour &= HOUR_HR_M; } clock_bcd_to_ts(&ct, ts, ct.ispm); return (0); } static int rs5c372a_settime(device_t dev, struct timespec *ts) { struct bcd_clocktime ct; uint8_t clock_regs[7]; uint8_t ctrl2; int err; bool is_12hr = true; err = iicdev_readfrom(dev, RS5C372_REG_CTRL2, &ctrl2, sizeof(ctrl2), IIC_WAIT); if (err != 0) return (err); if (ctrl2 & CTRL_PM) is_12hr = false; clock_ts_to_bcd(ts, &ct, is_12hr); clock_regs[RS5C372_REG_SEC] = ct.sec; clock_regs[RS5C372_REG_MIN] = ct.min; clock_regs[RS5C372_REG_HOUR] = ct.hour; clock_regs[RS5C372_REG_DAY] = ct.day; clock_regs[RS5C372_REG_DOW] = ct.dow; clock_regs[RS5C372_REG_MON] = ct.mon; clock_regs[RS5C372_REG_YEAR] = ct.year & 0xff; if (is_12hr) { if (ct.ispm) clock_regs[RS5C372_REG_HOUR] |= HOUR_PM; } err = iicdev_writeto(dev, RS5C372_REG_SEC, clock_regs, sizeof(clock_regs), IIC_WAIT); return (err); } static int rs5c372a_probe(device_t dev) { if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Richo RS5C372A RTC"); return (BUS_PROBE_DEFAULT); } static int rs5c372a_attach(device_t dev) { /* Register with 1s resolution */ clock_register(dev, 1000000); clock_schedule(dev, 1); return (0); } static device_method_t rs5c372a_methods[] = { /* Device methods */ DEVMETHOD(device_probe, rs5c372a_probe), DEVMETHOD(device_attach, rs5c372a_attach), /* Clock methods */ DEVMETHOD(clock_gettime, rs5c372a_gettime), DEVMETHOD(clock_settime, rs5c372a_settime), DEVMETHOD_END }; DEFINE_CLASS_0(rs5c372a, rs5c372a_driver, rs5c372a_methods, 0); DRIVER_MODULE(rs5c372a, iicbus, rs5c372a_driver, NULL, NULL); MODULE_VERSION(rs5c372a, 1); MODULE_DEPEND(rs5c372a, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); IICBUS_FDT_PNP_INFO(compat_data);