FL2440开发板spi驱动分析(2)

根据上文中的移植步骤,可以看出主要工作由两方面,
一是定义SPI控制器(struct platform_device s3c_device_spi0,struct s3c2410_spi_info s3c2410_spi0_platdata)。
二是定义SPI从设备(spi_board_info s3c2410_spi0_board[]),因为SPI总线上可以接多个从设备,所以是个结构体数组。

其实还有两个方面就是编写SPI控制器的驱动和编写SPI从设备驱动,只是内核中已经包含了这两部分的代码,前者在
spi_s3c24xx.c和spi_bitbang.c中,后者在spidev.c中,所以只需要make menuconfig配置下,将这几个文件编译进内核即可。

我们之所以要定义SPI控制器就是为了将SPI控制器的参数,如寄存器地址,中断号等内容保存起来,之后SPI控制器的驱动程序
就会读取这些参数,根据这些参数来进行收发数据。举个例子,s3c_device_spi0中定义了中断号,s3c24xx_spi_probe就会通
过platform_get_irq获取到该中断号,并向该中断注册中断处理函数s3c24xx_spi_irq。s3c2410_spi0_platdata定义了cs引脚,
s3c24xx_spi_probe就会通过s3c_device_spi0.dev.platform_data获取并设置该CS引脚。

下面首先会分析SPI控制器端的代码,再分析SPI从设备端的代码,最后分析
测试程序spidev_test.c发送数据时的内核执行流程,从中了解SPI驱动的框架。

需要注意的是SPI总线有可能接的是flash,那么该flash的驱动就不会像spidev.c中那么简单了。

一、SPI控制器端

1.SPI控制器设备注册

static void __init smdk2410_init(void)
{
	/*将spi控制器设备的部分参数存入dev.platform_data,驱动注册时会读取这些参数*/
	s3c_device_spi0.dev.platform_data= &s3c2410_spi0_platdata;
	/*注册从设备信息*/
	spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board));
	...
	/*该函数会循环调用platform_device_register()注册CPU集成的各种外设控制器,包括SPI控制器*/
	platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
}
2.SPI控制器驱动注册

