1. 程式人生 > >二、Linux spi 控制器驅動

二、Linux spi 控制器驅動

1、概覽

對於ARM平臺來說,大多數CPU都是SoC。spi控制器被整合在CPU內部。spi總線上的資料傳輸過程通常就是這個spi控制器來控制的。為了使spi控制器能工作在linux spi子系統中,我們就需要針對CPU內部的spi控制器編寫一個驅動。前面的博文提到過,在linux spi系統中struct spi_master物件就對應了一個spi控制器。編寫spi控制器驅動其實就是實現struct spi_master中的各種介面,並將該struct spi_master結構註冊到spi子系統中去。下面將結合程式碼,具體介紹如何實現一個spi控制器驅動。

2、向核心申明spi控制器裝置

對ARM平臺中各種控制器,主要有兩種方法在核心中申明一個spi控制器裝置。一種是在板級程式碼中向核心中註冊一個struct platform_device物件。另一種方法是使用device tree來描述spi控制器的各種資訊,然後由device tree相關程式碼解析並向核心申明一個spi控制器相關的裝置。對於第一種方法主要在2.6.xx的核心中普遍使用的。而第二種方法在3.xx的核心中使用的。雖然說新版的核心對於第一種方法仍然支援,但是已經很少使用了。對於描述spi控制器上述兩種方法同樣適用,下面具體介紹兩種方法。

2.1)板級程式碼中向核心註冊spi控制器

為了更好理解,我們可以通過程式碼來具體講解如何註冊一個描述spi控制器的struct platform_device

。鑑於新的平臺已經使用Device Tree來描述SoC中的各種控制器,我們選用比較老的CPU來講解這種方式描述spi控制器,我們用s3c64xx的板級程式碼作為示例。目前在3.xx核心中arch/arm/mach-s3c64xx/mach-crag6410.c中有如何註冊描述spi控制器的struct platform_device的。此檔案371行左右有如下程式碼:

371 static struct platform_device *crag6410_devices[] __initdata = {
    /*[......] */ /* 省略與spi無關程式碼 */
385         &s3c64xx_device_spi0,
    /*[......]*/
397 };

程式碼中定義了一個struct platform_device指標陣列nexcoder_devices[],這個陣列中的struct platform_device指標對應的struct platform_device用來表示CPU上個各種匯流排的控制器。這個陣列中的各個struct platform_device最終都會被註冊到核心中。由上面的程式碼可以看出與spi控制器相關的struct platform_device結構:s3c64xx_device_spi0,表示s3c64xx上的spi0。註冊crag6410_devices[]中的struct platform_device物件到核心中的的程式碼如下:

825 static void __init crag6410_machine_init(void)
826 {
    /* [......] */ /* 省略與spi無關程式碼 */
855         s3c64xx_spi0_set_platdata(NULL, 0, 2);
856 
857         platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices));
858 
    /* [......] */
864 }

s3c64xx_spi0_set_platdata() 設定一些spi控制器相關資訊到s3c64xx_device_spi0中去,包括片選資訊,該spi控制器的時鐘等等,這些資訊會在後面的spi控制器驅動中用到,程式碼如下:

1541 void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
1542                                                 int num_cs)
1543 {
1544         struct s3c64xx_spi_info pd;
1545 
1546         /* Reject invalid configuration */
1547         if (!num_cs || src_clk_nr < 0) {
1548                 pr_err("%s: Invalid SPI configuration\n", __func__);
1549                 return;
1550         }
1551 
1552         pd.num_cs = num_cs;
1553         pd.src_clk_nr = src_clk_nr;
1554         pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
1555 
1556         s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi0);
1557 }

我們回過頭來看看s3c64xx_device_spi0是什麼樣的,它們在檔案arch/arm/plat-samsung/devs.c中的1522行左右:

