I2Cとは
I2Cが何か分からない方は1章,2章,5.9章を確認下さい。2章で説明した通り照度センサデバイスは、I2Cで信号を送りレジスタを書き換える事で制御します。このI2Cを用いた制御について説明します。
使い方
照度センサデバイスドライバのltr578_init_clientから呼び出している、ltr578_master_sendから、更に呼び出しているltr578_i2c_write_blockに記載ある、i2c_master_sendがI2Cを制御する関数です。この関数は以下の通り定義されています。
kernel/mediatek/4.4/drivers/i2c/i2c-core.c
/**
* i2c_master_send - issue a single I2C message in master transmit mode
* @client: Handle to slave device
* @buf: Data that will be written to the slave
* @count: How many bytes to write, must be less than 64k since msg.len is u16
*
* Returns negative errno, or else the number of bytes written.
*/
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
このi2c_master_sendの呼び出し元であるltr578_master_sendは、以下の引数で呼ばれています。
kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/ltr578/ltr578.c
ltr578_master_send(client, LTR578_ALS_GAIN, (char *)&buf, 1);
第2引数のLTR578_ALS_GAIN(0x05) は、照度センサのレジスタアドレスです。データシート https://optoelectronics.liteon.com/upload/download/DS86-2016-0062/LTR-578ALS-02_FINAL_DS_V1.1.PDF の p11 にレジスタアドレスの記載があります。また p16 に内容の説明があります。第3引数のbufは、このレジスタに書き込む値です。
レジスタを書き換える方法は、データシートにも記載ある I2C Write Protocol (type 2) となり、まず照度センサを示す固定のSlave addressに対するWriteを、I2Cのプロトコル上で指定した上で、レジスタのアドレスと、その次に書き換える値を同時に送ります。
今回の使用ケースにおいては、Slave addressはclient引数に持っている値が使われます。レジスタのアドレスと書き換える値は、ltr578_master_sendでは第2第3引数になりますが、i2c_master_send実施時には、共に引数のbufに格納されてi2c_master_sendの引数になります。
static int ltr578_i2c_write_block(struct i2c_client *client, u8 addr, u8 *data, u8 len)
{
int err, idx, num;
char buf[C_I2C_FIFO_SIZE];
err = 0;
mutex_lock(<r578_i2c_mutex);
if (!client) {
mutex_unlock(<r578_i2c_mutex);
return -EINVAL;
} else if (len >= C_I2C_FIFO_SIZE) {
APS_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
mutex_unlock(<r578_i2c_mutex);
return -EINVAL;
}
num = 0;
buf[num++] = addr;
for (idx = 0; idx < len; idx++)
buf[num++] = data[idx];
err = i2c_master_send(client, buf, num);
その他のI2Cに関する主要な関数には、i2c_transferがあります。
kernel/mediatek/4.4/drivers/i2c/i2c-core.c
/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to
* terminate the operation; each message begins with a START.
* @num: Number of messages to be executed.
*
* Returns negative errno, else the number of messages executed.
*
* Note that there is no requirement that each message be sent to
* the same slave address, although that is the most common model.
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
近接照度センサドライバではltr578_i2c_read_blockで使われています。
kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/ltr578/ltr578.c
static int ltr578_i2c_read_block(struct i2c_client *client, u8 addr, u8 *data, u8 len)
{
int err;
u8 beg = addr;
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &beg, },
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = data, }
};
mutex_lock(<r578_i2c_mutex);
if (!client) {
mutex_unlock(<r578_i2c_mutex);
return -EINVAL;
} else if (len > C_I2C_FIFO_SIZE) {
mutex_unlock(<r578_i2c_mutex);
APS_LOG(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
return -EINVAL;
}
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
mutex_unlock(<r578_i2c_mutex);
if (err != 2) {
APS_ERR("i2c_transfer error: (%d %p %d) %d\n", addr, data, len, err);
err = -EIO;
} else {
err = 0; /*no error */
}
return err;
}
i2c_msg構造体の配列を引数に取る事で、2つのコマンドを連続で実行する事ができます。
レジスタを読み込む方法は、データシートにも記載ある I2C Read (Combined format) Protocolとなり、書き込みと同様の手法で、レジスタのアドレスのみをまず送ります。その次に、I2Cのプロトコル上でSlave addressに対するReadを指定すると、デバイス側からレジスタの値が帰ってきます。ltr578_i2c_read_blockにおいては、msgs[0]にレジスタのアドレスの書き込みを、msgs[1]にReadを指定することで、レジスタの値を取得しています。
その他、Readのみ実行する、i2c_master_recvも用意されています。これにより、I2C Read (Combined format) Protocol以外にも対応が可能です。
/**
* i2c_master_recv - issue a single I2C message in master receive mode
* @client: Handle to slave device
* @buf: Where to store data read from slave
* @count: How many bytes to read, must be less than 64k since msg.len is u16
*
* Returns negative errno, or else the number of bytes read.
*/
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
最後に
6章にてやっと、デバイスに命令して制御しているI2Cについて説明できました。1章の内容と照らし合わせて、どこの事を記載しているのか照らし合わせて頂けたら幸いです。
ソースコード内にドキュメントとして、「kernel/mediatek/4.4/Documentation/i2c/writing-clients (和訳)」もあります。
コメント