18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Real Time Clock (RTC) Driver for sd3078 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Zoro Li 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/bcd.h> 88c2ecf20Sopenharmony_ci#include <linux/i2c.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/regmap.h> 118c2ecf20Sopenharmony_ci#include <linux/rtc.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define SD3078_REG_SC 0x00 158c2ecf20Sopenharmony_ci#define SD3078_REG_MN 0x01 168c2ecf20Sopenharmony_ci#define SD3078_REG_HR 0x02 178c2ecf20Sopenharmony_ci#define SD3078_REG_DW 0x03 188c2ecf20Sopenharmony_ci#define SD3078_REG_DM 0x04 198c2ecf20Sopenharmony_ci#define SD3078_REG_MO 0x05 208c2ecf20Sopenharmony_ci#define SD3078_REG_YR 0x06 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define SD3078_REG_CTRL1 0x0f 238c2ecf20Sopenharmony_ci#define SD3078_REG_CTRL2 0x10 248c2ecf20Sopenharmony_ci#define SD3078_REG_CTRL3 0x11 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define KEY_WRITE1 0x80 278c2ecf20Sopenharmony_ci#define KEY_WRITE2 0x04 288c2ecf20Sopenharmony_ci#define KEY_WRITE3 0x80 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * The sd3078 has write protection 348c2ecf20Sopenharmony_ci * and we can choose whether or not to use it. 358c2ecf20Sopenharmony_ci * Write protection is turned off by default. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#define WRITE_PROTECT_EN 0 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct sd3078 { 408c2ecf20Sopenharmony_ci struct rtc_device *rtc; 418c2ecf20Sopenharmony_ci struct regmap *regmap; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * In order to prevent arbitrary modification of the time register, 468c2ecf20Sopenharmony_ci * when modification of the register, 478c2ecf20Sopenharmony_ci * the "write" bit needs to be written in a certain order. 488c2ecf20Sopenharmony_ci * 1. set WRITE1 bit 498c2ecf20Sopenharmony_ci * 2. set WRITE2 bit 508c2ecf20Sopenharmony_ci * 3. set WRITE3 bit 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic void sd3078_enable_reg_write(struct sd3078 *sd3078) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, 558c2ecf20Sopenharmony_ci KEY_WRITE1, KEY_WRITE1); 568c2ecf20Sopenharmony_ci regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, 578c2ecf20Sopenharmony_ci KEY_WRITE2, KEY_WRITE2); 588c2ecf20Sopenharmony_ci regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, 598c2ecf20Sopenharmony_ci KEY_WRITE3, KEY_WRITE3); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#if WRITE_PROTECT_EN 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * In order to prevent arbitrary modification of the time register, 658c2ecf20Sopenharmony_ci * we should disable the write function. 668c2ecf20Sopenharmony_ci * when disable write, 678c2ecf20Sopenharmony_ci * the "write" bit needs to be clear in a certain order. 688c2ecf20Sopenharmony_ci * 1. clear WRITE2 bit 698c2ecf20Sopenharmony_ci * 2. clear WRITE3 bit 708c2ecf20Sopenharmony_ci * 3. clear WRITE1 bit 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic void sd3078_disable_reg_write(struct sd3078 *sd3078) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, 758c2ecf20Sopenharmony_ci KEY_WRITE2, 0); 768c2ecf20Sopenharmony_ci regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, 778c2ecf20Sopenharmony_ci KEY_WRITE3, 0); 788c2ecf20Sopenharmony_ci regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, 798c2ecf20Sopenharmony_ci KEY_WRITE1, 0); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci unsigned char hour; 868c2ecf20Sopenharmony_ci unsigned char rtc_data[NUM_TIME_REGS] = {0}; 878c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 888c2ecf20Sopenharmony_ci struct sd3078 *sd3078 = i2c_get_clientdata(client); 898c2ecf20Sopenharmony_ci int ret; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data, 928c2ecf20Sopenharmony_ci NUM_TIME_REGS); 938c2ecf20Sopenharmony_ci if (ret < 0) { 948c2ecf20Sopenharmony_ci dev_err(dev, "reading from RTC failed with err:%d\n", ret); 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F); 998c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * The sd3078 supports 12/24 hour mode. 1038c2ecf20Sopenharmony_ci * When getting time, 1048c2ecf20Sopenharmony_ci * we need to convert the 12 hour mode to the 24 hour mode. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci hour = rtc_data[SD3078_REG_HR]; 1078c2ecf20Sopenharmony_ci if (hour & 0x80) /* 24H MODE */ 1088c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F); 1098c2ecf20Sopenharmony_ci else if (hour & 0x20) /* 12H MODE PM */ 1108c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12; 1118c2ecf20Sopenharmony_ci else /* 12H MODE AM */ 1128c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F); 1158c2ecf20Sopenharmony_ci tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07; 1168c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1; 1178c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned char rtc_data[NUM_TIME_REGS]; 1258c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 1268c2ecf20Sopenharmony_ci struct sd3078 *sd3078 = i2c_get_clientdata(client); 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec); 1308c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min); 1318c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80; 1328c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday); 1338c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07; 1348c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1; 1358c2ecf20Sopenharmony_ci rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#if WRITE_PROTECT_EN 1388c2ecf20Sopenharmony_ci sd3078_enable_reg_write(sd3078); 1398c2ecf20Sopenharmony_ci#endif 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data, 1428c2ecf20Sopenharmony_ci NUM_TIME_REGS); 1438c2ecf20Sopenharmony_ci if (ret < 0) { 1448c2ecf20Sopenharmony_ci dev_err(dev, "writing to RTC failed with err:%d\n", ret); 1458c2ecf20Sopenharmony_ci return ret; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#if WRITE_PROTECT_EN 1498c2ecf20Sopenharmony_ci sd3078_disable_reg_write(sd3078); 1508c2ecf20Sopenharmony_ci#endif 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct rtc_class_ops sd3078_rtc_ops = { 1568c2ecf20Sopenharmony_ci .read_time = sd3078_rtc_read_time, 1578c2ecf20Sopenharmony_ci .set_time = sd3078_rtc_set_time, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct regmap_config regmap_config = { 1618c2ecf20Sopenharmony_ci .reg_bits = 8, 1628c2ecf20Sopenharmony_ci .val_bits = 8, 1638c2ecf20Sopenharmony_ci .max_register = 0x11, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int sd3078_probe(struct i2c_client *client, 1678c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci struct sd3078 *sd3078; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 1738c2ecf20Sopenharmony_ci return -ENODEV; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (!sd3078) 1778c2ecf20Sopenharmony_ci return -ENOMEM; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci sd3078->regmap = devm_regmap_init_i2c(client, ®map_config); 1808c2ecf20Sopenharmony_ci if (IS_ERR(sd3078->regmap)) { 1818c2ecf20Sopenharmony_ci dev_err(&client->dev, "regmap allocation failed\n"); 1828c2ecf20Sopenharmony_ci return PTR_ERR(sd3078->regmap); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci i2c_set_clientdata(client, sd3078); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci sd3078->rtc = devm_rtc_allocate_device(&client->dev); 1888c2ecf20Sopenharmony_ci if (IS_ERR(sd3078->rtc)) 1898c2ecf20Sopenharmony_ci return PTR_ERR(sd3078->rtc); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci sd3078->rtc->ops = &sd3078_rtc_ops; 1928c2ecf20Sopenharmony_ci sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 1938c2ecf20Sopenharmony_ci sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ret = rtc_register_device(sd3078->rtc); 1968c2ecf20Sopenharmony_ci if (ret) 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci sd3078_enable_reg_write(sd3078); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct i2c_device_id sd3078_id[] = { 2058c2ecf20Sopenharmony_ci {"sd3078", 0}, 2068c2ecf20Sopenharmony_ci { } 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sd3078_id); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct of_device_id rtc_dt_match[] = { 2118c2ecf20Sopenharmony_ci { .compatible = "whwave,sd3078" }, 2128c2ecf20Sopenharmony_ci {}, 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rtc_dt_match); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic struct i2c_driver sd3078_driver = { 2178c2ecf20Sopenharmony_ci .driver = { 2188c2ecf20Sopenharmony_ci .name = "sd3078", 2198c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(rtc_dt_match), 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci .probe = sd3078_probe, 2228c2ecf20Sopenharmony_ci .id_table = sd3078_id, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cimodule_i2c_driver(sd3078_driver); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dianlong Li <long17.cool@163.com>"); 2288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SD3078 RTC driver"); 2298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 230