target/loongarch: Implement vmskltz/vmskgez/vmsknz
[qemu/kevin.git] / hw / i2c / pmbus_device.c
blob44fe4eddbbdc67755c8074471c6de4fc4142c0ed
1 /*
2 * PMBus wrapper over SMBus
4 * Copyright 2021 Google LLC
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 #include "qemu/osdep.h"
10 #include <math.h>
11 #include "hw/i2c/pmbus_device.h"
12 #include "migration/vmstate.h"
13 #include "qemu/module.h"
14 #include "qemu/log.h"
16 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value)
18 /* R is usually negative to fit large readings into 16 bits */
19 uint16_t y = (c.m * value + c.b) * pow(10, c.R);
20 return y;
23 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value)
25 /* X = (Y * 10^-R - b) / m */
26 uint32_t x = (value / pow(10, c.R) - c.b) / c.m;
27 return x;
30 uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
32 /* L = D * 2^(-e) */
33 if (exp < 0) {
34 return value << (-exp);
36 return value >> exp;
39 uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
41 /* D = L * 2^e */
42 if (exp < 0) {
43 return value >> (-exp);
45 return value << exp;
48 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
50 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
51 qemu_log_mask(LOG_GUEST_ERROR,
52 "PMBus device tried to send too much data");
53 len = 0;
56 for (int i = len - 1; i >= 0; i--) {
57 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1];
59 pmdev->out_buf_len += len;
62 /* Internal only, convert unsigned ints to the little endian bus */
63 static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size)
65 uint8_t bytes[8];
66 g_assert(size <= 8);
68 for (int i = 0; i < size; i++) {
69 bytes[i] = data & 0xFF;
70 data = data >> 8;
72 pmbus_send(pmdev, bytes, size);
75 void pmbus_send8(PMBusDevice *pmdev, uint8_t data)
77 pmbus_send_uint(pmdev, data, 1);
80 void pmbus_send16(PMBusDevice *pmdev, uint16_t data)
82 pmbus_send_uint(pmdev, data, 2);
85 void pmbus_send32(PMBusDevice *pmdev, uint32_t data)
87 pmbus_send_uint(pmdev, data, 4);
90 void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
92 pmbus_send_uint(pmdev, data, 8);
95 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
97 if (!data) {
98 qemu_log_mask(LOG_GUEST_ERROR,
99 "%s: %s: uninitialised read from 0x%02x\n",
100 __func__, DEVICE(pmdev)->canonical_path, pmdev->code);
101 return;
104 size_t len = strlen(data);
105 g_assert(len > 0);
106 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
107 pmdev->out_buf[len + pmdev->out_buf_len] = len;
109 for (int i = len - 1; i >= 0; i--) {
110 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i];
112 pmdev->out_buf_len += len + 1;
116 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
118 uint64_t ret = 0;
120 /* Exclude command code from return value */
121 pmdev->in_buf++;
122 pmdev->in_buf_len--;
124 for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
125 ret = ret << 8 | pmdev->in_buf[i];
127 return ret;
130 uint8_t pmbus_receive8(PMBusDevice *pmdev)
132 if (pmdev->in_buf_len - 1 != 1) {
133 qemu_log_mask(LOG_GUEST_ERROR,
134 "%s: length mismatch. Expected 1 byte, got %d bytes\n",
135 __func__, pmdev->in_buf_len - 1);
137 return pmbus_receive_uint(pmdev);
140 uint16_t pmbus_receive16(PMBusDevice *pmdev)
142 if (pmdev->in_buf_len - 1 != 2) {
143 qemu_log_mask(LOG_GUEST_ERROR,
144 "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
145 __func__, pmdev->in_buf_len - 1);
147 return pmbus_receive_uint(pmdev);
150 uint32_t pmbus_receive32(PMBusDevice *pmdev)
152 if (pmdev->in_buf_len - 1 != 4) {
153 qemu_log_mask(LOG_GUEST_ERROR,
154 "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
155 __func__, pmdev->in_buf_len - 1);
157 return pmbus_receive_uint(pmdev);
160 uint64_t pmbus_receive64(PMBusDevice *pmdev)
162 if (pmdev->in_buf_len - 1 != 8) {
163 qemu_log_mask(LOG_GUEST_ERROR,
164 "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
165 __func__, pmdev->in_buf_len - 1);
167 return pmbus_receive_uint(pmdev);
170 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
172 if (pmdev->out_buf_len == 0) {
173 qemu_log_mask(LOG_GUEST_ERROR,
174 "%s: tried to read from empty buffer",
175 __func__);
176 return PMBUS_ERR_BYTE;
178 uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1];
179 pmdev->out_buf_len--;
180 return data;
183 static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read)
185 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
186 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
188 if (pmdc->quick_cmd) {
189 pmdc->quick_cmd(pmdev, read);
193 static void pmbus_pages_alloc(PMBusDevice *pmdev)
195 /* some PMBus devices don't use the PAGE command, so they get 1 page */
196 PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev);
197 if (k->device_num_pages == 0) {
198 k->device_num_pages = 1;
200 pmdev->num_pages = k->device_num_pages;
201 pmdev->pages = g_new0(PMBusPage, k->device_num_pages);
204 void pmbus_check_limits(PMBusDevice *pmdev)
206 for (int i = 0; i < pmdev->num_pages; i++) {
207 if ((pmdev->pages[i].operation & PB_OP_ON) == 0) {
208 continue; /* don't check powered off devices */
211 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) {
212 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
213 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT;
216 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) {
217 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
218 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN;
221 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) {
222 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
223 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN;
226 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) {
227 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
228 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT;
231 if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) {
232 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
233 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN;
236 if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) {
237 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
238 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN;
241 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) {
242 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
243 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN;
246 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) {
247 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
248 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT;
251 if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) {
252 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
253 pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN;
256 if (pmdev->pages[i].read_temperature_1
257 > pmdev->pages[i].ot_fault_limit) {
258 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
259 pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT;
262 if (pmdev->pages[i].read_temperature_1
263 > pmdev->pages[i].ot_warn_limit) {
264 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
265 pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN;
270 void pmbus_idle(PMBusDevice *pmdev)
272 pmdev->code = PMBUS_IDLE_STATE;
275 /* assert the status_cml error upon receipt of malformed command */
276 static void pmbus_cml_error(PMBusDevice *pmdev)
278 for (int i = 0; i < pmdev->num_pages; i++) {
279 pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
280 pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
284 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
286 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
287 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
288 uint8_t ret = PMBUS_ERR_BYTE;
289 uint8_t index;
291 if (pmdev->out_buf_len != 0) {
292 ret = pmbus_out_buf_pop(pmdev);
293 return ret;
297 * Reading from all pages will return the value from page 0,
298 * means that all subsequent commands are to be applied to all output.
300 if (pmdev->page == PB_ALL_PAGES) {
301 index = 0;
302 } else if (pmdev->page > pmdev->num_pages - 1) {
303 qemu_log_mask(LOG_GUEST_ERROR,
304 "%s: page %d is out of range\n",
305 __func__, pmdev->page);
306 pmbus_cml_error(pmdev);
307 return PMBUS_ERR_BYTE;
308 } else {
309 index = pmdev->page;
312 switch (pmdev->code) {
313 case PMBUS_PAGE:
314 pmbus_send8(pmdev, pmdev->page);
315 break;
317 case PMBUS_OPERATION: /* R/W byte */
318 pmbus_send8(pmdev, pmdev->pages[index].operation);
319 break;
321 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
322 pmbus_send8(pmdev, pmdev->pages[index].on_off_config);
323 break;
325 case PMBUS_PHASE: /* R/W byte */
326 pmbus_send8(pmdev, pmdev->pages[index].phase);
327 break;
329 case PMBUS_WRITE_PROTECT: /* R/W byte */
330 pmbus_send8(pmdev, pmdev->pages[index].write_protect);
331 break;
333 case PMBUS_CAPABILITY:
334 pmbus_send8(pmdev, pmdev->capability);
335 if (pmdev->capability & BIT(7)) {
336 qemu_log_mask(LOG_UNIMP,
337 "%s: PEC is enabled but not yet supported.\n",
338 __func__);
340 break;
342 case PMBUS_VOUT_MODE: /* R/W byte */
343 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
344 pmbus_send8(pmdev, pmdev->pages[index].vout_mode);
345 } else {
346 goto passthough;
348 break;
350 case PMBUS_VOUT_COMMAND: /* R/W word */
351 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
352 pmbus_send16(pmdev, pmdev->pages[index].vout_command);
353 } else {
354 goto passthough;
356 break;
358 case PMBUS_VOUT_TRIM: /* R/W word */
359 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
360 pmbus_send16(pmdev, pmdev->pages[index].vout_trim);
361 } else {
362 goto passthough;
364 break;
366 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
367 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
368 pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset);
369 } else {
370 goto passthough;
372 break;
374 case PMBUS_VOUT_MAX: /* R/W word */
375 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
376 pmbus_send16(pmdev, pmdev->pages[index].vout_max);
377 } else {
378 goto passthough;
380 break;
382 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
383 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
384 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high);
385 } else {
386 goto passthough;
388 break;
390 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
391 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
392 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low);
393 } else {
394 goto passthough;
396 break;
398 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
399 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
400 pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate);
401 } else {
402 goto passthough;
404 break;
406 case PMBUS_VOUT_DROOP: /* R/W word */
407 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
408 pmbus_send16(pmdev, pmdev->pages[index].vout_droop);
409 } else {
410 goto passthough;
412 break;
414 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
415 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
416 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop);
417 } else {
418 goto passthough;
420 break;
422 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
423 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
424 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor);
425 } else {
426 goto passthough;
428 break;
430 case PMBUS_VOUT_MIN: /* R/W word */
431 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
432 pmbus_send16(pmdev, pmdev->pages[index].vout_min);
433 } else {
434 goto passthough;
436 break;
438 /* TODO: implement coefficients support */
440 case PMBUS_POUT_MAX: /* R/W word */
441 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
442 pmbus_send16(pmdev, pmdev->pages[index].pout_max);
443 } else {
444 goto passthough;
446 break;
448 case PMBUS_VIN_ON: /* R/W word */
449 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
450 pmbus_send16(pmdev, pmdev->pages[index].vin_on);
451 } else {
452 goto passthough;
454 break;
456 case PMBUS_VIN_OFF: /* R/W word */
457 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
458 pmbus_send16(pmdev, pmdev->pages[index].vin_off);
459 } else {
460 goto passthough;
462 break;
464 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
465 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
466 pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain);
467 } else {
468 goto passthough;
470 break;
472 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
473 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
474 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
475 } else {
476 goto passthough;
478 break;
480 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
481 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
482 pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response);
483 } else {
484 goto passthough;
486 break;
488 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
489 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
490 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit);
491 } else {
492 goto passthough;
494 break;
496 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
497 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
498 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit);
499 } else {
500 goto passthough;
502 break;
504 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
505 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
506 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit);
507 } else {
508 goto passthough;
510 break;
512 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
513 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
514 pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response);
515 } else {
516 goto passthough;
518 break;
520 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
521 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
522 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit);
523 } else {
524 goto passthough;
526 break;
528 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
529 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
530 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response);
531 } else {
532 goto passthough;
534 break;
536 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
537 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
538 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit);
539 } else {
540 goto passthough;
542 break;
544 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
545 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
546 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response);
547 } else {
548 goto passthough;
550 break;
552 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
553 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
554 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit);
555 } else {
556 goto passthough;
558 break;
560 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
561 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
562 pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit);
563 } else {
564 goto passthough;
566 break;
568 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
569 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
570 pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response);
571 } else {
572 goto passthough;
574 break;
576 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
577 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
578 pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit);
579 } else {
580 goto passthough;
582 break;
584 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
585 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
586 pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response);
587 } else {
588 goto passthough;
590 break;
592 case PMBUS_OT_WARN_LIMIT: /* R/W word */
593 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
594 pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit);
595 } else {
596 goto passthough;
598 break;
600 case PMBUS_UT_WARN_LIMIT: /* R/W word */
601 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
602 pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit);
603 } else {
604 goto passthough;
606 break;
608 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
609 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
610 pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit);
611 } else {
612 goto passthough;
614 break;
616 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
617 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
618 pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response);
619 } else {
620 goto passthough;
622 break;
624 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
625 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
626 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit);
627 } else {
628 goto passthough;
630 break;
632 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
633 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
634 pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response);
635 } else {
636 goto passthough;
638 break;
640 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
641 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
642 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit);
643 } else {
644 goto passthough;
646 break;
648 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
649 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
650 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit);
651 } else {
652 goto passthough;
654 break;
656 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
657 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
658 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit);
659 } else {
660 goto passthough;
662 break;
664 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
665 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
666 pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response);
667 } else {
668 goto passthough;
670 break;
672 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
673 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
674 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit);
675 } else {
676 goto passthough;
678 break;
680 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
681 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
682 pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response);
683 } else {
684 goto passthough;
686 break;
688 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
689 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
690 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit);
691 } else {
692 goto passthough;
694 break;
696 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
697 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
698 pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit);
699 } else {
700 goto passthough;
702 break;
704 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
705 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
706 pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response);
707 } else {
708 goto passthough;
710 break;
712 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
713 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
714 pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit);
715 } else {
716 goto passthough;
718 break;
720 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
721 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
722 pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit);
723 } else {
724 goto passthough;
726 break;
728 case PMBUS_STATUS_BYTE: /* R/W byte */
729 pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF);
730 break;
732 case PMBUS_STATUS_WORD: /* R/W word */
733 pmbus_send16(pmdev, pmdev->pages[index].status_word);
734 break;
736 case PMBUS_STATUS_VOUT: /* R/W byte */
737 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
738 pmbus_send8(pmdev, pmdev->pages[index].status_vout);
739 } else {
740 goto passthough;
742 break;
744 case PMBUS_STATUS_IOUT: /* R/W byte */
745 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
746 pmbus_send8(pmdev, pmdev->pages[index].status_iout);
747 } else {
748 goto passthough;
750 break;
752 case PMBUS_STATUS_INPUT: /* R/W byte */
753 if (pmdev->pages[index].page_flags & PB_HAS_VIN ||
754 pmdev->pages[index].page_flags & PB_HAS_IIN ||
755 pmdev->pages[index].page_flags & PB_HAS_PIN) {
756 pmbus_send8(pmdev, pmdev->pages[index].status_input);
757 } else {
758 goto passthough;
760 break;
762 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
763 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
764 pmbus_send8(pmdev, pmdev->pages[index].status_temperature);
765 } else {
766 goto passthough;
768 break;
770 case PMBUS_STATUS_CML: /* R/W byte */
771 pmbus_send8(pmdev, pmdev->pages[index].status_cml);
772 break;
774 case PMBUS_STATUS_OTHER: /* R/W byte */
775 pmbus_send8(pmdev, pmdev->pages[index].status_other);
776 break;
778 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
779 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
780 break;
782 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
783 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
784 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
785 } else {
786 goto passthough;
788 break;
790 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
791 if (pmdev->pages[index].page_flags & PB_HAS_EOUT) {
792 pmbus_send(pmdev, pmdev->pages[index].read_eout, 5);
793 } else {
794 goto passthough;
796 break;
798 case PMBUS_READ_VIN: /* Read-Only word */
799 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
800 pmbus_send16(pmdev, pmdev->pages[index].read_vin);
801 } else {
802 goto passthough;
804 break;
806 case PMBUS_READ_IIN: /* Read-Only word */
807 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
808 pmbus_send16(pmdev, pmdev->pages[index].read_iin);
809 } else {
810 goto passthough;
812 break;
814 case PMBUS_READ_VOUT: /* Read-Only word */
815 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
816 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
817 } else {
818 goto passthough;
820 break;
822 case PMBUS_READ_IOUT: /* Read-Only word */
823 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
824 pmbus_send16(pmdev, pmdev->pages[index].read_iout);
825 } else {
826 goto passthough;
828 break;
830 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
831 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
832 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1);
833 } else {
834 goto passthough;
836 break;
838 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
839 if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) {
840 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2);
841 } else {
842 goto passthough;
844 break;
846 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
847 if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) {
848 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3);
849 } else {
850 goto passthough;
852 break;
854 case PMBUS_READ_POUT: /* Read-Only word */
855 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
856 pmbus_send16(pmdev, pmdev->pages[index].read_pout);
857 } else {
858 goto passthough;
860 break;
862 case PMBUS_READ_PIN: /* Read-Only word */
863 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
864 pmbus_send16(pmdev, pmdev->pages[index].read_pin);
865 } else {
866 goto passthough;
868 break;
870 case PMBUS_REVISION: /* Read-Only byte */
871 pmbus_send8(pmdev, pmdev->pages[index].revision);
872 break;
874 case PMBUS_MFR_ID: /* R/W block */
875 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
876 pmbus_send_string(pmdev, pmdev->pages[index].mfr_id);
877 } else {
878 goto passthough;
880 break;
882 case PMBUS_MFR_MODEL: /* R/W block */
883 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
884 pmbus_send_string(pmdev, pmdev->pages[index].mfr_model);
885 } else {
886 goto passthough;
888 break;
890 case PMBUS_MFR_REVISION: /* R/W block */
891 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
892 pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision);
893 } else {
894 goto passthough;
896 break;
898 case PMBUS_MFR_LOCATION: /* R/W block */
899 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
900 pmbus_send_string(pmdev, pmdev->pages[index].mfr_location);
901 } else {
902 goto passthough;
904 break;
906 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
907 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
908 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min);
909 } else {
910 goto passthough;
912 break;
914 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
915 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
916 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max);
917 } else {
918 goto passthough;
920 break;
922 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
923 if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) {
924 pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max);
925 } else {
926 goto passthough;
928 break;
930 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
931 if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) {
932 pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max);
933 } else {
934 goto passthough;
936 break;
938 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
939 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
940 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min);
941 } else {
942 goto passthough;
944 break;
946 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
947 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
948 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max);
949 } else {
950 goto passthough;
952 break;
954 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
955 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) {
956 pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max);
957 } else {
958 goto passthough;
960 break;
962 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
963 if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) {
964 pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max);
965 } else {
966 goto passthough;
968 break;
970 case PMBUS_MFR_MAX_TEMP_1: /* R/W word */
971 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
972 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1);
973 } else {
974 goto passthough;
976 break;
978 case PMBUS_MFR_MAX_TEMP_2: /* R/W word */
979 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
980 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2);
981 } else {
982 goto passthough;
984 break;
986 case PMBUS_MFR_MAX_TEMP_3: /* R/W word */
987 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
988 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3);
989 } else {
990 goto passthough;
992 break;
994 case PMBUS_IDLE_STATE:
995 pmbus_send8(pmdev, PMBUS_ERR_BYTE);
996 break;
998 case PMBUS_CLEAR_FAULTS: /* Send Byte */
999 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1000 case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
1001 case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */
1002 case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */
1003 case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */
1004 case PMBUS_STORE_USER_ALL: /* Send Byte */
1005 case PMBUS_RESTORE_USER_ALL: /* Send Byte */
1006 case PMBUS_STORE_USER_CODE: /* Write-only Byte */
1007 case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */
1008 case PMBUS_QUERY: /* Write-Only */
1009 qemu_log_mask(LOG_GUEST_ERROR,
1010 "%s: reading from write only register 0x%02x\n",
1011 __func__, pmdev->code);
1012 break;
1014 passthough:
1015 default:
1016 /* Pass through read request if not handled */
1017 if (pmdc->receive_byte) {
1018 ret = pmdc->receive_byte(pmdev);
1020 break;
1023 if (pmdev->out_buf_len != 0) {
1024 ret = pmbus_out_buf_pop(pmdev);
1025 return ret;
1028 return ret;
1032 * PMBus clear faults command applies to all status registers, existing faults
1033 * should separately get re-asserted.
1035 static void pmbus_clear_faults(PMBusDevice *pmdev)
1037 for (uint8_t i = 0; i < pmdev->num_pages; i++) {
1038 pmdev->pages[i].status_word = 0;
1039 pmdev->pages[i].status_vout = 0;
1040 pmdev->pages[i].status_iout = 0;
1041 pmdev->pages[i].status_input = 0;
1042 pmdev->pages[i].status_temperature = 0;
1043 pmdev->pages[i].status_cml = 0;
1044 pmdev->pages[i].status_other = 0;
1045 pmdev->pages[i].status_mfr_specific = 0;
1046 pmdev->pages[i].status_fans_1_2 = 0;
1047 pmdev->pages[i].status_fans_3_4 = 0;
1053 * PMBus operation is used to turn On and Off PSUs
1054 * Therefore, default value for the Operation should be PB_OP_ON or 0x80
1056 static void pmbus_operation(PMBusDevice *pmdev)
1058 uint8_t index = pmdev->page;
1059 if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
1060 pmdev->pages[index].read_vout = 0;
1061 pmdev->pages[index].read_iout = 0;
1062 pmdev->pages[index].read_pout = 0;
1063 return;
1066 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
1067 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
1070 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
1071 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
1073 pmbus_check_limits(pmdev);
1076 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
1078 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
1079 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
1080 int ret = 0;
1081 uint8_t index;
1083 if (len == 0) {
1084 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
1085 return PMBUS_ERR_BYTE;
1088 if (!pmdev->pages) { /* allocate memory for pages on first use */
1089 pmbus_pages_alloc(pmdev);
1092 pmdev->in_buf_len = len;
1093 pmdev->in_buf = buf;
1095 pmdev->code = buf[0]; /* PMBus command code */
1096 if (len == 1) { /* Single length writes are command codes only */
1097 return 0;
1100 if (pmdev->code == PMBUS_PAGE) {
1101 pmdev->page = pmbus_receive8(pmdev);
1102 return 0;
1105 /* loop through all the pages when 0xFF is received */
1106 if (pmdev->page == PB_ALL_PAGES) {
1107 for (int i = 0; i < pmdev->num_pages; i++) {
1108 pmdev->page = i;
1109 pmbus_write_data(smd, buf, len);
1111 pmdev->page = PB_ALL_PAGES;
1112 return 0;
1115 if (pmdev->page > pmdev->num_pages - 1) {
1116 qemu_log_mask(LOG_GUEST_ERROR,
1117 "%s: page %u is out of range\n",
1118 __func__, pmdev->page);
1119 pmdev->page = 0; /* undefined behaviour - reset to page 0 */
1120 pmbus_cml_error(pmdev);
1121 return PMBUS_ERR_BYTE;
1124 index = pmdev->page;
1126 switch (pmdev->code) {
1127 case PMBUS_OPERATION: /* R/W byte */
1128 pmdev->pages[index].operation = pmbus_receive8(pmdev);
1129 pmbus_operation(pmdev);
1130 break;
1132 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
1133 pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1134 break;
1136 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1137 pmbus_clear_faults(pmdev);
1138 break;
1140 case PMBUS_PHASE: /* R/W byte */
1141 pmdev->pages[index].phase = pmbus_receive8(pmdev);
1142 break;
1144 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1145 case PMBUS_WRITE_PROTECT: /* R/W byte */
1146 pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1147 break;
1149 case PMBUS_VOUT_MODE: /* R/W byte */
1150 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1151 pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1152 } else {
1153 goto passthrough;
1155 break;
1157 case PMBUS_VOUT_COMMAND: /* R/W word */
1158 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1159 pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1160 } else {
1161 goto passthrough;
1163 break;
1165 case PMBUS_VOUT_TRIM: /* R/W word */
1166 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1167 pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1168 } else {
1169 goto passthrough;
1171 break;
1173 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
1174 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1175 pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1176 } else {
1177 goto passthrough;
1179 break;
1181 case PMBUS_VOUT_MAX: /* R/W word */
1182 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1183 pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1184 } else {
1185 goto passthrough;
1187 break;
1189 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
1190 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1191 pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1192 } else {
1193 goto passthrough;
1195 break;
1197 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
1198 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1199 pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1200 } else {
1201 goto passthrough;
1203 break;
1205 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
1206 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1207 pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1208 } else {
1209 goto passthrough;
1211 break;
1213 case PMBUS_VOUT_DROOP: /* R/W word */
1214 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1215 pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1216 } else {
1217 goto passthrough;
1219 break;
1221 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
1222 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1223 pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1224 } else {
1225 goto passthrough;
1227 break;
1229 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
1230 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1231 pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1232 } else {
1233 goto passthrough;
1235 break;
1237 case PMBUS_VOUT_MIN: /* R/W word */
1238 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1239 pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
1240 } else {
1241 goto passthrough;
1243 break;
1245 case PMBUS_POUT_MAX: /* R/W word */
1246 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1247 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1248 } else {
1249 goto passthrough;
1251 break;
1253 case PMBUS_VIN_ON: /* R/W word */
1254 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1255 pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1256 } else {
1257 goto passthrough;
1259 break;
1261 case PMBUS_VIN_OFF: /* R/W word */
1262 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1263 pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1264 } else {
1265 goto passthrough;
1267 break;
1269 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
1270 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1271 pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1272 } else {
1273 goto passthrough;
1275 break;
1277 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
1278 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1279 pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1280 } else {
1281 goto passthrough;
1283 break;
1285 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
1286 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1287 pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1288 } else {
1289 goto passthrough;
1291 break;
1293 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
1294 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1295 pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1296 } else {
1297 goto passthrough;
1299 break;
1301 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
1302 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1303 pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1304 } else {
1305 goto passthrough;
1307 break;
1309 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
1310 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1311 pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1312 } else {
1313 goto passthrough;
1315 break;
1317 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
1318 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1319 pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1320 } else {
1321 goto passthrough;
1323 break;
1325 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
1326 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1327 pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1328 } else {
1329 goto passthrough;
1331 break;
1333 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
1334 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1335 pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1336 } else {
1337 goto passthrough;
1339 break;
1341 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
1342 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1343 pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1344 } else {
1345 goto passthrough;
1347 break;
1349 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1350 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1351 pmdev->pages[index].iout_oc_lv_fault_response
1352 = pmbus_receive8(pmdev);
1353 } else {
1354 goto passthrough;
1356 break;
1358 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
1359 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1360 pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1361 } else {
1362 goto passthrough;
1364 break;
1366 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
1367 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1368 pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1369 } else {
1370 goto passthrough;
1372 break;
1374 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
1375 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1376 pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1377 } else {
1378 goto passthrough;
1380 break;
1382 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
1383 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1384 pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1385 } else {
1386 goto passthrough;
1388 break;
1390 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
1391 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1392 pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1393 } else {
1394 goto passthrough;
1396 break;
1398 case PMBUS_OT_WARN_LIMIT: /* R/W word */
1399 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1400 pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1401 } else {
1402 goto passthrough;
1404 break;
1406 case PMBUS_UT_WARN_LIMIT: /* R/W word */
1407 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1408 pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1409 } else {
1410 goto passthrough;
1412 break;
1414 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
1415 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1416 pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1417 } else {
1418 goto passthrough;
1420 break;
1422 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
1423 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1424 pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1425 } else {
1426 goto passthrough;
1428 break;
1430 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
1431 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1432 pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1433 } else {
1434 goto passthrough;
1436 break;
1438 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
1439 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1440 pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1441 } else {
1442 goto passthrough;
1444 break;
1446 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
1447 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1448 pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1449 } else {
1450 goto passthrough;
1452 break;
1454 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
1455 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1456 pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1457 } else {
1458 goto passthrough;
1460 break;
1462 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
1463 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1464 pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1465 } else {
1466 goto passthrough;
1468 break;
1470 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
1471 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1472 pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1473 } else {
1474 goto passthrough;
1476 break;
1478 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
1479 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1480 pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1481 } else {
1482 goto passthrough;
1484 break;
1486 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
1487 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1488 pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1489 } else {
1490 goto passthrough;
1492 break;
1494 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
1495 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1496 pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1497 } else {
1498 goto passthrough;
1500 break;
1502 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
1503 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1504 pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1505 } else {
1506 goto passthrough;
1508 break;
1510 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
1511 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1512 pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1513 } else {
1514 goto passthrough;
1516 break;
1518 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
1519 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1520 pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1521 } else {
1522 goto passthrough;
1524 break;
1526 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
1527 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1528 pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1529 } else {
1530 goto passthrough;
1532 break;
1534 case PMBUS_STATUS_BYTE: /* R/W byte */
1535 pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1536 break;
1538 case PMBUS_STATUS_WORD: /* R/W word */
1539 pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1540 break;
1542 case PMBUS_STATUS_VOUT: /* R/W byte */
1543 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1544 pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1545 } else {
1546 goto passthrough;
1548 break;
1550 case PMBUS_STATUS_IOUT: /* R/W byte */
1551 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1552 pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1553 } else {
1554 goto passthrough;
1556 break;
1558 case PMBUS_STATUS_INPUT: /* R/W byte */
1559 pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1560 break;
1562 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
1563 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1564 pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1565 } else {
1566 goto passthrough;
1568 break;
1570 case PMBUS_STATUS_CML: /* R/W byte */
1571 pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1572 break;
1574 case PMBUS_STATUS_OTHER: /* R/W byte */
1575 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1576 break;
1578 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
1579 pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
1580 break;
1582 case PMBUS_PAGE_PLUS_READ: /* Block Read-only */
1583 case PMBUS_CAPABILITY: /* Read-Only byte */
1584 case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */
1585 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
1586 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
1587 case PMBUS_READ_VIN: /* Read-Only word */
1588 case PMBUS_READ_IIN: /* Read-Only word */
1589 case PMBUS_READ_VCAP: /* Read-Only word */
1590 case PMBUS_READ_VOUT: /* Read-Only word */
1591 case PMBUS_READ_IOUT: /* Read-Only word */
1592 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
1593 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
1594 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
1595 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
1596 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
1597 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
1598 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
1599 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
1600 case PMBUS_READ_FREQUENCY: /* Read-Only word */
1601 case PMBUS_READ_POUT: /* Read-Only word */
1602 case PMBUS_READ_PIN: /* Read-Only word */
1603 case PMBUS_REVISION: /* Read-Only byte */
1604 case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */
1605 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
1606 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
1607 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
1608 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
1609 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
1610 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
1611 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
1612 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
1613 case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */
1614 case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */
1615 case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */
1616 case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */
1617 case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */
1618 case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */
1619 case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */
1620 qemu_log_mask(LOG_GUEST_ERROR,
1621 "%s: writing to read-only register 0x%02x\n",
1622 __func__, pmdev->code);
1623 break;
1625 passthrough:
1626 /* Unimplimented registers get passed to the device */
1627 default:
1628 if (pmdc->write_data) {
1629 ret = pmdc->write_data(pmdev, buf, len);
1631 break;
1633 pmbus_check_limits(pmdev);
1634 pmdev->in_buf_len = 0;
1635 return ret;
1638 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1640 if (!pmdev->pages) { /* allocate memory for pages on first use */
1641 pmbus_pages_alloc(pmdev);
1644 /* The 0xFF page is special for commands applying to all pages */
1645 if (index == PB_ALL_PAGES) {
1646 for (int i = 0; i < pmdev->num_pages; i++) {
1647 pmdev->pages[i].page_flags = flags;
1649 return 0;
1652 if (index > pmdev->num_pages - 1) {
1653 qemu_log_mask(LOG_GUEST_ERROR,
1654 "%s: index %u is out of range\n",
1655 __func__, index);
1656 return -1;
1659 pmdev->pages[index].page_flags = flags;
1661 return 0;
1664 /* TODO: include pmbus page info in vmstate */
1665 const VMStateDescription vmstate_pmbus_device = {
1666 .name = TYPE_PMBUS_DEVICE,
1667 .version_id = 0,
1668 .minimum_version_id = 0,
1669 .fields = (VMStateField[]) {
1670 VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1671 VMSTATE_UINT8(num_pages, PMBusDevice),
1672 VMSTATE_UINT8(code, PMBusDevice),
1673 VMSTATE_UINT8(page, PMBusDevice),
1674 VMSTATE_UINT8(capability, PMBusDevice),
1675 VMSTATE_END_OF_LIST()
1679 static void pmbus_device_finalize(Object *obj)
1681 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1682 g_free(pmdev->pages);
1685 static void pmbus_device_class_init(ObjectClass *klass, void *data)
1687 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1689 k->quick_cmd = pmbus_quick_cmd;
1690 k->write_data = pmbus_write_data;
1691 k->receive_byte = pmbus_receive_byte;
1694 static const TypeInfo pmbus_device_type_info = {
1695 .name = TYPE_PMBUS_DEVICE,
1696 .parent = TYPE_SMBUS_DEVICE,
1697 .instance_size = sizeof(PMBusDevice),
1698 .instance_finalize = pmbus_device_finalize,
1699 .abstract = true,
1700 .class_size = sizeof(PMBusDeviceClass),
1701 .class_init = pmbus_device_class_init,
1704 static void pmbus_device_register_types(void)
1706 type_register_static(&pmbus_device_type_info);
1709 type_init(pmbus_device_register_types)