標籤:
題外話:面對成功和失敗,一個人有沒有“冠軍之心”,直接影響他的表現。
幾周前剖析了Linux SPI 驅動架構,算是明白個所以然,對於這麼一個龐大的架構,並不是每一行代碼都要自己去敲,因為前人已經把這個架構搭建好了,作為驅動開發人員的我們只需要搞清楚哪一部分是需要自己修改或重新編寫就OK了。
結合Linux核心物件導向的設計思想,SPI總的設計思路大概是這樣的:
第①處:核心中抽象了SPI控制器,讓spi_master成為他的象徵,他的執行個體化對象就是與硬生生的SPI控制器對應的,在Linux核心中習慣將整合到SOC上的控制器用假想的platform匯流排來進行管理,於是乎spi_master的執行個體化就得依靠platform_device和platform_driver來聯手完成了。
細嚼慢咽:這裡的platform_device就相當於是spi_master的靜態描述:包括幾號控制器、寄存器地址、引腳配置等等,把這些資訊以“資源”的形式掛在platform_device上,等platform_driver找到命中註定的那個他之後就可以獲得這個(靜態描述)資源,probe中用這些資源就生出了spi_master執行個體對象。這一系列流程前輩們都已經做好了,我們要做的就是將這靜態描述platform_device修改成和自己SOC中的spi控制器一致的特性即可。即:適當修改arch/arm/mach-s5pv210/dev-spi.c中platform_device涉及的成員。
1 // SPI0的寄存器地址 2 static struct resource s5pv210_spi0_resource[] = { 3 [0] = { 4 .start = S5PV210_PA_SPI0, 5 .end = S5PV210_PA_SPI0 + 0x100 - 1, 6 .flags = IORESOURCE_MEM, 7 }, 8 [1] = { 9 .start = DMACH_SPI0_TX,10 .end = DMACH_SPI0_TX,11 .flags = IORESOURCE_DMA,12 },13 [2] = {14 .start = DMACH_SPI0_RX,15 .end = DMACH_SPI0_RX,16 .flags = IORESOURCE_DMA,17 },18 [3] = {19 .start = IRQ_SPI0,20 .end = IRQ_SPI0,21 .flags = IORESOURCE_IRQ,22 },23 };24 25 /**26 * struct s3c64xx_spi_info - SPI Controller defining structure27 * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field.28 * @src_clk_name: Platform name of the corresponding clock.29 * @clk_from_cmu: If the SPI clock/prescalar control block is present30 * by the platform‘s clock-management-unit and not in SPI controller.31 * @num_cs: Number of CS this controller emulates.32 * @cfg_gpio: Configure pins for this SPI controller.33 * @fifo_lvl_mask: All tx fifo_lvl fields start at offset-634 * @rx_lvl_offset: Depends on tx fifo_lvl field and bus number35 * @high_speed: If the controller supports HIGH_SPEED_EN bit36 * @tx_st_done: Depends on tx fifo_lvl field37 */38 static struct s3c64xx_spi_info s5pv210_spi0_pdata = {39 .cfg_gpio = s5pv210_spi_cfg_gpio, //將GPIO配置成SPI0引腳的函數40 .fifo_lvl_mask = 0x1ff,41 .rx_lvl_offset = 15,42 .high_speed = 1, //看s5pv210的使用手冊P901可知:這是用來配置CH_CFG寄存器的,主要是210用於從裝置時......43 .tx_st_done = 25,44 };45 46 static u64 spi_dmamask = DMA_BIT_MASK(32);47 48 struct platform_device s5pv210_device_spi0 = {49 .name = "s3c64xx-spi",50 .id = 0,51 .num_resources = ARRAY_SIZE(s5pv210_spi0_resource),52 .resource = s5pv210_spi0_resource,53 .dev = {54 .dma_mask = &spi_dmamask,55 .coherent_dma_mask = DMA_BIT_MASK(32),56 .platform_data = &s5pv210_spi0_pdata,//特殊的spi_master資料57 },58 };platform_device
第②處:添加/修改SPI外設“靜態描述”的結構。在arch/arm/mach-s5pv210/mach-smdkv210.c檔案中添加/修改SPI外設資訊。這個結構被spi_register_board_info()函數掛在boardlist上,正常也是前人做好的。核心在註冊了spi_master之後就開始在boardlist中搜尋spi外設,然後向spi匯流排中註冊spi_device,這個結構就是用spi_board_info來初始化的,這些過程也是前人做好的。
1 /* 2 * 以MMC SPI外設為例 3 */ 4 static struct s3c64xx_spi_csinfo smdk_spi0_csi[] = { 5 [SMDK_MMCSPI_CS] = { 6 .line = S5PV210_GPB(1), 7 .set_level = gpio_set_value, 8 .fb_delay = 0x2, 9 },10 };11 static struct s3c64xx_spi_csinfo smdk_spi1_csi[] = {12 [SMDK_MMCSPI_CS] = {13 .line = S5PV210_GPB(5),14 .set_level = gpio_set_value,15 .fb_delay = 0x2,16 },17 };18 //在這個結構中根據具體的外設來修改內容:這個要依靠外設的使用手冊來進行19 static struct spi_board_info s5pv210_spi_devs[] __initdata = {20 {21 .modalias = "spidev", /* MMC SPI ,mod別名*/22 .mode = SPI_MODE_0, /* CPOL=0, CPHA=0 */23 .max_speed_hz = 10000000,24 /* Connected to SPI-0 as 1st Slave */25 .bus_num = 0, //掛接在SPI0匯流排上26 .chip_select = 0,27 .controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],28 },29 {30 .modalias = "spidev", /* MMC SPI */31 .mode = SPI_MODE_0, /* CPOL=0, CPHA=0 */32 .max_speed_hz = 10000000,33 /* Connected to SPI-1 as 1st Slave */34 .bus_num = 1, //掛接在SPI1匯流排上35 .chip_select = 0,36 .controller_data = &smdk_spi1_csi[SMDK_MMCSPI_CS],37 },38 };spi_board_info
第③處:spi_master有了,spi_device也有了,接下來就是SPI驅動編寫的重頭任務了!編寫spi_driver驅動,就是要針對具體的外設編寫具體的操作“策略”。在核心源碼中有參考常式就是drivers/spi/spidev.c,總體的思路和之前學習的字元裝置一樣:構建spi_driver結構體,然後將它註冊到spi匯流排中,構建spi的file_operations結構體然後建立spi字元裝置,spi_driver找到spi_device就在probe中建立字元裝置節點提供給應用程式層。
SPI驅動編寫要點