include/hw/arm: move BSA definitions to bsa.h
[qemu/ar7.git] / hw / i2c / pmbus_device.c
blobcef51663d0ebc9689fc6ee2138a0fc8eee1fdab1
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 uint8_t pmbus_pages_num(PMBusDevice *pmdev)
195 const PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev);
197 /* some PMBus devices don't use the PAGE command, so they get 1 page */
198 return k->device_num_pages ? : 1;
201 static void pmbus_pages_alloc(PMBusDevice *pmdev)
203 pmdev->num_pages = pmbus_pages_num(pmdev);
204 pmdev->pages = g_new0(PMBusPage, pmdev->num_pages);
207 void pmbus_check_limits(PMBusDevice *pmdev)
209 for (int i = 0; i < pmdev->num_pages; i++) {
210 if ((pmdev->pages[i].operation & PB_OP_ON) == 0) {
211 continue; /* don't check powered off devices */
214 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) {
215 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
216 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT;
219 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) {
220 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
221 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN;
224 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) {
225 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
226 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN;
229 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) {
230 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
231 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT;
234 if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) {
235 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
236 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN;
239 if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) {
240 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
241 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN;
244 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) {
245 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
246 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN;
249 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) {
250 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
251 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT;
254 if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) {
255 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
256 pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN;
259 if (pmdev->pages[i].read_temperature_1
260 > pmdev->pages[i].ot_fault_limit) {
261 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
262 pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT;
265 if (pmdev->pages[i].read_temperature_1
266 > pmdev->pages[i].ot_warn_limit) {
267 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
268 pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN;
273 void pmbus_idle(PMBusDevice *pmdev)
275 pmdev->code = PMBUS_IDLE_STATE;
278 /* assert the status_cml error upon receipt of malformed command */
279 static void pmbus_cml_error(PMBusDevice *pmdev)
281 for (int i = 0; i < pmdev->num_pages; i++) {
282 pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
283 pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
287 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
289 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
290 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
291 uint8_t ret = PMBUS_ERR_BYTE;
292 uint8_t index;
294 if (pmdev->out_buf_len != 0) {
295 ret = pmbus_out_buf_pop(pmdev);
296 return ret;
300 * Reading from all pages will return the value from page 0,
301 * means that all subsequent commands are to be applied to all output.
303 if (pmdev->page == PB_ALL_PAGES) {
304 index = 0;
305 } else if (pmdev->page > pmdev->num_pages - 1) {
306 qemu_log_mask(LOG_GUEST_ERROR,
307 "%s: page %d is out of range\n",
308 __func__, pmdev->page);
309 pmbus_cml_error(pmdev);
310 return PMBUS_ERR_BYTE;
311 } else {
312 index = pmdev->page;
315 switch (pmdev->code) {
316 case PMBUS_PAGE:
317 pmbus_send8(pmdev, pmdev->page);
318 break;
320 case PMBUS_OPERATION: /* R/W byte */
321 pmbus_send8(pmdev, pmdev->pages[index].operation);
322 break;
324 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
325 pmbus_send8(pmdev, pmdev->pages[index].on_off_config);
326 break;
328 case PMBUS_PHASE: /* R/W byte */
329 pmbus_send8(pmdev, pmdev->pages[index].phase);
330 break;
332 case PMBUS_WRITE_PROTECT: /* R/W byte */
333 pmbus_send8(pmdev, pmdev->pages[index].write_protect);
334 break;
336 case PMBUS_CAPABILITY:
337 pmbus_send8(pmdev, pmdev->capability);
338 if (pmdev->capability & BIT(7)) {
339 qemu_log_mask(LOG_UNIMP,
340 "%s: PEC is enabled but not yet supported.\n",
341 __func__);
343 break;
345 case PMBUS_VOUT_MODE: /* R/W byte */
346 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
347 pmbus_send8(pmdev, pmdev->pages[index].vout_mode);
348 } else {
349 goto passthough;
351 break;
353 case PMBUS_VOUT_COMMAND: /* R/W word */
354 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
355 pmbus_send16(pmdev, pmdev->pages[index].vout_command);
356 } else {
357 goto passthough;
359 break;
361 case PMBUS_VOUT_TRIM: /* R/W word */
362 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
363 pmbus_send16(pmdev, pmdev->pages[index].vout_trim);
364 } else {
365 goto passthough;
367 break;
369 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
370 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
371 pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset);
372 } else {
373 goto passthough;
375 break;
377 case PMBUS_VOUT_MAX: /* R/W word */
378 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
379 pmbus_send16(pmdev, pmdev->pages[index].vout_max);
380 } else {
381 goto passthough;
383 break;
385 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
386 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
387 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high);
388 } else {
389 goto passthough;
391 break;
393 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
394 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
395 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low);
396 } else {
397 goto passthough;
399 break;
401 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
402 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
403 pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate);
404 } else {
405 goto passthough;
407 break;
409 case PMBUS_VOUT_DROOP: /* R/W word */
410 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
411 pmbus_send16(pmdev, pmdev->pages[index].vout_droop);
412 } else {
413 goto passthough;
415 break;
417 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
418 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
419 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop);
420 } else {
421 goto passthough;
423 break;
425 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
426 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
427 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor);
428 } else {
429 goto passthough;
431 break;
433 case PMBUS_VOUT_MIN: /* R/W word */
434 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
435 pmbus_send16(pmdev, pmdev->pages[index].vout_min);
436 } else {
437 goto passthough;
439 break;
441 /* TODO: implement coefficients support */
443 case PMBUS_POUT_MAX: /* R/W word */
444 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
445 pmbus_send16(pmdev, pmdev->pages[index].pout_max);
446 } else {
447 goto passthough;
449 break;
451 case PMBUS_VIN_ON: /* R/W word */
452 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
453 pmbus_send16(pmdev, pmdev->pages[index].vin_on);
454 } else {
455 goto passthough;
457 break;
459 case PMBUS_VIN_OFF: /* R/W word */
460 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
461 pmbus_send16(pmdev, pmdev->pages[index].vin_off);
462 } else {
463 goto passthough;
465 break;
467 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
468 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
469 pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain);
470 } else {
471 goto passthough;
473 break;
475 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
476 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
477 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
478 } else {
479 goto passthough;
481 break;
483 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
484 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
485 pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response);
486 } else {
487 goto passthough;
489 break;
491 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
492 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
493 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit);
494 } else {
495 goto passthough;
497 break;
499 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
500 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
501 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit);
502 } else {
503 goto passthough;
505 break;
507 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
508 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
509 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit);
510 } else {
511 goto passthough;
513 break;
515 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
516 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
517 pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response);
518 } else {
519 goto passthough;
521 break;
523 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
524 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
525 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit);
526 } else {
527 goto passthough;
529 break;
531 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
532 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
533 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response);
534 } else {
535 goto passthough;
537 break;
539 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
540 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
541 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit);
542 } else {
543 goto passthough;
545 break;
547 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
548 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
549 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response);
550 } else {
551 goto passthough;
553 break;
555 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
556 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
557 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit);
558 } else {
559 goto passthough;
561 break;
563 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
564 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
565 pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit);
566 } else {
567 goto passthough;
569 break;
571 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
572 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
573 pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response);
574 } else {
575 goto passthough;
577 break;
579 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
580 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
581 pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit);
582 } else {
583 goto passthough;
585 break;
587 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
588 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
589 pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response);
590 } else {
591 goto passthough;
593 break;
595 case PMBUS_OT_WARN_LIMIT: /* R/W word */
596 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
597 pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit);
598 } else {
599 goto passthough;
601 break;
603 case PMBUS_UT_WARN_LIMIT: /* R/W word */
604 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
605 pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit);
606 } else {
607 goto passthough;
609 break;
611 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
612 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
613 pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit);
614 } else {
615 goto passthough;
617 break;
619 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
620 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
621 pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response);
622 } else {
623 goto passthough;
625 break;
627 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
628 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
629 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit);
630 } else {
631 goto passthough;
633 break;
635 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
636 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
637 pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response);
638 } else {
639 goto passthough;
641 break;
643 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
644 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
645 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit);
646 } else {
647 goto passthough;
649 break;
651 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
652 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
653 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit);
654 } else {
655 goto passthough;
657 break;
659 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
660 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
661 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit);
662 } else {
663 goto passthough;
665 break;
667 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
668 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
669 pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response);
670 } else {
671 goto passthough;
673 break;
675 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
676 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
677 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit);
678 } else {
679 goto passthough;
681 break;
683 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
684 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
685 pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response);
686 } else {
687 goto passthough;
689 break;
691 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
692 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
693 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit);
694 } else {
695 goto passthough;
697 break;
699 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
700 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
701 pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit);
702 } else {
703 goto passthough;
705 break;
707 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
708 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
709 pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response);
710 } else {
711 goto passthough;
713 break;
715 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
716 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
717 pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit);
718 } else {
719 goto passthough;
721 break;
723 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
724 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
725 pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit);
726 } else {
727 goto passthough;
729 break;
731 case PMBUS_STATUS_BYTE: /* R/W byte */
732 pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF);
733 break;
735 case PMBUS_STATUS_WORD: /* R/W word */
736 pmbus_send16(pmdev, pmdev->pages[index].status_word);
737 break;
739 case PMBUS_STATUS_VOUT: /* R/W byte */
740 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
741 pmbus_send8(pmdev, pmdev->pages[index].status_vout);
742 } else {
743 goto passthough;
745 break;
747 case PMBUS_STATUS_IOUT: /* R/W byte */
748 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
749 pmbus_send8(pmdev, pmdev->pages[index].status_iout);
750 } else {
751 goto passthough;
753 break;
755 case PMBUS_STATUS_INPUT: /* R/W byte */
756 if (pmdev->pages[index].page_flags & PB_HAS_VIN ||
757 pmdev->pages[index].page_flags & PB_HAS_IIN ||
758 pmdev->pages[index].page_flags & PB_HAS_PIN) {
759 pmbus_send8(pmdev, pmdev->pages[index].status_input);
760 } else {
761 goto passthough;
763 break;
765 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
766 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
767 pmbus_send8(pmdev, pmdev->pages[index].status_temperature);
768 } else {
769 goto passthough;
771 break;
773 case PMBUS_STATUS_CML: /* R/W byte */
774 pmbus_send8(pmdev, pmdev->pages[index].status_cml);
775 break;
777 case PMBUS_STATUS_OTHER: /* R/W byte */
778 pmbus_send8(pmdev, pmdev->pages[index].status_other);
779 break;
781 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
782 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
783 break;
785 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
786 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
787 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
788 } else {
789 goto passthough;
791 break;
793 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
794 if (pmdev->pages[index].page_flags & PB_HAS_EOUT) {
795 pmbus_send(pmdev, pmdev->pages[index].read_eout, 5);
796 } else {
797 goto passthough;
799 break;
801 case PMBUS_READ_VIN: /* Read-Only word */
802 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
803 pmbus_send16(pmdev, pmdev->pages[index].read_vin);
804 } else {
805 goto passthough;
807 break;
809 case PMBUS_READ_IIN: /* Read-Only word */
810 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
811 pmbus_send16(pmdev, pmdev->pages[index].read_iin);
812 } else {
813 goto passthough;
815 break;
817 case PMBUS_READ_VOUT: /* Read-Only word */
818 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
819 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
820 } else {
821 goto passthough;
823 break;
825 case PMBUS_READ_IOUT: /* Read-Only word */
826 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
827 pmbus_send16(pmdev, pmdev->pages[index].read_iout);
828 } else {
829 goto passthough;
831 break;
833 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
834 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
835 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1);
836 } else {
837 goto passthough;
839 break;
841 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
842 if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) {
843 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2);
844 } else {
845 goto passthough;
847 break;
849 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
850 if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) {
851 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3);
852 } else {
853 goto passthough;
855 break;
857 case PMBUS_READ_POUT: /* Read-Only word */
858 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
859 pmbus_send16(pmdev, pmdev->pages[index].read_pout);
860 } else {
861 goto passthough;
863 break;
865 case PMBUS_READ_PIN: /* Read-Only word */
866 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
867 pmbus_send16(pmdev, pmdev->pages[index].read_pin);
868 } else {
869 goto passthough;
871 break;
873 case PMBUS_REVISION: /* Read-Only byte */
874 pmbus_send8(pmdev, pmdev->pages[index].revision);
875 break;
877 case PMBUS_MFR_ID: /* R/W block */
878 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
879 pmbus_send_string(pmdev, pmdev->pages[index].mfr_id);
880 } else {
881 goto passthough;
883 break;
885 case PMBUS_MFR_MODEL: /* R/W block */
886 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
887 pmbus_send_string(pmdev, pmdev->pages[index].mfr_model);
888 } else {
889 goto passthough;
891 break;
893 case PMBUS_MFR_REVISION: /* R/W block */
894 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
895 pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision);
896 } else {
897 goto passthough;
899 break;
901 case PMBUS_MFR_LOCATION: /* R/W block */
902 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
903 pmbus_send_string(pmdev, pmdev->pages[index].mfr_location);
904 } else {
905 goto passthough;
907 break;
909 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
910 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
911 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min);
912 } else {
913 goto passthough;
915 break;
917 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
918 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
919 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max);
920 } else {
921 goto passthough;
923 break;
925 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
926 if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) {
927 pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max);
928 } else {
929 goto passthough;
931 break;
933 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
934 if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) {
935 pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max);
936 } else {
937 goto passthough;
939 break;
941 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
942 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
943 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min);
944 } else {
945 goto passthough;
947 break;
949 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
950 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
951 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max);
952 } else {
953 goto passthough;
955 break;
957 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
958 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) {
959 pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max);
960 } else {
961 goto passthough;
963 break;
965 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
966 if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) {
967 pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max);
968 } else {
969 goto passthough;
971 break;
973 case PMBUS_MFR_MAX_TEMP_1: /* R/W word */
974 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
975 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1);
976 } else {
977 goto passthough;
979 break;
981 case PMBUS_MFR_MAX_TEMP_2: /* R/W word */
982 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
983 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2);
984 } else {
985 goto passthough;
987 break;
989 case PMBUS_MFR_MAX_TEMP_3: /* R/W word */
990 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
991 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3);
992 } else {
993 goto passthough;
995 break;
997 case PMBUS_IDLE_STATE:
998 pmbus_send8(pmdev, PMBUS_ERR_BYTE);
999 break;
1001 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1002 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1003 case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
1004 case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */
1005 case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */
1006 case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */
1007 case PMBUS_STORE_USER_ALL: /* Send Byte */
1008 case PMBUS_RESTORE_USER_ALL: /* Send Byte */
1009 case PMBUS_STORE_USER_CODE: /* Write-only Byte */
1010 case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */
1011 case PMBUS_QUERY: /* Write-Only */
1012 qemu_log_mask(LOG_GUEST_ERROR,
1013 "%s: reading from write only register 0x%02x\n",
1014 __func__, pmdev->code);
1015 break;
1017 passthough:
1018 default:
1019 /* Pass through read request if not handled */
1020 if (pmdc->receive_byte) {
1021 ret = pmdc->receive_byte(pmdev);
1023 break;
1026 if (pmdev->out_buf_len != 0) {
1027 ret = pmbus_out_buf_pop(pmdev);
1028 return ret;
1031 return ret;
1035 * PMBus clear faults command applies to all status registers, existing faults
1036 * should separately get re-asserted.
1038 static void pmbus_clear_faults(PMBusDevice *pmdev)
1040 for (uint8_t i = 0; i < pmdev->num_pages; i++) {
1041 pmdev->pages[i].status_word = 0;
1042 pmdev->pages[i].status_vout = 0;
1043 pmdev->pages[i].status_iout = 0;
1044 pmdev->pages[i].status_input = 0;
1045 pmdev->pages[i].status_temperature = 0;
1046 pmdev->pages[i].status_cml = 0;
1047 pmdev->pages[i].status_other = 0;
1048 pmdev->pages[i].status_mfr_specific = 0;
1049 pmdev->pages[i].status_fans_1_2 = 0;
1050 pmdev->pages[i].status_fans_3_4 = 0;
1056 * PMBus operation is used to turn On and Off PSUs
1057 * Therefore, default value for the Operation should be PB_OP_ON or 0x80
1059 static void pmbus_operation(PMBusDevice *pmdev)
1061 uint8_t index = pmdev->page;
1062 if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
1063 pmdev->pages[index].read_vout = 0;
1064 pmdev->pages[index].read_iout = 0;
1065 pmdev->pages[index].read_pout = 0;
1066 return;
1069 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
1070 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
1073 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
1074 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
1076 pmbus_check_limits(pmdev);
1079 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
1081 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
1082 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
1083 int ret = 0;
1084 uint8_t index;
1086 if (len == 0) {
1087 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
1088 return PMBUS_ERR_BYTE;
1091 if (!pmdev->pages) { /* allocate memory for pages on first use */
1092 pmbus_pages_alloc(pmdev);
1095 pmdev->in_buf_len = len;
1096 pmdev->in_buf = buf;
1098 pmdev->code = buf[0]; /* PMBus command code */
1099 if (len == 1) { /* Single length writes are command codes only */
1100 return 0;
1103 if (pmdev->code == PMBUS_PAGE) {
1104 pmdev->page = pmbus_receive8(pmdev);
1105 return 0;
1108 /* loop through all the pages when 0xFF is received */
1109 if (pmdev->page == PB_ALL_PAGES) {
1110 for (int i = 0; i < pmdev->num_pages; i++) {
1111 pmdev->page = i;
1112 pmbus_write_data(smd, buf, len);
1114 pmdev->page = PB_ALL_PAGES;
1115 return 0;
1118 if (pmdev->page > pmdev->num_pages - 1) {
1119 qemu_log_mask(LOG_GUEST_ERROR,
1120 "%s: page %u is out of range\n",
1121 __func__, pmdev->page);
1122 pmdev->page = 0; /* undefined behaviour - reset to page 0 */
1123 pmbus_cml_error(pmdev);
1124 return PMBUS_ERR_BYTE;
1127 index = pmdev->page;
1129 switch (pmdev->code) {
1130 case PMBUS_OPERATION: /* R/W byte */
1131 pmdev->pages[index].operation = pmbus_receive8(pmdev);
1132 pmbus_operation(pmdev);
1133 break;
1135 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
1136 pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1137 break;
1139 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1140 pmbus_clear_faults(pmdev);
1141 break;
1143 case PMBUS_PHASE: /* R/W byte */
1144 pmdev->pages[index].phase = pmbus_receive8(pmdev);
1145 break;
1147 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1148 case PMBUS_WRITE_PROTECT: /* R/W byte */
1149 pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1150 break;
1152 case PMBUS_VOUT_MODE: /* R/W byte */
1153 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1154 pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1155 } else {
1156 goto passthrough;
1158 break;
1160 case PMBUS_VOUT_COMMAND: /* R/W word */
1161 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1162 pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1163 } else {
1164 goto passthrough;
1166 break;
1168 case PMBUS_VOUT_TRIM: /* R/W word */
1169 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1170 pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1171 } else {
1172 goto passthrough;
1174 break;
1176 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
1177 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1178 pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1179 } else {
1180 goto passthrough;
1182 break;
1184 case PMBUS_VOUT_MAX: /* R/W word */
1185 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1186 pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1187 } else {
1188 goto passthrough;
1190 break;
1192 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
1193 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1194 pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1195 } else {
1196 goto passthrough;
1198 break;
1200 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
1201 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1202 pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1203 } else {
1204 goto passthrough;
1206 break;
1208 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
1209 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1210 pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1211 } else {
1212 goto passthrough;
1214 break;
1216 case PMBUS_VOUT_DROOP: /* R/W word */
1217 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1218 pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1219 } else {
1220 goto passthrough;
1222 break;
1224 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
1225 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1226 pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1227 } else {
1228 goto passthrough;
1230 break;
1232 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
1233 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1234 pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1235 } else {
1236 goto passthrough;
1238 break;
1240 case PMBUS_VOUT_MIN: /* R/W word */
1241 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1242 pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
1243 } else {
1244 goto passthrough;
1246 break;
1248 case PMBUS_POUT_MAX: /* R/W word */
1249 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1250 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1251 } else {
1252 goto passthrough;
1254 break;
1256 case PMBUS_VIN_ON: /* R/W word */
1257 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1258 pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1259 } else {
1260 goto passthrough;
1262 break;
1264 case PMBUS_VIN_OFF: /* R/W word */
1265 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1266 pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1267 } else {
1268 goto passthrough;
1270 break;
1272 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
1273 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1274 pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1275 } else {
1276 goto passthrough;
1278 break;
1280 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
1281 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1282 pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1283 } else {
1284 goto passthrough;
1286 break;
1288 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
1289 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1290 pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1291 } else {
1292 goto passthrough;
1294 break;
1296 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
1297 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1298 pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1299 } else {
1300 goto passthrough;
1302 break;
1304 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
1305 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1306 pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1307 } else {
1308 goto passthrough;
1310 break;
1312 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
1313 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1314 pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1315 } else {
1316 goto passthrough;
1318 break;
1320 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
1321 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1322 pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1323 } else {
1324 goto passthrough;
1326 break;
1328 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
1329 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1330 pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1331 } else {
1332 goto passthrough;
1334 break;
1336 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
1337 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1338 pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1339 } else {
1340 goto passthrough;
1342 break;
1344 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
1345 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1346 pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1347 } else {
1348 goto passthrough;
1350 break;
1352 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1353 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1354 pmdev->pages[index].iout_oc_lv_fault_response
1355 = pmbus_receive8(pmdev);
1356 } else {
1357 goto passthrough;
1359 break;
1361 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
1362 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1363 pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1364 } else {
1365 goto passthrough;
1367 break;
1369 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
1370 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1371 pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1372 } else {
1373 goto passthrough;
1375 break;
1377 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
1378 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1379 pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1380 } else {
1381 goto passthrough;
1383 break;
1385 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
1386 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1387 pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1388 } else {
1389 goto passthrough;
1391 break;
1393 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
1394 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1395 pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1396 } else {
1397 goto passthrough;
1399 break;
1401 case PMBUS_OT_WARN_LIMIT: /* R/W word */
1402 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1403 pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1404 } else {
1405 goto passthrough;
1407 break;
1409 case PMBUS_UT_WARN_LIMIT: /* R/W word */
1410 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1411 pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1412 } else {
1413 goto passthrough;
1415 break;
1417 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
1418 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1419 pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1420 } else {
1421 goto passthrough;
1423 break;
1425 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
1426 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1427 pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1428 } else {
1429 goto passthrough;
1431 break;
1433 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
1434 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1435 pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1436 } else {
1437 goto passthrough;
1439 break;
1441 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
1442 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1443 pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1444 } else {
1445 goto passthrough;
1447 break;
1449 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
1450 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1451 pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1452 } else {
1453 goto passthrough;
1455 break;
1457 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
1458 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1459 pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1460 } else {
1461 goto passthrough;
1463 break;
1465 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
1466 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1467 pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1468 } else {
1469 goto passthrough;
1471 break;
1473 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
1474 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1475 pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1476 } else {
1477 goto passthrough;
1479 break;
1481 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
1482 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1483 pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1484 } else {
1485 goto passthrough;
1487 break;
1489 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
1490 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1491 pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1492 } else {
1493 goto passthrough;
1495 break;
1497 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
1498 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1499 pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1500 } else {
1501 goto passthrough;
1503 break;
1505 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
1506 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1507 pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1508 } else {
1509 goto passthrough;
1511 break;
1513 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
1514 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1515 pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1516 } else {
1517 goto passthrough;
1519 break;
1521 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
1522 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1523 pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1524 } else {
1525 goto passthrough;
1527 break;
1529 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
1530 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1531 pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1532 } else {
1533 goto passthrough;
1535 break;
1537 case PMBUS_STATUS_BYTE: /* R/W byte */
1538 pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1539 break;
1541 case PMBUS_STATUS_WORD: /* R/W word */
1542 pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1543 break;
1545 case PMBUS_STATUS_VOUT: /* R/W byte */
1546 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1547 pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1548 } else {
1549 goto passthrough;
1551 break;
1553 case PMBUS_STATUS_IOUT: /* R/W byte */
1554 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1555 pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1556 } else {
1557 goto passthrough;
1559 break;
1561 case PMBUS_STATUS_INPUT: /* R/W byte */
1562 pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1563 break;
1565 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
1566 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1567 pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1568 } else {
1569 goto passthrough;
1571 break;
1573 case PMBUS_STATUS_CML: /* R/W byte */
1574 pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1575 break;
1577 case PMBUS_STATUS_OTHER: /* R/W byte */
1578 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1579 break;
1581 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
1582 pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
1583 break;
1585 case PMBUS_PAGE_PLUS_READ: /* Block Read-only */
1586 case PMBUS_CAPABILITY: /* Read-Only byte */
1587 case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */
1588 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
1589 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
1590 case PMBUS_READ_VIN: /* Read-Only word */
1591 case PMBUS_READ_IIN: /* Read-Only word */
1592 case PMBUS_READ_VCAP: /* Read-Only word */
1593 case PMBUS_READ_VOUT: /* Read-Only word */
1594 case PMBUS_READ_IOUT: /* Read-Only word */
1595 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
1596 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
1597 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
1598 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
1599 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
1600 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
1601 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
1602 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
1603 case PMBUS_READ_FREQUENCY: /* Read-Only word */
1604 case PMBUS_READ_POUT: /* Read-Only word */
1605 case PMBUS_READ_PIN: /* Read-Only word */
1606 case PMBUS_REVISION: /* Read-Only byte */
1607 case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */
1608 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
1609 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
1610 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
1611 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
1612 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
1613 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
1614 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
1615 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
1616 case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */
1617 case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */
1618 case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */
1619 case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */
1620 case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */
1621 case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */
1622 case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */
1623 qemu_log_mask(LOG_GUEST_ERROR,
1624 "%s: writing to read-only register 0x%02x\n",
1625 __func__, pmdev->code);
1626 break;
1628 passthrough:
1629 /* Unimplemented registers get passed to the device */
1630 default:
1631 if (pmdc->write_data) {
1632 ret = pmdc->write_data(pmdev, buf, len);
1634 break;
1636 pmbus_check_limits(pmdev);
1637 pmdev->in_buf_len = 0;
1638 return ret;
1641 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1643 if (!pmdev->pages) { /* allocate memory for pages on first use */
1644 pmbus_pages_alloc(pmdev);
1647 /* The 0xFF page is special for commands applying to all pages */
1648 if (index == PB_ALL_PAGES) {
1649 for (int i = 0; i < pmdev->num_pages; i++) {
1650 pmdev->pages[i].page_flags = flags;
1652 return 0;
1655 if (index > pmdev->num_pages - 1) {
1656 qemu_log_mask(LOG_GUEST_ERROR,
1657 "%s: index %u is out of range\n",
1658 __func__, index);
1659 return -1;
1662 pmdev->pages[index].page_flags = flags;
1664 return 0;
1667 /* TODO: include pmbus page info in vmstate */
1668 const VMStateDescription vmstate_pmbus_device = {
1669 .name = TYPE_PMBUS_DEVICE,
1670 .version_id = 0,
1671 .minimum_version_id = 0,
1672 .fields = (VMStateField[]) {
1673 VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1674 VMSTATE_UINT8(num_pages, PMBusDevice),
1675 VMSTATE_UINT8(code, PMBusDevice),
1676 VMSTATE_UINT8(page, PMBusDevice),
1677 VMSTATE_UINT8(capability, PMBusDevice),
1678 VMSTATE_END_OF_LIST()
1682 static void pmbus_device_finalize(Object *obj)
1684 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1685 g_free(pmdev->pages);
1688 static void pmbus_device_class_init(ObjectClass *klass, void *data)
1690 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1692 k->quick_cmd = pmbus_quick_cmd;
1693 k->write_data = pmbus_write_data;
1694 k->receive_byte = pmbus_receive_byte;
1697 static const TypeInfo pmbus_device_type_info = {
1698 .name = TYPE_PMBUS_DEVICE,
1699 .parent = TYPE_SMBUS_DEVICE,
1700 .instance_size = sizeof(PMBusDevice),
1701 .instance_finalize = pmbus_device_finalize,
1702 .abstract = true,
1703 .class_size = sizeof(PMBusDeviceClass),
1704 .class_init = pmbus_device_class_init,
1707 static void pmbus_device_register_types(void)
1709 type_register_static(&pmbus_device_type_info);
1712 type_init(pmbus_device_register_types)