1. 程式人生 > >spi協議->裸機程式->master驅動程式分析

spi協議->裸機程式->master驅動程式分析

SPI協議參考:

SPI協議及其工作原理淺析 

    http://bbs.chinaunix.net/thread-1916003-1-1.html

SPI匯流排協議及SPI時序圖詳解

    http://blog.163.com/sunshine_linting/blog/static/44893323201181482335951

一、概述     SPI, Serial Perripheral Interface, 序列外圍裝置介面, 是 Motorola 公司推出的一種同步序列介面技術. SPI 匯流排在物理上是通過接在外圍裝置微控制器(PICmicro) 上面的微處理控制單元 (MCU) 上叫作同步串列埠(Synchronous Serial Port) 的模組(Module)來實現的, 它允許 MCU 以全雙工

的同步序列方式, 與各種外圍裝置進行高速資料通訊.     SPI 主要應用在 EEPROM, Flash, 實時時鐘(RTC), 數模轉換器(ADC), 數字訊號處理器(DSP) 以及數字訊號解碼器之間. 它在晶片中只佔用四根管腳 (Pin) 用來控制以及資料傳輸, 節約了晶片的 pin 數目, 同時為 PCB 在佈局上節省了空間. 正是出於這種簡單易用的特性, 現在越來越多的晶片上都集成了 SPI技術.二、 特點

  1. 採用主-從模式(Master-Slave) 的控制方式     SPI 規定了兩個 SPI 裝置之間通訊必須由主裝置 (Master) 來控制次裝置 (Slave). 一個 Master 裝置可以通過提供 Clock 以及對 Slave 裝置進行片選

(Slave Select) 來控制多個 Slave 裝置, SPI 協議還規定 Slave 裝置的Clock 由 Master 裝置通過 SCK 管腳提供給 Slave 裝置, Slave 裝置本身不能產生或控制 Clock, 沒有 Clock 則 Slave 裝置不能正常工作.   2. 採用同步方式(Synchronous)傳輸資料     Master 裝置會根據將要交換的資料來產生相應的時鐘脈衝(Clock Pulse), 時鐘脈衝組成了時鐘訊號(Clock Signal) , 時鐘訊號通過時鐘極性 (CPOL) 和 時鐘相位 (CPHA) 控制著兩個 SPI 裝置間何時資料交換以及何時對接收到的資料進行取樣, 來保證資料在兩個裝置之間是同步傳輸的.

  3. 資料交換(Data Exchanges)     SPI 裝置間的資料傳輸之所以又被稱為資料交換, 是因為 SPI 協議規定一個 SPI 裝置不能在資料通訊過程中僅僅只充當一個 "傳送者(Transmitter)" 或者 "接收者(Receiver)".在每個 Clock 週期內, SPI 裝置都會發送並接收一個 bit 大小的資料, 相當於該裝置有一個 bit 大小的資料被交換了.     一個 Slave 裝置要想能夠接收到 Master 發過來的控制訊號, 必須在此之前能夠被 Master 裝置進行訪問 (Access). 所以, Master 裝置必須首先通過 SS/CS pin 對 Slave 裝置進行片選, 把想要訪問的 Slave 裝置選上.

    在資料傳輸的過程中,  每次接收到的資料必須在下一次資料傳輸之前被取樣. 如果之前接收到的資料沒有被讀取, 那麼這些已經接收完成的資料將有可能會被丟棄,  導致 SPI 物理模組最終失效. 因此, 在程式中一般都會在 SPI 傳輸完資料後, 去讀取 SPI 裝置裡的資料, 即使這些資料(Dummy Data)在我們的程式裡是無用的.

