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