18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * An rtc driver for the Dallas DS1742 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2006 Torsten Ertbjerg Rasmussen <tr@newtec.dk> 88c2ecf20Sopenharmony_ci * - nvram size determined from resource 98c2ecf20Sopenharmony_ci * - this ds1742 driver now supports ds1743. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/bcd.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/gfp.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 178c2ecf20Sopenharmony_ci#include <linux/rtc.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define RTC_SIZE 8 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define RTC_CONTROL 0 278c2ecf20Sopenharmony_ci#define RTC_CENTURY 0 288c2ecf20Sopenharmony_ci#define RTC_SECONDS 1 298c2ecf20Sopenharmony_ci#define RTC_MINUTES 2 308c2ecf20Sopenharmony_ci#define RTC_HOURS 3 318c2ecf20Sopenharmony_ci#define RTC_DAY 4 328c2ecf20Sopenharmony_ci#define RTC_DATE 5 338c2ecf20Sopenharmony_ci#define RTC_MONTH 6 348c2ecf20Sopenharmony_ci#define RTC_YEAR 7 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define RTC_CENTURY_MASK 0x3f 378c2ecf20Sopenharmony_ci#define RTC_SECONDS_MASK 0x7f 388c2ecf20Sopenharmony_ci#define RTC_DAY_MASK 0x07 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Bits in the Control/Century register */ 418c2ecf20Sopenharmony_ci#define RTC_WRITE 0x80 428c2ecf20Sopenharmony_ci#define RTC_READ 0x40 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Bits in the Seconds register */ 458c2ecf20Sopenharmony_ci#define RTC_STOP 0x80 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Bits in the Day register */ 488c2ecf20Sopenharmony_ci#define RTC_BATT_FLAG 0x80 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct rtc_plat_data { 518c2ecf20Sopenharmony_ci void __iomem *ioaddr_nvram; 528c2ecf20Sopenharmony_ci void __iomem *ioaddr_rtc; 538c2ecf20Sopenharmony_ci unsigned long last_jiffies; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct rtc_plat_data *pdata = dev_get_drvdata(dev); 598c2ecf20Sopenharmony_ci void __iomem *ioaddr = pdata->ioaddr_rtc; 608c2ecf20Sopenharmony_ci u8 century; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci century = bin2bcd((tm->tm_year + 1900) / 100); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci writeb(RTC_WRITE, ioaddr + RTC_CONTROL); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR); 678c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH); 688c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY); 698c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE); 708c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS); 718c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES); 728c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* RTC_CENTURY and RTC_CONTROL share same register */ 758c2ecf20Sopenharmony_ci writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY); 768c2ecf20Sopenharmony_ci writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int ds1742_rtc_read_time(struct device *dev, struct rtc_time *tm) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct rtc_plat_data *pdata = dev_get_drvdata(dev); 838c2ecf20Sopenharmony_ci void __iomem *ioaddr = pdata->ioaddr_rtc; 848c2ecf20Sopenharmony_ci unsigned int year, month, day, hour, minute, second, week; 858c2ecf20Sopenharmony_ci unsigned int century; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* give enough time to update RTC in case of continuous read */ 888c2ecf20Sopenharmony_ci if (pdata->last_jiffies == jiffies) 898c2ecf20Sopenharmony_ci msleep(1); 908c2ecf20Sopenharmony_ci pdata->last_jiffies = jiffies; 918c2ecf20Sopenharmony_ci writeb(RTC_READ, ioaddr + RTC_CONTROL); 928c2ecf20Sopenharmony_ci second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK; 938c2ecf20Sopenharmony_ci minute = readb(ioaddr + RTC_MINUTES); 948c2ecf20Sopenharmony_ci hour = readb(ioaddr + RTC_HOURS); 958c2ecf20Sopenharmony_ci day = readb(ioaddr + RTC_DATE); 968c2ecf20Sopenharmony_ci week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK; 978c2ecf20Sopenharmony_ci month = readb(ioaddr + RTC_MONTH); 988c2ecf20Sopenharmony_ci year = readb(ioaddr + RTC_YEAR); 998c2ecf20Sopenharmony_ci century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; 1008c2ecf20Sopenharmony_ci writeb(0, ioaddr + RTC_CONTROL); 1018c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(second); 1028c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(minute); 1038c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(hour); 1048c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(day); 1058c2ecf20Sopenharmony_ci tm->tm_wday = bcd2bin(week); 1068c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(month) - 1; 1078c2ecf20Sopenharmony_ci /* year is 1900 + tm->tm_year */ 1088c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const struct rtc_class_ops ds1742_rtc_ops = { 1148c2ecf20Sopenharmony_ci .read_time = ds1742_rtc_read_time, 1158c2ecf20Sopenharmony_ci .set_time = ds1742_rtc_set_time, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int ds1742_nvram_read(void *priv, unsigned int pos, void *val, 1198c2ecf20Sopenharmony_ci size_t bytes) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct rtc_plat_data *pdata = priv; 1228c2ecf20Sopenharmony_ci void __iomem *ioaddr = pdata->ioaddr_nvram; 1238c2ecf20Sopenharmony_ci u8 *buf = val; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (; bytes; bytes--) 1268c2ecf20Sopenharmony_ci *buf++ = readb(ioaddr + pos++); 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int ds1742_nvram_write(void *priv, unsigned int pos, void *val, 1318c2ecf20Sopenharmony_ci size_t bytes) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct rtc_plat_data *pdata = priv; 1348c2ecf20Sopenharmony_ci void __iomem *ioaddr = pdata->ioaddr_nvram; 1358c2ecf20Sopenharmony_ci u8 *buf = val; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (; bytes; bytes--) 1388c2ecf20Sopenharmony_ci writeb(*buf++, ioaddr + pos++); 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int ds1742_rtc_probe(struct platform_device *pdev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct rtc_device *rtc; 1458c2ecf20Sopenharmony_ci struct resource *res; 1468c2ecf20Sopenharmony_ci unsigned int cen, sec; 1478c2ecf20Sopenharmony_ci struct rtc_plat_data *pdata; 1488c2ecf20Sopenharmony_ci void __iomem *ioaddr; 1498c2ecf20Sopenharmony_ci int ret = 0; 1508c2ecf20Sopenharmony_ci struct nvmem_config nvmem_cfg = { 1518c2ecf20Sopenharmony_ci .name = "ds1742_nvram", 1528c2ecf20Sopenharmony_ci .reg_read = ds1742_nvram_read, 1538c2ecf20Sopenharmony_ci .reg_write = ds1742_nvram_write, 1548c2ecf20Sopenharmony_ci }; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!pdata) 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1628c2ecf20Sopenharmony_ci ioaddr = devm_ioremap_resource(&pdev->dev, res); 1638c2ecf20Sopenharmony_ci if (IS_ERR(ioaddr)) 1648c2ecf20Sopenharmony_ci return PTR_ERR(ioaddr); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci pdata->ioaddr_nvram = ioaddr; 1678c2ecf20Sopenharmony_ci pdata->ioaddr_rtc = ioaddr + resource_size(res) - RTC_SIZE; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci nvmem_cfg.size = resource_size(res) - RTC_SIZE; 1708c2ecf20Sopenharmony_ci nvmem_cfg.priv = pdata; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* turn RTC on if it was not on */ 1738c2ecf20Sopenharmony_ci ioaddr = pdata->ioaddr_rtc; 1748c2ecf20Sopenharmony_ci sec = readb(ioaddr + RTC_SECONDS); 1758c2ecf20Sopenharmony_ci if (sec & RTC_STOP) { 1768c2ecf20Sopenharmony_ci sec &= RTC_SECONDS_MASK; 1778c2ecf20Sopenharmony_ci cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; 1788c2ecf20Sopenharmony_ci writeb(RTC_WRITE, ioaddr + RTC_CONTROL); 1798c2ecf20Sopenharmony_ci writeb(sec, ioaddr + RTC_SECONDS); 1808c2ecf20Sopenharmony_ci writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci if (!(readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG)) 1838c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "voltage-low detected.\n"); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pdata->last_jiffies = jiffies; 1868c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pdata); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci rtc = devm_rtc_allocate_device(&pdev->dev); 1898c2ecf20Sopenharmony_ci if (IS_ERR(rtc)) 1908c2ecf20Sopenharmony_ci return PTR_ERR(rtc); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci rtc->ops = &ds1742_rtc_ops; 1938c2ecf20Sopenharmony_ci rtc->nvram_old_abi = true; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ret = rtc_register_device(rtc); 1968c2ecf20Sopenharmony_ci if (ret) 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (rtc_nvmem_register(rtc, &nvmem_cfg)) 2008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to register nvmem\n"); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic const struct of_device_id __maybe_unused ds1742_rtc_of_match[] = { 2068c2ecf20Sopenharmony_ci { .compatible = "maxim,ds1742", }, 2078c2ecf20Sopenharmony_ci { } 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ds1742_rtc_of_match); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct platform_driver ds1742_rtc_driver = { 2128c2ecf20Sopenharmony_ci .probe = ds1742_rtc_probe, 2138c2ecf20Sopenharmony_ci .driver = { 2148c2ecf20Sopenharmony_ci .name = "rtc-ds1742", 2158c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ds1742_rtc_of_match), 2168c2ecf20Sopenharmony_ci }, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cimodule_platform_driver(ds1742_rtc_driver); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); 2228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dallas DS1742 RTC driver"); 2238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2248c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rtc-ds1742"); 225