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