rtc: bq32000: add support to enable disable the trickle charge FET bypass
authorEnric Balletbo i Serra <enric.balletbo@collabora.com>
Fri, 27 Jan 2017 17:43:46 +0000 (18:43 +0100)
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>
Wed, 1 Feb 2017 11:44:23 +0000 (12:44 +0100)
The bq32000 includes a trickle charge circuit to maintain the charge of the
backup supply when a super capacitor is used.

You can enable the charging circuit by setting 'trickle-resistor-ohms',
additionally you can set TCFE to 1 to bypass the internal diode and boost
the charge voltage of the backup supply. You might want to enable/disable
the TCFE switch from userspace (e.g when device is only connected to a
battery)

This patch introduces a new sysfs entry to enable and disable this FET
form userspace.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Documentation/ABI/testing/sysfs-bus-i2c-devices-bq32k [new file with mode: 0644]
drivers/rtc/rtc-bq32k.c

diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-bq32k b/Documentation/ABI/testing/sysfs-bus-i2c-devices-bq32k
new file mode 100644 (file)
index 0000000..398b258
--- /dev/null
@@ -0,0 +1,7 @@
+What:          /sys/bus/i2c/devices/.../trickle_charge_bypass
+Date:          Jan 2017
+KernelVersion: 4.11
+Contact:       Enric Balletbo i Serra <eballetbo@gmail.com>
+Description:    Attribute for enable/disable the trickle charge bypass
+               The trickle_charge_bypass attribute allows the userspace to
+                enable/disable the Trickle charge FET bypass.
index 397742446007aa2479969969c60097ad3edb6889..2b223935001fb57e92abdbbd360b02db70ada47c 100644 (file)
@@ -34,6 +34,7 @@
 #define BQ32K_CALIBRATION      0x07    /* CAL_CFG1, calibration and control */
 #define BQ32K_TCH2             0x08    /* Trickle charge enable */
 #define BQ32K_CFG2             0x09    /* Trickle charger control */
+#define BQ32K_TCFE             BIT(6)  /* Trickle charge FET bypass */
 
 struct bq32k_regs {
        uint8_t         seconds;
@@ -188,6 +189,65 @@ static int trickle_charger_of_init(struct device *dev, struct device_node *node)
        return 0;
 }
 
+static ssize_t bq32k_sysfs_show_tricklecharge_bypass(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       int reg, error;
+
+       error = bq32k_read(dev, &reg, BQ32K_CFG2, 1);
+       if (error)
+               return error;
+
+       return sprintf(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0);
+}
+
+static ssize_t bq32k_sysfs_store_tricklecharge_bypass(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       int reg, enable, error;
+
+       if (kstrtoint(buf, 0, &enable))
+               return -EINVAL;
+
+       error = bq32k_read(dev, &reg, BQ32K_CFG2, 1);
+       if (error)
+               return error;
+
+       if (enable) {
+               reg |= BQ32K_TCFE;
+               error = bq32k_write(dev, &reg, BQ32K_CFG2, 1);
+               if (error)
+                       return error;
+
+               dev_info(dev, "Enabled trickle charge FET bypass.\n");
+       } else {
+               reg &= ~BQ32K_TCFE;
+               error = bq32k_write(dev, &reg, BQ32K_CFG2, 1);
+               if (error)
+                       return error;
+
+               dev_info(dev, "Disabled trickle charge FET bypass.\n");
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(trickle_charge_bypass, 0644,
+                  bq32k_sysfs_show_tricklecharge_bypass,
+                  bq32k_sysfs_store_tricklecharge_bypass);
+
+static int bq32k_sysfs_register(struct device *dev)
+{
+       return device_create_file(dev, &dev_attr_trickle_charge_bypass);
+}
+
+static void bq32k_sysfs_unregister(struct device *dev)
+{
+       device_remove_file(dev, &dev_attr_trickle_charge_bypass);
+}
+
 static int bq32k_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
@@ -224,11 +284,26 @@ static int bq32k_probe(struct i2c_client *client,
        if (IS_ERR(rtc))
                return PTR_ERR(rtc);
 
+       error = bq32k_sysfs_register(&client->dev);
+       if (error) {
+               dev_err(&client->dev,
+                       "Unable to create sysfs entries for rtc bq32000\n");
+               return error;
+       }
+
+
        i2c_set_clientdata(client, rtc);
 
        return 0;
 }
 
+static int bq32k_remove(struct i2c_client *client)
+{
+       bq32k_sysfs_unregister(&client->dev);
+
+       return 0;
+}
+
 static const struct i2c_device_id bq32k_id[] = {
        { "bq32000", 0 },
        { }
@@ -240,6 +315,7 @@ static struct i2c_driver bq32k_driver = {
                .name   = "bq32k",
        },
        .probe          = bq32k_probe,
+       .remove         = bq32k_remove,
        .id_table       = bq32k_id,
 };