基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)

该系列文章将分为四个部分:

   第一部分,将对SPI子系统整体进行描述,同时给出SPI的相关数据结构,最后描述SPI总线的注册。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一)

   第二部分,即本篇文章,该文将对SPI的主控制器(master)驱动进行描述。

   第三部分,该文将对SPI设备驱动,也称protocol 驱动,进行讲解。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(三)

   第四部分,通过SPI设备驱动留给用户层的API,我们将从上到下描述数据是如何通过SPI的protocol 驱动,由bitbang中转,最后由master驱动将数据传输出

                   去。 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(四)

本文属于第部分。

4. 主控制器驱动程序

4.1 定义 platform device

    下列数据结构位于arch/arm/plat-s3c24XX/devs.c

/* SPI (0) */

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 = {
	.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
        }
};

   platform设备给出了spi0接口的寄存器地址资源以及IRQ资源。注意其设备名为s3c2410-spi。

4.2 定义platform driver

 下列函数位于deivers/spi/s3c24xx.c。

MODULE_ALIAS("platform:s3c2410-spi");
static struct platform_driver s3c24xx_spi_driver = {
    .remove        = __exit_p(s3c24xx_spi_remove),
    .suspend    = s3c24xx_spi_suspend,
    .resume        = s3c24xx_spi_resume,
    .driver        = {
        .name    = "s3c2410-spi",
        .owner    = THIS_MODULE,
    },
};

static int __init s3c24xx_spi_init(void)
{
        return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//设备不可热插拔,所以使用该函数,而不是platform_driver_register
}

static void __exit s3c24xx_spi_exit(void)
{
        platform_driver_unregister(&s3c24xx_spi_driver);
}

module_init(s3c24xx_spi_init);
module_exit(s3c24xx_spi_exit);

  调用了platform_driver_probe注册platform驱动,注册完成以后将会调用platform的s3c24xx_spi_probe函数。

  NOTE:platform驱动的name和platform device的name是相同的。

4.2.1  s3c24xx_spi_probe函数

下列函数位于deivers/spi/s3c24xx.c。

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结构体,其中包括s3c24xx_spi结构的内存空间,使用master.dev.driver_data指向它*/
	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;
	}
	/*获得s3c24xx_spi结构,并清0该结构*/
	hw = spi_master_get_devdata(master);
	memset(hw, 0, sizeof(struct s3c24xx_spi));
	
	hw->master = spi_master_get(master);	/*保存master结构体,同时增加引用计数*/
	hw->pdata = pdata = pdev->dev.platform_data;	/*获取s3c2410_spi_info结构体指针*/
	hw->dev = &pdev->dev;					/*保存platform设备的dev*/

	if (pdata == NULL) {
		dev_err(&pdev->dev, "No platform data supplied\n");
		err = -ENOENT;
		goto err_no_pdata;
	}

	platform_set_drvdata(pdev, hw);	/*让platform_devuce.dev.driver_data 指向 s3c24xx_spi*/
	init_completion(&hw->done);		/*初始化completion*/

	/* setup the master state. */ /*填充master结构体的两个字段*/
	
	master->num_chipselect = hw->pdata->num_cs;
	master->bus_num = pdata->bus_num;

	/* setup the state for the bitbang driver */	/*填充bitbang字段*/

	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);	/*获取IO资源*/
	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,	/*申请IO内存*/
					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);			/*获取irq号*/
	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);	/*申请spi中断,ISR为 s3c24xx_spi_irq*/
	if (err) {
		dev_err(&pdev->dev, "Cannot claim IRQ\n");
		goto err_no_irq;
	}

	hw->clk = clk_get(&pdev->dev, "spi");	/*获取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) {		/*没有定义分配CS管脚的函数*/
		if (pdata->pin_cs < 0) {	/*pin_cs为cs管脚*/
			dev_err(&pdev->dev, "No chipselect pin\n");
			goto err_register;
		}

		err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));/*申请IO地址*/
		if (err) {
			dev_err(&pdev->dev, "Failed to get gpio for cs\n");
			goto err_register;
		}

		hw->set_cs = s3c24xx_spi_gpiocs; /*给出分配cs管脚函数*/
		gpio_direction_output(pdata->pin_cs, 1);	/*设置该管脚为输出,貌似多次一举,在probe已经调用过该函数*/  
	} else
		hw->set_cs = pdata->set_cs;

	s3c24xx_spi_initialsetup(hw);	/*spi控制器初始化*/

	/* 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;

 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);

 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 zeroed driver-private data to allocate; the pointer to this
 *	memory is in the driver_data field of the returned device,
 *	accessible with spi_master_get_devdata().
 * Context: can sleep
 *
 * 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.
 */
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]);

	return master;
}
EXPORT_SYMBOL_GPL(spi_alloc_master);

 
    该函数首先为spi_master结构体以及s3c24xx_spi结构体分配了空间,同时,spi_master.dev.driver_data指向了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;
};

   接着执行了该条语句:

       hw->pdata = pdata = pdev->dev.platform_data;    /*获取s3c2410_spi_info结构体指针*/

  NOTE:在这里获取platform_device.dev.platform_data,也就是平台设备的相关数据,而在4.1小结中的arch/arm/plat-s3c24XX/devs.c文件中并没有发现platform_data的身影,因此这正式需要我们移植的地方。

  随后初始化了completion,这个东东将用于实现同步I/O,详见下文。之后,为master定义了setup方法,为bitbang定义了3个方法。

  接着获取了一系列的资源,同时注册了中断服务程序。接着调用s3c24xx_spi_initialsetup初始化控制器。我们来看下该函数。

  该函数位于下列函数位于deivers/spi/s3c24xx.c。

