xref: /openbmc/qemu/hw/sensor/adm1272.c (revision 83baec642a13a69398a2643a1f905606c13cd363)
1c93488f1STitus Rwantare /*
2c93488f1STitus Rwantare  * Analog Devices ADM1272 High Voltage Positive Hot Swap Controller and Digital
3c93488f1STitus Rwantare  * Power Monitor with PMBus
4c93488f1STitus Rwantare  *
5c93488f1STitus Rwantare  * Copyright 2021 Google LLC
6c93488f1STitus Rwantare  *
7c93488f1STitus Rwantare  * SPDX-License-Identifier: GPL-2.0-or-later
8c93488f1STitus Rwantare  */
9c93488f1STitus Rwantare 
10c93488f1STitus Rwantare #include "qemu/osdep.h"
11c93488f1STitus Rwantare #include "hw/i2c/pmbus_device.h"
12c93488f1STitus Rwantare #include "hw/irq.h"
13c93488f1STitus Rwantare #include "migration/vmstate.h"
14c93488f1STitus Rwantare #include "qapi/error.h"
15c93488f1STitus Rwantare #include "qapi/visitor.h"
16c93488f1STitus Rwantare #include "qemu/log.h"
17c93488f1STitus Rwantare #include "qemu/module.h"
18c93488f1STitus Rwantare 
19c93488f1STitus Rwantare #define TYPE_ADM1272 "adm1272"
20c93488f1STitus Rwantare #define ADM1272(obj) OBJECT_CHECK(ADM1272State, (obj), TYPE_ADM1272)
21c93488f1STitus Rwantare 
22c93488f1STitus Rwantare #define ADM1272_RESTART_TIME            0xCC
23c93488f1STitus Rwantare #define ADM1272_MFR_PEAK_IOUT           0xD0
24c93488f1STitus Rwantare #define ADM1272_MFR_PEAK_VIN            0xD1
25c93488f1STitus Rwantare #define ADM1272_MFR_PEAK_VOUT           0xD2
26c93488f1STitus Rwantare #define ADM1272_MFR_PMON_CONTROL        0xD3
27c93488f1STitus Rwantare #define ADM1272_MFR_PMON_CONFIG         0xD4
28c93488f1STitus Rwantare #define ADM1272_MFR_ALERT1_CONFIG       0xD5
29c93488f1STitus Rwantare #define ADM1272_MFR_ALERT2_CONFIG       0xD6
30c93488f1STitus Rwantare #define ADM1272_MFR_PEAK_TEMPERATURE    0xD7
31c93488f1STitus Rwantare #define ADM1272_MFR_DEVICE_CONFIG       0xD8
32c93488f1STitus Rwantare #define ADM1272_MFR_POWER_CYCLE         0xD9
33c93488f1STitus Rwantare #define ADM1272_MFR_PEAK_PIN            0xDA
34c93488f1STitus Rwantare #define ADM1272_MFR_READ_PIN_EXT        0xDB
35c93488f1STitus Rwantare #define ADM1272_MFR_READ_EIN_EXT        0xDC
36c93488f1STitus Rwantare 
37c93488f1STitus Rwantare #define ADM1272_HYSTERESIS_LOW          0xF2
38c93488f1STitus Rwantare #define ADM1272_HYSTERESIS_HIGH         0xF3
39c93488f1STitus Rwantare #define ADM1272_STATUS_HYSTERESIS       0xF4
40c93488f1STitus Rwantare #define ADM1272_STATUS_GPIO             0xF5
41c93488f1STitus Rwantare #define ADM1272_STRT_UP_IOUT_LIM        0xF6
42c93488f1STitus Rwantare 
43c93488f1STitus Rwantare /* Defaults */
44c93488f1STitus Rwantare #define ADM1272_OPERATION_DEFAULT       0x80
45c93488f1STitus Rwantare #define ADM1272_CAPABILITY_DEFAULT      0xB0
46c93488f1STitus Rwantare #define ADM1272_CAPABILITY_NO_PEC       0x30
47c93488f1STitus Rwantare #define ADM1272_DIRECT_MODE             0x40
48c93488f1STitus Rwantare #define ADM1272_HIGH_LIMIT_DEFAULT      0x0FFF
49c93488f1STitus Rwantare #define ADM1272_PIN_OP_DEFAULT          0x7FFF
50c93488f1STitus Rwantare #define ADM1272_PMBUS_REVISION_DEFAULT  0x22
51c93488f1STitus Rwantare #define ADM1272_MFR_ID_DEFAULT          "ADI"
52c93488f1STitus Rwantare #define ADM1272_MODEL_DEFAULT           "ADM1272-A1"
53c93488f1STitus Rwantare #define ADM1272_MFR_DEFAULT_REVISION    "25"
54c93488f1STitus Rwantare #define ADM1272_DEFAULT_DATE            "160301"
55c93488f1STitus Rwantare #define ADM1272_RESTART_TIME_DEFAULT    0x64
56c93488f1STitus Rwantare #define ADM1272_PMON_CONTROL_DEFAULT    0x1
57c93488f1STitus Rwantare #define ADM1272_PMON_CONFIG_DEFAULT     0x3F35
58c93488f1STitus Rwantare #define ADM1272_DEVICE_CONFIG_DEFAULT   0x8
59c93488f1STitus Rwantare #define ADM1272_HYSTERESIS_HIGH_DEFAULT     0xFFFF
60c93488f1STitus Rwantare #define ADM1272_STRT_UP_IOUT_LIM_DEFAULT    0x000F
61c93488f1STitus Rwantare #define ADM1272_VOLT_DEFAULT            12000
62c93488f1STitus Rwantare #define ADM1272_IOUT_DEFAULT            25000
63c93488f1STitus Rwantare #define ADM1272_PWR_DEFAULT             300  /* 12V 25A */
64c93488f1STitus Rwantare #define ADM1272_SHUNT                   300 /* micro-ohms */
65c93488f1STitus Rwantare #define ADM1272_VOLTAGE_COEFF_DEFAULT   1
66c93488f1STitus Rwantare #define ADM1272_CURRENT_COEFF_DEFAULT   3
67c93488f1STitus Rwantare #define ADM1272_PWR_COEFF_DEFAULT       7
68c93488f1STitus Rwantare #define ADM1272_IOUT_OFFSET             0x5000
69c93488f1STitus Rwantare #define ADM1272_IOUT_OFFSET             0x5000
70c93488f1STitus Rwantare 
71c93488f1STitus Rwantare 
72c93488f1STitus Rwantare typedef struct ADM1272State {
73c93488f1STitus Rwantare     PMBusDevice parent;
74c93488f1STitus Rwantare 
75c93488f1STitus Rwantare     uint64_t ein_ext;
76c93488f1STitus Rwantare     uint32_t pin_ext;
77c93488f1STitus Rwantare     uint8_t restart_time;
78c93488f1STitus Rwantare 
79c93488f1STitus Rwantare     uint16_t peak_vin;
80c93488f1STitus Rwantare     uint16_t peak_vout;
81c93488f1STitus Rwantare     uint16_t peak_iout;
82c93488f1STitus Rwantare     uint16_t peak_temperature;
83c93488f1STitus Rwantare     uint16_t peak_pin;
84c93488f1STitus Rwantare 
85c93488f1STitus Rwantare     uint8_t pmon_control;
86c93488f1STitus Rwantare     uint16_t pmon_config;
87c93488f1STitus Rwantare     uint16_t alert1_config;
88c93488f1STitus Rwantare     uint16_t alert2_config;
89c93488f1STitus Rwantare     uint16_t device_config;
90c93488f1STitus Rwantare 
91c93488f1STitus Rwantare     uint16_t hysteresis_low;
92c93488f1STitus Rwantare     uint16_t hysteresis_high;
93c93488f1STitus Rwantare     uint8_t status_hysteresis;
94c93488f1STitus Rwantare     uint8_t status_gpio;
95c93488f1STitus Rwantare 
96c93488f1STitus Rwantare     uint16_t strt_up_iout_lim;
97c93488f1STitus Rwantare 
98c93488f1STitus Rwantare } ADM1272State;
99c93488f1STitus Rwantare 
100c93488f1STitus Rwantare static const PMBusCoefficients adm1272_coefficients[] = {
101c93488f1STitus Rwantare     [0] = { 6770, 0, -2 },        /* voltage, vrange 60V */
102c93488f1STitus Rwantare     [1] = { 4062, 0, -2 },        /* voltage, vrange 100V */
103c93488f1STitus Rwantare     [2] = { 1326, 20480, -1 },    /* current, vsense range 15mV */
104c93488f1STitus Rwantare     [3] = { 663, 20480, -1 },     /* current, vsense range 30mV */
105c93488f1STitus Rwantare     [4] = { 3512, 0, -2 },        /* power, vrange 60V, irange 15mV */
106c93488f1STitus Rwantare     [5] = { 21071, 0, -3 },       /* power, vrange 100V, irange 15mV */
107c93488f1STitus Rwantare     [6] = { 17561, 0, -3 },       /* power, vrange 60V, irange 30mV */
108c93488f1STitus Rwantare     [7] = { 10535, 0, -3 },       /* power, vrange 100V, irange 30mV */
109c93488f1STitus Rwantare     [8] = { 42, 31871, -1 },      /* temperature */
110c93488f1STitus Rwantare };
111c93488f1STitus Rwantare 
adm1272_check_limits(ADM1272State * s)112c93488f1STitus Rwantare static void adm1272_check_limits(ADM1272State *s)
113c93488f1STitus Rwantare {
114c93488f1STitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(s);
115c93488f1STitus Rwantare 
116c93488f1STitus Rwantare     pmbus_check_limits(pmdev);
117c93488f1STitus Rwantare 
118c93488f1STitus Rwantare     if (pmdev->pages[0].read_vout > s->peak_vout) {
119c93488f1STitus Rwantare         s->peak_vout = pmdev->pages[0].read_vout;
120c93488f1STitus Rwantare     }
121c93488f1STitus Rwantare 
122c93488f1STitus Rwantare     if (pmdev->pages[0].read_vin > s->peak_vin) {
123c93488f1STitus Rwantare         s->peak_vin = pmdev->pages[0].read_vin;
124c93488f1STitus Rwantare     }
125c93488f1STitus Rwantare 
126c93488f1STitus Rwantare     if (pmdev->pages[0].read_iout > s->peak_iout) {
127c93488f1STitus Rwantare         s->peak_iout = pmdev->pages[0].read_iout;
128c93488f1STitus Rwantare     }
129c93488f1STitus Rwantare 
130c93488f1STitus Rwantare     if (pmdev->pages[0].read_temperature_1 > s->peak_temperature) {
131c93488f1STitus Rwantare         s->peak_temperature = pmdev->pages[0].read_temperature_1;
132c93488f1STitus Rwantare     }
133c93488f1STitus Rwantare 
134c93488f1STitus Rwantare     if (pmdev->pages[0].read_pin > s->peak_pin) {
135c93488f1STitus Rwantare         s->peak_pin = pmdev->pages[0].read_pin;
136c93488f1STitus Rwantare     }
137c93488f1STitus Rwantare }
138c93488f1STitus Rwantare 
adm1272_millivolts_to_direct(uint32_t value)139c93488f1STitus Rwantare static uint16_t adm1272_millivolts_to_direct(uint32_t value)
140c93488f1STitus Rwantare {
141c93488f1STitus Rwantare     PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT];
142c93488f1STitus Rwantare     c.b = c.b * 1000;
143c93488f1STitus Rwantare     c.R = c.R - 3;
144c93488f1STitus Rwantare     return pmbus_data2direct_mode(c, value);
145c93488f1STitus Rwantare }
146c93488f1STitus Rwantare 
adm1272_direct_to_millivolts(uint16_t value)147c93488f1STitus Rwantare static uint32_t adm1272_direct_to_millivolts(uint16_t value)
148c93488f1STitus Rwantare {
149c93488f1STitus Rwantare     PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT];
150c93488f1STitus Rwantare     c.b = c.b * 1000;
151c93488f1STitus Rwantare     c.R = c.R - 3;
152c93488f1STitus Rwantare     return pmbus_direct_mode2data(c, value);
153c93488f1STitus Rwantare }
154c93488f1STitus Rwantare 
adm1272_milliamps_to_direct(uint32_t value)155c93488f1STitus Rwantare static uint16_t adm1272_milliamps_to_direct(uint32_t value)
156c93488f1STitus Rwantare {
157c93488f1STitus Rwantare     PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT];
158c93488f1STitus Rwantare     /* Y = (m * r_sense * x - b) * 10^R */
159c93488f1STitus Rwantare     c.m = c.m * ADM1272_SHUNT / 1000; /* micro-ohms */
160c93488f1STitus Rwantare     c.b = c.b * 1000;
161c93488f1STitus Rwantare     c.R = c.R - 3;
162c93488f1STitus Rwantare     return pmbus_data2direct_mode(c, value);
163c93488f1STitus Rwantare }
164c93488f1STitus Rwantare 
adm1272_direct_to_milliamps(uint16_t value)165c93488f1STitus Rwantare static uint32_t adm1272_direct_to_milliamps(uint16_t value)
166c93488f1STitus Rwantare {
167c93488f1STitus Rwantare     PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT];
168c93488f1STitus Rwantare     c.m = c.m * ADM1272_SHUNT / 1000;
169c93488f1STitus Rwantare     c.b = c.b * 1000;
170c93488f1STitus Rwantare     c.R = c.R - 3;
171c93488f1STitus Rwantare     return pmbus_direct_mode2data(c, value);
172c93488f1STitus Rwantare }
173c93488f1STitus Rwantare 
adm1272_watts_to_direct(uint32_t value)174c93488f1STitus Rwantare static uint16_t adm1272_watts_to_direct(uint32_t value)
175c93488f1STitus Rwantare {
176c93488f1STitus Rwantare     PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT];
177c93488f1STitus Rwantare     c.m = c.m * ADM1272_SHUNT / 1000;
178c93488f1STitus Rwantare     return pmbus_data2direct_mode(c, value);
179c93488f1STitus Rwantare }
180c93488f1STitus Rwantare 
adm1272_direct_to_watts(uint16_t value)181c93488f1STitus Rwantare static uint32_t adm1272_direct_to_watts(uint16_t value)
182c93488f1STitus Rwantare {
183c93488f1STitus Rwantare     PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT];
184c93488f1STitus Rwantare     c.m = c.m * ADM1272_SHUNT / 1000;
185c93488f1STitus Rwantare     return pmbus_direct_mode2data(c, value);
186c93488f1STitus Rwantare }
187c93488f1STitus Rwantare 
adm1272_exit_reset(Object * obj,ResetType type)188*ad80e367SPeter Maydell static void adm1272_exit_reset(Object *obj, ResetType type)
189c93488f1STitus Rwantare {
190c93488f1STitus Rwantare     ADM1272State *s = ADM1272(obj);
191c93488f1STitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
192c93488f1STitus Rwantare 
193c93488f1STitus Rwantare     pmdev->page = 0;
194c93488f1STitus Rwantare     pmdev->pages[0].operation = ADM1272_OPERATION_DEFAULT;
195c93488f1STitus Rwantare 
196c93488f1STitus Rwantare 
197c93488f1STitus Rwantare     pmdev->capability = ADM1272_CAPABILITY_NO_PEC;
198c93488f1STitus Rwantare     pmdev->pages[0].revision = ADM1272_PMBUS_REVISION_DEFAULT;
199c93488f1STitus Rwantare     pmdev->pages[0].vout_mode = ADM1272_DIRECT_MODE;
200c93488f1STitus Rwantare     pmdev->pages[0].vout_ov_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
201c93488f1STitus Rwantare     pmdev->pages[0].vout_uv_warn_limit = 0;
202c93488f1STitus Rwantare     pmdev->pages[0].iout_oc_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
203c93488f1STitus Rwantare     pmdev->pages[0].ot_fault_limit = ADM1272_HIGH_LIMIT_DEFAULT;
204c93488f1STitus Rwantare     pmdev->pages[0].ot_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
205c93488f1STitus Rwantare     pmdev->pages[0].vin_ov_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
206c93488f1STitus Rwantare     pmdev->pages[0].vin_uv_warn_limit = 0;
207c93488f1STitus Rwantare     pmdev->pages[0].pin_op_warn_limit = ADM1272_PIN_OP_DEFAULT;
208c93488f1STitus Rwantare 
209c93488f1STitus Rwantare     pmdev->pages[0].status_word = 0;
210c93488f1STitus Rwantare     pmdev->pages[0].status_vout = 0;
211c93488f1STitus Rwantare     pmdev->pages[0].status_iout = 0;
212c93488f1STitus Rwantare     pmdev->pages[0].status_input = 0;
213c93488f1STitus Rwantare     pmdev->pages[0].status_temperature = 0;
214c93488f1STitus Rwantare     pmdev->pages[0].status_mfr_specific = 0;
215c93488f1STitus Rwantare 
216c93488f1STitus Rwantare     pmdev->pages[0].read_vin
217c93488f1STitus Rwantare         = adm1272_millivolts_to_direct(ADM1272_VOLT_DEFAULT);
218c93488f1STitus Rwantare     pmdev->pages[0].read_vout
219c93488f1STitus Rwantare         = adm1272_millivolts_to_direct(ADM1272_VOLT_DEFAULT);
220c93488f1STitus Rwantare     pmdev->pages[0].read_iout
221c93488f1STitus Rwantare         = adm1272_milliamps_to_direct(ADM1272_IOUT_DEFAULT);
222c93488f1STitus Rwantare     pmdev->pages[0].read_temperature_1 = 0;
223c93488f1STitus Rwantare     pmdev->pages[0].read_pin = adm1272_watts_to_direct(ADM1272_PWR_DEFAULT);
224c93488f1STitus Rwantare     pmdev->pages[0].revision = ADM1272_PMBUS_REVISION_DEFAULT;
225c93488f1STitus Rwantare     pmdev->pages[0].mfr_id = ADM1272_MFR_ID_DEFAULT;
226c93488f1STitus Rwantare     pmdev->pages[0].mfr_model = ADM1272_MODEL_DEFAULT;
227c93488f1STitus Rwantare     pmdev->pages[0].mfr_revision = ADM1272_MFR_DEFAULT_REVISION;
228c93488f1STitus Rwantare     pmdev->pages[0].mfr_date = ADM1272_DEFAULT_DATE;
229c93488f1STitus Rwantare 
230c93488f1STitus Rwantare     s->pin_ext = 0;
231c93488f1STitus Rwantare     s->ein_ext = 0;
232c93488f1STitus Rwantare     s->restart_time = ADM1272_RESTART_TIME_DEFAULT;
233c93488f1STitus Rwantare 
234c93488f1STitus Rwantare     s->peak_vin = 0;
235c93488f1STitus Rwantare     s->peak_vout = 0;
236c93488f1STitus Rwantare     s->peak_iout = 0;
237c93488f1STitus Rwantare     s->peak_temperature = 0;
238c93488f1STitus Rwantare     s->peak_pin = 0;
239c93488f1STitus Rwantare 
240c93488f1STitus Rwantare     s->pmon_control = ADM1272_PMON_CONTROL_DEFAULT;
241c93488f1STitus Rwantare     s->pmon_config = ADM1272_PMON_CONFIG_DEFAULT;
242c93488f1STitus Rwantare     s->alert1_config = 0;
243c93488f1STitus Rwantare     s->alert2_config = 0;
244c93488f1STitus Rwantare     s->device_config = ADM1272_DEVICE_CONFIG_DEFAULT;
245c93488f1STitus Rwantare 
246c93488f1STitus Rwantare     s->hysteresis_low = 0;
247c93488f1STitus Rwantare     s->hysteresis_high = ADM1272_HYSTERESIS_HIGH_DEFAULT;
248c93488f1STitus Rwantare     s->status_hysteresis = 0;
249c93488f1STitus Rwantare     s->status_gpio = 0;
250c93488f1STitus Rwantare 
251c93488f1STitus Rwantare     s->strt_up_iout_lim = ADM1272_STRT_UP_IOUT_LIM_DEFAULT;
252c93488f1STitus Rwantare }
253c93488f1STitus Rwantare 
adm1272_read_byte(PMBusDevice * pmdev)254c93488f1STitus Rwantare static uint8_t adm1272_read_byte(PMBusDevice *pmdev)
255c93488f1STitus Rwantare {
256c93488f1STitus Rwantare     ADM1272State *s = ADM1272(pmdev);
257c93488f1STitus Rwantare 
258c93488f1STitus Rwantare     switch (pmdev->code) {
259c93488f1STitus Rwantare     case ADM1272_RESTART_TIME:
260c93488f1STitus Rwantare         pmbus_send8(pmdev, s->restart_time);
261c93488f1STitus Rwantare         break;
262c93488f1STitus Rwantare 
263c93488f1STitus Rwantare     case ADM1272_MFR_PEAK_IOUT:
264c93488f1STitus Rwantare         pmbus_send16(pmdev, s->peak_iout);
265c93488f1STitus Rwantare         break;
266c93488f1STitus Rwantare 
267c93488f1STitus Rwantare     case ADM1272_MFR_PEAK_VIN:
268c93488f1STitus Rwantare         pmbus_send16(pmdev, s->peak_vin);
269c93488f1STitus Rwantare         break;
270c93488f1STitus Rwantare 
271c93488f1STitus Rwantare     case ADM1272_MFR_PEAK_VOUT:
272c93488f1STitus Rwantare         pmbus_send16(pmdev, s->peak_vout);
273c93488f1STitus Rwantare         break;
274c93488f1STitus Rwantare 
275c93488f1STitus Rwantare     case ADM1272_MFR_PMON_CONTROL:
276c93488f1STitus Rwantare         pmbus_send8(pmdev, s->pmon_control);
277c93488f1STitus Rwantare         break;
278c93488f1STitus Rwantare 
279c93488f1STitus Rwantare     case ADM1272_MFR_PMON_CONFIG:
280c93488f1STitus Rwantare         pmbus_send16(pmdev, s->pmon_config);
281c93488f1STitus Rwantare         break;
282c93488f1STitus Rwantare 
283c93488f1STitus Rwantare     case ADM1272_MFR_ALERT1_CONFIG:
284c93488f1STitus Rwantare         pmbus_send16(pmdev, s->alert1_config);
285c93488f1STitus Rwantare         break;
286c93488f1STitus Rwantare 
287c93488f1STitus Rwantare     case ADM1272_MFR_ALERT2_CONFIG:
288c93488f1STitus Rwantare         pmbus_send16(pmdev, s->alert2_config);
289c93488f1STitus Rwantare         break;
290c93488f1STitus Rwantare 
291c93488f1STitus Rwantare     case ADM1272_MFR_PEAK_TEMPERATURE:
292c93488f1STitus Rwantare         pmbus_send16(pmdev, s->peak_temperature);
293c93488f1STitus Rwantare         break;
294c93488f1STitus Rwantare 
295c93488f1STitus Rwantare     case ADM1272_MFR_DEVICE_CONFIG:
296c93488f1STitus Rwantare         pmbus_send16(pmdev, s->device_config);
297c93488f1STitus Rwantare         break;
298c93488f1STitus Rwantare 
299c93488f1STitus Rwantare     case ADM1272_MFR_PEAK_PIN:
300c93488f1STitus Rwantare         pmbus_send16(pmdev, s->peak_pin);
301c93488f1STitus Rwantare         break;
302c93488f1STitus Rwantare 
303c93488f1STitus Rwantare     case ADM1272_MFR_READ_PIN_EXT:
304c93488f1STitus Rwantare         pmbus_send32(pmdev, s->pin_ext);
305c93488f1STitus Rwantare         break;
306c93488f1STitus Rwantare 
307c93488f1STitus Rwantare     case ADM1272_MFR_READ_EIN_EXT:
308c93488f1STitus Rwantare         pmbus_send64(pmdev, s->ein_ext);
309c93488f1STitus Rwantare         break;
310c93488f1STitus Rwantare 
311c93488f1STitus Rwantare     case ADM1272_HYSTERESIS_LOW:
312c93488f1STitus Rwantare         pmbus_send16(pmdev, s->hysteresis_low);
313c93488f1STitus Rwantare         break;
314c93488f1STitus Rwantare 
315c93488f1STitus Rwantare     case ADM1272_HYSTERESIS_HIGH:
316c93488f1STitus Rwantare         pmbus_send16(pmdev, s->hysteresis_high);
317c93488f1STitus Rwantare         break;
318c93488f1STitus Rwantare 
319c93488f1STitus Rwantare     case ADM1272_STATUS_HYSTERESIS:
320c93488f1STitus Rwantare         pmbus_send16(pmdev, s->status_hysteresis);
321c93488f1STitus Rwantare         break;
322c93488f1STitus Rwantare 
323c93488f1STitus Rwantare     case ADM1272_STATUS_GPIO:
324c93488f1STitus Rwantare         pmbus_send16(pmdev, s->status_gpio);
325c93488f1STitus Rwantare         break;
326c93488f1STitus Rwantare 
327c93488f1STitus Rwantare     case ADM1272_STRT_UP_IOUT_LIM:
328c93488f1STitus Rwantare         pmbus_send16(pmdev, s->strt_up_iout_lim);
329c93488f1STitus Rwantare         break;
330c93488f1STitus Rwantare 
331c93488f1STitus Rwantare     default:
332c93488f1STitus Rwantare         qemu_log_mask(LOG_GUEST_ERROR,
333c93488f1STitus Rwantare                       "%s: reading from unsupported register: 0x%02x\n",
334c93488f1STitus Rwantare                       __func__, pmdev->code);
335c93488f1STitus Rwantare         return 0xFF;
336c93488f1STitus Rwantare         break;
337c93488f1STitus Rwantare     }
338c93488f1STitus Rwantare 
339c93488f1STitus Rwantare     return 0;
340c93488f1STitus Rwantare }
341c93488f1STitus Rwantare 
adm1272_write_data(PMBusDevice * pmdev,const uint8_t * buf,uint8_t len)342c93488f1STitus Rwantare static int adm1272_write_data(PMBusDevice *pmdev, const uint8_t *buf,
343c93488f1STitus Rwantare                               uint8_t len)
344c93488f1STitus Rwantare {
345c93488f1STitus Rwantare     ADM1272State *s = ADM1272(pmdev);
346c93488f1STitus Rwantare 
347c93488f1STitus Rwantare     if (len == 0) {
348c93488f1STitus Rwantare         qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
349c93488f1STitus Rwantare         return -1;
350c93488f1STitus Rwantare     }
351c93488f1STitus Rwantare 
352c93488f1STitus Rwantare     pmdev->code = buf[0]; /* PMBus command code */
353c93488f1STitus Rwantare 
354c93488f1STitus Rwantare     if (len == 1) {
355c93488f1STitus Rwantare         return 0;
356c93488f1STitus Rwantare     }
357c93488f1STitus Rwantare 
358c93488f1STitus Rwantare     /* Exclude command code from buffer */
359c93488f1STitus Rwantare     buf++;
360c93488f1STitus Rwantare     len--;
361c93488f1STitus Rwantare 
362c93488f1STitus Rwantare     switch (pmdev->code) {
363c93488f1STitus Rwantare 
364c93488f1STitus Rwantare     case ADM1272_RESTART_TIME:
365c93488f1STitus Rwantare         s->restart_time = pmbus_receive8(pmdev);
366c93488f1STitus Rwantare         break;
367c93488f1STitus Rwantare 
368c93488f1STitus Rwantare     case ADM1272_MFR_PMON_CONTROL:
369c93488f1STitus Rwantare         s->pmon_control = pmbus_receive8(pmdev);
370c93488f1STitus Rwantare         break;
371c93488f1STitus Rwantare 
372c93488f1STitus Rwantare     case ADM1272_MFR_PMON_CONFIG:
373c93488f1STitus Rwantare         s->pmon_config = pmbus_receive16(pmdev);
374c93488f1STitus Rwantare         break;
375c93488f1STitus Rwantare 
376c93488f1STitus Rwantare     case ADM1272_MFR_ALERT1_CONFIG:
377c93488f1STitus Rwantare         s->alert1_config = pmbus_receive16(pmdev);
378c93488f1STitus Rwantare         break;
379c93488f1STitus Rwantare 
380c93488f1STitus Rwantare     case ADM1272_MFR_ALERT2_CONFIG:
381c93488f1STitus Rwantare         s->alert2_config = pmbus_receive16(pmdev);
382c93488f1STitus Rwantare         break;
383c93488f1STitus Rwantare 
384c93488f1STitus Rwantare     case ADM1272_MFR_DEVICE_CONFIG:
385c93488f1STitus Rwantare         s->device_config = pmbus_receive16(pmdev);
386c93488f1STitus Rwantare         break;
387c93488f1STitus Rwantare 
388c93488f1STitus Rwantare     case ADM1272_MFR_POWER_CYCLE:
389ef6ab292SPeter Maydell         device_cold_reset(DEVICE(s));
390c93488f1STitus Rwantare         break;
391c93488f1STitus Rwantare 
392c93488f1STitus Rwantare     case ADM1272_HYSTERESIS_LOW:
393c93488f1STitus Rwantare         s->hysteresis_low = pmbus_receive16(pmdev);
394c93488f1STitus Rwantare         break;
395c93488f1STitus Rwantare 
396c93488f1STitus Rwantare     case ADM1272_HYSTERESIS_HIGH:
397c93488f1STitus Rwantare         s->hysteresis_high = pmbus_receive16(pmdev);
398c93488f1STitus Rwantare         break;
399c93488f1STitus Rwantare 
400c93488f1STitus Rwantare     case ADM1272_STRT_UP_IOUT_LIM:
401c93488f1STitus Rwantare         s->strt_up_iout_lim = pmbus_receive16(pmdev);
402c93488f1STitus Rwantare         adm1272_check_limits(s);
403c93488f1STitus Rwantare         break;
404c93488f1STitus Rwantare 
405c93488f1STitus Rwantare     default:
406c93488f1STitus Rwantare         qemu_log_mask(LOG_GUEST_ERROR,
407c93488f1STitus Rwantare                       "%s: writing to unsupported register: 0x%02x\n",
408c93488f1STitus Rwantare                       __func__, pmdev->code);
409c93488f1STitus Rwantare         break;
410c93488f1STitus Rwantare     }
411c93488f1STitus Rwantare     return 0;
412c93488f1STitus Rwantare }
413c93488f1STitus Rwantare 
adm1272_get(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)414c93488f1STitus Rwantare static void adm1272_get(Object *obj, Visitor *v, const char *name, void *opaque,
415c93488f1STitus Rwantare                         Error **errp)
416c93488f1STitus Rwantare {
417c93488f1STitus Rwantare     uint16_t value;
418c93488f1STitus Rwantare 
419c93488f1STitus Rwantare     if (strcmp(name, "vin") == 0 || strcmp(name, "vout") == 0) {
420c93488f1STitus Rwantare         value = adm1272_direct_to_millivolts(*(uint16_t *)opaque);
421c93488f1STitus Rwantare     } else if (strcmp(name, "iout") == 0) {
422c93488f1STitus Rwantare         value = adm1272_direct_to_milliamps(*(uint16_t *)opaque);
423c93488f1STitus Rwantare     } else if (strcmp(name, "pin") == 0) {
424c93488f1STitus Rwantare         value = adm1272_direct_to_watts(*(uint16_t *)opaque);
425c93488f1STitus Rwantare     } else {
426c93488f1STitus Rwantare         value = *(uint16_t *)opaque;
427c93488f1STitus Rwantare     }
428c93488f1STitus Rwantare 
429c93488f1STitus Rwantare     visit_type_uint16(v, name, &value, errp);
430c93488f1STitus Rwantare }
431c93488f1STitus Rwantare 
adm1272_set(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)432c93488f1STitus Rwantare static void adm1272_set(Object *obj, Visitor *v, const char *name, void *opaque,
433c93488f1STitus Rwantare                         Error **errp)
434c93488f1STitus Rwantare {
435c93488f1STitus Rwantare     ADM1272State *s = ADM1272(obj);
436c93488f1STitus Rwantare     uint16_t *internal = opaque;
437c93488f1STitus Rwantare     uint16_t value;
438c93488f1STitus Rwantare 
439c93488f1STitus Rwantare     if (!visit_type_uint16(v, name, &value, errp)) {
440c93488f1STitus Rwantare         return;
441c93488f1STitus Rwantare     }
442c93488f1STitus Rwantare 
443c93488f1STitus Rwantare     if (strcmp(name, "vin") == 0 || strcmp(name, "vout") == 0) {
444c93488f1STitus Rwantare         *internal = adm1272_millivolts_to_direct(value);
445c93488f1STitus Rwantare     } else if (strcmp(name, "iout") == 0) {
446c93488f1STitus Rwantare         *internal = adm1272_milliamps_to_direct(value);
447c93488f1STitus Rwantare     } else if (strcmp(name, "pin") == 0) {
448c93488f1STitus Rwantare         *internal = adm1272_watts_to_direct(value);
449c93488f1STitus Rwantare     } else {
450c93488f1STitus Rwantare         *internal = value;
451c93488f1STitus Rwantare     }
452c93488f1STitus Rwantare 
453c93488f1STitus Rwantare     adm1272_check_limits(s);
454c93488f1STitus Rwantare }
455c93488f1STitus Rwantare 
456c93488f1STitus Rwantare static const VMStateDescription vmstate_adm1272 = {
457c93488f1STitus Rwantare     .name = "ADM1272",
458c93488f1STitus Rwantare     .version_id = 0,
459c93488f1STitus Rwantare     .minimum_version_id = 0,
460af10fff2SRichard Henderson     .fields = (const VMStateField[]){
461c93488f1STitus Rwantare         VMSTATE_PMBUS_DEVICE(parent, ADM1272State),
462c93488f1STitus Rwantare         VMSTATE_UINT64(ein_ext, ADM1272State),
463c93488f1STitus Rwantare         VMSTATE_UINT32(pin_ext, ADM1272State),
464c93488f1STitus Rwantare         VMSTATE_UINT8(restart_time, ADM1272State),
465c93488f1STitus Rwantare 
466c93488f1STitus Rwantare         VMSTATE_UINT16(peak_vin, ADM1272State),
467c93488f1STitus Rwantare         VMSTATE_UINT16(peak_vout, ADM1272State),
468c93488f1STitus Rwantare         VMSTATE_UINT16(peak_iout, ADM1272State),
469c93488f1STitus Rwantare         VMSTATE_UINT16(peak_temperature, ADM1272State),
470c93488f1STitus Rwantare         VMSTATE_UINT16(peak_pin, ADM1272State),
471c93488f1STitus Rwantare 
472c93488f1STitus Rwantare         VMSTATE_UINT8(pmon_control, ADM1272State),
473c93488f1STitus Rwantare         VMSTATE_UINT16(pmon_config, ADM1272State),
474c93488f1STitus Rwantare         VMSTATE_UINT16(alert1_config, ADM1272State),
475c93488f1STitus Rwantare         VMSTATE_UINT16(alert2_config, ADM1272State),
476c93488f1STitus Rwantare         VMSTATE_UINT16(device_config, ADM1272State),
477c93488f1STitus Rwantare 
478c93488f1STitus Rwantare         VMSTATE_UINT16(hysteresis_low, ADM1272State),
479c93488f1STitus Rwantare         VMSTATE_UINT16(hysteresis_high, ADM1272State),
480c93488f1STitus Rwantare         VMSTATE_UINT8(status_hysteresis, ADM1272State),
481c93488f1STitus Rwantare         VMSTATE_UINT8(status_gpio, ADM1272State),
482c93488f1STitus Rwantare 
483c93488f1STitus Rwantare         VMSTATE_UINT16(strt_up_iout_lim, ADM1272State),
484c93488f1STitus Rwantare         VMSTATE_END_OF_LIST()
485c93488f1STitus Rwantare     }
486c93488f1STitus Rwantare };
487c93488f1STitus Rwantare 
adm1272_init(Object * obj)488c93488f1STitus Rwantare static void adm1272_init(Object *obj)
489c93488f1STitus Rwantare {
490c93488f1STitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
491c93488f1STitus Rwantare     uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VIN | PB_HAS_IOUT |
492c93488f1STitus Rwantare                      PB_HAS_PIN | PB_HAS_TEMPERATURE | PB_HAS_MFR_INFO;
493c93488f1STitus Rwantare 
494c93488f1STitus Rwantare     pmbus_page_config(pmdev, 0, flags);
495c93488f1STitus Rwantare 
496c93488f1STitus Rwantare     object_property_add(obj, "vin", "uint16",
497c93488f1STitus Rwantare                         adm1272_get,
498c93488f1STitus Rwantare                         adm1272_set, NULL, &pmdev->pages[0].read_vin);
499c93488f1STitus Rwantare 
500c93488f1STitus Rwantare     object_property_add(obj, "vout", "uint16",
501c93488f1STitus Rwantare                         adm1272_get,
502c93488f1STitus Rwantare                         adm1272_set, NULL, &pmdev->pages[0].read_vout);
503c93488f1STitus Rwantare 
504c93488f1STitus Rwantare     object_property_add(obj, "iout", "uint16",
505c93488f1STitus Rwantare                         adm1272_get,
506c93488f1STitus Rwantare                         adm1272_set, NULL, &pmdev->pages[0].read_iout);
507c93488f1STitus Rwantare 
508c93488f1STitus Rwantare     object_property_add(obj, "pin", "uint16",
509c93488f1STitus Rwantare                         adm1272_get,
510c93488f1STitus Rwantare                         adm1272_set, NULL, &pmdev->pages[0].read_pin);
511c93488f1STitus Rwantare 
512c93488f1STitus Rwantare }
513c93488f1STitus Rwantare 
adm1272_class_init(ObjectClass * klass,void * data)514c93488f1STitus Rwantare static void adm1272_class_init(ObjectClass *klass, void *data)
515c93488f1STitus Rwantare {
516c93488f1STitus Rwantare     ResettableClass *rc = RESETTABLE_CLASS(klass);
517c93488f1STitus Rwantare     DeviceClass *dc = DEVICE_CLASS(klass);
518c93488f1STitus Rwantare     PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
519c93488f1STitus Rwantare 
520c93488f1STitus Rwantare     dc->desc = "Analog Devices ADM1272 Hot Swap controller";
521c93488f1STitus Rwantare     dc->vmsd = &vmstate_adm1272;
522c93488f1STitus Rwantare     k->write_data = adm1272_write_data;
523c93488f1STitus Rwantare     k->receive_byte = adm1272_read_byte;
524c93488f1STitus Rwantare     k->device_num_pages = 1;
525c93488f1STitus Rwantare 
526c93488f1STitus Rwantare     rc->phases.exit = adm1272_exit_reset;
527c93488f1STitus Rwantare }
528c93488f1STitus Rwantare 
529c93488f1STitus Rwantare static const TypeInfo adm1272_info = {
530c93488f1STitus Rwantare     .name = TYPE_ADM1272,
531c93488f1STitus Rwantare     .parent = TYPE_PMBUS_DEVICE,
532c93488f1STitus Rwantare     .instance_size = sizeof(ADM1272State),
533c93488f1STitus Rwantare     .instance_init = adm1272_init,
534c93488f1STitus Rwantare     .class_init = adm1272_class_init,
535c93488f1STitus Rwantare };
536c93488f1STitus Rwantare 
adm1272_register_types(void)537c93488f1STitus Rwantare static void adm1272_register_types(void)
538c93488f1STitus Rwantare {
539c93488f1STitus Rwantare     type_register_static(&adm1272_info);
540c93488f1STitus Rwantare }
541c93488f1STitus Rwantare 
542c93488f1STitus Rwantare type_init(adm1272_register_types)
543