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