1522 #ifdef CONFIG_S3C64XX_DEV_SPI0
1523 static struct resource s3c64xx_spi0_resource[] = {
1524         [0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256), //spi暫存器地址資訊
1525         [1] = DEFINE_RES_DMA(DMACH_SPI0_TX),
1526         [2] = DEFINE_RES_DMA(DMACH_SPI0_RX),
1527         [3] = DEFINE_RES_IRQ(IRQ_SPI0),//中斷資訊
1528 };
1529 
1530 struct platform_device s3c64xx_device_spi0 = {
1531         .name           = "s3c6410-spi",
1532         .id             = 0,
1533         .num_resources  = ARRAY_SIZE(s3c64xx_spi0_resource),
1534         .resource       = s3c64xx_spi0_resource,
1535         .dev = {
1536                 .dma_mask               = &samsung_device_dma_mask,
1537                 .coherent_dma_mask      = DMA_BIT_MASK(32),
1538         },
1539 };

程式碼中struct resource物件描述的是與spi控制器硬體相關資訊,包括暫存器起始地址和地址大小,DMA相關資訊,以及中斷號等資訊,這些資訊在spi控制器驅動中會用到。若希望一個spi控制器可以正常的工作在linux spi子系統中,構建並註冊一個描述spi控制器的struct platform_device物件是必須的。只有註冊了這個裝置,才能在spi控制器的平臺裝置驅動註冊時匹配這個裝置,並使用這個struct platform_device中的資訊來夠建和註冊struct spi_master物件到linux spi子系統中去。所有的spi裝置驅動必須要呼叫struct spi_master物件中介面來發送和接收資料。

2.2)device tree描述spi控制器

使用device tree來描述spi控制器相關資訊就會簡單很多,它的格式也比較固定,下面以基於TI的AM33xx的CPU來講解如何使用device tree來描述spi控制器資訊。在arch/arm/boot/dts/am33xxx.dtsi中有關於spi控制器的描述:

369         spi0: [email protected]48030000 {
370             compatible = "ti,omap4-mcspi";  //用來和驅動的of_device_id專案匹配
371             #address-cells = <1>;
372             #size-cells = <0>;
373             reg = <0x48030000 0x400>;    //spi控制器暫存器地址資訊
374             interrupt = <65>;            //spi相關中斷資訊
375             ti,spi-num-cs = <2>;         //片選腳個數
376             ti,hwmods = "spi0";
377             dmas = <&edma 16
378                 &edma 17
379                 &edma 18
380                 &edma 19>;
381             dma-names = "tx0", "rx0", "tx1", "rx1";
382             status = "disabled";       //控制是否解析該節點
383         };
384 
385         spi1: [email protected]481a0000 {
386             compatible = "ti,omap4-mcspi";
387             #address-cells = <1>;
388             #size-cells = <0>;
389             reg = <0x481a0000 0x400>;
390             interrupt = <125>;
391             ti,spi-num-cs = <2>;
392             ti,hwmods = "spi1";
393             dmas = <&edma 42
394                 &edma 43
395                 &edma 44
396                 &edma 45>;                           
397             dma-names = "tx0", "rx0", "tx1", "rx1";
398             status = "disabled";
399 
400         };

其實device tree中描述的spi控制器資訊在device tree解析時也會轉化成一個struct platform_device物件,並註冊的到核心。它和第一種方法本質上是一樣的,不過這種方法不需要在板級程式碼中構建各種struct platform_device物件來描述CPU上各種控制器的資訊,可以提高程式碼複用度。

每個描述spi控制器的device tree節點會被轉化為一個struct platform_device物件。device tree節點中的reginterrupt 屬性也會被轉化為對應的描述控制器暫存器和中斷資訊的struct resource物件,並被該節點轉化的struct platform_device物件中的resource成員引用。

關於如何Device Tree來描述spi控制器的詳細介紹,請參考核心原始碼中Documentation/devicetree/bindings/spi/目錄下的文件。
關於Device Tree的用法請參考核心原始碼中Documentation/devicetree/下的文件。

3、spi控制器平臺裝置驅動

和上一節一樣,本節中也會用兩個驅動,分別對應上一節中每個小節。用s3c64xx CPU的spi控制器驅動來講解在板級程式碼中用struct platform_device 物件來描述spi控制器時,驅動的寫法。用am33xx CPU的spi控制器驅動來講解用device tree描述spi控制器時驅動的寫法。

在開始講解這兩個驅動之前,先來詳細講解spi控制器驅動中會用到的各種資料結構。首先我們來了解一下spi_bus_type:

