Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docs/opentitan/earlgrey.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ Devices in this group implement subset(s) of the real HW.
* Initialization and scrambling with dummy key supported
* Wait for init completion (bus stall) emulated
* I2C controller
* Supports only one target mode address - ADDRESS1 and MASK1 are not implemented
* Timing features are not implemented
* Only 7-bit addressing with all MASK0 bits set is supported
* Loopback mode is not implemented

### Sparsely implemented devices
Expand Down
48 changes: 35 additions & 13 deletions hw/opentitan/ot_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* bus recover/override
* some interrupts will never be generated (except via INTR_TEST)
* bus timing registers are ignored
* - Target mode only supports TARGET_ID.ADDRESS0 with TARGET_ID.MASK0=0x7F.
* - Target mode only supports TARGET_ID.ADDRESS0 and TARGET_ID.MASK0
* - Loopback mode. Need more details about how it works in HW.
*/

Expand Down Expand Up @@ -352,6 +352,9 @@ struct OtI2CState {
/* TX: Scheduled responses for target mode. */
Fifo8 target_tx_fifo;

/* Target mode first address mask */
uint8_t address_mask_0;

uint32_t pclk; /* Current input clock */
const char *clock_src_name; /* IRQ name once connected */

Expand Down Expand Up @@ -982,22 +985,19 @@ static void ot_i2c_write(void *opaque, hwaddr addr, uint64_t val64,
break;
case R_TARGET_ID:
if (FIELD_EX32(val32, TARGET_ID, ADDRESS1)) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: %s: Target address 1 not supported.\n", __func__,
s->ot_id);
}
address = FIELD_EX32(val32, TARGET_ID, ADDRESS0);
if ((val32 & R_TARGET_ID_MASK0_MASK) != R_TARGET_ID_MASK0_MASK) {
qemu_log_mask(
LOG_UNIMP,
"%s: %s: Address Mask with any bits unset is not supported.\n",
"%s: %s: Target mode second address is not supported.\n",
__func__, s->ot_id);
break;
}
if (address != 0) {
ARRAY_FIELD_DP32(s->regs, TARGET_ID, ADDRESS0, address);
mask = FIELD_EX32(val32, TARGET_ID, MASK0);
ARRAY_FIELD_DP32(s->regs, TARGET_ID, MASK0, mask);
address = FIELD_EX32(val32, TARGET_ID, ADDRESS0);
mask = FIELD_EX32(val32, TARGET_ID, MASK0);

ARRAY_FIELD_DP32(s->regs, TARGET_ID, ADDRESS0, address);
ARRAY_FIELD_DP32(s->regs, TARGET_ID, MASK0, mask);
/* Update the address mask of this target on the bus. */
s->address_mask_0 = (uint8_t)mask;
if (address != 0u) {
/* Update the address of this target on the bus. */
i2c_slave_set_address(s->target, address);
}
Expand Down Expand Up @@ -1238,13 +1238,34 @@ static void ot_i2c_target_send_async(I2CSlave *target, uint8_t data)
}
}

static bool ot_i2c_target_match_and_add(I2CSlave *candidate, uint8_t address,
bool broadcast,
I2CNodeList *current_devs)
{
BusState *abus = qdev_get_parent_bus(DEVICE(candidate));
OtI2CState *s = OT_I2C(abus->parent);

/* Check address, subject to address masking. */
if (broadcast || (s->address_mask_0 &&
(address & s->address_mask_0) == candidate->address)) {
I2CNode *node = g_new0(struct I2CNode, 1u);
node->elt = candidate;
QLIST_INSERT_HEAD(current_devs, node, next);
return true;
}

/* Not found and not broadcast. */
return false;
}

static void ot_i2c_target_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
(void)data;

dc->desc = "OpenTitan I2C Target";
sc->match_and_add = &ot_i2c_target_match_and_add;
sc->event = &ot_i2c_target_event;
sc->send_async = &ot_i2c_target_send_async;
sc->recv = &ot_i2c_target_recv;
Expand Down Expand Up @@ -1308,6 +1329,7 @@ static void ot_i2c_reset_enter(Object *obj, ResetType type)
}

s->check_timings = true;
s->address_mask_0 = 0x0u;
}

static void ot_i2c_realize(DeviceState *dev, Error **errp)
Expand Down
Loading