+static int twl4030bci_get_charging_current(struct twl4030_bci *bci, int *curr)
+{
+ int ret, val, cgain;
+
+ val = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
+ if (val < 0)
+ return val;
+ if ((ret = twl4030bci_get_cgain(bci, &cgain)))
+ return ret;
+
+ val &= 0x1ff;
+ val *= 1666;
+ if (cgain)
+ val *= 2;
+
+ *curr = val;
+
+ return 0;
+}
+
+static int twl4030bci_get_eoc_current(struct twl4030_bci *bci, int *curr)
+{
+ int ret, cgain, ichgeocth;
+ u8 reg;
+
+ if ((ret = twl4030bci_get_cgain(bci, &cgain)))
+ return ret;
+
+ ret = twl4030_bci_read(TWL4030_BCIMFTH8, ®);
+ if (ret < 0) {
+ dev_warn(bci->dev, "error fetching BCIMFTH8 register");
+ return ret;
+ }
+ ichgeocth = reg >> 4;
+
+ *curr = (ichgeocth * 13310) * ((cgain) ? 2 : 1);
+
+ return 0;
+}
+
+static ssize_t twl4030_bci_ac_show_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 boot_bci;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &boot_bci,
+ TWL4030_PM_MASTER_BOOT_BCI);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", (boot_bci & TWL4030_BCIAUTOAC) ? 1 : 0);
+}
+
+static ssize_t twl4030_bci_ac_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct twl4030_bci *bci = container_of(psy, struct twl4030_bci, ac);
+ unsigned long enable;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &enable);
+ if (ret || enable > 1)
+ return -EINVAL;
+
+ bci->ac_charge_enable = enable;
+ twl4030_charger_enable_ac(enable);
+
+ return count;
+}
+static struct device_attribute dev_attr_enable_ac =
+ __ATTR(enable, S_IRUGO | S_IWUSR, twl4030_bci_ac_show_enable,
+ twl4030_bci_ac_store_enable);
+
+static ssize_t twl4030_bci_usb_show_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 boot_bci;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &boot_bci,
+ TWL4030_PM_MASTER_BOOT_BCI);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", (boot_bci & TWL4030_BCIAUTOUSB) ? 1 : 0);
+}
+
+static ssize_t twl4030_bci_usb_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct twl4030_bci *bci = container_of(psy, struct twl4030_bci, usb);
+ unsigned long enable;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &enable);
+ if (ret || enable > 1)
+ return -EINVAL;
+
+ bci->usb_charge_enable = enable;
+ twl4030_charger_enable_usb(bci, enable);
+
+ return count;
+}
+static struct device_attribute dev_attr_enable_usb =
+ __ATTR(enable, S_IRUGO | S_IWUSR, twl4030_bci_usb_show_enable,
+ twl4030_bci_usb_store_enable);
+
+static ssize_t show_charge_current(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
+ int ret, val;
+
+ if ((ret = twl4030bci_get_charging_current(bci, &val)))
+ return ret;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t store_charge_current(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
+ unsigned long new_current;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &new_current);
+ if (ret)
+ return -EINVAL;
+
+ /*
+ * Previously, this sysfs parameter used a different raw-register
+ * format. All legal values in this format fall numerically into the
+ * range [0, 1023]. Since in the new format, these encode values so
+ * low as to be meaningless, reject them here so that anybody trying
+ * to use the old format will have at least a chance of figuring out
+ * why it isn't working any more. Note that in both formats, the value
+ * 0 has the same meaning, so it is allowed where values in the range
+ * [1, 1023] are not.
+ */
+ if (new_current < 1024 && new_current != 0)
+ return -EINVAL;
+
+ ret = update_charge_parameters(bci, new_current, -1);
+ if (ret)
+ return ret;
+
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ bci->ac_current = new_current;
+ else
+ bci->usb_current = new_current;
+
+ return count;
+}
+static DEVICE_ATTR(charge_current, S_IRUGO | S_IWUSR, show_charge_current,
+ store_charge_current);
+
+static ssize_t show_end_of_charge_current(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
+ int ret, eoc_current;
+
+ if ((ret = twl4030bci_get_eoc_current(bci, &eoc_current)))
+ return ret;
+
+ return sprintf(buf, "%d\n", eoc_current);
+}
+
+static ssize_t store_end_of_charge_current(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
+ unsigned long new_eoc_current;
+ int eoc_current, ret;
+
+ ret = strict_strtoul(buf, 10, &new_eoc_current);
+ if (ret)
+ return -EINVAL;
+
+ eoc_current = new_eoc_current;
+ /* check for overflow */
+ if (eoc_current != new_eoc_current)
+ return -EINVAL;
+
+ if ((ret = update_charge_parameters(bci, -1, eoc_current)))
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(end_of_charge_current, S_IRUGO | S_IWUSR,
+ show_end_of_charge_current, store_end_of_charge_current);
+
+static struct attribute *bci_ac_attrs[] = {
+ &dev_attr_enable_ac.attr,
+ &dev_attr_charge_current.attr,
+ &dev_attr_end_of_charge_current.attr,
+ NULL,
+};
+
+static struct attribute *bci_usb_attrs[] = {
+ &dev_attr_enable_usb.attr,
+ &dev_attr_charge_current.attr,
+ &dev_attr_end_of_charge_current.attr,
+ NULL,
+};
+
+static const struct attribute_group bci_ac_attr_group = {
+ .attrs = bci_ac_attrs,
+};
+
+static const struct attribute_group bci_usb_attr_group = {
+ .attrs = bci_usb_attrs,
+};
+