Skip to content

Commit c990a45

Browse files
committed
[ot] hw/opentitan: ot_i2c_transport: initial implementation
Initial implementation. See top comment on description of the wire protocol. Signed-off-by: Alice Ziuziakowska <[email protected]>
1 parent 14b58e0 commit c990a45

File tree

1 file changed

+214
-6
lines changed

1 file changed

+214
-6
lines changed

hw/opentitan/ot_i2c_transport.c

Lines changed: 214 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,35 +25,240 @@
2525
* THE SOFTWARE.
2626
*/
2727

28+
/*
29+
* This device provides a method of performing I2C transactions with bus
30+
* devices in QEMU through an external chardev interface.
31+
* The device can issue read or write transactions to devices on the bus.
32+
* This is intended to be used by OpenTitanTool, but can be driven manually
33+
* for example with `netcat`.
34+
*
35+
* The protocol for issuing commands is as follows:
36+
*
37+
* - A write command begins with 'W', followed by an address byte to select the
38+
* target device. All subsequent bytes are payload bytes to be sent to the
39+
* device.
40+
*
41+
* - A read command begins with 'R', followed by an address byte to select the
42+
* target device. A byte is read and written back for every subsequent 'B'
43+
* byte.
44+
*
45+
* - All bytes values are encoded as a pair of case-insensitive ASCII hex
46+
* digits.
47+
*
48+
* - End of a command is indicated with an 'S', this ends the transaction.
49+
*
50+
* - For both commands, the address byte chosen will be echoed back if a device
51+
* with that address is found, otherwise '!!' will be sent.
52+
*
53+
* - For write commands, a written byte will be echoed back on ACK, otherwise
54+
* '!!' will be sent back on NACK.
55+
*
56+
* - Spaces and newlines in a sent command may appear anywhere except between
57+
* the pair of hex digits making up a byte, and are ignored. Unexpected
58+
* characters anywhere in a command will cause the current command to end and
59+
* command parser restarted.
60+
*
61+
* - There are no timing restrictions on the protocol. It is recommended to
62+
* buffer read commands to the OT I2C target device to allow OT software to
63+
* fill its FIFO with response data. Readiness of response should be
64+
* indicated via another mechanism. There is no requirement to do this for
65+
* other QEMU I2C devices.
66+
*
67+
* Examples:
68+
*
69+
* - Writing bytes 0xAB, 0xCD, 0xEF, to a device with address 0x51:
70+
*
71+
* W 51 ab cd ef S
72+
*
73+
* With the following output if the device exists and acknowledged all bytes:
74+
*
75+
* 51abcdef (address byte, followed by echoed payload data)
76+
*
77+
* - Reading 4 bytes from the device with address 0x51:
78+
*
79+
* R 51 B B B B S
80+
*
81+
* With the following example output from the device if the device exists:
82+
*
83+
* 51aabbccdd (address byte, followed by example read output)
84+
*/
85+
2886
#include "qemu/osdep.h"
87+
#include "qemu/fifo8.h"
2988
#include "qemu/typedefs.h"
3089
#include "qom/object.h"
3190
#include "chardev/char-fe.h"
3291
#include "hw/i2c/i2c.h"
3392
#include "hw/qdev-properties-system.h"
3493
#include "hw/qdev-properties.h"
94+
#include "trace.h"
3595

3696
#define TYPE_OT_I2C_TRANSPORT "ot-i2c-transport"
3797
OBJECT_DECLARE_SIMPLE_TYPE(OtI2CTransportState, OT_I2C_TRANSPORT)
3898

99+
#define CMD_FIFO_SIZE 256u
100+
101+
enum i2c_cmd_parser_state {
102+
OT_I2C_CMD_INIT,
103+
OT_I2C_CMD_ADDR_1,
104+
OT_I2C_CMD_ADDR_2,
105+
OT_I2C_CMD_PAYLOAD_1,
106+
OT_I2C_CMD_PAYLOAD_2,
107+
OT_I2C_CMD_BYTE,
108+
};
109+
39110
struct OtI2CTransportState {
40111
I2CSlave parent_obj;
41112

113+
Fifo8 cmd_bytes_fifo;
114+
enum i2c_cmd_parser_state parser_state;
115+
bool is_read_command;
116+
uint8_t byte;
117+
42118
I2CBus *bus;
43119
CharBackend chr;
44120
};
45121

