* [PATCH V5 0/3] rtc: atcrtc100: Add Andes ATCRTC100 RTC driver
@ 2025-01-10 9:26 CL Wang
2025-01-10 9:27 ` [PATCH V5 1/3] rtc: atcrtc100: Add " CL Wang
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: CL Wang @ 2025-01-10 9:26 UTC (permalink / raw)
To: cl634, alexandre.belloni, robh, krzk+dt, conor+dt
Cc: devicetree, linux-kernel, linux-rtc, tim609, ycliang
This patch series adds support for the Andes ATCRTC100 Real-Time Clock.
The series is now based on the rtc-next branch from:
git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git.
This V5 patch series is built upon the V4 series and has been rebased
exclusively onto the latest commit in the rtc-next branch, which
corresponds to Linux 6.13-rc1."
For details of the change log, please refer to the commit log of each patch.
Please kindly review.
CL Wang (3):
rtc: atcrtc100: Add ATCRTC100 RTC driver
dt-bindings: rtc: Add support for ATCRTC100 RTC
MAINTAINERS: Add entry for ATCRTC100 RTC driver
.../bindings/rtc/andestech,atcrtc100.yaml | 43 ++
MAINTAINERS | 6 +
drivers/rtc/Kconfig | 15 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-atcrtc100.c | 524 ++++++++++++++++++
5 files changed, 589 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/andestech,atcrtc100.yaml
create mode 100644 drivers/rtc/rtc-atcrtc100.c
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V5 1/3] rtc: atcrtc100: Add ATCRTC100 RTC driver
2025-01-10 9:26 [PATCH V5 0/3] rtc: atcrtc100: Add Andes ATCRTC100 RTC driver CL Wang
@ 2025-01-10 9:27 ` CL Wang
2025-03-31 22:15 ` Alexandre Belloni
2025-01-10 9:27 ` [PATCH V5 2/3] dt-bindings: rtc: Add support for ATCRTC100 RTC CL Wang
2025-01-10 9:27 ` [PATCH V5 3/3] MAINTAINERS: Add entry for ATCRTC100 RTC driver CL Wang
2 siblings, 1 reply; 6+ messages in thread
From: CL Wang @ 2025-01-10 9:27 UTC (permalink / raw)
To: cl634, alexandre.belloni, robh, krzk+dt, conor+dt
Cc: devicetree, linux-kernel, linux-rtc, tim609, ycliang
RTC driver for Andes ATCRTC100 Real-Time Clock.
Signed-off-by: CL Wang <cl634@andestech.com>
---
Changes for v1:
- Initial version of the ATCRTC100 driver.
Changes for v2:
- Replaced legacy APIs with devm APIs for system resource allocation.
- Used regmap APIs to access I/O registers.
Changes for v3:
- Removed 'of_match_ptr()'.
- Added check for WriteDone status before accessing Counter, Alarm, and Control registers.
Changes for v4:
- Refined the procedure for setting the wake-up source.
- Fixed coding style to comply with Linux coding style guidelines.
Changes for v5:
- Rebased onto the latest commit in rtc-next. No changes to the code.
---
drivers/rtc/Kconfig | 15 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-atcrtc100.c | 524 ++++++++++++++++++++++++++++++++++++
3 files changed, 540 insertions(+)
create mode 100644 drivers/rtc/rtc-atcrtc100.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a60bcc791a48..eac651f65578 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1040,6 +1040,21 @@ config RTC_DRV_ALPHA
Direct support for the real-time clock found on every Alpha
system, specifically MC146818 compatibles. If in doubt, say Y.
+config RTC_DRV_ATCRTC100
+ tristate "Andes ATCRTC100"
+ depends on RISCV
+ select REGMAP_MMIO
+ help
+ If you say yes here you will get support for the Andes ATCRTC100
+ RTC driver.
+
+ This driver provides support for the Andes ATCRTC100 real-time clock
+ device. It allows setting and retrieving the time and date, as well
+ as setting alarms.
+
+ To compile this driver as a module, choose M here: the module will
+ be called rtc-atcrtc100.
+
config RTC_DRV_DS1216
tristate "Dallas DS1216"
depends on SNI_RM
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 489b4ab07068..1a738d011e20 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
obj-$(CONFIG_RTC_DRV_ASPEED) += rtc-aspeed.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
+obj-$(CONFIG_RTC_DRV_ATCRTC100) += rtc-atcrtc100.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BBNSM) += rtc-nxp-bbnsm.o
obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.o
diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c
new file mode 100644
index 000000000000..40629f796f3b
--- /dev/null
+++ b/drivers/rtc/rtc-atcrtc100.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Andes ATCRTC100 real time clock.
+ *
+ * Copyright (C) 2024 Andes Technology Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+
+#define RTC_ID 0x00 /* ID and Revision */
+#define ID_OFF 12
+#define ID_MSK (0xFFFFF << ID_OFF)
+#define ATCRTC100ID (0x03011 << ID_OFF)
+#define RTC_RSV 0x4 /* Reserve */
+#define RTC_CNT 0x10 /* Counter */
+#define RTC_ALM 0x14 /* Alarm */
+#define DAY_OFF 17
+#define DAY_MSK 0x7FFF
+#define HOUR_OFF 12
+#define HOUR_MSK 0x1F
+#define MIN_OFF 6
+#define MIN_MSK 0x3F
+#define SEC_OFF 0
+#define SEC_MSK 0x3F
+#define RTC_SECOND(x) ((x >> SEC_OFF) & SEC_MSK) /* RTC sec */
+#define RTC_MINUTE(x) ((x >> MIN_OFF) & MIN_MSK) /* RTC min */
+#define RTC_HOUR(x) ((x >> HOUR_OFF) & HOUR_MSK) /* RTC hour */
+#define RTC_DAYS(x) ((x >> DAY_OFF) & DAY_MSK) /* RTC day */
+
+#define RTC_CR 0x18 /* Control */
+#define RTC_EN (0x1UL << 0)
+#define ALARM_WAKEUP (0x1UL << 1)
+#define ALARM_INT (0x1UL << 2)
+#define DAY_INT (0x1UL << 3)
+#define HOUR_INT (0x1UL << 4)
+#define MIN_INT (0x1UL << 5)
+#define SEC_INT (0x1UL << 6)
+#define HSEC_INT (0x1UL << 7)
+#define RTC_STA 0x1C /* Status */
+#define WRITE_DONE (0x1UL << 16)
+#define RTC_TRIM 0x20 /* Digital Trimming */
+
+#define ATCRTC_TIME_TO_SEC(D, H, M, S) (D * 86400LL + H * 3600 + M * 60 + S)
+
+#define ATCRTC_TIMEOUT_US 1000000
+#define ATCRTC_TIMEOUT_USLEEP_MIN 20
+#define ATCRTC_TIMEOUT_USLEEP_MAX 30
+
+struct atcrtc_dev {
+ struct rtc_device *rtc_dev;
+ struct regmap *regmap;
+ struct delayed_work rtc_work;
+ struct mutex lock;
+ unsigned int alarm_irq;
+ unsigned int time_irq;
+ unsigned char alarm_en;
+};
+
+static const struct regmap_config atcrtc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = RTC_TRIM,
+ .cache_type = REGCACHE_NONE,
+};
+
+/**
+ * atcrtc_check_write_done - Check whether the ATCRTC100 is ready or not.
+ * @rtc: Pointer of atcrtc_dev.
+ *
+ * The WriteDone bit in the status register indicates the synchronization
+ * progress of RTC register updates. This bit is cleared to zero whenever
+ * any RTC control register such as the Counter, Alarm, Control, or Digital
+ * Trimming registers is updated. It returns to one only after all previous
+ * updates to these registers have been fully synchronized to the RTC clock
+ * domain. If a register update is in the process of being synchronized, a
+ * second update to the same register may be ignored.
+ */
+static int atcrtc_check_write_done(struct atcrtc_dev *rtc)
+{
+ int loop;
+ int timeout;
+
+ might_sleep();
+ timeout = ATCRTC_TIMEOUT_US / ATCRTC_TIMEOUT_USLEEP_MIN;
+
+ for (loop = 0; loop < timeout; loop++) {
+ if (regmap_test_bits(rtc->regmap, RTC_STA, WRITE_DONE))
+ return 0;
+
+ usleep_range(ATCRTC_TIMEOUT_USLEEP_MIN,
+ ATCRTC_TIMEOUT_USLEEP_MAX);
+ }
+ dev_err(&rtc->rtc_dev->dev, "Device is busy too long\n");
+ return -EBUSY;
+}
+
+static irqreturn_t atcrtc_periodic_isr(int irq, void *dev)
+{
+ struct atcrtc_dev *rtc = dev;
+
+ if (regmap_test_bits(rtc->regmap, RTC_STA, SEC_INT)) {
+ regmap_write_bits(rtc->regmap, RTC_STA, SEC_INT, SEC_INT);
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static irqreturn_t atcrtc_alarm_isr(int irq, void *dev)
+{
+ struct atcrtc_dev *rtc = dev;
+
+ if (regmap_test_bits(rtc->regmap, RTC_STA, ALARM_INT)) {
+ regmap_write_bits(rtc->regmap, RTC_STA, ALARM_INT, ALARM_INT);
+ rtc->alarm_en = 0;
+ schedule_delayed_work(&rtc->rtc_work, 0);
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int atcrtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&rtc->lock);
+
+ ret = atcrtc_check_write_done(rtc);
+ if (ret) {
+ mutex_unlock(&rtc->lock);
+ return ret;
+ }
+
+ if (enable)
+ regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, ALARM_INT);
+ else
+ regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, 0);
+
+ mutex_unlock(&rtc->lock);
+
+ return 0;
+}
+
+static int atcrtc_alarm_enable(struct device *dev, unsigned int enable)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&rtc->lock);
+
+ ret = atcrtc_check_write_done(rtc);
+ if (ret) {
+ mutex_unlock(&rtc->lock);
+ return ret;
+ }
+
+ if (enable) {
+ regmap_update_bits(rtc->regmap,
+ RTC_CR,
+ ALARM_WAKEUP,
+ ALARM_WAKEUP);
+ } else {
+ regmap_update_bits(rtc->regmap, RTC_CR, ALARM_WAKEUP, 0);
+ }
+
+ mutex_unlock(&rtc->lock);
+
+ return 0;
+}
+
+static void atcrtc_alarm_clear(struct work_struct *work)
+{
+ struct atcrtc_dev *rtc =
+ container_of(work, struct atcrtc_dev, rtc_work.work);
+ int ret;
+
+ mutex_lock(&rtc->lock);
+ if (rtc->alarm_en == 0) {
+ ret = atcrtc_check_write_done(rtc);
+ if (ret) {
+ mutex_unlock(&rtc->lock);
+ return;
+ }
+ regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, 0);
+ }
+ mutex_unlock(&rtc->lock);
+}
+
+static time64_t atcrtc_read_rtc_time(struct atcrtc_dev *rtc)
+{
+ time64_t time;
+ unsigned int rtc_cnt;
+
+ regmap_read(rtc->regmap, RTC_CNT, &rtc_cnt);
+ time = ATCRTC_TIME_TO_SEC(RTC_DAYS(rtc_cnt),
+ RTC_HOUR(rtc_cnt),
+ RTC_MINUTE(rtc_cnt),
+ RTC_SECOND(rtc_cnt));
+ return time;
+}
+
+static int atcrtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+ time64_t time;
+
+ mutex_lock(&rtc->lock);
+ time = atcrtc_read_rtc_time(rtc);
+ mutex_unlock(&rtc->lock);
+
+ rtc_time64_to_tm(time, tm);
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(dev, "Invalid date: %lld\n", time);
+ rtc_time64_to_tm(0, tm);
+ }
+ return 0;
+}
+
+static void atcrtc_set_rtc_time(struct atcrtc_dev *rtc, time64_t time)
+{
+ int rem;
+ unsigned int counter;
+ unsigned int day;
+ unsigned int hour;
+ unsigned int min;
+ unsigned int sec;
+
+ day = div_s64_rem(time, 86400, &rem);
+ hour = rem / 3600;
+ rem -= hour * 3600;
+ min = rem / 60;
+ sec = rem - min * 60;
+ counter = ((day & DAY_MSK) << DAY_OFF)
+ | ((hour & HOUR_MSK) << HOUR_OFF)
+ | ((min & MIN_MSK) << MIN_OFF)
+ | ((sec & SEC_MSK) << SEC_OFF);
+
+ regmap_write(rtc->regmap, RTC_CNT, counter);
+}
+
+static int atcrtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+ time64_t sys_time;
+ int ret;
+
+ sys_time = rtc_tm_to_time64(tm);
+
+ mutex_lock(&rtc->lock);
+
+ ret = atcrtc_check_write_done(rtc);
+ if (ret) {
+ mutex_unlock(&rtc->lock);
+ return ret;
+ }
+ atcrtc_set_rtc_time(rtc, sys_time);
+
+ mutex_unlock(&rtc->lock);
+
+ return 0;
+}
+
+static int atcrtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+ struct rtc_time *tm = &wkalrm->time;
+ unsigned int rtc_alarm;
+
+ mutex_lock(&rtc->lock);
+
+ regmap_read(rtc->regmap, RTC_ALM, &rtc_alarm);
+ wkalrm->enabled = regmap_test_bits(rtc->regmap, RTC_CR, ALARM_INT);
+
+ mutex_unlock(&rtc->lock);
+
+ tm->tm_hour = (rtc_alarm >> HOUR_OFF) & HOUR_MSK;
+ tm->tm_min = (rtc_alarm >> MIN_OFF) & MIN_MSK;
+ tm->tm_sec = (rtc_alarm >> SEC_OFF) & SEC_MSK;
+
+ return 0;
+}
+
+static int atcrtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+ struct rtc_time *tm = &wkalrm->time;
+ unsigned int alm = 0;
+ int ret = rtc_valid_tm(tm);
+
+ if (ret < 0) {
+ dev_err(dev, "Invalid alarm value: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&rtc->lock);
+
+ ret = atcrtc_check_write_done(rtc);
+ if (ret) {
+ mutex_unlock(&rtc->lock);
+ return ret;
+ }
+
+ /* Disable alarm interrupt and clear the alarm flag */
+ regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, 0);
+ rtc->alarm_en = 0;
+
+ /* Set alarm time */
+ alm |= ((tm->tm_sec & SEC_MSK) << SEC_OFF);
+ alm |= ((tm->tm_min & MIN_MSK) << MIN_OFF);
+ alm |= ((tm->tm_hour & HOUR_MSK) << HOUR_OFF);
+ regmap_write(rtc->regmap, RTC_ALM, alm);
+
+ if (wkalrm->enabled) {
+ rtc->alarm_en = 1;
+ ret = atcrtc_check_write_done(rtc);
+ if (ret) {
+ mutex_unlock(&rtc->lock);
+ return ret;
+ }
+
+ regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, ALARM_INT);
+ }
+
+ mutex_unlock(&rtc->lock);
+ return 0;
+}
+
+static int atcrtc_hw_init(struct atcrtc_dev *rtc)
+{
+ unsigned int rtc_id;
+ int ret;
+
+ regmap_read(rtc->regmap, RTC_ID, &rtc_id);
+ if ((rtc_id & ID_MSK) != ATCRTC100ID)
+ return -ENOENT;
+
+ ret = atcrtc_check_write_done(rtc);
+ if (ret)
+ return ret;
+ regmap_update_bits(rtc->regmap, RTC_CR, RTC_EN, RTC_EN);
+
+ return 0;
+}
+
+static const struct rtc_class_ops rtc_ops = {
+ .read_time = atcrtc_read_time,
+ .set_time = atcrtc_set_time,
+ .read_alarm = atcrtc_read_alarm,
+ .set_alarm = atcrtc_set_alarm,
+ .alarm_irq_enable = atcrtc_alarm_irq_enable,
+};
+
+static int atcrtc_probe(struct platform_device *pdev)
+{
+ struct atcrtc_dev *atcrtc_dev;
+ void __iomem *reg_base;
+ int ret = 0;
+
+ atcrtc_dev = devm_kzalloc(&pdev->dev, sizeof(*atcrtc_dev), GFP_KERNEL);
+ if (!atcrtc_dev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, atcrtc_dev);
+ mutex_init(&atcrtc_dev->lock);
+
+ reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reg_base)) {
+ dev_err(&pdev->dev,
+ "Failed to map I/O space: %ld\n",
+ PTR_ERR(reg_base));
+ return PTR_ERR(atcrtc_dev->regmap);
+ }
+
+ atcrtc_dev->regmap = devm_regmap_init_mmio(&pdev->dev,
+ reg_base,
+ &atcrtc_regmap_config);
+ if (IS_ERR(atcrtc_dev->regmap)) {
+ dev_err(&pdev->dev,
+ "Failed to initialize regmap: %ld\n",
+ PTR_ERR(atcrtc_dev->regmap));
+ return PTR_ERR(atcrtc_dev->regmap);
+ }
+
+ ret = atcrtc_hw_init(atcrtc_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize driver: %d\n", ret);
+ return ret;
+ }
+
+ atcrtc_dev->alarm_irq = platform_get_irq(pdev, 1);
+ if (atcrtc_dev->alarm_irq < 0) {
+ dev_err(&pdev->dev,
+ "Failed to get IRQ for alarm: %d\n",
+ atcrtc_dev->alarm_irq);
+ return atcrtc_dev->alarm_irq;
+ }
+ atcrtc_dev->time_irq = platform_get_irq(pdev, 0);
+ if (atcrtc_dev->time_irq < 0) {
+ dev_err(&pdev->dev,
+ "Failed to get IRQ for periodic interrupt: %d\n",
+ atcrtc_dev->time_irq);
+ return atcrtc_dev->time_irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev,
+ atcrtc_dev->alarm_irq,
+ atcrtc_alarm_isr,
+ 0,
+ "atcrtc alarm",
+ atcrtc_dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request IRQ %d for alarm: %d\n",
+ atcrtc_dev->alarm_irq,
+ ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev,
+ atcrtc_dev->time_irq,
+ atcrtc_periodic_isr,
+ 0,
+ "atcrtc time",
+ atcrtc_dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request IRQ %d for periodic interrupt: %d\n",
+ atcrtc_dev->time_irq,
+ ret);
+ return ret;
+ }
+
+ atcrtc_dev->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(atcrtc_dev->rtc_dev)) {
+ dev_err(&pdev->dev,
+ "Failed to allocate RTC device: %ld\n",
+ PTR_ERR(atcrtc_dev->rtc_dev));
+ return PTR_ERR(atcrtc_dev->rtc_dev);
+ }
+
+ ret = atcrtc_alarm_enable(&pdev->dev, true);
+ if (ret)
+ return ret;
+ set_bit(RTC_FEATURE_ALARM, atcrtc_dev->rtc_dev->features);
+
+ ret = device_init_wakeup(&pdev->dev, true);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to initialize wake device: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = dev_pm_set_wake_irq(&pdev->dev, atcrtc_dev->alarm_irq);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set wake IRQ: %d\n", ret);
+ device_init_wakeup(&pdev->dev, false);
+ return ret;
+ }
+
+ atcrtc_dev->rtc_dev->ops = &rtc_ops;
+ /*
+ * There are 15 bits in the Day field of the Counter register.
+ * It can count up to 32,767 days, about 89.8 years.
+ */
+ atcrtc_dev->rtc_dev->range_max = mktime64(2089, 12, 31, 23, 59, 59);
+ atcrtc_dev->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
+
+ INIT_DELAYED_WORK(&atcrtc_dev->rtc_work, atcrtc_alarm_clear);
+ return devm_rtc_register_device(atcrtc_dev->rtc_dev);
+}
+
+static int atcrtc_resume(struct device *dev)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->alarm_irq);
+
+ return 0;
+}
+
+static int atcrtc_suspend(struct device *dev)
+{
+ struct atcrtc_dev *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->alarm_irq);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(atcrtc_pm_ops, atcrtc_suspend, atcrtc_resume);
+
+static const struct of_device_id atcrtc_dt_match[] = {
+ { .compatible = "andestech,atcrtc100" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atcrtc_dt_match);
+
+static struct platform_driver atcrtc_platform_driver = {
+ .driver = {
+ .name = "atcrtc100",
+ .of_match_table = atcrtc_dt_match,
+ .pm = pm_sleep_ptr(&atcrtc_pm_ops),
+ },
+ .probe = atcrtc_probe,
+};
+
+module_platform_driver(atcrtc_platform_driver);
+
+MODULE_AUTHOR("CL Wang <cl634@andestech.com>");
+MODULE_DESCRIPTION("Andes ATCRTC100 driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V5 2/3] dt-bindings: rtc: Add support for ATCRTC100 RTC
2025-01-10 9:26 [PATCH V5 0/3] rtc: atcrtc100: Add Andes ATCRTC100 RTC driver CL Wang
2025-01-10 9:27 ` [PATCH V5 1/3] rtc: atcrtc100: Add " CL Wang
@ 2025-01-10 9:27 ` CL Wang
2025-01-10 9:27 ` [PATCH V5 3/3] MAINTAINERS: Add entry for ATCRTC100 RTC driver CL Wang
2 siblings, 0 replies; 6+ messages in thread
From: CL Wang @ 2025-01-10 9:27 UTC (permalink / raw)
To: cl634, alexandre.belloni, robh, krzk+dt, conor+dt
Cc: devicetree, linux-kernel, linux-rtc, tim609, ycliang,
Conor Dooley
Document Device Tree bindings for the Andes ATCRTC100 Real-Time Clock.
Signed-off-by: CL Wang <cl634@andestech.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
---
Changes for v2:
- First version of devicetree bindings for the Andes ATCRTC100
Real-Time Clock.
Changes for v3:
- Used compatible as the filename.
- Placed allOf after maintainers.
- Replaced additionalProperties: false with unevaluatedProperties: false.
- Added descriptions for interrupts.
Changes for v4:
- Removed wakeup-source attribute.
Changes for v5:
- Rebased onto the latest commit in rtc-next. No changes to the content
itself.
- Add Conor's tag
---
.../bindings/rtc/andestech,atcrtc100.yaml | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/andestech,atcrtc100.yaml
diff --git a/Documentation/devicetree/bindings/rtc/andestech,atcrtc100.yaml b/Documentation/devicetree/bindings/rtc/andestech,atcrtc100.yaml
new file mode 100644
index 000000000000..ec0a736793c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/andestech,atcrtc100.yaml
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/andestech,atcrtc100.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Andes ATCRTC100 Real-Time Clock
+
+maintainers:
+ - CL Wang <cl634@andestech.com>
+
+allOf:
+ - $ref: rtc.yaml#
+
+properties:
+ compatible:
+ enum:
+ - andestech,atcrtc100
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: Periodic timekeeping interrupt
+ - description: RTC alarm interrupt
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ rtc@f0300000 {
+ compatible = "andestech,atcrtc100";
+ reg = <0xf0300000 0x100>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH>, <2 IRQ_TYPE_LEVEL_HIGH>;
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V5 3/3] MAINTAINERS: Add entry for ATCRTC100 RTC driver
2025-01-10 9:26 [PATCH V5 0/3] rtc: atcrtc100: Add Andes ATCRTC100 RTC driver CL Wang
2025-01-10 9:27 ` [PATCH V5 1/3] rtc: atcrtc100: Add " CL Wang
2025-01-10 9:27 ` [PATCH V5 2/3] dt-bindings: rtc: Add support for ATCRTC100 RTC CL Wang
@ 2025-01-10 9:27 ` CL Wang
2 siblings, 0 replies; 6+ messages in thread
From: CL Wang @ 2025-01-10 9:27 UTC (permalink / raw)
To: cl634, alexandre.belloni, robh, krzk+dt, conor+dt
Cc: devicetree, linux-kernel, linux-rtc, tim609, ycliang
Add support entry for the Andes ATCRTC100 RTC driver in the MAINTAINERS file.
Signed-off-by: CL Wang <cl634@andestech.com>
---
Changes for v3:
- Initial entry for the ATCRTC100 RTC driver.
Changes for v4:
- No changes
Changes for v5:
- Rebased onto the latest commit in rtc-next. No changes to the
content itself.
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1e930c7a58b1..b1c4143a3366 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3588,6 +3588,12 @@ F: drivers/power/reset/atc260x-poweroff.c
F: drivers/regulator/atc260x-regulator.c
F: include/linux/mfd/atc260x/*
+ATCRTC100 RTC DRIVER
+M: CL Wang <cl634@andestech.com>
+S: Supported
+F: Documentation/devicetree/bindings/rtc/andestech,atcrtc100.yaml
+F: drivers/rtc/rtc-atcrtc100.c
+
ATHEROS 71XX/9XXX GPIO DRIVER
M: Alban Bedel <albeu@free.fr>
S: Maintained
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V5 1/3] rtc: atcrtc100: Add ATCRTC100 RTC driver
2025-01-10 9:27 ` [PATCH V5 1/3] rtc: atcrtc100: Add " CL Wang
@ 2025-03-31 22:15 ` Alexandre Belloni
2025-04-11 8:39 ` CL Wang
0 siblings, 1 reply; 6+ messages in thread
From: Alexandre Belloni @ 2025-03-31 22:15 UTC (permalink / raw)
To: CL Wang
Cc: robh, krzk+dt, conor+dt, devicetree, linux-kernel, linux-rtc,
tim609, ycliang
Hello,
I'm sorry for the late review, I was pretty sure I reviewed v4.
On 10/01/2025 17:27:00+0800, CL Wang wrote:
> +#define RTC_SECOND(x) ((x >> SEC_OFF) & SEC_MSK) /* RTC sec */
> +#define RTC_MINUTE(x) ((x >> MIN_OFF) & MIN_MSK) /* RTC min */
> +#define RTC_HOUR(x) ((x >> HOUR_OFF) & HOUR_MSK) /* RTC hour */
> +#define RTC_DAYS(x) ((x >> DAY_OFF) & DAY_MSK) /* RTC day */
FIELD_PREP can probably replace those.
> +
> +#define RTC_CR 0x18 /* Control */
> +#define RTC_EN (0x1UL << 0)
> +#define ALARM_WAKEUP (0x1UL << 1)
> +#define ALARM_INT (0x1UL << 2)
> +#define DAY_INT (0x1UL << 3)
> +#define HOUR_INT (0x1UL << 4)
> +#define MIN_INT (0x1UL << 5)
> +#define SEC_INT (0x1UL << 6)
> +#define HSEC_INT (0x1UL << 7)
> +#define RTC_STA 0x1C /* Status */
> +#define WRITE_DONE (0x1UL << 16)
> +#define RTC_TRIM 0x20 /* Digital Trimming */
> +
> +#define ATCRTC_TIME_TO_SEC(D, H, M, S) (D * 86400LL + H * 3600 + M * 60 + S)
> +
> +#define ATCRTC_TIMEOUT_US 1000000
> +#define ATCRTC_TIMEOUT_USLEEP_MIN 20
> +#define ATCRTC_TIMEOUT_USLEEP_MAX 30
> +
> +struct atcrtc_dev {
> + struct rtc_device *rtc_dev;
> + struct regmap *regmap;
> + struct delayed_work rtc_work;
> + struct mutex lock;
This mutex is not necessary, simply use rtc_lock() in you interrupt
handler, the rtc core is already locking before calling the rtc_ops.
> + unsigned int alarm_irq;
> + unsigned int time_irq;
> + unsigned char alarm_en;
> +};
> +
> +/**
> + * atcrtc_check_write_done - Check whether the ATCRTC100 is ready or not.
> + * @rtc: Pointer of atcrtc_dev.
> + *
> + * The WriteDone bit in the status register indicates the synchronization
> + * progress of RTC register updates. This bit is cleared to zero whenever
> + * any RTC control register such as the Counter, Alarm, Control, or Digital
> + * Trimming registers is updated. It returns to one only after all previous
> + * updates to these registers have been fully synchronized to the RTC clock
> + * domain. If a register update is in the process of being synchronized, a
> + * second update to the same register may be ignored.
> + */
> +static int atcrtc_check_write_done(struct atcrtc_dev *rtc)
> +{
> + int loop;
> + int timeout;
> +
> + might_sleep();
> + timeout = ATCRTC_TIMEOUT_US / ATCRTC_TIMEOUT_USLEEP_MIN;
> +
> + for (loop = 0; loop < timeout; loop++) {
> + if (regmap_test_bits(rtc->regmap, RTC_STA, WRITE_DONE))
> + return 0;
> +
> + usleep_range(ATCRTC_TIMEOUT_USLEEP_MIN,
> + ATCRTC_TIMEOUT_USLEEP_MAX);
> + }
> + dev_err(&rtc->rtc_dev->dev, "Device is busy too long\n");
Is this error message useful, what would be the user action after seeing
this?
> + return -EBUSY;
> +}
> +
+
> +static time64_t atcrtc_read_rtc_time(struct atcrtc_dev *rtc)
Does this have to be in a separate function?
> +{
> + time64_t time;
> + unsigned int rtc_cnt;
> +
> + regmap_read(rtc->regmap, RTC_CNT, &rtc_cnt);
> + time = ATCRTC_TIME_TO_SEC(RTC_DAYS(rtc_cnt),
> + RTC_HOUR(rtc_cnt),
> + RTC_MINUTE(rtc_cnt),
> + RTC_SECOND(rtc_cnt));
> + return time;
> +}
> +
> +static int atcrtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct atcrtc_dev *rtc = dev_get_drvdata(dev);
> + time64_t time;
> +
> + mutex_lock(&rtc->lock);
> + time = atcrtc_read_rtc_time(rtc);
> + mutex_unlock(&rtc->lock);
> +
> + rtc_time64_to_tm(time, tm);
> + if (rtc_valid_tm(tm) < 0) {
This is not necessary, the core always checks whether the tm is valid.
> + dev_err(dev, "Invalid date: %lld\n", time);
> + rtc_time64_to_tm(0, tm);
> + }
> + return 0;
> +}
> +
> +static void atcrtc_set_rtc_time(struct atcrtc_dev *rtc, time64_t time)
> +{
> + int rem;
> + unsigned int counter;
> + unsigned int day;
> + unsigned int hour;
> + unsigned int min;
> + unsigned int sec;
> +
> + day = div_s64_rem(time, 86400, &rem);
> + hour = rem / 3600;
> + rem -= hour * 3600;
> + min = rem / 60;
> + sec = rem - min * 60;
You already had the broken down hour, min and sec, it is not necessary
to compute that again here, just fold this function in atcrtc_set_time
> + counter = ((day & DAY_MSK) << DAY_OFF)
> + | ((hour & HOUR_MSK) << HOUR_OFF)
> + | ((min & MIN_MSK) << MIN_OFF)
> + | ((sec & SEC_MSK) << SEC_OFF);
> +
> + regmap_write(rtc->regmap, RTC_CNT, counter);
> +}
> +
> +static int atcrtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct atcrtc_dev *rtc = dev_get_drvdata(dev);
> + time64_t sys_time;
> + int ret;
> +
> + sys_time = rtc_tm_to_time64(tm);
> +
> + mutex_lock(&rtc->lock);
> +
> + ret = atcrtc_check_write_done(rtc);
> + if (ret) {
> + mutex_unlock(&rtc->lock);
> + return ret;
> + }
> + atcrtc_set_rtc_time(rtc, sys_time);
> +
> + mutex_unlock(&rtc->lock);
> +
> + return 0;
> +}
> +
> +static int atcrtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + struct atcrtc_dev *rtc = dev_get_drvdata(dev);
> + struct rtc_time *tm = &wkalrm->time;
> + unsigned int rtc_alarm;
> +
> + mutex_lock(&rtc->lock);
> +
> + regmap_read(rtc->regmap, RTC_ALM, &rtc_alarm);
> + wkalrm->enabled = regmap_test_bits(rtc->regmap, RTC_CR, ALARM_INT);
> +
> + mutex_unlock(&rtc->lock);
> +
> + tm->tm_hour = (rtc_alarm >> HOUR_OFF) & HOUR_MSK;
> + tm->tm_min = (rtc_alarm >> MIN_OFF) & MIN_MSK;
> + tm->tm_sec = (rtc_alarm >> SEC_OFF) & SEC_MSK;
> +
> + return 0;
> +}
> +
> +static int atcrtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + struct atcrtc_dev *rtc = dev_get_drvdata(dev);
> + struct rtc_time *tm = &wkalrm->time;
> + unsigned int alm = 0;
> + int ret = rtc_valid_tm(tm);
> +
> + if (ret < 0) {
> + dev_err(dev, "Invalid alarm value: %d\n", ret);
> + return ret;
> + }
> +
> + mutex_lock(&rtc->lock);
> +
> + ret = atcrtc_check_write_done(rtc);
> + if (ret) {
> + mutex_unlock(&rtc->lock);
> + return ret;
> + }
> +
> + /* Disable alarm interrupt and clear the alarm flag */
> + regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, 0);
> + rtc->alarm_en = 0;
> +
> + /* Set alarm time */
> + alm |= ((tm->tm_sec & SEC_MSK) << SEC_OFF);
> + alm |= ((tm->tm_min & MIN_MSK) << MIN_OFF);
> + alm |= ((tm->tm_hour & HOUR_MSK) << HOUR_OFF);
> + regmap_write(rtc->regmap, RTC_ALM, alm);
> +
> + if (wkalrm->enabled) {
> + rtc->alarm_en = 1;
> + ret = atcrtc_check_write_done(rtc);
> + if (ret) {
> + mutex_unlock(&rtc->lock);
> + return ret;
> + }
> +
> + regmap_update_bits(rtc->regmap, RTC_CR, ALARM_INT, ALARM_INT);
> + }
> +
> + mutex_unlock(&rtc->lock);
> + return 0;
> +}
> +
> +static int atcrtc_hw_init(struct atcrtc_dev *rtc)
> +{
> + unsigned int rtc_id;
> + int ret;
> +
> + regmap_read(rtc->regmap, RTC_ID, &rtc_id);
> + if ((rtc_id & ID_MSK) != ATCRTC100ID)
> + return -ENOENT;
> +
> + ret = atcrtc_check_write_done(rtc);
> + if (ret)
> + return ret;
> + regmap_update_bits(rtc->regmap, RTC_CR, RTC_EN, RTC_EN);
This is losing some important information, the RTC must only be enabled
once the time has been correctly set, then you can check RTC_EN in
atcrtc_read_time() to know whether the time is actually valid or not.
> +
> + return 0;
> +}
> +
> +static const struct rtc_class_ops rtc_ops = {
> + .read_time = atcrtc_read_time,
> + .set_time = atcrtc_set_time,
> + .read_alarm = atcrtc_read_alarm,
> + .set_alarm = atcrtc_set_alarm,
> + .alarm_irq_enable = atcrtc_alarm_irq_enable,
> +};
> +
> +static int atcrtc_probe(struct platform_device *pdev)
> +{
> + struct atcrtc_dev *atcrtc_dev;
> + void __iomem *reg_base;
> + int ret = 0;
> +
> + atcrtc_dev = devm_kzalloc(&pdev->dev, sizeof(*atcrtc_dev), GFP_KERNEL);
> + if (!atcrtc_dev)
> + return -ENOMEM;
> + platform_set_drvdata(pdev, atcrtc_dev);
> + mutex_init(&atcrtc_dev->lock);
> +
> + reg_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(reg_base)) {
> + dev_err(&pdev->dev,
> + "Failed to map I/O space: %ld\n",
> + PTR_ERR(reg_base));
> + return PTR_ERR(atcrtc_dev->regmap);
> + }
> +
> + atcrtc_dev->regmap = devm_regmap_init_mmio(&pdev->dev,
> + reg_base,
> + &atcrtc_regmap_config);
> + if (IS_ERR(atcrtc_dev->regmap)) {
> + dev_err(&pdev->dev,
> + "Failed to initialize regmap: %ld\n",
> + PTR_ERR(atcrtc_dev->regmap));
> + return PTR_ERR(atcrtc_dev->regmap);
> + }
> +
> + ret = atcrtc_hw_init(atcrtc_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to initialize driver: %d\n", ret);
> + return ret;
> + }
> +
> + atcrtc_dev->alarm_irq = platform_get_irq(pdev, 1);
> + if (atcrtc_dev->alarm_irq < 0) {
> + dev_err(&pdev->dev,
> + "Failed to get IRQ for alarm: %d\n",
> + atcrtc_dev->alarm_irq);
> + return atcrtc_dev->alarm_irq;
> + }
> + atcrtc_dev->time_irq = platform_get_irq(pdev, 0);
> + if (atcrtc_dev->time_irq < 0) {
> + dev_err(&pdev->dev,
> + "Failed to get IRQ for periodic interrupt: %d\n",
> + atcrtc_dev->time_irq);
> + return atcrtc_dev->time_irq;
> + }
> +
> + ret = devm_request_irq(&pdev->dev,
> + atcrtc_dev->alarm_irq,
> + atcrtc_alarm_isr,
> + 0,
> + "atcrtc alarm",
> + atcrtc_dev);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Failed to request IRQ %d for alarm: %d\n",
> + atcrtc_dev->alarm_irq,
> + ret);
> + return ret;
> + }
> +
> + ret = devm_request_irq(&pdev->dev,
> + atcrtc_dev->time_irq,
> + atcrtc_periodic_isr,
> + 0,
> + "atcrtc time",
> + atcrtc_dev);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Failed to request IRQ %d for periodic interrupt: %d\n",
> + atcrtc_dev->time_irq,
> + ret);
> + return ret;
> + }
> +
> + atcrtc_dev->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
> + if (IS_ERR(atcrtc_dev->rtc_dev)) {
> + dev_err(&pdev->dev,
> + "Failed to allocate RTC device: %ld\n",
> + PTR_ERR(atcrtc_dev->rtc_dev));
> + return PTR_ERR(atcrtc_dev->rtc_dev);
> + }
> +
> + ret = atcrtc_alarm_enable(&pdev->dev, true);
Can't atcrtc_alarm_enable be part of atcrtc_hw_init so you don't have to
wait twice?
> + if (ret)
> + return ret;
> + set_bit(RTC_FEATURE_ALARM, atcrtc_dev->rtc_dev->features);
> +
> + ret = device_init_wakeup(&pdev->dev, true);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Failed to initialize wake device: %d\n",
> + ret);
> + return ret;
> + }
> +
> + ret = dev_pm_set_wake_irq(&pdev->dev, atcrtc_dev->alarm_irq);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to set wake IRQ: %d\n", ret);
> + device_init_wakeup(&pdev->dev, false);
> + return ret;
> + }
> +
> + atcrtc_dev->rtc_dev->ops = &rtc_ops;
> + /*
> + * There are 15 bits in the Day field of the Counter register.
> + * It can count up to 32,767 days, about 89.8 years.
> + */
> + atcrtc_dev->rtc_dev->range_max = mktime64(2089, 12, 31, 23, 59, 59);
> + atcrtc_dev->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
> +
> + INIT_DELAYED_WORK(&atcrtc_dev->rtc_work, atcrtc_alarm_clear);
> + return devm_rtc_register_device(atcrtc_dev->rtc_dev);
> +}
> +
> +static int atcrtc_resume(struct device *dev)
> +{
> + struct atcrtc_dev *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(rtc->alarm_irq);
> +
> + return 0;
> +}
> +
> +static int atcrtc_suspend(struct device *dev)
> +{
> + struct atcrtc_dev *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + enable_irq_wake(rtc->alarm_irq);
> +
> + return 0;
> +}
> +
> +static DEFINE_SIMPLE_DEV_PM_OPS(atcrtc_pm_ops, atcrtc_suspend, atcrtc_resume);
> +
> +static const struct of_device_id atcrtc_dt_match[] = {
> + { .compatible = "andestech,atcrtc100" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, atcrtc_dt_match);
> +
> +static struct platform_driver atcrtc_platform_driver = {
> + .driver = {
> + .name = "atcrtc100",
> + .of_match_table = atcrtc_dt_match,
> + .pm = pm_sleep_ptr(&atcrtc_pm_ops),
> + },
> + .probe = atcrtc_probe,
> +};
> +
> +module_platform_driver(atcrtc_platform_driver);
> +
> +MODULE_AUTHOR("CL Wang <cl634@andestech.com>");
> +MODULE_DESCRIPTION("Andes ATCRTC100 driver");
> +MODULE_LICENSE("GPL");
> --
> 2.34.1
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH V5 1/3] rtc: atcrtc100: Add ATCRTC100 RTC driver
2025-03-31 22:15 ` Alexandre Belloni
@ 2025-04-11 8:39 ` CL Wang
0 siblings, 0 replies; 6+ messages in thread
From: CL Wang @ 2025-04-11 8:39 UTC (permalink / raw)
To: Alexandre Belloni
Cc: robh, krzk+dt, conor+dt, devicetree, linux-kernel, linux-rtc,
tim609, ycliang, cl634
Hi Alexandre,
Thank you very much for your feedback on the patch, and sorry for the delayed response.
Below are my replies to your comments and questions. I will prepare and send the next
version of the patch as soon as possible.
> +#define RTC_MINUTE(x) ((x >> MIN_OFF) & MIN_MSK) /* RTC min */
> +#define RTC_HOUR(x) ((x >> HOUR_OFF) & HOUR_MSK) /* RTC hour */
> +#define RTC_DAYS(x) ((x >> DAY_OFF) & DAY_MSK) /* RTC day */
FIELD_PREP can probably replace those.
=> That's a good suggestion. I will update this to use bitfield-related macros instead.
> +struct atcrtc_dev {
> + struct rtc_device *rtc_dev;
> + struct regmap *regmap;
> + struct delayed_work rtc_work;
> + struct mutex lock;
This mutex is not necessary, simply use rtc_lock() in you interrupt handler, the rtc core is already locking before calling the rtc_ops.
=> You're absolutely right. I will remove the mutex and clean up this
part accordingly.
> + usleep_range(ATCRTC_TIMEOUT_USLEEP_MIN,
> + ATCRTC_TIMEOUT_USLEEP_MAX);
> + }
> + dev_err(&rtc->rtc_dev->dev, "Device is busy too long\n");
Is this error message useful, what would be the user action after seeing this?
==> This message indicates that the RTC hardware might be stuck in a busy state.
If this occurs, it suggests a potential hardware issue. During development, it
can serve as a hint to review the RTC module's design. In production, a system
reset might be required to recover. Based on that, I would prefer to keep this
error message for diagnostic purposes.
> +static time64_t atcrtc_read_rtc_time(struct atcrtc_dev *rtc)
Does this have to be in a separate function?
=> Not necessarily. It can be merged into atcrtc_read_time(). I will
make this adjustment.
> + rtc_time64_to_tm(time, tm);
> + if (rtc_valid_tm(tm) < 0) {
This is not necessary, the core always checks whether the tm is valid.
=> Thanks for pointing that out. I’ll remove this check.
> + rem -= hour * 3600;
> + min = rem / 60;
> + sec = rem - min * 60;
You already had the broken down hour, min and sec, it is not necessary to compute that again here, just fold this function in atcrtc_set_time
=> You're right, I will simplify this part by integrating it directly
into atcrtc_set_time().
> + ret = atcrtc_check_write_done(rtc);
> + if (ret)
> + return ret;
> + regmap_update_bits(rtc->regmap, RTC_CR, RTC_EN, RTC_EN);
This is losing some important information, the RTC must only be enabled once the time has been correctly set, then you can check RTC_EN in
atcrtc_read_time() to know whether the time is actually valid or not.
=> I will move the RTC_EN setting to atcrtc_set_time() and add a check for
this bit in atcrtc_read_time() to ensure the time from RTC is valid.
> + if (IS_ERR(atcrtc_dev->rtc_dev)) {
> + dev_err(&pdev->dev,
> + "Failed to allocate RTC device: %ld\n",
> + PTR_ERR(atcrtc_dev->rtc_dev));
> + return PTR_ERR(atcrtc_dev->rtc_dev);
> + }
> +
> + ret = atcrtc_alarm_enable(&pdev->dev, true);
Can't atcrtc_alarm_enable be part of atcrtc_hw_init so you don't have to wait twice?
=> After reviewing your comment, I agree. I think atcrtc_alarm_enable()
should instead be integrated into atcrtc_set_alarm() and removed from
here.
Thanks again for your detailed feedback. I'll revise the patch accordingly
and send out the updated version soon.
Best regards,
CL
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-04-11 8:39 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-10 9:26 [PATCH V5 0/3] rtc: atcrtc100: Add Andes ATCRTC100 RTC driver CL Wang
2025-01-10 9:27 ` [PATCH V5 1/3] rtc: atcrtc100: Add " CL Wang
2025-03-31 22:15 ` Alexandre Belloni
2025-04-11 8:39 ` CL Wang
2025-01-10 9:27 ` [PATCH V5 2/3] dt-bindings: rtc: Add support for ATCRTC100 RTC CL Wang
2025-01-10 9:27 ` [PATCH V5 3/3] MAINTAINERS: Add entry for ATCRTC100 RTC driver CL Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).