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