summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorAnshul Jain <anshulj@nvidia.com>2013-01-23 18:05:56 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:00:05 -0700
commit708261ffac8e7d84e41c855493f2695aa750e82e (patch)
tree82ac2d0721e01be012c6ab4b452b42fa52e92a5e /drivers/hwmon
parente510354e0aaa760fe4ed088cc908fd84ffdd392f (diff)
hwmon: ina3221: Support for turning off ina
This is an optimization to turn off INA device based on number of cores online. Also, this change removes support for turning on/off INA at runtime using sysfs node. Additionaly, this change creates a new API power2_*, which return 0 if ina device is turned off. Bug 1223376 Bug 1207777 Change-Id: I0beedffa10d7e11e280e96c2c58c4dd191b87819 Signed-off-by: Anshul Jain <anshulj@nvidia.com> Reviewed-on: http://git-master/r/193580 (cherry picked from commit 1b530bd6b64363214580f6717b53888613cf7d4e) Signed-off-by: Deepak Nibade <dnibade@nvidia.com> Reviewed-on: http://git-master/r/201924 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/ina3221.c248
1 files changed, 168 insertions, 80 deletions
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index 7b2ad053c94b..f38610f4695a 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -34,6 +34,7 @@
#include <linux/init.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
+#include <linux/cpu.h>
#include "linux/ina3221.h"
@@ -54,15 +55,18 @@
#define busv_register_to_mv(x) ((x >> 3) * 8)
#define shuntv_register_to_uv(x) ((x >> 3) * 40)
+#define CPU_THRESHOLD 2
+
struct ina3221_data {
struct device *hwmon_dev;
struct i2c_client *client;
struct ina3221_platform_data *plat_data;
struct mutex mutex;
u8 mode;
+ struct notifier_block nb;
};
-static s32 power_down_ina3221(struct i2c_client *client)
+static s32 __locked_power_down_ina3221(struct i2c_client *client)
{
s32 ret;
ret = i2c_smbus_write_word_data(client, INA3221_CONFIG,
@@ -72,52 +76,26 @@ static s32 power_down_ina3221(struct i2c_client *client)
return ret;
}
-static s32 show_mode(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static s32 __locked_power_up_ina3221(struct i2c_client *client, int config)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ina3221_data *data = i2c_get_clientdata(client);
- return sprintf(buf, "%d\n", data->mode);
+ s32 ret;
+ ret = i2c_smbus_write_word_data(client, INA3221_CONFIG,
+ __constant_cpu_to_be16(config));
+ if (ret < 0)
+ dev_err(&client->dev,
+ "Config data write failed, error: 0x%x", ret);
+ return ret;
}
-/* mode setting :
- * running_mode = 0 ---> Triggered mode
- * running_mode > 0 ---> Continuous mode
- */
-static s32 set_mode(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static s32 show_mode(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct ina3221_data *data = i2c_get_clientdata(client);
- long l;
int ret;
-
- ret = kstrtol(buf, 10, &l);
- if (ret < 0)
- goto err;
-
mutex_lock(&data->mutex);
- if ((l > 0) && (data->mode == TRIGGERED)) {
- ret = i2c_smbus_write_word_data(client, INA3221_CONFIG,
- __constant_cpu_to_be16(data->plat_data->cont_conf_data));
- if (ret < 0)
- goto error;
- data->mode = CONTINUOUS;
- } else if ((l == 0) && (data->mode == CONTINUOUS)) {
- ret = power_down_ina3221(client);
- if (ret < 0)
- goto error;
- data->mode = TRIGGERED;
- } else
- DEBUG_INA3221(("%s: mode already set\n", __func__));
- mutex_unlock(&data->mutex);
- return 1;
-error:
+ ret = sprintf(buf, "%d\n", data->mode);
mutex_unlock(&data->mutex);
-err:
- dev_err(dev, "%s: failed\n", __func__);
return ret;
}
@@ -148,12 +126,11 @@ static s32 show_voltage(struct device *dev,
bus_volt_reg_addr = (INA3221_BUS_VOL_CHAN1 + (index * 2));
if (data->mode == TRIGGERED) {
- /* fill config data */
- ret = i2c_smbus_write_word_data(client, INA3221_CONFIG,
- __constant_cpu_to_be16(data->plat_data->trig_conf_data));
+ ret = __locked_power_up_ina3221(client,
+ data->plat_data->trig_conf_data);
if (ret < 0) {
dev_err(dev,
- "config data write failed sts: 0x%x\n", ret);
+ "Power up failed, status: 0x%x\n", ret);
goto error;
}
}
@@ -170,7 +147,7 @@ static s32 show_voltage(struct device *dev,
if (data->mode == TRIGGERED) {
/* set ina3221 to power down mode */
- ret = power_down_ina3221(client);
+ ret = __locked_power_down_ina3221(client);
if (ret < 0)
goto error;
}
@@ -202,12 +179,11 @@ static s32 show_current(struct device *dev,
shunt_volt_reg_addr = (INA3221_SHUNT_VOL_CHAN1 + (index * 2));
if (data->mode == TRIGGERED) {
- /* fill config data */
- ret = i2c_smbus_write_word_data(client, INA3221_CONFIG,
- __constant_cpu_to_be16(data->plat_data->trig_conf_data));
+ ret = __locked_power_up_ina3221(client,
+ data->plat_data->trig_conf_data);
if (ret < 0) {
dev_err(dev,
- "config data write failed sts: 0x%x\n", ret);
+ "power up failed sts: 0x%x\n", ret);
goto error;
}
}
@@ -228,7 +204,7 @@ static s32 show_current(struct device *dev,
if (data->mode == TRIGGERED) {
/* set ina3221 to power down mode */
- ret = power_down_ina3221(client);
+ ret = __locked_power_down_ina3221(client);
if (ret < 0)
goto error;
}
@@ -242,37 +218,18 @@ error:
return ret;
}
-static s32 show_power(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
+static s32 __locked_calculate_power(struct i2c_client *client,
+ u8 shunt_volt_reg_addr,
+ u8 bus_volt_reg_addr,
+ int index)
{
- struct i2c_client *client = to_i2c_client(dev);
+
struct ina3221_data *data = i2c_get_clientdata(client);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- u8 index, bus_volt_reg_addr, shunt_volt_reg_addr;
- s32 ret;
- s32 voltage_uV;
s32 voltage_mV;
- s32 current_mA;
+ s32 voltage_uV;
s32 inverse_shunt_resistor;
+ s32 current_mA;
s32 power_mW;
-
- mutex_lock(&data->mutex);
- index = attr->index;
- bus_volt_reg_addr = (INA3221_BUS_VOL_CHAN1 + (index * 2));
- shunt_volt_reg_addr = (INA3221_SHUNT_VOL_CHAN1 + (index * 2));
-
- if (data->mode == TRIGGERED) {
- /* fill config data */
- ret = i2c_smbus_write_word_data(client, INA3221_CONFIG,
- __constant_cpu_to_be16(data->plat_data->trig_conf_data));
- if (ret < 0) {
- dev_err(dev,
- "config data write failed sts: 0x%x\n", ret);
- goto error;
- }
- }
-
/* getting voltage readings in micro volts*/
voltage_uV =
be16_to_cpu(i2c_smbus_read_word_data(client,
@@ -297,10 +254,44 @@ static s32 show_power(struct device *dev,
inverse_shunt_resistor = 1000 / data->plat_data->shunt_resistor[index];
current_mA = voltage_uV * inverse_shunt_resistor / 1000;
power_mW = voltage_mV * current_mA / 1000;
+ return power_mW;
+error:
+ return -EIO;
+}
+
+static s32 show_power(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ina3221_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ u8 index, bus_volt_reg_addr, shunt_volt_reg_addr;
+ s32 ret;
+ s32 power_mW;
+
+ mutex_lock(&data->mutex);
+ index = attr->index;
+ bus_volt_reg_addr = (INA3221_BUS_VOL_CHAN1 + (index * 2));
+ shunt_volt_reg_addr = (INA3221_SHUNT_VOL_CHAN1 + (index * 2));
if (data->mode == TRIGGERED) {
+ ret = __locked_power_up_ina3221(client,
+ data->plat_data->trig_conf_data);
+ if (ret < 0) {
+ dev_err(dev,
+ "power up failed sts: 0x%x\n", ret);
+ goto error;
+ }
+ }
+
+ power_mW = __locked_calculate_power(client, shunt_volt_reg_addr,
+ bus_volt_reg_addr, index);
+ if (power_mW < 0)
+ goto error;
+ if (data->mode == TRIGGERED) {
/* set ina3221 to power down mode */
- ret = power_down_ina3221(client);
+ ret = __locked_power_down_ina3221(client);
if (ret < 0)
goto error;
}
@@ -311,7 +302,94 @@ static s32 show_power(struct device *dev,
error:
mutex_unlock(&data->mutex);
dev_err(dev, "%s: failed\n", __func__);
- return ret;
+ return -EIO;
+}
+
+static s32 show_power2(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ina3221_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ u8 index, bus_volt_reg_addr, shunt_volt_reg_addr;
+ s32 power_mW;
+
+ mutex_lock(&data->mutex);
+
+ /*return 0 if INA is off*/
+ if (data->mode == TRIGGERED) {
+ mutex_unlock(&data->mutex);
+ return sprintf(buf, "%d mW\n", 0);
+ }
+ index = attr->index;
+ bus_volt_reg_addr = (INA3221_BUS_VOL_CHAN1 + (index * 2));
+ shunt_volt_reg_addr = (INA3221_SHUNT_VOL_CHAN1 + (index * 2));
+
+ power_mW = __locked_calculate_power(client, shunt_volt_reg_addr,
+ bus_volt_reg_addr, index);
+ if (power_mW < 0)
+ goto error;
+
+ DEBUG_INA3221(("%s power = %d\n", __func__, power_mW));
+ mutex_unlock(&data->mutex);
+ return sprintf(buf, "%d mW\n", power_mW);
+error:
+ mutex_unlock(&data->mutex);
+ dev_err(dev, "%s: failed\n", __func__);
+ return -EIO;
+}
+
+static int ina3221_hotplug_notify(struct notifier_block *nb,
+ unsigned long event,
+ void *hcpu)
+{
+ struct ina3221_data *data = container_of(nb, struct ina3221_data,
+ nb);
+ struct i2c_client *client = data->client;
+ int cpus;
+ int ret = 0;
+ if (event == CPU_ONLINE || event == CPU_DEAD) {
+ mutex_lock(&data->mutex);
+ cpus = num_online_cpus();
+ DEBUG_INA3221(("INA3221 got CPU notification %d\n", cpus));
+ if ((cpus >= CPU_THRESHOLD) && (data->mode == TRIGGERED)) {
+ /**
+ * Turn INA on when number of cpu
+ * cores crosses threshold
+ */
+ ret = __locked_power_up_ina3221(client,
+ data->plat_data->cont_conf_data);
+ DEBUG_INA3221(("Turning on ina3221, cpus:%d\n", cpus));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "INA can't be turned on: 0x%x\n", ret);
+ goto error;
+ }
+ data->mode = CONTINUOUS;
+ } else if ((cpus < CPU_THRESHOLD) &&
+ (data->mode == CONTINUOUS)) {
+ /**
+ * Turn off ina when number of cpu
+ * cores on are below threshold
+ */
+ ret = __locked_power_down_ina3221(client);
+ DEBUG_INA3221(("Turning off INA3221 cpus%d\n", cpus));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "INA can't be turned off: 0x%x\n", ret);
+ goto error;
+ }
+ data->mode = TRIGGERED;
+ }
+ mutex_unlock(&data->mutex);
+ return 0;
+ } else
+ return 0;
+
+error:
+ dev_err(&client->dev, "INA can't be turned off/on: 0x%x\n", ret);
+ return 0;
}
static struct sensor_device_attribute ina3221[] = {
@@ -319,15 +397,18 @@ static struct sensor_device_attribute ina3221[] = {
SENSOR_ATTR(in1_input_0, S_IRUGO, show_voltage, NULL, 0),
SENSOR_ATTR(curr1_input_0, S_IRUGO, show_current, NULL, 0),
SENSOR_ATTR(power1_input_0, S_IRUGO, show_power, NULL, 0),
+ SENSOR_ATTR(power2_input_0, S_IRUGO, show_power2, NULL, 0),
SENSOR_ATTR(rail_name_1, S_IRUGO, show_rail_name, NULL, 1),
SENSOR_ATTR(in1_input_1, S_IRUGO, show_voltage, NULL, 1),
SENSOR_ATTR(curr1_input_1, S_IRUGO, show_current, NULL, 1),
SENSOR_ATTR(power1_input_1, S_IRUGO, show_power, NULL, 1),
+ SENSOR_ATTR(power2_input_1, S_IRUGO, show_power2, NULL, 1),
SENSOR_ATTR(rail_name_2, S_IRUGO, show_rail_name, NULL, 2),
SENSOR_ATTR(in1_input_2, S_IRUGO, show_voltage, NULL, 2),
SENSOR_ATTR(curr1_input_2, S_IRUGO, show_current, NULL, 2),
SENSOR_ATTR(power1_input_2, S_IRUGO, show_power, NULL, 2),
- SENSOR_ATTR(running_mode, 0644, show_mode, set_mode, 0),
+ SENSOR_ATTR(power2_input_2, S_IRUGO, show_power2, NULL, 2),
+ SENSOR_ATTR(running_mode, S_IRUGO, show_mode, NULL, 0),
/* mode setting :
* running_mode = 0 ---> Triggered mode
* running_mode > 0 ---> Continuous mode
@@ -340,7 +421,8 @@ static int ina3221_probe(struct i2c_client *client,
struct ina3221_data *data;
int ret, i;
- data = kzalloc(sizeof(struct ina3221_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(struct ina3221_data),
+ GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto exit;
@@ -366,6 +448,9 @@ static int ina3221_probe(struct i2c_client *client,
goto exit_remove;
}
}
+ data->client = client;
+ data->nb.notifier_call = ina3221_hotplug_notify;
+ register_hotcpu_notifier(&(data->nb));
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -374,7 +459,7 @@ static int ina3221_probe(struct i2c_client *client,
}
/* set ina3221 to power down mode */
- ret = power_down_ina3221(client);
+ ret = __locked_power_down_ina3221(client);
if (ret < 0)
goto exit_remove;
@@ -393,10 +478,13 @@ static int ina3221_remove(struct i2c_client *client)
{
u8 i;
struct ina3221_data *data = i2c_get_clientdata(client);
+ mutex_lock(&data->mutex);
+ __locked_power_down_ina3221(client);
hwmon_device_unregister(data->hwmon_dev);
+ mutex_unlock(&data->mutex);
+ unregister_hotcpu_notifier(&(data->nb));
for (i = 0; i < ARRAY_SIZE(ina3221); i++)
device_remove_file(&client->dev, &ina3221[i].dev_attr);
- kfree(data);
return 0;
}