aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2012-03-07 06:54:50 -0500
committerGuenter Roeck <linux@roeck-us.net>2013-02-06 12:58:05 -0500
commit1640eaecc4c5f62c73a87445a55c0f0cbe3b4579 (patch)
treeaf5b6caaae370863cbd0efa191be7307a4f67b4d
parentce603b18f30aea1216a00673a33805d4f5a5e16b (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/zl610026
-rw-r--r--drivers/hwmon/pmbus/zl6100.c176
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.
121in1_lcrit_alarm Input voltage critical low alarm. 121in1_lcrit_alarm Input voltage critical low alarm.
122in1_crit_alarm Input voltage critical high alarm. 122in1_crit_alarm Input voltage critical high alarm.
123 123
124in2_label "vout1" 124in2_label "vmon"
125in2_input Measured output voltage. 125in2_input Measured voltage on VMON (ZL2004) or VDRV (ZL9101M,
126in2_lcrit Critical minimum output Voltage. 126 ZL9117M) pin. Reported voltage is 16x the voltage on the
127in2_crit Critical maximum output voltage. 127 pin (adjusted internally by the chip).
128in2_lcrit_alarm Critical output voltage critical low alarm. 128in2_lcrit Critical minumum VMON/VDRV Voltage.
129in2_crit_alarm Critical output voltage critical high alarm. 129in2_crit Critical maximum VMON/VDRV voltage.
130in2_lcrit_alarm VMON/VDRV voltage critical low alarm.
131in2_crit_alarm VMON/VDRV voltage critical high alarm.
132
133 vmon attributes are supported on ZL2004, ZL9101M,
134 and ZL9117M only.
135
136inX_label "vout1"
137inX_input Measured output voltage.
138inX_lcrit Critical minimum output Voltage.
139inX_crit Critical maximum output voltage.
140inX_lcrit_alarm Critical output voltage critical low alarm.
141inX_crit_alarm Critical output voltage critical high alarm.
142
143 X is 3 for ZL2004, ZL9101M, and ZL9117M, 2 otherwise.
130 144
131curr1_label "iout1" 145curr1_label "iout1"
132curr1_input Measured output current. 146curr1_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
50static ushort delay = ZL6100_WAIT_TIME; 60static ushort delay = ZL6100_WAIT_TIME;
51module_param(delay, ushort, 0644); 61module_param(delay, ushort, 0644);
52MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); 62MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
53 63
64/* Convert linear sensor value to milli-units */
65static 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
90static 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 */
55static inline void zl6100_wait(const struct zl6100_data *data) 131static 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;