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