I3C——代码相关(杂记)

    8150代码中当前能够使用的还只是sensor。我们来看下其中的定义以及对应的使用方法。如果支持I3C,那么SSC_TARGET_NO_I3C_SUPPORT该宏将不会被定义。

    AMSS/slpi_proc/ssc/build/ssc.scons

#'SSC_TARGET_NO_I3C_SUPPORT' to be removed after core team gives support
env.Append(CPPDEFINES = ['SSC_TARGET_NO_I3C_SUPPORT'])

    高通有给出一个地磁传感器的例子,我们就以Qcom给出的这个sensor入手

    /AMSS/slpi_proc/ssc/sensors/ak0991x/src/sns_ak0991x_lite.h

#ifndef SSC_TARGET_NO_I3C_SUPPORT
#define AK0991X_ENABLE_I3C_SUPPORT        // Enable support for I3C bus
#endif

    在ak0991x_sensor_process_registry_event方法中会给I3C需要的信息进行赋值

#ifdef AK0991X_ENABLE_I3C_SUPPORT
          state->com_port_info.i3c_address = state->registry_pf_cfg.i3c_address;
          // if I3C mode, set up the com port to always use the I3C address
          if(state->com_port_info.com_config.bus_type == SNS_BUS_I3C_SDR ||
             state->com_port_info.com_config.bus_type == SNS_BUS_I3C_HDR_DDR )
          {
            state->com_port_info.com_config.slave_control = state->com_port_info.i3c_address;
          }
#endif

    到这里我们就需要去看下对应I3C的这个结构体成员。

    首先state是ak0991x_state实例,com_port_info又是ak0991x_com_port_info实例,跟到这发现也仅仅是不存I3C的地址而已。

typedef struct ak0991x_com_port_info
{
  sns_com_port_config      com_config;
  sns_sync_com_port_handle *port_handle;
  uint8_t                  i2c_address;
  uint8_t                  i3c_address;
  bool                     in_i3c_mode;
} ak0991x_com_port_info;

    再看下bus_type:

sns_bus_type       bus_type;           /* Bus type from sns_bus_type.*/

    可以看出这里可以区分使用的是I2C还是I3C,而且还可以看到上面有SDR和HDR_DDR(double data rate)两种,MIPI联盟其实还定义了一种是HDR-TSL/TSP(ternary symbol)。

    我们可以看下高通对于sensor通信端口类型的分类:

typedef enum
{
  SNS_BUS_MIN            = 0,
  SNS_BUS_I2C            = SNS_BUS_MIN,
  SNS_BUS_SPI            = 1,
  SNS_BUS_UART           = 2, // DEPRECATED. Please use the async_uart sensor
  SNS_BUS_I3C_SDR        = 3, // I3C Standard Data Rate
  SNS_BUS_I3C_HDR_DDR    = 4, // I3C Double Data Rate
  SNS_BUS_I3C_I2C_LEGACY = 5, // for I2C devices on an I3C bus
  SNS_BUS_MAX            = SNS_BUS_I3C_I2C_LEGACY,
} sns_bus_type;

    如上可以看到有关I3C的有SDR(12.5Mbps)、HDR_DDR(16.84Mbps)、I3C_I2C_LEGACY(I2C设备挂载到I3C总线上,快速模式下(FM)和快速模式+(FM+)速率下与I2C从设备进行通信,速率分别为400 Kbps或1 Mbps),如下是MIPI联盟定义的在不同模式下的数据传输速率:

    回过头我们再来看state->com_port_info.com_config.slave_control = state->com_port_info.i3c_address;。看下slave_control的定义,可以看到如果是I3C设备,则为动态为从设备分配地址。

