18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * An I2C driver for the Intersil ISL 12022 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Roman Fietze <roman.fietze@telemotive.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on the Philips PCF8563 RTC 88c2ecf20Sopenharmony_ci * by Alessandro Zummo <a.zummo@towertech.it>. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/bcd.h> 138c2ecf20Sopenharmony_ci#include <linux/rtc.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* ISL register offsets */ 218c2ecf20Sopenharmony_ci#define ISL12022_REG_SC 0x00 228c2ecf20Sopenharmony_ci#define ISL12022_REG_MN 0x01 238c2ecf20Sopenharmony_ci#define ISL12022_REG_HR 0x02 248c2ecf20Sopenharmony_ci#define ISL12022_REG_DT 0x03 258c2ecf20Sopenharmony_ci#define ISL12022_REG_MO 0x04 268c2ecf20Sopenharmony_ci#define ISL12022_REG_YR 0x05 278c2ecf20Sopenharmony_ci#define ISL12022_REG_DW 0x06 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define ISL12022_REG_SR 0x07 308c2ecf20Sopenharmony_ci#define ISL12022_REG_INT 0x08 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* ISL register bits */ 338c2ecf20Sopenharmony_ci#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define ISL12022_SR_LBAT85 (1 << 2) 368c2ecf20Sopenharmony_ci#define ISL12022_SR_LBAT75 (1 << 1) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define ISL12022_INT_WRTC (1 << 6) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct i2c_driver isl12022_driver; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct isl12022 { 448c2ecf20Sopenharmony_ci struct rtc_device *rtc; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci bool write_enabled; /* true if write enable is set */ 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int isl12022_read_regs(struct i2c_client *client, uint8_t reg, 518c2ecf20Sopenharmony_ci uint8_t *data, size_t n) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .addr = client->addr, 568c2ecf20Sopenharmony_ci .flags = 0, 578c2ecf20Sopenharmony_ci .len = 1, 588c2ecf20Sopenharmony_ci .buf = data 598c2ecf20Sopenharmony_ci }, /* setup read ptr */ 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .addr = client->addr, 628c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 638c2ecf20Sopenharmony_ci .len = n, 648c2ecf20Sopenharmony_ci .buf = data 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci }; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci data[0] = reg; 718c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 728c2ecf20Sopenharmony_ci if (ret != ARRAY_SIZE(msgs)) { 738c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s: read error, ret=%d\n", 748c2ecf20Sopenharmony_ci __func__, ret); 758c2ecf20Sopenharmony_ci return -EIO; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int isl12022_write_reg(struct i2c_client *client, 838c2ecf20Sopenharmony_ci uint8_t reg, uint8_t val) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci uint8_t data[2] = { reg, val }; 868c2ecf20Sopenharmony_ci int err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci err = i2c_master_send(client, data, sizeof(data)); 898c2ecf20Sopenharmony_ci if (err != sizeof(data)) { 908c2ecf20Sopenharmony_ci dev_err(&client->dev, 918c2ecf20Sopenharmony_ci "%s: err=%d addr=%02x, data=%02x\n", 928c2ecf20Sopenharmony_ci __func__, err, data[0], data[1]); 938c2ecf20Sopenharmony_ci return -EIO; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * In the routines that deal directly with the isl12022 hardware, we use 1028c2ecf20Sopenharmony_ci * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 1078c2ecf20Sopenharmony_ci uint8_t buf[ISL12022_REG_INT + 1]; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf)); 1118c2ecf20Sopenharmony_ci if (ret) 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) { 1158c2ecf20Sopenharmony_ci dev_warn(&client->dev, 1168c2ecf20Sopenharmony_ci "voltage dropped below %u%%, " 1178c2ecf20Sopenharmony_ci "date and time is not reliable.\n", 1188c2ecf20Sopenharmony_ci buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 1228c2ecf20Sopenharmony_ci "%s: raw data is sec=%02x, min=%02x, hr=%02x, " 1238c2ecf20Sopenharmony_ci "mday=%02x, mon=%02x, year=%02x, wday=%02x, " 1248c2ecf20Sopenharmony_ci "sr=%02x, int=%02x", 1258c2ecf20Sopenharmony_ci __func__, 1268c2ecf20Sopenharmony_ci buf[ISL12022_REG_SC], 1278c2ecf20Sopenharmony_ci buf[ISL12022_REG_MN], 1288c2ecf20Sopenharmony_ci buf[ISL12022_REG_HR], 1298c2ecf20Sopenharmony_ci buf[ISL12022_REG_DT], 1308c2ecf20Sopenharmony_ci buf[ISL12022_REG_MO], 1318c2ecf20Sopenharmony_ci buf[ISL12022_REG_YR], 1328c2ecf20Sopenharmony_ci buf[ISL12022_REG_DW], 1338c2ecf20Sopenharmony_ci buf[ISL12022_REG_SR], 1348c2ecf20Sopenharmony_ci buf[ISL12022_REG_INT]); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F); 1378c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F); 1388c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F); 1398c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F); 1408c2ecf20Sopenharmony_ci tm->tm_wday = buf[ISL12022_REG_DW] & 0x07; 1418c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1; 1428c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " 1458c2ecf20Sopenharmony_ci "mday=%d, mon=%d, year=%d, wday=%d\n", 1468c2ecf20Sopenharmony_ci __func__, 1478c2ecf20Sopenharmony_ci tm->tm_sec, tm->tm_min, tm->tm_hour, 1488c2ecf20Sopenharmony_ci tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 1568c2ecf20Sopenharmony_ci struct isl12022 *isl12022 = i2c_get_clientdata(client); 1578c2ecf20Sopenharmony_ci size_t i; 1588c2ecf20Sopenharmony_ci int ret; 1598c2ecf20Sopenharmony_ci uint8_t buf[ISL12022_REG_DW + 1]; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " 1628c2ecf20Sopenharmony_ci "mday=%d, mon=%d, year=%d, wday=%d\n", 1638c2ecf20Sopenharmony_ci __func__, 1648c2ecf20Sopenharmony_ci tm->tm_sec, tm->tm_min, tm->tm_hour, 1658c2ecf20Sopenharmony_ci tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!isl12022->write_enabled) { 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1); 1708c2ecf20Sopenharmony_ci if (ret) 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Check if WRTC (write rtc enable) is set factory default is 1748c2ecf20Sopenharmony_ci * 0 (not set) */ 1758c2ecf20Sopenharmony_ci if (!(buf[0] & ISL12022_INT_WRTC)) { 1768c2ecf20Sopenharmony_ci dev_info(&client->dev, 1778c2ecf20Sopenharmony_ci "init write enable and 24 hour format\n"); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Set the write enable bit. */ 1808c2ecf20Sopenharmony_ci ret = isl12022_write_reg(client, 1818c2ecf20Sopenharmony_ci ISL12022_REG_INT, 1828c2ecf20Sopenharmony_ci buf[0] | ISL12022_INT_WRTC); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Write to any RTC register to start RTC, we use the 1878c2ecf20Sopenharmony_ci * HR register, setting the MIL bit to use the 24 hour 1888c2ecf20Sopenharmony_ci * format. */ 1898c2ecf20Sopenharmony_ci ret = isl12022_read_regs(client, ISL12022_REG_HR, 1908c2ecf20Sopenharmony_ci buf, 1); 1918c2ecf20Sopenharmony_ci if (ret) 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = isl12022_write_reg(client, 1958c2ecf20Sopenharmony_ci ISL12022_REG_HR, 1968c2ecf20Sopenharmony_ci buf[0] | ISL12022_HR_MIL); 1978c2ecf20Sopenharmony_ci if (ret) 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci isl12022->write_enabled = true; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* hours, minutes and seconds */ 2058c2ecf20Sopenharmony_ci buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec); 2068c2ecf20Sopenharmony_ci buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min); 2078c2ecf20Sopenharmony_ci buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* month, 1 - 12 */ 2128c2ecf20Sopenharmony_ci buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* year and century */ 2158c2ecf20Sopenharmony_ci buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci buf[ISL12022_REG_DW] = tm->tm_wday & 0x07; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* write register's data */ 2208c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(buf); i++) { 2218c2ecf20Sopenharmony_ci ret = isl12022_write_reg(client, ISL12022_REG_SC + i, 2228c2ecf20Sopenharmony_ci buf[ISL12022_REG_SC + i]); 2238c2ecf20Sopenharmony_ci if (ret) 2248c2ecf20Sopenharmony_ci return -EIO; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const struct rtc_class_ops isl12022_rtc_ops = { 2318c2ecf20Sopenharmony_ci .read_time = isl12022_rtc_read_time, 2328c2ecf20Sopenharmony_ci .set_time = isl12022_rtc_set_time, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int isl12022_probe(struct i2c_client *client, 2368c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct isl12022 *isl12022; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 2418c2ecf20Sopenharmony_ci return -ENODEV; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci isl12022 = devm_kzalloc(&client->dev, sizeof(struct isl12022), 2448c2ecf20Sopenharmony_ci GFP_KERNEL); 2458c2ecf20Sopenharmony_ci if (!isl12022) 2468c2ecf20Sopenharmony_ci return -ENOMEM; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci i2c_set_clientdata(client, isl12022); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci isl12022->rtc = devm_rtc_device_register(&client->dev, 2518c2ecf20Sopenharmony_ci isl12022_driver.driver.name, 2528c2ecf20Sopenharmony_ci &isl12022_rtc_ops, THIS_MODULE); 2538c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(isl12022->rtc); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2578c2ecf20Sopenharmony_cistatic const struct of_device_id isl12022_dt_match[] = { 2588c2ecf20Sopenharmony_ci { .compatible = "isl,isl12022" }, /* for backward compat., don't use */ 2598c2ecf20Sopenharmony_ci { .compatible = "isil,isl12022" }, 2608c2ecf20Sopenharmony_ci { }, 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, isl12022_dt_match); 2638c2ecf20Sopenharmony_ci#endif 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct i2c_device_id isl12022_id[] = { 2668c2ecf20Sopenharmony_ci { "isl12022", 0 }, 2678c2ecf20Sopenharmony_ci { } 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isl12022_id); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic struct i2c_driver isl12022_driver = { 2728c2ecf20Sopenharmony_ci .driver = { 2738c2ecf20Sopenharmony_ci .name = "rtc-isl12022", 2748c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2758c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(isl12022_dt_match), 2768c2ecf20Sopenharmony_ci#endif 2778c2ecf20Sopenharmony_ci }, 2788c2ecf20Sopenharmony_ci .probe = isl12022_probe, 2798c2ecf20Sopenharmony_ci .id_table = isl12022_id, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cimodule_i2c_driver(isl12022_driver); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMODULE_AUTHOR("roman.fietze@telemotive.de"); 2858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ISL 12022 RTC driver"); 2868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 287