linux spi子系统驱动分析

2.6.18内核下已经添加了完整的spi子系统了,参考mtd的分析,将从下到上层,再从上到下层的对其进行分析。
以下先从下到上的进行分析: 
driver/spi下有两个底层相关的spi驱动程序:
spi_s3c24xx.c和spi_s3c24xx_gpio.c
其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c允许用户指定3个gpio口,分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。
s3c2410自带了两个spi接口(spi0和spi1),在此我只研究基于s3c2410下spi接口的驱动程序spi_s3c24xx.c。
首先从spi驱动的检测函数进行分析:
static int s3c24xx_spi_probe(struct platform_device *pdev)
{
       struct s3c24xx_spi *hw;
       struct spi_master *master;
       struct spi_board_info *bi;
       struct resource *res;
       int err = 0;
       int i;
/* pi_alloc_master函数申请了struct spi_master+struct s3c24xx_spi大小的数据,       * spi_master_get_devdata和pi_master_get分别取出struct s3c24xx_spi和struct spi_master结构指针       */
       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;
      }
    /* 填充struct spi_master结构 */
       hw = spi_master_get_devdata(master);
       memset(hw, 0, sizeof(struct s3c24xx_spi));
       hw->master = spi_master_get(master);
       hw->pdata = pdev->dev.platform_data;
       hw->dev = &pdev->dev;
       if (hw->pdata == NULL) {
            dev_err(&pdev->dev, "No platform data supplied\n");
            err = -ENOENT;
            goto err_no_pdata;
       }
       platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw)
       init_completion(&hw->done);
       /* setup the state for the bitbang driver */

    /* 填充hw->bitbang结构(hw->bitbang结构充当一个中间层,相当与input system的input_handle struct) */
       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 */
      /* 申请spi所用到的资源:io、irq、时钟等 */
       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, (res->end - res->start)+1,
                                    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);
       if (hw->irq 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;
       }
       /* for the moment, permanently enable the clock */
       clk_enable(hw->clk);
       /* program defaults into the registers */
      /* 初始化spi相关的寄存器 */
       writeb(0xff, hw->regs + S3C2410_SPPRE);
       writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
       writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
       /* add by lfc */
       s3c2410_gpio_setpin(S3C2410_GPE13, 0);
       s3c2410_gpio_setpin(S3C2410_GPE12, 0);
       s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
       s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
       s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
       /* end add */
       /* setup any gpio we can */
    /* 片选 */
       if (!hw->pdata->set_cs) {
            s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);
            s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT);
       }
       /* register our spi controller */
      /* 最终通过调用spi_register_master来注册spi控制器(驱动) */
       err = spi_bitbang_start(&hw->bitbang);
       if (err) {
            dev_err(&pdev->dev, "Failed to register SPI master\n");
            goto err_register;
       }
       dev_dbg(hw->dev, "shutdown=%d\n", hw->bitbang.shutdown);
       /* register all the devices associated */
      /* 注册所用使用本spi驱动的设备 */
       bi = &hw->pdata->board_info[0];
       for (i = 0; i pdata->board_size; i++, bi++) {
            dev_info(hw->dev, "registering %s\n", bi->modalias);
            bi->controller_data = hw;
            spi_new_device(master, bi);
       }
       return 0;
err_register:
       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 driver-private data to preallocate; the pointer to this
