Linux SPI框架
水平有限,描述不当之处还请指出,转载请注明出处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 { 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 { 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; };
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_spi memset(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; }
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; }
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; }
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); else kfree(spidev); return status; }
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 { /* 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(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); }
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; }
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); }
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; }
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; }