1. 程式人生 > >嵌入式Linux驅動——SPI子系統解讀(二)

嵌入式Linux驅動——SPI子系統解讀(二)

第一部分,將對SPI子系統整體進行描述,同時給出SPI的相關資料結構,最後描述SPI匯流排的註冊。

第二部分,即本篇文章,該文將對SPI的主控制器(master)驅動進行描述。

第三部分,該文將對SPI裝置驅動,也稱protocol 驅動,進行講解。

第四部分,通過SPI裝置驅動留給使用者層的API,我們將從上到下描述資料是如何通過SPI的protocol 驅動,由bitbang中轉,最後由master驅動將資料傳輸出去。

本文屬於第二部分
4. 主控制器驅動程式

        SPI控制器的配置資訊和其驅動函式是沒有在一起的,關於控制器的配置資訊是在/kernel3.0/arch/arm/mach-exynos目錄下,而關於控制器的驅動程式則是在/kernel3.0/driver/spi目錄下。

4.1 定義 platform device

        1、SPI控制器中關於gpio口的配置資訊(clk、miso、mos),所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c

        其中沒有包含片選口(cs)的配置,因為片選訊號可以直接通過IO口設定也可以通過連線其它晶片來獲取,所以下面的函式在配置SPI控制器IO口的資訊的時候沒有配置片選,片選的配置在後面介紹。

static int exynos_spi_cfg_gpio(struct platform_device *pdev)
{
    int gpio;
    switch(pdev->id){       //通過id來選在屬於哪個spi控制器,itop4412中有3個spi控制
    case 0:
        s3c_gpio_cfgpin(EXYNOS5_CPA2(0),S3C_GPIO_SFN(2)); 
        s3c_gpio_cfgpin(EXYNOS5_CPA2(2),S3C_GPIO_SFN(2));  
        s3c_gpio_cfgpin(EXYNOS5_CPA2(3),S3C_GPIO_SFN(2));
        s3c_gpio_setpull(EXYNOS5_GPA2(0),S3C_GPIO_PULL_UP);
        s3c_gpio_setpull(EXYNOS5_GPA2(2),S3C_GPIO_PULL_UP);
        s3c_gpio_setpull(EXYNOS5_GPA2(3),S3C_GPIO_PULL_UP);

        for(gpio = EXYNOS5_GPA2(0); gpio < EXYNOS5_GPA2(4);gpio++)
        s5p_gpio_set_drvstr(gpio,S5P_GPIO_DRVSTR_LV3);
    break;
    case 1:
    	........
    case 2:
    	........
    default:
    	dev_err(&pdev->dev,"Invalid SPI Controller number!");
    	return -EINVAL;
    }
    return 0;	
}

        2、SPI控制器用到資源的配置,所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c

        SPI控制器會用到一些硬體資源,例如記憶體緩衝區、DMA、中斷等等。

static struct resource exynos_spi0_resource[] = {
    [0] = {
        .start = EXYNOS_PA_SPI0,                                    //資源的暫存器起始地址
        .end   = EXYNOS_PA_SPI0 + 0x100 - 1,                        //資源的暫存器結束地址
        .flags = IORESOURCE_MEM,                                    //資源的型別(記憶體緩衝區資源)        },
    },
    [1] = {
        .start = DMACH_SPI0_TX,                                                
        .end  = DMACH_SPI0_TX,
        .flags = IORESOURCE_DMA,                                    //DMA資源
    },
    [2] = {
        .start = DMACH_SPI0_RX,
        .end  = DMACH_SPI0_RX,
        .flags = IORESOURCE_DMA,                                    //DMA資源
    },
    [3] = {
        .start = IRQ_SPI0,
        .end  = IRQ_SPI0,
        .flags = IORESOURCE_IRQ,                                   //中斷資源
    },      
}; 

        3、SPI控制器的資料資訊,所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c

        在SPI控制器中引用了關於IO口的配置以及支援的從裝置的個數等一些資訊。

static struct s3c64xx_spi_info exynos_spi0_pdata = {
    .cfg_gpio           = exynos_spi_cfg_gpio,                  //spi控制器IO口的配置資訊
    .fifo_lvl_mask      = 0x1ff,                                   
    .rx_lvl_offset      = 15,
    .high_speed         = 1,                                    //支援HIGH_SPEED_EN
    .clk_from_cmu       = true,                                 //clk來自時鐘管理單元,而不是spi控制器
    .tx_st_done         = 25,
 //	.num_cs                                                     //控制器支援的從裝置個數
};  

        4、SPI控制器的定義以及註冊,所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c 