static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
{
	/* for the moment, permanently enable the clock */

	clk_enable(hw->clk);		/*使能时钟*/

	/* program defaults into the registers */

	writeb(0xff, hw->regs + S3C2410_SPPRE);	/*设置预分频系数,baudrate=pclk/2/(prescaler value+1)*/
	writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);/*使能master out keep*/
	writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);/*master, interrupt mode*/

	if (hw->pdata) {
		if (hw->set_cs == s3c24xx_spi_gpiocs)      /*set_cs 在probe方法中设置为s3c24xx_spi_gpiocs*/
			gpio_direction_output(hw->pdata->pin_cs, 1);	/*设置该管脚为输出,貌似多次一举,在probe已经调用过该函数*/    
*/
		if (hw->pdata->gpio_setup)
			hw->pdata->gpio_setup(hw->pdata, 1);		
	}
}

注意,这里设置了SPI0主控制器工作在master方式,使用中断模式。

最后调用了spi_bitbang_start函数,该函数非常重要,在下一小节中单独讲解。

4.2.2 spi_bitbang_start函数

  下列函数位于drivers/spi/spi_bitbang.c

/**
 * 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,
 * spi_bitbang_cleanup and spi_bitbang_setup_transfer 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;

	INIT_WORK(&bitbang->work, bitbang_work);	/*初始化工作,工作为bitbang_work*/
	spin_lock_init(&bitbang->lock);				/*初始化自旋锁*/
	INIT_LIST_HEAD(&bitbang->queue);			/*初始化链表头,链表为双向循环链表*/

	if (!bitbang->master->transfer)	/*master的transfer方法没有定义过*/
		bitbang->master->transfer = spi_bitbang_transfer;	/*使用spi_bitbang_transfe方法*/
	if (!bitbang->txrx_bufs) {		/*如果bitbang没有txrx_bufs方法,在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)	/*setup方法在probe函数中有定义*/
		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控制器*/
	if (status < 0)
		goto err2;

	return status;

err2:
	destroy_workqueue(bitbang->workqueue);
err1:
	return status;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
  在这里,定义了控制器的transfer方法为spi_bitbang_transfer。创建了一个工作队列和一个工作bitbang_work,同时创建了一个链表。这些东东的作用将在后面介绍。

  最后,调用了spi_register_master函数,该函数将完成SPI控制器的注册,其中还牵涉到spi_device的注册。因此该函数也非常重要。我们来看看这个函数

  下列函数位于drivers/spi/spi_bitbang.c

/**
 * spi_register_master - register SPI master controller
 * @master: initialized master, originally from spi_alloc_master()
 * Context: can sleep
 *
 * 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 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);	/*注册设备*/
	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);
	status = 0;
