// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018, Fuzhou Rockchip Electronics Co., Ltd * Author: Jeffy Chen * * Base on the Rockchip timer driver drivers/clocksource/rockchip_timer.c by * Daniel Lezcano */ #include #include #include #include #include #include #include #include #define DRV_NAME "rk-timer-rtc" #define TIMER_LOAD_COUNT0 0x00 #define TIMER_LOAD_COUNT1 0x04 #define TIMER_CURRENT_VALUE0 0x08 #define TIMER_CURRENT_VALUE1 0x0C #define TIMER_CONTROL_REG3288 0x10 #define TIMER_INT_STATUS 0x18 #define TIMER_ENABLE BIT(0) #define TIMER_MODE_USER_DEFINED_COUNT BIT(1) #define TIMER_INT_UNMASK BIT(2) /* Forbid any alarms which would trigger inside the threshold */ #define TIMER_ALARM_THRESHOLD_MS 10 #if !defined(UINT64_MAX) #define UINT64_MAX ((u64)-1) #endif /** * struct rk_timer_rtc_data - Differences between SoC variants * * @ctrl_reg_offset: The offset of timer control register */ struct rk_timer_rtc_data { int ctrl_reg_offset; }; /** * struct rk_timer_rtc - Driver data for Rockchip timer RTC * * @data: Pointer to rk_timer_rtc_data * @regmap: Register map of the timer * @rtc: Pointer to RTC device * @clk: The timer clock * @pclk: The peripheral clock * @freq: The freq of timer clock * @timebase: The base time of the timer RTC * @alarm_irq_enabled: Whether to report alarm irqs * @irq: The timer IRQ number */ struct rk_timer_rtc { const struct rk_timer_rtc_data *data; struct regmap *regmap; struct rtc_device *rtc; struct clk *clk; struct clk *pclk; u32 freq; u64 timebase; int alarm_irq_enabled; int irq; }; static inline u64 tick_to_sec(struct rk_timer_rtc *rk_timer_rtc, u64 tick) { do_div(tick, rk_timer_rtc->freq); return tick; } static inline u64 ms_to_tick(struct rk_timer_rtc *rk_timer_rtc, int ms) { return ms * rk_timer_rtc->freq / 1000; } static inline u64 tick_to_time64(struct rk_timer_rtc *rk_timer_rtc, u64 tick) { return tick_to_sec(rk_timer_rtc, tick) + rk_timer_rtc->timebase; } static inline u64 time64_to_tick(struct rk_timer_rtc *rk_timer_rtc, u64 time) { return (time - rk_timer_rtc->timebase) * rk_timer_rtc->freq; } static inline int rk_timer_rtc_write64(struct rk_timer_rtc *rk_timer_rtc, u32 reg, u64 val) { return regmap_bulk_write(rk_timer_rtc->regmap, reg, &val, 2); } static inline int rk_timer_rtc_read64(struct rk_timer_rtc *rk_timer_rtc, u32 reg, u64 *val) { u32 val_lo, val_hi, tmp_hi; int ret; do { ret = regmap_read(rk_timer_rtc->regmap, reg + 4, &val_hi); if (ret) return ret; ret = regmap_read(rk_timer_rtc->regmap, reg, &val_lo); if (ret) return ret; ret = regmap_read(rk_timer_rtc->regmap, reg + 4, &tmp_hi); if (ret) return ret; } while (val_hi != tmp_hi); *val = ((u64) val_hi << 32) | val_lo; return 0; } static inline int rk_timer_rtc_irq_clear(struct rk_timer_rtc *rk_timer_rtc) { return regmap_write(rk_timer_rtc->regmap, TIMER_INT_STATUS, 1); } static inline int rk_timer_rtc_irq_enable(struct rk_timer_rtc *rk_timer_rtc, unsigned int enabled) { /* Clear any pending irq before enable it */ if (enabled) rk_timer_rtc_irq_clear(rk_timer_rtc); return regmap_update_bits(rk_timer_rtc->regmap, rk_timer_rtc->data->ctrl_reg_offset, TIMER_INT_UNMASK, enabled ? TIMER_INT_UNMASK : 0); } static int rk_timer_rtc_reset(struct rk_timer_rtc *rk_timer_rtc) { int ret; ret = regmap_write(rk_timer_rtc->regmap, rk_timer_rtc->data->ctrl_reg_offset, 0); if (ret) return ret; /* Init load count to UINT64_MAX to keep timer running */ ret = rk_timer_rtc_write64(rk_timer_rtc, TIMER_LOAD_COUNT0, UINT64_MAX); if (ret) return ret; /* Clear any pending irq before enable it */ rk_timer_rtc_irq_clear(rk_timer_rtc); /* Enable timer in user-defined count mode with irq unmasked */ return regmap_write(rk_timer_rtc->regmap, rk_timer_rtc->data->ctrl_reg_offset, TIMER_ENABLE | TIMER_MODE_USER_DEFINED_COUNT | TIMER_INT_UNMASK); } static int rk_timer_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); int ret; u64 tick; ret = rk_timer_rtc_read64(rk_timer_rtc, TIMER_CURRENT_VALUE0, &tick); if (ret) return ret; rtc_time64_to_tm(tick_to_time64(rk_timer_rtc, tick), tm); dev_dbg(dev, "Read RTC: %4d-%02d-%02d(%d) %02d:%02d:%02d\n", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); return rtc_valid_tm(tm); } static int rk_timer_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); int ret; dev_dbg(dev, "Set RTC:%4d-%02d-%02d(%d) %02d:%02d:%02d\n", 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); ret = rtc_valid_tm(tm); if (ret) return ret; rk_timer_rtc->timebase = rtc_tm_to_time64(tm); dev_dbg(dev, "Setting new timebase:%lld\n", rk_timer_rtc->timebase); /* Restart timer for new timebase */ ret = rk_timer_rtc_reset(rk_timer_rtc); if (ret) { dev_err(dev, "Failed to reset timer:%d\n", ret); return ret; } /* Tell framework to check alarms */ rtc_update_irq(rk_timer_rtc->rtc, 1, RTC_IRQF | RTC_AF); return 0; } static int rk_timer_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); int ret; u64 tick; ret = rk_timer_rtc_read64(rk_timer_rtc, TIMER_LOAD_COUNT0, &tick); if (ret) return ret; rtc_time64_to_tm(tick_to_time64(rk_timer_rtc, tick), &alrm->time); dev_dbg(dev, "Read alarm: %4d-%02d-%02d(%d) %02d:%02d:%02d\n", 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec); return rtc_valid_tm(&alrm->time); } static int rk_timer_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); int ret; u64 alarm_tick, alarm_threshold_tick, cur_tick; dev_dbg(dev, "Set alarm:%4d-%02d-%02d(%d) %02d:%02d:%02d\n", 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1, alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec); ret = rtc_valid_tm(&alrm->time); if (ret) return ret; rk_timer_rtc->alarm_irq_enabled = false; alarm_tick = time64_to_tick(rk_timer_rtc, rtc_tm_to_time64(&alrm->time)); ret = rk_timer_rtc_read64(rk_timer_rtc, TIMER_CURRENT_VALUE0, &cur_tick); if (ret) return ret; /* Don't set an alarm in the past or about to pass */ alarm_threshold_tick = ms_to_tick(rk_timer_rtc, TIMER_ALARM_THRESHOLD_MS); if (alarm_tick <= (cur_tick + alarm_threshold_tick)) return -ETIME; /* * When the current value counts up to the load count, the timer will * stop and generate an irq. */ ret = rk_timer_rtc_write64(rk_timer_rtc, TIMER_LOAD_COUNT0, alarm_tick); if (ret) return ret; dev_dbg(dev, "New alarm enabled:%d\n", alrm->enabled); rk_timer_rtc->alarm_irq_enabled = alrm->enabled; return 0; } static int rk_timer_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); dev_dbg(dev, "Set alarm irq enabled:%d\n", enabled); rk_timer_rtc->alarm_irq_enabled = enabled; return 0; } static irqreturn_t rk_timer_rtc_alarm_irq(int irq, void *data) { struct device *dev = data; struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); int ret; dev_dbg(dev, "Received timer irq, alarm_irq_enabled:%d\n", rk_timer_rtc->alarm_irq_enabled); /* The timer is stopped now, reset the load count to start it again */ ret = rk_timer_rtc_write64(rk_timer_rtc, TIMER_LOAD_COUNT0, UINT64_MAX); if (ret) dev_err(dev, "Failed to set load count:%d\n", ret); ret = regmap_write(rk_timer_rtc->regmap, TIMER_INT_STATUS, 1); if (ret) dev_err(dev, "Failed to clear irq:%d\n", ret); /* Only report rtc irq when alarm irq is enabled */ if (rk_timer_rtc->alarm_irq_enabled) rtc_update_irq(rk_timer_rtc->rtc, 1, RTC_IRQF | RTC_AF); return IRQ_HANDLED; } static const struct rtc_class_ops rk_timer_rtc_ops = { .read_time = rk_timer_rtc_read_time, .set_time = rk_timer_rtc_set_time, .read_alarm = rk_timer_rtc_read_alarm, .set_alarm = rk_timer_rtc_set_alarm, .alarm_irq_enable = rk_timer_rtc_alarm_irq_enable, }; static struct regmap_config rk_timer_regmap_config = { .name = DRV_NAME, .reg_bits = 32, .val_bits = 32, .reg_stride = 4, }; static const struct of_device_id rk_timer_rtc_dt_match[]; static int rk_timer_rtc_probe(struct platform_device *pdev) { const struct of_device_id *match; struct device *dev = &pdev->dev; struct rk_timer_rtc *rk_timer_rtc; void __iomem *base; resource_size_t size; int ret; rk_timer_rtc = devm_kzalloc(dev, sizeof(*rk_timer_rtc), GFP_KERNEL); if (!rk_timer_rtc) return -ENOMEM; match = of_match_node(rk_timer_rtc_dt_match, dev->of_node); rk_timer_rtc->data = match->data; platform_set_drvdata(pdev, rk_timer_rtc); base = devm_of_iomap(dev, dev->of_node, 0, &size); if (!base) { dev_err(dev, "Failed to iomap\n"); return -EINVAL; } rk_timer_regmap_config.max_register = size - 4; rk_timer_rtc->regmap = devm_regmap_init_mmio(dev, base, &rk_timer_regmap_config); if (IS_ERR(rk_timer_rtc->regmap)) { ret = PTR_ERR(rk_timer_rtc->regmap); dev_err(dev, "Failed to init regmap:%d\n", ret); return ret; } rk_timer_rtc->irq = platform_get_irq(pdev, 0); if (rk_timer_rtc->irq < 0) { ret = rk_timer_rtc->irq; dev_err(dev, "Failed to get irq:%d\n", ret); return ret; } ret = devm_request_irq(dev, rk_timer_rtc->irq, rk_timer_rtc_alarm_irq, 0, dev_name(dev), dev); if (ret) { dev_err(dev, "Failed to request irq:%d\n", ret); return ret; } rk_timer_rtc->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(rk_timer_rtc->pclk)) { ret = PTR_ERR(rk_timer_rtc->pclk); pr_err("Failed to get timer pclk:%d\n", ret); return ret; } ret = clk_prepare_enable(rk_timer_rtc->pclk); if (ret) { dev_err(dev, "Failed to enable pclk:%d\n", ret); return ret; } rk_timer_rtc->clk = devm_clk_get(dev, "timer"); if (IS_ERR(rk_timer_rtc->clk)) { ret = PTR_ERR(rk_timer_rtc->clk); pr_err("Failed to get timer clk:%d\n", ret); goto err_disable_pclk; } ret = clk_prepare_enable(rk_timer_rtc->clk); if (ret) { dev_err(dev, "Failed to enable timer clk:%d\n", ret); goto err_disable_pclk; } rk_timer_rtc->freq = clk_get_rate(rk_timer_rtc->clk); dev_dbg(dev, "RTC timer freq:%d\n", rk_timer_rtc->freq); ret = rk_timer_rtc_reset(rk_timer_rtc); if (ret) { dev_err(dev, "Failed to reset timer:%d\n", ret); goto err_disable_clk; } ret = device_init_wakeup(dev, true); if (ret) { dev_err(dev, "Failed to init wakeup:%d\n", ret); goto err_disable_irq; } rk_timer_rtc->rtc = devm_rtc_device_register(dev, DRV_NAME, &rk_timer_rtc_ops, THIS_MODULE); if (IS_ERR(rk_timer_rtc->rtc)) { ret = PTR_ERR(rk_timer_rtc->rtc); dev_err(dev, "Failed to register rtc:%d\n", ret); goto err_uninit_wakeup; } return 0; err_uninit_wakeup: device_init_wakeup(&pdev->dev, false); err_disable_irq: rk_timer_rtc_irq_enable(rk_timer_rtc, false); err_disable_clk: clk_disable_unprepare(rk_timer_rtc->clk); err_disable_pclk: clk_disable_unprepare(rk_timer_rtc->pclk); return ret; } static int rk_timer_rtc_remove(struct platform_device *pdev) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(&pdev->dev); device_init_wakeup(&pdev->dev, false); rk_timer_rtc_irq_enable(rk_timer_rtc, false); clk_disable_unprepare(rk_timer_rtc->clk); clk_disable_unprepare(rk_timer_rtc->pclk); return 0; } #ifdef CONFIG_PM_SLEEP static int rk_timer_rtc_suspend(struct device *dev) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); if (device_may_wakeup(dev)) enable_irq_wake(rk_timer_rtc->irq); return 0; } static int rk_timer_rtc_resume(struct device *dev) { struct rk_timer_rtc *rk_timer_rtc = dev_get_drvdata(dev); if (device_may_wakeup(dev)) disable_irq_wake(rk_timer_rtc->irq); return 0; } #endif static SIMPLE_DEV_PM_OPS(rk_timer_rtc_pm_ops, rk_timer_rtc_suspend, rk_timer_rtc_resume); static const struct rk_timer_rtc_data rk3288_timer_rtc_data = { .ctrl_reg_offset = TIMER_CONTROL_REG3288, }; static const struct of_device_id rk_timer_rtc_dt_match[] = { { .compatible = "rockchip,rk3308-timer-rtc", .data = &rk3288_timer_rtc_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, rk_timer_rtc_dt_match); static struct platform_driver rk_timer_rtc_driver = { .probe = rk_timer_rtc_probe, .remove = rk_timer_rtc_remove, .driver = { .name = DRV_NAME, .pm = &rk_timer_rtc_pm_ops, .of_match_table = of_match_ptr(rk_timer_rtc_dt_match), }, }; module_platform_driver(rk_timer_rtc_driver); MODULE_DESCRIPTION("RTC driver for the rockchip timer"); MODULE_AUTHOR("Jeffy Chen "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME);