[PATCH 0/2] hwmon: (pmbus/ibm-cffps) Add version detection capability

2019-10-09 Thread Eddie James
The IBM common form factor power supply driver may need to detect which PSU
version is connected, since some systems can use a variety of power supplies.
This series adds a new compatible string with no version number, and some code
to parse the CCIN of the PSU to determine which version is applicable.

Eddie James (2):
  dt-bindings: hwmon: Document ibm,cffps compatible string
  hwmon: (pmbus/ibm-cffps) Add version detection capability

 .../devicetree/bindings/hwmon/ibm,cffps1.txt   |  3 ++
 drivers/hwmon/pmbus/ibm-cffps.c| 37 +++---
 2 files changed, 36 insertions(+), 4 deletions(-)

-- 
1.8.3.1



[PATCH 1/2] dt-bindings: hwmon: Document ibm,cffps compatible string

2019-10-09 Thread Eddie James
Document this string that indicates that any version of the power supply
may be connected. In this case, the driver must detect the version
automatically.

Signed-off-by: Eddie James 
---
 Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt 
b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
index 1036f65..d9a2719 100644
--- a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
+++ b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
@@ -5,6 +5,9 @@ Required properties:
  - compatible  : Must be one of the following:
"ibm,cffps1"
"ibm,cffps2"
+   or "ibm,cffps" if the system
+   must support any version of the
+   power supply
  - reg = < I2C bus address >;  : Address of the power supply on the
  I2C bus.
 
-- 
1.8.3.1



[PATCH 2/2] hwmon: (pmbus/ibm-cffps) Add version detection capability

2019-10-09 Thread Eddie James
Some systems may plug in either version 1 or version 2 of the IBM common
form factor power supply. Add a version-less compatibility string that
tells the driver to try and detect which version of the power supply is
connected.

Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ibm-cffps.c | 37 +
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index d44745e..d61547e 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -3,6 +3,7 @@
  * Copyright 2017 IBM Corp.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -29,6 +30,10 @@
 #define CFFPS_INPUT_HISTORY_CMD0xD6
 #define CFFPS_INPUT_HISTORY_SIZE   100
 
+#define CFFPS_CCIN_VERSION GENMASK(15, 8)
+#define CFFPS_CCIN_VERSION_10x2b
+#define CFFPS_CCIN_VERSION_20x2e
+
 /* STATUS_MFR_SPECIFIC bits */
 #define CFFPS_MFR_FAN_FAULTBIT(0)
 #define CFFPS_MFR_THERMAL_FAULTBIT(1)
@@ -54,7 +59,7 @@ enum {
CFFPS_DEBUGFS_NUM_ENTRIES
 };
 
-enum versions { cffps1, cffps2 };
+enum versions { cffps1, cffps2, cffps_unknown };
 
 struct ibm_cffps_input_history {
struct mutex update_lock;
@@ -395,7 +400,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
 {
int i, rc;
-   enum versions vs;
+   enum versions vs = cffps_unknown;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -405,8 +410,27 @@ static int ibm_cffps_probe(struct i2c_client *client,
vs = (enum versions)md;
else if (id)
vs = (enum versions)id->driver_data;
-   else
-   vs = cffps1;
+
+   if (vs == cffps_unknown) {
+   u16 ccin_version = CFFPS_CCIN_VERSION_1;
+   int ccin = i2c_smbus_read_word_swapped(client, CFFPS_CCIN_CMD);
+
+   if (ccin > 0)
+   ccin_version = FIELD_GET(CFFPS_CCIN_VERSION, ccin);
+
+   switch (ccin_version) {
+   default:
+   case CFFPS_CCIN_VERSION_1:
+   vs = cffps1;
+   break;
+   case CFFPS_CCIN_VERSION_2:
+   vs = cffps2;
+   break;
+   }
+
+   /* Set the client name to include the version number. */
+   snprintf(client->name, I2C_NAME_SIZE, "cffps%d", vs + 1);
+   }
 
client->dev.platform_data = &ibm_cffps_pdata;
rc = pmbus_do_probe(client, id, &ibm_cffps_info[vs]);
@@ -465,6 +489,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
 static const struct i2c_device_id ibm_cffps_id[] = {
{ "ibm_cffps1", cffps1 },
{ "ibm_cffps2", cffps2 },
+   { "ibm_cffps", cffps_unknown },
{}
 };
 MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
@@ -478,6 +503,10 @@ static int ibm_cffps_probe(struct i2c_client *client,
.compatible = "ibm,cffps2",
.data = (void *)cffps2
},
+   {
+   .compatible = "ibm,cffps",
+   .data = (void *)cffps_unknown
+   },
{}
 };
 MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
-- 
1.8.3.1



[PATCH v2 1/3] dt-bindings: hwmon: Document ibm,cffps2 compatible string

2019-08-30 Thread Eddie James
Document the compatible string for version 2 of the IBM CFFPS PSU.

Signed-off-by: Eddie James 
---
 Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt 
b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
index f68a0a6..1036f65 100644
--- a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
+++ b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
@@ -1,8 +1,10 @@
-Device-tree bindings for IBM Common Form Factor Power Supply Version 1
---
+Device-tree bindings for IBM Common Form Factor Power Supply Versions 1 and 2
+-
 
 Required properties:
- - compatible = "ibm,cffps1";
+ - compatible  : Must be one of the following:
+   "ibm,cffps1"
+   "ibm,cffps2"
  - reg = < I2C bus address >;  : Address of the power supply on the
  I2C bus.
 
-- 
1.8.3.1



[PATCH v2 0/3] pmbus: ibm-cffps: Add support for version 2 of PSU

2019-08-30 Thread Eddie James
Version 2 of this PSU supports a second page of data and changes the
format of the FW version command. Use the devicetree binding (or the I2C
device ID) to determine which version the driver should use. Therefore add
the new compatible string to the devicetree documentation and change the
Swift system devicetree to use version 2.

Changes since v1:
 - use an enum for the version instead of integers 1, 2, etc

Eddie James (3):
  dt-bindings: hwmon: Document ibm,cffps2 compatible string
  ARM: dts: aspeed: swift: Change power supplies to version 2
  pmbus: ibm-cffps: Add support for version 2 of the PSU

 .../devicetree/bindings/hwmon/ibm,cffps1.txt   |   8 +-
 arch/arm/boot/dts/aspeed-bmc-opp-swift.dts |   4 +-
 drivers/hwmon/pmbus/ibm-cffps.c| 110 -
 3 files changed, 95 insertions(+), 27 deletions(-)

-- 
1.8.3.1



[PATCH v2 2/3] ARM: dts: aspeed: swift: Change power supplies to version 2

2019-08-30 Thread Eddie James
Swift power supplies are version 2 of the IBM CFFPS. Make it so in the
devicetree.

Signed-off-by: Eddie James 
---
 arch/arm/boot/dts/aspeed-bmc-opp-swift.dts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts 
b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
index f14f745..815b865 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
@@ -494,7 +494,7 @@
};
 
power-supply@68 {
-   compatible = "ibm,cffps1";
+   compatible = "ibm,cffps2";
reg = <0x68>;
};
 
@@ -504,7 +504,7 @@
};
 
power-supply@69 {
-   compatible = "ibm,cffps1";
+   compatible = "ibm,cffps2";
reg = <0x69>;
};
 
-- 
1.8.3.1



[PATCH v2 3/3] pmbus: ibm-cffps: Add support for version 2 of the PSU

2019-08-30 Thread Eddie James
Version 2 of the PSU supports a second page of data and changes the
format of the FW version. Use the devicetree binding to differentiate
between the version the driver should use.

Signed-off-by: Eddie James 
---
Changes since v1:
 - use an enum for the version instead of integers 1, 2, etc

 drivers/hwmon/pmbus/ibm-cffps.c | 110 
 1 file changed, 88 insertions(+), 22 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index ee2ee9e..d44745e 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -12,6 +12,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "pmbus.h"
@@ -20,8 +21,9 @@
 #define CFFPS_PN_CMD   0x9B
 #define CFFPS_SN_CMD   0x9E
 #define CFFPS_CCIN_CMD 0xBD
-#define CFFPS_FW_CMD_START 0xFA
-#define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_FW_CMD   0xFA
+#define CFFPS1_FW_NUM_BYTES4
+#define CFFPS2_FW_NUM_WORDS3
 #define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
@@ -52,6 +54,8 @@ enum {
CFFPS_DEBUGFS_NUM_ENTRIES
 };
 
+enum versions { cffps1, cffps2 };
+
 struct ibm_cffps_input_history {
struct mutex update_lock;
unsigned long last_update;
@@ -61,6 +65,7 @@ struct ibm_cffps_input_history {
 };
 
 struct ibm_cffps {
+   enum versions version;
struct i2c_client *client;
 
struct ibm_cffps_input_history input_history;
@@ -132,6 +137,8 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char 
__user *buf,
struct ibm_cffps *psu = to_psu(idxp, idx);
char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
 
+   pmbus_set_page(psu->client, 0);
+
switch (idx) {
case CFFPS_DEBUGFS_INPUT_HISTORY:
return ibm_cffps_read_input_history(psu, buf, count, ppos);
@@ -152,16 +159,36 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, 
char __user *buf,
rc = snprintf(data, 5, "%04X", rc);
goto done;
case CFFPS_DEBUGFS_FW:
-   for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
-   rc = i2c_smbus_read_byte_data(psu->client,
- CFFPS_FW_CMD_START + i);
-   if (rc < 0)
-   return rc;
+   switch (psu->version) {
+   case cffps1:
+   for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
+   rc = i2c_smbus_read_byte_data(psu->client,
+ CFFPS_FW_CMD +
+   i);
+   if (rc < 0)
+   return rc;
+
+   snprintf(&data[i * 2], 3, "%02X", rc);
+   }
 
-   snprintf(&data[i * 2], 3, "%02X", rc);
-   }
+   rc = i * 2;
+   break;
+   case cffps2:
+   for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
+   rc = i2c_smbus_read_word_data(psu->client,
+ CFFPS_FW_CMD +
+   i);
+   if (rc < 0)
+   return rc;
+
+   snprintf(&data[i * 4], 5, "%04X", rc);
+   }
 
-   rc = i * 2;
+   rc = i * 4;
+   break;
+   default:
+   return -EOPNOTSUPP;
+   }
goto done;
default:
return -EINVAL;
@@ -279,6 +306,8 @@ static void ibm_cffps_led_brightness_set(struct 
led_classdev *led_cdev,
psu->led_state = CFFPS_LED_ON;
}
 
+   pmbus_set_page(psu->client, 0);
+
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
   psu->led_state);
if (rc < 0)
@@ -299,6 +328,8 @@ static int ibm_cffps_led_blink_set(struct led_classdev 
*led_cdev,
if (led_cdev->brightness == LED_OFF)
return 0;
 
+   pmbus_set_page(psu->client, 0);
+
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
   CFFPS_LED_BLINK);
if (rc < 0)
@@ -328,15 +359,32 @@ static void ibm_cffps_create_led_class(struct ibm_cffps 
*psu)
dev_warn(dev, "failed to register led class: %d\n", r

Re: [PATCH 3/3] pmbus: ibm-cffps: Add support for version 2 of the PSU

2019-08-30 Thread Eddie James



On 8/30/19 12:36 PM, Guenter Roeck wrote:

On Fri, Aug 30, 2019 at 11:09:45AM -0500, Eddie James wrote:

Version 2 of the PSU supports a second page of data and changes the
format of the FW version. Use the devicetree binding to differentiate
between the version the driver should use.

Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ibm-cffps.c | 109 
  1 file changed, 87 insertions(+), 22 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index ee2ee9e..ca26fbd 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -12,16 +12,20 @@
  #include 
  #include 
  #include 
+#include 
  #include 
  
  #include "pmbus.h"
  
+#define CFFPS_VERSIONS2

+

Any chance you can use an enum for the versions ? Using version
numbers 1/2 combined with array indices 0/1 is confusing, error
prone, and seems unnecessary.



Sure, good idea.

Thanks,

Eddie




Thanks,
Guenter


  #define CFFPS_FRU_CMD 0x9A
  #define CFFPS_PN_CMD  0x9B
  #define CFFPS_SN_CMD  0x9E
  #define CFFPS_CCIN_CMD0xBD
-#define CFFPS_FW_CMD_START 0xFA
-#define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_FW_CMD   0xFA
+#define CFFPS1_FW_NUM_BYTES4
+#define CFFPS2_FW_NUM_WORDS3
  #define CFFPS_SYS_CONFIG_CMD  0xDA
  
  #define CFFPS_INPUT_HISTORY_CMD			0xD6

@@ -61,6 +65,7 @@ struct ibm_cffps_input_history {
  };
  
  struct ibm_cffps {

+   int version;
struct i2c_client *client;
  
  	struct ibm_cffps_input_history input_history;

@@ -132,6 +137,8 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char 
__user *buf,
struct ibm_cffps *psu = to_psu(idxp, idx);
char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
  
+	pmbus_set_page(psu->client, 0);

+
switch (idx) {
case CFFPS_DEBUGFS_INPUT_HISTORY:
return ibm_cffps_read_input_history(psu, buf, count, ppos);
@@ -152,16 +159,36 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, 
char __user *buf,
rc = snprintf(data, 5, "%04X", rc);
goto done;
case CFFPS_DEBUGFS_FW:
-   for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
-   rc = i2c_smbus_read_byte_data(psu->client,
- CFFPS_FW_CMD_START + i);
-   if (rc < 0)
-   return rc;
+   switch (psu->version) {
+   case 1:
+   for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
+   rc = i2c_smbus_read_byte_data(psu->client,
+ CFFPS_FW_CMD +
+   i);
+   if (rc < 0)
+   return rc;
+
+   snprintf(&data[i * 2], 3, "%02X", rc);
+   }
  
-			snprintf(&data[i * 2], 3, "%02X", rc);

-   }
+   rc = i * 2;
+   break;
+   case 2:
+   for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
+   rc = i2c_smbus_read_word_data(psu->client,
+ CFFPS_FW_CMD +
+   i);
+   if (rc < 0)
+   return rc;
+
+   snprintf(&data[i * 4], 5, "%04X", rc);
+   }
  
-		rc = i * 2;

+   rc = i * 4;
+   break;
+   default:
+   return -EOPNOTSUPP;
+   }
goto done;
default:
return -EINVAL;
@@ -279,6 +306,8 @@ static void ibm_cffps_led_brightness_set(struct 
led_classdev *led_cdev,
psu->led_state = CFFPS_LED_ON;
}
  
+	pmbus_set_page(psu->client, 0);

+
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
   psu->led_state);
if (rc < 0)
@@ -299,6 +328,8 @@ static int ibm_cffps_led_blink_set(struct led_classdev 
*led_cdev,
if (led_cdev->brightness == LED_OFF)
return 0;
  
+	pmbus_set_page(psu->client, 0);

+
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
   CFFPS_LED_BLINK);
if (rc < 0)
@@ -328,15 +359,32 @@ static void ibm_cffps_create_led_class(struct ibm_cffps 
*psu)
dev_warn(dev,

[PATCH 1/3] dt-bindings: hwmon: Document ibm,cffps2 compatible string

2019-08-30 Thread Eddie James
Document the compatible string for version 2 of the IBM CFFPS PSU.

Signed-off-by: Eddie James 
---
 Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt 
b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
index f68a0a6..1036f65 100644
--- a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
+++ b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt
@@ -1,8 +1,10 @@
-Device-tree bindings for IBM Common Form Factor Power Supply Version 1
---
+Device-tree bindings for IBM Common Form Factor Power Supply Versions 1 and 2
+-
 
 Required properties:
- - compatible = "ibm,cffps1";
+ - compatible  : Must be one of the following:
+   "ibm,cffps1"
+   "ibm,cffps2"
  - reg = < I2C bus address >;  : Address of the power supply on the
  I2C bus.
 
-- 
1.8.3.1



[PATCH 2/3] ARM: dts: aspeed: swift: Change power supplies to version 2

2019-08-30 Thread Eddie James
Swift power supplies are version 2 of the IBM CFFPS. Make it so in the
devicetree.

Signed-off-by: Eddie James 
---
 arch/arm/boot/dts/aspeed-bmc-opp-swift.dts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts 
b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
index f14f745..815b865 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-swift.dts
@@ -494,7 +494,7 @@
};
 
power-supply@68 {
-   compatible = "ibm,cffps1";
+   compatible = "ibm,cffps2";
reg = <0x68>;
};
 
@@ -504,7 +504,7 @@
};
 
power-supply@69 {
-   compatible = "ibm,cffps1";
+   compatible = "ibm,cffps2";
reg = <0x69>;
};
 
-- 
1.8.3.1



[PATCH 3/3] pmbus: ibm-cffps: Add support for version 2 of the PSU

2019-08-30 Thread Eddie James
Version 2 of the PSU supports a second page of data and changes the
format of the FW version. Use the devicetree binding to differentiate
between the version the driver should use.

Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ibm-cffps.c | 109 
 1 file changed, 87 insertions(+), 22 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index ee2ee9e..ca26fbd 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -12,16 +12,20 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "pmbus.h"
 
+#define CFFPS_VERSIONS 2
+
 #define CFFPS_FRU_CMD  0x9A
 #define CFFPS_PN_CMD   0x9B
 #define CFFPS_SN_CMD   0x9E
 #define CFFPS_CCIN_CMD 0xBD
-#define CFFPS_FW_CMD_START 0xFA
-#define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_FW_CMD   0xFA
+#define CFFPS1_FW_NUM_BYTES4
+#define CFFPS2_FW_NUM_WORDS3
 #define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
@@ -61,6 +65,7 @@ struct ibm_cffps_input_history {
 };
 
 struct ibm_cffps {
+   int version;
struct i2c_client *client;
 
struct ibm_cffps_input_history input_history;
@@ -132,6 +137,8 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char 
__user *buf,
struct ibm_cffps *psu = to_psu(idxp, idx);
char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
 
+   pmbus_set_page(psu->client, 0);
+
switch (idx) {
case CFFPS_DEBUGFS_INPUT_HISTORY:
return ibm_cffps_read_input_history(psu, buf, count, ppos);
@@ -152,16 +159,36 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, 
char __user *buf,
rc = snprintf(data, 5, "%04X", rc);
goto done;
case CFFPS_DEBUGFS_FW:
-   for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
-   rc = i2c_smbus_read_byte_data(psu->client,
- CFFPS_FW_CMD_START + i);
-   if (rc < 0)
-   return rc;
+   switch (psu->version) {
+   case 1:
+   for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
+   rc = i2c_smbus_read_byte_data(psu->client,
+ CFFPS_FW_CMD +
+   i);
+   if (rc < 0)
+   return rc;
+
+   snprintf(&data[i * 2], 3, "%02X", rc);
+   }
 
-   snprintf(&data[i * 2], 3, "%02X", rc);
-   }
+   rc = i * 2;
+   break;
+   case 2:
+   for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
+   rc = i2c_smbus_read_word_data(psu->client,
+ CFFPS_FW_CMD +
+   i);
+   if (rc < 0)
+   return rc;
+
+   snprintf(&data[i * 4], 5, "%04X", rc);
+   }
 
-   rc = i * 2;
+   rc = i * 4;
+   break;
+   default:
+   return -EOPNOTSUPP;
+   }
goto done;
default:
return -EINVAL;
@@ -279,6 +306,8 @@ static void ibm_cffps_led_brightness_set(struct 
led_classdev *led_cdev,
psu->led_state = CFFPS_LED_ON;
}
 
+   pmbus_set_page(psu->client, 0);
+
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
   psu->led_state);
if (rc < 0)
@@ -299,6 +328,8 @@ static int ibm_cffps_led_blink_set(struct led_classdev 
*led_cdev,
if (led_cdev->brightness == LED_OFF)
return 0;
 
+   pmbus_set_page(psu->client, 0);
+
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
   CFFPS_LED_BLINK);
if (rc < 0)
@@ -328,15 +359,32 @@ static void ibm_cffps_create_led_class(struct ibm_cffps 
*psu)
dev_warn(dev, "failed to register led class: %d\n", rc);
 }
 
