// SPDX-License-Identifier: GPL-2.0 /* * Driver for the RTC found in the SpacemiT P1 PMIC * * Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved. */ #include #include #include #include #include #include #define MOD_NAME "spacemit-p1-rtc" /* * Six consecutive 1-byte registers hold the seconds, minutes, hours, * day-of-month, month, and year (respectively). * * The range of values in these registers is: * seconds 0-59 * minutes 0-59 * hours 0-59 * day 0-30 (struct tm is 1-31) * month 0-11 * year years since 2000 (struct tm is since 1900) * * Note that the day and month must be converted after reading and * before writing. */ #define RTC_TIME 0x0d /* Offset of the seconds register */ #define RTC_CTRL 0x1d #define RTC_EN BIT(2) /* Number of attempts to read a consistent time stamp before giving up */ #define RTC_READ_TRIES 20 /* At least 1 */ struct p1_rtc { struct regmap *regmap; struct rtc_device *rtc; }; /* * The P1 hardware documentation states that the register values are * latched to ensure a consistent time snapshot within the registers, * but these are in fact unstable due to a bug in the hardware design. * So we loop until we get two identical readings. */ static int p1_rtc_read_time(struct device *dev, struct rtc_time *t) { struct p1_rtc *p1 = dev_get_drvdata(dev); struct regmap *regmap = p1->regmap; u32 count = RTC_READ_TRIES; u8 seconds; u8 time[6]; int ret; if (!regmap_test_bits(regmap, RTC_CTRL, RTC_EN)) return -EINVAL; /* RTC is disabled */ ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time)); if (ret) return ret; do { seconds = time[0]; ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time)); if (ret) return ret; } while (time[0] != seconds && --count); if (!count) return -EIO; /* Unable to get a consistent result */ t->tm_sec = time[0] & GENMASK(5, 0); t->tm_min = time[1] & GENMASK(5, 0); t->tm_hour = time[2] & GENMASK(4, 0); t->tm_mday = (time[3] & GENMASK(4, 0)) + 1; t->tm_mon = time[4] & GENMASK(3, 0); t->tm_year = (time[5] & GENMASK(5, 0)) + 100; return 0; } /* * The P1 hardware documentation states that values in the registers are * latched so when written they represent a consistent time snapshot. * Nevertheless, this is not guaranteed by the implementation, so we must * disable the RTC while updating it. */ static int p1_rtc_set_time(struct device *dev, struct rtc_time *t) { struct p1_rtc *p1 = dev_get_drvdata(dev); struct regmap *regmap = p1->regmap; u8 time[6]; int ret; time[0] = t->tm_sec; time[1] = t->tm_min; time[2] = t->tm_hour; time[3] = t->tm_mday - 1; time[4] = t->tm_mon; time[5] = t->tm_year - 100; /* Disable the RTC to update; re-enable again when done */ ret = regmap_clear_bits(regmap, RTC_CTRL, RTC_EN); if (ret) return ret; /* If something goes wrong, leave the RTC disabled */ ret = regmap_bulk_write(regmap, RTC_TIME, time, sizeof(time)); if (ret) return ret; return regmap_set_bits(regmap, RTC_CTRL, RTC_EN); } static const struct rtc_class_ops p1_rtc_class_ops = { .read_time = p1_rtc_read_time, .set_time = p1_rtc_set_time, }; static int p1_rtc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rtc_device *rtc; struct p1_rtc *p1; p1 = devm_kzalloc(dev, sizeof(*p1), GFP_KERNEL); if (!p1) return -ENOMEM; dev_set_drvdata(dev, p1); p1->regmap = dev_get_regmap(dev->parent, NULL); if (!p1->regmap) return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); rtc = devm_rtc_allocate_device(dev); if (IS_ERR(rtc)) return dev_err_probe(dev, PTR_ERR(rtc), "error allocating device\n"); p1->rtc = rtc; rtc->ops = &p1_rtc_class_ops; rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->range_max = RTC_TIMESTAMP_END_2063; clear_bit(RTC_FEATURE_ALARM, rtc->features); clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); return devm_rtc_register_device(rtc); } static struct platform_driver p1_rtc_driver = { .probe = p1_rtc_probe, .driver = { .name = MOD_NAME, }, }; module_platform_driver(p1_rtc_driver); MODULE_DESCRIPTION("SpacemiT P1 RTC driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" MOD_NAME);