static int __init s3c24xx_spi_init(void)
{
        return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
platform_driver_probe会调用platform_driver_register()注册驱动。
可见spi控制器设备是作为platform设备向内核进行注册的,其驱动也是作为platform驱动进行注册。
以后也会看到I2C控制器也是以这样的方式注册。
platform设备和驱动的注册过程分析请参见之前的文章,这里只讲和SPI有关的部分。主要是驱动中的probe函数。
3.probe函数

static int __init s3c24xx_spi_probe(struct platform_device *pdev)
{
	struct s3c2410_spi_info *pdata;
	struct s3c24xx_spi *hw;
	struct spi_master *master;
	struct resource *res;
	int err = 0;
	
	/*分配两个连在一起的空间,master指向第一块,
	 *并将master->dev->driver_data指向第二块*/
	master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
	if (master == NULL) {
		dev_err(&pdev->dev, "No memory for spi_master\n");
		err = -ENOMEM;
		goto err_nomem;
	}
  /*hw指向第二块空间*/
	hw = spi_master_get_devdata(master);
	memset(hw, 0, sizeof(struct s3c24xx_spi));
	
  /*hw->master指向master,即建立master和hw之间的联系*/
	hw->master = spi_master_get(master);
	/*pdev就是移植时定义的s3c_device_spi0,platform_data则是s3c2410_spi0_platdata*/
	hw->pdata = pdata = pdev->dev.platform_data;
	hw->dev = &pdev->dev;

	if (pdata == NULL) {
		dev_err(&pdev->dev, "No platform data supplied\n");
		err = -ENOENT;
		goto err_no_pdata;
	}
	/*将s3c_device_spi0.dev.driver_data也指向hw*/
	platform_set_drvdata(pdev, hw);
	/*定义一个completion,用于数据收发*/
	init_completion(&hw->done);

	/* setup the master state. */

	master->num_chipselect = hw->pdata->num_cs;
	master->bus_num = pdata->bus_num;

	/* setup the state for the bitbang driver */
	/*下列函数会在分析数据发送过程时分析*/
	hw->bitbang.master         = hw->master;
	hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
	hw->bitbang.chipselect     = s3c24xx_spi_chipsel;
	hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;
	hw->bitbang.master->setup  = s3c24xx_spi_setup;

	dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);

	/* find and map our resources */
	/*获取移植时定义的各种resouse,包括寄存器地址,终端号*/
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
		err = -ENOENT;
		goto err_no_iores;
	}

	hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
					pdev->name);

	if (hw->ioarea == NULL) {
		dev_err(&pdev->dev, "Cannot reserve region\n");
		err = -ENXIO;
		goto err_no_iores;
	}

	hw->regs = ioremap(res->start, (res->end - res->start)+1);
	if (hw->regs == NULL) {
		dev_err(&pdev->dev, "Cannot map IO\n");
		err = -ENXIO;
		goto err_no_iomap;
	}

	hw->irq = platform_get_irq(pdev, 0);
	if (hw->irq < 0) {
		dev_err(&pdev->dev, "No IRQ specified\n");
		err = -ENOENT;
		goto err_no_irq;
	}

	err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
	if (err) {
		dev_err(&pdev->dev, "Cannot claim IRQ\n");
		goto err_no_irq;
	}
	/*获取时钟,暂不分析,是内核中提供的一套机制,可以获取和enable,disable特定的clk,
	 *其他的还有gpio*/
	hw->clk = clk_get(&pdev->dev, "spi");
	if (IS_ERR(hw->clk)) {
		dev_err(&pdev->dev, "No clock for device\n");
		err = PTR_ERR(hw->clk);
		goto err_no_clk;
	}
	/*SPI控制器硬件相关的初始化*/
	s3c24xx_spi_initialsetup(hw);

	/* setup any gpio we can */
	/*设置cs脚的函数,在收发数据时会用到*/
	if (!pdata->set_cs) {
		hw->set_cs = s3c24xx_spi_gpiocs;

		s3c2410_gpio_setpin(pdata->pin_cs, 1);
		s3c2410_gpio_cfgpin(pdata->pin_cs, S3C2410_GPIO_OUTPUT);
	} else
		hw->set_cs = pdata->set_cs;

	/* register our spi controller */
	/*4*/
	err = spi_bitbang_start(&hw->bitbang);
	if (err) {
		dev_err(&pdev->dev, "Failed to register SPI master\n");
		goto err_register;
	}

	return 0;

 err_register:
	clk_disable(hw->clk);
	clk_put(hw->clk);

 err_no_clk:
	free_irq(hw->irq, hw);

 err_no_irq:
	iounmap(hw->regs);

 err_no_iomap:
	release_resource(hw->ioarea);
	kfree(hw->ioarea);

 err_no_iores:
 err_no_pdata:
	spi_master_put(hw->master);;

 err_nomem:
	return err;
}
4.spi_bitbang_start

int spi_bitbang_start(struct spi_bitbang *bitbang)
{
	int	status;

	if (!bitbang->master || !bitbang->chipselect)
		return -EINVAL;

	/*稍后分析*/
	INIT_WORK(&bitbang->work, bitbang_work);
	spin_lock_init(&bitbang->lock);
	INIT_LIST_HEAD(&bitbang->queue);
	/*设置bitbang->master->transfer */
	if (!bitbang->master->transfer)
		bitbang->master->transfer = spi_bitbang_transfer;
	/*不为空,跳过*/
	if (!bitbang->txrx_bufs) {
		bitbang->use_dma = 0;
		bitbang->txrx_bufs = spi_bitbang_bufs;
		if (!bitbang->master->setup) {
			if (!bitbang->setup_transfer)
				bitbang->setup_transfer =
					 spi_bitbang_setup_transfer;
			bitbang->master->setup = spi_bitbang_setup;
			bitbang->master->cleanup = spi_bitbang_cleanup;
		}
	} else if (!bitbang->master->setup)
		return -EINVAL;

	/* this task is the only thing to touch the SPI bits */
	bitbang->busy = 0;
	bitbang->workqueue = create_singlethread_workqueue(
			bitbang->master->dev.parent->bus_id);
	if (bitbang->workqueue == NULL) {
		status = -EBUSY;
		goto err1;
	}

	/* driver may get busy before register() returns, especially
	 * if someone registered boardinfo for devices
	 */
	 /*注册SPI控制器 5*/
	status = spi_register_master(bitbang->master);
	if (status < 0)
		goto err2;

	return status;

err2:
	destroy_workqueue(bitbang->workqueue);
err1:
	return status;
}
5.spi_register_master

