18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* drivers/rtc/rtc-s3c.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2010 Samsung Electronics Co., Ltd. 58c2ecf20Sopenharmony_ci * http://www.samsung.com/ 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2004,2006 Simtec Electronics 88c2ecf20Sopenharmony_ci * Ben Dooks, <ben@simtec.co.uk> 98c2ecf20Sopenharmony_ci * http://armlinux.simtec.co.uk/ 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * S3C2410/S3C2440/S3C24XX Internal RTC Driver 128c2ecf20Sopenharmony_ci*/ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/fs.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/rtc.h> 218c2ecf20Sopenharmony_ci#include <linux/bcd.h> 228c2ecf20Sopenharmony_ci#include <linux/clk.h> 238c2ecf20Sopenharmony_ci#include <linux/log2.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci#include <linux/of_device.h> 278c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 288c2ecf20Sopenharmony_ci#include <linux/io.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <asm/irq.h> 318c2ecf20Sopenharmony_ci#include "rtc-s3c.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct s3c_rtc { 348c2ecf20Sopenharmony_ci struct device *dev; 358c2ecf20Sopenharmony_ci struct rtc_device *rtc; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci void __iomem *base; 388c2ecf20Sopenharmony_ci struct clk *rtc_clk; 398c2ecf20Sopenharmony_ci struct clk *rtc_src_clk; 408c2ecf20Sopenharmony_ci bool alarm_enabled; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci const struct s3c_rtc_data *data; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci int irq_alarm; 458c2ecf20Sopenharmony_ci int irq_tick; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci spinlock_t pie_lock; 488c2ecf20Sopenharmony_ci spinlock_t alarm_lock; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci int ticnt_save; 518c2ecf20Sopenharmony_ci int ticnt_en_save; 528c2ecf20Sopenharmony_ci bool wake_en; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct s3c_rtc_data { 568c2ecf20Sopenharmony_ci int max_user_freq; 578c2ecf20Sopenharmony_ci bool needs_src_clk; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci void (*irq_handler) (struct s3c_rtc *info, int mask); 608c2ecf20Sopenharmony_ci void (*set_freq) (struct s3c_rtc *info, int freq); 618c2ecf20Sopenharmony_ci void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); 628c2ecf20Sopenharmony_ci void (*select_tick_clk) (struct s3c_rtc *info); 638c2ecf20Sopenharmony_ci void (*save_tick_cnt) (struct s3c_rtc *info); 648c2ecf20Sopenharmony_ci void (*restore_tick_cnt) (struct s3c_rtc *info); 658c2ecf20Sopenharmony_ci void (*enable) (struct s3c_rtc *info); 668c2ecf20Sopenharmony_ci void (*disable) (struct s3c_rtc *info); 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int s3c_rtc_enable_clk(struct s3c_rtc *info) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci ret = clk_enable(info->rtc_clk); 748c2ecf20Sopenharmony_ci if (ret) 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (info->data->needs_src_clk) { 788c2ecf20Sopenharmony_ci ret = clk_enable(info->rtc_src_clk); 798c2ecf20Sopenharmony_ci if (ret) { 808c2ecf20Sopenharmony_ci clk_disable(info->rtc_clk); 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void s3c_rtc_disable_clk(struct s3c_rtc *info) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci if (info->data->needs_src_clk) 908c2ecf20Sopenharmony_ci clk_disable(info->rtc_src_clk); 918c2ecf20Sopenharmony_ci clk_disable(info->rtc_clk); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* IRQ Handlers */ 958c2ecf20Sopenharmony_cistatic irqreturn_t s3c_rtc_tickirq(int irq, void *id) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct s3c_rtc *info = (struct s3c_rtc *)id; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (info->data->irq_handler) 1008c2ecf20Sopenharmony_ci info->data->irq_handler(info, S3C2410_INTP_TIC); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic irqreturn_t s3c_rtc_alarmirq(int irq, void *id) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct s3c_rtc *info = (struct s3c_rtc *)id; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (info->data->irq_handler) 1108c2ecf20Sopenharmony_ci info->data->irq_handler(info, S3C2410_INTP_ALM); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* Update control registers */ 1168c2ecf20Sopenharmony_cistatic int s3c_rtc_setaie(struct device *dev, unsigned int enabled) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 1198c2ecf20Sopenharmony_ci unsigned long flags; 1208c2ecf20Sopenharmony_ci unsigned int tmp; 1218c2ecf20Sopenharmony_ci int ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 1268c2ecf20Sopenharmony_ci if (ret) 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (enabled) 1328c2ecf20Sopenharmony_ci tmp |= S3C2410_RTCALM_ALMEN; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci writeb(tmp, info->base + S3C2410_RTCALM); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->alarm_lock, flags); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (info->alarm_enabled && !enabled) 1398c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 1408c2ecf20Sopenharmony_ci else if (!info->alarm_enabled && enabled) 1418c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci info->alarm_enabled = enabled; 1448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->alarm_lock, flags); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* Set RTC frequency */ 1528c2ecf20Sopenharmony_cistatic int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!is_power_of_2(freq)) 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 1608c2ecf20Sopenharmony_ci if (ret) 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci spin_lock_irq(&info->pie_lock); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (info->data->set_freq) 1658c2ecf20Sopenharmony_ci info->data->set_freq(info, freq); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci spin_unlock_irq(&info->pie_lock); 1688c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* Time read/write */ 1748c2ecf20Sopenharmony_cistatic int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 1778c2ecf20Sopenharmony_ci unsigned int have_retried = 0; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 1818c2ecf20Sopenharmony_ci if (ret) 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciretry_get_time: 1858c2ecf20Sopenharmony_ci rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); 1868c2ecf20Sopenharmony_ci rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); 1878c2ecf20Sopenharmony_ci rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE); 1888c2ecf20Sopenharmony_ci rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON); 1898c2ecf20Sopenharmony_ci rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR); 1908c2ecf20Sopenharmony_ci rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* the only way to work out whether the system was mid-update 1938c2ecf20Sopenharmony_ci * when we read it is to check the second counter, and if it 1948c2ecf20Sopenharmony_ci * is zero, then we re-try the entire read 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (rtc_tm->tm_sec == 0 && !have_retried) { 1988c2ecf20Sopenharmony_ci have_retried = 1; 1998c2ecf20Sopenharmony_ci goto retry_get_time; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); 2038c2ecf20Sopenharmony_ci rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); 2048c2ecf20Sopenharmony_ci rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); 2058c2ecf20Sopenharmony_ci rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); 2068c2ecf20Sopenharmony_ci rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); 2078c2ecf20Sopenharmony_ci rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci rtc_tm->tm_year += 100; 2128c2ecf20Sopenharmony_ci rtc_tm->tm_mon -= 1; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dev_dbg(dev, "read time %ptR\n", rtc_tm); 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 2218c2ecf20Sopenharmony_ci int year = tm->tm_year - 100; 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci dev_dbg(dev, "set time %ptR\n", tm); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* we get around y2k by simply not supporting it */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (year < 0 || year >= 100) { 2298c2ecf20Sopenharmony_ci dev_err(dev, "rtc only supports 100 years\n"); 2308c2ecf20Sopenharmony_ci return -EINVAL; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 2348c2ecf20Sopenharmony_ci if (ret) 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); 2388c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); 2398c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); 2408c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); 2418c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON); 2428c2ecf20Sopenharmony_ci writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 2528c2ecf20Sopenharmony_ci struct rtc_time *alm_tm = &alrm->time; 2538c2ecf20Sopenharmony_ci unsigned int alm_en; 2548c2ecf20Sopenharmony_ci int ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 2578c2ecf20Sopenharmony_ci if (ret) 2588c2ecf20Sopenharmony_ci return ret; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); 2618c2ecf20Sopenharmony_ci alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); 2628c2ecf20Sopenharmony_ci alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); 2638c2ecf20Sopenharmony_ci alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON); 2648c2ecf20Sopenharmony_ci alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE); 2658c2ecf20Sopenharmony_ci alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci alm_en = readb(info->base + S3C2410_RTCALM); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* decode the alarm enable field */ 2768c2ecf20Sopenharmony_ci if (alm_en & S3C2410_RTCALM_SECEN) 2778c2ecf20Sopenharmony_ci alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (alm_en & S3C2410_RTCALM_MINEN) 2808c2ecf20Sopenharmony_ci alm_tm->tm_min = bcd2bin(alm_tm->tm_min); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (alm_en & S3C2410_RTCALM_HOUREN) 2838c2ecf20Sopenharmony_ci alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (alm_en & S3C2410_RTCALM_DAYEN) 2868c2ecf20Sopenharmony_ci alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (alm_en & S3C2410_RTCALM_MONEN) { 2898c2ecf20Sopenharmony_ci alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); 2908c2ecf20Sopenharmony_ci alm_tm->tm_mon -= 1; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (alm_en & S3C2410_RTCALM_YEAREN) 2948c2ecf20Sopenharmony_ci alm_tm->tm_year = bcd2bin(alm_tm->tm_year); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 3028c2ecf20Sopenharmony_ci struct rtc_time *tm = &alrm->time; 3038c2ecf20Sopenharmony_ci unsigned int alrm_en; 3048c2ecf20Sopenharmony_ci int ret; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci return ret; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; 3138c2ecf20Sopenharmony_ci writeb(0x00, info->base + S3C2410_RTCALM); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (tm->tm_sec < 60 && tm->tm_sec >= 0) { 3168c2ecf20Sopenharmony_ci alrm_en |= S3C2410_RTCALM_SECEN; 3178c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (tm->tm_min < 60 && tm->tm_min >= 0) { 3218c2ecf20Sopenharmony_ci alrm_en |= S3C2410_RTCALM_MINEN; 3228c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (tm->tm_hour < 24 && tm->tm_hour >= 0) { 3268c2ecf20Sopenharmony_ci alrm_en |= S3C2410_RTCALM_HOUREN; 3278c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (tm->tm_mon < 12 && tm->tm_mon >= 0) { 3318c2ecf20Sopenharmony_ci alrm_en |= S3C2410_RTCALM_MONEN; 3328c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (tm->tm_mday <= 31 && tm->tm_mday >= 1) { 3368c2ecf20Sopenharmony_ci alrm_en |= S3C2410_RTCALM_DAYEN; 3378c2ecf20Sopenharmony_ci writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci writeb(alrm_en, info->base + S3C2410_RTCALM); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci s3c_rtc_setaie(dev, alrm->enabled); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int s3c_rtc_proc(struct device *dev, struct seq_file *seq) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (info->data->enable_tick) 3618c2ecf20Sopenharmony_ci info->data->enable_tick(info, seq); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct rtc_class_ops s3c_rtcops = { 3698c2ecf20Sopenharmony_ci .read_time = s3c_rtc_gettime, 3708c2ecf20Sopenharmony_ci .set_time = s3c_rtc_settime, 3718c2ecf20Sopenharmony_ci .read_alarm = s3c_rtc_getalarm, 3728c2ecf20Sopenharmony_ci .set_alarm = s3c_rtc_setalarm, 3738c2ecf20Sopenharmony_ci .proc = s3c_rtc_proc, 3748c2ecf20Sopenharmony_ci .alarm_irq_enable = s3c_rtc_setaie, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic void s3c24xx_rtc_enable(struct s3c_rtc *info) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci unsigned int con, tmp; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci con = readw(info->base + S3C2410_RTCCON); 3828c2ecf20Sopenharmony_ci /* re-enable the device, and check it is ok */ 3838c2ecf20Sopenharmony_ci if ((con & S3C2410_RTCCON_RTCEN) == 0) { 3848c2ecf20Sopenharmony_ci dev_info(info->dev, "rtc disabled, re-enabling\n"); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci tmp = readw(info->base + S3C2410_RTCCON); 3878c2ecf20Sopenharmony_ci writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (con & S3C2410_RTCCON_CNTSEL) { 3918c2ecf20Sopenharmony_ci dev_info(info->dev, "removing RTCCON_CNTSEL\n"); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci tmp = readw(info->base + S3C2410_RTCCON); 3948c2ecf20Sopenharmony_ci writew(tmp & ~S3C2410_RTCCON_CNTSEL, 3958c2ecf20Sopenharmony_ci info->base + S3C2410_RTCCON); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (con & S3C2410_RTCCON_CLKRST) { 3998c2ecf20Sopenharmony_ci dev_info(info->dev, "removing RTCCON_CLKRST\n"); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci tmp = readw(info->base + S3C2410_RTCCON); 4028c2ecf20Sopenharmony_ci writew(tmp & ~S3C2410_RTCCON_CLKRST, 4038c2ecf20Sopenharmony_ci info->base + S3C2410_RTCCON); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void s3c24xx_rtc_disable(struct s3c_rtc *info) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned int con; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci con = readw(info->base + S3C2410_RTCCON); 4128c2ecf20Sopenharmony_ci con &= ~S3C2410_RTCCON_RTCEN; 4138c2ecf20Sopenharmony_ci writew(con, info->base + S3C2410_RTCCON); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci con = readb(info->base + S3C2410_TICNT); 4168c2ecf20Sopenharmony_ci con &= ~S3C2410_TICNT_ENABLE; 4178c2ecf20Sopenharmony_ci writeb(con, info->base + S3C2410_TICNT); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void s3c6410_rtc_disable(struct s3c_rtc *info) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci unsigned int con; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci con = readw(info->base + S3C2410_RTCCON); 4258c2ecf20Sopenharmony_ci con &= ~S3C64XX_RTCCON_TICEN; 4268c2ecf20Sopenharmony_ci con &= ~S3C2410_RTCCON_RTCEN; 4278c2ecf20Sopenharmony_ci writew(con, info->base + S3C2410_RTCCON); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int s3c_rtc_remove(struct platform_device *pdev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct s3c_rtc *info = platform_get_drvdata(pdev); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci s3c_rtc_setaie(info->dev, 0); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (info->data->needs_src_clk) 4378c2ecf20Sopenharmony_ci clk_unprepare(info->rtc_src_clk); 4388c2ecf20Sopenharmony_ci clk_unprepare(info->rtc_clk); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int s3c_rtc_probe(struct platform_device *pdev) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct s3c_rtc *info = NULL; 4468c2ecf20Sopenharmony_ci struct rtc_time rtc_tm; 4478c2ecf20Sopenharmony_ci int ret; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 4508c2ecf20Sopenharmony_ci if (!info) 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* find the IRQs */ 4548c2ecf20Sopenharmony_ci info->irq_tick = platform_get_irq(pdev, 1); 4558c2ecf20Sopenharmony_ci if (info->irq_tick < 0) 4568c2ecf20Sopenharmony_ci return info->irq_tick; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 4598c2ecf20Sopenharmony_ci info->data = of_device_get_match_data(&pdev->dev); 4608c2ecf20Sopenharmony_ci if (!info->data) { 4618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci spin_lock_init(&info->pie_lock); 4658c2ecf20Sopenharmony_ci spin_lock_init(&info->alarm_lock); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci info->irq_alarm = platform_get_irq(pdev, 0); 4708c2ecf20Sopenharmony_ci if (info->irq_alarm < 0) 4718c2ecf20Sopenharmony_ci return info->irq_alarm; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", 4748c2ecf20Sopenharmony_ci info->irq_tick, info->irq_alarm); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* get the memory region */ 4778c2ecf20Sopenharmony_ci info->base = devm_platform_ioremap_resource(pdev, 0); 4788c2ecf20Sopenharmony_ci if (IS_ERR(info->base)) 4798c2ecf20Sopenharmony_ci return PTR_ERR(info->base); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); 4828c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc_clk)) { 4838c2ecf20Sopenharmony_ci ret = PTR_ERR(info->rtc_clk); 4848c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 4858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to find rtc clock\n"); 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n"); 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci ret = clk_prepare_enable(info->rtc_clk); 4918c2ecf20Sopenharmony_ci if (ret) 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (info->data->needs_src_clk) { 4958c2ecf20Sopenharmony_ci info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); 4968c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc_src_clk)) { 4978c2ecf20Sopenharmony_ci ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk), 4988c2ecf20Sopenharmony_ci "failed to find rtc source clock\n"); 4998c2ecf20Sopenharmony_ci goto err_src_clk; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci ret = clk_prepare_enable(info->rtc_src_clk); 5028c2ecf20Sopenharmony_ci if (ret) 5038c2ecf20Sopenharmony_ci goto err_src_clk; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* check to see if everything is setup correctly */ 5078c2ecf20Sopenharmony_ci if (info->data->enable) 5088c2ecf20Sopenharmony_ci info->data->enable(info); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", 5118c2ecf20Sopenharmony_ci readw(info->base + S3C2410_RTCCON)); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Check RTC Time */ 5168c2ecf20Sopenharmony_ci if (s3c_rtc_gettime(&pdev->dev, &rtc_tm)) { 5178c2ecf20Sopenharmony_ci rtc_tm.tm_year = 100; 5188c2ecf20Sopenharmony_ci rtc_tm.tm_mon = 0; 5198c2ecf20Sopenharmony_ci rtc_tm.tm_mday = 1; 5208c2ecf20Sopenharmony_ci rtc_tm.tm_hour = 0; 5218c2ecf20Sopenharmony_ci rtc_tm.tm_min = 0; 5228c2ecf20Sopenharmony_ci rtc_tm.tm_sec = 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci s3c_rtc_settime(&pdev->dev, &rtc_tm); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* register RTC and exit */ 5308c2ecf20Sopenharmony_ci info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, 5318c2ecf20Sopenharmony_ci THIS_MODULE); 5328c2ecf20Sopenharmony_ci if (IS_ERR(info->rtc)) { 5338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot attach rtc\n"); 5348c2ecf20Sopenharmony_ci ret = PTR_ERR(info->rtc); 5358c2ecf20Sopenharmony_ci goto err_nortc; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, 5398c2ecf20Sopenharmony_ci 0, "s3c2410-rtc alarm", info); 5408c2ecf20Sopenharmony_ci if (ret) { 5418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); 5428c2ecf20Sopenharmony_ci goto err_nortc; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq, 5468c2ecf20Sopenharmony_ci 0, "s3c2410-rtc tick", info); 5478c2ecf20Sopenharmony_ci if (ret) { 5488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret); 5498c2ecf20Sopenharmony_ci goto err_nortc; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (info->data->select_tick_clk) 5538c2ecf20Sopenharmony_ci info->data->select_tick_clk(info); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci s3c_rtc_setfreq(info, 1); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cierr_nortc: 5628c2ecf20Sopenharmony_ci if (info->data->disable) 5638c2ecf20Sopenharmony_ci info->data->disable(info); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (info->data->needs_src_clk) 5668c2ecf20Sopenharmony_ci clk_disable_unprepare(info->rtc_src_clk); 5678c2ecf20Sopenharmony_cierr_src_clk: 5688c2ecf20Sopenharmony_ci clk_disable_unprepare(info->rtc_clk); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return ret; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int s3c_rtc_suspend(struct device *dev) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 5788c2ecf20Sopenharmony_ci int ret; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ret = s3c_rtc_enable_clk(info); 5818c2ecf20Sopenharmony_ci if (ret) 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* save TICNT for anyone using periodic interrupts */ 5858c2ecf20Sopenharmony_ci if (info->data->save_tick_cnt) 5868c2ecf20Sopenharmony_ci info->data->save_tick_cnt(info); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (info->data->disable) 5898c2ecf20Sopenharmony_ci info->data->disable(info); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (device_may_wakeup(dev) && !info->wake_en) { 5928c2ecf20Sopenharmony_ci if (enable_irq_wake(info->irq_alarm) == 0) 5938c2ecf20Sopenharmony_ci info->wake_en = true; 5948c2ecf20Sopenharmony_ci else 5958c2ecf20Sopenharmony_ci dev_err(dev, "enable_irq_wake failed\n"); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int s3c_rtc_resume(struct device *dev) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct s3c_rtc *info = dev_get_drvdata(dev); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (info->data->enable) 6068c2ecf20Sopenharmony_ci info->data->enable(info); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (info->data->restore_tick_cnt) 6098c2ecf20Sopenharmony_ci info->data->restore_tick_cnt(info); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci s3c_rtc_disable_clk(info); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (device_may_wakeup(dev) && info->wake_en) { 6148c2ecf20Sopenharmony_ci disable_irq_wake(info->irq_alarm); 6158c2ecf20Sopenharmony_ci info->wake_en = false; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci#endif 6218c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); 6318c2ecf20Sopenharmony_ci writeb(mask, info->base + S3C2410_INTP); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci unsigned int tmp = 0; 6378c2ecf20Sopenharmony_ci int val; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci tmp = readb(info->base + S3C2410_TICNT); 6408c2ecf20Sopenharmony_ci tmp &= S3C2410_TICNT_ENABLE; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci val = (info->rtc->max_user_freq / freq) - 1; 6438c2ecf20Sopenharmony_ci tmp |= val; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci writel(tmp, info->base + S3C2410_TICNT); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci unsigned int tmp = 0; 6518c2ecf20Sopenharmony_ci int val; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci tmp = readb(info->base + S3C2410_TICNT); 6548c2ecf20Sopenharmony_ci tmp &= S3C2410_TICNT_ENABLE; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci val = (info->rtc->max_user_freq / freq) - 1; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci tmp |= S3C2443_TICNT_PART(val); 6598c2ecf20Sopenharmony_ci writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci writel(tmp, info->base + S3C2410_TICNT); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci unsigned int tmp = 0; 6698c2ecf20Sopenharmony_ci int val; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci tmp = readb(info->base + S3C2410_TICNT); 6728c2ecf20Sopenharmony_ci tmp &= S3C2410_TICNT_ENABLE; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci val = (info->rtc->max_user_freq / freq) - 1; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci tmp |= S3C2443_TICNT_PART(val); 6778c2ecf20Sopenharmony_ci writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci writel(tmp, info->base + S3C2410_TICNT); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci int val; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci val = (info->rtc->max_user_freq / freq) - 1; 6878c2ecf20Sopenharmony_ci writel(val, info->base + S3C2410_TICNT); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci unsigned int ticnt; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci ticnt = readb(info->base + S3C2410_TICNT); 6958c2ecf20Sopenharmony_ci ticnt &= S3C2410_TICNT_ENABLE; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci unsigned int con; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci con = readw(info->base + S3C2410_RTCCON); 7058c2ecf20Sopenharmony_ci con |= S3C2443_RTCCON_TICSEL; 7068c2ecf20Sopenharmony_ci writew(con, info->base + S3C2410_RTCCON); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci unsigned int ticnt; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ticnt = readw(info->base + S3C2410_RTCCON); 7148c2ecf20Sopenharmony_ci ticnt &= S3C64XX_RTCCON_TICEN; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci info->ticnt_save = readb(info->base + S3C2410_TICNT); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci writeb(info->ticnt_save, info->base + S3C2410_TICNT); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); 7328c2ecf20Sopenharmony_ci info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; 7338c2ecf20Sopenharmony_ci info->ticnt_save = readl(info->base + S3C2410_TICNT); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci unsigned int con; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci writel(info->ticnt_save, info->base + S3C2410_TICNT); 7418c2ecf20Sopenharmony_ci if (info->ticnt_en_save) { 7428c2ecf20Sopenharmony_ci con = readw(info->base + S3C2410_RTCCON); 7438c2ecf20Sopenharmony_ci writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON); 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic struct s3c_rtc_data const s3c2410_rtc_data = { 7488c2ecf20Sopenharmony_ci .max_user_freq = 128, 7498c2ecf20Sopenharmony_ci .irq_handler = s3c24xx_rtc_irq, 7508c2ecf20Sopenharmony_ci .set_freq = s3c2410_rtc_setfreq, 7518c2ecf20Sopenharmony_ci .enable_tick = s3c24xx_rtc_enable_tick, 7528c2ecf20Sopenharmony_ci .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, 7538c2ecf20Sopenharmony_ci .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, 7548c2ecf20Sopenharmony_ci .enable = s3c24xx_rtc_enable, 7558c2ecf20Sopenharmony_ci .disable = s3c24xx_rtc_disable, 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic struct s3c_rtc_data const s3c2416_rtc_data = { 7598c2ecf20Sopenharmony_ci .max_user_freq = 32768, 7608c2ecf20Sopenharmony_ci .irq_handler = s3c24xx_rtc_irq, 7618c2ecf20Sopenharmony_ci .set_freq = s3c2416_rtc_setfreq, 7628c2ecf20Sopenharmony_ci .enable_tick = s3c24xx_rtc_enable_tick, 7638c2ecf20Sopenharmony_ci .select_tick_clk = s3c2416_rtc_select_tick_clk, 7648c2ecf20Sopenharmony_ci .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, 7658c2ecf20Sopenharmony_ci .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, 7668c2ecf20Sopenharmony_ci .enable = s3c24xx_rtc_enable, 7678c2ecf20Sopenharmony_ci .disable = s3c24xx_rtc_disable, 7688c2ecf20Sopenharmony_ci}; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic struct s3c_rtc_data const s3c2443_rtc_data = { 7718c2ecf20Sopenharmony_ci .max_user_freq = 32768, 7728c2ecf20Sopenharmony_ci .irq_handler = s3c24xx_rtc_irq, 7738c2ecf20Sopenharmony_ci .set_freq = s3c2443_rtc_setfreq, 7748c2ecf20Sopenharmony_ci .enable_tick = s3c24xx_rtc_enable_tick, 7758c2ecf20Sopenharmony_ci .select_tick_clk = s3c2416_rtc_select_tick_clk, 7768c2ecf20Sopenharmony_ci .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, 7778c2ecf20Sopenharmony_ci .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, 7788c2ecf20Sopenharmony_ci .enable = s3c24xx_rtc_enable, 7798c2ecf20Sopenharmony_ci .disable = s3c24xx_rtc_disable, 7808c2ecf20Sopenharmony_ci}; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic struct s3c_rtc_data const s3c6410_rtc_data = { 7838c2ecf20Sopenharmony_ci .max_user_freq = 32768, 7848c2ecf20Sopenharmony_ci .needs_src_clk = true, 7858c2ecf20Sopenharmony_ci .irq_handler = s3c6410_rtc_irq, 7868c2ecf20Sopenharmony_ci .set_freq = s3c6410_rtc_setfreq, 7878c2ecf20Sopenharmony_ci .enable_tick = s3c6410_rtc_enable_tick, 7888c2ecf20Sopenharmony_ci .save_tick_cnt = s3c6410_rtc_save_tick_cnt, 7898c2ecf20Sopenharmony_ci .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, 7908c2ecf20Sopenharmony_ci .enable = s3c24xx_rtc_enable, 7918c2ecf20Sopenharmony_ci .disable = s3c6410_rtc_disable, 7928c2ecf20Sopenharmony_ci}; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic const struct of_device_id s3c_rtc_dt_match[] = { 7958c2ecf20Sopenharmony_ci { 7968c2ecf20Sopenharmony_ci .compatible = "samsung,s3c2410-rtc", 7978c2ecf20Sopenharmony_ci .data = &s3c2410_rtc_data, 7988c2ecf20Sopenharmony_ci }, { 7998c2ecf20Sopenharmony_ci .compatible = "samsung,s3c2416-rtc", 8008c2ecf20Sopenharmony_ci .data = &s3c2416_rtc_data, 8018c2ecf20Sopenharmony_ci }, { 8028c2ecf20Sopenharmony_ci .compatible = "samsung,s3c2443-rtc", 8038c2ecf20Sopenharmony_ci .data = &s3c2443_rtc_data, 8048c2ecf20Sopenharmony_ci }, { 8058c2ecf20Sopenharmony_ci .compatible = "samsung,s3c6410-rtc", 8068c2ecf20Sopenharmony_ci .data = &s3c6410_rtc_data, 8078c2ecf20Sopenharmony_ci }, { 8088c2ecf20Sopenharmony_ci .compatible = "samsung,exynos3250-rtc", 8098c2ecf20Sopenharmony_ci .data = &s3c6410_rtc_data, 8108c2ecf20Sopenharmony_ci }, 8118c2ecf20Sopenharmony_ci { /* sentinel */ }, 8128c2ecf20Sopenharmony_ci}; 8138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic struct platform_driver s3c_rtc_driver = { 8168c2ecf20Sopenharmony_ci .probe = s3c_rtc_probe, 8178c2ecf20Sopenharmony_ci .remove = s3c_rtc_remove, 8188c2ecf20Sopenharmony_ci .driver = { 8198c2ecf20Sopenharmony_ci .name = "s3c-rtc", 8208c2ecf20Sopenharmony_ci .pm = &s3c_rtc_pm_ops, 8218c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(s3c_rtc_dt_match), 8228c2ecf20Sopenharmony_ci }, 8238c2ecf20Sopenharmony_ci}; 8248c2ecf20Sopenharmony_cimodule_platform_driver(s3c_rtc_driver); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S3C RTC Driver"); 8278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 8288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8298c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c2410-rtc"); 830