xref: /openbmc/qemu/hw/sensor/adm1266.c (revision 6f351a7a)
1*6f351a7aSTitus Rwantare /*
2*6f351a7aSTitus Rwantare  * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and
3*6f351a7aSTitus Rwantare  * Fault Recording with PMBus
4*6f351a7aSTitus Rwantare  *
5*6f351a7aSTitus Rwantare  * https://www.analog.com/media/en/technical-documentation/data-sheets/adm1266.pdf
6*6f351a7aSTitus Rwantare  *
7*6f351a7aSTitus Rwantare  * Copyright 2023 Google LLC
8*6f351a7aSTitus Rwantare  *
9*6f351a7aSTitus Rwantare  * SPDX-License-Identifier: GPL-2.0-or-later
10*6f351a7aSTitus Rwantare  */
11*6f351a7aSTitus Rwantare 
12*6f351a7aSTitus Rwantare #include "qemu/osdep.h"
13*6f351a7aSTitus Rwantare #include "hw/i2c/pmbus_device.h"
14*6f351a7aSTitus Rwantare #include "hw/irq.h"
15*6f351a7aSTitus Rwantare #include "migration/vmstate.h"
16*6f351a7aSTitus Rwantare #include "qapi/error.h"
17*6f351a7aSTitus Rwantare #include "qapi/visitor.h"
18*6f351a7aSTitus Rwantare #include "qemu/log.h"
19*6f351a7aSTitus Rwantare #include "qemu/module.h"
20*6f351a7aSTitus Rwantare 
21*6f351a7aSTitus Rwantare #define TYPE_ADM1266 "adm1266"
22*6f351a7aSTitus Rwantare OBJECT_DECLARE_SIMPLE_TYPE(ADM1266State, ADM1266)
23*6f351a7aSTitus Rwantare 
24*6f351a7aSTitus Rwantare #define ADM1266_BLACKBOX_CONFIG                 0xD3
25*6f351a7aSTitus Rwantare #define ADM1266_PDIO_CONFIG                     0xD4
26*6f351a7aSTitus Rwantare #define ADM1266_READ_STATE                      0xD9
27*6f351a7aSTitus Rwantare #define ADM1266_READ_BLACKBOX                   0xDE
28*6f351a7aSTitus Rwantare #define ADM1266_SET_RTC                         0xDF
29*6f351a7aSTitus Rwantare #define ADM1266_GPIO_SYNC_CONFIGURATION         0xE1
30*6f351a7aSTitus Rwantare #define ADM1266_BLACKBOX_INFORMATION            0xE6
31*6f351a7aSTitus Rwantare #define ADM1266_PDIO_STATUS                     0xE9
32*6f351a7aSTitus Rwantare #define ADM1266_GPIO_STATUS                     0xEA
33*6f351a7aSTitus Rwantare 
34*6f351a7aSTitus Rwantare /* Defaults */
35*6f351a7aSTitus Rwantare #define ADM1266_OPERATION_DEFAULT               0x80
36*6f351a7aSTitus Rwantare #define ADM1266_CAPABILITY_DEFAULT              0xA0
37*6f351a7aSTitus Rwantare #define ADM1266_CAPABILITY_NO_PEC               0x20
38*6f351a7aSTitus Rwantare #define ADM1266_PMBUS_REVISION_DEFAULT          0x22
39*6f351a7aSTitus Rwantare #define ADM1266_MFR_ID_DEFAULT                  "ADI"
40*6f351a7aSTitus Rwantare #define ADM1266_MFR_ID_DEFAULT_LEN              32
41*6f351a7aSTitus Rwantare #define ADM1266_MFR_MODEL_DEFAULT               "ADM1266-A1"
42*6f351a7aSTitus Rwantare #define ADM1266_MFR_MODEL_DEFAULT_LEN           32
43*6f351a7aSTitus Rwantare #define ADM1266_MFR_REVISION_DEFAULT            "25"
44*6f351a7aSTitus Rwantare #define ADM1266_MFR_REVISION_DEFAULT_LEN        8
45*6f351a7aSTitus Rwantare 
46*6f351a7aSTitus Rwantare #define ADM1266_NUM_PAGES               17
47*6f351a7aSTitus Rwantare /**
48*6f351a7aSTitus Rwantare  * PAGE Index
49*6f351a7aSTitus Rwantare  * Page 0 VH1.
50*6f351a7aSTitus Rwantare  * Page 1 VH2.
51*6f351a7aSTitus Rwantare  * Page 2 VH3.
52*6f351a7aSTitus Rwantare  * Page 3 VH4.
53*6f351a7aSTitus Rwantare  * Page 4 VP1.
54*6f351a7aSTitus Rwantare  * Page 5 VP2.
55*6f351a7aSTitus Rwantare  * Page 6 VP3.
56*6f351a7aSTitus Rwantare  * Page 7 VP4.
57*6f351a7aSTitus Rwantare  * Page 8 VP5.
58*6f351a7aSTitus Rwantare  * Page 9 VP6.
59*6f351a7aSTitus Rwantare  * Page 10 VP7.
60*6f351a7aSTitus Rwantare  * Page 11 VP8.
61*6f351a7aSTitus Rwantare  * Page 12 VP9.
62*6f351a7aSTitus Rwantare  * Page 13 VP10.
63*6f351a7aSTitus Rwantare  * Page 14 VP11.
64*6f351a7aSTitus Rwantare  * Page 15 VP12.
65*6f351a7aSTitus Rwantare  * Page 16 VP13.
66*6f351a7aSTitus Rwantare  */
67*6f351a7aSTitus Rwantare typedef struct ADM1266State {
68*6f351a7aSTitus Rwantare     PMBusDevice parent;
69*6f351a7aSTitus Rwantare 
70*6f351a7aSTitus Rwantare     char mfr_id[32];
71*6f351a7aSTitus Rwantare     char mfr_model[32];
72*6f351a7aSTitus Rwantare     char mfr_rev[8];
73*6f351a7aSTitus Rwantare } ADM1266State;
74*6f351a7aSTitus Rwantare 
75*6f351a7aSTitus Rwantare static const uint8_t adm1266_ic_device_id[] = {0x03, 0x41, 0x12, 0x66};
76*6f351a7aSTitus Rwantare static const uint8_t adm1266_ic_device_rev[] = {0x08, 0x01, 0x08, 0x07, 0x0,
77*6f351a7aSTitus Rwantare                                                 0x0, 0x07, 0x41, 0x30};
78*6f351a7aSTitus Rwantare 
79*6f351a7aSTitus Rwantare static void adm1266_exit_reset(Object *obj)
80*6f351a7aSTitus Rwantare {
81*6f351a7aSTitus Rwantare     ADM1266State *s = ADM1266(obj);
82*6f351a7aSTitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
83*6f351a7aSTitus Rwantare 
84*6f351a7aSTitus Rwantare     pmdev->page = 0;
85*6f351a7aSTitus Rwantare     pmdev->capability = ADM1266_CAPABILITY_NO_PEC;
86*6f351a7aSTitus Rwantare 
87*6f351a7aSTitus Rwantare     for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
88*6f351a7aSTitus Rwantare         pmdev->pages[i].operation = ADM1266_OPERATION_DEFAULT;
89*6f351a7aSTitus Rwantare         pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT;
90*6f351a7aSTitus Rwantare         pmdev->pages[i].vout_mode = 0;
91*6f351a7aSTitus Rwantare         pmdev->pages[i].read_vout = pmbus_data2linear_mode(12, 0);
92*6f351a7aSTitus Rwantare         pmdev->pages[i].vout_margin_high = pmbus_data2linear_mode(15, 0);
93*6f351a7aSTitus Rwantare         pmdev->pages[i].vout_margin_low = pmbus_data2linear_mode(3, 0);
94*6f351a7aSTitus Rwantare         pmdev->pages[i].vout_ov_fault_limit = pmbus_data2linear_mode(16, 0);
95*6f351a7aSTitus Rwantare         pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT;
96*6f351a7aSTitus Rwantare     }
97*6f351a7aSTitus Rwantare 
98*6f351a7aSTitus Rwantare     strncpy(s->mfr_id, ADM1266_MFR_ID_DEFAULT, 4);
99*6f351a7aSTitus Rwantare     strncpy(s->mfr_model, ADM1266_MFR_MODEL_DEFAULT, 11);
100*6f351a7aSTitus Rwantare     strncpy(s->mfr_rev, ADM1266_MFR_REVISION_DEFAULT, 3);
101*6f351a7aSTitus Rwantare }
102*6f351a7aSTitus Rwantare 
103*6f351a7aSTitus Rwantare static uint8_t adm1266_read_byte(PMBusDevice *pmdev)
104*6f351a7aSTitus Rwantare {
105*6f351a7aSTitus Rwantare     ADM1266State *s = ADM1266(pmdev);
106*6f351a7aSTitus Rwantare 
107*6f351a7aSTitus Rwantare     switch (pmdev->code) {
108*6f351a7aSTitus Rwantare     case PMBUS_MFR_ID:                    /* R/W block */
109*6f351a7aSTitus Rwantare         pmbus_send_string(pmdev, s->mfr_id);
110*6f351a7aSTitus Rwantare         break;
111*6f351a7aSTitus Rwantare 
112*6f351a7aSTitus Rwantare     case PMBUS_MFR_MODEL:                 /* R/W block */
113*6f351a7aSTitus Rwantare         pmbus_send_string(pmdev, s->mfr_model);
114*6f351a7aSTitus Rwantare         break;
115*6f351a7aSTitus Rwantare 
116*6f351a7aSTitus Rwantare     case PMBUS_MFR_REVISION:              /* R/W block */
117*6f351a7aSTitus Rwantare         pmbus_send_string(pmdev, s->mfr_rev);
118*6f351a7aSTitus Rwantare         break;
119*6f351a7aSTitus Rwantare 
120*6f351a7aSTitus Rwantare     case PMBUS_IC_DEVICE_ID:
121*6f351a7aSTitus Rwantare         pmbus_send(pmdev, adm1266_ic_device_id, sizeof(adm1266_ic_device_id));
122*6f351a7aSTitus Rwantare         break;
123*6f351a7aSTitus Rwantare 
124*6f351a7aSTitus Rwantare     case PMBUS_IC_DEVICE_REV:
125*6f351a7aSTitus Rwantare         pmbus_send(pmdev, adm1266_ic_device_rev, sizeof(adm1266_ic_device_rev));
126*6f351a7aSTitus Rwantare         break;
127*6f351a7aSTitus Rwantare 
128*6f351a7aSTitus Rwantare     default:
129*6f351a7aSTitus Rwantare         qemu_log_mask(LOG_UNIMP,
130*6f351a7aSTitus Rwantare                       "%s: reading from unimplemented register: 0x%02x\n",
131*6f351a7aSTitus Rwantare                       __func__, pmdev->code);
132*6f351a7aSTitus Rwantare         return 0xFF;
133*6f351a7aSTitus Rwantare     }
134*6f351a7aSTitus Rwantare 
135*6f351a7aSTitus Rwantare     return 0;
136*6f351a7aSTitus Rwantare }
137*6f351a7aSTitus Rwantare 
138*6f351a7aSTitus Rwantare static int adm1266_write_data(PMBusDevice *pmdev, const uint8_t *buf,
139*6f351a7aSTitus Rwantare                               uint8_t len)
140*6f351a7aSTitus Rwantare {
141*6f351a7aSTitus Rwantare     ADM1266State *s = ADM1266(pmdev);
142*6f351a7aSTitus Rwantare 
143*6f351a7aSTitus Rwantare     switch (pmdev->code) {
144*6f351a7aSTitus Rwantare     case PMBUS_MFR_ID:                    /* R/W block */
145*6f351a7aSTitus Rwantare         pmbus_receive_block(pmdev, (uint8_t *)s->mfr_id, sizeof(s->mfr_id));
146*6f351a7aSTitus Rwantare         break;
147*6f351a7aSTitus Rwantare 
148*6f351a7aSTitus Rwantare     case PMBUS_MFR_MODEL:                 /* R/W block */
149*6f351a7aSTitus Rwantare         pmbus_receive_block(pmdev, (uint8_t *)s->mfr_model,
150*6f351a7aSTitus Rwantare                             sizeof(s->mfr_model));
151*6f351a7aSTitus Rwantare         break;
152*6f351a7aSTitus Rwantare 
153*6f351a7aSTitus Rwantare     case PMBUS_MFR_REVISION:               /* R/W block*/
154*6f351a7aSTitus Rwantare         pmbus_receive_block(pmdev, (uint8_t *)s->mfr_rev, sizeof(s->mfr_rev));
155*6f351a7aSTitus Rwantare         break;
156*6f351a7aSTitus Rwantare 
157*6f351a7aSTitus Rwantare     case ADM1266_SET_RTC:   /* do nothing */
158*6f351a7aSTitus Rwantare         break;
159*6f351a7aSTitus Rwantare 
160*6f351a7aSTitus Rwantare     default:
161*6f351a7aSTitus Rwantare         qemu_log_mask(LOG_UNIMP,
162*6f351a7aSTitus Rwantare                       "%s: writing to unimplemented register: 0x%02x\n",
163*6f351a7aSTitus Rwantare                       __func__, pmdev->code);
164*6f351a7aSTitus Rwantare         break;
165*6f351a7aSTitus Rwantare     }
166*6f351a7aSTitus Rwantare     return 0;
167*6f351a7aSTitus Rwantare }
168*6f351a7aSTitus Rwantare 
169*6f351a7aSTitus Rwantare static void adm1266_get(Object *obj, Visitor *v, const char *name, void *opaque,
170*6f351a7aSTitus Rwantare                         Error **errp)
171*6f351a7aSTitus Rwantare {
172*6f351a7aSTitus Rwantare     uint16_t value;
173*6f351a7aSTitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
174*6f351a7aSTitus Rwantare     PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode;
175*6f351a7aSTitus Rwantare 
176*6f351a7aSTitus Rwantare     if (strcmp(name, "vout") == 0) {
177*6f351a7aSTitus Rwantare         value = pmbus_linear_mode2data(*(uint16_t *)opaque, mode->exp);
178*6f351a7aSTitus Rwantare     } else {
179*6f351a7aSTitus Rwantare         value = *(uint16_t *)opaque;
180*6f351a7aSTitus Rwantare     }
181*6f351a7aSTitus Rwantare 
182*6f351a7aSTitus Rwantare     visit_type_uint16(v, name, &value, errp);
183*6f351a7aSTitus Rwantare }
184*6f351a7aSTitus Rwantare 
185*6f351a7aSTitus Rwantare static void adm1266_set(Object *obj, Visitor *v, const char *name, void *opaque,
186*6f351a7aSTitus Rwantare                         Error **errp)
187*6f351a7aSTitus Rwantare {
188*6f351a7aSTitus Rwantare     uint16_t *internal = opaque;
189*6f351a7aSTitus Rwantare     uint16_t value;
190*6f351a7aSTitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
191*6f351a7aSTitus Rwantare     PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode;
192*6f351a7aSTitus Rwantare 
193*6f351a7aSTitus Rwantare     if (!visit_type_uint16(v, name, &value, errp)) {
194*6f351a7aSTitus Rwantare         return;
195*6f351a7aSTitus Rwantare     }
196*6f351a7aSTitus Rwantare 
197*6f351a7aSTitus Rwantare     *internal = pmbus_data2linear_mode(value, mode->exp);
198*6f351a7aSTitus Rwantare     pmbus_check_limits(pmdev);
199*6f351a7aSTitus Rwantare }
200*6f351a7aSTitus Rwantare 
201*6f351a7aSTitus Rwantare static const VMStateDescription vmstate_adm1266 = {
202*6f351a7aSTitus Rwantare     .name = "ADM1266",
203*6f351a7aSTitus Rwantare     .version_id = 0,
204*6f351a7aSTitus Rwantare     .minimum_version_id = 0,
205*6f351a7aSTitus Rwantare     .fields = (VMStateField[]){
206*6f351a7aSTitus Rwantare         VMSTATE_PMBUS_DEVICE(parent, ADM1266State),
207*6f351a7aSTitus Rwantare         VMSTATE_END_OF_LIST()
208*6f351a7aSTitus Rwantare     }
209*6f351a7aSTitus Rwantare };
210*6f351a7aSTitus Rwantare 
211*6f351a7aSTitus Rwantare static void adm1266_init(Object *obj)
212*6f351a7aSTitus Rwantare {
213*6f351a7aSTitus Rwantare     PMBusDevice *pmdev = PMBUS_DEVICE(obj);
214*6f351a7aSTitus Rwantare     uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VOUT_MARGIN |
215*6f351a7aSTitus Rwantare                      PB_HAS_VOUT_RATING | PB_HAS_STATUS_MFR_SPECIFIC;
216*6f351a7aSTitus Rwantare 
217*6f351a7aSTitus Rwantare     for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
218*6f351a7aSTitus Rwantare         pmbus_page_config(pmdev, i, flags);
219*6f351a7aSTitus Rwantare 
220*6f351a7aSTitus Rwantare         object_property_add(obj, "vout[*]", "uint16",
221*6f351a7aSTitus Rwantare                             adm1266_get,
222*6f351a7aSTitus Rwantare                             adm1266_set, NULL, &pmdev->pages[i].read_vout);
223*6f351a7aSTitus Rwantare     }
224*6f351a7aSTitus Rwantare }
225*6f351a7aSTitus Rwantare 
226*6f351a7aSTitus Rwantare static void adm1266_class_init(ObjectClass *klass, void *data)
227*6f351a7aSTitus Rwantare {
228*6f351a7aSTitus Rwantare     ResettableClass *rc = RESETTABLE_CLASS(klass);
229*6f351a7aSTitus Rwantare     DeviceClass *dc = DEVICE_CLASS(klass);
230*6f351a7aSTitus Rwantare     PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
231*6f351a7aSTitus Rwantare 
232*6f351a7aSTitus Rwantare     dc->desc = "Analog Devices ADM1266 Hot Swap controller";
233*6f351a7aSTitus Rwantare     dc->vmsd = &vmstate_adm1266;
234*6f351a7aSTitus Rwantare     k->write_data = adm1266_write_data;
235*6f351a7aSTitus Rwantare     k->receive_byte = adm1266_read_byte;
236*6f351a7aSTitus Rwantare     k->device_num_pages = 17;
237*6f351a7aSTitus Rwantare 
238*6f351a7aSTitus Rwantare     rc->phases.exit = adm1266_exit_reset;
239*6f351a7aSTitus Rwantare }
240*6f351a7aSTitus Rwantare 
241*6f351a7aSTitus Rwantare static const TypeInfo adm1266_info = {
242*6f351a7aSTitus Rwantare     .name = TYPE_ADM1266,
243*6f351a7aSTitus Rwantare     .parent = TYPE_PMBUS_DEVICE,
244*6f351a7aSTitus Rwantare     .instance_size = sizeof(ADM1266State),
245*6f351a7aSTitus Rwantare     .instance_init = adm1266_init,
246*6f351a7aSTitus Rwantare     .class_init = adm1266_class_init,
247*6f351a7aSTitus Rwantare };
248*6f351a7aSTitus Rwantare 
249*6f351a7aSTitus Rwantare static void adm1266_register_types(void)
250*6f351a7aSTitus Rwantare {
251*6f351a7aSTitus Rwantare     type_register_static(&adm1266_info);
252*6f351a7aSTitus Rwantare }
253*6f351a7aSTitus Rwantare 
254*6f351a7aSTitus Rwantare type_init(adm1266_register_types)
255