[PATCH] Add temporary adm1278 hwmon pmbus driver patch

Adriana Kobylak adriana.kobylak at gmail.com
Wed Mar 2 08:45:50 AEDT 2016


From: Yi Li <adamliyi at msn.com>

From: Yi Li <adamliyi at msn.com>

This patch temporarily adds adm1278 hwmon driver as a kernel patch to openbmc,
for testing on Barreleye.
If in the future this patch will still be useful, it should be put into linux kernel.

Barreleye has three adm1278 devices on three i2c buses.
This patch enables reading adm1278 sensors via hwmon sysfs interface.
The enabled sensors are: current, voltage (In and Out), power and temperature.
Detail usage can be found in 'readme_adm1278.txt'.

Signed-off-by: Yi Li <adamliyi at msn.com>
---
 .../recipes-kernel/linux/linux-obmc/barreleye.cfg  |   3 +
 .../linux/linux-obmc/hwmon_adm1278.patch           | 165 +++++++++++++++++++++
 .../linux/linux-obmc/readme_adm1278.txt            |  78 ++++++++++
 .../recipes-kernel/linux/linux-obmc_%.bbappend     |   3 +
 4 files changed, 249 insertions(+)
 create mode 100644 meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/barreleye.cfg
 create mode 100644 meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/hwmon_adm1278.patch
 create mode 100644 meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/readme_adm1278.txt
 create mode 100644 meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc_%.bbappend

