Linux内核4.14版本——SPI框架(3)——master相关

目录

目录

1. spi_master的编写流程

2. spi_alloc_master

3. devm_spi_register_controller

3.1 of_spi_register_master

3.2 bus_num的申请

3.3 初始化其他一些字段

3.4 spi_controller_initialize_queue(ctlr->transfer没有定义)

3.5 spi_match_controller_to_boardinfo

3.5.1 spi_match_controller_to_boardinfo

3.5.2 spi_register_board_info

3.6 of_register_spi_devices(从设备树中注册spi device设备)

3.6.1 spi_alloc_device

3.6.2 of_modalias_node

3.6.2 of_spi_parse_dt

3.6.3 spi_add_device


1. spi_master的编写流程

SPI控制器驱动在Linux中用struct spi_master定义,也就是struct spi_controller。

struct spi_controller {
	struct device	dev;

	struct list_head list;
    ....
}
#define spi_master	spi_controller
struct spi_master *master;

static int xxx_spi_probe(struct platform_device *pdev)
{
    /* 1. 分配master */
    struct spi_master *master;
    master = spi_alloc_master(&pdev->dev, sizeof(*xxx_spi));

    /* 2. 设置master */
	master->prepare_transfer_hardware = tsm_prepare_transfer_hardware;
	master->prepare_message = tsm_prepare_message;
	master->transfer_one = tsm_transfer_one;
	master->unprepare_transfer_hardware = tsm_unprepare_transfer_hardware;
	master->set_cs = tsm_spi_chipselect;
	master->setup = tsm_spi_setup;
	master->cleanup = tsm_spi_cleanup;
	master->mode_bits = SPI_CPOL | SPI_CPHA;

	/* Set to default valid value */
	master->bits_per_word_mask = SPI_BPW_MASK(8);
	master->max_speed_hz = tsm_spi->clk_rate;
	tsm_spi->speed_hz = master->max_speed_hz;
    .....

    /* 3. 注册master */
    ret = devm_spi_register_controller(&pdev->dev, master);
}

2. spi_alloc_master

spi_alloc_master->__spi_alloc_controller

struct spi_controller *__spi_alloc_controller(struct device *dev,
					      unsigned int size, bool slave)
{
	struct spi_controller	*ctlr;

	if (!dev)
		return NULL;

	ctlr = kzalloc(size + sizeof(*ctlr), GFP_KERNEL);
	if (!ctlr)
		return NULL;

	device_initialize(&ctlr->dev);
	ctlr->bus_num = -1;
	ctlr->num_chipselect = 1;
	ctlr->slave = slave;
	if (IS_ENABLED(CONFIG_SPI_SLAVE) && slave)
		ctlr->dev.class = &spi_slave_class;
	else
		ctlr->dev.class = &spi_master_class;
	ctlr->dev.parent = dev;
	pm_suspend_ignore_children(&ctlr->dev, true);
	spi_controller_set_devdata(ctlr, &ctlr[1]);

	return ctlr;
}

      简单来说,就是分配一个struct spi_controller结构体,设置其bus_num、num_chipselect默认值分别为-1,1。bus_num为-1,表示该control在spi bus上的编号动态分配,如果需要修改为自己需要的值,后面可以修改,也可以在设备树中修改。num_chipselect表示该control上的从设备的数量。

3. devm_spi_register_controller

devm_spi_register_controller->spi_register_controller

3.1 of_spi_register_master

int spi_register_controller(struct spi_controller *ctlr)
{
	.........
	if (!dev)
		return -ENODEV;

	if (!spi_controller_is_slave(ctlr)) {
		status = of_spi_register_master(ctlr);
		if (status)
			return status;
	}
	...........
}
static int of_spi_register_master(struct spi_controller *ctlr)
{
	int nb, i, *cs;
	struct device_node *np = ctlr->dev.of_node;

	if (!np)
		return 0;

	nb = of_gpio_named_count(np, "cs-gpios");
	ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);

	/* Return error only for an incorrectly formed cs-gpios property */
	if (nb == 0 || nb == -ENOENT)
		return 0;
	else if (nb < 0)
		return nb;

	cs = devm_kzalloc(&ctlr->dev, sizeof(int) * ctlr->num_chipselect,
			  GFP_KERNEL);
	ctlr->cs_gpios = cs;

	if (!ctlr->cs_gpios)
		return -ENOMEM;

	for (i = 0; i < ctlr->num_chipselect; i++)
		cs[i] = -ENOENT;

	for (i = 0; i < nb; i++)
		cs[i] = of_get_named_gpio(np, "cs-gpios", i);

	return 0;
}

    该函数主要是实现对num_chipselect字段的重新幅值。

3.2 bus_num的申请

