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