Skip to content

Commit e90ff8e

Browse files
rtc: abx80x: Add nvmem support
This adds support for the 256-byte internal RAM. There are two windows which can be used to access this RAM: 64 bytes at 0x40 (the "standard" address space) and 128 bytes at 0x80 (the "alternate" address space). We use the standard address space because it is also accessible over SPI (if such a port is ever done). We are limited to 32-byte reads for SMBus compatibility, so there's no advantage to using the alternate address space. There are some reserved bits in the EXTRAM register, and the datasheet doesn't say what to do with them. I've opted to skip a read/modify/write and just write the whole thing. If this driver is ever converted to regmap, this would be a good place to use regmap_update_bits. Signed-off-by: Sean Anderson <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexandre Belloni <[email protected]>
1 parent 38892c4 commit e90ff8e

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

drivers/rtc/rtc-abx80x.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
#include <linux/bcd.h>
14+
#include <linux/bitfield.h>
1415
#include <linux/i2c.h>
1516
#include <linux/kstrtox.h>
1617
#include <linux/module.h>
@@ -88,6 +89,16 @@
8889
#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
8990
#define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4
9091

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+
91102
static u8 trickle_resistors[] = {0, 3, 6, 11};
92103

93104
enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
@@ -674,6 +685,68 @@ static int abx80x_setup_watchdog(struct abx80x_priv *priv)
674685
}
675686
#endif
676687

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+
677750
static const struct i2c_device_id abx80x_id[] = {
678751
{ "abx80x", ABX80X },
679752
{ "ab0801", AB0801 },
@@ -840,6 +913,10 @@ static int abx80x_probe(struct i2c_client *client)
840913
return err;
841914
}
842915

916+
err = abx80x_setup_nvmem(priv);
917+
if (err)
918+
return err;
919+
843920
if (client->irq > 0) {
844921
dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
845922
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,

0 commit comments

Comments
 (0)