static u64 spi_dmamask = DMA_BIT_MASK(32);
struct platform_device exynos_device_spi0 = {
    .name	= "s3c64xx-spi",               //SPI控制器和驅動匹配的標識
    .id 	= 0;                           //SPI控制器的ID
    .num_resources = ARRAY_SIZE(exynos_spi0_resource),   //SPI控制用到的資源的數目
    .resource  = exynos_spi0_resource,     //SPI控制器用到的硬體資源
    .dev = {
            .dma_mask = &spi_dmamask,
            .coherent_dma_mask = DMA_BIT_MASK(32),
            .platform_data = &exynos_spi0_pdata,   
    },
};     

4.2 定義platform driver

        SPI控制器驅動的定義、註冊以及解除安裝,所在位置/kernel3.0/driver/spi/spi_s3c64xx.c

static struct platform_driver s3c64xx_spi_driver = {
    .driver = {
    	.name = "s3c64xx-spi",                                                   
    	.owner = THIS_MODULE,
    },
    .remove = s3c64xx_spi_remove,
    .suspend = s3c64xx_spi_suspend,
    .resume = s3c64xx_spi_resume,
};
MODULE_ALIAS("platform:s3c64xx-spi")                                      //SPI控制器驅動的定義
static int __init s3c64xx_spi_init(void)
{
    return platform_driver_probe(&s3c64xx_spi_driver,s3c64xx_spi_probe);  
};   
subsys_initcall(s3c64xx_spi_init); //SPI控制器驅動的註冊
static void __exit s3c64xx_spi_exit(void)
{
	platform_driver_unregister(&s3c64xx_spi_driver);
}; 
module_exit(s3c64xx_spi_exit);                                                   //SPI控制器驅動的解除安裝

注意:
一、s3c64xx_spi_init函式通過呼叫plaform_driver_probe函式主要實現兩個功能:
        1、註冊SPI控制器驅動(s3c64xx_spi_driver);
        2、呼叫s3c64xx_spi_probe函式,完成驅動的初始化。
二、plarformI_driver的name要和plarform_device的name要相同。
        上面實現了關於SPI控制器資訊(platform_device)的定義和SPI控制器驅動資訊(platform_driver)的定義以及註冊,下面詳細分析下SPI控制器驅動程式碼。
4.3  分析platform_driver函式
        1、s3c64xx_spi_probe函式,所在位置/kernel3.0/driver/spi/spi_s3c64xx.c
