hw/i2c: pmbus: refactor uint handling
[qemu/ar7.git] / hw / i2c / pmbus_device.c
blob8cb9db0f803778400545fecc41b1f689b9683982
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 <string.h>
12 #include "hw/i2c/pmbus_device.h"
13 #include "migration/vmstate.h"
14 #include "qemu/module.h"
15 #include "qemu/log.h"
17 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value)
19 /* R is usually negative to fit large readings into 16 bits */
20 uint16_t y = (c.m * value + c.b) * pow(10, c.R);
21 return y;
24 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value)
26 /* X = (Y * 10^-R - b) / m */
27 uint32_t x = (value / pow(10, c.R) - c.b) / c.m;
28 return x;
31 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
33 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
34 qemu_log_mask(LOG_GUEST_ERROR,
35 "PMBus device tried to send too much data");
36 len = 0;
39 for (int i = len - 1; i >= 0; i--) {
40 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1];
42 pmdev->out_buf_len += len;
45 /* Internal only, convert unsigned ints to the little endian bus */
46 static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size)
48 uint8_t bytes[8];
49 g_assert(size <= 8);
51 for (int i = 0; i < size; i++) {
52 bytes[i] = data & 0xFF;
53 data = data >> 8;
55 pmbus_send(pmdev, bytes, size);
58 void pmbus_send8(PMBusDevice *pmdev, uint8_t data)
60 pmbus_send_uint(pmdev, data, 1);
63 void pmbus_send16(PMBusDevice *pmdev, uint16_t data)
65 pmbus_send_uint(pmdev, data, 2);
68 void pmbus_send32(PMBusDevice *pmdev, uint32_t data)
70 pmbus_send_uint(pmdev, data, 4);
73 void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
75 pmbus_send_uint(pmdev, data, 8);
78 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
80 size_t len = strlen(data);
81 g_assert(len > 0);
82 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
83 pmdev->out_buf[len + pmdev->out_buf_len] = len;
85 for (int i = len - 1; i >= 0; i--) {
86 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i];
88 pmdev->out_buf_len += len + 1;
92 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
94 uint64_t ret = 0;
96 /* Exclude command code from return value */
97 pmdev->in_buf++;
98 pmdev->in_buf_len--;
100 for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
101 ret = ret << 8 | pmdev->in_buf[i];
103 return ret;
106 uint8_t pmbus_receive8(PMBusDevice *pmdev)
108 if (pmdev->in_buf_len - 1 != 1) {
109 qemu_log_mask(LOG_GUEST_ERROR,
110 "%s: length mismatch. Expected 1 byte, got %d bytes\n",
111 __func__, pmdev->in_buf_len - 1);
113 return pmbus_receive_uint(pmdev);
116 uint16_t pmbus_receive16(PMBusDevice *pmdev)
118 if (pmdev->in_buf_len - 1 != 2) {
119 qemu_log_mask(LOG_GUEST_ERROR,
120 "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
121 __func__, pmdev->in_buf_len - 1);
123 return pmbus_receive_uint(pmdev);
126 uint32_t pmbus_receive32(PMBusDevice *pmdev)
128 if (pmdev->in_buf_len - 1 != 4) {
129 qemu_log_mask(LOG_GUEST_ERROR,
130 "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
131 __func__, pmdev->in_buf_len - 1);
133 return pmbus_receive_uint(pmdev);
136 uint64_t pmbus_receive64(PMBusDevice *pmdev)
138 if (pmdev->in_buf_len - 1 != 8) {
139 qemu_log_mask(LOG_GUEST_ERROR,
140 "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
141 __func__, pmdev->in_buf_len - 1);
143 return pmbus_receive_uint(pmdev);
146 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
148 if (pmdev->out_buf_len == 0) {
149 qemu_log_mask(LOG_GUEST_ERROR,
150 "%s: tried to read from empty buffer",
151 __func__);
152 return PMBUS_ERR_BYTE;
154 uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1];
155 pmdev->out_buf_len--;
156 return data;
159 static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read)
161 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
162 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
164 if (pmdc->quick_cmd) {
165 pmdc->quick_cmd(pmdev, read);
169 static void pmbus_pages_alloc(PMBusDevice *pmdev)
171 /* some PMBus devices don't use the PAGE command, so they get 1 page */
172 PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev);
173 if (k->device_num_pages == 0) {
174 k->device_num_pages = 1;
176 pmdev->num_pages = k->device_num_pages;
177 pmdev->pages = g_new0(PMBusPage, k->device_num_pages);
180 void pmbus_check_limits(PMBusDevice *pmdev)
182 for (int i = 0; i < pmdev->num_pages; i++) {
183 if ((pmdev->pages[i].operation & PB_OP_ON) == 0) {
184 continue; /* don't check powered off devices */
187 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) {
188 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
189 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT;
192 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) {
193 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
194 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN;
197 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) {
198 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
199 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN;
202 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) {
203 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
204 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT;
207 if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) {
208 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
209 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN;
212 if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) {
213 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
214 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN;
217 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) {
218 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
219 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN;
222 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) {
223 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
224 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT;
227 if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) {
228 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
229 pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN;
232 if (pmdev->pages[i].read_temperature_1
233 > pmdev->pages[i].ot_fault_limit) {
234 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
235 pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT;
238 if (pmdev->pages[i].read_temperature_1
239 > pmdev->pages[i].ot_warn_limit) {
240 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
241 pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN;
246 /* assert the status_cml error upon receipt of malformed command */
247 static void pmbus_cml_error(PMBusDevice *pmdev)
249 for (int i = 0; i < pmdev->num_pages; i++) {
250 pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
251 pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
255 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
257 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
258 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
259 uint8_t ret = PMBUS_ERR_BYTE;
260 uint8_t index;
262 if (pmdev->out_buf_len != 0) {
263 ret = pmbus_out_buf_pop(pmdev);
264 return ret;
268 * Reading from all pages will return the value from page 0,
269 * this is unspecified behaviour in general.
271 if (pmdev->page == PB_ALL_PAGES) {
272 index = 0;
273 qemu_log_mask(LOG_GUEST_ERROR,
274 "%s: tried to read from all pages\n",
275 __func__);
276 pmbus_cml_error(pmdev);
277 } else if (pmdev->page > pmdev->num_pages - 1) {
278 qemu_log_mask(LOG_GUEST_ERROR,
279 "%s: page %d is out of range\n",
280 __func__, pmdev->page);
281 pmbus_cml_error(pmdev);
282 return PMBUS_ERR_BYTE;
283 } else {
284 index = pmdev->page;
287 switch (pmdev->code) {
288 case PMBUS_PAGE:
289 pmbus_send8(pmdev, pmdev->page);
290 break;
292 case PMBUS_OPERATION: /* R/W byte */
293 pmbus_send8(pmdev, pmdev->pages[index].operation);
294 break;
296 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
297 pmbus_send8(pmdev, pmdev->pages[index].on_off_config);
298 break;
300 case PMBUS_PHASE: /* R/W byte */
301 pmbus_send8(pmdev, pmdev->pages[index].phase);
302 break;
304 case PMBUS_WRITE_PROTECT: /* R/W byte */
305 pmbus_send8(pmdev, pmdev->pages[index].write_protect);
306 break;
308 case PMBUS_CAPABILITY:
309 pmbus_send8(pmdev, pmdev->capability);
310 if (pmdev->capability & BIT(7)) {
311 qemu_log_mask(LOG_UNIMP,
312 "%s: PEC is enabled but not yet supported.\n",
313 __func__);
315 break;
317 case PMBUS_VOUT_MODE: /* R/W byte */
318 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
319 pmbus_send8(pmdev, pmdev->pages[index].vout_mode);
320 } else {
321 goto passthough;
323 break;
325 case PMBUS_VOUT_COMMAND: /* R/W word */
326 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
327 pmbus_send16(pmdev, pmdev->pages[index].vout_command);
328 } else {
329 goto passthough;
331 break;
333 case PMBUS_VOUT_TRIM: /* R/W word */
334 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
335 pmbus_send16(pmdev, pmdev->pages[index].vout_trim);
336 } else {
337 goto passthough;
339 break;
341 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
342 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
343 pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset);
344 } else {
345 goto passthough;
347 break;
349 case PMBUS_VOUT_MAX: /* R/W word */
350 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
351 pmbus_send16(pmdev, pmdev->pages[index].vout_max);
352 } else {
353 goto passthough;
355 break;
357 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
358 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
359 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high);
360 } else {
361 goto passthough;
363 break;
365 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
366 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
367 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low);
368 } else {
369 goto passthough;
371 break;
373 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
374 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
375 pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate);
376 } else {
377 goto passthough;
379 break;
381 case PMBUS_VOUT_DROOP: /* R/W word */
382 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
383 pmbus_send16(pmdev, pmdev->pages[index].vout_droop);
384 } else {
385 goto passthough;
387 break;
389 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
390 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
391 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop);
392 } else {
393 goto passthough;
395 break;
397 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
398 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
399 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor);
400 } else {
401 goto passthough;
403 break;
405 case PMBUS_VOUT_MIN: /* R/W word */
406 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
407 pmbus_send16(pmdev, pmdev->pages[index].vout_min);
408 } else {
409 goto passthough;
411 break;
413 /* TODO: implement coefficients support */
415 case PMBUS_POUT_MAX: /* R/W word */
416 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
417 pmbus_send16(pmdev, pmdev->pages[index].pout_max);
418 } else {
419 goto passthough;
421 break;
423 case PMBUS_VIN_ON: /* R/W word */
424 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
425 pmbus_send16(pmdev, pmdev->pages[index].vin_on);
426 } else {
427 goto passthough;
429 break;
431 case PMBUS_VIN_OFF: /* R/W word */
432 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
433 pmbus_send16(pmdev, pmdev->pages[index].vin_off);
434 } else {
435 goto passthough;
437 break;
439 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
440 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
441 pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain);
442 } else {
443 goto passthough;
445 break;
447 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
448 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
449 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
450 } else {
451 goto passthough;
453 break;
455 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
456 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
457 pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response);
458 } else {
459 goto passthough;
461 break;
463 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
464 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
465 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit);
466 } else {
467 goto passthough;
469 break;
471 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
472 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
473 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit);
474 } else {
475 goto passthough;
477 break;
479 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
480 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
481 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit);
482 } else {
483 goto passthough;
485 break;
487 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
488 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
489 pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response);
490 } else {
491 goto passthough;
493 break;
495 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
496 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
497 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit);
498 } else {
499 goto passthough;
501 break;
503 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
504 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
505 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response);
506 } else {
507 goto passthough;
509 break;
511 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
512 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
513 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit);
514 } else {
515 goto passthough;
517 break;
519 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
520 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
521 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response);
522 } else {
523 goto passthough;
525 break;
527 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
528 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
529 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit);
530 } else {
531 goto passthough;
533 break;
535 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
536 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
537 pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit);
538 } else {
539 goto passthough;
541 break;
543 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
544 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
545 pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response);
546 } else {
547 goto passthough;
549 break;
551 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
552 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
553 pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit);
554 } else {
555 goto passthough;
557 break;
559 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
560 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
561 pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response);
562 } else {
563 goto passthough;
565 break;
567 case PMBUS_OT_WARN_LIMIT: /* R/W word */
568 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
569 pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit);
570 } else {
571 goto passthough;
573 break;
575 case PMBUS_UT_WARN_LIMIT: /* R/W word */
576 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
577 pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit);
578 } else {
579 goto passthough;
581 break;
583 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
584 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
585 pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit);
586 } else {
587 goto passthough;
589 break;
591 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
592 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
593 pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response);
594 } else {
595 goto passthough;
597 break;
599 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
600 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
601 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit);
602 } else {
603 goto passthough;
605 break;
607 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
608 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
609 pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response);
610 } else {
611 goto passthough;
613 break;
615 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
616 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
617 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit);
618 } else {
619 goto passthough;
621 break;
623 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
624 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
625 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit);
626 } else {
627 goto passthough;
629 break;
631 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
632 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
633 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit);
634 } else {
635 goto passthough;
637 break;
639 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
640 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
641 pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response);
642 } else {
643 goto passthough;
645 break;
647 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
648 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
649 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit);
650 } else {
651 goto passthough;
653 break;
655 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
656 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
657 pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response);
658 } else {
659 goto passthough;
661 break;
663 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
664 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
665 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit);
666 } else {
667 goto passthough;
669 break;
671 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
672 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
673 pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit);
674 } else {
675 goto passthough;
677 break;
679 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
680 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
681 pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response);
682 } else {
683 goto passthough;
685 break;
687 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
688 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
689 pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit);
690 } else {
691 goto passthough;
693 break;
695 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
696 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
697 pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit);
698 } else {
699 goto passthough;
701 break;
703 case PMBUS_STATUS_BYTE: /* R/W byte */
704 pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF);
705 break;
707 case PMBUS_STATUS_WORD: /* R/W word */
708 pmbus_send16(pmdev, pmdev->pages[index].status_word);
709 break;
711 case PMBUS_STATUS_VOUT: /* R/W byte */
712 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
713 pmbus_send8(pmdev, pmdev->pages[index].status_vout);
714 } else {
715 goto passthough;
717 break;
719 case PMBUS_STATUS_IOUT: /* R/W byte */
720 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
721 pmbus_send8(pmdev, pmdev->pages[index].status_iout);
722 } else {
723 goto passthough;
725 break;
727 case PMBUS_STATUS_INPUT: /* R/W byte */
728 if (pmdev->pages[index].page_flags & PB_HAS_VIN ||
729 pmdev->pages[index].page_flags & PB_HAS_IIN ||
730 pmdev->pages[index].page_flags & PB_HAS_PIN) {
731 pmbus_send8(pmdev, pmdev->pages[index].status_input);
732 } else {
733 goto passthough;
735 break;
737 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
738 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
739 pmbus_send8(pmdev, pmdev->pages[index].status_temperature);
740 } else {
741 goto passthough;
743 break;
745 case PMBUS_STATUS_CML: /* R/W byte */
746 pmbus_send8(pmdev, pmdev->pages[index].status_cml);
747 break;
749 case PMBUS_STATUS_OTHER: /* R/W byte */
750 pmbus_send8(pmdev, pmdev->pages[index].status_other);
751 break;
753 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
754 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
755 break;
757 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
758 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
759 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
760 } else {
761 goto passthough;
763 break;
765 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
766 if (pmdev->pages[index].page_flags & PB_HAS_EOUT) {
767 pmbus_send(pmdev, pmdev->pages[index].read_eout, 5);
768 } else {
769 goto passthough;
771 break;
773 case PMBUS_READ_VIN: /* Read-Only word */
774 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
775 pmbus_send16(pmdev, pmdev->pages[index].read_vin);
776 } else {
777 goto passthough;
779 break;
781 case PMBUS_READ_IIN: /* Read-Only word */
782 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
783 pmbus_send16(pmdev, pmdev->pages[index].read_iin);
784 } else {
785 goto passthough;
787 break;
789 case PMBUS_READ_VOUT: /* Read-Only word */
790 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
791 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
792 } else {
793 goto passthough;
795 break;
797 case PMBUS_READ_IOUT: /* Read-Only word */
798 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
799 pmbus_send16(pmdev, pmdev->pages[index].read_iout);
800 } else {
801 goto passthough;
803 break;
805 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
806 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
807 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1);
808 } else {
809 goto passthough;
811 break;
813 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
814 if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) {
815 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2);
816 } else {
817 goto passthough;
819 break;
821 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
822 if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) {
823 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3);
824 } else {
825 goto passthough;
827 break;
829 case PMBUS_READ_POUT: /* Read-Only word */
830 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
831 pmbus_send16(pmdev, pmdev->pages[index].read_pout);
832 } else {
833 goto passthough;
835 break;
837 case PMBUS_READ_PIN: /* Read-Only word */
838 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
839 pmbus_send16(pmdev, pmdev->pages[index].read_pin);
840 } else {
841 goto passthough;
843 break;
845 case PMBUS_REVISION: /* Read-Only byte */
846 pmbus_send8(pmdev, pmdev->pages[index].revision);
847 break;
849 case PMBUS_MFR_ID: /* R/W block */
850 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
851 pmbus_send_string(pmdev, pmdev->pages[index].mfr_id);
852 } else {
853 goto passthough;
855 break;
857 case PMBUS_MFR_MODEL: /* R/W block */
858 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
859 pmbus_send_string(pmdev, pmdev->pages[index].mfr_model);
860 } else {
861 goto passthough;
863 break;
865 case PMBUS_MFR_REVISION: /* R/W block */
866 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
867 pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision);
868 } else {
869 goto passthough;
871 break;
873 case PMBUS_MFR_LOCATION: /* R/W block */
874 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
875 pmbus_send_string(pmdev, pmdev->pages[index].mfr_location);
876 } else {
877 goto passthough;
879 break;
881 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
882 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
883 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min);
884 } else {
885 goto passthough;
887 break;
889 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
890 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
891 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max);
892 } else {
893 goto passthough;
895 break;
897 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
898 if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) {
899 pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max);
900 } else {
901 goto passthough;
903 break;
905 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
906 if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) {
907 pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max);
908 } else {
909 goto passthough;
911 break;
913 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
914 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
915 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min);
916 } else {
917 goto passthough;
919 break;
921 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
922 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
923 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max);
924 } else {
925 goto passthough;
927 break;
929 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
930 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) {
931 pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max);
932 } else {
933 goto passthough;
935 break;
937 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
938 if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) {
939 pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max);
940 } else {
941 goto passthough;
943 break;
945 case PMBUS_MFR_MAX_TEMP_1: /* R/W word */
946 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
947 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1);
948 } else {
949 goto passthough;
951 break;
953 case PMBUS_MFR_MAX_TEMP_2: /* R/W word */
954 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
955 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2);
956 } else {
957 goto passthough;
959 break;
961 case PMBUS_MFR_MAX_TEMP_3: /* R/W word */
962 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
963 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3);
964 } else {
965 goto passthough;
967 break;
969 case PMBUS_CLEAR_FAULTS: /* Send Byte */
970 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
971 case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
972 case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */
973 case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */
974 case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */
975 case PMBUS_STORE_USER_ALL: /* Send Byte */
976 case PMBUS_RESTORE_USER_ALL: /* Send Byte */
977 case PMBUS_STORE_USER_CODE: /* Write-only Byte */
978 case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */
979 case PMBUS_QUERY: /* Write-Only */
980 qemu_log_mask(LOG_GUEST_ERROR,
981 "%s: reading from write only register 0x%02x\n",
982 __func__, pmdev->code);
983 break;
985 passthough:
986 default:
987 /* Pass through read request if not handled */
988 if (pmdc->receive_byte) {
989 ret = pmdc->receive_byte(pmdev);
991 break;
994 if (pmdev->out_buf_len != 0) {
995 ret = pmbus_out_buf_pop(pmdev);
996 return ret;
999 return ret;
1003 * PMBus clear faults command applies to all status registers, existing faults
1004 * should separately get re-asserted.
1006 static void pmbus_clear_faults(PMBusDevice *pmdev)
1008 for (uint8_t i = 0; i < pmdev->num_pages; i++) {
1009 pmdev->pages[i].status_word = 0;
1010 pmdev->pages[i].status_vout = 0;
1011 pmdev->pages[i].status_iout = 0;
1012 pmdev->pages[i].status_input = 0;
1013 pmdev->pages[i].status_temperature = 0;
1014 pmdev->pages[i].status_cml = 0;
1015 pmdev->pages[i].status_other = 0;
1016 pmdev->pages[i].status_mfr_specific = 0;
1017 pmdev->pages[i].status_fans_1_2 = 0;
1018 pmdev->pages[i].status_fans_3_4 = 0;
1024 * PMBus operation is used to turn On and Off PSUs
1025 * Therefore, default value for the Operation should be PB_OP_ON or 0x80
1027 static void pmbus_operation(PMBusDevice *pmdev)
1029 uint8_t index = pmdev->page;
1030 if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
1031 pmdev->pages[index].read_vout = 0;
1032 pmdev->pages[index].read_iout = 0;
1033 pmdev->pages[index].read_pout = 0;
1034 return;
1037 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
1038 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
1041 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
1042 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
1044 pmbus_check_limits(pmdev);
1047 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
1049 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
1050 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
1051 int ret = 0;
1052 uint8_t index;
1054 if (len == 0) {
1055 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
1056 return PMBUS_ERR_BYTE;
1059 if (!pmdev->pages) { /* allocate memory for pages on first use */
1060 pmbus_pages_alloc(pmdev);
1063 pmdev->in_buf_len = len;
1064 pmdev->in_buf = buf;
1066 pmdev->code = buf[0]; /* PMBus command code */
1067 if (len == 1) { /* Single length writes are command codes only */
1068 return 0;
1071 if (pmdev->code == PMBUS_PAGE) {
1072 pmdev->page = pmbus_receive8(pmdev);
1073 return 0;
1076 /* loop through all the pages when 0xFF is received */
1077 if (pmdev->page == PB_ALL_PAGES) {
1078 for (int i = 0; i < pmdev->num_pages; i++) {
1079 pmdev->page = i;
1080 pmbus_write_data(smd, buf, len);
1082 pmdev->page = PB_ALL_PAGES;
1083 return 0;
1086 if (pmdev->page > pmdev->num_pages - 1) {
1087 qemu_log_mask(LOG_GUEST_ERROR,
1088 "%s: page %u is out of range\n",
1089 __func__, pmdev->page);
1090 pmdev->page = 0; /* undefined behaviour - reset to page 0 */
1091 pmbus_cml_error(pmdev);
1092 return PMBUS_ERR_BYTE;
1095 index = pmdev->page;
1097 switch (pmdev->code) {
1098 case PMBUS_OPERATION: /* R/W byte */
1099 pmdev->pages[index].operation = pmbus_receive8(pmdev);
1100 pmbus_operation(pmdev);
1101 break;
1103 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
1104 pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1105 break;
1107 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1108 pmbus_clear_faults(pmdev);
1109 break;
1111 case PMBUS_PHASE: /* R/W byte */
1112 pmdev->pages[index].phase = pmbus_receive8(pmdev);
1113 break;
1115 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1116 case PMBUS_WRITE_PROTECT: /* R/W byte */
1117 pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1118 break;
1120 case PMBUS_VOUT_MODE: /* R/W byte */
1121 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1122 pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1123 } else {
1124 goto passthrough;
1126 break;
1128 case PMBUS_VOUT_COMMAND: /* R/W word */
1129 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1130 pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1131 } else {
1132 goto passthrough;
1134 break;
1136 case PMBUS_VOUT_TRIM: /* R/W word */
1137 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1138 pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1139 } else {
1140 goto passthrough;
1142 break;
1144 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
1145 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1146 pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1147 } else {
1148 goto passthrough;
1150 break;
1152 case PMBUS_VOUT_MAX: /* R/W word */
1153 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1154 pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1155 } else {
1156 goto passthrough;
1158 break;
1160 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
1161 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1162 pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1163 } else {
1164 goto passthrough;
1166 break;
1168 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
1169 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1170 pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1171 } else {
1172 goto passthrough;
1174 break;
1176 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
1177 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1178 pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1179 } else {
1180 goto passthrough;
1182 break;
1184 case PMBUS_VOUT_DROOP: /* R/W word */
1185 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1186 pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1187 } else {
1188 goto passthrough;
1190 break;
1192 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
1193 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1194 pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1195 } else {
1196 goto passthrough;
1198 break;
1200 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
1201 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1202 pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1203 } else {
1204 goto passthrough;
1206 break;
1208 case PMBUS_VOUT_MIN: /* R/W word */
1209 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1210 pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
1211 } else {
1212 goto passthrough;
1214 break;
1216 case PMBUS_POUT_MAX: /* R/W word */
1217 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1218 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1219 } else {
1220 goto passthrough;
1222 break;
1224 case PMBUS_VIN_ON: /* R/W word */
1225 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1226 pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1227 } else {
1228 goto passthrough;
1230 break;
1232 case PMBUS_VIN_OFF: /* R/W word */
1233 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1234 pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1235 } else {
1236 goto passthrough;
1238 break;
1240 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
1241 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1242 pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1243 } else {
1244 goto passthrough;
1246 break;
1248 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
1249 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1250 pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1251 } else {
1252 goto passthrough;
1254 break;
1256 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
1257 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1258 pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1259 } else {
1260 goto passthrough;
1262 break;
1264 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
1265 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1266 pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1267 } else {
1268 goto passthrough;
1270 break;
1272 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
1273 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1274 pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1275 } else {
1276 goto passthrough;
1278 break;
1280 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
1281 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1282 pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1283 } else {
1284 goto passthrough;
1286 break;
1288 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
1289 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1290 pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1291 } else {
1292 goto passthrough;
1294 break;
1296 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
1297 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1298 pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1299 } else {
1300 goto passthrough;
1302 break;
1304 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
1305 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1306 pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1307 } else {
1308 goto passthrough;
1310 break;
1312 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
1313 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1314 pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1315 } else {
1316 goto passthrough;
1318 break;
1320 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1321 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1322 pmdev->pages[index].iout_oc_lv_fault_response
1323 = pmbus_receive8(pmdev);
1324 } else {
1325 goto passthrough;
1327 break;
1329 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
1330 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1331 pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1332 } else {
1333 goto passthrough;
1335 break;
1337 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
1338 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1339 pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1340 } else {
1341 goto passthrough;
1343 break;
1345 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
1346 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1347 pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1348 } else {
1349 goto passthrough;
1351 break;
1353 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
1354 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1355 pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1356 } else {
1357 goto passthrough;
1359 break;
1361 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
1362 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1363 pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1364 } else {
1365 goto passthrough;
1367 break;
1369 case PMBUS_OT_WARN_LIMIT: /* R/W word */
1370 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1371 pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1372 } else {
1373 goto passthrough;
1375 break;
1377 case PMBUS_UT_WARN_LIMIT: /* R/W word */
1378 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1379 pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1380 } else {
1381 goto passthrough;
1383 break;
1385 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
1386 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1387 pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1388 } else {
1389 goto passthrough;
1391 break;
1393 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
1394 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1395 pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1396 } else {
1397 goto passthrough;
1399 break;
1401 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
1402 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1403 pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1404 } else {
1405 goto passthrough;
1407 break;
1409 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
1410 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1411 pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1412 } else {
1413 goto passthrough;
1415 break;
1417 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
1418 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1419 pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1420 } else {
1421 goto passthrough;
1423 break;
1425 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
1426 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1427 pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1428 } else {
1429 goto passthrough;
1431 break;
1433 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
1434 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1435 pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1436 } else {
1437 goto passthrough;
1439 break;
1441 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
1442 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1443 pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1444 } else {
1445 goto passthrough;
1447 break;
1449 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
1450 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1451 pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1452 } else {
1453 goto passthrough;
1455 break;
1457 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
1458 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1459 pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1460 } else {
1461 goto passthrough;
1463 break;
1465 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
1466 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1467 pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1468 } else {
1469 goto passthrough;
1471 break;
1473 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
1474 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1475 pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1476 } else {
1477 goto passthrough;
1479 break;
1481 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
1482 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1483 pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1484 } else {
1485 goto passthrough;
1487 break;
1489 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
1490 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1491 pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1492 } else {
1493 goto passthrough;
1495 break;
1497 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
1498 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1499 pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1500 } else {
1501 goto passthrough;
1503 break;
1505 case PMBUS_STATUS_BYTE: /* R/W byte */
1506 pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1507 break;
1509 case PMBUS_STATUS_WORD: /* R/W word */
1510 pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1511 break;
1513 case PMBUS_STATUS_VOUT: /* R/W byte */
1514 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1515 pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1516 } else {
1517 goto passthrough;
1519 break;
1521 case PMBUS_STATUS_IOUT: /* R/W byte */
1522 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1523 pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1524 } else {
1525 goto passthrough;
1527 break;
1529 case PMBUS_STATUS_INPUT: /* R/W byte */
1530 pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1531 break;
1533 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
1534 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1535 pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1536 } else {
1537 goto passthrough;
1539 break;
1541 case PMBUS_STATUS_CML: /* R/W byte */
1542 pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1543 break;
1545 case PMBUS_STATUS_OTHER: /* R/W byte */
1546 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1547 break;
1549 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
1550 pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
1551 break;
1553 case PMBUS_PAGE_PLUS_READ: /* Block Read-only */
1554 case PMBUS_CAPABILITY: /* Read-Only byte */
1555 case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */
1556 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
1557 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
1558 case PMBUS_READ_VIN: /* Read-Only word */
1559 case PMBUS_READ_IIN: /* Read-Only word */
1560 case PMBUS_READ_VCAP: /* Read-Only word */
1561 case PMBUS_READ_VOUT: /* Read-Only word */
1562 case PMBUS_READ_IOUT: /* Read-Only word */
1563 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
1564 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
1565 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
1566 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
1567 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
1568 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
1569 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
1570 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
1571 case PMBUS_READ_FREQUENCY: /* Read-Only word */
1572 case PMBUS_READ_POUT: /* Read-Only word */
1573 case PMBUS_READ_PIN: /* Read-Only word */
1574 case PMBUS_REVISION: /* Read-Only byte */
1575 case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */
1576 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
1577 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
1578 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
1579 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
1580 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
1581 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
1582 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
1583 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
1584 case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */
1585 case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */
1586 case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */
1587 case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */
1588 case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */
1589 case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */
1590 case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */
1591 qemu_log_mask(LOG_GUEST_ERROR,
1592 "%s: writing to read-only register 0x%02x\n",
1593 __func__, pmdev->code);
1594 break;
1596 passthrough:
1597 /* Unimplimented registers get passed to the device */
1598 default:
1599 if (pmdc->write_data) {
1600 ret = pmdc->write_data(pmdev, buf, len);
1602 break;
1604 pmbus_check_limits(pmdev);
1605 pmdev->in_buf_len = 0;
1606 return ret;
1609 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1611 if (!pmdev->pages) { /* allocate memory for pages on first use */
1612 pmbus_pages_alloc(pmdev);
1615 /* The 0xFF page is special for commands applying to all pages */
1616 if (index == PB_ALL_PAGES) {
1617 for (int i = 0; i < pmdev->num_pages; i++) {
1618 pmdev->pages[i].page_flags = flags;
1620 return 0;
1623 if (index > pmdev->num_pages - 1) {
1624 qemu_log_mask(LOG_GUEST_ERROR,
1625 "%s: index %u is out of range\n",
1626 __func__, index);
1627 return -1;
1630 pmdev->pages[index].page_flags = flags;
1632 return 0;
1635 /* TODO: include pmbus page info in vmstate */
1636 const VMStateDescription vmstate_pmbus_device = {
1637 .name = TYPE_PMBUS_DEVICE,
1638 .version_id = 0,
1639 .minimum_version_id = 0,
1640 .fields = (VMStateField[]) {
1641 VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1642 VMSTATE_UINT8(num_pages, PMBusDevice),
1643 VMSTATE_UINT8(code, PMBusDevice),
1644 VMSTATE_UINT8(page, PMBusDevice),
1645 VMSTATE_UINT8(capability, PMBusDevice),
1646 VMSTATE_END_OF_LIST()
1650 static void pmbus_device_finalize(Object *obj)
1652 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1653 g_free(pmdev->pages);
1656 static void pmbus_device_class_init(ObjectClass *klass, void *data)
1658 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1660 k->quick_cmd = pmbus_quick_cmd;
1661 k->write_data = pmbus_write_data;
1662 k->receive_byte = pmbus_receive_byte;
1665 static const TypeInfo pmbus_device_type_info = {
1666 .name = TYPE_PMBUS_DEVICE,
1667 .parent = TYPE_SMBUS_DEVICE,
1668 .instance_size = sizeof(PMBusDevice),
1669 .instance_finalize = pmbus_device_finalize,
1670 .abstract = true,
1671 .class_size = sizeof(PMBusDeviceClass),
1672 .class_init = pmbus_device_class_init,
1675 static void pmbus_device_register_types(void)
1677 type_register_static(&pmbus_device_type_info);
1680 type_init(pmbus_device_register_types)