diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index a57d6e9..51b34f6 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -27,6 +27,7 @@
 #include <linux/mfd/core.h>
 #include <linux/of_device.h>
 #include <linux/acpi.h>
+#include <linux/delay.h>
 
 #define AXP20X_OFF	0x80
 
@@ -72,6 +73,7 @@ static const struct regmap_range axp20x_volatile_ranges[] = {
 	regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
 	regmap_reg_range(AXP20X_ACIN_V_ADC_H, AXP20X_IPSOUT_V_HIGH_L),
 	regmap_reg_range(AXP20X_GPIO20_SS, AXP20X_GPIO3_CTRL),
+	regmap_reg_range(AXP20X_CHRG_CC_31_24, AXP20X_DISCHRG_CC_7_0),
 	regmap_reg_range(AXP20X_FG_RES, AXP20X_RDC_L),
 };
 
@@ -582,6 +584,611 @@ static void axp20x_power_off(void)
 		     AXP20X_OFF);
 }
 
+#define kobj_to_device(x) container_of(x, struct device, kobj)
+
+int axp20x_get_adc_freq(struct axp20x_dev *axp)
+{
+	unsigned int res;
+	int ret, freq = 25;
+
+	ret = regmap_read(axp->regmap, AXP20X_ADC_RATE, &res);
+	if (ret < 0) {
+		dev_warn(axp->dev, "Unable to read ADC sampling frequency: %d\n", ret);
+		return freq;
+	}
+	switch ((res & 0xC0) >> 6) {
+	case 0:
+		freq = 25;
+		break;
+	case 1:
+		freq = 50;
+		break;
+	case 2:
+		freq = 100;
+		break;
+	case 3:
+		freq = 200;
+		break;
+	}
+	return freq;
+}
+
+static ssize_t axp20x_sysfs_read_bin_file(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *bin_attr,
+				char *buf, loff_t off, size_t count)
+{
+	int ret;
+
+	struct device *dev = kobj_to_device(kobj);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	ret = regmap_raw_read(axp->regmap, AXP20X_OCV(off), buf, count);
+	if (ret < 0)
+	{
+		dev_warn(axp->dev, "read_bin_file: error reading: %d\n", ret);
+		return ret;
+	}
+	return count;
+}
+
+static ssize_t axp20x_sysfs_write_bin_file(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *bin_attr,
+				char *buf, loff_t off, size_t count)
+{
+	int ret;
+
+	struct device *dev = kobj_to_device(kobj);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	ret = regmap_raw_write(axp->regmap, AXP20X_OCV(off), buf, count);
+	if (ret < 0)
+	{
+		dev_warn(axp->dev, "write_bin_file: error writing: %d\n", ret);
+		return ret;
+	}
+	return count;
+}
+
+static ssize_t axp20x_read_special(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int i, freq, ret = 0;
+	unsigned int res;
+	u32 lval1, lval2;
+	s64 llval;
+	u64 ullval;
+
+	const char *subsystem = kobject_name(kobj);
+	struct device *dev = kobj_to_device(kobj->parent);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	dev_dbg(axp->dev, "read_special: reading attribute %s of object %s\n", attr->attr.name, subsystem);
+
+	if (strcmp(subsystem, "battery") == 0) {
+		if (strcmp(attr->attr.name, "power") == 0) {
+			lval1 = 0;
+			for (i = 0; i < 3; i++) {
+				ret |= regmap_read(axp->regmap, AXP20X_PWR_BATT_H + i, &res);
+				lval1 |= res << ((2 - i) * 8);
+			}
+			llval = lval1 * 1100 / 1000;
+		} else if (strcmp(attr->attr.name, "charge") == 0) {
+			ret = regmap_raw_read(axp->regmap, AXP20X_CHRG_CC_31_24, &lval1, sizeof(lval1));
+			ret |= regmap_raw_read(axp->regmap, AXP20X_DISCHRG_CC_31_24, &lval2, sizeof(lval2));
+			be32_to_cpus(&lval1);
+			be32_to_cpus(&lval2);
+			ullval = abs((s64)lval1 - (s64)lval2) * 65536 * 500;
+			freq = axp20x_get_adc_freq(axp);
+			do_div(ullval, 3600 * freq);
+			llval = (lval1 < lval2) ? -ullval : ullval;
+		} else if (strcmp(attr->attr.name, "capacity") == 0) {
+			ret = regmap_read(axp->regmap, AXP20X_FG_RES, &res);
+			llval = res & 0x7f;
+		} else
+			return -EINVAL;
+	} else
+		return -EINVAL;
+
+	if (ret < 0) {
+		dev_warn(axp->dev, "Unable to read parameter: %d\n", ret);
+		return ret;
+	}
+	return sprintf(buf, "%lld\n", llval);
+}
+
+static ssize_t axp20x_write_int(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int reg, var, ret = 0, scale, width = 12, offset = 0;
+	unsigned int res;
+
+	const char *subsystem = kobject_name(kobj);
+	struct device *dev = kobj_to_device(kobj->parent);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	dev_dbg(axp->dev, "write_int: writing attribute %s of object %s\n", attr->attr.name, subsystem);
+
+	ret = kstrtoint(buf, 10, &var);
+	if (ret < 0)
+		return ret;
+
+	if (strcmp(subsystem, "control") == 0) {
+		if (strcmp(attr->attr.name, "battery_rdc") == 0) {
+			reg = AXP20X_RDC_H;
+			scale = 1074;
+			width = 13;
+			offset = 537;
+			/* TODO: Disable & enable fuel gauge */
+		} else
+			return -EINVAL;
+	} else
+		return -EINVAL;
+
+	res = (var + offset) / scale;
+
+	ret = regmap_write_bits(axp->regmap, reg, (1U << (width - 8)) - 1, (res >> 8) & 0xFF);
+	ret |= regmap_write_bits(axp->regmap, reg + 1, 0xFF, res & 0xFF);
+
+	if (ret < 0) {
+		dev_warn(axp->dev, "Unable to write parameter: %d\n", ret);
+		return ret;
+	}
+	return count;
+}
+
+static ssize_t axp20x_read_bool(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int val, ret, reg, bit;
+	unsigned int res;
+
+	const char *subsystem = kobject_name(kobj);
+	struct device *dev = kobj_to_device(kobj->parent);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	dev_dbg(axp->dev, "read_bool: reading attribute %s of object %s\n", attr->attr.name, subsystem);
+
+	if (strcmp(subsystem, "ac") == 0) {
+		if (strcmp(attr->attr.name, "connected") == 0) {
+			reg = AXP20X_PWR_INPUT_STATUS;
+			bit = 7;
+		} else if (strcmp(attr->attr.name, "used") == 0) {
+			reg = AXP20X_PWR_INPUT_STATUS;
+			bit = 6;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "vbus") == 0) {
+		if (strcmp(attr->attr.name, "connected") == 0) {
+			reg = AXP20X_PWR_INPUT_STATUS;
+			bit = 5;
+		} else if (strcmp(attr->attr.name, "used") == 0) {
+			reg = AXP20X_PWR_INPUT_STATUS;
+			bit = 4;
+		} else if (strcmp(attr->attr.name, "strong") == 0) {
+			reg = AXP20X_PWR_INPUT_STATUS;
+			bit = 3;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "battery") == 0) {
+		if (strcmp(attr->attr.name, "connected") == 0) {
+			reg = AXP20X_PWR_OP_MODE;
+			bit = 5;
+		} else if (strcmp(attr->attr.name, "charging") == 0) {
+			reg = AXP20X_PWR_INPUT_STATUS;
+			bit = 2;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "pmu") == 0) {
+		if (strcmp(attr->attr.name, "overheat") == 0) {
+			reg = AXP20X_PWR_OP_MODE;
+			bit = 7;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "charger") == 0) {
+		if (strcmp(attr->attr.name, "charging") == 0) {
+			reg = AXP20X_PWR_OP_MODE;
+			bit = 6;
+		} else if (strcmp(attr->attr.name, "cell_activation") == 0) {
+			reg = AXP20X_PWR_OP_MODE;
+			bit = 3;
+		} else if (strcmp(attr->attr.name, "low_power") == 0) {
+			reg = AXP20X_PWR_OP_MODE;
+			bit = 2;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "control") == 0) {
+		if (strcmp(attr->attr.name, "set_vbus_direct_mode") == 0) {
+			reg = AXP20X_VBUS_IPSOUT_MGMT;
+			bit = 6;
+		} else if (strcmp(attr->attr.name, "reset_charge_counter") == 0) {
+			reg = AXP20X_CC_CTRL;
+			bit = 5;
+		} else if (strcmp(attr->attr.name, "charge_rtc_battery") == 0) {
+			reg = AXP20X_CHRG_BAK_CTRL;
+			bit = 7;
+		} else if (strcmp(attr->attr.name, "disable_fuel_gauge") == 0) {
+			reg = AXP20X_FG_RES;
+			bit = 7;
+		} else
+			return -EINVAL;
+	} else
+		return -EINVAL;
+
+	ret = regmap_read(axp->regmap, reg, &res);
+	if (ret < 0) {
+		dev_warn(axp->dev, "Unable to read parameter: %d\n", ret);
+		return ret;
+	}
+	val = (res & BIT(bit)) == BIT(bit) ? 1 : 0;
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t axp20x_write_bool(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	int var, ret, reg, bit;
+
+	const char *subsystem = kobject_name(kobj);
+	struct device *dev = kobj_to_device(kobj->parent);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	dev_dbg(axp->dev, "write_bool: writing attribute %s of object %s", attr->attr.name, subsystem);
+
+	ret = kstrtoint(buf, 10, &var);
+	if (ret < 0)
+		return ret;
+
+	if (strcmp(subsystem, "control") == 0) {
+		if (strcmp(attr->attr.name, "set_vbus_direct_mode") == 0) {
+			reg = AXP20X_VBUS_IPSOUT_MGMT;
+			bit = 6;
+		} else if (strcmp(attr->attr.name, "reset_charge_counter") == 0) {
+			reg = AXP20X_CC_CTRL;
+			bit = 5;
+		} else if (strcmp(attr->attr.name, "charge_rtc_battery") == 0) {
+			reg = AXP20X_CHRG_BAK_CTRL;
+			bit = 7;
+		} else if (strcmp(attr->attr.name, "disable_fuel_gauge") == 0) {
+			reg = AXP20X_FG_RES;
+			bit = 7;
+		} else
+			return -EINVAL;
+	} else
+		return -EINVAL;
+
+	ret = regmap_update_bits(axp->regmap, reg, BIT(bit), var ? BIT(bit) : 0);
+	if (ret)
+		dev_warn(axp->dev, "Unable to write value: %d", ret);
+	return count;
+}
+
+static int axp20x_averaging_helper(struct regmap *reg_map, int reg_h,
+	int width)
+{
+	long acc = 0;
+	int ret, i;
+
+	for (i = 0; i < 3; i++) {
+		ret = axp20x_read_variable_width(reg_map, reg_h, width);
+		if (ret < 0)
+			return ret;
+		acc += ret;
+		msleep(20); /* For 100Hz sampling frequency */
+	}
+	return (int)(acc / 3);
+}
+
+static ssize_t axp20x_read_int(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int val, ret, scale, reg, width = 12, offset = 0;
+
+	const char *subsystem = kobject_name(kobj);
+	struct device *dev = kobj_to_device(kobj->parent);
+	struct axp20x_dev *axp = dev_get_drvdata(dev);
+
+	dev_dbg(axp->dev, "read_int: reading attribute %s of object %s\n", attr->attr.name, subsystem);
+
+	if (strcmp(subsystem, "ac") == 0) {
+		if (strcmp(attr->attr.name, "voltage") == 0) {
+			reg = AXP20X_ACIN_V_ADC_H;
+			scale = 1700;
+		} else if (strcmp(attr->attr.name, "amperage") == 0) {
+			reg = AXP20X_ACIN_I_ADC_H;
+			scale = 625;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "vbus") == 0) {
+		if (strcmp(attr->attr.name, "voltage") == 0) {
+			reg = AXP20X_VBUS_V_ADC_H;
+			scale = 1700;
+		} else if (strcmp(attr->attr.name, "amperage") == 0) {
+			reg = AXP20X_VBUS_I_ADC_H;
+			scale = 375;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "battery") == 0) {
+		if (strcmp(attr->attr.name, "voltage") == 0) {
+			reg = AXP20X_BATT_V_H;
+			scale = 1100;
+		} else if (strcmp(attr->attr.name, "amperage") == 0) {
+			reg = AXP20X_BATT_DISCHRG_I_H;
+			scale = 500;
+			width = 13;
+		} else if (strcmp(attr->attr.name, "ts_voltage") == 0) {
+			reg = AXP20X_TS_IN_H;
+			scale = 800;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "pmu") == 0) {
+		if (strcmp(attr->attr.name, "temp") == 0) {
+			reg = AXP20X_TEMP_ADC_H;
+			scale = 100;
+			offset = 144700;
+		} else if (strcmp(attr->attr.name, "voltage") == 0) {
+			reg = AXP20X_IPSOUT_V_HIGH_H;
+			scale = 1400;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "charger") == 0) {
+		if (strcmp(attr->attr.name, "amperage") == 0) {
+			reg = AXP20X_BATT_CHRG_I_H;
+			scale = 500;
+		} else
+			return -EINVAL;
+	} else if (strcmp(subsystem, "control") == 0) {
+		if (strcmp(attr->attr.name, "battery_rdc") == 0) {
+			reg = AXP20X_RDC_H;
+			width = 13;
+			scale = 1074;
+			offset = 537;
+		} else
+			return -EINVAL;
+	} else
+		return -EINVAL;
+
+	ret = axp20x_averaging_helper(axp->regmap, reg, width);
+
+	if (ret < 0) {
+		dev_warn(axp->dev, "Unable to read parameter: %d\n", ret);
+		return ret;
+	}
+	val = ret * scale - offset;
+	return sprintf(buf, "%d\n", val);
+}
+
+/* AC IN */
+static struct kobj_attribute ac_in_voltage = __ATTR(voltage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute ac_in_amperage = __ATTR(amperage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute ac_in_connected = __ATTR(connected, S_IRUGO, axp20x_read_bool, NULL);
+static struct kobj_attribute ac_in_used = __ATTR(used, S_IRUGO, axp20x_read_bool, NULL);
+
+static struct attribute *axp20x_attributes_ac[] = {
+	&ac_in_voltage.attr,
+	&ac_in_amperage.attr,
+	&ac_in_connected.attr,
+	&ac_in_used.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_group_ac = {
+	.attrs = axp20x_attributes_ac,
+};
+
+/* Vbus */
+static struct kobj_attribute vbus_voltage = __ATTR(voltage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute vbus_amperage = __ATTR(amperage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute vbus_connected = __ATTR(connected, S_IRUGO, axp20x_read_bool, NULL);
+static struct kobj_attribute vbus_used = __ATTR(used, S_IRUGO, axp20x_read_bool, NULL);
+static struct kobj_attribute vbus_strong = __ATTR(strong, S_IRUGO, axp20x_read_bool, NULL);
+
+static struct attribute *axp20x_attributes_vbus[] = {
+	&vbus_voltage.attr,
+	&vbus_amperage.attr,
+	&vbus_connected.attr,
+	&vbus_used.attr,
+	&vbus_strong.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_group_vbus = {
+	.attrs = axp20x_attributes_vbus,
+};
+
+/* Battery */
+static struct kobj_attribute batt_voltage = __ATTR(voltage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute batt_amperage = __ATTR(amperage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute batt_ts_voltage = __ATTR(ts_voltage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute batt_power = __ATTR(power, S_IRUGO, axp20x_read_special, NULL);
+static struct kobj_attribute batt_charge = __ATTR(charge, S_IRUGO, axp20x_read_special, NULL);
+static struct kobj_attribute batt_capacity = __ATTR(capacity, S_IRUGO, axp20x_read_special, NULL);
+static struct kobj_attribute batt_connected = __ATTR(connected, S_IRUGO, axp20x_read_bool, NULL);
+static struct kobj_attribute batt_charging = __ATTR(charging, S_IRUGO, axp20x_read_bool, NULL);
+
+static struct attribute *axp20x_attributes_battery[] = {
+	&batt_voltage.attr,
+	&batt_amperage.attr,
+	&batt_ts_voltage.attr,
+	&batt_power.attr,
+	&batt_charge.attr,
+	&batt_capacity.attr,
+	&batt_connected.attr,
+	&batt_charging.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_group_battery = {
+	.attrs = axp20x_attributes_battery,
+};
+
+/* PMU */
+static struct kobj_attribute pmu_temp = __ATTR(temp, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute pmu_voltage = __ATTR(voltage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute pmu_overheat = __ATTR(overheat, S_IRUGO, axp20x_read_bool, NULL);
+
+static struct attribute *axp20x_attributes_pmu[] = {
+	&pmu_temp.attr,
+	&pmu_voltage.attr,
+	&pmu_overheat.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_group_pmu = {
+	.attrs = axp20x_attributes_pmu,
+};
+
+/* Charger */
+static struct kobj_attribute charger_amperage = __ATTR(amperage, S_IRUGO, axp20x_read_int, NULL);
+static struct kobj_attribute charger_charging = __ATTR(charging, S_IRUGO, axp20x_read_bool, NULL);
+static struct kobj_attribute charger_cell_activation = __ATTR(cell_activation, S_IRUGO, axp20x_read_bool, NULL);
+static struct kobj_attribute charger_low_power = __ATTR(low_power, S_IRUGO, axp20x_read_bool, NULL);
+
+static struct attribute *axp20x_attributes_charger[] = {
+	&charger_amperage.attr,
+	&charger_charging.attr,
+	&charger_cell_activation.attr,
+	&charger_low_power.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_group_charger = {
+	.attrs = axp20x_attributes_charger,
+};
+
+/* Control (writeable) */
+static struct kobj_attribute control_vbus_direct_mode = __ATTR(set_vbus_direct_mode, (S_IRUGO | S_IWUSR),
+	axp20x_read_bool, axp20x_write_bool);
+static struct kobj_attribute control_reset_charge_counter = __ATTR(reset_charge_counter, (S_IRUGO | S_IWUSR),
+	axp20x_read_bool, axp20x_write_bool);
+static struct kobj_attribute control_charge_rtc_battery = __ATTR(charge_rtc_battery, (S_IRUGO | S_IWUSR),
+	axp20x_read_bool, axp20x_write_bool);
+static struct kobj_attribute control_disable_fuel_gauge = __ATTR(disable_fuel_gauge, (S_IRUGO | S_IWUSR),
+	axp20x_read_bool, axp20x_write_bool);
+static struct kobj_attribute control_battery_rdc = __ATTR(battery_rdc, (S_IRUGO | S_IWUSR),
+	axp20x_read_int, axp20x_write_int);
+
+static struct attribute *axp20x_attributes_control[] = {
+	&control_vbus_direct_mode.attr,
+	&control_reset_charge_counter.attr,
+	&control_charge_rtc_battery.attr,
+	&control_disable_fuel_gauge.attr,
+	&control_battery_rdc.attr,
+	NULL,
+};
+
+static const struct attribute_group axp20x_group_control = {
+	.attrs = axp20x_attributes_control,
+};
+
+static struct {
+	struct kobject *ac;
+	struct kobject *vbus;
+	struct kobject *battery;
+	struct kobject *pmu;
+	struct kobject *charger;
+	struct kobject *control;
+} subsystems;
+
+static struct bin_attribute axp20x_ocv_curve = __BIN_ATTR(ocv_curve, S_IRUGO | S_IWUSR,
+	axp20x_sysfs_read_bin_file, axp20x_sysfs_write_bin_file, AXP20X_OCV_MAX + 1);
+
+static void axp20x_sysfs_create_subgroup(const char name[], struct axp20x_dev *axp,
+	struct kobject *subgroup, const struct attribute_group *attrs)
+{
+	int ret;
+	struct kobject *parent = &axp->dev->kobj;
+	subgroup = kobject_create_and_add(name, parent);
+	if (subgroup != NULL) {
+		ret = sysfs_create_group(subgroup, attrs);
+		if (ret) {
+			dev_warn(axp->dev, "Unable to register sysfs group: %s: %d", name, ret);
+			kobject_put(subgroup);
+		}
+	}
+}
+
+static void axp20x_sysfs_remove_subgroup(struct kobject *subgroup,
+	const struct attribute_group *attrs)
+{
+	sysfs_remove_group(subgroup, attrs);
+	kobject_put(subgroup);
+}
+
+static int axp20x_sysfs_init(struct axp20x_dev *axp)
+{
+	int ret;
+	unsigned int res;
+
+	/* Enable all ADC channels in the first register */
+	ret = regmap_write(axp->regmap, AXP20X_ADC_EN1, 0xFF);
+	if (ret)
+		dev_warn(axp->dev, "Unable to enable ADC: %d", ret);
+
+	/*
+	 * Set ADC sampling frequency to 100Hz (default is 25)
+	 * Always measure battery temperature (default: only when charging)
+	 */
+	ret = regmap_update_bits(axp->regmap, AXP20X_ADC_RATE, 0xC3, 0x82);
+	if (ret)
+		dev_warn(axp->dev, "Unable to set ADC frequency and TS current output: %d", ret);
+
+	/* Enable fuel gauge and charge counter */
+	ret = regmap_update_bits(axp->regmap, AXP20X_FG_RES, 0x80, 0x00);
+	if (ret)
+		dev_warn(axp->dev, "Unable to enable battery fuel gauge: %d", ret);
+	/* ret = regmap_update_bits(axp->regmap, AXP20X_CC_CTRL, 0xC0, 0x00); */
+	ret |= regmap_update_bits(axp->regmap, AXP20X_CC_CTRL, 0xC0, 0x80);
+	if (ret)
+		dev_warn(axp->dev, "Unable to enable battery charge counter: %d", ret);
+
+	/* Enable battery detection */
+	ret = regmap_read(axp->regmap, AXP20X_OFF_CTRL, &res);
+	if (ret == 0) {
+		if ((res & 0x40) != 0x40) {
+			dev_info(axp->dev, "Battery detection is disabled, enabling");
+			ret = regmap_update_bits(axp->regmap, AXP20X_OFF_CTRL, 0x40, 0x40);
+			if (ret)
+				dev_warn(axp->dev, "Unable to enable battery detection: %d", ret);
+		}
+	} else
+		dev_warn(axp->dev, "Unable to read register AXP20X_OFF_CTRL: %d", ret);
+
+	/* Get info about backup (RTC) battery */
+	ret = regmap_read(axp->regmap, AXP20X_CHRG_BAK_CTRL, &res);
+	if (ret == 0) {
+		dev_info(axp->dev, "Backup (RTC) battery charging is %s",
+			(res & 0x80) == 0x80 ? "enabled" : "disabled");
+		if ((res & 0x60) != 0x20)
+			dev_warn(axp->dev, "Backup (RTC) battery target voltage is not 3.0V");
+	} else
+		dev_warn(axp->dev, "Unable to read register AXP20X_CHRG_BAK_CTRL: %d", ret);
+
+	axp20x_sysfs_create_subgroup("ac", axp, subsystems.ac, &axp20x_group_ac);
+	axp20x_sysfs_create_subgroup("vbus", axp, subsystems.vbus, &axp20x_group_vbus);
+	axp20x_sysfs_create_subgroup("battery", axp, subsystems.battery, &axp20x_group_battery);
+	axp20x_sysfs_create_subgroup("pmu", axp, subsystems.pmu, &axp20x_group_pmu);
+	axp20x_sysfs_create_subgroup("charger", axp, subsystems.charger, &axp20x_group_charger);
+	axp20x_sysfs_create_subgroup("control", axp, subsystems.control, &axp20x_group_control);
+
+	ret = sysfs_create_bin_file(&axp->dev->kobj, &axp20x_ocv_curve);
+	if (ret)
+		dev_warn(axp->dev, "Unable to create sysfs ocv_curve file: %d", ret);
+
+	ret = sysfs_create_link_nowarn(power_kobj, &axp->dev->kobj, "axp_pmu");
+	if (ret)
+		dev_warn(axp->dev, "Unable to create sysfs symlink: %d", ret);
+	return ret;
+}
+
+static void axp20x_sysfs_exit(struct axp20x_dev *axp)
+{
+	sysfs_delete_link(power_kobj, &axp->dev->kobj, "axp_pmu");
+	sysfs_remove_bin_file(&axp->dev->kobj, &axp20x_ocv_curve);
+	axp20x_sysfs_remove_subgroup(subsystems.control, &axp20x_group_control);
+	axp20x_sysfs_remove_subgroup(subsystems.charger, &axp20x_group_charger);
+	axp20x_sysfs_remove_subgroup(subsystems.pmu, &axp20x_group_pmu);
+	axp20x_sysfs_remove_subgroup(subsystems.battery, &axp20x_group_battery);
+	axp20x_sysfs_remove_subgroup(subsystems.vbus, &axp20x_group_vbus);
+	axp20x_sysfs_remove_subgroup(subsystems.ac, &axp20x_group_ac);
+}
+
 int axp20x_match_device(struct axp20x_dev *axp20x)
 {
 	struct device *dev = axp20x->dev;
@@ -669,6 +1276,10 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
 		pm_power_off = axp20x_power_off;
 	}
 
+	if (axp20x->variant == AXP209_ID || axp20x->variant == AXP202_ID) {
+		axp20x_sysfs_init(axp20x);
+	}
+
 	dev_info(axp20x->dev, "AXP20X driver loaded\n");
 
 	return 0;
@@ -682,6 +1293,10 @@ int axp20x_device_remove(struct axp20x_dev *axp20x)
 		pm_power_off = NULL;
 	}
 
+	if (axp20x->variant == AXP209_ID || axp20x->variant == AXP202_ID) {
+		axp20x_sysfs_exit(axp20x);
+	}
+
 	mfd_remove_devices(axp20x->dev);
 	regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc);