Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: spacemit: support the SpacemiT P1 RTC

Add support for the RTC found in the SpacemiT P1 PMIC. Initially
only setting and reading the time are supported.

The PMIC is implemented as a multi-function device. This RTC is
probed based on this driver being named in a MFD cell in the simple
MFD I2C driver.

Signed-off-by: Alex Elder <elder@riscstar.com>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20250825172057.163883-5-elder@riscstar.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Alex Elder and committed by
Alexandre Belloni
a6de182d 6266aea8

+178
+10
drivers/rtc/Kconfig
··· 406 406 This driver can also be built as a module. If so, the module 407 407 will be called rtc-max77686. 408 408 409 + config RTC_DRV_SPACEMIT_P1 410 + tristate "SpacemiT P1 RTC" 411 + depends on ARCH_SPACEMIT || COMPILE_TEST 412 + select MFD_SPACEMIT_P1 413 + default ARCH_SPACEMIT 414 + help 415 + Enable support for the RTC function in the SpacemiT P1 PMIC. 416 + This driver can also be built as a module, which will be called 417 + "spacemit-p1-rtc". 418 + 409 419 config RTC_DRV_NCT3018Y 410 420 tristate "Nuvoton NCT3018Y" 411 421 depends on OF
+1
drivers/rtc/Makefile
··· 171 171 obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o 172 172 obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o 173 173 obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o 174 + obj-$(CONFIG_RTC_DRV_SPACEMIT_P1) += rtc-spacemit-p1.o 174 175 obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o 175 176 obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o 176 177 obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
+167
drivers/rtc/rtc-spacemit-p1.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Driver for the RTC found in the SpacemiT P1 PMIC 4 + * 5 + * Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved. 6 + */ 7 + 8 + #include <linux/bits.h> 9 + #include <linux/device.h> 10 + #include <linux/module.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/regmap.h> 13 + #include <linux/rtc.h> 14 + 15 + #define MOD_NAME "spacemit-p1-rtc" 16 + 17 + /* 18 + * Six consecutive 1-byte registers hold the seconds, minutes, hours, 19 + * day-of-month, month, and year (respectively). 20 + * 21 + * The range of values in these registers is: 22 + * seconds 0-59 23 + * minutes 0-59 24 + * hours 0-59 25 + * day 0-30 (struct tm is 1-31) 26 + * month 0-11 27 + * year years since 2000 (struct tm is since 1900) 28 + * 29 + * Note that the day and month must be converted after reading and 30 + * before writing. 31 + */ 32 + #define RTC_TIME 0x0d /* Offset of the seconds register */ 33 + 34 + #define RTC_CTRL 0x1d 35 + #define RTC_EN BIT(2) 36 + 37 + /* Number of attempts to read a consistent time stamp before giving up */ 38 + #define RTC_READ_TRIES 20 /* At least 1 */ 39 + 40 + struct p1_rtc { 41 + struct regmap *regmap; 42 + struct rtc_device *rtc; 43 + }; 44 + 45 + /* 46 + * The P1 hardware documentation states that the register values are 47 + * latched to ensure a consistent time snapshot within the registers, 48 + * but these are in fact unstable due to a bug in the hardware design. 49 + * So we loop until we get two identical readings. 50 + */ 51 + static int p1_rtc_read_time(struct device *dev, struct rtc_time *t) 52 + { 53 + struct p1_rtc *p1 = dev_get_drvdata(dev); 54 + struct regmap *regmap = p1->regmap; 55 + u32 count = RTC_READ_TRIES; 56 + u8 seconds; 57 + u8 time[6]; 58 + int ret; 59 + 60 + if (!regmap_test_bits(regmap, RTC_CTRL, RTC_EN)) 61 + return -EINVAL; /* RTC is disabled */ 62 + 63 + ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time)); 64 + if (ret) 65 + return ret; 66 + 67 + do { 68 + seconds = time[0]; 69 + ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time)); 70 + if (ret) 71 + return ret; 72 + } while (time[0] != seconds && --count); 73 + 74 + if (!count) 75 + return -EIO; /* Unable to get a consistent result */ 76 + 77 + t->tm_sec = time[0] & GENMASK(5, 0); 78 + t->tm_min = time[1] & GENMASK(5, 0); 79 + t->tm_hour = time[2] & GENMASK(4, 0); 80 + t->tm_mday = (time[3] & GENMASK(4, 0)) + 1; 81 + t->tm_mon = time[4] & GENMASK(3, 0); 82 + t->tm_year = (time[5] & GENMASK(5, 0)) + 100; 83 + 84 + return 0; 85 + } 86 + 87 + /* 88 + * The P1 hardware documentation states that values in the registers are 89 + * latched so when written they represent a consistent time snapshot. 90 + * Nevertheless, this is not guaranteed by the implementation, so we must 91 + * disable the RTC while updating it. 92 + */ 93 + static int p1_rtc_set_time(struct device *dev, struct rtc_time *t) 94 + { 95 + struct p1_rtc *p1 = dev_get_drvdata(dev); 96 + struct regmap *regmap = p1->regmap; 97 + u8 time[6]; 98 + int ret; 99 + 100 + time[0] = t->tm_sec; 101 + time[1] = t->tm_min; 102 + time[2] = t->tm_hour; 103 + time[3] = t->tm_mday - 1; 104 + time[4] = t->tm_mon; 105 + time[5] = t->tm_year - 100; 106 + 107 + /* Disable the RTC to update; re-enable again when done */ 108 + ret = regmap_clear_bits(regmap, RTC_CTRL, RTC_EN); 109 + if (ret) 110 + return ret; 111 + 112 + /* If something goes wrong, leave the RTC disabled */ 113 + ret = regmap_bulk_write(regmap, RTC_TIME, time, sizeof(time)); 114 + if (ret) 115 + return ret; 116 + 117 + return regmap_set_bits(regmap, RTC_CTRL, RTC_EN); 118 + } 119 + 120 + static const struct rtc_class_ops p1_rtc_class_ops = { 121 + .read_time = p1_rtc_read_time, 122 + .set_time = p1_rtc_set_time, 123 + }; 124 + 125 + static int p1_rtc_probe(struct platform_device *pdev) 126 + { 127 + struct device *dev = &pdev->dev; 128 + struct rtc_device *rtc; 129 + struct p1_rtc *p1; 130 + 131 + p1 = devm_kzalloc(dev, sizeof(*p1), GFP_KERNEL); 132 + if (!p1) 133 + return -ENOMEM; 134 + dev_set_drvdata(dev, p1); 135 + 136 + p1->regmap = dev_get_regmap(dev->parent, NULL); 137 + if (!p1->regmap) 138 + return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); 139 + 140 + rtc = devm_rtc_allocate_device(dev); 141 + if (IS_ERR(rtc)) 142 + return dev_err_probe(dev, PTR_ERR(rtc), 143 + "error allocating device\n"); 144 + p1->rtc = rtc; 145 + 146 + rtc->ops = &p1_rtc_class_ops; 147 + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 148 + rtc->range_max = RTC_TIMESTAMP_END_2063; 149 + 150 + clear_bit(RTC_FEATURE_ALARM, rtc->features); 151 + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); 152 + 153 + return devm_rtc_register_device(rtc); 154 + } 155 + 156 + static struct platform_driver p1_rtc_driver = { 157 + .probe = p1_rtc_probe, 158 + .driver = { 159 + .name = MOD_NAME, 160 + }, 161 + }; 162 + 163 + module_platform_driver(p1_rtc_driver); 164 + 165 + MODULE_DESCRIPTION("SpacemiT P1 RTC driver"); 166 + MODULE_LICENSE("GPL"); 167 + MODULE_ALIAS("platform:" MOD_NAME);