230 struct bus_type spi_bus_type = {
231         .name           = "spi",
232         .dev_attrs      = spi_dev_attrs,
233         .match          = spi_match_device,
234         .uevent         = spi_uevent,
235         .pm             = &spi_pm,
236 };
237 EXPORT_SYMBOL_GPL(spi_bus_type);

這個資料結構非常重要,邏輯上它代表的是spi匯流排。所有的struct spi_master物件和struct spi_device物件註冊到spi子系統後都會連結到這個資料結構中去。並且struct spi_driver註冊到spi子系統後也會連結到這個資料結構中。其中的match 回撥函式,即spi_match_device 函式中實現了spi裝置和spi裝置驅動匹配的策略。這個函式會在下一篇博文 《三、Linux spi裝置驅動》中具體講解。

下面這個資料結構可以說是spi控制器驅動中的核心,它就是struct spi_master:

273 struct spi_master {
274         struct device   dev;
275 
276         struct list_head list;
277 
278         /* other than negative (== assign one dynamically), bus_num is fully
279          * board-specific.  usually that simplifies to being SOC-specific.
280          * example:  one SOC has three SPI controllers, numbered 0..2,
281          * and one board's schematics might show it using SPI-2.  software
282          * would normally use bus_num=2 for that controller.
283          */
284         s16                     bus_num;
285 
286         /* chipselects will be integral to many controllers; some others
287          * might use board-specific GPIOs.
288          */
289         u16                     num_chipselect;
290 
291         /* some SPI controllers pose alignment requirements on DMAable
292          * buffers; let protocol drivers know about these requirements.
293          */
294         u16                     dma_alignment;
295 
296         /* spi_device.mode flags understood by this controller driver */
297         u16                     mode_bits;
298 
299         /* other constraints relevant to this driver */
300         u16                     flags;
301 #define SPI_MASTER_HALF_DUPLEX  BIT(0)          /* can't do full duplex */
302 #define SPI_MASTER_NO_RX        BIT(1)          /* can't do buffer read */
303 #define SPI_MASTER_NO_TX        BIT(2)          /* can't do buffer write */
304 
305         /* lock and mutex for SPI bus locking */
306         spinlock_t              bus_lock_spinlock;
307         struct mutex            bus_lock_mutex;
308 
309         /* flag indicating that the SPI bus is locked for exclusive use */
310         bool                    bus_lock_flag;
311 
312         /* Setup mode and clock, etc (spi driver may call many times).
313          *
314          * IMPORTANT:  this may be called when transfers to another
315          * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
316          * which could break those transfers.
317          */
318         int                     (*setup)(struct spi_device *spi);
319 
320         /* bidirectional bulk transfers
321          *
322          * + The transfer() method may not sleep; its main role is
323          *   just to add the message to the queue.
324          * + For now there's no remove-from-queue operation, or
325          *   any other request management
326          * + To a given spi_device, message queueing is pure fifo
327          *
328          * + The master's main job is to process its message queue,
329          *   selecting a chip then transferring data
330          * + If there are multiple spi_device children, the i/o queue
331          *   arbitration algorithm is unspecified (round robin, fifo,
332          *   priority, reservations, preemption, etc)
333          *
334          * + Chipselect stays active during the entire message
335          *   (unless modified by spi_transfer.cs_change != 0).
336          * + The message transfers use clock and SPI mode parameters
337          *   previously established by setup() for this device
338          */
339         int                     (*transfer)(struct spi_device *spi,
340                                                 struct spi_message *mesg);
341 
342         /* called on release() to free memory provided by spi_master */
343         void                    (*cleanup)(struct spi_device *spi);
344 
345         /*
346          * These hooks are for drivers that want to use the generic
347          * master transfer queueing mechanism. If these are used, the
348          * transfer() function above must NOT be specified by the driver.
349          * Over time we expect SPI drivers to be phased over to this API.
350          */
351         bool                            queued;
352         struct kthread_worker           kworker;
353         struct task_struct              *kworker_task;
354         struct kthread_work             pump_messages;
355         spinlock_t                      queue_lock;
356         struct list_head                queue;
357         struct spi_message              *cur_msg;
358         bool                            busy;
359         bool                            running;
360         bool                            rt;
361 
362         int (*prepare_transfer_hardware)(struct spi_master *master);
363         int (*transfer_one_message)(struct spi_master *master,
364                                     struct spi_message *mesg);
365         int (*unprepare_transfer_hardware)(struct spi_master *master);
366         /* gpio chip select */
367         int                     *cs_gpios;
368 };

