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