<span style=" font-size:10pt;font-family:sans-serif">Test and verified
working.</span><br><br><span style=" font-size:10pt;font-family:sans-serif">Tested-by: George
Keishing <gkeishin@in.ibm.com></span><br><br><br><span style=" font-size:10pt;color:blue;font-family:sans-serif"><b>Thanks
and Regards,</b></span><br><span style=" font-size:10pt;color:blue;font-family:sans-serif"> 
 George Keishing</span><br><span style=" font-size:10pt;color:blue;font-family:sans-serif"> 
 IBM Systems &Technology Lab, Firmware Development,</span><br><span style=" font-size:10pt;font-family:sans-serif"><i>“</i></span><span style=" font-size:12pt">There isn't enough time in a day to be lazy!!! </span><span style=" font-size:9pt;font-family:sans-serif">.”</span><br><img src=cid:_2_0B0C07D40B0C05680015FAEB65258265 style="border:0px solid;"><br><br><br><br><br><span style=" font-size:9pt;color:#5f5f5f;font-family:sans-serif">From:
       </span><span style=" font-size:9pt;font-family:sans-serif">Andrew
Jeffery <andrew@aj.id.au></span><br><span style=" font-size:9pt;color:#5f5f5f;font-family:sans-serif">To:
       </span><span style=" font-size:9pt;font-family:sans-serif">gkeishin@in.ibm.com</span><br><span style=" font-size:9pt;color:#5f5f5f;font-family:sans-serif">Date:
       </span><span style=" font-size:9pt;font-family:sans-serif">04/04/2018
09:23 AM</span><br><span style=" font-size:9pt;color:#5f5f5f;font-family:sans-serif">Subject:
       </span><span style=" font-size:9pt;font-family:sans-serif">Fwd:
[PATCH linux dev-4.13 2/5] pmbus (max31785): Add support for devicetree
configuration</span><br><hr noshade><br><br><br><tt><span style=" font-size:10pt"><br><br>----- Original message -----<br>From: Andrew Jeffery <andrew@aj.id.au><br>To: joel@jms.id.au<br>Cc: Andrew Jeffery <andrew@aj.id.au>, openbmc@lists.ozlabs.org<br>Subject: [PATCH linux dev-4.13 2/5] pmbus (max31785): Add support for devicetree
configuration<br>Date: Tue,  3 Apr 2018 23:56:52 +0930<br><br>Signed-off-by: Andrew Jeffery <andrew@aj.id.au><br>---<br> drivers/hwmon/pmbus/max31785.c | 318 +++++++++++++++++++++++++++++++++++++++++<br> 1 file changed, 318 insertions(+)<br><br>diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c<br>index bffab449be39..9a7e745b6b31 100644<br>--- a/drivers/hwmon/pmbus/max31785.c<br>+++ b/drivers/hwmon/pmbus/max31785.c<br>@@ -16,13 +16,23 @@<br> <br> enum max31785_regs {<br>                  MFR_REVISION
               
                 =
0x9b,<br>+                
MFR_FAULT_RESPONSE              
  = 0xd9,<br>+                
MFR_TEMP_SENSOR_CONFIG            
    = 0xf0,<br>                  MFR_FAN_CONFIG
               
                 =
0xf1,<br>+                
MFR_FAN_FAULT_LIMIT              
  = 0xf5,<br> };<br> <br> #define MAX31785              
               
               
    0x3030<br> #define MAX31785A              
               
               
    0x3040<br> <br> #define MFR_FAN_CONFIG_DUAL_TACH          
      BIT(12)<br>+#define MFR_FAN_CONFIG_TSFO          
               
       BIT(9)<br>+#define MFR_FAN_CONFIG_TACHO          
               
       BIT(8)<br>+#define MFR_FAN_CONFIG_HEALTH          
               
       BIT(4)<br>+#define MFR_FAN_CONFIG_ROTOR_HI_LO        
        BIT(3)<br>+#define MFR_FAN_CONFIG_ROTOR          
               
       BIT(2)<br>+<br>+#define MFR_FAULT_RESPONSE_MONITOR        
        BIT(0)<br> <br> #define MAX31785_NR_PAGES            
               
     23<br> #define MAX31785_NR_FAN_PAGES          
               
       6<br>@@ -239,6 +249,271 @@ static int max31785_write_word_data(struct i2c_client
*client, int page,<br>                  return
-ENODATA;<br> }<br> <br>+/*<br>+ * Returns negative error codes if an unrecoverable problem is detected,
0 if a<br>+ * recoverable problem is detected, or a positive value on success.<br>+ */<br>+static int max31785_of_fan_config(struct i2c_client *client,<br>+                
                 
               
                 
 struct pmbus_driver_info *info,<br>+                
                 
               
                 
 struct device_node *child)<br>+{<br>+                
int mfr_cfg = 0, mfr_fault_resp = 0, pb_cfg;<br>+                
struct device *dev = &client->dev;<br>+                
char *lock_polarity = NULL;<br>+                
const char *sval;<br>+                
u32 page;<br>+                
u32 uval;<br>+                
int ret;<br>+<br>+                
if (!of_device_is_compatible(child, "pmbus-fan"))<br>+                
                 return
0;<br>+<br>+                
ret = of_property_read_u32(child, "reg", &page);<br>+                
if (ret < 0) {<br>+                
                 dev_err(&client->dev,
"Missing valid reg property\n");<br>+                
                 return
ret;<br>+                
}<br>+<br>+                
if (!(info->func[page] & PMBUS_HAVE_FAN12)) {<br>+                
                 dev_err(dev,
"Page %d does not have fan capabilities\n", page);<br>+                
                 return
-ENXIO;<br>+                
}<br>+<br>+                
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
pb_cfg = i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);<br>+                
if (pb_cfg < 0)<br>+                
                 return