三、 工作機制 

  1. 概述

    通常情況下, 我們只需要對上圖所描述的四個管腳(pin) 進行程式設計即可控制整個 SPI 裝置之間的資料通訊:     SCK:Serial Clock主要的作用是 Master 裝置往 Slave 裝置傳輸時鐘訊號, 控制資料交換的時機以及速率;     SS/CS:Slave Select/Chip Select, 用於 Master 裝置片選 Slave 裝置, 使被選中的 Slave 裝置能夠被 Master 裝置所訪問;     SDO/MOSI: Serial Data Output/Master Out Slave In, 在 Master 上面也被稱為 Tx-Channel, 作為資料的出口, 主要用於 SPI 裝置傳送資料;     SDI/MISO: Serial Data Input/Master In Slave Out, 在 Master 上面也被稱為 Rx-Channel, 作為資料的入口, 主要用於SPI 裝置接收資料;     SPI 裝置在進行通訊的過程中, Master 裝置和 Slave 裝置之間會產生一個資料鏈路迴環(Data Loop), 就像上圖所畫的那樣, 通過 SDO 和 SDI 管腳, SSPSR 控制資料移入移出 SSPBUF, Controller 確定 SPI 匯流排的通訊模式, SCK 傳輸時鐘訊號.   2.工作模式

    CPOL位表示初始時鐘極性:       CPOL = 0表示時鐘初始為低電平,所以開始的第一(leading)邊緣是上升沿,第二邊緣(trailing)是下降沿。        CPOL = 1時的時鐘啟動為高電平,所以第一(leading)的邊緣是下降沿。     CPHA的指示用於取樣資料的時鐘相位(注意是取樣資料,取樣是指的將資料線上的資料所存起來)       CPHA = 0時表示邊沿超前       CPHA = 1表示邊沿滯後   以 S3C2440 為例:

    模式0:CPOL = 0 CPHA = 0 主機從機的資料均是在下降沿改變,上升沿鎖存     模式1:CPOL = 0CPHA = 1 主機從機的資料均是在上升沿改變,下降沿鎖存     模式2:CPOL = 1CPHA = 0 主機從機的資料均是在上升沿改變,下降沿鎖存     模式3:CPOL = 0CPHA = 1 主機從機的資料均是在下降沿改變,上升沿鎖存   以模式0為例,看看具體時序圖:     通常情況下,我們並不關心初始時鐘電平狀態,大多采用上升沿鎖存,下降沿改變資料的方式,也就是模式0和模式3,下面看一個具體的spi_flash讀資料的時序圖

    顯然,主機和從機都是在上升沿鎖存資料,下降沿改變資料。四、裸機程式     在使用spi控制器的情況下,裸機程式十分簡單,以 S3C2440 為例,簡單說明一下。       1、配置引腳         1. 設定片選輸出低電平        2. 配置 GPE11 SPIMISO 、GPE12 SPIMOSI 、GPE13 SPICLK 為特殊功能引腳       2、配置控制暫存器         SPCONn[6-5]:SPI模式選擇,查詢 中斷 DMA等         SPCONn[4]  :時鐘使能         SPCONn[3]  :主機模式、從機模式         SPCONn[2]  :CPOL         SPCONn[1]  :CPHA         SPCONn[0]  :接收資料是是否自動傳送,這裡選普通模式,我們自己傳送就好       3、傳送資料         1、檢查狀態暫存器 傳輸是否可用 SPSTAn[0] 1表示可用,0表示正在傳送或者接收         2、將資料寫入 SPTDATn       4、接收資料         1、檢查狀態暫存器 傳輸是否可用 SPSTAn[0] 1表示可用,0表示正在傳送或者接收         2、寫資料 0xff 到 SPTDATn ,這就是普通模式的要求,接收的同時傳送,0xff傳送完了,資料也就接收完了         3、檢查狀態暫存器 傳輸是否可用 SPSTAn[0] 1表示可用,0表示正在傳送或者接收         4、從 SPRDATn 取資料