static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{
    struct resource *mem_res,*dmatx_res,*damrx_res;
    struct s3c64xx_spi_driver_data *sdd;
    struct s3c64xx_spi_info *sci;
    struct spi_master *master;
    int ret;
    printk("%s(%d)\n",__FUNCTION__,__LINE__);	    //列印檔案以及函式資訊
    if(pdev->id < 0)                                                                                //platform_device定義的時候是從0開始
    {
    	dev_err(&pdev->dev,"Invalid platform device id-%d\n",pdev->id);
    	return -ENODEV;
    }
    if(pdev->dev.platform_data == NULL)             //檢測定義的platform_device的資訊是否配置
    {
    	dev_err(&pdev->dev,"plaform_data missing\n");
    	return -ENODEV;
    }
    
    sci = pdev->dev.platform_data;
    if(!sci->src_clk_name)                          //src_clk_name的初始化是在s3c64xx_spi_set_info()函式傳參實現的
    {
    	dev_err(dev_err(&pdev->dev,"Board init must call s3c64xx_spi_set_info()\n"));
    	return -EINVAL;
    }
    
    dmatx_res = platform_get_resource(pdev,IORESOURCE_DMA,0);
    if(dmatx_res == NULL)                           //檢測是否有IORESOURCE_DMA資源DMACH_SPI0_TX
    {
    	dev_err(&pdev->dev,"Unable to get SPI-Tx dma resource\n");
    	return -ENXIO;
    }
    
    dmatx_res = platform_get_resource(pdev,IORESOURCE_DMA,0);
    if(dmarx_res == NULL)                           //檢測是否有IORESOURCE_DMA資源DMACH_SPI0_RX
    {
    	dev_err(&pdev->dev,"Unable to get SPI-Rx dma resource\n");
    	return -ENXIO;
    }

    mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0);
    if(mem_res == NULL)                             //檢測是否有IORESOURCE_MEM資源
    {
        dev_err(&pdev->dev,"Unable to get SPI MEM resource\n");
        return -ENOMEM;
    }
    
    master = spi_alloc_master(&pdev->dev,sizeof(struct s3c64xx_spi_driver_data));
    if(master == NULL)  //為s3c64xx_spi_dreiver_data和spi_master申請空間,並進行初始化。
    {
        dev_err(&pdev->dev,"Unable to allocate SPI Master\n");
        return -ENOMEM;
    }
	
    //將master放到pdev->dev->p->driver_data(將資訊放到驅動的私有指標裡面,後面會新增到一個連結串列)
    platform_set_drvdata(pdev,master);
	
    //sdd獲取master->dev->p->driver_data裡面的驅動資訊
    sdd = spi_master_get_devdata(master);
	
    //sdd結構體變數的初始化
    sdd->master         = master;
    sdd->cntrlr_info    = sci;
    sdd->pdev           =pdev;
    sdd->sfr_start      = mem_res->start;
    sdd->tx_dmach       = dmatx_res->start;
    sdd->rx_damch       = dmarx_res->start;
    sdd->cur_bpw        = 8;
    
    //master結構體變數的初始化
    master->bus_num     = pdev->id;                 //一個platform_device(SPI控制器對應一個master)
    master->set_up      = s3c64xx_spi_transfer;     //SPI控制器驅動中的set_up函式賦給master的函式指標set_up
    master->transfer    = s3c64xx_spi_transfer;     //SPI控制器驅動中的transfer函式數賦給master的函式指標transfer
    master->num_chipselect = sci->num_cs;           //將SPI控制器定義的從裝置的個數放入master
    master->dma_alignment = 8;                      //SPI控制器中DMA緩衝區的對齊方式
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;  //SPI控制器的模式
    
    //申請MEM的IO資源
    if(request_mem_region(mem_res->start,resource_size(mem_res),pdev->name)==NULL)
    {
        dev_err(&pdev->dev,"Req mem region failed\n");      
        ret = -ENXIO;
        goto err0;
    }
    
    //建立對映
    sdd->regs = ioremap(mem_res->start,resource_size(mem_res));
    if(sdd->regs == NULL)
    {
        dev_err(&pdev->dev,"Uable to remap IO\n");         
        ret = -ENXIO;
        goto err1;
    }
    
    //檢測GPIO
    if(sci->cfg_gpio == NULL || sci->cfg_gpio(pdev))
    {
        dev_err(&pdev->dev,"Unable to config gpio\n");
        ret = -EBUSY;
        goto err2;
    }
    
    //申請spi時鐘clk
    sdd->clk = clk_get(&pdev->dev,"spi");
    if(IS_ERR(sdd->clk))
    {
        dev_err(&pdev->dev,"Unable to acquire clock 'spi'\n");
        ret = PTR_ERR(sdd->clk);
        goto err3;
    }
    
    //使能spi時鐘clk
    if(clk_enable(sdd->clk))
    {
        dev_err(&pdev->dev,"Couldn't enable clock 'spi'\n");
        ret = -EBUSY;
        goto err4;
    }
    
    //申請平臺時鐘
    sdd->src_clk = clk_get(&pdev->dev,sci->src_clk_name);
    if(IS_ERR(sdd->src_clk))
    {
        dev_err(&pdev->dev,"Unable to acquire clock '%s'\n",sci->src_clk_name);
        ret = PTR_ERR(sdd->src_clk);
        goto err5;
    }
    
    //使能平臺時鐘
    if(clk_enable(sdd->src_clk))
    {
        dev_err(&pdev->dev,"Couldn't enable clock '%s'\n",sci->src_clk_name);
        ret -EBUSY;
        goto err6;
    }
    
    //建立工作佇列
    sdd->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent));
    if(sdd->workqueue == NULL)
    {
        dev_err(&pdev->dev,"Unable to create workqueue\n");
        ret = -ENOMEM;
        goto err7;
    }
    
    printk("%s(%d)\n",__FUNCTION__,__LINE__);
    
    //對應SPI控制器硬體初始化
    s3c64xx_spi_hwinit(sdd,pdev->id);
    
    spin_lock_init(&sdd->lock);
    init_completion(&sdd->xfer_completion);
    INIT_WORK(&sdd->work,s3c64xx_spi_work);
    INIT_LIST_HEAD(&sdd->queue);                //初始化連結串列頭,前驅後繼指向自己
    
    //master資訊的檢測以及註冊裝置的裝置樹(of_register_spi_devices(master))
    if(spi_register_master(master))
    {
        dev_err(&pdev->dev,"cannot register SPI master\n");
        ret = -EBUSY;
        goto err8;
    }
    
    dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d ""with %d Slaves attached\n",
    				pdev->id, master->num_chipselect);
    dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",mem_res->end, mem_res->start,
    				sdd->rx_dmach, sdd->tx_dmach);
    printk("%s(%d)\n", __FUNCTION__, __LINE__);
    return 0;