-static struct pmbus_driver_info ibm_cffps_info = {
-   .pages = 1,
-   .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
-   PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMB

[PATCH 0/3] pmbus: ibm-cffps: Add support for version 2 of PSU

2019-08-30 Thread Eddie James
Version 2 of this PSU supports a second page of data and changes the
format of the FW version command. Use the devicetree binding (or the I2C
device ID) to determine which version the driver should use. Therefore add
the new compatible string to the devicetree documentation and change the
Swift system devicetree to use version 2.

Eddie James (3):
  dt-bindings: hwmon: Document ibm,cffps2 compatible string
  ARM: dts: aspeed: swift: Change power supplies to version 2
  pmbus: ibm-cffps: Add support for version 2 of the PSU

 .../devicetree/bindings/hwmon/ibm,cffps1.txt   |   8 +-
 arch/arm/boot/dts/aspeed-bmc-opp-swift.dts |   4 +-
 drivers/hwmon/pmbus/ibm-cffps.c| 109 -
 3 files changed, 94 insertions(+), 27 deletions(-)

-- 
1.8.3.1



[PATCH] OCC: FSI and hwmon: Add sequence numbering

2019-06-26 Thread Eddie James
Sequence numbering of the commands submitted to the OCC is required by
the OCC interface specification. Add sequence numbering and check for
the correct sequence number on the response.

Signed-off-by: Eddie James 
---
 drivers/fsi/fsi-occ.c  | 15 ---
 drivers/hwmon/occ/common.c |  4 ++--
 drivers/hwmon/occ/common.h |  1 +
 3 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index a2301ce..7da9c81 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -412,6 +412,7 @@ int fsi_occ_submit(struct device *dev, const void *request, 
size_t req_len,
msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
struct occ *occ = dev_get_drvdata(dev);
struct occ_response *resp = response;
+   u8 seq_no;
u16 resp_data_length;
unsigned long start;
int rc;
@@ -426,6 +427,8 @@ int fsi_occ_submit(struct device *dev, const void *request, 
size_t req_len,
 
mutex_lock(&occ->occ_lock);
 
+   /* Extract the seq_no from the command (first byte) */
+   seq_no = *(const u8 *)request;
rc = occ_putsram(occ, OCC_SRAM_CMD_ADDR, request, req_len);
if (rc)
goto done;
@@ -441,11 +444,17 @@ int fsi_occ_submit(struct device *dev, const void 
*request, size_t req_len,
if (rc)
goto done;
 
-   if (resp->return_status == OCC_RESP_CMD_IN_PRG) {
+   if (resp->return_status == OCC_RESP_CMD_IN_PRG ||
+   resp->seq_no != seq_no) {
rc = -ETIMEDOUT;
 
-   if (time_after(jiffies, start + timeout))
-   break;
+   if (time_after(jiffies, start + timeout)) {
+   dev_err(occ->dev, "resp timeout status=%02x "
+   "resp seq_no=%d our seq_no=%d\n",
+   resp->return_status, resp->seq_no,
+   seq_no);
+   goto done;
+   }
 
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait_time);
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index d593517..a7d2b16 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -124,12 +124,12 @@ struct extended_sensor {
 static int occ_poll(struct occ *occ)
 {
int rc;
-   u16 checksum = occ->poll_cmd_data + 1;
+   u16 checksum = occ->poll_cmd_data + occ->seq_no + 1;
u8 cmd[8];
struct occ_poll_response_header *header;
 
/* big endian */
-   cmd[0] = 0; /* sequence number */
+   cmd[0] = occ->seq_no++; /* sequence number */
cmd[1] = 0; /* cmd type */
cmd[2] = 0; /* data length msb */
cmd[3] = 1; /* data length lsb */
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index fc13f3c..67e6968 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -95,6 +95,7 @@ struct occ {
struct occ_sensors sensors;
 
int powr_sample_time_us;/* average power sample time */
+   u8 seq_no;
u8 poll_cmd_data;   /* to perform OCC poll command */
int (*send_cmd)(struct occ *occ, u8 *cmd);
 
-- 
1.8.3.1



Re: [PATCH] hwmon (occ): Switch power average to between poll responses

2019-05-09 Thread Eddie James



On 5/8/19 4:03 PM, Guenter Roeck wrote:

On Tue, May 07, 2019 at 02:35:51PM -0500, Eddie James wrote:

The average power reported in the hwmon OCC driver is not useful
because the time it represents is too short. Instead, store the
previous power accumulator reported by the OCC and average it with the
latest accumulator to obtain an average between poll responses. This
does operate under the assumption that the user polls regularly.


That looks really bad. Effectively it means that the number reported
as average power is more or less useless/random. On top of that, the code
gets so complicated that it is all but impossible to understand.

Does it really make sense to report an average that has effectively
no useful meaning (and is, for example, influenced just by reading it) ?



Yea... that's a good point. Basically our userspace environment has no 
good way to do this either, so I tried to shoe-horn this into the 
driver, but this approach is indeed bad. I'll abandon this patch.



Thanks!

Eddie




Guenter


Signed-off-by: Eddie James 
---
  drivers/hwmon/occ/common.c | 133 -
  drivers/hwmon/occ/common.h |   7 +++
  2 files changed, 103 insertions(+), 37 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index e6d3fb5..6ffcee7 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -118,6 +118,53 @@ struct extended_sensor {
u8 data[6];
  } __packed;
  
+static void occ_update_prev_power_avgs(struct occ *occ)

+{
+   u8 i;
+   struct power_sensor_1 *ps1;
+   struct power_sensor_2 *ps2;
+   struct power_sensor_a0 *psa0;
+   struct occ_sensor *power = &occ->sensors.power;
+   struct occ_power_avg *prevs = occ->prev_power_avgs;
+
+   switch (power->version) {
+   case 1:
+   for (i = 0; i < power->num_sensors; ++i) {
+   ps1 = ((struct power_sensor_1 *)power->data) + i;
+
+   prevs[i].accumulator =
+   get_unaligned_be32(&ps1->accumulator);
+   prevs[i].update_tag =
+   get_unaligned_be32(&ps1->update_tag);
+   }
+   break;
+   case 2:
+   for (i = 0; i < power->num_sensors; ++i) {
+   ps2 = ((struct power_sensor_2 *)power->data) + i;
+
+   prevs[i].accumulator =
+   get_unaligned_be64(&ps2->accumulator);
+   prevs[i].update_tag =
+   get_unaligned_be32(&ps2->update_tag);
+   }
+   break;
+   case 0xA0:
+   for (i = 0; i < power->num_sensors; ++i) {
+   psa0 = ((struct power_sensor_a0 *)power->data) + i;
+
+   prevs[i].accumulator = psa0->system.accumulator;
+   prevs[i].update_tag = psa0->system.update_tag;
+   prevs[i + 1].accumulator = psa0->proc.accumulator;
+   prevs[i + 1].update_tag = psa0->proc.update_tag;
+   prevs[i + 2].accumulator = psa0->vdd.accumulator;
+   prevs[i + 2].update_tag = psa0->vdd.update_tag;
+   prevs[i + 3].accumulator = psa0->vdn.accumulator;
+   prevs[i + 3].update_tag = psa0->vdn.update_tag;
+   }
+   break;
+   }
+}
+
  static int occ_poll(struct occ *occ)
  {
int rc;
@@ -135,6 +182,8 @@ static int occ_poll(struct occ *occ)
cmd[6] = checksum & 0xFF;   /* checksum lsb */
cmd[7] = 0;
  
+	occ_update_prev_power_avgs(occ);

+
/* mutex should already be locked if necessary */
rc = occ->send_cmd(occ, cmd);
if (rc) {
@@ -208,6 +257,7 @@ int occ_update_response(struct occ *occ)
/* limit the maximum rate of polling the OCC */
if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
rc = occ_poll(occ);
+   occ->prev_update = occ->last_update;
occ->last_update = jiffies;
} else {
rc = occ->last_error;
@@ -364,6 +414,14 @@ static ssize_t occ_show_freq_2(struct device *dev,
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
  }
  
+static u64 occ_power_avg(struct occ *occ, u8 idx, u64 accum, u32 samples)

+{
+   struct occ_power_avg *avg = &occ->prev_power_avgs[idx];
+
+   return div_u64((accum - avg->accumulator) * 100ULL,
+  samples - avg->update_tag);
+}
+
  static ssize_t occ_show_power_1(struct device *dev,
struct device_attribute *attr, char *buf)
  {
@@ -385,13 +443,12 @@ static ssize_t occ_show_power_1(struct device *dev,
 

[PATCH] hwmon (occ): Switch power average to between poll responses

2019-05-07 Thread Eddie James
The average power reported in the hwmon OCC driver is not useful
because the time it represents is too short. Instead, store the
previous power accumulator reported by the OCC and average it with the
latest accumulator to obtain an average between poll responses. This
does operate under the assumption that the user polls regularly.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 133 -
 drivers/hwmon/occ/common.h |   7 +++
 2 files changed, 103 insertions(+), 37 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index e6d3fb5..6ffcee7 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -118,6 +118,53 @@ struct extended_sensor {
u8 data[6];
 } __packed;
 
+static void occ_update_prev_power_avgs(struct occ *occ)
+{
+   u8 i;
+   struct power_sensor_1 *ps1;
+   struct power_sensor_2 *ps2;
+   struct power_sensor_a0 *psa0;
+   struct occ_sensor *power = &occ->sensors.power;
+   struct occ_power_avg *prevs = occ->prev_power_avgs;
+
+   switch (power->version) {
+   case 1:
+   for (i = 0; i < power->num_sensors; ++i) {
+   ps1 = ((struct power_sensor_1 *)power->data) + i;
+
+   prevs[i].accumulator =
+   get_unaligned_be32(&ps1->accumulator);
+   prevs[i].update_tag =
+   get_unaligned_be32(&ps1->update_tag);
+   }
+   break;
+   case 2:
+   for (i = 0; i < power->num_sensors; ++i) {
+   ps2 = ((struct power_sensor_2 *)power->data) + i;
+
+   prevs[i].accumulator =
+   get_unaligned_be64(&ps2->accumulator);
+   prevs[i].update_tag =
+   get_unaligned_be32(&ps2->update_tag);
+   }
+   break;
+   case 0xA0:
+   for (i = 0; i < power->num_sensors; ++i) {
+   psa0 = ((struct power_sensor_a0 *)power->data) + i;
+
+   prevs[i].accumulator = psa0->system.accumulator;
+   prevs[i].update_tag = psa0->system.update_tag;
+   prevs[i + 1].accumulator = psa0->proc.accumulator;
+   prevs[i + 1].update_tag = psa0->proc.update_tag;
+   prevs[i + 2].accumulator = psa0->vdd.accumulator;
+   prevs[i + 2].update_tag = psa0->vdd.update_tag;
+   prevs[i + 3].accumulator = psa0->vdn.accumulator;
+   prevs[i + 3].update_tag = psa0->vdn.update_tag;
+   }
+   break;
+   }
+}
+
 static int occ_poll(struct occ *occ)
 {
int rc;
@@ -135,6 +182,8 @@ static int occ_poll(struct occ *occ)
cmd[6] = checksum & 0xFF;   /* checksum lsb */
cmd[7] = 0;
 
+   occ_update_prev_power_avgs(occ);
+
/* mutex should already be locked if necessary */
rc = occ->send_cmd(occ, cmd);
if (rc) {
@@ -208,6 +257,7 @@ int occ_update_response(struct occ *occ)
/* limit the maximum rate of polling the OCC */
if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
rc = occ_poll(occ);
+   occ->prev_update = occ->last_update;
occ->last_update = jiffies;
} else {
rc = occ->last_error;
@@ -364,6 +414,14 @@ static ssize_t occ_show_freq_2(struct device *dev,
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
 }
 
+static u64 occ_power_avg(struct occ *occ, u8 idx, u64 accum, u32 samples)
+{
+   struct occ_power_avg *avg = &occ->prev_power_avgs[idx];
+
+   return div_u64((accum - avg->accumulator) * 100ULL,
+  samples - avg->update_tag);
+}
+
 static ssize_t occ_show_power_1(struct device *dev,
struct device_attribute *attr, char *buf)
 {
@@ -385,13 +443,12 @@ static ssize_t occ_show_power_1(struct device *dev,
val = get_unaligned_be16(&power->sensor_id);
break;
case 1:
-   val = get_unaligned_be32(&power->accumulator) /
-   get_unaligned_be32(&power->update_tag);
-   val *= 100ULL;
+   val = occ_power_avg(occ, sattr->index,
+   get_unaligned_be32(&power->accumulator),
+   get_unaligned_be32(&power->update_tag));
break;
case 2:
-   val = (u64)get_unaligned_be32(&power->update_tag) *
-  occ->powr_sample_time_us;
+   val = jiffies_to_usecs(occ->last_update - occ->pre

Re: [PATCH v2 1/1] hwmon (occ): Add temp sensor value check

2019-04-17 Thread Eddie James



On 4/17/19 1:03 PM, Alexander Amelkin wrote:

From: Alexander Soldatov 

The occ driver supports two formats for the temp sensor value.

The OCC firmware for P8 supports only the first format, for which
no range checking or error processing is performed in the driver.
Inspecting the OCC sources for P8 reveals that OCC may send
a special value 0x to indicate that a sensor read timeout
has occurred, see

https://github.com/open-power/occ/blob/master_p8/src/occ/cmdh/cmdh_fsp_cmds.c#L395

That situation wasn't handled in the driver. This patch adds invalid
temp value check for the sensor data format 1 and handles it the same
way as it is done for the format 2, where EREMOTEIO is reported for
this case.



Thanks Alexander and Guenter.


Reviewed-by: Eddie James 




Signed-off-by: Alexander Soldatov 
Signed-off-by: Alexander Amelkin 
Reviewed-by: Alexander Amelkin 
Cc: Edward A. James 
Cc: Joel Stanley 
---
  drivers/hwmon/occ/common.c | 4 
  1 file changed, 4 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 4679acb..825631c 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -235,6 +235,12 @@ static ssize_t occ_show_temp_1(struct device *dev,
val = get_unaligned_be16(&temp->sensor_id);
break;
case 1:
+   /*
+* If a sensor reading has expired and couldn't be refreshed,
+* OCC returns 0x for that sensor.
+*/
+   if (temp->value == 0x)
+   return -EREMOTEIO;
val = get_unaligned_be16(&temp->value) * 1000;
break;
default:




[PATCH v2 3/3] hwmon (occ): Add more details to Kconfig help text

2019-04-16 Thread Eddie James
The help text needs to spell out how the driver runs on a BMC, as it
previously seemed to indicate it ran on a POWER processor.

Signed-off-by: Eddie James 
---
Changes since v1: fix Signed-off-by; git config got mixed up.

 drivers/hwmon/occ/Kconfig | 12 
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
index 2a3869f..1658634 100644
--- a/drivers/hwmon/occ/Kconfig
+++ b/drivers/hwmon/occ/Kconfig
@@ -9,8 +9,10 @@ config SENSORS_OCC_P8_I2C
select SENSORS_OCC
help
 This option enables support for monitoring sensors provided by the
-On-Chip Controller (OCC) on a POWER8 processor. Communications with
-the OCC are established through I2C bus.
+On-Chip Controller (OCC) on a POWER8 processor. However, this driver
+can only run on a baseboard management controller (BMC) connected to
+the P8, not the POWER processor itself. Communications with the OCC are
+established through I2C bus.
 
 This driver can also be built as a module. If so, the module will be
 called occ-p8-hwmon.
@@ -22,8 +24,10 @@ config SENSORS_OCC_P9_SBE
select SENSORS_OCC
help
 This option enables support for monitoring sensors provided by the
-On-Chip Controller (OCC) on a POWER9 processor. Communications with
-the OCC are established through SBE fifo on an FSI bus.
+On-Chip Controller (OCC) on a POWER9 processor. However, this driver
+can only run on a baseboard management controller (BMC) connected to
+the P9, not the POWER processor itself. Communications with the OCC are
+established through SBE fifo on an FSI bus.
 
 This driver can also be built as a module. If so, the module will be
 called occ-p9-hwmon.
-- 
2.7.4



[PATCH 2/3] hwmon (occ): Prevent sysfs error attribute from returning error

2019-04-16 Thread Eddie James
The error sysfs attribute returns the stored error state of the OCC and
doesn't depend on the OCC poll response. Therefore, split the error
attribute into it's own function to avoid failing out of the function if
the poll response fails.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/sysfs.c | 19 +--
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index 1cb1e65..c73be07 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -63,9 +63,6 @@ static ssize_t occ_sysfs_show(struct device *dev,
else
val = 1;
break;
-   case 8:
-   val = occ->error;
-   break;
default:
return -EINVAL;
}
@@ -73,6 +70,16 @@ static ssize_t occ_sysfs_show(struct device *dev,
return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
 }
 
+static ssize_t occ_error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+   struct occ *occ = dev_get_drvdata(dev);
+
+   occ_update_response(occ);
+
+   return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error);
+}
+
 static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
 static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1);
 static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);
@@ -81,7 +88,7 @@ static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, 
occ_sysfs_show, NULL, 4);
 static SENSOR_DEVICE_ATTR(occ_quick_pwr_drop, 0444, occ_sysfs_show, NULL, 5);
 static SENSOR_DEVICE_ATTR(occ_state, 0444, occ_sysfs_show, NULL, 6);
 static SENSOR_DEVICE_ATTR(occs_present, 0444, occ_sysfs_show, NULL, 7);
-static SENSOR_DEVICE_ATTR(occ_error, 0444, occ_sysfs_show, NULL, 8);
+static DEVICE_ATTR_RO(occ_error);
 
 static struct attribute *occ_attributes[] = {
&sensor_dev_attr_occ_master.dev_attr.attr,
@@ -92,7 +99,7 @@ static struct attribute *occ_attributes[] = {
&sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr,
&sensor_dev_attr_occ_state.dev_attr.attr,
&sensor_dev_attr_occs_present.dev_attr.attr,
-   &sensor_dev_attr_occ_error.dev_attr.attr,
+   &dev_attr_occ_error.attr,
NULL
 };
 
@@ -156,7 +163,7 @@ void occ_sysfs_poll_done(struct occ *occ)
}
 
if (occ->error && occ->error != occ->prev_error) {
-   name = sensor_dev_attr_occ_error.dev_attr.attr.name;
+   name = dev_attr_occ_error.attr.name;
sysfs_notify(&occ->bus_dev->kobj, NULL, name);
}
 
-- 
2.7.4



[PATCH 3/3] hwmon (occ): Add more details to Kconfig help text

2019-04-16 Thread Eddie James
From: Eddie James 

The help text needs to spell out how the driver runs on a BMC, as it
previously seemed to indicate it ran on a POWER processor.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/Kconfig | 12 
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
index 2a3869f..1658634 100644
--- a/drivers/hwmon/occ/Kconfig
+++ b/drivers/hwmon/occ/Kconfig
@@ -9,8 +9,10 @@ config SENSORS_OCC_P8_I2C
select SENSORS_OCC
help
 This option enables support for monitoring sensors provided by the
-On-Chip Controller (OCC) on a POWER8 processor. Communications with
-the OCC are established through I2C bus.
+On-Chip Controller (OCC) on a POWER8 processor. However, this driver
+can only run on a baseboard management controller (BMC) connected to
+the P8, not the POWER processor itself. Communications with the OCC are
+established through I2C bus.
 
 This driver can also be built as a module. If so, the module will be
 called occ-p8-hwmon.
@@ -22,8 +24,10 @@ config SENSORS_OCC_P9_SBE
select SENSORS_OCC
help
 This option enables support for monitoring sensors provided by the
-On-Chip Controller (OCC) on a POWER9 processor. Communications with
-the OCC are established through SBE fifo on an FSI bus.
+On-Chip Controller (OCC) on a POWER9 processor. However, this driver
+can only run on a baseboard management controller (BMC) connected to
+the P9, not the POWER processor itself. Communications with the OCC are
+established through SBE fifo on an FSI bus.
 
 This driver can also be built as a module. If so, the module will be
 called occ-p9-hwmon.
-- 
2.7.4



[PATCH 1/3] hwmon (occ): Store error condition for rate-limited polls

2019-04-16 Thread Eddie James
The OCC driver limits the rate of sending poll commands to the OCC. If a
user reads a hwmon entry after a poll response resulted in an error and
is rate-limited, the error is invisible to the user. Fix this by storing
the last error and returning that in the rate-limited case.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 4 
 drivers/hwmon/occ/common.h | 3 ++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 9d197e9..13a6290 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -141,6 +141,7 @@ static int occ_poll(struct occ *occ)
/* mutex should already be locked if necessary */
rc = occ->send_cmd(occ, cmd);
if (rc) {
+   occ->last_error = rc;
if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
occ->error = rc;
 
@@ -149,6 +150,7 @@ static int occ_poll(struct occ *occ)
 
/* clear error since communication was successful */
occ->error_count = 0;
+   occ->last_error = 0;
occ->error = 0;
 
/* check for safe state */
@@ -210,6 +212,8 @@ int occ_update_response(struct occ *occ)
if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
rc = occ_poll(occ);
occ->last_update = jiffies;
+   } else {
+   rc = occ->last_error;
}
 
mutex_unlock(&occ->lock);
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index ed2cf42..fc13f3c 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -106,7 +106,8 @@ struct occ {
struct attribute_group group;
const struct attribute_group *groups[2];
 
-   int error;  /* latest transfer error */
+   int error;  /* final transfer error after retry */
+   int last_error; /* latest transfer error */
unsigned int error_count;   /* number of xfr errors observed */
unsigned long last_safe;/* time OCC entered "safe" state */
 
-- 
2.7.4



Re: [PATCH] hwmon (occ): Fix extended status bits

2019-04-15 Thread Eddie James



On 4/15/19 3:28 PM, Guenter Roeck wrote:

On Mon, Apr 15, 2019 at 06:37:20PM +0800, Lei YU wrote:

The occ's extended status is checked and shown as sysfs attributes. But
the code was incorrectly checking the "status" bits.
Fix it by checking the "ext_status" bits.

Signed-off-by: Lei YU 

Looks ok to me, but can I get a confirmation from someone at IBM ?



Yes. Thanks Lei.


Reviewed-by: Eddie James 




Thanks,
Guenter


---
  drivers/hwmon/occ/sysfs.c | 8 
  1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index fe3d15e..a71ca94 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -42,16 +42,16 @@ static ssize_t occ_sysfs_show(struct device *dev,
val = !!(header->status & OCC_STAT_ACTIVE);
break;
case 2:
-   val = !!(header->status & OCC_EXT_STAT_DVFS_OT);
+   val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
break;
case 3:
-   val = !!(header->status & OCC_EXT_STAT_DVFS_POWER);
+   val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
break;
case 4:
-   val = !!(header->status & OCC_EXT_STAT_MEM_THROTTLE);
+   val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE);
break;
case 5:
-   val = !!(header->status & OCC_EXT_STAT_QUICK_DROP);
+   val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
break;
case 6:
val = header->occ_state;




Re: [PATCH v2 2/2] hwmon: OCC drivers are ARM-only

2019-04-10 Thread Eddie James



On 4/10/19 5:56 AM, Jean Delvare wrote:

These drivers are for a BMC inside PowerPC servers. The BMC runs on
ARM hardware, so only propose the drivers on this architecture, unless
build-testing.



Thanks!

Reviewed-by: Eddie James 




Signed-off-by: Jean Delvare 
Cc: Eddie James 
Cc: Guenter Roeck 
---
If PowerPC BMCs are ever based on another architecture and these drivers
are compatible with them, then the list can be extended.

  drivers/hwmon/occ/Kconfig |2 ++
  1 file changed, 2 insertions(+)

--- linux-5.0.orig/drivers/hwmon/occ/Kconfig2019-04-10 11:54:07.014895111 
+0200
+++ linux-5.0/drivers/hwmon/occ/Kconfig 2019-04-10 12:12:27.379725640 +0200
@@ -5,6 +5,7 @@
  config SENSORS_OCC_P8_I2C
tristate "POWER8 OCC through I2C"
depends on I2C
+   depends on ARM || ARM64 || COMPILE_TEST
select SENSORS_OCC
help
 This option enables support for monitoring sensors provided by the
@@ -17,6 +18,7 @@ config SENSORS_OCC_P8_I2C
  config SENSORS_OCC_P9_SBE
tristate "POWER9 OCC through SBE"
depends on FSI_OCC
+   depends on ARM || ARM64 || COMPILE_TEST
select SENSORS_OCC
help
 This option enables support for monitoring sensors provided by the





Re: [PATCH v2 1/2] hwmon: (occ) Move common code to a separate module

2019-04-10 Thread Eddie James



On 4/10/19 5:47 AM, Jean Delvare wrote:

Instead of duplicating the common code into the 2 (binary) drivers,
move the common code to a separate module. This is cleaner.

Signed-off-by: Jean Delvare 
Cc: Eddie James 
Cc: Guenter Roeck 
---
Eddie, can you please give it a try and confirm it works?



Yes, this works well.

Reviewed-by: Eddie James 

Tested-by: Eddie James 




Note: I kept the module names as they were before, hence the extra
"*-objs :=" statements. They could be removed if we rename the source
files, but that's better done in git directly. I don't mind either way
personally.

  drivers/hwmon/occ/Kconfig  |3 +--
  drivers/hwmon/occ/Makefile |6 --
  drivers/hwmon/occ/common.c |7 +++
  drivers/hwmon/occ/sysfs.c  |2 ++
  4 files changed, 14 insertions(+), 4 deletions(-)

--- linux-5.0.orig/drivers/hwmon/occ/Kconfig2019-04-10 11:30:05.579537638 
+0200
+++ linux-5.0/drivers/hwmon/occ/Kconfig 2019-04-10 11:31:20.843383376 +0200
@@ -27,5 +27,4 @@ config SENSORS_OCC_P9_SBE
 called occ-p9-hwmon.
  
  config SENSORS_OCC

-   bool "POWER On-Chip Controller"
-   depends on SENSORS_OCC_P8_I2C || SENSORS_OCC_P9_SBE
+   tristate
--- linux-5.0.orig/drivers/hwmon/occ/Makefile   2019-03-04 00:21:29.0 
+0100
+++ linux-5.0/drivers/hwmon/occ/Makefile2019-04-10 11:33:23.631765535 
+0200
@@ -1,5 +1,7 @@
-occ-p8-hwmon-objs := common.o sysfs.o p8_i2c.o
-occ-p9-hwmon-objs := common.o sysfs.o p9_sbe.o
+occ-hwmon-common-objs := common.o sysfs.o
+occ-p8-hwmon-objs := p8_i2c.o
+occ-p9-hwmon-objs := p9_sbe.o
  
+obj-$(CONFIG_SENSORS_OCC) += occ-hwmon-common.o

  obj-$(CONFIG_SENSORS_OCC_P8_I2C) += occ-p8-hwmon.o
  obj-$(CONFIG_SENSORS_OCC_P9_SBE) += occ-p9-hwmon.o
--- linux-5.0.orig/drivers/hwmon/occ/common.c   2019-03-04 00:21:29.0 
+0100
+++ linux-5.0/drivers/hwmon/occ/common.c2019-04-10 11:44:53.035573580 
+0200
@@ -1,11 +1,13 @@
  // SPDX-License-Identifier: GPL-2.0
  
  #include 

+#include 
  #include 
  #include 
  #include 
  #include 
  #include 
+#include 
  #include 
  #include 
  #include 
@@ -1096,3 +1098,8 @@ int occ_setup(struct occ *occ, const cha
  
  	return rc;

  }
+EXPORT_SYMBOL_GPL(occ_setup);
+
+MODULE_AUTHOR("Eddie James ");
+MODULE_DESCRIPTION("Common OCC hwmon code");
+MODULE_LICENSE("GPL");
--- linux-5.0.orig/drivers/hwmon/occ/sysfs.c2019-03-04 00:21:29.0 
+0100
+++ linux-5.0/drivers/hwmon/occ/sysfs.c 2019-04-10 11:39:38.627003382 +0200
@@ -12,6 +12,7 @@
  
  #include 

  #include 
+#include 
  #include 
  #include 
  #include 
@@ -186,3 +187,4 @@ void occ_shutdown(struct occ *occ)
  {
sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);
  }
+EXPORT_SYMBOL_GPL(occ_shutdown);






Re: [PATCH] hwmon: OCC drivers are PowerPC-only

2019-04-09 Thread Eddie James



On 4/9/19 2:44 PM, Jean Delvare wrote:

Hi Eddie,

Thanks for the quick answer.

On Tue, 9 Apr 2019 10:20:22 -0500, Eddie James wrote:

On 4/9/19 7:45 AM, Jean Delvare wrote:

Don't propose PowerPC-only drivers on other architectures, unless
build-testing.

This driver does NOT only run on PowerPC; rather it runs on a BMC
processor connected to a PowerPC processor. BMC will most likely be ARM,

Thanks for clarifying. So, you have a server with one or more PowerPC
processors, running any operating system decided by the customer, and
in the same system, is a BMC, running a Linux operating system provided
by IBM? Do I understand it correctly?



Exactly, though the operating system running on the BMC (OpenBMC) is 
used/developed by a number of other companies as well.






but shouldn't be restricted to that arch only.

Why not? Restricting drivers to the architectures or platforms where
they make sense significantly eases the work of the maintainers of
distribution kernels. Each new kernel version comes with several dozens
of new options. Without hints, they have no idea what is needed on
which architecture, and they either select everything, resulting in an
overweight kernel forever, or nothing, resulting in missing features
until someone complains.



I see, that makes sense to restrict it then. It is possible someone will 
want to run OpenBMC managing a Power processor on something other than 
an ARM chip, but I don't think that configuration is needed right now.





Regardless of any dependency at the Kconfig level, the help text of
these drivers should explain exactly what you wrote above. At the
moment, the reader has no way to guess that the drivers only make sense
for an embedded Linux running on a BMC. As I understand it now, general
purpose distributions do not need these drivers, right? But for example
SUSE kernels included them on all architectures. I just restricted them
to ppc64, but apparently I was wrong and it should be disabled there
too.

A good Kconfig help text should help the user decide whether they need
the driver or not.



Yep, the Kconfig text can probably be improved as well. I'll look into that.





Also drop configuration symbol SENSORS_OCC which serves no purpose
that I can see.

Signed-off-by: Jean Delvare 
Cc: Eddie James 
Cc: Guenter Roeck 
---
SENSORS_OCC *would* serve a purpose if the common code between the
POWER8 driver and the POWER9 driver would go in a separate, shared
module, and occ-p8-hwmon and occ-p9-hwmon would only contain the
specific code. This would avoid packaging the same code twice in 2
separate modules, therefore saving some storage space for ppc
distributions.

Well you'd never have both P8 and P9 enabled at once, so space shouldn't
be an issue. I agree this could be cleaner but I think I was getting

Where "you" is IBM, provider of the embedded Linux running on the BMC?



Yes, though again could be any distributor of OpenBMC.

Thanks for looking into this!

Eddie





duplicate symbol errors for the compile test and so I did it this way.
If this doesn't lead to errors in the compile test, I'm fine with this
(without the change for PPC only though).

Dropping SENSORS_OCC can't result in duplicate symbols because it is a
no-op. But while this is the easiest solution, it it not the cleanest
in my opinion. OTOH, if you are certain that both modules will never be
shipped at the same time, then it indeed doesn't matter that much. I
can provide a patch going in either direction. Guenter, any preference?





Re: [PATCH] hwmon: OCC drivers are PowerPC-only

2019-04-09 Thread Eddie James



On 4/9/19 7:45 AM, Jean Delvare wrote:

Don't propose PowerPC-only drivers on other architectures, unless
build-testing.



This driver does NOT only run on PowerPC; rather it runs on a BMC 
processor connected to a PowerPC processor. BMC will most likely be ARM, 
but shouldn't be restricted to that arch only.





Also drop configuration symbol SENSORS_OCC which serves no purpose
that I can see.

Signed-off-by: Jean Delvare 
Cc: Eddie James 
Cc: Guenter Roeck 
---
SENSORS_OCC *would* serve a purpose if the common code between the
POWER8 driver and the POWER9 driver would go in a separate, shared
module, and occ-p8-hwmon and occ-p9-hwmon would only contain the
specific code. This would avoid packaging the same code twice in 2
separate modules, therefore saving some storage space for ppc
distributions.



Well you'd never have both P8 and P9 enabled at once, so space shouldn't 
be an issue. I agree this could be cleaner but I think I was getting 
duplicate symbol errors for the compile test and so I did it this way. 
If this doesn't lead to errors in the compile test, I'm fine with this 
(without the change for PPC only though).



Thanks,

Eddie



As far as I can see, this would simply require exporting 2 functions
(occ_setup and occ_shutdown). Is there any reason why things were not
done that way in the first place? This would look cleaner to me.

  drivers/hwmon/Makefile|2 +-
  drivers/hwmon/occ/Kconfig |8 ++--
  2 files changed, 3 insertions(+), 7 deletions(-)

--- linux-5.0.orig/drivers/hwmon/occ/Kconfig2019-03-04 00:21:29.0 
+0100
+++ linux-5.0/drivers/hwmon/occ/Kconfig 2019-04-09 14:08:41.316551071 +0200
@@ -5,7 +5,7 @@
  config SENSORS_OCC_P8_I2C
tristate "POWER8 OCC through I2C"
depends on I2C
-   select SENSORS_OCC
+   depends on POWERPC || COMPILE_TEST
help
 This option enables support for monitoring sensors provided by the
 On-Chip Controller (OCC) on a POWER8 processor. Communications with
@@ -17,7 +17,7 @@ config SENSORS_OCC_P8_I2C
  config SENSORS_OCC_P9_SBE
tristate "POWER9 OCC through SBE"
depends on FSI_OCC
-   select SENSORS_OCC
+   depends on POWERPC || COMPILE_TEST
help
 This option enables support for monitoring sensors provided by the
 On-Chip Controller (OCC) on a POWER9 processor. Communications with
@@ -25,7 +25,3 @@ config SENSORS_OCC_P9_SBE
  
  	 This driver can also be built as a module. If so, the module will be

 called occ-p9-hwmon.
-
-config SENSORS_OCC
-   bool "POWER On-Chip Controller"
-   depends on SENSORS_OCC_P8_I2C || SENSORS_OCC_P9_SBE
--- linux-5.0.orig/drivers/hwmon/Makefile   2019-03-04 00:21:29.0 
+0100
+++ linux-5.0/drivers/hwmon/Makefile2019-04-09 14:33:49.605510047 +0200
@@ -178,7 +178,7 @@ obj-$(CONFIG_SENSORS_WM831X)+= wm831x-h
  obj-$(CONFIG_SENSORS_WM8350)  += wm8350-hwmon.o
  obj-$(CONFIG_SENSORS_XGENE)   += xgene-hwmon.o
  
-obj-$(CONFIG_SENSORS_OCC)	+= occ/

+obj-y  += occ/
  obj-$(CONFIG_PMBUS)   += pmbus/
  
  ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG







[PATCH] hwmon: occ: Fix power sensor indexing

2019-03-19 Thread Eddie James
In the case of power sensor version 0xA0, the sensor indexing overlapped
with the "caps" power sensors, resulting in probe failure and kernel
warnings. Fix this by specifying the next index for each power sensor
version.

Fixes: 54076cb ("hwmon (occ): Add sensor attributes and register ...")
Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 6 --
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 391118c..c888f4a 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -889,6 +889,8 @@ static int occ_setup_sensor_attrs(struct occ *occ)
s++;
}
}
+
+   s = (sensors->power.num_sensors * 4) + 1;
} else {
for (i = 0; i < sensors->power.num_sensors; ++i) {
s = i + 1;
@@ -917,11 +919,11 @@ static int occ_setup_sensor_attrs(struct occ *occ)
 show_power, NULL, 3, i);
attr++;
}
-   }
 
-   if (sensors->caps.num_sensors >= 1) {
s = sensors->power.num_sensors + 1;
+   }
 
+   if (sensors->caps.num_sensors >= 1) {
snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
 0, 0);
-- 
1.8.3.1



[PATCH v2 1/2] hwmon (occ): Fix license headers

2019-01-28 Thread Eddie James
Files have inconsistent license information.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c |  3 ++-
 drivers/hwmon/occ/common.h |  3 ++-
 drivers/hwmon/occ/p8_i2c.c |  3 ++-
 drivers/hwmon/occ/p9_sbe.c |  3 ++-
 drivers/hwmon/occ/sysfs.c  | 13 ++---
 5 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 391118c..b91a80a 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 7c44df3..ed2cf42 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -1,4 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright IBM Corp 2019 */
 
 #ifndef OCC_COMMON_H
 #define OCC_COMMON_H
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index b59efc9..76fb787 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index b65c1d1..f6387cc 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index 743b26ec..fe3d15e 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -1,14 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * OCC hwmon driver sysfs interface
- *
- * Copyright (C) IBM Corporation 2018
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
-- 
1.8.3.1



[PATCH v2 2/2] fsi: occ: Fix license header

2019-01-28 Thread Eddie James
OCC driver source is missing the IBM Copyright

Signed-off-by: Eddie James 
---
 drivers/fsi/fsi-occ.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index a2301ce..d75038f 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
-- 
1.8.3.1



[PATCH v2 0/2] occ: FSI and hwmon: Fix license headers

2019-01-28 Thread Eddie James
From: Eddie James 

Source files for the FSI OCC and OCC hwmon drivers had a combination of bad
license information and missing IBM copyright. Correct the licenses.

Changes since v1:
 - correct header comment style

Eddie James (2):
  hwmon (occ): Fix license headers
  fsi: occ: Fix license header

 drivers/fsi/fsi-occ.c  |  3 ++-
 drivers/hwmon/occ/common.c |  3 ++-
 drivers/hwmon/occ/common.h |  3 ++-
 drivers/hwmon/occ/p8_i2c.c |  3 ++-
 drivers/hwmon/occ/p9_sbe.c |  3 ++-
 drivers/hwmon/occ/sysfs.c  | 13 ++---
 6 files changed, 12 insertions(+), 16 deletions(-)

-- 
1.8.3.1



Re: [PATCH 1/2] hwmon (occ): Fix license headers

2019-01-21 Thread Eddie James




On 01/21/2019 02:19 PM, Joel Stanley wrote:

On Tue, 22 Jan 2019 at 06:56, Eddie James  wrote:


--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -1,4 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019

We want to use the /* */ syntax for headers. Take a closer look at
Documentation/process/license-rules.rst:


Oops. Do I put the copyright in the copyright in the same comment block 
then? Or use // for that?




2. Style:

The SPDX license identifier is added in form of a comment.  The comment
style depends on the file type::

   C source: // SPDX-License-Identifier: 
   C header: /* SPDX-License-Identifier:  */





[PATCH 1/2] hwmon (occ): Fix license headers

2019-01-21 Thread Eddie James
Files have inconsistent license information.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c |  3 ++-
 drivers/hwmon/occ/common.h |  3 ++-
 drivers/hwmon/occ/p8_i2c.c |  3 ++-
 drivers/hwmon/occ/p9_sbe.c |  3 ++-
 drivers/hwmon/occ/sysfs.c  | 13 ++---
 5 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 391118c..b91a80a 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 7c44df3..55d4967 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -1,4 +1,5 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #ifndef OCC_COMMON_H
 #define OCC_COMMON_H
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index b59efc9..76fb787 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index b65c1d1..f6387cc 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index 743b26ec..fe3d15e 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -1,14 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * OCC hwmon driver sysfs interface
- *
- * Copyright (C) IBM Corporation 2018
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
-- 
1.8.3.1



[PATCH 2/2] fsi: occ: Fix license header

2019-01-21 Thread Eddie James
OCC driver source is missing the IBM Copyright

Signed-off-by: Eddie James 
---
 drivers/fsi/fsi-occ.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index a2301ce..d75038f 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
 
 #include 
 #include 
-- 
1.8.3.1



[PATCH 0/2] occ: FSI and hwmon: Fix license headers

2019-01-21 Thread Eddie James
Source files for the FSI OCC and OCC hwmon drivers had a combination of bad
license information and missing IBM copyright. Correct the licenses.

Eddie James (2):
  hwmon (occ): Fix license headers
  fsi: occ: Fix license header

 drivers/fsi/fsi-occ.c  |  3 ++-
 drivers/hwmon/occ/common.c |  3 ++-
 drivers/hwmon/occ/common.h |  3 ++-
 drivers/hwmon/occ/p8_i2c.c |  3 ++-
 drivers/hwmon/occ/p9_sbe.c |  3 ++-
 drivers/hwmon/occ/sysfs.c  | 13 ++---
 6 files changed, 12 insertions(+), 16 deletions(-)

-- 
1.8.3.1



Re: [PATCH] hwmon (occ): Fix potential integer overflow

2019-01-07 Thread Eddie James




On 01/07/2019 12:34 PM, Gustavo A. R. Silva wrote:

Cast get_unaligned_be32(...) to u64 in order to give the compiler
complete information about the proper arithmetic to use and avoid
a potential integer overflow.

Notice that such function call is used in contexts that expect
expressions of type u64 (64 bits, unsigned); and the following
expressions are currently being evaluated using 32-bit
arithmetic:

val = get_unaligned_be32(&power->update_tag) *
 occ->powr_sample_time_us;

val = get_unaligned_be32(&power->vdn.update_tag) *
 occ->powr_sample_time_us;


Thanks,

Reviewed-by: Eddie James 



Addresses-Coverity-ID: 1442357 ("Unintentional integer overflow")
Addresses-Coverity-ID: 1442476 ("Unintentional integer overflow")
Addresses-Coverity-ID: 1442508 ("Unintentional integer overflow")
Fixes: ff692d80b2e2 ("hwmon (occ): Add sensor types and versions")
Cc: sta...@vger.kernel.org
Signed-off-by: Gustavo A. R. Silva 
---
  drivers/hwmon/occ/common.c | 24 
  1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 423903f87955..391118c8aae8 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -380,8 +380,8 @@ static ssize_t occ_show_power_1(struct device *dev,
val *= 100ULL;
break;
case 2:
-   val = get_unaligned_be32(&power->update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->update_tag) *
+  occ->powr_sample_time_us;
break;
case 3:
val = get_unaligned_be16(&power->value) * 100ULL;
@@ -425,8 +425,8 @@ static ssize_t occ_show_power_2(struct device *dev,
   &power->update_tag);
break;
case 2:
-   val = get_unaligned_be32(&power->update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->update_tag) *
+  occ->powr_sample_time_us;
break;
case 3:
val = get_unaligned_be16(&power->value) * 100ULL;
@@ -463,8 +463,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->system.update_tag);
break;
case 2:
-   val = get_unaligned_be32(&power->system.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->system.update_tag) *
+  occ->powr_sample_time_us;
break;
case 3:
val = get_unaligned_be16(&power->system.value) * 100ULL;
@@ -477,8 +477,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->proc.update_tag);
break;
case 6:
-   val = get_unaligned_be32(&power->proc.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->proc.update_tag) *
+  occ->powr_sample_time_us;
break;
case 7:
val = get_unaligned_be16(&power->proc.value) * 100ULL;
@@ -491,8 +491,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->vdd.update_tag);
break;
case 10:
-   val = get_unaligned_be32(&power->vdd.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->vdd.update_tag) *
+  occ->powr_sample_time_us;
break;
case 11:
val = get_unaligned_be16(&power->vdd.value) * 100ULL;
@@ -505,8 +505,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
   &power->vdn.update_tag);
break;
case 14:
-   val = get_unaligned_be32(&power->vdn.update_tag) *
-   occ->powr_sample_time_us;
+   val = (u64)get_unaligned_be32(&power->vdn.update_tag) *
+  occ->powr_sample_time_us;
break;
case 15:
val = get_unaligned_be16(&power->vdn.value) * 100ULL;




Re: [PATCH v6 00/10] hwmon and fsi: Add On-Chip Controller Driver

2018-11-12 Thread Eddie James




On 11/09/2018 05:03 PM, Guenter Roeck wrote:

On Thu, Nov 08, 2018 at 03:05:19PM -0600, Eddie James wrote:

From: Eddie James 

This series adds a hwmon driver to support the OCC on POWER8 and POWER9
processors. The OCC is an embedded processor that provides realtime power and
thermal monitoring and management.

The series also adds a "bus" driver to handle atomic communication between the
service processor and the OCC on a POWER9 chip. This communication takes place
over FSI bus to the SBE (Self-Boot engine) FIFO, which in turn communicates
with the OCC. The driver for the SBEFIFO is already available as an FSI client
driver.

For POWER8 OCCs, communication between the service processor and the OCC is
achieved over I2C bus.


I am not entirely happy with the series - there are still lots of proprietary
attributes, and I would have preferred the use of the _info API at this point -
but this has taken long enough. Series applied to hwmon-next. Please send any
fixes as follow-up patches.


Thanks a lot Guenter, appreciate your help getting here.

Eddie



Thanks,
Guenter


Changes since v5:
  * Makefile fix when compiling both P8 and P9 versions
  * Spelling fix in hwmon doc
  * Added an additional sentence for P9 binding doc to explain that OCC isn't
an FSI slave device.

Changes since v4:
  * Make the hwmon attributes conform almost completely to standard names and
values. The only exception is powerX_cap_user and powerX_cap_user_source.
  * Improve hwmon documentation.
  * Add ibm,p9-occ dt documentation.

Changes since v3:
  * Add the FSI OCC driver.
  * Pull the sysfs attribute code into it's own file for cleanliness.
  * Various fixes for attribute creation and integer overflow.

Changes since v2:
  * Add sysfs_notify for the error and throttling attributes when change is
detected.
  * Removed occs_present counting of devices bound.
  * Improved remove() of P9 driver to avoid bad behavior with relation to OCC
driver when unbound.
  * Added default cases (return EINVAL) for all sensor show functions.
  * Added temperature fault sensor.
  * Added back dt binding documentation for P9 to address checkpatch warning.
  * Added occs_present attribute from the poll response.

Changes since v1:
  * Remove wait loop in P9 code, as that is now handled by FSI OCC driver.
  * Removed dt binding documentation for P9, FSI OCC driver will probe OCC hwmon
driver automatically.
  * Moved OCC response code definitions to the OCC include file.
  * Fixed includes.
  * Changed some structure fields to __beXX as that is what they are.
  * Changed some errnos.
  * Removed some dev_err().
  * Refactored P8 code a bit to use #defined addresses and magic values, and
changed "goto retry" to a loop.
  * Refactored error handling a bit.

Eddie James (10):
   dt-bindings: fsi: Add P9 OCC device documentation
   fsi: Add On-Chip Controller (OCC) driver
   Documentation: hwmon: Add OCC documentation
   dt-bindings: i2c: Add P8 OCC hwmon device documentation
   hwmon: Add On-Chip Controller (OCC) hwmon driver
   hwmon (occ): Add command transport method for P8 and P9
   hwmon (occ): Parse OCC poll response
   hwmon (occ): Add sensor types and versions
   hwmon (occ): Add sensor attributes and register hwmon device
   hwmon (occ): Add sysfs attributes for additional OCC data

  .../devicetree/bindings/fsi/ibm,p9-occ.txt |   16 +
  .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   |   25 +
  Documentation/hwmon/occ|  112 ++
  drivers/fsi/Kconfig|   10 +
  drivers/fsi/Makefile   |1 +
  drivers/fsi/fsi-occ.c  |  599 +++
  drivers/hwmon/Kconfig  |2 +
  drivers/hwmon/Makefile |1 +
  drivers/hwmon/occ/Kconfig  |   31 +
  drivers/hwmon/occ/Makefile |5 +
  drivers/hwmon/occ/common.c | 1098 
  drivers/hwmon/occ/common.h |  128 +++
  drivers/hwmon/occ/p8_i2c.c |  255 +
  drivers/hwmon/occ/p9_sbe.c |  106 ++
  drivers/hwmon/occ/sysfs.c  |  188 
  include/linux/fsi-occ.h|   25 +
  16 files changed, 2602 insertions(+)
  create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
  create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
  create mode 100644 Documentation/hwmon/occ
  create mode 100644 drivers/fsi/fsi-occ.c
  create mode 100644 drivers/hwmon/occ/Kconfig
  create mode 100644 drivers/hwmon/occ/Makefile
  create mode 100644 drivers/hwmon/occ/common.c
  create mode 100644 drivers/hwmon/occ/common.h
  create mode 100644 drivers/hwmon/occ/p8_i2c.c
  create mode 100644 drivers/hwmon/occ/p9_sbe.c
  create mode 100644 drivers/h

[PATCH v6 02/10] fsi: Add On-Chip Controller (OCC) driver

2018-11-08 Thread Eddie James
From: Eddie James 

The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides an atomic communications channel between a service
processor (e.g. a BMC) and the OCC. The driver is dependent on the FSI
SBEFIFO driver to get hardware access through the SBE to the OCC SRAM.
Commands are issued to the SBE to send or fetch data to the SRAM.

Signed-off-by: Eddie James 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Benjamin Herrenschmidt 
Signed-off-by: Joel Stanley 
---
 drivers/fsi/Kconfig |  10 +
 drivers/fsi/Makefile|   1 +
 drivers/fsi/fsi-occ.c   | 599 
 include/linux/fsi-occ.h |  25 ++
 4 files changed, 635 insertions(+)
 create mode 100644 drivers/fsi/fsi-occ.c
 create mode 100644 include/linux/fsi-occ.h

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index af3a20d..ea2f4a1 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -64,4 +64,14 @@ config FSI_SBEFIFO
a pipe-like FSI device for communicating with the self boot engine
(SBE) on POWER processors.
 
+config FSI_OCC
+   tristate "OCC SBEFIFO client device driver"
+   depends on FSI_SBEFIFO
+   ---help---
+   This option enables an SBEFIFO based On-Chip Controller (OCC) device
+   driver. The OCC is a device embedded on a POWER processor that collects
+   and aggregates sensor data from the processor and system. The OCC can
+   provide the raw sensor data as well as perform thermal and power
+   management on the system.
+
 endif
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index a50d6ce..62687ec 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
+obj-$(CONFIG_FSI_OCC) += fsi-occ.o
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
new file mode 100644
index 000..a2301ce
--- /dev/null
+++ b/drivers/fsi/fsi-occ.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define OCC_SRAM_BYTES 4096
+#define OCC_CMD_DATA_BYTES 4090
+#define OCC_RESP_DATA_BYTES4089
+
+#define OCC_SRAM_CMD_ADDR  0xFFFBE000
+#define OCC_SRAM_RSP_ADDR  0xFFFBF000
+
+/*
+ * Assume we don't have much FFDC, if we do we'll overflow and
+ * fail the command. This needs to be big enough for simple
+ * commands as well.
+ */
+#define OCC_SBE_STATUS_WORDS   32
+
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+struct occ {
+   struct device *dev;
+   struct device *sbefifo;
+   char name[32];
+   int idx;
+   struct miscdevice mdev;
+   struct mutex occ_lock;
+};
+
+#define to_occ(x)  container_of((x), struct occ, mdev)
+
+struct occ_response {
+   u8 seq_no;
+   u8 cmd_type;
+   u8 return_status;
+   __be16 data_length;
+   u8 data[OCC_RESP_DATA_BYTES + 2];   /* two bytes checksum */
+} __packed;
+
+struct occ_client {
+   struct occ *occ;
+   struct mutex lock;
+   size_t data_size;
+   size_t read_offset;
+   u8 *buffer;
+};
+
+#define to_client(x)   container_of((x), struct occ_client, xfr)
+
+static DEFINE_IDA(occ_ida);
+
+static int occ_open(struct inode *inode, struct file *file)
+{
+   struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
+   struct miscdevice *mdev = file->private_data;
+   struct occ *occ = to_occ(mdev);
+
+   if (!client)
+   return -ENOMEM;
+
+   client->buffer = (u8 *)__get_free_page(GFP_KERNEL);
+   if (!client->buffer) {
+   kfree(client);
+   return -ENOMEM;
+   }
+
+   client->occ = occ;
+   mutex_init(&client->lock);
+   file->private_data = client;
+
+   /* We allocate a 1-page buffer, make sure it all fits */
+   BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
+   BUILD_BUG_ON((OCC_RESP_DATA_BYTES + 7) > PAGE_SIZE);
+
+   return 0;
+}
+
+static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
+   loff_t *offset)
+{
+   struct occ_client *client = file->private_data;
+   ssize_t rc = 0;
+
+   if (!client)
+   return -ENODEV;
+
+   if (len > OCC_SRAM_BYTES)
+   return -EINVAL;
+
+   mutex_lock(&client->lock);
+
+   /* This should not be possible ... */
+   if (WARN_ON_ONCE(client->read_offset > client-

[PATCH v6 09/10] hwmon (occ): Add sensor attributes and register hwmon device

2018-11-08 Thread Eddie James
From: Eddie James 

Setup the sensor attributes for every OCC sensor found by the first poll
response. Register the attributes with hwmon.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 337 +
 drivers/hwmon/occ/common.h |  16 +++
 2 files changed, 353 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index f7220132..c6c8161 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "common.h"
@@ -641,6 +643,324 @@ static ssize_t occ_show_extended(struct device *dev,
return rc;
 }
 
+/*
+ * Some helper macros to make it easier to define an occ_attribute. Since these
+ * are dynamically allocated, we shouldn't use the existing kernel macros which
+ * stringify the name argument.
+ */
+#define ATTR_OCC(_name, _mode, _show, _store) {
\
+   .attr   = { \
+   .name = _name,  \
+   .mode = VERIFY_OCTAL_PERMISSIONS(_mode),\
+   },  \
+   .show   = _show,\
+   .store  = _store,   \
+}
+
+#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) {\
+   .dev_attr   = ATTR_OCC(_name, _mode, _show, _store),\
+   .index  = _index,   \
+   .nr = _nr,  \
+}
+
+#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index)
\
+   ((struct sensor_device_attribute_2) \
+   SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
+
+/*
+ * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
+ * use our own instead of the built-in hwmon attribute types.
+ */
+static int occ_setup_sensor_attrs(struct occ *occ)
+{
+   unsigned int i, s, num_attrs = 0;
+   struct device *dev = occ->bus_dev;
+   struct occ_sensors *sensors = &occ->sensors;
+   struct occ_attribute *attr;
+   struct temp_sensor_2 *temp;
+   ssize_t (*show_temp)(struct device *, struct device_attribute *,
+char *) = occ_show_temp_1;
+   ssize_t (*show_freq)(struct device *, struct device_attribute *,
+char *) = occ_show_freq_1;
+   ssize_t (*show_power)(struct device *, struct device_attribute *,
+ char *) = occ_show_power_1;
+   ssize_t (*show_caps)(struct device *, struct device_attribute *,
+char *) = occ_show_caps_1_2;
+
+   switch (sensors->temp.version) {
+   case 1:
+   num_attrs += (sensors->temp.num_sensors * 2);
+   break;
+   case 2:
+   num_attrs += (sensors->temp.num_sensors * 4);
+   show_temp = occ_show_temp_2;
+   break;
+   default:
+   sensors->temp.num_sensors = 0;
+   }
+
+   switch (sensors->freq.version) {
+   case 2:
+   show_freq = occ_show_freq_2;
+   /* fall through */
+   case 1:
+   num_attrs += (sensors->freq.num_sensors * 2);
+   break;
+   default:
+   sensors->freq.num_sensors = 0;
+   }
+
+   switch (sensors->power.version) {
+   case 2:
+   show_power = occ_show_power_2;
+   /* fall through */
+   case 1:
+   num_attrs += (sensors->power.num_sensors * 4);
+   break;
+   case 0xA0:
+   num_attrs += (sensors->power.num_sensors * 16);
+   show_power = occ_show_power_a0;
+   break;
+   default:
+   sensors->power.num_sensors = 0;
+   }
+
+   switch (sensors->caps.version) {
+   case 1:
+   num_attrs += (sensors->caps.num_sensors * 7);
+   break;
+   case 3:
+   show_caps = occ_show_caps_3;
+   /* fall through */
+   case 2:
+   num_attrs += (sensors->caps.num_sensors * 8);
+   break;
+   default:
+   sensors->caps.num_sensors = 0;
+   }
+
+   switch (sensors->extended.version) {
+   case 1:
+   num_attrs += (sensors->extended.num_sensors * 3);
+   break;
+   default:
+   sensors->extended.num_sensors = 0;
+   }
+
+   occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs,
+ GFP_KERNEL);

[PATCH v6 03/10] Documentation: hwmon: Add OCC documentation

2018-11-08 Thread Eddie James
From: Eddie James 

Document the hwmon interface for the OCC.

Signed-off-by: Eddie James 
---
 Documentation/hwmon/occ | 112 
 1 file changed, 112 insertions(+)
 create mode 100644 Documentation/hwmon/occ

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
new file mode 100644
index 000..e787596
--- /dev/null
+++ b/Documentation/hwmon/occ
@@ -0,0 +1,112 @@
+Kernel driver occ-hwmon
+===
+
+Supported chips:
+  * POWER8
+  * POWER9
+
+Author: Eddie James 
+
+Description
+---
+
+This driver supports hardware monitoring for the On-Chip Controller (OCC)
+embedded on POWER processors. The OCC is a device that collects and aggregates
+sensor data from the processor and the system. The OCC can provide the raw
+sensor data as well as perform thermal and power management on the system.
+
+The P8 version of this driver is a client driver of I2C. It may be probed
+manually if an "ibm,p8-occ-hwmon" compatible device is found under the
+appropriate I2C bus node in the device-tree.
+
+The P9 version of this driver is a client driver of the FSI-based OCC driver.
+It will be probed automatically by the FSI-based OCC driver.
+
+Sysfs entries
+-
+
+The following attributes are supported. All attributes are read-only unless
+specified.
+
+The OCC sensor ID is an integer that represents the unique identifier of the
+sensor with respect to the OCC. For example, a temperature sensor for the third
+DIMM slot in the system may have a sensor ID of 7. This mapping is unavailable
+to the device driver, which must therefore export the sensor ID as-is.
+
+Some entries are only present with certain OCC sensor versions or only on
+certain OCCs in the system. The version number is not exported to the user
+but can be inferred.
+
+temp[1-n]_labelOCC sensor ID.
+[with temperature sensor version 1]
+temp[1-n]_inputMeasured temperature of the component in millidegrees
+   Celsius.
+[with temperature sensor version >= 2]
+temp[1-n]_type The FRU (Field Replaceable Unit) type
+   (represented by an integer) for the component
+   that this sensor measures.
+temp[1-n]_faultTemperature sensor fault boolean; 1 to indicate
+   that a fault is present or 0 to indicate that
+   no fault is present.
+[with type == 3 (FRU type is VRM)]
+temp[1-n]_alarmVRM temperature alarm boolean; 1 to 
indicate
+   alarm, 0 to indicate no alarm
+[else]
+temp[1-n]_inputMeasured temperature of the component in
+   millidegrees Celsius.
+
+freq[1-n]_labelOCC sensor ID.
+freq[1-n]_inputMeasured frequency of the component in MHz.
+
+power[1-n]_input   Latest measured power reading of the component in
+   microwatts.
+power[1-n]_average Average power of the component in microwatts.
+power[1-n]_average_intervalThe amount of time over which the power average
+   was taken in microseconds.
+[with power sensor version < 2]
+power[1-n]_label   OCC sensor ID.
+[with power sensor version >= 2]
+power[1-n]_label   OCC sensor ID + function ID + channel in the form
+   of a string, delimited by underscores, i.e. "0_15_1".
+   Both the function ID and channel are integers that
+   further identify the power sensor.
+[with power sensor version 0xa0]
+power[1-n]_label   OCC sensor ID + sensor type in the form of a string,
+   delimited by an underscore, i.e. "0_system". Sensor
+   type will be one of "system", "proc", "vdd" or "vdn".
+   For this sensor version, OCC sensor ID will be the same
+   for all power sensors.
+[present only on "master" OCC; represents the whole system power; only one of
+ this type of power sensor will be present]
+power[1-n]_label   "system"
+power[1-n]_input   Latest system output power in microwatts.
+power[1-n]_cap Current system power cap in microwatts.
+power[1-n]_cap_not_redundant   System power cap in microwatts when
+   there is not redundant power.
+power[1-n]_cap_max Maximum power cap that the OCC can enforce in
+   microwatts.
+power[1-n]_cap_min Minimum power cap that the OCC can enforce in
+   microwatts.
+power[1-n]_cap_userThe power cap set by the user, in 
microwatts.
+   This attribute will retur

[PATCH v6 04/10] dt-bindings: i2c: Add P8 OCC hwmon device documentation

2018-11-08 Thread Eddie James
From: Eddie James 

Document the bindings for I2C-based OCC hwmon device.

Signed-off-by: Eddie James 
Acked-by: Rob Herring 
---
 .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   | 25 ++
 1 file changed, 25 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt

diff --git a/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt 
b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
new file mode 100644
index 000..5dc5d2e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
@@ -0,0 +1,25 @@
+Device-tree bindings for I2C-based On-Chip Controller hwmon device
+--
+
+Required properties:
+ - compatible = "ibm,p8-occ-hwmon";
+ - reg = ;: I2C bus address
+
+Examples:
+
+i2c-bus@100 {
+#address-cells = <1>;
+#size-cells = <0>;
+clock-frequency = <10>;
+< more properties >
+
+occ-hwmon@1 {
+compatible = "ibm,p8-occ-hwmon";
+reg = <0x50>;
+};
+
+occ-hwmon@2 {
+compatible = "ibm,p8-occ-hwmon";
+reg = <0x51>;
+};
+};
-- 
1.8.3.1



[PATCH v6 08/10] hwmon (occ): Add sensor types and versions

2018-11-08 Thread Eddie James
From: Eddie James 

Add structures to define all sensor types and versions. Add sysfs show
and store functions for each sensor type. Add a method to construct the
"set user power cap" command and send it to the OCC. Add rate limit to
polling the OCC (in case user-space reads our hwmon entries rapidly).

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 621 +
 drivers/hwmon/occ/common.h |   6 +
 drivers/hwmon/occ/p8_i2c.c |   1 +
 drivers/hwmon/occ/p9_sbe.c |   1 +
 4 files changed, 629 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index a066509..f7220132 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,10 +1,116 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include 
+#include 
+#include 
 #include 
+#include 
+#include 
+#include 
 
 #include "common.h"
 
+#define EXTN_FLAG_SENSOR_IDBIT(7)
+
+#define OCC_UPDATE_FREQUENCY   msecs_to_jiffies(1000)
+
+#define OCC_TEMP_SENSOR_FAULT  0xFF
+
+#define OCC_FRU_TYPE_VRM   3
+
+/* OCC sensor type and version definitions */
+
+struct temp_sensor_1 {
+   u16 sensor_id;
+   u16 value;
+} __packed;
+
+struct temp_sensor_2 {
+   u32 sensor_id;
+   u8 fru_type;
+   u8 value;
+} __packed;
+
+struct freq_sensor_1 {
+   u16 sensor_id;
+   u16 value;
+} __packed;
+
+struct freq_sensor_2 {
+   u32 sensor_id;
+   u16 value;
+} __packed;
+
+struct power_sensor_1 {
+   u16 sensor_id;
+   u32 update_tag;
+   u32 accumulator;
+   u16 value;
+} __packed;
+
+struct power_sensor_2 {
+   u32 sensor_id;
+   u8 function_id;
+   u8 apss_channel;
+   u16 reserved;
+   u32 update_tag;
+   u64 accumulator;
+   u16 value;
+} __packed;
+
+struct power_sensor_data {
+   u16 value;
+   u32 update_tag;
+   u64 accumulator;
+} __packed;
+
+struct power_sensor_data_and_time {
+   u16 update_time;
+   u16 value;
+   u32 update_tag;
+   u64 accumulator;
+} __packed;
+
+struct power_sensor_a0 {
+   u32 sensor_id;
+   struct power_sensor_data_and_time system;
+   u32 reserved;
+   struct power_sensor_data_and_time proc;
+   struct power_sensor_data vdd;
+   struct power_sensor_data vdn;
+} __packed;
+
+struct caps_sensor_2 {
+   u16 cap;
+   u16 system_power;
+   u16 n_cap;
+   u16 max;
+   u16 min;
+   u16 user;
+   u8 user_source;
+} __packed;
+
+struct caps_sensor_3 {
+   u16 cap;
+   u16 system_power;
+   u16 n_cap;
+   u16 max;
+   u16 hard_min;
+   u16 soft_min;
+   u16 user;
+   u8 user_source;
+} __packed;
+
+struct extended_sensor {
+   union {
+   u8 name[4];
+   u32 sensor_id;
+   };
+   u8 flags;
+   u8 reserved;
+   u8 data[6];
+} __packed;
+
 static int occ_poll(struct occ *occ)
 {
u16 checksum = occ->poll_cmd_data + 1;
@@ -20,9 +126,521 @@ static int occ_poll(struct occ *occ)
cmd[6] = checksum & 0xFF;   /* checksum lsb */
cmd[7] = 0;
 
+   /* mutex should already be locked if necessary */
return occ->send_cmd(occ, cmd);
 }
 
+static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
+{
+   int rc;
+   u8 cmd[8];
+   u16 checksum = 0x24;
+   __be16 user_power_cap_be = cpu_to_be16(user_power_cap);
+
+   cmd[0] = 0;
+   cmd[1] = 0x22;
+   cmd[2] = 0;
+   cmd[3] = 2;
+
+   memcpy(&cmd[4], &user_power_cap_be, 2);
+
+   checksum += cmd[4] + cmd[5];
+   cmd[6] = checksum >> 8;
+   cmd[7] = checksum & 0xFF;
+
+   rc = mutex_lock_interruptible(&occ->lock);
+   if (rc)
+   return rc;
+
+   rc = occ->send_cmd(occ, cmd);
+
+   mutex_unlock(&occ->lock);
+
+   return rc;
+}
+
+static int occ_update_response(struct occ *occ)
+{
+   int rc = mutex_lock_interruptible(&occ->lock);
+
+   if (rc)
+   return rc;
+
+   /* limit the maximum rate of polling the OCC */
+   if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
+   rc = occ_poll(occ);
+   occ->last_update = jiffies;
+   }
+
+   mutex_unlock(&occ->lock);
+   return rc;
+}
+
+static ssize_t occ_show_temp_1(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   int rc;
+   u32 val = 0;
+   struct temp_sensor_1 *temp;
+   struct occ *occ = dev_get_drvdata(dev);
+   struct occ_sensors *sensors = &occ->sensors;
+   struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+   rc = occ_update_response(occ);
+   if (rc)
+   return rc;
+
+   temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index;
+
+   switch (sattr->nr) {
+   case

[PATCH v6 10/10] hwmon (occ): Add sysfs attributes for additional OCC data

2018-11-08 Thread Eddie James
From: Eddie James 

The OCC provides a variety of additional information about the state of
the host processor, such as throttling, error conditions, and the number
of OCCs detected in the system. This information is essential to service
processor applications such as fan control and host management.
Therefore, export this data in the form of sysfs attributes attached to
the platform device (to which the hwmon device is also attached).

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/Makefile |   4 +-
 drivers/hwmon/occ/common.c |  45 ++-
 drivers/hwmon/occ/common.h |  17 
 drivers/hwmon/occ/p8_i2c.c |  10 +++
 drivers/hwmon/occ/p9_sbe.c |   1 +
 drivers/hwmon/occ/sysfs.c  | 188 +
 6 files changed, 260 insertions(+), 5 deletions(-)
 create mode 100644 drivers/hwmon/occ/sysfs.c

diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index 57c0e91..3fec12d 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1,5 +1,5 @@
-occ-p8-hwmon-objs := common.o p8_i2c.o
-occ-p9-hwmon-objs := common.o p9_sbe.o
+occ-p8-hwmon-objs := common.o sysfs.o p8_i2c.o
+occ-p9-hwmon-objs := common.o sysfs.o p9_sbe.o
 
 obj-$(CONFIG_SENSORS_OCC_P8_I2C) += occ-p8-hwmon.o
 obj-$(CONFIG_SENSORS_OCC_P9_SBE) += occ-p9-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index c6c8161..423903f 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -14,6 +14,11 @@
 
 #define EXTN_FLAG_SENSOR_IDBIT(7)
 
+#define OCC_ERROR_COUNT_THRESHOLD  2   /* required by OCC spec */
+
+#define OCC_STATE_SAFE 4
+#define OCC_SAFE_TIMEOUT   msecs_to_jiffies(6) /* 1 min */
+
 #define OCC_UPDATE_FREQUENCY   msecs_to_jiffies(1000)
 
 #define OCC_TEMP_SENSOR_FAULT  0xFF
@@ -115,8 +120,10 @@ struct extended_sensor {
 
 static int occ_poll(struct occ *occ)
 {
+   int rc;
u16 checksum = occ->poll_cmd_data + 1;
u8 cmd[8];
+   struct occ_poll_response_header *header;
 
/* big endian */
cmd[0] = 0; /* sequence number */
@@ -129,7 +136,35 @@ static int occ_poll(struct occ *occ)
cmd[7] = 0;
 
/* mutex should already be locked if necessary */
-   return occ->send_cmd(occ, cmd);
+   rc = occ->send_cmd(occ, cmd);
+   if (rc) {
+   if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
+   occ->error = rc;
+
+   goto done;
+   }
+
+   /* clear error since communication was successful */
+   occ->error_count = 0;
+   occ->error = 0;
+
+   /* check for safe state */
+   header = (struct occ_poll_response_header *)occ->resp.data;
+   if (header->occ_state == OCC_STATE_SAFE) {
+   if (occ->last_safe) {
+   if (time_after(jiffies,
+  occ->last_safe + OCC_SAFE_TIMEOUT))
+   occ->error = -EHOSTDOWN;
+   } else {
+   occ->last_safe = jiffies;
+   }
+   } else {
+   occ->last_safe = 0;
+   }
+
+done:
+   occ_sysfs_poll_done(occ);
+   return rc;
 }
 
 static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
@@ -161,7 +196,7 @@ static int occ_set_user_power_cap(struct occ *occ, u16 
user_power_cap)
return rc;
 }
 
-static int occ_update_response(struct occ *occ)
+int occ_update_response(struct occ *occ)
 {
int rc = mutex_lock_interruptible(&occ->lock);
 
@@ -1055,5 +1090,9 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
 
-   return 0;
+   rc = occ_setup_sysfs(occ);
+   if (rc)
+   dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
+
+   return rc;
 }
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 00ac101..da80e65 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -104,8 +104,25 @@ struct occ {
struct occ_attribute *attrs;
struct attribute_group group;
const struct attribute_group *groups[2];
+
+   int error;  /* latest transfer error */
+   unsigned int error_count;   /* number of xfr errors observed */
+   unsigned long last_safe;/* time OCC entered "safe" state */
+
+   /*
+* Store the previous state data for comparison in order to notify
+* sysfs readers of state changes.
+*/
+   int prev_error;
+   u8 prev_stat;
+   u8 prev_ext_stat;
+   u8 prev_occs_present;
 };
 
 int occ_setup(struct occ *occ, const char *name);
+int occ_setup_sysfs(struct occ *occ);
+void occ_shutdown(struct occ *occ);
+void occ_sysfs_poll_done(struct occ *occ);
+int occ_update_response(struct occ *occ);
 
 #endif /* OCC_COMMON_H */
diff --

[PATCH v6 01/10] dt-bindings: fsi: Add P9 OCC device documentation

2018-11-08 Thread Eddie James
From: Eddie James 

Document the bindings for the FSI-attached POWER9 On-Chip Controller.

Signed-off-by: Eddie James 
---
 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt | 16 
 1 file changed, 16 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt

diff --git a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt 
b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
new file mode 100644
index 000..99ca986
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
@@ -0,0 +1,16 @@
+Device-tree bindings for FSI-attached POWER9 On-Chip Controller (OCC)
+-
+
+This is the binding for the P9 On-Chip Controller accessed over FSI from a
+service processor. See fsi.txt for details on bindings for FSI slave and CFAM
+nodes. The OCC is not an FSI slave device itself, rather it is accessed
+through the SBE fifo.
+
+Required properties:
+ - compatible = "ibm,p9-occ"
+
+Examples:
+
+occ {
+compatible = "ibm,p9-occ";
+};
-- 
1.8.3.1



[PATCH v6 06/10] hwmon (occ): Add command transport method for P8 and P9

2018-11-08 Thread Eddie James
From: Eddie James 

For the P8 OCC, add the procedure to send a command to the OCC over I2C
bus. This involves writing the OCC command registers with serial
communication operations (SCOMs) interpreted by the I2C slave. For the
P9 OCC, add a procedure to use the OCC in-kernel API to send a command
to the OCC through the SBE.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/p8_i2c.c | 185 -
 drivers/hwmon/occ/p9_sbe.c |  38 +-
 2 files changed, 221 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 4f3937d..e3326ff 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -2,11 +2,29 @@
 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #include "common.h"
 
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+/* OCB (on-chip control bridge - interface to OCC) registers */
+#define OCB_DATA1  0x6B035
+#define OCB_ADDR   0x6B070
+#define OCB_DATA3  0x6B075
+
+/* OCC SRAM address space */
+#define OCC_SRAM_ADDR_CMD  0x6000
+#define OCC_SRAM_ADDR_RESP 0x7000
+
+#define OCC_DATA_ATTN  0x2001
+
 struct p8_i2c_occ {
struct occ occ;
struct i2c_client *client;
@@ -14,9 +32,174 @@ struct p8_i2c_occ {
 
 #define to_p8_i2c_occ(x)   container_of((x), struct p8_i2c_occ, occ)
 
+static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
+{
+   ssize_t rc;
+   __be64 buf;
+   struct i2c_msg msgs[2];
+
+   /* p8 i2c slave requires shift */
+   address <<= 1;
+
+   msgs[0].addr = client->addr;
+   msgs[0].flags = client->flags & I2C_M_TEN;
+   msgs[0].len = sizeof(u32);
+   /* address is a scom address; bus-endian */
+   msgs[0].buf = (char *)&address;
+
+   /* data from OCC is big-endian */
+   msgs[1].addr = client->addr;
+   msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+   msgs[1].len = sizeof(u64);
+   msgs[1].buf = (char *)&buf;
+
+   rc = i2c_transfer(client->adapter, msgs, 2);
+   if (rc < 0)
+   return rc;
+
+   *(u64 *)data = be64_to_cpu(buf);
+
+   return 0;
+}
+
+static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
+{
+   u32 buf[3];
+   ssize_t rc;
+
+   /* p8 i2c slave requires shift */
+   address <<= 1;
+
+   /* address is bus-endian; data passed through from user as-is */
+   buf[0] = address;
+   memcpy(&buf[1], &data[4], sizeof(u32));
+   memcpy(&buf[2], data, sizeof(u32));
+
+   rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
+   if (rc < 0)
+   return rc;
+   else if (rc != sizeof(buf))
+   return -EIO;
+
+   return 0;
+}
+
+static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
+ u32 data0, u32 data1)
+{
+   u8 buf[8];
+
+   memcpy(buf, &data0, 4);
+   memcpy(buf + 4, &data1, 4);
+
+   return p8_i2c_occ_putscom(client, address, buf);
+}
+
+static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
+u8 *data)
+{
+   __be32 data0, data1;
+
+   memcpy(&data0, data, 4);
+   memcpy(&data1, data + 4, 4);
+
+   return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
+ be32_to_cpu(data1));
+}
+
 static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
 {
-   return -EOPNOTSUPP;
+   int i, rc;
+   unsigned long start;
+   u16 data_length;
+   const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
+   const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
+   struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ);
+   struct i2c_client *client = ctx->client;
+   struct occ_response *resp = &occ->resp;
+
+   start = jiffies;
+
+   /* set sram address for command */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
+   if (rc)
+   return rc;
+
+   /* write command (expected to already be BE), we need bus-endian... */
+   rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
+   if (rc)
+   return rc;
+
+   /* trigger OCC attention */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
+   if (rc)
+   return rc;
+
+   do {
+   /* set sram address for response */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
+   OCC_SRAM_ADDR_RESP, 0);
+   if (rc)
+   return rc;
+
+   rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 

[PATCH v6 07/10] hwmon (occ): Parse OCC poll response

2018-11-08 Thread Eddie James
From: Eddie James 

Add method to parse the response from the OCC poll command. This only
needs to be done during probe(), since the OCC shouldn't change the
number or format of sensors while it's running. The parsed response
allows quick access to sensor data, as well as information on the
number and version of sensors, which we need to instantiate hwmon
attributes.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 61 ++
 drivers/hwmon/occ/common.h | 55 +
 2 files changed, 116 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 81acd4b..a066509 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include 
+#include 
 
 #include "common.h"
 
@@ -22,6 +23,64 @@ static int occ_poll(struct occ *occ)
return occ->send_cmd(occ, cmd);
 }
 
+/* only need to do this once at startup, as OCC won't change sensors on us */
+static void occ_parse_poll_response(struct occ *occ)
+{
+   unsigned int i, old_offset, offset = 0, size = 0;
+   struct occ_sensor *sensor;
+   struct occ_sensors *sensors = &occ->sensors;
+   struct occ_response *resp = &occ->resp;
+   struct occ_poll_response *poll =
+   (struct occ_poll_response *)&resp->data[0];
+   struct occ_poll_response_header *header = &poll->header;
+   struct occ_sensor_data_block *block = &poll->block;
+
+   dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
+header->occ_code_level);
+
+   for (i = 0; i < header->num_sensor_data_blocks; ++i) {
+   block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+   old_offset = offset;
+   offset = (block->header.num_sensors *
+ block->header.sensor_length) + sizeof(block->header);
+   size += offset;
+
+   /* validate all the length/size fields */
+   if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
+   dev_warn(occ->bus_dev, "exceeded response buffer\n");
+   return;
+   }
+
+   dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
+   old_offset, offset - 1, block->header.eye_catcher,
+   block->header.num_sensors);
+
+   /* match sensor block type */
+   if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
+   sensor = &sensors->temp;
+   else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
+   sensor = &sensors->freq;
+   else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
+   sensor = &sensors->power;
+   else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
+   sensor = &sensors->caps;
+   else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
+   sensor = &sensors->extended;
+   else {
+   dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
+block->header.eye_catcher);
+   continue;
+   }
+
+   sensor->num_sensors = block->header.num_sensors;
+   sensor->version = block->header.sensor_format;
+   sensor->data = &block->data;
+   }
+
+   dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
+   sizeof(*header), size + sizeof(*header));
+}
+
 int occ_setup(struct occ *occ, const char *name)
 {
int rc;
@@ -36,5 +95,7 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
 
+   occ_parse_poll_response(occ);
+
return 0;
 }
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index b44fee2..0a7a107 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -20,10 +20,65 @@ struct occ_response {
__be16 checksum;
 } __packed;
 
+struct occ_sensor_data_block_header {
+   u8 eye_catcher[4];
+   u8 reserved;
+   u8 sensor_format;
+   u8 sensor_length;
+   u8 num_sensors;
+} __packed;
+
+struct occ_sensor_data_block {
+   struct occ_sensor_data_block_header header;
+   u32 data;
+} __packed;
+
+struct occ_poll_response_header {
+   u8 status;
+   u8 ext_status;
+   u8 occs_present;
+   u8 config_data;
+   u8 occ_state;
+   u8 mode;
+   u8 ips_status;
+   u8 error_log_id;
+   __be32 error_log_start_address;
+   __be16 error_log_length;
+  

[PATCH v6 05/10] hwmon: Add On-Chip Controller (OCC) hwmon driver

2018-11-08 Thread Eddie James
From: Eddie James 

The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides a hwmon interface to the OCC from a service
processor (e.g. a BMC). The driver supports both POWER8 and POWER9 OCCs.
Communications with the POWER8 OCC are established over standard I2C
bus. The driver communicates with the POWER9 OCC through the FSI-based
OCC driver, which handles the lower-level communication details.

This patch lays out the structure of the OCC hwmon driver. There are two
platform drivers, one each for P8 and P9 OCCs. These are probed through
the I2C tree and the FSI-based OCC driver, respectively. The patch also
defines the first common structures and methods between the two OCC
versions.

Signed-off-by: Eddie James 
---
 drivers/hwmon/Kconfig  |  2 ++
 drivers/hwmon/Makefile |  1 +
 drivers/hwmon/occ/Kconfig  | 31 +
 drivers/hwmon/occ/Makefile |  5 
 drivers/hwmon/occ/common.c | 40 +++
 drivers/hwmon/occ/common.h | 34 +++
 drivers/hwmon/occ/p8_i2c.c | 61 +
 drivers/hwmon/occ/p9_sbe.c | 68 ++
 8 files changed, 242 insertions(+)
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 81da17a..532a053 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1293,6 +1293,8 @@ config SENSORS_NSA320
  This driver can also be built as a module. If so, the module
  will be called nsa320-hwmon.
 
+source drivers/hwmon/occ/Kconfig
+
 config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 93f7f41..f5c7b44 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -178,6 +178,7 @@ obj-$(CONFIG_SENSORS_WM831X)+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)   += wm8350-hwmon.o
 obj-$(CONFIG_SENSORS_XGENE)+= xgene-hwmon.o
 
+obj-$(CONFIG_SENSORS_OCC)  += occ/
 obj-$(CONFIG_PMBUS)+= pmbus/
 
 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
new file mode 100644
index 000..6668662
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,31 @@
+#
+# On-Chip Controller configuration
+#
+
+config SENSORS_OCC_P8_I2C
+   tristate "POWER8 OCC through I2C"
+   depends on I2C
+   select SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the
+On-Chip Controller (OCC) on a POWER8 processor. Communications with
+the OCC are established through I2C bus.
+
+This driver can also be built as a module. If so, the module will be
+called occ-p8-hwmon.
+
+config SENSORS_OCC_P9_SBE
+   tristate "POWER9 OCC through SBE"
+   depends on FSI_OCC
+   select SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the
+On-Chip Controller (OCC) on a POWER9 processor. Communications with
+the OCC are established through SBE fifo on an FSI bus.
+
+This driver can also be built as a module. If so, the module will be
+called occ-p9-hwmon.
+
+config SENSORS_OCC
+   bool "POWER On-Chip Controller"
+   depends on SENSORS_OCC_P8_I2C || SENSORS_OCC_P9_SBE
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 000..57c0e91
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1,5 @@
+occ-p8-hwmon-objs := common.o p8_i2c.o
+occ-p9-hwmon-objs := common.o p9_sbe.o
+
+obj-$(CONFIG_SENSORS_OCC_P8_I2C) += occ-p8-hwmon.o
+obj-$(CONFIG_SENSORS_OCC_P9_SBE) += occ-p9-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
new file mode 100644
index 000..81acd4b
--- /dev/null
+++ b/drivers/hwmon/occ/common.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+
+#include "common.h"
+
+static int occ_poll(struct occ *occ)
+{
+   u16 checksum = occ->poll_cmd_data + 1;
+   u8 cmd[8];
+
+   /* big endian */
+   cmd[0] = 0; /* sequence number */
+   cmd[1] = 0; /* cmd type */
+   cmd[2] = 0; /* data length msb */
+   cmd[3] = 1; /* data length lsb */
+   cmd[4] = occ->poll_cmd_data;/* data */
+   cmd[5] = checksum >> 8; /* checksum msb */
+   cmd[6] 

[PATCH v6 00/10] hwmon and fsi: Add On-Chip Controller Driver

2018-11-08 Thread Eddie James
From: Eddie James 

This series adds a hwmon driver to support the OCC on POWER8 and POWER9
processors. The OCC is an embedded processor that provides realtime power and
thermal monitoring and management.

The series also adds a "bus" driver to handle atomic communication between the
service processor and the OCC on a POWER9 chip. This communication takes place
over FSI bus to the SBE (Self-Boot engine) FIFO, which in turn communicates
with the OCC. The driver for the SBEFIFO is already available as an FSI client
driver.

For POWER8 OCCs, communication between the service processor and the OCC is
achieved over I2C bus.

Changes since v5:
 * Makefile fix when compiling both P8 and P9 versions
 * Spelling fix in hwmon doc
 * Added an additional sentence for P9 binding doc to explain that OCC isn't
   an FSI slave device.

Changes since v4:
 * Make the hwmon attributes conform almost completely to standard names and
   values. The only exception is powerX_cap_user and powerX_cap_user_source.
 * Improve hwmon documentation.
 * Add ibm,p9-occ dt documentation.

Changes since v3:
 * Add the FSI OCC driver.
 * Pull the sysfs attribute code into it's own file for cleanliness.
 * Various fixes for attribute creation and integer overflow.

Changes since v2:
 * Add sysfs_notify for the error and throttling attributes when change is
   detected.
 * Removed occs_present counting of devices bound.
 * Improved remove() of P9 driver to avoid bad behavior with relation to OCC
   driver when unbound.
 * Added default cases (return EINVAL) for all sensor show functions.
 * Added temperature fault sensor.
 * Added back dt binding documentation for P9 to address checkpatch warning.
 * Added occs_present attribute from the poll response.

Changes since v1:
 * Remove wait loop in P9 code, as that is now handled by FSI OCC driver.
 * Removed dt binding documentation for P9, FSI OCC driver will probe OCC hwmon
   driver automatically.
 * Moved OCC response code definitions to the OCC include file.
 * Fixed includes.
 * Changed some structure fields to __beXX as that is what they are.
 * Changed some errnos.
 * Removed some dev_err().
 * Refactored P8 code a bit to use #defined addresses and magic values, and
   changed "goto retry" to a loop.
 * Refactored error handling a bit.

Eddie James (10):
  dt-bindings: fsi: Add P9 OCC device documentation
  fsi: Add On-Chip Controller (OCC) driver
  Documentation: hwmon: Add OCC documentation
  dt-bindings: i2c: Add P8 OCC hwmon device documentation
  hwmon: Add On-Chip Controller (OCC) hwmon driver
  hwmon (occ): Add command transport method for P8 and P9
  hwmon (occ): Parse OCC poll response
  hwmon (occ): Add sensor types and versions
  hwmon (occ): Add sensor attributes and register hwmon device
  hwmon (occ): Add sysfs attributes for additional OCC data

 .../devicetree/bindings/fsi/ibm,p9-occ.txt |   16 +
 .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   |   25 +
 Documentation/hwmon/occ|  112 ++
 drivers/fsi/Kconfig|   10 +
 drivers/fsi/Makefile   |1 +
 drivers/fsi/fsi-occ.c  |  599 +++
 drivers/hwmon/Kconfig  |2 +
 drivers/hwmon/Makefile |1 +
 drivers/hwmon/occ/Kconfig  |   31 +
 drivers/hwmon/occ/Makefile |5 +
 drivers/hwmon/occ/common.c | 1098 
 drivers/hwmon/occ/common.h |  128 +++
 drivers/hwmon/occ/p8_i2c.c |  255 +
 drivers/hwmon/occ/p9_sbe.c |  106 ++
 drivers/hwmon/occ/sysfs.c  |  188 
 include/linux/fsi-occ.h|   25 +
 16 files changed, 2602 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
 create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
 create mode 100644 Documentation/hwmon/occ
 create mode 100644 drivers/fsi/fsi-occ.c
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c
 create mode 100644 drivers/hwmon/occ/sysfs.c
 create mode 100644 include/linux/fsi-occ.h

-- 
1.8.3.1



[PATCH v5 03/10] Documentation: hwmon: Add OCC documentation

2018-08-30 Thread Eddie James
Document the hwmon interface for the OCC.

Signed-off-by: Eddie James 
---
 Documentation/hwmon/occ | 112 
 1 file changed, 112 insertions(+)
 create mode 100644 Documentation/hwmon/occ

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
new file mode 100644
index 000..adc2fa4
--- /dev/null
+++ b/Documentation/hwmon/occ
@@ -0,0 +1,112 @@
+Kernel driver occ-hwmon
+===
+
+Supported chips:
+  * POWER8
+  * POWER9
+
+Author: Eddie James 
+
+Description
+---
+
+This driver supports hardware monitoring for the On-Chip Controller (OCC)
+embedded on POWER processors. The OCC is a device that collects and aggregates
+sensor data from the processor and the system. The OCC can provide the raw
+sensor data as well as perform thermal and power management on the system.
+
+The P8 version of this driver is a client driver of I2C. It may be probed
+manually if an "ibm,p8-occ-hwmon" compatible device is found under the
+appropriate I2C bus node in the device-tree.
+
+The P9 version of this driver is a client driver of the FSI-based OCC driver.
+It will be probed automatically by the FSI-based OCC driver.
+
+Sysfs entries
+-
+
+The following attributes are supported. All attributes are read-only unless
+specified.
+
+The OCC sensor ID is an integer that represents the unique identififer of the
+sensor with respect to the OCC. For example, a temperature sensor for the 3rd
+DIMM slot in the system may have a sensor ID of 7. This mapping is unavailable
+to the device driver, which must therefore export the sensor ID as-is.
+
+Some entries are only present with certain OCC sensor versions or only on
+certain OCCs in the system. The version number is not exported to the user
+but can be inferred.
+
+temp[1-n]_labelOCC sensor ID.
+[with temperature sensor version 1]
+temp[1-n]_inputMeasured temperature of the component in millidegrees
+   Celsius.
+[with temperature sensor version >= 2]
+temp[1-n]_type The FRU (Field Replaceable Unit) type
+   (represented by an integer) for the component
+   that this sensor measures.
+temp[1-n]_faultTemperature sensor fault boolean; 1 to indicate
+   that a fault is present or 0 to indicate that
+   no fault is present.
+[with type == 3 (FRU type is VRM)]
+temp[1-n]_alarmVRM temperature alarm boolean; 1 to 
indicate
+   alarm, 0 to indicate no alarm
+[else]
+temp[1-n]_inputMeasured temperature of the component in
+   millidegrees Celsius.
+
+freq[1-n]_labelOCC sensor ID.
+freq[1-n]_inputMeasured frequency of the component in MHz.
+
+power[1-n]_input   Latest measured power reading of the component in
+   microwatts.
+power[1-n]_average Average power of the component in microwatts.
+power[1-n]_average_intervalThe amount of time over which the power average
+   was taken in microseconds.
+[with power sensor version < 2]
+power[1-n]_label   OCC sensor ID.
+[with power sensor version >= 2]
+power[1-n]_label   OCC sensor ID + function ID + channel in the form
+   of a string, delimited by underscores, i.e. "0_15_1".
+   Both the function ID and channel are integers that
+   further identify the power sensor.
+[with power sensor version 0xa0]
+power[1-n]_label   OCC sensor ID + sensor type in the form of a string,
+   delimited by an underscore, i.e. "0_system". Sensor
+   type will be one of "system", "proc", "vdd" or "vdn".
+   For this sensor version, OCC sensor ID will be the same
+   for all power sensors.
+[present only on "master" OCC; represents the whole system power; only one of
+ this type of power sensor will be present]
+power[1-n]_label   "system"
+power[1-n]_input   Latest system output power in microwatts.
+power[1-n]_cap Current system power cap in microwatts.
+power[1-n]_cap_not_redundant   System power cap in microwatts when
+   there is not redundant power.
+power[1-n]_cap_max Maximum power cap that the OCC can enforce in
+   microwatts.
+power[1-n]_cap_min Minimum power cap that the OCC can enforce in
+   microwatts.
+power[1-n]_cap_userThe power cap set by the user, in 
microwatts.
+   This attribute will retur

[PATCH v5 01/10] dt-bindings: fsi: Add P9 OCC device documentation

2018-08-30 Thread Eddie James
Document the bindings for the FSI-attached POWER9 On-Chip Controller.

Signed-off-by: Eddie James 
---
 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt | 15 +++
 1 file changed, 15 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt

diff --git a/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt 
b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
new file mode 100644
index 000..46372f6
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
@@ -0,0 +1,15 @@
+Device-tree bindings for FSI-attached POWER9 On-Chip Controller (OCC)
+-
+
+This is the binding for the P9 On-Chip Controller accessed over FSI from a
+service processor. See fsi.txt for details on bindings for FSI slave and CFAM
+nodes.
+
+Required properties:
+ - compatible = "ibm,p9-occ"
+
+Examples:
+
+occ {
+compatible = "ibm,p9-occ";
+};
-- 
1.8.3.1



[PATCH v5 00/10] hwmon and fsi: Add On-Chip Controller Driver

2018-08-30 Thread Eddie James
This series adds a hwmon driver to support the OCC on POWER8 and POWER9
processors. The OCC is an embedded processor that provides realtime power and
thermal monitoring and management.

The series also adds a "bus" driver to handle atomic communication between the
service processor and the OCC on a POWER9 chip. This communication takes place
over FSI bus to the SBE (Self-Boot engine) FIFO, which in turn communicates
with the OCC. The driver for the SBEFIFO is already available as an FSI client
driver.

For POWER8 OCCs, communication between the service processor and the OCC is
achieved over I2C bus.

Changes since v4:
 * Make the hwmon attributes conform almost completely to standard names and
   values. The only exception is powerX_cap_user and powerX_cap_user_source.
 * Improve hwmon documentation.
 * Add ibm,p9-occ dt documentation.

Changes since v3:
 * Add the FSI OCC driver.
 * Pull the sysfs attribute code into it's own file for cleanliness.
 * Various fixes for attribute creation and integer overflow.

Changes since v2:
 * Add sysfs_notify for the error and throttling attributes when change is
   detected.
 * Removed occs_present counting of devices bound.
 * Improved remove() of P9 driver to avoid bad behavior with relation to OCC
   driver when unbound.
 * Added default cases (return EINVAL) for all sensor show functions.
 * Added temperature fault sensor.
 * Added back dt binding documentation for P9 to address checkpatch warning.
 * Added occs_present attribute from the poll response.

Changes since v1:
 * Remove wait loop in P9 code, as that is now handled by FSI OCC driver.
 * Removed dt binding documentation for P9, FSI OCC driver will probe OCC hwmon
   driver automatically.
 * Moved OCC response code definitions to the OCC include file.
 * Fixed includes.
 * Changed some structure fields to __beXX as that is what they are.
 * Changed some errnos.
 * Removed some dev_err().
 * Refactored P8 code a bit to use #defined addresses and magic values, and
   changed "goto retry" to a loop.
 * Refactored error handling a bit.

Eddie James (10):
  dt-bindings: fsi: Add P9 OCC device documentation
  fsi: Add On-Chip Controller (OCC) driver
  Documentation: hwmon: Add OCC documentation
  dt-bindings: i2c: Add P8 OCC hwmon device documentation
  hwmon: Add On-Chip Controller (OCC) hwmon driver
  hwmon (occ): Add command transport method for P8 and P9
  hwmon (occ): Parse OCC poll response
  hwmon (occ): Add sensor types and versions
  hwmon (occ): Add sensor attributes and register hwmon device
  hwmon (occ): Add sysfs attributes for additional OCC data

 .../devicetree/bindings/fsi/ibm,p9-occ.txt |   15 +
 .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   |   25 +
 Documentation/hwmon/occ|  112 ++
 drivers/fsi/Kconfig|   10 +
 drivers/fsi/Makefile   |1 +
 drivers/fsi/fsi-occ.c  |  599 +++
 drivers/hwmon/Kconfig  |2 +
 drivers/hwmon/Makefile |1 +
 drivers/hwmon/occ/Kconfig  |   28 +
 drivers/hwmon/occ/Makefile |   11 +
 drivers/hwmon/occ/common.c | 1098 
 drivers/hwmon/occ/common.h |  128 +++
 drivers/hwmon/occ/p8_i2c.c |  255 +
 drivers/hwmon/occ/p9_sbe.c |  106 ++
 drivers/hwmon/occ/sysfs.c  |  188 
 include/linux/fsi-occ.h|   25 +
 16 files changed, 2604 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt
 create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
 create mode 100644 Documentation/hwmon/occ
 create mode 100644 drivers/fsi/fsi-occ.c
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c
 create mode 100644 drivers/hwmon/occ/sysfs.c
 create mode 100644 include/linux/fsi-occ.h

-- 
1.8.3.1



[PATCH v5 06/10] hwmon (occ): Add command transport method for P8 and P9

2018-08-30 Thread Eddie James
For the P8 OCC, add the procedure to send a command to the OCC over I2C
bus. This involves writing the OCC command registers with serial
communication operations (SCOMs) interpreted by the I2C slave. For the
P9 OCC, add a procedure to use the OCC in-kernel API to send a command
to the OCC through the SBE.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/p8_i2c.c | 185 -
 drivers/hwmon/occ/p9_sbe.c |  38 +-
 2 files changed, 221 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 85e051d..3e34579 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -2,11 +2,29 @@
 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #include "common.h"
 
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+/* OCB (on-chip control bridge - interface to OCC) registers */
+#define OCB_DATA1  0x6B035
+#define OCB_ADDR   0x6B070
+#define OCB_DATA3  0x6B075
+
+/* OCC SRAM address space */
+#define OCC_SRAM_ADDR_CMD  0x6000
+#define OCC_SRAM_ADDR_RESP 0x7000
+
+#define OCC_DATA_ATTN  0x2001
+
 struct p8_i2c_occ {
struct occ occ;
struct i2c_client *client;
@@ -14,9 +32,174 @@ struct p8_i2c_occ {
 
 #define to_p8_i2c_occ(x)   container_of((x), struct p8_i2c_occ, occ)
 
+static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
+{
+   ssize_t rc;
+   __be64 buf;
+   struct i2c_msg msgs[2];
+
+   /* p8 i2c slave requires shift */
+   address <<= 1;
+
+   msgs[0].addr = client->addr;
+   msgs[0].flags = client->flags & I2C_M_TEN;
+   msgs[0].len = sizeof(u32);
+   /* address is a scom address; bus-endian */
+   msgs[0].buf = (char *)&address;
+
+   /* data from OCC is big-endian */
+   msgs[1].addr = client->addr;
+   msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+   msgs[1].len = sizeof(u64);
+   msgs[1].buf = (char *)&buf;
+
+   rc = i2c_transfer(client->adapter, msgs, 2);
+   if (rc < 0)
+   return rc;
+
+   *(u64 *)data = be64_to_cpu(buf);
+
+   return 0;
+}
+
+static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
+{
+   u32 buf[3];
+   ssize_t rc;
+
+   /* p8 i2c slave requires shift */
+   address <<= 1;
+
+   /* address is bus-endian; data passed through from user as-is */
+   buf[0] = address;
+   memcpy(&buf[1], &data[4], sizeof(u32));
+   memcpy(&buf[2], data, sizeof(u32));
+
+   rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
+   if (rc < 0)
+   return rc;
+   else if (rc != sizeof(buf))
+   return -EIO;
+
+   return 0;
+}
+
+static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
+ u32 data0, u32 data1)
+{
+   u8 buf[8];
+
+   memcpy(buf, &data0, 4);
+   memcpy(buf + 4, &data1, 4);
+
+   return p8_i2c_occ_putscom(client, address, buf);
+}
+
+static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
+u8 *data)
+{
+   __be32 data0, data1;
+
+   memcpy(&data0, data, 4);
+   memcpy(&data1, data + 4, 4);
+
+   return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
+ be32_to_cpu(data1));
+}
+
 static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
 {
-   return -EOPNOTSUPP;
+   int i, rc;
+   unsigned long start;
+   u16 data_length;
+   const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
+   const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
+   struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ);
+   struct i2c_client *client = ctx->client;
+   struct occ_response *resp = &occ->resp;
+
+   start = jiffies;
+
+   /* set sram address for command */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
+   if (rc)
+   return rc;
+
+   /* write command (expected to already be BE), we need bus-endian... */
+   rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
+   if (rc)
+   return rc;
+
+   /* trigger OCC attention */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
+   if (rc)
+   return rc;
+
+   do {
+   /* set sram address for response */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
+   OCC_SRAM_ADDR_RESP, 0);
+   if (rc)
+   return rc;
+
+   rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 *)re

