[PATCH v1 3/4 RFC] rtc: pcf2127: implement reading battery status bits

From: Uwe Kleine-KÃnig
Date: Fri Oct 02 2015 - 05:17:55 EST


The rtc features a battery switch-over function. Export battery status
and if a switch-over occured via sysfs.

Signed-off-by: Uwe Kleine-KÃnig <u.kleine-koenig@xxxxxxxxxxxxxx>
---

Notes:
Note this patch is wrong, don't merge!

Calling sysfs_create_files in the driver's probe is too late. I didn't
find a nice alternative though. But in case someone feels like pointing
me in the right direction or using this even with this being wrong, here
comes the patch anyhow.

drivers/rtc/rtc-pcf2127.c | 132 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 130 insertions(+), 2 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 7eb6ff26185e..db057db88031 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -22,7 +22,11 @@

#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */
#define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */
+
#define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */
+#define PCF2127_REG_CTRL3_BF 0x08
+#define PCF2127_REG_CTRL3_BLF 0x04
+
#define PCF2127_REG_SC (0x03) /* datetime */
#define PCF2127_REG_MN (0x04)
#define PCF2127_REG_HR (0x05)
@@ -54,7 +58,7 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
return -EIO;
}

- if (buf[PCF2127_REG_CTRL3] & 0x04)
+ if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF)
dev_info(&client->dev,
"low voltage detected, check/replace RTC battery.\n");

@@ -186,10 +190,116 @@ static const struct rtc_class_ops pcf2127_rtc_ops = {
.set_time = pcf2127_rtc_set_time,
};

+struct pcf2127_bitattr {
+ struct device_attribute attr;
+ unsigned bitmask;
+};
+
+static ssize_t pcf2127_bitattr_clear(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char i2cbuf[2] = { PCF2127_REG_CTRL3, };
+ struct pcf2127_bitattr *bitattr =
+ container_of(attr, struct pcf2127_bitattr, attr);
+ int ret;
+ long val;
+
+ if (!count)
+ return 0;
+
+ ret = kstrtol(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ /* we only accept writing 0 */
+ if (val != 0)
+ return -EINVAL;
+
+ ret = i2c_master_send(client, i2cbuf, 1);
+ if (!ret)
+ ret = -EIO;
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(client, i2cbuf + 1, 1);
+ if (!ret)
+ ret = -EIO;
+ if (ret < 0)
+ return ret;
+
+ i2cbuf[1] &= ~bitattr->bitmask;
+
+ ret = i2c_master_send(client, i2cbuf, 2);
+ if (!ret)
+ ret = -EIO;
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t pcf2127_bitattr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char reg = PCF2127_REG_CTRL3;
+ struct pcf2127_bitattr *bitattr =
+ container_of(attr, struct pcf2127_bitattr, attr);
+ int ret;
+
+ ret = i2c_master_send(client, &reg, 1);
+ if (!ret)
+ ret = -EIO;
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(client, &reg, 1);
+ if (!ret)
+ ret = -EIO;
+ if (ret < 0)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE,
+ "%d\n", !!(reg & bitattr->bitmask));
+}
+
+static struct pcf2127_bitattr pcf2127_battery_low = {
+ .attr = {
+ .attr = {
+ .name = "battery_low",
+ .mode = S_IRUGO,
+ },
+ .show = pcf2127_bitattr_show,
+ },
+ .bitmask = PCF2127_REG_CTRL3_BLF,
+};
+
+static struct pcf2127_bitattr pcf2127_battery_switch_over = {
+ .attr = {
+ .attr = {
+ .name = "battery_switch_over",
+ .mode = S_IWUSR | S_IRUGO,
+ },
+ .show = pcf2127_bitattr_show,
+ .store = pcf2127_bitattr_clear,
+ },
+ .bitmask = PCF2127_REG_CTRL3_BF,
+};
+
+static const struct attribute *pcf2127_attributes[] = {
+ &pcf2127_battery_low.attr.attr,
+ &pcf2127_battery_switch_over.attr.attr,
+ NULL
+};
+
static int pcf2127_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf2127 *pcf2127;
+ int ret;

dev_dbg(&client->dev, "%s\n", __func__);

@@ -203,11 +313,28 @@ static int pcf2127_probe(struct i2c_client *client,

i2c_set_clientdata(client, pcf2127);

+ ret = sysfs_create_files(&client->dev.kobj, pcf2127_attributes);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to register sysfs attributes\n");
+ return ret;
+ }
+
pcf2127->rtc = devm_rtc_device_register(&client->dev,
pcf2127_driver.driver.name,
&pcf2127_rtc_ops, THIS_MODULE);

- return PTR_ERR_OR_ZERO(pcf2127->rtc);
+ if (IS_ERR(pcf2127->rtc)) {
+ ret = PTR_ERR(pcf2127->rtc);
+ sysfs_remove_files(&client->dev.kobj, pcf2127_attributes);
+ }
+
+ return ret;
+}
+
+static int pcf2127_remove(struct i2c_client *client)
+{
+ sysfs_remove_files(&client->dev.kobj, pcf2127_attributes);
+ return 0;
}

static const struct i2c_device_id pcf2127_id[] = {
@@ -231,6 +358,7 @@ static struct i2c_driver pcf2127_driver = {
.of_match_table = of_match_ptr(pcf2127_of_match),
},
.probe = pcf2127_probe,
+ .remove = pcf2127_remove,
.id_table = pcf2127_id,
};

--
2.6.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/