本文共 17643 字,大约阅读时间需要 58 分钟。
Allein.Cao原创作品,转载请注明出处:
内核版本: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 __initspi_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中添加设备信息,当驱动注册的时候,会根据你配置的设备信息产生对应的结构,删除设备亦是如此,整个过程只需要我们改动设备侧而驱动是一模一样的,理所当然提高了驱动的可移植性!