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