diff options
Diffstat (limited to 'drivers/power/twl4030_charger.c')
-rw-r--r-- | drivers/power/twl4030_charger.c | 152 |
1 files changed, 124 insertions, 28 deletions
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); |