typedef struct
{
  sns_bus_type       bus_type;           /* Bus type from sns_bus_type.*/
  uint32_t           slave_control;      /* Slave Address for I2C.
                                            Dynamic slave address I3C.
                                            Chip Select for SPI.*/
  sns_reg_addr_type  reg_addr_type;      /* Register address type for the slave.*/
  uint32_t           min_bus_speed_KHz;  /* Minimum bus clock supported by slave in kHz.*/
  uint32_t           max_bus_speed_KHz;  /* Maximum bus clock supported by slave in kHz.*/
  uint8_t            bus_instance;       /* Platform bus instance number (BLSP number).*/

} sns_com_port_config;

    我们再来看看该sensor中另一个与I3C相关的方法——ak0991x_enter_i3c_mode,该方法功能是如果配置的总线类型为i3c,则将AK0991X硬件从i2c切换到i3c模式,并配置i3c设置,如最大读取长度等。

/**
 * Enters I3C mode.
 *
 * If the configured bus type is I3C, this will switch the AK0991x
 * hardware from I2C to I3C mode and configure I3C settings such as 
 * maximum read length.
 * This function will do nothing for non-I3C bus types.
 *
 * @param[i] instance      Pointer to instance. May be NULL if called from sensor.
 * @param[i] com_port      pointer to com port structure
 * @param[i] scp_service   synch COM port service
 *
 * @return sns_rc
 * SNS_RC_FAILED - COM port failure, or bus type is not I3C
 * SNS_RC_SUCCESS
 */
sns_rc ak0991x_enter_i3c_mode(sns_sensor_instance *const instance,
                              ak0991x_com_port_info *com_port,
                              sns_sync_com_port_service * scp_service);

    我们一点点来看下该方法具体做了什么配置工作:

sns_rc ak0991x_enter_i3c_mode(sns_sensor_instance *const instance,
                              ak0991x_com_port_info *com_port,
                              sns_sync_com_port_service * scp_service)
{
    ... ...
    i2c_com_config.slave_control = com_port->i2c_address;
    //注册串行通信端口,返回成功或者失败,但是此时不会打开端口
    rv = scp_service->api->sns_scp_register_com_port(&i2c_com_config, &i2c_port_handle);
    ... ...
    //此处为根据i2c_port_handle打开对应的通讯端口
    rv = scp_service->api->sns_scp_open(i2c_port_handle);
    ... ...
    /**----------为I3C设备动态分配地址----------**/
    rv = scp_service->api->
    sns_scp_issue_ccc( i2c_port_handle,
                       SNS_SYNC_COM_PORT_CCC_SETDASA,
                       buffer, 1, &xfer_bytes );
    //sns_scp_issue_ccc该方法是主设备向从设备发送一个CCCs命令(MIPI联盟规范中定义为“通用命令代码”)
    //重点关注的是第二个参数,此参数即可知道是什么类型的命令。详细的命令解释请参照结构体:sns_sync_com_port_ccc[注1]
    //此处的意思是将动态地址分配给具有已知静态地址的从设备,写两个byte
    //需要注意的是使用SNS_SYNC_COM_PORT_CCC_SETDASA命令,必须在使用从设备的I2C静态地址打开的端口上发送,且如果地址更改,端口必须关闭并重新打开
    ... ...
    /**----------设置最大读取的大小----------**/
    ... ...
    rv = scp_service->api->
      sns_scp_issue_ccc( com_port->port_handle,
                         SNS_SYNC_COM_PORT_CCC_SETMRL,//在单个命令中设置最大读取长度,写两个或三个byte
                         buffer, 3, &xfer_bytes );
    ... ...
    /**----------禁用中断----------**/
    buffer[0] = 0x1;
    ... ...
    sns_scp_issue_ccc( com_port->port_handle,
                         SNS_SYNC_COM_PORT_CCC_DISEC,//禁用从设备驱动的中断,1代表关闭中断,写一个byte
                         buffer, 1, &xfer_bytes );
    ... ...
    /**----------获取debug信息----------**/
    ... ...
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_GETMWL,//获得最大写入长度,以字节为单位的最大长度(最高位优先),读两个byte
                       buffer, 2, &xfer_bytes );
    ... ...
    rv = scp_service->api->
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_SETMWL,//设置最大写入长度,以字节为单位的最大长度(最高位优先),读两个byte
                       buffer, 2, &xfer_bytes );
    ... ...
    rv = scp_service->api->
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_GETMRL,//获取最大读取长度,最大读取长度:2字节(最高位优先)
                       buffer, 2, &xfer_bytes );
    ... ...
    rv = scp_service->api->
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_GETPID,//获取从设备的临时ID(PID),读取6个byte
                       buffer, 6, &xfer_bytes );
    ... ...
    rv = scp_service->api->
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_GETBCR,//获取设备的总线特性,读一个byte
                       buffer, 1, &xfer_bytes );
    ... ...
    rv = scp_service->api->
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_GETDCR,//获取设备的设备特性,读一个byte
                       buffer, 1, &xfer_bytes );
    ... ...
    rv = scp_service->api->
    sns_scp_issue_ccc( com_port->port_handle,
                       SNS_SYNC_COM_PORT_CCC_GETSTATUS,//获取设备的运行状态,读两个byte,
                       buffer, 2, &xfer_bytes );

