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