|
25 | 25 |
|
26 | 26 | /* Control register 1 */
|
27 | 27 | #define PCF2127_REG_CTRL1 0x00
|
| 28 | +#define PCF2127_BIT_CTRL1_TSF1 BIT(4) |
28 | 29 | /* Control register 2 */
|
29 | 30 | #define PCF2127_REG_CTRL2 0x01
|
| 31 | +#define PCF2127_BIT_CTRL2_TSIE BIT(2) |
| 32 | +#define PCF2127_BIT_CTRL2_TSF2 BIT(5) |
30 | 33 | /* Control register 3 */
|
31 | 34 | #define PCF2127_REG_CTRL3 0x02
|
| 35 | +#define PCF2127_BIT_CTRL3_BLIE BIT(0) |
| 36 | +#define PCF2127_BIT_CTRL3_BIE BIT(1) |
32 | 37 | #define PCF2127_BIT_CTRL3_BLF BIT(2)
|
| 38 | +#define PCF2127_BIT_CTRL3_BF BIT(3) |
| 39 | +#define PCF2127_BIT_CTRL3_BTSE BIT(4) |
33 | 40 | /* Time and date registers */
|
34 | 41 | #define PCF2127_REG_SC 0x03
|
35 | 42 | #define PCF2127_BIT_SC_OSF BIT(7)
|
|
46 | 53 | #define PCF2127_BIT_WD_CTL_CD0 BIT(6)
|
47 | 54 | #define PCF2127_BIT_WD_CTL_CD1 BIT(7)
|
48 | 55 | #define PCF2127_REG_WD_VAL 0x11
|
| 56 | +/* Tamper timestamp registers */ |
| 57 | +#define PCF2127_REG_TS_CTRL 0x12 |
| 58 | +#define PCF2127_BIT_TS_CTRL_TSOFF BIT(6) |
| 59 | +#define PCF2127_BIT_TS_CTRL_TSM BIT(7) |
| 60 | +#define PCF2127_REG_TS_SC 0x13 |
| 61 | +#define PCF2127_REG_TS_MN 0x14 |
| 62 | +#define PCF2127_REG_TS_HR 0x15 |
| 63 | +#define PCF2127_REG_TS_DM 0x16 |
| 64 | +#define PCF2127_REG_TS_MO 0x17 |
| 65 | +#define PCF2127_REG_TS_YR 0x18 |
49 | 66 | /*
|
50 | 67 | * RAM registers
|
51 | 68 | * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
|
@@ -305,6 +322,97 @@ static const struct watchdog_ops pcf2127_watchdog_ops = {
|
305 | 322 | .set_timeout = pcf2127_wdt_set_timeout,
|
306 | 323 | };
|
307 | 324 |
|
| 325 | +/* sysfs interface */ |
| 326 | + |
| 327 | +static ssize_t timestamp0_store(struct device *dev, |
| 328 | + struct device_attribute *attr, |
| 329 | + const char *buf, size_t count) |
| 330 | +{ |
| 331 | + struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent); |
| 332 | + int ret; |
| 333 | + |
| 334 | + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1, |
| 335 | + PCF2127_BIT_CTRL1_TSF1, 0); |
| 336 | + if (ret) { |
| 337 | + dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret); |
| 338 | + return ret; |
| 339 | + } |
| 340 | + |
| 341 | + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2, |
| 342 | + PCF2127_BIT_CTRL2_TSF2, 0); |
| 343 | + if (ret) { |
| 344 | + dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret); |
| 345 | + return ret; |
| 346 | + } |
| 347 | + |
| 348 | + ret = pcf2127_wdt_active_ping(&pcf2127->wdd); |
| 349 | + if (ret) |
| 350 | + return ret; |
| 351 | + |
| 352 | + return count; |
| 353 | +}; |
| 354 | + |
| 355 | +static ssize_t timestamp0_show(struct device *dev, |
| 356 | + struct device_attribute *attr, char *buf) |
| 357 | +{ |
| 358 | + struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent); |
| 359 | + struct rtc_time tm; |
| 360 | + int ret; |
| 361 | + unsigned char data[25]; |
| 362 | + |
| 363 | + ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data, |
| 364 | + sizeof(data)); |
| 365 | + if (ret) { |
| 366 | + dev_err(dev, "%s: read error ret=%d\n", __func__, ret); |
| 367 | + return ret; |
| 368 | + } |
| 369 | + |
| 370 | + dev_dbg(dev, |
| 371 | + "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, " |
| 372 | + "ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n", |
| 373 | + __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2], |
| 374 | + data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC], |
| 375 | + data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR], |
| 376 | + data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO], |
| 377 | + data[PCF2127_REG_TS_YR]); |
| 378 | + |
| 379 | + ret = pcf2127_wdt_active_ping(&pcf2127->wdd); |
| 380 | + if (ret) |
| 381 | + return ret; |
| 382 | + |
| 383 | + if (!(data[PCF2127_REG_CTRL1] & PCF2127_BIT_CTRL1_TSF1) && |
| 384 | + !(data[PCF2127_REG_CTRL2] & PCF2127_BIT_CTRL2_TSF2)) |
| 385 | + return 0; |
| 386 | + |
| 387 | + tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F); |
| 388 | + tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F); |
| 389 | + tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F); |
| 390 | + tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F); |
| 391 | + /* TS_MO register (month) value range: 1-12 */ |
| 392 | + tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1; |
| 393 | + tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]); |
| 394 | + if (tm.tm_year < 70) |
| 395 | + tm.tm_year += 100; /* assume we are in 1970...2069 */ |
| 396 | + |
| 397 | + ret = rtc_valid_tm(&tm); |
| 398 | + if (ret) |
| 399 | + return ret; |
| 400 | + |
| 401 | + return sprintf(buf, "%llu\n", |
| 402 | + (unsigned long long)rtc_tm_to_time64(&tm)); |
| 403 | +}; |
| 404 | + |
| 405 | +static DEVICE_ATTR_RW(timestamp0); |
| 406 | + |
| 407 | +static struct attribute *pcf2127_attrs[] = { |
| 408 | + &dev_attr_timestamp0.attr, |
| 409 | + NULL |
| 410 | +}; |
| 411 | + |
| 412 | +static const struct attribute_group pcf2127_attr_group = { |
| 413 | + .attrs = pcf2127_attrs, |
| 414 | +}; |
| 415 | + |
308 | 416 | static int pcf2127_probe(struct device *dev, struct regmap *regmap,
|
309 | 417 | const char *name, bool has_nvmem)
|
310 | 418 | {
|
@@ -371,6 +479,58 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
|
371 | 479 | if (ret)
|
372 | 480 | return ret;
|
373 | 481 |
|
| 482 | + /* |
| 483 | + * Disable battery low/switch-over timestamp and interrupts. |
| 484 | + * Clear battery interrupt flags which can block new trigger events. |
| 485 | + * Note: This is the default chip behaviour but added to ensure |
| 486 | + * correct tamper timestamp and interrupt function. |
| 487 | + */ |
| 488 | + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL3, |
| 489 | + PCF2127_BIT_CTRL3_BTSE | |
| 490 | + PCF2127_BIT_CTRL3_BF | |
| 491 | + PCF2127_BIT_CTRL3_BIE | |
| 492 | + PCF2127_BIT_CTRL3_BLIE, 0); |
| 493 | + if (ret) { |
| 494 | + dev_err(dev, "%s: interrupt config (ctrl3) failed\n", |
| 495 | + __func__); |
| 496 | + return ret; |
| 497 | + } |
| 498 | + |
| 499 | + /* |
| 500 | + * Enable timestamp function and store timestamp of first trigger |
| 501 | + * event until TSF1 and TFS2 interrupt flags are cleared. |
| 502 | + */ |
| 503 | + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL, |
| 504 | + PCF2127_BIT_TS_CTRL_TSOFF | |
| 505 | + PCF2127_BIT_TS_CTRL_TSM, |
| 506 | + PCF2127_BIT_TS_CTRL_TSM); |
| 507 | + if (ret) { |
| 508 | + dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n", |
| 509 | + __func__); |
| 510 | + return ret; |
| 511 | + } |
| 512 | + |
| 513 | + /* |
| 514 | + * Enable interrupt generation when TSF1 or TSF2 timestamp flags |
| 515 | + * are set. Interrupt signal is an open-drain output and can be |
| 516 | + * left floating if unused. |
| 517 | + */ |
| 518 | + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2, |
| 519 | + PCF2127_BIT_CTRL2_TSIE, |
| 520 | + PCF2127_BIT_CTRL2_TSIE); |
| 521 | + if (ret) { |
| 522 | + dev_err(dev, "%s: tamper detection config (ctrl2) failed\n", |
| 523 | + __func__); |
| 524 | + return ret; |
| 525 | + } |
| 526 | + |
| 527 | + ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group); |
| 528 | + if (ret) { |
| 529 | + dev_err(dev, "%s: tamper sysfs registering failed\n", |
| 530 | + __func__); |
| 531 | + return ret; |
| 532 | + } |
| 533 | + |
374 | 534 | return rtc_register_device(pcf2127->rtc);
|
375 | 535 | }
|
376 | 536 |
|
|
0 commit comments