水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7733476

====

Linux的SPI子系统采用主机驱动和外设驱动分离的思想,首先主机SPI控制器是一种平台设备,因此它以platform的方式注册进内核,外设的信息是以boardinfo形式静态定义的,在创建spi_master时,会根据外设的bus_num和主机的bus_num是否相等,来选择是否将该外设挂接在该SPI主控制器下。先看SPI子系统中几个关键的数据结构:

struct spi_master用来描述一个SPI主控制器

struct spi_master {  struct device    dev;  s16    bus_num; /*总线编号*/  u16    num_chipselect;/*支持的外设数量*/  u16    dma_alignment;  int   (*transfer)(struct spi_device *spi, struct spi_message *mesg);/*用于将消息添加到队列*/  void  (*cleanup)(struct spi_device *spi);
};  

struct spi_device用来描述一个SPI从设备

struct spi_device {  struct device       dev;  struct spi_master   *master;                 /*从设备所属的SPI主控器*/  u32         max_speed_hz;   /*最大传输频率*/  u8          chip_select;    /*片选号,用于区别其他从设备*/  u8          mode;           /*传输模式*/
/*各个mode的定义*/
#define SPI_CPHA    0x01             /* clock phase */
#define SPI_CPOL    0x02             /* clock polarity */
#define SPI_MODE_0  (0|0)        /* (original MicroWire) */
#define SPI_MODE_1  (0|SPI_CPHA)
#define SPI_MODE_2  (SPI_CPOL|0)
#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04         /* chipselect active high? */
#define SPI_LSB_FIRST   0x08         /* per-word bits-on-wire */
#define SPI_3WIRE   0x10             /* SI/SO signals shared */
#define SPI_LOOP    0x20             /* loopback mode */  u8          bits_per_word; /*每个字的比特数*/  int         irq;           /*所使用的中断*/  void            *controller_state;  void            *controller_data;  char            modalias[32];  /*设备名,在和从设备驱动匹配时会用到*/
};  

struct spi_driver用来描述一个SPI从设备的驱动,它的形式和struct platform_driver是一致的

struct spi_driver {  int         (*probe)(struct spi_device *spi);  int         (*remove)(struct spi_device *spi);  void            (*shutdown)(struct spi_device *spi);  int         (*suspend)(struct spi_device *spi, pm_message_t mesg);  int         (*resume)(struct spi_device *spi);  struct device_driver    driver;
};  

SPI子系统初始化的第一步就是将SPI总线注册进内核,并且在/sys下创建一个spi_master的类,以后注册的从设备都将挂接在该总线下

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总线  if (status < 0)  goto err1;  status = class_register(&spi_master_class);//注册spi_master类  if (status < 0)  goto err2;  return 0;
err2:  bus_unregister(&spi_bus_type);  err1:  kfree(buf);  buf = NULL;  err0:  return status;
}  

我们来看spi_bus_type的定义

struct bus_type spi_bus_type = {  .name       = "spi",  .dev_attrs  = spi_dev_attrs,  .match      = spi_match_device,  .uevent     = spi_uevent,  .suspend    = spi_suspend,  .resume     = spi_resume,
};  

来看挂接在SPI总线下的从设备和从设备驱动是如何匹配的,也就是spi_match_device函数

static int spi_match_device(struct device *dev, struct device_driver *drv)
{  const struct spi_device *spi = to_spi_device(dev);  return strcmp(spi->modalias, drv->name) == 0;
}  

这里可以看到是将struct device_driver中的name字段与struct spi_device中的modalias字段进行匹配

这里已经完成了SPI子系统初始化的第一步,也就是注册SPI总线,这一步是和平台无关的,第二步是和平台相关的初始化,下一节再做介绍。

====

上节介绍了SPI子系统中的一些重要数据结构和SPI子系统初始化的第一步,也就是注册SPI总线。这节介绍针对于s3c24xx平台的SPI子系统初始化,在看具体的代码之前,先上一张自己画的图,帮助理清初始化的主要步骤