diff --git a/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/barreleye.cfg b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/barreleye.cfg
new file mode 100644
index 0000000..086f191
--- /dev/null
+++ b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/barreleye.cfg
@@ -0,0 +1,3 @@
+CONFIG_PMBUS=y
+CONFIG_SENSORS_PMBUS=y
+CONFIG_SENSORS_ADM1275=y
diff --git a/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/hwmon_adm1278.patch b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/hwmon_adm1278.patch
new file mode 100644
index 0000000..074d39f
--- /dev/null
+++ b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/hwmon_adm1278.patch
@@ -0,0 +1,165 @@
+diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
+index 188af4c..a45075d 100644
+--- a/drivers/hwmon/pmbus/adm1275.c
++++ b/drivers/hwmon/pmbus/adm1275.c
+@@ -24,7 +24,7 @@
+ #include <linux/bitops.h>
+ #include "pmbus.h"
+ 
+-enum chips { adm1075, adm1275, adm1276, adm1293, adm1294 };
++enum chips { adm1075, adm1275, adm1276, adm1278, adm1293, adm1294 };
+ 
+ #define ADM1275_MFR_STATUS_IOUT_WARN2	BIT(0)
+ #define ADM1293_MFR_STATUS_VAUX_UV_WARN	BIT(5)
+@@ -70,6 +70,22 @@ enum chips { adm1075, adm1275, adm1276, adm1293, adm1294 };
+ #define ADM1075_VAUX_OV_WARN		BIT(7)
+ #define ADM1075_VAUX_UV_WARN		BIT(6)
+ 
++#define ADM1278_PMON_CONTROL		0xd3
++#define ADM1278_PMON_CONFIG		0xd4
++#define ADM1278_CFG_TSFLT		BIT(15)
++#define ADM1278_CFG_SIMULTANEOUS	BIT(14)
++#define ADM1278_CFG_PMON_MODE		BIT(4)
++#define ADM1278_CFG_TEMP1_EN		BIT(3)
++#define ADM1278_CFG_VIN_EN		BIT(2)
++#define ADM1278_CFG_VOUT_EN		BIT(1)
++#define ADM1278_PEAK_TEMPERATURE	0xd7
++
++#define ADM1278_R_SENSE	1000	/* R_sense resistor value in microohmsi */
++
++static int r_sense = ADM1278_R_SENSE;
++module_param(r_sense, int, 0644);
++MODULE_PARM_DESC(r_sense, "Rsense resistor value in microohms");
++
+ struct adm1275_data {
+ 	int id;
+ 	bool have_oc_fault;
+@@ -186,6 +202,11 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
+ 	case PMBUS_VIRT_READ_VIN_MAX:
+ 		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
+ 		break;
++	case PMBUS_VIRT_READ_TEMP_MAX:
++		if (data->id != adm1278)
++			return -ENODATA;
++		ret = pmbus_read_word_data(client, 0, ADM1278_PEAK_TEMPERATURE);
++		break;
+ 	case PMBUS_VIRT_READ_PIN_MIN:
+ 		if (!data->have_pin_min)
+ 			return -ENXIO;
+@@ -199,6 +220,7 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
+ 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ 	case PMBUS_VIRT_RESET_VIN_HISTORY:
++	case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ 		break;
+ 	case PMBUS_VIRT_RESET_PIN_HISTORY:
+ 		if (!data->have_pin_max)
+@@ -239,6 +261,9 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
+ 	case PMBUS_VIRT_RESET_VIN_HISTORY:
+ 		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
+ 		break;
++	case PMBUS_VIRT_RESET_TEMP_HISTORY:
++		ret = pmbus_write_word_data(client, 0, ADM1278_PEAK_TEMPERATURE, 0);
++		break;
+ 	case PMBUS_VIRT_RESET_PIN_HISTORY:
+ 		ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
+ 		if (!ret && data->have_pin_min)
+@@ -312,6 +337,7 @@ static const struct i2c_device_id adm1275_id[] = {
+ 	{ "adm1075", adm1075 },
+ 	{ "adm1275", adm1275 },
+ 	{ "adm1276", adm1276 },
++	{ "adm1278", adm1278 },
+ 	{ "adm1293", adm1293 },
+ 	{ "adm1294", adm1294 },
+ 	{ }
+@@ -335,6 +361,8 @@ static int adm1275_probe(struct i2c_client *client,
+ 				     | I2C_FUNC_SMBUS_BLOCK_DATA))
+ 		return -ENODEV;
+ 
++	/* i2c_aspeed driver does not handle i2c_smbus_read_block_data correctly */
++#if 0
+ 	ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer);
+ 	if (ret < 0) {
+ 		dev_err(&client->dev, "Failed to read Manufacturer ID\n");
+@@ -363,6 +391,7 @@ static int adm1275_probe(struct i2c_client *client,
+ 		dev_notice(&client->dev,
+ 			   "Device mismatch: Configured %s, detected %s\n",
+ 			   id->name, mid->name);
++#endif
+ 
+ 	config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
+ 	if (config < 0)
+@@ -377,7 +406,9 @@ static int adm1275_probe(struct i2c_client *client,
+ 	if (!data)
+ 		return -ENOMEM;
+ 
+-	data->id = mid->driver_data;
++	/* i2c_aspeed driver does not handle i2c_smbus_read_block_data correctly */
++	//data->id = mid->driver_data;
++	data->id = adm1278;
+ 
+ 	info = &data->info;
+ 
+@@ -460,6 +491,62 @@ static int adm1275_probe(struct i2c_client *client,
+ 			info->func[0] |=
+ 			  PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ 		break;
++	case adm1278:
++		/* Configure monitoring */
++		ret = i2c_smbus_write_byte_data(client,
++			ADM1278_PMON_CONTROL, 0);
++		if (ret < 0)
++			return ret;
++		ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG);
++		ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG,
++						ADM1278_CFG_PMON_MODE |
++						ADM1278_CFG_TEMP1_EN |
++						ADM1278_CFG_VIN_EN |
++						ADM1278_CFG_VOUT_EN);
++		if (ret < 0)
++			return ret;
++		ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG);
++		dev_info(&client->dev, "adm1278 config: 0x%x\n", ret);
++		ret = i2c_smbus_write_byte_data(client, ADM1278_PMON_CONTROL,1);
++		if (ret < 0)
++			return ret;
++
++		info->func[0] |= PMBUS_HAVE_VIN
++			| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
++			| PMBUS_HAVE_PIN
++			| PMBUS_HAVE_STATUS_INPUT
++			| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
++
++		data->have_oc_fault = false;
++		data->have_uc_fault = false;
++		data->have_vout = true;
++		data->have_vaux_status = false;
++		data->have_mfr_vaux_status = false;
++		data->have_iout_min = false;
++		data->have_pin_min = false;
++		data->have_pin_max = true;
++
++		info->m[PSC_VOLTAGE_IN] = 19599;
++		info->b[PSC_VOLTAGE_IN] = 0;
++		info->R[PSC_VOLTAGE_IN] = -2;
++
++		info->m[PSC_VOLTAGE_OUT] = 19599;
++		info->b[PSC_VOLTAGE_OUT] = 0;
++		info->R[PSC_VOLTAGE_OUT] = -2;
++
++		info->m[PSC_CURRENT_OUT] = 800 * r_sense / 1000;
++		info->b[PSC_CURRENT_OUT] = 20475;
++		info->R[PSC_CURRENT_OUT] = -1;
++
++		info->m[PSC_POWER] = 6123 * r_sense / 1000;
++		info->b[PSC_POWER] = 0;
++		info->R[PSC_POWER] = -2;
++
++		info->format[PSC_TEMPERATURE] = direct;
++		info->m[PSC_TEMPERATURE] = 42;
++		info->b[PSC_TEMPERATURE] = 31880;
++		info->R[PSC_TEMPERATURE] = -1;
++		break;
+ 	case adm1293:
+ 	case adm1294:
+ 		data->have_iout_min = true;
diff --git a/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/readme_adm1278.txt b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/readme_adm1278.txt
new file mode 100644
index 0000000..e86d977
--- /dev/null
+++ b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc/readme_adm1278.txt
@@ -0,0 +1,78 @@
+README for adm1278 hwmon driver
+==================================
+Yi Li <shliyi at cn.ibm.com>
+
+
+This is a temporary kernel patch to enable hwmon driver for adm1278 chip on Barreleye.
+When this patch is merged into linux kernel, this patch will be removed from openbmc.
+
+The adm1278 driver is created according to datasheet:
+http://www.analog.com/media/en/technical-documentation/data-sheets/ADM1278.pdf
+
+The patch heavily re-used adm1278 enabling code from: https://github.com/facebook/openbmc/blob/master/meta-aspeed/recipes-kernel/linux/files/patch-2.6.28.9/0000-linux-openbmc.patch.
+
+This patch has been tested on barreleye, by following these steps:
+
+1) There are 3 adm1278 devices on Barreleye
+
+I2C5: P12v_a for CPU0
+I2C6: P12v_b for CPU1
+I2C7: P12v_c for HDD and IO Board
+
+2) adm1278 driver is based on adm1275.c, which depends on pmbus. This patch builds
+adm1275 and pmbus into kernel.
+
+3) When kernel booted, initialize the adm1278 devices:
+
+root at barreleye:~# echo adm1278 0x10 > /sys/class/i2c-adapter/i2c-4/new_device
+root at barreleye:~# echo adm1278 0x10 > /sys/class/i2c-adapter/i2c-5/new_device
+root at barreleye:~# echo adm1278 0x10 > /sys/class/i2c-adapter/i2c-6/new_device
+
+There will be three new hwmon sysfs entries created:
+
+root at barreleye:~# ls /sys/class/hwmon/hwmon3/
+curr1_highest         in1_highest           in1_reset_history     in2_min_alarm         power1_label          temp1_input
+curr1_input           in1_input             in2_highest           in2_reset_history     power1_max            temp1_max
+curr1_label           in1_label             in2_input             name                  power1_reset_history  temp1_max_alarm
+curr1_max             in1_max               in2_label             power/                subsystem/            temp1_reset_history
+curr1_max_alarm       in1_max_alarm         in2_max               power1_alarm          temp1_crit            uevent
+curr1_reset_history   in1_min               in2_max_alarm         power1_input          temp1_crit_alarm
+device/               in1_min_alarm         in2_min               power1_input_highest  temp1_highest
+root at barreleye:~# ls /sys/class/hwmon/hwmon4/
+curr1_highest         in1_highest           in1_reset_history     in2_min_alarm         power1_label          temp1_input
+curr1_input           in1_input             in2_highest           in2_reset_history     power1_max            temp1_max
+curr1_label           in1_label             in2_input             name                  power1_reset_history  temp1_max_alarm
+curr1_max             in1_max               in2_label             power/                subsystem/            temp1_reset_history
+curr1_max_alarm       in1_max_alarm         in2_max               power1_alarm          temp1_crit            uevent
+curr1_reset_history   in1_min               in2_max_alarm         power1_input          temp1_crit_alarm
+device/               in1_min_alarm         in2_min               power1_input_highest  temp1_highest
+root at barreleye:~# ls /sys/class/hwmon/hwmon5/
+curr1_highest         in1_highest           in1_reset_history     in2_min_alarm         power1_label          temp1_input
+curr1_input           in1_input             in2_highest           in2_reset_history     power1_max            temp1_max
+curr1_label           in1_label             in2_input             name                  power1_reset_history  temp1_max_alarm
+curr1_max             in1_max               in2_label             power/                subsystem/            temp1_reset_history
+curr1_max_alarm       in1_max_alarm         in2_max               power1_alarm          temp1_crit            uevent
+curr1_reset_history   in1_min               in2_max_alarm         power1_input          temp1_crit_alarm
+device/               in1_min_alarm         in2_min               power1_input_highest  temp1_highest
+
+4) For details of what each hwmon sysfs attributes mean, please refer to:
+https://www.kernel.org/doc/Documentation/hwmon/pmbus
+For short, 'curr1_*' refers to 'IOUT', 'in1_*' refers to 'vin', 'in2_*' refers to 'vout', 'power1_*' refers to 'input power',
+'temp1_*' for 'temperature'.
+
+5) Remaining issue:
+
+5.1) Currently, i2c_aspeed driver does not handle "i2c_smbus_read_block_data()" correctly. So this patch has to bypass some detection code.
+We need to fix this issue when the patch is merged to kernel.
+5.2) According to adm1278 datasheet, there is a sense resistor used to measure power and current. The resistor will affect conversion between
+adm1278 register value to real-world value for current and power. I am not very sure about the resistor value. So using 1 mili-ohms (or 1000 micro-ohms) as default value. When build the adm1275 driver as kernel module, we can set this resistor value by:
+
+# insmod adm1275.ko r_sense=500
+
+This will set the 'sense resistor' to 500 micro-ohms.
+5.3) Some of the sensor value, e.g, 'temp1_input' seems not reasonable, e.g:
+
+root at barreleye:~# cat /sys/class/hwmon/hwmon4/temp1_input
+-270952
+
+Need further check on that.
diff --git a/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc_%.bbappend b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc_%.bbappend
new file mode 100644
index 0000000..b6e8e16
--- /dev/null
+++ b/meta-openbmc-machines/meta-openpower/meta-rackspace/meta-barreleye/recipes-kernel/linux/linux-obmc_%.bbappend
@@ -0,0 +1,3 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/linux-obmc:"
+SRC_URI += "file://barreleye.cfg"
+SRC_URI += "file://hwmon_adm1278.patch"
-- 
1.8.2.2



More information about the openbmc mailing list