diff options
author | Guenter Roeck <linux@roeck-us.net> | 2012-03-07 06:54:50 -0500 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2013-02-06 12:58:05 -0500 |
commit | 1640eaecc4c5f62c73a87445a55c0f0cbe3b4579 (patch) | |
tree | af5b6caaae370863cbd0efa191be7307a4f67b4d | |
parent | ce603b18f30aea1216a00673a33805d4f5a5e16b (diff) |
hwmon: (pmbus/zl6100) Add support for VMON/VDRV
Some of the ZL6100 compatible chips support monitoring a separate voltage pin,
VMON (ZL2004) or VDRV (ZL91xx). Report it as in2 / vmon.
The chips support implicit warning limits for VMON/VDRV, as percentage of the
respective critical voltage. Support by reading/writing the critical voltages
and calculating the associated warning voltages.
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r-- | Documentation/hwmon/zl6100 | 26 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/zl6100.c | 176 |
2 files changed, 188 insertions, 14 deletions
diff --git a/Documentation/hwmon/zl6100 b/Documentation/hwmon/zl6100 index a995b41724fd..3d924b6b59e9 100644 --- a/Documentation/hwmon/zl6100 +++ b/Documentation/hwmon/zl6100 | |||
@@ -121,12 +121,26 @@ in1_max_alarm Input voltage high alarm. | |||
121 | in1_lcrit_alarm Input voltage critical low alarm. | 121 | in1_lcrit_alarm Input voltage critical low alarm. |
122 | in1_crit_alarm Input voltage critical high alarm. | 122 | in1_crit_alarm Input voltage critical high alarm. |
123 | 123 | ||
124 | in2_label "vout1" | 124 | in2_label "vmon" |
125 | in2_input Measured output voltage. | 125 | in2_input Measured voltage on VMON (ZL2004) or VDRV (ZL9101M, |
126 | in2_lcrit Critical minimum output Voltage. | 126 | ZL9117M) pin. Reported voltage is 16x the voltage on the |
127 | in2_crit Critical maximum output voltage. | 127 | pin (adjusted internally by the chip). |
128 | in2_lcrit_alarm Critical output voltage critical low alarm. | 128 | in2_lcrit Critical minumum VMON/VDRV Voltage. |
129 | in2_crit_alarm Critical output voltage critical high alarm. | 129 | in2_crit Critical maximum VMON/VDRV voltage. |
130 | in2_lcrit_alarm VMON/VDRV voltage critical low alarm. | ||
131 | in2_crit_alarm VMON/VDRV voltage critical high alarm. | ||
132 | |||
133 | vmon attributes are supported on ZL2004, ZL9101M, | ||
134 | and ZL9117M only. | ||
135 | |||
136 | inX_label "vout1" | ||
137 | inX_input Measured output voltage. | ||
138 | inX_lcrit Critical minimum output Voltage. | ||
139 | inX_crit Critical maximum output voltage. | ||
140 | inX_lcrit_alarm Critical output voltage critical low alarm. | ||
141 | inX_crit_alarm Critical output voltage critical high alarm. | ||
142 | |||
143 | X is 3 for ZL2004, ZL9101M, and ZL9117M, 2 otherwise. | ||
130 | 144 | ||
131 | curr1_label "iout1" | 145 | curr1_label "iout1" |
132 | curr1_input Measured output current. | 146 | curr1_input Measured output current. |
diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index fc5eed8e85bb..819644121259 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Hardware monitoring driver for ZL6100 and compatibles | 2 | * Hardware monitoring driver for ZL6100 and compatibles |
3 | * | 3 | * |
4 | * Copyright (c) 2011 Ericsson AB. | 4 | * Copyright (c) 2011 Ericsson AB. |
5 | * Copyright (c) 2012 Guenter Roeck | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |
@@ -45,12 +46,87 @@ struct zl6100_data { | |||
45 | 46 | ||
46 | #define ZL6100_MFR_XTEMP_ENABLE (1 << 7) | 47 | #define ZL6100_MFR_XTEMP_ENABLE (1 << 7) |
47 | 48 | ||
49 | #define MFR_VMON_OV_FAULT_LIMIT 0xf5 | ||
50 | #define MFR_VMON_UV_FAULT_LIMIT 0xf6 | ||
51 | #define MFR_READ_VMON 0xf7 | ||
52 | |||
53 | #define VMON_UV_WARNING (1 << 5) | ||
54 | #define VMON_OV_WARNING (1 << 4) | ||
55 | #define VMON_UV_FAULT (1 << 1) | ||
56 | #define VMON_OV_FAULT (1 << 0) | ||
57 | |||
48 | #define ZL6100_WAIT_TIME 1000 /* uS */ | 58 | #define ZL6100_WAIT_TIME 1000 /* uS */ |
49 | 59 | ||
50 | static ushort delay = ZL6100_WAIT_TIME; | 60 | static ushort delay = ZL6100_WAIT_TIME; |
51 | module_param(delay, ushort, 0644); | 61 | module_param(delay, ushort, 0644); |
52 | MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); | 62 | MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); |
53 | 63 | ||
64 | /* Convert linear sensor value to milli-units */ | ||
65 | static long zl6100_l2d(s16 l) | ||
66 | { | ||
67 | s16 exponent; | ||
68 | s32 mantissa; | ||
69 | long val; | ||
70 | |||
71 | exponent = l >> 11; | ||
72 | mantissa = ((s16)((l & 0x7ff) << 5)) >> 5; | ||
73 | |||
74 | val = mantissa; | ||
75 | |||
76 | /* scale result to milli-units */ | ||
77 | val = val * 1000L; | ||
78 | |||
79 | if (exponent >= 0) | ||
80 | val <<= exponent; | ||
81 | else | ||
82 | val >>= -exponent; | ||
83 | |||
84 | return val; | ||
85 | } | ||
86 | |||
87 | #define MAX_MANTISSA (1023 * 1000) | ||
88 | #define MIN_MANTISSA (511 * 1000) | ||
89 | |||
90 | static u16 zl6100_d2l(long val) | ||
91 | { | ||
92 | s16 exponent = 0, mantissa; | ||
93 | bool negative = false; | ||
94 | |||
95 | /* simple case */ | ||
96 | if (val == 0) | ||
97 | return 0; | ||
98 | |||
99 | if (val < 0) { | ||
100 | negative = true; | ||
101 | val = -val; | ||
102 | } | ||
103 | |||
104 | /* Reduce large mantissa until it fits into 10 bit */ | ||
105 | while (val >= MAX_MANTISSA && exponent < 15) { | ||
106 | exponent++; | ||
107 | val >>= 1; | ||
108 | } | ||
109 | /* Increase small mantissa to improve precision */ | ||
110 | while (val < MIN_MANTISSA && exponent > -15) { | ||
111 | exponent--; | ||
112 | val <<= 1; | ||
113 | } | ||
114 | |||
115 | /* Convert mantissa from milli-units to units */ | ||
116 | mantissa = DIV_ROUND_CLOSEST(val, 1000); | ||
117 | |||
118 | /* Ensure that resulting number is within range */ | ||
119 | if (mantissa > 0x3ff) | ||
120 | mantissa = 0x3ff; | ||
121 | |||
122 | /* restore sign */ | ||
123 | if (negative) | ||
124 | mantissa = -mantissa; | ||
125 | |||
126 | /* Convert to 5 bit exponent, 11 bit mantissa */ | ||
127 | return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); | ||
128 | } | ||
129 | |||
54 | /* Some chips need a delay between accesses */ | 130 | /* Some chips need a delay between accesses */ |
55 | static inline void zl6100_wait(const struct zl6100_data *data) | 131 | static inline void zl6100_wait(const struct zl6100_data *data) |
56 | { | 132 | { |
@@ -65,9 +141,9 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) | |||
65 | { | 141 | { |
66 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | 142 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); |
67 | struct zl6100_data *data = to_zl6100_data(info); | 143 | struct zl6100_data *data = to_zl6100_data(info); |
68 | int ret; | 144 | int ret, vreg; |
69 | 145 | ||
70 | if (page || reg >= PMBUS_VIRT_BASE) | 146 | if (page > 0) |
71 | return -ENXIO; | 147 | return -ENXIO; |
72 | 148 | ||
73 | if (data->id == zl2005) { | 149 | if (data->id == zl2005) { |
@@ -83,9 +159,39 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) | |||
83 | } | 159 | } |
84 | } | 160 | } |
85 | 161 | ||
162 | switch (reg) { | ||
163 | case PMBUS_VIRT_READ_VMON: | ||
164 | vreg = MFR_READ_VMON; | ||
165 | break; | ||
166 | case PMBUS_VIRT_VMON_OV_WARN_LIMIT: | ||
167 | case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: | ||
168 | vreg = MFR_VMON_OV_FAULT_LIMIT; | ||
169 | break; | ||
170 | case PMBUS_VIRT_VMON_UV_WARN_LIMIT: | ||
171 | case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: | ||
172 | vreg = MFR_VMON_UV_FAULT_LIMIT; | ||
173 | break; | ||
174 | default: | ||
175 | if (reg >= PMBUS_VIRT_BASE) | ||
176 | return -ENXIO; | ||
177 | vreg = reg; | ||
178 | break; | ||
179 | } | ||
180 | |||
86 | zl6100_wait(data); | 181 | zl6100_wait(data); |
87 | ret = pmbus_read_word_data(client, page, reg); | 182 | ret = pmbus_read_word_data(client, page, vreg); |
88 | data->access = ktime_get(); | 183 | data->access = ktime_get(); |
184 | if (ret < 0) | ||
185 | return ret; | ||
186 | |||
187 | switch (reg) { | ||
188 | case PMBUS_VIRT_VMON_OV_WARN_LIMIT: | ||
189 | ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10)); | ||
190 | break; | ||
191 | case PMBUS_VIRT_VMON_UV_WARN_LIMIT: | ||
192 | ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10)); | ||
193 | break; | ||
194 | } | ||
89 | 195 | ||
90 | return ret; | 196 | return ret; |
91 | } | 197 | } |
@@ -94,13 +200,35 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) | |||
94 | { | 200 | { |
95 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | 201 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); |
96 | struct zl6100_data *data = to_zl6100_data(info); | 202 | struct zl6100_data *data = to_zl6100_data(info); |
97 | int ret; | 203 | int ret, status; |
98 | 204 | ||
99 | if (page > 0) | 205 | if (page > 0) |
100 | return -ENXIO; | 206 | return -ENXIO; |
101 | 207 | ||
102 | zl6100_wait(data); | 208 | zl6100_wait(data); |
103 | ret = pmbus_read_byte_data(client, page, reg); | 209 | |
210 | switch (reg) { | ||
211 | case PMBUS_VIRT_STATUS_VMON: | ||
212 | ret = pmbus_read_byte_data(client, 0, | ||
213 | PMBUS_STATUS_MFR_SPECIFIC); | ||
214 | if (ret < 0) | ||
215 | break; | ||
216 | |||
217 | status = 0; | ||
218 | if (ret & VMON_UV_WARNING) | ||
219 | status |= PB_VOLTAGE_UV_WARNING; | ||
220 | if (ret & VMON_OV_WARNING) | ||
221 | status |= PB_VOLTAGE_OV_WARNING; | ||
222 | if (ret & VMON_UV_FAULT) | ||
223 | status |= PB_VOLTAGE_UV_FAULT; | ||
224 | if (ret & VMON_OV_FAULT) | ||
225 | status |= PB_VOLTAGE_OV_FAULT; | ||
226 | ret = status; | ||
227 | break; | ||
228 | default: | ||
229 | ret = pmbus_read_byte_data(client, page, reg); | ||
230 | break; | ||
231 | } | ||
104 | data->access = ktime_get(); | 232 | data->access = ktime_get(); |
105 | 233 | ||
106 | return ret; | 234 | return ret; |
@@ -111,13 +239,38 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, | |||
111 | { | 239 | { |
112 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | 240 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); |
113 | struct zl6100_data *data = to_zl6100_data(info); | 241 | struct zl6100_data *data = to_zl6100_data(info); |
114 | int ret; | 242 | int ret, vreg; |
115 | 243 | ||
116 | if (page || reg >= PMBUS_VIRT_BASE) | 244 | if (page > 0) |
117 | return -ENXIO; | 245 | return -ENXIO; |
118 | 246 | ||
247 | switch (reg) { | ||
248 | case PMBUS_VIRT_VMON_OV_WARN_LIMIT: | ||
249 | word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9)); | ||
250 | vreg = MFR_VMON_OV_FAULT_LIMIT; | ||
251 | pmbus_clear_cache(client); | ||
252 | break; | ||
253 | case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: | ||
254 | vreg = MFR_VMON_OV_FAULT_LIMIT; | ||
255 | pmbus_clear_cache(client); | ||
256 | break; | ||
257 | case PMBUS_VIRT_VMON_UV_WARN_LIMIT: | ||
258 | word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11)); | ||
259 | vreg = MFR_VMON_UV_FAULT_LIMIT; | ||
260 | pmbus_clear_cache(client); | ||
261 | break; | ||
262 | case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: | ||
263 | vreg = MFR_VMON_UV_FAULT_LIMIT; | ||
264 | pmbus_clear_cache(client); | ||
265 | break; | ||
266 | default: | ||
267 | if (reg >= PMBUS_VIRT_BASE) | ||
268 | return -ENXIO; | ||
269 | vreg = reg; | ||
270 | } | ||
271 | |||
119 | zl6100_wait(data); | 272 | zl6100_wait(data); |
120 | ret = pmbus_write_word_data(client, page, reg, word); | 273 | ret = pmbus_write_word_data(client, page, vreg, word); |
121 | data->access = ktime_get(); | 274 | data->access = ktime_get(); |
122 | 275 | ||
123 | return ret; | 276 | return ret; |
@@ -225,6 +378,13 @@ static int zl6100_probe(struct i2c_client *client, | |||
225 | | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 378 | | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
226 | | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; | 379 | | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; |
227 | 380 | ||
381 | /* | ||
382 | * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage | ||
383 | * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. | ||
384 | */ | ||
385 | if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) | ||
386 | info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; | ||
387 | |||
228 | ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); | 388 | ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); |
229 | if (ret < 0) | 389 | if (ret < 0) |
230 | return ret; | 390 | return ret; |