Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: add support for NXP PCF85363 real-time clock

Note that alarms are not currently implemented.

64 bytes of nvmem is supported and exposed in
sysfs (# is the instance number, starting with 0):

/sys/bus/nvmem/devices/pcf85363-#/nvmem

Signed-off-by: Eric Nelson <eric@nelint.com>
Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
Tested-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

authored by

Eric Nelson and committed by
Alexandre Belloni
a9687aa2 b6ee15ef

+251
+17
Documentation/devicetree/bindings/rtc/pcf85363.txt
··· 1 + NXP PCF85363 Real Time Clock 2 + ============================ 3 + 4 + Required properties: 5 + - compatible: Should contain "nxp,pcf85363". 6 + - reg: I2C address for chip. 7 + 8 + Optional properties: 9 + - interrupts: IRQ line for the RTC (not implemented). 10 + 11 + Example: 12 + 13 + pcf85363: pcf85363@51 { 14 + compatible = "nxp,pcf85363"; 15 + reg = <0x51>; 16 + }; 17 +
+13
drivers/rtc/Kconfig
··· 433 433 This driver can also be built as a module. If so, the module 434 434 will be called rtc-pcf85063. 435 435 436 + config RTC_DRV_PCF85363 437 + tristate "NXP PCF85363" 438 + depends on I2C 439 + select REGMAP_I2C 440 + help 441 + If you say yes here you get support for the PCF85363 RTC chip. 442 + 443 + This driver can also be built as a module. If so, the module 444 + will be called rtc-pcf85363. 445 + 446 + The nvmem interface will be named pcf85363-#, where # is the 447 + zero-based instance number. 448 + 436 449 config RTC_DRV_PCF8563 437 450 tristate "Philips PCF8563/Epson RTC8564" 438 451 help
+1
drivers/rtc/Makefile
··· 114 114 obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o 115 115 obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o 116 116 obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o 117 + obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o 117 118 obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o 118 119 obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o 119 120 obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
+220
drivers/rtc/rtc-pcf85363.c
··· 1 + /* 2 + * drivers/rtc/rtc-pcf85363.c 3 + * 4 + * Driver for NXP PCF85363 real-time clock. 5 + * 6 + * Copyright (C) 2017 Eric Nelson 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + * 12 + * Based loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert 13 + */ 14 + #include <linux/module.h> 15 + #include <linux/i2c.h> 16 + #include <linux/slab.h> 17 + #include <linux/rtc.h> 18 + #include <linux/init.h> 19 + #include <linux/err.h> 20 + #include <linux/errno.h> 21 + #include <linux/bcd.h> 22 + #include <linux/of.h> 23 + #include <linux/of_device.h> 24 + #include <linux/regmap.h> 25 + 26 + /* 27 + * Date/Time registers 28 + */ 29 + #define DT_100THS 0x00 30 + #define DT_SECS 0x01 31 + #define DT_MINUTES 0x02 32 + #define DT_HOURS 0x03 33 + #define DT_DAYS 0x04 34 + #define DT_WEEKDAYS 0x05 35 + #define DT_MONTHS 0x06 36 + #define DT_YEARS 0x07 37 + 38 + /* 39 + * Alarm registers 40 + */ 41 + #define DT_SECOND_ALM1 0x08 42 + #define DT_MINUTE_ALM1 0x09 43 + #define DT_HOUR_ALM1 0x0a 44 + #define DT_DAY_ALM1 0x0b 45 + #define DT_MONTH_ALM1 0x0c 46 + #define DT_MINUTE_ALM2 0x0d 47 + #define DT_HOUR_ALM2 0x0e 48 + #define DT_WEEKDAY_ALM2 0x0f 49 + #define DT_ALARM_EN 0x10 50 + 51 + /* 52 + * Time stamp registers 53 + */ 54 + #define DT_TIMESTAMP1 0x11 55 + #define DT_TIMESTAMP2 0x17 56 + #define DT_TIMESTAMP3 0x1d 57 + #define DT_TS_MODE 0x23 58 + 59 + /* 60 + * control registers 61 + */ 62 + #define CTRL_OFFSET 0x24 63 + #define CTRL_OSCILLATOR 0x25 64 + #define CTRL_BATTERY 0x26 65 + #define CTRL_PIN_IO 0x27 66 + #define CTRL_FUNCTION 0x28 67 + #define CTRL_INTA_EN 0x29 68 + #define CTRL_INTB_EN 0x2a 69 + #define CTRL_FLAGS 0x2b 70 + #define CTRL_RAMBYTE 0x2c 71 + #define CTRL_WDOG 0x2d 72 + #define CTRL_STOP_EN 0x2e 73 + #define CTRL_RESETS 0x2f 74 + #define CTRL_RAM 0x40 75 + 76 + #define NVRAM_SIZE 0x40 77 + 78 + static struct i2c_driver pcf85363_driver; 79 + 80 + struct pcf85363 { 81 + struct device *dev; 82 + struct rtc_device *rtc; 83 + struct nvmem_config nvmem_cfg; 84 + struct regmap *regmap; 85 + }; 86 + 87 + static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm) 88 + { 89 + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); 90 + unsigned char buf[DT_YEARS + 1]; 91 + int ret, len = sizeof(buf); 92 + 93 + /* read the RTC date and time registers all at once */ 94 + ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len); 95 + if (ret) { 96 + dev_err(dev, "%s: error %d\n", __func__, ret); 97 + return ret; 98 + } 99 + 100 + tm->tm_year = bcd2bin(buf[DT_YEARS]); 101 + /* adjust for 1900 base of rtc_time */ 102 + tm->tm_year += 100; 103 + 104 + tm->tm_wday = buf[DT_WEEKDAYS] & 7; 105 + buf[DT_SECS] &= 0x7F; 106 + tm->tm_sec = bcd2bin(buf[DT_SECS]); 107 + buf[DT_MINUTES] &= 0x7F; 108 + tm->tm_min = bcd2bin(buf[DT_MINUTES]); 109 + tm->tm_hour = bcd2bin(buf[DT_HOURS]); 110 + tm->tm_mday = bcd2bin(buf[DT_DAYS]); 111 + tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1; 112 + 113 + return 0; 114 + } 115 + 116 + static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) 117 + { 118 + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); 119 + unsigned char buf[DT_YEARS + 1]; 120 + int len = sizeof(buf); 121 + 122 + buf[DT_100THS] = 0; 123 + buf[DT_SECS] = bin2bcd(tm->tm_sec); 124 + buf[DT_MINUTES] = bin2bcd(tm->tm_min); 125 + buf[DT_HOURS] = bin2bcd(tm->tm_hour); 126 + buf[DT_DAYS] = bin2bcd(tm->tm_mday); 127 + buf[DT_WEEKDAYS] = tm->tm_wday; 128 + buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); 129 + buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); 130 + 131 + return regmap_bulk_write(pcf85363->regmap, DT_100THS, 132 + buf, len); 133 + } 134 + 135 + static const struct rtc_class_ops rtc_ops = { 136 + .read_time = pcf85363_rtc_read_time, 137 + .set_time = pcf85363_rtc_set_time, 138 + }; 139 + 140 + static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val, 141 + size_t bytes) 142 + { 143 + struct pcf85363 *pcf85363 = priv; 144 + 145 + return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset, 146 + val, bytes); 147 + } 148 + 149 + static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val, 150 + size_t bytes) 151 + { 152 + struct pcf85363 *pcf85363 = priv; 153 + 154 + return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset, 155 + val, bytes); 156 + } 157 + 158 + static const struct regmap_config regmap_config = { 159 + .reg_bits = 8, 160 + .val_bits = 8, 161 + }; 162 + 163 + static int pcf85363_probe(struct i2c_client *client, 164 + const struct i2c_device_id *id) 165 + { 166 + struct pcf85363 *pcf85363; 167 + 168 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 169 + return -ENODEV; 170 + 171 + pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363), 172 + GFP_KERNEL); 173 + if (!pcf85363) 174 + return -ENOMEM; 175 + 176 + pcf85363->regmap = devm_regmap_init_i2c(client, &regmap_config); 177 + if (IS_ERR(pcf85363->regmap)) { 178 + dev_err(&client->dev, "regmap allocation failed\n"); 179 + return PTR_ERR(pcf85363->regmap); 180 + } 181 + 182 + pcf85363->dev = &client->dev; 183 + i2c_set_clientdata(client, pcf85363); 184 + 185 + pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev); 186 + if (IS_ERR(pcf85363->rtc)) 187 + return PTR_ERR(pcf85363->rtc); 188 + 189 + pcf85363->nvmem_cfg.name = "pcf85363-"; 190 + pcf85363->nvmem_cfg.word_size = 1; 191 + pcf85363->nvmem_cfg.stride = 1; 192 + pcf85363->nvmem_cfg.size = NVRAM_SIZE; 193 + pcf85363->nvmem_cfg.reg_read = pcf85363_nvram_read; 194 + pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write; 195 + pcf85363->nvmem_cfg.priv = pcf85363; 196 + pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg; 197 + pcf85363->rtc->ops = &rtc_ops; 198 + 199 + return rtc_register_device(pcf85363->rtc); 200 + } 201 + 202 + static const struct of_device_id dev_ids[] = { 203 + { .compatible = "nxp,pcf85363" }, 204 + {} 205 + }; 206 + MODULE_DEVICE_TABLE(of, dev_ids); 207 + 208 + static struct i2c_driver pcf85363_driver = { 209 + .driver = { 210 + .name = "pcf85363", 211 + .of_match_table = of_match_ptr(dev_ids), 212 + }, 213 + .probe = pcf85363_probe, 214 + }; 215 + 216 + module_i2c_driver(pcf85363_driver); 217 + 218 + MODULE_AUTHOR("Eric Nelson"); 219 + MODULE_DESCRIPTION("pcf85363 I2C RTC driver"); 220 + MODULE_LICENSE("GPL");