aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/bq27x00_battery.c20
-rw-r--r--drivers/power/twl4030_charger.c152
2 files changed, 140 insertions, 32 deletions
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index b309713..4581cbe 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -62,6 +62,8 @@
#define BQ27000_FLAG_FC BIT(5)
#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */
+#define BQ27000_FLAGS_IMPORTANT (BQ27000_FLAG_FC|BQ27000_FLAG_CHGS|BIT(31))
+
#define BQ27500_REG_SOC 0x2C
#define BQ27500_REG_DCAP 0x3C /* Design capacity */
#define BQ27500_FLAG_DSC BIT(0)
@@ -74,6 +76,8 @@
#define BQ27425_REG_OFFSET 0x04
#define BQ27425_REG_SOC 0x18 /* Register address plus offset */
+#define BQ27500_FLAGS_IMPORTANT (BQ27500_FLAG_FC|BQ27500_FLAG_DSC|BIT(31))
+
#define BQ27000_RS 20 /* Resistor sense */
#define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000)
@@ -413,12 +417,16 @@ static void bq27x00_update(struct bq27x00_device_info *di)
struct bq27x00_reg_cache cache = {0, };
bool is_bq27500 = di->chip == BQ27500;
bool is_bq27425 = di->chip == BQ27425;
+ int flags_changed;
cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
+ if ((cache.flags & 0xff) == 0xff)
+ /* read error */
+ cache.flags = -1;
if (cache.flags >= 0) {
if (!is_bq27500 && !is_bq27425
&& (cache.flags & BQ27000_FLAG_CI)) {
- dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
+ dev_dbg(di->dev, "battery is not calibrated! ignoring capacity values\n");
cache.capacity = -ENODATA;
cache.energy = -ENODATA;
cache.time_to_empty = -ENODATA;
@@ -454,10 +462,14 @@ static void bq27x00_update(struct bq27x00_device_info *di)
di->charge_design_full = bq27x00_battery_read_ilmd(di);
}
- if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) {
- di->cache = cache;
+ flags_changed = di->cache.flags ^ cache.flags;
+ di->cache = cache;
+ if (is_bq27500)
+ flags_changed &= BQ27500_FLAGS_IMPORTANT;
+ else
+ flags_changed &= BQ27000_FLAGS_IMPORTANT;
+ if (flags_changed)
power_supply_changed(&di->bat);
- }
di->last_update = jiffies;
}
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index be98e70..4f0b23d 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -31,6 +31,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)
@@ -79,6 +84,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;
@@ -159,11 +167,72 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts);
- /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */
- if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID))
- return 1;
+ 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;
+}
- return 0;
+
+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(TWL_MODULE_MAIN_CHARGE, 0xE7,
+ TWL4030_BCIMFKEY);
+ if (status < 0)
+ return status;
+ status = twl_i2c_write_u8(TWL_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(TWL_MODULE_MAIN_CHARGE, 0xE7,
+ TWL4030_BCIMFKEY);
+ if (status < 0)
+ return status;
+ status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, cur & 0xff,
+ TWL4030_BCIIREF1);
+ return status;
}
/*
@@ -178,21 +247,16 @@ 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 (regulator_enable(bci->usb_reg) == 0)
+ 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)
@@ -358,14 +422,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;
@@ -379,12 +476,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);
}
/*
@@ -574,6 +666,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,
@@ -602,7 +698,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);