diff options
Diffstat (limited to 'drivers/rtc/rtc-abx80x.c')
| -rw-r--r-- | drivers/rtc/rtc-abx80x.c | 87 |
1 files changed, 84 insertions, 3 deletions
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 2e0e6432901b..3fee27914ba8 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -11,10 +11,11 @@ */ #include <linux/bcd.h> +#include <linux/bitfield.h> #include <linux/i2c.h> #include <linux/kstrtox.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/rtc.h> #include <linux/watchdog.h> @@ -38,7 +39,7 @@ #define ABX8XX_REG_STATUS 0x0f #define ABX8XX_STATUS_AF BIT(2) #define ABX8XX_STATUS_BLF BIT(4) -#define ABX8XX_STATUS_WDT BIT(6) +#define ABX8XX_STATUS_WDT BIT(5) #define ABX8XX_REG_CTRL1 0x10 #define ABX8XX_CTRL_WRITE BIT(0) @@ -88,6 +89,16 @@ #define ABX8XX_TRICKLE_STANDARD_DIODE 0x8 #define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4 +#define ABX8XX_REG_EXTRAM 0x3f +#define ABX8XX_EXTRAM_XADS GENMASK(1, 0) + +#define ABX8XX_SRAM_BASE 0x40 +#define ABX8XX_SRAM_WIN_SIZE 0x40 +#define ABX8XX_RAM_SIZE 256 + +#define NVMEM_ADDR_LOWER GENMASK(5, 0) +#define NVMEM_ADDR_UPPER GENMASK(7, 6) + static u8 trickle_resistors[] = {0, 3, 6, 11}; enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, @@ -674,6 +685,72 @@ static int abx80x_setup_watchdog(struct abx80x_priv *priv) } #endif +static int abx80x_nvmem_xfer(struct abx80x_priv *priv, unsigned int offset, + void *val, size_t bytes, bool write) +{ + int ret; + + while (bytes) { + u8 extram, reg, len, lower, upper; + + lower = FIELD_GET(NVMEM_ADDR_LOWER, offset); + upper = FIELD_GET(NVMEM_ADDR_UPPER, offset); + extram = FIELD_PREP(ABX8XX_EXTRAM_XADS, upper); + reg = ABX8XX_SRAM_BASE + lower; + len = min(lower + bytes, (size_t)ABX8XX_SRAM_WIN_SIZE) - lower; + len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); + + ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM, + extram); + if (ret) + return ret; + + if (write) { + ret = i2c_smbus_write_i2c_block_data(priv->client, reg, + len, val); + if (ret) + return ret; + } else { + ret = i2c_smbus_read_i2c_block_data(priv->client, reg, + len, val); + if (ret <= 0) + return ret ? ret : -EIO; + len = ret; + } + + offset += len; + val += len; + bytes -= len; + } + + return 0; +} + +static int abx80x_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return abx80x_nvmem_xfer(priv, offset, val, bytes, false); +} + +static int abx80x_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return abx80x_nvmem_xfer(priv, offset, val, bytes, true); +} + +static int abx80x_setup_nvmem(struct abx80x_priv *priv) +{ + struct nvmem_config config = { + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = abx80x_nvmem_read, + .reg_write = abx80x_nvmem_write, + .size = ABX8XX_RAM_SIZE, + .priv = priv, + }; + + return devm_rtc_nvmem_register(priv->rtc, &config); +} + static const struct i2c_device_id abx80x_id[] = { { "abx80x", ABX80X }, { "ab0801", AB0801 }, @@ -840,6 +917,10 @@ static int abx80x_probe(struct i2c_client *client) return err; } + err = abx80x_setup_nvmem(priv); + if (err) + return err; + if (client->irq > 0) { dev_info(&client->dev, "IRQ %d supplied\n", client->irq); err = devm_request_threaded_irq(&client->dev, client->irq, NULL, @@ -915,7 +996,7 @@ static struct i2c_driver abx80x_driver = { .name = "rtc-abx80x", .of_match_table = of_match_ptr(abx80x_of_match), }, - .probe_new = abx80x_probe, + .probe = abx80x_probe, .id_table = abx80x_id, }; |