這個資料結構的定義比較長,裡面有很多元素我們是不需要關心的,spi子系統會實現其中一些。我們選擇需要我們實現的專案來講解。

  • s16 bus_num
    需要在驅動中設定。代表該匯流排的編號,這個與硬體相關。

  • u16 num_chipselect
    片選的個數,與spi總線上連線的裝置有關。

  • u16 mode_bits
    spi控制器所支援模式的標誌位。包括相位、極性、片選模式(高電平或低電平)。

  • int (*setup) (struct spi_device *spi)
    這個回撥函式可以選擇實現。這個函式的作用就是在傳送資料給一個spi裝置之前, 根據spi裝置中記錄的spi匯流排的電氣資訊重新配置spi控制器,使spi控制器的速率、工作模式等滿足spi裝置的要求。

  • int (*transfer)(struct spi_device *spi, struct spi_message *mesg)
    在比較老的核心中,這個回撥函式是必須要實現的。在使用spi匯流排進行分段資料傳輸時,需要呼叫這個函式來傳輸資料。這個介面已經不建議使用了,將來可能會從核心中移除,後面介紹替代介面。

  • void (*cleanup) (struct spi_device *spi)
    選擇實現該介面。主要用來釋放一些資源。

  • int (*prepare_transfer_hardware)(struct spi_master *master)
    選擇實現該介面。傳送資料之前使spi控制器做好準備,可能是從低功耗模式轉化到正常工作模式等。

  • int (*unprepare_transfer_hardware)(struct spi_master *master)
    選擇實現該介面。傳送資料後可以使用該介面切換spi控制狀態。

  • int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg)
    如果未實現int (*transfer)(struct spi_device *spi, struct spi_message *mesg)介面,這個介面是必須實現的。transfer_one_messagetranfer介面的區別是:如果驅動實現transfer 介面,還要實現工作佇列來負責排程和控制spi介面傳送資料。而transfer_one_message機制就不需要再實現隊列了。spi子系統中已經實現了一個核心執行緒來負責資料的排程和傳輸。

  • int *cs_gpios
    選擇實現。與片選訊號有關係。

下面介紹倆個與資料傳輸有關係的資料結構: struct spi_messagestruct spi_transfer。一個struct spi_message代表針對一個裝置進行一次分段資料傳輸。而一個struct spi_transfer表示一段資料傳輸。所以一個struct spi_message物件中包含多個struct spi_transfer物件。

首先介紹struct spi_transfer, 一個struct spi_transfer代表一次spi資料傳輸,其中包含了讀和寫資料的緩衝區對,struct spi_transfer結構體定義如下:

495 struct spi_transfer {
496         /* it's ok if tx_buf == rx_buf (right?)
497          * for MicroWire, one buffer must be null
498          * buffers must work with dma_*map_single() calls, unless
499          *   spi_message.is_dma_mapped reports a pre-existing mapping
500          */
501         const void      *tx_buf;
502         void            *rx_buf;
503         unsigned        len;
504 
505         dma_addr_t      tx_dma;
506         dma_addr_t      rx_dma;
507 
508         unsigned        cs_change:1;
509         u8              bits_per_word;
510         u16             delay_usecs;
511         u32             speed_hz;
512 
513         struct list_head transfer_list;
514 };
  • const void *tx_buf
    傳送資料緩衝區,傳送至spi裝置的資料存放在這個緩衝區中。

  • void *rx_buf
    接收資料緩衝區,從spi裝置接收的資料儲存在這個緩衝區中。

  • unsigned len
    緩衝區長度。

  • dma_addr_t tx_dma
    如果使用dma,傳送緩衝區的dma地址。

  • dma_addr_t rx_dma
    如果使用dma,接搜緩衝區的dma地址。

  • unsigned cs_change:1
    本次資料傳輸後,是否改變片選訊號狀態。

  • u8 bits_per_word
    字長資訊,每次寫入spi控制器的資料大小,這與硬體的實現有關係有關係。bits_per_word = 0時,會使用spi_device中的預設值。

  • u16 delay_usecs
    本次資料傳輸結束後,需要延時時間,才能改變片選訊號狀態,或進行下一次資料傳輸,或進行下一個spi_message資料傳輸。

  • u32 speed_hz
    傳輸速率,不一定使用這個值,選擇spi控制器支援的最大速度,spi裝置支援的最大速度和這個值之間的最小值。

  • struct list_head transfer_list
    通過這個連結串列頭,將這個transfer連結到一個spi_message.transfers中。

