xref: /openbmc/qemu/hw/i2c/pmbus_device.c (revision 2df1eb27)
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 
1248     if (pmdev->code == PMBUS_CLEAR_FAULTS) {
1249         pmbus_clear_faults(pmdev);
1250     }
1251 
1252     if (len == 1) { /* Single length writes are command codes only */
1253         return 0;
1254     }
1255 
1256     if (pmdev->code == PMBUS_PAGE) {
1257         pmdev->page = pmbus_receive8(pmdev);
1258 
1259         if (pmdev->page > pmdev->num_pages - 1 && pmdev->page != PB_ALL_PAGES) {
1260             qemu_log_mask(LOG_GUEST_ERROR,
1261                           "%s: page %u is out of range\n",
1262                           __func__, pmdev->page);
1263             pmdev->page = 0; /* undefined behaviour - reset to page 0 */
1264             pmbus_cml_error(pmdev);
1265             return PMBUS_ERR_BYTE;
1266         }
1267         return 0;
1268     }
1269 
1270     /* loop through all the pages when 0xFF is received */
1271     if (pmdev->page == PB_ALL_PAGES) {
1272         for (int i = 0; i < pmdev->num_pages; i++) {
1273             pmdev->page = i;
1274             pmbus_write_data(smd, buf, len);
1275         }
1276         pmdev->page = PB_ALL_PAGES;
1277         return 0;
1278     }
1279 
1280     index = pmdev->page;
1281 
1282     switch (pmdev->code) {
1283     case PMBUS_OPERATION:                 /* R/W byte */
1284         pmdev->pages[index].operation = pmbus_receive8(pmdev);
1285         pmbus_operation(pmdev);
1286         break;
1287 
1288     case PMBUS_ON_OFF_CONFIG:             /* R/W byte */
1289         pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1290         break;
1291 
1292     case PMBUS_CLEAR_FAULTS:              /* Send Byte */
1293         pmbus_clear_faults(pmdev);
1294         break;
1295 
1296     case PMBUS_PHASE:                     /* R/W byte */
1297         pmdev->pages[index].phase = pmbus_receive8(pmdev);
1298         break;
1299 
1300     case PMBUS_PAGE_PLUS_WRITE:           /* Block Write-only */
1301     case PMBUS_WRITE_PROTECT:             /* R/W byte */
1302         pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1303         break;
1304 
1305     case PMBUS_VOUT_MODE:                 /* R/W byte */
1306         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1307             pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1308         } else {
1309             goto passthrough;
1310         }
1311         break;
1312 
1313     case PMBUS_VOUT_COMMAND:              /* R/W word */
1314         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1315             pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1316         } else {
1317             goto passthrough;
1318         }
1319         break;
1320 
1321     case PMBUS_VOUT_TRIM:                 /* R/W word */
1322         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1323             pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1324         } else {
1325             goto passthrough;
1326         }
1327         break;
1328 
1329     case PMBUS_VOUT_CAL_OFFSET:           /* R/W word */
1330         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1331             pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1332         } else {
1333             goto passthrough;
1334         }
1335         break;
1336 
1337     case PMBUS_VOUT_MAX:                  /* R/W word */
1338         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1339             pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1340         } else {
1341             goto passthrough;
1342         }
1343         break;
1344 
1345     case PMBUS_VOUT_MARGIN_HIGH:          /* R/W word */
1346         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1347             pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1348         } else {
1349             goto passthrough;
1350         }
1351         break;
1352 
1353     case PMBUS_VOUT_MARGIN_LOW:           /* R/W word */
1354         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1355             pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1356         } else {
1357             goto passthrough;
1358         }
1359         break;
1360 
1361     case PMBUS_VOUT_TRANSITION_RATE:      /* R/W word */
1362         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1363             pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1364         } else {
1365             goto passthrough;
1366         }
1367         break;
1368 
1369     case PMBUS_VOUT_DROOP:                /* R/W word */
1370         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1371             pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1372         } else {
1373             goto passthrough;
1374         }
1375         break;
1376 
1377     case PMBUS_VOUT_SCALE_LOOP:           /* R/W word */
1378         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1379             pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1380         } else {
1381             goto passthrough;
1382         }
1383         break;
1384 
1385     case PMBUS_VOUT_SCALE_MONITOR:        /* R/W word */
1386         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1387             pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1388         } else {
1389             goto passthrough;
1390         }
1391         break;
1392 
1393     case PMBUS_VOUT_MIN:                  /* R/W word */
1394         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1395             pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
1396         } else {
1397             goto passthrough;
1398         }
1399         break;
1400 
1401     case PMBUS_POUT_MAX:                  /* R/W word */
1402         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1403             pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1404         } else {
1405             goto passthrough;
1406         }
1407         break;
1408 
1409     case PMBUS_VIN_ON:                    /* R/W word */
1410         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1411             pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1412         } else {
1413             goto passthrough;
1414         }
1415         break;
1416 
1417     case PMBUS_VIN_OFF:                   /* R/W word */
1418         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1419             pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1420         } else {
1421             goto passthrough;
1422         }
1423         break;
1424 
1425     case PMBUS_IOUT_CAL_GAIN:             /* R/W word */
1426         if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1427             pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1428         } else {
1429             goto passthrough;
1430         }
1431         break;
1432 
1433     case PMBUS_FAN_CONFIG_1_2:            /* R/W byte */
1434         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1435             pmdev->pages[index].fan_config_1_2 = pmbus_receive8(pmdev);
1436         } else {
1437             goto passthrough;
1438         }
1439         break;
1440 
1441     case PMBUS_FAN_COMMAND_1:             /* R/W word */
1442         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1443             pmdev->pages[index].fan_command_1 = pmbus_receive16(pmdev);
1444         } else {
1445             goto passthrough;
1446         }
1447         break;
1448 
1449     case PMBUS_FAN_COMMAND_2:             /* R/W word */
1450         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1451             pmdev->pages[index].fan_command_2 = pmbus_receive16(pmdev);
1452         } else {
1453             goto passthrough;
1454         }
1455         break;
1456 
1457     case PMBUS_FAN_CONFIG_3_4:            /* R/W byte */
1458         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1459             pmdev->pages[index].fan_config_3_4 = pmbus_receive8(pmdev);
1460         } else {
1461             goto passthrough;
1462         }
1463         break;
1464 
1465     case PMBUS_FAN_COMMAND_3:             /* R/W word */
1466         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1467             pmdev->pages[index].fan_command_3 = pmbus_receive16(pmdev);
1468         } else {
1469             goto passthrough;
1470         }
1471         break;
1472 
1473     case PMBUS_FAN_COMMAND_4:             /* R/W word */
1474         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1475             pmdev->pages[index].fan_command_4 = pmbus_receive16(pmdev);
1476         } else {
1477             goto passthrough;
1478         }
1479         break;
1480 
1481     case PMBUS_VOUT_OV_FAULT_LIMIT:       /* R/W word */
1482         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1483             pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1484         } else {
1485             goto passthrough;
1486         }
1487         break;
1488 
1489     case PMBUS_VOUT_OV_FAULT_RESPONSE:    /* R/W byte */
1490         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1491             pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1492         } else {
1493             goto passthrough;
1494         }
1495         break;
1496 
1497     case PMBUS_VOUT_OV_WARN_LIMIT:        /* R/W word */
1498         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1499             pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1500         } else {
1501             goto passthrough;
1502         }
1503         break;
1504 
1505     case PMBUS_VOUT_UV_WARN_LIMIT:        /* R/W word */
1506         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1507             pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1508         } else {
1509             goto passthrough;
1510         }
1511         break;
1512 
1513     case PMBUS_VOUT_UV_FAULT_LIMIT:       /* R/W word */
1514         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1515             pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1516         } else {
1517             goto passthrough;
1518         }
1519         break;
1520 
1521     case PMBUS_VOUT_UV_FAULT_RESPONSE:    /* R/W byte */
1522         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1523             pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1524         } else {
1525             goto passthrough;
1526         }
1527         break;
1528 
1529     case PMBUS_IOUT_OC_FAULT_LIMIT:       /* R/W word */
1530         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1531             pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1532         } else {
1533             goto passthrough;
1534         }
1535         break;
1536 
1537     case PMBUS_IOUT_OC_FAULT_RESPONSE:    /* R/W byte */
1538         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1539             pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1540         } else {
1541             goto passthrough;
1542         }
1543         break;
1544 
1545     case PMBUS_IOUT_OC_LV_FAULT_LIMIT:    /* R/W word */
1546         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1547             pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1548         } else {
1549             goto passthrough;
1550         }
1551         break;
1552 
1553     case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1554         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1555             pmdev->pages[index].iout_oc_lv_fault_response
1556                 = pmbus_receive8(pmdev);
1557         } else {
1558             goto passthrough;
1559         }
1560         break;
1561 
1562     case PMBUS_IOUT_OC_WARN_LIMIT:        /* R/W word */
1563         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1564             pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1565         } else {
1566             goto passthrough;
1567         }
1568         break;
1569 
1570     case PMBUS_IOUT_UC_FAULT_LIMIT:       /* R/W word */
1571         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1572             pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1573         } else {
1574             goto passthrough;
1575         }
1576         break;
1577 
1578     case PMBUS_IOUT_UC_FAULT_RESPONSE:    /* R/W byte */
1579         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1580             pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1581         } else {
1582             goto passthrough;
1583         }
1584         break;
1585 
1586     case PMBUS_OT_FAULT_LIMIT:            /* R/W word */
1587         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1588             pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1589         } else {
1590             goto passthrough;
1591         }
1592         break;
1593 
1594     case PMBUS_OT_FAULT_RESPONSE:         /* R/W byte */
1595         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1596             pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1597         } else {
1598             goto passthrough;
1599         }
1600         break;
1601 
1602     case PMBUS_OT_WARN_LIMIT:             /* R/W word */
1603         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1604             pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1605         } else {
1606             goto passthrough;
1607         }
1608         break;
1609 
1610     case PMBUS_UT_WARN_LIMIT:             /* R/W word */
1611         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1612             pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1613         } else {
1614             goto passthrough;
1615         }
1616         break;
1617 
1618     case PMBUS_UT_FAULT_LIMIT:            /* R/W word */
1619         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1620             pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1621         } else {
1622             goto passthrough;
1623         }
1624         break;
1625 
1626     case PMBUS_UT_FAULT_RESPONSE:         /* R/W byte */
1627         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1628             pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1629         } else {
1630             goto passthrough;
1631         }
1632         break;
1633 
1634     case PMBUS_VIN_OV_FAULT_LIMIT:        /* R/W word */
1635         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1636             pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1637         } else {
1638             goto passthrough;
1639         }
1640         break;
1641 
1642     case PMBUS_VIN_OV_FAULT_RESPONSE:     /* R/W byte */
1643         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1644             pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1645         } else {
1646             goto passthrough;
1647         }
1648         break;
1649 
1650     case PMBUS_VIN_OV_WARN_LIMIT:         /* R/W word */
1651         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1652             pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1653         } else {
1654             goto passthrough;
1655         }
1656         break;
1657 
1658     case PMBUS_VIN_UV_WARN_LIMIT:         /* R/W word */
1659         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1660             pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1661         } else {
1662             goto passthrough;
1663         }
1664         break;
1665 
1666     case PMBUS_VIN_UV_FAULT_LIMIT:        /* R/W word */
1667         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1668             pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1669         } else {
1670             goto passthrough;
1671         }
1672         break;
1673 
1674     case PMBUS_VIN_UV_FAULT_RESPONSE:     /* R/W byte */
1675         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1676             pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1677         } else {
1678             goto passthrough;
1679         }
1680         break;
1681 
1682     case PMBUS_IIN_OC_FAULT_LIMIT:        /* R/W word */
1683         if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1684             pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1685         } else {
1686             goto passthrough;
1687         }
1688         break;
1689 
1690     case PMBUS_IIN_OC_FAULT_RESPONSE:     /* R/W byte */
1691         if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1692             pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1693         } else {
1694             goto passthrough;
1695         }
1696         break;
1697 
1698     case PMBUS_IIN_OC_WARN_LIMIT:         /* R/W word */
1699         if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1700             pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1701         } else {
1702             goto passthrough;
1703         }
1704         break;
1705 
1706     case PMBUS_POUT_OP_FAULT_LIMIT:       /* R/W word */
1707         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1708             pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1709         } else {
1710             goto passthrough;
1711         }
1712         break;
1713 
1714     case PMBUS_POUT_OP_FAULT_RESPONSE:    /* R/W byte */
1715         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1716             pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1717         } else {
1718             goto passthrough;
1719         }
1720         break;
1721 
1722     case PMBUS_POUT_OP_WARN_LIMIT:        /* R/W word */
1723         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1724             pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1725         } else {
1726             goto passthrough;
1727         }
1728         break;
1729 
1730     case PMBUS_PIN_OP_WARN_LIMIT:         /* R/W word */
1731         if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1732             pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1733         } else {
1734             goto passthrough;
1735         }
1736         break;
1737 
1738     case PMBUS_STATUS_BYTE:               /* R/W byte */
1739         pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1740         break;
1741 
1742     case PMBUS_STATUS_WORD:               /* R/W word */
1743         pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1744         break;
1745 
1746     case PMBUS_STATUS_VOUT:               /* R/W byte */
1747         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1748             pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1749         } else {
1750             goto passthrough;
1751         }
1752         break;
1753 
1754     case PMBUS_STATUS_IOUT:               /* R/W byte */
1755         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1756             pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1757         } else {
1758             goto passthrough;
1759         }
1760         break;
1761 
1762     case PMBUS_STATUS_INPUT:              /* R/W byte */
1763         pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1764         break;
1765 
1766     case PMBUS_STATUS_TEMPERATURE:        /* R/W byte */
1767         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1768             pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1769         } else {
1770             goto passthrough;
1771         }
1772         break;
1773 
1774     case PMBUS_STATUS_CML:                /* R/W byte */
1775         pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1776         break;
1777 
1778     case PMBUS_STATUS_OTHER:              /* R/W byte */
1779         pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1780         break;
1781 
1782     case PMBUS_STATUS_MFR_SPECIFIC:        /* R/W byte */
1783         pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
1784         break;
1785 
1786     case PMBUS_STATUS_FANS_1_2:           /* R/W byte */
1787         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1788             pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
1789         } else {
1790             goto passthrough;
1791         }
1792         break;
1793 
1794     case PMBUS_STATUS_FANS_3_4:           /* R/W byte */
1795         if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
1796             pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
1797         } else {
1798             goto passthrough;
1799         }
1800         break;
1801 
1802     case PMBUS_PAGE_PLUS_READ:            /* Block Read-only */
1803     case PMBUS_CAPABILITY:                /* Read-Only byte */
1804     case PMBUS_COEFFICIENTS:              /* Read-only block 5 bytes */
1805     case PMBUS_READ_EIN:                  /* Read-Only block 5 bytes */
1806     case PMBUS_READ_EOUT:                 /* Read-Only block 5 bytes */
1807     case PMBUS_READ_VIN:                  /* Read-Only word */
1808     case PMBUS_READ_IIN:                  /* Read-Only word */
1809     case PMBUS_READ_VCAP:                 /* Read-Only word */
1810     case PMBUS_READ_VOUT:                 /* Read-Only word */
1811     case PMBUS_READ_IOUT:                 /* Read-Only word */
1812     case PMBUS_READ_TEMPERATURE_1:        /* Read-Only word */
1813     case PMBUS_READ_TEMPERATURE_2:        /* Read-Only word */
1814     case PMBUS_READ_TEMPERATURE_3:        /* Read-Only word */
1815     case PMBUS_READ_FAN_SPEED_1:          /* Read-Only word */
1816     case PMBUS_READ_FAN_SPEED_2:          /* Read-Only word */
1817     case PMBUS_READ_FAN_SPEED_3:          /* Read-Only word */
1818     case PMBUS_READ_FAN_SPEED_4:          /* Read-Only word */
1819     case PMBUS_READ_DUTY_CYCLE:           /* Read-Only word */
1820     case PMBUS_READ_FREQUENCY:            /* Read-Only word */
1821     case PMBUS_READ_POUT:                 /* Read-Only word */
1822     case PMBUS_READ_PIN:                  /* Read-Only word */
1823     case PMBUS_REVISION:                  /* Read-Only byte */
1824     case PMBUS_APP_PROFILE_SUPPORT:       /* Read-Only block-read */
1825     case PMBUS_MFR_VIN_MIN:               /* Read-Only word */
1826     case PMBUS_MFR_VIN_MAX:               /* Read-Only word */
1827     case PMBUS_MFR_IIN_MAX:               /* Read-Only word */
1828     case PMBUS_MFR_PIN_MAX:               /* Read-Only word */
1829     case PMBUS_MFR_VOUT_MIN:              /* Read-Only word */
1830     case PMBUS_MFR_VOUT_MAX:              /* Read-Only word */
1831     case PMBUS_MFR_IOUT_MAX:              /* Read-Only word */
1832     case PMBUS_MFR_POUT_MAX:              /* Read-Only word */
1833     case PMBUS_MFR_TAMBIENT_MAX:          /* Read-Only word */
1834     case PMBUS_MFR_TAMBIENT_MIN:          /* Read-Only word */
1835     case PMBUS_MFR_EFFICIENCY_LL:         /* Read-Only block 14 bytes */
1836     case PMBUS_MFR_EFFICIENCY_HL:         /* Read-Only block 14 bytes */
1837     case PMBUS_MFR_PIN_ACCURACY:          /* Read-Only byte */
1838     case PMBUS_IC_DEVICE_ID:              /* Read-Only block-read */
1839     case PMBUS_IC_DEVICE_REV:             /* Read-Only block-read */
1840         qemu_log_mask(LOG_GUEST_ERROR,
1841                       "%s: writing to read-only register 0x%02x\n",
1842                       __func__, pmdev->code);
1843         break;
1844 
1845 passthrough:
1846     /* Unimplemented registers get passed to the device */
1847     default:
1848         if (pmdc->write_data) {
1849             ret = pmdc->write_data(pmdev, buf, len);
1850         }
1851         break;
1852     }
1853     pmbus_check_limits(pmdev);
1854     pmdev->in_buf_len = 0;
1855     return ret;
1856 }
1857 
1858 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1859 {
1860     if (!pmdev->pages) { /* allocate memory for pages on first use */
1861         pmbus_pages_alloc(pmdev);
1862     }
1863 
1864     /* The 0xFF page is special for commands applying to all pages */
1865     if (index == PB_ALL_PAGES) {
1866         for (int i = 0; i < pmdev->num_pages; i++) {
1867             pmdev->pages[i].page_flags = flags;
1868         }
1869         return 0;
1870     }
1871 
1872     if (index > pmdev->num_pages - 1) {
1873         qemu_log_mask(LOG_GUEST_ERROR,
1874                       "%s: index %u is out of range\n",
1875                       __func__, index);
1876         return -1;
1877     }
1878 
1879     pmdev->pages[index].page_flags = flags;
1880 
1881     return 0;
1882 }
1883 
1884 /* TODO: include pmbus page info in vmstate */
1885 const VMStateDescription vmstate_pmbus_device = {
1886     .name = TYPE_PMBUS_DEVICE,
1887     .version_id = 0,
1888     .minimum_version_id = 0,
1889     .fields = (const VMStateField[]) {
1890         VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1891         VMSTATE_UINT8(num_pages, PMBusDevice),
1892         VMSTATE_UINT8(code, PMBusDevice),
1893         VMSTATE_UINT8(page, PMBusDevice),
1894         VMSTATE_UINT8(capability, PMBusDevice),
1895         VMSTATE_END_OF_LIST()
1896     }
1897 };
1898 
1899 static void pmbus_device_finalize(Object *obj)
1900 {
1901     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1902     g_free(pmdev->pages);
1903 }
1904 
1905 static void pmbus_device_class_init(ObjectClass *klass, void *data)
1906 {
1907     SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1908 
1909     k->quick_cmd = pmbus_quick_cmd;
1910     k->write_data = pmbus_write_data;
1911     k->receive_byte = pmbus_receive_byte;
1912 }
1913 
1914 static const TypeInfo pmbus_device_type_info = {
1915     .name = TYPE_PMBUS_DEVICE,
1916     .parent = TYPE_SMBUS_DEVICE,
1917     .instance_size = sizeof(PMBusDevice),
1918     .instance_finalize = pmbus_device_finalize,
1919     .abstract = true,
1920     .class_size = sizeof(PMBusDeviceClass),
1921     .class_init = pmbus_device_class_init,
1922 };
1923 
1924 static void pmbus_device_register_types(void)
1925 {
1926     type_register_static(&pmbus_device_type_info);
1927 }
1928 
1929 type_init(pmbus_device_register_types)
1930