pb_cfg;<br>+<br>+                
if (of_property_read_bool(child->parent, "use-stored-presence"))
{<br>+                
                 if
(!(pb_cfg & PB_FAN_1_INSTALLED))<br>+                
                 
               
dev_info(dev, "Fan %d is configured but not installed\n",<br>+                
                 
               
                 
page);<br>+                
} else {<br>+                
                 pb_cfg
|= PB_FAN_1_INSTALLED;<br>+                
}<br>+<br>+                
ret = of_property_read_string(child, "maxim,fan-rotor-input",
&sval);<br>+                
if (ret < 0) {<br>+                
                 dev_err(dev,
"Missing valid maxim,fan-rotor-input property for fan %d\n",<br>+                
                 
               
                 page);<br>+                
                 return
ret;<br>+                
}<br>+<br>+                
if (strcmp("tach", sval) && strcmp("lock",
sval)) {<br>+                
                 dev_err(dev,
"maxim,fan-rotor-input has invalid value for fan %d: %s\n",<br>+                
                 
               
                 page,
sval);<br>+                
                 return
-EINVAL;<br>+                
} else if (!strcmp("lock", sval)) {<br>+                
                 mfr_cfg
|= MFR_FAN_CONFIG_ROTOR;<br>+<br>+                
                 ret
= i2c_smbus_write_word_data(client, MFR_FAN_FAULT_LIMIT, 1);<br>+                
                 if
(ret < 0)<br>+                
                 
               
return ret;<br>+<br>+                
                 ret
= of_property_read_string(child, "maxim,fan-lock-polarity",<br>+                
                 
               
                 
               
      &sval);<br>+                
                 if
(ret < 0) {<br>+                
                 
               
dev_err(dev, "Missing valid maxim,fan-lock-polarity property for fan
%d\n",<br>+                
                 
               
                 
               
page);<br>+                
                 
               
return ret;<br>+                
                 }<br>+<br>+                
                 if
(strcmp("low", sval) && strcmp("high", sval))
{<br>+                
                 
               
dev_err(dev, "maxim,fan-lock-polarity has invalid value for fan %d:
%s\n",<br>+                
                 
               
                 
               
page, lock_polarity);<br>+                
                 
               
return -EINVAL;<br>+                
                 }
else if (!strcmp("high", sval))<br>+                
                 
               
mfr_cfg |= MFR_FAN_CONFIG_ROTOR_HI_LO;<br>+                
}<br>+<br>+                
if (!of_property_read_string(child, "fan-mode", &sval)) {<br>+                
                 if
(!strcmp("rpm", sval))<br>+                
                 
               
pb_cfg |= PB_FAN_1_RPM;<br>+                
                 else
if (!strcmp("pwm", sval))<br>+                
                 
               
pb_cfg &= ~PB_FAN_1_RPM;<br>+                
                 else
{<br>+                
                 
               
dev_err(dev, "fan-mode has invalid value for fan %d: %s\n",<br>+                
                 
               
                 
               
page, sval);<br>+                
                 
               
return -EINVAL;<br>+                
                 }<br>+                
}<br>+<br>+                
ret = of_property_read_u32(child, "tach-pulses", &uval);<br>+                
if (ret < 0) {<br>+                
                 pb_cfg
&= ~PB_FAN_1_PULSE_MASK;<br>+                
} else if (uval && (uval - 1) < 4) {<br>+                
                 pb_cfg
= ((pb_cfg & ~PB_FAN_1_PULSE_MASK) | ((uval - 1) << 4));<br>+                
} else {<br>+                
                 dev_err(dev,
"tach-pulses has invalid value for fan %d: %u\n",<br>+                
                 
               
                 page,
uval);<br>+                
                 return
-EINVAL;<br>+                
}<br>+<br>+                
if (of_property_read_bool(child, "maxim,fan-health"))<br>+                
                 mfr_cfg
