|
11 | 11 | */
|
12 | 12 |
|
13 | 13 | #include <linux/bcd.h>
|
| 14 | +#include <linux/bitfield.h> |
14 | 15 | #include <linux/i2c.h>
|
15 | 16 | #include <linux/kstrtox.h>
|
16 | 17 | #include <linux/module.h>
|
|
88 | 89 | #define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
|
89 | 90 | #define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4
|
90 | 91 |
|
| 92 | +#define ABX8XX_REG_EXTRAM 0x3f |
| 93 | +#define ABX8XX_EXTRAM_XADS GENMASK(1, 0) |
| 94 | + |
| 95 | +#define ABX8XX_SRAM_BASE 0x40 |
| 96 | +#define ABX8XX_SRAM_WIN_SIZE 0x40 |
| 97 | +#define ABX8XX_RAM_SIZE 256 |
| 98 | + |
| 99 | +#define NVMEM_ADDR_LOWER GENMASK(5, 0) |
| 100 | +#define NVMEM_ADDR_UPPER GENMASK(7, 6) |
| 101 | + |
91 | 102 | static u8 trickle_resistors[] = {0, 3, 6, 11};
|
92 | 103 |
|
93 | 104 | enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
|
@@ -674,6 +685,68 @@ static int abx80x_setup_watchdog(struct abx80x_priv *priv)
|
674 | 685 | }
|
675 | 686 | #endif
|
676 | 687 |
|
| 688 | +static int abx80x_nvmem_xfer(struct abx80x_priv *priv, unsigned int offset, |
| 689 | + void *val, size_t bytes, bool write) |
| 690 | +{ |
| 691 | + int ret; |
| 692 | + |
| 693 | + while (bytes) { |
| 694 | + u8 extram, reg, len, lower, upper; |
| 695 | + |
| 696 | + lower = FIELD_GET(NVMEM_ADDR_LOWER, offset); |
| 697 | + upper = FIELD_GET(NVMEM_ADDR_UPPER, offset); |
| 698 | + extram = FIELD_PREP(ABX8XX_EXTRAM_XADS, upper); |
| 699 | + reg = ABX8XX_SRAM_BASE + lower; |
| 700 | + len = min(lower + bytes, (size_t)ABX8XX_SRAM_WIN_SIZE) - lower; |
| 701 | + len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); |
| 702 | + |
| 703 | + ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM, |
| 704 | + extram); |
| 705 | + if (ret) |
| 706 | + return ret; |
| 707 | + |
| 708 | + if (write) |
| 709 | + ret = i2c_smbus_write_i2c_block_data(priv->client, reg, |
| 710 | + len, val); |
| 711 | + else |
| 712 | + ret = i2c_smbus_read_i2c_block_data(priv->client, reg, |
| 713 | + len, val); |
| 714 | + if (ret) |
| 715 | + return ret; |
| 716 | + |
| 717 | + offset += len; |
| 718 | + val += len; |
| 719 | + bytes -= len; |
| 720 | + } |
| 721 | + |
| 722 | + return 0; |
| 723 | +} |
| 724 | + |
| 725 | +static int abx80x_nvmem_read(void *priv, unsigned int offset, void *val, |
| 726 | + size_t bytes) |
| 727 | +{ |
| 728 | + return abx80x_nvmem_xfer(priv, offset, val, bytes, false); |
| 729 | +} |
| 730 | + |
| 731 | +static int abx80x_nvmem_write(void *priv, unsigned int offset, void *val, |
| 732 | + size_t bytes) |
| 733 | +{ |
| 734 | + return abx80x_nvmem_xfer(priv, offset, val, bytes, true); |
| 735 | +} |
| 736 | + |
| 737 | +static int abx80x_setup_nvmem(struct abx80x_priv *priv) |
| 738 | +{ |
| 739 | + struct nvmem_config config = { |
| 740 | + .type = NVMEM_TYPE_BATTERY_BACKED, |
| 741 | + .reg_read = abx80x_nvmem_read, |
| 742 | + .reg_write = abx80x_nvmem_write, |
| 743 | + .size = ABX8XX_RAM_SIZE, |
| 744 | + .priv = priv, |
| 745 | + }; |
| 746 | + |
| 747 | + return devm_rtc_nvmem_register(priv->rtc, &config); |
| 748 | +} |
| 749 | + |
677 | 750 | static const struct i2c_device_id abx80x_id[] = {
|
678 | 751 | { "abx80x", ABX80X },
|
679 | 752 | { "ab0801", AB0801 },
|
@@ -840,6 +913,10 @@ static int abx80x_probe(struct i2c_client *client)
|
840 | 913 | return err;
|
841 | 914 | }
|
842 | 915 |
|
| 916 | + err = abx80x_setup_nvmem(priv); |
| 917 | + if (err) |
| 918 | + return err; |
| 919 | + |
843 | 920 | if (client->irq > 0) {
|
844 | 921 | dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
|
845 | 922 | err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
|
0 commit comments