done:
	return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);

   该函数中,执行了相关的检查,然后注册了master设备,随后调用了scan_boardinfo。函数如下:

   下列函数位于drivers/spi/spi.c

/* FIXME someone should add support for a __setup("spi", ...) that
 * creates board info from kernel command lines
 */

static void scan_boardinfo(struct spi_master *master)
{
    struct boardinfo    *bi;

    mutex_lock(&board_lock);
    /*以board_list为链表头,遍历所有的boardinfo结构,链表由spi_register_board_info添加*/
    list_for_each_entry(bi, &board_list, list) {    
        struct spi_board_info    *chip = bi->board_info;
        unsigned        n;
        /*遍历该boardinfo指向的spi_board_info数组*/
        for (n = bi->n_board_info; n > 0; n--, chip++) {
            if (chip->bus_num != master->bus_num) /*通过bus_num对spi设备和master进行匹配*/
                continue;
            /* NOTE: this relies on spi_new_device to
             * issue diagnostics when given bogus inputs
             */
             /*执行到此,表示匹配完成,SPI设备由该SPI接口来控制,开始创建spi_device*/
            (void) spi_new_device(master, chip);
        }
    }
    mutex_unlock(&board_lock);
}

  NOTE:这个函数通过boardinfo遍历的spi_board_info数组,而spi_board_info是在内核初始化过程中由spi_register_board_info进行注册的,在

               linux/arch/arm/mach-s3c2440/mach-smdk2440.c中并没有调用过该函数,因此这也是需要移植的地方。

  S3C2440共有两个接口:spi0和spi1。chip->bus_num表示该设备使用哪个spi接口,而master->bus_num正好表示了当前的接口。

  该函数中,遍历spi_board_info,通过bus_num完成SPI设备和SPI控制器的匹配,匹配成功则开始建立spi_device设备,该过程通过调用spi_new_device实现。我们接着看下这个函数。

  下列函数位于drivers/spi/spi.c

/**
 * spi_new_device - instantiate one new SPI device
 * @master: Controller to which device is connected
 * @chip: Describes the SPI device
 * Context: can sleep
 *
 * 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).
 *
 * Returns the new device, or NULL.
 */
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结构,并初始化一些字段*/
	if (!proxy)
		return NULL;

	WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
	/*从spi_board_info获取SPI从设备的参数*/
	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;
}
EXPORT_SYMBOL_GPL(spi_new_device);
 

   首先,创建了spi_device结构,让后通过板级信息spi_board_info将SPI从设备的相关信息复制给spi_device结构,从而完成了spi_device结构的定义,最后调用spi_add_device,完成spi_device的注册。

   看下spi_add_device函数,该函数位于drivers/spi/spi.c

/**
 * spi_add_device - Add spi_device allocated with spi_alloc_device
 * @spi: spi_device to register
 *
 * Companion function to spi_alloc_device.  Devices allocated with
 * spi_alloc_device can be added onto the spi bus with this function.
 *
 * Returns 0 on success; negative errno on failure
 */
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) {
		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);

	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->master->setup(spi);	/*调用setup方法,即s3c24xx_spi_setup函数*/
	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;
}
EXPORT_SYMBOL_GPL(spi_add_device);

  在注册spi_device之前,调用了master的setup方法,该方法又将调用s3c24xx_spi_setupxfer和s3c24xx_spi_chipsel函数。

  s3c24xx_spi_setupxfer函数计算预分频系数并写入寄存器。

  s3c24xx_spi_chipsel函数用于禁止或使能CS信号。当使能CS信号时,要设置控制寄存器。这里调用是禁止CS信号。

  下列代码位于 deivers/spi/s3c24xx.c。
