该系列文章将分为四个部分:
第一部分,将对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。
下列函数位于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); } }
最后调用了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信号。
#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调用所引起的一系列函数调用都已讲解完毕。下面总结下整个调用过程:
可以看到,调用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设备驱动。