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