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