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

rtc: add driver for Marvell 88PM886 PMIC RTC

RTC lives on the chip's base register page. Add the relevant register
definitions and implement a basic set/read time functionality. Tested
with the samsung,coreprimevelte smartphone which contains this PMIC and
whose vendor kernel tree has also served as the sole reference for this.

Signed-off-by: Karel Balej <balejk@matfyz.cz>
Acked-by: Lee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20241012193345.18594-2-balejk@matfyz.cz
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Karel Balej and committed by
Alexandre Belloni
82ee16cf e8ba8a2b

+118
+1
MAINTAINERS
··· 13706 13706 F: drivers/input/misc/88pm886-onkey.c 13707 13707 F: drivers/mfd/88pm886.c 13708 13708 F: drivers/regulator/88pm886-regulator.c 13709 + F: drivers/rtc/rtc-88pm886.c 13709 13710 F: include/linux/mfd/88pm886.h 13710 13711 13711 13712 MARVELL ARMADA 3700 PHY DRIVERS
+10
drivers/rtc/Kconfig
··· 182 182 This driver can also be built as a module. If so, the module 183 183 will be called rtc-88pm80x. 184 184 185 + config RTC_DRV_88PM886 186 + tristate "Marvell 88PM886 RTC driver" 187 + depends on MFD_88PM886_PMIC 188 + help 189 + If you say yes here you will get support for the RTC function in the 190 + Marvell 88PM886 chip. 191 + 192 + This driver can also be built as a module. If so, the module 193 + will be called rtc-88pm886. 194 + 185 195 config RTC_DRV_ABB5ZES3 186 196 select REGMAP_I2C 187 197 tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3"
+1
drivers/rtc/Makefile
··· 21 21 22 22 obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o 23 23 obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o 24 + obj-$(CONFIG_RTC_DRV_88PM886) += rtc-88pm886.o 24 25 obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o 25 26 obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o 26 27 obj-$(CONFIG_RTC_DRV_ABEOZ9) += rtc-ab-eoz9.o
+97
drivers/rtc/rtc-88pm886.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <linux/limits.h> 3 + #include <linux/mod_devicetable.h> 4 + #include <linux/platform_device.h> 5 + #include <linux/rtc.h> 6 + 7 + #include <linux/mfd/88pm886.h> 8 + 9 + /* 10 + * Time is calculated as the sum of a 32-bit read-only advancing counter and a 11 + * writeable constant offset stored in the chip's spare registers. 12 + */ 13 + 14 + static int pm886_rtc_read_time(struct device *dev, struct rtc_time *tm) 15 + { 16 + struct regmap *regmap = dev_get_drvdata(dev); 17 + u32 time; 18 + u32 buf; 19 + int ret; 20 + 21 + ret = regmap_bulk_read(regmap, PM886_REG_RTC_SPARE1, &buf, 4); 22 + if (ret) 23 + return ret; 24 + time = buf; 25 + 26 + ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4); 27 + if (ret) 28 + return ret; 29 + time += buf; 30 + 31 + rtc_time64_to_tm(time, tm); 32 + 33 + return 0; 34 + } 35 + 36 + static int pm886_rtc_set_time(struct device *dev, struct rtc_time *tm) 37 + { 38 + struct regmap *regmap = dev_get_drvdata(dev); 39 + u32 buf; 40 + int ret; 41 + 42 + ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4); 43 + if (ret) 44 + return ret; 45 + 46 + buf = rtc_tm_to_time64(tm) - buf; 47 + 48 + return regmap_bulk_write(regmap, PM886_REG_RTC_SPARE1, &buf, 4); 49 + } 50 + 51 + static const struct rtc_class_ops pm886_rtc_ops = { 52 + .read_time = pm886_rtc_read_time, 53 + .set_time = pm886_rtc_set_time, 54 + }; 55 + 56 + static int pm886_rtc_probe(struct platform_device *pdev) 57 + { 58 + struct pm886_chip *chip = dev_get_drvdata(pdev->dev.parent); 59 + struct device *dev = &pdev->dev; 60 + struct rtc_device *rtc; 61 + int ret; 62 + 63 + platform_set_drvdata(pdev, chip->regmap); 64 + 65 + rtc = devm_rtc_allocate_device(dev); 66 + if (IS_ERR(rtc)) 67 + return dev_err_probe(dev, PTR_ERR(rtc), 68 + "Failed to allocate RTC device\n"); 69 + 70 + rtc->ops = &pm886_rtc_ops; 71 + rtc->range_max = U32_MAX; 72 + 73 + ret = devm_rtc_register_device(rtc); 74 + if (ret) 75 + return dev_err_probe(dev, ret, "Failed to register RTC device\n"); 76 + 77 + return 0; 78 + } 79 + 80 + static const struct platform_device_id pm886_rtc_id_table[] = { 81 + { "88pm886-rtc", }, 82 + { } 83 + }; 84 + MODULE_DEVICE_TABLE(platform, pm886_rtc_id_table); 85 + 86 + static struct platform_driver pm886_rtc_driver = { 87 + .driver = { 88 + .name = "88pm886-rtc", 89 + }, 90 + .probe = pm886_rtc_probe, 91 + .id_table = pm886_rtc_id_table, 92 + }; 93 + module_platform_driver(pm886_rtc_driver); 94 + 95 + MODULE_DESCRIPTION("Marvell 88PM886 RTC driver"); 96 + MODULE_AUTHOR("Karel Balej <balejk@matfyz.cz>"); 97 + MODULE_LICENSE("GPL");
+9
include/linux/mfd/88pm886.h
··· 31 31 #define PM886_INT_WC BIT(1) 32 32 #define PM886_INT_MASK_MODE BIT(2) 33 33 34 + #define PM886_REG_RTC_CNT1 0xd1 35 + #define PM886_REG_RTC_CNT2 0xd2 36 + #define PM886_REG_RTC_CNT3 0xd3 37 + #define PM886_REG_RTC_CNT4 0xd4 38 + #define PM886_REG_RTC_SPARE1 0xea 39 + #define PM886_REG_RTC_SPARE2 0xeb 40 + #define PM886_REG_RTC_SPARE3 0xec 41 + #define PM886_REG_RTC_SPARE4 0xed 42 + #define PM886_REG_RTC_SPARE5 0xee 34 43 #define PM886_REG_RTC_SPARE6 0xef 35 44 36 45 #define PM886_REG_BUCK_EN 0x08