水平有限,描述不當之處還請指出,轉載請注明出處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_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; }
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); elsekfree(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; }
?