linux spi子系统驱动分析

来源:互联网 发布:网络电视看翡翠台直播 编辑:程序博客网 时间:2024/05/16 06:05

linux spi子系统驱动分析

2.6.18内核下已经添加了完整的spi子系统了,参考mtd的分析,将从下到上层,再从上到下层的对其进行分析。

以下先从下到上的进行分析:

driver/spi下有两个底层相关的spi驱动程序:

spi_s3c24xx.c和spi_s3c24xx_gpio.c

其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c允许用户指定3个gpio口,分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。

s3c2410自带了两个spi接口(spi0和spi1),在此我只研究基于s3c2410下spi接口的驱动程序spi_s3c24xx.c。

首先从spi驱动的检测函数进行分析:

static int s3c24xx_spi_probe(struct platform_device *pdev)

{

struct s3c24xx_spi *hw;

struct spi_master *master;

struct spi_board_info *bi;

struct resource *res;

int err = 0;

int i;

/* pi_alloc_master函数申请了struct spi_master+struct s3c24xx_spi大小的数据,

* spi_master_get_devdata和pi_master_get分别取出struct s3c24xx_spi和struct spi_master结构指针

*/

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;

}

/* 填充struct spi_master结构 */

hw = spi_master_get_devdata(master);

memset(hw, 0, sizeof(struct s3c24xx_spi));

hw->master = spi_master_get(master);

hw->pdata = pdev->dev.platform_data;

hw->dev = &pdev->dev;

if (hw->pdata == NULL) {

dev_err(&pdev->dev, "No platform data supplied/n");

err = -ENOENT;

goto err_no_pdata;

}

platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw)

init_completion(&hw->done);

/* setup the state for the bitbang driver */

/* 填充hw->bitbang结构(hw->bitbang结构充当一个中间层,相当与input system的input_handle struct) */

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 */

/* 申请spi所用到的资源:io、irq、时钟等 */

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;

}

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;

}

/* for the moment, permanently enable the clock */

clk_enable(hw->clk);

/* program defaults into the registers */

/* 初始化spi相关的寄存器 */

writeb(0xff, hw->regs + S3C2410_SPPRE);

writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);

writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);

/* add by lfc */

s3c2410_gpio_setpin(S3C2410_GPE13, 0);

s3c2410_gpio_setpin(S3C2410_GPE12, 0);

s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);

s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);

s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);

/* end add */

/* setup any gpio we can */

/* 片选 */

if (!hw->pdata->set_cs) {

s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);

s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT);

}

/* register our spi controller */

/* 最终通过调用spi_register_master来注册spi控制器(驱动) */

err = spi_bitbang_start(&hw->bitbang);

if (err) {

dev_err(&pdev->dev, "Failed to register SPI master/n");

goto err_register;

}

dev_dbg(hw->dev, "shutdown=%d/n", hw->bitbang.shutdown);

/* register all the devices associated */

/* 注册所用使用本spi驱动的设备 */

bi = &hw->pdata->board_info[0];

for (i = 0; i < hw->pdata->board_size; i++, bi++) {

dev_info(hw->dev, "registering %s/n", bi->modalias);

bi->controller_data = hw;

spi_new_device(master, bi);

}

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;

}

/*

* spi_alloc_master - allocate SPI master controller

* @dev: the controller, possibly using the platform_bus

* @size: how much driver-private data to preallocate; the pointer to this

* memory is in the class_data field of the returned class_device,

* accessible with spi_master_get_devdata().

*

* This call is used only by SPI master controller drivers, which are the

* only ones directly touching chip registers. It's how they allocate

* an spi_master structure, prior to calling spi_register_master().

*

* This must be called from context that can sleep. It returns the SPI

* master structure on success, else NULL.

*

* The caller is responsible for assigning the bus number and initializing

* the master's methods before calling spi_register_master(); and (after errors

* adding the device) calling spi_master_put() to prevent a memory leak.

*/

