18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ST M48T59 RTC driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Wind River Systems, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Mark Zhan <rongkai.zhan@windriver.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/rtc.h> 178c2ecf20Sopenharmony_ci#include <linux/rtc/m48t59.h> 188c2ecf20Sopenharmony_ci#include <linux/bcd.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#ifndef NO_IRQ 228c2ecf20Sopenharmony_ci#define NO_IRQ (-1) 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg)) 268c2ecf20Sopenharmony_ci#define M48T59_WRITE(val, reg) \ 278c2ecf20Sopenharmony_ci (pdata->write_byte(dev, pdata->offset + reg, val)) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define M48T59_SET_BITS(mask, reg) \ 308c2ecf20Sopenharmony_ci M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg)) 318c2ecf20Sopenharmony_ci#define M48T59_CLEAR_BITS(mask, reg) \ 328c2ecf20Sopenharmony_ci M48T59_WRITE((M48T59_READ(reg) & ~(mask)), (reg)) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct m48t59_private { 358c2ecf20Sopenharmony_ci void __iomem *ioaddr; 368c2ecf20Sopenharmony_ci int irq; 378c2ecf20Sopenharmony_ci struct rtc_device *rtc; 388c2ecf20Sopenharmony_ci spinlock_t lock; /* serialize the NVRAM and RTC access */ 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * This is the generic access method when the chip is memory-mapped 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_cistatic void 458c2ecf20Sopenharmony_cim48t59_mem_writeb(struct device *dev, u32 ofs, u8 val) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci writeb(val, m48t59->ioaddr+ofs); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic u8 538c2ecf20Sopenharmony_cim48t59_mem_readb(struct device *dev, u32 ofs) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return readb(m48t59->ioaddr+ofs); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * NOTE: M48T59 only uses BCD mode 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 668c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 678c2ecf20Sopenharmony_ci unsigned long flags; 688c2ecf20Sopenharmony_ci u8 val; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 718c2ecf20Sopenharmony_ci /* Issue the READ command */ 728c2ecf20Sopenharmony_ci M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)); 758c2ecf20Sopenharmony_ci /* tm_mon is 0-11 */ 768c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1; 778c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(M48T59_READ(M48T59_MDAY)); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci val = M48T59_READ(M48T59_WDAY); 808c2ecf20Sopenharmony_ci if ((pdata->type == M48T59RTC_TYPE_M48T59) && 818c2ecf20Sopenharmony_ci (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { 828c2ecf20Sopenharmony_ci dev_dbg(dev, "Century bit is enabled\n"); 838c2ecf20Sopenharmony_ci tm->tm_year += 100; /* one century */ 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARC 868c2ecf20Sopenharmony_ci /* Sun SPARC machines count years since 1968 */ 878c2ecf20Sopenharmony_ci tm->tm_year += 68; 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci tm->tm_wday = bcd2bin(val & 0x07); 918c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(M48T59_READ(M48T59_HOUR) & 0x3F); 928c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(M48T59_READ(M48T59_MIN) & 0x7F); 938c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(M48T59_READ(M48T59_SEC) & 0x7F); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Clear the READ bit */ 968c2ecf20Sopenharmony_ci M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL); 978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci dev_dbg(dev, "RTC read time %ptR\n", tm); 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 1068c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 1078c2ecf20Sopenharmony_ci unsigned long flags; 1088c2ecf20Sopenharmony_ci u8 val = 0; 1098c2ecf20Sopenharmony_ci int year = tm->tm_year; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARC 1128c2ecf20Sopenharmony_ci /* Sun SPARC machines count years since 1968 */ 1138c2ecf20Sopenharmony_ci year -= 68; 1148c2ecf20Sopenharmony_ci#endif 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n", 1178c2ecf20Sopenharmony_ci year + 1900, tm->tm_mon, tm->tm_mday, 1188c2ecf20Sopenharmony_ci tm->tm_hour, tm->tm_min, tm->tm_sec); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (year < 0) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 1248c2ecf20Sopenharmony_ci /* Issue the WRITE command */ 1258c2ecf20Sopenharmony_ci M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci M48T59_WRITE((bin2bcd(tm->tm_sec) & 0x7F), M48T59_SEC); 1288c2ecf20Sopenharmony_ci M48T59_WRITE((bin2bcd(tm->tm_min) & 0x7F), M48T59_MIN); 1298c2ecf20Sopenharmony_ci M48T59_WRITE((bin2bcd(tm->tm_hour) & 0x3F), M48T59_HOUR); 1308c2ecf20Sopenharmony_ci M48T59_WRITE((bin2bcd(tm->tm_mday) & 0x3F), M48T59_MDAY); 1318c2ecf20Sopenharmony_ci /* tm_mon is 0-11 */ 1328c2ecf20Sopenharmony_ci M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); 1338c2ecf20Sopenharmony_ci M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100)) 1368c2ecf20Sopenharmony_ci val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); 1378c2ecf20Sopenharmony_ci val |= (bin2bcd(tm->tm_wday) & 0x07); 1388c2ecf20Sopenharmony_ci M48T59_WRITE(val, M48T59_WDAY); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Clear the WRITE bit */ 1418c2ecf20Sopenharmony_ci M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); 1428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* 1478c2ecf20Sopenharmony_ci * Read alarm time and date in RTC 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 1528c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 1538c2ecf20Sopenharmony_ci struct rtc_time *tm = &alrm->time; 1548c2ecf20Sopenharmony_ci unsigned long flags; 1558c2ecf20Sopenharmony_ci u8 val; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* If no irq, we don't support ALARM */ 1588c2ecf20Sopenharmony_ci if (m48t59->irq == NO_IRQ) 1598c2ecf20Sopenharmony_ci return -EIO; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 1628c2ecf20Sopenharmony_ci /* Issue the READ command */ 1638c2ecf20Sopenharmony_ci M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)); 1668c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARC 1678c2ecf20Sopenharmony_ci /* Sun SPARC machines count years since 1968 */ 1688c2ecf20Sopenharmony_ci tm->tm_year += 68; 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci /* tm_mon is 0-11 */ 1718c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci val = M48T59_READ(M48T59_WDAY); 1748c2ecf20Sopenharmony_ci if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) 1758c2ecf20Sopenharmony_ci tm->tm_year += 100; /* one century */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(M48T59_READ(M48T59_ALARM_DATE)); 1788c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(M48T59_READ(M48T59_ALARM_HOUR)); 1798c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(M48T59_READ(M48T59_ALARM_MIN)); 1808c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(M48T59_READ(M48T59_ALARM_SEC)); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Clear the READ bit */ 1838c2ecf20Sopenharmony_ci M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL); 1848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci dev_dbg(dev, "RTC read alarm time %ptR\n", tm); 1878c2ecf20Sopenharmony_ci return rtc_valid_tm(tm); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* 1918c2ecf20Sopenharmony_ci * Set alarm time and date in RTC 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_cistatic int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 1968c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 1978c2ecf20Sopenharmony_ci struct rtc_time *tm = &alrm->time; 1988c2ecf20Sopenharmony_ci u8 mday, hour, min, sec; 1998c2ecf20Sopenharmony_ci unsigned long flags; 2008c2ecf20Sopenharmony_ci int year = tm->tm_year; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARC 2038c2ecf20Sopenharmony_ci /* Sun SPARC machines count years since 1968 */ 2048c2ecf20Sopenharmony_ci year -= 68; 2058c2ecf20Sopenharmony_ci#endif 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* If no irq, we don't support ALARM */ 2088c2ecf20Sopenharmony_ci if (m48t59->irq == NO_IRQ) 2098c2ecf20Sopenharmony_ci return -EIO; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (year < 0) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * 0xff means "always match" 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci mday = tm->tm_mday; 2188c2ecf20Sopenharmony_ci mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff; 2198c2ecf20Sopenharmony_ci if (mday == 0xff) 2208c2ecf20Sopenharmony_ci mday = M48T59_READ(M48T59_MDAY); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hour = tm->tm_hour; 2238c2ecf20Sopenharmony_ci hour = (hour < 24) ? bin2bcd(hour) : 0x00; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci min = tm->tm_min; 2268c2ecf20Sopenharmony_ci min = (min < 60) ? bin2bcd(min) : 0x00; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci sec = tm->tm_sec; 2298c2ecf20Sopenharmony_ci sec = (sec < 60) ? bin2bcd(sec) : 0x00; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 2328c2ecf20Sopenharmony_ci /* Issue the WRITE command */ 2338c2ecf20Sopenharmony_ci M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci M48T59_WRITE(mday, M48T59_ALARM_DATE); 2368c2ecf20Sopenharmony_ci M48T59_WRITE(hour, M48T59_ALARM_HOUR); 2378c2ecf20Sopenharmony_ci M48T59_WRITE(min, M48T59_ALARM_MIN); 2388c2ecf20Sopenharmony_ci M48T59_WRITE(sec, M48T59_ALARM_SEC); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Clear the WRITE bit */ 2418c2ecf20Sopenharmony_ci M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); 2428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n", 2458c2ecf20Sopenharmony_ci year + 1900, tm->tm_mon, tm->tm_mday, 2468c2ecf20Sopenharmony_ci tm->tm_hour, tm->tm_min, tm->tm_sec); 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* 2518c2ecf20Sopenharmony_ci * Handle commands from user-space 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic int m48t59_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 2568c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 2578c2ecf20Sopenharmony_ci unsigned long flags; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 2608c2ecf20Sopenharmony_ci if (enabled) 2618c2ecf20Sopenharmony_ci M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR); 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci M48T59_WRITE(0x00, M48T59_INTR); 2648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int m48t59_rtc_proc(struct device *dev, struct seq_file *seq) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 2728c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 2738c2ecf20Sopenharmony_ci unsigned long flags; 2748c2ecf20Sopenharmony_ci u8 val; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 2778c2ecf20Sopenharmony_ci val = M48T59_READ(M48T59_FLAGS); 2788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci seq_printf(seq, "battery\t\t: %s\n", 2818c2ecf20Sopenharmony_ci (val & M48T59_FLAGS_BF) ? "low" : "normal"); 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* 2868c2ecf20Sopenharmony_ci * IRQ handler for the RTC 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic irqreturn_t m48t59_rtc_interrupt(int irq, void *dev_id) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct device *dev = (struct device *)dev_id; 2918c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(dev); 2928c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = dev_get_drvdata(dev); 2938c2ecf20Sopenharmony_ci u8 event; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci spin_lock(&m48t59->lock); 2968c2ecf20Sopenharmony_ci event = M48T59_READ(M48T59_FLAGS); 2978c2ecf20Sopenharmony_ci spin_unlock(&m48t59->lock); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (event & M48T59_FLAGS_AF) { 3008c2ecf20Sopenharmony_ci rtc_update_irq(m48t59->rtc, 1, (RTC_AF | RTC_IRQF)); 3018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return IRQ_NONE; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic const struct rtc_class_ops m48t59_rtc_ops = { 3088c2ecf20Sopenharmony_ci .read_time = m48t59_rtc_read_time, 3098c2ecf20Sopenharmony_ci .set_time = m48t59_rtc_set_time, 3108c2ecf20Sopenharmony_ci .read_alarm = m48t59_rtc_readalarm, 3118c2ecf20Sopenharmony_ci .set_alarm = m48t59_rtc_setalarm, 3128c2ecf20Sopenharmony_ci .proc = m48t59_rtc_proc, 3138c2ecf20Sopenharmony_ci .alarm_irq_enable = m48t59_rtc_alarm_irq_enable, 3148c2ecf20Sopenharmony_ci}; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic const struct rtc_class_ops m48t02_rtc_ops = { 3178c2ecf20Sopenharmony_ci .read_time = m48t59_rtc_read_time, 3188c2ecf20Sopenharmony_ci .set_time = m48t59_rtc_set_time, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int m48t59_nvram_read(void *priv, unsigned int offset, void *val, 3228c2ecf20Sopenharmony_ci size_t size) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct platform_device *pdev = priv; 3258c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3268c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev); 3278c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = platform_get_drvdata(pdev); 3288c2ecf20Sopenharmony_ci ssize_t cnt = 0; 3298c2ecf20Sopenharmony_ci unsigned long flags; 3308c2ecf20Sopenharmony_ci u8 *buf = val; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci for (; cnt < size; cnt++) 3358c2ecf20Sopenharmony_ci *buf++ = M48T59_READ(cnt); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int m48t59_nvram_write(void *priv, unsigned int offset, void *val, 3438c2ecf20Sopenharmony_ci size_t size) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct platform_device *pdev = priv; 3468c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3478c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev); 3488c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = platform_get_drvdata(pdev); 3498c2ecf20Sopenharmony_ci ssize_t cnt = 0; 3508c2ecf20Sopenharmony_ci unsigned long flags; 3518c2ecf20Sopenharmony_ci u8 *buf = val; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci spin_lock_irqsave(&m48t59->lock, flags); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci for (; cnt < size; cnt++) 3568c2ecf20Sopenharmony_ci M48T59_WRITE(*buf++, cnt); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&m48t59->lock, flags); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int m48t59_rtc_probe(struct platform_device *pdev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev); 3668c2ecf20Sopenharmony_ci struct m48t59_private *m48t59 = NULL; 3678c2ecf20Sopenharmony_ci struct resource *res; 3688c2ecf20Sopenharmony_ci int ret = -ENOMEM; 3698c2ecf20Sopenharmony_ci const struct rtc_class_ops *ops; 3708c2ecf20Sopenharmony_ci struct nvmem_config nvmem_cfg = { 3718c2ecf20Sopenharmony_ci .name = "m48t59-", 3728c2ecf20Sopenharmony_ci .word_size = 1, 3738c2ecf20Sopenharmony_ci .stride = 1, 3748c2ecf20Sopenharmony_ci .reg_read = m48t59_nvram_read, 3758c2ecf20Sopenharmony_ci .reg_write = m48t59_nvram_write, 3768c2ecf20Sopenharmony_ci .priv = pdev, 3778c2ecf20Sopenharmony_ci }; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* This chip could be memory-mapped or I/O-mapped */ 3808c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3818c2ecf20Sopenharmony_ci if (!res) { 3828c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IO, 0); 3838c2ecf20Sopenharmony_ci if (!res) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 3888c2ecf20Sopenharmony_ci /* If we are I/O-mapped, the platform should provide 3898c2ecf20Sopenharmony_ci * the operations accessing chip registers. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci if (!pdata || !pdata->write_byte || !pdata->read_byte) 3928c2ecf20Sopenharmony_ci return -EINVAL; 3938c2ecf20Sopenharmony_ci } else if (res->flags & IORESOURCE_MEM) { 3948c2ecf20Sopenharmony_ci /* we are memory-mapped */ 3958c2ecf20Sopenharmony_ci if (!pdata) { 3968c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), 3978c2ecf20Sopenharmony_ci GFP_KERNEL); 3988c2ecf20Sopenharmony_ci if (!pdata) 3998c2ecf20Sopenharmony_ci return -ENOMEM; 4008c2ecf20Sopenharmony_ci /* Ensure we only kmalloc platform data once */ 4018c2ecf20Sopenharmony_ci pdev->dev.platform_data = pdata; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci if (!pdata->type) 4048c2ecf20Sopenharmony_ci pdata->type = M48T59RTC_TYPE_M48T59; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Try to use the generic memory read/write ops */ 4078c2ecf20Sopenharmony_ci if (!pdata->write_byte) 4088c2ecf20Sopenharmony_ci pdata->write_byte = m48t59_mem_writeb; 4098c2ecf20Sopenharmony_ci if (!pdata->read_byte) 4108c2ecf20Sopenharmony_ci pdata->read_byte = m48t59_mem_readb; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci m48t59 = devm_kzalloc(&pdev->dev, sizeof(*m48t59), GFP_KERNEL); 4148c2ecf20Sopenharmony_ci if (!m48t59) 4158c2ecf20Sopenharmony_ci return -ENOMEM; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci m48t59->ioaddr = pdata->ioaddr; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (!m48t59->ioaddr) { 4208c2ecf20Sopenharmony_ci /* ioaddr not mapped externally */ 4218c2ecf20Sopenharmony_ci m48t59->ioaddr = devm_ioremap(&pdev->dev, res->start, 4228c2ecf20Sopenharmony_ci resource_size(res)); 4238c2ecf20Sopenharmony_ci if (!m48t59->ioaddr) 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Try to get irq number. We also can work in 4288c2ecf20Sopenharmony_ci * the mode without IRQ. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci m48t59->irq = platform_get_irq(pdev, 0); 4318c2ecf20Sopenharmony_ci if (m48t59->irq <= 0) 4328c2ecf20Sopenharmony_ci m48t59->irq = NO_IRQ; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (m48t59->irq != NO_IRQ) { 4358c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, m48t59->irq, 4368c2ecf20Sopenharmony_ci m48t59_rtc_interrupt, IRQF_SHARED, 4378c2ecf20Sopenharmony_ci "rtc-m48t59", &pdev->dev); 4388c2ecf20Sopenharmony_ci if (ret) 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci switch (pdata->type) { 4428c2ecf20Sopenharmony_ci case M48T59RTC_TYPE_M48T59: 4438c2ecf20Sopenharmony_ci ops = &m48t59_rtc_ops; 4448c2ecf20Sopenharmony_ci pdata->offset = 0x1ff0; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case M48T59RTC_TYPE_M48T02: 4478c2ecf20Sopenharmony_ci ops = &m48t02_rtc_ops; 4488c2ecf20Sopenharmony_ci pdata->offset = 0x7f0; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case M48T59RTC_TYPE_M48T08: 4518c2ecf20Sopenharmony_ci ops = &m48t02_rtc_ops; 4528c2ecf20Sopenharmony_ci pdata->offset = 0x1ff0; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci default: 4558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unknown RTC type\n"); 4568c2ecf20Sopenharmony_ci return -ENODEV; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci spin_lock_init(&m48t59->lock); 4608c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, m48t59); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci m48t59->rtc = devm_rtc_allocate_device(&pdev->dev); 4638c2ecf20Sopenharmony_ci if (IS_ERR(m48t59->rtc)) 4648c2ecf20Sopenharmony_ci return PTR_ERR(m48t59->rtc); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci m48t59->rtc->nvram_old_abi = true; 4678c2ecf20Sopenharmony_ci m48t59->rtc->ops = ops; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci nvmem_cfg.size = pdata->offset; 4708c2ecf20Sopenharmony_ci ret = rtc_nvmem_register(m48t59->rtc, &nvmem_cfg); 4718c2ecf20Sopenharmony_ci if (ret) 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci ret = rtc_register_device(m48t59->rtc); 4758c2ecf20Sopenharmony_ci if (ret) 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/* work with hotplug and coldplug */ 4828c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rtc-m48t59"); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic struct platform_driver m48t59_rtc_driver = { 4858c2ecf20Sopenharmony_ci .driver = { 4868c2ecf20Sopenharmony_ci .name = "rtc-m48t59", 4878c2ecf20Sopenharmony_ci }, 4888c2ecf20Sopenharmony_ci .probe = m48t59_rtc_probe, 4898c2ecf20Sopenharmony_ci}; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cimodule_platform_driver(m48t59_rtc_driver); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>"); 4948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver"); 4958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 496