Blame view
drivers/rtc/rtc-pcf2127.c
10.3 KB
18cb6368f rtc: add NXP PCF2... |
1 |
/* |
cee2cc215 rtc: pcf2127: add... |
2 |
* An I2C and SPI driver for the NXP PCF2127/29 RTC |
18cb6368f rtc: add NXP PCF2... |
3 4 5 6 7 8 |
* Copyright 2013 Til-Technologies * * Author: Renaud Cerrato <r.cerrato@til-technologies.fr> * * based on the other drivers in this same directory. * |
cee2cc215 rtc: pcf2127: add... |
9 |
* Datasheet: http://cache.nxp.com/documents/data_sheet/PCF2127.pdf |
18cb6368f rtc: add NXP PCF2... |
10 11 12 13 14 15 16 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/i2c.h> |
9408ec1af rtc: pcf2127: add... |
17 |
#include <linux/spi/spi.h> |
18cb6368f rtc: add NXP PCF2... |
18 19 20 21 22 |
#include <linux/bcd.h> #include <linux/rtc.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/of.h> |
907b32625 rtc: pcf2127: con... |
23 |
#include <linux/regmap.h> |
18cb6368f rtc: add NXP PCF2... |
24 |
|
18cb6368f rtc: add NXP PCF2... |
25 26 |
#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */ #define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */ |
f97cfddc8 rtc: pcf2127: fix... |
27 |
|
18cb6368f rtc: add NXP PCF2... |
28 |
#define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */ |
f97cfddc8 rtc: pcf2127: fix... |
29 |
#define PCF2127_REG_CTRL3_BLF BIT(2) |
18cb6368f rtc: add NXP PCF2... |
30 31 32 33 34 35 36 |
#define PCF2127_REG_SC (0x03) /* datetime */ #define PCF2127_REG_MN (0x04) #define PCF2127_REG_HR (0x05) #define PCF2127_REG_DM (0x06) #define PCF2127_REG_DW (0x07) #define PCF2127_REG_MO (0x08) #define PCF2127_REG_YR (0x09) |
653ebd75e rtc: pcf2127: use... |
37 |
#define PCF2127_OSF BIT(7) /* Oscillator Fail flag */ |
18cb6368f rtc: add NXP PCF2... |
38 39 |
struct pcf2127 { struct rtc_device *rtc; |
907b32625 rtc: pcf2127: con... |
40 |
struct regmap *regmap; |
18cb6368f rtc: add NXP PCF2... |
41 42 43 44 45 46 |
}; /* * In the routines that deal directly with the pcf2127 hardware, we use * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. */ |
907b32625 rtc: pcf2127: con... |
47 |
static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) |
18cb6368f rtc: add NXP PCF2... |
48 |
{ |
907b32625 rtc: pcf2127: con... |
49 50 51 |
struct pcf2127 *pcf2127 = dev_get_drvdata(dev); unsigned char buf[10]; int ret; |
3769a375a rtc: pcf2127: bul... |
52 |
int i; |
18cb6368f rtc: add NXP PCF2... |
53 |
|
3769a375a rtc: pcf2127: bul... |
54 55 56 57 58 59 60 61 62 63 64 65 66 |
for (i = 0; i <= PCF2127_REG_CTRL3; i++) { ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1 + i, (unsigned int *)(buf + i)); if (ret) { dev_err(dev, "%s: read error ", __func__); return ret; } } ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_SC, (buf + PCF2127_REG_SC), ARRAY_SIZE(buf) - PCF2127_REG_SC); |
907b32625 rtc: pcf2127: con... |
67 68 69 70 |
if (ret) { dev_err(dev, "%s: read error ", __func__); return ret; |
18cb6368f rtc: add NXP PCF2... |
71 |
} |
f97cfddc8 rtc: pcf2127: fix... |
72 |
if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF) |
907b32625 rtc: pcf2127: con... |
73 |
dev_info(dev, |
653ebd75e rtc: pcf2127: use... |
74 75 |
"low voltage detected, check/replace RTC battery. "); |
653ebd75e rtc: pcf2127: use... |
76 77 78 79 80 81 |
if (buf[PCF2127_REG_SC] & PCF2127_OSF) { /* * no need clear the flag here, * it will be cleared once the new date is saved */ |
907b32625 rtc: pcf2127: con... |
82 |
dev_warn(dev, |
653ebd75e rtc: pcf2127: use... |
83 84 85 |
"oscillator stop detected, date/time is not reliable "); return -EINVAL; |
18cb6368f rtc: add NXP PCF2... |
86 |
} |
907b32625 rtc: pcf2127: con... |
87 |
dev_dbg(dev, |
18cb6368f rtc: add NXP PCF2... |
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
"%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, " "sec=%02x, min=%02x, hr=%02x, " "mday=%02x, wday=%02x, mon=%02x, year=%02x ", __func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]); tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F); tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F); tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */ tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F); tm->tm_wday = buf[PCF2127_REG_DW] & 0x07; tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]); if (tm->tm_year < 70) tm->tm_year += 100; /* assume we are in 1970...2069 */ |
907b32625 rtc: pcf2127: con... |
107 |
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " |
18cb6368f rtc: add NXP PCF2... |
108 109 110 111 112 |
"mday=%d, mon=%d, year=%d, wday=%d ", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); |
821f51c4d rtc: use rtc_vali... |
113 |
return rtc_valid_tm(tm); |
18cb6368f rtc: add NXP PCF2... |
114 |
} |
907b32625 rtc: pcf2127: con... |
115 |
static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm) |
18cb6368f rtc: add NXP PCF2... |
116 |
{ |
907b32625 rtc: pcf2127: con... |
117 118 |
struct pcf2127 *pcf2127 = dev_get_drvdata(dev); unsigned char buf[7]; |
18cb6368f rtc: add NXP PCF2... |
119 |
int i = 0, err; |
907b32625 rtc: pcf2127: con... |
120 |
dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " |
18cb6368f rtc: add NXP PCF2... |
121 122 123 124 125 |
"mday=%d, mon=%d, year=%d, wday=%d ", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); |
18cb6368f rtc: add NXP PCF2... |
126 |
/* hours, minutes and seconds */ |
653ebd75e rtc: pcf2127: use... |
127 |
buf[i++] = bin2bcd(tm->tm_sec); /* this will also clear OSF flag */ |
18cb6368f rtc: add NXP PCF2... |
128 129 130 131 132 133 134 135 136 137 138 139 |
buf[i++] = bin2bcd(tm->tm_min); buf[i++] = bin2bcd(tm->tm_hour); buf[i++] = bin2bcd(tm->tm_mday); buf[i++] = tm->tm_wday & 0x07; /* month, 1 - 12 */ buf[i++] = bin2bcd(tm->tm_mon + 1); /* year */ buf[i++] = bin2bcd(tm->tm_year % 100); /* write register's data */ |
907b32625 rtc: pcf2127: con... |
140 141 142 |
err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i); if (err) { dev_err(dev, |
18cb6368f rtc: add NXP PCF2... |
143 |
"%s: err=%d", __func__, err); |
907b32625 rtc: pcf2127: con... |
144 |
return err; |
18cb6368f rtc: add NXP PCF2... |
145 146 147 148 149 150 151 152 153 |
} return 0; } #ifdef CONFIG_RTC_INTF_DEV static int pcf2127_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { |
907b32625 rtc: pcf2127: con... |
154 |
struct pcf2127 *pcf2127 = dev_get_drvdata(dev); |
f97cfddc8 rtc: pcf2127: fix... |
155 156 |
int touser; int ret; |
18cb6368f rtc: add NXP PCF2... |
157 158 159 |
switch (cmd) { case RTC_VL_READ: |
907b32625 rtc: pcf2127: con... |
160 161 |
ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &touser); if (ret) |
f97cfddc8 rtc: pcf2127: fix... |
162 |
return ret; |
907b32625 rtc: pcf2127: con... |
163 |
touser = touser & PCF2127_REG_CTRL3_BLF ? 1 : 0; |
18cb6368f rtc: add NXP PCF2... |
164 |
|
f97cfddc8 rtc: pcf2127: fix... |
165 |
if (copy_to_user((void __user *)arg, &touser, sizeof(int))) |
18cb6368f rtc: add NXP PCF2... |
166 167 168 169 170 171 172 173 174 |
return -EFAULT; return 0; default: return -ENOIOCTLCMD; } } #else #define pcf2127_rtc_ioctl NULL #endif |
18cb6368f rtc: add NXP PCF2... |
175 176 177 178 179 |
static const struct rtc_class_ops pcf2127_rtc_ops = { .ioctl = pcf2127_rtc_ioctl, .read_time = pcf2127_rtc_read_time, .set_time = pcf2127_rtc_set_time, }; |
907b32625 rtc: pcf2127: con... |
180 181 |
static int pcf2127_probe(struct device *dev, struct regmap *regmap, const char *name) |
18cb6368f rtc: add NXP PCF2... |
182 183 |
{ struct pcf2127 *pcf2127; |
907b32625 rtc: pcf2127: con... |
184 185 |
dev_dbg(dev, "%s ", __func__); |
18cb6368f rtc: add NXP PCF2... |
186 |
|
907b32625 rtc: pcf2127: con... |
187 |
pcf2127 = devm_kzalloc(dev, sizeof(*pcf2127), GFP_KERNEL); |
18cb6368f rtc: add NXP PCF2... |
188 189 |
if (!pcf2127) return -ENOMEM; |
907b32625 rtc: pcf2127: con... |
190 |
pcf2127->regmap = regmap; |
18cb6368f rtc: add NXP PCF2... |
191 |
|
907b32625 rtc: pcf2127: con... |
192 193 194 195 |
dev_set_drvdata(dev, pcf2127); pcf2127->rtc = devm_rtc_device_register(dev, name, &pcf2127_rtc_ops, THIS_MODULE); |
18cb6368f rtc: add NXP PCF2... |
196 |
|
7ab26cd1e drivers/rtc/rtc-p... |
197 |
return PTR_ERR_OR_ZERO(pcf2127->rtc); |
18cb6368f rtc: add NXP PCF2... |
198 |
} |
18cb6368f rtc: add NXP PCF2... |
199 200 201 |
#ifdef CONFIG_OF static const struct of_device_id pcf2127_of_match[] = { { .compatible = "nxp,pcf2127" }, |
cee2cc215 rtc: pcf2127: add... |
202 |
{ .compatible = "nxp,pcf2129" }, |
18cb6368f rtc: add NXP PCF2... |
203 204 205 206 |
{} }; MODULE_DEVICE_TABLE(of, pcf2127_of_match); #endif |
9408ec1af rtc: pcf2127: add... |
207 |
#if IS_ENABLED(CONFIG_I2C) |
907b32625 rtc: pcf2127: con... |
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
static int pcf2127_i2c_write(void *context, const void *data, size_t count) { struct device *dev = context; struct i2c_client *client = to_i2c_client(dev); int ret; ret = i2c_master_send(client, data, count); if (ret != count) return ret < 0 ? ret : -EIO; return 0; } static int pcf2127_i2c_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { struct device *dev = context; struct i2c_client *client = to_i2c_client(dev); int ret; void *buf; if (WARN_ON(reg_size != 1)) return -EINVAL; buf = kmalloc(val_size + 1, GFP_KERNEL); if (!buf) return -ENOMEM; memcpy(buf, reg, 1); memcpy(buf + 1, val, val_size); ret = i2c_master_send(client, buf, val_size + 1); |
6e4fbdc7a rtc: pcf2127: fix... |
241 242 |
kfree(buf); |
907b32625 rtc: pcf2127: con... |
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
if (ret != val_size + 1) return ret < 0 ? ret : -EIO; return 0; } static int pcf2127_i2c_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { struct device *dev = context; struct i2c_client *client = to_i2c_client(dev); int ret; if (WARN_ON(reg_size != 1)) return -EINVAL; ret = i2c_master_send(client, reg, 1); if (ret != 1) return ret < 0 ? ret : -EIO; ret = i2c_master_recv(client, val, val_size); if (ret != val_size) return ret < 0 ? ret : -EIO; return 0; } /* * The reason we need this custom regmap_bus instead of using regmap_init_i2c() * is that the STOP condition is required between set register address and * read register data when reading from registers. */ static const struct regmap_bus pcf2127_i2c_regmap = { .write = pcf2127_i2c_write, .gather_write = pcf2127_i2c_gather_write, .read = pcf2127_i2c_read, }; static struct i2c_driver pcf2127_i2c_driver; static int pcf2127_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct regmap *regmap; static const struct regmap_config config = { .reg_bits = 8, .val_bits = 8, }; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap, &client->dev, &config); if (IS_ERR(regmap)) { dev_err(&client->dev, "%s: regmap allocation failed: %ld ", __func__, PTR_ERR(regmap)); return PTR_ERR(regmap); } return pcf2127_probe(&client->dev, regmap, pcf2127_i2c_driver.driver.name); } static const struct i2c_device_id pcf2127_i2c_id[] = { { "pcf2127", 0 }, |
cee2cc215 rtc: pcf2127: add... |
310 |
{ "pcf2129", 0 }, |
907b32625 rtc: pcf2127: con... |
311 312 313 314 315 |
{ } }; MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); static struct i2c_driver pcf2127_i2c_driver = { |
18cb6368f rtc: add NXP PCF2... |
316 |
.driver = { |
907b32625 rtc: pcf2127: con... |
317 |
.name = "rtc-pcf2127-i2c", |
18cb6368f rtc: add NXP PCF2... |
318 319 |
.of_match_table = of_match_ptr(pcf2127_of_match), }, |
907b32625 rtc: pcf2127: con... |
320 321 |
.probe = pcf2127_i2c_probe, .id_table = pcf2127_i2c_id, |
18cb6368f rtc: add NXP PCF2... |
322 |
}; |
9408ec1af rtc: pcf2127: add... |
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
static int pcf2127_i2c_register_driver(void) { return i2c_add_driver(&pcf2127_i2c_driver); } static void pcf2127_i2c_unregister_driver(void) { i2c_del_driver(&pcf2127_i2c_driver); } #else static int pcf2127_i2c_register_driver(void) { return 0; } static void pcf2127_i2c_unregister_driver(void) { } #endif #if IS_ENABLED(CONFIG_SPI_MASTER) static struct spi_driver pcf2127_spi_driver; static int pcf2127_spi_probe(struct spi_device *spi) { static const struct regmap_config config = { .reg_bits = 8, .val_bits = 8, .read_flag_mask = 0xa0, .write_flag_mask = 0x20, }; struct regmap *regmap; regmap = devm_regmap_init_spi(spi, &config); if (IS_ERR(regmap)) { dev_err(&spi->dev, "%s: regmap allocation failed: %ld ", __func__, PTR_ERR(regmap)); return PTR_ERR(regmap); } return pcf2127_probe(&spi->dev, regmap, pcf2127_spi_driver.driver.name); } static const struct spi_device_id pcf2127_spi_id[] = { { "pcf2127", 0 }, |
cee2cc215 rtc: pcf2127: add... |
374 |
{ "pcf2129", 0 }, |
9408ec1af rtc: pcf2127: add... |
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
{ } }; MODULE_DEVICE_TABLE(spi, pcf2127_spi_id); static struct spi_driver pcf2127_spi_driver = { .driver = { .name = "rtc-pcf2127-spi", .of_match_table = of_match_ptr(pcf2127_of_match), }, .probe = pcf2127_spi_probe, .id_table = pcf2127_spi_id, }; static int pcf2127_spi_register_driver(void) { return spi_register_driver(&pcf2127_spi_driver); } static void pcf2127_spi_unregister_driver(void) { spi_unregister_driver(&pcf2127_spi_driver); } #else static int pcf2127_spi_register_driver(void) { return 0; } static void pcf2127_spi_unregister_driver(void) { } #endif static int __init pcf2127_init(void) { int ret; ret = pcf2127_i2c_register_driver(); if (ret) { pr_err("Failed to register pcf2127 i2c driver: %d ", ret); return ret; } ret = pcf2127_spi_register_driver(); if (ret) { pr_err("Failed to register pcf2127 spi driver: %d ", ret); pcf2127_i2c_unregister_driver(); } return ret; } module_init(pcf2127_init) static void __exit pcf2127_exit(void) { pcf2127_spi_unregister_driver(); pcf2127_i2c_unregister_driver(); } module_exit(pcf2127_exit) |
18cb6368f rtc: add NXP PCF2... |
439 440 |
MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>"); |
cee2cc215 rtc: pcf2127: add... |
441 |
MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver"); |
4d8318bc9 rtc: pcf2127: mak... |
442 |
MODULE_LICENSE("GPL v2"); |