|= MFR_FAN_CONFIG_HEALTH;<br>+<br>+                
if (of_property_read_bool(child, "maxim,fan-no-watchdog") ||<br>+                
                 of_property_read_bool(child,
"maxim,tmp-no-fault-ramp"))<br>+                
                 mfr_cfg
|= MFR_FAN_CONFIG_TSFO;<br>+<br>+                
if (of_property_read_bool(child, "maxim,fan-dual-tach"))<br>+                
                 mfr_cfg
|= MFR_FAN_CONFIG_DUAL_TACH;<br>+<br>+                
if (of_property_read_bool(child, "maxim,fan-no-fault-ramp"))<br>+                
                 mfr_cfg
|= MFR_FAN_CONFIG_TACHO;<br>+<br>+                
if (!of_property_read_u32(child, "maxim,fan-startup", &uval))
{<br>+                
                 uval
/= 2;<br>+                
                 if
(uval < 5) {<br>+                
                 
               
mfr_cfg |= uval;<br>+                
                 }
else {<br>+                
                 
               
dev_err(dev, "maxim,fan-startup has invalid value for fan %d: %u\n",<br>+                
                 
               
                 
               
page, uval);<br>+                
                 
               
return -EINVAL;<br>+                
                 }<br>+                
}<br>+<br>+                
if (!of_property_read_u32(child, "maxim,fan-ramp", &uval))
{<br>+                
                 if
(uval < 8) {<br>+                
                 
               
mfr_cfg |= uval << 5;<br>+                
                 }
else {<br>+                
                 
               
dev_err(dev, "maxim,fan-ramp has invalid value for fan %d: %u\n",<br>+                
                 
               
                 
               
page, uval);<br>+                
                 
               
return -EINVAL;<br>+                
                 }<br>+                
}<br>+<br>+                
if (!of_property_read_u32(child, "maxim,tmp-hysteresis", &uval))
{<br>+                
                 uval
/= 2;<br>+                
                 uval
-= 1;<br>+                
                 if
(uval < 4) {<br>+                
                 
               
mfr_cfg |= uval << 10;<br>+                
                 }
else {<br>+                
                 
               
dev_err(dev, "maxim,tmp-hysteresis has invalid value for fan %d, %u\n",<br>+                
                 
               
                 
               
page, uval);<br>+                
                 
               
return -EINVAL;<br>+                
                 }<br>+                
}<br>+<br>+                
if (!of_property_read_u32(child, "maxim,fan-pwm-freq", &uval))
{<br>+                
                 u16
val;<br>+<br>+                
                 if
(uval == 30) {<br>+                
                 
               
val = 0;<br>+                
                 }
else if (uval == 50) {<br>+                
                 
               
val = 1;<br>+                
                 }
else if (uval == 100) {<br>+                
                 
               
val = 2;<br>+                
                 }
else if (uval == 150) {<br>+                
                 
               
val = 3;<br>+                
                 }
else if (uval == 25000) {<br>+                
                 
               
val = 7;<br>+                
                 }
else {<br>+                
                 
               
dev_err(dev, "maxim,fan-pwm-freq has invalid value for fan %d: %u\n",<br>+                
                 
               
                 
               
page, uval);<br>+                
                 
               
return -EINVAL;<br>+                
                 }<br>+<br>+                
                 mfr_cfg
|= val << 13;<br>+                
}<br>+<br>+                
if (of_property_read_bool(child, "maxim,fan-fault-pin-mon"))<br>+                
                 mfr_fault_resp
