xref: /qemu/hw/sensor/max31785.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
162365482SMaheswara Kurapati // SPDX-License-Identifier: GPL-2.0-or-later
262365482SMaheswara Kurapati /*
362365482SMaheswara Kurapati  * Maxim MAX31785 PMBus 6-Channel Fan Controller
462365482SMaheswara Kurapati  *
562365482SMaheswara Kurapati  * Datasheet:
662365482SMaheswara Kurapati  * https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
762365482SMaheswara Kurapati  *
862365482SMaheswara Kurapati  * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
962365482SMaheswara Kurapati  */
1062365482SMaheswara Kurapati 
1162365482SMaheswara Kurapati #include "qemu/osdep.h"
1262365482SMaheswara Kurapati #include "hw/i2c/pmbus_device.h"
1362365482SMaheswara Kurapati #include "hw/irq.h"
1462365482SMaheswara Kurapati #include "migration/vmstate.h"
1562365482SMaheswara Kurapati #include "qapi/error.h"
1662365482SMaheswara Kurapati #include "qapi/visitor.h"
1762365482SMaheswara Kurapati #include "qemu/log.h"
1862365482SMaheswara Kurapati #include "qemu/module.h"
1962365482SMaheswara Kurapati 
2062365482SMaheswara Kurapati #define TYPE_MAX31785 "max31785"
2162365482SMaheswara Kurapati #define MAX31785(obj) OBJECT_CHECK(MAX31785State, (obj), TYPE_MAX31785)
2262365482SMaheswara Kurapati 
2362365482SMaheswara Kurapati /* MAX31785 mfr specific PMBus commands */
2462365482SMaheswara Kurapati #define MAX31785_MFR_MODE               0xD1
2562365482SMaheswara Kurapati #define MAX31785_MFR_PSEN_CONFIG        0xD2
2662365482SMaheswara Kurapati #define MAX31785_MFR_VOUT_PEAK          0xD4
2762365482SMaheswara Kurapati #define MAX31785_MFR_TEMPERATURE_PEAK   0xD6
2862365482SMaheswara Kurapati #define MAX31785_MFR_VOUT_MIN           0xD7
2962365482SMaheswara Kurapati #define MAX31785_MFR_FAULT_RESPONSE     0xD9
3062365482SMaheswara Kurapati #define MAX31785_MFR_NV_FAULT_LOG       0xDC
3162365482SMaheswara Kurapati #define MAX31785_MFR_TIME_COUNT         0xDD
3262365482SMaheswara Kurapati #define MAX31785_MFR_TEMP_SENSOR_CONFIG 0xF0
3362365482SMaheswara Kurapati #define MAX31785_MFR_FAN_CONFIG         0xF1
3462365482SMaheswara Kurapati #define MAX31785_MFR_FAN_LUT            0xF2
3562365482SMaheswara Kurapati #define MAX31785_MFR_READ_FAN_PWM       0xF3
3662365482SMaheswara Kurapati #define MAX31785_MFR_FAN_FAULT_LIMIT    0xF5
3762365482SMaheswara Kurapati #define MAX31785_MFR_FAN_WARN_LIMIT     0xF6
3862365482SMaheswara Kurapati #define MAX31785_MFR_FAN_RUN_TIME       0xF7
3962365482SMaheswara Kurapati #define MAX31785_MFR_FAN_PWM_AVG        0xF8
4062365482SMaheswara Kurapati #define MAX31785_MFR_FAN_PWM2RPM        0xF9
4162365482SMaheswara Kurapati 
4262365482SMaheswara Kurapati /* defaults as per the data sheet */
4362365482SMaheswara Kurapati #define MAX31785_DEFAULT_CAPABILITY            0x10
4462365482SMaheswara Kurapati #define MAX31785_DEFAULT_VOUT_MODE             0x40
4562365482SMaheswara Kurapati #define MAX31785_DEFAULT_VOUT_SCALE_MONITOR    0x7FFF
4662365482SMaheswara Kurapati #define MAX31785_DEFAULT_FAN_COMMAND_1         0x7FFF
4762365482SMaheswara Kurapati #define MAX31785_DEFAULT_OV_FAULT_LIMIT        0x7FFF
4862365482SMaheswara Kurapati #define MAX31785_DEFAULT_OV_WARN_LIMIT         0x7FFF
4962365482SMaheswara Kurapati #define MAX31785_DEFAULT_OT_FAULT_LIMIT        0x7FFF
5062365482SMaheswara Kurapati #define MAX31785_DEFAULT_OT_WARN_LIMIT         0x7FFF
5162365482SMaheswara Kurapati #define MAX31785_DEFAULT_PMBUS_REVISION        0x11
5262365482SMaheswara Kurapati #define MAX31785_DEFAULT_MFR_ID                0x4D
5362365482SMaheswara Kurapati #define MAX31785_DEFAULT_MFR_MODEL             0x53
5462365482SMaheswara Kurapati #define MAX31785_DEFAULT_MFR_REVISION          0x3030
5562365482SMaheswara Kurapati #define MAX31785A_DEFAULT_MFR_REVISION         0x3040
5662365482SMaheswara Kurapati #define MAX31785B_DEFAULT_MFR_REVISION         0x3061
5762365482SMaheswara Kurapati #define MAX31785B_DEFAULT_MFR_TEMPERATURE_PEAK 0x8000
5862365482SMaheswara Kurapati #define MAX31785B_DEFAULT_MFR_VOUT_MIN         0x7FFF
5962365482SMaheswara Kurapati #define MAX31785_DEFAULT_TEXT                  0x3130313031303130
6062365482SMaheswara Kurapati 
6162365482SMaheswara Kurapati /* MAX31785 pages */
6262365482SMaheswara Kurapati #define MAX31785_TOTAL_NUM_PAGES      23
6362365482SMaheswara Kurapati #define MAX31785_FAN_PAGES            6
6462365482SMaheswara Kurapati #define MAX31785_MIN_FAN_PAGE         0
6562365482SMaheswara Kurapati #define MAX31785_MAX_FAN_PAGE         5
6662365482SMaheswara Kurapati #define MAX31785_MIN_TEMP_PAGE        6
6762365482SMaheswara Kurapati #define MAX31785_MAX_TEMP_PAGE        16
6862365482SMaheswara Kurapati #define MAX31785_MIN_ADC_VOLTAGE_PAGE 17
6962365482SMaheswara Kurapati #define MAX31785_MAX_ADC_VOLTAGE_PAGE 22
7062365482SMaheswara Kurapati 
7162365482SMaheswara Kurapati /* FAN_CONFIG_1_2 */
7262365482SMaheswara Kurapati #define MAX31785_MFR_FAN_CONFIG                0xF1
7362365482SMaheswara Kurapati #define MAX31785_FAN_CONFIG_ENABLE             BIT(7)
7462365482SMaheswara Kurapati #define MAX31785_FAN_CONFIG_RPM_PWM            BIT(6)
7562365482SMaheswara Kurapati #define MAX31785_FAN_CONFIG_PULSE(pulse)       (pulse << 4)
7662365482SMaheswara Kurapati #define MAX31785_DEFAULT_FAN_CONFIG_1_2(pulse)                                 \
7762365482SMaheswara Kurapati     (MAX31785_FAN_CONFIG_ENABLE | MAX31785_FAN_CONFIG_PULSE(pulse))
7862365482SMaheswara Kurapati #define MAX31785_DEFAULT_MFR_FAN_CONFIG        0x0000
7962365482SMaheswara Kurapati 
8062365482SMaheswara Kurapati /* fan speed in RPM */
8162365482SMaheswara Kurapati #define MAX31785_DEFAULT_FAN_SPEED   0x7fff
8262365482SMaheswara Kurapati #define MAX31785_DEFAULT_FAN_STATUS  0x00
8362365482SMaheswara Kurapati 
8462365482SMaheswara Kurapati #define MAX31785_DEFAULT_FAN_MAX_PWM 0x2710
8562365482SMaheswara Kurapati 
8662365482SMaheswara Kurapati /*
8762365482SMaheswara Kurapati  * MAX31785State:
8862365482SMaheswara Kurapati  * @code: The command code received
8962365482SMaheswara Kurapati  * @page: Each page corresponds to a device monitored by the Max 31785
9062365482SMaheswara Kurapati  * The page register determines the available commands depending on device
9162365482SMaheswara Kurapati  * _____________________________________________________________________________
9262365482SMaheswara Kurapati  * |   0   |  Fan Connected to PWM0                                            |
9362365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
9462365482SMaheswara Kurapati  * |   1   |  Fan Connected to PWM1                                            |
9562365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
9662365482SMaheswara Kurapati  * |   2   |  Fan Connected to PWM2                                            |
9762365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
9862365482SMaheswara Kurapati  * |   3   |  Fan Connected to PWM3                                            |
9962365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
10062365482SMaheswara Kurapati  * |   4   |  Fan Connected to PWM4                                            |
10162365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
10262365482SMaheswara Kurapati  * |   5   |  Fan Connected to PWM5                                            |
10362365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
10462365482SMaheswara Kurapati  * |   6   |  Remote Thermal Diode Connected to ADC 0                          |
10562365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
10662365482SMaheswara Kurapati  * |   7   |  Remote Thermal Diode Connected to ADC 1                          |
10762365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
10862365482SMaheswara Kurapati  * |   8   |  Remote Thermal Diode Connected to ADC 2                          |
10962365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
11062365482SMaheswara Kurapati  * |   9   |  Remote Thermal Diode Connected to ADC 3                          |
11162365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
11262365482SMaheswara Kurapati  * |  10   |  Remote Thermal Diode Connected to ADC 4                          |
11362365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
11462365482SMaheswara Kurapati  * |  11   |  Remote Thermal Diode Connected to ADC 5                          |
11562365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
11662365482SMaheswara Kurapati  * |  12   |  Internal Temperature Sensor                                      |
11762365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
11862365482SMaheswara Kurapati  * |  13   |  Remote I2C Temperature Sensor with Address 0                     |
11962365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
12062365482SMaheswara Kurapati  * |  14   |  Remote I2C Temperature Sensor with Address 1                     |
12162365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
12262365482SMaheswara Kurapati  * |  15   |  Remote I2C Temperature Sensor with Address 2                     |
12362365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
12462365482SMaheswara Kurapati  * |  16   |  Remote I2C Temperature Sensor with Address 3                     |
12562365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
12662365482SMaheswara Kurapati  * |  17   |  Remote I2C Temperature Sensor with Address 4                     |
12762365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
12862365482SMaheswara Kurapati  * |  17   |  Remote Voltage Connected to ADC0                                 |
12962365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
13062365482SMaheswara Kurapati  * |  18   |  Remote Voltage Connected to ADC1                                 |
13162365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
13262365482SMaheswara Kurapati  * |  19   |  Remote Voltage Connected to ADC2                                 |
13362365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
13462365482SMaheswara Kurapati  * |  20   |  Remote Voltage Connected to ADC3                                 |
13562365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
13662365482SMaheswara Kurapati  * |  21   |  Remote Voltage Connected to ADC4                                 |
13762365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
13862365482SMaheswara Kurapati  * |  22   |  Remote Voltage Connected to ADC5                                 |
13962365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
14062365482SMaheswara Kurapati  * |23-254 |  Reserved                                                         |
14162365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
14262365482SMaheswara Kurapati  * |  255  |  Applies to all pages                                             |
14362365482SMaheswara Kurapati  * |_______|___________________________________________________________________|
14462365482SMaheswara Kurapati  */
14562365482SMaheswara Kurapati 
14662365482SMaheswara Kurapati /* Place holder to save the max31785 mfr specific registers */
14762365482SMaheswara Kurapati typedef struct MAX31785State {
14862365482SMaheswara Kurapati     PMBusDevice parent;
14962365482SMaheswara Kurapati     uint16_t mfr_mode[MAX31785_TOTAL_NUM_PAGES];
15062365482SMaheswara Kurapati     uint16_t vout_peak[MAX31785_TOTAL_NUM_PAGES];
15162365482SMaheswara Kurapati     uint16_t temperature_peak[MAX31785_TOTAL_NUM_PAGES];
15262365482SMaheswara Kurapati     uint16_t vout_min[MAX31785_TOTAL_NUM_PAGES];
15362365482SMaheswara Kurapati     uint8_t  fault_response[MAX31785_TOTAL_NUM_PAGES];
15462365482SMaheswara Kurapati     uint32_t time_count[MAX31785_TOTAL_NUM_PAGES];
15562365482SMaheswara Kurapati     uint16_t temp_sensor_config[MAX31785_TOTAL_NUM_PAGES];
15662365482SMaheswara Kurapati     uint16_t fan_config[MAX31785_TOTAL_NUM_PAGES];
15762365482SMaheswara Kurapati     uint16_t read_fan_pwm[MAX31785_TOTAL_NUM_PAGES];
15862365482SMaheswara Kurapati     uint16_t fan_fault_limit[MAX31785_TOTAL_NUM_PAGES];
15962365482SMaheswara Kurapati     uint16_t fan_warn_limit[MAX31785_TOTAL_NUM_PAGES];
16062365482SMaheswara Kurapati     uint16_t fan_run_time[MAX31785_TOTAL_NUM_PAGES];
16162365482SMaheswara Kurapati     uint16_t fan_pwm_avg[MAX31785_TOTAL_NUM_PAGES];
16262365482SMaheswara Kurapati     uint64_t fan_pwm2rpm[MAX31785_TOTAL_NUM_PAGES];
16362365482SMaheswara Kurapati     uint64_t mfr_location;
16462365482SMaheswara Kurapati     uint64_t mfr_date;
16562365482SMaheswara Kurapati     uint64_t mfr_serial;
16662365482SMaheswara Kurapati     uint16_t mfr_revision;
16762365482SMaheswara Kurapati } MAX31785State;
16862365482SMaheswara Kurapati 
max31785_read_byte(PMBusDevice * pmdev)16962365482SMaheswara Kurapati static uint8_t max31785_read_byte(PMBusDevice *pmdev)
17062365482SMaheswara Kurapati {
17162365482SMaheswara Kurapati     MAX31785State *s = MAX31785(pmdev);
17262365482SMaheswara Kurapati     switch (pmdev->code) {
17362365482SMaheswara Kurapati 
17462365482SMaheswara Kurapati     case PMBUS_FAN_CONFIG_1_2:
17562365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
17662365482SMaheswara Kurapati             pmbus_send8(pmdev, pmdev->pages[pmdev->page].fan_config_1_2);
17762365482SMaheswara Kurapati         }
17862365482SMaheswara Kurapati         break;
17962365482SMaheswara Kurapati 
18062365482SMaheswara Kurapati     case PMBUS_FAN_COMMAND_1:
18162365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
18262365482SMaheswara Kurapati             pmbus_send16(pmdev, pmdev->pages[pmdev->page].fan_command_1);
18362365482SMaheswara Kurapati         }
18462365482SMaheswara Kurapati         break;
18562365482SMaheswara Kurapati 
18662365482SMaheswara Kurapati     case PMBUS_READ_FAN_SPEED_1:
18762365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
18862365482SMaheswara Kurapati             pmbus_send16(pmdev, pmdev->pages[pmdev->page].read_fan_speed_1);
18962365482SMaheswara Kurapati         }
19062365482SMaheswara Kurapati         break;
19162365482SMaheswara Kurapati 
19262365482SMaheswara Kurapati     case PMBUS_STATUS_FANS_1_2:
19362365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
19462365482SMaheswara Kurapati             pmbus_send16(pmdev, pmdev->pages[pmdev->page].status_fans_1_2);
19562365482SMaheswara Kurapati         }
19662365482SMaheswara Kurapati         break;
19762365482SMaheswara Kurapati 
19862365482SMaheswara Kurapati     case PMBUS_MFR_REVISION:
19962365482SMaheswara Kurapati         pmbus_send16(pmdev, MAX31785_DEFAULT_MFR_REVISION);
20062365482SMaheswara Kurapati         break;
20162365482SMaheswara Kurapati 
20262365482SMaheswara Kurapati     case PMBUS_MFR_ID:
20362365482SMaheswara Kurapati         pmbus_send8(pmdev, 0x4d); /* Maxim */
20462365482SMaheswara Kurapati         break;
20562365482SMaheswara Kurapati 
20662365482SMaheswara Kurapati     case PMBUS_MFR_MODEL:
20762365482SMaheswara Kurapati         pmbus_send8(pmdev, 0x53);
20862365482SMaheswara Kurapati         break;
20962365482SMaheswara Kurapati 
21062365482SMaheswara Kurapati     case PMBUS_MFR_LOCATION:
21162365482SMaheswara Kurapati         pmbus_send64(pmdev, s->mfr_location);
21262365482SMaheswara Kurapati         break;
21362365482SMaheswara Kurapati 
21462365482SMaheswara Kurapati     case PMBUS_MFR_DATE:
21562365482SMaheswara Kurapati         pmbus_send64(pmdev, s->mfr_date);
21662365482SMaheswara Kurapati         break;
21762365482SMaheswara Kurapati 
21862365482SMaheswara Kurapati     case PMBUS_MFR_SERIAL:
21962365482SMaheswara Kurapati         pmbus_send64(pmdev, s->mfr_serial);
22062365482SMaheswara Kurapati         break;
22162365482SMaheswara Kurapati 
22262365482SMaheswara Kurapati     case MAX31785_MFR_MODE:
22362365482SMaheswara Kurapati         pmbus_send16(pmdev, s->mfr_mode[pmdev->page]);
22462365482SMaheswara Kurapati         break;
22562365482SMaheswara Kurapati 
22662365482SMaheswara Kurapati     case MAX31785_MFR_VOUT_PEAK:
22762365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
22862365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
22962365482SMaheswara Kurapati             pmbus_send16(pmdev, s->vout_peak[pmdev->page]);
23062365482SMaheswara Kurapati         }
23162365482SMaheswara Kurapati         break;
23262365482SMaheswara Kurapati 
23362365482SMaheswara Kurapati     case MAX31785_MFR_TEMPERATURE_PEAK:
23462365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) &&
23562365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) {
23662365482SMaheswara Kurapati             pmbus_send16(pmdev, s->temperature_peak[pmdev->page]);
23762365482SMaheswara Kurapati         }
23862365482SMaheswara Kurapati         break;
23962365482SMaheswara Kurapati 
24062365482SMaheswara Kurapati     case MAX31785_MFR_VOUT_MIN:
24162365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
24262365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
24362365482SMaheswara Kurapati             pmbus_send16(pmdev, s->vout_min[pmdev->page]);
24462365482SMaheswara Kurapati         }
24562365482SMaheswara Kurapati         break;
24662365482SMaheswara Kurapati 
24762365482SMaheswara Kurapati     case MAX31785_MFR_FAULT_RESPONSE:
24862365482SMaheswara Kurapati         pmbus_send8(pmdev, s->fault_response[pmdev->page]);
24962365482SMaheswara Kurapati         break;
25062365482SMaheswara Kurapati 
25162365482SMaheswara Kurapati     case MAX31785_MFR_TIME_COUNT: /* R/W 32 */
25262365482SMaheswara Kurapati         pmbus_send32(pmdev, s->time_count[pmdev->page]);
25362365482SMaheswara Kurapati         break;
25462365482SMaheswara Kurapati 
25562365482SMaheswara Kurapati     case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */
25662365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) &&
25762365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) {
25862365482SMaheswara Kurapati             pmbus_send16(pmdev, s->temp_sensor_config[pmdev->page]);
25962365482SMaheswara Kurapati         }
26062365482SMaheswara Kurapati         break;
26162365482SMaheswara Kurapati 
26262365482SMaheswara Kurapati     case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */
26362365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
26462365482SMaheswara Kurapati             pmbus_send16(pmdev, s->fan_config[pmdev->page]);
26562365482SMaheswara Kurapati         }
26662365482SMaheswara Kurapati         break;
26762365482SMaheswara Kurapati 
26862365482SMaheswara Kurapati     case MAX31785_MFR_READ_FAN_PWM: /* R/W 16 */
26962365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
27062365482SMaheswara Kurapati             pmbus_send16(pmdev, s->read_fan_pwm[pmdev->page]);
27162365482SMaheswara Kurapati         }
27262365482SMaheswara Kurapati         break;
27362365482SMaheswara Kurapati 
27462365482SMaheswara Kurapati     case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */
27562365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
27662365482SMaheswara Kurapati             pmbus_send16(pmdev, s->fan_fault_limit[pmdev->page]);
27762365482SMaheswara Kurapati         }
27862365482SMaheswara Kurapati         break;
27962365482SMaheswara Kurapati 
28062365482SMaheswara Kurapati     case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */
28162365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
28262365482SMaheswara Kurapati             pmbus_send16(pmdev, s->fan_warn_limit[pmdev->page]);
28362365482SMaheswara Kurapati         }
28462365482SMaheswara Kurapati         break;
28562365482SMaheswara Kurapati 
28662365482SMaheswara Kurapati     case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */
28762365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
28862365482SMaheswara Kurapati             pmbus_send16(pmdev, s->fan_run_time[pmdev->page]);
28962365482SMaheswara Kurapati         }
29062365482SMaheswara Kurapati         break;
29162365482SMaheswara Kurapati 
29262365482SMaheswara Kurapati     case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */
29362365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
29462365482SMaheswara Kurapati             pmbus_send16(pmdev, s->fan_pwm_avg[pmdev->page]);
29562365482SMaheswara Kurapati         }
29662365482SMaheswara Kurapati         break;
29762365482SMaheswara Kurapati 
29862365482SMaheswara Kurapati     case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */
29962365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
30062365482SMaheswara Kurapati             pmbus_send64(pmdev, s->fan_pwm2rpm[pmdev->page]);
30162365482SMaheswara Kurapati         }
30262365482SMaheswara Kurapati         break;
30362365482SMaheswara Kurapati 
30462365482SMaheswara Kurapati     default:
30562365482SMaheswara Kurapati         qemu_log_mask(LOG_GUEST_ERROR,
30662365482SMaheswara Kurapati         "%s: reading from unsupported register: 0x%02x\n",
30762365482SMaheswara Kurapati         __func__, pmdev->code);
30862365482SMaheswara Kurapati         break;
30962365482SMaheswara Kurapati     }
31062365482SMaheswara Kurapati 
31162365482SMaheswara Kurapati     return 0xFF;
31262365482SMaheswara Kurapati }
31362365482SMaheswara Kurapati 
max31785_write_data(PMBusDevice * pmdev,const uint8_t * buf,uint8_t len)31462365482SMaheswara Kurapati static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf,
31562365482SMaheswara Kurapati                                uint8_t len)
31662365482SMaheswara Kurapati {
31762365482SMaheswara Kurapati     MAX31785State *s = MAX31785(pmdev);
31862365482SMaheswara Kurapati     if (len == 0) {
31962365482SMaheswara Kurapati         qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
32062365482SMaheswara Kurapati         return -1;
32162365482SMaheswara Kurapati     }
32262365482SMaheswara Kurapati 
32362365482SMaheswara Kurapati     pmdev->code = buf[0]; /* PMBus command code */
32462365482SMaheswara Kurapati 
32562365482SMaheswara Kurapati     if (len == 1) {
32662365482SMaheswara Kurapati         return 0;
32762365482SMaheswara Kurapati     }
32862365482SMaheswara Kurapati 
32962365482SMaheswara Kurapati     /* Exclude command code from buffer */
33062365482SMaheswara Kurapati     buf++;
33162365482SMaheswara Kurapati     len--;
33262365482SMaheswara Kurapati 
33362365482SMaheswara Kurapati     switch (pmdev->code) {
33462365482SMaheswara Kurapati 
33562365482SMaheswara Kurapati     case PMBUS_FAN_CONFIG_1_2:
33662365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
33762365482SMaheswara Kurapati             pmdev->pages[pmdev->page].fan_config_1_2 = pmbus_receive8(pmdev);
33862365482SMaheswara Kurapati         }
33962365482SMaheswara Kurapati         break;
34062365482SMaheswara Kurapati 
34162365482SMaheswara Kurapati     case PMBUS_FAN_COMMAND_1:
34262365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
34362365482SMaheswara Kurapati             pmdev->pages[pmdev->page].fan_command_1 = pmbus_receive16(pmdev);
34462365482SMaheswara Kurapati             pmdev->pages[pmdev->page].read_fan_speed_1 =
34562365482SMaheswara Kurapati                 ((MAX31785_DEFAULT_FAN_SPEED / MAX31785_DEFAULT_FAN_MAX_PWM) *
34662365482SMaheswara Kurapati                 pmdev->pages[pmdev->page].fan_command_1);
34762365482SMaheswara Kurapati         }
34862365482SMaheswara Kurapati         break;
34962365482SMaheswara Kurapati 
35062365482SMaheswara Kurapati     case PMBUS_MFR_LOCATION: /* R/W 64 */
35162365482SMaheswara Kurapati         s->mfr_location = pmbus_receive64(pmdev);
35262365482SMaheswara Kurapati         break;
35362365482SMaheswara Kurapati 
35462365482SMaheswara Kurapati     case PMBUS_MFR_DATE: /* R/W 64 */
35562365482SMaheswara Kurapati         s->mfr_date = pmbus_receive64(pmdev);
35662365482SMaheswara Kurapati         break;
35762365482SMaheswara Kurapati 
35862365482SMaheswara Kurapati     case PMBUS_MFR_SERIAL: /* R/W 64 */
35962365482SMaheswara Kurapati         s->mfr_serial = pmbus_receive64(pmdev);
36062365482SMaheswara Kurapati         break;
36162365482SMaheswara Kurapati 
36262365482SMaheswara Kurapati     case MAX31785_MFR_MODE: /* R/W word */
36362365482SMaheswara Kurapati         s->mfr_mode[pmdev->page] = pmbus_receive16(pmdev);
36462365482SMaheswara Kurapati         break;
36562365482SMaheswara Kurapati 
36662365482SMaheswara Kurapati     case MAX31785_MFR_VOUT_PEAK: /* R/W word */
36762365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
36862365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
36962365482SMaheswara Kurapati             s->vout_peak[pmdev->page] = pmbus_receive16(pmdev);
37062365482SMaheswara Kurapati         }
37162365482SMaheswara Kurapati         break;
37262365482SMaheswara Kurapati 
37362365482SMaheswara Kurapati     case MAX31785_MFR_TEMPERATURE_PEAK: /* R/W word */
37462365482SMaheswara Kurapati         if ((pmdev->page >= 6) && (pmdev->page <= 16)) {
37562365482SMaheswara Kurapati             s->temperature_peak[pmdev->page] = pmbus_receive16(pmdev);
37662365482SMaheswara Kurapati         }
37762365482SMaheswara Kurapati         break;
37862365482SMaheswara Kurapati 
37962365482SMaheswara Kurapati     case MAX31785_MFR_VOUT_MIN: /* R/W word */
38062365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
38162365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
38262365482SMaheswara Kurapati             s->vout_min[pmdev->page] = pmbus_receive16(pmdev);
38362365482SMaheswara Kurapati         }
38462365482SMaheswara Kurapati         break;
38562365482SMaheswara Kurapati 
38662365482SMaheswara Kurapati     case MAX31785_MFR_FAULT_RESPONSE: /* R/W 8 */
38762365482SMaheswara Kurapati         s->fault_response[pmdev->page] = pmbus_receive8(pmdev);
38862365482SMaheswara Kurapati         break;
38962365482SMaheswara Kurapati 
39062365482SMaheswara Kurapati     case MAX31785_MFR_TIME_COUNT: /* R/W 32 */
39162365482SMaheswara Kurapati         s->time_count[pmdev->page] = pmbus_receive32(pmdev);
39262365482SMaheswara Kurapati         break;
39362365482SMaheswara Kurapati 
39462365482SMaheswara Kurapati     case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */
39562365482SMaheswara Kurapati         if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) &&
39662365482SMaheswara Kurapati             (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) {
39762365482SMaheswara Kurapati             s->temp_sensor_config[pmdev->page] = pmbus_receive16(pmdev);
39862365482SMaheswara Kurapati         }
39962365482SMaheswara Kurapati         break;
40062365482SMaheswara Kurapati 
40162365482SMaheswara Kurapati     case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */
40262365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
40362365482SMaheswara Kurapati             s->fan_config[pmdev->page] = pmbus_receive16(pmdev);
40462365482SMaheswara Kurapati         }
40562365482SMaheswara Kurapati         break;
40662365482SMaheswara Kurapati 
40762365482SMaheswara Kurapati     case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */
40862365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
40962365482SMaheswara Kurapati             s->fan_fault_limit[pmdev->page] = pmbus_receive16(pmdev);
41062365482SMaheswara Kurapati         }
41162365482SMaheswara Kurapati         break;
41262365482SMaheswara Kurapati 
41362365482SMaheswara Kurapati     case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */
41462365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
41562365482SMaheswara Kurapati             s->fan_warn_limit[pmdev->page] = pmbus_receive16(pmdev);
41662365482SMaheswara Kurapati         }
41762365482SMaheswara Kurapati         break;
41862365482SMaheswara Kurapati 
41962365482SMaheswara Kurapati     case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */
42062365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
42162365482SMaheswara Kurapati             s->fan_run_time[pmdev->page] = pmbus_receive16(pmdev);
42262365482SMaheswara Kurapati         }
42362365482SMaheswara Kurapati         break;
42462365482SMaheswara Kurapati 
42562365482SMaheswara Kurapati     case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */
42662365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
42762365482SMaheswara Kurapati             s->fan_pwm_avg[pmdev->page] = pmbus_receive16(pmdev);
42862365482SMaheswara Kurapati         }
42962365482SMaheswara Kurapati         break;
43062365482SMaheswara Kurapati 
43162365482SMaheswara Kurapati     case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */
43262365482SMaheswara Kurapati         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
43362365482SMaheswara Kurapati             s->fan_pwm2rpm[pmdev->page] = pmbus_receive64(pmdev);
43462365482SMaheswara Kurapati         }
43562365482SMaheswara Kurapati         break;
43662365482SMaheswara Kurapati 
43762365482SMaheswara Kurapati     default:
43862365482SMaheswara Kurapati         qemu_log_mask(LOG_GUEST_ERROR,
43962365482SMaheswara Kurapati                       "%s: writing to unsupported register: 0x%02x\n",
44062365482SMaheswara Kurapati                       __func__, pmdev->code);
44162365482SMaheswara Kurapati         break;
44262365482SMaheswara Kurapati     }
44362365482SMaheswara Kurapati 
44462365482SMaheswara Kurapati     return 0;
44562365482SMaheswara Kurapati }
44662365482SMaheswara Kurapati 
max31785_exit_reset(Object * obj,ResetType type)447ad80e367SPeter Maydell static void max31785_exit_reset(Object *obj, ResetType type)
44862365482SMaheswara Kurapati {
44962365482SMaheswara Kurapati     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
45062365482SMaheswara Kurapati     MAX31785State *s = MAX31785(obj);
45162365482SMaheswara Kurapati 
45262365482SMaheswara Kurapati     pmdev->capability = MAX31785_DEFAULT_CAPABILITY;
45362365482SMaheswara Kurapati 
45462365482SMaheswara Kurapati     for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) {
45562365482SMaheswara Kurapati         pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE;
45662365482SMaheswara Kurapati         pmdev->pages[i].fan_command_1 = MAX31785_DEFAULT_FAN_COMMAND_1;
45762365482SMaheswara Kurapati         pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION;
45862365482SMaheswara Kurapati         pmdev->pages[i].fan_config_1_2 = MAX31785_DEFAULT_FAN_CONFIG_1_2(0);
45962365482SMaheswara Kurapati         pmdev->pages[i].read_fan_speed_1 = MAX31785_DEFAULT_FAN_SPEED;
46062365482SMaheswara Kurapati         pmdev->pages[i].status_fans_1_2 = MAX31785_DEFAULT_FAN_STATUS;
46162365482SMaheswara Kurapati     }
46262365482SMaheswara Kurapati 
46362365482SMaheswara Kurapati     for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) {
46462365482SMaheswara Kurapati         pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE;
46562365482SMaheswara Kurapati         pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION;
46662365482SMaheswara Kurapati         pmdev->pages[i].ot_fault_limit = MAX31785_DEFAULT_OT_FAULT_LIMIT;
46762365482SMaheswara Kurapati         pmdev->pages[i].ot_warn_limit = MAX31785_DEFAULT_OT_WARN_LIMIT;
46862365482SMaheswara Kurapati     }
46962365482SMaheswara Kurapati 
47062365482SMaheswara Kurapati     for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE;
47162365482SMaheswara Kurapati          i <= MAX31785_MAX_ADC_VOLTAGE_PAGE;
47262365482SMaheswara Kurapati          i++) {
47362365482SMaheswara Kurapati         pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE;
47462365482SMaheswara Kurapati         pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION;
47562365482SMaheswara Kurapati         pmdev->pages[i].vout_scale_monitor =
47662365482SMaheswara Kurapati             MAX31785_DEFAULT_VOUT_SCALE_MONITOR;
47762365482SMaheswara Kurapati         pmdev->pages[i].vout_ov_fault_limit = MAX31785_DEFAULT_OV_FAULT_LIMIT;
47862365482SMaheswara Kurapati         pmdev->pages[i].vout_ov_warn_limit = MAX31785_DEFAULT_OV_WARN_LIMIT;
47962365482SMaheswara Kurapati     }
48062365482SMaheswara Kurapati 
48162365482SMaheswara Kurapati     s->mfr_location = MAX31785_DEFAULT_TEXT;
48262365482SMaheswara Kurapati     s->mfr_date = MAX31785_DEFAULT_TEXT;
48362365482SMaheswara Kurapati     s->mfr_serial = MAX31785_DEFAULT_TEXT;
48462365482SMaheswara Kurapati }
48562365482SMaheswara Kurapati 
48662365482SMaheswara Kurapati static const VMStateDescription vmstate_max31785 = {
48762365482SMaheswara Kurapati     .name = TYPE_MAX31785,
48862365482SMaheswara Kurapati     .version_id = 0,
48962365482SMaheswara Kurapati     .minimum_version_id = 0,
490af10fff2SRichard Henderson     .fields = (const VMStateField[]){
49162365482SMaheswara Kurapati         VMSTATE_PMBUS_DEVICE(parent, MAX31785State),
49262365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(mfr_mode, MAX31785State,
49362365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
49462365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(vout_peak, MAX31785State,
49562365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
49662365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(temperature_peak, MAX31785State,
49762365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
49862365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(vout_min, MAX31785State,
49962365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
50062365482SMaheswara Kurapati         VMSTATE_UINT8_ARRAY(fault_response, MAX31785State,
50162365482SMaheswara Kurapati                             MAX31785_TOTAL_NUM_PAGES),
50262365482SMaheswara Kurapati         VMSTATE_UINT32_ARRAY(time_count, MAX31785State,
50362365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
50462365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX31785State,
50562365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
50662365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(fan_config, MAX31785State,
50762365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
50862365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(read_fan_pwm, MAX31785State,
50962365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
51062365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(fan_fault_limit, MAX31785State,
51162365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
51262365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(fan_warn_limit, MAX31785State,
51362365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
51462365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(fan_run_time, MAX31785State,
51562365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
51662365482SMaheswara Kurapati         VMSTATE_UINT16_ARRAY(fan_pwm_avg, MAX31785State,
51762365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
51862365482SMaheswara Kurapati         VMSTATE_UINT64_ARRAY(fan_pwm2rpm, MAX31785State,
51962365482SMaheswara Kurapati                              MAX31785_TOTAL_NUM_PAGES),
52062365482SMaheswara Kurapati         VMSTATE_UINT64(mfr_location, MAX31785State),
52162365482SMaheswara Kurapati         VMSTATE_UINT64(mfr_date, MAX31785State),
52262365482SMaheswara Kurapati         VMSTATE_UINT64(mfr_serial, MAX31785State),
52362365482SMaheswara Kurapati         VMSTATE_END_OF_LIST()
52462365482SMaheswara Kurapati     }
52562365482SMaheswara Kurapati };
52662365482SMaheswara Kurapati 
max31785_init(Object * obj)52762365482SMaheswara Kurapati static void max31785_init(Object *obj)
52862365482SMaheswara Kurapati {
52962365482SMaheswara Kurapati     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
53062365482SMaheswara Kurapati 
53162365482SMaheswara Kurapati     for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) {
53262365482SMaheswara Kurapati         pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE);
53362365482SMaheswara Kurapati     }
53462365482SMaheswara Kurapati 
53562365482SMaheswara Kurapati     for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) {
53662365482SMaheswara Kurapati         pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_TEMPERATURE);
53762365482SMaheswara Kurapati     }
53862365482SMaheswara Kurapati 
53962365482SMaheswara Kurapati     for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE;
54062365482SMaheswara Kurapati         i <= MAX31785_MAX_ADC_VOLTAGE_PAGE;
54162365482SMaheswara Kurapati         i++) {
54262365482SMaheswara Kurapati         pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_VOUT |
54362365482SMaheswara Kurapati                                     PB_HAS_VOUT_RATING);
54462365482SMaheswara Kurapati     }
54562365482SMaheswara Kurapati }
54662365482SMaheswara Kurapati 
max31785_class_init(ObjectClass * klass,const void * data)547*12d1a768SPhilippe Mathieu-Daudé static void max31785_class_init(ObjectClass *klass, const void *data)
54862365482SMaheswara Kurapati {
54962365482SMaheswara Kurapati     ResettableClass *rc = RESETTABLE_CLASS(klass);
55062365482SMaheswara Kurapati     DeviceClass *dc = DEVICE_CLASS(klass);
55162365482SMaheswara Kurapati     PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
55262365482SMaheswara Kurapati     dc->desc = "Maxim MAX31785 6-Channel Fan Controller";
55362365482SMaheswara Kurapati     dc->vmsd = &vmstate_max31785;
55462365482SMaheswara Kurapati     k->write_data = max31785_write_data;
55562365482SMaheswara Kurapati     k->receive_byte = max31785_read_byte;
55662365482SMaheswara Kurapati     k->device_num_pages = MAX31785_TOTAL_NUM_PAGES;
55762365482SMaheswara Kurapati     rc->phases.exit = max31785_exit_reset;
55862365482SMaheswara Kurapati }
55962365482SMaheswara Kurapati 
56062365482SMaheswara Kurapati static const TypeInfo max31785_info = {
56162365482SMaheswara Kurapati     .name = TYPE_MAX31785,
56262365482SMaheswara Kurapati     .parent = TYPE_PMBUS_DEVICE,
56362365482SMaheswara Kurapati     .instance_size = sizeof(MAX31785State),
56462365482SMaheswara Kurapati     .instance_init = max31785_init,
56562365482SMaheswara Kurapati     .class_init = max31785_class_init,
56662365482SMaheswara Kurapati };
56762365482SMaheswara Kurapati 
max31785_register_types(void)56862365482SMaheswara Kurapati static void max31785_register_types(void)
56962365482SMaheswara Kurapati {
57062365482SMaheswara Kurapati     type_register_static(&max31785_info);
57162365482SMaheswara Kurapati }
57262365482SMaheswara Kurapati 
57362365482SMaheswara Kurapati type_init(max31785_register_types)
574