1*5bccae6eSSangbeom Kim /* 2*5bccae6eSSangbeom Kim * Copyright (c) 2013 Samsung Electronics Co., Ltd 3*5bccae6eSSangbeom Kim * http://www.samsung.com 4*5bccae6eSSangbeom Kim * 5*5bccae6eSSangbeom Kim * Copyright (C) 2013 Google, Inc 6*5bccae6eSSangbeom Kim * 7*5bccae6eSSangbeom Kim * This program is free software; you can redistribute it and/or modify 8*5bccae6eSSangbeom Kim * it under the terms of the GNU General Public License as published by 9*5bccae6eSSangbeom Kim * the Free Software Foundation; either version 2 of the License, or 10*5bccae6eSSangbeom Kim * (at your option) any later version. 11*5bccae6eSSangbeom Kim * 12*5bccae6eSSangbeom Kim * This program is distributed in the hope that it will be useful, 13*5bccae6eSSangbeom Kim * but WITHOUT ANY WARRANTY; without even the implied warranty of 14*5bccae6eSSangbeom Kim * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15*5bccae6eSSangbeom Kim * GNU General Public License for more details. 16*5bccae6eSSangbeom Kim */ 17*5bccae6eSSangbeom Kim 18*5bccae6eSSangbeom Kim #include <linux/module.h> 19*5bccae6eSSangbeom Kim #include <linux/i2c.h> 20*5bccae6eSSangbeom Kim #include <linux/slab.h> 21*5bccae6eSSangbeom Kim #include <linux/bcd.h> 22*5bccae6eSSangbeom Kim #include <linux/bitops.h> 23*5bccae6eSSangbeom Kim #include <linux/regmap.h> 24*5bccae6eSSangbeom Kim #include <linux/rtc.h> 25*5bccae6eSSangbeom Kim #include <linux/delay.h> 26*5bccae6eSSangbeom Kim #include <linux/platform_device.h> 27*5bccae6eSSangbeom Kim #include <linux/mfd/samsung/core.h> 28*5bccae6eSSangbeom Kim #include <linux/mfd/samsung/irq.h> 29*5bccae6eSSangbeom Kim #include <linux/mfd/samsung/rtc.h> 30*5bccae6eSSangbeom Kim 31*5bccae6eSSangbeom Kim struct s5m_rtc_info { 32*5bccae6eSSangbeom Kim struct device *dev; 33*5bccae6eSSangbeom Kim struct sec_pmic_dev *s5m87xx; 34*5bccae6eSSangbeom Kim struct regmap *rtc; 35*5bccae6eSSangbeom Kim struct rtc_device *rtc_dev; 36*5bccae6eSSangbeom Kim int irq; 37*5bccae6eSSangbeom Kim int device_type; 38*5bccae6eSSangbeom Kim int rtc_24hr_mode; 39*5bccae6eSSangbeom Kim bool wtsr_smpl; 40*5bccae6eSSangbeom Kim }; 41*5bccae6eSSangbeom Kim 42*5bccae6eSSangbeom Kim static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm, 43*5bccae6eSSangbeom Kim int rtc_24hr_mode) 44*5bccae6eSSangbeom Kim { 45*5bccae6eSSangbeom Kim tm->tm_sec = data[RTC_SEC] & 0x7f; 46*5bccae6eSSangbeom Kim tm->tm_min = data[RTC_MIN] & 0x7f; 47*5bccae6eSSangbeom Kim if (rtc_24hr_mode) { 48*5bccae6eSSangbeom Kim tm->tm_hour = data[RTC_HOUR] & 0x1f; 49*5bccae6eSSangbeom Kim } else { 50*5bccae6eSSangbeom Kim tm->tm_hour = data[RTC_HOUR] & 0x0f; 51*5bccae6eSSangbeom Kim if (data[RTC_HOUR] & HOUR_PM_MASK) 52*5bccae6eSSangbeom Kim tm->tm_hour += 12; 53*5bccae6eSSangbeom Kim } 54*5bccae6eSSangbeom Kim 55*5bccae6eSSangbeom Kim tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f); 56*5bccae6eSSangbeom Kim tm->tm_mday = data[RTC_DATE] & 0x1f; 57*5bccae6eSSangbeom Kim tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; 58*5bccae6eSSangbeom Kim tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100; 59*5bccae6eSSangbeom Kim tm->tm_yday = 0; 60*5bccae6eSSangbeom Kim tm->tm_isdst = 0; 61*5bccae6eSSangbeom Kim } 62*5bccae6eSSangbeom Kim 63*5bccae6eSSangbeom Kim static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data) 64*5bccae6eSSangbeom Kim { 65*5bccae6eSSangbeom Kim data[RTC_SEC] = tm->tm_sec; 66*5bccae6eSSangbeom Kim data[RTC_MIN] = tm->tm_min; 67*5bccae6eSSangbeom Kim 68*5bccae6eSSangbeom Kim if (tm->tm_hour >= 12) 69*5bccae6eSSangbeom Kim data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK; 70*5bccae6eSSangbeom Kim else 71*5bccae6eSSangbeom Kim data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK; 72*5bccae6eSSangbeom Kim 73*5bccae6eSSangbeom Kim data[RTC_WEEKDAY] = 1 << tm->tm_wday; 74*5bccae6eSSangbeom Kim data[RTC_DATE] = tm->tm_mday; 75*5bccae6eSSangbeom Kim data[RTC_MONTH] = tm->tm_mon + 1; 76*5bccae6eSSangbeom Kim data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; 77*5bccae6eSSangbeom Kim 78*5bccae6eSSangbeom Kim if (tm->tm_year < 100) { 79*5bccae6eSSangbeom Kim pr_err("s5m8767 RTC cannot handle the year %d.\n", 80*5bccae6eSSangbeom Kim 1900 + tm->tm_year); 81*5bccae6eSSangbeom Kim return -EINVAL; 82*5bccae6eSSangbeom Kim } else { 83*5bccae6eSSangbeom Kim return 0; 84*5bccae6eSSangbeom Kim } 85*5bccae6eSSangbeom Kim } 86*5bccae6eSSangbeom Kim 87*5bccae6eSSangbeom Kim static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info) 88*5bccae6eSSangbeom Kim { 89*5bccae6eSSangbeom Kim int ret; 90*5bccae6eSSangbeom Kim unsigned int data; 91*5bccae6eSSangbeom Kim 92*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data); 93*5bccae6eSSangbeom Kim if (ret < 0) { 94*5bccae6eSSangbeom Kim dev_err(info->dev, "failed to read update reg(%d)\n", ret); 95*5bccae6eSSangbeom Kim return ret; 96*5bccae6eSSangbeom Kim } 97*5bccae6eSSangbeom Kim 98*5bccae6eSSangbeom Kim data |= RTC_TIME_EN_MASK; 99*5bccae6eSSangbeom Kim data |= RTC_UDR_MASK; 100*5bccae6eSSangbeom Kim 101*5bccae6eSSangbeom Kim ret = regmap_write(info->rtc, SEC_RTC_UDR_CON, data); 102*5bccae6eSSangbeom Kim if (ret < 0) { 103*5bccae6eSSangbeom Kim dev_err(info->dev, "failed to write update reg(%d)\n", ret); 104*5bccae6eSSangbeom Kim return ret; 105*5bccae6eSSangbeom Kim } 106*5bccae6eSSangbeom Kim 107*5bccae6eSSangbeom Kim do { 108*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data); 109*5bccae6eSSangbeom Kim } while ((data & RTC_UDR_MASK) && !ret); 110*5bccae6eSSangbeom Kim 111*5bccae6eSSangbeom Kim return ret; 112*5bccae6eSSangbeom Kim } 113*5bccae6eSSangbeom Kim 114*5bccae6eSSangbeom Kim static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info) 115*5bccae6eSSangbeom Kim { 116*5bccae6eSSangbeom Kim int ret; 117*5bccae6eSSangbeom Kim unsigned int data; 118*5bccae6eSSangbeom Kim 119*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data); 120*5bccae6eSSangbeom Kim if (ret < 0) { 121*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to read update reg(%d)\n", 122*5bccae6eSSangbeom Kim __func__, ret); 123*5bccae6eSSangbeom Kim return ret; 124*5bccae6eSSangbeom Kim } 125*5bccae6eSSangbeom Kim 126*5bccae6eSSangbeom Kim data &= ~RTC_TIME_EN_MASK; 127*5bccae6eSSangbeom Kim data |= RTC_UDR_MASK; 128*5bccae6eSSangbeom Kim 129*5bccae6eSSangbeom Kim ret = regmap_write(info->rtc, SEC_RTC_UDR_CON, data); 130*5bccae6eSSangbeom Kim if (ret < 0) { 131*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to write update reg(%d)\n", 132*5bccae6eSSangbeom Kim __func__, ret); 133*5bccae6eSSangbeom Kim return ret; 134*5bccae6eSSangbeom Kim } 135*5bccae6eSSangbeom Kim 136*5bccae6eSSangbeom Kim do { 137*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &data); 138*5bccae6eSSangbeom Kim } while ((data & RTC_UDR_MASK) && !ret); 139*5bccae6eSSangbeom Kim 140*5bccae6eSSangbeom Kim return ret; 141*5bccae6eSSangbeom Kim } 142*5bccae6eSSangbeom Kim 143*5bccae6eSSangbeom Kim static void s5m8763_data_to_tm(u8 *data, struct rtc_time *tm) 144*5bccae6eSSangbeom Kim { 145*5bccae6eSSangbeom Kim tm->tm_sec = bcd2bin(data[RTC_SEC]); 146*5bccae6eSSangbeom Kim tm->tm_min = bcd2bin(data[RTC_MIN]); 147*5bccae6eSSangbeom Kim 148*5bccae6eSSangbeom Kim if (data[RTC_HOUR] & HOUR_12) { 149*5bccae6eSSangbeom Kim tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f); 150*5bccae6eSSangbeom Kim if (data[RTC_HOUR] & HOUR_PM) 151*5bccae6eSSangbeom Kim tm->tm_hour += 12; 152*5bccae6eSSangbeom Kim } else { 153*5bccae6eSSangbeom Kim tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); 154*5bccae6eSSangbeom Kim } 155*5bccae6eSSangbeom Kim 156*5bccae6eSSangbeom Kim tm->tm_wday = data[RTC_WEEKDAY] & 0x07; 157*5bccae6eSSangbeom Kim tm->tm_mday = bcd2bin(data[RTC_DATE]); 158*5bccae6eSSangbeom Kim tm->tm_mon = bcd2bin(data[RTC_MONTH]); 159*5bccae6eSSangbeom Kim tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100; 160*5bccae6eSSangbeom Kim tm->tm_year -= 1900; 161*5bccae6eSSangbeom Kim } 162*5bccae6eSSangbeom Kim 163*5bccae6eSSangbeom Kim static void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data) 164*5bccae6eSSangbeom Kim { 165*5bccae6eSSangbeom Kim data[RTC_SEC] = bin2bcd(tm->tm_sec); 166*5bccae6eSSangbeom Kim data[RTC_MIN] = bin2bcd(tm->tm_min); 167*5bccae6eSSangbeom Kim data[RTC_HOUR] = bin2bcd(tm->tm_hour); 168*5bccae6eSSangbeom Kim data[RTC_WEEKDAY] = tm->tm_wday; 169*5bccae6eSSangbeom Kim data[RTC_DATE] = bin2bcd(tm->tm_mday); 170*5bccae6eSSangbeom Kim data[RTC_MONTH] = bin2bcd(tm->tm_mon); 171*5bccae6eSSangbeom Kim data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100); 172*5bccae6eSSangbeom Kim data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100); 173*5bccae6eSSangbeom Kim } 174*5bccae6eSSangbeom Kim 175*5bccae6eSSangbeom Kim static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm) 176*5bccae6eSSangbeom Kim { 177*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = dev_get_drvdata(dev); 178*5bccae6eSSangbeom Kim u8 data[8]; 179*5bccae6eSSangbeom Kim int ret; 180*5bccae6eSSangbeom Kim 181*5bccae6eSSangbeom Kim ret = regmap_bulk_read(info->rtc, SEC_RTC_SEC, data, 8); 182*5bccae6eSSangbeom Kim if (ret < 0) 183*5bccae6eSSangbeom Kim return ret; 184*5bccae6eSSangbeom Kim 185*5bccae6eSSangbeom Kim switch (info->device_type) { 186*5bccae6eSSangbeom Kim case S5M8763X: 187*5bccae6eSSangbeom Kim s5m8763_data_to_tm(data, tm); 188*5bccae6eSSangbeom Kim break; 189*5bccae6eSSangbeom Kim 190*5bccae6eSSangbeom Kim case S5M8767X: 191*5bccae6eSSangbeom Kim s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode); 192*5bccae6eSSangbeom Kim break; 193*5bccae6eSSangbeom Kim 194*5bccae6eSSangbeom Kim default: 195*5bccae6eSSangbeom Kim return -EINVAL; 196*5bccae6eSSangbeom Kim } 197*5bccae6eSSangbeom Kim 198*5bccae6eSSangbeom Kim dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, 199*5bccae6eSSangbeom Kim 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, 200*5bccae6eSSangbeom Kim tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday); 201*5bccae6eSSangbeom Kim 202*5bccae6eSSangbeom Kim return rtc_valid_tm(tm); 203*5bccae6eSSangbeom Kim } 204*5bccae6eSSangbeom Kim 205*5bccae6eSSangbeom Kim static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm) 206*5bccae6eSSangbeom Kim { 207*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = dev_get_drvdata(dev); 208*5bccae6eSSangbeom Kim u8 data[8]; 209*5bccae6eSSangbeom Kim int ret = 0; 210*5bccae6eSSangbeom Kim 211*5bccae6eSSangbeom Kim switch (info->device_type) { 212*5bccae6eSSangbeom Kim case S5M8763X: 213*5bccae6eSSangbeom Kim s5m8763_tm_to_data(tm, data); 214*5bccae6eSSangbeom Kim break; 215*5bccae6eSSangbeom Kim case S5M8767X: 216*5bccae6eSSangbeom Kim ret = s5m8767_tm_to_data(tm, data); 217*5bccae6eSSangbeom Kim break; 218*5bccae6eSSangbeom Kim default: 219*5bccae6eSSangbeom Kim return -EINVAL; 220*5bccae6eSSangbeom Kim } 221*5bccae6eSSangbeom Kim 222*5bccae6eSSangbeom Kim if (ret < 0) 223*5bccae6eSSangbeom Kim return ret; 224*5bccae6eSSangbeom Kim 225*5bccae6eSSangbeom Kim dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, 226*5bccae6eSSangbeom Kim 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, 227*5bccae6eSSangbeom Kim tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday); 228*5bccae6eSSangbeom Kim 229*5bccae6eSSangbeom Kim ret = regmap_raw_write(info->rtc, SEC_RTC_SEC, data, 8); 230*5bccae6eSSangbeom Kim if (ret < 0) 231*5bccae6eSSangbeom Kim return ret; 232*5bccae6eSSangbeom Kim 233*5bccae6eSSangbeom Kim ret = s5m8767_rtc_set_time_reg(info); 234*5bccae6eSSangbeom Kim 235*5bccae6eSSangbeom Kim return ret; 236*5bccae6eSSangbeom Kim } 237*5bccae6eSSangbeom Kim 238*5bccae6eSSangbeom Kim static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 239*5bccae6eSSangbeom Kim { 240*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = dev_get_drvdata(dev); 241*5bccae6eSSangbeom Kim u8 data[8]; 242*5bccae6eSSangbeom Kim unsigned int val; 243*5bccae6eSSangbeom Kim int ret, i; 244*5bccae6eSSangbeom Kim 245*5bccae6eSSangbeom Kim ret = regmap_bulk_read(info->rtc, SEC_ALARM0_SEC, data, 8); 246*5bccae6eSSangbeom Kim if (ret < 0) 247*5bccae6eSSangbeom Kim return ret; 248*5bccae6eSSangbeom Kim 249*5bccae6eSSangbeom Kim switch (info->device_type) { 250*5bccae6eSSangbeom Kim case S5M8763X: 251*5bccae6eSSangbeom Kim s5m8763_data_to_tm(data, &alrm->time); 252*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_ALARM0_CONF, &val); 253*5bccae6eSSangbeom Kim if (ret < 0) 254*5bccae6eSSangbeom Kim return ret; 255*5bccae6eSSangbeom Kim 256*5bccae6eSSangbeom Kim alrm->enabled = !!val; 257*5bccae6eSSangbeom Kim 258*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_STATUS, &val); 259*5bccae6eSSangbeom Kim if (ret < 0) 260*5bccae6eSSangbeom Kim return ret; 261*5bccae6eSSangbeom Kim 262*5bccae6eSSangbeom Kim break; 263*5bccae6eSSangbeom Kim 264*5bccae6eSSangbeom Kim case S5M8767X: 265*5bccae6eSSangbeom Kim s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); 266*5bccae6eSSangbeom Kim dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, 267*5bccae6eSSangbeom Kim 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon, 268*5bccae6eSSangbeom Kim alrm->time.tm_mday, alrm->time.tm_hour, 269*5bccae6eSSangbeom Kim alrm->time.tm_min, alrm->time.tm_sec, 270*5bccae6eSSangbeom Kim alrm->time.tm_wday); 271*5bccae6eSSangbeom Kim 272*5bccae6eSSangbeom Kim alrm->enabled = 0; 273*5bccae6eSSangbeom Kim for (i = 0; i < 7; i++) { 274*5bccae6eSSangbeom Kim if (data[i] & ALARM_ENABLE_MASK) { 275*5bccae6eSSangbeom Kim alrm->enabled = 1; 276*5bccae6eSSangbeom Kim break; 277*5bccae6eSSangbeom Kim } 278*5bccae6eSSangbeom Kim } 279*5bccae6eSSangbeom Kim 280*5bccae6eSSangbeom Kim alrm->pending = 0; 281*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_STATUS, &val); 282*5bccae6eSSangbeom Kim if (ret < 0) 283*5bccae6eSSangbeom Kim return ret; 284*5bccae6eSSangbeom Kim break; 285*5bccae6eSSangbeom Kim 286*5bccae6eSSangbeom Kim default: 287*5bccae6eSSangbeom Kim return -EINVAL; 288*5bccae6eSSangbeom Kim } 289*5bccae6eSSangbeom Kim 290*5bccae6eSSangbeom Kim if (val & ALARM0_STATUS) 291*5bccae6eSSangbeom Kim alrm->pending = 1; 292*5bccae6eSSangbeom Kim else 293*5bccae6eSSangbeom Kim alrm->pending = 0; 294*5bccae6eSSangbeom Kim 295*5bccae6eSSangbeom Kim return 0; 296*5bccae6eSSangbeom Kim } 297*5bccae6eSSangbeom Kim 298*5bccae6eSSangbeom Kim static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info) 299*5bccae6eSSangbeom Kim { 300*5bccae6eSSangbeom Kim u8 data[8]; 301*5bccae6eSSangbeom Kim int ret, i; 302*5bccae6eSSangbeom Kim struct rtc_time tm; 303*5bccae6eSSangbeom Kim 304*5bccae6eSSangbeom Kim ret = regmap_bulk_read(info->rtc, SEC_ALARM0_SEC, data, 8); 305*5bccae6eSSangbeom Kim if (ret < 0) 306*5bccae6eSSangbeom Kim return ret; 307*5bccae6eSSangbeom Kim 308*5bccae6eSSangbeom Kim s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); 309*5bccae6eSSangbeom Kim dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, 310*5bccae6eSSangbeom Kim 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, 311*5bccae6eSSangbeom Kim tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday); 312*5bccae6eSSangbeom Kim 313*5bccae6eSSangbeom Kim switch (info->device_type) { 314*5bccae6eSSangbeom Kim case S5M8763X: 315*5bccae6eSSangbeom Kim ret = regmap_write(info->rtc, SEC_ALARM0_CONF, 0); 316*5bccae6eSSangbeom Kim break; 317*5bccae6eSSangbeom Kim 318*5bccae6eSSangbeom Kim case S5M8767X: 319*5bccae6eSSangbeom Kim for (i = 0; i < 7; i++) 320*5bccae6eSSangbeom Kim data[i] &= ~ALARM_ENABLE_MASK; 321*5bccae6eSSangbeom Kim 322*5bccae6eSSangbeom Kim ret = regmap_raw_write(info->rtc, SEC_ALARM0_SEC, data, 8); 323*5bccae6eSSangbeom Kim if (ret < 0) 324*5bccae6eSSangbeom Kim return ret; 325*5bccae6eSSangbeom Kim 326*5bccae6eSSangbeom Kim ret = s5m8767_rtc_set_alarm_reg(info); 327*5bccae6eSSangbeom Kim 328*5bccae6eSSangbeom Kim break; 329*5bccae6eSSangbeom Kim 330*5bccae6eSSangbeom Kim default: 331*5bccae6eSSangbeom Kim return -EINVAL; 332*5bccae6eSSangbeom Kim } 333*5bccae6eSSangbeom Kim 334*5bccae6eSSangbeom Kim return ret; 335*5bccae6eSSangbeom Kim } 336*5bccae6eSSangbeom Kim 337*5bccae6eSSangbeom Kim static int s5m_rtc_start_alarm(struct s5m_rtc_info *info) 338*5bccae6eSSangbeom Kim { 339*5bccae6eSSangbeom Kim int ret; 340*5bccae6eSSangbeom Kim u8 data[8]; 341*5bccae6eSSangbeom Kim u8 alarm0_conf; 342*5bccae6eSSangbeom Kim struct rtc_time tm; 343*5bccae6eSSangbeom Kim 344*5bccae6eSSangbeom Kim ret = regmap_bulk_read(info->rtc, SEC_ALARM0_SEC, data, 8); 345*5bccae6eSSangbeom Kim if (ret < 0) 346*5bccae6eSSangbeom Kim return ret; 347*5bccae6eSSangbeom Kim 348*5bccae6eSSangbeom Kim s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); 349*5bccae6eSSangbeom Kim dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, 350*5bccae6eSSangbeom Kim 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, 351*5bccae6eSSangbeom Kim tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday); 352*5bccae6eSSangbeom Kim 353*5bccae6eSSangbeom Kim switch (info->device_type) { 354*5bccae6eSSangbeom Kim case S5M8763X: 355*5bccae6eSSangbeom Kim alarm0_conf = 0x77; 356*5bccae6eSSangbeom Kim ret = regmap_write(info->rtc, SEC_ALARM0_CONF, alarm0_conf); 357*5bccae6eSSangbeom Kim break; 358*5bccae6eSSangbeom Kim 359*5bccae6eSSangbeom Kim case S5M8767X: 360*5bccae6eSSangbeom Kim data[RTC_SEC] |= ALARM_ENABLE_MASK; 361*5bccae6eSSangbeom Kim data[RTC_MIN] |= ALARM_ENABLE_MASK; 362*5bccae6eSSangbeom Kim data[RTC_HOUR] |= ALARM_ENABLE_MASK; 363*5bccae6eSSangbeom Kim data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; 364*5bccae6eSSangbeom Kim if (data[RTC_DATE] & 0x1f) 365*5bccae6eSSangbeom Kim data[RTC_DATE] |= ALARM_ENABLE_MASK; 366*5bccae6eSSangbeom Kim if (data[RTC_MONTH] & 0xf) 367*5bccae6eSSangbeom Kim data[RTC_MONTH] |= ALARM_ENABLE_MASK; 368*5bccae6eSSangbeom Kim if (data[RTC_YEAR1] & 0x7f) 369*5bccae6eSSangbeom Kim data[RTC_YEAR1] |= ALARM_ENABLE_MASK; 370*5bccae6eSSangbeom Kim 371*5bccae6eSSangbeom Kim ret = regmap_raw_write(info->rtc, SEC_ALARM0_SEC, data, 8); 372*5bccae6eSSangbeom Kim if (ret < 0) 373*5bccae6eSSangbeom Kim return ret; 374*5bccae6eSSangbeom Kim ret = s5m8767_rtc_set_alarm_reg(info); 375*5bccae6eSSangbeom Kim 376*5bccae6eSSangbeom Kim break; 377*5bccae6eSSangbeom Kim 378*5bccae6eSSangbeom Kim default: 379*5bccae6eSSangbeom Kim return -EINVAL; 380*5bccae6eSSangbeom Kim } 381*5bccae6eSSangbeom Kim 382*5bccae6eSSangbeom Kim return ret; 383*5bccae6eSSangbeom Kim } 384*5bccae6eSSangbeom Kim 385*5bccae6eSSangbeom Kim static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 386*5bccae6eSSangbeom Kim { 387*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = dev_get_drvdata(dev); 388*5bccae6eSSangbeom Kim u8 data[8]; 389*5bccae6eSSangbeom Kim int ret; 390*5bccae6eSSangbeom Kim 391*5bccae6eSSangbeom Kim switch (info->device_type) { 392*5bccae6eSSangbeom Kim case S5M8763X: 393*5bccae6eSSangbeom Kim s5m8763_tm_to_data(&alrm->time, data); 394*5bccae6eSSangbeom Kim break; 395*5bccae6eSSangbeom Kim 396*5bccae6eSSangbeom Kim case S5M8767X: 397*5bccae6eSSangbeom Kim s5m8767_tm_to_data(&alrm->time, data); 398*5bccae6eSSangbeom Kim break; 399*5bccae6eSSangbeom Kim 400*5bccae6eSSangbeom Kim default: 401*5bccae6eSSangbeom Kim return -EINVAL; 402*5bccae6eSSangbeom Kim } 403*5bccae6eSSangbeom Kim 404*5bccae6eSSangbeom Kim dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__, 405*5bccae6eSSangbeom Kim 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon, 406*5bccae6eSSangbeom Kim alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min, 407*5bccae6eSSangbeom Kim alrm->time.tm_sec, alrm->time.tm_wday); 408*5bccae6eSSangbeom Kim 409*5bccae6eSSangbeom Kim ret = s5m_rtc_stop_alarm(info); 410*5bccae6eSSangbeom Kim if (ret < 0) 411*5bccae6eSSangbeom Kim return ret; 412*5bccae6eSSangbeom Kim 413*5bccae6eSSangbeom Kim ret = regmap_raw_write(info->rtc, SEC_ALARM0_SEC, data, 8); 414*5bccae6eSSangbeom Kim if (ret < 0) 415*5bccae6eSSangbeom Kim return ret; 416*5bccae6eSSangbeom Kim 417*5bccae6eSSangbeom Kim ret = s5m8767_rtc_set_alarm_reg(info); 418*5bccae6eSSangbeom Kim if (ret < 0) 419*5bccae6eSSangbeom Kim return ret; 420*5bccae6eSSangbeom Kim 421*5bccae6eSSangbeom Kim if (alrm->enabled) 422*5bccae6eSSangbeom Kim ret = s5m_rtc_start_alarm(info); 423*5bccae6eSSangbeom Kim 424*5bccae6eSSangbeom Kim return ret; 425*5bccae6eSSangbeom Kim } 426*5bccae6eSSangbeom Kim 427*5bccae6eSSangbeom Kim static int s5m_rtc_alarm_irq_enable(struct device *dev, 428*5bccae6eSSangbeom Kim unsigned int enabled) 429*5bccae6eSSangbeom Kim { 430*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = dev_get_drvdata(dev); 431*5bccae6eSSangbeom Kim 432*5bccae6eSSangbeom Kim if (enabled) 433*5bccae6eSSangbeom Kim return s5m_rtc_start_alarm(info); 434*5bccae6eSSangbeom Kim else 435*5bccae6eSSangbeom Kim return s5m_rtc_stop_alarm(info); 436*5bccae6eSSangbeom Kim } 437*5bccae6eSSangbeom Kim 438*5bccae6eSSangbeom Kim static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data) 439*5bccae6eSSangbeom Kim { 440*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = data; 441*5bccae6eSSangbeom Kim 442*5bccae6eSSangbeom Kim rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); 443*5bccae6eSSangbeom Kim 444*5bccae6eSSangbeom Kim return IRQ_HANDLED; 445*5bccae6eSSangbeom Kim } 446*5bccae6eSSangbeom Kim 447*5bccae6eSSangbeom Kim static const struct rtc_class_ops s5m_rtc_ops = { 448*5bccae6eSSangbeom Kim .read_time = s5m_rtc_read_time, 449*5bccae6eSSangbeom Kim .set_time = s5m_rtc_set_time, 450*5bccae6eSSangbeom Kim .read_alarm = s5m_rtc_read_alarm, 451*5bccae6eSSangbeom Kim .set_alarm = s5m_rtc_set_alarm, 452*5bccae6eSSangbeom Kim .alarm_irq_enable = s5m_rtc_alarm_irq_enable, 453*5bccae6eSSangbeom Kim }; 454*5bccae6eSSangbeom Kim 455*5bccae6eSSangbeom Kim static void s5m_rtc_enable_wtsr(struct s5m_rtc_info *info, bool enable) 456*5bccae6eSSangbeom Kim { 457*5bccae6eSSangbeom Kim int ret; 458*5bccae6eSSangbeom Kim ret = regmap_update_bits(info->rtc, SEC_WTSR_SMPL_CNTL, 459*5bccae6eSSangbeom Kim WTSR_ENABLE_MASK, 460*5bccae6eSSangbeom Kim enable ? WTSR_ENABLE_MASK : 0); 461*5bccae6eSSangbeom Kim if (ret < 0) 462*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", 463*5bccae6eSSangbeom Kim __func__, ret); 464*5bccae6eSSangbeom Kim } 465*5bccae6eSSangbeom Kim 466*5bccae6eSSangbeom Kim static void s5m_rtc_enable_smpl(struct s5m_rtc_info *info, bool enable) 467*5bccae6eSSangbeom Kim { 468*5bccae6eSSangbeom Kim int ret; 469*5bccae6eSSangbeom Kim ret = regmap_update_bits(info->rtc, SEC_WTSR_SMPL_CNTL, 470*5bccae6eSSangbeom Kim SMPL_ENABLE_MASK, 471*5bccae6eSSangbeom Kim enable ? SMPL_ENABLE_MASK : 0); 472*5bccae6eSSangbeom Kim if (ret < 0) 473*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", 474*5bccae6eSSangbeom Kim __func__, ret); 475*5bccae6eSSangbeom Kim } 476*5bccae6eSSangbeom Kim 477*5bccae6eSSangbeom Kim static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) 478*5bccae6eSSangbeom Kim { 479*5bccae6eSSangbeom Kim u8 data[2]; 480*5bccae6eSSangbeom Kim unsigned int tp_read; 481*5bccae6eSSangbeom Kim int ret; 482*5bccae6eSSangbeom Kim struct rtc_time tm; 483*5bccae6eSSangbeom Kim 484*5bccae6eSSangbeom Kim ret = regmap_read(info->rtc, SEC_RTC_UDR_CON, &tp_read); 485*5bccae6eSSangbeom Kim if (ret < 0) { 486*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to read control reg(%d)\n", 487*5bccae6eSSangbeom Kim __func__, ret); 488*5bccae6eSSangbeom Kim return ret; 489*5bccae6eSSangbeom Kim } 490*5bccae6eSSangbeom Kim 491*5bccae6eSSangbeom Kim /* Set RTC control register : Binary mode, 24hour mode */ 492*5bccae6eSSangbeom Kim data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 493*5bccae6eSSangbeom Kim data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); 494*5bccae6eSSangbeom Kim 495*5bccae6eSSangbeom Kim info->rtc_24hr_mode = 1; 496*5bccae6eSSangbeom Kim ret = regmap_raw_write(info->rtc, SEC_ALARM0_CONF, data, 2); 497*5bccae6eSSangbeom Kim if (ret < 0) { 498*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", 499*5bccae6eSSangbeom Kim __func__, ret); 500*5bccae6eSSangbeom Kim return ret; 501*5bccae6eSSangbeom Kim } 502*5bccae6eSSangbeom Kim 503*5bccae6eSSangbeom Kim /* In first boot time, Set rtc time to 1/1/2012 00:00:00(SUN) */ 504*5bccae6eSSangbeom Kim if ((tp_read & RTC_TCON_MASK) == 0) { 505*5bccae6eSSangbeom Kim dev_dbg(info->dev, "rtc init\n"); 506*5bccae6eSSangbeom Kim tm.tm_sec = 0; 507*5bccae6eSSangbeom Kim tm.tm_min = 0; 508*5bccae6eSSangbeom Kim tm.tm_hour = 0; 509*5bccae6eSSangbeom Kim tm.tm_wday = 0; 510*5bccae6eSSangbeom Kim tm.tm_mday = 1; 511*5bccae6eSSangbeom Kim tm.tm_mon = 0; 512*5bccae6eSSangbeom Kim tm.tm_year = 112; 513*5bccae6eSSangbeom Kim tm.tm_yday = 0; 514*5bccae6eSSangbeom Kim tm.tm_isdst = 0; 515*5bccae6eSSangbeom Kim ret = s5m_rtc_set_time(info->dev, &tm); 516*5bccae6eSSangbeom Kim } 517*5bccae6eSSangbeom Kim 518*5bccae6eSSangbeom Kim ret = regmap_update_bits(info->rtc, SEC_RTC_UDR_CON, 519*5bccae6eSSangbeom Kim RTC_TCON_MASK, tp_read | RTC_TCON_MASK); 520*5bccae6eSSangbeom Kim if (ret < 0) 521*5bccae6eSSangbeom Kim dev_err(info->dev, "%s: fail to update TCON reg(%d)\n", 522*5bccae6eSSangbeom Kim __func__, ret); 523*5bccae6eSSangbeom Kim 524*5bccae6eSSangbeom Kim return ret; 525*5bccae6eSSangbeom Kim } 526*5bccae6eSSangbeom Kim 527*5bccae6eSSangbeom Kim static int s5m_rtc_probe(struct platform_device *pdev) 528*5bccae6eSSangbeom Kim { 529*5bccae6eSSangbeom Kim struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent); 530*5bccae6eSSangbeom Kim struct sec_platform_data *pdata = s5m87xx->pdata; 531*5bccae6eSSangbeom Kim struct s5m_rtc_info *info; 532*5bccae6eSSangbeom Kim int ret; 533*5bccae6eSSangbeom Kim 534*5bccae6eSSangbeom Kim if (!pdata) { 535*5bccae6eSSangbeom Kim dev_err(pdev->dev.parent, "Platform data not supplied\n"); 536*5bccae6eSSangbeom Kim return -ENODEV; 537*5bccae6eSSangbeom Kim } 538*5bccae6eSSangbeom Kim 539*5bccae6eSSangbeom Kim info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 540*5bccae6eSSangbeom Kim if (!info) 541*5bccae6eSSangbeom Kim return -ENOMEM; 542*5bccae6eSSangbeom Kim 543*5bccae6eSSangbeom Kim info->dev = &pdev->dev; 544*5bccae6eSSangbeom Kim info->s5m87xx = s5m87xx; 545*5bccae6eSSangbeom Kim info->rtc = s5m87xx->rtc; 546*5bccae6eSSangbeom Kim info->device_type = s5m87xx->device_type; 547*5bccae6eSSangbeom Kim info->wtsr_smpl = s5m87xx->wtsr_smpl; 548*5bccae6eSSangbeom Kim 549*5bccae6eSSangbeom Kim switch (pdata->device_type) { 550*5bccae6eSSangbeom Kim case S5M8763X: 551*5bccae6eSSangbeom Kim info->irq = s5m87xx->irq_base + S5M8763_IRQ_ALARM0; 552*5bccae6eSSangbeom Kim break; 553*5bccae6eSSangbeom Kim 554*5bccae6eSSangbeom Kim case S5M8767X: 555*5bccae6eSSangbeom Kim info->irq = s5m87xx->irq_base + S5M8767_IRQ_RTCA1; 556*5bccae6eSSangbeom Kim break; 557*5bccae6eSSangbeom Kim 558*5bccae6eSSangbeom Kim default: 559*5bccae6eSSangbeom Kim ret = -EINVAL; 560*5bccae6eSSangbeom Kim dev_err(&pdev->dev, "Unsupported device type: %d\n", ret); 561*5bccae6eSSangbeom Kim return ret; 562*5bccae6eSSangbeom Kim } 563*5bccae6eSSangbeom Kim 564*5bccae6eSSangbeom Kim platform_set_drvdata(pdev, info); 565*5bccae6eSSangbeom Kim 566*5bccae6eSSangbeom Kim ret = s5m8767_rtc_init_reg(info); 567*5bccae6eSSangbeom Kim 568*5bccae6eSSangbeom Kim if (info->wtsr_smpl) { 569*5bccae6eSSangbeom Kim s5m_rtc_enable_wtsr(info, true); 570*5bccae6eSSangbeom Kim s5m_rtc_enable_smpl(info, true); 571*5bccae6eSSangbeom Kim } 572*5bccae6eSSangbeom Kim 573*5bccae6eSSangbeom Kim device_init_wakeup(&pdev->dev, 1); 574*5bccae6eSSangbeom Kim 575*5bccae6eSSangbeom Kim info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc", 576*5bccae6eSSangbeom Kim &s5m_rtc_ops, THIS_MODULE); 577*5bccae6eSSangbeom Kim 578*5bccae6eSSangbeom Kim if (IS_ERR(info->rtc_dev)) 579*5bccae6eSSangbeom Kim return PTR_ERR(info->rtc_dev); 580*5bccae6eSSangbeom Kim 581*5bccae6eSSangbeom Kim ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, 582*5bccae6eSSangbeom Kim s5m_rtc_alarm_irq, 0, "rtc-alarm0", 583*5bccae6eSSangbeom Kim info); 584*5bccae6eSSangbeom Kim if (ret < 0) 585*5bccae6eSSangbeom Kim dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", 586*5bccae6eSSangbeom Kim info->irq, ret); 587*5bccae6eSSangbeom Kim 588*5bccae6eSSangbeom Kim return ret; 589*5bccae6eSSangbeom Kim } 590*5bccae6eSSangbeom Kim 591*5bccae6eSSangbeom Kim static void s5m_rtc_shutdown(struct platform_device *pdev) 592*5bccae6eSSangbeom Kim { 593*5bccae6eSSangbeom Kim struct s5m_rtc_info *info = platform_get_drvdata(pdev); 594*5bccae6eSSangbeom Kim int i; 595*5bccae6eSSangbeom Kim unsigned int val = 0; 596*5bccae6eSSangbeom Kim if (info->wtsr_smpl) { 597*5bccae6eSSangbeom Kim for (i = 0; i < 3; i++) { 598*5bccae6eSSangbeom Kim s5m_rtc_enable_wtsr(info, false); 599*5bccae6eSSangbeom Kim regmap_read(info->rtc, SEC_WTSR_SMPL_CNTL, &val); 600*5bccae6eSSangbeom Kim pr_debug("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val); 601*5bccae6eSSangbeom Kim if (val & WTSR_ENABLE_MASK) 602*5bccae6eSSangbeom Kim pr_emerg("%s: fail to disable WTSR\n", 603*5bccae6eSSangbeom Kim __func__); 604*5bccae6eSSangbeom Kim else { 605*5bccae6eSSangbeom Kim pr_info("%s: success to disable WTSR\n", 606*5bccae6eSSangbeom Kim __func__); 607*5bccae6eSSangbeom Kim break; 608*5bccae6eSSangbeom Kim } 609*5bccae6eSSangbeom Kim } 610*5bccae6eSSangbeom Kim } 611*5bccae6eSSangbeom Kim /* Disable SMPL when power off */ 612*5bccae6eSSangbeom Kim s5m_rtc_enable_smpl(info, false); 613*5bccae6eSSangbeom Kim } 614*5bccae6eSSangbeom Kim 615*5bccae6eSSangbeom Kim static const struct platform_device_id s5m_rtc_id[] = { 616*5bccae6eSSangbeom Kim { "s5m-rtc", 0 }, 617*5bccae6eSSangbeom Kim }; 618*5bccae6eSSangbeom Kim 619*5bccae6eSSangbeom Kim static struct platform_driver s5m_rtc_driver = { 620*5bccae6eSSangbeom Kim .driver = { 621*5bccae6eSSangbeom Kim .name = "s5m-rtc", 622*5bccae6eSSangbeom Kim .owner = THIS_MODULE, 623*5bccae6eSSangbeom Kim }, 624*5bccae6eSSangbeom Kim .probe = s5m_rtc_probe, 625*5bccae6eSSangbeom Kim .shutdown = s5m_rtc_shutdown, 626*5bccae6eSSangbeom Kim .id_table = s5m_rtc_id, 627*5bccae6eSSangbeom Kim }; 628*5bccae6eSSangbeom Kim 629*5bccae6eSSangbeom Kim module_platform_driver(s5m_rtc_driver); 630*5bccae6eSSangbeom Kim 631*5bccae6eSSangbeom Kim /* Module information */ 632*5bccae6eSSangbeom Kim MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); 633*5bccae6eSSangbeom Kim MODULE_DESCRIPTION("Samsung S5M RTC driver"); 634*5bccae6eSSangbeom Kim MODULE_LICENSE("GPL"); 635*5bccae6eSSangbeom Kim MODULE_ALIAS("platform:s5m-rtc"); 636