Android Kernel高通I2C读写

Qualcomm 专栏收录该内容
2 篇文章 0 订阅

目录

1. i2c_transfer

1.1. i2s_msg类型如下

1.2. i2c_adapter类型如下

1.3. i2c_transfer调用关系

2. 实例参考

2.1. 写入数据到IIC外设

2.2. 从IIC外设读取数据

2.3. 使用i2c-tool


Linux I2C子系统提供了几种操作I2C外设的API,主要是分为两大类,一类是SMBUS协议的方式,一类是基于I2C标准协议的方式,这两种的实现代码都是在下列路径:

<project>/LINUX/android/kernel/msm-4.9/drivers/i2c/

1. i2c_transfer

咱们这里主要使用I2C标准协议的方式进行开发,当前高通平台支持DMA的方式,主要用的核心I2C API如下路径:

<project>/LINUX/android/kernel/msm-3.18/drivers/i2c/i2c-core.c 

该API需要参考其中的i2c_transfer函数,其函数原型如下(内核还封装了其它的API,个人比较喜欢用这个i2c_transfer)

/**
 * 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)
{
	int ret;

	/* REVISIT the fault reporting model here is weak:
	 *
	 *  - When we get an error after receiving N bytes from a slave,
	 *    there is no way to report "N".
	 *
	 *  - When we get a NAK after transmitting N bytes to a slave,
	 *    there is no way to report "N" ... or to let the master
	 *    continue executing the rest of this combined message, if
	 *    that's the appropriate response.
	 *
	 *  - When for example "num" is two and we successfully complete
	 *    the first message but get an error part way through the
	 *    second, it's unclear whether that should be reported as
	 *    one (discarding status on the second message) or errno
	 *    (discarding status on the first one).
	 */

	if (adap->algo->master_xfer) {
#ifdef DEBUG
		for (ret = 0; ret < num; ret++) {
			dev_dbg(&adap->dev,
				"master_xfer[%d] %c, addr=0x%02x, len=%d%s\n",
				ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W',
				msgs[ret].addr, msgs[ret].len,
				(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
		}
#endif

		if (in_atomic() || irqs_disabled()) {
			ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
			if (!ret)
				/* I2C activity is ongoing. */
				return -EAGAIN;
		} else {
			i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
		}

		ret = __i2c_transfer(adap, msgs, num);
		i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);

		return ret;
	} else {
		dev_dbg(&adap->dev, "I2C level transfers not supported\n");
		return -EOPNOTSUPP;
	}
}
EXPORT_SYMBOL(i2c_transfer);

它有三个参数

adap: I2C操作的句柄,这个是在probe驱动的时候由platform生成

msgs: I2C子系统为了实现这种通信方式,封装了i2c_msg结构体,每一个START信号,都对应一个i2c_msg对象。

num: i2c_msg对象的个数

1.1. i2s_msg类型如下

<project>LINUX/android/kernel/msm-4.9/include/uapi/linux/i2c.h

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_RD		0x0001	/* read data, from slave to master */
					/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

当i2c_msg结构体中的flags设定的值为I2C_M_RD,则代表当前为读取操作,默认值为WRITE操作。

1.2. i2c_adapter类型如下

<project>LINUX/android/kernel/msm-4.9/include/linux/i2c.h

/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
struct i2c_adapter {
	struct module *owner;
	unsigned int class;		  /* classes to allow probing for */
	const struct i2c_algorithm *algo; /* the algorithm to access the bus */
	void *algo_data;

	/* data fields that are valid for all devices	*/
	const struct i2c_lock_operations *lock_ops;
	struct rt_mutex bus_lock;
	struct rt_mutex mux_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */

	int nr;
	char name[48];
	struct completion dev_released;

	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;

	struct i2c_bus_recovery_info *bus_recovery_info;
	const struct i2c_adapter_quirks *quirks;
};

该结构体中定义了一个i2c_algorithm结构体如下所示:

struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};

结构体中声明了几个函数指针,其中用到的是master_xfer该函数将会在高通的i2c设备中进行定义,有兴趣的可以继续研究。

