xref: /openbmc/qemu/hw/misc/ibm-cffps.c (revision 7d87775f)
1 /*
2  * IBM CFF power supplies device
3  *
4  * Copyright (c) 2019 IBM Corporation.
5  *
6  * This code is licensed under the GPL version 2 or later. See the
7  * COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "hw/hw.h"
12 #include "hw/i2c/i2c.h"
13 #include "qapi/error.h"
14 #include "qapi/visitor.h"
15 #include "hw/misc/pmbus_regs.h"
16 #include "migration/vmstate.h"
17 
18 #define CFFPS_CCIN                              0xBD
19 #define CFFPS_FW_CMD_START                      0xFA
20 #define CFFPS_FW_NUM_BYTES                      4
21 #define CFFPS_SYS_CONFIG                        0xDA
22 #define CFFPS_INPUT_HISTORY                     0xD6
23 #define CFFPS_INPUT_HISTORY_SIZE                100
24 
25 #define FW_VERSION      0x04030201
26 #define CCIN            0x2B1D
27 
28 typedef struct IBMCffpsState {
29     /*< private >*/
30     I2CSlave i2c;
31     /*< public >*/
32 
33     uint8_t regs[0x100];
34 
35     uint8_t len;
36     uint8_t buf[32];
37     uint8_t pointer;
38 
39 } IBMCffpsState;
40 
41 #define TYPE_IBM_CFFPS "ibm-cffps"
42 #define IBM_CFFPS(obj) OBJECT_CHECK(IBMCffpsState, (obj), TYPE_IBM_CFFPS)
43 
44 static void ibm_cffps_read(IBMCffpsState *s)
45 {
46     s->len = 0;
47 
48     switch (s->pointer) {
49     case PMBUS_MFR_MODEL: /* FRU */
50         s->buf[s->len++] = 7;  /* Byte count */
51         s->buf[s->len++] = 'F';
52         s->buf[s->len++] = 'R';
53         s->buf[s->len++] = 'U';
54         s->buf[s->len++] = '0';
55         s->buf[s->len++] = '1';
56         s->buf[s->len++] = '2';
57         s->buf[s->len++] = '3';
58         break;
59     case PMBUS_MFR_REVISION: /* PART NUMBER */
60         s->buf[s->len++] = 7;   /* Byte count */
61         s->buf[s->len++] = 'P';
62         s->buf[s->len++] = 'A';
63         s->buf[s->len++] = 'R';
64         s->buf[s->len++] = '0';
65         s->buf[s->len++] = '1';
66         s->buf[s->len++] = '2';
67         s->buf[s->len++] = '3';
68         break;
69     case PMBUS_MFR_SERIAL:
70         s->buf[s->len++] = 6;   /* Byte count */
71         s->buf[s->len++] = 'X';
72         s->buf[s->len++] = 'Y';
73         s->buf[s->len++] = 'Z';
74         s->buf[s->len++] = '0';
75         s->buf[s->len++] = '1';
76         s->buf[s->len++] = '2';
77         break;
78     case CFFPS_CCIN:
79         s->buf[s->len++] = (CCIN >> 8) & 0xFF;
80         s->buf[s->len++] = CCIN & 0xFF;
81         break;
82     case CFFPS_FW_CMD_START ... CFFPS_FW_CMD_START + CFFPS_FW_NUM_BYTES - 1:
83         s->buf[s->len++] = (FW_VERSION >> (CFFPS_FW_NUM_BYTES -
84                            (s->pointer - CFFPS_FW_CMD_START) - 1) * 8) & 0xFF;
85         break;
86     case CFFPS_INPUT_HISTORY: /* TODO */
87         s->buf[s->len++] = 0x0;
88         break;
89     default:
90         s->buf[s->len++] = s->regs[s->pointer];
91         break;
92     }
93 }
94 
95 static void ibm_cffps_write(IBMCffpsState *s)
96 {
97     switch (s->pointer) {
98     case CFFPS_SYS_CONFIG:
99         s->regs[s->pointer] = s->buf[0];
100         break;
101     }
102 }
103 
104 static uint8_t ibm_cffps_recv(I2CSlave *i2c)
105 {
106     IBMCffpsState *s = IBM_CFFPS(i2c);
107 
108     if (s->len < sizeof(s->buf)) {
109         return s->buf[s->len++];
110     } else {
111         return 0xff;
112     }
113 }
114 
115 static int ibm_cffps_send(I2CSlave *i2c, uint8_t data)
116 {
117     IBMCffpsState *s = IBM_CFFPS(i2c);
118 
119     if (s->len == 0) {
120         /*
121          * first byte is the register pointer for a read or write
122          * operation
123          */
124         s->pointer = data;
125         s->len++;
126     } else {
127         /*
128          * next bytes are data to write.
129          */
130         if (s->len <= sizeof(s->buf)) {
131             s->buf[s->len - 1] = data;
132         }
133         s->len++;
134         ibm_cffps_write(s);
135     }
136 
137     return 0;
138 }
139 
140 static int ibm_cffps_event(I2CSlave *i2c, enum i2c_event event)
141 {
142     IBMCffpsState *s = IBM_CFFPS(i2c);
143 
144     /* TODO: handle SMBus "block read" */
145 
146     switch (event) {
147     case I2C_START_RECV:
148         ibm_cffps_read(s);
149         break;
150     case I2C_START_SEND:
151     case I2C_NACK:
152     case I2C_FINISH:
153          s->pointer = 0xFF;
154         break;
155     default:
156         return -1;
157     }
158 
159     s->len = 0;
160     return 0;
161 }
162 
163 static const VMStateDescription vmstate_ibm_cffps = {
164     .name = TYPE_IBM_CFFPS,
165     .version_id = 0,
166     .minimum_version_id = 0,
167     .fields = (VMStateField[]) {
168         VMSTATE_UINT8(len, IBMCffpsState),
169         VMSTATE_UINT8_ARRAY(buf, IBMCffpsState, 32),
170         VMSTATE_UINT8(pointer, IBMCffpsState),
171         VMSTATE_UINT8_ARRAY(regs, IBMCffpsState, 0x100),
172         VMSTATE_I2C_SLAVE(i2c, IBMCffpsState),
173         VMSTATE_END_OF_LIST()
174     }
175 };
176 
177 static void ibm_cffps_reset(DeviceState *dev)
178 {
179     IBMCffpsState *s = IBM_CFFPS(dev);
180 
181     memset(s->regs, 0, sizeof(s->regs));
182     s->pointer = 0xFF;
183 }
184 
185 static void ibm_cffps_realize(DeviceState *dev, Error **errp)
186 {
187     ;
188 }
189 
190 static void ibm_cffps_class_init(ObjectClass *klass, void *data)
191 {
192     DeviceClass *dc = DEVICE_CLASS(klass);
193     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
194 
195     dc->realize = ibm_cffps_realize;
196     device_class_set_legacy_reset(dc, ibm_cffps_reset);
197     k->event = ibm_cffps_event;
198     k->recv = ibm_cffps_recv;
199     k->send = ibm_cffps_send;
200     dc->vmsd = &vmstate_ibm_cffps;
201 }
202 
203 static const TypeInfo ibm_cffps_info = {
204     .name          = TYPE_IBM_CFFPS,
205     .parent        = TYPE_I2C_SLAVE,
206     .instance_size = sizeof(IBMCffpsState),
207     .class_init    = ibm_cffps_class_init,
208 };
209 
210 static void ibm_cffps_register_types(void)
211 {
212     type_register_static(&ibm_cffps_info);
213 }
214 
215 type_init(ibm_cffps_register_types)
216