[PATCH v5 07/10] hwmon (occ): Parse OCC poll response

2018-08-30 Thread Eddie James
Add method to parse the response from the OCC poll command. This only
needs to be done during probe(), since the OCC shouldn't change the
number or format of sensors while it's running. The parsed response
allows quick access to sensor data, as well as information on the
number and version of sensors, which we need to instantiate hwmon
attributes.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 61 ++
 drivers/hwmon/occ/common.h | 55 +
 2 files changed, 116 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 81acd4b..a066509 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include 
+#include 
 
 #include "common.h"
 
@@ -22,6 +23,64 @@ static int occ_poll(struct occ *occ)
return occ->send_cmd(occ, cmd);
 }
 
+/* only need to do this once at startup, as OCC won't change sensors on us */
+static void occ_parse_poll_response(struct occ *occ)
+{
+   unsigned int i, old_offset, offset = 0, size = 0;
+   struct occ_sensor *sensor;
+   struct occ_sensors *sensors = &occ->sensors;
+   struct occ_response *resp = &occ->resp;
+   struct occ_poll_response *poll =
+   (struct occ_poll_response *)&resp->data[0];
+   struct occ_poll_response_header *header = &poll->header;
+   struct occ_sensor_data_block *block = &poll->block;
+
+   dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
+header->occ_code_level);
+
+   for (i = 0; i < header->num_sensor_data_blocks; ++i) {
+   block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+   old_offset = offset;
+   offset = (block->header.num_sensors *
+ block->header.sensor_length) + sizeof(block->header);
+   size += offset;
+
+   /* validate all the length/size fields */
+   if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
+   dev_warn(occ->bus_dev, "exceeded response buffer\n");
+   return;
+   }
+
+   dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
+   old_offset, offset - 1, block->header.eye_catcher,
+   block->header.num_sensors);
+
+   /* match sensor block type */
+   if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
+   sensor = &sensors->temp;
+   else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
+   sensor = &sensors->freq;
+   else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
+   sensor = &sensors->power;
+   else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
+   sensor = &sensors->caps;
+   else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
+   sensor = &sensors->extended;
+   else {
+   dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
+block->header.eye_catcher);
+   continue;
+   }
+
+   sensor->num_sensors = block->header.num_sensors;
+   sensor->version = block->header.sensor_format;
+   sensor->data = &block->data;
+   }
+
+   dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
+   sizeof(*header), size + sizeof(*header));
+}
+
 int occ_setup(struct occ *occ, const char *name)
 {
int rc;
@@ -36,5 +95,7 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
 
+   occ_parse_poll_response(occ);
+
return 0;
 }
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index b44fee2..0a7a107 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -20,10 +20,65 @@ struct occ_response {
__be16 checksum;
 } __packed;
 
