]>
Commit | Line | Data |
---|---|---|
7215456a TR |
1 | /* |
2 | * Maxim MAX34451 PMBus 16-Channel V/I monitor and 12-Channel Sequencer/Marginer | |
3 | * | |
4 | * Copyright 2021 Google LLC | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0-or-later | |
7 | */ | |
8 | ||
9 | #include "qemu/osdep.h" | |
10 | #include "hw/i2c/pmbus_device.h" | |
11 | #include "hw/irq.h" | |
12 | #include "migration/vmstate.h" | |
13 | #include "qapi/error.h" | |
14 | #include "qapi/visitor.h" | |
15 | #include "qemu/log.h" | |
16 | #include "qemu/module.h" | |
17 | ||
18 | #define TYPE_MAX34451 "max34451" | |
19 | #define MAX34451(obj) OBJECT_CHECK(MAX34451State, (obj), TYPE_MAX34451) | |
20 | ||
21 | #define MAX34451_MFR_MODE 0xD1 | |
22 | #define MAX34451_MFR_PSEN_CONFIG 0xD2 | |
23 | #define MAX34451_MFR_VOUT_PEAK 0xD4 | |
24 | #define MAX34451_MFR_IOUT_PEAK 0xD5 | |
25 | #define MAX34451_MFR_TEMPERATURE_PEAK 0xD6 | |
26 | #define MAX34451_MFR_VOUT_MIN 0xD7 | |
27 | #define MAX34451_MFR_NV_LOG_CONFIG 0xD8 | |
28 | #define MAX34451_MFR_FAULT_RESPONSE 0xD9 | |
29 | #define MAX34451_MFR_FAULT_RETRY 0xDA | |
30 | #define MAX34451_MFR_NV_FAULT_LOG 0xDC | |
31 | #define MAX34451_MFR_TIME_COUNT 0xDD | |
32 | #define MAX34451_MFR_MARGIN_CONFIG 0xDF | |
33 | #define MAX34451_MFR_FW_SERIAL 0xE0 | |
34 | #define MAX34451_MFR_IOUT_AVG 0xE2 | |
35 | #define MAX34451_MFR_CHANNEL_CONFIG 0xE4 | |
36 | #define MAX34451_MFR_TON_SEQ_MAX 0xE6 | |
37 | #define MAX34451_MFR_PWM_CONFIG 0xE7 | |
38 | #define MAX34451_MFR_SEQ_CONFIG 0xE8 | |
39 | #define MAX34451_MFR_STORE_ALL 0xEE | |
40 | #define MAX34451_MFR_RESTORE_ALL 0xEF | |
41 | #define MAX34451_MFR_TEMP_SENSOR_CONFIG 0xF0 | |
42 | #define MAX34451_MFR_STORE_SINGLE 0xFC | |
43 | #define MAX34451_MFR_CRC 0xFE | |
44 | ||
45 | #define MAX34451_NUM_MARGINED_PSU 12 | |
46 | #define MAX34451_NUM_PWR_DEVICES 16 | |
47 | #define MAX34451_NUM_TEMP_DEVICES 5 | |
48 | #define MAX34451_NUM_PAGES 21 | |
49 | ||
50 | #define DEFAULT_OP_ON 0x80 | |
51 | #define DEFAULT_CAPABILITY 0x20 | |
52 | #define DEFAULT_ON_OFF_CONFIG 0x1a | |
53 | #define DEFAULT_VOUT_MODE 0x40 | |
54 | #define DEFAULT_TEMPERATURE 2500 | |
55 | #define DEFAULT_SCALE 0x7FFF | |
56 | #define DEFAULT_OV_LIMIT 0x7FFF | |
57 | #define DEFAULT_OC_LIMIT 0x7FFF | |
58 | #define DEFAULT_OT_LIMIT 0x7FFF | |
59 | #define DEFAULT_VMIN 0x7FFF | |
60 | #define DEFAULT_TON_FAULT_LIMIT 0xFFFF | |
61 | #define DEFAULT_CHANNEL_CONFIG 0x20 | |
62 | #define DEFAULT_TEXT 0x3130313031303130 | |
63 | ||
64 | /** | |
65 | * MAX34451State: | |
66 | * @code: The command code received | |
67 | * @page: Each page corresponds to a device monitored by the Max 34451 | |
68 | * The page register determines the available commands depending on device | |
69 | ___________________________________________________________________________ | |
70 | | 0 | Power supply monitored by RS0, controlled by PSEN0, and | | |
71 | | | margined with PWM0. | | |
72 | |_______|___________________________________________________________________| | |
73 | | 1 | Power supply monitored by RS1, controlled by PSEN1, and | | |
74 | | | margined with PWM1. | | |
75 | |_______|___________________________________________________________________| | |
76 | | 2 | Power supply monitored by RS2, controlled by PSEN2, and | | |
77 | | | margined with PWM2. | | |
78 | |_______|___________________________________________________________________| | |
79 | | 3 | Power supply monitored by RS3, controlled by PSEN3, and | | |
80 | | | margined with PWM3. | | |
81 | |_______|___________________________________________________________________| | |
82 | | 4 | Power supply monitored by RS4, controlled by PSEN4, and | | |
83 | | | margined with PWM4. | | |
84 | |_______|___________________________________________________________________| | |
85 | | 5 | Power supply monitored by RS5, controlled by PSEN5, and | | |
86 | | | margined with PWM5. | | |
87 | |_______|___________________________________________________________________| | |
88 | | 6 | Power supply monitored by RS6, controlled by PSEN6, and | | |
89 | | | margined with PWM6. | | |
90 | |_______|___________________________________________________________________| | |
91 | | 7 | Power supply monitored by RS7, controlled by PSEN7, and | | |
92 | | | margined with PWM7. | | |
93 | |_______|___________________________________________________________________| | |
94 | | 8 | Power supply monitored by RS8, controlled by PSEN8, and | | |
95 | | | optionally margined by OUT0 of external DS4424 at I2C address A0h.| | |
96 | |_______|___________________________________________________________________| | |
97 | | 9 | Power supply monitored by RS9, controlled by PSEN9, and | | |
98 | | | optionally margined by OUT1 of external DS4424 at I2C address A0h.| | |
99 | |_______|___________________________________________________________________| | |
100 | | 10 | Power supply monitored by RS10, controlled by PSEN10, and | | |
101 | | | optionally margined by OUT2 of external DS4424 at I2C address A0h.| | |
102 | |_______|___________________________________________________________________| | |
103 | | 11 | Power supply monitored by RS11, controlled by PSEN11, and | | |
104 | | | optionally margined by OUT3 of external DS4424 at I2C address A0h.| | |
105 | |_______|___________________________________________________________________| | |
106 | | 12 | ADC channel 12 (monitors voltage or current) or GPI. | | |
107 | |_______|___________________________________________________________________| | |
108 | | 13 | ADC channel 13 (monitors voltage or current) or GPI. | | |
109 | |_______|___________________________________________________________________| | |
110 | | 14 | ADC channel 14 (monitors voltage or current) or GPI. | | |
111 | |_______|___________________________________________________________________| | |
112 | | 15 | ADC channel 15 (monitors voltage or current) or GPI. | | |
113 | |_______|___________________________________________________________________| | |
114 | | 16 | Internal temperature sensor. | | |
115 | |_______|___________________________________________________________________| | |
116 | | 17 | External DS75LV temperature sensor with I2C address 90h. | | |
117 | |_______|___________________________________________________________________| | |
118 | | 18 | External DS75LV temperature sensor with I2C address 92h. | | |
119 | |_______|___________________________________________________________________| | |
120 | | 19 | External DS75LV temperature sensor with I2C address 94h. | | |
121 | |_______|___________________________________________________________________| | |
122 | | 20 | External DS75LV temperature sensor with I2C address 96h. | | |
123 | |_______|___________________________________________________________________| | |
124 | | 21=E2=80=93254| Reserved. | | |
125 | |_______|___________________________________________________________________| | |
126 | | 255 | Applies to all pages. | | |
127 | |_______|___________________________________________________________________| | |
128 | * | |
129 | * @operation: Turn on and off power supplies | |
130 | * @on_off_config: Configure the power supply on and off transition behaviour | |
131 | * @write_protect: protect against changes to the device's memory | |
132 | * @vout_margin_high: the voltage when OPERATION is set to margin high | |
133 | * @vout_margin_low: the voltage when OPERATION is set to margin low | |
134 | * @vout_scale: scale ADC reading to actual device reading if different | |
135 | * @iout_cal_gain: set ratio of the voltage at the ADC input to sensed current | |
136 | */ | |
137 | typedef struct MAX34451State { | |
138 | PMBusDevice parent; | |
139 | ||
140 | uint16_t power_good_on[MAX34451_NUM_PWR_DEVICES]; | |
141 | uint16_t power_good_off[MAX34451_NUM_PWR_DEVICES]; | |
142 | uint16_t ton_delay[MAX34451_NUM_MARGINED_PSU]; | |
143 | uint16_t ton_max_fault_limit[MAX34451_NUM_MARGINED_PSU]; | |
144 | uint16_t toff_delay[MAX34451_NUM_MARGINED_PSU]; | |
145 | uint8_t status_mfr_specific[MAX34451_NUM_PWR_DEVICES]; | |
146 | /* Manufacturer specific function */ | |
147 | uint64_t mfr_location; | |
148 | uint64_t mfr_date; | |
149 | uint64_t mfr_serial; | |
150 | uint16_t mfr_mode; | |
151 | uint32_t psen_config[MAX34451_NUM_MARGINED_PSU]; | |
152 | uint16_t vout_peak[MAX34451_NUM_PWR_DEVICES]; | |
153 | uint16_t iout_peak[MAX34451_NUM_PWR_DEVICES]; | |
154 | uint16_t temperature_peak[MAX34451_NUM_TEMP_DEVICES]; | |
155 | uint16_t vout_min[MAX34451_NUM_PWR_DEVICES]; | |
156 | uint16_t nv_log_config; | |
157 | uint32_t fault_response[MAX34451_NUM_PWR_DEVICES]; | |
158 | uint16_t fault_retry; | |
159 | uint32_t fault_log; | |
160 | uint32_t time_count; | |
161 | uint16_t margin_config[MAX34451_NUM_MARGINED_PSU]; | |
162 | uint16_t fw_serial; | |
163 | uint16_t iout_avg[MAX34451_NUM_PWR_DEVICES]; | |
164 | uint16_t channel_config[MAX34451_NUM_PWR_DEVICES]; | |
165 | uint16_t ton_seq_max[MAX34451_NUM_MARGINED_PSU]; | |
166 | uint32_t pwm_config[MAX34451_NUM_MARGINED_PSU]; | |
167 | uint32_t seq_config[MAX34451_NUM_MARGINED_PSU]; | |
168 | uint16_t temp_sensor_config[MAX34451_NUM_TEMP_DEVICES]; | |
169 | uint16_t store_single; | |
170 | uint16_t crc; | |
171 | } MAX34451State; | |
172 | ||
173 | ||
174 | static void max34451_check_limits(MAX34451State *s) | |
175 | { | |
176 | PMBusDevice *pmdev = PMBUS_DEVICE(s); | |
177 | ||
178 | pmbus_check_limits(pmdev); | |
179 | ||
180 | for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { | |
181 | if (pmdev->pages[i].read_vout == 0) { /* PSU disabled */ | |
182 | continue; | |
183 | } | |
184 | ||
185 | if (pmdev->pages[i].read_vout > s->vout_peak[i]) { | |
186 | s->vout_peak[i] = pmdev->pages[i].read_vout; | |
187 | } | |
188 | ||
189 | if (pmdev->pages[i].read_vout < s->vout_min[i]) { | |
190 | s->vout_min[i] = pmdev->pages[i].read_vout; | |
191 | } | |
192 | ||
193 | if (pmdev->pages[i].read_iout > s->iout_peak[i]) { | |
194 | s->iout_peak[i] = pmdev->pages[i].read_iout; | |
195 | } | |
196 | } | |
197 | ||
198 | for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { | |
199 | if (pmdev->pages[i + 16].read_temperature_1 > s->temperature_peak[i]) { | |
200 | s->temperature_peak[i] = pmdev->pages[i + 16].read_temperature_1; | |
201 | } | |
202 | } | |
203 | } | |
204 | ||
205 | static uint8_t max34451_read_byte(PMBusDevice *pmdev) | |
206 | { | |
207 | MAX34451State *s = MAX34451(pmdev); | |
208 | switch (pmdev->code) { | |
209 | ||
210 | case PMBUS_POWER_GOOD_ON: | |
211 | if (pmdev->page < 16) { | |
212 | pmbus_send16(pmdev, s->power_good_on[pmdev->page]); | |
213 | } | |
214 | break; | |
215 | ||
216 | case PMBUS_POWER_GOOD_OFF: | |
217 | if (pmdev->page < 16) { | |
218 | pmbus_send16(pmdev, s->power_good_off[pmdev->page]); | |
219 | } | |
220 | break; | |
221 | ||
222 | case PMBUS_TON_DELAY: | |
223 | if (pmdev->page < 12) { | |
224 | pmbus_send16(pmdev, s->ton_delay[pmdev->page]); | |
225 | } | |
226 | break; | |
227 | ||
228 | case PMBUS_TON_MAX_FAULT_LIMIT: | |
229 | if (pmdev->page < 12) { | |
230 | pmbus_send16(pmdev, s->ton_max_fault_limit[pmdev->page]); | |
231 | } | |
232 | break; | |
233 | ||
234 | case PMBUS_TOFF_DELAY: | |
235 | if (pmdev->page < 12) { | |
236 | pmbus_send16(pmdev, s->toff_delay[pmdev->page]); | |
237 | } | |
238 | break; | |
239 | ||
240 | case PMBUS_STATUS_MFR_SPECIFIC: | |
241 | if (pmdev->page < 16) { | |
242 | pmbus_send8(pmdev, s->status_mfr_specific[pmdev->page]); | |
243 | } | |
244 | break; | |
245 | ||
246 | case PMBUS_MFR_ID: | |
247 | pmbus_send8(pmdev, 0x4d); /* Maxim */ | |
248 | break; | |
249 | ||
250 | case PMBUS_MFR_MODEL: | |
251 | pmbus_send8(pmdev, 0x59); | |
252 | break; | |
253 | ||
254 | case PMBUS_MFR_LOCATION: | |
255 | pmbus_send64(pmdev, s->mfr_location); | |
256 | break; | |
257 | ||
258 | case PMBUS_MFR_DATE: | |
259 | pmbus_send64(pmdev, s->mfr_date); | |
260 | break; | |
261 | ||
262 | case PMBUS_MFR_SERIAL: | |
263 | pmbus_send64(pmdev, s->mfr_serial); | |
264 | break; | |
265 | ||
266 | case MAX34451_MFR_MODE: | |
267 | pmbus_send16(pmdev, s->mfr_mode); | |
268 | break; | |
269 | ||
270 | case MAX34451_MFR_PSEN_CONFIG: | |
271 | if (pmdev->page < 12) { | |
272 | pmbus_send32(pmdev, s->psen_config[pmdev->page]); | |
273 | } | |
274 | break; | |
275 | ||
276 | case MAX34451_MFR_VOUT_PEAK: | |
277 | if (pmdev->page < 16) { | |
278 | pmbus_send16(pmdev, s->vout_peak[pmdev->page]); | |
279 | } | |
280 | break; | |
281 | ||
282 | case MAX34451_MFR_IOUT_PEAK: | |
283 | if (pmdev->page < 16) { | |
284 | pmbus_send16(pmdev, s->iout_peak[pmdev->page]); | |
285 | } | |
286 | break; | |
287 | ||
288 | case MAX34451_MFR_TEMPERATURE_PEAK: | |
289 | if (15 < pmdev->page && pmdev->page < 21) { | |
290 | pmbus_send16(pmdev, s->temperature_peak[pmdev->page % 16]); | |
291 | } else { | |
292 | pmbus_send16(pmdev, s->temperature_peak[0]); | |
293 | } | |
294 | break; | |
295 | ||
296 | case MAX34451_MFR_VOUT_MIN: | |
297 | if (pmdev->page < 16) { | |
298 | pmbus_send16(pmdev, s->vout_min[pmdev->page]); | |
299 | } | |
300 | break; | |
301 | ||
302 | case MAX34451_MFR_NV_LOG_CONFIG: | |
303 | pmbus_send16(pmdev, s->nv_log_config); | |
304 | break; | |
305 | ||
306 | case MAX34451_MFR_FAULT_RESPONSE: | |
307 | if (pmdev->page < 16) { | |
308 | pmbus_send32(pmdev, s->fault_response[pmdev->page]); | |
309 | } | |
310 | break; | |
311 | ||
312 | case MAX34451_MFR_FAULT_RETRY: | |
313 | pmbus_send32(pmdev, s->fault_retry); | |
314 | break; | |
315 | ||
316 | case MAX34451_MFR_NV_FAULT_LOG: | |
317 | pmbus_send32(pmdev, s->fault_log); | |
318 | break; | |
319 | ||
320 | case MAX34451_MFR_TIME_COUNT: | |
321 | pmbus_send32(pmdev, s->time_count); | |
322 | break; | |
323 | ||
324 | case MAX34451_MFR_MARGIN_CONFIG: | |
325 | if (pmdev->page < 12) { | |
326 | pmbus_send16(pmdev, s->margin_config[pmdev->page]); | |
327 | } | |
328 | break; | |
329 | ||
330 | case MAX34451_MFR_FW_SERIAL: | |
331 | if (pmdev->page == 255) { | |
332 | pmbus_send16(pmdev, 1); /* Firmware revision */ | |
333 | } | |
334 | break; | |
335 | ||
336 | case MAX34451_MFR_IOUT_AVG: | |
337 | if (pmdev->page < 16) { | |
338 | pmbus_send16(pmdev, s->iout_avg[pmdev->page]); | |
339 | } | |
340 | break; | |
341 | ||
342 | case MAX34451_MFR_CHANNEL_CONFIG: | |
343 | if (pmdev->page < 16) { | |
344 | pmbus_send16(pmdev, s->channel_config[pmdev->page]); | |
345 | } | |
346 | break; | |
347 | ||
348 | case MAX34451_MFR_TON_SEQ_MAX: | |
349 | if (pmdev->page < 12) { | |
350 | pmbus_send16(pmdev, s->ton_seq_max[pmdev->page]); | |
351 | } | |
352 | break; | |
353 | ||
354 | case MAX34451_MFR_PWM_CONFIG: | |
355 | if (pmdev->page < 12) { | |
356 | pmbus_send32(pmdev, s->pwm_config[pmdev->page]); | |
357 | } | |
358 | break; | |
359 | ||
360 | case MAX34451_MFR_SEQ_CONFIG: | |
361 | if (pmdev->page < 12) { | |
362 | pmbus_send32(pmdev, s->seq_config[pmdev->page]); | |
363 | } | |
364 | break; | |
365 | ||
366 | case MAX34451_MFR_TEMP_SENSOR_CONFIG: | |
367 | if (15 < pmdev->page && pmdev->page < 21) { | |
368 | pmbus_send32(pmdev, s->temp_sensor_config[pmdev->page % 16]); | |
369 | } | |
370 | break; | |
371 | ||
372 | case MAX34451_MFR_STORE_SINGLE: | |
373 | pmbus_send32(pmdev, s->store_single); | |
374 | break; | |
375 | ||
376 | case MAX34451_MFR_CRC: | |
377 | pmbus_send32(pmdev, s->crc); | |
378 | break; | |
379 | ||
380 | default: | |
381 | qemu_log_mask(LOG_GUEST_ERROR, | |
382 | "%s: reading from unsupported register: 0x%02x\n", | |
383 | __func__, pmdev->code); | |
384 | break; | |
385 | } | |
386 | return 0xFF; | |
387 | } | |
388 | ||
389 | static int max34451_write_data(PMBusDevice *pmdev, const uint8_t *buf, | |
390 | uint8_t len) | |
391 | { | |
392 | MAX34451State *s = MAX34451(pmdev); | |
393 | ||
394 | if (len == 0) { | |
395 | qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__); | |
396 | return -1; | |
397 | } | |
398 | ||
399 | pmdev->code = buf[0]; /* PMBus command code */ | |
400 | ||
401 | if (len == 1) { | |
402 | return 0; | |
403 | } | |
404 | ||
405 | /* Exclude command code from buffer */ | |
406 | buf++; | |
407 | len--; | |
408 | uint8_t index = pmdev->page; | |
409 | ||
410 | switch (pmdev->code) { | |
411 | case MAX34451_MFR_STORE_ALL: | |
412 | case MAX34451_MFR_RESTORE_ALL: | |
413 | case MAX34451_MFR_STORE_SINGLE: | |
414 | /* | |
415 | * TODO: hardware behaviour is to move the contents of volatile | |
416 | * memory to non-volatile memory. | |
417 | */ | |
418 | break; | |
419 | ||
420 | case PMBUS_POWER_GOOD_ON: /* R/W word */ | |
421 | if (pmdev->page < MAX34451_NUM_PWR_DEVICES) { | |
422 | s->power_good_on[pmdev->page] = pmbus_receive16(pmdev); | |
423 | } | |
424 | break; | |
425 | ||
426 | case PMBUS_POWER_GOOD_OFF: /* R/W word */ | |
427 | if (pmdev->page < MAX34451_NUM_PWR_DEVICES) { | |
428 | s->power_good_off[pmdev->page] = pmbus_receive16(pmdev); | |
429 | } | |
430 | break; | |
431 | ||
432 | case PMBUS_TON_DELAY: /* R/W word */ | |
433 | if (pmdev->page < 12) { | |
434 | s->ton_delay[pmdev->page] = pmbus_receive16(pmdev); | |
435 | } | |
436 | break; | |
437 | ||
438 | case PMBUS_TON_MAX_FAULT_LIMIT: /* R/W word */ | |
439 | if (pmdev->page < 12) { | |
440 | s->ton_max_fault_limit[pmdev->page] | |
441 | = pmbus_receive16(pmdev); | |
442 | } | |
443 | break; | |
444 | ||
445 | case PMBUS_TOFF_DELAY: /* R/W word */ | |
446 | if (pmdev->page < 12) { | |
447 | s->toff_delay[pmdev->page] = pmbus_receive16(pmdev); | |
448 | } | |
449 | break; | |
450 | ||
451 | case PMBUS_MFR_LOCATION: /* R/W 64 */ | |
452 | s->mfr_location = pmbus_receive64(pmdev); | |
453 | break; | |
454 | ||
455 | case PMBUS_MFR_DATE: /* R/W 64 */ | |
456 | s->mfr_date = pmbus_receive64(pmdev); | |
457 | break; | |
458 | ||
459 | case PMBUS_MFR_SERIAL: /* R/W 64 */ | |
460 | s->mfr_serial = pmbus_receive64(pmdev); | |
461 | break; | |
462 | ||
463 | case MAX34451_MFR_MODE: /* R/W word */ | |
464 | s->mfr_mode = pmbus_receive16(pmdev); | |
465 | break; | |
466 | ||
467 | case MAX34451_MFR_PSEN_CONFIG: /* R/W 32 */ | |
468 | if (pmdev->page < 12) { | |
469 | s->psen_config[pmdev->page] = pmbus_receive32(pmdev); | |
470 | } | |
471 | break; | |
472 | ||
473 | case MAX34451_MFR_VOUT_PEAK: /* R/W word */ | |
474 | if (pmdev->page < 16) { | |
475 | s->vout_peak[pmdev->page] = pmbus_receive16(pmdev); | |
476 | } | |
477 | break; | |
478 | ||
479 | case MAX34451_MFR_IOUT_PEAK: /* R/W word */ | |
480 | if (pmdev->page < 16) { | |
481 | s->iout_peak[pmdev->page] = pmbus_receive16(pmdev); | |
482 | } | |
483 | break; | |
484 | ||
485 | case MAX34451_MFR_TEMPERATURE_PEAK: /* R/W word */ | |
486 | if (15 < pmdev->page && pmdev->page < 21) { | |
487 | s->temperature_peak[pmdev->page % 16] | |
488 | = pmbus_receive16(pmdev); | |
489 | } | |
490 | break; | |
491 | ||
492 | case MAX34451_MFR_VOUT_MIN: /* R/W word */ | |
493 | if (pmdev->page < 16) { | |
494 | s->vout_min[pmdev->page] = pmbus_receive16(pmdev); | |
495 | } | |
496 | break; | |
497 | ||
498 | case MAX34451_MFR_NV_LOG_CONFIG: /* R/W word */ | |
499 | s->nv_log_config = pmbus_receive16(pmdev); | |
500 | break; | |
501 | ||
502 | case MAX34451_MFR_FAULT_RESPONSE: /* R/W 32 */ | |
503 | if (pmdev->page < 16) { | |
504 | s->fault_response[pmdev->page] = pmbus_receive32(pmdev); | |
505 | } | |
506 | break; | |
507 | ||
508 | case MAX34451_MFR_FAULT_RETRY: /* R/W word */ | |
509 | s->fault_retry = pmbus_receive16(pmdev); | |
510 | break; | |
511 | ||
512 | case MAX34451_MFR_TIME_COUNT: /* R/W 32 */ | |
513 | s->time_count = pmbus_receive32(pmdev); | |
514 | break; | |
515 | ||
516 | case MAX34451_MFR_MARGIN_CONFIG: /* R/W word */ | |
517 | if (pmdev->page < 12) { | |
518 | s->margin_config[pmdev->page] = pmbus_receive16(pmdev); | |
519 | } | |
520 | break; | |
521 | ||
522 | case MAX34451_MFR_CHANNEL_CONFIG: /* R/W word */ | |
523 | if (pmdev->page < 16) { | |
524 | s->channel_config[pmdev->page] = pmbus_receive16(pmdev); | |
525 | } | |
526 | break; | |
527 | ||
528 | case MAX34451_MFR_TON_SEQ_MAX: /* R/W word */ | |
529 | if (pmdev->page < 12) { | |
530 | s->ton_seq_max[pmdev->page] = pmbus_receive16(pmdev); | |
531 | } | |
532 | break; | |
533 | ||
534 | case MAX34451_MFR_PWM_CONFIG: /* R/W 32 */ | |
535 | if (pmdev->page < 12) { | |
536 | s->pwm_config[pmdev->page] = pmbus_receive32(pmdev); | |
537 | } | |
538 | break; | |
539 | ||
540 | case MAX34451_MFR_SEQ_CONFIG: /* R/W 32 */ | |
541 | if (pmdev->page < 12) { | |
542 | s->seq_config[pmdev->page] = pmbus_receive32(pmdev); | |
543 | } | |
544 | break; | |
545 | ||
546 | case MAX34451_MFR_TEMP_SENSOR_CONFIG: /* R/W word */ | |
547 | if (15 < pmdev->page && pmdev->page < 21) { | |
548 | s->temp_sensor_config[pmdev->page % 16] | |
549 | = pmbus_receive16(pmdev); | |
550 | } | |
551 | break; | |
552 | ||
553 | case MAX34451_MFR_CRC: /* R/W word */ | |
554 | s->crc = pmbus_receive16(pmdev); | |
555 | break; | |
556 | ||
557 | case MAX34451_MFR_NV_FAULT_LOG: | |
558 | case MAX34451_MFR_FW_SERIAL: | |
559 | case MAX34451_MFR_IOUT_AVG: | |
560 | /* Read only commands */ | |
561 | pmdev->pages[index].status_word |= PMBUS_STATUS_CML; | |
562 | pmdev->pages[index].status_cml |= PB_CML_FAULT_INVALID_DATA; | |
563 | qemu_log_mask(LOG_GUEST_ERROR, | |
564 | "%s: writing to read-only register 0x%02x\n", | |
565 | __func__, pmdev->code); | |
566 | break; | |
567 | ||
568 | default: | |
569 | qemu_log_mask(LOG_GUEST_ERROR, | |
570 | "%s: writing to unsupported register: 0x%02x\n", | |
571 | __func__, pmdev->code); | |
572 | break; | |
573 | } | |
574 | ||
575 | return 0; | |
576 | } | |
577 | ||
578 | static void max34451_get(Object *obj, Visitor *v, const char *name, | |
579 | void *opaque, Error **errp) | |
580 | { | |
581 | visit_type_uint16(v, name, (uint16_t *)opaque, errp); | |
582 | } | |
583 | ||
584 | static void max34451_set(Object *obj, Visitor *v, const char *name, | |
585 | void *opaque, Error **errp) | |
586 | { | |
587 | MAX34451State *s = MAX34451(obj); | |
588 | uint16_t *internal = opaque; | |
589 | uint16_t value; | |
590 | if (!visit_type_uint16(v, name, &value, errp)) { | |
591 | return; | |
592 | } | |
593 | ||
594 | *internal = value; | |
595 | max34451_check_limits(s); | |
596 | } | |
597 | ||
598 | /* used to init uint16_t arrays */ | |
599 | static inline void *memset_word(void *s, uint16_t c, size_t n) | |
600 | { | |
601 | size_t i; | |
602 | uint16_t *p = s; | |
603 | ||
604 | for (i = 0; i < n; i++) { | |
605 | p[i] = c; | |
606 | } | |
607 | ||
608 | return s; | |
609 | } | |
610 | ||
611 | static void max34451_exit_reset(Object *obj) | |
612 | { | |
613 | PMBusDevice *pmdev = PMBUS_DEVICE(obj); | |
614 | MAX34451State *s = MAX34451(obj); | |
615 | pmdev->capability = DEFAULT_CAPABILITY; | |
616 | ||
617 | for (int i = 0; i < MAX34451_NUM_PAGES; i++) { | |
618 | pmdev->pages[i].operation = DEFAULT_OP_ON; | |
619 | pmdev->pages[i].on_off_config = DEFAULT_ON_OFF_CONFIG; | |
620 | pmdev->pages[i].revision = 0x11; | |
621 | pmdev->pages[i].vout_mode = DEFAULT_VOUT_MODE; | |
622 | } | |
623 | ||
624 | for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { | |
625 | pmdev->pages[i].vout_scale_monitor = DEFAULT_SCALE; | |
626 | pmdev->pages[i].vout_ov_fault_limit = DEFAULT_OV_LIMIT; | |
627 | pmdev->pages[i].vout_ov_warn_limit = DEFAULT_OV_LIMIT; | |
628 | pmdev->pages[i].iout_oc_warn_limit = DEFAULT_OC_LIMIT; | |
629 | pmdev->pages[i].iout_oc_fault_limit = DEFAULT_OC_LIMIT; | |
630 | } | |
631 | ||
632 | for (int i = 0; i < MAX34451_NUM_MARGINED_PSU; i++) { | |
633 | pmdev->pages[i].ton_max_fault_limit = DEFAULT_TON_FAULT_LIMIT; | |
634 | } | |
635 | ||
636 | for (int i = 16; i < MAX34451_NUM_TEMP_DEVICES + 16; i++) { | |
637 | pmdev->pages[i].read_temperature_1 = DEFAULT_TEMPERATURE; | |
638 | pmdev->pages[i].ot_warn_limit = DEFAULT_OT_LIMIT; | |
639 | pmdev->pages[i].ot_fault_limit = DEFAULT_OT_LIMIT; | |
640 | } | |
641 | ||
642 | memset_word(s->ton_max_fault_limit, DEFAULT_TON_FAULT_LIMIT, | |
643 | MAX34451_NUM_MARGINED_PSU); | |
644 | memset_word(s->channel_config, DEFAULT_CHANNEL_CONFIG, | |
645 | MAX34451_NUM_PWR_DEVICES); | |
646 | memset_word(s->vout_min, DEFAULT_VMIN, MAX34451_NUM_PWR_DEVICES); | |
647 | ||
648 | s->mfr_location = DEFAULT_TEXT; | |
649 | s->mfr_date = DEFAULT_TEXT; | |
650 | s->mfr_serial = DEFAULT_TEXT; | |
651 | } | |
652 | ||
653 | static const VMStateDescription vmstate_max34451 = { | |
654 | .name = TYPE_MAX34451, | |
655 | .version_id = 0, | |
656 | .minimum_version_id = 0, | |
657 | .fields = (VMStateField[]){ | |
658 | VMSTATE_PMBUS_DEVICE(parent, MAX34451State), | |
659 | VMSTATE_UINT16_ARRAY(power_good_on, MAX34451State, | |
660 | MAX34451_NUM_PWR_DEVICES), | |
661 | VMSTATE_UINT16_ARRAY(power_good_off, MAX34451State, | |
662 | MAX34451_NUM_PWR_DEVICES), | |
663 | VMSTATE_UINT16_ARRAY(ton_delay, MAX34451State, | |
664 | MAX34451_NUM_MARGINED_PSU), | |
665 | VMSTATE_UINT16_ARRAY(ton_max_fault_limit, MAX34451State, | |
666 | MAX34451_NUM_MARGINED_PSU), | |
667 | VMSTATE_UINT16_ARRAY(toff_delay, MAX34451State, | |
668 | MAX34451_NUM_MARGINED_PSU), | |
669 | VMSTATE_UINT8_ARRAY(status_mfr_specific, MAX34451State, | |
670 | MAX34451_NUM_PWR_DEVICES), | |
671 | VMSTATE_UINT64(mfr_location, MAX34451State), | |
672 | VMSTATE_UINT64(mfr_date, MAX34451State), | |
673 | VMSTATE_UINT64(mfr_serial, MAX34451State), | |
674 | VMSTATE_UINT16(mfr_mode, MAX34451State), | |
675 | VMSTATE_UINT32_ARRAY(psen_config, MAX34451State, | |
676 | MAX34451_NUM_MARGINED_PSU), | |
677 | VMSTATE_UINT16_ARRAY(vout_peak, MAX34451State, | |
678 | MAX34451_NUM_PWR_DEVICES), | |
679 | VMSTATE_UINT16_ARRAY(iout_peak, MAX34451State, | |
680 | MAX34451_NUM_PWR_DEVICES), | |
681 | VMSTATE_UINT16_ARRAY(temperature_peak, MAX34451State, | |
682 | MAX34451_NUM_TEMP_DEVICES), | |
683 | VMSTATE_UINT16_ARRAY(vout_min, MAX34451State, MAX34451_NUM_PWR_DEVICES), | |
684 | VMSTATE_UINT16(nv_log_config, MAX34451State), | |
685 | VMSTATE_UINT32_ARRAY(fault_response, MAX34451State, | |
686 | MAX34451_NUM_PWR_DEVICES), | |
687 | VMSTATE_UINT16(fault_retry, MAX34451State), | |
688 | VMSTATE_UINT32(fault_log, MAX34451State), | |
689 | VMSTATE_UINT32(time_count, MAX34451State), | |
690 | VMSTATE_UINT16_ARRAY(margin_config, MAX34451State, | |
691 | MAX34451_NUM_MARGINED_PSU), | |
692 | VMSTATE_UINT16(fw_serial, MAX34451State), | |
693 | VMSTATE_UINT16_ARRAY(iout_avg, MAX34451State, MAX34451_NUM_PWR_DEVICES), | |
694 | VMSTATE_UINT16_ARRAY(channel_config, MAX34451State, | |
695 | MAX34451_NUM_PWR_DEVICES), | |
696 | VMSTATE_UINT16_ARRAY(ton_seq_max, MAX34451State, | |
697 | MAX34451_NUM_MARGINED_PSU), | |
698 | VMSTATE_UINT32_ARRAY(pwm_config, MAX34451State, | |
699 | MAX34451_NUM_MARGINED_PSU), | |
700 | VMSTATE_UINT32_ARRAY(seq_config, MAX34451State, | |
701 | MAX34451_NUM_MARGINED_PSU), | |
702 | VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX34451State, | |
703 | MAX34451_NUM_TEMP_DEVICES), | |
704 | VMSTATE_UINT16(store_single, MAX34451State), | |
705 | VMSTATE_UINT16(crc, MAX34451State), | |
706 | VMSTATE_END_OF_LIST() | |
707 | } | |
708 | }; | |
709 | ||
710 | static void max34451_init(Object *obj) | |
711 | { | |
712 | PMBusDevice *pmdev = PMBUS_DEVICE(obj); | |
713 | uint64_t psu_flags = PB_HAS_VOUT | PB_HAS_IOUT | PB_HAS_VOUT_MODE | | |
714 | PB_HAS_IOUT_GAIN; | |
715 | ||
716 | for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { | |
717 | pmbus_page_config(pmdev, i, psu_flags); | |
718 | } | |
719 | ||
720 | for (int i = 0; i < MAX34451_NUM_MARGINED_PSU; i++) { | |
721 | pmbus_page_config(pmdev, i, psu_flags | PB_HAS_VOUT_MARGIN); | |
722 | } | |
723 | ||
724 | for (int i = 16; i < MAX34451_NUM_TEMP_DEVICES + 16; i++) { | |
725 | pmbus_page_config(pmdev, i, PB_HAS_TEMPERATURE | PB_HAS_VOUT_MODE); | |
726 | } | |
727 | ||
728 | /* get and set the voltage in millivolts, max is 32767 mV */ | |
729 | for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) { | |
730 | object_property_add(obj, "vout[*]", "uint16", | |
731 | max34451_get, | |
732 | max34451_set, NULL, &pmdev->pages[i].read_vout); | |
733 | } | |
734 | ||
735 | /* | |
736 | * get and set the temperature of the internal temperature sensor in | |
737 | * centidegrees Celcius i.e.: 2500 -> 25.00 C, max is 327.67 C | |
738 | */ | |
739 | for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) { | |
740 | object_property_add(obj, "temperature[*]", "uint16", | |
741 | max34451_get, | |
742 | max34451_set, | |
743 | NULL, | |
744 | &pmdev->pages[i + 16].read_temperature_1); | |
745 | } | |
746 | ||
747 | } | |
748 | ||
749 | static void max34451_class_init(ObjectClass *klass, void *data) | |
750 | { | |
751 | ResettableClass *rc = RESETTABLE_CLASS(klass); | |
752 | DeviceClass *dc = DEVICE_CLASS(klass); | |
753 | PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass); | |
754 | dc->desc = "Maxim MAX34451 16-Channel V/I monitor"; | |
755 | dc->vmsd = &vmstate_max34451; | |
756 | k->write_data = max34451_write_data; | |
757 | k->receive_byte = max34451_read_byte; | |
758 | k->device_num_pages = MAX34451_NUM_PAGES; | |
759 | rc->phases.exit = max34451_exit_reset; | |
760 | } | |
761 | ||
762 | static const TypeInfo max34451_info = { | |
763 | .name = TYPE_MAX34451, | |
764 | .parent = TYPE_PMBUS_DEVICE, | |
765 | .instance_size = sizeof(MAX34451State), | |
766 | .instance_init = max34451_init, | |
767 | .class_init = max34451_class_init, | |
768 | }; | |
769 | ||
770 | static void max34451_register_types(void) | |
771 | { | |
772 | type_register_static(&max34451_info); | |
773 | } | |
774 | ||
775 | type_init(max34451_register_types) |