int spi_register_master(struct spi_master *master)
{
	static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
	struct device		*dev = master->dev.parent;
	int			status = -ENODEV;
	int			dynamic = 0;

	if (!dev)
		return -ENODEV;

	/* even if it's just one always-selected device, there must
	 * be at least one chipselect
	 */
	if (master->num_chipselect == 0)
		return -EINVAL;

	/* convention:  dynamically assigned bus IDs count down from the max */
	if (master->bus_num < 0) {
		/* FIXME switch to an IDR based scheme, something like
		 * I2C now uses, so we can't run out of "dynamic" IDs
		 */
		master->bus_num = atomic_dec_return(&dyn_bus_id);
		dynamic = 1;
	}
	
	
	/*设置bus_id,spi0,并注册入内核*/
	/* register the device, then userspace will see it.
	 * registration fails if the bus ID is in use.
	 */
	snprintf(master->dev.bus_id, sizeof master->dev.bus_id,
		"spi%u", master->bus_num);
	status = device_add(&master->dev);
	if (status < 0)
		goto done;
	dev_dbg(dev, "registered master %s%s\n", master->dev.bus_id,
			dynamic ? " (dynamic)" : "");

	/*6*/
	/* populate children from any spi device tables */
	scan_boardinfo(master);
	status = 0;
done:
	return status;
}
6.scan_boardinfo

static void scan_boardinfo(struct spi_master *master)
{
	struct boardinfo	*bi;
	/*第1步中spi_register_board_info已将信息添加到board_list中,
	 *此时找出来,并注册入内核
	 */
	mutex_lock(&board_lock);
	list_for_each_entry(bi, &board_list, list) {
		struct spi_board_info	*chip = bi->board_info;
		unsigned		n;

		for (n = bi->n_board_info; n > 0; n--, chip++) {
			if (chip->bus_num != master->bus_num)
				continue;
			/* NOTE: this relies on spi_new_device to
			 * issue diagnostics when given bogus inputs
			 */
			 /*7*/
			(void) spi_new_device(master, chip);
		}
	}
	mutex_unlock(&board_lock);
}
7.spi_new_device

struct spi_device *spi_new_device(struct spi_master *master,
				  struct spi_board_info *chip)
{
	struct spi_device	*proxy;
	int			status;

	/* NOTE:  caller did any chip->bus_num checks necessary.
	 *
	 * Also, unless we change the return value convention to use
	 * error-or-pointer (not NULL-or-pointer), troubleshootability
	 * suggests syslogged diagnostics are best here (ugh).
	 */
	/*分配spi_device*/
	proxy = spi_alloc_device(master);
	if (!proxy)
		return NULL;

	WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
	/*将获取到数据保存到spi_device中*/
	proxy->chip_select = chip->chip_select;
	proxy->max_speed_hz = chip->max_speed_hz;
	proxy->mode = chip->mode;
	proxy->irq = chip->irq;
	strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
	proxy->dev.platform_data = (void *) chip->platform_data;
	proxy->controller_data = chip->controller_data;
	proxy->controller_state = NULL;
	/*8*/
	status = spi_add_device(proxy);
	if (status < 0) {
		spi_dev_put(proxy);
		return NULL;
	}

