1. 程式人生 > >Linux系統spi驅動程式分析---(一)

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控制暫存器的地址對映和初始化賦值。