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
ibm_cffps_read(IBMCffpsState * s)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
ibm_cffps_write(IBMCffpsState * s)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
ibm_cffps_recv(I2CSlave * i2c)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
ibm_cffps_send(I2CSlave * i2c,uint8_t data)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
ibm_cffps_event(I2CSlave * i2c,enum i2c_event event)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
ibm_cffps_reset(DeviceState * dev)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
ibm_cffps_realize(DeviceState * dev,Error ** errp)185 static void ibm_cffps_realize(DeviceState *dev, Error **errp)
186 {
187 ;
188 }
189
ibm_cffps_class_init(ObjectClass * klass,void * data)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
ibm_cffps_register_types(void)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