+struct occ_sensor_data_block_header {
+   u8 eye_catcher[4];
+   u8 reserved;
+   u8 sensor_format;
+   u8 sensor_length;
+   u8 num_sensors;
+} __packed;
+
+struct occ_sensor_data_block {
+   struct occ_sensor_data_block_header header;
+   u32 data;
+} __packed;
+
+struct occ_poll_response_header {
+   u8 status;
+   u8 ext_status;
+   u8 occs_present;
+   u8 config_data;
+   u8 occ_state;
+   u8 mode;
+   u8 ips_status;
+   u8 error_log_id;
+   __be32 error_log_start_address;
+   __be16 error_log_length;
+   u16 reserved;
+   

[PATCH v5 10/10] hwmon (occ): Add sysfs attributes for additional OCC data

2018-08-30 Thread Eddie James
The OCC provides a variety of additional information about the state of
the host processor, such as throttling, error conditions, and the number
of OCCs detected in the system. This information is essential to service
processor applications such as fan control and host management.
Therefore, export this data in the form of sysfs attributes attached to
the platform device (to which the hwmon device is also attached).

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/Makefile |   2 +-
 drivers/hwmon/occ/common.c |  45 ++-
 drivers/hwmon/occ/common.h |  17 
 drivers/hwmon/occ/p8_i2c.c |  10 +++
 drivers/hwmon/occ/p9_sbe.c |   1 +
 drivers/hwmon/occ/sysfs.c  | 188 +
 6 files changed, 259 insertions(+), 4 deletions(-)
 create mode 100644 drivers/hwmon/occ/sysfs.c

diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index ab5c3e9..46a8636 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1,4 +1,4 @@
-occ-hwmon-objs := common.o
+occ-hwmon-objs := common.o sysfs.o
 
 ifeq ($(CONFIG_SENSORS_OCC_P9_SBE), y)
 occ-hwmon-objs += p9_sbe.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index c6c8161..423903f 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -14,6 +14,11 @@
 
 #define EXTN_FLAG_SENSOR_IDBIT(7)
 
+#define OCC_ERROR_COUNT_THRESHOLD  2   /* required by OCC spec */
+
+#define OCC_STATE_SAFE 4
+#define OCC_SAFE_TIMEOUT   msecs_to_jiffies(6) /* 1 min */
+
 #define OCC_UPDATE_FREQUENCY   msecs_to_jiffies(1000)
 
 #define OCC_TEMP_SENSOR_FAULT  0xFF
@@ -115,8 +120,10 @@ struct extended_sensor {
 
 static int occ_poll(struct occ *occ)
 {
+   int rc;
u16 checksum = occ->poll_cmd_data + 1;
u8 cmd[8];
+   struct occ_poll_response_header *header;
 
/* big endian */
cmd[0] = 0; /* sequence number */
@@ -129,7 +136,35 @@ static int occ_poll(struct occ *occ)
cmd[7] = 0;
 
/* mutex should already be locked if necessary */
-   return occ->send_cmd(occ, cmd);
+   rc = occ->send_cmd(occ, cmd);
+   if (rc) {
+   if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
+   occ->error = rc;
+
+   goto done;
+   }
+
+   /* clear error since communication was successful */
+   occ->error_count = 0;
+   occ->error = 0;
+
+   /* check for safe state */
+   header = (struct occ_poll_response_header *)occ->resp.data;
+   if (header->occ_state == OCC_STATE_SAFE) {
+   if (occ->last_safe) {
+   if (time_after(jiffies,
+  occ->last_safe + OCC_SAFE_TIMEOUT))
+   occ->error = -EHOSTDOWN;
+   } else {
+   occ->last_safe = jiffies;
+   }
+   } else {
+   occ->last_safe = 0;
+   }
+
+done:
+   occ_sysfs_poll_done(occ);
+   return rc;
 }
 
 static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
@@ -161,7 +196,7 @@ static int occ_set_user_power_cap(struct occ *occ, u16 
user_power_cap)
return rc;
 }
 
-static int occ_update_response(struct occ *occ)
+int occ_update_response(struct occ *occ)
 {
int rc = mutex_lock_interruptible(&occ->lock);
 
@@ -1055,5 +1090,9 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
 
-   return 0;
+   rc = occ_setup_sysfs(occ);
+   if (rc)
+   dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
+
+   return rc;
 }
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 00ac101..da80e65 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -104,8 +104,25 @@ struct occ {
struct occ_attribute *attrs;
struct attribute_group group;
const struct attribute_group *groups[2];
+
+   int error;  /* latest transfer error */
+   unsigned int error_count;   /* number of xfr errors observed */
+   unsigned long last_safe;/* time OCC entered "safe" state */
+
+   /*
+* Store the previous state data for comparison in order to notify
+* sysfs readers of state changes.
+*/
+   int prev_error;
+   u8 prev_stat;
+   u8 prev_ext_stat;
+   u8 prev_occs_present;
 };
 
 int occ_setup(struct occ *occ, const char *name);
+int occ_setup_sysfs(struct occ *occ);
+void occ_shutdown(struct occ *occ);
+void occ_sysfs_poll_done(struct occ *occ);
+int occ_update_response(struct occ *occ);
 
 #endif /* OCC_COMMON_H */
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index a736220..0dc7fa3 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c

[PATCH v5 08/10] hwmon (occ): Add sensor types and versions

2018-08-30 Thread Eddie James
Add structures to define all sensor types and versions. Add sysfs show
and store functions for each sensor type. Add a method to construct the
"set user power cap" command and send it to the OCC. Add rate limit to
polling the OCC (in case user-space reads our hwmon entries rapidly).

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 621 +
 drivers/hwmon/occ/common.h |   6 +
 drivers/hwmon/occ/p8_i2c.c |   1 +
 drivers/hwmon/occ/p9_sbe.c |   1 +
 4 files changed, 629 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index a066509..f7220132 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,10 +1,116 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include 
+#include 
+#include 
 #include 
+#include 
+#include 
+#include 
 
 #include "common.h"
 
+#define EXTN_FLAG_SENSOR_IDBIT(7)
+
+#define OCC_UPDATE_FREQUENCY   msecs_to_jiffies(1000)
+
+#define OCC_TEMP_SENSOR_FAULT  0xFF
+
+#define OCC_FRU_TYPE_VRM   3
+
+/* OCC sensor type and version definitions */
+
+struct temp_sensor_1 {
+   u16 sensor_id;
+   u16 value;
+} __packed;
+
+struct temp_sensor_2 {
+   u32 sensor_id;
+   u8 fru_type;
+   u8 value;
+} __packed;
+
+struct freq_sensor_1 {
+   u16 sensor_id;
+   u16 value;
+} __packed;
+
+struct freq_sensor_2 {
+   u32 sensor_id;
+   u16 value;
+} __packed;
+
+struct power_sensor_1 {
+   u16 sensor_id;
+   u32 update_tag;
+   u32 accumulator;
+   u16 value;
+} __packed;
+
+struct power_sensor_2 {
+   u32 sensor_id;
+   u8 function_id;
+   u8 apss_channel;
+   u16 reserved;
+   u32 update_tag;
+   u64 accumulator;
+   u16 value;
+} __packed;
+
+struct power_sensor_data {
+   u16 value;
+   u32 update_tag;
+   u64 accumulator;
+} __packed;
+
+struct power_sensor_data_and_time {
+   u16 update_time;
+   u16 value;
+   u32 update_tag;
+   u64 accumulator;
+} __packed;
+
+struct power_sensor_a0 {
+   u32 sensor_id;
+   struct power_sensor_data_and_time system;
+   u32 reserved;
+   struct power_sensor_data_and_time proc;
+   struct power_sensor_data vdd;
+   struct power_sensor_data vdn;
+} __packed;
+
+struct caps_sensor_2 {
+   u16 cap;
+   u16 system_power;
+   u16 n_cap;
+   u16 max;
+   u16 min;
+   u16 user;
+   u8 user_source;
+} __packed;
+
+struct caps_sensor_3 {
+   u16 cap;
+   u16 system_power;
+   u16 n_cap;
+   u16 max;
+   u16 hard_min;
+   u16 soft_min;
+   u16 user;
+   u8 user_source;
+} __packed;
+
+struct extended_sensor {
+   union {
+   u8 name[4];
+   u32 sensor_id;
+   };
+   u8 flags;
+   u8 reserved;
+   u8 data[6];
+} __packed;
+
 static int occ_poll(struct occ *occ)
 {
u16 checksum = occ->poll_cmd_data + 1;
@@ -20,9 +126,521 @@ static int occ_poll(struct occ *occ)
cmd[6] = checksum & 0xFF;   /* checksum lsb */
cmd[7] = 0;
 
+   /* mutex should already be locked if necessary */
return occ->send_cmd(occ, cmd);
 }
 
+static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
+{
+   int rc;
+   u8 cmd[8];
+   u16 checksum = 0x24;
+   __be16 user_power_cap_be = cpu_to_be16(user_power_cap);
+
+   cmd[0] = 0;
+   cmd[1] = 0x22;
+   cmd[2] = 0;
+   cmd[3] = 2;
+
+   memcpy(&cmd[4], &user_power_cap_be, 2);
+
+   checksum += cmd[4] + cmd[5];
+   cmd[6] = checksum >> 8;
+   cmd[7] = checksum & 0xFF;
+
+   rc = mutex_lock_interruptible(&occ->lock);
+   if (rc)
+   return rc;
+
+   rc = occ->send_cmd(occ, cmd);
+
+   mutex_unlock(&occ->lock);
+
+   return rc;
+}
+
+static int occ_update_response(struct occ *occ)
+{
+   int rc = mutex_lock_interruptible(&occ->lock);
+
+   if (rc)
+   return rc;
+
+   /* limit the maximum rate of polling the OCC */
+   if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
+   rc = occ_poll(occ);
+   occ->last_update = jiffies;
+   }
+
+   mutex_unlock(&occ->lock);
+   return rc;
+}
+
+static ssize_t occ_show_temp_1(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   int rc;
+   u32 val = 0;
+   struct temp_sensor_1 *temp;
+   struct occ *occ = dev_get_drvdata(dev);
+   struct occ_sensors *sensors = &occ->sensors;
+   struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+   rc = occ_update_response(occ);
+   if (rc)
+   return rc;
+
+   temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index;
+
+   switch (sattr->nr) {
+   case 0:
+   v

[PATCH v5 09/10] hwmon (occ): Add sensor attributes and register hwmon device

2018-08-30 Thread Eddie James
Setup the sensor attributes for every OCC sensor found by the first poll
response. Register the attributes with hwmon.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 337 +
 drivers/hwmon/occ/common.h |  16 +++
 2 files changed, 353 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index f7220132..c6c8161 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "common.h"
@@ -641,6 +643,324 @@ static ssize_t occ_show_extended(struct device *dev,
return rc;
 }
 
+/*
+ * Some helper macros to make it easier to define an occ_attribute. Since these
+ * are dynamically allocated, we shouldn't use the existing kernel macros which
+ * stringify the name argument.
+ */
+#define ATTR_OCC(_name, _mode, _show, _store) {
\
+   .attr   = { \
+   .name = _name,  \
+   .mode = VERIFY_OCTAL_PERMISSIONS(_mode),\
+   },  \
+   .show   = _show,\
+   .store  = _store,   \
+}
+
+#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) {\
+   .dev_attr   = ATTR_OCC(_name, _mode, _show, _store),\
+   .index  = _index,   \
+   .nr = _nr,  \
+}
+
+#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index)
\
+   ((struct sensor_device_attribute_2) \
+   SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
+
+/*
+ * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
+ * use our own instead of the built-in hwmon attribute types.
+ */
+static int occ_setup_sensor_attrs(struct occ *occ)
+{
+   unsigned int i, s, num_attrs = 0;
+   struct device *dev = occ->bus_dev;
+   struct occ_sensors *sensors = &occ->sensors;
+   struct occ_attribute *attr;
+   struct temp_sensor_2 *temp;
+   ssize_t (*show_temp)(struct device *, struct device_attribute *,
+char *) = occ_show_temp_1;
+   ssize_t (*show_freq)(struct device *, struct device_attribute *,
+char *) = occ_show_freq_1;
+   ssize_t (*show_power)(struct device *, struct device_attribute *,
+ char *) = occ_show_power_1;
+   ssize_t (*show_caps)(struct device *, struct device_attribute *,
+char *) = occ_show_caps_1_2;
+
+   switch (sensors->temp.version) {
+   case 1:
+   num_attrs += (sensors->temp.num_sensors * 2);
+   break;
+   case 2:
+   num_attrs += (sensors->temp.num_sensors * 4);
+   show_temp = occ_show_temp_2;
+   break;
+   default:
+   sensors->temp.num_sensors = 0;
+   }
+
+   switch (sensors->freq.version) {
+   case 2:
+   show_freq = occ_show_freq_2;
+   /* fall through */
+   case 1:
+   num_attrs += (sensors->freq.num_sensors * 2);
+   break;
+   default:
+   sensors->freq.num_sensors = 0;
+   }
+
+   switch (sensors->power.version) {
+   case 2:
+   show_power = occ_show_power_2;
+   /* fall through */
+   case 1:
+   num_attrs += (sensors->power.num_sensors * 4);
+   break;
+   case 0xA0:
+   num_attrs += (sensors->power.num_sensors * 16);
+   show_power = occ_show_power_a0;
+   break;
+   default:
+   sensors->power.num_sensors = 0;
+   }
+
+   switch (sensors->caps.version) {
+   case 1:
+   num_attrs += (sensors->caps.num_sensors * 7);
+   break;
+   case 3:
+   show_caps = occ_show_caps_3;
+   /* fall through */
+   case 2:
+   num_attrs += (sensors->caps.num_sensors * 8);
+   break;
+   default:
+   sensors->caps.num_sensors = 0;
+   }
+
+   switch (sensors->extended.version) {
+   case 1:
+   num_attrs += (sensors->extended.num_sensors * 3);
+   break;
+   default:
+   sensors->extended.num_sensors = 0;
+   }
+
+   occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs,
+ GFP_KERNEL);
+   if (!occ->attrs)

[PATCH v5 04/10] dt-bindings: i2c: Add P8 OCC hwmon device documentation

2018-08-30 Thread Eddie James
Document the bindings for I2C-based OCC hwmon device.

Signed-off-by: Eddie James 
Acked-by: Rob Herring 
---
 .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   | 25 ++
 1 file changed, 25 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt

diff --git a/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt 
b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
new file mode 100644
index 000..5dc5d2e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
@@ -0,0 +1,25 @@
+Device-tree bindings for I2C-based On-Chip Controller hwmon device
+--
+
+Required properties:
+ - compatible = "ibm,p8-occ-hwmon";
+ - reg = ;: I2C bus address
+
+Examples:
+
+i2c-bus@100 {
+#address-cells = <1>;
+#size-cells = <0>;
+clock-frequency = <10>;
+< more properties >
+
+occ-hwmon@1 {
+compatible = "ibm,p8-occ-hwmon";
+reg = <0x50>;
+};
+
+occ-hwmon@2 {
+compatible = "ibm,p8-occ-hwmon";
+reg = <0x51>;
+};
+};
-- 
1.8.3.1



[PATCH v5 05/10] hwmon: Add On-Chip Controller (OCC) hwmon driver

2018-08-30 Thread Eddie James
The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides a hwmon interface to the OCC from a service
processor (e.g. a BMC). The driver supports both POWER8 and POWER9 OCCs.
Communications with the POWER8 OCC are established over standard I2C
bus. The driver communicates with the POWER9 OCC through the FSI-based
OCC driver, which handles the lower-level communication details.

This patch lays out the structure of the OCC hwmon driver. There are two
platform drivers, one each for P8 and P9 OCCs. These are probed through
the I2C tree and the FSI-based OCC driver, respectively. The patch also
defines the first common structures and methods between the two OCC
versions.

Signed-off-by: Eddie James 
---
 drivers/hwmon/Kconfig  |  2 ++
 drivers/hwmon/Makefile |  1 +
 drivers/hwmon/occ/Kconfig  | 28 +++
 drivers/hwmon/occ/Makefile | 11 
 drivers/hwmon/occ/common.c | 40 +++
 drivers/hwmon/occ/common.h | 34 +++
 drivers/hwmon/occ/p8_i2c.c | 61 +
 drivers/hwmon/occ/p9_sbe.c | 68 ++
 8 files changed, 245 insertions(+)
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 81da17a..532a053 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1293,6 +1293,8 @@ config SENSORS_NSA320
  This driver can also be built as a module. If so, the module
  will be called nsa320-hwmon.
 
+source drivers/hwmon/occ/Kconfig
+
 config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 93f7f41..f5c7b44 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -178,6 +178,7 @@ obj-$(CONFIG_SENSORS_WM831X)+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)   += wm8350-hwmon.o
 obj-$(CONFIG_SENSORS_XGENE)+= xgene-hwmon.o
 
