Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/opentitan/i2c_transport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# OpenTitan I2C Transport

The `ot-i2c-transport` device provides a way to execute I2C transactions with QEMU I2C bus devices
through an externally driven `chardev` interface.

The device can perform read or write transactions with bus devices and is intended to be used to
drive the Opentitan I2C device Target mode functionality through `opentitanlib`.

## Configuration

A chardev and transport device can be created in QEMU with the following arguments:

`-chardev pty,i2ctp -device ot-i2c-transport,bus=<bus>,chardev=i2ctp`

Where `bus` is any of the system's I2C buses. On earlgrey for example, these are `ot-i2c0`,
`ot-i2c1`, and `ot-i2c2`.

## Protocol

- The protocol over the chardev mirrors the I2C specification closely.

- A start and repeated start condition is signalled with a `s` byte, followed by a byte containing
the 7-bit target address of the transaction and the read/write bit as the least significant bit
(0 indicates a write transfer, 1 indicates a read transfer).

- On a repeated start condition, the I2C target address of the first transfer will be used, and

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand how you discriminate signaling (START/STOP/RESTART/ACK/NACK) from payload.

For example in b's123s123S', is the second s a restart or the 4th byte of the payload?

the provided address is ignored (transfer direction can be changed). This is a limitation of QEMU.

- When a read transfer is active, a `R` byte can be sent to read a byte of data from the target
I2C device.

- When a write transfer is active, a `W` byte, followed by a data byte, can be sent to write that
byte to the target I2C device.

- A stop condition to end the transaction is signalled with a `S` byte.

- Following a start/repeated start condition and the address byte, as well as after every byte
written during a write transfer, an ACK is signalled with a `.` byte, and a NACK is signalled with
a `!` byte. If the start of a transfer is NACKed, the transaction should be terminated with a stop
condition.

- On a parser error, a `x` byte will be returned, and the parser will not accept any more command
bytes until reset.

- The implementation will throttle processing of written bytes to allow control to return to the
vCPU, so that OT I2C may process the current transaction and prepare response bytes for upcoming
reads.
4 changes: 4 additions & 0 deletions hw/opentitan/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ config OT_I2C
select I2C
bool

config OT_I2C_TRANSPORT
select I2C
bool

config OT_IBEX_WRAPPER
select OT_VMAPPER
bool
Expand Down
1 change: 1 addition & 0 deletions hw/opentitan/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ system_ss.add(when: 'CONFIG_OT_GPIO_DJ', if_true: files('ot_gpio_dj.c'))
system_ss.add(when: 'CONFIG_OT_GPIO_EG', if_true: files('ot_gpio_eg.c'))
system_ss.add(when: 'CONFIG_OT_HMAC', if_true: [files('ot_hmac.c'), libtomcrypt_dep])
system_ss.add(when: 'CONFIG_OT_I2C', if_true: files('ot_i2c.c'))
system_ss.add(when: 'CONFIG_OT_I2C_TRANSPORT', if_true: files('ot_i2c_transport.c'))
system_ss.add(when: 'CONFIG_OT_IBEX_WRAPPER', if_true: files('ot_ibex_wrapper.c'))
system_ss.add(when: 'CONFIG_OT_KEY_SINK', if_true: files('ot_key_sink.c'))
system_ss.add(when: 'CONFIG_OT_KEYMGR', if_true: files('ot_keymgr.c'))
Expand Down
18 changes: 17 additions & 1 deletion hw/opentitan/ot_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -1165,10 +1165,13 @@ static int ot_i2c_target_event(I2CSlave *target, enum i2c_event event)
}

switch (event) {
case I2C_START_SEND:
case I2C_START_SEND_ASYNC:
/* Set the first byte to the target address + RW bit as 0. */
ot_i2c_target_set_acqdata(s, target->address << 1u, SIGNAL_START);
i2c_ack(s->bus);
if (event == I2C_START_SEND_ASYNC) {
i2c_ack(s->bus);
}
break;
case I2C_START_RECV:
/* Set the first byte to the target address + RW bit as 1. */
Expand Down Expand Up @@ -1226,6 +1229,18 @@ static uint8_t ot_i2c_target_recv(I2CSlave *target)
return data;
}

static int ot_i2c_target_send(I2CSlave *target, uint8_t data)
{
BusState *abus = qdev_get_parent_bus(DEVICE(target));
OtI2CState *s = OT_I2C(abus->parent);
if (!ot_i2c_target_enabled(s)) {
return -1;
}

ot_i2c_target_set_acqdata(s, data, SIGNAL_NONE);
return 0;
}

static void ot_i2c_target_send_async(I2CSlave *target, uint8_t data)
{
BusState *abus = qdev_get_parent_bus(DEVICE(target));
Expand All @@ -1246,6 +1261,7 @@ static void ot_i2c_target_class_init(ObjectClass *klass, void *data)

dc->desc = "OpenTitan I2C Target";
sc->event = &ot_i2c_target_event;
sc->send = &ot_i2c_target_send;
sc->send_async = &ot_i2c_target_send_async;
sc->recv = &ot_i2c_target_recv;
}
Expand Down
Loading
Loading