18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Derived from driver/rtc/rtc-au1xxx.c 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/rtc.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <loongson1.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20) 198c2ecf20Sopenharmony_ci#define LS1X_RTC_REGS(x) \ 208c2ecf20Sopenharmony_ci ((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x))) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/*RTC programmable counters 0 and 1*/ 238c2ecf20Sopenharmony_ci#define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20)) 248c2ecf20Sopenharmony_ci#define SYS_CNTRL_ERS (1 << 23) 258c2ecf20Sopenharmony_ci#define SYS_CNTRL_RTS (1 << 20) 268c2ecf20Sopenharmony_ci#define SYS_CNTRL_RM2 (1 << 19) 278c2ecf20Sopenharmony_ci#define SYS_CNTRL_RM1 (1 << 18) 288c2ecf20Sopenharmony_ci#define SYS_CNTRL_RM0 (1 << 17) 298c2ecf20Sopenharmony_ci#define SYS_CNTRL_RS (1 << 16) 308c2ecf20Sopenharmony_ci#define SYS_CNTRL_BP (1 << 14) 318c2ecf20Sopenharmony_ci#define SYS_CNTRL_REN (1 << 13) 328c2ecf20Sopenharmony_ci#define SYS_CNTRL_BRT (1 << 12) 338c2ecf20Sopenharmony_ci#define SYS_CNTRL_TEN (1 << 11) 348c2ecf20Sopenharmony_ci#define SYS_CNTRL_BTT (1 << 10) 358c2ecf20Sopenharmony_ci#define SYS_CNTRL_E0 (1 << 8) 368c2ecf20Sopenharmony_ci#define SYS_CNTRL_ETS (1 << 7) 378c2ecf20Sopenharmony_ci#define SYS_CNTRL_32S (1 << 5) 388c2ecf20Sopenharmony_ci#define SYS_CNTRL_TTS (1 << 4) 398c2ecf20Sopenharmony_ci#define SYS_CNTRL_TM2 (1 << 3) 408c2ecf20Sopenharmony_ci#define SYS_CNTRL_TM1 (1 << 2) 418c2ecf20Sopenharmony_ci#define SYS_CNTRL_TM0 (1 << 1) 428c2ecf20Sopenharmony_ci#define SYS_CNTRL_TS (1 << 0) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Programmable Counter 0 Registers */ 458c2ecf20Sopenharmony_ci#define SYS_TOYTRIM (LS1X_RTC_REGS(0)) 468c2ecf20Sopenharmony_ci#define SYS_TOYWRITE0 (LS1X_RTC_REGS(4)) 478c2ecf20Sopenharmony_ci#define SYS_TOYWRITE1 (LS1X_RTC_REGS(8)) 488c2ecf20Sopenharmony_ci#define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC)) 498c2ecf20Sopenharmony_ci#define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10)) 508c2ecf20Sopenharmony_ci#define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14)) 518c2ecf20Sopenharmony_ci#define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18)) 528c2ecf20Sopenharmony_ci#define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C)) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Programmable Counter 1 Registers */ 558c2ecf20Sopenharmony_ci#define SYS_RTCTRIM (LS1X_RTC_REGS(0x40)) 568c2ecf20Sopenharmony_ci#define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44)) 578c2ecf20Sopenharmony_ci#define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48)) 588c2ecf20Sopenharmony_ci#define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C)) 598c2ecf20Sopenharmony_ci#define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50)) 608c2ecf20Sopenharmony_ci#define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54)) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define LS1X_SEC_OFFSET (4) 638c2ecf20Sopenharmony_ci#define LS1X_MIN_OFFSET (10) 648c2ecf20Sopenharmony_ci#define LS1X_HOUR_OFFSET (16) 658c2ecf20Sopenharmony_ci#define LS1X_DAY_OFFSET (21) 668c2ecf20Sopenharmony_ci#define LS1X_MONTH_OFFSET (26) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define LS1X_SEC_MASK (0x3f) 708c2ecf20Sopenharmony_ci#define LS1X_MIN_MASK (0x3f) 718c2ecf20Sopenharmony_ci#define LS1X_HOUR_MASK (0x1f) 728c2ecf20Sopenharmony_ci#define LS1X_DAY_MASK (0x1f) 738c2ecf20Sopenharmony_ci#define LS1X_MONTH_MASK (0x3f) 748c2ecf20Sopenharmony_ci#define LS1X_YEAR_MASK (0xffffffff) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK) 778c2ecf20Sopenharmony_ci#define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK) 788c2ecf20Sopenharmony_ci#define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK) 798c2ecf20Sopenharmony_ci#define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK) 808c2ecf20Sopenharmony_ci#define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned long v; 878c2ecf20Sopenharmony_ci time64_t t; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci v = readl(SYS_TOYREAD0); 908c2ecf20Sopenharmony_ci t = readl(SYS_TOYREAD1); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci memset(rtm, 0, sizeof(struct rtc_time)); 938c2ecf20Sopenharmony_ci t = mktime64((t & LS1X_YEAR_MASK), ls1x_get_month(v), 948c2ecf20Sopenharmony_ci ls1x_get_day(v), ls1x_get_hour(v), 958c2ecf20Sopenharmony_ci ls1x_get_min(v), ls1x_get_sec(v)); 968c2ecf20Sopenharmony_ci rtc_time64_to_tm(t, rtm); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci unsigned long v, t, c; 1048c2ecf20Sopenharmony_ci int ret = -ETIMEDOUT; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET) 1078c2ecf20Sopenharmony_ci | (rtm->tm_mday << LS1X_DAY_OFFSET) 1088c2ecf20Sopenharmony_ci | (rtm->tm_hour << LS1X_HOUR_OFFSET) 1098c2ecf20Sopenharmony_ci | (rtm->tm_min << LS1X_MIN_OFFSET) 1108c2ecf20Sopenharmony_ci | (rtm->tm_sec << LS1X_SEC_OFFSET); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci writel(v, SYS_TOYWRITE0); 1138c2ecf20Sopenharmony_ci c = 0x10000; 1148c2ecf20Sopenharmony_ci /* add timeout check counter, for more safe */ 1158c2ecf20Sopenharmony_ci while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c) 1168c2ecf20Sopenharmony_ci usleep_range(1000, 3000); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (!c) { 1198c2ecf20Sopenharmony_ci dev_err(dev, "set time timeout!\n"); 1208c2ecf20Sopenharmony_ci goto err; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci t = rtm->tm_year + 1900; 1248c2ecf20Sopenharmony_ci writel(t, SYS_TOYWRITE1); 1258c2ecf20Sopenharmony_ci c = 0x10000; 1268c2ecf20Sopenharmony_ci while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c) 1278c2ecf20Sopenharmony_ci usleep_range(1000, 3000); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!c) { 1308c2ecf20Sopenharmony_ci dev_err(dev, "set time timeout!\n"); 1318c2ecf20Sopenharmony_ci goto err; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_cierr: 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const struct rtc_class_ops ls1x_rtc_ops = { 1398c2ecf20Sopenharmony_ci .read_time = ls1x_rtc_read_time, 1408c2ecf20Sopenharmony_ci .set_time = ls1x_rtc_set_time, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int ls1x_rtc_probe(struct platform_device *pdev) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct rtc_device *rtcdev; 1468c2ecf20Sopenharmony_ci unsigned long v; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci v = readl(SYS_COUNTER_CNTRL); 1498c2ecf20Sopenharmony_ci if (!(v & RTC_CNTR_OK)) { 1508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "rtc counters not working\n"); 1518c2ecf20Sopenharmony_ci return -ENODEV; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* set to 1 HZ if needed */ 1558c2ecf20Sopenharmony_ci if (readl(SYS_TOYTRIM) != 32767) { 1568c2ecf20Sopenharmony_ci v = 0x100000; 1578c2ecf20Sopenharmony_ci while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v) 1588c2ecf20Sopenharmony_ci usleep_range(1000, 3000); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!v) { 1618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "time out\n"); 1628c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci writel(32767, SYS_TOYTRIM); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci /* this loop coundn't be endless */ 1678c2ecf20Sopenharmony_ci while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) 1688c2ecf20Sopenharmony_ci usleep_range(1000, 3000); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci rtcdev = devm_rtc_allocate_device(&pdev->dev); 1718c2ecf20Sopenharmony_ci if (IS_ERR(rtcdev)) 1728c2ecf20Sopenharmony_ci return PTR_ERR(rtcdev); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rtcdev); 1758c2ecf20Sopenharmony_ci rtcdev->ops = &ls1x_rtc_ops; 1768c2ecf20Sopenharmony_ci rtcdev->range_min = RTC_TIMESTAMP_BEGIN_1900; 1778c2ecf20Sopenharmony_ci rtcdev->range_max = RTC_TIMESTAMP_END_2099; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return rtc_register_device(rtcdev); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct platform_driver ls1x_rtc_driver = { 1838c2ecf20Sopenharmony_ci .driver = { 1848c2ecf20Sopenharmony_ci .name = "ls1x-rtc", 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci .probe = ls1x_rtc_probe, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cimodule_platform_driver(ls1x_rtc_driver); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciMODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>"); 1928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 193