---
hw/arm/Kconfig | 1 +
hw/arm/aspeed.c | 6 +-
hw/sensor/Kconfig | 4 +
hw/sensor/max31785.c | 580 ++++++++++++++++++++++++++++++++++++++++++
hw/sensor/meson.build | 1 +
5 files changed, 590 insertions(+), 2 deletions(-)
create mode 100644 hw/sensor/max31785.c
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 219262a8da36..77ef0fa967b2 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -408,6 +408,7 @@ config NPCM7XX
select SSI
select UNIMP
select PCA954X
+ select MAX31785
config FSL_IMX25
bool
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 0e6edd2be4fa..85630b497793 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -619,7 +619,6 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState
*bmc)
LEDState *led;
/* Bus 3: TODO bmp280@77 */
- /* Bus 3: TODO max31785@52 */
dev = DEVICE(i2c_slave_new(TYPE_PCA9552, 0x60));
qdev_prop_set_string(dev, "description", "pca1");
i2c_slave_realize_and_unref(I2C_SLAVE(dev),
@@ -635,6 +634,8 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState
*bmc)
qdev_get_gpio_in(DEVICE(led), 0));
}
i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "dps310", 0x76);
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3), "max31785",
+ 0x52);
i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "tmp423", 0x4c);
i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), "tmp423", 0x4c);
@@ -779,13 +780,14 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc)
create_pca9552(soc, 7, 0x31);
create_pca9552(soc, 7, 0x32);
create_pca9552(soc, 7, 0x33);
- /* Bus 7: TODO max31785@52 */
create_pca9552(soc, 7, 0x60);
create_pca9552(soc, 7, 0x61);
i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "dps310", 0x76);
/* Bus 7: TODO si7021-a20@20 */
i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105,
0x48);
+ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "max31785",
+ 0x52);
aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x50, 64 * KiB);
aspeed_eeprom_init(aspeed_i2c_get_bus(&soc->i2c, 7), 0x51, 64 * KiB);
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index df392e786904..e03bd09b50e8 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -34,3 +34,7 @@ config LSM303DLHC_MAG
config ISL_PMBUS_VR
bool
depends on PMBUS
+
+config MAX31785
+ bool
+ depends on PMBUS
diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c
new file mode 100644
index 000000000000..11bf9977b6fd
--- /dev/null
+++ b/hw/sensor/max31785.c
@@ -0,0 +1,580 @@
+/*
+ * Maxim MAX31785 PMBus 6-Channel Fan Controller
+ *
+ * Datasheet:
+ * https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
+ *
+ * Copyright(c) 2022 Qualcomm Innovative Center, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#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 = (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)
diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build
index 12b6992bc845..9e9be602c349 100644
--- a/hw/sensor/meson.build
+++ b/hw/sensor/meson.build
@@ -6,3 +6,4 @@ softmmu_ss.add(when: 'CONFIG_ADM1272', if_true:
files('adm1272.c'))
softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c'))
softmmu_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true:
files('lsm303dlhc_mag.c'))
softmmu_ss.add(when: 'CONFIG_ISL_PMBUS_VR', if_true: files('isl_pmbus_vr.c'))
+softmmu_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.c'))
--
2.25.1