+obj-$(CONFIG_SENSORS_OCC)  += occ/
 obj-$(CONFIG_PMBUS)+= pmbus/
 
 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
new file mode 100644
index 000..9579b63
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,28 @@
+#
+# On-Chip Controller configuration
+#
+
+config SENSORS_OCC
+   tristate "POWER On-Chip Controller"
+   help
+This option enables support for monitoring a variety of system sensors
+provided by the On-Chip Controller (OCC) on a POWER processor.
+
+This driver can also be built as a module. If so, the module will be
+called occ-hwmon.
+
+config SENSORS_OCC_P8_I2C
+   bool "POWER8 OCC through I2C"
+   depends on I2C && SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the OCC
+on a POWER8 processor. Communications with the OCC are established
+through I2C bus.
+
+config SENSORS_OCC_P9_SBE
+   bool "POWER9 OCC through SBE"
+   depends on FSI_OCC && SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the OCC
+on a POWER9 processor. Communications with the OCC are established
+through SBEFIFO on an FSI bus.
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 000..ab5c3e9
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1,11 @@
+occ-hwmon-objs := common.o
+
+ifeq ($(CONFIG_SENSORS_OCC_P9_SBE), y)
+occ-hwmon-objs += p9_sbe.o
+endif
+
+ifeq ($(CONFIG_SENSORS_OCC_P8_I2C), y)
+occ-hwmon-objs += p8_i2c.o
+endif
+
+obj-$(CONFIG_SENSORS_OCC) += occ-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
new file mode 100644
index 000..81acd4b
--- /dev/null
+++ b/drivers/hwmon/occ/common.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+
+#include "common.h"
+
+static int occ_poll(struct occ *occ)
+{
+   u16 checksum = occ->poll_cmd_data + 1;
+   u8 cmd[8];
+
+   /* big endian */
+   cmd[0] = 0; /* sequence number */
+   cmd[1] = 0; /* cmd type */
+   cmd[2] = 0; /* data length msb */
+   cmd[3] = 1; /* data length lsb */
+   cmd[4] = occ->poll_cmd_data;/* data */
+   cmd[5] = checksum >> 8; /* checksum msb */
+   cmd[6] = checksum & 0xFF;   /* checksum lsb */
+ 

[PATCH v5 02/10] fsi: Add On-Chip Controller (OCC) driver

2018-08-30 Thread Eddie James
The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides an atomic communications channel between a service
processor (e.g. a BMC) and the OCC. The driver is dependent on the FSI
SBEFIFO driver to get hardware access through the SBE to the OCC SRAM.
Commands are issued to the SBE to send or fetch data to the SRAM.

Signed-off-by: Eddie James 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Benjamin Herrenschmidt 
Signed-off-by: Joel Stanley 
---
 drivers/fsi/Kconfig |  10 +
 drivers/fsi/Makefile|   1 +
 drivers/fsi/fsi-occ.c   | 599 
 include/linux/fsi-occ.h |  25 ++
 4 files changed, 635 insertions(+)
 create mode 100644 drivers/fsi/fsi-occ.c
 create mode 100644 include/linux/fsi-occ.h

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index af3a20d..ea2f4a1 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -64,4 +64,14 @@ config FSI_SBEFIFO
a pipe-like FSI device for communicating with the self boot engine
(SBE) on POWER processors.
 
+config FSI_OCC
+   tristate "OCC SBEFIFO client device driver"
+   depends on FSI_SBEFIFO
+   ---help---
+   This option enables an SBEFIFO based On-Chip Controller (OCC) device
+   driver. The OCC is a device embedded on a POWER processor that collects
+   and aggregates sensor data from the processor and system. The OCC can
+   provide the raw sensor data as well as perform thermal and power
+   management on the system.
+
 endif
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index a50d6ce..62687ec 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
+obj-$(CONFIG_FSI_OCC) += fsi-occ.o
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
new file mode 100644
index 000..2a2049d
--- /dev/null
+++ b/drivers/fsi/fsi-occ.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define OCC_SRAM_BYTES 4096
+#define OCC_CMD_DATA_BYTES 4090
+#define OCC_RESP_DATA_BYTES4089
+
+#define OCC_SRAM_CMD_ADDR  0xFFFBE000
+#define OCC_SRAM_RSP_ADDR  0xFFFBF000
+
+/*
+ * Assume we don't have much FFDC, if we do we'll overflow and
+ * fail the command. This needs to be big enough for simple
+ * commands as well.
+ */
+#define OCC_SBE_STATUS_WORDS   32
+
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+struct occ {
+   struct device *dev;
+   struct device *sbefifo;
+   char name[32];
+   int idx;
+   struct miscdevice mdev;
+   struct mutex occ_lock;
+};
+
+#define to_occ(x)  container_of((x), struct occ, mdev)
+
+struct occ_response {
+   u8 seq_no;
+   u8 cmd_type;
+   u8 return_status;
+   __be16 data_length;
+   u8 data[OCC_RESP_DATA_BYTES + 2];   /* two bytes checksum */
+} __packed;
+
+struct occ_client {
+   struct occ *occ;
+   struct mutex lock;
+   size_t data_size;
+   size_t read_offset;
+   u8 *buffer;
+};
+
+#define to_client(x)   container_of((x), struct occ_client, xfr)
+
+static DEFINE_IDA(occ_ida);
+
+static int occ_open(struct inode *inode, struct file *file)
+{
+   struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
+   struct miscdevice *mdev = file->private_data;
+   struct occ *occ = to_occ(mdev);
+
+   if (!client)
+   return -ENOMEM;
+
+   client->buffer = (u8 *)__get_free_page(GFP_KERNEL);
+   if (!client->buffer) {
+   kfree(client);
+   return -ENOMEM;
+   }
+
+   client->occ = occ;
+   mutex_init(&client->lock);
+   file->private_data = client;
+
+   /* We allocate a 1-page buffer, make sure it all fits */
+   BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
+   BUILD_BUG_ON((OCC_RESP_DATA_BYTES + 7) > PAGE_SIZE);
+
+   return 0;
+}
+
+static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
+   loff_t *offset)
+{
+   struct occ_client *client = file->private_data;
+   ssize_t rc = 0;
+
+   if (!client)
+   return -ENODEV;
+
+   if (len > OCC_SRAM_BYTES)
+   return -EINVAL;
+
+   mutex_lock(&client->lock);
+
+   /* This should not be possible ... */
+   if (WARN_ON_ONCE(client->read_offset > client->data_size)) {
+   rc = -EIO

Re: [PATCH v4 2/9] Documentation: hwmon: Add OCC documentation

2018-08-30 Thread Eddie James




On 07/25/2018 11:36 AM, Guenter Roeck wrote:

On Wed, Jul 11, 2018 at 04:01:31PM -0500, Eddie James wrote:

Document the hwmon interface for the OCC.

Signed-off-by: Eddie James 
---
  Documentation/hwmon/occ | 73 +
  1 file changed, 73 insertions(+)
  create mode 100644 Documentation/hwmon/occ

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
new file mode 100644
index 000..465fa1a
--- /dev/null
+++ b/Documentation/hwmon/occ
@@ -0,0 +1,73 @@
+Kernel driver occ-hwmon
+===
+
+Supported chips:
+  * POWER8
+  * POWER9
+
+Author: Eddie James 
+
+Description
+---
+
+This driver supports hardware monitoring for the On-Chip Controller (OCC)
+embedded on POWER processors. The OCC is a device that collects and aggregates
+sensor data from the processor and the system. The OCC can provide the raw
+sensor data as well as perform thermal and power management on the system.
+
+The P8 version of this driver is a client driver of I2C. It may be probed
+manually if an "ibm,p8-occ-hwmon" compatible device is found under the
+appropriate I2C bus node in the device-tree.
+
+The P9 version of this driver is a client driver of the FSI-based OCC driver.
+It will be probed automatically by the FSI-based OCC driver.
+
+Sysfs entries
+-
+
+The following attributes are supported. All attributes are read-only unless
+specified.
+
+temp[1-n]_labelOCC sensor id.
+temp[1-n]_inputMeasured temperature in millidegrees C.
+[with temperature sensor version 2+]
+temp[1-n]_fru_type Given FRU (Field Replaceable Unit) type.

What is this ? An integer ? A string ?


+temp[1-n]_faultTemperature sensor fault.
+
+freq[1-n]_labelOCC sensor id.
+freq[1-n]_inputMeasured frequency.

What does that have to do with hardware monitoring, and what exactly does it
measure ? AC voltage frequency ? Frequency of rainstorms in the surrounding
area ?


+
+power[1-n]_label   OCC sensor id.
+power[1-n]_input   Measured power in microwatts.
+power[1-n]_update_tag  Number of 250us samples represented in accumulator.

update_tag to represent number of samples ? Odd choice for
an attribute name. Why not "_samples" ? Also, if each sample
represents a specific amount of time, why not report a time ?


+power[1-n]_accumulator Accumulation of 250us power readings.

There is no explanation of "accumulation". Is this the energy ?
If so, why not use energy attributes ? And what is the unit of
this measurement ?


+[with power sensor version 2+]
+power[1-n]_function_id Identifies what the power reading is for.

String ? Number ? Slot index ?  Bitmap ? And why isn't that reported
in the label ? After all, that is what the label is supposed to be
used for.


+power[1-n]_apss_channelIndicates APSS channel.
+

Does that provide any value to the user ?


+[power version 0xa0 only]
+power1_id  OCC sensor id.

This is inconsistent with the other attributes and even with itself.


+power[1-n]_label   Sensor type, "system", "proc", "vdd", or "vdn".
+power[1-n]_input   Most recent power reading in microwatts.

Overall I am left with no idea what
_id
_label
_function_id
_apps_channel
are and how they relate to each other, except that it all looks quite
inconsistent. You might want to consider merging all those attributes into
the label in some consistent way.


+power[1-n]_update_tag  Number of samples in the accumulator.
+power[1-n]_accumulator Accumulation of power readings.

Same as above.


+[with sensor type "system" and "proc" only]
+power[1-n]_update_time Time in us that the power value is read.
+
+caps1_current  Current OCC power cap in watts.
+caps1_reading  Current system output power in watts.
+caps1_norm Power cap without redundant power.
+caps1_max  Maximum power cap.

Why do those have to be non-standard attributes ? Please explain why you can not
use power[1-n]_cap attributes.


+[caps version 1 and 2 only]
+caps1_min  Minimum power cap.
+[caps version 3+]
+caps1_min_hard Hard minimum cap that can be set and held.
+caps1_min_soft Soft minimum cap below hard, not guaranteed.
+caps1_user The powercap specified by the user. Will be 0 if no
+   user powercap exists. This attribute is read-write.
+[caps version 1+]
+caps1_user_source  Indicates how the user power limit was set.
+
+extn[1-n]_labelASCII id or sensor id.
+extn[1-n]_flagsIndicates type of label attribute.
+extn[1-n]_inputData.

Great non-explanation.

Not reviewing the series further. I am sure I asked that each non-standard
attribute

[PATCH v4 2/9] Documentation: hwmon: Add OCC documentation

2018-07-11 Thread Eddie James
Document the hwmon interface for the OCC.

Signed-off-by: Eddie James 
---
 Documentation/hwmon/occ | 73 +
 1 file changed, 73 insertions(+)
 create mode 100644 Documentation/hwmon/occ

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
new file mode 100644
index 000..465fa1a
--- /dev/null
+++ b/Documentation/hwmon/occ
@@ -0,0 +1,73 @@
+Kernel driver occ-hwmon
+===
+
+Supported chips:
+  * POWER8
+  * POWER9
+
+Author: Eddie James 
+
+Description
+---
+
+This driver supports hardware monitoring for the On-Chip Controller (OCC)
+embedded on POWER processors. The OCC is a device that collects and aggregates
+sensor data from the processor and the system. The OCC can provide the raw
+sensor data as well as perform thermal and power management on the system.
+
+The P8 version of this driver is a client driver of I2C. It may be probed
+manually if an "ibm,p8-occ-hwmon" compatible device is found under the
+appropriate I2C bus node in the device-tree.
+
+The P9 version of this driver is a client driver of the FSI-based OCC driver.
+It will be probed automatically by the FSI-based OCC driver.
+
+Sysfs entries
+-
+
+The following attributes are supported. All attributes are read-only unless
+specified.
+
+temp[1-n]_labelOCC sensor id.
+temp[1-n]_inputMeasured temperature in millidegrees C.
+[with temperature sensor version 2+]
+temp[1-n]_fru_type Given FRU (Field Replaceable Unit) type.
+temp[1-n]_faultTemperature sensor fault.
+
+freq[1-n]_labelOCC sensor id.
+freq[1-n]_inputMeasured frequency.
+
+power[1-n]_label   OCC sensor id.
+power[1-n]_input   Measured power in microwatts.
+power[1-n]_update_tag  Number of 250us samples represented in accumulator.
+power[1-n]_accumulator Accumulation of 250us power readings.
+[with power sensor version 2+]
+power[1-n]_function_id Identifies what the power reading is for.
+power[1-n]_apss_channelIndicates APSS channel.
+
+[power version 0xa0 only]
+power1_id  OCC sensor id.
+power[1-n]_label   Sensor type, "system", "proc", "vdd", or "vdn".
+power[1-n]_input   Most recent power reading in microwatts.
+power[1-n]_update_tag  Number of samples in the accumulator.
+power[1-n]_accumulator Accumulation of power readings.
+[with sensor type "system" and "proc" only]
+power[1-n]_update_time Time in us that the power value is read.
+
+caps1_current  Current OCC power cap in watts.
+caps1_reading  Current system output power in watts.
+caps1_norm Power cap without redundant power.
+caps1_max  Maximum power cap.
+[caps version 1 and 2 only]
+caps1_min  Minimum power cap.
+[caps version 3+]
+caps1_min_hard Hard minimum cap that can be set and held.
+caps1_min_soft Soft minimum cap below hard, not guaranteed.
+caps1_user The powercap specified by the user. Will be 0 if no
+   user powercap exists. This attribute is read-write.
+[caps version 1+]
+caps1_user_source  Indicates how the user power limit was set.
+
+extn[1-n]_labelASCII id or sensor id.
+extn[1-n]_flagsIndicates type of label attribute.
+extn[1-n]_inputData.
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v4 7/9] hwmon (occ): Add sensor types and versions

2018-07-11 Thread Eddie James
Add structures to define all sensor types and versions. Add sysfs show
and store functions for each sensor type. Add a method to construct the
"set user power cap" command and send it to the OCC. Add rate limit to
polling the OCC (in case user-space reads our hwmon entries rapidly).

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 648 +
 drivers/hwmon/occ/common.h |   5 +
 2 files changed, 653 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 73f62aa..1719536 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -11,10 +11,119 @@
  */
 
 #include 
+#include 
+#include 
 #include 
+#include 
+#include 
 
 #include "common.h"
 
+#define OCC_UPDATE_FREQUENCY   msecs_to_jiffies(1000)
+
+#define OCC_TEMP_SENSOR_FAULT  0xFF
+
+#define OCC_FRU_TYPE_VRM   0x3
+
+/* OCC sensor type and version definitions */
+
+struct temp_sensor_1 {
+   u16 sensor_id;
+   u16 value;
+} __packed;
+
+struct temp_sensor_2 {
+   u32 sensor_id;
+   u8 fru_type;
+   u8 value;
+} __packed;
+
+struct freq_sensor_1 {
+   u16 sensor_id;
+   u16 value;
+} __packed;
+
+struct freq_sensor_2 {
+   u32 sensor_id;
+   u16 value;
+} __packed;
+
+struct power_sensor_1 {
+   u16 sensor_id;
+   u32 update_tag;
+   u32 accumulator;
+   u16 value;
+} __packed;
+
+struct power_sensor_2 {
+   u32 sensor_id;
+   u8 function_id;
+   u8 apss_channel;
+   u16 reserved;
+   u32 update_tag;
+   u64 accumulator;
+   u16 value;
+} __packed;
+
+struct power_sensor_data {
+   u16 value;
+   u32 update_tag;
+   u64 accumulator;
+} __packed;
+
+struct power_sensor_data_and_time {
+   u16 update_time;
+   u16 value;
+   u32 update_tag;
+   u64 accumulator;
+} __packed;
+
+struct power_sensor_a0 {
+   u32 sensor_id;
+   struct power_sensor_data_and_time system;
+   u32 reserved;
+   struct power_sensor_data_and_time proc;
+   struct power_sensor_data vdd;
+   struct power_sensor_data vdn;
+} __packed;
+
+struct caps_sensor_1 {
+   u16 curr_powercap;
+   u16 curr_powerreading;
+   u16 norm_powercap;
+   u16 max_powercap;
+   u16 min_powercap;
+   u16 user_powerlimit;
+} __packed;
+
+struct caps_sensor_2 {
+   u16 curr_powercap;
+   u16 curr_powerreading;
+   u16 norm_powercap;
+   u16 max_powercap;
+   u16 min_powercap;
+   u16 user_powerlimit;
+   u8 user_powerlimit_source;
+} __packed;
+
+struct caps_sensor_3 {
+   u16 curr_powercap;
+   u16 curr_powerreading;
+   u16 norm_powercap;
+   u16 max_powercap;
+   u16 hard_min_powercap;
+   u16 soft_min_powercap;
+   u16 user_powerlimit;
+   u8 user_powerlimit_source;
+} __packed;
+
+struct extended_sensor {
+   u8 name[4];
+   u8 flags;
+   u8 reserved;
+   u8 data[6];
+} __packed;
+
 static int occ_poll(struct occ *occ)
 {
u16 checksum = occ->poll_cmd_data + 1;
@@ -30,9 +139,545 @@ static int occ_poll(struct occ *occ)
cmd[6] = checksum & 0xFF;   /* checksum lsb */
cmd[7] = 0;
 
+   /* mutex should already be locked if necessary */
return occ->send_cmd(occ, cmd);
 }
 
+static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
+{
+   int rc;
+   u8 cmd[8];
+   u16 checksum = 0x24;
+   __be16 user_power_cap_be = cpu_to_be16(user_power_cap);
+
+   cmd[0] = 0;
+   cmd[1] = 0x22;
+   cmd[2] = 0;
+   cmd[3] = 2;
+
+   memcpy(&cmd[4], &user_power_cap_be, 2);
+
+   checksum += cmd[4] + cmd[5];
+   cmd[6] = checksum >> 8;
+   cmd[7] = checksum & 0xFF;
+
+   rc = mutex_lock_interruptible(&occ->lock);
+   if (rc)
+   return rc;
+
+   rc = occ->send_cmd(occ, cmd);
+
+   mutex_unlock(&occ->lock);
+
+   return rc;
+}
+
+static int occ_update_response(struct occ *occ)
+{
+   int rc = mutex_lock_interruptible(&occ->lock);
+
+   if (rc)
+   return rc;
+
+   /* limit the maximum rate of polling the OCC */
+   if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) {
+   rc = occ_poll(occ);
+   occ->last_update = jiffies;
+   }
+
+   mutex_unlock(&occ->lock);
+   return rc;
+}
+
+static ssize_t occ_show_temp_1(struct device *dev,
+  struct device_attribute *attr, char *buf)
+{
+   int rc;
+   u32 val = 0;
+   struct temp_sensor_1 *temp;
+   struct occ *occ = dev_get_drvdata(dev);
+   struct occ_sensors *sensors = &occ->sensors;
+   struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+   rc = occ_update_response(occ);
+   if (rc)
+   return rc;
+
+   temp = ((struct temp_

[PATCH v4 6/9] hwmon (occ): Parse OCC poll response

2018-07-11 Thread Eddie James
Add method to parse the response from the OCC poll command. This only
needs to be done during probe(), since the OCC shouldn't change the
number or format of sensors while it's running. The parsed response
allows quick access to sensor data, as well as information on the
number and version of sensors, which we need to instantiate hwmon
attributes.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 61 ++
 drivers/hwmon/occ/common.h | 55 +
 2 files changed, 116 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 1fd3453..73f62aa 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -11,6 +11,7 @@
  */
 
 #include 
+#include 
 
 #include "common.h"
 
@@ -32,6 +33,64 @@ static int occ_poll(struct occ *occ)
return occ->send_cmd(occ, cmd);
 }
 
+/* only need to do this once at startup, as OCC won't change sensors on us */
+static void occ_parse_poll_response(struct occ *occ)
+{
+   unsigned int i, old_offset, offset = 0, size = 0;
+   struct occ_sensor *sensor;
+   struct occ_sensors *sensors = &occ->sensors;
+   struct occ_response *resp = &occ->resp;
+   struct occ_poll_response *poll =
+   (struct occ_poll_response *)&resp->data[0];
+   struct occ_poll_response_header *header = &poll->header;
+   struct occ_sensor_data_block *block = &poll->block;
+
+   dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
+header->occ_code_level);
+
+   for (i = 0; i < header->num_sensor_data_blocks; ++i) {
+   block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+   old_offset = offset;
+   offset = (block->header.num_sensors *
+ block->header.sensor_length) + sizeof(block->header);
+   size += offset;
+
+   /* validate all the length/size fields */
+   if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
+   dev_warn(occ->bus_dev, "exceeded response buffer\n");
+   return;
+   }
+
+   dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
+   old_offset, offset - 1, block->header.eye_catcher,
+   block->header.num_sensors);
+
+   /* match sensor block type */
+   if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
+   sensor = &sensors->temp;
+   else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
+   sensor = &sensors->freq;
+   else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
+   sensor = &sensors->power;
+   else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
+   sensor = &sensors->caps;
+   else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
+   sensor = &sensors->extended;
+   else {
+   dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
+block->header.eye_catcher);
+   continue;
+   }
+
+   sensor->num_sensors = block->header.num_sensors;
+   sensor->version = block->header.sensor_format;
+   sensor->data = &block->data;
+   }
+
+   dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
+   sizeof(*header), size + sizeof(*header));
+}
+
 int occ_setup(struct occ *occ, const char *name)
 {
int rc;
@@ -46,5 +105,7 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
 
+   occ_parse_poll_response(occ);
+
return 0;
 }
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 775b421..cfcd353 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -29,10 +29,65 @@ struct occ_response {
__be16 checksum;
 } __packed;
 
