Allein.Cao原创作品,转载请注明出处:
http://blog.csdn.net/alleincao/article/details/7523169
内核版本:2.6.32.2
硬件:S3C2440
控制器驱动是与SOC密切相关的一个模块,在linux中,一般都会采用设备驱动和控制器驱动分离的思想,两者通过一个core进行关联,这样能够最大程度保证代码的可移植性,具体可以参考《linux设备驱动开发详解》
上面提到设备驱动和控制器驱动通过core进行关联,更具体地来说,它是通过一个自己的bus来实现的,在spi中,是通过core文件中注册的spi_bus_type实现,在i2c中,则是通过core中的i2c_bus_type实现(其实spi和i2c子系统是比较相像的),具体到spi的驱动中,这个core就是spi.c文件,所以在此我们简单看一下spi.c。
struct bus_type spi_bus_type = { //spi_device和spi_driver都向其注册
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device, //总线的match函数
.uevent = spi_uevent,
.suspend = spi_suspend,
.resume = spi_resume,
};
static int __init spi_init(void) //初始化函数
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type); //注册spi_bus_type
if (status < 0)
goto err1;
status = class_register(&spi_master_class); //创建spi_master_class类
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
根据前面分析,我们知道控制器驱动对应文件spi_s3c24xx.c,其完成的主要工作就是从BSP中取出master controller的信息(通过系统platform_bus),产生master controller并注册到spi_bus_type,接着扫描BSP中注册的设备链表产生spi_device并一并将它们注册到spi_bus_type,这样,当spi_driver注册到spi_bus_type时就可以与spi_device进行匹配(具体是首先通过spi_bus_type的match函数(一般是通过比较两者的名字实现),进一步通过spi_driver的probe函数实现),看其初始化函数
static struct platform_driver s3c24xx_spi_driver = {
.remove = __exit_p(s3c24xx_spi_remove),
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
.pm = S3C24XX_SPI_PMOPS,
},
};
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
Master controller设备需要在BSP中向platfor_bus注册,具体是在/arch/arm/plat-s3c24xx/devs.c中定义:
static struct resource s3c_spi0_resource[] = {
[0] = {
.start = S3C24XX_PA_SPI,
.end = S3C24XX_PA_SPI + 0x1f,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI0,
.end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
struct platform_device s3c_device_spi0 = { //spi控制器信息
.name = "s3c2410-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &s3c_device_spi0_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
并在/arch/arm/mach-s3c2440/mach-mini2440.c中向platform_bus进行注册
static struct platform_device *mini2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&mini2440_device_eth,
&s3c24xx_uda134x,
&s3c_device_nand,
&s3c_device_sdi,
&s3c_device_usbgadget,
&s3c_device_spi0,
};
static void __init mini2440_machine_init(void)
{
#if defined (LCD_WIDTH)
s3c24xx_fb_set_platdata(&mini2440_fb_info);
#endif
s3c_i2c0_set_platdata(NULL);
s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;
s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
s3c_pm_init();
}
MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board")
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = mini2440_map_io,
.init_machine = mini2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
我们回头看platform_bus_type的match函数:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
分析可知BSP中注册的控制器名字和spi_s3c24xx.c注册的驱动名字可以通过名字进行匹配,紧接着会调用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;
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); //分配spi_master,见下面分析
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
hw = spi_master_get_devdata(master); //获得struct s3c24xx_spi
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
hw->pdata = pdata = pdev->dev.platform_data; //获取平台设备信息,需要我们自己在BSP添加
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. */
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //spi_device.mode flags understood by this controller driver
master->num_chipselect = hw->pdata->num_cs;//支持的spi外设个数
master->bus_num = pdata->bus_num; //总线号
/* setup the state for the bitbang driver */
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->master->setup = s3c24xx_spi_setup;
hw->master->cleanup = s3c24xx_spi_cleanup;
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, resource_size(res),
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, resource_size(res));
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) { //设置“设置片选函数”,可以是单独的GPIO,在BSP中配置
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);//初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang); //见下面分析
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
………………………………………………………………………………………………………………………………………………….
}
其中:
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
master = kzalloc(size + sizeof *master, GFP_KERNEL); //分配空间
if (!master)
return NULL;
device_initialize(&master->dev);
master->dev.class = &spi_master_class;
master->dev.parent = get_device(dev);
spi_master_set_devdata(master, &master[1]);// master[1]即struct s3c24xx_spi,作为spi_master的私有数据,可以通过spi_master_get_devdata(master)获得其指针。
return master;
}
spi_bitbang_start主要实现master->hw->bitbang的初始化设置,包括初始化bitbang中的work_struct、建立工作队列、注册master等等:
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work, bitbang_work);//初始化bitbang->work,其工作函数是bitbang_work
spin_lock_init(&bitbang->lock); //初始化操作
INIT_LIST_HEAD(&bitbang->queue);
if (!bitbang->master->mode_bits)
bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;//真正的数据传输函数
if (!bitbang->txrx_bufs) { //此处为 s3c24xx_spi_txrx,见s3c24xx_spi_probe函数
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->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
*/
status = spi_register_master(bitbang->master); //将spi master注册到spi_bus_type,见下面分析
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
spi_register_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)
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注册到spi_bus_type
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_device并注册到spi_bus_type
status = 0;
done:
return status;
}
scan_boardinfo函数:
static void scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
mutex_lock(&board_lock);
list_for_each_entry(bi, &board_list, list) { //遍历board_list链表中的每个boardinfo结构
struct spi_board_info *chip = bi->board_info;
unsigned n;
//读取每个boardinfo中的n_board_info个spi_board_info信息分别产生一个spi_device
for (n = bi->n_board_info; n > 0; n--, chip++) {
if (chip->bus_num != master->bus_num) //判断是否由当前spi master控制
continue;
/* NOTE: this relies on spi_new_device to
* issue diagnostics when given bogus inputs
*/
(void) spi_new_device(master, chip); //产生spi device并注册到spi_bus_type
}
}
mutex_unlock(&board_lock);
}
现在问题出来了,既然是扫描board_list链表产生spi device,那么是在哪里往board_list注册信息的呢?答案是BSP中通过调用spi.c中的spi_register_board_info函数实现的,见下面分析。
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); //添加n个spi_board_info信息
if (!bi)
return -ENOMEM;
bi->n_board_info = n;
memcpy(bi->board_info, info, n * sizeof *info);
mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list); //添加到board_list列表
mutex_unlock(&board_lock);
return 0;
}
spi_new_device函数用于读取已经注册的spi设备信息,产生spi_device并注册到spi_bus_type:
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).
*/
proxy = spi_alloc_device(master); //分配spi_device,总线类型为spi_bus_type,见下面分析
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
// 根据BSP信息,设置spi_device对应字段
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;
}
spi_alloc_device函数
struct spi_device *spi_alloc_device(struct spi_master *master)
{
struct spi_device *spi;
struct device *dev = master->dev.parent;
if (!spi_master_get(master))
return NULL;
spi = kzalloc(sizeof *spi, GFP_KERNEL);
if (!spi) {
dev_err(dev, "cannot alloc spi_device\n");
spi_master_put(master);
return NULL;
}
spi->master = master;
spi->dev.parent = dev;
spi->dev.bus = &spi_bus_type; //在spi.c注册的spi_bus_type
spi->dev.release = spidev_release;
device_initialize(&spi->dev);
return spi;
}
spi_add_device函数,用于将spi_device注册到spi_bus_type
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev = spi->master->dev.parent;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= spi->master->num_chipselect) { //设备序号大于master支持的最大值?
dev_err(dev, "cs%d >= max %d\n",
spi->chip_select,
spi->master->num_chipselect);
return -EINVAL;
}
/* Set the bus ID string */
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
/* We need to make sure there's no other device with this
* chipselect **BEFORE** we call setup(), else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
//该设备已经在spi_bus_type中注册?
if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))
!= NULL) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
status = -EBUSY;
goto done;
}
/* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
*/
status = spi_setup(spi); //对设备模式及时钟进行设置,见下面分析
if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n",
"setup", dev_name(&spi->dev), status);
goto done;
}
/* Device may be bound to an active driver when this returns */
status = device_add(&spi->dev); //将spi_device添加到系统中
if (status < 0)
dev_err(dev, "can't %s %s, status %d\n",
"add", dev_name(&spi->dev), status);
else
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
spi_setup函数:
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits;
int status;
/* help drivers fail *cleanly* when they need options
* that aren't supported with their current master
*/
bad_bits = spi->mode & ~spi->master->mode_bits; //spi master支持该设备的模式位?
if (bad_bits) {
dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
bad_bits);
return -EINVAL;
}
if (!spi->bits_per_word)
spi->bits_per_word = 8;
//利用master的setup函数进行设置,其值为s3c24xx_spi_setup,在s3c24xx_spi_probe函数中设置
status = spi->master->setup(spi); //见下面分析
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
"%u bits/w, %u Hz max --> %d\n",
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
(spi->mode & SPI_3WIRE) ? "3wire, " : "",
(spi->mode & SPI_LOOP) ? "loopback, " : "",
spi->bits_per_word, spi->max_speed_hz,
status);
return status;
}
s3c24xx_spi_setup函数
static int s3c24xx_spi_setup(struct spi_device *spi)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state;
struct s3c24xx_spi *hw = to_hw(spi);
int ret;
/* allocate settings on the first call */
if (!cs) {
cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
if (!cs) {
dev_err(&spi->dev, "no memory for controller state\n");
return -ENOMEM;
}
cs->spcon = SPCON_DEFAULT;
cs->hz = -1;
spi->controller_state = cs;
}
/* initialise the state from the device */
ret = s3c24xx_spi_update_state(spi, NULL); //见下面分析
if (ret)
return ret;
spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy) {
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
/* need to ndelay for 0.5 clocktick ? */
}
spin_unlock(&hw->bitbang.lock);
return 0;
}
s3c24xx_spi_update_state主要用于根据spi_device或者spi_transfer设置spi_device->controller_state。
static int s3c24xx_spi_update_state(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
struct s3c24xx_spi_devstate *cs = spi->controller_state;
unsigned int bpw;
unsigned int hz;
unsigned int div;
unsigned long clk;
//transfer存在,则以其作为设置标准
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
if (!bpw)
bpw = 8;
if (!hz)
hz = spi->max_speed_hz;
if (bpw != 8) {
dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
return -EINVAL;
}
if (spi->mode != cs->mode) {
u8 spcon = SPCON_DEFAULT;
if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB;
if (spi->mode & SPI_CPOL)
spcon |= S3C2410_SPCON_CPOL_HIGH;
cs->mode = spi->mode;
cs->spcon = spcon;
}
if (cs->hz != hz) {
clk = clk_get_rate(hw->clk);
div = DIV_ROUND_UP(clk, hz * 2) - 1;
if (div > 255)
div = 255;
dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n",
div, hz, clk / (2 * (div + 1)));
cs->hz = hz;
cs->sppre = div;
}
return 0;
}
总结:
我们发现在设备驱动侧所有的设备信息都是借助于在BSP中提供的设备信息来创建对应的结构体,其实这是linux中的一种思想,将设备和驱动分离。linux内核这么做是为了提高可移植性,当我们添加一些新的设备的时候,只要在BSP中添加设备信息,当驱动注册的时候,会根据你配置的设备信息产生对应的结构,删除设备亦是如此,整个过程只需要我们改动设备侧而驱动是一模一样的,理所当然提高了驱动的可移植性!