The new axp20x hwmon sysfs entry "axp20_hwmon_enable_polling_for_average"
allows to enable 25Hz periodic sampling of ACIN voltage/current and
calculate moving average. It gets activated if set to "1" and disabled
if set to "0".
This periodic sampling and calculating moving average is disabled by
default. Because waking up the CPU many times per second is not nice
for many reasons (battery life, performance, reliability, etc.)
Still it has lower overhead than doing the same in the userland and
may be useful for some power consumption tests.
Comparison of the current draw (mA) for different workloads, measured
by the axp20x hwmon (on the left side) and with a multimeter (on the
right side) for different sunxi hardware and without anything extra
on USB/SATA:
Olinuxino-Lime (Allwinner A10):
300 315
555 590
990 1060
Cubietruck (Allwinner A20):
215 240
310 345
730 800
Cubieboard2 (Allwinner A20):
80 245
180 415
325 610
490 820
Mele A2000 (Allwinner A10):
180 450
280 560
450 770
AXP209 on Olinuxino-Lime and Cubietruck reports reasonably accurate
results. But not so much for the other devices.
Signed-off-by: Siarhei Siamashka <[email protected]>
---
drivers/power/axp_power/axp-mfd.c | 4 ++
drivers/power/axp_power/axp20-mfd.h | 73 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/axp-mfd.h | 3 ++
3 files changed, 80 insertions(+)
diff --git a/drivers/power/axp_power/axp-mfd.c
b/drivers/power/axp_power/axp-mfd.c
index cfa894a..6fd1264 100644
--- a/drivers/power/axp_power/axp-mfd.c
+++ b/drivers/power/axp_power/axp-mfd.c
@@ -365,6 +365,10 @@ static int __devexit axp_mfd_remove(struct i2c_client
*client)
#ifdef CONFIG_AXP_HWMON
if (chip->itm_enabled == 1) {
+ cancel_delayed_work(&axp_hwmon_work);
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+
hwmon_device_unregister(chip->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &axp20_group);
}
diff --git a/drivers/power/axp_power/axp20-mfd.h
b/drivers/power/axp_power/axp20-mfd.h
index 8d679ac..75c522a 100644
--- a/drivers/power/axp_power/axp20-mfd.h
+++ b/drivers/power/axp_power/axp20-mfd.h
@@ -52,6 +52,8 @@ show_acin_voltage(struct device *dev, struct device_attribute
*devattr,
struct axp_mfd_chip *data = axp20_update_device(dev);
if (attr->index == 3)
return sprintf(buf, "ACIN voltage\n");
+ if (attr->index == 4)
+ return sprintf(buf, "%d\n", data->acin_avg_voltage / 64);
return sprintf(buf, "%d\n", data->acin_voltage);
}
@@ -63,6 +65,8 @@ show_acin_current(struct device *dev, struct device_attribute
*devattr,
struct axp_mfd_chip *data = axp20_update_device(dev);
if (attr->index == 3)
return sprintf(buf, "ACIN current\n");
+ if (attr->index == 4)
+ return sprintf(buf, "%d\n", data->acin_avg_current / 64);
return sprintf(buf, "%d\n", data->acin_current);
}
@@ -73,6 +77,8 @@ show_acin_power(struct device *dev, struct device_attribute
*devattr, char *buf)
struct axp_mfd_chip *data = axp20_update_device(dev);
if (attr->index == 3)
return sprintf(buf, "ACIN power\n");
+ if (attr->index == 4)
+ return sprintf(buf, "%d\n", data->acin_avg_power / 64);
return sprintf(buf, "%d\n", data->acin_power);
}
@@ -82,10 +88,13 @@ static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp,
NULL, 2);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_acin_voltage, NULL, 0);
static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_acin_voltage, NULL, 3);
+static SENSOR_DEVICE_ATTR(in0_average, S_IRUGO, show_acin_voltage, NULL, 4);
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_acin_current, NULL, 0);
static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_acin_current, NULL, 3);
+static SENSOR_DEVICE_ATTR(curr1_average, S_IRUGO, show_acin_current, NULL, 4);
static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, show_acin_power, NULL, 0);
static SENSOR_DEVICE_ATTR(power1_label, S_IRUGO, show_acin_power, NULL, 3);
+static SENSOR_DEVICE_ATTR(power1_average, S_IRUGO, show_acin_power, NULL, 4);
static struct attribute *axp20_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
@@ -94,10 +103,13 @@ static struct attribute *axp20_attributes[] = {
&sensor_dev_attr_temp1_label.dev_attr.attr,
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in0_average.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_curr1_label.dev_attr.attr,
+ &sensor_dev_attr_curr1_average.dev_attr.attr,
&sensor_dev_attr_power1_input.dev_attr.attr,
&sensor_dev_attr_power1_label.dev_attr.attr,
+ &sensor_dev_attr_power1_average.dev_attr.attr,
NULL
};
@@ -125,6 +137,8 @@ static int axp_read_adc(struct device *dev, struct
i2c_client *client, int reg)
return (high << 4) + (low & 0x0F);
}
+static int axp_hwmon_wq_enabled;
+
/*
* * function that update the status of the chips (temperature)
* */
@@ -156,6 +170,23 @@ static struct axp_mfd_chip *axp20_update_device(struct
device *dev)
data->acin_power = data->acin_voltage * data->acin_current;
+ /* Calculate running moving average for N=64 */
+ data->acin_avg_power -= data->acin_avg_power / 64;
+ data->acin_avg_power += data->acin_voltage * data->acin_current;
+
+ data->acin_avg_voltage -= data->acin_avg_voltage / 64;
+ data->acin_avg_voltage += data->acin_voltage;
+
+ data->acin_avg_current -= data->acin_avg_current / 64;
+ data->acin_avg_current += data->acin_current;
+
+ /* But if we have no active polling, they are meaningless */
+ if (!axp_hwmon_wq_enabled || !data->valid) {
+ data->acin_avg_power = 0;
+ data->acin_avg_voltage = 0;
+ data->acin_avg_current = 0;
+ }
+
data->last_updated = jiffies;
data->valid = 1;
}
@@ -164,6 +195,18 @@ static struct axp_mfd_chip *axp20_update_device(struct
device *dev)
return data;
}
+static void axp_hwmon_work_handler(struct work_struct *w);
+static struct workqueue_struct *wq;
+static DECLARE_DELAYED_WORK(axp_hwmon_work, axp_hwmon_work_handler);
+
+static struct device *wq_dev_arg;
+
+static void axp_hwmon_work_handler(struct work_struct *w)
+{
+ axp20_update_device(wq_dev_arg);
+ queue_delayed_work(wq, &axp_hwmon_work, HZ / 25);
+}
+
#endif
@@ -218,6 +261,10 @@ static int __devinit axp20_init_chip(struct axp_mfd_chip
*chip)
err = PTR_ERR(chip->hwmon_dev);
goto exit_remove_files;
}
+ wq = create_singlethread_workqueue("axp_hwmon_wq");
+ wq_dev_arg = chip->dev;
+ if (axp_hwmon_wq_enabled)
+ queue_delayed_work(wq, &axp_hwmon_work, HZ / 25);
} else {
dev_info(chip->dev, "AXP internal temperature monitoring
disabled\n");
/* TODO enable it ?*/
@@ -558,6 +605,29 @@ static ssize_t axp20_regs_store(struct device *dev,
return count;
}
+#ifdef CONFIG_AXP_HWMON
+static ssize_t axp20_hwmon_enable_polling_for_average_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", axp_hwmon_wq_enabled);
+}
+
+static ssize_t axp20_hwmon_enable_polling_for_average_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ axp_hwmon_wq_enabled = simple_strtoul(buf, NULL, 10);
+
+ cancel_delayed_work(&axp_hwmon_work);
+ flush_workqueue(wq);
+
+ if (axp_hwmon_wq_enabled)
+ queue_delayed_work(wq, &axp_hwmon_work, HZ / 25);
+
+ return count;
+}
+#endif
+
static struct device_attribute axp20_mfd_attrs[] = {
AXP_MFD_ATTR(axp20_offvol),
AXP_MFD_ATTR(axp20_noedelay),
@@ -569,4 +639,7 @@ static struct device_attribute axp20_mfd_attrs[] = {
AXP_MFD_ATTR(axp20_ovtemclsen),
AXP_MFD_ATTR(axp20_reg),
AXP_MFD_ATTR(axp20_regs),
+#ifdef CONFIG_AXP_HWMON
+ AXP_MFD_ATTR(axp20_hwmon_enable_polling_for_average),
+#endif
};
diff --git a/include/linux/mfd/axp-mfd.h b/include/linux/mfd/axp-mfd.h
index 1226d17..f6beb5b 100644
--- a/include/linux/mfd/axp-mfd.h
+++ b/include/linux/mfd/axp-mfd.h
@@ -136,6 +136,9 @@ struct axp_mfd_chip {
s16 acin_voltage; /* range from 0 to 6962 mV */
s16 acin_current; /* range from 0 to 2559 mA */
s32 acin_power;
+ s32 acin_avg_voltage;
+ s32 acin_avg_current;
+ s32 acin_avg_power;
unsigned long last_updated; /* in jiffies */
char valid; /* zero until following fields are valid */
struct device *hwmon_dev;
--
1.8.3.2
--
You received this message because you are subscribed to the Google Groups
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.