我们再来看下在api中是如何定义的AMSS/slpi_proc/core/api/buses/i2c_api.h

#define I3C_FLAG_USE_7E         0x00010000 /**< Must be set to send out a 0x7E for I3C. */
#define I3C_FLAG_IBI_CTRL       0x00020000 /**< When set IBI is either ACKed or NACKed based on a preconfigured table in HW. */
#define I3C_FLAG_CONTINUE       0x00040000 /**< Set internally for DAA transfers. */

//I2C与I3C速率对比
#define I2C_STANDARD_MODE_FREQ_KHZ          100     /**< I2C stadard speed 100 KHz. */
#define I2C_FAST_MODE_FREQ_KHZ              400     /**< I2C fast mode speed 400 KHz. */
#define I2C_FAST_MODE_PLUS_FREQ_KHZ         1000    /**< I2C fast mode plus speed 1 MHz. */
#define I3C_I2C_ENUMERATE_MODE_FREQ_KHZ     370     /**< I3C enumeration speed 370 KHz. */
#define I3C_SDR_DATA_RATE_12500_KHZ         12500   /**< I3C SDR speed 12.5 MHz. */

//如下可以看到总线类型已经支持I3C
typedef enum
{
    I2C     = 0,    /**< I2C protocol. */
    SMBUS   = 1,    /**< SMBUS protocol. */
    I3C     = 2     /**< I3C protocol. */
} bus_protocol;

//I3C支持的通信方式
typedef enum
{
    //传统I2C设备之间的通讯
    I2C_LEGACY      = 0,    /**< Communicate with an I2C device on I3C bus. */
    //与支持SDR的从设备进行通讯
    I3C_SDR         = 1,    /**< Communicate with a slave supporting I3C Standard Data Rate. */
    //与支持HDR_DDR的从设备进行通讯
    I3C_HDR_DDR     = 2,    /**< Communicate with a slave supporting I3C HDR Dual Data Rate. */
    //通用命令代码传输
    I3C_CCC         = 3,    /**< Common Command Code transfers. */
    //从支持它的从属节点读取IBI有效负载
    I3C_IBI_READ    = 4     /**< Read IBI payload from slaves that support it. */
} i3c_mode;