/*注释已经写得很清楚了,本函数旨在分配spi_master struct

*其中,device为主控制设备,size为需要预分配的设备私有数据大小

*该函数被spi主控制器驱动所调用,用于在调用spi_register_master注册主控制器前

*分配spi_master struct,分配bus number和初始化主控制器的操作方法

*注意在分配spi_master struct的时候多分配了大小为size的设备私有数据

*并通过spi_master_set_devdata函数把其放到class_data field里,以后可以通过spi_master_get_devdata来访问

*/

struct spi_master * __init_or_module

spi_alloc_master(struct device *dev, unsigned size)

{

struct spi_master *master;

if (!dev)

return NULL;

master = kzalloc(size + sizeof *master, SLAB_KERNEL);

if (!master)

return NULL;

class_device_initialize(&master->cdev);

master->cdev.class = &spi_master_class;

master->cdev.dev = get_device(dev);

spi_master_set_devdata(master, &master[1]);

return master;

}

/*

* spi_bitbang_start - start up a polled/bitbanging SPI master driver

* @bitbang: driver handle

*

* Caller should have zero-initialized all parts of the structure, and then

* provided callbacks for chip selection and I/O loops. If the master has

* a transfer method, its final step should call spi_bitbang_transfer; or,

* that's the default if the transfer routine is not initialized. It should

* also set up the bus number and number of chipselects.

*

* For i/o loops, provide callbacks either per-word (for bitbanging, or for

* hardware that basically exposes a shift register) or per-spi_transfer

* (which takes better advantage of hardware like fifos or DMA engines).

*

* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup and

* spi_bitbang_cleanup to handle those spi master methods. Those methods are

* the defaults if the bitbang->txrx_bufs routine isn't initialized.

*

* This routine registers the spi_master, which will process requests in a

* dedicated task, keeping IRQs unblocked most of the time. To stop

* processing those requests, call spi_bitbang_stop().

*/

int spi_bitbang_start(struct spi_bitbang *bitbang)

{

int status;

if (!bitbang->master || !bitbang->chipselect)

return -EINVAL;

/*bitbang_work

* 初始化a work,后面再create_singlethread_workqueue,

* 等到有数据要传输的时候,在spi_bitbang_transfer函数中通过调用queue_work(bitbang->workqueue, &bitbang->work)

* 把work扔进workqueue中调度运行

* 这是内核的一贯做法,在mmc/sd驱动中也是这样处理的^_^

*/

INIT_WORK(&bitbang->work, bitbang_work, bitbang);

/* 初始化自旋锁和链表头,以后用到 */

spin_lock_init(&bitbang->lock);

spi_new_device INIT_LIST_HEAD(&bitbang->queue);

if (!bitbang->master->transfer)

bitbang->master->transfer = spi_bitbang_transfer;//spi数据的传输就是通过调用这个方法来实现的

/* spi_s3c24xx.c驱动中有相应的txrx_bufs处理方法,在bitbang_work中被调用 */

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;

}

/* spi_s3c24xx.c驱动中有相应的setup处理方法,在稍后的spi_new_device中被调用 */

} 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->cdev.dev->bus_id);

if (bitbang->workqueue == NULL) {

status = -EBUSY;

goto err1;

}

/* driver may get busy before register() returns, especially

* if someone registered boardinfo for devices

*/

status = spi_register_master(bitbang->master);

if (status < 0)

goto err2;

return status;

err2:

destroy_workqueue(bitbang->workqueue);

err1:

return status;

}

/**

* spi_register_master - register SPI master controller

* @master: initialized master, originally from spi_alloc_master()

*

* SPI master controllers connect to their drivers using some non-SPI bus,

* such as the platform bus. The final stage of probe() in that code

* includes calling spi_register_master() to hook up to this SPI bus glue.

*

* SPI controllers use board specific (often SOC specific) bus numbers,

* and board-specific addressing for SPI devices combines those numbers

* with chip select numbers. Since SPI does not directly support dynamic

* device identification, boards need configuration tables telling which

* chip is at which address.

*

* This must be called from context that can sleep. It returns zero on

* success, else a negative error code (dropping the master's refcount).

* After a successful return, the caller is responsible for calling

* spi_unregister_master().

*/

int __init_or_module