err8:          //銷燬工作佇列
    destroy_workqueue(sdd->workqueue);
err7:          //關閉平臺時鐘
    clk_disable(sdd->src_clk);
err6:          //回退平臺時鐘
    clk_put(sdd->src_clk);
err5:          //關閉spi時鐘
    clk_disable(sdd->clk);
err4:          //回退spi時鐘
    clk_put(sdd->clk);
err3:
err2:          //取消IO對映
    iounmap((void *) sdd->regs);
err1:          //釋放MEM記憶體緩衝區
    release_mem_region(mem_res->start, resource_size(mem_res));
err0:          //將pdev裡面dri_data置空
    platform_set_drvdata(pdev, NULL);
    spi_master_put(master);  //回收master

    return ret;     
};

        2、spi_alloc_master函式,所在位置/kernel3.0/driver/spi/spi.c
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
    struct spi_master	*master;

    if (!dev)
    	return NULL;

    master = kzalloc(size + sizeof *master, GFP_KERNEL);
    if (!master)
    	return NULL;    

    device_initialize(&master->dev);
    master->dev.class = &spi_master_class;
    master->dev.parent = get_device(dev);
    spi_master_set_devdata(master, &master[1]);     //將s3c64xx_spi_driver_data的首地址
                                                    //賦給master->dev->p->driver_data
    return master;  
}    
EXPORT_SYMBOL_GPL(spi_alloc_master);

該函式首先為spi_master結構體以及s3c64xx_spi_driver_data結構體分配了空間,同時將spi_master.dev.driver_data指向了s3c64xx_spi_driver_data。
        3、s3c64xx_spi_driver_data結構體
struct s3c64xx_spi_driver_data
{
    void __iomem            *regs;            //指向IO重對映後的控制器暫存器地址的指標 
    struct clk              *clk;             //SPI時鐘
    struct clk              *src_clk;         //平臺時鐘
    struct platform_device  *pdev;            //平臺裝置
    struct spi_master       *master;          //SPI控制器
    struct workqueue_struct *workqueue;       //SPI請求的工作佇列
    struct s3c64xx_spi_info *cntrlr_info;     //特定平臺的SPI控制器的資訊
    struct spi_device       *tgl_spi;         //指向最後一次片選選定的spi從裝置
    struct work_struct      work;             //
    struct list_head        queue;            //工作佇列用來存放SPI請求
    spinlock_t              lock;             //控制器特定的鎖
    enum dma_ch             rx_dmach;         //控制器的Rx的DMA通道
    enum dma_ch             tx_dmach;         //控制器的Tx的DMA通道
    unsigned long           sfr_start;        //SPI控制器匯流排的地址
    struct completion       xfer_completion;  //完成轉送的任務
    unsigned                state;            //顯示狀態的標誌
    unsigned                cur_mode,cur_bpw; //儲存控制器的活動配置 儲存每個字的活動位
    unsigned                cur_speed;        //儲存轉送的時鐘速率
}

該結構體包含了SPI驅動模組的所有資訊,包含SPI模組用到的時鐘、平臺裝置的資訊、SPI控制器master、SPI新增的工作佇列、SPI控制器的配置資訊、新增的SPI從裝置資訊等。
        4、platform_set_drvdata函式