typedef enum i3c_ccc
{
    B_ENEC        = (0x0000 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Enable slave event driven interrupts. */
    B_DISEC       = (0x0001 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Disable slave event driven interrupts. */
    B_ENTAS0      = (0x0002 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Set activity state 0 (normal operation). */
    B_ENTAS1      = (0x0003 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Set activity state 1. */
    B_ENTAS2      = (0x0004 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Set activity state 2. */
    B_ENTAS3      = (0x0005 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Set activity state 3. */
    B_RSTDAA      = (0x0006 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Forget current dynamic dddress and wait for new assignment. */
    B_ENTDAA      = (0x0007 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Entering master initiation of slave dynamic address assignment. */
    B_DEFSLVS     = (0x0008 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Master defines dynamic address, dcr type, and static address (or 0) per slave. */
    B_SETMWL      = (0x0009 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Maximum write length in a single command. */
    B_SETMRL      = (0x000a | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Maximum read length in a single command. */
    B_ENTTM       = (0x000b | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Master has entered test mode. */
    B_ENTHDR0     = (0x0020 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - ddr mode. */
    B_ENTHDR1     = (0x0021 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - tsp mode. */
    B_ENTHDR2     = (0x0022 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - tsl mode. */
    B_ENTHDR3     = (0x0023 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - future. */
    B_ENTHDR4     = (0x0024 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - future. */
    B_ENTHDR5     = (0x0025 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - future. */
    B_ENTHDR6     = (0x0026 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - future. */
    B_ENTHDR7     = (0x0027 | (I3C_CCC_NO_PAYLOAD    << 8)), /**< Master has entered hdr - future. */
    B_SETXTIME    = (0x0028 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Framework for exchanging event timing information. */
    D_ENEC        = (0x0080 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Enable slave event driven interrupts. */
    D_DISEC       = (0x0081 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Disable slave event driven interrupts. */
    D_ENTAS0      = (0x0082 | (I3C_CCC_SLAVE_PAYLOAD << 8)), /**< Set activity state 0 (normal operation). */
    D_ENTAS1      = (0x0083 | (I3C_CCC_SLAVE_PAYLOAD << 8)), /**< Set activity state 1. */
    D_ENTAS2      = (0x0084 | (I3C_CCC_SLAVE_PAYLOAD << 8)), /**< Set activity state 2. */
    D_ENTAS3      = (0x0085 | (I3C_CCC_SLAVE_PAYLOAD << 8)), /**< Set activity state 3. */
    D_RSTDAA      = (0x0086 | (I3C_CCC_SLAVE_PAYLOAD << 8)), /**< Forget current dynamic address and wait for new assignment. */
    D_SETDASA     = (0x0087 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Master assigns a dynamic address to a slave with a known static address. */
    D_SETNEWDA    = (0x0088 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Master assigns a new dynamic address to any i3c slave. */
    D_SETMWL      = (0x0089 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Maximum write length in a single command. */
    D_SETMRL      = (0x008a | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Maximum read length in a single command. */
    D_GETMWL      = (0x008b | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Get slave's maximum possible write length. */
    D_GETMRL      = (0x008c | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Get a slave's maximum possible read length. */
    D_GETPID      = (0x008d | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Get a slave's provisional id. */
    D_GETBCR      = (0x008e | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Get a device's bus characteristic register (bcr). */
    D_GETDCR      = (0x008f | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Get a device's device characteristics register (dcr). */
    D_GETSTATUS   = (0x0090 | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Get a device's operating status. */
    D_GETACCMST   = (0x0091 | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Current master is requesting and confirming a bus mastership from a secondary master. */
    D_SETBRGTGT   = (0x0093 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Master tells bridge (to/from i2c, spi, uart, etc.) what endpoints it is talking to (by dynamic address and type/id). */
    D_GETMXDS     = (0x0094 | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Master asks slave for its sdr mode max. read and write data speeds (& optionally max. read turnaround time). */
    D_GETHDRCAP   = (0x0095 | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Master asks slave what hdr modes it supports. */
    D_SETXTIME    = (0x0098 | (I3C_CCC_WRITE_PAYLOAD << 8)), /**< Framework for exchanging event timing information. */
    D_GETXTIME    = (0x0099 | (I3C_CCC_READ_PAYLOAD  << 8)), /**< Framework for exchanging event timing information. */

} i3c_ccc;

//I3C设备信息
typedef struct i3c_device
{
    uint8           dynamic_slave_addr;    /**< Dynamic slave address assigned to the slave. */
    uint8           bcr;                   /**< BCR value obtained during broadcast ENTDAA CCC. */
    uint8           dcr;                   /**< DCR value obtained during broadcast ENTDAA CCC. */
    uint8           pid[6];                /**< PID value obtained during broadcast ENTDAA CCC. */
    boolean         allocated;             /**< Flag to denote that the address is in use. */
    void           *ibi_handle;            /**< Handle to the i3c core that processes the IBI event. */
    void          (*ibi_cb)(uint32 status,
                            uint32 xfered,
                            void  *ctxt);  /**< Client callback called to notify IBI event. */
    void           *ibi_ctxt;              /**< Client context called to notify IBI event. */
    uint8          *ibi_rbuffer;           /**< IBI mandatory bytes. */
    uint32          ibi_rlen;              /**< Number of IBI mandatory bytes. */

} i3c_device;

 

注1:

AMSS/slpi_proc/ssc/inc/utils/sns_com_port_types.h
typedef enum sns_sync_com_port_ccc
{
  SNS_SYNC_COM_PORT_CCC_ENEC,
  /**< Enable slave event driven interrupts. Write. One byte.
   * Set to 1 to enable interrupts. See spec for other values */

  SNS_SYNC_COM_PORT_CCC_DISEC,
  /**< Disable slave event driven interrupts. Write. One byte.
   * Set to 1 to disable interrupts. See spec for other values */

  SNS_SYNC_COM_PORT_CCC_SETDASA,
  /**< Assigns a dynamic address to a slave with a known static address. Write. One byte.
   * The dynamic address (left shifted one bit to allow for r/w bit).
   * NOTE: this must be sent on a port opened with the slave's I2C static address.
   *       The port must be closed & re-opened if the address changes */

  SNS_SYNC_COM_PORT_CCC_RSTDAA,
  /**< Resets dynamic address assignment. Zero bytes.
   * NOTE: This must be sent on a port opened with the slave's current I3C dynamic
   *       address.
   *       The port must be closed & re-opened to communicate with the slave if the
   *       I2C static address is not the same as the previously assigned I3C address */

  SNS_SYNC_COM_PORT_CCC_SETMWL,
  /**< Set maximum write length. Write. Two bytes.
   * Max length in bytes (MSB first) */

  SNS_SYNC_COM_PORT_CCC_SETMRL,
  /**< Set maximum read length in a single command. Write. Two or Three bytes.
   * Max read length: 2 bytes (MSB first). Max IBI read length (if IBI data supported) */

  SNS_SYNC_COM_PORT_CCC_GETMWL,
  /**< Get maximum write length. Read. Two bytes.
   * Max length in bytes (MSB first) */

  SNS_SYNC_COM_PORT_CCC_GETMRL,
  /**< Get maximum read length. Read. Two or Three bytes
   * Max read length: 2 bytes (MSB first). Max IBI read length (if IBI data supported) */

  SNS_SYNC_COM_PORT_CCC_GETPID,
  /**< Get a slave's provisional id (PID). Read. Six bytes */

  SNS_SYNC_COM_PORT_CCC_GETBCR,
  /**< Get a device's bus characteristic (BCR). Read. One byte */

  SNS_SYNC_COM_PORT_CCC_GETDCR,
  /**< Get a device's device characteristics (DCR). Read. One byte */

  SNS_SYNC_COM_PORT_CCC_GETSTATUS,
  /**< Get a device's operating status. Read. Two bytes
   * MSB first: vendor reserved byte
   * LSB second: see SNS_CCC_STATUS_* masks */

  SNS_SYNC_COM_PORT_CCC_GETMXDS,
  /**< Get sdr mode max read and write data speeds (& optionally max read turnaround time).
   * Read. Two or Five bytes
   * byte[0]: Max write clock
   * byte[1]: Max Read clock | clock to data turnaround time:
   * optional bytes[2..4]: Max read turnaround time in uSec, LSB first.
   * */

  SNS_SYNC_COM_PORT_CCC_SETXTIME,
  /**< Framework for exchanging event timing information. Write. Various number of bytes
   * byte[0]: SETXTIME defining byte
   * optional bytes: See SNS_CCC_SETXTIME_* for number of optional bytes */

  SNS_SYNC_COM_PORT_CCC_GETXTIME,
  /**< Framework for exchanging event timing information. Read. Four bytes.
   * byte[0]: Supported modes. See SNS_CCC_GETXTIME_SUPPORT_*
   * byte[1]: State. See SNS_CCC_GETXTIME_STATE_
   * byte[2]: Internal oscilator frequency, in increments of 0.5 MHz (0-->~32kHz)
   * byte[3]: Inaccuracy byte. Max variation of slave's oscillator, in 0.1% increments */
} sns_sync_com_port_ccc;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章