I2Cを用いた制御

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(&ltr578_i2c_mutex);
	if (!client) {
		mutex_unlock(&ltr578_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(&ltr578_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(&ltr578_i2c_mutex);
	if (!client) {
		mutex_unlock(&ltr578_i2c_mutex);
		return -EINVAL;
	} else if (len > C_I2C_FIFO_SIZE) {
		mutex_unlock(&ltr578_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(&ltr578_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 (和訳)」もあります。

コメント

タイトルとURLをコピーしました