static inline void platform_set_drvdata(struct platform_device *pdev,void *data)
{
    dev_set_drvdata(&pdev->dev,data);
}

int dev_set_drvdata(struct device *dev,void *data)
{
    int error;
    
    if(!dev->p)
    {
        error = device_private_init(dev);
        if(error)
            reuturn error;
    }
    dev->p->driver_data = data;
    return 0;
}
EXPORT_SYMBOL(dev_set_drvdata);

int device_private_init(struct device *dev)
{
    dev->p = kzalloc(sizeof(*dev->p),GFP_KERNEL);
    if(!dev->p)
        return -ENOMEM;
    dev->p->device = dev;
    klist_init(&dev->p->klist_children,klist_children_get,klist_children_put);
    return 0;
}

void klist_init(struct klist *k,void(*get)(struct klist_node*),void(*put)(struct klist_node*))
{
    INIT_LIST_HEAD(&k->k_list);
    spin_lock_init(&k->k_lock);
    k->get = get;
    k->put = put;
}
EXPORT_SYMBOL_GPL(klist_init);

static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

該函式實現將pdev->dev->p->driver_data = master。
        5、spi_master_get_devdata函式
static inline void *spi_master_get_devdata(struct spi_master *master)
{
    return dev_get_drvdata(&master->dev);
}

void *dev_get_drvdata(const struct device *dev)
{
    if(dev && dev->p)
        return dev->p->driver_data;
    return NULL;
}
EXPORT_SYMBOL(dev_get_drvdata);

該函式實現將sdd = master->dev->p->driver_data。
        6、s3c64xx_spi_hwinit函式
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd,int channel)
{
    struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
    void __iomem *regs = sdd->regs;
    unsigned int val;
   
    sdd->cur_speed = 0;
    S3C64XX_SPI_DEACT(sdd);
   
    //禁用中斷,如果不是DMA模式,使用輪詢
    writel(0,reg + S3C64XX_SPI_INT_EN);
    
    if(!sci->clk_from_cmu)
        writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,regs + S3C64XX_SPI_CLK_CFG);
    writel(0,regs + S3C64XX_SPI_MODE_CFG);
    writel(0,regs + S3C64XX_SPI_PACKET_CNT);
    
    //清除任何中斷的暫掛位
    writel(readl(regs + S3C64XX_SPI_PENDING_CLR),regs + S3C64XX_SPI_PENDING_CLR);
    writel(0,regs + S3C64XX_SPI_SWAP_CFG);
    
    //SPI模式的設定
    val = readl(regs + S3C64XX_SPI_MODE_CFG);
    val &= ~S3C64XX_SPI_MODE_4BURST;
    val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
    val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
    writel(val,regs + S3C64XX_SPI_MODE_CFG);
    
    flush_fifo(sdd);
}    

該函式進行硬體的初始化
        7、flush_fifo函式
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
{
    struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
    void __iomem *regs = sdd->regs;
    unsigned long loops;
    u32 val;
    
    writel(0,regs + S3C64XX_SPI_PACKET_CNT);
    
    val = readl(regs + S3C64XX_SPI_CH_CFG);
    val |= S3C64XX_SPI_CH_SW_RST;
    val &= ~S3C64XX_SPI_CH_HS_EN;
    writel(val,regs + S3C64XX_SPI_CH_CFG);
    
    //重新整理Tx fifo
    loops = msecs_to_loops(1);
    do{
        val = readl(regs + S3C64XX_SPI_STATUS);
    }while(TX_FIFO_LVL(val,sci) && loops--);
    if(loops == 0)
        dev_warn(&sdd->pdev->dev,"Timed out flushing TX FIFO\n");
        
    //重新整理Rx fifo
    loops = msecs_to_loops(1);
    do{
        val = readl(regs + S3C64XX_SPI_STATUS);
        if(RX_FIFO_LVL(val,sci))
            readl(regs + S3C64XX_SPI_RX_DATA);
        else
            break;
    }while(loops--);
    if(loops = 0)
        dev_warn(&sdd->pdev->dev,"Timed out flushing RX FIFO\n");
       
    val = readl(regs + S3C64XX_SPI_CH_CFG);
    val &= ~S3C64XX_SPI_CH_SW_RST;
    writel(val,regs + S3C64XX_SPI_CH_CFG);
    
    val = readl(regs + S3C64XX_SPI_MODE_CFG);
    val &= ~(S3C64XX_SPI_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON);
    writel(val,regs + S3C64XX_SPI_CH_CFG);
}