+struct occ_sensor_data_block_header {
+   u8 eye_catcher[4];
+   u8 reserved;
+   u8 sensor_format;
+   u8 sensor_length;
+   u8 num_sensors;
+} __packed;
+
+struct occ_sensor_data_block {
+   struct occ_sensor_data_block_header header;
+   u32 data;
+} __packed;
+
+struct occ_poll_response_header {
+   u8 status;
+   u8 ext_status;
+   u8 occs_present;
+   u8 config_data;
+   u8 occ_state;
+   u8 mode;
+   u8 ips_status;
+   u8 error_log_id;
+   __be32 error_log_start_address;
+   __be16 error_log_length;
+   u16 reserved;
+   u8 occ_code_level[

[PATCH v4 4/9] hwmon: Add On-Chip Controller (OCC) hwmon driver

2018-07-11 Thread Eddie James
The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides a hwmon interface to the OCC from a service
processor (e.g. a BMC). The driver supports both POWER8 and POWER9 OCCs.
Communications with the POWER8 OCC are established over standard I2C
bus. The driver communicates with the POWER9 OCC through the FSI-based
OCC driver, which handles the lower-level communication details.

This patch lays out the structure of the OCC hwmon driver. There are two
platform drivers, one each for P8 and P9 OCCs. These are probed through
the I2C tree and the FSI-based OCC driver, respectively. The patch also
defines the first common structures and methods between the two OCC
versions.

Signed-off-by: Eddie James 
---
 drivers/hwmon/Kconfig  |  2 ++
 drivers/hwmon/Makefile |  1 +
 drivers/hwmon/occ/Kconfig  | 28 +
 drivers/hwmon/occ/Makefile | 11 +++
 drivers/hwmon/occ/common.c | 50 +
 drivers/hwmon/occ/common.h | 43 +
 drivers/hwmon/occ/p8_i2c.c | 71 +
 drivers/hwmon/occ/p9_sbe.c | 78 ++
 8 files changed, 284 insertions(+)
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a4e5d3c..9b3871e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1293,6 +1293,8 @@ config SENSORS_NSA320
  This driver can also be built as a module. If so, the module
  will be called nsa320-hwmon.
 
+source drivers/hwmon/occ/Kconfig
+
 config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 93f7f41..f5c7b44 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -178,6 +178,7 @@ obj-$(CONFIG_SENSORS_WM831X)+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)   += wm8350-hwmon.o
 obj-$(CONFIG_SENSORS_XGENE)+= xgene-hwmon.o
 
+obj-$(CONFIG_SENSORS_OCC)  += occ/
 obj-$(CONFIG_PMBUS)+= pmbus/
 
 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
new file mode 100644
index 000..9579b63
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,28 @@
+#
+# On-Chip Controller configuration
+#
+
+config SENSORS_OCC
+   tristate "POWER On-Chip Controller"
+   help
+This option enables support for monitoring a variety of system sensors
+provided by the On-Chip Controller (OCC) on a POWER processor.
+
+This driver can also be built as a module. If so, the module will be
+called occ-hwmon.
+
+config SENSORS_OCC_P8_I2C
+   bool "POWER8 OCC through I2C"
+   depends on I2C && SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the OCC
+on a POWER8 processor. Communications with the OCC are established
+through I2C bus.
+
+config SENSORS_OCC_P9_SBE
+   bool "POWER9 OCC through SBE"
+   depends on FSI_OCC && SENSORS_OCC
+   help
+This option enables support for monitoring sensors provided by the OCC
+on a POWER9 processor. Communications with the OCC are established
+through SBEFIFO on an FSI bus.
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 000..ab5c3e9
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1,11 @@
+occ-hwmon-objs := common.o
+
+ifeq ($(CONFIG_SENSORS_OCC_P9_SBE), y)
+occ-hwmon-objs += p9_sbe.o
+endif
+
+ifeq ($(CONFIG_SENSORS_OCC_P8_I2C), y)
+occ-hwmon-objs += p8_i2c.o
+endif
+
+obj-$(CONFIG_SENSORS_OCC) += occ-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
new file mode 100644
index 000..1fd3453
--- /dev/null
+++ b/drivers/hwmon/occ/common.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OCC hwmon driver common functionality
+ *
+ * Copyright (C) IBM Corporation 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include 
+
+#include "common.h"
+
+static int occ_poll(struct occ *occ)
+{
+   u16 checksum = occ->poll_cmd_data + 1;
+   u8 cmd[8];
+
+   /* big endian */
+   cmd[0] = 0; /* sequence number */
+   

[PATCH v4 8/9] hwmon (occ): Add sensor attributes and register hwmon device

2018-07-11 Thread Eddie James
Setup the sensor attributes for every OCC sensor found by the first poll
response. Register the attributes with hwmon.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/common.c | 452 +
 drivers/hwmon/occ/common.h |  16 ++
 2 files changed, 468 insertions(+)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 1719536..da55919 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -11,10 +11,12 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "common.h"
@@ -678,6 +680,439 @@ static ssize_t occ_show_extended(struct device *dev,
return rc;
 }
 
+/*
+ * Some helper macros to make it easier to define an occ_attribute. Since these
+ * are dynamically allocated, we shouldn't use the existing kernel macros which
+ * stringify the name argument.
+ */
+#define ATTR_OCC(_name, _mode, _show, _store) {
\
+   .attr   = { \
+   .name = _name,  \
+   .mode = VERIFY_OCTAL_PERMISSIONS(_mode),\
+   },  \
+   .show   = _show,\
+   .store  = _store,   \
+}
+
+#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) {\
+   .dev_attr   = ATTR_OCC(_name, _mode, _show, _store),\
+   .index  = _index,   \
+   .nr = _nr,  \
+}
+
+#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index)
\
+   ((struct sensor_device_attribute_2) \
+   SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
+
+/*
+ * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
+ * use our own instead of the built-in hwmon attribute types.
+ */
+static int occ_setup_sensor_attrs(struct occ *occ)
+{
+   unsigned int i, s, num_attrs = 0;
+   struct device *dev = occ->bus_dev;
+   struct occ_sensors *sensors = &occ->sensors;
+   struct occ_attribute *attr;
+   struct temp_sensor_2 *temp;
+   ssize_t (*show_temp)(struct device *, struct device_attribute *,
+char *) = occ_show_temp_1;
+   ssize_t (*show_freq)(struct device *, struct device_attribute *,
+char *) = occ_show_freq_1;
+   ssize_t (*show_power)(struct device *, struct device_attribute *,
+ char *) = occ_show_power_1;
+   ssize_t (*show_caps)(struct device *, struct device_attribute *,
+char *) = occ_show_caps_1;
+
+   switch (sensors->temp.version) {
+   case 1:
+   num_attrs += (sensors->temp.num_sensors * 2);
+   break;
+   case 2:
+   num_attrs += (sensors->temp.num_sensors * 4);
+   show_temp = occ_show_temp_2;
+   break;
+   default:
+   sensors->temp.num_sensors = 0;
+   }
+
+   switch (sensors->freq.version) {
+   case 2:
+   show_freq = occ_show_freq_2;
+   /* fall through */
+   case 1:
+   num_attrs += (sensors->freq.num_sensors * 2);
+   break;
+   default:
+   sensors->freq.num_sensors = 0;
+   }
+
+   switch (sensors->power.version) {
+   case 1:
+   num_attrs += (sensors->power.num_sensors * 4);
+   break;
+   case 2:
+   num_attrs += (sensors->power.num_sensors * 6);
+   show_power = occ_show_power_2;
+   break;
+   case 0xA0:
+   num_attrs += (sensors->power.num_sensors * 19);
+   show_power = occ_show_power_a0;
+   break;
+   default:
+   sensors->power.num_sensors = 0;
+   }
+
+   switch (sensors->caps.version) {
+   case 1:
+   num_attrs += (sensors->caps.num_sensors * 6);
+   break;
+   case 2:
+   num_attrs += (sensors->caps.num_sensors * 7);
+   show_caps = occ_show_caps_2;
+   break;
+   case 3:
+   num_attrs += (sensors->caps.num_sensors * 8);
+   show_caps = occ_show_caps_3;
+   break;
+   default:
+   sensors->caps.num_sensors = 0;
+   }
+
+   switch (sensors->extended.version) {
+   case 1:
+   num_attrs += (sensors->extended.num_sensors * 3);
+   break;
+   default:
+   sensors->extended.num_sensors = 0;
+   }
+
+   occ->attrs = devm_kzalloc(dev, siz

[PATCH v4 3/9] dt-bindings: i2c: Add P8 OCC hwmon device documentation

2018-07-11 Thread Eddie James
Document the bindings for I2C-based OCC hwmon device.

Signed-off-by: Eddie James 
Acked-by: Rob Herring 
---
 .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   | 25 ++
 1 file changed, 25 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt

diff --git a/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt 
b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
new file mode 100644
index 000..5dc5d2e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
@@ -0,0 +1,25 @@
+Device-tree bindings for I2C-based On-Chip Controller hwmon device
+--
+
+Required properties:
+ - compatible = "ibm,p8-occ-hwmon";
+ - reg = ;: I2C bus address
+
+Examples:
+
+i2c-bus@100 {
+#address-cells = <1>;
+#size-cells = <0>;
+clock-frequency = <10>;
+< more properties >
+
+occ-hwmon@1 {
+compatible = "ibm,p8-occ-hwmon";
+reg = <0x50>;
+};
+
+occ-hwmon@2 {
+compatible = "ibm,p8-occ-hwmon";
+reg = <0x51>;
+};
+};
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v4 5/9] hwmon (occ): Add command transport method for P8 and P9

2018-07-11 Thread Eddie James
For the P8 OCC, add the procedure to send a command to the OCC over I2C
bus. This involves writing the OCC command registers with serial
communication operations (SCOMs) interpreted by the I2C slave. For the
P9 OCC, add a procedure to use the OCC in-kernel API to send a command
to the OCC through the SBE.

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/p8_i2c.c | 185 -
 drivers/hwmon/occ/p9_sbe.c |  38 +-
 2 files changed, 221 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 899294b..9a920db 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -12,11 +12,29 @@
 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
+#include 
+#include 
 
 #include "common.h"
 
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+/* OCB (on-chip control bridge - interface to OCC) registers */
+#define OCB_DATA1  0x6B035
+#define OCB_ADDR   0x6B070
+#define OCB_DATA3  0x6B075
+
+/* OCC SRAM address space */
+#define OCC_SRAM_ADDR_CMD  0x6000
+#define OCC_SRAM_ADDR_RESP 0x7000
+
+#define OCC_DATA_ATTN  0x2001
+
 struct p8_i2c_occ {
struct occ occ;
struct i2c_client *client;
@@ -24,9 +42,174 @@ struct p8_i2c_occ {
 
 #define to_p8_i2c_occ(x)   container_of((x), struct p8_i2c_occ, occ)
 
+static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
+{
+   ssize_t rc;
+   __be64 buf;
+   struct i2c_msg msgs[2];
+
+   /* p8 i2c slave requires shift */
+   address <<= 1;
+
+   msgs[0].addr = client->addr;
+   msgs[0].flags = client->flags & I2C_M_TEN;
+   msgs[0].len = sizeof(u32);
+   /* address is a scom address; bus-endian */
+   msgs[0].buf = (char *)&address;
+
+   /* data from OCC is big-endian */
+   msgs[1].addr = client->addr;
+   msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+   msgs[1].len = sizeof(u64);
+   msgs[1].buf = (char *)&buf;
+
+   rc = i2c_transfer(client->adapter, msgs, 2);
+   if (rc < 0)
+   return rc;
+
+   *(u64 *)data = be64_to_cpu(buf);
+
+   return 0;
+}
+
+static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
+{
+   u32 buf[3];
+   ssize_t rc;
+
+   /* p8 i2c slave requires shift */
+   address <<= 1;
+
+   /* address is bus-endian; data passed through from user as-is */
+   buf[0] = address;
+   memcpy(&buf[1], &data[4], sizeof(u32));
+   memcpy(&buf[2], data, sizeof(u32));
+
+   rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
+   if (rc < 0)
+   return rc;
+   else if (rc != sizeof(buf))
+   return -EIO;
+
+   return 0;
+}
+
+static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
+ u32 data0, u32 data1)
+{
+   u8 buf[8];
+
+   memcpy(buf, &data0, 4);
+   memcpy(buf + 4, &data1, 4);
+
+   return p8_i2c_occ_putscom(client, address, buf);
+}
+
+static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
+u8 *data)
+{
+   __be32 data0, data1;
+
+   memcpy(&data0, data, 4);
+   memcpy(&data1, data + 4, 4);
+
+   return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
+ be32_to_cpu(data1));
+}
+
 static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
 {
-   return -EOPNOTSUPP;
+   int i, rc;
+   unsigned long start;
+   u16 data_length;
+   const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
+   const long int wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
+   struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ);
+   struct i2c_client *client = ctx->client;
+   struct occ_response *resp = &occ->resp;
+
+   start = jiffies;
+
+   /* set sram address for command */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
+   if (rc)
+   return rc;
+
+   /* write command (expected to already be BE), we need bus-endian... */
+   rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
+   if (rc)
+   return rc;
+
+   /* trigger OCC attention */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
+   if (rc)
+   return rc;
+
+   do {
+   /* set sram address for response */
+   rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
+   OCC_SRAM_ADDR_RESP, 0);
+   if (rc)
+   return rc;
+
+   rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 *)re