#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)

static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev)
{
	return spi_master_get_devdata(sdev->master);
}

static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
	gpio_set_value(spi->pin_cs, pol);
}

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:	/*CS无效时*/
		hw->set_cs(hw->pdata, spi->chip_select, cspol^1);/*即调用s3c24xx_spi_gpiocs使CS无效*/
		break;

	case BITBANG_CS_ACTIVE:		/*CS有效时*/
		spcon = readb(hw->regs + S3C2410_SPCON); /*获取目前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;
		/*激活时钟sck输出*/
		spcon |= S3C2410_SPCON_ENSCK;

		/* write new configration */

		writeb(spcon, hw->regs + S3C2410_SPCON); /*保存新的配置*/
		hw->set_cs(hw->pdata, spi->chip_select, cspol);/*即调用s3c24xx_spi_gpiocs使CS有效*/

		break;
	}
}

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;

	/*没有transfer,则使用spi_device进行配置*/
	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 /= 2;

	if (div > 0)
		div -= 1;

	if (div > 255)	/*只有8位*/
		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);/*即调用s3c24xx_spi_chipsel使CS无效*/
		/* need to ndelay for 0.5 clocktick ? */
	}
	spin_unlock(&hw->bitbang.lock);

	return 0;
}

/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)

static int s3c24xx_spi_setup(struct spi_device *spi) /*maser.setup方法*/
{
	int ret;

	if (!spi->bits_per_word)
		spi->bits_per_word = 8;	/*没有设置则使用8位*/

	if (spi->mode & ~MODEBITS) {	/*检查mode是否有错*/
		dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
			spi->mode & ~MODEBITS);
		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",
		__func__, spi->mode, spi->bits_per_word,
		spi->max_speed_hz);

	return 0;
}

至此,在probe函数中, 由spi_bitbang_start调用所引起的一系列函数调用都已讲解完毕。下面总结下整个调用过程:

基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)_第1张图片

  可以看到,调用spi_bitbang_start以后,spi_master和spi_device都将被注册到内核中。

  下面来看下platform driver的其他几个方法。

4.2.3 remove,suspend以及resume方法

static int __exit s3c24xx_spi_remove(struct platform_device *dev)
{
	struct s3c24xx_spi *hw = platform_get_drvdata(dev);

	platform_set_drvdata(dev, NULL);

	spi_unregister_master(hw->master);	/*注销spi主控制器*/

	clk_disable(hw->clk);			/*禁止时钟*/
	clk_put(hw->clk);			/*释放CLK*/

	free_irq(hw->irq, hw);			/*注销IRQ*/
	iounmap(hw->regs);			/*解除映射*/

	if (hw->set_cs == s3c24xx_spi_gpiocs)
		gpio_free(hw->pdata->pin_cs);	/*释放用于cs的gpio*/

	release_resource(hw->ioarea);
	kfree(hw->ioarea);					

	spi_master_put(hw->master);		/*减少master引用计数*/
	return 0;
}


#ifdef CONFIG_PM	/*如果定义了电源管理*/

static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
{
	struct s3c24xx_spi *hw = platform_get_drvdata(pdev);

	if (hw->pdata && hw->pdata->gpio_setup)
		hw->pdata->gpio_setup(hw->pdata, 0);

	clk_disable(hw->clk);
	return 0;
}

static int s3c24xx_spi_resume(struct platform_device *pdev)
{
	struct s3c24xx_spi *hw = platform_get_drvdata(pdev);

	s3c24xx_spi_initialsetup(hw);
	return 0;
}

#else
#define s3c24xx_spi_suspend NULL
#define s3c24xx_spi_resume  NULL
#endif
  至此,master 驱动的大体结构都已分析完毕,随后第三篇文章将介绍spi设备驱动。
 

你可能感兴趣的:(c,linux,struct,嵌入式,Diagnostics)