[Gta04-owner] [PATCH] twl4030_charger: add software controlled linear charging mode.
Andreas Kemnade
andreas at kemnade.info
Sat Jun 8 18:36:16 CEST 2013
adds a sysfs control node to achieve that.
It can be set to
auto: normal automatic charging is enabled (default)
off: charging is off
on: charing is on (software controlled)
CC/CV mode is still automatically done,
but end of charge due to low current not.
Note: If linear charging mode is used there should be some method of
stopping charging automatically. It is not a so time-critical, but
it is the wrong setting for leaving a charger connected for several days
since Lithium batteries should not be kept at 100% for longer periods.
Linear charging does a good job with not so reliable power sources, since
several voltage controlling is then often too intelligent.
It was used with a bike hub dynamo since a year or so. In that case there
are automatically charging stops when the cyclist needs a break.
Signed-off-by: Andreas Kemnade <andreas at kemnade.info>
---
drivers/power/twl4030_charger.c | 101 +++++++++++++++++++++++++++++++++++---
1 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index 02d67cb..fbd9877 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -24,6 +24,8 @@
#include <linux/usb/otg.h>
#include <linux/regulator/machine.h>
+#define TWL4030_BCIMDEN 0x00
+#define TWL4030_BCIMDKEY 0x01
#define TWL4030_BCIMSTATEC 0x02
#define TWL4030_BCIICHG 0x08
#define TWL4030_BCIVAC 0x0a
@@ -34,6 +36,8 @@
#define TWL4030_BCIIREF1 0x27
#define TWL4030_BCIIREF2 0x28
#define TWL4030_BCIMFKEY 0x11
+#define TWL4030_BCIMFEN3 0x14
+#define TWL4030_BCIWDKEY 0x21
@@ -43,6 +47,7 @@
#define TWL4030_BCIAUTOAC BIT(0)
#define TWL4030_CGAIN BIT(5)
#define TWL4030_USBFASTMCHG BIT(2)
+#define TWL4030_USBSLOWMCHG BIT(1)
#define TWL4030_STS_VBUS BIT(7)
#define TWL4030_STS_USB_ID BIT(2)
#define TWL4030_BBCHEN BIT(4)
@@ -84,6 +89,7 @@ static bool allow_usb;
module_param(allow_usb, bool, 0644);
MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
+static int do_lin;
static int default_usb_current = 100000;
module_param(default_usb_current, int, 0644);
MODULE_PARM_DESC(default_usb_current, "Default usb current for newly connected devices in uA");
@@ -257,16 +263,38 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
twl4030_charger_set_max_current(600000);
else
twl4030_charger_set_max_current(default_usb_current);
- /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
- ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
- if (ret < 0)
- return ret;
-
+ if (!do_lin) {
+ /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
+ ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
+ if (ret < 0)
+ return ret;
+ }
/* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
- ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0,
+ ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE,
+ TWL4030_USBSLOWMCHG,
TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
+ if (do_lin) {
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x33,
+ TWL4030_BCIWDKEY);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x2a,
+ TWL4030_BCIMDKEY);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x26,
+ TWL4030_BCIMDKEY);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0xf3,
+ TWL4030_BCIWDKEY);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x9c,
+ TWL4030_BCIMFKEY);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0xf0,
+ TWL4030_BCIMFEN3);
+ }
+
} else {
- ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+ if (!do_lin) {
+ ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+ } else {
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x2a,
+ TWL4030_BCIMDKEY);
+ }
if (bci->usb_enabled) {
regulator_disable(bci->usb_reg);
bci->usb_enabled = 0;
@@ -422,6 +450,58 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
return NOTIFY_OK;
}
+static ssize_t
+twl4030_bci_lin_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret = -EINVAL;
+ u8 val;
+ twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMDEN);
+ ret = sprintf(buf, "%d\n", (int)val);
+ return ret;
+}
+
+static ssize_t
+twl4030_bci_lin_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long flags;
+ int status = 0;
+ if (sysfs_streq(buf, "on")) {
+ status = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x30,
+ TWL4030_PM_MASTER_BOOT_BCI);
+
+ status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x33,
+ TWL4030_BCIWDKEY);
+ status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x2a,
+ TWL4030_BCIMDKEY);
+ status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x26,
+ TWL4030_BCIMDKEY);
+ status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0xf3,
+ TWL4030_BCIWDKEY);
+ do_lin = 1;
+
+ } else if (sysfs_streq(buf, "off")) {
+ status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0x2a,
+ TWL4030_BCIMDKEY);
+ } else if (sysfs_streq(buf, "auto")) {
+ if (do_lin) {
+ struct twl4030_bci *bci = dev_get_drvdata(dev);
+ status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
+ 0x2a, TWL4030_BCIMDKEY);
+ do_lin = 0;
+ twl4030_charger_enable_usb(bci, true);
+ }
+ } else {
+ return -EINVAL;
+ }
+
+
+ return (status == 0) ? n : status;
+}
+
+static DEVICE_ATTR(lin, 0644, twl4030_bci_lin_show, twl4030_bci_lin_store);
+
/*
* sysfs max_current store
@@ -519,6 +599,7 @@ static int twl4030_bci_get_property(struct power_supply *psy,
int is_charging;
int state;
int ret;
+ u8 i2cval;
state = twl4030bci_state(bci);
if (state < 0)
@@ -555,7 +636,8 @@ static int twl4030_bci_get_property(struct power_supply *psy,
}
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- if (!is_charging)
+ twl4030_bci_read(TWL4030_BCIMFEN3, &i2cval);
+ if ((!is_charging) && (i2cval == 0))
return -ENODATA;
/* current measurement is shared between AC and USB */
ret = twl4030_charger_get_current();
@@ -668,6 +750,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
if (device_create_file(&pdev->dev, &dev_attr_max_current))
dev_warn(&pdev->dev, "could not create sysfs file\n");
+ if (device_create_file(&pdev->dev, &dev_attr_lin))
+ dev_warn(&pdev->dev, "could not create sysfs file\n");
twl4030_charger_enable_ac(true);
@@ -700,6 +784,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
{
struct twl4030_bci *bci = platform_get_drvdata(pdev);
device_remove_file(&pdev->dev, &dev_attr_max_current);
+ device_remove_file(&pdev->dev, &dev_attr_lin);
twl4030_charger_enable_ac(false);
twl4030_charger_enable_usb(bci, false);
twl4030_charger_enable_backup(0, 0);
--
1.7.2.5
More information about the Gta04-owner
mailing list