mirror of https://github.com/OpenIPC/firmware.git
590 lines
16 KiB
Diff
590 lines
16 KiB
Diff
--- linux-4.9.37/drivers/rtc/rtc-goke.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ linux-4.9.y/drivers/rtc/rtc-goke.c 2021-06-07 13:01:34.000000000 +0300
|
|
@@ -0,0 +1,586 @@
|
|
+/*
|
|
+ * Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
|
|
+ */
|
|
+
|
|
+#include <linux/bcd.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <linux/log2.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/rtc.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+
|
|
+union u_spi_rw {
|
|
+ struct {
|
|
+ unsigned int spi_wdata : 8; /* [7:0] */
|
|
+ unsigned int spi_rdata : 8; /* [15:8] */
|
|
+ unsigned int spi_addr : 7; /* [22:16] */
|
|
+ unsigned int spi_rw : 1; /* [23] */
|
|
+ unsigned int spi_start : 1; /* [24] */
|
|
+ unsigned int reserved : 6; /* [30:25] */
|
|
+ unsigned int spi_busy : 1; /* [31] */
|
|
+ } bits;
|
|
+ unsigned int u32;
|
|
+};
|
|
+
|
|
+#define SPI_CLK_DIV (0x000)
|
|
+#define SPI_RW (0x004)
|
|
+
|
|
+#define SPI_WRITE (0)
|
|
+#define SPI_READ (1)
|
|
+
|
|
+/* RTC REG */
|
|
+#define RTC_10MS_COUN 0x00
|
|
+#define RTC_S_COUNT 0x01
|
|
+#define RTC_M_COUNT 0x02
|
|
+#define RTC_H_COUNT 0x03
|
|
+#define RTC_D_COUNT_L 0x04
|
|
+#define RTC_D_COUNT_H 0x05
|
|
+
|
|
+#define RTC_MR_10MS 0x06
|
|
+#define RTC_MR_S 0x07
|
|
+#define RTC_MR_M 0x08
|
|
+#define RTC_MR_H 0x09
|
|
+#define RTC_MR_D_L 0x0A
|
|
+#define RTC_MR_D_H 0x0B
|
|
+
|
|
+#define RTC_LR_10MS 0x0C
|
|
+#define RTC_LR_S 0x0D
|
|
+#define RTC_LR_M 0x0E
|
|
+#define RTC_LR_H 0x0F
|
|
+#define RTC_LR_D_L 0x10
|
|
+#define RTC_LR_D_H 0x11
|
|
+
|
|
+#define RTC_LORD 0x12
|
|
+
|
|
+#define RTC_IMSC 0x13
|
|
+#define RTC_INT_CLR 0x14
|
|
+#define RTC_INT 0x15
|
|
+#define RTC_INT_RAW 0x16
|
|
+
|
|
+#define RTC_CLK 0x17
|
|
+#define RTC_POR_N 0x18
|
|
+#define RTC_SAR_CTRL 0x1A
|
|
+#define RTC_CLK_CFG 0x1B
|
|
+
|
|
+#define RTC_FREQ_H 0x51
|
|
+#define RTC_FREQ_L 0x52
|
|
+
|
|
+#if defined(CONFIG_ARCH_GK7205V200) || defined(CONFIG_ARCH_GK7205V300) || \
|
|
+ defined(CONFIG_ARCH_GK7202V300) || defined(CONFIG_ARCH_GK7605V100)
|
|
+
|
|
+#define RTC_REG_LOCK1 0x64
|
|
+#define RTC_REG_LOCK2 0x65
|
|
+#define RTC_REG_LOCK3 0x66
|
|
+#define RTC_REG_LOCK4 0x67
|
|
+#endif
|
|
+
|
|
+
|
|
+#define FREQ_H_DEFAULT 0x8
|
|
+#define FREQ_L_DEFAULT 0x1B
|
|
+
|
|
+#define LV_CTL_DEFAULT 0x20
|
|
+#define CLK_DIV_DEFAULT 0x4
|
|
+#define INT_RST_DEFAULT 0x0
|
|
+#define INT_MSK_DEFAULT 0x4
|
|
+
|
|
+#define AIE_INT_MASK BIT(0)
|
|
+#define LV_INT_MASK BIT(1)
|
|
+#define REG_LOAD_STAT BIT(0)
|
|
+#define REG_LOCK_STAT BIT(1)
|
|
+#define REG_LOCK_BYPASS BIT(2)
|
|
+
|
|
+#define RTC_RW_RETRY_CNT 5
|
|
+#define SPI_RW_RETRY_CNT 500
|
|
+#define RTC_SLEEP_TIME_MS 20
|
|
+
|
|
+#define DATE_TO_SEC(d, h, m, s) (s + m*60 + h*60*60 + d*24*60*60)
|
|
+#define SEC_TO_DAY(s) (s/(60*60*24))
|
|
+
|
|
+struct goke_rtc {
|
|
+ struct rtc_device *rtc_dev;
|
|
+ void __iomem *regs;
|
|
+ int rtc_irq;
|
|
+};
|
|
+
|
|
+static int goke_spi_write(void *spi_reg, unsigned char reg,
|
|
+ unsigned char val)
|
|
+{
|
|
+ union u_spi_rw w_data, r_data;
|
|
+ int cnt = SPI_RW_RETRY_CNT;
|
|
+
|
|
+ r_data.u32 = 0;
|
|
+ w_data.u32 = 0;
|
|
+
|
|
+ w_data.bits.spi_wdata = val;
|
|
+ w_data.bits.spi_addr = reg;
|
|
+ w_data.bits.spi_rw = SPI_WRITE;
|
|
+ w_data.bits.spi_start = 0x1;
|
|
+
|
|
+ writel(w_data.u32, (spi_reg+SPI_RW));
|
|
+
|
|
+ do
|
|
+ r_data.u32 = readl(spi_reg+SPI_RW);
|
|
+ while (r_data.bits.spi_busy && (--cnt));
|
|
+
|
|
+ if (r_data.bits.spi_busy)
|
|
+ return -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int goke_spi_rtc_write(void *spi_reg, unsigned char reg,
|
|
+ unsigned char val)
|
|
+{
|
|
+ return goke_spi_write(spi_reg, reg, val);
|
|
+}
|
|
+
|
|
+static int goke_spi_read(void *spi_reg, unsigned char reg,
|
|
+ unsigned char *val)
|
|
+{
|
|
+ union u_spi_rw w_data, r_data;
|
|
+ int cnt = SPI_RW_RETRY_CNT;
|
|
+
|
|
+ r_data.u32 = 0;
|
|
+ w_data.u32 = 0;
|
|
+ w_data.bits.spi_addr = reg;
|
|
+ w_data.bits.spi_rw = SPI_READ;
|
|
+ w_data.bits.spi_start = 0x1;
|
|
+
|
|
+ writel(w_data.u32, (spi_reg+SPI_RW));
|
|
+
|
|
+ do
|
|
+ r_data.u32 = readl(spi_reg+SPI_RW);
|
|
+ while (r_data.bits.spi_busy && (--cnt));
|
|
+
|
|
+ if (r_data.bits.spi_busy)
|
|
+ return -EIO;
|
|
+
|
|
+ *val = r_data.bits.spi_rdata;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int goke_spi_rtc_read(void *spi_reg, unsigned char reg,
|
|
+ unsigned char *val)
|
|
+{
|
|
+ return goke_spi_read(spi_reg, reg, val);
|
|
+}
|
|
+
|
|
+static int goke_rtc_read_time(struct device *dev, struct rtc_time *time)
|
|
+{
|
|
+ struct goke_rtc *rtc = dev_get_drvdata(dev);
|
|
+ unsigned char dayl = 0, dayh = 0;
|
|
+ unsigned char second = 0, minute = 0, hour = 0;
|
|
+ unsigned long seconds = 0;
|
|
+ unsigned int day = 0;
|
|
+ unsigned char raw_value = 0;
|
|
+ int cnt = RTC_RW_RETRY_CNT;
|
|
+ unsigned int ret = 0;
|
|
+
|
|
+ ret = (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_INT_RAW, &raw_value);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (raw_value & LV_INT_MASK) {
|
|
+ //dev_err(dev, "low voltage detected, date/time is not reliable.\n");
|
|
+ (unsigned int)goke_spi_write(rtc->regs, RTC_INT_CLR, 1);
|
|
+ //return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value);
|
|
+ if (raw_value & REG_LOCK_BYPASS)
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LORD,
|
|
+ (~(REG_LOCK_BYPASS)) & raw_value);
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value);
|
|
+ /* lock the time */
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LORD,
|
|
+ (REG_LOCK_STAT) | raw_value);
|
|
+ /* wait rtc load flag */
|
|
+ do {
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value);
|
|
+ msleep(RTC_SLEEP_TIME_MS);
|
|
+ } while ((ret || (raw_value & REG_LOCK_STAT)) && (--cnt));
|
|
+
|
|
+ if (!ret && (raw_value & REG_LOCK_STAT))
|
|
+ return -EBUSY;
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_S_COUNT, &second);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_M_COUNT, &minute);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_H_COUNT, &hour);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_D_COUNT_L, &dayl);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_D_COUNT_H, &dayh);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ day = (dayl | (dayh << 8));
|
|
+ seconds = DATE_TO_SEC(day, hour, minute, second);
|
|
+
|
|
+ rtc_time_to_tm(seconds, time);
|
|
+
|
|
+ return rtc_valid_tm(time);
|
|
+}
|
|
+
|
|
+static int goke_rtc_set_time(struct device *dev, struct rtc_time *time)
|
|
+{
|
|
+ struct goke_rtc *rtc = dev_get_drvdata(dev);
|
|
+ unsigned int ret = 0;
|
|
+ unsigned int days;
|
|
+ unsigned long seconds = 0;
|
|
+ unsigned int cnt = RTC_RW_RETRY_CNT;
|
|
+ unsigned char raw_value = 0;
|
|
+
|
|
+ ret = rtc_tm_to_time(time, &seconds);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ days = SEC_TO_DAY(seconds);
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LR_10MS, 0);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LR_S, time->tm_sec);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LR_M, time->tm_min);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LR_H, time->tm_hour);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LR_D_L, (days & 0xFF));
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LR_D_H, (days >> 8));
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_LORD,
|
|
+ (raw_value | REG_LOAD_STAT));
|
|
+ /* wait rtc load flag */
|
|
+ do {
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value);
|
|
+ msleep(RTC_SLEEP_TIME_MS);
|
|
+ } while ((ret || (raw_value & REG_LOAD_STAT)) && (--cnt));
|
|
+
|
|
+ if (!ret && (raw_value & REG_LOAD_STAT))
|
|
+ return -EBUSY;
|
|
+
|
|
+ if (ret)
|
|
+ dev_err(dev, "IO err.\n");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int goke_rtc_read_alarm(struct device *dev,
|
|
+ struct rtc_wkalrm *alrm)
|
|
+{
|
|
+ struct goke_rtc *rtc = dev_get_drvdata(dev);
|
|
+ unsigned char dayl = 0, dayh = 0;
|
|
+ unsigned char second = 0, minute = 0, hour = 0;
|
|
+ unsigned long seconds = 0;
|
|
+ unsigned int day = 0;
|
|
+ unsigned char int_state = 0;
|
|
+ unsigned int ret = 0;
|
|
+
|
|
+ memset(alrm, 0, sizeof(struct rtc_wkalrm));
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_MR_S, &second);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_MR_M, &minute);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_MR_H, &hour);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_MR_D_L, &dayl);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_MR_D_H, &dayh);
|
|
+
|
|
+ day = (unsigned int)(dayl | (dayh << 8));
|
|
+ seconds = DATE_TO_SEC(day, hour, minute, second);
|
|
+
|
|
+ rtc_time_to_tm(seconds, &alrm->time);
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_IMSC, &int_state);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ alrm->enabled = !!(int_state & AIE_INT_MASK);
|
|
+ alrm->pending = alrm->enabled;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int goke_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|
+{
|
|
+ struct goke_rtc *rtc = dev_get_drvdata(dev);
|
|
+ unsigned int days;
|
|
+ unsigned long seconds = 0;
|
|
+ unsigned char val = 0;
|
|
+ unsigned int ret = 0;
|
|
+
|
|
+ rtc_tm_to_time(&alrm->time, &seconds);
|
|
+
|
|
+ days = SEC_TO_DAY(seconds);
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_MR_10MS, 0);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_MR_S, alrm->time.tm_sec);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_MR_M, alrm->time.tm_min);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_MR_H, alrm->time.tm_hour);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_MR_D_L, (days & 0xFF));
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_MR_D_H, (days >> 8));
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_IMSC, &val);
|
|
+ if (alrm->enabled)
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_IMSC,
|
|
+ val | AIE_INT_MASK);
|
|
+ else
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_IMSC,
|
|
+ val & ~AIE_INT_MASK);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int goke_rtc_alarm_irq_enable(struct device *dev,
|
|
+ unsigned int enabled)
|
|
+{
|
|
+ struct goke_rtc *rtc = dev_get_drvdata(dev);
|
|
+ unsigned char val = 0;
|
|
+ unsigned int ret = 0;
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_IMSC, &val);
|
|
+ if (enabled)
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_IMSC,
|
|
+ val | AIE_INT_MASK);
|
|
+ else
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_IMSC,
|
|
+ val & ~AIE_INT_MASK);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * interrupt function
|
|
+ * do nothing. left for future
|
|
+ */
|
|
+static irqreturn_t goke_rtc_alm_interrupt(int irq, void *data)
|
|
+{
|
|
+ struct goke_rtc *rtc = (struct goke_rtc *)data;
|
|
+ unsigned char val = 0;
|
|
+ unsigned int ret = 0;
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_read(rtc->regs, RTC_INT, &val);
|
|
+ ret |= (unsigned int)goke_spi_write(rtc->regs, RTC_INT_CLR, AIE_INT_MASK);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(&rtc->rtc_dev->dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (val & AIE_INT_MASK)
|
|
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+#define FREQ_MAX_VAL 3277000
|
|
+#define FREQ_MIN_VAL 3276000
|
|
+
|
|
+static int goke_rtc_ioctl(struct device *dev,
|
|
+ unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ struct goke_rtc *rtc = dev_get_drvdata(dev);
|
|
+ unsigned int ret = 0;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case RTC_PLL_SET: {
|
|
+ char freq_l, freq_h;
|
|
+ struct rtc_pll_info pll_info;
|
|
+
|
|
+ if (copy_from_user(&pll_info, (struct rtc_pll_info *)(uintptr_t)arg,
|
|
+ sizeof(struct rtc_pll_info)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ /* freq = 32700 + (freq /3052)*100 */
|
|
+ if (pll_info.pll_value > FREQ_MAX_VAL
|
|
+ || pll_info.pll_value < FREQ_MIN_VAL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ pll_info.pll_value = (pll_info.pll_value - 3270000)
|
|
+ * 3052 / 10000;
|
|
+
|
|
+ freq_l = (char)((unsigned int)pll_info.pll_value & 0xff);
|
|
+ freq_h = (char)(((unsigned int)pll_info.pll_value >> 8) & 0xf);
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_FREQ_H, freq_h);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(rtc->regs, RTC_FREQ_L, freq_l);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+ case RTC_PLL_GET: {
|
|
+ char freq_l, freq_h;
|
|
+ struct rtc_pll_info pll_info;
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_FREQ_H, &freq_h);
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(rtc->regs, RTC_FREQ_L, &freq_l);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pll_info.pll_value = (((unsigned char)freq_h & 0xf) << 8) + freq_l;
|
|
+ pll_info.pll_value = 3270000
|
|
+ + (pll_info.pll_value * 10000) / 3052;
|
|
+
|
|
+ pll_info.pll_max = FREQ_MAX_VAL;
|
|
+ pll_info.pll_min = FREQ_MIN_VAL;
|
|
+
|
|
+ if (copy_to_user((void __user *)(uintptr_t)arg,
|
|
+ &pll_info, sizeof(struct rtc_pll_info)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+ default:
|
|
+ return -ENOIOCTLCMD;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct rtc_class_ops goke_rtc_ops = {
|
|
+ .read_time = goke_rtc_read_time,
|
|
+ .set_time = goke_rtc_set_time,
|
|
+ .read_alarm = goke_rtc_read_alarm,
|
|
+ .set_alarm = goke_rtc_set_alarm,
|
|
+ .alarm_irq_enable = goke_rtc_alarm_irq_enable,
|
|
+ .ioctl = goke_rtc_ioctl,
|
|
+};
|
|
+
|
|
+static int goke_rtc_init(struct goke_rtc *rtc)
|
|
+{
|
|
+ void *spi_reg = rtc->regs;
|
|
+ unsigned int ret = 0;
|
|
+ unsigned char val = 0;
|
|
+ /*
|
|
+ * clk div value = (apb_clk/spi_clk)/2-1,
|
|
+ * apb clk = 100MHz, spi_clk = 10MHz,so value= 0x4
|
|
+ */
|
|
+ writel(CLK_DIV_DEFAULT, (spi_reg+SPI_CLK_DIV));
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_IMSC, INT_MSK_DEFAULT);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_SAR_CTRL, LV_CTL_DEFAULT);
|
|
+
|
|
+#if defined(CONFIG_ARCH_GK7205V200) || defined(CONFIG_ARCH_GK7205V300) || \
|
|
+ defined(CONFIG_ARCH_GK7202V300) || defined(CONFIG_ARCH_GK7605V100)
|
|
+ /* default driver capability */
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_REG_LOCK4, 0x5A);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_REG_LOCK3, 0x5A);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_REG_LOCK2, 0xAB);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_REG_LOCK1, 0xCD);
|
|
+#endif
|
|
+
|
|
+ /*driver capability */
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_CLK_CFG, 0x01);
|
|
+
|
|
+ /* default FREQ COEF */
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_FREQ_H, FREQ_H_DEFAULT);
|
|
+ ret |= (unsigned int)goke_spi_rtc_write(spi_reg, RTC_FREQ_L, FREQ_L_DEFAULT);
|
|
+
|
|
+ ret |= (unsigned int)goke_spi_rtc_read(spi_reg, RTC_INT_RAW, &val);
|
|
+ if (ret) {
|
|
+ dev_err(&rtc->rtc_dev->dev, "IO err.\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (val & LV_INT_MASK) {
|
|
+ /* dev_err(&rtc->rtc_dev->dev,
|
|
+ "low voltage detected, date/time is not reliable.\n"); */
|
|
+ goke_spi_write(rtc->regs, RTC_INT_CLR, 1);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int goke_rtc_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct resource *mem = NULL;
|
|
+ struct goke_rtc *rtc;
|
|
+ int ret;
|
|
+
|
|
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
|
+ if (!rtc)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ rtc->regs = devm_ioremap_resource(&pdev->dev, mem);
|
|
+ if (IS_ERR((const void *)rtc->regs)) {
|
|
+ dev_err(&pdev->dev, "could not map I/O memory\n");
|
|
+ return PTR_ERR((const void *)rtc->regs);
|
|
+ }
|
|
+
|
|
+ rtc->rtc_irq = platform_get_irq(pdev, 0);
|
|
+ ret = devm_request_irq(&pdev->dev, rtc->rtc_irq,
|
|
+ goke_rtc_alm_interrupt, 0, pdev->name, rtc);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "could not request irq %d\n", rtc->rtc_irq);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ platform_set_drvdata(pdev, rtc);
|
|
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
|
|
+ &goke_rtc_ops, THIS_MODULE);
|
|
+ if (IS_ERR(rtc->rtc_dev)) {
|
|
+ dev_err(&pdev->dev, "could not register rtc device\n");
|
|
+ return PTR_ERR(rtc->rtc_dev);
|
|
+ }
|
|
+
|
|
+ if (goke_rtc_init(rtc)) {
|
|
+ dev_err(&pdev->dev, "goke_rtc_init failed.\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ dev_info(&pdev->dev, "RTC driver for goke enabled\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int goke_rtc_remove(struct platform_device *pdev)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id goke_rtc_match[] = {
|
|
+ { .compatible = "goke,rtc" },
|
|
+ {},
|
|
+};
|
|
+
|
|
+static struct platform_driver goke_rtc_driver = {
|
|
+ .probe = goke_rtc_probe,
|
|
+ .remove = goke_rtc_remove,
|
|
+ .driver = {
|
|
+ .name = "goke_rtc",
|
|
+ .of_match_table = goke_rtc_match,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(goke_rtc_driver);
|
|
+
|
|
+
|
|
+MODULE_AUTHOR("Goke");
|
|
+MODULE_DESCRIPTION("Goke RTC driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|