static void SPIFlash_Set_CS(char val)
{
    if (val)
        GPGDAT |= (1<<10);
    else
        GPGDAT &= ~(1<<10);
}
static void SPI_GPIO_Init(void)
{
    /* GPG1 OLED_CSn output 
    * GPG10 FLASH_CSn output
    */
    GPGCON &= ~((3<<(1*2)) | (3<<(10*2)));
    GPGCON |= (1<<(1*2)) | (1<<(10*2));
    GPGDAT |= (1<<1) | (1<<10);
<span class="hljs-comment">/* 
* GPF3  OLED_DC   output
* GPE11 SPIMISO   
* GPE12 SPIMOSI   
* GPE13 SPICLK    
*/</span>
GPFCON &amp;= ~(<span class="hljs-number">3</span>&lt;&lt;(<span class="hljs-number">3</span>*<span class="hljs-number">2</span>));    
GPFCON |= (<span class="hljs-number">1</span>&lt;&lt;(<span class="hljs-number">3</span>*<span class="hljs-number">2</span>));    

GPECON &amp;= ~((<span class="hljs-number">3</span>&lt;&lt;(<span class="hljs-number">11</span>*<span class="hljs-number">2</span>)) | (<span class="hljs-number">3</span>&lt;&lt;(<span class="hljs-number">12</span>*<span class="hljs-number">2</span>)) | (<span class="hljs-number">3</span>&lt;&lt;(<span class="hljs-number">13</span>*<span class="hljs-number">2</span>)));
GPECON |= ((<span class="hljs-number">2</span>&lt;&lt;(<span class="hljs-number">11</span>*<span class="hljs-number">2</span>)) | (<span class="hljs-number">2</span>&lt;&lt;(<span class="hljs-number">12</span>*<span class="hljs-number">2</span>)) | (<span class="hljs-number">2</span>&lt;&lt;(<span class="hljs-number">13</span>*<span class="hljs-number">2</span>)));

}

void SPISendByte(unsigned char val) { while (!(SPSTA0 & 1)); SPTDAT0 = val; }

unsigned char SPIRecvByte(void) { SPTDAT0 = 0xff; while (!(SPSTA0 & 1)); return SPRDAT0; }

static void SPIControllerInit(void) { /* OLED : 100ns, 10MHz * FLASH : 104MHz * 取10MHz * 10 = 50 / 2 / (Prescaler value + 1) * Prescaler value = 1.5 = 2 * Baud rate = 50/2/3=8.3MHz */ SPPRE0 = 2; SPPRE1 = 2;

<span class="hljs-comment">/* [6:5] : 00, polling mode
* [4]   : 1 = enable 
* [3]   : 1 = master
* [2]   : 0
* [1]   : 0 = format A
* [0]   : 0 = normal mode
*/</span>
SPCON0 = (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">4</span>) | (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">3</span>);
SPCON1 = (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">4</span>) | (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">3</span>);

}

void SPIInit(void) { /* 初始化引腳 */ SPI_GPIO_Init();

SPIControllerInit();

}

    實際的SPI通訊中,是由片選函式、位元組讀、位元組寫三部分組合而成~例如:

void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
{
    int i;
SPIFlash_Set_CS(<span class="hljs-number">0</span>);
SPISendByte(<span class="hljs-number">0x03</span>);
SPISendByte(addr &gt;&gt; <span class="hljs-number">16</span>);
SPISendByte(addr &gt;&gt; <span class="hljs-number">8</span>);
SPISendByte(addr &amp; <span class="hljs-number">0xff</span>);
<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; len; i++)
    buf[i] = SPIRecvByte();

SPIFlash_Set_CS(<span class="hljs-number">1</span>);    

}

