diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/bq27x00_battery.c | 20 | ||||
-rw-r--r-- | drivers/power/twl4030_charger.c | 152 |
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); |