显然,SPI是一种平台特定的资源,所以它是以platform平台设备的方式注册进 内核的,因此它的struct platform_device结构是已经静态定义好了的,现在只待它的struct platform_driver注册,然后和platform_device匹配。

初始化的入口:

static int __init s3c24xx_spi_init(void)
{  return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}  

platform_driver_probe()会调用 platform_driver_register()来注册驱动,然后在注册的过程中寻求匹配的platform_device,一旦匹配成功,便会调 用probe函数,也就是s3c24xx_spi_probe(),在看这个函数之前,还得介绍几个相关的数据结构。

struct s3c2410_spi_info是一个板级结构,也是在移植时就定义好的,在初始化spi_master时用到,platform_device-->dev-->platform_data会指向这个结构。

struct s3c2410_spi_info {  int          pin_cs;    /* simple gpio cs */  unsigned int         num_cs;    /* total chipselects */  int          bus_num;/* bus number to use. */  void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);  void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
};  

struct 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;
};  

struct spi_bitbang用于控制实际的数据传输

struct spi_bitbang {  struct workqueue_struct *workqueue;  /*工作队列*/  struct work_struct  work;  spinlock_t      lock;  struct list_head    queue;  u8          busy;  u8          use_dma;  u8          flags;      /* extra spi->mode support */  struct spi_master   *master;         /*bitbang所属的master*/  /*用于设置设备传输时的时钟,字长等*/  int (*setup_transfer)(struct spi_device *spi, struct spi_transfer *t);  void (*chipselect)(struct spi_device *spi, int is_on);
#define BITBANG_CS_ACTIVE   1   /* normally nCS, active low */
#define BITBANG_CS_INACTIVE 0  /*针对于平台的传输控制函数*/  int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);  /* txrx_word[SPI_MODE_*]() just looks like a shift register */  u32 (*txrx_word[4])(struct spi_device *spi,  unsigned nsecs,  u32 word, u8 bits);
};  

下面来看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;  /*创建spi_master,并将spi_master->private_data指向s3c24xx_spi*/  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;  }  hw = spi_master_get_devdata(master);//获取s3c24xx_spimemset(hw, 0, sizeof(struct s3c24xx_spi));  hw->master = spi_master_get(master);  hw->pdata = pdata = pdev->dev.platform_data;  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. */  /*片选数和SPI主控制器编号是在platform_data中已经定义好了的*/  master->num_chipselect = hw->pdata->num_cs;  master->bus_num = pdata->bus_num;  /* setup the state for the bitbang driver */  /*设置bitbang的所属master和控制传输的相关函数*/  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);  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;  }  /*映射SPI控制寄存器*/  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 < 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) {  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);  /* register our spi controller */  /* 注册主机SPI控制器 */  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);  }

-

int spi_bitbang_start(struct spi_bitbang *bitbang)
{  int status;  if (!bitbang->master || !bitbang->chipselect)  return -EINVAL;  /*初始化一个struct work,处理函数为bitbang_work*/  INIT_WORK(&bitbang->work, bitbang_work);  spin_lock_init(&bitbang->lock);  INIT_LIST_HEAD(&bitbang->queue);  /*检测bitbang中的函数是否都定义了,如果没定义,则默认使用spi_bitbang_xxx*/  if (!bitbang->master->transfer)  bitbang->master->transfer = spi_bitbang_transfer;  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;  }  } else if (!bitbang->master->setup)  return -EINVAL;  /* this task is the only thing to touch the SPI bits */  bitbang->busy = 0;  /*创建bitbang的工作队列*/  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 */  /*注册spi_master*/  status = spi_register_master(bitbang->master);  if (status < 0)  goto err2;  return status;  err2:  destroy_workqueue(bitbang->workqueue);  err1:  return status;
}  

下一个关键函数就是spi_register_master(),用于注册spi_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)//片选数不能为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设备  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_master下的从设备  status = 0;  done:  return status;
}  

-