下面介紹struct spi_message資料結構:

544 struct spi_message {
545         struct list_head        transfers;
546 
547         struct spi_device       *spi;
548 
549         unsigned                is_dma_mapped:1;
550 
551         /* REVISIT:  we might want a flag affecting the behavior of the
552          * last transfer ... allowing things like "read 16 bit length L"
553          * immediately followed by "read L bytes".  Basically imposing
554          * a specific message scheduling algorithm.
555          *
556          * Some controller drivers (message-at-a-time queue processing)
557          * could provide that as their default scheduling algorithm.  But
558          * others (with multi-message pipelines) could need a flag to
559          * tell them about such special cases.
560          */
561 
562         /* completion is reported through a callback */
563         void                    (*complete)(void *context);
564         void                    *context;
565         unsigned                actual_length;
566         int                     status;
567 
568         /* for optional use by whatever driver currently owns the
569          * spi_message ...  between calls to spi_async and then later
570          * complete(), that's the spi_master controller driver.
571          */
572         struct list_head        queue;
573         void                    *state;
574 };
  • struct list_head transfers
    這個不用說了,所有需要傳輸的struct spi_transfer物件都會連結到這個連結串列上。

  • struct spi_device *spi
    資料傳輸至這個spi裝置。

  • unsigned is_dma_mapped:1
    是否是dma傳輸?如果是,呼叫者需要在struct spi_transfer中提供傳送和接收資料的dma地址和虛擬地址。

  • void (*complete)(void *context)
    這個回撥函式用來通知喚醒呼叫程序。

  • void *context
    complete()回撥的引數,通常為一個struct completion物件。

  • unsigned actual_length
    本次message傳輸實際傳輸資料的長度。

  • int status
    本次傳輸的結果,成功status == 0。

  • struct list_head queue
    通過這個連結串列頭將該message連結到等待spi控制器傳輸的message連結串列中。

  • void *state
    擁有這個message的驅動會使用這個成員來獲取一些狀態資訊。

3.1)板級程式碼描述spi控制器的驅動

描述s3c64xx平臺的spi控制器使用的是struct platform_device資料結構,對應的驅動那就是struct platform_driver資料結構。下面來看看描述s3c64xx平臺的spi控制器的struct platform_device對應的struct platform_driver是如何實現的。原始碼kernel/drivers/spi/spi-s3c64xx.c就是s3c64xx平臺spi控制器的驅動程式碼。
先來看看程式碼中的struct platform_driver是如何定義和註冊的。

1556 static struct platform_driver s3c64xx_spi_driver = {
1557         .driver = {
1558                 .name   = "s3c64xx-spi",
1559                 .owner = THIS_MODULE,
1560                 .pm = &s3c64xx_spi_pm,
1561                 .of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
1562         },
1563         .remove = s3c64xx_spi_remove,
1564         .id_table = s3c64xx_spi_driver_ids,
1565 };
1566 MODULE_ALIAS("platform:s3c64xx-spi");
1567 
1568 static int __init s3c64xx_spi_init(void)
1569 {
1570         return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
1571 }
1572 subsys_initcall(s3c64xx_spi_init);
1573 
1574 static void __exit s3c64xx_spi_exit(void)
1575 {
1576         platform_driver_unregister(&s3c64xx_spi_driver);
1577 }
1578 module_exit(s3c64xx_spi_exit);
1579 
1580 MODULE_AUTHOR("Jaswinder Singh <[email protected]>");
1581 MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
1582 MODULE_LICENSE("GPL");

其中static int __init s3c64xx_spi_init(void)static void __exit s3c64xx_spi_exit(void)就不多說了,照寫就行了,基本上是固定格式。

s3c64xx_spi_init(void)中有一個platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe)呼叫,這函式就是將s3c64xx_spi_driver註冊到核心,如何該驅動匹配到一個裝置,就呼叫s3c64xx_spi_probe去探測裝置。
其實上述程式碼有一種更為簡單的寫法,僅供參考,如下:

