hw/timer/armv7m_systick: Add input clocks
[qemu.git] / hw / i2c / pmbus_device.c
blob24f8f522d9f19bdaf091ad6a212edf4330b939eb
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(const uint8_t *buf, uint8_t len)
94 uint64_t ret = 0;
96 /* Exclude command code from return value */
97 buf++;
98 len--;
100 for (int i = len - 1; i >= 0; i--) {
101 ret = ret << 8 | 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->in_buf, pmdev->in_buf_len);
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->in_buf, pmdev->in_buf_len);
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->in_buf, pmdev->in_buf_len);
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->in_buf, pmdev->in_buf_len);
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 0xFF;
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 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
248 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
249 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
250 uint8_t ret = 0xFF;
251 uint8_t index = pmdev->page;
253 if (pmdev->out_buf_len != 0) {
254 ret = pmbus_out_buf_pop(pmdev);
255 return ret;
258 switch (pmdev->code) {
259 case PMBUS_PAGE:
260 pmbus_send8(pmdev, pmdev->page);
261 break;
263 case PMBUS_OPERATION: /* R/W byte */
264 pmbus_send8(pmdev, pmdev->pages[index].operation);
265 break;
267 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
268 pmbus_send8(pmdev, pmdev->pages[index].on_off_config);
269 break;
271 case PMBUS_PHASE: /* R/W byte */
272 pmbus_send8(pmdev, pmdev->pages[index].phase);
273 break;
275 case PMBUS_WRITE_PROTECT: /* R/W byte */
276 pmbus_send8(pmdev, pmdev->pages[index].write_protect);
277 break;
279 case PMBUS_CAPABILITY:
280 pmbus_send8(pmdev, pmdev->capability);
281 break;
283 case PMBUS_VOUT_MODE: /* R/W byte */
284 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
285 pmbus_send8(pmdev, pmdev->pages[index].vout_mode);
286 } else {
287 goto passthough;
289 break;
291 case PMBUS_VOUT_COMMAND: /* R/W word */
292 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
293 pmbus_send16(pmdev, pmdev->pages[index].vout_command);
294 } else {
295 goto passthough;
297 break;
299 case PMBUS_VOUT_TRIM: /* R/W word */
300 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
301 pmbus_send16(pmdev, pmdev->pages[index].vout_trim);
302 } else {
303 goto passthough;
305 break;
307 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
308 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
309 pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset);
310 } else {
311 goto passthough;
313 break;
315 case PMBUS_VOUT_MAX: /* R/W word */
316 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
317 pmbus_send16(pmdev, pmdev->pages[index].vout_max);
318 } else {
319 goto passthough;
321 break;
323 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
324 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
325 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high);
326 } else {
327 goto passthough;
329 break;
331 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
332 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
333 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low);
334 } else {
335 goto passthough;
337 break;
339 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
340 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
341 pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate);
342 } else {
343 goto passthough;
345 break;
347 case PMBUS_VOUT_DROOP: /* R/W word */
348 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
349 pmbus_send16(pmdev, pmdev->pages[index].vout_droop);
350 } else {
351 goto passthough;
353 break;
355 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
356 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
357 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop);
358 } else {
359 goto passthough;
361 break;
363 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
364 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
365 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor);
366 } else {
367 goto passthough;
369 break;
371 /* TODO: implement coefficients support */
373 case PMBUS_POUT_MAX: /* R/W word */
374 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
375 pmbus_send16(pmdev, pmdev->pages[index].pout_max);
376 } else {
377 goto passthough;
379 break;
381 case PMBUS_VIN_ON: /* R/W word */
382 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
383 pmbus_send16(pmdev, pmdev->pages[index].vin_on);
384 } else {
385 goto passthough;
387 break;
389 case PMBUS_VIN_OFF: /* R/W word */
390 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
391 pmbus_send16(pmdev, pmdev->pages[index].vin_off);
392 } else {
393 goto passthough;
395 break;
397 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
398 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
399 pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain);
400 } else {
401 goto passthough;
403 break;
405 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
406 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
407 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
408 } else {
409 goto passthough;
411 break;
413 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
414 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
415 pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response);
416 } else {
417 goto passthough;
419 break;
421 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
422 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
423 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit);
424 } else {
425 goto passthough;
427 break;
429 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
430 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
431 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit);
432 } else {
433 goto passthough;
435 break;
437 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
438 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
439 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit);
440 } else {
441 goto passthough;
443 break;
445 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
446 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
447 pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response);
448 } else {
449 goto passthough;
451 break;
453 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
454 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
455 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit);
456 } else {
457 goto passthough;
459 break;
461 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
462 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
463 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response);
464 } else {
465 goto passthough;
467 break;
469 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
470 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
471 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit);
472 } else {
473 goto passthough;
475 break;
477 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
478 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
479 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response);
480 } else {
481 goto passthough;
483 break;
485 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
486 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
487 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit);
488 } else {
489 goto passthough;
491 break;
493 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
494 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
495 pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit);
496 } else {
497 goto passthough;
499 break;
501 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
502 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
503 pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response);
504 } else {
505 goto passthough;
507 break;
509 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
510 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
511 pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit);
512 } else {
513 goto passthough;
515 break;
517 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
518 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
519 pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response);
520 } else {
521 goto passthough;
523 break;
525 case PMBUS_OT_WARN_LIMIT: /* R/W word */
526 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
527 pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit);
528 } else {
529 goto passthough;
531 break;
533 case PMBUS_UT_WARN_LIMIT: /* R/W word */
534 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
535 pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit);
536 } else {
537 goto passthough;
539 break;
541 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
542 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
543 pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit);
544 } else {
545 goto passthough;
547 break;
549 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
550 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
551 pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response);
552 } else {
553 goto passthough;
555 break;
557 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
558 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
559 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit);
560 } else {
561 goto passthough;
563 break;
565 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
566 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
567 pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response);
568 } else {
569 goto passthough;
571 break;
573 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
574 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
575 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit);
576 } else {
577 goto passthough;
579 break;
581 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
582 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
583 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit);
584 } else {
585 goto passthough;
587 break;
589 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
590 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
591 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit);
592 } else {
593 goto passthough;
595 break;
597 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
598 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
599 pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response);
600 } else {
601 goto passthough;
603 break;
605 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
606 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
607 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit);
608 } else {
609 goto passthough;
611 break;
613 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
614 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
615 pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response);
616 } else {
617 goto passthough;
619 break;
621 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
622 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
623 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit);
624 } else {
625 goto passthough;
627 break;
629 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
630 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
631 pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit);
632 } else {
633 goto passthough;
635 break;
637 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
638 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
639 pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response);
640 } else {
641 goto passthough;
643 break;
645 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
646 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
647 pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit);
648 } else {
649 goto passthough;
651 break;
653 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
654 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
655 pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit);
656 } else {
657 goto passthough;
659 break;
661 case PMBUS_STATUS_BYTE: /* R/W byte */
662 pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF);
663 break;
665 case PMBUS_STATUS_WORD: /* R/W word */
666 pmbus_send16(pmdev, pmdev->pages[index].status_word);
667 break;
669 case PMBUS_STATUS_VOUT: /* R/W byte */
670 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
671 pmbus_send8(pmdev, pmdev->pages[index].status_vout);
672 } else {
673 goto passthough;
675 break;
677 case PMBUS_STATUS_IOUT: /* R/W byte */
678 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
679 pmbus_send8(pmdev, pmdev->pages[index].status_iout);
680 } else {
681 goto passthough;
683 break;
685 case PMBUS_STATUS_INPUT: /* R/W byte */
686 if (pmdev->pages[index].page_flags & PB_HAS_VIN ||
687 pmdev->pages[index].page_flags & PB_HAS_IIN ||
688 pmdev->pages[index].page_flags & PB_HAS_PIN) {
689 pmbus_send8(pmdev, pmdev->pages[index].status_input);
690 } else {
691 goto passthough;
693 break;
695 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
696 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
697 pmbus_send8(pmdev, pmdev->pages[index].status_temperature);
698 } else {
699 goto passthough;
701 break;
703 case PMBUS_STATUS_CML: /* R/W byte */
704 pmbus_send8(pmdev, pmdev->pages[index].status_cml);
705 break;
707 case PMBUS_STATUS_OTHER: /* R/W byte */
708 pmbus_send8(pmdev, pmdev->pages[index].status_other);
709 break;
711 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
712 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
713 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
714 } else {
715 goto passthough;
717 break;
719 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
720 if (pmdev->pages[index].page_flags & PB_HAS_EOUT) {
721 pmbus_send(pmdev, pmdev->pages[index].read_eout, 5);
722 } else {
723 goto passthough;
725 break;
727 case PMBUS_READ_VIN: /* Read-Only word */
728 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
729 pmbus_send16(pmdev, pmdev->pages[index].read_vin);
730 } else {
731 goto passthough;
733 break;
735 case PMBUS_READ_IIN: /* Read-Only word */
736 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
737 pmbus_send16(pmdev, pmdev->pages[index].read_iin);
738 } else {
739 goto passthough;
741 break;
743 case PMBUS_READ_VOUT: /* Read-Only word */
744 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
745 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
746 } else {
747 goto passthough;
749 break;
751 case PMBUS_READ_IOUT: /* Read-Only word */
752 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
753 pmbus_send16(pmdev, pmdev->pages[index].read_iout);
754 } else {
755 goto passthough;
757 break;
759 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
760 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
761 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1);
762 } else {
763 goto passthough;
765 break;
767 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
768 if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) {
769 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2);
770 } else {
771 goto passthough;
773 break;
775 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
776 if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) {
777 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3);
778 } else {
779 goto passthough;
781 break;
783 case PMBUS_READ_POUT: /* Read-Only word */
784 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
785 pmbus_send16(pmdev, pmdev->pages[index].read_pout);
786 } else {
787 goto passthough;
789 break;
791 case PMBUS_READ_PIN: /* Read-Only word */
792 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
793 pmbus_send16(pmdev, pmdev->pages[index].read_pin);
794 } else {
795 goto passthough;
797 break;
799 case PMBUS_REVISION: /* Read-Only byte */
800 pmbus_send8(pmdev, pmdev->pages[index].revision);
801 break;
803 case PMBUS_MFR_ID: /* R/W block */
804 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
805 pmbus_send_string(pmdev, pmdev->pages[index].mfr_id);
806 } else {
807 goto passthough;
809 break;
811 case PMBUS_MFR_MODEL: /* R/W block */
812 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
813 pmbus_send_string(pmdev, pmdev->pages[index].mfr_model);
814 } else {
815 goto passthough;
817 break;
819 case PMBUS_MFR_REVISION: /* R/W block */
820 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
821 pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision);
822 } else {
823 goto passthough;
825 break;
827 case PMBUS_MFR_LOCATION: /* R/W block */
828 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
829 pmbus_send_string(pmdev, pmdev->pages[index].mfr_location);
830 } else {
831 goto passthough;
833 break;
835 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
836 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
837 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min);
838 } else {
839 goto passthough;
841 break;
843 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
844 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
845 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max);
846 } else {
847 goto passthough;
849 break;
851 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
852 if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) {
853 pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max);
854 } else {
855 goto passthough;
857 break;
859 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
860 if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) {
861 pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max);
862 } else {
863 goto passthough;
865 break;
867 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
868 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
869 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min);
870 } else {
871 goto passthough;
873 break;
875 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
876 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
877 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max);
878 } else {
879 goto passthough;
881 break;
883 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
884 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) {
885 pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max);
886 } else {
887 goto passthough;
889 break;
891 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
892 if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) {
893 pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max);
894 } else {
895 goto passthough;
897 break;
899 case PMBUS_MFR_MAX_TEMP_1: /* R/W word */
900 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
901 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1);
902 } else {
903 goto passthough;
905 break;
907 case PMBUS_MFR_MAX_TEMP_2: /* R/W word */
908 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
909 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2);
910 } else {
911 goto passthough;
913 break;
915 case PMBUS_MFR_MAX_TEMP_3: /* R/W word */
916 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
917 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3);
918 } else {
919 goto passthough;
921 break;
923 case PMBUS_CLEAR_FAULTS: /* Send Byte */
924 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
925 case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
926 case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */
927 case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */
928 case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */
929 case PMBUS_STORE_USER_ALL: /* Send Byte */
930 case PMBUS_RESTORE_USER_ALL: /* Send Byte */
931 case PMBUS_STORE_USER_CODE: /* Write-only Byte */
932 case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */
933 case PMBUS_QUERY: /* Write-Only */
934 qemu_log_mask(LOG_GUEST_ERROR,
935 "%s: reading from write only register 0x%02x\n",
936 __func__, pmdev->code);
937 break;
939 passthough:
940 default:
941 /* Pass through read request if not handled */
942 if (pmdc->receive_byte) {
943 ret = pmdc->receive_byte(pmdev);
945 break;
948 if (pmdev->out_buf_len != 0) {
949 ret = pmbus_out_buf_pop(pmdev);
950 return ret;
953 return ret;
957 * PMBus clear faults command applies to all status registers, existing faults
958 * should separately get re-asserted.
960 static void pmbus_clear_faults(PMBusDevice *pmdev)
962 for (uint8_t i = 0; i < pmdev->num_pages; i++) {
963 pmdev->pages[i].status_word = 0;
964 pmdev->pages[i].status_vout = 0;
965 pmdev->pages[i].status_iout = 0;
966 pmdev->pages[i].status_input = 0;
967 pmdev->pages[i].status_temperature = 0;
968 pmdev->pages[i].status_cml = 0;
969 pmdev->pages[i].status_other = 0;
970 pmdev->pages[i].status_mfr_specific = 0;
971 pmdev->pages[i].status_fans_1_2 = 0;
972 pmdev->pages[i].status_fans_3_4 = 0;
978 * PMBus operation is used to turn On and Off PSUs
979 * Therefore, default value for the Operation should be PB_OP_ON or 0x80
981 static void pmbus_operation(PMBusDevice *pmdev)
983 uint8_t index = pmdev->page;
984 if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
985 pmdev->pages[index].read_vout = 0;
986 pmdev->pages[index].read_iout = 0;
987 pmdev->pages[index].read_pout = 0;
988 return;
991 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
992 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
995 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
996 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
998 pmbus_check_limits(pmdev);
1001 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
1003 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
1004 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
1005 int ret = 0;
1006 uint8_t index;
1008 if (len == 0) {
1009 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
1010 return -1;
1013 if (!pmdev->pages) { /* allocate memory for pages on first use */
1014 pmbus_pages_alloc(pmdev);
1017 pmdev->in_buf_len = len;
1018 pmdev->in_buf = buf;
1020 pmdev->code = buf[0]; /* PMBus command code */
1021 if (len == 1) { /* Single length writes are command codes only */
1022 return 0;
1025 if (pmdev->code == PMBUS_PAGE) {
1026 pmdev->page = pmbus_receive8(pmdev);
1027 return 0;
1029 /* loop through all the pages when 0xFF is received */
1030 if (pmdev->page == PB_ALL_PAGES) {
1031 for (int i = 0; i < pmdev->num_pages; i++) {
1032 pmdev->page = i;
1033 pmbus_write_data(smd, buf, len);
1035 pmdev->page = PB_ALL_PAGES;
1036 return 0;
1039 index = pmdev->page;
1041 switch (pmdev->code) {
1042 case PMBUS_OPERATION: /* R/W byte */
1043 pmdev->pages[index].operation = pmbus_receive8(pmdev);
1044 pmbus_operation(pmdev);
1045 break;
1047 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
1048 pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1049 break;
1051 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1052 pmbus_clear_faults(pmdev);
1053 break;
1055 case PMBUS_PHASE: /* R/W byte */
1056 pmdev->pages[index].phase = pmbus_receive8(pmdev);
1057 break;
1059 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1060 case PMBUS_WRITE_PROTECT: /* R/W byte */
1061 pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1062 break;
1064 case PMBUS_VOUT_MODE: /* R/W byte */
1065 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1066 pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1067 } else {
1068 goto passthrough;
1070 break;
1072 case PMBUS_VOUT_COMMAND: /* R/W word */
1073 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1074 pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1075 } else {
1076 goto passthrough;
1078 break;
1080 case PMBUS_VOUT_TRIM: /* R/W word */
1081 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1082 pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1083 } else {
1084 goto passthrough;
1086 break;
1088 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
1089 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1090 pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1091 } else {
1092 goto passthrough;
1094 break;
1096 case PMBUS_VOUT_MAX: /* R/W word */
1097 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1098 pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1099 } else {
1100 goto passthrough;
1102 break;
1104 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
1105 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1106 pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1107 } else {
1108 goto passthrough;
1110 break;
1112 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
1113 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1114 pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1115 } else {
1116 goto passthrough;
1118 break;
1120 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
1121 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1122 pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1123 } else {
1124 goto passthrough;
1126 break;
1128 case PMBUS_VOUT_DROOP: /* R/W word */
1129 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1130 pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1131 } else {
1132 goto passthrough;
1134 break;
1136 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
1137 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1138 pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1139 } else {
1140 goto passthrough;
1142 break;
1144 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
1145 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1146 pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1147 } else {
1148 goto passthrough;
1150 break;
1152 case PMBUS_POUT_MAX: /* R/W word */
1153 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1154 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1155 } else {
1156 goto passthrough;
1158 break;
1160 case PMBUS_VIN_ON: /* R/W word */
1161 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1162 pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1163 } else {
1164 goto passthrough;
1166 break;
1168 case PMBUS_VIN_OFF: /* R/W word */
1169 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1170 pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1171 } else {
1172 goto passthrough;
1174 break;
1176 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
1177 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1178 pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1179 } else {
1180 goto passthrough;
1182 break;
1184 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
1185 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1186 pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1187 } else {
1188 goto passthrough;
1190 break;
1192 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
1193 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1194 pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1195 } else {
1196 goto passthrough;
1198 break;
1200 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
1201 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1202 pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1203 } else {
1204 goto passthrough;
1206 break;
1208 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
1209 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1210 pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1211 } else {
1212 goto passthrough;
1214 break;
1216 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
1217 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1218 pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1219 } else {
1220 goto passthrough;
1222 break;
1224 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
1225 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1226 pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1227 } else {
1228 goto passthrough;
1230 break;
1232 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
1233 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1234 pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1235 } else {
1236 goto passthrough;
1238 break;
1240 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
1241 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1242 pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1243 } else {
1244 goto passthrough;
1246 break;
1248 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
1249 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1250 pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1251 } else {
1252 goto passthrough;
1254 break;
1256 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1257 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1258 pmdev->pages[index].iout_oc_lv_fault_response
1259 = pmbus_receive8(pmdev);
1260 } else {
1261 goto passthrough;
1263 break;
1265 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
1266 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1267 pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1268 } else {
1269 goto passthrough;
1271 break;
1273 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
1274 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1275 pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1276 } else {
1277 goto passthrough;
1279 break;
1281 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
1282 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1283 pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1284 } else {
1285 goto passthrough;
1287 break;
1289 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
1290 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1291 pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1292 } else {
1293 goto passthrough;
1295 break;
1297 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
1298 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1299 pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1300 } else {
1301 goto passthrough;
1303 break;
1305 case PMBUS_OT_WARN_LIMIT: /* R/W word */
1306 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1307 pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1308 } else {
1309 goto passthrough;
1311 break;
1313 case PMBUS_UT_WARN_LIMIT: /* R/W word */
1314 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1315 pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1316 } else {
1317 goto passthrough;
1319 break;
1321 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
1322 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1323 pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1324 } else {
1325 goto passthrough;
1327 break;
1329 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
1330 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1331 pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1332 } else {
1333 goto passthrough;
1335 break;
1337 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
1338 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1339 pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1340 } else {
1341 goto passthrough;
1343 break;
1345 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
1346 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1347 pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1348 } else {
1349 goto passthrough;
1351 break;
1353 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
1354 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1355 pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1356 } else {
1357 goto passthrough;
1359 break;
1361 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
1362 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1363 pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1364 } else {
1365 goto passthrough;
1367 break;
1369 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
1370 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1371 pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1372 } else {
1373 goto passthrough;
1375 break;
1377 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
1378 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1379 pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1380 } else {
1381 goto passthrough;
1383 break;
1385 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
1386 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1387 pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1388 } else {
1389 goto passthrough;
1391 break;
1393 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
1394 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1395 pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1396 } else {
1397 goto passthrough;
1399 break;
1401 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
1402 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1403 pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1404 } else {
1405 goto passthrough;
1407 break;
1409 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
1410 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1411 pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1412 } else {
1413 goto passthrough;
1415 break;
1417 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
1418 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1419 pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1420 } else {
1421 goto passthrough;
1423 break;
1425 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
1426 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1427 pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1428 } else {
1429 goto passthrough;
1431 break;
1433 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
1434 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1435 pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1436 } else {
1437 goto passthrough;
1439 break;
1441 case PMBUS_STATUS_BYTE: /* R/W byte */
1442 pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1443 break;
1445 case PMBUS_STATUS_WORD: /* R/W word */
1446 pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1447 break;
1449 case PMBUS_STATUS_VOUT: /* R/W byte */
1450 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1451 pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1452 } else {
1453 goto passthrough;
1455 break;
1457 case PMBUS_STATUS_IOUT: /* R/W byte */
1458 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1459 pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1460 } else {
1461 goto passthrough;
1463 break;
1465 case PMBUS_STATUS_INPUT: /* R/W byte */
1466 pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1467 break;
1469 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
1470 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1471 pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1472 } else {
1473 goto passthrough;
1475 break;
1477 case PMBUS_STATUS_CML: /* R/W byte */
1478 pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1479 break;
1481 case PMBUS_STATUS_OTHER: /* R/W byte */
1482 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1483 break;
1485 case PMBUS_PAGE_PLUS_READ: /* Block Read-only */
1486 case PMBUS_CAPABILITY: /* Read-Only byte */
1487 case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */
1488 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
1489 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
1490 case PMBUS_READ_VIN: /* Read-Only word */
1491 case PMBUS_READ_IIN: /* Read-Only word */
1492 case PMBUS_READ_VCAP: /* Read-Only word */
1493 case PMBUS_READ_VOUT: /* Read-Only word */
1494 case PMBUS_READ_IOUT: /* Read-Only word */
1495 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
1496 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
1497 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
1498 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
1499 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
1500 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
1501 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
1502 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
1503 case PMBUS_READ_FREQUENCY: /* Read-Only word */
1504 case PMBUS_READ_POUT: /* Read-Only word */
1505 case PMBUS_READ_PIN: /* Read-Only word */
1506 case PMBUS_REVISION: /* Read-Only byte */
1507 case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */
1508 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
1509 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
1510 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
1511 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
1512 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
1513 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
1514 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
1515 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
1516 case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */
1517 case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */
1518 case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */
1519 case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */
1520 case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */
1521 case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */
1522 case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */
1523 qemu_log_mask(LOG_GUEST_ERROR,
1524 "%s: writing to read-only register 0x%02x\n",
1525 __func__, pmdev->code);
1526 break;
1528 passthrough:
1529 /* Unimplimented registers get passed to the device */
1530 default:
1531 if (pmdc->write_data) {
1532 ret = pmdc->write_data(pmdev, buf, len);
1534 break;
1536 pmbus_check_limits(pmdev);
1537 pmdev->in_buf_len = 0;
1538 return ret;
1541 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1543 if (!pmdev->pages) { /* allocate memory for pages on first use */
1544 pmbus_pages_alloc(pmdev);
1547 /* The 0xFF page is special for commands applying to all pages */
1548 if (index == PB_ALL_PAGES) {
1549 for (int i = 0; i < pmdev->num_pages; i++) {
1550 pmdev->pages[i].page_flags = flags;
1552 return 0;
1555 if (index > pmdev->num_pages - 1) {
1556 qemu_log_mask(LOG_GUEST_ERROR,
1557 "%s: index %u is out of range\n",
1558 __func__, index);
1559 return -1;
1562 pmdev->pages[index].page_flags = flags;
1564 return 0;
1567 /* TODO: include pmbus page info in vmstate */
1568 const VMStateDescription vmstate_pmbus_device = {
1569 .name = TYPE_PMBUS_DEVICE,
1570 .version_id = 0,
1571 .minimum_version_id = 0,
1572 .fields = (VMStateField[]) {
1573 VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1574 VMSTATE_UINT8(num_pages, PMBusDevice),
1575 VMSTATE_UINT8(code, PMBusDevice),
1576 VMSTATE_UINT8(page, PMBusDevice),
1577 VMSTATE_UINT8(capability, PMBusDevice),
1578 VMSTATE_END_OF_LIST()
1582 static void pmbus_device_finalize(Object *obj)
1584 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1585 g_free(pmdev->pages);
1588 static void pmbus_device_class_init(ObjectClass *klass, void *data)
1590 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1592 k->quick_cmd = pmbus_quick_cmd;
1593 k->write_data = pmbus_write_data;
1594 k->receive_byte = pmbus_receive_byte;
1597 static const TypeInfo pmbus_device_type_info = {
1598 .name = TYPE_PMBUS_DEVICE,
1599 .parent = TYPE_SMBUS_DEVICE,
1600 .instance_size = sizeof(PMBusDevice),
1601 .instance_finalize = pmbus_device_finalize,
1602 .abstract = true,
1603 .class_size = sizeof(PMBusDeviceClass),
1604 .class_init = pmbus_device_class_init,
1607 static void pmbus_device_register_types(void)
1609 type_register_static(&pmbus_device_type_info);
1612 type_init(pmbus_device_register_types)