static void scan_boardinfo(struct spi_master *master)
{  struct boardinfo    *bi;  mutex_lock(&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;  /* NOTE: this relies on spi_new_device to * issue diagnostics when given bogus inputs */  /*bus_num相等则创建新设备*/  (void) spi_new_device(master, chip);  }  }  mutex_unlock(&board_lock);
}  

spi_board_info是板级信息,是在移植时就写好的,并且要将其注册

struct spi_board_info {  char        modalias[32];  /*名字*/  const void  *platform_data;  void        *controller_data;  int     irq;          /*中断号*/  u32     max_speed_hz; /*最高传输速率*/  u16     bus_num;      /*所属的spi_master编号*/  u16     chip_select;  /*片选号*/  u8      mode;         /*传输模式*/
};  

最后一步就是将相应的从设备注册进内核

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). */  /*创建SPI_device*/  proxy = spi_alloc_device(master);  if (!proxy)return NULL;  WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));  /*初始化*/  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;
}

本节以spidev设备驱动为例,来阐述SPI数据传输的过程。spidev是内核中一个通用的设备驱动,我们注册的从设备都可以使用该驱动,只需在注册 时将从设备的modalias字段设置为"spidev",这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送, 后读取,就需要两个字段),每个字段都被封装成一个transfer,N个transfer可以被添加到message中,作为一个消息包进行传输。当用 户发出传输数据的请求时,message并不会立刻传输到从设备,而是由之前定义的transfer()函数将message放入一个等待队列中,这些 message会以FIFO的方式有workqueue调度进行传输,这样能够避免SPI从设备同一时间对主SPI控制器的竞争。和之前一样,还是习惯先 画一张图来描述数据传输的主要过程。在使用spidev设备驱动时,需要先初始化spidev. spidev是以字符设备的形式注册进内核的。

static int __init spidev_init(void)
{  int status;  /* Claim our 256 reserved device numbers.  Then register a class * that will key udev/mdev to add/remove /dev nodes.  Last, register * the driver which manages those device numbers. */  BUILD_BUG_ON(N_SPI_MINORS > 256);  /*将spidev作为字符设备注册*/  status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  if (status < 0)  return status;  /*创建spidev类*/  spidev_class = class_create(THIS_MODULE, "spidev");  if (IS_ERR(spidev_class)) {  unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  return PTR_ERR(spidev_class);  }  /*注册spidev的driver,可与modalias字段为"spidev"的spi_device匹配*/  status = spi_register_driver(&spidev_spi);  if (status < 0) {  class_destroy(spidev_class);  unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  }  return status;
}  

与相应的从设备匹配成功后,则调用spidev中的probe函数

static int spidev_probe(struct spi_device *spi)
{  struct spidev_data  *spidev;  int         status;  unsigned long       minor;  /* Allocate driver data */  spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);  if (!spidev)  return -ENOMEM;  /* Initialize the driver data */  spidev->spi = spi;//设定spi  spin_lock_init(&spidev->spi_lock);  mutex_init(&spidev->buf_lock);  INIT_LIST_HEAD(&spidev->device_entry);  /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */  mutex_lock(&device_list_lock);  minor = find_first_zero_bit(minors, N_SPI_MINORS);//寻找没被占用的次设备号  if (minor < N_SPI_MINORS) {  struct device *dev;  /*计算设备号*/  spidev->devt = MKDEV(SPIDEV_MAJOR, minor);  /*在spidev_class下创建设备*/  dev = device_create(spidev_class, &spi->dev, spidev->devt,  spidev, "spidev%d.%d",  spi->master->bus_num, spi->chip_select);  status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  } else {  dev_dbg(&spi->dev, "no minor number available!\n");  status = -ENODEV;  }  if (status == 0) {  set_bit(minor, minors);//将minors的相应位置位,表示该位对应的次设备号已被占用 list_add(&spidev->device_entry, &device_list);//将创建的spidev添加到device_list  }  mutex_unlock(&device_list_lock);  if (status == 0)  spi_set_drvdata(spi, spidev);  elsekfree(spidev);  return status;
}  