五、master 驅動例項詳細分析     上一篇文章中,分析了 master 和 spi 裝置驅動的框架,我們知道 master 是在 atmel_spi_probe 函式中分配、設定、註冊。其實在哪不重要,主要是設定的那些東西,我們再來看一下都設定了哪些東西

    master = spi_alloc_master(&pdev->dev, sizeof *as);          /* 設定 master  */       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;   // 所支援的模式       master->bus_num = pdev->id;   // 控制器編號,用於分辨外圍spi裝置是連線在哪一個控制器上       master->num_chipselect = 4;  // 片選最大值+1,spi裝置的片選值要小於它       master->setup = atmel_spi_setup; // 一個控制器上可能接有多個spi裝置,它們的頻率和模式是不一樣的,用於裝置之間切換時設定這些資訊。       master->transfer = atmel_spi_transfer;   // 最重要的傳送函式       ret = request_irq(irq, atmel_spi_interrupt, 0,  dev_name(&pdev->dev), master);     master->transfer 函式,就跟I2c裡的adapter->xxx_i2c_xfer函式一樣,是最底層的傳送接收函式,在spi中,裝置驅動層呼叫到master層transfer函式的過程是這樣的spi_sync(spi, &m)->spi_async(spi,&m)->master->transfer(spi, &m),也就是說,裝置驅動層傳遞給我們一個spi_device 和 一個 spi_message ,還有一點值得注意的。

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int status;
message-&gt;complete = spi_complete;
message-&gt;context = &amp;done;
status = spi_async(spi, message);
<span class="hljs-keyword">if</span> (status == <span class="hljs-number">0</span>) {
	wait_for_completion(&amp;done);
	status = message-&gt;status;
}
message-&gt;context = <span class="hljs-literal">NULL</span>;
<span class="hljs-keyword">return</span> status;

}

    在 spi_sync 中,呼叫 spi_async(spi, message) 之後,會 wait_for_completion(&done)休眠,那麼我們在master的傳送函式中,傳送完成,自然要 mesg->complete(mesg->context),還要設定 mesg->status = 0

    在 spi_new_device 我們根據 Board_list 裡的資訊建立了 spi_device,它含有一下資訊

struct spi_device *spi_new_device(struct spi_master *master,  
                  struct spi_board_info *chip)  
{  
    struct spi_device   *proxy;  
    int         status;  
proxy = spi_alloc_device(master);  

proxy-&gt;chip_select = chip-&gt;chip_select;  
proxy-&gt;max_speed_hz = chip-&gt;max_speed_hz;  
proxy-&gt;mode = chip-&gt;mode;  
proxy-&gt;irq = chip-&gt;irq;  
strlcpy(proxy-&gt;modalias, chip-&gt;modalias, <span class="hljs-keyword">sizeof</span>(proxy-&gt;modalias));  
proxy-&gt;dev.platform_data = (<span class="hljs-keyword">void</span> *) chip-&gt;platform_data;  
proxy-&gt;controller_data = chip-&gt;controller_data;  
proxy-&gt;controller_state = <span class="hljs-literal">NULL</span>;  

status = spi_add_device(proxy);  

<span class="hljs-keyword">return</span> proxy;  

}

    對於 spi_message,它對應於一個不可打斷的spi傳輸過程,可以理解為片選選中直到取消選中的過程(特殊情況下,一個spi_message裡面是可以取消片選再選中的),而 spi_message 由 spi_transfer 組成,根據 tx_buf  rx_buf 是否為空來判斷是 寫還是讀 操作。     那麼,我們master->transfer函式需要做什麼?

      1、傳輸前,根據 spi_device 裡的資訊,對 master 進行設定(呼叫master->setup),因為一個spi控制器可能接有多個spi裝置,它們的頻率 模式可能是不一樣的       2、取出 spi_message 裡的 spi_transfer 進行暫存器操作,傳送或接收,這個過程和 I2C 的傳輸過程是類似的。     下面開始分析程式碼,先看setup函式。程式碼參考:韋東山老師的spi教程