	return proxy;
}
8.spi_add_device
int spi_add_device(struct spi_device *spi)
{
	static DEFINE_MUTEX(spi_add_lock);
	struct device *dev = spi->master->dev.parent;
	int status;

	/*这里要做一些判断,可以理解chip_select,num_chipselect的含义*/
	/* Chipselects are numbered 0..max; validate. */
	if (spi->chip_select >= spi->master->num_chipselect) {
		dev_err(dev, "cs%d >= max %d\n",
			spi->chip_select,
			spi->master->num_chipselect);
		return -EINVAL;
	}
	/*设置bus_id,spi0.0,可以理解bus_id,chip_select的含义
	 *可以在/sys/bus/spi/下找到spidev0.0的文件
	 */
	/* Set the bus ID string */
	snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id,
			"%s.%u", spi->master->dev.bus_id,
			spi->chip_select);


	/* We need to make sure there's no other device with this
	 * chipselect **BEFORE** we call setup(), else we'll trash
	 * its configuration.  Lock against concurrent add() calls.
	 */
	mutex_lock(&spi_add_lock);

	if (bus_find_device_by_name(&spi_bus_type, NULL, spi->dev.bus_id)
			!= NULL) {
		dev_err(dev, "chipselect %d already in use\n",
				spi->chip_select);
		status = -EBUSY;
		goto done;
	}

	/* Drivers may modify this initial i/o setup, but will
	 * normally rely on the device being setup.  Devices
	 * using SPI_CS_HIGH can't coexist well otherwise...
	 */
	 /*调用之前设置的s3c24xx_spi_setup函数*/
	status = spi->master->setup(spi);
	if (status < 0) {
		dev_err(dev, "can't %s %s, status %d\n",
				"setup", spi->dev.bus_id, status);
		goto done;
	}

	/* Device may be bound to an active driver when this returns */
	status = device_add(&spi->dev);
	if (status < 0)
		dev_err(dev, "can't %s %s, status %d\n",
				"add", spi->dev.bus_id, status);
	else
		dev_dbg(dev, "registered child %s\n", spi->dev.bus_id);

done:
	mutex_unlock(&spi_add_lock);
	return status;
}
二、SPI从设备端
代码是在spidev.c中,
1.spidev_init

static int __init spidev_init(void)
{
	int status;

	/* Claim our 256 reserved device numbers.  Then register a class
	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
	 * the driver which manages those device numbers.
	 */
	BUILD_BUG_ON(N_SPI_MINORS > 256);
	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
	if (status < 0)
		return status;

	spidev_class = class_create(THIS_MODULE, "spidev");
	if (IS_ERR(spidev_class)) {
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
		return PTR_ERR(spidev_class);
	}

	status = spi_register_driver(&spidev_spi);
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
	}
	return status;
}
里面需要分析是spi_register_driver函数

int spi_register_driver(struct spi_driver *sdrv)
{
	sdrv->driver.bus = &spi_bus_type;
	if (sdrv->probe)
		sdrv->driver.probe = spi_drv_probe;
	if (sdrv->remove)
		sdrv->driver.remove = spi_drv_remove;
	if (sdrv->shutdown)
		sdrv->driver.shutdown = spi_drv_shutdown;
	return driver_register(&sdrv->driver);
}
注意spi_bus_type是在spi_init()函数中建立的,对应的是/sys/bus/spi目录。
spi_drv_probe()函数是驱动注册时发现到匹配的设备后调用的函数。

static int spi_drv_probe(struct device *dev)
{
	const struct spi_driver		*sdrv = to_spi_driver(dev->driver);

	return sdrv->probe(to_spi_device(dev));
}
该函数会通过container_of方法,找到spi_device,再作为spidev_probe()函数的参数。

2.spidev_probe

static int spidev_probe(struct spi_device *spi)
{
	struct spidev_data	*spidev;
	int			status;
	unsigned long		minor;
	
	/*创建spidev_data*/
	/* Allocate driver data */
	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	if (!spidev)
		return -ENOMEM;

	/*指向(一)中的spi_device*/
	/* Initialize the driver data */
	spidev->spi = spi;
	spin_lock_init(&spidev->spi_lock);
	mutex_init(&spidev->buf_lock);

	INIT_LIST_HEAD(&spidev->device_entry);

	/* If we can allocate a minor number, hook up this device.
	 * Reusing minors is fine so long as udev or mdev is working.
	 */
	mutex_lock(&device_list_lock);
	/*设置devt,并创建spidev0.0设备文件*/
	minor = find_first_zero_bit(minors, N_SPI_MINORS);
	if (minor < N_SPI_MINORS) {
		struct device *dev;

		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
		dev = device_create(spidev_class, &spi->dev, spidev->devt,
				    spidev, "spidev%d.%d",
				    spi->master->bus_num, spi->chip_select);
		status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
	} else {
		dev_dbg(&spi->dev, "no minor number available!\n");
		status = -ENODEV;
	}
	/*将创建的spidev连接到device_list中
	*在open函数中会查找到该spidev*/     
	if (status == 0) {
		set_bit(minor, minors);
		list_add(&spidev->device_entry, &device_list);
	}
	mutex_unlock(&device_list_lock);

	if (status == 0)
		spi_set_drvdata(spi, spidev);
	else
		kfree(spidev);

	return status;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值