該函式將FIFO的RX、TX重新整理
        8、init_completion函式
static inline void init_completion(struct completion *x)
{
    x->done = 0;
    init_waitqueue_head(&x->wait);
}

#define init_waitqueue_head(q) \

do{
    static struct lock_class_key __key;
    __init_waitqueue_head((q),&__key);
}while(0);

void __init_waitqueue_head(wait_queue_head_t *q,struct lock_class_key *key)
{
    spin_lock_init(&q->lock);
    lockdep_set_class(&q->lock,key);
    INIT_LIST_HEAD(&q->task_list);
}

static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

        9、spi_register_master函式
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;
    struct boardinfo *bi;
    int status = -ENODEV;
    int dynamic = 0;
    
    if(!dev)
        return -ENODEV;
    
    //如果master設定的片選從裝置個數為0則報錯   
    if(master->num_chipselect == 0)
        return -EINVAL;
    
    //SPI控制器ID不能小於0
    if(master->bus_num < 0)
    {
        master->bus_num = atomic_dec_return(&dyn_bus_id);
        dynamic = 1;
    }
    
    spin_lock_init(&master->bus_lock_spinlock);
    mutex_init(&master->bus_lock_mutex);
    master->bus_lock_flag = 0;
    
    //為SPI控制器設定名字,eg:spi0、spi1
    dev_set_name(&master->dev,"spi%u",master->bus_num);
    //將控制器新增到核心
    status = device_add(&master->dev);
    if(status < 0)
        goto done;
    dev_dbg(dev,"registered master %s%s\n",dev_name(&master->dev),dynamic?"(dynamic)":"");
    
    mutex_lock(&board_lock);
    //將master->list新增到spi_master_list列表
    list_add_tail(&master->list,&spi_master_list);
    //迴圈遍歷spi裝置配置結構體,然後與spi控制的匯流排號匹配,成功則生成新spi裝置
    list_for_each_entry(bi,&board_list,list) 
        spi_match_master_to_boardinfo(master,&bi->board_info);
    mutex_unlock(&board_lock);
    
    status = 0;
    //將master控制器下對應的spi裝置新增到裝置樹
    of_register_spi_devices(master);
done:
    return status;  
}

該函式生成了控制器master
int device_add(struct device *dev)
{
    struct device *parent = NULL;
    struct class_interface *class_intf;
    int error = -EINVAL;
    
    dev = get_device(dev);
    if(!dev)
        goto done;
    
    //如果dev->p為空,則將dev->p->device = dev
    if(!dev->p)
    {
        error = device_private_init(dev);
        if(error)
            goto done;
    }
    
    //初始化名字
    if(dev->init_name)
    {
        dev_set_name(dev,"%s",dev->init_name);
        dev->init_name = NULL;
    }
    
    if(!dev_name(dev))
    {
        error = -EINVAL;
        goto name_error;
    }
    
    pr_debug("device:'%s':'%s\n'",dev_name(dev),__func__);
    
    parent = get_device(dev->parent);
    setup_parent(dev,parent);
    
    if(parent)
        set_dev_node(dev,dev_to_node(parent));
        
    error = kobject_add(&dev->kobj,dev->kobj.parent,NULL);
    if(error)
        goto Error;
    
    if(platform_notify)
        platform_notify(dev);
        
    error = device_create_file(dev,&uevent_attr);
    if(error)
        goto attrError;
        
    if(MAJOR(dev->devt))
    {
        error = device_create_file(dev,&devt_attr);
        if(error)
            goto ueventattrError;
            
        error = device_create_sys_dev_entry(dev);
        if(error)
            goto devtattrError;
            
        devtmpfs_create_node(dev);
    }
    
    error = device_add_class_symlinks(dev);
    if(error)
        goto SymlinkError;
        
    error = device_add_attrs(dev);
    if(error)
        goto AttrsError;
    
    error = bus_add_device(dev);
    if(error)
        goto BusError;
    
    error = dpm_sysfs_add(dev);
    if(error)
        goto DPMError;
    device_pm_add(dev);
    
    if(dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
            BUS_NOTIFY_ADD_DEVICE,dev);
    
    kobject_uevent(&dev->kobj,KOBJ_ADD);
    bus_probe_device(dev); 
    if(parent)
        klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children);
        
    if(dev->class)
    {
        mutex_lock(&dev->class->p->class_mutex);
        klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);
        list_for_each_entry(class_intf,&dev->class->p->class_interfaces,node)
            if(class_intf->add_dev)
                class_intf->add_dev(dev,class_intf);
        mutex_unlock(&dev->class->p->class_mutex);
    }