然后就可以利用spidev模块提供的接口来实现主从设备之间的数据传输了。我们以spidev_write()函数为例来分析数据传输的过程,实际上spidev_read()和其是差不多的,只是前面的一些步骤不一样,可以参照上图。

static ssize_t spidev_write(struct file *filp, const char __user *buf,  size_t count, loff_t *f_pos)
{  struct spidev_data  *spidev;  ssize_t         status = 0;  unsigned long       missing;  /* chipselect only toggles at start or end of operation */  if (count > bufsiz)  return -EMSGSIZE;  spidev = filp->private_data;  mutex_lock(&spidev->buf_lock);  //将用户要发送的数据拷贝到spidev->buffer  missing = copy_from_user(spidev->buffer, buf, count);  if (missing == 0) {//全部拷贝成功,则调用spidev_sysn_write()  status = spidev_sync_write(spidev, count);  } else  status = -EFAULT;  mutex_unlock(&spidev->buf_lock);  return status;
}  

-

static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len)
{  struct spi_transfer t = {//设置传输字段  .tx_buf     = spidev->buffer,  .len        = len,  };  struct spi_message   m;//创建message  spi_message_init(&m);  spi_message_add_tail(&t, &m);//将transfer添加到message中  return spidev_sync(spidev, &m);
}  

我们来看看struct spi_transfer和struct spi_message是如何定义的

struct spi_transfer {  /* it's ok if tx_buf == rx_buf (right?) * for MicroWire, one buffer must be null * buffers must work with dma_*map_single() calls, unless *   spi_message.is_dma_mapped reports a pre-existing mapping */  const void  *tx_buf;//发送缓冲区  void        *rx_buf;//接收缓冲区  unsigned    len;    //传输数据的长度  dma_addr_t  tx_dma;  dma_addr_t  rx_dma;  unsigned    cs_change:1; //该位如果为1,则表示当该transfer传输完后,改变片选信号  u8      bits_per_word;//字比特数  u16     delay_usecs;  //传输后的延时   u32     speed_hz;  //指定的时钟  struct list_head transfer_list;//用于将该transfer链入message
};  

-

struct spi_message {  struct list_head    transfers;//用于链接spi_transfer  struct spi_device   *spi;      //指向目的从设备  unsigned        is_dma_mapped:1;  /* REVISIT:  we might want a flag affecting the behavior of the * last transfer ... allowing things like "read 16 bit length L" * immediately followed by "read L bytes".  Basically imposing * a specific message scheduling algorithm. * * Some controller drivers (message-at-a-time queue processing) * could provide that as their default scheduling algorithm.  But * others (with multi-message pipelines) could need a flag to * tell them about such special cases. */  /* completion is reported through a callback */  void            (*complete)(void *context);//用于异步传输完成时调用的回调函数  void            *context;                  //回调函数的参数  unsigned        actual_length;            //实际传输的长度  int         status;  /* for optional use by whatever driver currently owns the * spi_message ...  between calls to spi_async and then later * complete(), that's the spi_master controller driver. */  struct list_head    queue; //用于将该message链入bitbang等待队列  void            *state;
};  

继续跟踪源码,进入spidev_sync(),从这一步开始,read和write就完全一样了

spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{DECLARE_COMPLETION_ONSTACK(done);  int status;  message->complete = spidev_complete;//设置回调函数  message->context = &done;              spin_lock_irq(&spidev->spi_lock);  if (spidev->spi == NULL)  status = -ESHUTDOWN;  else  status = spi_async(spidev->spi, message);//调用spi核心层的函数spi_async()
spin_unlock_irq(&spidev->spi_lock);  if (status == 0) {  wait_for_completion(&done);  status = message->status;  if (status == 0)  status = message->actual_length;  }  return status;
}

-

static inline int spi_async(struct spi_device *spi, struct spi_message *message)
{message->spi = spi;  /*调用master的transfer函数将message放入等待队列*/  return spi->master->transfer(spi, message);
}  

s3c24xx平台下的transfer函数是在bitbang_start()函数中定义的,为bitbang_transfer()

int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{  struct spi_bitbang  *bitbang;  unsigned long       flags;  int         status = 0;  m->actual_length = 0;  m->status = -EINPROGRESS;  bitbang = spi_master_get_devdata(spi->master);  spin_lock_irqsave(&bitbang->lock, flags);  if (!spi->max_speed_hz)  status = -ENETDOWN;  else {  list_add_tail(&m->queue, &bitbang->queue);//将message添加到bitbang的等待队列  queue_work(bitbang->workqueue, &bitbang->work);//调度运行work  }  spin_unlock_irqrestore(&bitbang->lock, flags);  return status;
}  

这里可以看到transfer函数不负责实际的数据传输,而是将message添加到 等待队列中。同样在spi_bitbang_start()中,有这样一个定义INIT_WORK(&bitbang->work, bitbang_work);因此bitbang_work()函数会被调度运行,类似于底半部机制

static void bitbang_work(struct work_struct *work)
{  struct spi_bitbang  *bitbang = container_of(work, struct spi_bitbang, work);//获取bitbang  unsigned long       flags;  spin_lock_irqsave(&bitbang->lock, flags);  bitbang->busy = 1;  while (!list_empty(&bitbang->queue)) {//等待队列不为空  struct spi_message  *m;  struct spi_device   *spi;  unsigned        nsecs;  struct spi_transfer *t = NULL;  unsigned        tmp;  unsigned        cs_change;  int         status;  int         (*setup_transfer)(struct spi_device *, struct spi_transfer *);  /*取出等待队列中的的第一个message*/  m = container_of(bitbang->queue.next, struct spi_message, queue);  list_del_init(&m->queue);//将message从队列中删除  spin_unlock_irqrestore(&bitbang->lock, flags);  /* FIXME this is made-up ... the correct value is known to * word-at-a-time bitbang code, and presumably chipselect() * should enforce these requirements too? */  nsecs = 100;  spi = m->spi;  tmp = 0;  cs_change = 1;  status = 0;  setup_transfer = NULL;  /*遍历message中的所有传输字段,逐一进行传输*/  list_for_each_entry (t, &m->transfers, transfer_list) {  /* override or restore speed and wordsize */  if (t->speed_hz || t->bits_per_word) {  setup_transfer = bitbang->setup_transfer;  if (!setup_transfer) {  status = -ENOPROTOOPT;  break;  }  }  /*调用setup_transfer根据transfer中的信息进行时钟、字比特数的设定*/  if (setup_transfer) {  status = setup_transfer(spi, t);  if (status < 0)  break;  }  /* set up default clock polarity, and activate chip; * this implicitly updates clock and spi modes as * previously recorded for this device via setup(). * (and also deselects any other chip that might be * selected ...) */  if (cs_change) {//使能外设的片选  bitbang->chipselect(spi, BITBANG_CS_ACTIVE);  ndelay(nsecs);  }  cs_change = t->cs_change;//这里确定进行了这个字段的传输后是否要改变片选状态  if (!t->tx_buf && !t->rx_buf && t->len) {  status = -EINVAL;  break;  }  /* transfer data.  the lower level code handles any * new dma mappings it needs. our caller always gave * us dma-safe buffers. */  if (t->len) {  /* REVISIT dma API still needs a designated * DMA_ADDR_INVALID; ~0 might be better. */  if (!m->is_dma_mapped)  t->rx_dma = t->tx_dma = 0;  /*调用针对于平台的传输函数txrx_bufs*/  status = bitbang->txrx_bufs(spi, t);  }  if (status > 0)  m->actual_length += status;  if (status != t->len) {  /* always report some kind of error */  if (status >= 0)  status = -EREMOTEIO;  break;  }  status = 0;  /* protocol tweaks before next transfer */  /*如果要求在传输完一个字段后进行delay,则进行delay*/  if (t->delay_usecs)  udelay(t->delay_usecs);  if (!cs_change)  continue;  /*最后一个字段传输完毕了,则跳出循环*/  if (t->transfer_list.next == &m->transfers)  break;  /* sometimes a short mid-message deselect of the chip * may be needed to terminate a mode or command */  ndelay(nsecs);  bitbang->chipselect(spi, BITBANG_CS_INACTIVE);  ndelay(nsecs);  } m->status = status;  m->complete(m->context);  /* restore speed and wordsize */  if (setup_transfer)  setup_transfer(spi, NULL);  /* normally deactivate chipselect ... unless no error and * cs_change has hinted that the next message will probably * be for this chip too. */  if (!(status == 0 && cs_change)) {  ndelay(nsecs);  bitbang->chipselect(spi, BITBANG_CS_INACTIVE);  ndelay(nsecs);  }  spin_lock_irqsave(&bitbang->lock, flags);  }  bitbang->busy = 0;  spin_unlock_irqrestore(&bitbang->lock, flags);
}  

只要bitbang->queue等待队列不为空,就表示相应的SPI主控制器上还有 传输任务没有完成,因此bitbang_work()会被不断地调度执行。 bitbang_work()中的工作主要是两个循环,外循环遍历等待队列中的message,内循环遍历message中的transfer,在 bitbang_work()中,传输总是以transfer为单位的。当选定了一个transfer后,便会调用transfer_txrx()函数, 进行实际的数据传输,显然这个函数是针对于平台的SPI控制器而实现的,在s3c24xx平台中,该函数为s3c24xx_spi_txrx();

static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{  struct s3c24xx_spi *hw = to_hw(spi);  dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, t->len);  hw->tx = t->tx_buf;//获取发送缓冲区  hw->rx = t->rx_buf;//获取读取缓存区  hw->len = t->len;  //获取数据长度  hw->count = 0;  init_completion(&hw->done);//初始化完成量  /* send the first byte */  /*只发送第一个字节,其他的在中断中发送(读取)*/  writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);  wait_for_completion(&hw->done);  return hw->count;
}  

-

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
{  /*如果tx不为空,也就是说当前是从主机向从机发送数据,则直接将tx[count]发送过去, 如果tx为空,也就是说当前是从从机向主机发送数据,则向从机写入0*/  return hw->tx ? hw->tx[count] : 0;
}  

负责SPI数据传输的中断函数:

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
{  struct s3c24xx_spi *hw = dev;  unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);  unsigned int count = hw->count;  /*冲突检测*/  if (spsta & S3C2410_SPSTA_DCOL) {  dev_dbg(hw->dev, "data-collision\n");  complete(&hw->done);  goto irq_done;  }  /*设备忙检测*/  if (!(spsta & S3C2410_SPSTA_READY)) {  dev_dbg(hw->dev, "spi not ready for tx?\n");  complete(&hw->done);  goto irq_done;  }  hw->count++;  if (hw->rx)//读取数据到缓冲区  hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);  count++;  if (count < hw->len)//向从机写入数据  writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);  else//count == len,一个字段发送完成,唤醒完成量  complete(&hw->done);  irq_done:  return IRQ_HANDLED;
}  

