博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入浅出spi驱动之控制器驱动(二)
阅读量:2255 次
发布时间:2019-05-09

本文共 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中添加设备信息,当驱动注册的时候,会根据你配置的设备信息产生对应的结构,删除设备亦是如此,整个过程只需要我们改动设备侧而驱动是一模一样的,理所当然提高了驱动的可移植性!

你可能感兴趣的文章
MySQL忘记密码(mysql5.5 for windows xp)
查看>>
远程访问VMware虚拟机Ubuntu的mysql
查看>>
禁止运行程序多个实例
查看>>
Unity直接渲染YUV几种格式
查看>>
可以下载专利文献的网址
查看>>
delphi写webservice服务会发生的一些问题
查看>>
apache下部署delphi写的cgi
查看>>
delphi TTcpClient TTcpServer分析
查看>>
svn的branch/tag
查看>>
网络传输中文本传输与二进制传输与字符集
查看>>
windows messagebox样式
查看>>
Delphi应用程序日志写入系统日志
查看>>
缺乏版本控制,软件测试易陷混乱风险
查看>>
C++中extern “C”含义深层探索
查看>>
《Visual C++ 2010入门教程》系列五:合理组织项目、使用外部工具让工作更有效
查看>>
直接用socket实现HTTP协议(下载专用)
查看>>
List Control将找到的内容列着色
查看>>
drwtsn32简介
查看>>
NTSD简介
查看>>
NTSD(Command Line)调试DMP格式文件
查看>>