122+
static void ot_i2c_transport_put_send_failure(OtI2CTransportState *s)
123+
{
124+
uint8_t fmt_byte[2] = { '!', '!' };
125+
qemu_chr_fe_write(&s->chr, fmt_byte, ARRAY_SIZE(fmt_byte));
126+
}
127+
128+
static void
129+
ot_i2c_transport_put_formatted_byte(OtI2CTransportState *s, uint8_t data)
130+
{
131+
char hex[16] = "0123456789ABCDEF";
132+
uint8_t fmt_byte[2] = { hex[data >> 4u], hex[data & 0xfu] };
133+
qemu_chr_fe_write(&s->chr, fmt_byte, ARRAY_SIZE(fmt_byte));
134+
}
135+
136+
static void ot_i2c_transport_start_transfer(OtI2CTransportState *s)
137+
{
138+
if (i2c_start_transfer(s->bus, s->byte, s->is_read_command) != 0) {
139+
ot_i2c_transport_put_send_failure(s);
140+
} else {
141+
ot_i2c_transport_put_formatted_byte(s, s->byte);
142+
}
143+
}
144+
145+
static void ot_i2c_transport_do_read(OtI2CTransportState *s)
146+
{
147+
ot_i2c_transport_put_formatted_byte(s, i2c_recv(s->bus));
148+
}
149+
150+
static void ot_i2c_transport_do_write(OtI2CTransportState *s)
151+
{
152+
if (i2c_send(s->bus, s->byte) != 0) {
153+
ot_i2c_transport_put_send_failure(s);
154+
} else {
155+
ot_i2c_transport_put_formatted_byte(s, s->byte);
156+
}
157+
}
158+
159+
static void ot_i2c_transport_command_byte(OtI2CTransportState *s, uint8_t byte)
160+
{
161+
int nibble;
162+
switch (s->parser_state) {
163+
case OT_I2C_CMD_INIT:
164+
// parse either a R or W for a read or write command
165+
if (byte != 'R' && byte != 'W') {
166+
break;
167+
}
168+
s->is_read_command = byte == 'R';
169+
s->byte = 0u;
170+
s->parser_state = OT_I2C_CMD_ADDR_1;
171+
break;
172+
case OT_I2C_CMD_ADDR_1:
173+
// parse first nibble of address byte
174+
if (byte == '\n' || byte == ' ') {
175+
break;
176+
}
177+
nibble = g_ascii_xdigit_value(byte);
178+
if (nibble < 0) {
179+
s->parser_state = OT_I2C_CMD_INIT;
180+
break;
181+
}
182+
s->byte = (uint8_t)nibble;
183+
s->parser_state = OT_I2C_CMD_ADDR_2;
184+
break;
185+
case OT_I2C_CMD_ADDR_2:
186+
// parse second nibble of address byte
187+
nibble = g_ascii_xdigit_value(byte);
188+
if (nibble < 0) {
189+
s->parser_state = OT_I2C_CMD_INIT;
190+
break;
191+
}
192+
s->byte = (s->byte << 4u) | nibble;
193+
ot_i2c_transport_start_transfer(s);
194+
s->parser_state =
195+
s->is_read_command ? OT_I2C_CMD_BYTE : OT_I2C_CMD_PAYLOAD_1;
196+
break;
197+
case OT_I2C_CMD_PAYLOAD_1:
198+
// parse first nibble of write payload byte
199+
if (byte == '\n' || byte == ' ') {
200+
break;
201+
}
202+
// signal no more bytes to write with an S
203+
if (byte == 'S') {
204+
s->parser_state = OT_I2C_CMD_INIT;
205+
i2c_end_transfer(s->bus);
206+
break;
207+
}
208+
nibble = g_ascii_xdigit_value(byte);
209+
if (nibble < 0) {
210+
s->parser_state = OT_I2C_CMD_INIT;
211+
i2c_end_transfer(s->bus);
212+
break;
213+
}
214+
s->byte = (uint8_t)nibble;
215+
s->parser_state = OT_I2C_CMD_PAYLOAD_2;
216+
break;
217+
case OT_I2C_CMD_PAYLOAD_2:
218+
// parse second nibble of write payload byte
219+
nibble = g_ascii_xdigit_value(byte);
220+
if (nibble < 0) {
221+
s->parser_state = OT_I2C_CMD_INIT;
222+
i2c_end_transfer(s->bus);
223+
break;
224+
}
225+
s->byte = (s->byte << 4u) | nibble;
226+
ot_i2c_transport_do_write(s);
227+
s->parser_state = OT_I2C_CMD_PAYLOAD_1;
228+
break;
229+
case OT_I2C_CMD_BYTE:
230+
// parse a B to read a byte from the I2C bus
231+
if (byte == '\n' || byte == ' ') {
232+
break;
233+
}
234+
if (byte == 'B') {
235+
ot_i2c_transport_do_read(s);
236+
s->parser_state = OT_I2C_CMD_BYTE;
237+
} else {
238+
i2c_end_transfer(s->bus);
239+
s->parser_state = OT_I2C_CMD_INIT;
240+
}
241+
break;
242+
}
243+
}
244+
46245
static int ot_i2c_transport_can_receive(void *opaque)
47246
{
48-
(void)opaque;
49-
return 1;
247+
OtI2CTransportState *s = OT_I2C_TRANSPORT(opaque);
248+
return fifo8_num_free(&s->cmd_bytes_fifo);
50249
}
51250

52251
static void ot_i2c_transport_receive(void *opaque, const uint8_t *buf, int size)
53252
{
54-
(void)opaque;
55-
(void)buf;
56-
(void)size;
253+
OtI2CTransportState *s = OT_I2C_TRANSPORT(opaque);
254+
g_assert(fifo8_num_free(&s->cmd_bytes_fifo) >= size);
255+
fifo8_push_all(&s->cmd_bytes_fifo, buf, size);
256+
257+
// Drive the command state machine with each byte
258+
while (!fifo8_is_empty(&s->cmd_bytes_fifo)) {
259+
uint8_t byte = fifo8_pop(&s->cmd_bytes_fifo);
260+
ot_i2c_transport_command_byte(s, byte);
261+
}
57262
}
58263

59264
static void ot_i2c_transport_realize(DeviceState *dev, Error **errp)
@@ -69,7 +274,10 @@ static void ot_i2c_transport_realize(DeviceState *dev, Error **errp)
69274
static void ot_i2c_transport_init(Object *obj)
70275
{
71276
OtI2CTransportState *s = OT_I2C_TRANSPORT(obj);
72-
(void)s;
277+
fifo8_create(&s->cmd_bytes_fifo, CMD_FIFO_SIZE);
278+
s->parser_state = OT_I2C_CMD_INIT;
279+
s->is_read_command = false;
280+
s->byte = 0u;
73281
}
74282

75283
static Property ot_i2c_transport_properties[] = {

0 commit comments

Comments
 (0)