679 lines
18 KiB
C
679 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021, Artinchip Technology Co., Ltd
|
|
* Author: Dehuang Wu <dehuang.wu@artinchip.com>
|
|
* Matteo <duanmt@artinchip.com>
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/rtc.h>
|
|
#include <linux/of.h>
|
|
#include <linux/reboot-reason.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/pm_wakeirq.h>
|
|
|
|
#define AIC_RTC_NAME "aic-rtc"
|
|
|
|
#define RTC_REG_CTL (0x0000)
|
|
#define RTC_REG_INIT (0x0004)
|
|
#define RTC_REG_IRQ_EN (0x0008)
|
|
#define RTC_REG_IRQ_STA (0x000C)
|
|
#define RTC_REG_TIME0 (0x0020)
|
|
#define RTC_REG_TIME1 (0x0024)
|
|
#define RTC_REG_TIME2 (0x0028)
|
|
#define RTC_REG_TIME3 (0x002C)
|
|
#define RTC_REG_ALARM0 (0x0030)
|
|
#define RTC_REG_ALARM1 (0x0034)
|
|
#define RTC_REG_ALARM2 (0x0038)
|
|
#define RTC_REG_ALARM3 (0x003C)
|
|
#define RTC_REG_CALI0 (0x0040)
|
|
#define RTC_REG_CALI1 (0x0044)
|
|
#define RTC_REG_ANALOG0 (0x0050)
|
|
#define RTC_REG_ANALOG1 (0x0054)
|
|
#define RTC_REG_ANALOG2 (0x0058)
|
|
#define RTC_REG_ANALOG3 (0x005C)
|
|
#define RTC_REG_WR_EN (0x00FC)
|
|
#define RTC_REG_SYSBAK (0x0100)
|
|
#define RTC_REG_TCNT (0x0800)
|
|
#define RTC_REG_32K_DET (0x0804)
|
|
#define RTC_REG_VER (0x08FC)
|
|
|
|
#define RTC_CTL_IO_OUTPUT_ENABLE 1
|
|
#define RTC_CTL_IO_ALARM_OUTPUT 2
|
|
#define RTC_CTL_IO_32K_CLK_OUTPUT 3
|
|
#define RTC_CTL_IO_SEL_SHIFT 4
|
|
#define RTC_CTL_IO_SEL_MASK GENMASK(5, 4)
|
|
#define RTC_CTL_IO_SEL(x) (((x) & 0x3) << RTC_CTL_IO_SEL_SHIFT)
|
|
#define RTC_CTL_ALARM_EN BIT(2)
|
|
#define RTC_CTL_TCNT_EN BIT(0)
|
|
|
|
#define RTC_IRQ_32K_ERR_EN BIT(2)
|
|
#define RTC_IRQ_ALARM_EN BIT(0)
|
|
|
|
#define RTC_IRQ_STA_32K_ERR BIT(2)
|
|
#define RTC_IRQ_STA_ALARM_IO BIT(1)
|
|
#define RTC_IRQ_STA_ALARM BIT(0)
|
|
|
|
#define RTC_CAL1_FAST_DIR BIT(7)
|
|
|
|
#define RTC_ANA0_RC1M_ISEL BIT(7)
|
|
#define RTC_ANA0_RC1M_EN BIT(6)
|
|
#define RTC_ANA0_LDO18_BYPASS BIT(4)
|
|
#define RTC_ANA0_LDO18_VOL_MASK GENMASK(3, 1)
|
|
#define RTC_ANA0_LDO18_VOL_SHIFT (1)
|
|
#define RTC_ANA0_LDO18_EN BIT(0)
|
|
|
|
#define RTC_ANA0_LDO18_VOL_120 7
|
|
|
|
#define RTC_ANA1_PD_CUR_SEL_MASK GENMASK(6, 5)
|
|
#define RTC_ANA1_PD_CUR_SEL_SHIFT (5)
|
|
#define RTC_ANA1_PD_CUR_EN BIT(4)
|
|
#define RTC_ANA1_LDO11_VOL_MASK GENMASK(3, 1)
|
|
#define RTC_ANA1_LDO11_VOL_SHIFT (1)
|
|
#define RTC_ANA1_LDO11_LPEN BIT(0)
|
|
|
|
#define RTC_ANA1_PD_CUR_SEL_025 0
|
|
#define RTC_ANA1_PD_CUR_SEL_050 1
|
|
#define RTC_ANA1_PD_CUR_SEL_075 2
|
|
#define RTC_ANA1_PD_CUR_SEL_100 3
|
|
|
|
#define RTC_ANA1_LDO11_VOL_090 4
|
|
#define RTC_ANA1_LDO11_VOL_080 6
|
|
|
|
#define RTC_ANA2_XTAL32K_DRV_MASK GENMASK(3, 0)
|
|
|
|
#define RTC_ANA3_XTAL32K_EN BIT(0)
|
|
|
|
#define RTC_32K_DET_EN BIT(0)
|
|
|
|
#define RTC_WR_EN_KEY 0xAC
|
|
|
|
#define RTC_DRV_TIMEOUT msecs_to_jiffies(2000)
|
|
#define RTC_32K_FREQ (32 * 1024 * 100)
|
|
|
|
#define RTC_WRITE_ENABLE(base) writeb(RTC_WR_EN_KEY, base + RTC_REG_WR_EN)
|
|
#define RTC_WRITE_DISABLE(base) writeb(0, base + RTC_REG_WR_EN)
|
|
#define RTC_WRITEB(val, reg, base) \
|
|
do { \
|
|
RTC_WRITE_ENABLE(base); \
|
|
writeb((val) & 0xFF, (base) + (reg)); \
|
|
RTC_WRITE_DISABLE(base); \
|
|
} while (0)
|
|
#define RTC_WRITEL(val, reg, base) \
|
|
do { \
|
|
RTC_WRITE_ENABLE(base); \
|
|
writeb((val) & 0xFF, (base) + (reg)); \
|
|
writeb(((val) >> 8) & 0xFF, (base) + (reg) + 0x4); \
|
|
writeb(((val) >> 16) & 0xFF, (base) + (reg) + 0x8); \
|
|
writeb(((val) >> 24) & 0xFF, (base) + (reg) + 0xC); \
|
|
RTC_WRITE_DISABLE(base); \
|
|
} while (0)
|
|
#define RTC_READL(reg) (readb(reg) | (readb((reg) + 0x4) << 8) \
|
|
| (readb((reg) + 0x8) << 16) \
|
|
| (readb((reg) + 0xC) << 24))
|
|
|
|
#define RTC_REBOOT_REASON_MASK GENMASK(7, 4)
|
|
#define RTC_REBOOT_REASON_SHIFT 4
|
|
|
|
struct aic_rtc_dev {
|
|
void __iomem *base;
|
|
struct rtc_device *rtc_dev;
|
|
struct attribute_group attrs;
|
|
struct clk *clk;
|
|
u32 clk_rate;
|
|
u32 clk_drv;
|
|
int irq;
|
|
bool alarm_io;
|
|
bool output_32k;
|
|
bool cal_fast;
|
|
s32 cal_val;
|
|
s32 pwkey_gpio;
|
|
|
|
struct completion complete;
|
|
};
|
|
|
|
static void __iomem *g_rtc_base;
|
|
static enum aic_reboot_reason g_prev_reason = REBOOT_REASON_INVALID;
|
|
static DEFINE_SPINLOCK(user_lock);
|
|
|
|
static char *reason[] = {"Cold Reboot", "CMD Reboot", "CMD Shutdown",
|
|
"Suspend", "Upgrade", "U-Boot Upgrade", "", "",
|
|
"SW Lockup", "Hw Lockup", "Panic", "Ramdump",
|
|
"", "", "", ""};
|
|
|
|
void aic_set_software_reboot_reason(enum aic_reboot_reason r)
|
|
{
|
|
u32 cur = 0;
|
|
|
|
if (!g_rtc_base)
|
|
return;
|
|
|
|
spin_lock(&user_lock);
|
|
cur = readb(g_rtc_base + RTC_REG_SYSBAK);
|
|
|
|
if (cur >> RTC_REBOOT_REASON_SHIFT) {
|
|
spin_unlock(&user_lock);
|
|
return;
|
|
}
|
|
|
|
cur |= (r << RTC_REBOOT_REASON_SHIFT) & RTC_REBOOT_REASON_MASK;
|
|
RTC_WRITEB(cur, RTC_REG_SYSBAK, g_rtc_base);
|
|
spin_unlock(&user_lock);
|
|
|
|
if (likely(r <= RTC_REBOOT_REASON_MASK >> RTC_REBOOT_REASON_SHIFT))
|
|
pr_info("Set reboot reason: [%d] %s\n", r, reason[r]);
|
|
}
|
|
EXPORT_SYMBOL(aic_set_software_reboot_reason);
|
|
|
|
enum aic_reboot_reason aic_get_software_reboot_reason(void)
|
|
{
|
|
u32 cur = 0;
|
|
|
|
if (!g_rtc_base)
|
|
return REBOOT_REASON_INVALID;
|
|
|
|
if (g_prev_reason == REBOOT_REASON_INVALID) {
|
|
spin_lock(&user_lock);
|
|
cur = readb(g_rtc_base + RTC_REG_SYSBAK);
|
|
g_prev_reason = cur >> RTC_REBOOT_REASON_SHIFT;
|
|
cur &= ~RTC_REBOOT_REASON_MASK;
|
|
|
|
RTC_WRITEB(cur, RTC_REG_SYSBAK, g_rtc_base);
|
|
spin_unlock(&user_lock);
|
|
}
|
|
|
|
pr_info("The software reboot reason: [%d] %s\n", g_prev_reason,
|
|
reason[g_prev_reason]);
|
|
return g_prev_reason;
|
|
}
|
|
EXPORT_SYMBOL(aic_get_software_reboot_reason);
|
|
|
|
static ssize_t status_show(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
void __iomem *base = chip->base;
|
|
u32 ver = readl(base + RTC_REG_VER);
|
|
u32 ctl = readl(base + RTC_REG_CTL);
|
|
|
|
return sprintf(buf, "In RTC V%d.%02d:\n"
|
|
"Module Enable: %d\n"
|
|
"Alarm Enable: %d, Output alarm IO: %d/%d, Output 32K: %d\n"
|
|
"Clock rate: %d, Driver: %d\n"
|
|
"Calibration %s, Value: %d\n",
|
|
ver >> 8, ver & 0xFF, (u32)(ctl & RTC_CTL_TCNT_EN),
|
|
ctl & RTC_CTL_ALARM_EN ? 1 : 0,
|
|
(u32)(ctl & RTC_CTL_IO_SEL_MASK) >> RTC_CTL_IO_SEL_SHIFT,
|
|
chip->alarm_io, chip->output_32k,
|
|
chip->clk_rate, chip->clk_drv,
|
|
chip->cal_fast ? "Fast" : "Slow", chip->cal_val);
|
|
return 0;
|
|
}
|
|
static DEVICE_ATTR_RO(status);
|
|
|
|
|
|
static void aic_rtc_low_power(void __iomem *base)
|
|
{
|
|
u8 val = 0;
|
|
|
|
val |= RTC_ANA0_RC1M_EN | RTC_ANA0_LDO18_EN;
|
|
val |= RTC_ANA0_LDO18_VOL_120 << RTC_ANA0_LDO18_VOL_SHIFT;
|
|
RTC_WRITEB(val, RTC_REG_ANALOG0, base);
|
|
|
|
val = RTC_ANA1_PD_CUR_SEL_075 << RTC_ANA1_PD_CUR_SEL_SHIFT;
|
|
val |= RTC_ANA1_LDO11_VOL_090 << RTC_ANA1_LDO11_VOL_SHIFT;
|
|
val |= RTC_ANA1_LDO11_LPEN;
|
|
RTC_WRITEB(val, RTC_REG_ANALOG1, base);
|
|
}
|
|
|
|
static void aic_rtc_set_32k_drv(void __iomem *base, u8 drv)
|
|
{
|
|
u8 val = 0;
|
|
|
|
val = readb(base + RTC_REG_ANALOG2);
|
|
val &= ~RTC_ANA2_XTAL32K_DRV_MASK;
|
|
val |= drv;
|
|
RTC_WRITEB(val, RTC_REG_ANALOG2, base);
|
|
}
|
|
|
|
static ssize_t driver_capability_show(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
char *buf)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
void __iomem *base = chip->base;
|
|
s8 drv[50] = "";
|
|
s8 sta[50] = "";
|
|
u32 i, detect = 0;
|
|
|
|
init_completion(&chip->complete);
|
|
|
|
for (i = 0; i <= RTC_ANA2_XTAL32K_DRV_MASK; i++) {
|
|
aic_rtc_set_32k_drv(base, i);
|
|
|
|
detect = readl(base + RTC_REG_32K_DET);
|
|
detect |= RTC_32K_DET_EN;
|
|
writel(detect, base + RTC_REG_32K_DET);
|
|
|
|
sprintf(drv + strlen(drv), " %2d", i);
|
|
if (!wait_for_completion_timeout(&chip->complete,
|
|
RTC_DRV_TIMEOUT)) {
|
|
pr_debug("32K-clk driver %d is OK\n", i);
|
|
sprintf(sta + strlen(sta), " OK");
|
|
} else {
|
|
pr_debug("32k-clk driver %d is failure\n", i);
|
|
sprintf(sta + strlen(sta), " -");
|
|
}
|
|
}
|
|
|
|
return sprintf(buf, "The status of RTC driver:\nDriver%s\nStatus%s\n",
|
|
drv, sta);
|
|
}
|
|
static DEVICE_ATTR_RO(driver_capability);
|
|
|
|
static ssize_t aicupg_store(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
if (strlen(buf) == 2 && !strncmp(buf, "1", 1)) {
|
|
/* Goto BROM upgmode */
|
|
aic_set_software_reboot_reason(REBOOT_REASON_UPGRADE);
|
|
} else if (strlen(buf) == 2 && !strncmp(buf, "2", 1)) {
|
|
/* Goto U-Boot upgmode */
|
|
aic_set_software_reboot_reason(REBOOT_REASON_BL_UPGRADE);
|
|
} else {
|
|
dev_info(dev, "Invalid argument: %s\n", buf);
|
|
}
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_WO(aicupg);
|
|
|
|
static ssize_t reboot_reason_show(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
char *buf)
|
|
{
|
|
return sprintf(buf, "%d\n", aic_get_software_reboot_reason());
|
|
}
|
|
static DEVICE_ATTR_RO(reboot_reason);
|
|
|
|
static struct attribute *aic_rtc_attr[] = {
|
|
&dev_attr_status.attr,
|
|
&dev_attr_driver_capability.attr,
|
|
&dev_attr_reboot_reason.attr,
|
|
&dev_attr_aicupg.attr,
|
|
NULL
|
|
};
|
|
|
|
static void aic_rtc_enable(struct aic_rtc_dev *chip, u32 enable)
|
|
{
|
|
u8 val = 0;
|
|
|
|
val = readb(chip->base + RTC_REG_CTL);
|
|
if (enable)
|
|
val |= RTC_CTL_TCNT_EN;
|
|
else
|
|
val &= ~RTC_CTL_TCNT_EN;
|
|
RTC_WRITEB(val, RTC_REG_CTL, chip->base);
|
|
}
|
|
|
|
static int aic_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
u32 time = readl(chip->base + RTC_REG_TCNT);
|
|
|
|
rtc_time64_to_tm(time, tm);
|
|
dev_dbg(dev, "Get RTC time: %04d-%02d-%02d %02d:%02d:%02d\n",
|
|
tm->tm_year, tm->tm_mon, tm->tm_mday,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
u32 time = 0;
|
|
|
|
aic_rtc_enable(chip, 0);
|
|
time = (u32)rtc_tm_to_time64(tm);
|
|
RTC_WRITEL(time, RTC_REG_TIME0, chip->base);
|
|
RTC_WRITEB(1, RTC_REG_INIT, chip->base);
|
|
aic_rtc_enable(chip, 1);
|
|
|
|
dev_dbg(dev, "Set RTC time: %04d-%02d-%02d %02d:%02d:%02d\n",
|
|
tm->tm_year, tm->tm_mon, tm->tm_mday,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
u32 alarm_time, now;
|
|
|
|
/* get alarm target time */
|
|
alarm_time = RTC_READL(chip->base + RTC_REG_ALARM0);
|
|
rtc_time64_to_tm(alarm_time, &alarm->time);
|
|
|
|
/* check if alarm has triggered */
|
|
now = readl(chip->base + RTC_REG_TCNT);
|
|
alarm->pending = alarm_time > now;
|
|
alarm->enabled = readb(chip->base + RTC_REG_IRQ_EN) & RTC_IRQ_ALARM_EN;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
|
|
RTC_WRITEB(enabled ? (RTC_IRQ_32K_ERR_EN | RTC_IRQ_ALARM_EN) : 0,
|
|
RTC_REG_IRQ_EN, chip->base);
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
|
{
|
|
struct aic_rtc_dev *chip = dev_get_drvdata(dev);
|
|
time64_t alarm_time;
|
|
u32 value;
|
|
|
|
/* First, disable alarm */
|
|
value = readb(chip->base + RTC_REG_CTL);
|
|
value &= ~RTC_CTL_ALARM_EN;
|
|
RTC_WRITEB(value, RTC_REG_CTL, chip->base);
|
|
|
|
/* set alarm time value */
|
|
alarm_time = rtc_tm_to_time64(&alarm->time);
|
|
RTC_WRITEL((u32)alarm_time, RTC_REG_ALARM0, chip->base);
|
|
dev_dbg(dev, "Set a alarm(%d): %04d-%02d-%02d %02d:%02d:%02d\n",
|
|
alarm->enabled,
|
|
alarm->time.tm_year, alarm->time.tm_mon,
|
|
alarm->time.tm_mday, alarm->time.tm_hour,
|
|
alarm->time.tm_min, alarm->time.tm_sec);
|
|
|
|
/* Then, enable alarm */
|
|
value |= RTC_CTL_ALARM_EN;
|
|
RTC_WRITEB(value, RTC_REG_CTL, chip->base);
|
|
|
|
/* enable alarm irq */
|
|
aic_rtc_alarm_irq_enable(dev, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_nvmem_write(void *priv, unsigned int offset, void *val,
|
|
size_t bytes)
|
|
{
|
|
int i;
|
|
u32 *pval = (u32 *)val;
|
|
struct aic_rtc_dev *chip = (struct aic_rtc_dev *)priv;
|
|
|
|
pr_debug("%s() write %ld byte, offset %d\n", __func__, bytes, offset);
|
|
spin_lock(&user_lock);
|
|
for (i = 0; i < (bytes + 3) / 4; i++, pval++) {
|
|
RTC_WRITEB(*pval, RTC_REG_SYSBAK + offset + i * 4, chip->base);
|
|
pr_debug("%s() SYS_BAK reg %#x: %#x\n", __func__,
|
|
RTC_REG_SYSBAK + offset + i * 4, *pval);
|
|
}
|
|
spin_unlock(&user_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_nvmem_read(void *priv, unsigned int offset,
|
|
void *val, size_t bytes)
|
|
{
|
|
int i;
|
|
u32 *pval = (u32 *)val;
|
|
struct aic_rtc_dev *chip = (struct aic_rtc_dev *)priv;
|
|
|
|
pr_debug("%s() read %ld byte, offset %d\n", __func__, bytes, offset);
|
|
spin_lock(&user_lock);
|
|
for (i = 0; i < (bytes + 3) / 4; i++, pval++) {
|
|
*pval = readb(chip->base + RTC_REG_SYSBAK + offset + i * 4);
|
|
pr_debug("%s() SYS_BAK reg %#x: %#x\n", __func__,
|
|
RTC_REG_SYSBAK + offset + i * 4, *pval);
|
|
}
|
|
spin_unlock(&user_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct rtc_class_ops aic_rtc_ops = {
|
|
.read_time = aic_rtc_read_time,
|
|
.set_time = aic_rtc_set_time,
|
|
.read_alarm = aic_rtc_read_alarm,
|
|
.set_alarm = aic_rtc_set_alarm,
|
|
.alarm_irq_enable = aic_rtc_alarm_irq_enable,
|
|
};
|
|
|
|
static irqreturn_t aic_rtc_irq(int irq, void *dev_id)
|
|
{
|
|
struct aic_rtc_dev *chip = (struct aic_rtc_dev *)dev_id;
|
|
struct device *dev = &chip->rtc_dev->dev;
|
|
u8 val;
|
|
|
|
val = readb(chip->base + RTC_REG_IRQ_STA);
|
|
RTC_WRITEB(val, RTC_REG_IRQ_STA, chip->base);
|
|
dev_dbg(dev, "IRQ status %#x\n", val);
|
|
if (val & RTC_IRQ_STA_32K_ERR) {
|
|
dev_err(dev, "The 32K clk is not fast enough");
|
|
complete(&chip->complete);
|
|
}
|
|
|
|
if (val & RTC_IRQ_STA_ALARM) {
|
|
rtc_update_irq(chip->rtc_dev, 1, RTC_AF | RTC_IRQF);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
/* calibrate: (100 * 1024 * 1024 + cval)/( freq / 32) = 1024
|
|
* freq = 100 * actual OSC frequency
|
|
*/
|
|
static void aic_rtc_cali(struct aic_rtc_dev *chip)
|
|
{
|
|
s32 cval = 0;
|
|
|
|
if (chip->clk_rate == RTC_32K_FREQ)
|
|
return; /* It's perfect, need not calibrate */
|
|
|
|
cval = 100 * 1024 * 1024 - chip->clk_rate * 32;
|
|
if (chip->clk_rate > RTC_32K_FREQ)
|
|
cval -= 50;
|
|
else
|
|
cval += 50;
|
|
|
|
cval /= 100;
|
|
dev_dbg(&chip->rtc_dev->dev, "Calibration %d(clk %d)",
|
|
cval, chip->clk_rate);
|
|
chip->cal_val = cval;
|
|
if (cval > 0) {
|
|
cval |= RTC_CAL1_FAST_DIR << 8;
|
|
chip->cal_fast = true;
|
|
} else {
|
|
cval = -cval;
|
|
}
|
|
|
|
RTC_WRITEB(cval >> 8, RTC_REG_CALI1, chip->base);
|
|
RTC_WRITEB(cval & 0xFF, RTC_REG_CALI0, chip->base);
|
|
}
|
|
|
|
static int aic_rtc_parse_dt(struct device *dev, struct aic_rtc_dev *chip)
|
|
{
|
|
u8 val = 0;
|
|
u32 ret = 0;
|
|
struct device_node *np = dev->of_node;
|
|
|
|
chip->alarm_io = of_property_read_bool(np, "aic,alarm-io-output");
|
|
if (chip->alarm_io) {
|
|
/* Use power-gpios to replace alarm_io, if power on by alarm */
|
|
chip->pwkey_gpio = of_get_named_gpio(np, "power-gpios", 0);
|
|
if (gpio_is_valid(chip->pwkey_gpio)) {
|
|
gpio_direction_output(chip->pwkey_gpio, 1);
|
|
dev_info(dev, "Set power-gpios on.\n");
|
|
}
|
|
|
|
/* Check & clean poweroff alarm status */
|
|
val = readb(chip->base + RTC_REG_IRQ_STA);
|
|
if (val) {
|
|
dev_dbg(dev, "IRQ_STA is %#x\n", val);
|
|
RTC_WRITEB(val, RTC_REG_IRQ_STA, chip->base);
|
|
if (val & RTC_IRQ_STA_ALARM_IO)
|
|
dev_info(dev, "Powered by RTC alarm.\n");
|
|
}
|
|
|
|
/* RTC_IO = alarm output */
|
|
val = readb(chip->base + RTC_REG_CTL);
|
|
val |= RTC_CTL_IO_SEL(RTC_CTL_IO_ALARM_OUTPUT);
|
|
RTC_WRITEB(val, RTC_REG_CTL, chip->base);
|
|
} else {
|
|
chip->output_32k = of_property_read_bool(np,
|
|
"aic,32k-io-output");
|
|
if (chip->output_32k) {
|
|
val = readb(chip->base + RTC_REG_CTL);
|
|
val |= RTC_CTL_IO_SEL(RTC_CTL_IO_32K_CLK_OUTPUT);
|
|
RTC_WRITEB(val, RTC_REG_CTL, chip->base);
|
|
}
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "clock-rate", &chip->clk_rate);
|
|
if (ret)
|
|
dev_warn(dev, "Can't parse RTC clock-rate\n");
|
|
else
|
|
aic_rtc_cali(chip);
|
|
|
|
ret = of_property_read_u32(np, "aic,clock-driver", &chip->clk_drv);
|
|
if (ret)
|
|
dev_warn(dev, "Can't parse RTC 32K clock driver\n");
|
|
else
|
|
aic_rtc_set_32k_drv(chip->base, chip->clk_drv);
|
|
|
|
if (of_property_read_bool(np, "wakeup-source")) {
|
|
device_init_wakeup(dev, true);
|
|
dev_pm_set_wake_irq(dev, chip->irq);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_rtc_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct aic_rtc_dev *chip;
|
|
struct resource *res;
|
|
int ret;
|
|
struct nvmem_config nvmem_cfg = {
|
|
.name = "rtc_nvram",
|
|
.word_size = 4,
|
|
.stride = 4,
|
|
.size = 64,
|
|
.type = NVMEM_TYPE_BATTERY_BACKED,
|
|
.reg_read = aic_rtc_nvmem_read,
|
|
.reg_write = aic_rtc_nvmem_write,
|
|
};
|
|
|
|
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
|
if (!chip)
|
|
return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
chip->base = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(chip->base))
|
|
return PTR_ERR(chip->base);
|
|
|
|
g_rtc_base = chip->base;
|
|
aic_get_software_reboot_reason();
|
|
|
|
chip->rtc_dev = devm_rtc_allocate_device(dev);
|
|
if (IS_ERR(chip->rtc_dev))
|
|
return PTR_ERR(chip->rtc_dev);
|
|
|
|
/* Before access RTC register, need to enable bus clock */
|
|
chip->clk = devm_clk_get(dev, NULL);
|
|
if (IS_ERR(chip->clk))
|
|
return PTR_ERR(chip->clk);
|
|
|
|
ret = clk_prepare_enable(chip->clk);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = platform_get_irq(pdev, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
chip->irq = ret;
|
|
ret = devm_request_irq(dev, chip->irq, aic_rtc_irq, 0, AIC_RTC_NAME,
|
|
chip);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
aic_rtc_parse_dt(dev, chip);
|
|
|
|
chip->attrs.attrs = aic_rtc_attr;
|
|
ret = sysfs_create_group(&pdev->dev.kobj, &chip->attrs);
|
|
if (ret)
|
|
return ret;
|
|
|
|
init_completion(&chip->complete);
|
|
aic_rtc_enable(chip, 1);
|
|
aic_rtc_low_power(chip->base);
|
|
platform_set_drvdata(pdev, chip);
|
|
|
|
chip->rtc_dev->ops = &aic_rtc_ops;
|
|
chip->rtc_dev->range_max = U32_MAX;
|
|
|
|
device_set_wakeup_capable(dev, 1);
|
|
ret = rtc_register_device(chip->rtc_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
nvmem_cfg.priv = chip;
|
|
rtc_nvmem_register(chip->rtc_dev, &nvmem_cfg);
|
|
|
|
dev_info(dev, "Artinchip RTC loaded\n");
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused aic_rtc_suspend(struct device *dev)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
/* process some voltage configuration if needed */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused aic_rtc_resume(struct device *dev)
|
|
{
|
|
dev_dbg(dev, "%s\n", __func__);
|
|
|
|
/* process some voltage configuration if needed */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(aic_rtc_pm_ops, aic_rtc_suspend, aic_rtc_resume);
|
|
|
|
static const struct of_device_id aic_rtc_match[] = {
|
|
{ .compatible = "artinchip,aic-rtc-v1.0" },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, aic_rtc_match);
|
|
|
|
static struct platform_driver aic_rtc_driver = {
|
|
.probe = aic_rtc_probe,
|
|
.driver = {
|
|
.name = AIC_RTC_NAME,
|
|
.of_match_table = aic_rtc_match,
|
|
.pm = &aic_rtc_pm_ops,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(aic_rtc_driver);
|