spi_register_master(struct spi_master *master)

{

static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1);

struct device *dev = master->cdev.dev;

int status = -ENODEV;

int dynamic = 0;

if (!dev)

return -ENODEV;

/* convention: dynamically assigned bus IDs count down from the max */

if (master->bus_num < 0) {

master->bus_num = atomic_dec_return(&dyn_bus_id);

dynamic = 1;

}

/* register the device, then userspace will see it.

* registration fails if the bus ID is in use.

*/

snprintf(master->cdev.class_id, sizeof master->cdev.class_id,

"spi%u", master->bus_num);

status = class_device_add(&master->cdev);//注册设备

if (status < 0)

goto done;

dev_dbg(dev, "registered master %s%s/n", master->cdev.class_id,

dynamic ? " (dynamic)" : "");

/* populate children from any spi device tables */

scan_boardinfo(master);

status = 0;

done:

return status;

}

/* FIXME someone should add support for a __setup("spi", ...) that

* creates board info from kernel command lines

*/

/*

* scan board_list for spi_board_info which is registered by spi_register_board_info

* 很可惜,s3c24xx的spi驱动中不支持spi_register_board_info这种标准方式注册方式,而是直接调用spi_new_device内部函数

*/

static void __init_or_module

scan_boardinfo(struct spi_master *master)

{

struct boardinfo *bi;

struct device *dev = master->cdev.dev;

down(&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;

/* some controllers only have one chip, so they

* might not use chipselects. otherwise, the

* chipselects are numbered 0..max.

*/

if (chip->chip_select >= master->num_chipselect

&& master->num_chipselect) {

dev_dbg(dev, "cs%d > max %d/n",

chip->chip_select,

master->num_chipselect);

continue;

}

(void) spi_new_device(master, chip);

}

}

up(&board_lock);

}

/*

* Board-specific early init code calls this (probably during arch_initcall)

* with segments of the SPI device table. Any device nodes are created later,

* after the relevant parent SPI controller (bus_num) is defined. We keep

* this table of devices forever, so that reloading a controller driver will

* not make Linux forget about these hard-wired devices.

*

* Other code can also call this, e.g. a particular add-on board might provide

* SPI devices through its expansion connector, so code initializing that board

* would naturally declare its SPI devices.

*

* The board info passed can safely be __initdata ... but be careful of

* any embedded pointers (platform_data, etc), they're copied as-is.

*/

int __init

spi_register_board_info(struct spi_board_info const *info, unsigned n)

{

struct boardinfo *bi;

bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);

if (!bi)

return -ENOMEM;

bi->n_board_info = n;

memcpy(bi->board_info, info, n * sizeof *info);

down(&board_lock);

list_add_tail(&bi->list, &board_list);

up(&board_lock);

return 0;

}

/* On typical mainboards, this is purely internal; and it's not needed

* after board init creates the hard-wired devices. Some development

* platforms may not be able to use spi_register_board_info though, and

* this is exported so that for example a USB or parport based adapter

* driver could add devices (which it would learn about out-of-band).

*/

struct spi_device *__init_or_module

spi_new_device(struct spi_master *master, struct spi_board_info *chip)

{

struct spi_device *proxy;//这个结构很重要,以后就是通过这个结构来操作实际的spi设备的

struct device *dev = master->cdev.dev;

int status;

/* NOTE: caller did any chip->bus_num checks necessary */

if (!spi_master_get(master))

return NULL;

proxy = kzalloc(sizeof *proxy, GFP_KERNEL);

if (!proxy) {

dev_err(dev, "can't alloc dev for cs%d/n",

chip->chip_select);

goto fail;

}

/* 初始化spi_device 结构各成员 */

proxy->master = master;

proxy->chip_select = chip->chip_select;

proxy->max_speed_hz = chip->max_speed_hz;

proxy->mode = chip->mode;

proxy->irq = chip->irq;

proxy->modalias = chip->modalias;

snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,

"%s.%u", master->cdev.class_id,

chip->chip_select);

proxy->dev.parent = dev;

proxy->dev.bus = &spi_bus_type;

