xref: /openbmc/qemu/hw/i2c/pmbus_device.c (revision 096b778f)
1 /*
2  * PMBus wrapper over SMBus
3  *
4  * Copyright 2021 Google LLC
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "qemu/osdep.h"
10 #include <math.h>
11 #include <string.h>
12 #include "hw/i2c/pmbus_device.h"
13 #include "migration/vmstate.h"
14 #include "qemu/module.h"
15 #include "qemu/log.h"
16 
17 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value)
18 {
19     /* R is usually negative to fit large readings into 16 bits */
20     uint16_t y = (c.m * value + c.b) * pow(10, c.R);
21     return y;
22 }
23 
24 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value)
25 {
26     /* X = (Y * 10^-R - b) / m */
27     uint32_t x = (value / pow(10, c.R) - c.b) / c.m;
28     return x;
29 }
30 
31 uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
32 {
33     /* L = D * 2^(-e) */
34     if (exp < 0) {
35         return value << (-exp);
36     }
37     return value >> exp;
38 }
39 
40 uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
41 {
42     /* D = L * 2^e */
43     if (exp < 0) {
44         return value >> (-exp);
45     }
46     return value << exp;
47 }
48 
49 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
50 {
51     if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
52         qemu_log_mask(LOG_GUEST_ERROR,
53                       "PMBus device tried to send too much data");
54         len = 0;
55     }
56 
57     for (int i = len - 1; i >= 0; i--) {
58         pmdev->out_buf[i + pmdev->out_buf_len] = data[len - i - 1];
59     }
60     pmdev->out_buf_len += len;
61 }
62 
63 /* Internal only, convert unsigned ints to the little endian bus */
64 static void pmbus_send_uint(PMBusDevice *pmdev, uint64_t data, uint8_t size)
65 {
66     uint8_t bytes[8];
67     g_assert(size <= 8);
68 
69     for (int i = 0; i < size; i++) {
70         bytes[i] = data & 0xFF;
71         data = data >> 8;
72     }
73     pmbus_send(pmdev, bytes, size);
74 }
75 
76 void pmbus_send8(PMBusDevice *pmdev, uint8_t data)
77 {
78     pmbus_send_uint(pmdev, data, 1);
79 }
80 
81 void pmbus_send16(PMBusDevice *pmdev, uint16_t data)
82 {
83     pmbus_send_uint(pmdev, data, 2);
84 }
85 
86 void pmbus_send32(PMBusDevice *pmdev, uint32_t data)
87 {
88     pmbus_send_uint(pmdev, data, 4);
89 }
90 
91 void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
92 {
93     pmbus_send_uint(pmdev, data, 8);
94 }
95 
96 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
97 {
98     size_t len = strlen(data);
99     g_assert(len > 0);
100     g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
101     pmdev->out_buf[len + pmdev->out_buf_len] = len;
102 
103     for (int i = len - 1; i >= 0; i--) {
104         pmdev->out_buf[i + pmdev->out_buf_len] = data[len - 1 - i];
105     }
106     pmdev->out_buf_len += len + 1;
107 }
108 
109 
110 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
111 {
112     uint64_t ret = 0;
113 
114     /* Exclude command code from return value */
115     pmdev->in_buf++;
116     pmdev->in_buf_len--;
117 
118     for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
119         ret = ret << 8 | pmdev->in_buf[i];
120     }
121     return ret;
122 }
123 
124 uint8_t pmbus_receive8(PMBusDevice *pmdev)
125 {
126     if (pmdev->in_buf_len - 1 != 1) {
127         qemu_log_mask(LOG_GUEST_ERROR,
128                       "%s: length mismatch. Expected 1 byte, got %d bytes\n",
129                       __func__, pmdev->in_buf_len - 1);
130     }
131     return pmbus_receive_uint(pmdev);
132 }
133 
134 uint16_t pmbus_receive16(PMBusDevice *pmdev)
135 {
136     if (pmdev->in_buf_len - 1 != 2) {
137         qemu_log_mask(LOG_GUEST_ERROR,
138                       "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
139                       __func__, pmdev->in_buf_len - 1);
140     }
141     return pmbus_receive_uint(pmdev);
142 }
143 
144 uint32_t pmbus_receive32(PMBusDevice *pmdev)
145 {
146     if (pmdev->in_buf_len - 1 != 4) {
147         qemu_log_mask(LOG_GUEST_ERROR,
148                       "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
149                       __func__, pmdev->in_buf_len - 1);
150     }
151     return pmbus_receive_uint(pmdev);
152 }
153 
154 uint64_t pmbus_receive64(PMBusDevice *pmdev)
155 {
156     if (pmdev->in_buf_len - 1 != 8) {
157         qemu_log_mask(LOG_GUEST_ERROR,
158                       "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
159                       __func__, pmdev->in_buf_len - 1);
160     }
161     return pmbus_receive_uint(pmdev);
162 }
163 
164 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
165 {
166     if (pmdev->out_buf_len == 0) {
167         qemu_log_mask(LOG_GUEST_ERROR,
168                       "%s: tried to read from empty buffer",
169                       __func__);
170         return PMBUS_ERR_BYTE;
171     }
172     uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1];
173     pmdev->out_buf_len--;
174     return data;
175 }
176 
177 static void pmbus_quick_cmd(SMBusDevice *smd, uint8_t read)
178 {
179     PMBusDevice *pmdev = PMBUS_DEVICE(smd);
180     PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
181 
182     if (pmdc->quick_cmd) {
183         pmdc->quick_cmd(pmdev, read);
184     }
185 }
186 
187 static void pmbus_pages_alloc(PMBusDevice *pmdev)
188 {
189     /* some PMBus devices don't use the PAGE command, so they get 1 page */
190     PMBusDeviceClass *k = PMBUS_DEVICE_GET_CLASS(pmdev);
191     if (k->device_num_pages == 0) {
192         k->device_num_pages = 1;
193     }
194     pmdev->num_pages = k->device_num_pages;
195     pmdev->pages = g_new0(PMBusPage, k->device_num_pages);
196 }
197 
198 void pmbus_check_limits(PMBusDevice *pmdev)
199 {
200     for (int i = 0; i < pmdev->num_pages; i++) {
201         if ((pmdev->pages[i].operation & PB_OP_ON) == 0) {
202             continue;   /* don't check powered off devices */
203         }
204 
205         if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_fault_limit) {
206             pmdev->pages[i].status_word |= PB_STATUS_VOUT;
207             pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_FAULT;
208         }
209 
210         if (pmdev->pages[i].read_vout > pmdev->pages[i].vout_ov_warn_limit) {
211             pmdev->pages[i].status_word |= PB_STATUS_VOUT;
212             pmdev->pages[i].status_vout |= PB_STATUS_VOUT_OV_WARN;
213         }
214 
215         if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_warn_limit) {
216             pmdev->pages[i].status_word |= PB_STATUS_VOUT;
217             pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_WARN;
218         }
219 
220         if (pmdev->pages[i].read_vout < pmdev->pages[i].vout_uv_fault_limit) {
221             pmdev->pages[i].status_word |= PB_STATUS_VOUT;
222             pmdev->pages[i].status_vout |= PB_STATUS_VOUT_UV_FAULT;
223         }
224 
225         if (pmdev->pages[i].read_vin > pmdev->pages[i].vin_ov_warn_limit) {
226             pmdev->pages[i].status_word |= PB_STATUS_INPUT;
227             pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_OV_WARN;
228         }
229 
230         if (pmdev->pages[i].read_vin < pmdev->pages[i].vin_uv_warn_limit) {
231             pmdev->pages[i].status_word |= PB_STATUS_INPUT;
232             pmdev->pages[i].status_input |= PB_STATUS_INPUT_VIN_UV_WARN;
233         }
234 
235         if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_warn_limit) {
236             pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
237             pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_WARN;
238         }
239 
240         if (pmdev->pages[i].read_iout > pmdev->pages[i].iout_oc_fault_limit) {
241             pmdev->pages[i].status_word |= PB_STATUS_IOUT_POUT;
242             pmdev->pages[i].status_iout |= PB_STATUS_IOUT_OC_FAULT;
243         }
244 
245         if (pmdev->pages[i].read_pin > pmdev->pages[i].pin_op_warn_limit) {
246             pmdev->pages[i].status_word |= PB_STATUS_INPUT;
247             pmdev->pages[i].status_input |= PB_STATUS_INPUT_PIN_OP_WARN;
248         }
249 
250         if (pmdev->pages[i].read_temperature_1
251                 > pmdev->pages[i].ot_fault_limit) {
252             pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
253             pmdev->pages[i].status_temperature |= PB_STATUS_OT_FAULT;
254         }
255 
256         if (pmdev->pages[i].read_temperature_1
257                 > pmdev->pages[i].ot_warn_limit) {
258             pmdev->pages[i].status_word |= PB_STATUS_TEMPERATURE;
259             pmdev->pages[i].status_temperature |= PB_STATUS_OT_WARN;
260         }
261     }
262 }
263 
264 /* assert the status_cml error upon receipt of malformed command */
265 static void pmbus_cml_error(PMBusDevice *pmdev)
266 {
267     for (int i = 0; i < pmdev->num_pages; i++) {
268         pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
269         pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
270     }
271 }
272 
273 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
274 {
275     PMBusDevice *pmdev = PMBUS_DEVICE(smd);
276     PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
277     uint8_t ret = PMBUS_ERR_BYTE;
278     uint8_t index;
279 
280     if (pmdev->out_buf_len != 0) {
281         ret = pmbus_out_buf_pop(pmdev);
282         return ret;
283     }
284 
285     /*
286      * Reading from all pages will return the value from page 0,
287      * this is unspecified behaviour in general.
288      */
289     if (pmdev->page == PB_ALL_PAGES) {
290         index = 0;
291         qemu_log_mask(LOG_GUEST_ERROR,
292                       "%s: tried to read from all pages\n",
293                       __func__);
294         pmbus_cml_error(pmdev);
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_CLEAR_FAULTS:              /* Send Byte */
988     case PMBUS_PAGE_PLUS_WRITE:           /* Block Write-only */
989     case PMBUS_STORE_DEFAULT_ALL:         /* Send Byte */
990     case PMBUS_RESTORE_DEFAULT_ALL:       /* Send Byte */
991     case PMBUS_STORE_DEFAULT_CODE:        /* Write-only Byte */
992     case PMBUS_RESTORE_DEFAULT_CODE:      /* Write-only Byte */
993     case PMBUS_STORE_USER_ALL:            /* Send Byte */
994     case PMBUS_RESTORE_USER_ALL:          /* Send Byte */
995     case PMBUS_STORE_USER_CODE:           /* Write-only Byte */
996     case PMBUS_RESTORE_USER_CODE:         /* Write-only Byte */
997     case PMBUS_QUERY:                     /* Write-Only */
998         qemu_log_mask(LOG_GUEST_ERROR,
999                       "%s: reading from write only register 0x%02x\n",
1000                       __func__, pmdev->code);
1001         break;
1002 
1003 passthough:
1004     default:
1005         /* Pass through read request if not handled */
1006         if (pmdc->receive_byte) {
1007             ret = pmdc->receive_byte(pmdev);
1008         }
1009         break;
1010     }
1011 
1012     if (pmdev->out_buf_len != 0) {
1013         ret = pmbus_out_buf_pop(pmdev);
1014         return ret;
1015     }
1016 
1017     return ret;
1018 }
1019 
1020 /*
1021  * PMBus clear faults command applies to all status registers, existing faults
1022  * should separately get re-asserted.
1023  */
1024 static void pmbus_clear_faults(PMBusDevice *pmdev)
1025 {
1026     for (uint8_t i = 0; i < pmdev->num_pages; i++) {
1027         pmdev->pages[i].status_word = 0;
1028         pmdev->pages[i].status_vout = 0;
1029         pmdev->pages[i].status_iout = 0;
1030         pmdev->pages[i].status_input = 0;
1031         pmdev->pages[i].status_temperature = 0;
1032         pmdev->pages[i].status_cml = 0;
1033         pmdev->pages[i].status_other = 0;
1034         pmdev->pages[i].status_mfr_specific = 0;
1035         pmdev->pages[i].status_fans_1_2 = 0;
1036         pmdev->pages[i].status_fans_3_4 = 0;
1037     }
1038 
1039 }
1040 
1041 /*
1042  * PMBus operation is used to turn On and Off PSUs
1043  * Therefore, default value for the Operation should be PB_OP_ON or 0x80
1044  */
1045 static void pmbus_operation(PMBusDevice *pmdev)
1046 {
1047     uint8_t index = pmdev->page;
1048     if ((pmdev->pages[index].operation & PB_OP_ON) == 0) {
1049         pmdev->pages[index].read_vout = 0;
1050         pmdev->pages[index].read_iout = 0;
1051         pmdev->pages[index].read_pout = 0;
1052         return;
1053     }
1054 
1055     if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_HIGH)) {
1056         pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_high;
1057     }
1058 
1059     if (pmdev->pages[index].operation & (PB_OP_ON | PB_OP_MARGIN_LOW)) {
1060         pmdev->pages[index].read_vout = pmdev->pages[index].vout_margin_low;
1061     }
1062     pmbus_check_limits(pmdev);
1063 }
1064 
1065 static int pmbus_write_data(SMBusDevice *smd, uint8_t *buf, uint8_t len)
1066 {
1067     PMBusDevice *pmdev = PMBUS_DEVICE(smd);
1068     PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
1069     int ret = 0;
1070     uint8_t index;
1071 
1072     if (len == 0) {
1073         qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
1074         return PMBUS_ERR_BYTE;
1075     }
1076 
1077     if (!pmdev->pages) { /* allocate memory for pages on first use */
1078         pmbus_pages_alloc(pmdev);
1079     }
1080 
1081     pmdev->in_buf_len = len;
1082     pmdev->in_buf = buf;
1083 
1084     pmdev->code = buf[0]; /* PMBus command code */
1085     if (len == 1) { /* Single length writes are command codes only */
1086         return 0;
1087     }
1088 
1089     if (pmdev->code == PMBUS_PAGE) {
1090         pmdev->page = pmbus_receive8(pmdev);
1091         return 0;
1092     }
1093 
1094     /* loop through all the pages when 0xFF is received */
1095     if (pmdev->page == PB_ALL_PAGES) {
1096         for (int i = 0; i < pmdev->num_pages; i++) {
1097             pmdev->page = i;
1098             pmbus_write_data(smd, buf, len);
1099         }
1100         pmdev->page = PB_ALL_PAGES;
1101         return 0;
1102     }
1103 
1104     if (pmdev->page > pmdev->num_pages - 1) {
1105         qemu_log_mask(LOG_GUEST_ERROR,
1106                         "%s: page %u is out of range\n",
1107                         __func__, pmdev->page);
1108         pmdev->page = 0; /* undefined behaviour - reset to page 0 */
1109         pmbus_cml_error(pmdev);
1110         return PMBUS_ERR_BYTE;
1111     }
1112 
1113     index = pmdev->page;
1114 
1115     switch (pmdev->code) {
1116     case PMBUS_OPERATION:                 /* R/W byte */
1117         pmdev->pages[index].operation = pmbus_receive8(pmdev);
1118         pmbus_operation(pmdev);
1119         break;
1120 
1121     case PMBUS_ON_OFF_CONFIG:             /* R/W byte */
1122         pmdev->pages[index].on_off_config = pmbus_receive8(pmdev);
1123         break;
1124 
1125     case PMBUS_CLEAR_FAULTS:              /* Send Byte */
1126         pmbus_clear_faults(pmdev);
1127         break;
1128 
1129     case PMBUS_PHASE:                     /* R/W byte */
1130         pmdev->pages[index].phase = pmbus_receive8(pmdev);
1131         break;
1132 
1133     case PMBUS_PAGE_PLUS_WRITE:           /* Block Write-only */
1134     case PMBUS_WRITE_PROTECT:             /* R/W byte */
1135         pmdev->pages[index].write_protect = pmbus_receive8(pmdev);
1136         break;
1137 
1138     case PMBUS_VOUT_MODE:                 /* R/W byte */
1139         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MODE) {
1140             pmdev->pages[index].vout_mode = pmbus_receive8(pmdev);
1141         } else {
1142             goto passthrough;
1143         }
1144         break;
1145 
1146     case PMBUS_VOUT_COMMAND:              /* R/W word */
1147         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1148             pmdev->pages[index].vout_command = pmbus_receive16(pmdev);
1149         } else {
1150             goto passthrough;
1151         }
1152         break;
1153 
1154     case PMBUS_VOUT_TRIM:                 /* R/W word */
1155         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1156             pmdev->pages[index].vout_trim = pmbus_receive16(pmdev);
1157         } else {
1158             goto passthrough;
1159         }
1160         break;
1161 
1162     case PMBUS_VOUT_CAL_OFFSET:           /* R/W word */
1163         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1164             pmdev->pages[index].vout_cal_offset = pmbus_receive16(pmdev);
1165         } else {
1166             goto passthrough;
1167         }
1168         break;
1169 
1170     case PMBUS_VOUT_MAX:                  /* R/W word */
1171         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1172             pmdev->pages[index].vout_max = pmbus_receive16(pmdev);
1173         } else {
1174             goto passthrough;
1175         }
1176         break;
1177 
1178     case PMBUS_VOUT_MARGIN_HIGH:          /* R/W word */
1179         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1180             pmdev->pages[index].vout_margin_high = pmbus_receive16(pmdev);
1181         } else {
1182             goto passthrough;
1183         }
1184         break;
1185 
1186     case PMBUS_VOUT_MARGIN_LOW:           /* R/W word */
1187         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_MARGIN) {
1188             pmdev->pages[index].vout_margin_low = pmbus_receive16(pmdev);
1189         } else {
1190             goto passthrough;
1191         }
1192         break;
1193 
1194     case PMBUS_VOUT_TRANSITION_RATE:      /* R/W word */
1195         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1196             pmdev->pages[index].vout_transition_rate = pmbus_receive16(pmdev);
1197         } else {
1198             goto passthrough;
1199         }
1200         break;
1201 
1202     case PMBUS_VOUT_DROOP:                /* R/W word */
1203         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1204             pmdev->pages[index].vout_droop = pmbus_receive16(pmdev);
1205         } else {
1206             goto passthrough;
1207         }
1208         break;
1209 
1210     case PMBUS_VOUT_SCALE_LOOP:           /* R/W word */
1211         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1212             pmdev->pages[index].vout_scale_loop = pmbus_receive16(pmdev);
1213         } else {
1214             goto passthrough;
1215         }
1216         break;
1217 
1218     case PMBUS_VOUT_SCALE_MONITOR:        /* R/W word */
1219         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1220             pmdev->pages[index].vout_scale_monitor = pmbus_receive16(pmdev);
1221         } else {
1222             goto passthrough;
1223         }
1224         break;
1225 
1226     case PMBUS_VOUT_MIN:                  /* R/W word */
1227         if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
1228             pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
1229         } else {
1230             goto passthrough;
1231         }
1232         break;
1233 
1234     case PMBUS_POUT_MAX:                  /* R/W word */
1235         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1236             pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
1237         } else {
1238             goto passthrough;
1239         }
1240         break;
1241 
1242     case PMBUS_VIN_ON:                    /* R/W word */
1243         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1244             pmdev->pages[index].vin_on = pmbus_receive16(pmdev);
1245         } else {
1246             goto passthrough;
1247         }
1248         break;
1249 
1250     case PMBUS_VIN_OFF:                   /* R/W word */
1251         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1252             pmdev->pages[index].vin_off = pmbus_receive16(pmdev);
1253         } else {
1254             goto passthrough;
1255         }
1256         break;
1257 
1258     case PMBUS_IOUT_CAL_GAIN:             /* R/W word */
1259         if (pmdev->pages[index].page_flags & PB_HAS_IOUT_GAIN) {
1260             pmdev->pages[index].iout_cal_gain = pmbus_receive16(pmdev);
1261         } else {
1262             goto passthrough;
1263         }
1264         break;
1265 
1266     case PMBUS_VOUT_OV_FAULT_LIMIT:       /* R/W word */
1267         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1268             pmdev->pages[index].vout_ov_fault_limit = pmbus_receive16(pmdev);
1269         } else {
1270             goto passthrough;
1271         }
1272         break;
1273 
1274     case PMBUS_VOUT_OV_FAULT_RESPONSE:    /* R/W byte */
1275         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1276             pmdev->pages[index].vout_ov_fault_response = pmbus_receive8(pmdev);
1277         } else {
1278             goto passthrough;
1279         }
1280         break;
1281 
1282     case PMBUS_VOUT_OV_WARN_LIMIT:        /* R/W word */
1283         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1284             pmdev->pages[index].vout_ov_warn_limit = pmbus_receive16(pmdev);
1285         } else {
1286             goto passthrough;
1287         }
1288         break;
1289 
1290     case PMBUS_VOUT_UV_WARN_LIMIT:        /* R/W word */
1291         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1292             pmdev->pages[index].vout_uv_warn_limit = pmbus_receive16(pmdev);
1293         } else {
1294             goto passthrough;
1295         }
1296         break;
1297 
1298     case PMBUS_VOUT_UV_FAULT_LIMIT:       /* R/W word */
1299         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1300             pmdev->pages[index].vout_uv_fault_limit = pmbus_receive16(pmdev);
1301         } else {
1302             goto passthrough;
1303         }
1304         break;
1305 
1306     case PMBUS_VOUT_UV_FAULT_RESPONSE:    /* R/W byte */
1307         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1308             pmdev->pages[index].vout_uv_fault_response = pmbus_receive8(pmdev);
1309         } else {
1310             goto passthrough;
1311         }
1312         break;
1313 
1314     case PMBUS_IOUT_OC_FAULT_LIMIT:       /* R/W word */
1315         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1316             pmdev->pages[index].iout_oc_fault_limit = pmbus_receive16(pmdev);
1317         } else {
1318             goto passthrough;
1319         }
1320         break;
1321 
1322     case PMBUS_IOUT_OC_FAULT_RESPONSE:    /* R/W byte */
1323         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1324             pmdev->pages[index].iout_oc_fault_response = pmbus_receive8(pmdev);
1325         } else {
1326             goto passthrough;
1327         }
1328         break;
1329 
1330     case PMBUS_IOUT_OC_LV_FAULT_LIMIT:    /* R/W word */
1331         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1332             pmdev->pages[index].iout_oc_lv_fault_limit = pmbus_receive16(pmdev);
1333         } else {
1334             goto passthrough;
1335         }
1336         break;
1337 
1338     case PMBUS_IOUT_OC_LV_FAULT_RESPONSE: /* R/W byte */
1339         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1340             pmdev->pages[index].iout_oc_lv_fault_response
1341                 = pmbus_receive8(pmdev);
1342         } else {
1343             goto passthrough;
1344         }
1345         break;
1346 
1347     case PMBUS_IOUT_OC_WARN_LIMIT:        /* R/W word */
1348         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1349             pmdev->pages[index].iout_oc_warn_limit = pmbus_receive16(pmdev);
1350         } else {
1351             goto passthrough;
1352         }
1353         break;
1354 
1355     case PMBUS_IOUT_UC_FAULT_LIMIT:       /* R/W word */
1356         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1357             pmdev->pages[index].iout_uc_fault_limit = pmbus_receive16(pmdev);
1358         } else {
1359             goto passthrough;
1360         }
1361         break;
1362 
1363     case PMBUS_IOUT_UC_FAULT_RESPONSE:    /* R/W byte */
1364         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1365             pmdev->pages[index].iout_uc_fault_response = pmbus_receive8(pmdev);
1366         } else {
1367             goto passthrough;
1368         }
1369         break;
1370 
1371     case PMBUS_OT_FAULT_LIMIT:            /* R/W word */
1372         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1373             pmdev->pages[index].ot_fault_limit = pmbus_receive16(pmdev);
1374         } else {
1375             goto passthrough;
1376         }
1377         break;
1378 
1379     case PMBUS_OT_FAULT_RESPONSE:         /* R/W byte */
1380         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1381             pmdev->pages[index].ot_fault_response = pmbus_receive8(pmdev);
1382         } else {
1383             goto passthrough;
1384         }
1385         break;
1386 
1387     case PMBUS_OT_WARN_LIMIT:             /* R/W word */
1388         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1389             pmdev->pages[index].ot_warn_limit = pmbus_receive16(pmdev);
1390         } else {
1391             goto passthrough;
1392         }
1393         break;
1394 
1395     case PMBUS_UT_WARN_LIMIT:             /* R/W word */
1396         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1397             pmdev->pages[index].ut_warn_limit = pmbus_receive16(pmdev);
1398         } else {
1399             goto passthrough;
1400         }
1401         break;
1402 
1403     case PMBUS_UT_FAULT_LIMIT:            /* R/W word */
1404         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1405             pmdev->pages[index].ut_fault_limit = pmbus_receive16(pmdev);
1406         } else {
1407             goto passthrough;
1408         }
1409         break;
1410 
1411     case PMBUS_UT_FAULT_RESPONSE:         /* R/W byte */
1412         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1413             pmdev->pages[index].ut_fault_response = pmbus_receive8(pmdev);
1414         } else {
1415             goto passthrough;
1416         }
1417         break;
1418 
1419     case PMBUS_VIN_OV_FAULT_LIMIT:        /* R/W word */
1420         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1421             pmdev->pages[index].vin_ov_fault_limit = pmbus_receive16(pmdev);
1422         } else {
1423             goto passthrough;
1424         }
1425         break;
1426 
1427     case PMBUS_VIN_OV_FAULT_RESPONSE:     /* R/W byte */
1428         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1429             pmdev->pages[index].vin_ov_fault_response = pmbus_receive8(pmdev);
1430         } else {
1431             goto passthrough;
1432         }
1433         break;
1434 
1435     case PMBUS_VIN_OV_WARN_LIMIT:         /* R/W word */
1436         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1437             pmdev->pages[index].vin_ov_warn_limit = pmbus_receive16(pmdev);
1438         } else {
1439             goto passthrough;
1440         }
1441         break;
1442 
1443     case PMBUS_VIN_UV_WARN_LIMIT:         /* R/W word */
1444         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1445             pmdev->pages[index].vin_uv_warn_limit = pmbus_receive16(pmdev);
1446         } else {
1447             goto passthrough;
1448         }
1449         break;
1450 
1451     case PMBUS_VIN_UV_FAULT_LIMIT:        /* R/W word */
1452         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1453             pmdev->pages[index].vin_uv_fault_limit = pmbus_receive16(pmdev);
1454         } else {
1455             goto passthrough;
1456         }
1457         break;
1458 
1459     case PMBUS_VIN_UV_FAULT_RESPONSE:     /* R/W byte */
1460         if (pmdev->pages[index].page_flags & PB_HAS_VIN) {
1461             pmdev->pages[index].vin_uv_fault_response = pmbus_receive8(pmdev);
1462         } else {
1463             goto passthrough;
1464         }
1465         break;
1466 
1467     case PMBUS_IIN_OC_FAULT_LIMIT:        /* R/W word */
1468         if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1469             pmdev->pages[index].iin_oc_fault_limit = pmbus_receive16(pmdev);
1470         } else {
1471             goto passthrough;
1472         }
1473         break;
1474 
1475     case PMBUS_IIN_OC_FAULT_RESPONSE:     /* R/W byte */
1476         if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1477             pmdev->pages[index].iin_oc_fault_response = pmbus_receive8(pmdev);
1478         } else {
1479             goto passthrough;
1480         }
1481         break;
1482 
1483     case PMBUS_IIN_OC_WARN_LIMIT:         /* R/W word */
1484         if (pmdev->pages[index].page_flags & PB_HAS_IIN) {
1485             pmdev->pages[index].iin_oc_warn_limit = pmbus_receive16(pmdev);
1486         } else {
1487             goto passthrough;
1488         }
1489         break;
1490 
1491     case PMBUS_POUT_OP_FAULT_LIMIT:       /* R/W word */
1492         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1493             pmdev->pages[index].pout_op_fault_limit = pmbus_receive16(pmdev);
1494         } else {
1495             goto passthrough;
1496         }
1497         break;
1498 
1499     case PMBUS_POUT_OP_FAULT_RESPONSE:    /* R/W byte */
1500         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1501             pmdev->pages[index].pout_op_fault_response = pmbus_receive8(pmdev);
1502         } else {
1503             goto passthrough;
1504         }
1505         break;
1506 
1507     case PMBUS_POUT_OP_WARN_LIMIT:        /* R/W word */
1508         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1509             pmdev->pages[index].pout_op_warn_limit = pmbus_receive16(pmdev);
1510         } else {
1511             goto passthrough;
1512         }
1513         break;
1514 
1515     case PMBUS_PIN_OP_WARN_LIMIT:         /* R/W word */
1516         if (pmdev->pages[index].page_flags & PB_HAS_PIN) {
1517             pmdev->pages[index].pin_op_warn_limit = pmbus_receive16(pmdev);
1518         } else {
1519             goto passthrough;
1520         }
1521         break;
1522 
1523     case PMBUS_STATUS_BYTE:               /* R/W byte */
1524         pmdev->pages[index].status_word = pmbus_receive8(pmdev);
1525         break;
1526 
1527     case PMBUS_STATUS_WORD:               /* R/W word */
1528         pmdev->pages[index].status_word = pmbus_receive16(pmdev);
1529         break;
1530 
1531     case PMBUS_STATUS_VOUT:               /* R/W byte */
1532         if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
1533             pmdev->pages[index].status_vout = pmbus_receive8(pmdev);
1534         } else {
1535             goto passthrough;
1536         }
1537         break;
1538 
1539     case PMBUS_STATUS_IOUT:               /* R/W byte */
1540         if (pmdev->pages[index].page_flags & PB_HAS_IOUT) {
1541             pmdev->pages[index].status_iout = pmbus_receive8(pmdev);
1542         } else {
1543             goto passthrough;
1544         }
1545         break;
1546 
1547     case PMBUS_STATUS_INPUT:              /* R/W byte */
1548         pmdev->pages[index].status_input = pmbus_receive8(pmdev);
1549         break;
1550 
1551     case PMBUS_STATUS_TEMPERATURE:        /* R/W byte */
1552         if (pmdev->pages[index].page_flags & PB_HAS_TEMPERATURE) {
1553             pmdev->pages[index].status_temperature = pmbus_receive8(pmdev);
1554         } else {
1555             goto passthrough;
1556         }
1557         break;
1558 
1559     case PMBUS_STATUS_CML:                /* R/W byte */
1560         pmdev->pages[index].status_cml = pmbus_receive8(pmdev);
1561         break;
1562 
1563     case PMBUS_STATUS_OTHER:              /* R/W byte */
1564         pmdev->pages[index].status_other = pmbus_receive8(pmdev);
1565         break;
1566 
1567     case PMBUS_STATUS_MFR_SPECIFIC:        /* R/W byte */
1568         pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
1569         break;
1570 
1571     case PMBUS_PAGE_PLUS_READ:            /* Block Read-only */
1572     case PMBUS_CAPABILITY:                /* Read-Only byte */
1573     case PMBUS_COEFFICIENTS:              /* Read-only block 5 bytes */
1574     case PMBUS_READ_EIN:                  /* Read-Only block 5 bytes */
1575     case PMBUS_READ_EOUT:                 /* Read-Only block 5 bytes */
1576     case PMBUS_READ_VIN:                  /* Read-Only word */
1577     case PMBUS_READ_IIN:                  /* Read-Only word */
1578     case PMBUS_READ_VCAP:                 /* Read-Only word */
1579     case PMBUS_READ_VOUT:                 /* Read-Only word */
1580     case PMBUS_READ_IOUT:                 /* Read-Only word */
1581     case PMBUS_READ_TEMPERATURE_1:        /* Read-Only word */
1582     case PMBUS_READ_TEMPERATURE_2:        /* Read-Only word */
1583     case PMBUS_READ_TEMPERATURE_3:        /* Read-Only word */
1584     case PMBUS_READ_FAN_SPEED_1:          /* Read-Only word */
1585     case PMBUS_READ_FAN_SPEED_2:          /* Read-Only word */
1586     case PMBUS_READ_FAN_SPEED_3:          /* Read-Only word */
1587     case PMBUS_READ_FAN_SPEED_4:          /* Read-Only word */
1588     case PMBUS_READ_DUTY_CYCLE:           /* Read-Only word */
1589     case PMBUS_READ_FREQUENCY:            /* Read-Only word */
1590     case PMBUS_READ_POUT:                 /* Read-Only word */
1591     case PMBUS_READ_PIN:                  /* Read-Only word */
1592     case PMBUS_REVISION:                  /* Read-Only byte */
1593     case PMBUS_APP_PROFILE_SUPPORT:       /* Read-Only block-read */
1594     case PMBUS_MFR_VIN_MIN:               /* Read-Only word */
1595     case PMBUS_MFR_VIN_MAX:               /* Read-Only word */
1596     case PMBUS_MFR_IIN_MAX:               /* Read-Only word */
1597     case PMBUS_MFR_PIN_MAX:               /* Read-Only word */
1598     case PMBUS_MFR_VOUT_MIN:              /* Read-Only word */
1599     case PMBUS_MFR_VOUT_MAX:              /* Read-Only word */
1600     case PMBUS_MFR_IOUT_MAX:              /* Read-Only word */
1601     case PMBUS_MFR_POUT_MAX:              /* Read-Only word */
1602     case PMBUS_MFR_TAMBIENT_MAX:          /* Read-Only word */
1603     case PMBUS_MFR_TAMBIENT_MIN:          /* Read-Only word */
1604     case PMBUS_MFR_EFFICIENCY_LL:         /* Read-Only block 14 bytes */
1605     case PMBUS_MFR_EFFICIENCY_HL:         /* Read-Only block 14 bytes */
1606     case PMBUS_MFR_PIN_ACCURACY:          /* Read-Only byte */
1607     case PMBUS_IC_DEVICE_ID:              /* Read-Only block-read */
1608     case PMBUS_IC_DEVICE_REV:             /* Read-Only block-read */
1609         qemu_log_mask(LOG_GUEST_ERROR,
1610                       "%s: writing to read-only register 0x%02x\n",
1611                       __func__, pmdev->code);
1612         break;
1613 
1614 passthrough:
1615     /* Unimplimented registers get passed to the device */
1616     default:
1617         if (pmdc->write_data) {
1618             ret = pmdc->write_data(pmdev, buf, len);
1619         }
1620         break;
1621     }
1622     pmbus_check_limits(pmdev);
1623     pmdev->in_buf_len = 0;
1624     return ret;
1625 }
1626 
1627 int pmbus_page_config(PMBusDevice *pmdev, uint8_t index, uint64_t flags)
1628 {
1629     if (!pmdev->pages) { /* allocate memory for pages on first use */
1630         pmbus_pages_alloc(pmdev);
1631     }
1632 
1633     /* The 0xFF page is special for commands applying to all pages */
1634     if (index == PB_ALL_PAGES) {
1635         for (int i = 0; i < pmdev->num_pages; i++) {
1636             pmdev->pages[i].page_flags = flags;
1637         }
1638         return 0;
1639     }
1640 
1641     if (index > pmdev->num_pages - 1) {
1642         qemu_log_mask(LOG_GUEST_ERROR,
1643                       "%s: index %u is out of range\n",
1644                       __func__, index);
1645         return -1;
1646     }
1647 
1648     pmdev->pages[index].page_flags = flags;
1649 
1650     return 0;
1651 }
1652 
1653 /* TODO: include pmbus page info in vmstate */
1654 const VMStateDescription vmstate_pmbus_device = {
1655     .name = TYPE_PMBUS_DEVICE,
1656     .version_id = 0,
1657     .minimum_version_id = 0,
1658     .fields = (VMStateField[]) {
1659         VMSTATE_SMBUS_DEVICE(smb, PMBusDevice),
1660         VMSTATE_UINT8(num_pages, PMBusDevice),
1661         VMSTATE_UINT8(code, PMBusDevice),
1662         VMSTATE_UINT8(page, PMBusDevice),
1663         VMSTATE_UINT8(capability, PMBusDevice),
1664         VMSTATE_END_OF_LIST()
1665     }
1666 };
1667 
1668 static void pmbus_device_finalize(Object *obj)
1669 {
1670     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
1671     g_free(pmdev->pages);
1672 }
1673 
1674 static void pmbus_device_class_init(ObjectClass *klass, void *data)
1675 {
1676     SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
1677 
1678     k->quick_cmd = pmbus_quick_cmd;
1679     k->write_data = pmbus_write_data;
1680     k->receive_byte = pmbus_receive_byte;
1681 }
1682 
1683 static const TypeInfo pmbus_device_type_info = {
1684     .name = TYPE_PMBUS_DEVICE,
1685     .parent = TYPE_SMBUS_DEVICE,
1686     .instance_size = sizeof(PMBusDevice),
1687     .instance_finalize = pmbus_device_finalize,
1688     .abstract = true,
1689     .class_size = sizeof(PMBusDeviceClass),
1690     .class_init = pmbus_device_class_init,
1691 };
1692 
1693 static void pmbus_device_register_types(void)
1694 {
1695     type_register_static(&pmbus_device_type_info);
1696 }
1697 
1698 type_init(pmbus_device_register_types)
1699