From: Alexandre Belloni <alexandre.belloni@bootlin.com>
To: xianwei.zhao@amlogic.com
Cc: Yiting Deng <yiting.deng@amlogic.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
linux-amlogic@lists.infradead.org, linux-rtc@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v5 2/3] rtc: support for the Amlogic on-chip RTC
Date: Mon, 11 Nov 2024 23:07:23 +0100 [thread overview]
Message-ID: <202411112207234c96cc30@mail.local> (raw)
In-Reply-To: <20241108-rtc-v5-2-0194727c778b@amlogic.com>
On 08/11/2024 13:54:42+0800, Xianwei Zhao via B4 Relay wrote:
> +static int aml_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 time_sec;
> +
> + /* if RTC disabled, read time failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, read time failed\n");
These messages should be dropped, they probably won't be seen by any
user.
> + return -EINVAL;
> + }
> +
> + regmap_read(rtc->map, RTC_REAL_TIME, &time_sec);
> + if (rtc->config->gray_stored)
> + time_sec = gray_to_binary(time_sec);
> + rtc_time64_to_tm(time_sec, tm);
> + dev_dbg(dev, "%s: read time = %us\n", __func__, time_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 time_sec;
> +
> + /* if RTC disabled, first enable it */
> + if (!rtc->rtc_enabled) {
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, RTC_ENABLE);
> + usleep_range(100, 200);
> + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE);
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC enable failed\n");
> + return -EINVAL;
> + }
> + }
> +
> + time_sec = rtc_tm_to_time64(tm);
> + if (rtc->config->gray_stored)
> + time_sec = binary_to_gray(time_sec);
> + regmap_write(rtc->map, RTC_COUNTER_REG, time_sec);
> + dev_dbg(dev, "%s: set time = %us\n", __func__, time_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + time64_t alarm_sec;
> +
> + /* if RTC disabled, set alarm failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, set alarm failed\n");
> + return -EINVAL;
> + }
> +
> + regmap_update_bits(rtc->map, RTC_CTRL,
> + RTC_ALRM0_EN, RTC_ALRM0_EN);
> + regmap_update_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, 0);
> +
> + alarm_sec = rtc_tm_to_time64(&alarm->time);
> + if (rtc->config->gray_stored)
> + alarm_sec = binary_to_gray(alarm_sec);
> + regmap_write(rtc->map, RTC_ALARM0_REG, alarm_sec);
> +
> + dev_dbg(dev, "%s: alarm->enabled=%d alarm_set=%llds\n", __func__,
> + alarm->enabled, alarm_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 alarm_sec;
> + int alarm_enable;
> + int alarm_mask;
> +
> + /* if RTC disabled, read alarm failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, read alarm failed\n");
> + return -EINVAL;
> + }
> +
> + regmap_read(rtc->map, RTC_ALARM0_REG, &alarm_sec);
> + if (rtc->config->gray_stored)
> + alarm_sec = gray_to_binary(alarm_sec);
> + rtc_time64_to_tm(alarm_sec, &alarm->time);
> +
> + alarm_enable = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN);
> + alarm_mask = regmap_test_bits(rtc->map, RTC_INT_MASK, RTC_ALRM0_IRQ_MSK);
> + alarm->enabled = (alarm_enable && !alarm_mask) ? 1 : 0;
> + dev_dbg(dev, "%s: alarm->enabled=%d alarm=%us\n", __func__,
> + alarm->enabled, alarm_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_read_offset(struct device *dev, long *offset)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 reg_val;
> + long val;
> + int sign, match_counter, enable;
> +
> + /* if RTC disabled, read offset failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, read offset failed\n");
> + return -EINVAL;
> + }
> +
> + regmap_read(rtc->map, RTC_SEC_ADJUST_REG, ®_val);
> + enable = FIELD_GET(RTC_ADJ_VALID, reg_val);
> + if (!enable) {
> + val = 0;
> + } else {
> + sign = FIELD_GET(RTC_SEC_ADJUST_CTRL, reg_val);
> + match_counter = FIELD_GET(RTC_MATCH_COUNTER, reg_val);
> + val = 1000000000 / (match_counter + 1);
> + if (sign == RTC_SWALLOW_SECOND)
> + val = -val;
> + }
> + *offset = val;
> +
> + return 0;
> +}
> +
> +static int aml_rtc_set_offset(struct device *dev, long offset)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + int sign = 0;
> + int match_counter = 0;
> + int enable = 0;
> + u32 reg_val;
> +
> + /* if RTC disabled, set offset failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, set offset failed\n");
> + return -EINVAL;
> + }
> +
> + if (offset) {
> + enable = 1;
> + sign = offset < 0 ? RTC_SWALLOW_SECOND : RTC_INSERT_SECOND;
> + match_counter = 1000000000 / abs(offset) - 1;
> + if (match_counter < 0 || match_counter > RTC_MATCH_COUNTER)
> + return -EINVAL;
> + }
> +
> + reg_val = FIELD_PREP(RTC_ADJ_VALID, enable) |
> + FIELD_PREP(RTC_SEC_ADJUST_CTRL, sign) |
> + FIELD_PREP(RTC_MATCH_COUNTER, match_counter);
> + regmap_write(rtc->map, RTC_SEC_ADJUST_REG, reg_val);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_alarm_enable(struct device *dev, unsigned int enabled)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> +
> + if (enabled) {
> + regmap_update_bits(rtc->map, RTC_CTRL,
> + RTC_ALRM0_EN, RTC_ALRM0_EN);
> + regmap_update_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, 0);
> + } else {
> + regmap_update_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK);
> + regmap_update_bits(rtc->map, RTC_CTRL,
> + RTC_ALRM0_EN, 0);
> + }
> +
> + return 0;
> +}
> +
> +static const struct rtc_class_ops aml_rtc_ops = {
> + .read_time = aml_rtc_read_time,
> + .set_time = aml_rtc_set_time,
> + .read_alarm = aml_rtc_read_alarm,
> + .set_alarm = aml_rtc_set_alarm,
> + .alarm_irq_enable = aml_rtc_alarm_enable,
> + .read_offset = aml_rtc_read_offset,
> + .set_offset = aml_rtc_set_offset,
> +};
> +
> +static irqreturn_t aml_rtc_handler(int irq, void *data)
> +{
> + struct aml_rtc_data *rtc = (struct aml_rtc_data *)data;
> +
> + regmap_write(rtc->map, RTC_ALARM0_REG, 0);
> + regmap_write(rtc->map, RTC_INT_CLR, RTC_ALRM0_IRQ_STATUS);
> +
> + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void aml_rtc_init(struct aml_rtc_data *rtc)
> +{
> + u32 reg_val = 0;
> +
> + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE);
> + if (!rtc->rtc_enabled) {
> + if (clk_get_rate(rtc->rtc_clk) == OSC_24M) {
> + /* select 24M oscillator */
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, RTC_OSC_SEL);
> +
> + /*
> + * Set RTC oscillator to freq_out to freq_in/((N0*M0+N1*M1)/(M0+M1))
> + * Enable clock_in gate of oscillator 24MHz
> + * Set N0 to 733, N1 to 732
> + */
> + reg_val = FIELD_PREP(RTC_OSCIN_IN_EN, 1)
> + | FIELD_PREP(RTC_OSCIN_OUT_CFG, 1)
> + | FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_N0)
> + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_N1);
> + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL0, RTC_OSCIN_IN_EN
> + | RTC_OSCIN_OUT_CFG | RTC_OSCIN_OUT_N0M0
> + | RTC_OSCIN_OUT_N1M1, reg_val);
> +
> + /* Set M0 to 2, M1 to 3, so freq_out = 32768 Hz*/
> + reg_val = FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_M0)
> + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_M1);
> + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL1, RTC_OSCIN_OUT_N0M0
> + | RTC_OSCIN_OUT_N1M1, reg_val);
> + } else {
> + /* select 32K oscillator */
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, 0);
> + }
> + }
> + regmap_write_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK);
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN, 0);
> +}
> +
> +static int aml_rtc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct aml_rtc_data *rtc;
> + void __iomem *base;
> + int ret = 0;
> +
> + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
> + if (!rtc)
> + return -ENOMEM;
> +
> + rtc->config = of_device_get_match_data(dev);
> + if (!rtc->config)
> + return -ENODEV;
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");
> +
> + rtc->map = devm_regmap_init_mmio(dev, base, &aml_rtc_regmap_config);
> + if (IS_ERR(rtc->map))
> + return dev_err_probe(dev, PTR_ERR(rtc->map), "regmap init failed\n");
> +
> + rtc->irq = platform_get_irq(pdev, 0);
> + if (rtc->irq < 0)
> + return rtc->irq;
> +
> + rtc->rtc_clk = devm_clk_get(dev, "osc");
> + if (IS_ERR(rtc->rtc_clk))
> + return dev_err_probe(dev, PTR_ERR(rtc->rtc_clk),
> + "failed to find rtc clock\n");
> + if (clk_get_rate(rtc->rtc_clk) != OSC_32K && clk_get_rate(rtc->rtc_clk) != OSC_24M)
> + return dev_err_probe(dev, -EINVAL, "Invalid clock configuration\n");
> +
> + rtc->sys_clk = devm_clk_get_enabled(dev, "sys");
> + if (IS_ERR(rtc->sys_clk))
> + return dev_err_probe(dev, PTR_ERR(rtc->sys_clk),
> + "failed to get_enable rtc sys clk\n");
> + aml_rtc_init(rtc);
> +
> + device_init_wakeup(dev, 1);
> + platform_set_drvdata(pdev, rtc);
> +
> + rtc->rtc_dev = devm_rtc_allocate_device(dev);
> + if (IS_ERR(rtc->rtc_dev)) {
> + ret = PTR_ERR(rtc->rtc_dev);
> + goto err_clk;
> + }
> +
> + ret = devm_request_irq(dev, rtc->irq, aml_rtc_handler,
> + IRQF_ONESHOT, "aml-rtc alarm", rtc);
> + if (ret) {
> + dev_err_probe(dev, ret, "IRQ%d request failed, ret = %d\n",
> + rtc->irq, ret);
> + goto err_clk;
> + }
> +
> + rtc->rtc_dev->ops = &aml_rtc_ops;
> + rtc->rtc_dev->range_min = 0;
> + rtc->rtc_dev->range_max = U32_MAX;
> +
> + ret = devm_rtc_register_device(rtc->rtc_dev);
> + if (ret) {
> + dev_err_probe(&pdev->dev, ret, "Failed to register RTC device: %d\n", ret);
> + goto err_clk;
> + }
> +
> + return 0;
> +err_clk:
> + clk_disable_unprepare(rtc->sys_clk);
> + device_init_wakeup(dev, 0);
> +
> + return ret;
> +}
> +
> +static int aml_rtc_suspend(struct device *dev)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + enable_irq_wake(rtc->irq);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_resume(struct device *dev)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(rtc->irq);
> +
> + return 0;
> +}
Building this, i got:
drivers/rtc/rtc-amlogic-a4.c:409:12: error: ‘aml_rtc_resume’ defined but not used [-Werror=unused-function]
409 | static int aml_rtc_resume(struct device *dev)
| ^~~~~~~~~~~~~~
drivers/rtc/rtc-amlogic-a4.c:399:12: error: ‘aml_rtc_suspend’ defined but not used [-Werror=unused-function]
399 | static int aml_rtc_suspend(struct device *dev)
| ^~~~~~~~~~~~~~~
> +
> +static SIMPLE_DEV_PM_OPS(aml_rtc_pm_ops,
> + aml_rtc_suspend, aml_rtc_resume);
> +
> +static void aml_rtc_remove(struct platform_device *pdev)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(&pdev->dev);
> +
> + /* disable RTC */
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, 0);
You can't do this, this defeats the purpose of the RTC. Once started an
set, it must not be stopped.
> + clk_disable_unprepare(rtc->sys_clk);
> + device_init_wakeup(&pdev->dev, 0);
> +}
> +
> +static const struct aml_rtc_config a5_rtc_config = {
> +};
> +
> +static const struct aml_rtc_config a4_rtc_config = {
> + .gray_stored = true,
> +};
> +
> +static const struct of_device_id aml_rtc_device_id[] = {
> + {
> + .compatible = "amlogic,a4-rtc",
> + .data = &a4_rtc_config,
> + },
> + {
> + .compatible = "amlogic,a5-rtc",
> + .data = &a5_rtc_config,
> + },
> +};
> +MODULE_DEVICE_TABLE(of, aml_rtc_device_id);
> +
> +static struct platform_driver aml_rtc_driver = {
> + .probe = aml_rtc_probe,
> + .remove = aml_rtc_remove,
> + .driver = {
> + .name = "aml-rtc",
> + .pm = &aml_rtc_pm_ops,
> + .of_match_table = aml_rtc_device_id,
> + },
> +};
> +
> +module_platform_driver(aml_rtc_driver);
> +MODULE_DESCRIPTION("Amlogic RTC driver");
> +MODULE_AUTHOR("Yiting Deng <yiting.deng@amlogic.com>");
> +MODULE_LICENSE("GPL");
>
> --
> 2.37.1
>
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
WARNING: multiple messages have this Message-ID (diff)
From: Alexandre Belloni <alexandre.belloni@bootlin.com>
To: xianwei.zhao@amlogic.com
Cc: Yiting Deng <yiting.deng@amlogic.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
linux-amlogic@lists.infradead.org, linux-rtc@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v5 2/3] rtc: support for the Amlogic on-chip RTC
Date: Mon, 11 Nov 2024 23:07:23 +0100 [thread overview]
Message-ID: <202411112207234c96cc30@mail.local> (raw)
In-Reply-To: <20241108-rtc-v5-2-0194727c778b@amlogic.com>
On 08/11/2024 13:54:42+0800, Xianwei Zhao via B4 Relay wrote:
> +static int aml_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 time_sec;
> +
> + /* if RTC disabled, read time failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, read time failed\n");
These messages should be dropped, they probably won't be seen by any
user.
> + return -EINVAL;
> + }
> +
> + regmap_read(rtc->map, RTC_REAL_TIME, &time_sec);
> + if (rtc->config->gray_stored)
> + time_sec = gray_to_binary(time_sec);
> + rtc_time64_to_tm(time_sec, tm);
> + dev_dbg(dev, "%s: read time = %us\n", __func__, time_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 time_sec;
> +
> + /* if RTC disabled, first enable it */
> + if (!rtc->rtc_enabled) {
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, RTC_ENABLE);
> + usleep_range(100, 200);
> + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE);
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC enable failed\n");
> + return -EINVAL;
> + }
> + }
> +
> + time_sec = rtc_tm_to_time64(tm);
> + if (rtc->config->gray_stored)
> + time_sec = binary_to_gray(time_sec);
> + regmap_write(rtc->map, RTC_COUNTER_REG, time_sec);
> + dev_dbg(dev, "%s: set time = %us\n", __func__, time_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + time64_t alarm_sec;
> +
> + /* if RTC disabled, set alarm failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, set alarm failed\n");
> + return -EINVAL;
> + }
> +
> + regmap_update_bits(rtc->map, RTC_CTRL,
> + RTC_ALRM0_EN, RTC_ALRM0_EN);
> + regmap_update_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, 0);
> +
> + alarm_sec = rtc_tm_to_time64(&alarm->time);
> + if (rtc->config->gray_stored)
> + alarm_sec = binary_to_gray(alarm_sec);
> + regmap_write(rtc->map, RTC_ALARM0_REG, alarm_sec);
> +
> + dev_dbg(dev, "%s: alarm->enabled=%d alarm_set=%llds\n", __func__,
> + alarm->enabled, alarm_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 alarm_sec;
> + int alarm_enable;
> + int alarm_mask;
> +
> + /* if RTC disabled, read alarm failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, read alarm failed\n");
> + return -EINVAL;
> + }
> +
> + regmap_read(rtc->map, RTC_ALARM0_REG, &alarm_sec);
> + if (rtc->config->gray_stored)
> + alarm_sec = gray_to_binary(alarm_sec);
> + rtc_time64_to_tm(alarm_sec, &alarm->time);
> +
> + alarm_enable = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN);
> + alarm_mask = regmap_test_bits(rtc->map, RTC_INT_MASK, RTC_ALRM0_IRQ_MSK);
> + alarm->enabled = (alarm_enable && !alarm_mask) ? 1 : 0;
> + dev_dbg(dev, "%s: alarm->enabled=%d alarm=%us\n", __func__,
> + alarm->enabled, alarm_sec);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_read_offset(struct device *dev, long *offset)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + u32 reg_val;
> + long val;
> + int sign, match_counter, enable;
> +
> + /* if RTC disabled, read offset failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, read offset failed\n");
> + return -EINVAL;
> + }
> +
> + regmap_read(rtc->map, RTC_SEC_ADJUST_REG, ®_val);
> + enable = FIELD_GET(RTC_ADJ_VALID, reg_val);
> + if (!enable) {
> + val = 0;
> + } else {
> + sign = FIELD_GET(RTC_SEC_ADJUST_CTRL, reg_val);
> + match_counter = FIELD_GET(RTC_MATCH_COUNTER, reg_val);
> + val = 1000000000 / (match_counter + 1);
> + if (sign == RTC_SWALLOW_SECOND)
> + val = -val;
> + }
> + *offset = val;
> +
> + return 0;
> +}
> +
> +static int aml_rtc_set_offset(struct device *dev, long offset)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> + int sign = 0;
> + int match_counter = 0;
> + int enable = 0;
> + u32 reg_val;
> +
> + /* if RTC disabled, set offset failed */
> + if (!rtc->rtc_enabled) {
> + dev_err(dev, "RTC disabled, set offset failed\n");
> + return -EINVAL;
> + }
> +
> + if (offset) {
> + enable = 1;
> + sign = offset < 0 ? RTC_SWALLOW_SECOND : RTC_INSERT_SECOND;
> + match_counter = 1000000000 / abs(offset) - 1;
> + if (match_counter < 0 || match_counter > RTC_MATCH_COUNTER)
> + return -EINVAL;
> + }
> +
> + reg_val = FIELD_PREP(RTC_ADJ_VALID, enable) |
> + FIELD_PREP(RTC_SEC_ADJUST_CTRL, sign) |
> + FIELD_PREP(RTC_MATCH_COUNTER, match_counter);
> + regmap_write(rtc->map, RTC_SEC_ADJUST_REG, reg_val);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_alarm_enable(struct device *dev, unsigned int enabled)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> +
> + if (enabled) {
> + regmap_update_bits(rtc->map, RTC_CTRL,
> + RTC_ALRM0_EN, RTC_ALRM0_EN);
> + regmap_update_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, 0);
> + } else {
> + regmap_update_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK);
> + regmap_update_bits(rtc->map, RTC_CTRL,
> + RTC_ALRM0_EN, 0);
> + }
> +
> + return 0;
> +}
> +
> +static const struct rtc_class_ops aml_rtc_ops = {
> + .read_time = aml_rtc_read_time,
> + .set_time = aml_rtc_set_time,
> + .read_alarm = aml_rtc_read_alarm,
> + .set_alarm = aml_rtc_set_alarm,
> + .alarm_irq_enable = aml_rtc_alarm_enable,
> + .read_offset = aml_rtc_read_offset,
> + .set_offset = aml_rtc_set_offset,
> +};
> +
> +static irqreturn_t aml_rtc_handler(int irq, void *data)
> +{
> + struct aml_rtc_data *rtc = (struct aml_rtc_data *)data;
> +
> + regmap_write(rtc->map, RTC_ALARM0_REG, 0);
> + regmap_write(rtc->map, RTC_INT_CLR, RTC_ALRM0_IRQ_STATUS);
> +
> + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void aml_rtc_init(struct aml_rtc_data *rtc)
> +{
> + u32 reg_val = 0;
> +
> + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE);
> + if (!rtc->rtc_enabled) {
> + if (clk_get_rate(rtc->rtc_clk) == OSC_24M) {
> + /* select 24M oscillator */
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, RTC_OSC_SEL);
> +
> + /*
> + * Set RTC oscillator to freq_out to freq_in/((N0*M0+N1*M1)/(M0+M1))
> + * Enable clock_in gate of oscillator 24MHz
> + * Set N0 to 733, N1 to 732
> + */
> + reg_val = FIELD_PREP(RTC_OSCIN_IN_EN, 1)
> + | FIELD_PREP(RTC_OSCIN_OUT_CFG, 1)
> + | FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_N0)
> + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_N1);
> + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL0, RTC_OSCIN_IN_EN
> + | RTC_OSCIN_OUT_CFG | RTC_OSCIN_OUT_N0M0
> + | RTC_OSCIN_OUT_N1M1, reg_val);
> +
> + /* Set M0 to 2, M1 to 3, so freq_out = 32768 Hz*/
> + reg_val = FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_M0)
> + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_M1);
> + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL1, RTC_OSCIN_OUT_N0M0
> + | RTC_OSCIN_OUT_N1M1, reg_val);
> + } else {
> + /* select 32K oscillator */
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, 0);
> + }
> + }
> + regmap_write_bits(rtc->map, RTC_INT_MASK,
> + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK);
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN, 0);
> +}
> +
> +static int aml_rtc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct aml_rtc_data *rtc;
> + void __iomem *base;
> + int ret = 0;
> +
> + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
> + if (!rtc)
> + return -ENOMEM;
> +
> + rtc->config = of_device_get_match_data(dev);
> + if (!rtc->config)
> + return -ENODEV;
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");
> +
> + rtc->map = devm_regmap_init_mmio(dev, base, &aml_rtc_regmap_config);
> + if (IS_ERR(rtc->map))
> + return dev_err_probe(dev, PTR_ERR(rtc->map), "regmap init failed\n");
> +
> + rtc->irq = platform_get_irq(pdev, 0);
> + if (rtc->irq < 0)
> + return rtc->irq;
> +
> + rtc->rtc_clk = devm_clk_get(dev, "osc");
> + if (IS_ERR(rtc->rtc_clk))
> + return dev_err_probe(dev, PTR_ERR(rtc->rtc_clk),
> + "failed to find rtc clock\n");
> + if (clk_get_rate(rtc->rtc_clk) != OSC_32K && clk_get_rate(rtc->rtc_clk) != OSC_24M)
> + return dev_err_probe(dev, -EINVAL, "Invalid clock configuration\n");
> +
> + rtc->sys_clk = devm_clk_get_enabled(dev, "sys");
> + if (IS_ERR(rtc->sys_clk))
> + return dev_err_probe(dev, PTR_ERR(rtc->sys_clk),
> + "failed to get_enable rtc sys clk\n");
> + aml_rtc_init(rtc);
> +
> + device_init_wakeup(dev, 1);
> + platform_set_drvdata(pdev, rtc);
> +
> + rtc->rtc_dev = devm_rtc_allocate_device(dev);
> + if (IS_ERR(rtc->rtc_dev)) {
> + ret = PTR_ERR(rtc->rtc_dev);
> + goto err_clk;
> + }
> +
> + ret = devm_request_irq(dev, rtc->irq, aml_rtc_handler,
> + IRQF_ONESHOT, "aml-rtc alarm", rtc);
> + if (ret) {
> + dev_err_probe(dev, ret, "IRQ%d request failed, ret = %d\n",
> + rtc->irq, ret);
> + goto err_clk;
> + }
> +
> + rtc->rtc_dev->ops = &aml_rtc_ops;
> + rtc->rtc_dev->range_min = 0;
> + rtc->rtc_dev->range_max = U32_MAX;
> +
> + ret = devm_rtc_register_device(rtc->rtc_dev);
> + if (ret) {
> + dev_err_probe(&pdev->dev, ret, "Failed to register RTC device: %d\n", ret);
> + goto err_clk;
> + }
> +
> + return 0;
> +err_clk:
> + clk_disable_unprepare(rtc->sys_clk);
> + device_init_wakeup(dev, 0);
> +
> + return ret;
> +}
> +
> +static int aml_rtc_suspend(struct device *dev)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + enable_irq_wake(rtc->irq);
> +
> + return 0;
> +}
> +
> +static int aml_rtc_resume(struct device *dev)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev))
> + disable_irq_wake(rtc->irq);
> +
> + return 0;
> +}
Building this, i got:
drivers/rtc/rtc-amlogic-a4.c:409:12: error: ‘aml_rtc_resume’ defined but not used [-Werror=unused-function]
409 | static int aml_rtc_resume(struct device *dev)
| ^~~~~~~~~~~~~~
drivers/rtc/rtc-amlogic-a4.c:399:12: error: ‘aml_rtc_suspend’ defined but not used [-Werror=unused-function]
399 | static int aml_rtc_suspend(struct device *dev)
| ^~~~~~~~~~~~~~~
> +
> +static SIMPLE_DEV_PM_OPS(aml_rtc_pm_ops,
> + aml_rtc_suspend, aml_rtc_resume);
> +
> +static void aml_rtc_remove(struct platform_device *pdev)
> +{
> + struct aml_rtc_data *rtc = dev_get_drvdata(&pdev->dev);
> +
> + /* disable RTC */
> + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, 0);
You can't do this, this defeats the purpose of the RTC. Once started an
set, it must not be stopped.
> + clk_disable_unprepare(rtc->sys_clk);
> + device_init_wakeup(&pdev->dev, 0);
> +}
> +
> +static const struct aml_rtc_config a5_rtc_config = {
> +};
> +
> +static const struct aml_rtc_config a4_rtc_config = {
> + .gray_stored = true,
> +};
> +
> +static const struct of_device_id aml_rtc_device_id[] = {
> + {
> + .compatible = "amlogic,a4-rtc",
> + .data = &a4_rtc_config,
> + },
> + {
> + .compatible = "amlogic,a5-rtc",
> + .data = &a5_rtc_config,
> + },
> +};
> +MODULE_DEVICE_TABLE(of, aml_rtc_device_id);
> +
> +static struct platform_driver aml_rtc_driver = {
> + .probe = aml_rtc_probe,
> + .remove = aml_rtc_remove,
> + .driver = {
> + .name = "aml-rtc",
> + .pm = &aml_rtc_pm_ops,
> + .of_match_table = aml_rtc_device_id,
> + },
> +};
> +
> +module_platform_driver(aml_rtc_driver);
> +MODULE_DESCRIPTION("Amlogic RTC driver");
> +MODULE_AUTHOR("Yiting Deng <yiting.deng@amlogic.com>");
> +MODULE_LICENSE("GPL");
>
> --
> 2.37.1
>
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
next prev parent reply other threads:[~2024-11-11 22:07 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-08 5:54 [PATCH v5 0/3] support for amlogic rtc Xianwei Zhao
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-08 5:54 ` [PATCH v5 1/3] dt-bindings: rtc: Add Amlogic A4 and A5 RTC Xianwei Zhao
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-08 5:54 ` [PATCH v5 2/3] rtc: support for the Amlogic on-chip RTC Xianwei Zhao
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-11 22:07 ` Alexandre Belloni [this message]
2024-11-11 22:07 ` Alexandre Belloni
2024-11-12 3:02 ` Xianwei Zhao
2024-11-12 3:02 ` Xianwei Zhao
2024-11-08 5:54 ` [PATCH v5 3/3] MAINTAINERS: Add an entry for Amlogic RTC driver Xianwei Zhao
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-08 5:54 ` Xianwei Zhao via B4 Relay
2024-11-11 21:47 ` [PATCH v5 0/3] support for amlogic rtc Alexandre Belloni
2024-11-11 21:47 ` Alexandre Belloni
2024-11-11 22:01 ` Alexandre Belloni
2024-11-11 22:01 ` Alexandre Belloni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=202411112207234c96cc30@mail.local \
--to=alexandre.belloni@bootlin.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-amlogic@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rtc@vger.kernel.org \
--cc=robh@kernel.org \
--cc=xianwei.zhao@amlogic.com \
--cc=yiting.deng@amlogic.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.