static int s3c2440_spi_setup(struct spi_device *spi)
{
    struct s3c_spi_info *info;
    struct clk *clk;
<span class="hljs-keyword">int</span> cpol = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> cpha = <span class="hljs-number">0</span>;

<span class="hljs-keyword">int</span> div;

info = spi_master_get_devdata(spi-&gt;master);
clk = clk_get(<span class="hljs-literal">NULL</span>, <span class="hljs-string">"plck"</span>);

<span class="hljs-comment"><span class="hljs-comment">/* 設定傳輸模式 : mode</span>

* 傳輸頻率 : max_speed_hz * bits_per_word : 不用管 */

<span class="hljs-comment">/* spi_mode: CPOL,CPHA組成一個值,0,1,2,3
 */</span>

<span class="hljs-keyword">if</span> (spi-&gt;mode &amp; <span class="hljs-number">1</span>)
{
    cpha = <span class="hljs-number">1</span>;
}
<span class="hljs-keyword">if</span> (spi-&gt;mode &amp; <span class="hljs-number">2</span>)
{
    cpol = <span class="hljs-number">1</span>;
}

<span class="hljs-comment">/* 寫入SPCON0,1 */</span>
<span class="hljs-comment">/* [6:5] : 01, polling mode
 * [4]   : 1 = enable 
 * [3]   : 1 = master
 * [2]   : CPOL
 * [1]   : CPHA
 * [0]   : 0 = normal mode
 */</span>
writeb((<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">5</span>) | (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">4</span>) | (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">3</span>) | (cpol &lt;&lt; <span class="hljs-number">2</span>) | (cpha &lt;&lt; <span class="hljs-number">1</span>), info-&gt;reg_base + S3C2410_SPCON);

<span class="hljs-comment">/*
 * Baud rate = PCLK / 2 / (Prescaler value + 1)
 * Prescaler value = PCLK/(2*Baud rate) - 1
 * Prescaler Value : 0,1,...,255
 */</span>
div = DIV_ROUND_UP(clk_get_rate(clk), spi-&gt;max_speed_hz * <span class="hljs-number">2</span>) - <span class="hljs-number">1</span>;
clk_put(clk);

<span class="hljs-keyword">if</span> (div &gt; <span class="hljs-number">255</span>)
	div = <span class="hljs-number">255</span>;

writeb(div, info-&gt;reg_base + S3C2410_SPPRE);
    
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

    info 是我們用 spi_alloc_master 申請記憶體時多申請出來的那個自定義結構體,韋東山老師在裡面放了 暫存器基地址等資訊,後面完整程式碼會看到,setup 函式很簡單,就是根據 spi_device 裡的模式,設定master的模式,根據 spi_device 的最大頻率,設定 master 的工作頻率。   下面來看transfer函式     思路:

      1、首先一個 spi_message 對應一個 片選過程,我們要先片選

      2、master->setup3、取出 spi_message 中的 第一個 spi_transfer

如果是讀:跟裸機一樣,先寫0xff,等待中斷

        如果是寫:那麼將 spi_transfer->tx_buf[0]裡的資料寫到暫存器,等待休眠

何時喚醒?

在中斷服務程式裡,判斷當前 spi_transfer 的傳送接收完畢了,喚醒,取出下一個 spi_transfer ,如果有所得 spi_transfer 傳輸完了,則喚醒裝置驅動層程序

中斷服務程式:

讀:先把上次讀的取出來,判斷是否還有資料要讀?如果有則繼續傳送0xff,等待中斷,沒有則喚醒,進入下一個spi_transfer

        寫:判斷當前 spi_transfer 的傳送是否完成,沒有完成則把下一個Buf裡的資料傳送或接收。完成了,則喚醒,進入下一個spi_transfer。

附上韋東山老師完整原始碼:

#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/slab.h>

#include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> #include <linux/spi/s3c24xx.h> #include <linux/module.h>

#include <plat/regs-spi.h> #include <mach/regs-gpio.h>

/* 構造註冊spi_master */

static struct spi_master *spi0_controller; static struct spi_master *spi1_controller;

struct s3c_spi_info { int irq; unsigned int reg_base; struct completion done; struct spi_transfer *cur_t; int cur_cnt; struct platform_device *pdev; };

static int s3c2440_spi_setup(struct spi_device *spi) { struct s3c_spi_info *info; struct clk *clk;

<span class="hljs-keyword">int</span> cpol = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> cpha = <span class="hljs-number">0</span>;

<span class="hljs-keyword">int</span> div;

info = spi_master_get_devdata(spi-&gt;master);
clk = clk_get(<span class="hljs-literal">NULL</span>, <span class="hljs-string">"plck"</span>);

<span class="hljs-comment"><span class="hljs-comment">/* 設定傳輸模式 : mode</span>

* 傳輸頻率 : max_speed_hz * bits_per_word : 不用管 */

<span class="hljs-comment">/* spi_mode: CPOL,CPHA組成一個值,0,1,2,3
 */</span>

<span class="hljs-keyword">if</span> (spi-&gt;mode &amp; <span class="hljs-number">1</span>)
{
    cpha = <span class="hljs-number">1</span>;
}
<span class="hljs-keyword">if</span> (spi-&gt;mode &amp; <span class="hljs-number">2</span>)
{
    cpol = <span class="hljs-number">1</span>;
}

<span class="hljs-comment">/* 寫入SPCON0,1 */</span>
<span class="hljs-comment">/* [6:5] : 01, polling mode
 * [4]   : 1 = enable 
 * [3]   : 1 = master
 * [2]   : CPOL
 * [1]   : CPHA
 * [0]   : 0 = normal mode
 */</span>
writeb((<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">5</span>) | (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">4</span>) | (<span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">3</span>) | (cpol &lt;&lt; <span class="hljs-number">2</span>) | (cpha &lt;&lt; <span class="hljs-number">1</span>), info-&gt;reg_base + S3C2410_SPCON);

<span class="hljs-comment">/*
 * Baud rate = PCLK / 2 / (Prescaler value + 1)
 * Prescaler value = PCLK/(2*Baud rate) - 1
 * Prescaler Value : 0,1,...,255
 */</span>
div = DIV_ROUND_UP(clk_get_rate(clk), spi-&gt;max_speed_hz * <span class="hljs-number">2</span>) - <span class="hljs-number">1</span>;
clk_put(clk);

<span class="hljs-keyword">if</span> (div &gt; <span class="hljs-number">255</span>)
	div = <span class="hljs-number">255</span>;

writeb(div, info-&gt;reg_base + S3C2410_SPPRE);
    
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

static int s3c2440_spi_transfer(struct spi_device *spi, struct spi_message *mesg) { struct spi_master *master = spi->master; struct s3c_spi_info *info; struct spi_transfer *t = NULL;

info = spi_master_get_devdata(master);

<span class="hljs-comment">/* 1. 選中晶片 */</span>
s3c2410_gpio_setpin(spi-&gt;chip_select, <span class="hljs-number">0</span>);  <span class="hljs-comment">/* 預設為低電平選中 */</span>

<span class="hljs-comment">/* 2. 發資料 */</span>

<span class="hljs-comment">/* 2.1 傳送第1個spi_transfer之前setup */</span>
master-&gt;setup(spi);

<span class="hljs-comment">/* 2.2 從spi_message中逐個取出spi_transfer,執行它 */</span>
list_for_each_entry (t, &amp;mesg-&gt;transfers, transfer_list) {
    <span class="hljs-comment">/* 處理這個spi_transfer */</span>
    info-&gt;<span class="hljs-keyword">cur_t</span> = t;
    info-&gt;cur_cnt = <span class="hljs-number">0</span>;
	init_completion(&amp;info-&gt;done);

    <span class="hljs-comment">/* 如果該spi_transfer的speed_hz或bits_per_word
     * 不是0, 則呼叫setup來設定
     */</span>

    <span class="hljs-keyword">if</span> (t-&gt;tx_buf)
    {
        <span class="hljs-comment">/* 傳送 */</span>
        writeb(((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)t-&gt;tx_buf)[<span class="hljs-number">0</span>], info-&gt;reg_base + S3C2410_SPTDAT);

        <span class="hljs-comment">/* 它會觸發中斷 */</span>

        <span class="hljs-comment">/* 休眠 等待當前transfer 傳送完畢 */</span>
        wait_for_completion(&amp;info-&gt;done);
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(t-&gt;rx_buf)
    {
        <span class="hljs-comment">/* 接收 */</span>
        writeb(<span class="hljs-number">0xff</span>, info-&gt;reg_base + S3C2410_SPTDAT);
        <span class="hljs-comment">/* 它會觸發中斷 */</span>

        <span class="hljs-comment">/* 休眠 等待當前 transfer 接收完畢 */</span>
        wait_for_completion(&amp;info-&gt;done);
    }

    <span class="hljs-comment">/* 如果該spi_transfer的cs_change為1
     * 則取消片選
     */</span>
}


<span class="hljs-comment">/* 2.3 喚醒等待的程序 */</span>
mesg-&gt;status = <span class="hljs-number">0</span>;
mesg-&gt;complete(mesg-&gt;context);    

<span class="hljs-comment">/* 3. 取消片選 */</span>
s3c2410_gpio_setpin(spi-&gt;chip_select, <span class="hljs-number">1</span>);  <span class="hljs-comment">/* 預設為低電平選中 */</span>

<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

static irqreturn_t s3c2440_spi_irq(int irq, void *dev_id) { struct spi_master *master = (struct spi_master *)dev_id; struct s3c_spi_info *info = spi_master_get_devdata(master); struct spi_transfer *t = info->cur_t;

<span class="hljs-keyword">if</span> (!t)
{
    <span class="hljs-comment">/* 誤觸發 */</span>
	<span class="hljs-keyword">return</span> IRQ_HANDLED;            
}

<span class="hljs-comment">/* 處理 spi_transfer */</span>

<span class="hljs-keyword">if</span> (t-&gt;tx_buf) <span class="hljs-comment">/* 是傳送 */</span>
{
    info-&gt;cur_cnt++;
    
    <span class="hljs-keyword">if</span> (info-&gt;cur_cnt &lt; t-&gt;len)<span class="hljs-comment">/* 沒發完? */</span>
        writeb(((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)t-&gt;tx_buf)[info-&gt;cur_cnt], info-&gt;reg_base + S3C2410_SPTDAT);        
    <span class="hljs-keyword">else</span>
        complete(&amp;info-&gt;done); <span class="hljs-comment">/* 喚醒 */</span>
}
<span class="hljs-keyword">else</span> <span class="hljs-comment">/* 接收 */</span>
{
    <span class="hljs-comment">/* 讀/存資料 */</span>
    ((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)t-&gt;rx_buf)[info-&gt;cur_cnt] = readb(info-&gt;reg_base + S3C2410_SPRDAT);
    info-&gt;cur_cnt++;
    
    <span class="hljs-keyword">if</span> (info-&gt;cur_cnt &lt; t-&gt;len)<span class="hljs-comment">/* 沒收完? */</span>
        writeb(<span class="hljs-number">0xff</span>, info-&gt;reg_base + S3C2410_SPTDAT);        
    <span class="hljs-keyword">else</span>
        complete(&amp;info-&gt;done); <span class="hljs-comment">/* 喚醒 */</span>
}
<span class="hljs-keyword">return</span> IRQ_HANDLED;    

}

static void s3c2440_spi_controler_init(int which) { struct clk *clk = clk_get(NULL, “spi”);

<span class="hljs-comment">/* 使能spi controller 0/1的時鐘 */</span>
clk_enable(clk);
clk_put(clk);

<span class="hljs-comment">/* GPIO */</span>
<span class="hljs-keyword">if</span> (which == <span class="hljs-number">0</span>)
{
    <span class="hljs-comment">/* SPI controller 0 */</span>
    <span class="hljs-comment">/*
     * GPE11 SPIMISO   
     * GPE12 SPIMOSI   
     * GPE13 SPICLK    
     */</span>
    s3c2410_gpio_cfgpin(S3C2410_GPE(<span class="hljs-number">11</span>), S3C2410_GPE11_SPIMISO0);
    s3c2410_gpio_cfgpin(S3C2410_GPE(<span class="hljs-number">12</span>), S3C2410_GPE12_SPIMOSI0);
    s3c2410_gpio_cfgpin(S3C2410_GPE(<span class="hljs-number">13</span>), S3C2410_GPE13_SPICLK0);        

}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (which == <span class="hljs-number">1</span>)
{
    <span class="hljs-comment">/* SPI controller 1 */</span>
    <span class="hljs-comment">/*
     * GPG5 SPIMISO   
     * GPG6 SPIMOSI   
     * GPG7 SPICLK    
     */</span>
    s3c2410_gpio_cfgpin(S3C2410_GPG(<span class="hljs-number">5</span>), S3C2410_GPG5_SPIMISO1);
    s3c2410_gpio_cfgpin(S3C2410_GPG(<span class="hljs-number">6</span>), S3C2410_GPG6_SPIMOSI1);
    s3c2410_gpio_cfgpin(S3C2410_GPG(<span class="hljs-number">7</span>), S3C2410_GPG7_SPICLK1);        

    <span class="hljs-comment">/* 使能spi controller 1的時鐘 */</span>
}

}

static struct spi_master *create_spi_master_s3c2440(int bus_num, unsigned int reg_base_phy, int irq) { int ret; struct spi_master *master; struct s3c_spi_info *info; struct platform_device *pdev;

pdev = platform_device_alloc(<span class="hljs-string">"s3c2440_spi"</span>, bus_num);
platform_device_add(pdev);

master = spi_alloc_master(&amp;pdev-&gt;dev, <span class="hljs-keyword">sizeof</span>(struct s3c_spi_info));
master-&gt;bus_num = bus_num;
master-&gt;num_chipselect = <span class="hljs-number">0xffff</span>;
master-&gt;mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

master-&gt;setup    = s3c2440_spi_setup;
master-&gt;transfer = s3c2440_spi_transfer;

info = spi_master_get_devdata(master);
info-&gt;reg_base = (<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span>)ioremap(reg_base_phy, <span class="hljs-number">0x18</span>);
info-&gt;irq = irq;
info-&gt;pdev = pdev;

<span class="hljs-comment">/* 硬體初始化 */</span>
s3c2440_spi_controler_init(bus_num);

ret = request_irq(irq, s3c2440_spi_irq, <span class="hljs-number">0</span>, <span class="hljs-string">"s3c2440_spi"</span>, master);

spi_register_master(master);

<span class="hljs-keyword">return</span> master;

}

static void destroy_spi_master_s3c2440(struct spi_master *master) { struct s3c_spi_info *info = spi_master_get_devdata(master);;

spi_unregister_master(master);
platform_device_del(info-&gt;pdev);
platform_device_put(info-&gt;pdev);
free_irq(info-&gt;irq, master);
iounmap((<span class="hljs-keyword">void</span> *)info-&gt;reg_base);
<span class="hljs-comment">//kfree(master);</span>

}

static int spi_s3c2440_init(void) { spi0_controller = create_spi_master_s3c2440(0, 0x59000000, IRQ_SPI0); spi1_controller = create_spi_master_s3c2440(1, 0x59000020, IRQ_SPI1);

<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

static void spi_s3c2440_exit(void) { destroy_spi_master_s3c2440(spi0_controller); destroy_spi_master_s3c2440(spi1_controller); }

module_init(spi_s3c2440_init); module_exit(spi_s3c2440_exit); MODULE_DESCRIPTION(“SPI Controller Driver”); MODULE_AUTHOR(); MODULE_LICENSE(“GPL”);