1a9687aa2SEric Nelson /* 2a9687aa2SEric Nelson * drivers/rtc/rtc-pcf85363.c 3a9687aa2SEric Nelson * 4a9687aa2SEric Nelson * Driver for NXP PCF85363 real-time clock. 5a9687aa2SEric Nelson * 6a9687aa2SEric Nelson * Copyright (C) 2017 Eric Nelson 7a9687aa2SEric Nelson * 8a9687aa2SEric Nelson * This program is free software; you can redistribute it and/or modify 9a9687aa2SEric Nelson * it under the terms of the GNU General Public License version 2 as 10a9687aa2SEric Nelson * published by the Free Software Foundation. 11a9687aa2SEric Nelson * 12a9687aa2SEric Nelson * Based loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert 13a9687aa2SEric Nelson */ 14a9687aa2SEric Nelson #include <linux/module.h> 15a9687aa2SEric Nelson #include <linux/i2c.h> 16a9687aa2SEric Nelson #include <linux/slab.h> 17a9687aa2SEric Nelson #include <linux/rtc.h> 18a9687aa2SEric Nelson #include <linux/init.h> 19a9687aa2SEric Nelson #include <linux/err.h> 20a9687aa2SEric Nelson #include <linux/errno.h> 21a9687aa2SEric Nelson #include <linux/bcd.h> 22a9687aa2SEric Nelson #include <linux/of.h> 23a9687aa2SEric Nelson #include <linux/of_device.h> 24a9687aa2SEric Nelson #include <linux/regmap.h> 25a9687aa2SEric Nelson 26a9687aa2SEric Nelson /* 27a9687aa2SEric Nelson * Date/Time registers 28a9687aa2SEric Nelson */ 29a9687aa2SEric Nelson #define DT_100THS 0x00 30a9687aa2SEric Nelson #define DT_SECS 0x01 31a9687aa2SEric Nelson #define DT_MINUTES 0x02 32a9687aa2SEric Nelson #define DT_HOURS 0x03 33a9687aa2SEric Nelson #define DT_DAYS 0x04 34a9687aa2SEric Nelson #define DT_WEEKDAYS 0x05 35a9687aa2SEric Nelson #define DT_MONTHS 0x06 36a9687aa2SEric Nelson #define DT_YEARS 0x07 37a9687aa2SEric Nelson 38a9687aa2SEric Nelson /* 39a9687aa2SEric Nelson * Alarm registers 40a9687aa2SEric Nelson */ 41a9687aa2SEric Nelson #define DT_SECOND_ALM1 0x08 42a9687aa2SEric Nelson #define DT_MINUTE_ALM1 0x09 43a9687aa2SEric Nelson #define DT_HOUR_ALM1 0x0a 44a9687aa2SEric Nelson #define DT_DAY_ALM1 0x0b 45a9687aa2SEric Nelson #define DT_MONTH_ALM1 0x0c 46a9687aa2SEric Nelson #define DT_MINUTE_ALM2 0x0d 47a9687aa2SEric Nelson #define DT_HOUR_ALM2 0x0e 48a9687aa2SEric Nelson #define DT_WEEKDAY_ALM2 0x0f 49a9687aa2SEric Nelson #define DT_ALARM_EN 0x10 50a9687aa2SEric Nelson 51a9687aa2SEric Nelson /* 52a9687aa2SEric Nelson * Time stamp registers 53a9687aa2SEric Nelson */ 54a9687aa2SEric Nelson #define DT_TIMESTAMP1 0x11 55a9687aa2SEric Nelson #define DT_TIMESTAMP2 0x17 56a9687aa2SEric Nelson #define DT_TIMESTAMP3 0x1d 57a9687aa2SEric Nelson #define DT_TS_MODE 0x23 58a9687aa2SEric Nelson 59a9687aa2SEric Nelson /* 60a9687aa2SEric Nelson * control registers 61a9687aa2SEric Nelson */ 62a9687aa2SEric Nelson #define CTRL_OFFSET 0x24 63a9687aa2SEric Nelson #define CTRL_OSCILLATOR 0x25 64a9687aa2SEric Nelson #define CTRL_BATTERY 0x26 65a9687aa2SEric Nelson #define CTRL_PIN_IO 0x27 66a9687aa2SEric Nelson #define CTRL_FUNCTION 0x28 67a9687aa2SEric Nelson #define CTRL_INTA_EN 0x29 68a9687aa2SEric Nelson #define CTRL_INTB_EN 0x2a 69a9687aa2SEric Nelson #define CTRL_FLAGS 0x2b 70a9687aa2SEric Nelson #define CTRL_RAMBYTE 0x2c 71a9687aa2SEric Nelson #define CTRL_WDOG 0x2d 72a9687aa2SEric Nelson #define CTRL_STOP_EN 0x2e 73a9687aa2SEric Nelson #define CTRL_RESETS 0x2f 74a9687aa2SEric Nelson #define CTRL_RAM 0x40 75a9687aa2SEric Nelson 76a9687aa2SEric Nelson #define NVRAM_SIZE 0x40 77a9687aa2SEric Nelson 78a9687aa2SEric Nelson static struct i2c_driver pcf85363_driver; 79a9687aa2SEric Nelson 80a9687aa2SEric Nelson struct pcf85363 { 81a9687aa2SEric Nelson struct device *dev; 82a9687aa2SEric Nelson struct rtc_device *rtc; 83a9687aa2SEric Nelson struct regmap *regmap; 84a9687aa2SEric Nelson }; 85a9687aa2SEric Nelson 86a9687aa2SEric Nelson static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm) 87a9687aa2SEric Nelson { 88a9687aa2SEric Nelson struct pcf85363 *pcf85363 = dev_get_drvdata(dev); 89a9687aa2SEric Nelson unsigned char buf[DT_YEARS + 1]; 90a9687aa2SEric Nelson int ret, len = sizeof(buf); 91a9687aa2SEric Nelson 92a9687aa2SEric Nelson /* read the RTC date and time registers all at once */ 93a9687aa2SEric Nelson ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len); 94a9687aa2SEric Nelson if (ret) { 95a9687aa2SEric Nelson dev_err(dev, "%s: error %d\n", __func__, ret); 96a9687aa2SEric Nelson return ret; 97a9687aa2SEric Nelson } 98a9687aa2SEric Nelson 99a9687aa2SEric Nelson tm->tm_year = bcd2bin(buf[DT_YEARS]); 100a9687aa2SEric Nelson /* adjust for 1900 base of rtc_time */ 101a9687aa2SEric Nelson tm->tm_year += 100; 102a9687aa2SEric Nelson 103a9687aa2SEric Nelson tm->tm_wday = buf[DT_WEEKDAYS] & 7; 104a9687aa2SEric Nelson buf[DT_SECS] &= 0x7F; 105a9687aa2SEric Nelson tm->tm_sec = bcd2bin(buf[DT_SECS]); 106a9687aa2SEric Nelson buf[DT_MINUTES] &= 0x7F; 107a9687aa2SEric Nelson tm->tm_min = bcd2bin(buf[DT_MINUTES]); 108a9687aa2SEric Nelson tm->tm_hour = bcd2bin(buf[DT_HOURS]); 109a9687aa2SEric Nelson tm->tm_mday = bcd2bin(buf[DT_DAYS]); 110a9687aa2SEric Nelson tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1; 111a9687aa2SEric Nelson 112a9687aa2SEric Nelson return 0; 113a9687aa2SEric Nelson } 114a9687aa2SEric Nelson 115a9687aa2SEric Nelson static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) 116a9687aa2SEric Nelson { 117a9687aa2SEric Nelson struct pcf85363 *pcf85363 = dev_get_drvdata(dev); 118a9687aa2SEric Nelson unsigned char buf[DT_YEARS + 1]; 119a9687aa2SEric Nelson int len = sizeof(buf); 120a9687aa2SEric Nelson 121a9687aa2SEric Nelson buf[DT_100THS] = 0; 122a9687aa2SEric Nelson buf[DT_SECS] = bin2bcd(tm->tm_sec); 123a9687aa2SEric Nelson buf[DT_MINUTES] = bin2bcd(tm->tm_min); 124a9687aa2SEric Nelson buf[DT_HOURS] = bin2bcd(tm->tm_hour); 125a9687aa2SEric Nelson buf[DT_DAYS] = bin2bcd(tm->tm_mday); 126a9687aa2SEric Nelson buf[DT_WEEKDAYS] = tm->tm_wday; 127a9687aa2SEric Nelson buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); 128a9687aa2SEric Nelson buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); 129a9687aa2SEric Nelson 130a9687aa2SEric Nelson return regmap_bulk_write(pcf85363->regmap, DT_100THS, 131a9687aa2SEric Nelson buf, len); 132a9687aa2SEric Nelson } 133a9687aa2SEric Nelson 134a9687aa2SEric Nelson static const struct rtc_class_ops rtc_ops = { 135a9687aa2SEric Nelson .read_time = pcf85363_rtc_read_time, 136a9687aa2SEric Nelson .set_time = pcf85363_rtc_set_time, 137a9687aa2SEric Nelson }; 138a9687aa2SEric Nelson 139a9687aa2SEric Nelson static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val, 140a9687aa2SEric Nelson size_t bytes) 141a9687aa2SEric Nelson { 142a9687aa2SEric Nelson struct pcf85363 *pcf85363 = priv; 143a9687aa2SEric Nelson 144a9687aa2SEric Nelson return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset, 145a9687aa2SEric Nelson val, bytes); 146a9687aa2SEric Nelson } 147a9687aa2SEric Nelson 148a9687aa2SEric Nelson static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val, 149a9687aa2SEric Nelson size_t bytes) 150a9687aa2SEric Nelson { 151a9687aa2SEric Nelson struct pcf85363 *pcf85363 = priv; 152a9687aa2SEric Nelson 153a9687aa2SEric Nelson return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset, 154a9687aa2SEric Nelson val, bytes); 155a9687aa2SEric Nelson } 156a9687aa2SEric Nelson 157a9687aa2SEric Nelson static const struct regmap_config regmap_config = { 158a9687aa2SEric Nelson .reg_bits = 8, 159a9687aa2SEric Nelson .val_bits = 8, 160*c57849ddSAlexandre Belloni .max_register = 0x7f, 161a9687aa2SEric Nelson }; 162a9687aa2SEric Nelson 163a9687aa2SEric Nelson static int pcf85363_probe(struct i2c_client *client, 164a9687aa2SEric Nelson const struct i2c_device_id *id) 165a9687aa2SEric Nelson { 166a9687aa2SEric Nelson struct pcf85363 *pcf85363; 1670e7a412fSAlexandre Belloni struct nvmem_config nvmem_cfg = { 1680e7a412fSAlexandre Belloni .name = "pcf85363-", 1690e7a412fSAlexandre Belloni .word_size = 1, 1700e7a412fSAlexandre Belloni .stride = 1, 1710e7a412fSAlexandre Belloni .size = NVRAM_SIZE, 1720e7a412fSAlexandre Belloni .reg_read = pcf85363_nvram_read, 1730e7a412fSAlexandre Belloni .reg_write = pcf85363_nvram_write, 1740e7a412fSAlexandre Belloni }; 17524849d17SAlexandre Belloni int ret; 176a9687aa2SEric Nelson 177a9687aa2SEric Nelson if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 178a9687aa2SEric Nelson return -ENODEV; 179a9687aa2SEric Nelson 180a9687aa2SEric Nelson pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363), 181a9687aa2SEric Nelson GFP_KERNEL); 182a9687aa2SEric Nelson if (!pcf85363) 183a9687aa2SEric Nelson return -ENOMEM; 184a9687aa2SEric Nelson 185a9687aa2SEric Nelson pcf85363->regmap = devm_regmap_init_i2c(client, ®map_config); 186a9687aa2SEric Nelson if (IS_ERR(pcf85363->regmap)) { 187a9687aa2SEric Nelson dev_err(&client->dev, "regmap allocation failed\n"); 188a9687aa2SEric Nelson return PTR_ERR(pcf85363->regmap); 189a9687aa2SEric Nelson } 190a9687aa2SEric Nelson 191a9687aa2SEric Nelson pcf85363->dev = &client->dev; 192a9687aa2SEric Nelson i2c_set_clientdata(client, pcf85363); 193a9687aa2SEric Nelson 194a9687aa2SEric Nelson pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev); 195a9687aa2SEric Nelson if (IS_ERR(pcf85363->rtc)) 196a9687aa2SEric Nelson return PTR_ERR(pcf85363->rtc); 197a9687aa2SEric Nelson 198a9687aa2SEric Nelson pcf85363->rtc->ops = &rtc_ops; 199a9687aa2SEric Nelson 20024849d17SAlexandre Belloni ret = rtc_register_device(pcf85363->rtc); 20124849d17SAlexandre Belloni 2020e7a412fSAlexandre Belloni nvmem_cfg.priv = pcf85363; 2030e7a412fSAlexandre Belloni rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg); 20424849d17SAlexandre Belloni 20524849d17SAlexandre Belloni return ret; 206a9687aa2SEric Nelson } 207a9687aa2SEric Nelson 208a9687aa2SEric Nelson static const struct of_device_id dev_ids[] = { 209a9687aa2SEric Nelson { .compatible = "nxp,pcf85363" }, 210a9687aa2SEric Nelson {} 211a9687aa2SEric Nelson }; 212a9687aa2SEric Nelson MODULE_DEVICE_TABLE(of, dev_ids); 213a9687aa2SEric Nelson 214a9687aa2SEric Nelson static struct i2c_driver pcf85363_driver = { 215a9687aa2SEric Nelson .driver = { 216a9687aa2SEric Nelson .name = "pcf85363", 217a9687aa2SEric Nelson .of_match_table = of_match_ptr(dev_ids), 218a9687aa2SEric Nelson }, 219a9687aa2SEric Nelson .probe = pcf85363_probe, 220a9687aa2SEric Nelson }; 221a9687aa2SEric Nelson 222a9687aa2SEric Nelson module_i2c_driver(pcf85363_driver); 223a9687aa2SEric Nelson 224a9687aa2SEric Nelson MODULE_AUTHOR("Eric Nelson"); 225a9687aa2SEric Nelson MODULE_DESCRIPTION("pcf85363 I2C RTC driver"); 226a9687aa2SEric Nelson MODULE_LICENSE("GPL"); 227