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;
}
}
好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)