From 541fe6819a018e4cb68e3fcad53fe826df9c4a88 Mon Sep 17 00:00:00 2001 From: zador-blood-stained Date: Thu, 18 Feb 2016 15:34:48 +0300 Subject: [PATCH] New version of axp sysfs patch --- .../sunxi-dev/axp20x-sysfs-interface.patch | 121 ++++++++++++++++-- .../sunxi-next/axp20x-sysfs-interface.patch | 121 ++++++++++++++++-- 2 files changed, 226 insertions(+), 16 deletions(-) diff --git a/patch/kernel/sunxi-dev/axp20x-sysfs-interface.patch b/patch/kernel/sunxi-dev/axp20x-sysfs-interface.patch index e17686b9a0..f3fec4a79a 100644 --- a/patch/kernel/sunxi-dev/axp20x-sysfs-interface.patch +++ b/patch/kernel/sunxi-dev/axp20x-sysfs-interface.patch @@ -1,5 +1,5 @@ diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c -index 9842199..a49c7c9 100644 +index 9842199..c313710 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -26,6 +26,7 @@ @@ -18,7 +18,7 @@ index 9842199..a49c7c9 100644 regmap_reg_range(AXP20X_FG_RES, AXP20X_RDC_L), }; -@@ -606,6 +608,506 @@ static void axp20x_power_off(void) +@@ -606,6 +608,611 @@ static void axp20x_power_off(void) AXP20X_OFF); } @@ -51,6 +51,44 @@ index 9842199..a49c7c9 100644 + 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; @@ -97,6 +135,45 @@ index 9842199..a49c7c9 100644 + 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; @@ -166,6 +243,9 @@ index 9842199..a49c7c9 100644 + } 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 @@ -204,12 +284,15 @@ index 9842199..a49c7c9 100644 + } 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, var ? BIT(bit) : 0, BIT(bit)); ++ 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; @@ -288,6 +371,14 @@ index 9842199..a49c7c9 100644 + 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; + @@ -400,17 +491,23 @@ index 9842199..a49c7c9 100644 +}; + +/* Control (writeable) */ -+static struct kobj_attribute control_vbus_direct_mode = __ATTR(set_vbus_direct_mode, S_IRUGO | S_IWUSR, ++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, ++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, ++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, +}; + @@ -427,6 +524,9 @@ index 9842199..a49c7c9 100644 + 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) +{ @@ -505,6 +605,10 @@ index 9842199..a49c7c9 100644 + 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); @@ -514,6 +618,7 @@ index 9842199..a49c7c9 100644 +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); @@ -525,7 +630,7 @@ index 9842199..a49c7c9 100644 static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) { const struct acpi_device_id *acpi_id; -@@ -711,6 +1213,10 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, +@@ -711,6 +1318,10 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, pm_power_off = axp20x_power_off; } @@ -536,7 +641,7 @@ index 9842199..a49c7c9 100644 dev_info(&i2c->dev, "AXP20X driver loaded\n"); return 0; -@@ -720,6 +1226,10 @@ static int axp20x_i2c_remove(struct i2c_client *i2c) +@@ -720,6 +1331,10 @@ static int axp20x_i2c_remove(struct i2c_client *i2c) { struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); diff --git a/patch/kernel/sunxi-next/axp20x-sysfs-interface.patch b/patch/kernel/sunxi-next/axp20x-sysfs-interface.patch index e17686b9a0..f3fec4a79a 100644 --- a/patch/kernel/sunxi-next/axp20x-sysfs-interface.patch +++ b/patch/kernel/sunxi-next/axp20x-sysfs-interface.patch @@ -1,5 +1,5 @@ diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c -index 9842199..a49c7c9 100644 +index 9842199..c313710 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -26,6 +26,7 @@ @@ -18,7 +18,7 @@ index 9842199..a49c7c9 100644 regmap_reg_range(AXP20X_FG_RES, AXP20X_RDC_L), }; -@@ -606,6 +608,506 @@ static void axp20x_power_off(void) +@@ -606,6 +608,611 @@ static void axp20x_power_off(void) AXP20X_OFF); } @@ -51,6 +51,44 @@ index 9842199..a49c7c9 100644 + 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; @@ -97,6 +135,45 @@ index 9842199..a49c7c9 100644 + 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; @@ -166,6 +243,9 @@ index 9842199..a49c7c9 100644 + } 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 @@ -204,12 +284,15 @@ index 9842199..a49c7c9 100644 + } 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, var ? BIT(bit) : 0, BIT(bit)); ++ 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; @@ -288,6 +371,14 @@ index 9842199..a49c7c9 100644 + 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; + @@ -400,17 +491,23 @@ index 9842199..a49c7c9 100644 +}; + +/* Control (writeable) */ -+static struct kobj_attribute control_vbus_direct_mode = __ATTR(set_vbus_direct_mode, S_IRUGO | S_IWUSR, ++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, ++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, ++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, +}; + @@ -427,6 +524,9 @@ index 9842199..a49c7c9 100644 + 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) +{ @@ -505,6 +605,10 @@ index 9842199..a49c7c9 100644 + 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); @@ -514,6 +618,7 @@ index 9842199..a49c7c9 100644 +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); @@ -525,7 +630,7 @@ index 9842199..a49c7c9 100644 static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) { const struct acpi_device_id *acpi_id; -@@ -711,6 +1213,10 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, +@@ -711,6 +1318,10 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, pm_power_off = axp20x_power_off; } @@ -536,7 +641,7 @@ index 9842199..a49c7c9 100644 dev_info(&i2c->dev, "AXP20X driver loaded\n"); return 0; -@@ -720,6 +1226,10 @@ static int axp20x_i2c_remove(struct i2c_client *i2c) +@@ -720,6 +1331,10 @@ static int axp20x_i2c_remove(struct i2c_client *i2c) { struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);