Linux spi驅動分析(一)----匯流排驅動
一、SPI匯流排驅動介紹
SPI匯流排總共需要四根線,包括MOSI、MISO、CLK和CS。本文首先從SPI設備註冊開始來講述SPI匯流排驅動。
二、設備註冊
在系統啟動的時候,會按照順序執行一些初始化程式,比如device_initcall和module_init等巨集。這些巨集是按照順序執行的,
比如device_initcall的優先順序高於module_init,現在我們看下在系統啟動的時候註冊的spi裝置資訊。
程式如下:
點選(此處)摺疊或開啟
- /* SPI controller
*/
- #if defined(CONFIG_GSC3280_SPI)
- #ifdef CONFIG_SPI1
- static struct resource spi1_resources[]
= {
- [0]
= {
- .start = GSC3280_SPI1_BASEADDR
& 0x1fffffff,
- .end =
(GSC3280_SPI1_BASEADDR
& 0x1fffffff)+ 0x54
- 1 ,
- .flags = IORESOURCE_MEM,
- },
- [1]
= {
- .start = EXT_GSC3280_SPI1_IRQ,
- .end = EXT_GSC3280_SPI1_IRQ,
- .flags = IORESOURCE_IRQ,
- },
- };
- static struct platform_device gsc3280_spi1_device
= {
- .name =
"gsc3280-spi",
- .id =
- #ifdef CONFIG_GSC3280_SPI_DMA
- .dev =
{
- .dma_mask =
NULL,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data =
NULL,
- },
- #endif
- .resource = spi1_resources,
- .num_resources = ARRAY_SIZE(spi1_resources),
- };
- #endif
- /* SPI devices
*/
- #if defined(CONFIG_SPI_FLASH_W25Q)
- static struct gsc3280_spi_info w25q_spi1_dev_platdata
= {
- .pin_cs = 87,
- .num_cs = 1,
- .cs_value = 0,
- .lsb_flg = 0,
- .bits_per_word = 8,
- };
- #endif
- static struct spi_board_info gsc3280_spi_devices[]
= {
- #if defined(CONFIG_SPI_FLASH_W25Q)
- {
- .modalias =
"spi-w25q",
- .bus_num = 1,
- .chip_select = 3,
- .mode = SPI_MODE_3,
- .max_speed_hz = 5
* 1000 * 1000,
- .controller_data =
&w25q_spi1_dev_platdata,
- },
- #endif
- };
- static int __init gsc3280_spi_devices_init(void)
- {
- spi_register_board_info(gsc3280_spi_devices, ARRAY_SIZE(gsc3280_spi_devices));
- return 0;
- }
- device_initcall(gsc3280_spi_devices_init);
- #endif //end #if defined(CONFIG_GSC3280_SPI)
點選(此處)摺疊或開啟
- int __init
- spi_register_board_info(struct spi_board_info const
*info, unsigned n)
- {
- struct boardinfo
*bi;
- int i;
- bi = kzalloc(n
* sizeof(*bi), GFP_KERNEL);
- if (!bi)
- return -ENOMEM;
- for (i
= 0; i
< n; i++, bi++,
info++) {
- struct spi_master
*master;
- memcpy(&bi->board_info, info,
sizeof(*info));
- mutex_lock(&board_lock);
- list_add_tail(&bi->list, &board_list);
- list_for_each_entry(master, &spi_master_list, list)
- spi_match_master_to_boardinfo(master, &bi->board_info);
- mutex_unlock(&board_lock);
- }
- return 0;
- }
對於此處,n為1,在程式中首先建立相應的記憶體,在for迴圈中,將資訊儲存到記憶體中,然後插入board_list連結串列,接著遍歷
spi_master_list連結串列,注意此處,由於device_initcall的優先順序高於module_init,所以此時spi_master_list連結串列為空,那麼還
不能呼叫spi_match_master_to_boardinfo函式建立spi裝置,具體的建立裝置將在spi匯流排驅動的探測函式中,使用spi_register_master()
函式建立裝置。
三、匯流排驅動探測、退出和電源管理函式
3.1、探測函式gsc3280_spi_probe
程式如下:
點選(此處)摺疊或開啟
- static int __init gsc3280_spi_probe(struct platform_device
*pdev)
- {
- int ret = 0;
- struct gsc3280_spi *gscs;
- struct spi_master *master;
- struct resource *mem,
*ioarea;
- DBG("############\n");
- DBG("gsc3280 spi probe start\n");
- master = spi_alloc_master(&pdev->dev, sizeof(struct
gsc3280_spi));
- if (!master)
{
- ret =
-ENOMEM;
- DBG("!!!!spi_alloc_master error\n");
- goto exit;
- }
- gscs = spi_master_get_devdata(master);
- memset(gscs, 0, sizeof(struct gsc3280_spi));
- gscs->master
= spi_master_get(master);
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem)
{
- DBG("!!!!no mem resource!\n");
- ret =
-EINVAL;
- goto err_kfree;
- }
- ioarea = request_mem_region(mem->start, resource_size(mem),
pdev->name);
- if (!ioarea)
{
- DBG("!!!!SPI region already claimed!\n");
- ret =
-EBUSY;
- goto err_kfree;
- }
- gscs->regs
= ioremap_nocache(mem->start, resource_size(mem));
- if (!gscs->regs)
{
- DBG("!!!!SPI ioremap error!\n");
- ret =
-ENOMEM;
- goto err_release_reg;
- }
- DBG("gscs->regs = 0x%p\n", gscs->regs);
- gscs->irq
= platform_get_irq(pdev, 0);
- if (gscs->irq
< 0)
{
- DBG("!!!!no irq resource!\n");
- ret = gscs->irq;
- goto err_unmap;
- }
- ret = request_irq(gscs->irq, gsc3280_spi_irq,
IRQF_DISABLED, dev_name(&pdev->dev),
gscs);
- if (ret
< 0)
{
- DBG("!!!!can not get IRQ!\n");
- goto err_irq;
- }
- gscs->clk
= clk_get(NULL,
"spi1");
- if (IS_ERR(gscs->clk))
{
- DBG("!!!!failed to find spi1 clock source!\n");
- ret = PTR_ERR(gscs->clk);
- goto err_irq;
- }
- gscs->max_freq
= clk_get_rate(gscs->clk);
- DBG("rate is %d\n", gscs->max_freq);
- clk_enable(gscs->clk);
- gscs->bus_num
= pdev->id;
- gscs->num_cs
= 4;
- gscs->prev_chip
= NULL;
- INIT_LIST_HEAD(&gscs->queue);
- spin_lock_init(&gscs->slock);
- #ifdef CONFIG_GSC3280_SPI_DMA
- gscs->dma_priv
= pdev->dev.platform_data
= &spi_platform_data;
- if (!gscs->dma_priv)
- goto err_clk; //return
-ENOMEM;
- gscs->dma_ops
= &gscs_dma_ops;
- gscs->dma_inited
= 0;
- gscs->dma_addr
= (dma_addr_t)(gscs->regs
+ 0x24)
& 0x1fffffff;
- #endif
- platform_set_drvdata(pdev, master);
- master->mode_bits
= SPI_CPOL | SPI_CPHA;
- master->bus_num
= gscs->bus_num;
- master->num_chipselect
= gscs->num_cs;
- master->cleanup
= gsc3280_spi_cleanup;
- master->setup
= gsc3280_spi_setup;
- master->transfer
= gsc3280_spi_transfer;
- gsc3280_spi_hw_init(gscs);
- #ifdef CONFIG_SPI_GSC3280_DMA
- if (gscs->dma_ops
&& gscs->dma_ops->dma_init)
{
- ret = gscs->dma_ops->dma_init(gscs);
- if (ret)
{
- dev_warn(&master->dev,
"DMA init failed\n");
- gscs->dma_inited
= 0;
- }
- }
- #endif
- ret = gsc3280_init_queue(gscs);
- if (ret
!= 0)
{
- DBG("!!!!problem initializing queue!\n");
- goto err_diable_hw;
- }
- ret = gsc3280_start_queue(gscs);
- if (ret
!= 0)
{
- DBG("!!!!problem starting queue!\n");
- goto err_queue_alloc;
- }
- ret = spi_register_master(master);
- if (ret
!= 0)
{
- DBG("!!!!register spi master error!\n");
- goto err_queue_alloc;
- }
- DBG("gsc3280 spi probe success\n");
- DBG("############\n");
- return 0;
- //err_free_master:
- //spi_master_put(master);
- err_queue_alloc:
- gsc3280_spi_destroy_queue(gscs);
- #ifdef CONFIG_SPI_GSC3280_DMA
- if (gscs->dma_ops
&& gscs->dma_ops->dma_exit)
- gscs->dma_ops->dma_exit(gscs);
- #endif
- err_diable_hw:
- gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
- //err_clk:
- clk_disable(gscs->clk);
- clk_put(gscs->clk);
- err_irq:
- free_irq(gscs->irq, gscs);
- err_unmap:
- iounmap(gscs->regs);
- err_release_reg:
- release_mem_region(mem->start, resource_size(mem));
- err_kfree:
- kfree(gscs);
- kfree(master);
- exit:
- printk(KERN_ERR
"!!!!!!gsc3280 probe error!!!!!!\n");
- return ret;
- }
1) 首先是匯流排資源的註冊,包括申請IO空間和中斷。
2) 接下來註冊了中斷函式。
3) 然後註冊了spi_master所需要的函式,包括清除、設定和傳輸等函式,在四中會講述。
4) gsc3280_spi_hw_init函式初始化了SPI匯流排暫存器,接下來講述。
5) 匯流排驅動採用queue機制實現多裝置SPI讀寫,接下來初始化和啟動了queue,接下來講述。
6) 使用spi_register_master函式註冊master,此函式即實現建立了SPI裝置結構體,接下來講述。
SPI匯流排暫存器初始化函式gsc3280_spi_hw_init:
點選(此處)摺疊或開啟
- /* Restart the controller, disable all interrupts, clean fifo
*/
- static void gsc3280_spi_hw_init(struct gsc3280_spi
*gscs)
- {
- gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
- gsc3280_spi_mask_intr(gscs, GSC_SPI_SR_MASK);
- if (!gscs->fifo_len)
{
- gscs->fifo_len
= 0x10;
- __raw_writew(0x00, gscs->regs
+ GSC_SPI_TXFTLR);
- __raw_writew(0x00, gscs->regs
+ GSC_SPI_RXFTLR);
- }
- gsc3280_enable_spi(gscs, GSC_SPI_ENABLE);
- }
由程式可以看出,此函式首先禁止SPI,遮蔽中斷,然後設定fifo深度,最後使能SPI。
初始化queue函式gsc3280_init_queue:
點選(此處)摺疊或開啟
- static int __devinit gsc3280_init_queue(struct gsc3280_spi
*gscs)
- {
- gscs->queue_state
= GSC_SPI_QUEUE_STOP;
- gscs->busy
= 0;
- tasklet_init(&gscs->pump_transfers, gsc3280_spi_pump_transfers,
(unsigned long)gscs);
- INIT_WORK(&gscs->pump_messages, gsc3280_spi_pump_messages);
- gscs->workqueue
= create_singlethread_workqueue(dev_name(gscs->master->dev.parent));
- if (gscs->workqueue
==
NULL) {
- DBG("!!!!create_singlethread_workqueue error!\n");
- return -EBUSY;
- }
- else
- return 0;
- }
開始queue函式gsc3280_start_queue:
點選(此處)摺疊或開啟
- static int gsc3280_start_queue(struct gsc3280_spi
*gscs)
- {
- unsigned long flags;
- spin_lock_irqsave(&gscs->lock, flags);
- if ((gscs->run
== GSC_SPI_QUEUE_RUN)
|| gscs->busy)
{
- spin_unlock_irqrestore(&gscs->lock, flags);
- return -EBUSY;
- }
- gscs->run
= GSC_SPI_QUEUE_RUN;
- gscs->cur_msg
= NULL;
- gscs->cur_transfer
= NULL;
- gscs->cur_chip
= NULL;
- gscs->prev_chip
= NULL;
- spin_unlock_irqrestore(&gscs->lock, flags);
- queue_work(gscs->workqueue,
&gscs->pump_messages);
- return 0;
- }
此函式首先對queue的狀態進行判斷,然後初始化相關成員變數,最後排程queue。
最後看下master註冊函式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;
- /* even
if it's just one always-selected device, there must
- * be at least one chipselect
- */
- if (master->num_chipselect
== 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;
- }
- spin_lock_init(&master->bus_lock_spinlock);
- mutex_init(&master->bus_lock_mutex);
- master->bus_lock_flag
= 0;
- /* 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);
- if (status
< 0)
- goto done;
- dev_dbg(dev,
"registered master %s%s\n", dev_name(&master->dev),
- dynamic ?
" (dynamic)" :
"");
- mutex_lock(&board_lock);
- list_add_tail(&master->list,
&spi_master_list);
- list_for_each_entry(bi,
&board_list, list)
- spi_match_master_to_boardinfo(master,
&bi->board_info);
- mutex_unlock(&board_lock);
- status = 0;
- /* Register devices from the device tree
*/
- of_register_spi_devices(master);
- done:
- return status;
- }
- EXPORT_SYMBOL_GPL(spi_register_master);
說明:
1) 首先對master成員變數進行檢查。
2) 初始化成員變數。
3) 將master->list插入到spi_master_list連結串列中。
4) 語句list_for_each_entry(bi, &board_list, list)實現遍歷board_list連結串列,在二設備註冊中已經講述了將裝置插入到
board_list連結串列中。此時的board_list連結串列不為空,已經有相應裝置結構體資訊了。
5) 語句spi_match_master_to_boardinfo(master, &bi->board_info);實現裝置的建立,函式程式如下:
點選(此處)摺疊或開啟
- static void spi_match_master_to_boardinfo(struct spi_master
*master,
- struct spi_board_info *bi)
- {
- struct spi_device *dev;
- if (master->bus_num
!= bi->bus_num)
- return;
- dev = spi_new_device(master, bi);
- if (!dev)
- dev_err(master->dev.parent,
"can't create new device for %s\n",
- bi-