*    memory is in the class_data field of the returned class_device,
*    accessible with spi_master_get_devdata().
*
* 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.
*/
/*注释已经写得很清楚了,本函数旨在分配spi_master struct *其中,device为主控制设备,size为需要预分配的设备私有数据大小 *该函数被spi主控制器驱动所调用,用于在调用spi_register_master注册主控制器前 *分配spi_master struct,分配bus number和初始化主控制器的操作方法 *注意在分配spi_master struct的时候多分配了大小为size的设备私有数据 *并通过spi_master_set_devdata函数把其放到class_data field里,以后可以通过spi_master_get_devdata来访问
*/
struct spi_master * __init_or_module
spi_alloc_master(struct device *dev, unsigned size)
{
       struct spi_master    *master;
       if (!dev)
            return NULL;
       master = kzalloc(size + sizeof *master, SLAB_KERNEL);
       if (!master)
            return NULL;
       class_device_initialize(&master->cdev);
       master->cdev.class = &spi_master_class;
       master->cdev.dev = get_device(dev);
       spi_master_set_devdata(master, &master[1]);
       return master;
}
/*
* 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 and
* spi_bitbang_cleanup 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;
    /*bitbang_work    * 初始化a work,后面再create_singlethread_workqueue,    * 等到有数据要传输的时候,在spi_bitbang_transfer函数中通过调用queue_work(bitbang->workqueue, &bitbang->work)    * 把work扔进workqueue中调度运行    * 这是内核的一贯做法,在mmc/sd驱动中也是这样处理的^_^    */
       INIT_WORK(&bitbang->work, bitbang_work, bitbang);
    /* 初始化自旋锁和链表头,以后用到 */
       spin_lock_init(&bitbang->lock);
    spi_new_device INIT_LIST_HEAD(&bitbang->queue);
       if (!bitbang->master->transfer)
            bitbang->master->transfer = spi_bitbang_transfer;//spi数据的传输就是通过调用这个方法来实现的
    /* spi_s3c24xx.c驱动中有相应的txrx_bufs处理方法,在bitbang_work中被调用 */
       if (!bitbang->txrx_bufs) {
            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;
            }
    /* spi_s3c24xx.c驱动中有相应的setup处理方法,在稍后的spi_new_device中被调用 */
       } 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(
                     bitbang->master->cdev.dev->bus_id);
       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);
       if (status workqueue);
err1:
       return status;
}
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
*
* 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 __init_or_module
spi_register_master(struct spi_master *master)
{
       static atomic_t       dyn_bus_id = ATOMIC_INIT((1cdev.dev;
       int                   status = -ENODEV;
       int                   dynamic = 0;
       if (!dev)
            return -ENODEV;
       /* convention:   dynamically assigned bus IDs count down from the max */
       if (master->bus_num 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.
      */
       snprintf(master->cdev.class_id, sizeof master->cdev.class_id,
            "spi%u", master->bus_num);
       status = class_device_add(&master->cdev);//注册设备
       if (status cdev.class_id,
                     dynamic ? " (dynamic)" : "");
       /* populate children from any spi device tables */
       scan_boardinfo(master);
       status = 0;
done:
       return status;
}
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
/* * scan board_list for spi_board_info which is registered by spi_register_board_info * 很可惜,s3c24xx的spi驱动中不支持spi_register_board_info这种标准方式注册方式,而是直接调用spi_new_device内部函数 */
static void __init_or_module
scan_boardinfo(struct spi_master *master)
{
       struct boardinfo        *bi;
       struct device           *dev = master->cdev.dev;
       down(&board_lock);
       list_for_each_entry(bi, &board_list, list) {
            struct spi_board_info *chip = bi->board_info;
            unsigned             n;
            for (n = bi->n_board_info; n > 0; n--, chip++) {
                     if (chip->bus_num != master->bus_num)
                               continue;
                     /* some controllers only have one chip, so they
                     * might not use chipselects.   otherwise, the
                     * chipselects are numbered 0..max.
                     */
                     if (chip->chip_select >= master->num_chipselect
                                    && master->num_chipselect) {
                               dev_dbg(dev, "cs%d > max %d\n",
                                    chip->chip_select,
                                    master->num_chipselect);
                               continue;
                     }
                     (void) spi_new_device(master, chip);
            }
       }
       up(&board_lock);
}
/*
* Board-specific early init code calls this (probably during arch_initcall)
* with segments of the SPI device table.   Any device nodes are created later,
* after the relevant parent SPI controller (bus_num) is defined.   We keep
* this table of devices forever, so that reloading a controller driver will
* not make Linux forget about these hard-wired devices.
*
* Other code can also call this, e.g. a particular add-on board might provide
* SPI devices through its expansion connector, so code initializing that board
* would naturally declare its SPI devices.
*
* The board info passed can safely be __initdata ... but be careful of
* any embedded pointers (platform_data, etc), they're copied as-is.
*/
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);
       if (!bi)
            return -ENOMEM;
       bi->n_board_info = n;
       memcpy(bi->board_info, info, n * sizeof *info);
       down(&board_lock);
       list_add_tail(&bi->list, &board_list);
       up(&board_lock);
       return 0;
}
/* 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).
*/
struct spi_device *__init_or_module
spi_new_device(struct spi_master *master, struct spi_board_info *chip)
{
       struct spi_device    *proxy;//这个结构很重要,以后就是通过这个结构来操作实际的spi设备的
       struct device           *dev = master->cdev.dev;
       int                   status;
       /* NOTE:   caller did any chip->bus_num checks necessary */
       if (!spi_master_get(master))
            return NULL;
       proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
       if (!proxy) {
            dev_err(dev, "can't alloc dev for cs%d\n",
                     chip->chip_select);
            goto fail;
      }
    /* 初始化spi_device 结构各成员 */
       proxy->master = master;
       proxy->chip_select = chip->chip_select;
       proxy->max_speed_hz = chip->max_speed_hz;
       proxy->mode = chip->mode;
       proxy->irq = chip->irq;
       proxy->modalias = chip->modalias;
       snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
                     "%s.%u", master->cdev.class_id,
                     chip->chip_select);
       proxy->dev.parent = dev;
       proxy->dev.bus = &spi_bus_type;
       proxy->dev.platform_data = (void *) chip->platform_data;
       proxy->controller_data = chip->controller_data;
       proxy->controller_state = NULL;
       proxy->dev.release = spidev_release;
       /* drivers may modify this default i/o setup */
    /* 调用master->setup(即s3c24xx_spi_setup)函数初始化spi设备 */
       status = master->setup(proxy);
       if (status dev.bus_id, status);
            goto fail;
       }
       /* driver core catches callers that misbehave by defining
      * devices that already exist.
      */
       status = device_register(&proxy->dev);//真正注册原始设备
       if (status dev.bus_id, status);
            goto fail;
       }
       dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
       return proxy;
