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