[PATCH v4 9/9] hwmon (occ): Add sysfs attributes for additional OCC data

2018-07-11 Thread Eddie James
The OCC provides a variety of additional information about the state of
the host processor, such as throttling, error conditions, and the number
of OCCs detected in the system. This information is essential to service
processor applications such as fan control and host management.
Therefore, export this data in the form of sysfs attributes attached to
the platform device (to which the hwmon device is also attached).

Signed-off-by: Eddie James 
---
 drivers/hwmon/occ/Makefile |   2 +-
 drivers/hwmon/occ/common.c |  45 ++-
 drivers/hwmon/occ/common.h |  17 
 drivers/hwmon/occ/p8_i2c.c |  10 +++
 drivers/hwmon/occ/p9_sbe.c |   1 +
 drivers/hwmon/occ/sysfs.c  | 188 +
 6 files changed, 259 insertions(+), 4 deletions(-)
 create mode 100644 drivers/hwmon/occ/sysfs.c

diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index ab5c3e9..46a8636 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1,4 +1,4 @@
-occ-hwmon-objs := common.o
+occ-hwmon-objs := common.o sysfs.o
 
 ifeq ($(CONFIG_SENSORS_OCC_P9_SBE), y)
 occ-hwmon-objs += p9_sbe.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index da55919..7bc52af 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -21,6 +21,11 @@
 
 #include "common.h"
 
+#define OCC_ERROR_COUNT_THRESHOLD  2   /* required by OCC spec */
+
+#define OCC_STATE_SAFE 4
+#define OCC_SAFE_TIMEOUT   msecs_to_jiffies(6) /* 1 min */
+
 #define OCC_UPDATE_FREQUENCY   msecs_to_jiffies(1000)
 
 #define OCC_TEMP_SENSOR_FAULT  0xFF
@@ -128,8 +133,10 @@ struct extended_sensor {
 
 static int occ_poll(struct occ *occ)
 {
+   int rc;
u16 checksum = occ->poll_cmd_data + 1;
u8 cmd[8];
+   struct occ_poll_response_header *header;
 
/* big endian */
cmd[0] = 0; /* sequence number */
@@ -142,7 +149,35 @@ static int occ_poll(struct occ *occ)
cmd[7] = 0;
 
/* mutex should already be locked if necessary */
-   return occ->send_cmd(occ, cmd);
+   rc = occ->send_cmd(occ, cmd);
+   if (rc) {
+   if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
+   occ->error = rc;
+
+   goto done;
+   }
+
+   /* clear error since communication was successful */
+   occ->error_count = 0;
+   occ->error = 0;
+
+   /* check for safe state */
+   header = (struct occ_poll_response_header *)occ->resp.data;
+   if (header->occ_state == OCC_STATE_SAFE) {
+   if (occ->last_safe) {
+   if (time_after(jiffies,
+  occ->last_safe + OCC_SAFE_TIMEOUT))
+   occ->error = -EHOSTDOWN;
+   } else {
+   occ->last_safe = jiffies;
+   }
+   } else {
+   occ->last_safe = 0;
+   }
+
+done:
+   occ_sysfs_poll_done(occ);
+   return rc;
 }
 
 static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
@@ -174,7 +209,7 @@ static int occ_set_user_power_cap(struct occ *occ, u16 
user_power_cap)
return rc;
 }
 
-static int occ_update_response(struct occ *occ)
+int occ_update_response(struct occ *occ)
 {
int rc = mutex_lock_interruptible(&occ->lock);
 
@@ -1207,5 +1242,9 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
 
-   return 0;
+   rc = occ_setup_sysfs(occ);
+   if (rc)
+   dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
+
+   return rc;
 }
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 2c1c06e..7bacbb6 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -112,8 +112,25 @@ struct occ {
struct occ_attribute *attrs;
struct attribute_group group;
const struct attribute_group *groups[2];
+
+   int error;  /* latest transfer error */
+   unsigned int error_count;   /* number of xfr errors observed */
+   unsigned long last_safe;/* time OCC entered "safe" state */
+
+   /*
+* Store the previous state data for comparison in order to notify
+* sysfs readers of state changes.
+*/
+   int prev_error;
+   u8 prev_stat;
+   u8 prev_ext_stat;
+   u8 prev_occs_present;
 };
 
 int occ_setup(struct occ *occ, const char *name);
+int occ_setup_sysfs(struct occ *occ);
+void occ_shutdown(struct occ *occ);
+void occ_sysfs_poll_done(struct occ *occ);
+int occ_update_response(struct occ *occ);
 
 #endif /* OCC_COMMON_H */
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 9a920db..63f3db8 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -232,6 +232,15 @@ static 

[PATCH v4 1/9] fsi: Add On-Chip Controller (OCC) driver

2018-07-11 Thread Eddie James
The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.

This driver provides an atomic communications channel between a service
processor (e.g. a BMC) and the OCC. The driver is dependent on the FSI
SBEFIFO driver to get hardware access through the SBE to the OCC SRAM.
Commands are issued to the SBE to send or fetch data to the SRAM.

Signed-off-by: Eddie James 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Benjamin Herrenschmidt 
Signed-off-by: Joel Stanley 
---
 drivers/fsi/Kconfig |  10 +
 drivers/fsi/Makefile|   1 +
 drivers/fsi/fsi-occ.c   | 609 
 include/linux/fsi-occ.h |  34 +++
 4 files changed, 654 insertions(+)
 create mode 100644 drivers/fsi/fsi-occ.c
 create mode 100644 include/linux/fsi-occ.h

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 24f84a9..322cec3 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -39,4 +39,14 @@ config FSI_SBEFIFO
a pipe-like FSI device for communicating with the self boot engine
(SBE) on POWER processors.
 
+config FSI_OCC
+   tristate "OCC SBEFIFO client device driver"
+   depends on FSI_SBEFIFO
+   ---help---
+   This option enables an SBEFIFO based On-Chip Controller (OCC) device
+   driver. The OCC is a device embedded on a POWER processor that collects
+   and aggregates sensor data from the processor and system. The OCC can
+   provide the raw sensor data as well as perform thermal and power
+   management on the system.
+
 endif
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 851182e..75fdc6d 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
 obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
+obj-$(CONFIG_FSI_OCC) += fsi-occ.o
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
new file mode 100644
index 000..bba67a6
--- /dev/null
+++ b/drivers/fsi/fsi-occ.c
@@ -0,0 +1,609 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * On-Chip Controller Driver
+ *
+ * Copyright (C) IBM Corporation 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define OCC_SRAM_BYTES 4096
+#define OCC_CMD_DATA_BYTES 4090
+#define OCC_RESP_DATA_BYTES4089
+
+#define OCC_SRAM_CMD_ADDR  0xFFFBE000
+#define OCC_SRAM_RSP_ADDR  0xFFFBF000
+
+/*
+ * Assume we don't have much FFDC, if we do we'll overflow and
+ * fail the command. This needs to be big enough for simple
+ * commands as well.
+ */
+#define OCC_SBE_STATUS_WORDS   32
+
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+struct occ {
+   struct device *dev;
+   struct device *sbefifo;
+   char name[32];
+   int idx;
+   struct miscdevice mdev;
+   struct mutex occ_lock;
+};
+
+#define to_occ(x)  container_of((x), struct occ, mdev)
+
+struct occ_response {
+   u8 seq_no;
+   u8 cmd_type;
+   u8 return_status;
+   __be16 data_length;
+   u8 data[OCC_RESP_DATA_BYTES + 2];   /* two bytes checksum */
+} __packed;
+
+struct occ_client {
+   struct occ *occ;
+   struct mutex lock;
+   size_t data_size;
+   size_t read_offset;
+   u8 *buffer;
+};
+
+#define to_client(x)   container_of((x), struct occ_client, xfr)
+
+static DEFINE_IDA(occ_ida);
+
+static int occ_open(struct inode *inode, struct file *file)
+{
+   struct occ_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
+   struct miscdevice *mdev = file->private_data;
+   struct occ *occ = to_occ(mdev);
+
+   if (!client)
+   return -ENOMEM;
+
+   client->buffer = (u8 *)__get_free_page(GFP_KERNEL);
+   if (!client->buffer) {
+   kfree(client);
+   return -ENOMEM;
+   }
+
+   client->occ = occ;
+   mutex_init(&client->lock);
+   file->private_data = client;
+
+   /* We allocate a 1-page buffer, make sure it all fits */
+   BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
+   BUILD_BUG_ON((OCC_RESP_DATA_BYTES + 7) > PAGE_SIZE);
+
+   return 0;
+}
+
+static ssize_t occ_read(struct file *file, char __user *buf, size_t len,
+   loff_t *offset)
+{
+   struct occ_client *client = file->private_

[PATCH v4 0/9] hwmon and fsi: Add On-Chip Controller (OCC) driver

2018-07-11 Thread Eddie James
This series adds a hwmon driver to support the OCC on POWER8 and POWER9
processors. The OCC is an embedded processor that provides realtime power and
thermal monitoring and management.

The series also adds a "bus" driver to handle atomic communication between the
service processor and the OCC on a POWER9 chip. This communication takes place
over FSI bus to the SBE (Self-Boot engine) FIFO, which in turn communicates
with the OCC. The driver for the SBEFIFO is already in linux-next as an FSI
client driver.

For POWER8 OCCs, communication between the service processor and the OCC is
achieved over I2C bus.

Changes since v3:
 * Add the FSI OCC driver.
 * Pull the sysfs attribute code into it's own file for cleanliness.
 * Various fixes for attribute creation and integer overflow.

Changes since v2:
 * Add sysfs_notify for the error and throttling attributes when change is
   detected.
 * Removed occs_present counting of devices bound.
 * Improved remove() of P9 driver to avoid bad behavior with relation to OCC
   driver when unbound.
 * Added default cases (return EINVAL) for all sensor show functions.
 * Added temperature fault sensor.
 * Added back dt binding documentation for P9 to address checkpatch warning.
 * Added occs_present attribute from the poll response.

Changes since v1:
 * Remove wait loop in P9 code, as that is now handled by FSI OCC driver.
 * Removed dt binding documentation for P9, FSI OCC driver will probe OCC hwmon
   driver automatically.
 * Moved OCC response code definitions to the OCC include file.
 * Fixed includes.
 * Changed some structure fields to __beXX as that is what they are.
 * Changed some errnos.
 * Removed some dev_err().
 * Refactored P8 code a bit to use #defined addresses and magic values, and
   changed "goto retry" to a loop.
 * Refactored error handling a bit.

Eddie James (9):
  fsi: Add On-Chip Controller (OCC) driver
  Documentation: hwmon: Add OCC documentation
  dt-bindings: i2c: Add P8 OCC hwmon device documentation
  hwmon: Add On-Chip Controller (OCC) hwmon driver
  hwmon (occ): Add command transport method for P8 and P9
  hwmon (occ): Parse OCC poll response
  hwmon (occ): Add sensor types and versions
  hwmon (occ): Add sensor attributes and register hwmon device
  hwmon (occ): Add sysfs attributes for additional OCC data

 .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt   |   25 +
 Documentation/hwmon/occ|   73 ++
 drivers/fsi/Kconfig|   10 +
 drivers/fsi/Makefile   |1 +
 drivers/fsi/fsi-occ.c  |  609 ++
 drivers/hwmon/Kconfig  |2 +
 drivers/hwmon/Makefile |1 +
 drivers/hwmon/occ/Kconfig  |   28 +
 drivers/hwmon/occ/Makefile |   11 +
 drivers/hwmon/occ/common.c | 1250 
 drivers/hwmon/occ/common.h |  136 +++
 drivers/hwmon/occ/p8_i2c.c |  264 +
 drivers/hwmon/occ/p9_sbe.c |  115 ++
 drivers/hwmon/occ/sysfs.c  |  188 +++
 include/linux/fsi-occ.h|   34 +
 15 files changed, 2747 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
 create mode 100644 Documentation/hwmon/occ
 create mode 100644 drivers/fsi/fsi-occ.c
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/common.c
 create mode 100644 drivers/hwmon/occ/common.h
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 create mode 100644 drivers/hwmon/occ/p9_sbe.c
 create mode 100644 drivers/hwmon/occ/sysfs.c
 create mode 100644 include/linux/fsi-occ.h

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: hwmon driver with misc interface

2018-07-10 Thread Eddie James




On 07/08/2018 08:26 PM, Guenter Roeck wrote:

On 07/08/2018 06:05 PM, Benjamin Herrenschmidt wrote:

On Sun, 2018-07-08 at 16:30 -0700, Guenter Roeck wrote:


Trying to be be reasonable

Let's make some ground rules.

- Do not attach foreign attributes (not related to hardware 
monitoring) to
    the hwmon device. Attach foreign attributes to its parent, eg 
the platform
    or i2c driver, or to a separate (misc ?) device if that is not 
feasible

    for some reason.
- Avoid foreign subsystem drivers. If the chip has an input device, 
a watchdog,

    and a hardware monitor, there should be three drivers.
    This is to some degree flexible; for example, PMBus drivers may 
register
    as power regulators, and some chips also have gpio support. But 
what,

    for example, the applesmc driver does is really not acceptable.


This rule can be a bit nasty if the various "parts" of the chip need
tight interlock, share an interrupt etc... the solution to that is to
have most of the common code in a "parent" driver that creates child
devices with separate drivers that directly link onto the parent and
use exported functions, but it can easily bloat the driver
significantly for little benefit.



But that is what mfd drivers are for, or am I missing something ?
After all, it has "multi-function device" right in its name.

Sure, there is somebloat, but on the plus side it ensures that
all bits and pieces are reviewed by the respective maintainers,
and the cross-functional API is _forced_ to be clean.


That said, this is maybe not *too much* of an issue in the OCC case,
see below.


- Private hwmon attributes are acceptable as long as they are clearly
    documented and explained as necessary. This is not a free ride; 
you should
    have good reasons for private attributes and be able to explain 
that and why
    you need them. In this context, "because the hardware provides 
the information"

    is not a valid reason. The use case is important, not the fact that
    the hardware provides some random information.

Can you work with that ?


Anything is always possible :-) The main question for me here is
whether to keep what we do today:

* sbefifo (the transport driver)
  |
  * fsi-occ platform driver
 ("passes occ hwmon commands to sbefifo and adds /dev/occ")
  |
  * occ-hwmon

Or can I collapse fsi-occ and occ-hwmon into one.

Now /dev/occ is just a "raw" interface to send commands to the OCC, via
the same path occ-hwmon does. There's locking needed there between the
two so it currently happens in fsi-occ.


From what you are saying, you prefer that we keep it separate, which is

our current design. I find it a bit messy but it's not a huge deal
frankly, so let's do so.


Other drivers solve that with an API from parent to child, either with
a direct function call or with a callback function provided to the child.
Another option would be to handle it through regmap; That nowadays
supports custom accesses implemented in the parent driver (see 
regmap_read

and regmap_write in struct regmap_config). The child driver gets the
regmap pointer and uses the regmap API. I don't see that as messy.


The pre-requisite sbefifo driver is now in Greg's tree (and I'll have
some fixes for it in -next this week), so you should be able to at
least test build when Eddie resubmits.


Ok.


As for the various sysfs files for monitoring the base functionality of
the occ, Eddie, you can always move them to fsi-occ.



Yes, I think that would be more appropriate.


This still won't work, since then we wouldn't have those attributes 
available in the P8 version of the driver (which has no fsi-occ driver). 
In addition, how would the poll response data get from the hwmon driver 
to the fsi-occ driver? Yet another interface? Seems awkward.


How about debugfs? We don't really mind where the attributes are, just 
that the data is exposed somewhere...


Thanks,
Eddie



Thanks,
Guenter


Cheers,
Ben.





--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-16 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 138 +-
 1 file changed, 137 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index ef2c5bf..70cecb0 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -37,6 +38,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -64,15 +66,24 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
 #ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
 #endif
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -359,6 +370,122 @@ static void ucd9000_probe_gpio(struct i2c_client *client,
 }
 #endif /* CONFIG_GPIOLIB */
 
+#ifdef CONFIG_DEBUG_FS
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2];
+   char *res;
+   int rc;
+
+   rc = ucd9000_get_mfr_status(client, buffer);
+   if (rc < 0)
+   return rc;
+
+   res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX));
+   *res++ = '\n';
+   *res = 0;
+
+   return simple_read_from_buffer(buf, count, ppos, str, res - str);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report GPI faults in their MFR_STATUS register, so only
+* create the GPI fault debugfs attributes for those chips.
+*/
+   if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+   mid->driver_data == ucd90910) {
+   entries = devm_kzalloc(&client->dev,
+  sizeof(*entries) * UCD9000_GPI_COUNT,
+  GFP_KERNEL);
+   if (!entries)
+   ret

[PATCH v7 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-16 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v6:
 - don't return null terminator for mfr_status

Changes since v5:
 - enclose gpio code in #ifdef GPIOLIB
 - don't initialize buffers for mfr_status; set last char to 0 instead
 - cap the size argument to bin2hex

Changes since v4:
 - max-sized buffers for smbus transfers
 - used bin2hex instead of my own code

Changes since v3:
 - remove setting of gpio_chip->owner
 - format the mfr_status data
 - switch to #ifdef rather than #if IS_ENABLED for debugfs

Changes since v2:
 - split the gpio registration into it's own function

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple


Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 350 +-
 1 file changed, 349 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-16 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 212 ++
 1 file changed, 212 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..ef2c5bf 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +37,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +59,17 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+#ifdef CONFIG_GPIOLIB
+   struct gpio_chip gpio;
+#endif
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +169,196 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+#ifdef CONFIG_GPIOLIB
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+ 

Re: [PATCH v6 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-16 Thread Eddie James



On 03/16/2018 02:59 PM, Guenter Roeck wrote:

On Fri, Mar 16, 2018 at 02:25:59PM -0500, Eddie James wrote:

From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ucd9000.c | 138 +-
  1 file changed, 137 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index ef2c5bf..88c98fb 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
+#include 

  #include 
  #include 
  #include 
@@ -37,6 +38,7 @@
  #define UCD9000_NUM_PAGES 0xd6
  #define UCD9000_FAN_CONFIG_INDEX  0xe7
  #define UCD9000_FAN_CONFIG0xe8
+#define UCD9000_MFR_STATUS 0xf3
  #define UCD9000_GPIO_SELECT   0xfa
  #define UCD9000_GPIO_CONFIG   0xfb
  #define UCD9000_DEVICE_ID 0xfd
@@ -64,15 +66,24 @@
  #define UCD901XX_NUM_GPIOS26
  #define UCD90910_NUM_GPIOS26
  
+#define UCD9000_DEBUGFS_NAME_LEN	24

+#define UCD9000_GPI_COUNT  8
+
  struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
  #ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
  #endif
+   struct dentry *debugfs;
  };
  #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
  
+struct ucd9000_debugfs_entry {

+   struct i2c_client *client;
+   u8 index;
+};
+
  static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
  {
int fan_config = 0;
@@ -359,6 +370,122 @@ static void ucd9000_probe_gpio(struct i2c_client *client,
  }
  #endif /* CONFIG_GPIOLIB */
  
+#ifdef CONFIG_DEBUG_FS

+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2];
+   char *res;
+   int rc;
+
+   rc = ucd9000_get_mfr_status(client, buffer);
+   if (rc < 0)
+   return rc;
+
+   res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX));
+   *res++ = '\n';
+   *res++ = 0;
+

Unless I am missing something, this now returns the terminating '\0'
to the user. Is this really what you want ?


Yes, that was my intention. I thought that was normal. It worked fine 
when I cat'ed the file. Up to you, I can change it if you like.


Thanks,
Eddie



I see other code adding the terminator, but it doesn't usually return it
to the user.

Guenter


+   return simple_read_from_buffer(buf, count, ppos, str, res - str);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report

[PATCH v6 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-16 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 212 ++
 1 file changed, 212 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..ef2c5bf 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +37,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +59,17 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+#ifdef CONFIG_GPIOLIB
+   struct gpio_chip gpio;
+#endif
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +169,196 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+#ifdef CONFIG_GPIOLIB
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+ 

[PATCH v6 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-16 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v5:
 - enclose gpio code in #ifdef GPIOLIB
 - don't initialize buffers for mfr_status; set last char to 0 instead
 - cap the size argument to bin2hex

Changes since v4:
 - max-sized buffers for smbus transfers
 - used bin2hex instead of my own code

Changes since v3:
 - remove setting of gpio_chip->owner
 - format the mfr_status data
 - switch to #ifdef rather than #if IS_ENABLED for debugfs

Changes since v2:
 - split the gpio registration into it's own function

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 350 +-
 1 file changed, 349 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v6 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-16 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 138 +-
 1 file changed, 137 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index ef2c5bf..88c98fb 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -37,6 +38,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -64,15 +66,24 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
 #ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
 #endif
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -359,6 +370,122 @@ static void ucd9000_probe_gpio(struct i2c_client *client,
 }
 #endif /* CONFIG_GPIOLIB */
 
+#ifdef CONFIG_DEBUG_FS
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2];
+   char *res;
+   int rc;
+
+   rc = ucd9000_get_mfr_status(client, buffer);
+   if (rc < 0)
+   return rc;
+
+   res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX));
+   *res++ = '\n';
+   *res++ = 0;
+
+   return simple_read_from_buffer(buf, count, ppos, str, res - str);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report GPI faults in their MFR_STATUS register, so only
+* create the GPI fault debugfs attributes for those chips.
+*/
+   if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+   mid->driver_data == ucd90910) {
+   entries = devm_kzalloc(&client->dev,
+  sizeof(*entries) * UCD9000_GPI_COUNT,
+  GFP_KERNEL);
+   if (!entries)
+   ret

Re: [PATCH v5 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-16 Thread Eddie James



On 03/16/2018 08:40 AM, Guenter Roeck wrote:

On 03/15/2018 03:21 PM, Eddie James wrote:

From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.



Sorry for not noticing earlier. The 0day reports should be addressed 
by selecting GPIOLIB

in the Kconfig entry.


Getting kbuild recursive dependencies when I select GPIOLIB for ucd9000 :(

May have to do "depends on" instead and #ifdef GPIOLIB in ucd9000, 
unless you have another recommendation?


Thanks
Eddie



Guenter


Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ucd9000.c | 201 
++

  1 file changed, 201 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c 
b/drivers/hwmon/pmbus/ucd9000.c

index b74dbec..a34ffc4 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
  #include 
  #include 
  #include 
+#include 
  #include "pmbus.h"
    enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, 
ucd90910 };

@@ -35,8 +36,18 @@
  #define UCD9000_NUM_PAGES    0xd6
  #define UCD9000_FAN_CONFIG_INDEX    0xe7
  #define UCD9000_FAN_CONFIG    0xe8
+#define UCD9000_GPIO_SELECT    0xfa
+#define UCD9000_GPIO_CONFIG    0xfb
  #define UCD9000_DEVICE_ID    0xfd
  +/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE    BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE    BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE    BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS    BIT(3)
+#define UCD9000_GPIO_INPUT    0
+#define UCD9000_GPIO_OUTPUT    1
+
  #define UCD9000_MON_TYPE(x)    (((x) >> 5) & 0x07)
  #define UCD9000_MON_PAGE(x)    ((x) & 0x0f)
  @@ -47,9 +58,15 @@
    #define UCD9000_NUM_FAN    4
  +#define UCD9000_GPIO_NAME_LEN    16
+#define UCD9090_NUM_GPIOS    23
+#define UCD901XX_NUM_GPIOS    26
+#define UCD90910_NUM_GPIOS    26
+
  struct ucd9000_data {
  u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
  struct pmbus_driver_info info;
+    struct gpio_chip gpio;
  };
  #define to_ucd9000_data(_info) container_of(_info, struct 
ucd9000_data, info)
  @@ -149,6 +166,188 @@ static int ucd9000_read_byte_data(struct 
i2c_client *client, int page, int reg)

  };
  MODULE_DEVICE_TABLE(of, ucd9000_of_match);
  +static int ucd9000_gpio_read_config(struct i2c_client *client,
+    unsigned int offset)
+{
+    int ret;
+
+    /* No page set required */
+    ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, 
offset);

