 af10fff2a3
			
		
	
	
		af10fff2a3
		
	
	
	
	
		
			
			Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20231221031652.119827-54-richard.henderson@linaro.org>
		
			
				
	
	
		
			574 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			574 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * Maxim MAX31785 PMBus 6-Channel Fan Controller
 | |
|  *
 | |
|  * Datasheet:
 | |
|  * https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
 | |
|  *
 | |
|  * Copyright(c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "hw/i2c/pmbus_device.h"
 | |
| #include "hw/irq.h"
 | |
| #include "migration/vmstate.h"
 | |
| #include "qapi/error.h"
 | |
| #include "qapi/visitor.h"
 | |
| #include "qemu/log.h"
 | |
| #include "qemu/module.h"
 | |
| 
 | |
| #define TYPE_MAX31785 "max31785"
 | |
| #define MAX31785(obj) OBJECT_CHECK(MAX31785State, (obj), TYPE_MAX31785)
 | |
| 
 | |
| /* MAX31785 mfr specific PMBus commands */
 | |
| #define MAX31785_MFR_MODE               0xD1
 | |
| #define MAX31785_MFR_PSEN_CONFIG        0xD2
 | |
| #define MAX31785_MFR_VOUT_PEAK          0xD4
 | |
| #define MAX31785_MFR_TEMPERATURE_PEAK   0xD6
 | |
| #define MAX31785_MFR_VOUT_MIN           0xD7
 | |
| #define MAX31785_MFR_FAULT_RESPONSE     0xD9
 | |
| #define MAX31785_MFR_NV_FAULT_LOG       0xDC
 | |
| #define MAX31785_MFR_TIME_COUNT         0xDD
 | |
| #define MAX31785_MFR_TEMP_SENSOR_CONFIG 0xF0
 | |
| #define MAX31785_MFR_FAN_CONFIG         0xF1
 | |
| #define MAX31785_MFR_FAN_LUT            0xF2
 | |
| #define MAX31785_MFR_READ_FAN_PWM       0xF3
 | |
| #define MAX31785_MFR_FAN_FAULT_LIMIT    0xF5
 | |
| #define MAX31785_MFR_FAN_WARN_LIMIT     0xF6
 | |
| #define MAX31785_MFR_FAN_RUN_TIME       0xF7
 | |
| #define MAX31785_MFR_FAN_PWM_AVG        0xF8
 | |
| #define MAX31785_MFR_FAN_PWM2RPM        0xF9
 | |
| 
 | |
| /* defaults as per the data sheet */
 | |
| #define MAX31785_DEFAULT_CAPABILITY            0x10
 | |
| #define MAX31785_DEFAULT_VOUT_MODE             0x40
 | |
| #define MAX31785_DEFAULT_VOUT_SCALE_MONITOR    0x7FFF
 | |
| #define MAX31785_DEFAULT_FAN_COMMAND_1         0x7FFF
 | |
| #define MAX31785_DEFAULT_OV_FAULT_LIMIT        0x7FFF
 | |
| #define MAX31785_DEFAULT_OV_WARN_LIMIT         0x7FFF
 | |
| #define MAX31785_DEFAULT_OT_FAULT_LIMIT        0x7FFF
 | |
| #define MAX31785_DEFAULT_OT_WARN_LIMIT         0x7FFF
 | |
| #define MAX31785_DEFAULT_PMBUS_REVISION        0x11
 | |
| #define MAX31785_DEFAULT_MFR_ID                0x4D
 | |
| #define MAX31785_DEFAULT_MFR_MODEL             0x53
 | |
| #define MAX31785_DEFAULT_MFR_REVISION          0x3030
 | |
| #define MAX31785A_DEFAULT_MFR_REVISION         0x3040
 | |
| #define MAX31785B_DEFAULT_MFR_REVISION         0x3061
 | |
| #define MAX31785B_DEFAULT_MFR_TEMPERATURE_PEAK 0x8000
 | |
| #define MAX31785B_DEFAULT_MFR_VOUT_MIN         0x7FFF
 | |
| #define MAX31785_DEFAULT_TEXT                  0x3130313031303130
 | |
| 
 | |
| /* MAX31785 pages */
 | |
| #define MAX31785_TOTAL_NUM_PAGES      23
 | |
| #define MAX31785_FAN_PAGES            6
 | |
| #define MAX31785_MIN_FAN_PAGE         0
 | |
| #define MAX31785_MAX_FAN_PAGE         5
 | |
| #define MAX31785_MIN_TEMP_PAGE        6
 | |
| #define MAX31785_MAX_TEMP_PAGE        16
 | |
| #define MAX31785_MIN_ADC_VOLTAGE_PAGE 17
 | |
| #define MAX31785_MAX_ADC_VOLTAGE_PAGE 22
 | |
| 
 | |
| /* FAN_CONFIG_1_2 */
 | |
| #define MAX31785_MFR_FAN_CONFIG                0xF1
 | |
| #define MAX31785_FAN_CONFIG_ENABLE             BIT(7)
 | |
| #define MAX31785_FAN_CONFIG_RPM_PWM            BIT(6)
 | |
| #define MAX31785_FAN_CONFIG_PULSE(pulse)       (pulse << 4)
 | |
| #define MAX31785_DEFAULT_FAN_CONFIG_1_2(pulse)                                 \
 | |
|     (MAX31785_FAN_CONFIG_ENABLE | MAX31785_FAN_CONFIG_PULSE(pulse))
 | |
| #define MAX31785_DEFAULT_MFR_FAN_CONFIG        0x0000
 | |
| 
 | |
| /* fan speed in RPM */
 | |
| #define MAX31785_DEFAULT_FAN_SPEED   0x7fff
 | |
| #define MAX31785_DEFAULT_FAN_STATUS  0x00
 | |
| 
 | |
| #define MAX31785_DEFAULT_FAN_MAX_PWM 0x2710
 | |
| 
 | |
| /*
 | |
|  * MAX31785State:
 | |
|  * @code: The command code received
 | |
|  * @page: Each page corresponds to a device monitored by the Max 31785
 | |
|  * The page register determines the available commands depending on device
 | |
|  * _____________________________________________________________________________
 | |
|  * |   0   |  Fan Connected to PWM0                                            |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   1   |  Fan Connected to PWM1                                            |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   2   |  Fan Connected to PWM2                                            |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   3   |  Fan Connected to PWM3                                            |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   4   |  Fan Connected to PWM4                                            |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   5   |  Fan Connected to PWM5                                            |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   6   |  Remote Thermal Diode Connected to ADC 0                          |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   7   |  Remote Thermal Diode Connected to ADC 1                          |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   8   |  Remote Thermal Diode Connected to ADC 2                          |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |   9   |  Remote Thermal Diode Connected to ADC 3                          |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  10   |  Remote Thermal Diode Connected to ADC 4                          |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  11   |  Remote Thermal Diode Connected to ADC 5                          |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  12   |  Internal Temperature Sensor                                      |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  13   |  Remote I2C Temperature Sensor with Address 0                     |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  14   |  Remote I2C Temperature Sensor with Address 1                     |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  15   |  Remote I2C Temperature Sensor with Address 2                     |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  16   |  Remote I2C Temperature Sensor with Address 3                     |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  17   |  Remote I2C Temperature Sensor with Address 4                     |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  17   |  Remote Voltage Connected to ADC0                                 |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  18   |  Remote Voltage Connected to ADC1                                 |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  19   |  Remote Voltage Connected to ADC2                                 |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  20   |  Remote Voltage Connected to ADC3                                 |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  21   |  Remote Voltage Connected to ADC4                                 |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  22   |  Remote Voltage Connected to ADC5                                 |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |23-254 |  Reserved                                                         |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  * |  255  |  Applies to all pages                                             |
 | |
|  * |_______|___________________________________________________________________|
 | |
|  */
 | |
| 
 | |
| /* Place holder to save the max31785 mfr specific registers */
 | |
| typedef struct MAX31785State {
 | |
|     PMBusDevice parent;
 | |
|     uint16_t mfr_mode[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t vout_peak[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t temperature_peak[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t vout_min[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint8_t  fault_response[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint32_t time_count[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t temp_sensor_config[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t fan_config[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t read_fan_pwm[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t fan_fault_limit[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t fan_warn_limit[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t fan_run_time[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint16_t fan_pwm_avg[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint64_t fan_pwm2rpm[MAX31785_TOTAL_NUM_PAGES];
 | |
|     uint64_t mfr_location;
 | |
|     uint64_t mfr_date;
 | |
|     uint64_t mfr_serial;
 | |
|     uint16_t mfr_revision;
 | |
| } MAX31785State;
 | |
| 
 | |
| static uint8_t max31785_read_byte(PMBusDevice *pmdev)
 | |
| {
 | |
|     MAX31785State *s = MAX31785(pmdev);
 | |
|     switch (pmdev->code) {
 | |
| 
 | |
|     case PMBUS_FAN_CONFIG_1_2:
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send8(pmdev, pmdev->pages[pmdev->page].fan_config_1_2);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_FAN_COMMAND_1:
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, pmdev->pages[pmdev->page].fan_command_1);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_READ_FAN_SPEED_1:
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, pmdev->pages[pmdev->page].read_fan_speed_1);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_STATUS_FANS_1_2:
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, pmdev->pages[pmdev->page].status_fans_1_2);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_REVISION:
 | |
|         pmbus_send16(pmdev, MAX31785_DEFAULT_MFR_REVISION);
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_ID:
 | |
|         pmbus_send8(pmdev, 0x4d); /* Maxim */
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_MODEL:
 | |
|         pmbus_send8(pmdev, 0x53);
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_LOCATION:
 | |
|         pmbus_send64(pmdev, s->mfr_location);
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_DATE:
 | |
|         pmbus_send64(pmdev, s->mfr_date);
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_SERIAL:
 | |
|         pmbus_send64(pmdev, s->mfr_serial);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_MODE:
 | |
|         pmbus_send16(pmdev, s->mfr_mode[pmdev->page]);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_VOUT_PEAK:
 | |
|         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
 | |
|             pmbus_send16(pmdev, s->vout_peak[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_TEMPERATURE_PEAK:
 | |
|         if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) {
 | |
|             pmbus_send16(pmdev, s->temperature_peak[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_VOUT_MIN:
 | |
|         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
 | |
|             pmbus_send16(pmdev, s->vout_min[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAULT_RESPONSE:
 | |
|         pmbus_send8(pmdev, s->fault_response[pmdev->page]);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_TIME_COUNT: /* R/W 32 */
 | |
|         pmbus_send32(pmdev, s->time_count[pmdev->page]);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */
 | |
|         if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) {
 | |
|             pmbus_send16(pmdev, s->temp_sensor_config[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, s->fan_config[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_READ_FAN_PWM: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, s->read_fan_pwm[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, s->fan_fault_limit[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, s->fan_warn_limit[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, s->fan_run_time[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send16(pmdev, s->fan_pwm_avg[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmbus_send64(pmdev, s->fan_pwm2rpm[pmdev->page]);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         qemu_log_mask(LOG_GUEST_ERROR,
 | |
|         "%s: reading from unsupported register: 0x%02x\n",
 | |
|         __func__, pmdev->code);
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     return 0xFF;
 | |
| }
 | |
| 
 | |
| static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf,
 | |
|                                uint8_t len)
 | |
| {
 | |
|     MAX31785State *s = MAX31785(pmdev);
 | |
|     if (len == 0) {
 | |
|         qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     pmdev->code = buf[0]; /* PMBus command code */
 | |
| 
 | |
|     if (len == 1) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* Exclude command code from buffer */
 | |
|     buf++;
 | |
|     len--;
 | |
| 
 | |
|     switch (pmdev->code) {
 | |
| 
 | |
|     case PMBUS_FAN_CONFIG_1_2:
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmdev->pages[pmdev->page].fan_config_1_2 = pmbus_receive8(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_FAN_COMMAND_1:
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             pmdev->pages[pmdev->page].fan_command_1 = pmbus_receive16(pmdev);
 | |
|             pmdev->pages[pmdev->page].read_fan_speed_1 =
 | |
|                 ((MAX31785_DEFAULT_FAN_SPEED / MAX31785_DEFAULT_FAN_MAX_PWM) *
 | |
|                 pmdev->pages[pmdev->page].fan_command_1);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_LOCATION: /* R/W 64 */
 | |
|         s->mfr_location = pmbus_receive64(pmdev);
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_DATE: /* R/W 64 */
 | |
|         s->mfr_date = pmbus_receive64(pmdev);
 | |
|         break;
 | |
| 
 | |
|     case PMBUS_MFR_SERIAL: /* R/W 64 */
 | |
|         s->mfr_serial = pmbus_receive64(pmdev);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_MODE: /* R/W word */
 | |
|         s->mfr_mode[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_VOUT_PEAK: /* R/W word */
 | |
|         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
 | |
|             s->vout_peak[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_TEMPERATURE_PEAK: /* R/W word */
 | |
|         if ((pmdev->page >= 6) && (pmdev->page <= 16)) {
 | |
|             s->temperature_peak[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_VOUT_MIN: /* R/W word */
 | |
|         if ((pmdev->page >= MAX31785_MIN_ADC_VOLTAGE_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_ADC_VOLTAGE_PAGE)) {
 | |
|             s->vout_min[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAULT_RESPONSE: /* R/W 8 */
 | |
|         s->fault_response[pmdev->page] = pmbus_receive8(pmdev);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_TIME_COUNT: /* R/W 32 */
 | |
|         s->time_count[pmdev->page] = pmbus_receive32(pmdev);
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_TEMP_SENSOR_CONFIG: /* R/W 16 */
 | |
|         if ((pmdev->page >= MAX31785_MIN_TEMP_PAGE) &&
 | |
|             (pmdev->page <= MAX31785_MAX_TEMP_PAGE)) {
 | |
|             s->temp_sensor_config[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_CONFIG: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             s->fan_config[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_FAULT_LIMIT: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             s->fan_fault_limit[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_WARN_LIMIT: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             s->fan_warn_limit[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_RUN_TIME: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             s->fan_run_time[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_PWM_AVG: /* R/W 16 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             s->fan_pwm_avg[pmdev->page] = pmbus_receive16(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case MAX31785_MFR_FAN_PWM2RPM: /* R/W 64 */
 | |
|         if (pmdev->page <= MAX31785_MAX_FAN_PAGE) {
 | |
|             s->fan_pwm2rpm[pmdev->page] = pmbus_receive64(pmdev);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         qemu_log_mask(LOG_GUEST_ERROR,
 | |
|                       "%s: writing to unsupported register: 0x%02x\n",
 | |
|                       __func__, pmdev->code);
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void max31785_exit_reset(Object *obj)
 | |
| {
 | |
|     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 | |
|     MAX31785State *s = MAX31785(obj);
 | |
| 
 | |
|     pmdev->capability = MAX31785_DEFAULT_CAPABILITY;
 | |
| 
 | |
|     for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) {
 | |
|         pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE;
 | |
|         pmdev->pages[i].fan_command_1 = MAX31785_DEFAULT_FAN_COMMAND_1;
 | |
|         pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION;
 | |
|         pmdev->pages[i].fan_config_1_2 = MAX31785_DEFAULT_FAN_CONFIG_1_2(0);
 | |
|         pmdev->pages[i].read_fan_speed_1 = MAX31785_DEFAULT_FAN_SPEED;
 | |
|         pmdev->pages[i].status_fans_1_2 = MAX31785_DEFAULT_FAN_STATUS;
 | |
|     }
 | |
| 
 | |
|     for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) {
 | |
|         pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE;
 | |
|         pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION;
 | |
|         pmdev->pages[i].ot_fault_limit = MAX31785_DEFAULT_OT_FAULT_LIMIT;
 | |
|         pmdev->pages[i].ot_warn_limit = MAX31785_DEFAULT_OT_WARN_LIMIT;
 | |
|     }
 | |
| 
 | |
|     for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE;
 | |
|          i <= MAX31785_MAX_ADC_VOLTAGE_PAGE;
 | |
|          i++) {
 | |
|         pmdev->pages[i].vout_mode = MAX31785_DEFAULT_VOUT_MODE;
 | |
|         pmdev->pages[i].revision = MAX31785_DEFAULT_PMBUS_REVISION;
 | |
|         pmdev->pages[i].vout_scale_monitor =
 | |
|             MAX31785_DEFAULT_VOUT_SCALE_MONITOR;
 | |
|         pmdev->pages[i].vout_ov_fault_limit = MAX31785_DEFAULT_OV_FAULT_LIMIT;
 | |
|         pmdev->pages[i].vout_ov_warn_limit = MAX31785_DEFAULT_OV_WARN_LIMIT;
 | |
|     }
 | |
| 
 | |
|     s->mfr_location = MAX31785_DEFAULT_TEXT;
 | |
|     s->mfr_date = MAX31785_DEFAULT_TEXT;
 | |
|     s->mfr_serial = MAX31785_DEFAULT_TEXT;
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_max31785 = {
 | |
|     .name = TYPE_MAX31785,
 | |
|     .version_id = 0,
 | |
|     .minimum_version_id = 0,
 | |
|     .fields = (const VMStateField[]){
 | |
|         VMSTATE_PMBUS_DEVICE(parent, MAX31785State),
 | |
|         VMSTATE_UINT16_ARRAY(mfr_mode, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(vout_peak, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(temperature_peak, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(vout_min, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT8_ARRAY(fault_response, MAX31785State,
 | |
|                             MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT32_ARRAY(time_count, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(fan_config, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(read_fan_pwm, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(fan_fault_limit, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(fan_warn_limit, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(fan_run_time, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT16_ARRAY(fan_pwm_avg, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT64_ARRAY(fan_pwm2rpm, MAX31785State,
 | |
|                              MAX31785_TOTAL_NUM_PAGES),
 | |
|         VMSTATE_UINT64(mfr_location, MAX31785State),
 | |
|         VMSTATE_UINT64(mfr_date, MAX31785State),
 | |
|         VMSTATE_UINT64(mfr_serial, MAX31785State),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void max31785_init(Object *obj)
 | |
| {
 | |
|     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 | |
| 
 | |
|     for (int i = MAX31785_MIN_FAN_PAGE; i <= MAX31785_MAX_FAN_PAGE; i++) {
 | |
|         pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE);
 | |
|     }
 | |
| 
 | |
|     for (int i = MAX31785_MIN_TEMP_PAGE; i <= MAX31785_MAX_TEMP_PAGE; i++) {
 | |
|         pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_TEMPERATURE);
 | |
|     }
 | |
| 
 | |
|     for (int i = MAX31785_MIN_ADC_VOLTAGE_PAGE;
 | |
|         i <= MAX31785_MAX_ADC_VOLTAGE_PAGE;
 | |
|         i++) {
 | |
|         pmbus_page_config(pmdev, i, PB_HAS_VOUT_MODE | PB_HAS_VOUT |
 | |
|                                     PB_HAS_VOUT_RATING);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void max31785_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     ResettableClass *rc = RESETTABLE_CLASS(klass);
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
|     PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
 | |
|     dc->desc = "Maxim MAX31785 6-Channel Fan Controller";
 | |
|     dc->vmsd = &vmstate_max31785;
 | |
|     k->write_data = max31785_write_data;
 | |
|     k->receive_byte = max31785_read_byte;
 | |
|     k->device_num_pages = MAX31785_TOTAL_NUM_PAGES;
 | |
|     rc->phases.exit = max31785_exit_reset;
 | |
| }
 | |
| 
 | |
| static const TypeInfo max31785_info = {
 | |
|     .name = TYPE_MAX31785,
 | |
|     .parent = TYPE_PMBUS_DEVICE,
 | |
|     .instance_size = sizeof(MAX31785State),
 | |
|     .instance_init = max31785_init,
 | |
|     .class_init = max31785_class_init,
 | |
| };
 | |
| 
 | |
| static void max31785_register_types(void)
 | |
| {
 | |
|     type_register_static(&max31785_info);
 | |
| }
 | |
| 
 | |
| type_init(max31785_register_types)
 |