// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Andes ATCRTC100 real time clock. * * Copyright (C) 2025 Andes Technology Corporation */ #include #include #include #include #include #include #include #include #include #include #include /* Register Offsets */ #define RTC_ID 0x00 /* ID and Revision Register */ #define RTC_RSV 0x04 /* Reserved Register */ #define RTC_CNT 0x10 /* Counter Register */ #define RTC_ALM 0x14 /* Alarm Register */ #define RTC_CR 0x18 /* Control Register */ #define RTC_STA 0x1C /* Status Register */ #define RTC_TRIM 0x20 /* Digital Trimming Register */ /* RTC_ID Register */ #define ID_MSK GENMASK(31, 8) #define ID_ATCRTC100 0x030110 /* RTC_CNT and RTC_ALM Register Fields */ #define SEC_MSK GENMASK(5, 0) #define MIN_MSK GENMASK(11, 6) #define HOUR_MSK GENMASK(16, 12) #define DAY_MSK GENMASK(31, 17) #define RTC_SEC_GET(x) FIELD_GET(SEC_MSK, x) #define RTC_MIN_GET(x) FIELD_GET(MIN_MSK, x) #define RTC_HOUR_GET(x) FIELD_GET(HOUR_MSK, x) #define RTC_DAY_GET(x) FIELD_GET(DAY_MSK, x) #define RTC_SEC_SET(x) FIELD_PREP(SEC_MSK, x) #define RTC_MIN_SET(x) FIELD_PREP(MIN_MSK, x) #define RTC_HOUR_SET(x) FIELD_PREP(HOUR_MSK, x) #define RTC_DAY_SET(x) FIELD_PREP(DAY_MSK, x) /* RTC_CR Register Bits */ #define RTC_EN BIT(0) /* RTC Enable */ #define ALARM_WAKEUP BIT(1) /* Alarm Wakeup Enable */ #define ALARM_INT BIT(2) /* Alarm Interrupt Enable */ #define DAY_INT BIT(3) /* Day Interrupt Enable */ #define HOUR_INT BIT(4) /* Hour Interrupt Enable */ #define MIN_INT BIT(5) /* Minute Interrupt Enable */ #define SEC_INT BIT(6) /* Second Periodic Interrupt Enable */ #define HSEC_INT BIT(7) /* Half-Second Periodic Interrupt Enable */ /* RTC_STA Register Bits */ #define WRITE_DONE BIT(16) /* Register write completion status */ /* Time conversion macro */ #define ATCRTC_TIME_TO_SEC(D, H, M, S) \ ((time64_t)(D) * 86400 + (H) * 3600 + (M) * 60 + (S)) /* Timeout for waiting for the write_done bit */ #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 work_struct rtc_work; unsigned int alarm_irq; bool 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 - Wait for RTC registers to be synchronized. * @rtc: Pointer to the atcrtc_dev structure. * * 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 (Counter, Alarm, Control, etc.) is written. * It returns to one only after all previous updates have been fully * synchronized to the RTC clock domain. This function polls the WriteDone * bit with a timeout to ensure the device is ready for the next operation. * * Return: 0 on success, or -EBUSY on timeout. */ static int atcrtc_check_write_done(struct atcrtc_dev *rtc) { unsigned int val; /* * Using read_poll_timeout is more efficient than a manual loop * with usleep_range. */ return regmap_read_poll_timeout(rtc->regmap, RTC_STA, val, val & WRITE_DONE, ATCRTC_TIMEOUT_USLEEP_MIN, ATCRTC_TIMEOUT_US); } static irqreturn_t atcrtc_alarm_isr(int irq, void *dev) { struct atcrtc_dev *rtc = dev; unsigned int status; regmap_read(rtc->regmap, RTC_STA, &status); if (status & ALARM_INT) { regmap_write(rtc->regmap, RTC_STA, ALARM_INT); rtc->alarm_en = false; schedule_work(&rtc->rtc_work); 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); unsigned int mask; int ret; ret = atcrtc_check_write_done(rtc); if (ret) return ret; mask = ALARM_WAKEUP | ALARM_INT; regmap_update_bits(rtc->regmap, RTC_CR, mask, enable ? mask : 0); return 0; } static void atcrtc_alarm_clear(struct work_struct *work) { struct atcrtc_dev *rtc = container_of(work, struct atcrtc_dev, rtc_work); int ret; rtc_lock(rtc->rtc_dev); if (!rtc->alarm_en) { ret = atcrtc_check_write_done(rtc); if (ret) dev_info(&rtc->rtc_dev->dev, "failed to sync before clearing alarm: %d\n", ret); else regmap_update_bits(rtc->regmap, RTC_CR, ALARM_WAKEUP | ALARM_INT, 0); } rtc_unlock(rtc->rtc_dev); } static int atcrtc_read_time(struct device *dev, struct rtc_time *tm) { struct atcrtc_dev *rtc = dev_get_drvdata(dev); time64_t time; unsigned int rtc_cnt; if (!regmap_test_bits(rtc->regmap, RTC_CR, RTC_EN)) return -EIO; regmap_read(rtc->regmap, RTC_CNT, &rtc_cnt); time = ATCRTC_TIME_TO_SEC(RTC_DAY_GET(rtc_cnt), RTC_HOUR_GET(rtc_cnt), RTC_MIN_GET(rtc_cnt), RTC_SEC_GET(rtc_cnt)); rtc_time64_to_tm(time, tm); return 0; } static int atcrtc_set_time(struct device *dev, struct rtc_time *tm) { struct atcrtc_dev *rtc = dev_get_drvdata(dev); time64_t time; unsigned int counter; unsigned int day; int ret; time = rtc_tm_to_time64(tm); day = div_s64(time, 86400); counter = RTC_DAY_SET(day) | RTC_HOUR_SET(tm->tm_hour) | RTC_MIN_SET(tm->tm_min) | RTC_SEC_SET(tm->tm_sec); ret = atcrtc_check_write_done(rtc); if (ret) return ret; regmap_write(rtc->regmap, RTC_CNT, counter); ret = atcrtc_check_write_done(rtc); if (ret) return ret; regmap_update_bits(rtc->regmap, RTC_CR, RTC_EN, RTC_EN); 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; wkalrm->enabled = regmap_test_bits(rtc->regmap, RTC_CR, ALARM_INT); regmap_read(rtc->regmap, RTC_ALM, &rtc_alarm); tm->tm_hour = RTC_HOUR_GET(rtc_alarm); tm->tm_min = RTC_MIN_GET(rtc_alarm); tm->tm_sec = RTC_SEC_GET(rtc_alarm); /* The RTC alarm does not support day/month/year fields */ tm->tm_mday = -1; tm->tm_mon = -1; tm->tm_year = -1; 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 rtc_alarm; int ret; /* Disable alarm first before setting a new one */ ret = atcrtc_alarm_irq_enable(dev, 0); if (ret) return ret; rtc->alarm_en = false; rtc_alarm = RTC_SEC_SET(tm->tm_sec) | RTC_MIN_SET(tm->tm_min) | RTC_HOUR_SET(tm->tm_hour); ret = atcrtc_check_write_done(rtc); if (ret) return ret; regmap_write(rtc->regmap, RTC_ALM, rtc_alarm); rtc->alarm_en = wkalrm->enabled; ret = atcrtc_alarm_irq_enable(dev, wkalrm->enabled); return ret; } 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; unsigned int rtc_id; int ret; atcrtc_dev = devm_kzalloc(&pdev->dev, sizeof(*atcrtc_dev), GFP_KERNEL); if (!atcrtc_dev) return -ENOMEM; platform_set_drvdata(pdev, atcrtc_dev); reg_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg_base)) return dev_err_probe(&pdev->dev, PTR_ERR(reg_base), "Failed to map I/O space\n"); atcrtc_dev->regmap = devm_regmap_init_mmio(&pdev->dev, reg_base, &atcrtc_regmap_config); if (IS_ERR(atcrtc_dev->regmap)) return dev_err_probe(&pdev->dev, PTR_ERR(atcrtc_dev->regmap), "Failed to initialize regmap\n"); regmap_read(atcrtc_dev->regmap, RTC_ID, &rtc_id); if (FIELD_GET(ID_MSK, rtc_id) != ID_ATCRTC100) return dev_err_probe(&pdev->dev, -ENODEV, "Failed to initialize RTC: unsupported hardware ID 0x%x\n", rtc_id); ret = platform_get_irq(pdev, 1); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Failed to get IRQ for alarm\n"); atcrtc_dev->alarm_irq = ret; ret = devm_request_irq(&pdev->dev, atcrtc_dev->alarm_irq, atcrtc_alarm_isr, 0, "atcrtc_alarm", atcrtc_dev); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to request IRQ %d for alarm\n", atcrtc_dev->alarm_irq); atcrtc_dev->rtc_dev = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(atcrtc_dev->rtc_dev)) return dev_err_probe(&pdev->dev, PTR_ERR(atcrtc_dev->rtc_dev), "Failed to allocate RTC device\n"); set_bit(RTC_FEATURE_ALARM, atcrtc_dev->rtc_dev->features); ret = device_init_wakeup(&pdev->dev, true); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to initialize wake capability\n"); ret = dev_pm_set_wake_irq(&pdev->dev, atcrtc_dev->alarm_irq); if (ret) { device_init_wakeup(&pdev->dev, false); return dev_err_probe(&pdev->dev, ret, "Failed to set wake IRQ\n"); } atcrtc_dev->rtc_dev->ops = &rtc_ops; INIT_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 "); MODULE_DESCRIPTION("Andes ATCRTC100 driver"); MODULE_LICENSE("GPL");