blob: de16f30e2e2a0d9529e47c6f6070e164fb080f0c [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/time64.h>
#include <linux/delay.h>
#include <linux/amlogic/rtc.h>
#include <linux/amlogic/scpi_protocol.h>
#include <linux/clk.h>
#define RTC_CALIBRATION
#define OSC_24MHZ (24000000)
#define OSC_32KHZ (32768)
static void meson_set_time(struct meson_rtc_data *rtc, u32 time_sec)
{
writel(time_sec, rtc->reg_base + RTC_COUNTER_REG);
}
static u32 meson_read_time(struct meson_rtc_data *rtc)
{
u32 time_val;
time_val = readl(rtc->reg_base + RTC_REAL_TIME);
return time_val;
}
static u32 meson_read_alarm(struct meson_rtc_data *rtc)
{
u32 alarm_val;
alarm_val = readl(rtc->reg_base + RTC_ALARM0_REG);
return alarm_val;
}
static void meson_set_alarm(struct meson_rtc_data *rtc, u32 alarm_sec)
{
writel(alarm_sec, rtc->reg_base + RTC_ALARM0_REG);
}
static int meson_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
u32 time_sec;
time64_t time64;
time_sec = meson_read_time(rtc);
time64 = (time64_t)time_sec;
dev_dbg(dev, "%s: read time = %u\n",
__func__, time_sec);
rtc_time64_to_tm(time64, tm);
return 0;
}
static int meson_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
time64_t time_sec;
time_sec = rtc_tm_to_time64(tm);
meson_set_time(rtc, (u32)time_sec);
dev_dbg(dev, "%s: set_time = %llu\n",
__func__, time_sec);
return 0;
}
static int meson_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
u32 alarm_sec;
u32 reg_val;
time64_t alarm_sec64;
/* Disable alarm0 first */
reg_val = readl(rtc->reg_base + RTC_CTRL);
reg_val &= (~(1 << RTC_ALRM0_EN_BIT));
writel(reg_val, rtc->reg_base + RTC_CTRL);
if (alarm->enabled) {
alarm_sec64 = rtc_tm_to_time64(&alarm->time);
if (alarm_sec64 > 0x7fffffff) {
dev_err(dev, "alarm value invalid!\n");
return -EINVAL;
}
alarm_sec = (u32)alarm_sec64;
meson_set_alarm(rtc, alarm_sec);
/* Enable alarm0 */
reg_val = readl(rtc->reg_base + RTC_CTRL);
reg_val |= (1 << RTC_ALRM0_EN_BIT);
writel(reg_val, rtc->reg_base + RTC_CTRL);
}
rtc->alarm_enabled = alarm->enabled;
dev_dbg(dev, "%s: alarm->enabled=%d alarm_set=0x%llx alarm=0x%x\n",
__func__, alarm->enabled, alarm_sec64, alarm_sec);
return 0;
}
static int meson_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
u32 alarm_sec;
u32 reg_val;
alarm_sec = meson_read_alarm(rtc);
rtc_time64_to_tm((time64_t)alarm_sec, &alarm->time);
dev_dbg(dev, "%s: alarm->enabled=%d alarm=0x%x\n", __func__,
alarm->enabled, alarm_sec);
reg_val = readl(rtc->reg_base + RTC_INT_STATUS);
alarm->enabled = rtc->alarm_enabled;
alarm->pending = (reg_val & (1 << RTC_ALRM0_STATUS_BIT)) ? 1 : 0;
return 0;
}
static irqreturn_t meson_rtc_handler(int irq, void *data)
{
struct meson_rtc_data *rtc = (struct meson_rtc_data *)data;
rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
return IRQ_HANDLED;
}
static int meson_rtc_alarm_enable(struct device *dev, unsigned int enabled)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
u32 reg_val;
dev_dbg(dev, "%s: enabled = %d\n", __func__,
enabled);
/* Operate RTC alarm0 by default */
if (enabled) {
/* Enable alarm0 */
reg_val = readl(rtc->reg_base + RTC_CTRL);
reg_val |= (1 << RTC_ALRM0_EN_BIT);
writel(reg_val, rtc->reg_base + RTC_CTRL);
} else {
/* Disable alarm0 */
reg_val = readl(rtc->reg_base + RTC_CTRL);
reg_val &= (~(1 << RTC_ALRM0_EN_BIT));
writel(reg_val, rtc->reg_base + RTC_CTRL);
}
rtc->alarm_enabled = enabled;
return 0;
}
static void meson_rtc_clk_config(struct meson_rtc_data *rtc)
{
u32 reg_val;
if (rtc->freq == OSC_24MHZ) {
reg_val = readl(rtc->reg_base + RTC_CTRL);
/* Select RTC oscillator to 24MHz clock output */
reg_val |= (1 << RTC_OSC_SEL_BIT);
writel(reg_val, rtc->reg_base + RTC_CTRL);
/* Set RTC oscillator to freq_out to freq_in/((N0*M0+N1*M1)/(M0+M1)) */
reg_val = readl(rtc->reg_base + RTC_OSCIN_CTRL0);
reg_val &= (~(0x3 << 28));
reg_val |= (0x1 << 28);
/* Enable clock_in gate of oscillator 24MHz */
reg_val |= (1 << 31);
/* N0 is set to 733, N1 is set to 732 by default*/
writel(reg_val, rtc->reg_base + RTC_OSCIN_CTRL0);
/* Set M0 to 2, M1 to 3, so freq_out = 32768 Hz*/
reg_val = readl(rtc->reg_base + RTC_OSCIN_CTRL1);
reg_val &= ~(0xfff);
reg_val |= (0x1 << 0);
reg_val &= ~(0xfff << 12);
reg_val |= (0x2 << 12);
writel(reg_val, rtc->reg_base + RTC_OSCIN_CTRL1);
}
}
#ifdef RTC_CALIBRATION
static int meson_rtc_adjust_sec(struct device *dev, u32 match_counter,
enum meson_rtc_adj ops)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev->parent);
u32 reg_val;
if (match_counter > 0x7ffff) {
pr_err("%s: invalid match_counter\n", __func__);
return -EINVAL;
}
reg_val = readl(rtc->reg_base + RTC_SEC_ADJUST_REG);
/* Set sec_adjust_ctrl */
reg_val &= (~(0x3 << 19));
if (ops == ADJUST_DROP)
reg_val |= (0x2 << 19);
else if (ops == ADJUST_INSERT)
reg_val |= (0x3 << 19);
/* Set match_counter */
reg_val &= (~(0x7ffff << 0));
reg_val |= match_counter;
/* Valid adjust */
reg_val |= (0x1 << 23);
writel(reg_val, rtc->reg_base + RTC_SEC_ADJUST_REG);
return 0;
}
static int meson_rtc_set_calibration(struct device *dev, u32 calibration)
{
enum meson_rtc_adj cal_ops;
u32 match_counter;
u32 sec_adjust_ctrl;
int ret;
match_counter = calibration & 0x7ffff;
sec_adjust_ctrl = (calibration >> 30) & 0x3;
cal_ops = sec_adjust_ctrl;
if (cal_ops == ADJUST_MAX) {
pr_err("%s: calibration ops val invalid!\n", __func__);
return -EINVAL;
}
ret = meson_rtc_adjust_sec(dev, match_counter, cal_ops);
dev_dbg(dev, "%s: Success to store RTC calibration attribute\n",
__func__);
return ret;
}
static int meson_rtc_get_calibration(struct device *dev, u32 *calibration)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev->parent);
enum meson_rtc_adj cal_ops;
u32 match_counter;
u32 reg_val;
u32 sec_adjust_ctrl;
reg_val = readl(rtc->reg_base + RTC_SEC_ADJUST_REG);
match_counter = reg_val & 0x7ffff;
sec_adjust_ctrl = (reg_val >> 19) & 3U;
if (sec_adjust_ctrl == 0 || sec_adjust_ctrl == 1)
cal_ops = ADJUST_NONE;
else if (sec_adjust_ctrl == 2)
cal_ops = ADJUST_DROP;
else
cal_ops = ADJUST_INSERT;
*calibration = ((cal_ops & 0x3) << 30) | (match_counter & 0x7ffff);
return 0;
}
/* The calibration value transferred from buf takes bit[18:0] to represent */
/* match counter, while takes bit[31:30] to represent operation style */
/* which can be set to: 0 represents no calibration; 1 represents drop one */
/* second every match counter; 2 represents insert one second every match counter */
static ssize_t rtc_calibration_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int retval;
int calibration = 0;
if (sscanf(buf, " %i ", &calibration) != 1) {
dev_err(dev, "Failed to store RTC calibration attribute\n");
return -EINVAL;
}
retval = meson_rtc_set_calibration(dev, calibration);
return retval ? retval : count;
}
static ssize_t rtc_calibration_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int retval = 0;
u32 calibration = 0;
retval = meson_rtc_get_calibration(dev, &calibration);
if (retval < 0) {
dev_err(dev, "Failed to read RTC calibration attribute\n");
sprintf(buf, "0\n");
return retval;
}
return sprintf(buf, "0x%x\n", calibration);
}
static DEVICE_ATTR_RW(rtc_calibration);
static struct attribute *meson_rtc_attrs[] = {
&dev_attr_rtc_calibration.attr,
NULL
};
static const struct attribute_group meson_rtc_sysfs_files = {
.attrs = meson_rtc_attrs,
};
#endif
static const struct rtc_class_ops aml_meson_rtc_ops = {
.read_time = meson_rtc_read_time,
.set_time = meson_rtc_set_time,
.read_alarm = meson_rtc_read_alarm,
.set_alarm = meson_rtc_set_alarm,
.alarm_irq_enable = meson_rtc_alarm_enable,
};
static void meson_rtc_init(struct device *dev, struct meson_rtc_data *rtc)
{
u32 reg_val;
u32 rtc_enable;
reg_val = readl(rtc->reg_base + RTC_CTRL);
rtc_enable = ((reg_val >> RTC_ENABLE_BIT) & 1);
dev_dbg(dev, "%s: rtc enable = %u\n", __func__, rtc_enable);
/* If rtc is not enable, enable and init it */
if (!rtc_enable) {
meson_rtc_clk_config(rtc);
/* Enable RTC */
reg_val |= (1 << RTC_ENABLE_BIT);
writel(reg_val, rtc->reg_base + RTC_CTRL);
usleep_range(100, 200);
}
/* Mask alarm0 irq */
reg_val = readl(rtc->reg_base + RTC_INT_MASK);
reg_val |= (1 << RTC_ALRM0_IRQ_MSK_BIT);
writel(reg_val, rtc->reg_base + RTC_INT_MASK);
rtc->alarm_enabled = false;
/* Show rtc time */
reg_val = readl(rtc->reg_base + RTC_REAL_TIME);
dev_dbg(dev, "%s: rtc time stamp = %u\n", __func__, reg_val);
}
static int meson_rtc_probe(struct platform_device *pdev)
{
struct meson_rtc_data *rtc;
struct device_node *node = pdev->dev.of_node;
int ret;
u32 freq = 0;
if (!node) {
dev_err(&pdev->dev,
"meson-rtc requires of_node!\n");
return -EINVAL;
}
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
rtc->irq = platform_get_irq(pdev, 0);
if (rtc->irq < 0)
return rtc->irq;
rtc->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rtc->reg_base)) {
pr_err("resource ioremap failed\n");
return PTR_ERR(rtc->reg_base);
}
rtc->clock = of_clk_get_by_name(pdev->dev.of_node, "rtc_clk");
if (IS_ERR_OR_NULL(rtc->clock)) {
pr_err("invalid rtc clock\n");
return -EINVAL;
}
ret = of_property_read_u32(pdev->dev.of_node,
"osc_freq", &freq);
if (!ret) {
if (freq != OSC_24MHZ && freq != OSC_32KHZ) {
pr_err("%s: Invalid clock configuration\n",
__func__);
return -EINVAL;
}
dev_dbg(&pdev->dev, "%s: Select %dHz oscillator as rtc source clock\n",
__func__, freq);
rtc->freq = freq;
} else {
pr_err("%s: Get rtc clock frequency failed!\n", __func__);
return -EINVAL;
}
clk_set_rate(rtc->clock, rtc->freq);
clk_prepare_enable(rtc->clock);
meson_rtc_init(&pdev->dev, rtc);
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, rtc);
rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc->rtc_dev))
return PTR_ERR(rtc->rtc_dev);
ret = devm_request_irq(&pdev->dev, rtc->irq, meson_rtc_handler,
IRQF_ONESHOT, "meson-rtc alarm", rtc);
if (ret) {
dev_err(&pdev->dev, "IRQ%d request failed, ret = %d\n",
rtc->irq, ret);
return ret;
}
rtc->rtc_dev->ops = &aml_meson_rtc_ops;
#ifdef RTC_CALIBRATION
ret = rtc_add_group(rtc->rtc_dev, &meson_rtc_sysfs_files);
if (ret)
return ret;
#endif
ret = devm_rtc_register_device(rtc->rtc_dev);
if (ret)
return ret;
dev_dbg(&pdev->dev, "%s: probe done\n", __func__);
return 0;
}
static int meson_rtc_suspend(struct device *dev)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
u32 reg_val;
if (device_may_wakeup(dev)) {
enable_irq_wake(rtc->irq);
/* Unmask alarm0 irq */
reg_val = readl(rtc->reg_base + RTC_INT_MASK);
reg_val &= (~(1 << RTC_ALRM0_IRQ_MSK_BIT));
writel(reg_val, rtc->reg_base + RTC_INT_MASK);
}
return 0;
}
static int meson_rtc_resume(struct device *dev)
{
struct meson_rtc_data *rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(rtc->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(meson_rtc_pm_ops,
meson_rtc_suspend, meson_rtc_resume);
static void meson_rtc_shutdown(struct platform_device *pdev)
{
struct meson_rtc_data *rtc = dev_get_drvdata(&pdev->dev);
u32 reg_val;
if (device_may_wakeup(&pdev->dev)) {
enable_irq_wake(rtc->irq);
/* Unmask alarm0 irq */
reg_val = readl(rtc->reg_base + RTC_INT_MASK);
reg_val &= (~(1 << RTC_ALRM0_IRQ_MSK_BIT));
writel(reg_val, rtc->reg_base + RTC_INT_MASK);
}
}
static const struct of_device_id meson_rtc_dt_match[] = {
{ .compatible = "amlogic,meson-rtc"},
{}
};
MODULE_DEVICE_TABLE(of, meson_rtc_dt_match);
static struct platform_driver meson_rtc_driver = {
.probe = meson_rtc_probe,
.driver = {
.name = "meson-rtc",
.of_match_table = meson_rtc_dt_match,
.pm = &meson_rtc_pm_ops,
},
.shutdown = meson_rtc_shutdown,
};
int __init rtc_init(void)
{
return platform_driver_register(&meson_rtc_driver);
}
void __exit rtc_exit(void)
{
platform_driver_unregister(&meson_rtc_driver);
}