Linux内核4.14版本——SPI框架(4)——spi传输以及master需要设置的字段

目录

1. spi_sync

1.1 __spi_validate(参数message检查)

1.2 transfer选择

1.2.1 ctlr->transfer == spi_queued_transfer(没有定义ctlr->transfer,同步传输)

1.2.2 spi_async_locked(定义ctlr->transfer)(异步传输)

1.3 等待传输完成

2. ctrl要实现的一些函数字段

2.1 如果设置ctlr->transfer则其不用设置,异步传输

2.2 如果ctlr->transfer没有设置,同步传输

2.3 transfer_one_message里面如果需要,设置spi->controller->set_cs


       spi传输的核心函数是spi_sync,所有与spi控制器的读写操作都是使用这个函数,我们沿着这个函数分析,看看如何调用到spi master的,以及spi master需要实现的一些字段。spi_sync是同步传输,spi_async是异步传输。

spi_async
   __spi_async
         ctlr->transfer(spi_queued_transfer)       // ctlr->transfer = spi_queued_transfer;
             __spi_queued_transfer
                   spi_pump_messages
                        __spi_pump_messages
         	              unprepare_transfer_hardware
                              prepare_transfer_hardware
                              
                              prepare_message
                              ctlr->transfer_one_message(ctlr, ctlr->cur_msg);
                                       spi_set_cs-->spi->controller->set_cs
                                       ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
                                       spi_set_cs

1. spi_sync

spi_sync->__spi_sync

1.1 __spi_validate(参数message检查)

