xref: /openbmc/qemu/hw/i2c/smbus_slave.c (revision 9cf27d74a829f651c0da5d80c014a6cef9d4cbd8)
1 /*
2  * QEMU SMBus device emulation.
3  *
4  * This code is a helper for SMBus device emulation.  It implements an
5  * I2C device inteface and runs the SMBus protocol from the device
6  * point of view and maps those to simple calls to emulate.
7  *
8  * Copyright (c) 2007 CodeSourcery.
9  * Written by Paul Brook
10  *
11  * This code is licensed under the LGPL.
12  */
13 
14 /* TODO: Implement PEC.  */
15 
16 #include "qemu/osdep.h"
17 #include "hw/hw.h"
18 #include "hw/i2c/i2c.h"
19 #include "hw/i2c/smbus_slave.h"
20 
21 //#define DEBUG_SMBUS 1
22 
23 #ifdef DEBUG_SMBUS
24 #define DPRINTF(fmt, ...) \
25 do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
26 #define BADF(fmt, ...) \
27 do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
28 #else
29 #define DPRINTF(fmt, ...) do {} while(0)
30 #define BADF(fmt, ...) \
31 do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
32 #endif
33 
34 enum {
35     SMBUS_IDLE,
36     SMBUS_WRITE_DATA,
37     SMBUS_RECV_BYTE,
38     SMBUS_READ_DATA,
39     SMBUS_DONE,
40     SMBUS_CONFUSED = -1
41 };
42 
43 static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
44 {
45     SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
46 
47     DPRINTF("Quick Command %d\n", recv);
48     if (sc->quick_cmd) {
49         sc->quick_cmd(dev, recv);
50     }
51 }
52 
53 static void smbus_do_write(SMBusDevice *dev)
54 {
55     SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
56 
57     DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len);
58     if (sc->write_data) {
59         sc->write_data(dev, dev->data_buf, dev->data_len);
60     }
61 }
62 
63 static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
64 {
65     SMBusDevice *dev = SMBUS_DEVICE(s);
66 
67     switch (event) {
68     case I2C_START_SEND:
69         switch (dev->mode) {
70         case SMBUS_IDLE:
71             DPRINTF("Incoming data\n");
72             dev->mode = SMBUS_WRITE_DATA;
73             break;
74         default:
75             BADF("Unexpected send start condition in state %d\n", dev->mode);
76             dev->mode = SMBUS_CONFUSED;
77             break;
78         }
79         break;
80 
81     case I2C_START_RECV:
82         switch (dev->mode) {
83         case SMBUS_IDLE:
84             DPRINTF("Read mode\n");
85             dev->mode = SMBUS_RECV_BYTE;
86             break;
87         case SMBUS_WRITE_DATA:
88             if (dev->data_len == 0) {
89                 BADF("Read after write with no data\n");
90                 dev->mode = SMBUS_CONFUSED;
91             } else {
92                 smbus_do_write(dev);
93                 DPRINTF("Read mode\n");
94                 dev->data_len = 0;
95                 dev->mode = SMBUS_READ_DATA;
96             }
97             break;
98         default:
99             BADF("Unexpected recv start condition in state %d\n", dev->mode);
100             dev->mode = SMBUS_CONFUSED;
101             break;
102         }
103         break;
104 
105     case I2C_FINISH:
106         if (dev->data_len == 0) {
107             if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) {
108                 smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA);
109             }
110         } else {
111             switch (dev->mode) {
112             case SMBUS_WRITE_DATA:
113                 smbus_do_write(dev);
114                 break;
115 
116             case SMBUS_READ_DATA:
117                 BADF("Unexpected stop during receive\n");
118                 break;
119 
120             default:
121                 /* Nothing to do.  */
122                 break;
123             }
124         }
125         dev->mode = SMBUS_IDLE;
126         dev->data_len = 0;
127         break;
128 
129     case I2C_NACK:
130         switch (dev->mode) {
131         case SMBUS_DONE:
132             /* Nothing to do.  */
133             break;
134         case SMBUS_READ_DATA:
135             dev->mode = SMBUS_DONE;
136             break;
137         default:
138             BADF("Unexpected NACK in state %d\n", dev->mode);
139             dev->mode = SMBUS_CONFUSED;
140             break;
141         }
142     }
143 
144     return 0;
145 }
146 
147 static uint8_t smbus_i2c_recv(I2CSlave *s)
148 {
149     SMBusDevice *dev = SMBUS_DEVICE(s);
150     SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
151     uint8_t ret;
152 
153     switch (dev->mode) {
154     case SMBUS_RECV_BYTE:
155         if (sc->receive_byte) {
156             ret = sc->receive_byte(dev);
157         } else {
158             ret = 0;
159         }
160         DPRINTF("Receive Byte %02x\n", ret);
161         dev->mode = SMBUS_DONE;
162         break;
163     case SMBUS_READ_DATA:
164         if (sc->read_data) {
165             ret = sc->read_data(dev, dev->data_len);
166             dev->data_len++;
167         } else {
168             ret = 0;
169         }
170         DPRINTF("Read data %02x\n", ret);
171         break;
172     default:
173         BADF("Unexpected read in state %d\n", dev->mode);
174         dev->mode = SMBUS_CONFUSED;
175         ret = 0;
176         break;
177     }
178     return ret;
179 }
180 
181 static int smbus_i2c_send(I2CSlave *s, uint8_t data)
182 {
183     SMBusDevice *dev = SMBUS_DEVICE(s);
184 
185     switch (dev->mode) {
186     case SMBUS_WRITE_DATA:
187         DPRINTF("Write data %02x\n", data);
188         if (dev->data_len >= sizeof(dev->data_buf)) {
189             BADF("Too many bytes sent\n");
190         } else {
191             dev->data_buf[dev->data_len++] = data;
192         }
193         break;
194     default:
195         BADF("Unexpected write in state %d\n", dev->mode);
196         break;
197     }
198     return 0;
199 }
200 
201 static void smbus_device_class_init(ObjectClass *klass, void *data)
202 {
203     I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
204 
205     sc->event = smbus_i2c_event;
206     sc->recv = smbus_i2c_recv;
207     sc->send = smbus_i2c_send;
208 }
209 
210 static const TypeInfo smbus_device_type_info = {
211     .name = TYPE_SMBUS_DEVICE,
212     .parent = TYPE_I2C_SLAVE,
213     .instance_size = sizeof(SMBusDevice),
214     .abstract = true,
215     .class_size = sizeof(SMBusDeviceClass),
216     .class_init = smbus_device_class_init,
217 };
218 
219 static void smbus_device_register_types(void)
220 {
221     type_register_static(&smbus_device_type_info);
222 }
223 
224 type_init(smbus_device_register_types)
225