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;

 

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