static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	.......
	struct spi_controller *ctlr = spi->controller;

	status = __spi_validate(spi, message);
	if (status != 0)
		return status;
	.........
}
static int __spi_validate(struct spi_device *spi, struct spi_message *message)
{
	struct spi_controller *ctlr = spi->controller;
	struct spi_transfer *xfer;
	int w_size;

	if (list_empty(&message->transfers))
		return -EINVAL;

	/* Half-duplex links include original MicroWire, and ones with
	 * only one data pin like SPI_3WIRE (switches direction) or where
	 * either MOSI or MISO is missing.  They can also be caused by
	 * software limitations.
	 */
	if ((ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX) ||
	    (spi->mode & SPI_3WIRE)) {
		unsigned flags = ctlr->flags;

		list_for_each_entry(xfer, &message->transfers, transfer_list) {
			if (xfer->rx_buf && xfer->tx_buf)
				return -EINVAL;
			if ((flags & SPI_CONTROLLER_NO_TX) && xfer->tx_buf)
				return -EINVAL;
			if ((flags & SPI_CONTROLLER_NO_RX) && xfer->rx_buf)
				return -EINVAL;
		}
	}

	/**
	 * Set transfer bits_per_word and max speed as spi device default if
	 * it is not set for this transfer.
	 * Set transfer tx_nbits and rx_nbits as single transfer default
	 * (SPI_NBITS_SINGLE) if it is not set for this transfer.
	 */
	message->frame_length = 0;
	list_for_each_entry(xfer, &message->transfers, transfer_list) {
		message->frame_length += xfer->len;
		if (!xfer->bits_per_word)
			xfer->bits_per_word = spi->bits_per_word;

		if (!xfer->speed_hz)
			xfer->speed_hz = spi->max_speed_hz;
		if (!xfer->speed_hz)
			xfer->speed_hz = ctlr->max_speed_hz;

		if (ctlr->max_speed_hz && xfer->speed_hz > ctlr->max_speed_hz)
			xfer->speed_hz = ctlr->max_speed_hz;

		if (__spi_validate_bits_per_word(ctlr, xfer->bits_per_word))
			return -EINVAL;

		/*
		 * SPI transfer length should be multiple of SPI word size
		 * where SPI word size should be power-of-two multiple
		 */
		if (xfer->bits_per_word <= 8)
			w_size = 1;
		else if (xfer->bits_per_word <= 16)
			w_size = 2;
		else
			w_size = 4;

		/* No partial transfers accepted */
		if (xfer->len % w_size)
			return -EINVAL;

		if (xfer->speed_hz && ctlr->min_speed_hz &&
		    xfer->speed_hz < ctlr->min_speed_hz)
			return -EINVAL;

		if (xfer->tx_buf && !xfer->tx_nbits)
			xfer->tx_nbits = SPI_NBITS_SINGLE;
		if (xfer->rx_buf && !xfer->rx_nbits)
			xfer->rx_nbits = SPI_NBITS_SINGLE;
		/* check transfer tx/rx_nbits:
		 * 1. check the value matches one of single, dual and quad
		 * 2. check tx/rx_nbits match the mode in spi_device
		 */
		if (xfer->tx_buf) {
			if (xfer->tx_nbits != SPI_NBITS_SINGLE &&
				xfer->tx_nbits != SPI_NBITS_DUAL &&
				xfer->tx_nbits != SPI_NBITS_QUAD)
				return -EINVAL;
			if ((xfer->tx_nbits == SPI_NBITS_DUAL) &&
				!(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
				return -EINVAL;
			if ((xfer->tx_nbits == SPI_NBITS_QUAD) &&
				!(spi->mode & SPI_TX_QUAD))
				return -EINVAL;
		}
		/* check transfer rx_nbits */
		if (xfer->rx_buf) {
			if (xfer->rx_nbits != SPI_NBITS_SINGLE &&
				xfer->rx_nbits != SPI_NBITS_DUAL &&
				xfer->rx_nbits != SPI_NBITS_QUAD)
				return -EINVAL;
			if ((xfer->rx_nbits == SPI_NBITS_DUAL) &&
				!(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
				return -EINVAL;
			if ((xfer->rx_nbits == SPI_NBITS_QUAD) &&
				!(spi->mode & SPI_RX_QUAD))
				return -EINVAL;
		}
	}

	message->status = -EINPROGRESS;

	return 0;
}

这个主要是master、device、transfer的参数的检查。device的参数主要在spi_add_device->spi_setup中设置,详细见文章。

1.2 transfer选择

static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	......

	/* If we're not using the legacy transfer method then we will
	 * try to transfer in the calling context so special case.
	 * This code would be less tricky if we could remove the
	 * support for driver implemented message queues.
	 */
	if (ctlr->transfer == spi_queued_transfer) {
		spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);

		trace_spi_message_submit(message);

		status = __spi_queued_transfer(spi, message, false);

		spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
	} else {
		status = spi_async_locked(spi, message);
	}
	.........
}

1.2.1 ctlr->transfer == spi_queued_transfer(没有定义ctlr->transfer,同步传输)

      在前面的文章中介绍过,如果没有定义ctlr->transfer的话,会调用spi_controller_initialize_queue函数,再这个函数里面会把ctlr->transfer = spi_queued_transfer。并且会调用spi_init_queue初始化queue。

static int spi_init_queue(struct spi_controller *ctlr)
{
	.......
	kthread_init_work(&ctlr->pump_messages, spi_pump_messages);
	......
	return 0;
}

static int spi_controller_initialize_queue(struct spi_controller *ctlr)
{
	.....
	ctlr->transfer = spi_queued_transfer;
	if (!ctlr->transfer_one_message)
		ctlr->transfer_one_message = spi_transfer_one_message;

	/* Initialize and start queue */
	ret = spi_init_queue(ctlr);
	.......
}

__spi_queued_transfer->kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages)->spi_pump_messages->__spi_pump_messages

static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
{
	......
	ctlr->unprepare_transfer_hardware(如果存在就执行)
	ctlr->prepare_transfer_hardware(如果存在就执行)
	ctlr->prepare_message(如果存在就执行)
	ctlr->transfer_one_message(执行)
	.....
}

ctlr->transfer_one_message如果存在就执行,如果没有定义,会在前面赋值为spi_transfer_one_message。

static int spi_transfer_one_message(struct spi_controller *ctlr,
				    struct spi_message *msg)
{
	......
	spi_set_cs(msg->spi, true);
    ......
	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
			............
			ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
			......
	}
    ......
	spi_set_cs(msg->spi, false);
    ......
}

1.2.2 spi_async_locked(定义ctlr->transfer)(异步传输)

spi_async_locked->__spi_async->ctlr->transfer(spi, message)。执行自己定义的transfer。

1.3 等待传输完成

static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	if (status == 0) {
		......
		wait_for_completion(&done);
		status = message->status;
	}
	message->context = NULL;
	return status;
}

2. ctrl要实现的一些函数字段

2.1 如果设置ctlr->transfer则其不用设置,异步传输

实现ctlr->transfer

2.2 如果ctlr->transfer没有设置,同步传输

    ctlr->unprepare_transfer_hardware(如果存在就执行)
    ctlr->prepare_transfer_hardware(如果存在就执行)
    ctlr->prepare_message(如果存在就执行)
    ctlr->transfer_one_message(如果存在执行,如果不存在实现ctlr->transfer_one)

2.3 transfer_one_message里面如果需要,设置spi->controller->set_cs

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值