[Gta04-owner] [PATCH] twl4030_charger: set usb max current

Andreas Kemnade andreas at kemnade.info
Thu Mar 29 22:12:38 CEST 2012


It provides a sysfs file here:
/sys/bus/platform/devices/twl4030_bci/max_current
/sys/class/power_supply/twl4030_usb/device/max_current
(symlinked stuff)

Into this sysfs file the maximum usb current in uA can be written or read.
The setting stays the same until the next reconnection.
Then the current is set to 600000uA if the kernel parameter
allow_usb is set, or to the value of the newly added parameter
default_usb_current, which expects the default current in uA.
(default 100000uA)

Signed-off-by: Andreas Kemnade <andreas at kemnade.info>
---
 drivers/power/twl4030_charger.c |  145 ++++++++++++++++++++++++++++++++------
 1 files changed, 122 insertions(+), 23 deletions(-)

diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index c6dee6d..7c376cc 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -30,6 +30,11 @@
 #define TWL4030_BCIMFSTS4	0x10
 #define TWL4030_BCICTL1		0x23
 #define TWL4030_BB_CFG		0x12
+#define TWL4030_BCIIREF1	0x27
+#define TWL4030_BCIIREF2	0x28
+#define TWL4030_BCIMFKEY	0x11
+
+
 
 #define TWL4030_BCIAUTOWEN	BIT(5)
 #define TWL4030_CONFIG_DONE	BIT(4)
@@ -78,6 +83,9 @@ static bool allow_usb;
 module_param(allow_usb, bool, 0644);
 MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
 
+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");
 struct twl4030_bci {
 	struct device		*dev;
 	struct power_supply	ac;
@@ -160,6 +168,71 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
 
 	return (hwsts & TWL4030_STS_VBUS);
 }
+/*
+ * TI provided formulas:
+ * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
+ * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
+ * Here we use integer approximation of:
+ * CGAIN == 0: val * 1.6618 - 0.85 * 1000
+ * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2
+ */
+/*
+ * convert twl register value for currents into uA
+ */
+static int regval2ua(int regval, bool cgain)
+{
+	if (cgain)
+		return (regval * 16618 - 850 * 10000) / 5;
+	else
+		return (regval * 16618 - 850 * 10000) / 10;
+}
+
+/*
+ * convert uA currents into twl register value
+ */
+static int ua2regval(int ua, bool cgain)
+{
+	int ret;
+	if (cgain & TWL4030_CGAIN)
+		ua /= 2;
+	ret = (ua * 10 + 850 * 10000) / 16618;
+	/* rounding problems */
+	if (ret < 512)
+		ret = 512;
+	return ret;
+}
+
+
+static int twl4030_charger_set_max_current(int cur)
+{
+	u8 bcictl1;
+	int status;
+	/* get setting of CGAIN bit */
+	status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+	if (status < 0)
+		return status;
+	cur = ua2regval(cur, bcictl1 & TWL4030_CGAIN);
+	/* wie have only 10 bit */
+	if (cur > 0x3ff)
+		return -EINVAL;
+	/* disable write protection for one write access for BCIIREF */
+	status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0xE7,
+			TWL4030_BCIMFKEY);
+	if (status < 0)
+		return status;
+	status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
+			(cur & 0x100) ? 3 : 2, TWL4030_BCIIREF2);
+	if (status < 0)
+		return status;
+	/* disable write protection for one write access for BCIIREF */
+	status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 0xE7,
+			TWL4030_BCIMFKEY);
+	if (status < 0)
+		return status;
+	status = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, cur & 0xff,
+			TWL4030_BCIIREF1);
+	return status;
+}
 
 /*
  * Enable/Disable USB Charge functionality.
@@ -173,21 +246,15 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
 		if (!twl4030_bci_have_vbus(bci))
 			return -ENODEV;
 
-		/*
-		 * Until we can find out what current the device can provide,
-		 * require a module param to enable USB charging.
-		 */
-		if (!allow_usb) {
-			dev_warn(bci->dev, "USB charging is disabled.\n");
-			return -EACCES;
-		}
-
 		/* Need to keep regulator on */
 		if (!bci->usb_enabled) {
 			regulator_enable(bci->usb_reg);
 			bci->usb_enabled = 1;
 		}
-
+		if (allow_usb)
+			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)
@@ -353,14 +420,47 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
 	return NOTIFY_OK;
 }
 
+
 /*
- * TI provided formulas:
- * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
- * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
- * Here we use integer approximation of:
- * CGAIN == 0: val * 1.6618 - 0.85
- * CGAIN == 1: (val * 1.6618 - 0.85) * 2
+ * sysfs max_current store
+ */
+static ssize_t
+twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t n)
+{
+	int cur = 0;
+	int status = 0;
+	status = kstrtoint(buf, 10, &cur);
+	if (status)
+		return status;
+	if (cur < 0)
+		return -EINVAL;
+	status = twl4030_charger_set_max_current(cur);
+	return (status == 0) ? n : status;
+}
+
+/*
+ * sysfs max_current show
  */
+static ssize_t twl4030_bci_max_current_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int status = 0;
+	int cur;
+	u8 bcictl1;
+	cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
+	if (cur < 0)
+		return cur;
+	status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+	if (status < 0)
+		return status;
+	cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
+	return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
+}
+
+static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
+			twl4030_bci_max_current_store);
+
 static int twl4030_charger_get_current(void)
 {
 	int curr;
@@ -374,12 +474,7 @@ static int twl4030_charger_get_current(void)
 	ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
 	if (ret)
 		return ret;
-
-	ret = (curr * 16618 - 850 * 10000) / 10;
-	if (bcictl1 & TWL4030_CGAIN)
-		ret *= 2;
-
-	return ret;
+	return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
 }
 
 /*
@@ -568,6 +663,10 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
 	if (ret < 0)
 		dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
 
+	if (device_create_file(&pdev->dev, &dev_attr_max_current))
+		dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+
 	twl4030_charger_enable_ac(true);
 	twl4030_charger_enable_usb(bci, true);
 	twl4030_charger_enable_backup(pdata->bb_uvolt,
@@ -597,7 +696,7 @@ fail_register_ac:
 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);
 	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