<project>/LINUX/android/kernel/msm-4.9/drivers/i2c/busses/i2c-msm-v2.c

static const struct i2c_algorithm i2c_msm_frmwrk_algrtm = {
	.master_xfer	= i2c_msm_frmwrk_xfer,
	.functionality	= i2c_msm_frmwrk_func,
};

其函数定义如下所示,在往下追深就是设计到了实际物理读写的API,

static int
i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
	int ret = 0;
	struct i2c_msm_ctrl      *ctrl = i2c_get_adapdata(adap);
	struct i2c_msm_xfer      *xfer = &ctrl->xfer;

	if (IS_ERR_OR_NULL(msgs) || num < 1) {
		dev_err(ctrl->dev,
		"Error on msgs Accessing invalid message pointer or message buffer\n");
		return -EINVAL;
	}

	/* if system is suspended just bail out */
	if (ctrl->pwr_state == I2C_MSM_PM_SYS_SUSPENDED) {
		dev_err(ctrl->dev,
				"slave:0x%x is calling xfer when system is suspended\n",
				msgs->addr);
		return -EIO;
	}

	ret = i2c_msm_pm_xfer_start(ctrl);
	if (ret)
		return ret;

	/* init xfer */
	xfer->msgs         = msgs;
	xfer->msg_cnt      = num;
	xfer->mode_id      = I2C_MSM_XFER_MODE_NONE;
	xfer->err          = 0;
	xfer->rx_cnt       = 0;
	xfer->tx_cnt       = 0;
	xfer->rx_ovrhd_cnt = 0;
	xfer->tx_ovrhd_cnt = 0;
	atomic_set(&xfer->event_cnt, 0);
	init_completion(&xfer->complete);
	init_completion(&xfer->rx_complete);

	xfer->cur_buf.is_init = false;
	xfer->cur_buf.msg_idx = 0;

	i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_XFER_BEG, num,
								msgs->addr, 0);

	i2c_msm_xfer_scan(ctrl);
	i2c_msm_xfer_calc_timeout(ctrl);
	xfer->mode_id = i2c_msm_qup_choose_mode(ctrl);

	dev_dbg(ctrl->dev, "xfer() mode:%d msg_cnt:%d rx_cbt:%zu tx_cnt:%zu\n",
		xfer->mode_id, xfer->msg_cnt, xfer->rx_cnt, xfer->tx_cnt);

	switch (xfer->mode_id) {
	case I2C_MSM_XFER_MODE_FIFO:
		ret = i2c_msm_fifo_xfer(ctrl);
		break;
	case I2C_MSM_XFER_MODE_BLOCK:
		ret = i2c_msm_blk_xfer(ctrl);
		break;
	case I2C_MSM_XFER_MODE_DMA:
		ret = i2c_msm_dma_xfer(ctrl);
		break;
	default:
		ret = -EINTR;
	};

	i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_SCAN_SUM,
		((xfer->rx_cnt & 0xff) | ((xfer->rx_ovrhd_cnt & 0xff) << 16)),
		((xfer->tx_cnt & 0xff) | ((xfer->tx_ovrhd_cnt & 0xff) << 16)),
		((ctrl->xfer.timeout & 0xfff) | ((xfer->mode_id & 0xf) << 24)));

	ret = i2c_msm_qup_post_xfer(ctrl, ret);
	/* on success, return number of messages sent (which is index + 1)*/
	if (!ret)
		ret = xfer->cur_buf.msg_idx + 1;

	i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_XFER_END, ret, xfer->err,
						xfer->cur_buf.msg_idx + 1);
	/* process and dump profiling data */
	if (xfer->err || (ctrl->dbgfs.dbg_lvl >= MSM_PROF))
		i2c_msm_prof_evnt_dump(ctrl);

	i2c_msm_pm_xfer_end(ctrl);
	return ret;
}

1.3. i2c_transfer调用关系

i2c_transfer实际调用的是__i2c_transfer,其中会调用的上文提到的master_xfer