done:
    put_device(dev);
    return error;
DPMError:
    bus_remove_device(dev);
BusError:
    device_remove_attrs(dev);
AttrsError:
    device_remove_class_symlinks(dev);
SymlinkError:
    if(MAJOR(dev->devt))
        devtmpfs_delete_node(dev);
    if(MAJOR(dev->devt))
        device_remove_sys_dev_entry(dev);
devtattrError:
    if(MAJOR(dev->devt))
        device_remove_file(dev,&devt_attr);
ueventattrError:
    device_remove_file(dev,&uevent_attr);
attrError:
    kobject_uevent(&dev->kobj,KOBJ_REMOVE);
    kobject_del(&dev->kobj);
Error:
    cleanup_device_parent(dev);
    if(parent)
        put_device(parent);
name_error:
    kfree(dev->p);
    dev->p = NULL;
    goto done;
}  

static void spi_match_master_to_boardinfo(struct spi_master *master,
                                            struct spi_board_info *bi)
{
    struct spi_device *dev;
    
    //master控制器上匯流排號和板級資訊匯流排號匹配
    if(master->bus_num != bi->bus_num)
        return;
    //生成spidev
    dev = spi_new_device(master,bi);
    if(!dev)
        dev_err(master->dev.parent,"can't create new device for %s\n",bi->modalias);
    
}


struct spi_device *spi_new_device(struct spi_master *master,
                                    struct spi_board_info *chip)
{
    struct spi_device *proxy;
    int status;
    
    //通過傳入的master來生成一個spidev
    proxy = spi_alloc_device(master);
    if(!proxy)
        return NULL;
        
    WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
    //根據板級資訊配置新spidev
    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;
}
EXPORT_SYMBOL_GPL(spi_new_device);

int spi_add_device(struct spi_device *spi)
{
    static DEFINE_MUTEX(spi_add_lock);
    struct device *dev = spi->master->dev.parent;
    struct device *d;
    int status;
    
    //從裝置的片選號不能大於控制器設定的最大片選數量
    if(spi->chip_select >= spi->master->num_chipselect)
    {
        dev_err(dev,"cs%d >= max %d\n",spi->chip_select,spi->master->num_chipselect);
        return -EINVAL;
    }
                    
    //從裝置設定名字
    dev_set_name(&spi->dev, "%s.%u",dev_name(&spi->master->dev),spi->chip_select);
    
    mutex_lock(&spi_add_lock);
    //從spi總線上查詢該名字是否被設定,也就是該片選號是否被用
    d = bus_find_device_by_name(&spi_bus_type,NULL,dev_name(&spi->dev));
    if(d != NULL)
    {
        dev_err(dev,"chipselect %d already in use\n",spi->chip_select);
        put_device(d);
        status = -EBUSY;
        goto done;
    }
    //spidev進行一些模式設定
    status = spi_setup(spi);
    if(status < 0)
    {
        dev_err(dev,"can't setup %s,status %d\n",dev_name(&spi->dev),status);
        goto done;
    }
    //spidev裝置繫結驅動後新增進核心,生成各種檔案
    status = device_add(&spi->dev);
    if(status < 0)
        dev_err(dev,"can't add %s,status %d\n",dev_name(&spi->dev),status);
    else
        dev_dbg(dev,"registered child %s\n",dev_name(&spi->dev));

done:
    mutex_unlock(&spi_add_lock);
    return status;
}
EXPORT_SYMBOL_GPL(spi_add_device);

至此,master 驅動的大體結構都已分析完畢,隨後第三篇文章將介紹spi裝置驅動。