Linux系统spi驱动程序分析---(一)

说明:本文将分析Linux-2.6.17源代码中的spi驱动程序,其内容为本人阅读源代码后的一些理解。由于本人水平有限,所以内容可能比较杂乱零散,权当个人笔记记录之用。而以下内容均以powerpc架构为例说明。


在Linux系统中,spi驱动的设计采用了分层设计模式的思想,将spi驱动分为三层:

  1. spi主控制器层:即对cpu所集成的spi控制器进行初始化设置的代码,所以对cpu的spi控制器的寄存器设置也在此代码中实现;
  2. spi通用层:提供一些spi主控制器层和spi从设备层中所需的设备注册函数,以及spi总线类型等数据结构;
  3. spi从设备层:将从设备作为字符设备类型进行注册,并提供操作从设备的方法,以及从设备spidev_data等数据结构。


首先分析spi主控制器层:

spi_mpc83xx.c文件中有如下主要结构体:

/* SPI Controller registers */
struct mpc83xx_spi_reg {
	//u8 res1[0x20]; /* 此寄存器保留 */
	__be32 mode;
	__be32 event;
	__be32 mask;
	__be32 command;
	__be32 transmit;
	__be32 receive;
        /* 以下寄存器对应四个从设备的模式设置 */
	__be32 res[2];	
	__be32 mode0;
	__be32 mode1;
	__be32 mode2;
	__be32 mode3;
};

以上结构体为spi主控制器的几个寄存器,对其进行赋值即可操作spi寄存器。

/* SPI Controller driver's private data. */
struct mpc83xx_spi {
	/* bitbang has to be first */
	struct spi_bitbang bitbang;
	struct completion done;

	struct mpc83xx_spi_reg __iomem *base;

	/* rx & tx bufs from the spi_transfer */
	const void *tx;
	void *rx;

	/* functions to deal with different sized buffers */
	void (*get_rx) (u32 rx_data, struct mpc83xx_spi *);
	u32(*get_tx) (struct mpc83xx_spi *);

	unsigned int count;
	u32 irq;

	unsigned nsecs;		/* (clock cycle time)/2 */

	u32 sysclk;
	void (*activate_cs) (u8 cs, u8 polarity);
	void (*deactivate_cs) (u8 cs, u8 polarity);
};
以上结构体为spi主控制器数据结构(spi_master)中cdev的私有数据,即可以认为其为spi主控制器数据结构中的私有数据。此结构体中包含了spi控制寄存器的基地址、传输和接受的buf、中断号以及系统时钟值。

/* SPI platform_driver */
static struct platform_driver mpc83xx_spi_driver = {
	.probe = mpc83xx_spi_probe,
	.remove = __devexit_p(mpc83xx_spi_remove),
	.driver = {
		   .name = "mpc83xx_spi",
	},
};
以上结构体为spi的平台驱动结构体,其代表在平台总线上spi主控制器对应的驱动数据结构。probe函数用于探测总线上是否有注册过的对应设备。


以我的理解:在系统中注册设备,是指对设备的数据结构进行初始化赋值,并调用相关函数进行注册。而设备注册过之后,其所初始化赋值的结构体就能被平台驱动探测到,驱动从而找到对应的设备结构体,再根据设备结构体的初始化值来进行spi寄存器的相关设置。


spi_mpc83xx.c文件中有如下主要函数:

static int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t);

static int mpc83xx_spi_setup(struct spi_device *spi);

static int __init mpc83xx_spi_probe(struct platform_device *dev);

static int __init mpc83xx_spi_init(void);

首先,在boot内核的过程中,系统会直接调用被module_init(mpc83xx_spi_init)过的mpc83xx_spi_init(void)函数,而此函数的定义如下所示:

static int __init mpc83xx_spi_init(void)
{
	return platform_driver_register(&mpc83xx_spi_driver);
}
其调用平台驱动注册函数,将上文提到过的mpc83xx_spi_driver平台驱动结构体注册到平台总线上。而platform_driver_register()函数又会把已初始化的platform_bus_type赋值给mpc83xx_spi_driver平台驱动结构体中device的bus成员,以及调用driver_register(&mpc83xx_spi_driver.driver)来注册驱动。platform_driver_register()函数定义如下:

/**
 *	platform_driver_register
 *	@drv: platform driver structure
 */