这里可以看到一点,即使tx为空,也就是说用户申请的是从从设备读取数据,也要不断地向从设备写入数据,只不过写入从设备的是无效数据(0),这样做得目的是为了维持SPI总线上的时钟。至此,SPI框架已分析完毕。

转载于:https://www.cnblogs.com/tfanalysis/articles/3766212.html

Linux SPI框架相关推荐

  1. linux SPI驱动代码追踪

    一.Linux SPI 框架概述 linux系统下的spi驱动程序从逻辑上可以分为3个部分: SPI Core:SPI Core 是 Linux 内核用来维护和管理 spi 的核心部分,SPI Cor ...

  2. Linux SPI驱动框架(2)——控制器驱动层

    SPI控制器驱动层   上节中,讲了SPI核心层的东西,这一部分,以全志平台SPI控制器驱动为例,对SPI控制器驱动进行说明. SPI控制器驱动,即SPI硬件控制器对应的驱动,核心部分需要实现硬件SP ...

  3. Linux SPI驱动框架(3)——设备驱动层

    SPI设备驱动层   Linux SPI驱动框架(1)和(2)中分别介绍了SPI框架中核心层,和控制器驱动层.其实实际开发过程中,不是IC原厂工程师比较少会接触控制器驱动层,设备驱动层才是接触比较多的 ...

  4. i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架

    Linux SPI 驱动框架分为两部分: SPI总线控制器驱动:SOC的 SPI 控制器外设驱动 SPI设备驱动:基于SPI总线控制器驱动编写,针对具体的SPI从机设备 一.SPI总线控制器驱动 基于 ...

  5. Linux SPI驱动框架(1)——核心层

    概述   linux SPI驱动框架主要分为核心层,控制器驱动层以及设备驱动层.具体结构可参考下图   图中,最下层是硬件空间,SPI总线控制器,总线控制器负责硬件上的数据交互.内核空间中,需要有对应 ...

  6. linux spi不使用框架,Linux spi驱动框架之执行流程

    Linux spi驱动架构由三部分构成:SPI核心层.SPI控制器驱动层.和SPI设备驱动程序. 1.SPI核心层: SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义.SPI控制器驱 ...

  7. Linux spi驱动框架之执行流程-nuc970-att7022

    转载地址:http://blog.csdn.net/chenliang0224/article/details/51236499 Linux spi驱动架构由三部分构成:SPI核心层.SPI控制器驱动 ...

  8. Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化

    我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...

  9. Linux SPI总线和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能: 1 ...