+    if (ret < 0)
+    return ret;
+
+    return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+    struct i2c_client *client  = gpiochip_get_data(gc);
+    int ret;
+
+    ret = ucd9000_gpio_read_config(client, offset);
+    if (ret < 0)
+    return ret;
+
+    return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+    struct i2c_client *client = gpiochip_get_data(gc);
+    int ret;
+
+    ret = ucd9000_gpio_read_config(client, offset);
+    if (ret < 0) {
+    dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+    offset, ret);
+    return;
+    }
+
+    if (value) {
+    if (ret & UCD9000_GPIO_CONFIG_STATUS)
+    return;
+
+    ret |= UCD9000_GPIO_CONFIG_STATUS;
+    } else {
+    if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+    return;
+
+    ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+    }
+
+    ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+    /* Page set not required */
+    ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+    if (ret < 0) {
+    dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+    offset, ret);
+    return;
+    }
+
+    ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+    ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+    if (ret < 0)
+    dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+    offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+  unsigned int offset)
+{
+    struct i2c_client *client = gpiochip_get_data(gc);
+    int ret;
+
+    ret = ucd9000_gpio_read_config(client, offset);
+    if (ret < 0)
+    return ret;
+
+    return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+  unsigned int offset, bool direction_out,
+  int requested_out)
+{
+    struct i2c_client *client = gpiochip_get_data(gc);
+    int ret, config, out_val;
+
+    ret = ucd9000_gpio_read_config(client, offset);
+    if (ret < 0)
+ 

[PATCH v5 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-15 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 137 +-
 1 file changed, 136 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index a34ffc4..c1a1560 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -306,6 +317,121 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#ifdef CONFIG_DEBUG_FS
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX];
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[I2C_SMBUS_BLOCK_MAX] = { 0 };
+   char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2] = { 0 };
+   char *res;
+   int rc;
+
+   rc = ucd9000_get_mfr_status(client, buffer);
+   if (rc < 0)
+   return rc;
+
+   res = bin2hex(str, buffer, rc);
+   *res++ = '\n';
+
+   return simple_read_from_buffer(buf, count, ppos, str, (res - str) + 1);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report GPI faults in their MFR_STATUS register, so only
+* create the GPI fault debugfs attributes for those chips.
+*/
+   if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+   mid->driver_data == ucd90910) {
+   entries = devm_kzalloc(&client->dev,
+  sizeof(*entries) * UCD9000_GPI_COUNT,
+  GFP_KERNEL);
+   if (!entries)
+   return -ENOMEM;
+
+   for (i =

[PATCH v5 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-15 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 201 ++
 1 file changed, 201 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..a34ffc4 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,188 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   if (direction_out) {
+   out_val = requested_out ? UCD9

[PATCH v5 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-15 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v4:
 - max-sized buffers for smbus transfers
 - used bin2hex instead of my own code

Changes since v3:
 - remove setting of gpio_chip->owner
 - format the mfr_status data
 - switch to #ifdef rather than #if IS_ENABLED for debugfs

Changes since v2:
 - split the gpio registration into it's own function

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 338 +-
 1 file changed, 337 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v4 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-14 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v3:
 - remove setting of gpio_chip->owner
 - format the mfr_status data
 - switch to #ifdef rather than #if IS_ENABLED for debugfs

Changes since v2:
 - split the gpio registration into it's own function

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 354 +-
 1 file changed, 353 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v4 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-14 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 153 +-
 1 file changed, 152 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index a34ffc4..f03c404 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -306,6 +317,137 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#ifdef CONFIG_DEBUG_FS
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[5] = { 0 };   /* Need max 5 bytes for any ucd9000 chip. */
+   char str[12] = { 0 };   /* Two chars per byte plus \n and \0. */
+   int i, num_bytes, num_chars = 0, rc;
+
+   num_bytes = ucd9000_get_mfr_status(client, buffer);
+   if (num_bytes < 0)
+   return num_bytes;
+
+   for (i = 0; i < num_bytes; ++i) {
+   rc = snprintf(&str[num_chars], (sizeof(str) - 1) - num_chars,
+ "%02x", buffer[i]);
+   if (rc <= 0)
+   break;
+
+   num_chars += rc;
+   }
+
+   str[num_chars] = '\n';
+
+   return simple_read_from_buffer(buf, count, ppos, str, num_chars + 2);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->n

[PATCH v4 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-14 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 201 ++
 1 file changed, 201 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..a34ffc4 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,188 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   if (direction_out) {
+   out_val = requested_out ? UCD9

Re: [PATCH v2 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-14 Thread Eddie James



On 03/14/2018 02:19 PM, Guenter Roeck wrote:

On Tue, Mar 13, 2018 at 03:59:09PM -0500, Eddie James wrote:

From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ucd9000.c | 140 +-
  1 file changed, 139 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 023fb9e..b073d8e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
+#include 

  #include 
  #include 
  #include 
@@ -36,6 +37,7 @@
  #define UCD9000_NUM_PAGES 0xd6
  #define UCD9000_FAN_CONFIG_INDEX  0xe7
  #define UCD9000_FAN_CONFIG0xe8
+#define UCD9000_MFR_STATUS 0xf3
  #define UCD9000_GPIO_SELECT   0xfa
  #define UCD9000_GPIO_CONFIG   0xfb
  #define UCD9000_DEVICE_ID 0xfd
@@ -63,13 +65,22 @@
  #define UCD901XX_NUM_GPIOS26
  #define UCD90910_NUM_GPIOS26
  
+#define UCD9000_DEBUGFS_NAME_LEN	24

+#define UCD9000_GPI_COUNT  8
+
  struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
  };
  #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
  
+struct ucd9000_debugfs_entry {

+   struct i2c_client *client;
+   u8 index;
+};
+
  static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
  {
int fan_config = 0;
@@ -306,6 +317,124 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
  }
  
+#if IS_ENABLED(CONFIG_DEBUG_FS)

DEBUG_FS is bool, so #ifdef CONFIG_DEBUG_FS is fine here.


Ok.




+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[5] = { 0 };   /* Need max 5 bytes for any ucd9000 chip. */
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   return simple_read_from_buffer(buf, count, ppos, buffer, ret);

Doesn't this report the raw binary data to userspace ? The output
should be human-readable.


Yes, sorry I thought that was your suggestion when you mentioned hexdump 
earlier... Will fix.


Thanks,
Eddie




+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+  

Re: [PATCH v2 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-14 Thread Eddie James



On 03/14/2018 01:55 PM, Guenter Roeck wrote:

On Tue, Mar 13, 2018 at 11:13:04PM +0200, Andy Shevchenko wrote:

On Tue, Mar 13, 2018 at 10:59 PM, Eddie James
 wrote:

From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.
+   /*
+* Note:
+*
+* Pinmux support has not been added to the new gpio_chip.
+* This support should be added when possible given the mux
+* behavior of these IO devices.
+*/
+   data->gpio.label = (const char *)&client->name;

Hmm... Why do you need this casting?


+   data->gpio.get_direction = ucd9000_gpio_get_direction;
+   data->gpio.direction_input = ucd9000_gpio_direction_input;
+   data->gpio.direction_output = ucd9000_gpio_direction_output;
+   data->gpio.get = ucd9000_gpio_get;
+   data->gpio.set = ucd9000_gpio_set;
+   data->gpio.can_sleep = 1;

Isn't it type of boolean?


You are right.


+   data->gpio.base = -1;
+   data->gpio.parent = &client->dev;
+   data->gpio.owner = THIS_MODULE;
+   data->gpio.of_node = client->dev.of_node;

I think GPIO core does this for you.


Same here. I checked and found that it also sets the owner, so maybe this can be
dropped as well.


Ah true. Especially since there is a TODO: remove chip->owner in the core.




+   if (data->gpio.ngpio) {

Hmm...

I would rather reorganize the above part to a separate helper, like

static int ..._probe_gpio()
{
...
   switch () {
default:
  return 0; /* GPIO part is optional */
   }
   return 0;
}

ret = _probe_gpio();
if (ret)
  dev_warn();


I am neutral to positiva on that.
Might as well add the call to devm_gpiochip_add_data()
into that function as well.


Sure. I can do a v4.

Thanks,
Eddie




+   ret = devm_gpiochip_add_data(&client->dev, &data->gpio,
+client);
+   if (ret)
+   dev_warn(&client->dev, "Could not add gpiochip: %d\n",
+ret);
+   }
+
 return pmbus_do_probe(client, mid, info);
  }

--
1.8.3.1




--
With Best Regards,
Andy Shevchenko


--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v3 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-14 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 200 ++
 1 file changed, 200 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..321b837 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,185 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   if (direction_out) {
+   out_val = requested_out ? UCD9

[PATCH v3 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-14 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 140 +-
 1 file changed, 139 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 321b837..4158c23 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -306,6 +317,124 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[5] = { 0 };   /* Need max 5 bytes for any ucd9000 chip. */
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   return simple_read_from_buffer(buf, count, ppos, buffer, ret);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report GPI faults in their MFR_STATUS register, so only
+* create the GPI fault debugfs attributes for those chips.
+*/
+   if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+   mid->driver_data == ucd90910

[PATCH v3 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-14 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v2:
 - split the gpio registration into it's own function

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 340 +-
 1 file changed, 339 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-13 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 201 ++
 1 file changed, 201 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..023fb9e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,146 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0)
+   return ret;
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret, config, out_val;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0)
+   return ret;
+
+   if (direction_out) {
+   out_val = requested_out ? UCD9

[PATCH v2 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-13 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Changes since v1:
 - dropped dev_err messages
 - made gpio chip registration conditional on having gpio pins
 - made mfr_status debugfs attribute more simple

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 341 +-
 1 file changed, 340 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-13 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 140 +-
 1 file changed, 139 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 023fb9e..b073d8e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -306,6 +317,124 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+  char __user *buf, size_t count,
+  loff_t *ppos)
+{
+   struct i2c_client *client = file->private_data;
+   u8 buffer[5] = { 0 };   /* Need max 5 bytes for any ucd9000 chip. */
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0)
+   return ret;
+
+   return simple_read_from_buffer(buf, count, ppos, buffer, ret);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+   .llseek = noop_llseek,
+   .read = ucd9000_debugfs_read_mfr_status,
+   .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_entry *entries;
+   int i;
+   char name[UCD9000_DEBUGFS_NAME_LEN];
+
+   debugfs = pmbus_get_debugfs_dir(client);
+   if (!debugfs)
+   return -ENOENT;
+
+   data->debugfs = debugfs_create_dir(client->name, debugfs);
+   if (!data->debugfs)
+   return -ENOENT;
+
+   /*
+* Of the chips this driver supports, only the UCD9090, UCD90160,
+* and UCD90910 report GPI faults in their MFR_STATUS register, so only
+* create the GPI fault debugfs attributes for those chips.
+*/
+   if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+   mid->driver_data == ucd90910

Re: [PATCH 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-13 Thread Eddie James



On 03/13/2018 02:29 PM, Guenter Roeck wrote:

On Tue, Mar 13, 2018 at 02:01:51PM -0500, Eddie James wrote:


On 03/10/2018 10:50 AM, Guenter Roeck wrote:

On 03/09/2018 11:19 AM, Eddie James wrote:

From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
   drivers/hwmon/pmbus/ucd9000.c | 172
+-
   1 file changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c
b/drivers/hwmon/pmbus/ucd9000.c
index e3a507f..297da0e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
    * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    */
   +#include 
   #include 
   #include 
   #include 
@@ -36,6 +37,7 @@
   #define UCD9000_NUM_PAGES    0xd6
   #define UCD9000_FAN_CONFIG_INDEX    0xe7
   #define UCD9000_FAN_CONFIG    0xe8
+#define UCD9000_MFR_STATUS    0xf3
   #define UCD9000_GPIO_SELECT    0xfa
   #define UCD9000_GPIO_CONFIG    0xfb
   #define UCD9000_DEVICE_ID    0xfd
@@ -63,13 +65,22 @@
   #define UCD901XX_NUM_GPIOS    26
   #define UCD90910_NUM_GPIOS    26
   +#define UCD9000_DEBUGFS_NAME_LEN    24
+#define UCD9000_GPI_COUNT    8
+
   struct ucd9000_data {
   u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
   struct pmbus_driver_info info;
   struct gpio_chip gpio;
+    struct dentry *debugfs;
   };
   #define to_ucd9000_data(_info) container_of(_info, struct
ucd9000_data, info)
   +struct ucd9000_debugfs_entry {
+    struct i2c_client *client;
+    u8 index;
+};
+
   static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
   {
   int fan_config = 0;
@@ -328,6 +339,156 @@ static int ucd9000_gpio_direction_output(struct
gpio_chip *gc,
     val);
   }
   +#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8
*buffer)
+{
+    int ret = pmbus_set_page(client, 0);
+
+    if (ret < 0)
+    return ret;
+
+    /*
+ * With the ucd90120 and ucd90124 devices, this command
[MFR_STATUS]
+ * is 2 bytes long (bits 0-15).  With the ucd90240 this command is
5
+ * bytes long.  With all other devices, it is 4 bytes long.
+ */
+    return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS,
buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+    struct ucd9000_debugfs_entry *entry = data;
+    struct i2c_client *client = entry->client;
+    u8 buffer[4];
+    int ret;
+
+    /*
+ * This attribute is only created for devices that return 4 bytes
for
+ * status_mfr, so it's safe to call with 4-byte buffer.
+ */
+    ret = ucd9000_get_mfr_status(client, buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    /*
+ * Attribute only created for devices with gpi fault bits at bits
+ * 16-23, which is the second byte of the response.
+ */
+    *val = !!(buffer[1] & BIT(entry->index));
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static int ucd9000_debugfs_show_mfr_status_word2(void *data, u64 *val)
+{
+    struct i2c_client *client = data;
+    __be16 buffer;
+    int ret;
+
+    ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    *val = be16_to_cpu(buffer);
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word2,
+ ucd9000_debugfs_show_mfr_status_word2, NULL,
+ "%04llx\n");
+
+static int ucd9000_debugfs_show_mfr_status_word4(void *data, u64 *val)
+{
+    struct i2c_client *client = data;
+    __be32 buffer;
+    int ret;
+
+    ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+    if (ret < 0) {
+    dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+    ret);
+
+    return ret;
+    }
+
+    *val = be32_to_cpu(buffer);
+
+    return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word4,
+ ucd9000_debugfs_show_mfr_status_word4, NULL,
+ "%08llx\n");
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+    const struct i2c_device_id *mid,
+    struct ucd9000_data *data)
+{
+    struct dentry *debugfs;
+    struct ucd9000_debugfs_entry *entries;
+    int i;
+    char name[UCD9000_DEBUGFS_NAME_LEN];
+
+    debugfs = pmbus_get_debugfs_dir(client);
+

[PATCH 1/2] hwmon: (ucd9000) Add gpio chip interface

2018-03-09 Thread Eddie James
From: Christopher Bostic 

Add a struct gpio_chip and define some methods so that this device's
I/O can be accessed via /sys/class/gpio.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 220 ++
 1 file changed, 220 insertions(+)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index b74dbec..e3a507f 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 #include "pmbus.h"
 
 enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
@@ -35,8 +36,18 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_GPIO_SELECT0xfa
+#define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
 
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE  BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT1
+
 #define UCD9000_MON_TYPE(x)(((x) >> 5) & 0x07)
 #define UCD9000_MON_PAGE(x)((x) & 0x0f)
 
@@ -47,9 +58,15 @@
 
 #define UCD9000_NUM_FAN4
 
+#define UCD9000_GPIO_NAME_LEN  16
+#define UCD9090_NUM_GPIOS  23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90910_NUM_GPIOS 26
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
+   struct gpio_chip gpio;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
@@ -149,6 +166,168 @@ static int ucd9000_read_byte_data(struct i2c_client 
*client, int page, int reg)
 };
 MODULE_DEVICE_TABLE(of, ucd9000_of_match);
 
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+   unsigned int offset)
+{
+   int ret;
+
+   /* No page set required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to select GPIO %d: %d\n", offset,
+   ret);
+
+   return ret;
+   }
+
+   return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+   struct i2c_client *client  = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+
+   return ret;
+   }
+
+   return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+int value)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+
+   return;
+   }
+
+   if (value) {
+   if (ret & UCD9000_GPIO_CONFIG_STATUS)
+   return;
+
+   ret |= UCD9000_GPIO_CONFIG_STATUS;
+   } else {
+   if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+   return;
+
+   ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+   }
+
+   ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+   /* Page set not required */
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+
+   return;
+   }
+
+   ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+   ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+   if (ret < 0)
+   dev_err(&client->dev, "Failed to write GPIO %d config: %d\n",
+   offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+   struct i2c_client *client = gpiochip_get_data(gc);
+   int ret;
+
+   ret = ucd9000_gpio_read_config(client, offset);
+   if (ret < 0) {
+   dev_err(&client->dev, "failed to read GPIO %d config: %d\n",
+   offset, ret);
+
+   return ret;
+   }
+
+   return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(st

[PATCH 2/2] hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

2018-03-09 Thread Eddie James
From: Christopher Bostic 

Expose the gpiN_fault fields of mfr_status as individual debugfs
attributes. This provides a way for users to be easily notified of gpi
faults. Also provide the whole mfr_status register in debugfs.

Signed-off-by: Christopher Bostic 
Signed-off-by: Andrew Jeffery 
Signed-off-by: Eddie James 
---
 drivers/hwmon/pmbus/ucd9000.c | 172 +-
 1 file changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index e3a507f..297da0e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -36,6 +37,7 @@
 #define UCD9000_NUM_PAGES  0xd6
 #define UCD9000_FAN_CONFIG_INDEX   0xe7
 #define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
 #define UCD9000_GPIO_SELECT0xfa
 #define UCD9000_GPIO_CONFIG0xfb
 #define UCD9000_DEVICE_ID  0xfd
@@ -63,13 +65,22 @@
 #define UCD901XX_NUM_GPIOS 26
 #define UCD90910_NUM_GPIOS 26
 
+#define UCD9000_DEBUGFS_NAME_LEN   24
+#define UCD9000_GPI_COUNT  8
+
 struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
struct gpio_chip gpio;
+   struct dentry *debugfs;
 };
 #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
 
+struct ucd9000_debugfs_entry {
+   struct i2c_client *client;
+   u8 index;
+};
+
 static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
 {
int fan_config = 0;
@@ -328,6 +339,156 @@ static int ucd9000_gpio_direction_output(struct gpio_chip 
*gc,
  val);
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+   int ret = pmbus_set_page(client, 0);
+
+   if (ret < 0)
+   return ret;
+
+   /*
+* With the ucd90120 and ucd90124 devices, this command [MFR_STATUS]
+* is 2 bytes long (bits 0-15).  With the ucd90240 this command is 5
+* bytes long.  With all other devices, it is 4 bytes long.
+*/
+   return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+   struct ucd9000_debugfs_entry *entry = data;
+   struct i2c_client *client = entry->client;
+   u8 buffer[4];
+   int ret;
+
+   /*
+* This attribute is only created for devices that return 4 bytes for
+* status_mfr, so it's safe to call with 4-byte buffer.
+*/
+   ret = ucd9000_get_mfr_status(client, buffer);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+   ret);
+
+   return ret;
+   }
+
+   /*
+* Attribute only created for devices with gpi fault bits at bits
+* 16-23, which is the second byte of the response.
+*/
+   *val = !!(buffer[1] & BIT(entry->index));
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static int ucd9000_debugfs_show_mfr_status_word2(void *data, u64 *val)
+{
+   struct i2c_client *client = data;
+   __be16 buffer;
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+   ret);
+
+   return ret;
+   }
+
+   *val = be16_to_cpu(buffer);
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word2,
+ucd9000_debugfs_show_mfr_status_word2, NULL,
+"%04llx\n");
+
+static int ucd9000_debugfs_show_mfr_status_word4(void *data, u64 *val)
+{
+   struct i2c_client *client = data;
+   __be32 buffer;
+   int ret;
+
+   ret = ucd9000_get_mfr_status(client, (u8 *)&buffer);
+   if (ret < 0) {
+   dev_err(&client->dev, "Failed to read mfr status. rc:%d\n",
+   ret);
+
+   return ret;
+   }
+
+   *val = be32_to_cpu(buffer);
+
+   return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_word4,
+ucd9000_debugfs_show_mfr_status_word4, NULL,
+"%08llx\n");
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+   const struct i2c_device_id *mid,
+   struct ucd9000_data *data)
+{
+   struct dentry *debugfs;
+   struct ucd9000_debugfs_e

[PATCH 0/2] hwmon: (ucd9000) Add gpio and debugfs interfaces

2018-03-09 Thread Eddie James
The ucd9000 series chips have gpio pins. Add a gpio chip interface to the ucd
device so that users can query and set the state of the gpio pins.

Add a debugfs interface using the existing pmbus debugfs directory to provide
MFR_STATUS and the status of the gpi faults to users.

Christopher Bostic (2):
  hwmon: (ucd9000) Add gpio chip interface
  hwmon: (ucd9000) Add debugfs attributes to provide mfr_status

 drivers/hwmon/pmbus/ucd9000.c | 392 +-
 1 file changed, 391 insertions(+), 1 deletion(-)

-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] hwmon: pmbus: ibm-cffps depends on LEDS_CLASS

2018-01-15 Thread Eddie James


On Fri, Jan 12, 2018 at 06:19:00PM +0100, Guenter Roeck wrote:

On Fri, Jan 12, 2018 at 04:49:00PM +0100, Arnd Bergmann wrote:
> Building without CONFIG_LEDS_CLASS causes a link failure:
>
> drivers/hwmon/pmbus/ibm-cffps.o: In function `ibm_cffps_probe':
> ibm-cffps.c:(.text+0x4f4): undefined reference to 
`devm_of_led_classdev_register'

>
> This adds the required dependency.
>
> Fixes: f69316d62c70 ("hwmon: (pmbus) Add IBM Common Form Factor 
(CFF) power supply driver")

> Signed-off-by: Arnd Bergmann 

I wanted to let Edward decide if he wants the new dependency or 
conditional code
in the driver. Not having heard from him, I'll take your patch instead 
for now.


Thanks, yes this is a good solution for this driver. Didn't think about 
that during testing... Thanks Arnd.


Eddie



Thanks,
Guenter

> ---
>  drivers/hwmon/pmbus/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index 08479006c7f9..6e4298e99222 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -39,6 +39,7 @@ config SENSORS_ADM1275
>
>  config SENSORS_IBM_CFFPS
>   tristate "IBM Common Form Factor Power Supply"
> + depends on LEDS_CLASS
>   help
>    If you say yes here you get hardware monitoring support for the IBM
>    Common Form Factor power supply.
> --
> 2.9.0
>



--
To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v3] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-11 Thread Eddie James
This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---

 Changes since v2:
 - Use fixed size buffer for led name.

 Changes since v1:
 - move led registration into a separate function.
 - improve comments.

 drivers/hwmon/pmbus/ibm-cffps.c | 96 +
 1 file changed, 88 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..93d9a9e 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -25,6 +26,7 @@
 #define CFFPS_CCIN_CMD 0xBD
 #define CFFPS_FW_CMD_START 0xFA
 #define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
 #define CFFPS_INPUT_HISTORY_SIZE   100
@@ -39,6 +41,11 @@
 #define CFFPS_MFR_VAUX_FAULT   BIT(6)
 #define CFFPS_MFR_CURRENT_SHARE_WARNINGBIT(7)
 
+#define CFFPS_LED_BLINKBIT(0)
+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
 enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,10 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
 
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
+
+   char led_name[32];
+   u8 led_state;
+   struct led_classdev led;
 };
 
 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
@@ -258,6 +269,69 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
 }
 
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
+static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
+{
+   int rc;
+   struct i2c_client *client = psu->client;
+   struct device *dev = &client->dev;
+
+   snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
+client->addr);
+   psu->led.name = psu->led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(dev, &psu->led);
+   if (rc)
+   dev_warn(dev, "failed to register led class: %d\n", rc);
+}
+
 static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -286,6 +360,20 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
 
+   /*
+* Don't fail the probe if there isn't enough memory for leds and
+* debugfs.
+*/
+   psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
+   if (!psu)
+   return 0;
+
+   psu->client = client;
+   mutex_init(&psu->input_history.update_lock);
+   psu->input_history.last_update = jiffies - HZ;
+
+   ibm_cffps_create_led_class(psu);
+
/* Don't fail the probe if we can't create debugfs */
debugfs = pmbus_g

Re: [PATCH v2] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-11 Thread Eddie James



On 01/10/2018 12:16 PM, Guenter Roeck wrote:

On Wed, Jan 10, 2018 at 11:29:43AM -0600, Eddie James wrote:

This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---

  Changes since v1:
  - move led registration into a separate function.
  - improve comments.

  drivers/hwmon/pmbus/ibm-cffps.c | 99 +
  1 file changed, 91 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..cd9f685 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
  #include 
  #include 
  #include 
+#include 
  #include 
  #include 
  #include 
@@ -25,6 +26,7 @@
  #define CFFPS_CCIN_CMD0xBD
  #define CFFPS_FW_CMD_START0xFA
  #define CFFPS_FW_NUM_BYTES4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
  
  #define CFFPS_INPUT_HISTORY_CMD			0xD6

  #define CFFPS_INPUT_HISTORY_SIZE  100
@@ -39,6 +41,11 @@
  #define CFFPS_MFR_VAUX_FAULT  BIT(6)
  #define CFFPS_MFR_CURRENT_SHARE_WARNING   BIT(7)
  
+#define CFFPS_LED_BLINKBIT(0)

+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
  enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
  
  	int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];

+
+   u8 led_state;
+   struct led_classdev led;
  };
  
  #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])

@@ -258,6 +268,69 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
  }
  
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,

+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
+static void ibm_cffps_create_led_class(size_t name_size, struct ibm_cffps *psu)
+{
+   int rc;
+   struct i2c_client *client = psu->client;
+   struct device *dev = &client->dev;
+   char *led_name = (void *)(&psu[1]);
+

I really dislike this hack. Why not allocate a sufficient
fixed length, say, 32 bytes, and just use it ? If you want
to be really wasteful, use 48 or 64 bytes; that is still better
than this code.


That's fair, I found this trick in some sony hid driver, but fixed 
length is much cleaner.





+   snprintf(led_name, name_size, "%s-%x", client->name, client->addr);

%02x, maybe ? Your call, though.

Thanks,
Guenter


+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(dev, &psu->led);
+   if (rc)
+   dev_warn(dev, "failed to register led class: %d\n", rc);
+}
+
  static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +350,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
  {
int i, rc;
+   size_t name_size;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +360,23 @@ stat

[PATCH v2] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-10 Thread Eddie James
This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---

 Changes since v1:
 - move led registration into a separate function.
 - improve comments.

 drivers/hwmon/pmbus/ibm-cffps.c | 99 +
 1 file changed, 91 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..cd9f685 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -25,6 +26,7 @@
 #define CFFPS_CCIN_CMD 0xBD
 #define CFFPS_FW_CMD_START 0xFA
 #define CFFPS_FW_NUM_BYTES 4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
 
 #define CFFPS_INPUT_HISTORY_CMD0xD6
 #define CFFPS_INPUT_HISTORY_SIZE   100
@@ -39,6 +41,11 @@
 #define CFFPS_MFR_VAUX_FAULT   BIT(6)
 #define CFFPS_MFR_CURRENT_SHARE_WARNINGBIT(7)
 
+#define CFFPS_LED_BLINKBIT(0)
+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
 enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
 
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
+
+   u8 led_state;
+   struct led_classdev led;
 };
 
 #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
@@ -258,6 +268,69 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
 }
 
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
+static void ibm_cffps_create_led_class(size_t name_size, struct ibm_cffps *psu)
+{
+   int rc;
+   struct i2c_client *client = psu->client;
+   struct device *dev = &client->dev;
+   char *led_name = (void *)(&psu[1]);
+
+   snprintf(led_name, name_size, "%s-%x", client->name, client->addr);
+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(dev, &psu->led);
+   if (rc)
+   dev_warn(dev, "failed to register led class: %d\n", rc);
+}
+
 static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +350,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
 {
int i, rc;
+   size_t name_size;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +360,23 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
 
+   /* client name, '-', 8 chars for addr, and null terminator */
+   name_size = strlen(client->name) + 10;
+
+   /*
+* Don't fail the probe if there isn't enough memory for leds and
+* debugfs.
+*/
+   psu = devm_kzalloc(&client->dev, sizeof(*psu) + name_size, GFP_KER

Re: [PATCH] hwmon (pmbus): cffps: Add led class device for power supply fault led

2018-01-10 Thread Eddie James



On 01/09/2018 04:50 PM, Guenter Roeck wrote:

On Tue, Jan 09, 2018 at 04:05:24PM -0600, Eddie James wrote:

This power supply device doesn't correctly manage it's own fault led.
Add an led class device and register it so that userspace can manage
power supply fault led as necessary.

Signed-off-by: Eddie James 
---
  drivers/hwmon/pmbus/ibm-cffps.c | 90 +
  1 file changed, 82 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2d6f4f4..1e95dea 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -13,6 +13,7 @@
  #include 
  #include 
  #include 
+#include 
  #include 
  #include 
  #include 
@@ -25,6 +26,7 @@
  #define CFFPS_CCIN_CMD0xBD
  #define CFFPS_FW_CMD_START0xFA
  #define CFFPS_FW_NUM_BYTES4
+#define CFFPS_SYS_CONFIG_CMD   0xDA
  
  #define CFFPS_INPUT_HISTORY_CMD			0xD6

  #define CFFPS_INPUT_HISTORY_SIZE  100
@@ -39,6 +41,11 @@
  #define CFFPS_MFR_VAUX_FAULT  BIT(6)
  #define CFFPS_MFR_CURRENT_SHARE_WARNING   BIT(7)
  
+#define CFFPS_LED_BLINKBIT(0)

+#define CFFPS_LED_ON   BIT(1)
+#define CFFPS_LED_OFF  BIT(2)
+#define CFFPS_BLINK_RATE_MS250
+
  enum {
CFFPS_DEBUGFS_INPUT_HISTORY = 0,
CFFPS_DEBUGFS_FRU,
@@ -63,6 +70,9 @@ struct ibm_cffps {
struct ibm_cffps_input_history input_history;
  
  	int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];

+
+   u8 led_state;
+   struct led_classdev led;
  };
  
  #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])

@@ -258,6 +268,51 @@ static int ibm_cffps_read_word_data(struct i2c_client 
*client, int page,
return rc;
  }
  
+static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,

+enum led_brightness brightness)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   if (brightness == LED_OFF) {
+   psu->led_state = CFFPS_LED_OFF;
+   } else {
+   brightness = LED_FULL;
+   if (psu->led_state != CFFPS_LED_BLINK)
+   psu->led_state = CFFPS_LED_ON;
+   }
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  psu->led_state);
+   if (rc < 0)
+   return;
+
+   led_cdev->brightness = brightness;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+  unsigned long *delay_on,
+  unsigned long *delay_off)
+{
+   int rc;
+   struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+   psu->led_state = CFFPS_LED_BLINK;
+
+   if (led_cdev->brightness == LED_OFF)
+   return 0;
+
+   rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+  CFFPS_LED_BLINK);
+   if (rc < 0)
+   return rc;
+
+   *delay_on = CFFPS_BLINK_RATE_MS;
+   *delay_off = CFFPS_BLINK_RATE_MS;
+
+   return 0;
+}
+
  static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
@@ -277,6 +332,8 @@ static int ibm_cffps_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
  {
int i, rc;
+   char *led_name;
+   size_t name_length;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
@@ -286,6 +343,31 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (rc)
return rc;
  
+	/* client name, '-', 8 chars for addr, and null */

+   name_length = strlen(client->name) + 10;
+
+   /* Don't fail the probe if we can't create led devices */
+   psu = devm_kzalloc(&client->dev, sizeof(*psu) + name_length,
+  GFP_KERNEL);
+   if (!psu)
+   return 0;

... and no debugfs either ?


Well we need that struct allocated for debugfs as well. I'll clarify the 
comment.





+
+   psu->client = client;
+   mutex_init(&psu->input_history.update_lock);
+   psu->input_history.last_update = jiffies - HZ;
+
+   led_name = (void *)(&psu[1]);
+   snprintf(led_name, name_length, "%s-%x", client->name, client->addr);
+   psu->led.name = led_name;
+   psu->led.max_brightness = LED_FULL;
+   psu->led.brightness_set = ibm_cffps_led_brightness_set;
+   psu->led.blink_set = ibm_cffps_led_blink_set;
+
+   rc = devm_led_classdev_register(&client->dev, 

  1   2   3   >