proxy->dev.platform_data = (void *) chip->platform_data;

proxy->controller_data = chip->controller_data;

proxy->controller_state = NULL;

proxy->dev.release = spidev_release;

/* drivers may modify this default i/o setup */

/* 调用master->setup(即s3c24xx_spi_setup)函数初始化spi设备 */

status = master->setup(proxy);

if (status < 0) {

dev_dbg(dev, "can't %s %s, status %d/n",

"setup", proxy->dev.bus_id, status);

goto fail;

}

/* driver core catches callers that misbehave by defining

* devices that already exist.

*/

status = device_register(&proxy->dev);//真正注册原始设备

if (status < 0) {

dev_dbg(dev, "can't %s %s, status %d/n",

"add", proxy->dev.bus_id, status);

goto fail;

}

dev_dbg(dev, "registered child %s/n", proxy->dev.bus_id);

return proxy;

fail:

spi_master_put(master);

kfree(proxy);

return NULL;

}

static int s3c24xx_spi_setup(struct spi_device *spi)

{

int ret;

/* 进行一些检查性操作 */

if (!spi->bits_per_word)

spi->bits_per_word = 8;

if ((spi->mode & SPI_LSB_FIRST) != 0)

return -EINVAL;

ret = s3c24xx_spi_setupxfer(spi, NULL);

if (ret < 0) {

dev_err(&spi->dev, "setupxfer returned %d/n", ret);

return ret;

}

dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz/n",

__FUNCTION__, spi->mode, spi->bits_per_word,

spi->max_speed_hz);

return 0;

}

static int s3c24xx_spi_setupxfer(struct spi_device *spi,

struct spi_transfer *t)

{

struct s3c24xx_spi *hw = to_hw(spi);

unsigned int bpw;

unsigned int hz;

unsigned int div;

bpw = t ? t->bits_per_word : spi->bits_per_word;

hz = t ? t->speed_hz : spi->max_speed_hz;

if (bpw != 8) {

dev_err(&spi->dev, "invalid bits-per-word (%d)/n", bpw);

return -EINVAL;

}

div = clk_get_rate(hw->clk) / hz;

/* is clk = pclk / (2 * (pre+1)), or is it

* clk = (pclk * 2) / ( pre + 1) */

div = (div / 2) - 1;//求出预分频值

if (div < 0)

div = 1;

if (div > 255)

div = 255;

dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)/n", div, hz);

writeb(div, hw->regs + S3C2410_SPPRE);//设置预分频值

spin_lock(&hw->bitbang.lock);

if (!hw->bitbang.busy) {

hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);//修改时钟,先禁用spi

/* need to ndelay for 0.5 clocktick ? */

}

spin_unlock(&hw->bitbang.lock);

return 0;

}

static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)

{

struct s3c24xx_spi *hw = to_hw(spi);

unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;

unsigned int spcon;

switch (value) {

case BITBANG_CS_INACTIVE:

/* 禁用spi(禁用片选) */

if (hw->pdata->set_cs)

hw->pdata->set_cs(hw->pdata, value, cspol);

else

s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1);

break;

case BITBANG_CS_ACTIVE:

/*

* 启用spi:根据需要设置寄存器并启用使能片选

* (如果spi_board_info中没有设置相应的mode选项的话,那就只能使用默认值SPPIN_DEFAULT和SPCON_DEFAULT了)

*/

spcon = readb(hw->regs + S3C2410_SPCON);

if (spi->mode & SPI_CPHA)

spcon |= S3C2410_SPCON_CPHA_FMTB;

else

spcon &= ~S3C2410_SPCON_CPHA_FMTB;

if (spi->mode & SPI_CPOL)

spcon |= S3C2410_SPCON_CPOL_HIGH;

else

spcon &= ~S3C2410_SPCON_CPOL_HIGH;

spcon |= S3C2410_SPCON_ENSCK;

/* write new configration */

writeb(spcon, hw->regs + S3C2410_SPCON);

if (hw->pdata->set_cs)

hw->pdata->set_cs(hw->pdata, value, cspol);

else

s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol);

break;

}

}