<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>