static struct platform_driver s3c64xx_spi_driver = {
         .driver = {
                 .name   = "s3c64xx-spi",
                 .owner = THIS_MODULE,
                 .pm = &s3c64xx_spi_pm,
                 .of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
         },
         .remove = s3c64xx_spi_remove,

         .id_table = s3c64xx_spi_driver_ids,
 };
 module_platform_driver(&s3c64xx_spi_driver);
 MODULE_ALIAS("platform:s3c64xx-spi");

這種方法可能開起來更簡潔一些。程式碼使用了module_platform_driver這樣一個巨集。

我們可以先忽略s3c64xx_spi_driver中的of_match_table項,這個專案是使用device tree時才會用到的,下面小節會講解。
s3c64xx_spi_driver.driver.name 項,代表的驅動的名字,這個名字可以在/sys/bus/platform/drivers下面看到。如果s3c64xx_spi_driver.driver.of_match_tables3c64xx_spi_driver.id_table都未設定的情況下,s3c64xx_spi_driver.driver.name項會被用來和platform_device.name匹配。
struct platform_driver物件和struct platform_device對像的匹配原則為:最先匹配of_match_table中的專案,其次匹配id_table中的專案,最後是根據platform_driver.driver.nameplatform_device.name去匹配。可能有人會問:為什麼不是platform_driver.driver.nameplatform_device.dev.name去匹配?其實platform_device.dev.name是由platform_device.nameplatform_device.id連線而成,具體是如何連線的,可以參考kernel/drivers/base/platform.c的platform_add_device()。所以直接用platform_driver.driver.nameplatform_device.dev.name來匹配驅動和裝置是不會成功匹配的。具體platform_driverplatform_device匹配的程式碼如下:

721 static int platform_match(struct device *dev, struct device_driver *drv)
722 {
723         struct platform_device *pdev = to_platform_device(dev);
724         struct platform_driver *pdrv = to_platform_driver(drv);
725 
726         /* Attempt an OF style match first */
727         if (of_driver_match_device(dev, drv))
728                 return 1;
729 
730         /* Then try ACPI style match */
731         if (acpi_driver_match_device(dev, drv))
732                 return 1;
733 
734         /* Then try to match against the id table */
735         if (pdrv->id_table)
736                 return platform_match_id(pdrv->id_table, pdev) != NULL;
737 
738         /* fall-back to driver name match */
739         return (strcmp(pdev->name, drv->name) == 0);
740 }

platform_match()中我們可以看到具體的驅動和裝置的匹配規則。

我們回過來看看s3c64xx_spi_probe(struct platform_device *pdev),這個函式是s3c64xx平臺spi控制器驅動的關鍵,程式碼比較長,這個函式是在platform_driverplatform_device匹配後才執行的,具體到我們這個例子,即s3c64xx_spi_drivers3c64xx_device_spi0匹配後才執行的。具體的匹配規則上面已經講述過了。其中函式的引數具體到我們的例子就是一個指向s3c64xx_device_spi0的指標。下面以s3c64xx_device_spi0為例分步講解。

第1步,獲取spi0控制器的暫存器、中斷的相關資訊,這些資訊都是和CPU緊密相關的,不同平臺是不一樣的。

下面程式碼是獲取暫存器的實體地址:

1218         mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

這個mem_res其實就是就是s3c64xx_device_spi0.resource[0],即s3c64xx_spi0_resource[0],這裡面記錄了s3c64xx的spi0控制器的暫存器組實體地址。把這個地址對映到核心的虛擬地址中就可以訪問spi0控制器的暫存器了。

下面一行程式碼獲取中斷資訊,spi0的中斷資訊,就是在2.1節提到的s3c64xx_spi0_resource[3]

1052         irq = platform_get_irq(pdev, 0);

第2步,分配描述spi控制器驅動的私有資料和struct spi_master物件,程式碼如下:

1058         master = spi_alloc_master(&pdev->dev,
1059                                 sizeof(struct s3c64xx_spi_driver_data));