/**
 * __i2c_transfer - unlocked flavor of i2c_transfer
 * @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.
 *
 * Adapter lock must be held when calling this function. No debug logging
 * takes place. adap->algo->master_xfer existence isn't checked.
 */
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	unsigned long orig_jiffies;
	int ret, try;

	if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
		return -EOPNOTSUPP;

	/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
	 * enabled.  This is an efficient way of keeping the for-loop from
	 * being executed when not needed.
	 */
	if (static_key_false(&i2c_trace_msg)) {
		int i;
		for (i = 0; i < num; i++)
			if (msgs[i].flags & I2C_M_RD)
				trace_i2c_read(adap, &msgs[i], i);
			else
				trace_i2c_write(adap, &msgs[i], i);
	}

	/* Retry automatically on arbitration loss */
	orig_jiffies = jiffies;
	for (ret = 0, try = 0; try <= adap->retries; try++) {
		ret = adap->algo->master_xfer(adap, msgs, num);
		if (ret != -EAGAIN)
			break;
		if (time_after(jiffies, orig_jiffies + adap->timeout))
			break;
	}

	if (static_key_false(&i2c_trace_msg)) {
		int i;
		for (i = 0; i < ret; i++)
			if (msgs[i].flags & I2C_M_RD)
				trace_i2c_reply(adap, &msgs[i], i);
		trace_i2c_result(adap, i, ret);
	}

	return ret;
}
EXPORT_SYMBOL(__i2c_transfer);

2. 实例参考

2.1. 写入数据到IIC外设

写入IIC外设一般操作比较简单,时许也比较简单,一般只要直接写入地址+数据流就行,其测试的IIC时序如下

代码:

static int test_i2c_write(struct mimxrt685_i2c *pdata, uint16_t write_len, uint8_t *write_buf)
{
    int res = 0;

    struct i2c_msg msg;

    if (pdata == NULL)
    {
        pr_err("%s, pdata is NULL\n", __func__);
        return -1;
    }

    msg.addr = pdata->client->addr,
    msg.flags = pdata->client->flags,
    msg.buf = write_buf,
    msg.len = write_len,

    res = i2c_transfer(pdata->client->adapter, &msg, sizeof(msg) / sizeof(struct i2c_msg));
    if (res < 0)
    {
        dev_err(&pdata->client->dev, "%s [ERROR] i2c_master_send - errno: %x\n", __func__, res);

        return -2;
    }

    return res;
}

实际的波形如下:

 

2.2. 从IIC外设读取数据

从IIC外设读取操作,不同的芯片外设时许存在不同的情况。

  1. 如一些简单的芯片,直接通过读取寄存器地址读取数据,直接读取地址即可,如下图所示:

  1. 如稍微繁琐的一些芯片,读取信息,需要先往里面写一些数据,然后再读取,这期间是是会存在两次Start信号,如下图所示:

本例使用的第二种方式,代码如下:

static int test_i2c_read(struct mimxrt685_i2c* pdata, uint8_t write_len, uint8_t* write_buf, uint16_t read_len, uint8_t* read_buf)
{
    int res = 0;
    int i = 0;

    struct i2c_msg msg[2];

    if (pdata == NULL)
    {
        pr_err("%s, pdata is NULL\n", __func__);
        return -1;
    }

    for (i = 0; i < write_len; i ++)
    {
        pr_info("%s, write_buf[%d] = 0x%02x", __func__, i, write_buf[i]);
    }

    msg[0].addr = pdata->client->addr,
    msg[0].flags = 0,
    msg[0].buf = write_buf,
    msg[0].len = write_len,

    msg[1].addr = pdata->client->addr,
    msg[1].flags = I2C_M_RD,
    msg[1].buf = read_buf,
    msg[1].len = read_len,

    res = i2c_transfer(pdata->client->adapter, msg, sizeof(msg) / sizeof(struct i2c_msg));
    if (res < 0)
    {
        dev_err(&pdata->client->dev, "%s [ERROR] i2c_master_receive - error: %x\n", __func__, res);

        return -2;
    }

    return res;
}

 对应的实际波形如下:

2.3. 使用i2c-tool

在开始验证可以通过i2c_tool工具进行遍历i2c总线的设备,进行寻址,读,写测试等等

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值