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