fail:
       spi_master_put(master);
       kfree(proxy);
       return NULL;
}
static int s3c24xx_spi_setup(struct spi_device *spi)
{
       int ret;
    /* 进行一些检查性操作 */
       if (!spi->bits_per_word)
            spi->bits_per_word = 8;
       if ((spi->mode & SPI_LSB_FIRST) != 0)
            return -EINVAL;
       ret = s3c24xx_spi_setupxfer(spi, NULL);
       if (ret dev, "setupxfer returned %d\n", ret);
            return ret;
       }
       dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
            __FUNCTION__, spi->mode, spi->bits_per_word,
            spi->max_speed_hz);
       return 0;
}
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;
       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 = (div / 2) - 1;//求出预分频值
       if (div   255)
            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);//修改时钟,先禁用spi
            /* need to ndelay for 0.5 clocktick ? */
       }
       spin_unlock(&hw->bitbang.lock);
       return 0;
}
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:
    /* 禁用spi(禁用片选) */
            if (hw->pdata->set_cs)
                     hw->pdata->set_cs(hw->pdata, value, cspol);
            else
                     s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1);
            break;
       case BITBANG_CS_ACTIVE:
/*    * 启用spi:根据需要设置寄存器并启用使能片选     * (如果spi_board_info中没有设置相应的mode选项的话,那就只能使用默认值SPPIN_DEFAULT和SPCON_DEFAULT了)     */
            spcon = readb(hw->regs + S3C2410_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;
            spcon |= S3C2410_SPCON_ENSCK;
            /* write new configration */
            writeb(spcon, hw->regs + S3C2410_SPCON);
            if (hw->pdata->set_cs)
                     hw->pdata->set_cs(hw->pdata, value, cspol);
            else
                     s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol);
            break;
       }
}
好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值