[v1,4/4,RFC] rtc: pcf2127: implement access to nvram

Submitted by Uwe Kleine-König on Oct. 2, 2015, 9:17 a.m.

Details

Message ID 1443777442-15920-5-git-send-email-u.kleine-koenig@pengutronix.de
State New
Headers show

Commit Message

Uwe Kleine-König Oct. 2, 2015, 9:17 a.m.
The rtc features a 512 byte battery backed RAM. Make it available via
sysfs.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---

Notes:
    This patch is broken in the same way as the previous one. Don't merge!

 drivers/rtc/rtc-pcf2127.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index db057db88031..e21dc5dfc9fb 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -35,6 +35,10 @@ 
 #define PCF2127_REG_MO          (0x08)
 #define PCF2127_REG_YR          (0x09)
 
+#define PCF2127_REG_RAM_addr_MSB	0x1a
+#define PCF2127_REG_RAM_wrt_cmd		0x1c
+#define PCF2127_REG_RAM_rd_cmd		0x1d
+
 #define PCF2127_OSF             BIT(7)  /* Oscillator Fail flag */
 
 static struct i2c_driver pcf2127_driver;
@@ -295,6 +299,106 @@  static const struct attribute *pcf2127_attributes[] = {
 	NULL
 };
 
+#define PCF2127_NVRAM_SIZE 512
+
+static ssize_t pcf2127_nvram_read(struct file *filp, struct kobject *kobj,
+				  struct bin_attribute *attr, char *buf,
+				  loff_t off, size_t count)
+{
+	struct i2c_client *client;
+	int ret;
+	unsigned char i2cbuf[3] = { PCF2127_REG_RAM_addr_MSB, off >> 8, off };
+
+	if (off >= PCF2127_NVRAM_SIZE)
+		return 0;
+
+	if (count > PCF2127_NVRAM_SIZE - off)
+		count = PCF2127_NVRAM_SIZE - off;
+
+	if (!count)
+		return 0;
+
+	client = kobj_to_i2c_client(kobj);
+
+	/* set offset */
+	ret = i2c_master_send(client, i2cbuf, 3);
+	if (!ret)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to set RAM address\n");
+		return ret;
+	}
+
+	i2cbuf[0] = PCF2127_REG_RAM_rd_cmd;
+	ret = i2c_master_send(client, i2cbuf, 1);
+	if (!ret)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to issue read command\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, buf, count);
+	if (!ret)
+		ret = -EIO;
+	if (ret < 0)
+		dev_err(&client->dev, "failed to read data\n");
+
+	return ret;
+}
+
+static ssize_t pcf2127a_nvram_write(struct file *filp, struct kobject *kobj,
+				    struct bin_attribute *attr, char *buf,
+				    loff_t off, size_t count)
+{
+	struct i2c_client *client;
+	int ret;
+	unsigned char i2cbuf[PCF2127_NVRAM_SIZE + 1] = {
+		PCF2127_REG_RAM_addr_MSB, off >> 8, off };
+
+	if (off >= PCF2127_NVRAM_SIZE)
+		return 0;
+
+	if (count > PCF2127_NVRAM_SIZE - off)
+		count = PCF2127_NVRAM_SIZE - off;
+
+	if (!count)
+		return 0;
+
+	client = kobj_to_i2c_client(kobj);
+
+	/* set offset */
+	ret = i2c_master_send(client, i2cbuf, 3);
+	if (!ret)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to set RAM address\n");
+		return ret;
+	}
+
+	i2cbuf[0] = PCF2127_REG_RAM_wrt_cmd;
+	memcpy(i2cbuf + 1, buf, count);
+	ret = i2c_master_send(client, i2cbuf, count + 1);
+	if (!ret)
+		ret = -EIO;
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to issue write command\n");
+		return ret;
+	}
+
+	return ret - 1;
+}
+
+static const struct bin_attribute pcf2127_nvram = {
+	.attr = {
+		.name = "nvram",
+		.mode = S_IWUSR | S_IRUGO,
+	},
+	.read = pcf2127_nvram_read,
+	.write = pcf2127a_nvram_write,
+	.size = PCF2127_NVRAM_SIZE,
+};
+
 static int pcf2127_probe(struct i2c_client *client,
 				const struct i2c_device_id *id)
 {
@@ -319,12 +423,21 @@  static int pcf2127_probe(struct i2c_client *client,
 		return ret;
 	}
 
+	ret = sysfs_create_bin_file(&client->dev.kobj, &pcf2127_nvram);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed to register sysfs nvram attribute\n");
+		goto err_create_bin_file;
+	}
+
 	pcf2127->rtc = devm_rtc_device_register(&client->dev,
 				pcf2127_driver.driver.name,
 				&pcf2127_rtc_ops, THIS_MODULE);
 
 	if (IS_ERR(pcf2127->rtc)) {
 		ret = PTR_ERR(pcf2127->rtc);
+		sysfs_remove_bin_file(&client->dev.kobj, &pcf2127_nvram);
+err_create_bin_file:
 		sysfs_remove_files(&client->dev.kobj, pcf2127_attributes);
 	}
 
@@ -333,6 +446,7 @@  static int pcf2127_probe(struct i2c_client *client,
 
 static int pcf2127_remove(struct i2c_client *client)
 {
+	sysfs_remove_bin_file(&client->dev.kobj, &pcf2127_nvram);
 	sysfs_remove_files(&client->dev.kobj, pcf2127_attributes);
 	return 0;
 }