int spi_register_controller(struct spi_controller *ctlr)
{
	/* even if it's just one always-selected device, there must
	 * be at least one chipselect
	 */
	if (ctlr->num_chipselect == 0)
		return -EINVAL;
	if (ctlr->bus_num >= 0) {
		/* devices with a fixed bus num must check-in with the num */
		mutex_lock(&board_lock);
		id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
			ctlr->bus_num + 1, GFP_KERNEL);
		mutex_unlock(&board_lock);
		if (WARN(id < 0, "couldn't get idr"))
			return id == -ENOSPC ? -EBUSY : id;
		ctlr->bus_num = id;
	} else if (ctlr->dev.of_node) {
		/* allocate dynamic bus number using Linux idr */
		id = of_alias_get_id(ctlr->dev.of_node, "spi");
		if (id >= 0) {
			ctlr->bus_num = id;
			mutex_lock(&board_lock);
			id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
				       ctlr->bus_num + 1, GFP_KERNEL);
			mutex_unlock(&board_lock);
			if (WARN(id < 0, "couldn't get idr"))
				return id == -ENOSPC ? -EBUSY : id;
		}
	}
	if (ctlr->bus_num < 0) {
		first_dynamic = of_alias_get_highest_id("spi");
		if (first_dynamic < 0)
			first_dynamic = 0;
		else
			first_dynamic++;

		mutex_lock(&board_lock);
		id = idr_alloc(&spi_master_idr, ctlr, first_dynamic,
			       0, GFP_KERNEL);
		mutex_unlock(&board_lock);
		if (WARN(id < 0, "couldn't get idr"))
			return id;
		ctlr->bus_num = id;
	}
	...........
}

      bus_num大于0,检查是否有效以及是否已经申请过了。若bus_num小于0,先从设备树中找,找不到,则动态分配。

3.3 初始化其他一些字段

int spi_register_controller(struct spi_controller *ctlr)
{
	INIT_LIST_HEAD(&ctlr->queue);
	spin_lock_init(&ctlr->queue_lock);
	spin_lock_init(&ctlr->bus_lock_spinlock);
	mutex_init(&ctlr->bus_lock_mutex);
	mutex_init(&ctlr->io_mutex);
	ctlr->bus_lock_flag = 0;
	init_completion(&ctlr->xfer_completion);

	...........
}

3.4 spi_controller_initialize_queue(ctlr->transfer没有定义)

int spi_register_controller(struct spi_controller *ctlr)
{

	/* If we're using a queued driver, start the queue */
	if (ctlr->transfer)
		dev_info(dev, "controller is unqueued, this is deprecated\n");
	else {
		status = spi_controller_initialize_queue(ctlr);

	...........
}

如果没有定义ctlr->transfer的话,则调用spi_controller_initialize_queue函数。

static int spi_controller_initialize_queue(struct spi_controller *ctlr)
{
	int ret;

	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);
	if (ret) {
		dev_err(&ctlr->dev, "problem initializing queue\n");
		goto err_init_queue;
	}
	ctlr->queued = true;
	ret = spi_start_queue(ctlr);
	......
}

      (1)配置spi_master(spi_control)里面关于传输的函数。

      (2)初始化queue,开始queue。

        master需要填充哪些参数,这个我们后面再谈。

3.5 spi_match_controller_to_boardinfo

int spi_register_controller(struct spi_controller *ctlr)
{	
	mutex_lock(&board_lock);
	list_add_tail(&ctlr->list, &spi_controller_list);
	list_for_each_entry(bi, &board_list, list)
		spi_match_controller_to_boardinfo(ctlr, &bi->board_info);
	mutex_unlock(&board_lock);
	...........
}

    (1)将该control加入到spi_controller_list链表中。

    (2)调用spi_match_controller_to_boardinfo注册board_list中的spi device设置。

3.5.1 spi_match_controller_to_boardinfo

static void spi_match_controller_to_boardinfo(struct spi_controller *ctlr,
					      struct spi_board_info *bi)
{
	struct spi_device *dev;

	if (ctlr->bus_num != bi->bus_num)
		return;

	dev = spi_new_device(ctlr, bi);
	if (!dev)
		dev_err(ctlr->dev.parent, "can't create new device for %s\n",
			bi->modalias);
}

3.5.2 spi_register_board_info

int spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
	struct boardinfo *bi;
	int i;

	if (!n)
		return 0;

	bi = kcalloc(n, sizeof(*bi), GFP_KERNEL);
	if (!bi)
		return -ENOMEM;

	for (i = 0; i < n; i++, bi++, info++) {
		struct spi_controller *ctlr;

		memcpy(&bi->board_info, info, sizeof(*info));
		if (info->properties) {
			bi->board_info.properties =
					property_entries_dup(info->properties);
			if (IS_ERR(bi->board_info.properties))
				return PTR_ERR(bi->board_info.properties);
		}

		mutex_lock(&board_lock);
		list_add_tail(&bi->list, &board_list);
		list_for_each_entry(ctlr, &spi_controller_list, list)
			spi_match_controller_to_boardinfo(ctlr,
							  &bi->board_info);
		mutex_unlock(&board_lock);
	}

	return 0;
}

3.6 of_register_spi_devices(从设备树中注册spi device设备)

int spi_register_controller(struct spi_controller *ctlr)
{	
	...........
	/* Register devices from the device tree and ACPI */
	of_register_spi_devices(ctlr);
	acpi_register_spi_devices(ctlr);
	...........
}
static struct spi_device *
of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
{
	struct spi_device *spi;
	int rc;

	/* Alloc an spi_device */
	spi = spi_alloc_device(ctlr);
	if (!spi) {
		dev_err(&ctlr->dev, "spi_device alloc error for %pOF\n", nc);
		rc = -ENOMEM;
		goto err_out;
	}

	/* Select device driver */
	rc = of_modalias_node(nc, spi->modalias,
				sizeof(spi->modalias));
	if (rc < 0) {
		dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
		goto err_out;
	}

	rc = of_spi_parse_dt(ctlr, spi, nc);
	if (rc)
		goto err_out;

	/* Store a pointer to the node in the device structure */
	of_node_get(nc);
	spi->dev.of_node = nc;
	spi->dev.fwnode = of_fwnode_handle(nc);

	/* Register the new device */
	rc = spi_add_device(spi);
	......
}

3.6.1 spi_alloc_device

3.6.2 of_modalias_node

int of_modalias_node(struct device_node *node, char *modalias, int len)
{
	const char *compatible, *p;
	int cplen;

	compatible = of_get_property(node, "compatible", &cplen);
	if (!compatible || strlen(compatible) > cplen)
		return -ENODEV;
	p = strchr(compatible, ',');
	strlcpy(modalias, p ? p + 1 : compatible, len);
	return 0;
}

      of_modalias_node会把设备树compatible字段,后面的值赋值给spi->modalias。例如下面的代码,spi->modalias会被赋值为w25q64。

&uspi0 {
    status = "okay";
    //max-freq = <48000000>;
    w25q64@00 {
        ......
        compatible = "winbond,w25q64";
        .....
    };
};

3.6.2 of_spi_parse_dt

static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
			   struct device_node *nc)
{
	u32 value;
	int rc;

	/* Mode (clock phase/polarity/etc.) */
	if (of_property_read_bool(nc, "spi-cpha"))
		spi->mode |= SPI_CPHA;
	if (of_property_read_bool(nc, "spi-cpol"))
		spi->mode |= SPI_CPOL;
	if (of_property_read_bool(nc, "spi-cs-high"))
		spi->mode |= SPI_CS_HIGH;
	if (of_property_read_bool(nc, "spi-3wire"))
		spi->mode |= SPI_3WIRE;
	if (of_property_read_bool(nc, "spi-lsb-first"))
		spi->mode |= SPI_LSB_FIRST;

	/* Device DUAL/QUAD mode */
	if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
		switch (value) {
		case 1:
			break;
		case 2:
			spi->mode |= SPI_TX_DUAL;
			break;
		case 4:
			spi->mode |= SPI_TX_QUAD;
			break;
		default:
			dev_warn(&ctlr->dev,
				"spi-tx-bus-width %d not supported\n",
				value);
			break;
		}
	}

	if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
		switch (value) {
		case 1:
			break;
		case 2:
			spi->mode |= SPI_RX_DUAL;
			break;
		case 4:
			spi->mode |= SPI_RX_QUAD;
			break;
		default:
			dev_warn(&ctlr->dev,
				"spi-rx-bus-width %d not supported\n",
				value);
			break;
		}
	}

	if (spi_controller_is_slave(ctlr)) {
		if (strcmp(nc->name, "slave")) {
			dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
				nc);
			return -EINVAL;
		}
		return 0;
	}

	/* Device address */
	rc = of_property_read_u32(nc, "reg", &value);
	if (rc) {
		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
			nc, rc);
		return rc;
	}
	spi->chip_select = value;

	/* Device speed */
	rc = of_property_read_u32(nc, "spi-max-frequency", &value);
	if (rc) {
		dev_err(&ctlr->dev,
			"%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);
		return rc;
	}
	spi->max_speed_hz = value;

	return 0;
}

      如果使用设备树添加spi device,那么你必须在设备树中添加一些值。其他好理解,reg表示该spi control中该设备的片选编号。

3.6.3 spi_add_device

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值