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;