最新文章

  1. Windows Azure Pack集成配置SPF
  2. R语言因子分析FA(factor analysis)步骤实战
  3. 虚幻引擎UE4-命令行使用的一些技巧
  4. 在项目开始前,为客户做专门的“需求变更流程”培训是必要的
  5. java oracle 乐观锁,oracle为什么默认乐观锁
  6. C++radix sort基数排序的实现算法之一(附完整源码)
  7. P4299 首都(LCT、重心)
  8. sublime运行python代码_怎么用sublime运行python
  9. 2019.1.15 作业
  10. NumPy用户指南(2)——安装NumPy
  11. 机器学习技法-01-2-Large-Margin Separating Hyperplane
  12. 【知识点和练习题】心田花开:二年级语文汉语拼音补习
  13. 基于stm32无线充电器设计
  14. Pxe +ks+ cobbler+ cobbler-web 实现centos6.7,centos7.2无人值守全自动化网络安装系统。
  15. class ts 扩展方法_ts各种类型和用法
  16. 永磁同步电机(三)——三相永磁同步电机仿真
  17. Qt5 学习之路及嵌入式开发教程11:Qt5标准输入对话框类及QSlider控件
  18. Excel·VBA自定义正则表达式函数、使用
  19. 机器学习实战——分类
  20. 求知若饥,虚心若愚Stay Hungry, Stay Foolish--2005斯坦福大学05年毕业演讲

热门文章

  1. 在NSUserDefaults中存储自定义类型的数据
  2. CentOS6 下Vim安装和配置
  3. VS2010 自动化整理代码(1)--- VS正则表达替换 PK Vim
  4. oracle11g高可用,oracle11g 搭建DG环境总结
  5. 虚拟鼠标代替安卓触屏_美术学院18级虚拟空间设计专业数字图像程序基础课程优秀结课成果展示优秀学生王雨禾作品展示...
  6. python网页表格读取_是否可以读取网页html表格数据?
  7. micropython esp8266教程_(一)ESP8266/nodemcu如何使用MicroPython进行开发
  8. delphi 停电文本数据丢失_NLP中的文本分析和特征工程
  9. ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别
  10. Linux系统更新最新版R语言方法