18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * EPSON TOYOCOM RTC-7301SF/DG Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on rtc-rp5c01.c 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Datasheet: http://www5.epsondevice.com/en/products/parallel/rtc7301sf.html 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/rtc.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DRV_NAME "rtc-r7301" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define RTC7301_1_SEC 0x0 /* Bank 0 and Band 1 */ 248c2ecf20Sopenharmony_ci#define RTC7301_10_SEC 0x1 /* Bank 0 and Band 1 */ 258c2ecf20Sopenharmony_ci#define RTC7301_AE BIT(3) 268c2ecf20Sopenharmony_ci#define RTC7301_1_MIN 0x2 /* Bank 0 and Band 1 */ 278c2ecf20Sopenharmony_ci#define RTC7301_10_MIN 0x3 /* Bank 0 and Band 1 */ 288c2ecf20Sopenharmony_ci#define RTC7301_1_HOUR 0x4 /* Bank 0 and Band 1 */ 298c2ecf20Sopenharmony_ci#define RTC7301_10_HOUR 0x5 /* Bank 0 and Band 1 */ 308c2ecf20Sopenharmony_ci#define RTC7301_DAY_OF_WEEK 0x6 /* Bank 0 and Band 1 */ 318c2ecf20Sopenharmony_ci#define RTC7301_1_DAY 0x7 /* Bank 0 and Band 1 */ 328c2ecf20Sopenharmony_ci#define RTC7301_10_DAY 0x8 /* Bank 0 and Band 1 */ 338c2ecf20Sopenharmony_ci#define RTC7301_1_MONTH 0x9 /* Bank 0 */ 348c2ecf20Sopenharmony_ci#define RTC7301_10_MONTH 0xa /* Bank 0 */ 358c2ecf20Sopenharmony_ci#define RTC7301_1_YEAR 0xb /* Bank 0 */ 368c2ecf20Sopenharmony_ci#define RTC7301_10_YEAR 0xc /* Bank 0 */ 378c2ecf20Sopenharmony_ci#define RTC7301_100_YEAR 0xd /* Bank 0 */ 388c2ecf20Sopenharmony_ci#define RTC7301_1000_YEAR 0xe /* Bank 0 */ 398c2ecf20Sopenharmony_ci#define RTC7301_ALARM_CONTROL 0xe /* Bank 1 */ 408c2ecf20Sopenharmony_ci#define RTC7301_ALARM_CONTROL_AIE BIT(0) 418c2ecf20Sopenharmony_ci#define RTC7301_ALARM_CONTROL_AF BIT(1) 428c2ecf20Sopenharmony_ci#define RTC7301_TIMER_CONTROL 0xe /* Bank 2 */ 438c2ecf20Sopenharmony_ci#define RTC7301_TIMER_CONTROL_TIE BIT(0) 448c2ecf20Sopenharmony_ci#define RTC7301_TIMER_CONTROL_TF BIT(1) 458c2ecf20Sopenharmony_ci#define RTC7301_CONTROL 0xf /* All banks */ 468c2ecf20Sopenharmony_ci#define RTC7301_CONTROL_BUSY BIT(0) 478c2ecf20Sopenharmony_ci#define RTC7301_CONTROL_STOP BIT(1) 488c2ecf20Sopenharmony_ci#define RTC7301_CONTROL_BANK_SEL_0 BIT(2) 498c2ecf20Sopenharmony_ci#define RTC7301_CONTROL_BANK_SEL_1 BIT(3) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct rtc7301_priv { 528c2ecf20Sopenharmony_ci struct regmap *regmap; 538c2ecf20Sopenharmony_ci int irq; 548c2ecf20Sopenharmony_ci spinlock_t lock; 558c2ecf20Sopenharmony_ci u8 bank; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic const struct regmap_config rtc7301_regmap_config = { 598c2ecf20Sopenharmony_ci .reg_bits = 32, 608c2ecf20Sopenharmony_ci .val_bits = 8, 618c2ecf20Sopenharmony_ci .reg_stride = 4, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int reg_stride = regmap_get_reg_stride(priv->regmap); 678c2ecf20Sopenharmony_ci unsigned int val; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci regmap_read(priv->regmap, reg_stride * reg, &val); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return val & 0xf; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void rtc7301_write(struct rtc7301_priv *priv, u8 val, unsigned int reg) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci int reg_stride = regmap_get_reg_stride(priv->regmap); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci regmap_write(priv->regmap, reg_stride * reg, val); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void rtc7301_update_bits(struct rtc7301_priv *priv, unsigned int reg, 828c2ecf20Sopenharmony_ci u8 mask, u8 val) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int reg_stride = regmap_get_reg_stride(priv->regmap); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci regmap_update_bits(priv->regmap, reg_stride * reg, mask, val); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int rtc7301_wait_while_busy(struct rtc7301_priv *priv) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int retries = 100; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci while (retries-- > 0) { 948c2ecf20Sopenharmony_ci u8 val; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci val = rtc7301_read(priv, RTC7301_CONTROL); 978c2ecf20Sopenharmony_ci if (!(val & RTC7301_CONTROL_BUSY)) 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci udelay(300); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void rtc7301_stop(struct rtc7301_priv *priv) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, 1098c2ecf20Sopenharmony_ci RTC7301_CONTROL_STOP); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void rtc7301_start(struct rtc7301_priv *priv) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, 0); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void rtc7301_select_bank(struct rtc7301_priv *priv, u8 bank) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u8 val = 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (bank == priv->bank) 1228c2ecf20Sopenharmony_ci return; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (bank & BIT(0)) 1258c2ecf20Sopenharmony_ci val |= RTC7301_CONTROL_BANK_SEL_0; 1268c2ecf20Sopenharmony_ci if (bank & BIT(1)) 1278c2ecf20Sopenharmony_ci val |= RTC7301_CONTROL_BANK_SEL_1; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci rtc7301_update_bits(priv, RTC7301_CONTROL, 1308c2ecf20Sopenharmony_ci RTC7301_CONTROL_BANK_SEL_0 | 1318c2ecf20Sopenharmony_ci RTC7301_CONTROL_BANK_SEL_1, val); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci priv->bank = bank; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void rtc7301_get_time(struct rtc7301_priv *priv, struct rtc_time *tm, 1378c2ecf20Sopenharmony_ci bool alarm) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci int year; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci tm->tm_sec = rtc7301_read(priv, RTC7301_1_SEC); 1428c2ecf20Sopenharmony_ci tm->tm_sec += (rtc7301_read(priv, RTC7301_10_SEC) & ~RTC7301_AE) * 10; 1438c2ecf20Sopenharmony_ci tm->tm_min = rtc7301_read(priv, RTC7301_1_MIN); 1448c2ecf20Sopenharmony_ci tm->tm_min += (rtc7301_read(priv, RTC7301_10_MIN) & ~RTC7301_AE) * 10; 1458c2ecf20Sopenharmony_ci tm->tm_hour = rtc7301_read(priv, RTC7301_1_HOUR); 1468c2ecf20Sopenharmony_ci tm->tm_hour += (rtc7301_read(priv, RTC7301_10_HOUR) & ~RTC7301_AE) * 10; 1478c2ecf20Sopenharmony_ci tm->tm_mday = rtc7301_read(priv, RTC7301_1_DAY); 1488c2ecf20Sopenharmony_ci tm->tm_mday += (rtc7301_read(priv, RTC7301_10_DAY) & ~RTC7301_AE) * 10; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (alarm) { 1518c2ecf20Sopenharmony_ci tm->tm_wday = -1; 1528c2ecf20Sopenharmony_ci tm->tm_mon = -1; 1538c2ecf20Sopenharmony_ci tm->tm_year = -1; 1548c2ecf20Sopenharmony_ci tm->tm_yday = -1; 1558c2ecf20Sopenharmony_ci tm->tm_isdst = -1; 1568c2ecf20Sopenharmony_ci return; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci tm->tm_wday = (rtc7301_read(priv, RTC7301_DAY_OF_WEEK) & ~RTC7301_AE); 1608c2ecf20Sopenharmony_ci tm->tm_mon = rtc7301_read(priv, RTC7301_10_MONTH) * 10 + 1618c2ecf20Sopenharmony_ci rtc7301_read(priv, RTC7301_1_MONTH) - 1; 1628c2ecf20Sopenharmony_ci year = rtc7301_read(priv, RTC7301_1000_YEAR) * 1000 + 1638c2ecf20Sopenharmony_ci rtc7301_read(priv, RTC7301_100_YEAR) * 100 + 1648c2ecf20Sopenharmony_ci rtc7301_read(priv, RTC7301_10_YEAR) * 10 + 1658c2ecf20Sopenharmony_ci rtc7301_read(priv, RTC7301_1_YEAR); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci tm->tm_year = year - 1900; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void rtc7301_write_time(struct rtc7301_priv *priv, struct rtc_time *tm, 1718c2ecf20Sopenharmony_ci bool alarm) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci int year; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_sec % 10, RTC7301_1_SEC); 1768c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_sec / 10, RTC7301_10_SEC); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_min % 10, RTC7301_1_MIN); 1798c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_min / 10, RTC7301_10_MIN); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_hour % 10, RTC7301_1_HOUR); 1828c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_hour / 10, RTC7301_10_HOUR); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_mday % 10, RTC7301_1_DAY); 1858c2ecf20Sopenharmony_ci rtc7301_write(priv, tm->tm_mday / 10, RTC7301_10_DAY); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Don't care for alarm register */ 1888c2ecf20Sopenharmony_ci rtc7301_write(priv, alarm ? RTC7301_AE : tm->tm_wday, 1898c2ecf20Sopenharmony_ci RTC7301_DAY_OF_WEEK); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (alarm) 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rtc7301_write(priv, (tm->tm_mon + 1) % 10, RTC7301_1_MONTH); 1958c2ecf20Sopenharmony_ci rtc7301_write(priv, (tm->tm_mon + 1) / 10, RTC7301_10_MONTH); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci year = tm->tm_year + 1900; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci rtc7301_write(priv, year % 10, RTC7301_1_YEAR); 2008c2ecf20Sopenharmony_ci rtc7301_write(priv, (year / 10) % 10, RTC7301_10_YEAR); 2018c2ecf20Sopenharmony_ci rtc7301_write(priv, (year / 100) % 10, RTC7301_100_YEAR); 2028c2ecf20Sopenharmony_ci rtc7301_write(priv, year / 1000, RTC7301_1000_YEAR); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void rtc7301_alarm_irq(struct rtc7301_priv *priv, unsigned int enabled) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci rtc7301_update_bits(priv, RTC7301_ALARM_CONTROL, 2088c2ecf20Sopenharmony_ci RTC7301_ALARM_CONTROL_AF | 2098c2ecf20Sopenharmony_ci RTC7301_ALARM_CONTROL_AIE, 2108c2ecf20Sopenharmony_ci enabled ? RTC7301_ALARM_CONTROL_AIE : 0); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int rtc7301_read_time(struct device *dev, struct rtc_time *tm) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 2168c2ecf20Sopenharmony_ci unsigned long flags; 2178c2ecf20Sopenharmony_ci int err; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 0); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci err = rtc7301_wait_while_busy(priv); 2248c2ecf20Sopenharmony_ci if (!err) 2258c2ecf20Sopenharmony_ci rtc7301_get_time(priv, tm, false); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int rtc7301_set_time(struct device *dev, struct rtc_time *tm) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 2358c2ecf20Sopenharmony_ci unsigned long flags; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci rtc7301_stop(priv); 2408c2ecf20Sopenharmony_ci udelay(300); 2418c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 0); 2428c2ecf20Sopenharmony_ci rtc7301_write_time(priv, tm, false); 2438c2ecf20Sopenharmony_ci rtc7301_start(priv); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int rtc7301_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 2538c2ecf20Sopenharmony_ci unsigned long flags; 2548c2ecf20Sopenharmony_ci u8 alrm_ctrl; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (priv->irq <= 0) 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 1); 2628c2ecf20Sopenharmony_ci rtc7301_get_time(priv, &alarm->time, true); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci alarm->enabled = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AIE); 2678c2ecf20Sopenharmony_ci alarm->pending = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AF); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int rtc7301_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 2778c2ecf20Sopenharmony_ci unsigned long flags; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (priv->irq <= 0) 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 1); 2858c2ecf20Sopenharmony_ci rtc7301_write_time(priv, &alarm->time, true); 2868c2ecf20Sopenharmony_ci rtc7301_alarm_irq(priv, alarm->enabled); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int rtc7301_alarm_irq_enable(struct device *dev, unsigned int enabled) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 2968c2ecf20Sopenharmony_ci unsigned long flags; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (priv->irq <= 0) 2998c2ecf20Sopenharmony_ci return -EINVAL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 1); 3048c2ecf20Sopenharmony_ci rtc7301_alarm_irq(priv, enabled); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic const struct rtc_class_ops rtc7301_rtc_ops = { 3128c2ecf20Sopenharmony_ci .read_time = rtc7301_read_time, 3138c2ecf20Sopenharmony_ci .set_time = rtc7301_set_time, 3148c2ecf20Sopenharmony_ci .read_alarm = rtc7301_read_alarm, 3158c2ecf20Sopenharmony_ci .set_alarm = rtc7301_set_alarm, 3168c2ecf20Sopenharmony_ci .alarm_irq_enable = rtc7301_alarm_irq_enable, 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic irqreturn_t rtc7301_irq_handler(int irq, void *dev_id) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct rtc_device *rtc = dev_id; 3228c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(rtc->dev.parent); 3238c2ecf20Sopenharmony_ci unsigned long flags; 3248c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 3258c2ecf20Sopenharmony_ci u8 alrm_ctrl; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 1); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); 3328c2ecf20Sopenharmony_ci if (alrm_ctrl & RTC7301_ALARM_CONTROL_AF) { 3338c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 3348c2ecf20Sopenharmony_ci rtc7301_alarm_irq(priv, false); 3358c2ecf20Sopenharmony_ci rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void rtc7301_init(struct rtc7301_priv *priv) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci unsigned long flags; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci rtc7301_select_bank(priv, 2); 3508c2ecf20Sopenharmony_ci rtc7301_write(priv, 0, RTC7301_TIMER_CONTROL); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int __init rtc7301_rtc_probe(struct platform_device *dev) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci void __iomem *regs; 3588c2ecf20Sopenharmony_ci struct rtc7301_priv *priv; 3598c2ecf20Sopenharmony_ci struct rtc_device *rtc; 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); 3638c2ecf20Sopenharmony_ci if (!priv) 3648c2ecf20Sopenharmony_ci return -ENOMEM; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci regs = devm_platform_ioremap_resource(dev, 0); 3678c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 3688c2ecf20Sopenharmony_ci return PTR_ERR(regs); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, 3718c2ecf20Sopenharmony_ci &rtc7301_regmap_config); 3728c2ecf20Sopenharmony_ci if (IS_ERR(priv->regmap)) 3738c2ecf20Sopenharmony_ci return PTR_ERR(priv->regmap); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci priv->irq = platform_get_irq(dev, 0); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 3788c2ecf20Sopenharmony_ci priv->bank = -1; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci rtc7301_init(priv); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci platform_set_drvdata(dev, priv); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci rtc = devm_rtc_device_register(&dev->dev, DRV_NAME, &rtc7301_rtc_ops, 3858c2ecf20Sopenharmony_ci THIS_MODULE); 3868c2ecf20Sopenharmony_ci if (IS_ERR(rtc)) 3878c2ecf20Sopenharmony_ci return PTR_ERR(rtc); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (priv->irq > 0) { 3908c2ecf20Sopenharmony_ci ret = devm_request_irq(&dev->dev, priv->irq, 3918c2ecf20Sopenharmony_ci rtc7301_irq_handler, IRQF_SHARED, 3928c2ecf20Sopenharmony_ci dev_name(&dev->dev), rtc); 3938c2ecf20Sopenharmony_ci if (ret) { 3948c2ecf20Sopenharmony_ci priv->irq = 0; 3958c2ecf20Sopenharmony_ci dev_err(&dev->dev, "unable to request IRQ\n"); 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci device_set_wakeup_capable(&dev->dev, true); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int rtc7301_suspend(struct device *dev) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 4118c2ecf20Sopenharmony_ci enable_irq_wake(priv->irq); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int rtc7301_resume(struct device *dev) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct rtc7301_priv *priv = dev_get_drvdata(dev); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 4218c2ecf20Sopenharmony_ci disable_irq_wake(priv->irq); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci#endif 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(rtc7301_pm_ops, rtc7301_suspend, rtc7301_resume); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic const struct of_device_id rtc7301_dt_match[] = { 4318c2ecf20Sopenharmony_ci { .compatible = "epson,rtc7301sf" }, 4328c2ecf20Sopenharmony_ci { .compatible = "epson,rtc7301dg" }, 4338c2ecf20Sopenharmony_ci {} 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rtc7301_dt_match); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic struct platform_driver rtc7301_rtc_driver = { 4388c2ecf20Sopenharmony_ci .driver = { 4398c2ecf20Sopenharmony_ci .name = DRV_NAME, 4408c2ecf20Sopenharmony_ci .of_match_table = rtc7301_dt_match, 4418c2ecf20Sopenharmony_ci .pm = &rtc7301_pm_ops, 4428c2ecf20Sopenharmony_ci }, 4438c2ecf20Sopenharmony_ci}; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cimodule_platform_driver_probe(rtc7301_rtc_driver, rtc7301_rtc_probe); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); 4488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EPSON TOYOCOM RTC-7301SF/DG Driver"); 4508c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rtc-r7301"); 451