/* * QEMU I3C bus interface. * * Copyright 2023 Google LLC * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #ifndef QEMU_INCLUDE_HW_I3C_I3C_H_ #define QEMU_INCLUDE_HW_I3C_I3C_H_ #include "hw/qdev-core.h" #include "qom/object.h" #include "hw/i2c/i2c.h" #define TYPE_I3C_TARGET "i3c-target" OBJECT_DECLARE_TYPE(I3CTarget, I3CTargetClass, I3C_TARGET) typedef enum I3CEvent { I3C_START_RECV, I3C_START_SEND, I3C_STOP, I3C_NACK, } I3CEvent; typedef enum I3CCCC { /* Broadcast CCCs */ I3C_CCC_ENEC = 0x00, I3C_CCC_DISEC = 0x01, I3C_CCC_ENTAS0 = 0x02, I3C_CCC_ENTAS1 = 0x03, I3C_CCC_ENTAS2 = 0x04, I3C_CCC_ENTAS3 = 0x05, I3C_CCC_RSTDAA = 0x06, I3C_CCC_ENTDAA = 0x07, I3C_CCC_DEFTGTS = 0x08, I3C_CCC_SETMWL = 0x09, I3C_CCC_SETMRL = 0x0a, I3C_CCC_ENTTM = 0x0b, I3C_CCC_SETBUSCON = 0x0c, I3C_CCC_ENDXFER = 0x12, I3C_CCC_ENTHDR0 = 0x20, I3C_CCC_ENTHDR1 = 0x21, I3C_CCC_ENTHDR2 = 0x22, I3C_CCC_ENTHDR3 = 0x23, I3C_CCC_ENTHDR4 = 0x24, I3C_CCC_ENTHDR5 = 0x25, I3C_CCC_ENTHDR6 = 0x26, I3C_CCC_ENTHDR7 = 0x27, I3C_CCC_SETXTIME = 0x28, I3C_CCC_SETAASA = 0x29, I3C_CCC_RSTACT = 0x2a, I3C_CCC_DEFGRPA = 0x2b, I3C_CCC_RSTGRPA = 0x2c, I3C_CCC_MLANE = 0x2d, /* Direct CCCs */ I3C_CCCD_ENEC = 0x80, I3C_CCCD_DISEC = 0x81, I3C_CCCD_ENTAS0 = 0x82, I3C_CCCD_ENTAS1 = 0x83, I3C_CCCD_ENTAS2 = 0x84, I3C_CCCD_ENTAS3 = 0x85, I3C_CCCD_SETDASA = 0x87, I3C_CCCD_SETNEWDA = 0x88, I3C_CCCD_SETMWL = 0x89, I3C_CCCD_SETMRL = 0x8a, I3C_CCCD_GETMWL = 0x8b, I3C_CCCD_GETMRL = 0x8c, I3C_CCCD_GETPID = 0x8d, I3C_CCCD_GETBCR = 0x8e, I3C_CCCD_GETDCR = 0x8f, I3C_CCCD_GETSTATUS = 0x90, I3C_CCCD_GETACCCR = 0x91, I3C_CCCD_ENDXFER = 0x92, I3C_CCCD_SETBRGTGT = 0x93, I3C_CCCD_GETMXDS = 0x94, I3C_CCCD_GETCAPS = 0x95, I3C_CCCD_SETROUTE = 0x96, I3C_CCCD_SETXTIME = 0x98, I3C_CCCD_GETXTIME = 0x99, I3C_CCCD_RSTACT = 0x9a, I3C_CCCD_SETGRPA = 0x9b, I3C_CCCD_RSTGRPA = 0x9c, I3C_CCCD_MLANE = 0x9d, } I3CCCC; #define CCC_IS_DIRECT(_ccc) (_ccc & 0x80) #define I3C_BROADCAST 0x7e #define I3C_HJ_ADDR 0x02 #define I3C_ENTDAA_SIZE 8 struct I3CTargetClass { DeviceClass parent; /* * Controller to target. Returns 0 for success, non-zero for NAK or other * error. */ int (*send)(I3CTarget *s, const uint8_t *data, uint32_t num_to_send, uint32_t *num_sent); /* * Target to controller. I3C targets are able to terminate reads early, so * this returns the number of bytes read from the target. */ uint32_t (*recv)(I3CTarget *s, uint8_t *data, uint32_t num_to_read); /* Notify the target of a bus state change. */ int (*event)(I3CTarget *s, enum I3CEvent event); /* * Handle a read CCC transmitted from a controller. * CCCs are I3C commands that I3C targets support. * The target can NACK the CCC if it does not support it. */ int (*handle_ccc_read)(I3CTarget *s, uint8_t *data, uint32_t num_to_read, uint32_t *num_read); /* * Handle a write CCC transmitted from a controller. * CCCs are I3C commands that I3C targets support. * The target can NACK the CCC if it does not support it. */ int (*handle_ccc_write)(I3CTarget *s, const uint8_t *data, uint32_t num_to_send, uint32_t *num_sent); }; struct I3CTarget { DeviceState qdev; uint8_t address; uint8_t static_address; uint8_t dcr; uint8_t bcr; uint64_t pid; /* CCC State tracking. */ I3CCCC curr_ccc; uint8_t ccc_byte_offset; bool in_ccc; }; struct I3CNode { I3CTarget *target; QLIST_ENTRY(I3CNode) next; }; typedef struct I3CNode I3CNode; typedef QLIST_HEAD(I3CNodeList, I3CNode) I3CNodeList; #define TYPE_I3C_BUS "i3c-bus" OBJECT_DECLARE_TYPE(I3CBus, I3CBusClass, I3C_BUS) struct I3CBus { BusState qbus; /* Legacy I2C. */ I2CBus *i2c_bus; I3CNodeList current_devs; bool broadcast; uint8_t ccc; bool in_ccc; bool in_entdaa; uint8_t saved_address; }; struct I3CBusClass { DeviceClass parent; /* Handle an incoming IBI request from a target */ int (*ibi_handle) (I3CBus *bus, I3CTarget *target, uint8_t addr, bool is_recv); /* Receive data from an IBI request */ int (*ibi_recv) (I3CBus *bus, uint8_t data); /* Do anything that needs to be done, since the IBI is finished. */ int (*ibi_finish) (I3CBus *bus); }; I3CBus *i3c_init_bus(DeviceState *parent, const char *name); I3CBus *i3c_init_bus_type(const char *type, DeviceState *parent, const char *name); void i3c_set_target_address(I3CTarget *dev, uint8_t address); bool i3c_bus_busy(I3CBus *bus); /* * Start a transfer on an I3C bus. * If is_recv is known at compile-time (i.e. a device will always be sending or * will always be receiving at a certain point), prefer to use i3c_start_recv or * i3c_start_send instead. * * Returns 0 on success, non-zero on an error. */ int i3c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv); /* * Start a receive transfer on an I3C bus. * * Returns 0 on success, non-zero on an error */ int i3c_start_recv(I3CBus *bus, uint8_t address); /* * Start a send transfer on an I3C bus. * * Returns 0 on success, non-zero on an error */ int i3c_start_send(I3CBus *bus, uint8_t address); void i3c_end_transfer(I3CBus *bus); void i3c_nack(I3CBus *bus); int i3c_send_byte(I3CBus *bus, uint8_t data); int i3c_send(I3CBus *bus, const uint8_t *data, uint32_t num_to_send, uint32_t *num_sent); /* * I3C receives can only NACK on a CCC. The target should NACK a CCC it does not * support. */ int i3c_recv_byte(I3CBus *bus, uint8_t *data); int i3c_recv(I3CBus *bus, uint8_t *data, uint32_t num_to_read, uint32_t *num_read); bool i3c_scan_bus(I3CBus *bus, uint8_t address); int i3c_do_entdaa(I3CBus *bus, uint8_t address, uint64_t *pid, uint8_t *bcr, uint8_t *dcr); int i3c_start_device_transfer(I3CTarget *dev, int send_length); bool i3c_target_match(I3CBus *bus, I3CTarget *target, uint8_t address); int i3c_target_send_ibi(I3CTarget *t, uint8_t addr, bool is_recv); int i3c_target_send_ibi_bytes(I3CTarget *t, uint8_t data); int i3c_target_ibi_finish(I3CTarget *t, uint8_t data); /* * Legacy I2C functions. * * These are wrapper for I2C functions that take in an I3C bus instead of an I2C * bus. Internally they use the I2C bus (and devices attached to it) that's a * part of the I3C bus */ void legacy_i2c_nack(I3CBus *bus); uint8_t legacy_i2c_recv(I3CBus *bus); int legacy_i2c_send(I3CBus *bus, uint8_t data); int legacy_i2c_start_transfer(I3CBus *bus, uint8_t address, bool is_recv); int legacy_i2c_start_recv(I3CBus *bus, uint8_t address); int legacy_i2c_start_send(I3CBus *bus, uint8_t address); void legacy_i2c_end_transfer(I3CBus *bus); I2CSlave *legacy_i2c_device_create_simple(I3CBus *bus, const char *name, uint8_t addr); /** * Create an I3C Target. * * The target returned from this function still needs to be realized. */ I3CTarget *i3c_target_new(const char *name, uint8_t addr, uint8_t dcr, uint8_t bcr, uint64_t pid); /** * Create and realize an I3C target. * * Create the target, initialize it, put it on the specified I3C bus, and * realize it. */ I3CTarget *i3c_target_create_simple(I3CBus *bus, const char *name, uint8_t addr, uint8_t dcr, uint8_t bcr, uint64_t pid); /* Realize and drop the reference count on an I3C target. */ bool i3c_target_realize_and_unref(I3CTarget *dev, I3CBus *bus, Error **errp); #endif /* QEMU_INCLUDE_HW_I3C_I3C_H_ */