master
c 1,941 lines 61.6 KB
Raw
1 /*
2 * PMBus wrapper over SMBus
3 *
4 * Copyright 2021 Google LLC
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10 #include <math.h>
11 #include "hw/i2c/pmbus_device.h"
12 #include "migration/vmstate.h"
13 #include "qemu/module.h"
14 #include "qemu/log.h"
15
16 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value)
17 {
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;
21 }
22
23 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value)
24 {
25 /* X = (Y * 10^-R - b) / m */
26 double x = (value / pow(10, c.R) - c.b) / c.m;
27 return (x > 0
28 ? (x < G_MAXUINT32 ? (uint32_t)x : G_MAXUINT32)
29 : 0);
30 }
31
32 uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
33 {
34 /* L = D * 2^(-e) */
35 if (exp < 0) {
36 return value << (-exp);
37 }
38 return value >> exp;
39 }
40
41 uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
42 {
43 /* D = L * 2^e */
44 if (exp < 0) {
45 return value >> (-exp);
46 }
47 return value << exp;
48 }
49
50 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
51 {
52 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
53 qemu_log_mask(LOG_GUEST_ERROR,
54 "PMBus device tried to send too much data");
55 len = 0;
56 }
57
58 for (int i = len - 1; i >= 0; i--) {
59 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1];
60 }
61 pmdev->out_buf_len += len;
62 }
63
64 /* Internal only, convert unsigned ints to the little endian bus */
65 static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size)
66 {
67 uint8_t bytes[8];
68 g_assert(size <= 8);
69
70 for (int i = 0; i < size; i++) {
71 bytes[i] = data & 0xFF;
72 data = data >> 8;
73 }
74 pmbus_send(pmdev, bytes, size);
75 }
76
77 void pmbus_send8(PMBusDevice *pmdev, uint8_t data)
78 {
79 pmbus_send_uint(pmdev, data, 1);
80 }
81
82 void pmbus_send16(PMBusDevice *pmdev, uint16_t data)
83 {
84 pmbus_send_uint(pmdev, data, 2);
85 }
86
87 void pmbus_send32(PMBusDevice *pmdev, uint32_t data)
88 {
89 pmbus_send_uint(pmdev, data, 4);
90 }
91
92 void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
93 {
94 pmbus_send_uint(pmdev, data, 8);
95 }
96
97 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
98 {
99 if (!data) {
100 qemu_log_mask(LOG_GUEST_ERROR,
101 "%s: %s: uninitialised read from 0x%02x\n",
102 __func__, DEVICE(pmdev)->canonical_path, pmdev->code);
103 return;
104 }
105
106 size_t len = strlen(data);
107 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
108 pmdev->out_buf[len + pmdev->out_buf_len] = len;
109
110 for (int i = len - 1; i >= 0; i--) {
111 pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i];
112 }
113 pmdev->out_buf_len += len + 1;
114 }
115
116 uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len)
117 {
118 /* dest may contain data from previous writes */
119 memset(dest, 0, len);
120
121 /* Exclude command code from return value */
122 pmdev->in_buf++;
123 pmdev->in_buf_len--;
124
125 /* The byte after the command code denotes the length */
126 uint8_t sent_len = pmdev->in_buf[0];
127
128 if (sent_len != pmdev->in_buf_len - 1) {
129 qemu_log_mask(LOG_GUEST_ERROR,
130 "%s: length mismatch. Expected %d bytes, got %d bytes\n",
131 __func__, sent_len, pmdev->in_buf_len - 1);
132 }
133
134 /* exclude length byte */
135 pmdev->in_buf++;
136 pmdev->in_buf_len--;
137
138 if (pmdev->in_buf_len < len) {
139 len = pmdev->in_buf_len;
140 }
141 memcpy(dest, pmdev->in_buf, len);
142 return len;
143 }
144
145
146 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
147 {
148 uint64_t ret = 0;
149
150 /* Exclude command code from return value */
151 pmdev->in_buf++;
152 pmdev->in_buf_len--;
153
154 for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
155 ret = ret << 8 | pmdev->in_buf[i];
156 }
157 return ret;
158 }
159
160 uint8_t pmbus_receive8(PMBusDevice *pmdev)
161 {
162 if (pmdev->in_buf_len - 1 != 1) {
163 qemu_log_mask(LOG_GUEST_ERROR,
164 "%s: length mismatch. Expected 1 byte, got %d bytes\n",
165 __func__, pmdev->in_buf_len - 1);
166 }
167 return pmbus_receive_uint(pmdev);
168 }
169
170 uint16_t pmbus_receive16(PMBusDevice *pmdev)
171 {
172 if (pmdev->in_buf_len - 1 != 2) {
173 qemu_log_mask(LOG_GUEST_ERROR,
174 "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
175 __func__, pmdev->in_buf_len - 1);
176 }
177 return pmbus_receive_uint(pmdev);
178 }
179
180 uint32_t pmbus_receive32(PMBusDevice *pmdev)
181 {
182 if (pmdev->in_buf_len - 1 != 4) {
183 qemu_log_mask(LOG_GUEST_ERROR,
184 "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
185 __func__, pmdev->in_buf_len - 1);
186 }
187 return pmbus_receive_uint(pmdev);
188 }
189
190 uint64_t pmbus_receive64(PMBusDevice *pmdev)
191 {
192 if (pmdev->in_buf_len - 1 != 8) {
193 qemu_log_mask(LOG_GUEST_ERROR,
194 "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
195 __func__, pmdev->in_buf_len - 1);
196 }
197 return pmbus_receive_uint(pmdev);
198 }
199
200 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
201 {
202 if (pmdev->out_buf_len == 0) {
203 qemu_log_mask(LOG_GUEST_ERROR,
204 "%s: tried to read from empty buffer",
205 __func__);
206 return PMBUS_ERR_BYTE;
207 }
208 uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1];
209 pmdev->out_buf_len--;
210 return data;
211 }
212
213 static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read)
214 {
215 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
216 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
217
218 if (pmdc->quick_cmd) {
219 pmdc->quick_cmd(pmdev, read);
220 }
221 }
222
223 static uint8_t pmbus_pages_num(PMBusDevice *pmdev)
224 {
225 const PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev);
226
227 /* some PMBus devices don't use the PAGE command, so they get 1 page */
228 return k->device_num_pages ? : 1;
229 }
230
231 static void pmbus_pages_alloc(PMBusDevice *pmdev)
232 {
233 pmdev->num_pages = pmbus_pages_num(pmdev);
234 pmdev->pages = g_new0(PMBusPage, pmdev->num_pages);
235 }
236
237 void pmbus_check_limits(PMBusDevice *pmdev)
238 {
239 for (int i = 0; i < pmdev->num_pages; i++) {
240 if ((pmdev->pages[i].operation & PB_OP_ON) == 0) {
241 continue; /* don't check powered off devices */
242 }
243
244 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) {
245 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
246 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT;
247 }
248
249 if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) {
250 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
251 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN;
252 }
253
254 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) {
255 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
256 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN;
257 }
258
259 if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) {
260 pmdev->pages[i].status_word |= PB_STATUS_VOUT;
261 pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT;
262 }
263
264 if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) {
265 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
266 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN;
267 }
268
269 if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) {
270 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
271 pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN;
272 }
273
274 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) {
275 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
276 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN;
277 }
278
279 if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) {
280 pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
281 pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT;
282 }
283
284 if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) {
285 pmdev->pages[i].status_word |= PB_STATUS_INPUT;
286 pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN;
287 }
288
289 if (pmdev->pages[i].read_temperature_1
290 > pmdev->pages[i].ot_fault_limit) {
291 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
292 pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT;
293 }
294
295 if (pmdev->pages[i].read_temperature_1
296 > pmdev->pages[i].ot_warn_limit) {
297 pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
298 pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN;
299 }
300 }
301 }
302
303 void pmbus_idle(PMBusDevice *pmdev)
304 {
305 pmdev->code = PMBUS_IDLE_STATE;
306 }
307
308 /* assert the status_cml error upon receipt of malformed command */
309 static void pmbus_cml_error(PMBusDevice *pmdev)
310 {
311 for (int i = 0; i < pmdev->num_pages; i++) {
312 pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
313 pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
314 }
315 }
316
317 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
318 {
319 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
320 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
321 uint8_t ret = PMBUS_ERR_BYTE;
322 uint8_t index;
323
324 if (pmdev->out_buf_len != 0) {
325 ret = pmbus_out_buf_pop(pmdev);
326 return ret;
327 }
328
329 /*
330 * Reading from all pages will return the value from page 0,
331 * means that all subsequent commands are to be applied to all output.
332 */
333 if (pmdev->page == PB_ALL_PAGES) {
334 index = 0;
335 } else if (pmdev->page > pmdev->num_pages - 1) {
336 qemu_log_mask(LOG_GUEST_ERROR,
337 "%s: page %d is out of range\n",
338 __func__, pmdev->page);
339 pmbus_cml_error(pmdev);
340 return PMBUS_ERR_BYTE;
341 } else {
342 index = pmdev->page;
343 }
344
345 switch (pmdev->code) {
346 case PMBUS_PAGE:
347 pmbus_send8(pmdev, pmdev->page);
348 break;
349
350 case PMBUS_OPERATION: /* R/W byte */
351 pmbus_send8(pmdev, pmdev->pages[index].operation);
352 break;
353
354 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
355 pmbus_send8(pmdev, pmdev->pages[index].on_off_config);
356 break;
357
358 case PMBUS_PHASE: /* R/W byte */
359 pmbus_send8(pmdev, pmdev->pages[index].phase);
360 break;
361
362 case PMBUS_WRITE_PROTECT: /* R/W byte */
363 pmbus_send8(pmdev, pmdev->pages[index].write_protect);
364 break;
365
366 case PMBUS_CAPABILITY:
367 pmbus_send8(pmdev, pmdev->capability);
368 if (pmdev->capability & BIT(7)) {
369 qemu_log_mask(LOG_UNIMP,
370 "%s: PEC is enabled but not yet supported.\n",
371 __func__);
372 }
373 break;
374
375 case PMBUS_VOUT_MODE: /* R/W byte */
376 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
377 pmbus_send8(pmdev, pmdev->pages[index].vout_mode);
378 } else {
379 goto passthough;
380 }
381 break;
382
383 case PMBUS_VOUT_COMMAND: /* R/W word */
384 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
385 pmbus_send16(pmdev, pmdev->pages[index].vout_command);
386 } else {
387 goto passthough;
388 }
389 break;
390
391 case PMBUS_VOUT_TRIM: /* R/W word */
392 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
393 pmbus_send16(pmdev, pmdev->pages[index].vout_trim);
394 } else {
395 goto passthough;
396 }
397 break;
398
399 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
400 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
401 pmbus_send16(pmdev, pmdev->pages[index].vout_cal_offset);
402 } else {
403 goto passthough;
404 }
405 break;
406
407 case PMBUS_VOUT_MAX: /* R/W word */
408 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
409 pmbus_send16(pmdev, pmdev->pages[index].vout_max);
410 } else {
411 goto passthough;
412 }
413 break;
414
415 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
416 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
417 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_high);
418 } else {
419 goto passthough;
420 }
421 break;
422
423 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
424 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
425 pmbus_send16(pmdev, pmdev->pages[index].vout_margin_low);
426 } else {
427 goto passthough;
428 }
429 break;
430
431 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
432 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
433 pmbus_send16(pmdev, pmdev->pages[index].vout_transition_rate);
434 } else {
435 goto passthough;
436 }
437 break;
438
439 case PMBUS_VOUT_DROOP: /* R/W word */
440 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
441 pmbus_send16(pmdev, pmdev->pages[index].vout_droop);
442 } else {
443 goto passthough;
444 }
445 break;
446
447 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
448 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
449 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_loop);
450 } else {
451 goto passthough;
452 }
453 break;
454
455 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
456 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
457 pmbus_send16(pmdev, pmdev->pages[index].vout_scale_monitor);
458 } else {
459 goto passthough;
460 }
461 break;
462
463 case PMBUS_VOUT_MIN: /* R/W word */
464 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
465 pmbus_send16(pmdev, pmdev->pages[index].vout_min);
466 } else {
467 goto passthough;
468 }
469 break;
470
471 /* TODO: implement coefficients support */
472
473 case PMBUS_POUT_MAX: /* R/W word */
474 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
475 pmbus_send16(pmdev, pmdev->pages[index].pout_max);
476 } else {
477 goto passthough;
478 }
479 break;
480
481 case PMBUS_VIN_ON: /* R/W word */
482 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
483 pmbus_send16(pmdev, pmdev->pages[index].vin_on);
484 } else {
485 goto passthough;
486 }
487 break;
488
489 case PMBUS_VIN_OFF: /* R/W word */
490 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
491 pmbus_send16(pmdev, pmdev->pages[index].vin_off);
492 } else {
493 goto passthough;
494 }
495 break;
496
497 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
498 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
499 pmbus_send16(pmdev, pmdev->pages[index].iout_cal_gain);
500 } else {
501 goto passthough;
502 }
503 break;
504
505 case PMBUS_FAN_CONFIG_1_2: /* R/W byte */
506 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
507 pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2);
508 } else {
509 goto passthough;
510 }
511 break;
512
513 case PMBUS_FAN_COMMAND_1: /* R/W word */
514 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
515 pmbus_send16(pmdev, pmdev->pages[index].fan_command_1);
516 } else {
517 goto passthough;
518 }
519 break;
520
521 case PMBUS_FAN_COMMAND_2: /* R/W word */
522 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
523 pmbus_send16(pmdev, pmdev->pages[index].fan_command_2);
524 } else {
525 goto passthough;
526 }
527 break;
528
529 case PMBUS_FAN_CONFIG_3_4: /* R/W byte */
530 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
531 pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4);
532 } else {
533 goto passthough;
534 }
535 break;
536
537 case PMBUS_FAN_COMMAND_3: /* R/W word */
538 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
539 pmbus_send16(pmdev, pmdev->pages[index].fan_command_3);
540 } else {
541 goto passthough;
542 }
543 break;
544
545 case PMBUS_FAN_COMMAND_4: /* R/W word */
546 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
547 pmbus_send16(pmdev, pmdev->pages[index].fan_command_4);
548 } else {
549 goto passthough;
550 }
551 break;
552
553 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
554 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
555 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
556 } else {
557 goto passthough;
558 }
559 break;
560
561 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
562 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
563 pmbus_send8(pmdev, pmdev->pages[index].vout_ov_fault_response);
564 } else {
565 goto passthough;
566 }
567 break;
568
569 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
570 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
571 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_warn_limit);
572 } else {
573 goto passthough;
574 }
575 break;
576
577 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
578 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
579 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_warn_limit);
580 } else {
581 goto passthough;
582 }
583 break;
584
585 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
586 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
587 pmbus_send16(pmdev, pmdev->pages[index].vout_uv_fault_limit);
588 } else {
589 goto passthough;
590 }
591 break;
592
593 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
594 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
595 pmbus_send8(pmdev, pmdev->pages[index].vout_uv_fault_response);
596 } else {
597 goto passthough;
598 }
599 break;
600
601 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
602 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
603 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_fault_limit);
604 } else {
605 goto passthough;
606 }
607 break;
608
609 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
610 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
611 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_fault_response);
612 } else {
613 goto passthough;
614 }
615 break;
616
617 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
618 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
619 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_lv_fault_limit);
620 } else {
621 goto passthough;
622 }
623 break;
624
625 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
626 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
627 pmbus_send8(pmdev, pmdev->pages[index].iout_oc_lv_fault_response);
628 } else {
629 goto passthough;
630 }
631 break;
632
633 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
634 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
635 pmbus_send16(pmdev, pmdev->pages[index].iout_oc_warn_limit);
636 } else {
637 goto passthough;
638 }
639 break;
640
641 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
642 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
643 pmbus_send16(pmdev, pmdev->pages[index].iout_uc_fault_limit);
644 } else {
645 goto passthough;
646 }
647 break;
648
649 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
650 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
651 pmbus_send8(pmdev, pmdev->pages[index].iout_uc_fault_response);
652 } else {
653 goto passthough;
654 }
655 break;
656
657 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
658 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
659 pmbus_send16(pmdev, pmdev->pages[index].ot_fault_limit);
660 } else {
661 goto passthough;
662 }
663 break;
664
665 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
666 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
667 pmbus_send8(pmdev, pmdev->pages[index].ot_fault_response);
668 } else {
669 goto passthough;
670 }
671 break;
672
673 case PMBUS_OT_WARN_LIMIT: /* R/W word */
674 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
675 pmbus_send16(pmdev, pmdev->pages[index].ot_warn_limit);
676 } else {
677 goto passthough;
678 }
679 break;
680
681 case PMBUS_UT_WARN_LIMIT: /* R/W word */
682 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
683 pmbus_send16(pmdev, pmdev->pages[index].ut_warn_limit);
684 } else {
685 goto passthough;
686 }
687 break;
688
689 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
690 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
691 pmbus_send16(pmdev, pmdev->pages[index].ut_fault_limit);
692 } else {
693 goto passthough;
694 }
695 break;
696
697 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
698 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
699 pmbus_send8(pmdev, pmdev->pages[index].ut_fault_response);
700 } else {
701 goto passthough;
702 }
703 break;
704
705 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
706 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
707 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_fault_limit);
708 } else {
709 goto passthough;
710 }
711 break;
712
713 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
714 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
715 pmbus_send8(pmdev, pmdev->pages[index].vin_ov_fault_response);
716 } else {
717 goto passthough;
718 }
719 break;
720
721 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
722 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
723 pmbus_send16(pmdev, pmdev->pages[index].vin_ov_warn_limit);
724 } else {
725 goto passthough;
726 }
727 break;
728
729 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
730 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
731 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_warn_limit);
732 } else {
733 goto passthough;
734 }
735 break;
736
737 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
738 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
739 pmbus_send16(pmdev, pmdev->pages[index].vin_uv_fault_limit);
740 } else {
741 goto passthough;
742 }
743 break;
744
745 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
746 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
747 pmbus_send8(pmdev, pmdev->pages[index].vin_uv_fault_response);
748 } else {
749 goto passthough;
750 }
751 break;
752
753 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
754 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
755 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_fault_limit);
756 } else {
757 goto passthough;
758 }
759 break;
760
761 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
762 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
763 pmbus_send8(pmdev, pmdev->pages[index].iin_oc_fault_response);
764 } else {
765 goto passthough;
766 }
767 break;
768
769 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
770 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
771 pmbus_send16(pmdev, pmdev->pages[index].iin_oc_warn_limit);
772 } else {
773 goto passthough;
774 }
775 break;
776
777 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
778 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
779 pmbus_send16(pmdev, pmdev->pages[index].pout_op_fault_limit);
780 } else {
781 goto passthough;
782 }
783 break;
784
785 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
786 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
787 pmbus_send8(pmdev, pmdev->pages[index].pout_op_fault_response);
788 } else {
789 goto passthough;
790 }
791 break;
792
793 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
794 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
795 pmbus_send16(pmdev, pmdev->pages[index].pout_op_warn_limit);
796 } else {
797 goto passthough;
798 }
799 break;
800
801 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
802 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
803 pmbus_send16(pmdev, pmdev->pages[index].pin_op_warn_limit);
804 } else {
805 goto passthough;
806 }
807 break;
808
809 case PMBUS_STATUS_BYTE: /* R/W byte */
810 pmbus_send8(pmdev, pmdev->pages[index].status_word & 0xFF);
811 break;
812
813 case PMBUS_STATUS_WORD: /* R/W word */
814 pmbus_send16(pmdev, pmdev->pages[index].status_word);
815 break;
816
817 case PMBUS_STATUS_VOUT: /* R/W byte */
818 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
819 pmbus_send8(pmdev, pmdev->pages[index].status_vout);
820 } else {
821 goto passthough;
822 }
823 break;
824
825 case PMBUS_STATUS_IOUT: /* R/W byte */
826 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
827 pmbus_send8(pmdev, pmdev->pages[index].status_iout);
828 } else {
829 goto passthough;
830 }
831 break;
832
833 case PMBUS_STATUS_INPUT: /* R/W byte */
834 if (pmdev->pages[index].page_flags & PB_HAS_VIN ||
835 pmdev->pages[index].page_flags & PB_HAS_IIN ||
836 pmdev->pages[index].page_flags & PB_HAS_PIN) {
837 pmbus_send8(pmdev, pmdev->pages[index].status_input);
838 } else {
839 goto passthough;
840 }
841 break;
842
843 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
844 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
845 pmbus_send8(pmdev, pmdev->pages[index].status_temperature);
846 } else {
847 goto passthough;
848 }
849 break;
850
851 case PMBUS_STATUS_CML: /* R/W byte */
852 pmbus_send8(pmdev, pmdev->pages[index].status_cml);
853 break;
854
855 case PMBUS_STATUS_OTHER: /* R/W byte */
856 pmbus_send8(pmdev, pmdev->pages[index].status_other);
857 break;
858
859 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
860 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
861 break;
862
863 case PMBUS_STATUS_FANS_1_2: /* R/W byte */
864 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
865 pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
866 } else {
867 goto passthough;
868 }
869 break;
870
871 case PMBUS_STATUS_FANS_3_4: /* R/W byte */
872 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
873 pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
874 } else {
875 goto passthough;
876 }
877 break;
878
879 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
880 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
881 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
882 } else {
883 goto passthough;
884 }
885 break;
886
887 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
888 if (pmdev->pages[index].page_flags & PB_HAS_EOUT) {
889 pmbus_send(pmdev, pmdev->pages[index].read_eout, 5);
890 } else {
891 goto passthough;
892 }
893 break;
894
895 case PMBUS_READ_VIN: /* Read-Only word */
896 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
897 pmbus_send16(pmdev, pmdev->pages[index].read_vin);
898 } else {
899 goto passthough;
900 }
901 break;
902
903 case PMBUS_READ_IIN: /* Read-Only word */
904 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
905 pmbus_send16(pmdev, pmdev->pages[index].read_iin);
906 } else {
907 goto passthough;
908 }
909 break;
910
911 case PMBUS_READ_VCAP: /* Read-Only word */
912 if (pmdev->pages[index].page_flags & PB_HAS_VCAP) {
913 pmbus_send16(pmdev, pmdev->pages[index].read_vcap);
914 } else {
915 goto passthough;
916 }
917 break;
918
919 case PMBUS_READ_VOUT: /* Read-Only word */
920 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
921 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
922 } else {
923 goto passthough;
924 }
925 break;
926
927 case PMBUS_READ_IOUT: /* Read-Only word */
928 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
929 pmbus_send16(pmdev, pmdev->pages[index].read_iout);
930 } else {
931 goto passthough;
932 }
933 break;
934
935 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
936 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
937 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_1);
938 } else {
939 goto passthough;
940 }
941 break;
942
943 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
944 if (pmdev->pages[index].page_flags & PB_HAS_TEMP2) {
945 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_2);
946 } else {
947 goto passthough;
948 }
949 break;
950
951 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
952 if (pmdev->pages[index].page_flags & PB_HAS_TEMP3) {
953 pmbus_send16(pmdev, pmdev->pages[index].read_temperature_3);
954 } else {
955 goto passthough;
956 }
957 break;
958
959 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
960 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
961 pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1);
962 } else {
963 goto passthough;
964 }
965 break;
966
967 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
968 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
969 pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2);
970 } else {
971 goto passthough;
972 }
973 break;
974
975 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
976 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
977 pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3);
978 } else {
979 goto passthough;
980 }
981 break;
982
983 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
984 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
985 pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4);
986 } else {
987 goto passthough;
988 }
989 break;
990
991 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
992 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
993 pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle);
994 } else {
995 goto passthough;
996 }
997 break;
998
999 case PMBUS_READ_FREQUENCY: /* Read-Only word */
1000 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1001 pmbus_send16(pmdev, pmdev->pages[index].read_frequency);
1002 } else {
1003 goto passthough;
1004 }
1005 break;
1006
1007 case PMBUS_READ_POUT: /* Read-Only word */
1008 if (pmdev->pages[index].page_flags & PB_HAS_POUT) {
1009 pmbus_send16(pmdev, pmdev->pages[index].read_pout);
1010 } else {
1011 goto passthough;
1012 }
1013 break;
1014
1015 case PMBUS_READ_PIN: /* Read-Only word */
1016 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1017 pmbus_send16(pmdev, pmdev->pages[index].read_pin);
1018 } else {
1019 goto passthough;
1020 }
1021 break;
1022
1023 case PMBUS_REVISION: /* Read-Only byte */
1024 pmbus_send8(pmdev, pmdev->pages[index].revision);
1025 break;
1026
1027 case PMBUS_MFR_ID: /* R/W block */
1028 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
1029 pmbus_send_string(pmdev, pmdev->pages[index].mfr_id);
1030 } else {
1031 goto passthough;
1032 }
1033 break;
1034
1035 case PMBUS_MFR_MODEL: /* R/W block */
1036 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
1037 pmbus_send_string(pmdev, pmdev->pages[index].mfr_model);
1038 } else {
1039 goto passthough;
1040 }
1041 break;
1042
1043 case PMBUS_MFR_REVISION: /* R/W block */
1044 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
1045 pmbus_send_string(pmdev, pmdev->pages[index].mfr_revision);
1046 } else {
1047 goto passthough;
1048 }
1049 break;
1050
1051 case PMBUS_MFR_LOCATION: /* R/W block */
1052 if (pmdev->pages[index].page_flags & PB_HAS_MFR_INFO) {
1053 pmbus_send_string(pmdev, pmdev->pages[index].mfr_location);
1054 } else {
1055 goto passthough;
1056 }
1057 break;
1058
1059 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
1060 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
1061 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_min);
1062 } else {
1063 goto passthough;
1064 }
1065 break;
1066
1067 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
1068 if (pmdev->pages[index].page_flags & PB_HAS_VIN_RATING) {
1069 pmbus_send16(pmdev, pmdev->pages[index].mfr_vin_max);
1070 } else {
1071 goto passthough;
1072 }
1073 break;
1074
1075 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
1076 if (pmdev->pages[index].page_flags & PB_HAS_IIN_RATING) {
1077 pmbus_send16(pmdev, pmdev->pages[index].mfr_iin_max);
1078 } else {
1079 goto passthough;
1080 }
1081 break;
1082
1083 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
1084 if (pmdev->pages[index].page_flags & PB_HAS_PIN_RATING) {
1085 pmbus_send16(pmdev, pmdev->pages[index].mfr_pin_max);
1086 } else {
1087 goto passthough;
1088 }
1089 break;
1090
1091 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
1092 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1093 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_min);
1094 } else {
1095 goto passthough;
1096 }
1097 break;
1098
1099 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
1100 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1101 pmbus_send16(pmdev, pmdev->pages[index].mfr_vout_max);
1102 } else {
1103 goto passthough;
1104 }
1105 break;
1106
1107 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
1108 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_RATING) {
1109 pmbus_send16(pmdev, pmdev->pages[index].mfr_iout_max);
1110 } else {
1111 goto passthough;
1112 }
1113 break;
1114
1115 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
1116 if (pmdev->pages[index].page_flags & PB_HAS_POUT_RATING) {
1117 pmbus_send16(pmdev, pmdev->pages[index].mfr_pout_max);
1118 } else {
1119 goto passthough;
1120 }
1121 break;
1122
1123 case PMBUS_MFR_MAX_TEMP_1: /* R/W word */
1124 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
1125 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_1);
1126 } else {
1127 goto passthough;
1128 }
1129 break;
1130
1131 case PMBUS_MFR_MAX_TEMP_2: /* R/W word */
1132 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
1133 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_2);
1134 } else {
1135 goto passthough;
1136 }
1137 break;
1138
1139 case PMBUS_MFR_MAX_TEMP_3: /* R/W word */
1140 if (pmdev->pages[index].page_flags & PB_HAS_TEMP_RATING) {
1141 pmbus_send16(pmdev, pmdev->pages[index].mfr_max_temp_3);
1142 } else {
1143 goto passthough;
1144 }
1145 break;
1146
1147 case PMBUS_IDLE_STATE:
1148 pmbus_send8(pmdev, PMBUS_ERR_BYTE);
1149 break;
1150
1151 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1152 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1153 case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
1154 case PMBUS_RESTORE_DEFAULT_ALL: /* Send Byte */
1155 case PMBUS_STORE_DEFAULT_CODE: /* Write-only Byte */
1156 case PMBUS_RESTORE_DEFAULT_CODE: /* Write-only Byte */
1157 case PMBUS_STORE_USER_ALL: /* Send Byte */
1158 case PMBUS_RESTORE_USER_ALL: /* Send Byte */
1159 case PMBUS_STORE_USER_CODE: /* Write-only Byte */
1160 case PMBUS_RESTORE_USER_CODE: /* Write-only Byte */
1161 case PMBUS_QUERY: /* Write-Only */
1162 qemu_log_mask(LOG_GUEST_ERROR,
1163 "%s: reading from write only register 0x%02x\n",
1164 __func__, pmdev->code);
1165 break;
1166
1167 passthough:
1168 default:
1169 /* Pass through read request if not handled */
1170 if (pmdc->receive_byte) {
1171 ret = pmdc->receive_byte(pmdev);
1172 }
1173 break;
1174 }
1175
1176 if (pmdev->out_buf_len != 0) {
1177 ret = pmbus_out_buf_pop(pmdev);
1178 return ret;
1179 }
1180
1181 return ret;
1182 }
1183
1184 /*
1185 * PMBus clear faults command applies to all status registers, existing faults
1186 * should separately get re-asserted.
1187 */
1188 static void pmbus_clear_faults(PMBusDevice *pmdev)
1189 {
1190 for (uint8_t i = 0; i < pmdev->num_pages; i++) {
1191 pmdev->pages[i].status_word = 0;
1192 pmdev->pages[i].status_vout = 0;
1193 pmdev->pages[i].status_iout = 0;
1194 pmdev->pages[i].status_input = 0;
1195 pmdev->pages[i].status_temperature = 0;
1196 pmdev->pages[i].status_cml = 0;
1197 pmdev->pages[i].status_other = 0;
1198 pmdev->pages[i].status_mfr_specific = 0;
1199 pmdev->pages[i].status_fans_1_2 = 0;
1200 pmdev->pages[i].status_fans_3_4 = 0;
1201 }
1202
1203 }
1204
1205 /*
1206 * PMBus operation is used to turn On and Off PSUs
1207 * Therefore, default value for the Operation should be PB_OP_ON or 0x80
1208 */
1209 static void pmbus_operation(PMBusDevice *pmdev)
1210 {
1211 uint8_t index = pmdev->page;
1212 if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
1213 pmdev->pages[index].read_vout = 0;
1214 pmdev->pages[index].read_iout = 0;
1215 pmdev->pages[index].read_pout = 0;
1216 return;
1217 }
1218
1219 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
1220 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
1221 }
1222
1223 if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
1224 pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
1225 }
1226 pmbus_check_limits(pmdev);
1227 }
1228
1229 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
1230 {
1231 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
1232 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
1233 int ret = 0;
1234 uint8_t index;
1235
1236 if (len == 0) {
1237 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
1238 return PMBUS_ERR_BYTE;
1239 }
1240
1241 if (!pmdev->pages) { /* allocate memory for pages on first use */
1242 pmbus_pages_alloc(pmdev);
1243 }
1244
1245 pmdev->in_buf_len = len;
1246 pmdev->in_buf = buf;
1247
1248 /* clear the output buffer on any new write transaction */
1249 if (pmdev->out_buf_len != 0) {
1250 qemu_log_mask(LOG_GUEST_ERROR,
1251 "%s: previous read was not completed, %d bytes dropped\n",
1252 __func__, pmdev->out_buf_len);
1253
1254 pmdev->out_buf_len = 0;
1255 memset(pmdev->out_buf, 0, sizeof(pmdev->out_buf));
1256 }
1257
1258 pmdev->code = buf[0]; /* PMBus command code */
1259
1260 if (pmdev->code == PMBUS_CLEAR_FAULTS) {
1261 pmbus_clear_faults(pmdev);
1262 }
1263
1264 if (len == 1) { /* Single length writes are command codes only */
1265 return 0;
1266 }
1267
1268 if (pmdev->code == PMBUS_PAGE) {
1269 pmdev->page = pmbus_receive8(pmdev);
1270
1271 if (pmdev->page > pmdev->num_pages - 1 && pmdev->page != PB_ALL_PAGES) {
1272 qemu_log_mask(LOG_GUEST_ERROR,
1273 "%s: page %u is out of range\n",
1274 __func__, pmdev->page);
1275 pmdev->page = 0; /* undefined behaviour - reset to page 0 */
1276 pmbus_cml_error(pmdev);
1277 return PMBUS_ERR_BYTE;
1278 }
1279 return 0;
1280 }
1281
1282 /* loop through all the pages when 0xFF is received */
1283 if (pmdev->page == PB_ALL_PAGES) {
1284 for (int i = 0; i < pmdev->num_pages; i++) {
1285 pmdev->page = i;
1286 pmbus_write_data(smd, buf, len);
1287 }
1288 pmdev->page = PB_ALL_PAGES;
1289 return 0;
1290 }
1291
1292 index = pmdev->page;
1293
1294 switch (pmdev->code) {
1295 case PMBUS_OPERATION: /* R/W byte */
1296 pmdev->pages[index].operation = pmbus_receive8(pmdev);
1297 pmbus_operation(pmdev);
1298 break;
1299
1300 case PMBUS_ON_OFF_CONFIG: /* R/W byte */
1301 pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1302 break;
1303
1304 case PMBUS_CLEAR_FAULTS: /* Send Byte */
1305 pmbus_clear_faults(pmdev);
1306 break;
1307
1308 case PMBUS_PHASE: /* R/W byte */
1309 pmdev->pages[index].phase = pmbus_receive8(pmdev);
1310 break;
1311
1312 case PMBUS_PAGE_PLUS_WRITE: /* Block Write-only */
1313 case PMBUS_WRITE_PROTECT: /* R/W byte */
1314 pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1315 break;
1316
1317 case PMBUS_VOUT_MODE: /* R/W byte */
1318 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1319 pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1320 } else {
1321 goto passthrough;
1322 }
1323 break;
1324
1325 case PMBUS_VOUT_COMMAND: /* R/W word */
1326 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1327 pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1328 } else {
1329 goto passthrough;
1330 }
1331 break;
1332
1333 case PMBUS_VOUT_TRIM: /* R/W word */
1334 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1335 pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1336 } else {
1337 goto passthrough;
1338 }
1339 break;
1340
1341 case PMBUS_VOUT_CAL_OFFSET: /* R/W word */
1342 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1343 pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1344 } else {
1345 goto passthrough;
1346 }
1347 break;
1348
1349 case PMBUS_VOUT_MAX: /* R/W word */
1350 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1351 pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1352 } else {
1353 goto passthrough;
1354 }
1355 break;
1356
1357 case PMBUS_VOUT_MARGIN_HIGH: /* R/W word */
1358 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1359 pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1360 } else {
1361 goto passthrough;
1362 }
1363 break;
1364
1365 case PMBUS_VOUT_MARGIN_LOW: /* R/W word */
1366 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1367 pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1368 } else {
1369 goto passthrough;
1370 }
1371 break;
1372
1373 case PMBUS_VOUT_TRANSITION_RATE: /* R/W word */
1374 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1375 pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1376 } else {
1377 goto passthrough;
1378 }
1379 break;
1380
1381 case PMBUS_VOUT_DROOP: /* R/W word */
1382 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1383 pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1384 } else {
1385 goto passthrough;
1386 }
1387 break;
1388
1389 case PMBUS_VOUT_SCALE_LOOP: /* R/W word */
1390 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1391 pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1392 } else {
1393 goto passthrough;
1394 }
1395 break;
1396
1397 case PMBUS_VOUT_SCALE_MONITOR: /* R/W word */
1398 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1399 pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1400 } else {
1401 goto passthrough;
1402 }
1403 break;
1404
1405 case PMBUS_VOUT_MIN: /* R/W word */
1406 if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1407 pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
1408 } else {
1409 goto passthrough;
1410 }
1411 break;
1412
1413 case PMBUS_POUT_MAX: /* R/W word */
1414 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1415 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1416 } else {
1417 goto passthrough;
1418 }
1419 break;
1420
1421 case PMBUS_VIN_ON: /* R/W word */
1422 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1423 pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1424 } else {
1425 goto passthrough;
1426 }
1427 break;
1428
1429 case PMBUS_VIN_OFF: /* R/W word */
1430 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1431 pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1432 } else {
1433 goto passthrough;
1434 }
1435 break;
1436
1437 case PMBUS_IOUT_CAL_GAIN: /* R/W word */
1438 if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1439 pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1440 } else {
1441 goto passthrough;
1442 }
1443 break;
1444
1445 case PMBUS_FAN_CONFIG_1_2: /* R/W byte */
1446 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1447 pmdev->pages[index].fan_config_1_2 = pmbus_receive8(pmdev);
1448 } else {
1449 goto passthrough;
1450 }
1451 break;
1452
1453 case PMBUS_FAN_COMMAND_1: /* R/W word */
1454 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1455 pmdev->pages[index].fan_command_1 = pmbus_receive16(pmdev);
1456 } else {
1457 goto passthrough;
1458 }
1459 break;
1460
1461 case PMBUS_FAN_COMMAND_2: /* R/W word */
1462 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1463 pmdev->pages[index].fan_command_2 = pmbus_receive16(pmdev);
1464 } else {
1465 goto passthrough;
1466 }
1467 break;
1468
1469 case PMBUS_FAN_CONFIG_3_4: /* R/W byte */
1470 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1471 pmdev->pages[index].fan_config_3_4 = pmbus_receive8(pmdev);
1472 } else {
1473 goto passthrough;
1474 }
1475 break;
1476
1477 case PMBUS_FAN_COMMAND_3: /* R/W word */
1478 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1479 pmdev->pages[index].fan_command_3 = pmbus_receive16(pmdev);
1480 } else {
1481 goto passthrough;
1482 }
1483 break;
1484
1485 case PMBUS_FAN_COMMAND_4: /* R/W word */
1486 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1487 pmdev->pages[index].fan_command_4 = pmbus_receive16(pmdev);
1488 } else {
1489 goto passthrough;
1490 }
1491 break;
1492
1493 case PMBUS_VOUT_OV_FAULT_LIMIT: /* R/W word */
1494 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1495 pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1496 } else {
1497 goto passthrough;
1498 }
1499 break;
1500
1501 case PMBUS_VOUT_OV_FAULT_RESPONSE: /* R/W byte */
1502 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1503 pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1504 } else {
1505 goto passthrough;
1506 }
1507 break;
1508
1509 case PMBUS_VOUT_OV_WARN_LIMIT: /* R/W word */
1510 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1511 pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1512 } else {
1513 goto passthrough;
1514 }
1515 break;
1516
1517 case PMBUS_VOUT_UV_WARN_LIMIT: /* R/W word */
1518 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1519 pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1520 } else {
1521 goto passthrough;
1522 }
1523 break;
1524
1525 case PMBUS_VOUT_UV_FAULT_LIMIT: /* R/W word */
1526 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1527 pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1528 } else {
1529 goto passthrough;
1530 }
1531 break;
1532
1533 case PMBUS_VOUT_UV_FAULT_RESPONSE: /* R/W byte */
1534 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1535 pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1536 } else {
1537 goto passthrough;
1538 }
1539 break;
1540
1541 case PMBUS_IOUT_OC_FAULT_LIMIT: /* R/W word */
1542 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1543 pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1544 } else {
1545 goto passthrough;
1546 }
1547 break;
1548
1549 case PMBUS_IOUT_OC_FAULT_RESPONSE: /* R/W byte */
1550 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1551 pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1552 } else {
1553 goto passthrough;
1554 }
1555 break;
1556
1557 case PMBUS_IOUT_OC_LV_FAULT_LIMIT: /* R/W word */
1558 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1559 pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1560 } else {
1561 goto passthrough;
1562 }
1563 break;
1564
1565 case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1566 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1567 pmdev->pages[index].iout_oc_lv_fault_response
1568 = pmbus_receive8(pmdev);
1569 } else {
1570 goto passthrough;
1571 }
1572 break;
1573
1574 case PMBUS_IOUT_OC_WARN_LIMIT: /* R/W word */
1575 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1576 pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1577 } else {
1578 goto passthrough;
1579 }
1580 break;
1581
1582 case PMBUS_IOUT_UC_FAULT_LIMIT: /* R/W word */
1583 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1584 pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1585 } else {
1586 goto passthrough;
1587 }
1588 break;
1589
1590 case PMBUS_IOUT_UC_FAULT_RESPONSE: /* R/W byte */
1591 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1592 pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1593 } else {
1594 goto passthrough;
1595 }
1596 break;
1597
1598 case PMBUS_OT_FAULT_LIMIT: /* R/W word */
1599 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1600 pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1601 } else {
1602 goto passthrough;
1603 }
1604 break;
1605
1606 case PMBUS_OT_FAULT_RESPONSE: /* R/W byte */
1607 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1608 pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1609 } else {
1610 goto passthrough;
1611 }
1612 break;
1613
1614 case PMBUS_OT_WARN_LIMIT: /* R/W word */
1615 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1616 pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1617 } else {
1618 goto passthrough;
1619 }
1620 break;
1621
1622 case PMBUS_UT_WARN_LIMIT: /* R/W word */
1623 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1624 pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1625 } else {
1626 goto passthrough;
1627 }
1628 break;
1629
1630 case PMBUS_UT_FAULT_LIMIT: /* R/W word */
1631 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1632 pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1633 } else {
1634 goto passthrough;
1635 }
1636 break;
1637
1638 case PMBUS_UT_FAULT_RESPONSE: /* R/W byte */
1639 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1640 pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1641 } else {
1642 goto passthrough;
1643 }
1644 break;
1645
1646 case PMBUS_VIN_OV_FAULT_LIMIT: /* R/W word */
1647 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1648 pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1649 } else {
1650 goto passthrough;
1651 }
1652 break;
1653
1654 case PMBUS_VIN_OV_FAULT_RESPONSE: /* R/W byte */
1655 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1656 pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1657 } else {
1658 goto passthrough;
1659 }
1660 break;
1661
1662 case PMBUS_VIN_OV_WARN_LIMIT: /* R/W word */
1663 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1664 pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1665 } else {
1666 goto passthrough;
1667 }
1668 break;
1669
1670 case PMBUS_VIN_UV_WARN_LIMIT: /* R/W word */
1671 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1672 pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1673 } else {
1674 goto passthrough;
1675 }
1676 break;
1677
1678 case PMBUS_VIN_UV_FAULT_LIMIT: /* R/W word */
1679 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1680 pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1681 } else {
1682 goto passthrough;
1683 }
1684 break;
1685
1686 case PMBUS_VIN_UV_FAULT_RESPONSE: /* R/W byte */
1687 if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1688 pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1689 } else {
1690 goto passthrough;
1691 }
1692 break;
1693
1694 case PMBUS_IIN_OC_FAULT_LIMIT: /* R/W word */
1695 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1696 pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1697 } else {
1698 goto passthrough;
1699 }
1700 break;
1701
1702 case PMBUS_IIN_OC_FAULT_RESPONSE: /* R/W byte */
1703 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1704 pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1705 } else {
1706 goto passthrough;
1707 }
1708 break;
1709
1710 case PMBUS_IIN_OC_WARN_LIMIT: /* R/W word */
1711 if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1712 pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1713 } else {
1714 goto passthrough;
1715 }
1716 break;
1717
1718 case PMBUS_POUT_OP_FAULT_LIMIT: /* R/W word */
1719 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1720 pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1721 } else {
1722 goto passthrough;
1723 }
1724 break;
1725
1726 case PMBUS_POUT_OP_FAULT_RESPONSE: /* R/W byte */
1727 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1728 pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1729 } else {
1730 goto passthrough;
1731 }
1732 break;
1733
1734 case PMBUS_POUT_OP_WARN_LIMIT: /* R/W word */
1735 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1736 pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1737 } else {
1738 goto passthrough;
1739 }
1740 break;
1741
1742 case PMBUS_PIN_OP_WARN_LIMIT: /* R/W word */
1743 if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1744 pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1745 } else {
1746 goto passthrough;
1747 }
1748 break;
1749
1750 case PMBUS_STATUS_BYTE: /* R/W byte */
1751 pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1752 break;
1753
1754 case PMBUS_STATUS_WORD: /* R/W word */
1755 pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1756 break;
1757
1758 case PMBUS_STATUS_VOUT: /* R/W byte */
1759 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1760 pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1761 } else {
1762 goto passthrough;
1763 }
1764 break;
1765
1766 case PMBUS_STATUS_IOUT: /* R/W byte */
1767 if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1768 pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1769 } else {
1770 goto passthrough;
1771 }
1772 break;
1773
1774 case PMBUS_STATUS_INPUT: /* R/W byte */
1775 pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1776 break;
1777
1778 case PMBUS_STATUS_TEMPERATURE: /* R/W byte */
1779 if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1780 pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1781 } else {
1782 goto passthrough;
1783 }
1784 break;
1785
1786 case PMBUS_STATUS_CML: /* R/W byte */
1787 pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1788 break;
1789
1790 case PMBUS_STATUS_OTHER: /* R/W byte */
1791 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1792 break;
1793
1794 case PMBUS_STATUS_MFR_SPECIFIC: /* R/W byte */
1795 pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
1796 break;
1797
1798 case PMBUS_STATUS_FANS_1_2: /* R/W byte */
1799 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1800 pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
1801 } else {
1802 goto passthrough;
1803 }
1804 break;
1805
1806 case PMBUS_STATUS_FANS_3_4: /* R/W byte */
1807 if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1808 pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
1809 } else {
1810 goto passthrough;
1811 }
1812 break;
1813
1814 case PMBUS_PAGE_PLUS_READ: /* Block Read-only */
1815 case PMBUS_CAPABILITY: /* Read-Only byte */
1816 case PMBUS_COEFFICIENTS: /* Read-only block 5 bytes */
1817 case PMBUS_READ_EIN: /* Read-Only block 5 bytes */
1818 case PMBUS_READ_EOUT: /* Read-Only block 5 bytes */
1819 case PMBUS_READ_VIN: /* Read-Only word */
1820 case PMBUS_READ_IIN: /* Read-Only word */
1821 case PMBUS_READ_VCAP: /* Read-Only word */
1822 case PMBUS_READ_VOUT: /* Read-Only word */
1823 case PMBUS_READ_IOUT: /* Read-Only word */
1824 case PMBUS_READ_TEMPERATURE_1: /* Read-Only word */
1825 case PMBUS_READ_TEMPERATURE_2: /* Read-Only word */
1826 case PMBUS_READ_TEMPERATURE_3: /* Read-Only word */
1827 case PMBUS_READ_FAN_SPEED_1: /* Read-Only word */
1828 case PMBUS_READ_FAN_SPEED_2: /* Read-Only word */
1829 case PMBUS_READ_FAN_SPEED_3: /* Read-Only word */
1830 case PMBUS_READ_FAN_SPEED_4: /* Read-Only word */
1831 case PMBUS_READ_DUTY_CYCLE: /* Read-Only word */
1832 case PMBUS_READ_FREQUENCY: /* Read-Only word */
1833 case PMBUS_READ_POUT: /* Read-Only word */
1834 case PMBUS_READ_PIN: /* Read-Only word */
1835 case PMBUS_REVISION: /* Read-Only byte */
1836 case PMBUS_APP_PROFILE_SUPPORT: /* Read-Only block-read */
1837 case PMBUS_MFR_VIN_MIN: /* Read-Only word */
1838 case PMBUS_MFR_VIN_MAX: /* Read-Only word */
1839 case PMBUS_MFR_IIN_MAX: /* Read-Only word */
1840 case PMBUS_MFR_PIN_MAX: /* Read-Only word */
1841 case PMBUS_MFR_VOUT_MIN: /* Read-Only word */
1842 case PMBUS_MFR_VOUT_MAX: /* Read-Only word */
1843 case PMBUS_MFR_IOUT_MAX: /* Read-Only word */
1844 case PMBUS_MFR_POUT_MAX: /* Read-Only word */
1845 case PMBUS_MFR_TAMBIENT_MAX: /* Read-Only word */
1846 case PMBUS_MFR_TAMBIENT_MIN: /* Read-Only word */
1847 case PMBUS_MFR_EFFICIENCY_LL: /* Read-Only block 14 bytes */
1848 case PMBUS_MFR_EFFICIENCY_HL: /* Read-Only block 14 bytes */
1849 case PMBUS_MFR_PIN_ACCURACY: /* Read-Only byte */
1850 case PMBUS_IC_DEVICE_ID: /* Read-Only block-read */
1851 case PMBUS_IC_DEVICE_REV: /* Read-Only block-read */
1852 qemu_log_mask(LOG_GUEST_ERROR,
1853 "%s: writing to read-only register 0x%02x\n",
1854 __func__, pmdev->code);
1855 break;
1856
1857 passthrough:
1858 /* Unimplemented registers get passed to the device */
1859 default:
1860 if (pmdc->write_data) {
1861 ret = pmdc->write_data(pmdev, buf, len);
1862 }
1863 break;
1864 }
1865 pmbus_check_limits(pmdev);
1866 pmdev->in_buf_len = 0;
1867 return ret;
1868 }
1869
1870 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1871 {
1872 if (!pmdev->pages) { /* allocate memory for pages on first use */
1873 pmbus_pages_alloc(pmdev);
1874 }
1875
1876 /* The 0xFF page is special for commands applying to all pages */
1877 if (index == PB_ALL_PAGES) {
1878 for (int i = 0; i < pmdev->num_pages; i++) {
1879 pmdev->pages[i].page_flags = flags;
1880 }
1881 return 0;
1882 }
1883
1884 if (index > pmdev->num_pages - 1) {
1885 qemu_log_mask(LOG_GUEST_ERROR,
1886 "%s: index %u is out of range\n",
1887 __func__, index);
1888 return -1;
1889 }
1890
1891 pmdev->pages[index].page_flags = flags;
1892
1893 return 0;
1894 }
1895
1896 /* TODO: include pmbus page info in vmstate */
1897 const VMStateDescription vmstate_pmbus_device = {
1898 .name = TYPE_PMBUS_DEVICE,
1899 .version_id = 0,
1900 .minimum_version_id = 0,
1901 .fields = (const VMStateField[]) {
1902 VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1903 VMSTATE_UINT8(num_pages, PMBusDevice),
1904 VMSTATE_UINT8(code, PMBusDevice),
1905 VMSTATE_UINT8(page, PMBusDevice),
1906 VMSTATE_UINT8(capability, PMBusDevice),
1907 VMSTATE_END_OF_LIST()
1908 }
1909 };
1910
1911 static void pmbus_device_finalize(Object *obj)
1912 {
1913 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1914 g_free(pmdev->pages);
1915 }
1916
1917 static void pmbus_device_class_init(ObjectClass *klass, const void *data)
1918 {
1919 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1920
1921 k->quick_cmd = pmbus_quick_cmd;
1922 k->write_data = pmbus_write_data;
1923 k->receive_byte = pmbus_receive_byte;
1924 }
1925
1926 static const TypeInfo pmbus_device_type_info = {
1927 .name = TYPE_PMBUS_DEVICE,
1928 .parent = TYPE_SMBUS_DEVICE,
1929 .instance_size = sizeof(PMBusDevice),
1930 .instance_finalize = pmbus_device_finalize,
1931 .abstract = true,
1932 .class_size = sizeof(PMBusDeviceClass),
1933 .class_init = pmbus_device_class_init,
1934 };
1935
1936 static void pmbus_device_register_types(void)
1937 {
1938 type_register_static(&pmbus_device_type_info);
1939 }
1940
1941 type_init(pmbus_device_register_types)