int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type; /* 此bus中的match函数能检测已经注册的各个平台设备和此平台驱动是否匹配 */
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;
	if (drv->suspend)
		drv->driver.suspend = platform_drv_suspend;
	if (drv->resume)
		drv->driver.resume = platform_drv_resume;
	return driver_register(&drv->driver);
}

其中几个if语句表示如果mpc83xx_spi_driver平台驱动结构体中probe等函数指针已被赋值的话,则mpc83xx_spi_driver平台驱动结构中的driver结构相应的函数指针也会被初始化。而platform_drv_probe()函数功能为调用mpc83xx_spi_driver平台驱动结构体中的probe函数。

而driver_register(&drv->driver)函数将此平台驱动结构中的设备驱动添加到平台bus总线上,其函数定义如下所示:

/**
 *	driver_register - register driver with bus
 *	@drv:	driver to register
**/
int driver_register(struct device_driver * drv)
{
	INIT_LIST_HEAD(&drv->devices);
	init_completion(&drv->unloaded);
	return bus_add_driver(drv);
}
之后将以此顺序来依次调用接下来的这些函数:bus_add_driver(drv) -> driver_attach(drv) -> driver_probe_device(drv, dev) -> drv->probe

由以上函数的调用顺序可知,当driver_probe_device(drv, dev)函数检测到系统中有与平台驱动相对应的平台设备时,将调用drv->probe函数。而drv->probe函数就是上文中提到的platform_drv_probe()函数,所以最终调用的函数是mpc83xx_spi_probe(struct platform_device *dev),其定义如下所示:

static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
	struct spi_master *master;
	struct mpc83xx_spi *mpc83xx_spi;
	struct fsl_spi_platform_data *pdata;
	struct resource *r;
	u32 regval;
	int ret = 0;

	/* Get resources(memory, IRQ) associated with the device */
	master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi));

	if (master == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	platform_set_drvdata(dev, master);
	pdata = dev->dev.platform_data;

	if (pdata == NULL) {
		ret = -ENODEV;
		goto free_master;
	}

	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
	if (r == NULL) {
		ret = -ENODEV;
		goto free_master;
	}

	mpc83xx_spi = spi_master_get_devdata(master);
	mpc83xx_spi->bitbang.master = spi_master_get(master);
	mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
	mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
	mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
	mpc83xx_spi->sysclk = pdata->sysclk;
	mpc83xx_spi->activate_cs = pdata->activate_cs;
	mpc83xx_spi->deactivate_cs = pdata->deactivate_cs;
	mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
	mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;

	mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
	init_completion(&mpc83xx_spi->done);

	mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
	if (mpc83xx_spi->base == NULL) {
		ret = -ENOMEM;
		goto put_master;
	}

	mpc83xx_spi->irq = platform_get_irq(dev, 0);

	if (mpc83xx_spi->irq < 0) {
		ret = -ENXIO;
		goto unmap_io;
	}

	/* Register for SPI Interrupt */
	ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq,
			  0, "mpc83xx_spi", mpc83xx_spi);

	if (ret != 0)
		goto unmap_io;

	master->bus_num = pdata->bus_num;
	master->num_chipselect = pdata->max_chipselect;

	/* SPI controller initializations */
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode0, 0);
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0);
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff);

	/* Enable SPI interface */
	regval = pdata->initial_spmode | SPMODE_INIT_VAL;
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode0, regval);
	mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0x8000100F);

	ret = spi_bitbang_start(&mpc83xx_spi->bitbang);

	if (ret != 0)
		goto free_irq;

	printk(KERN_INFO
	       "%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n",
	       dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq);

	return ret;

free_irq:
	free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
	iounmap(mpc83xx_spi->base);
put_master:
	spi_master_put(master);
free_master:
	kfree(master);
err:
	return ret;
}
此函数的作用在于映射spi控制寄存器的基地址,注册spi控制器的中断号,填充mpc83xx_spi结构体以及初始化spi控制寄存器。
而mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)与mpc83xx_spi_setup(struct spi_device *spi)函数在注册spi从设备时才被调用,其作用为根据从设备传入的参数来初始化spi控制寄存器,所以这两个函数将放在spi从设备驱动中进行讲解。


spi主控制器驱动层基本就这些内容,而还有一些数据传输等函数并未进行分析,可能会再后面的文章中再进行详细的分析。总之spi主控制器驱动层的主要功能在于注册spi的平台驱动,将spi的平台驱动加入到平台总线中,然后再进行spi控制寄存器的地址映射和初始化赋值。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值