這個呼叫分配了一塊連續記憶體,大小為sizeof(struct spi_master) + sizeof(struct s3c64xx_spi_driver_data)。其中sizeof(struct spi_master)大小給spi_master使用,剩下的記憶體起始地址通過spi_master_set_devdata()呼叫,設定給了spi_master.dev.driver_data

下面看看struct s3c64xx_spi_driver_data,先貼程式碼:

180 struct s3c64xx_spi_driver_data {
181         void __iomem                    *regs;
182         struct clk                      *clk;
183         struct clk                      *src_clk;
184         struct platform_device          *pdev;
185         struct spi_master               *master;
186         struct s3c64xx_spi_info  *cntrlr_info;
187         struct spi_device               *tgl_spi;
188         spinlock_t                      lock;
189         unsigned long                   sfr_start;
190         struct completion               xfer_completion;
191         unsigned                        state;
192         unsigned                        cur_mode, cur_bpw;
193         unsigned                        cur_speed;
194         struct s3c64xx_spi_dma_data     rx_dma;
195         struct s3c64xx_spi_dma_data     tx_dma;
196         struct s3c64xx_spi_port_config  *port_conf;
197         unsigned int                    port_id;
198 };

這個資料結構描述的是s3c64xx平臺spi控制器相關的資訊,其中包括spi控制器暫存器地址,spi控制器時鐘源,dma相關資訊,和spi控制器的配置資訊,還有一些記錄狀態的資料以及各種鎖。這裡面還有一個struct spi_master指標,它會指向與該資料描述的spi控制器對應的spi_master

繼續看程式碼:

1064 
1065         platform_set_drvdata(pdev, master);
1066 
1067         sdd = spi_master_get_devdata(master);
1068         sdd->port_conf = s3c64xx_spi_get_port_config(pdev);
1069         sdd->master = master;
1070         sdd->cntrlr_info = sci;
1071         sdd->pdev = pdev;
1072         sdd->sfr_start = mem_res->start;
1073         if (pdev->dev.of_node) {
1074                 ret = of_alias_get_id(pdev->dev.of_node, "spi");
1075                 if (ret < 0) {
1076                         dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
1077                                 ret);
1078                         goto err0;
1079                 }
1080                 sdd->port_id = ret;
1081         } else {
1082                 sdd->port_id = pdev->id;
1083         }
1084 
1085         sdd->cur_bpw = 8;

這段程式碼比較簡單,設定一些指標的地址,以及spi控制器相關的資料。

第3步,設定struct spi_master結構體的資訊,包括驅動中需要實現的介面:

1268         master->bus_num = sdd->port_id;
1269         master->setup = s3c64xx_spi_setup;
1270         master->cleanup = s3c64xx_spi_cleanup;
1271         master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
1272         master->transfer_one_message = s3c64xx_spi_transfer_one_message;
1273         master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
1274         master->num_chipselect = sci->num_cs;
1275         master->dma_alignment = 8;
1276         /* the spi->mode bits understood by this driver: */
1277         master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

這裡面,s3c64xx_spi_setup()s3c64xx_spi_cleanup()s3c64xx_spi_prepare_transfer()s3c64xx_spi_transfer_one_message()s3c64xx_spi_unprepare_transfer()是必須要實現的介面,這些介面是和spi控制器相關的。s3c64xx_spi_transfer_one_message()就是為了傳輸一個struct spi_message中的資料。這些介面不詳細介紹,有興趣可以直接閱讀程式碼。

第4步,對映暫存器地址到核心虛擬地址空間,申請中斷和配置spi介面:

1279         sdd->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
1280         if (sdd->regs == NULL) {
1281                 dev_err(&pdev->dev, "Unable to remap IO\n");
1282                 ret = -ENXIO;
1283                 goto err1;
1284         }
1285 
1286         if (!sci->cfg_gpio && pdev->dev.of_node) {
1287                 if (s3c64xx_spi_parse_dt_gpio(sdd))
1288                         return -EBUSY;
1289         } else if (sci->cfg_gpio == NULL || sci->cfg_gpio()) {
1290                 dev_err(&pdev->dev, "Unable to config gpio\n");
1291                 ret = -EBUSY;
1292                 goto err2;
1293         }
1294 
1295         /* Setup clocks */
1296         sdd->clk = clk_get(&pdev->dev, "spi");
1297         if (IS_ERR(sdd->clk)) {
1298                 dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
1299                 ret = PTR_ERR(sdd->clk);
1300                 goto err3;
1301         }
1302 
1303         if (clk_prepare_enable(sdd->clk)) {
1304                 dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
1305                 ret = -EBUSY;
1306                 goto err4;
1307         }
1308 
1309         sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
1310         sdd->src_clk = clk_get(&pdev->dev, clk_name);
1311         if (IS_ERR(sdd->src_clk)) {
1312                 dev_err(&pdev->dev,
1313                         "Unable to acquire clock '%s'\n", clk_name);
1314                 ret = PTR_ERR(sdd->src_clk);
1315                 goto err5;
1316         }
1317 
1318         if (clk_prepare_enable(sdd->src_clk)) {
1319                 dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
1320                 ret = -EBUSY;
1321                 goto err6;
1322         }
1323 
1324         /* Setup Deufult Mode */
1325         s3c64xx_spi_hwinit(sdd, sdd->port_id);
1326 
1327         spin_lock_init(&sdd->lock);
1328         init_completion(&sdd->xfer_completion);
1329         INIT_LIST_HEAD(&sdd->queue);
1330 
1331         ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd);
1332         if (ret != 0) {
1333                 dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
1334                         irq, ret);
1335                 goto err7;
1336         }
1337 
1338         writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
1339                S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
1340                sdd->regs + S3C64XX_SPI_INT_EN);

第5步,註冊spi_master到spi子系統中去:

1342         if (spi_register_master(master)) {
1343                 dev_err(&pdev->dev, "cannot register SPI master\n");
1344                 ret = -EBUSY;
1345                 goto err8;
1346         }

這一步完成後,spi控制器的驅動就完成了。

3.2)device tree描述spi控制器的驅動

和不使用device tree的spi控制器驅動基本類似,不過有一些不同。下面通過簡單講解kernel/drivers/spi/spi-omap2-mcspi.c這個am33xx處理器的spi控制器驅動,來看看使用device tree時,spi控制器驅動如何編寫。

同樣,該驅動也是struct platform_device驅動,也就是說實現的是一個struct platform_driver。驅動檔案中首先需要申明struct of_device_id陣列,這個陣列就決定了該struct platform_driver適用於那些struct platform_device。當然我們這個驅動是為了驅動spi控制器的。

先看看struct of_device_id的申明:

1123 static struct omap2_mcspi_platform_config omap2_pdata = {
1124         .regs_offset = 0,
1125 };
1126 
1127 static struct omap2_mcspi_platform_config omap4_pdata = {
1128         .regs_offset = OMAP4_MCSPI_REG_OFFSET,
1129 };
1130 
1131 static const struct of_device_id omap_mcspi_of_match[] = {
1132         {
1133                 .compatible = "ti,omap2-mcspi",
1134                 .data = &omap2_pdata,
1135         },
1136         {
1137                 .compatible = "ti,omap4-mcspi",
1138                 .data = &omap4_pdata,
1139         },
1140         { },
1141 };
1142 MODULE_DEVICE_TABLE(of, omap_mcspi_of_match);
1143 

上述程式碼說明了,該驅動適用於device tree中compatible屬性值為”ti,omap2-mcspi”或compatible屬性值為”ti,omap4-mcspi”的驅動。2.2節中描述spi控制器的device tree節點中,compatible屬性值為”ti,omap4-mcspi”,所以該驅動適用於device tree中描述的spi控制器。

下面程式碼是平臺驅動的固定格式了,貼出程式碼,不再詳述:

1342 static struct platform_driver omap2_mcspi_driver = {
1343         .driver = {
1344                 .name =         "omap2_mcspi",
1345                 .owner =        THIS_MODULE,
1346                 .pm =           &omap2_mcspi_pm_ops,
1347                 .of_match_table = omap_mcspi_of_match,
1348         },
1349         .probe =        omap2_mcspi_probe,
1350         .remove =       omap2_mcspi_remove,
1351 };
1352 
1353 module_platform_driver(omap2_mcspi_driver);

下面看一下omap2_mcspi_probe()介面,這個和上一小節的s3c64xx_spi_probe()很類似,我們簡單看一下。

1144 static int omap2_mcspi_probe(struct platform_devi