armbian_build/patch/kernel/archive/sunxi-6.6/patches.armbian/drv-mfd-axp20x-add-sysfs-interface.patch
Gunjan Gupta d1186b8a0e kernel: sunxi: Add patches for 6.6 kernel
I have changed the way the patches are generated a bit. Instead of using orange-pi branch from megous tree for 6.6 kernel, I have used the following kernel branches

	a83t-suspend, af8133j, anx, audio,
	axp, cam, drm, err, fixes, mbus,
	modem, opi3, pb, pinetab, pp, ppkb,
	samuel, speed, tbs-a711, ths

These branches were carefully chosen to include only allwinner related patches and remove importing of the rockchip related patches into the allwinner kernel.

Following patches are modified to fix patch application failure
- patches.armbian/arm64-dts-sun50i-h616-orangepi-zero2-reg_usb1_vbus-status-ok.patch
- patches.armbian/arm64-dts-sun50i-h616-orangepi-zero2-Enable-GPU-mali.patch
- patches.armbian/arm64-dts-allwinner-h616-Add-efuse_xlate-cpu-frequency-scaling-v1_6_2.patch
- patches.armbian/arm64-dts-allwinner-h616-LED-green_power_on-red_status_heartbeat.patch
- patches.armbian/arm64-dts-allwinner-overlay-Add-Overlays-for-sunxi64.patch
- patches.armbian/arm64-dts-sun50i-h616-bigtreetech-cb1.patch

Following patches are modified because of kernel api change to fix compilation failure
- patches.armbian/drv-gpu-drm-sun4i-Add-HDMI-audio-sun4i-hdmi-encoder.patch
- patches.armbian/drv-of-Device-Tree-Overlay-ConfigFS-interface.patch
2023-10-30 22:58:11 +05:30

659 lines
21 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: The-going <48602507+The-going@users.noreply.github.com>
Date: Tue, 1 Feb 2022 21:38:26 +0300
Subject: drv:mfd:axp20x add sysfs interface
---
drivers/mfd/axp20x.c | 614 ++++++++++
1 file changed, 614 insertions(+)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index e1f3489c1292..118db04a534f 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -82,6 +82,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),
};
@@ -1171,6 +1172,611 @@ static int axp20x_power_off(struct sys_off_data *data)
return NOTIFY_DONE;
}
+#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;
@@ -1375,6 +1981,10 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
SYS_OFF_PRIO_DEFAULT,
axp20x_power_off, axp20x);
+ if (axp20x->variant == AXP209_ID || axp20x->variant == AXP202_ID) {
+ axp20x_sysfs_init(axp20x);
+ }
+
dev_info(axp20x->dev, "AXP20X driver loaded\n");
return 0;
@@ -1383,6 +1993,10 @@ EXPORT_SYMBOL(axp20x_device_probe);
void axp20x_device_remove(struct axp20x_dev *axp20x)
{
+ 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);
}
--
Armbian