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