firmware/br-ext-chip-goke/board/gk7205v200/kernel/patches/00_drivers-rtc-rtc-goke.c.p...

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");
+