|= MFR_FAULT_RESPONSE_MONITOR;<br>+<br>+                
ret = i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,<br>+                
                 
               
                 
               
pb_cfg & ~PB_FAN_1_INSTALLED);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
ret = i2c_smbus_write_word_data(client, MFR_FAN_CONFIG, mfr_cfg);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
ret = i2c_smbus_write_byte_data(client, MFR_FAULT_RESPONSE,<br>+                
                 
               
                 
               
mfr_fault_resp);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
ret = i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12, pb_cfg);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
/*<br>+                
 * Fans are on pages 0 - 5. If the page property of a fan node is<br>+                
 * greater than 5 we will have errored in checks above out above.<br>+                
 * Therefore we don't need to cope with values up to 31, and the int<br>+                
 * return type is enough.<br>+                
 *<br>+                
 * The bit mask return value is used to populate a bitfield of fans<br>+                
 * who are both configured in the devicetree _and_ reported as<br>+                
 * installed by the hardware. Any fans that are not configured in
the<br>+                
 * devicetree but are reported as installed by the hardware will have<br>+                
 * their hardware configuration updated to unset the installed bit.<br>+                
 */<br>+                
return BIT(page);<br>+}<br>+<br>+static int max31785_of_tmp_config(struct i2c_client *client,<br>+                
                 
               
                 
 struct pmbus_driver_info *info,<br>+                
                 
               
                 
 struct device_node *child)<br>+{<br>+                
struct device *dev = &client->dev;<br>+                
struct device_node *np;<br>+                
u16 mfr_tmp_cfg = 0;<br>+                
u32 page;<br>+                
u32 uval;<br>+                
int ret;<br>+                
int i;<br>+<br>+                
if (!of_device_is_compatible(child, "pmbus-temperature"))<br>+                
                 return
0;<br>+<br>+                
ret = of_property_read_u32(child, "reg", &page);<br>+                
if (ret < 0) {<br>+                
                 dev_err(&client->dev,
"Missing valid reg property\n");<br>+                
                 return
ret;<br>+                
}<br>+<br>+                
if (!(info->func[page] & PMBUS_HAVE_TEMP)) {<br>+                
                 dev_err(dev,
"Page %d does not have temp capabilities\n", page);<br>+                
                 return
-ENXIO;<br>+                
}<br>+<br>+                
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
if (!of_property_read_u32(child, "maxim,tmp-offset", &uval))
{<br>+                
                 if
(uval < 32)<br>+                
                 
               
mfr_tmp_cfg |= uval << 10;<br>+                
}<br>+<br>+                
i = 0;<br>+                
while ((np = of_parse_phandle(child, "maxim,tmp-fans", i))) {<br>+                
                 if
(of_property_read_u32(np, "reg", &uval)) {<br>+                
                 
               
dev_err(&client->dev, "Failed to read fan reg property for
phandle index %d\n",<br>+                
                 
               
                 
               
i);<br>+                
                 }
else {<br>+                
                 
               
if (uval < 6)<br>+                
                 
               
                 mfr_tmp_cfg
|= BIT(uval);<br>+                
                 
               
else<br>+                
                 
               
                 dev_warn(&client->dev,
"Invalid fan page: %d\n",<br>+                
                 
               
                 
               
                 uval);<br>+                
                 }<br>+                
                 i++;<br>+                
}<br>+<br>+                
ret = i2c_smbus_write_word_data(client, MFR_TEMP_SENSOR_CONFIG,<br>+                
                 
               
                 
               
mfr_tmp_cfg);<br>+                
if (ret < 0)<br>+                
                 return
ret;<br>+<br>+                
return 0;<br>+}<br>+<br> #define MAX31785_FAN_FUNCS \<br>                  (PMBUS_HAVE_FAN12
| PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_PWM12)<br> <br>@@ -334,9 +609,12 @@ static int max31785_probe(struct i2c_client *client,<br>                  
               
                 
 const struct i2c_device_id *id)<br> {<br>                  struct
device *dev = &client->dev;<br>+                
struct device_node *child;<br>                  struct
pmbus_driver_info *info;<br>                  bool
dual_tach = false;<br>+                
u32 fans;<br>                  s64
ret;<br>+                
int i;<br> <br>                  if
(!i2c_check_functionality(client->adapter,<br>                  
               
                 
               
     I2C_FUNC_SMBUS_BYTE_DATA |<br>@@ -366,6 +644,46 @@ static int max31785_probe(struct i2c_client *client,<br>                  
               
return -ENODEV;<br>                  }<br> <br>+                
fans = 0;<br>+                
for_each_child_of_node(dev->of_node, child) {<br>+                
                 ret
= max31785_of_fan_config(client, info, child);<br>+                
                 if
(ret < 0) {<br>+                
                 
               
of_node_put(child);<br>+                
                 
               
return ret;<br>+                
                 }<br>+<br>+                
                 if
(ret)<br>+                
                 
               
fans |= ret;<br>+<br>+                
                 ret
= max31785_of_tmp_config(client, info, child);<br>+                
                 if
(ret < 0) {<br>+                
                 
               
of_node_put(child);<br>+                
                 
               
return ret;<br>+                
                 }<br>+                
}<br>+<br>+                
for (i = 0; i < MAX31785_NR_PAGES; i++) {<br>+                
                 bool
have_fan = !!(info->func[i] & PMBUS_HAVE_FAN12);<br>+                
                 bool
fan_configured = !!(fans & BIT(i));<br>+<br>+                
                 if
(!have_fan || fan_configured)<br>+                
                 
               
continue;<br>+<br>+                
                 ret
= i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);<br>+                
                 if
(ret < 0)<br>+                
                 
               
return ret;<br>+<br>+                
                 ret
= i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);<br>+                
                 if
(ret < 0)<br>+                
                 
               
return ret;<br>+<br>+                
                 ret
&= ~PB_FAN_1_INSTALLED;<br>+                
                 ret
= i2c_smbus_write_word_data(client, PMBUS_FAN_CONFIG_12,<br>+                
                 
               
                 
               
                 ret);<br>+                
                 if
(ret < 0)<br>+                
                 
               
return ret;<br>+                
}<br>+<br>                  if
(dual_tach) {<br>                  
               
ret = max31785_configure_dual_tach(client, info);<br>                  
               
if (ret < 0)<br>-- <br>2.14.1<br><br></span></tt><br><br><BR>