水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7734150
上节介绍了SPI子系统中的一些重要数据结构和SPI子系统初始化的第一步,也就是注册SPI总线。这节介绍针对于s3c24xx平台的SPI子系统初始化,在看具体的代码之前,先上一张自己画的图,帮助理清初始化的主要步骤
显然,SPI是一种平台特定的资源,所以它是以platform平台设备的方式注册进内核的,因此它的struct platform_device结构是已经静态定义好了的,现在只待它的struct platform_driver注册,然后和platform_device匹配。
初始化的入口:
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
platform_driver_probe()会调用platform_driver_register()来注册驱动,然后在注册的过程中寻求匹配的platform_device,一旦匹配成功,便会调用probe函数,也就是s3c24xx_spi_probe(),在看这个函数之前,还得介绍几个相关的数据结构。
struct s3c2410_spi_info是一个板级结构,也是在移植时就定义好的,在初始化spi_master时用到,platform_device-->dev-->platform_data会指向这个结构。
struct s3c2410_spi_info {
int pin_cs; /* simple gpio cs */
unsigned int num_cs; /* total chipselects */
int bus_num;/* bus number to use. */
void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
};
struct s3c24xx_spi用来具体描述s3c24xx平台上一个SPI控制器
struct s3c24xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *regs;
int irq;
int len;
int count;
void (*set_cs)(struct s3c2410_spi_info *spi,
int cs, int pol);
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
struct clk *clk;
struct resource *ioarea;
struct spi_master *master;
struct spi_device *curdev;
struct device *dev;
struct s3c2410_spi_info *pdata;
};
struct spi_bitbang用于控制实际的数据传输
struct spi_bitbang {
struct workqueue_struct *workqueue; /*工作队列*/
struct work_struct work;
spinlock_t lock;
struct list_head queue;
u8 busy;
u8 use_dma;
u8 flags; /* extra spi->mode support */
struct spi_master *master; /*bitbang所属的master*/
/*用于设置设备传输时的时钟,字长等*/
int (*setup_transfer)(struct spi_device *spi,
struct spi_transfer *t);
void (*chipselect)(struct spi_device *spi, int is_on);
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
#define BITBANG_CS_INACTIVE 0
/*针对于平台的传输控制函数*/
int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
/* txrx_word[SPI_MODE_*]() just looks like a shift register */
u32 (*txrx_word[4])(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits);
};
下面来看s3c24xx_spi_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;
/*创建spi_master,并将spi_master->private_data指向s3c24xx_spi*/
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 = spi_master_get_devdata(master);//获取s3c24xx_spi
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
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;
}
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
/* setup the master state. */
/*片选数和SPI主控制器编号是在platform_data中已经定义好了的*/
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = pdata->bus_num;
/* setup the state for the bitbang driver */
/*设置bitbang的所属master和控制传输的相关函数*/
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 */
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;
}
/*映射SPI控制寄存器*/
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;
}
/* setup any gpio we can */
if (!pdata->set_cs) {
if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n");
goto err_register;
}
err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
hw->set_cs = s3c24xx_spi_gpiocs;//设定片选函数
gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw);
/* register our spi controller */
/* 注册主机SPI控制器 */
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:
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_free(pdata->pin_cs);
clk_disable(hw->clk);
clk_put(hw->clk);
err_no_clk:
free_irq(hw->irq, hw);
err_no_irq:
iounmap(hw->regs);
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
/*初始化一个struct work,处理函数为bitbang_work*/
INIT_WORK(&bitbang->work, bitbang_work);
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
/*检测bitbang中的函数是否都定义了,如果没定义,则默认使用spi_bitbang_xxx*/
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的工作队列*/
bitbang->workqueue = create_singlethread_workqueue(
dev_name(bitbang->master->dev.parent));
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
/*注册spi_master*/
status = spi_register_master(bitbang->master);
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
下一个关键函数就是spi_register_master(),用于注册spi_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)//片选数不能为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;
}
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev);//添加spi_master设备
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
/* populate children from any spi device tables */
scan_boardinfo(master);//遍历板级信息,寻找可以挂接在该spi_master下的从设备
status = 0;
done:
return status;
}
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
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
*/
/*bus_num相等则创建新设备*/
(void) spi_new_device(master, chip);
}
}
mutex_unlock(&board_lock);
}
spi_board_info是板级信息,是在移植时就写好的,并且要将其注册
struct spi_board_info {
char modalias[32]; /*名字*/
const void *platform_data;
void *controller_data;
int irq; /*中断号*/
u32 max_speed_hz; /*最高传输速率*/
u16 bus_num; /*所属的spi_master编号*/
u16 chip_select; /*片选号*/
u8 mode; /*传输模式*/
};
最后一步就是将相应的从设备注册进内核
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));
/*初始化*/
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;
/*将新设备添加进内核*/
status = spi_add_device(proxy);
if (status < 0) {
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
分享到:
相关推荐
linux驱动 SPI 驱动框架源码分析(下)
linux 驱动修炼 SPI驱动框架源码分析(上)
linuxSPI驱动框架源码分析.docx
linuxSPI驱动框架源码分析.pdf
linux驱动修炼 SPI驱动框架源码分析(中)
DM6467t硬件开发平台,Linux系统开发环境,SPI驱动源代码和相关测试代码以及makefile,测试通过,可以在目标板上正常运行。
基于子系统去开发驱动程序已经是linux内核中普遍的做法了。前面写过基于I2C子系统的驱动开发。本文介绍另外一种常用总线SPI的开发方法。SPI子系统的开发和I2C有很多的相似性,大家可以对比学习。本主题分为两个部分...
全文3万字,对SPI框架进行了详细分析,涉及到框架中的主要工作流程和每一个变量、指针、数组、结构体、链表。
1. Linux下SPI驱动框架 1.1 SPI主机驱动 Linux使用spi_master结构体表示spi主机驱动,定义在 drivers/linux/spi/spi.h transfer : 数据传输函数 transfer_one_message: SPI发送护具函数,发送一个spi_message数据 ...
基于Linux设备树的spi驱动源码,纯C语言编写,包含驱动文件veba5_drv.c,头文件veba5_reg.h,Makefile文件,应用程序veba5_app.c。可以拿来直接编译运行! 能学到什么: spi驱动源码可以直接运行,稍加修改就可以用...
对linux中SPI驱动框架分析的思维导图,主要分析了从SPI device经过SPI Core到调用SPI Master的过程
linux spi驱动设计实现 很详细的讲解了spi驱动的框架及其设计实现
文档基于ads7846 spi触摸屏驱动详细分析了linux下的spi 框架,功能,与实现过程。
Linux驱动修炼之道-SPI驱动框架源码分析(中).pdf Linux驱动修炼之道-SPI驱动框架源码分析(下).pdf Linux驱动修炼之道-SPI驱动框架源码分析(上).pdf Linux驱动修炼之道-RTC子系统框架与源码分析.pdf ...
本文的目的为基于linux2.6.30.4 framebuffer移植LCD驱动到FL2440开发板。...6、 嵌入式linux应用程序中LCD驱动接口函数的调用及其必要包含头文件和数据结构; 7、 编写一个应用程序在LCD上显示一幅自定义图片;
Nand FLash的硬件知识,Linux下面Nand Flash的框架, 以及如何去在Linux的框架下,实现对应的Nand Flash的驱动。 在1.7的版本上: 1. 添加了Nand Flash位翻转的详细介绍 2. 添加了Nand Flash的结构图
一篇比较完整的linux平台下I2C总线驱动介绍,包括驱动框架、机制,并且结合device实例,应用空间调用进行讲述。
基于linux下spi框架编写的rc522模块的驱动程序。
《创龙ARM Linux通用驱动视频》 ...并且讲解Linux驱动的框架以及经常使用到的input机制、I2C、SPI总线等驱动模型。本视频只要针对初学者,从最简单的地方入手,让初学者更快地进入linux驱动的开发。
《创龙ARM Linux通用驱动视频》 ...并且讲解Linux驱动的框架以及经常使用到的input机制、I2C、SPI总线等驱动模型。本视频只要针对初学者,从最简单的地方入手,让初学者更快地进入linux驱动的开发。