Linux I2C驅動完全分析(二)

來源:互聯網
上載者:User

博主按:大熱的天,剛剛負重從五道口走到石板房,大約4公裡吧。終於讓我找了一個咖啡屋休息一下,繼續寫這篇驅動分析。單身的生活就是這樣無聊啊。 不發牢騷了,活出個樣兒來給自己看!千難萬險腳下踩,啥也難不倒咱!繼續整!~

 

先說一下,本文中有個疑惑,一直沒有搞懂,寫在這裡,望高人指點一二,不勝感激!

#define I2C_M_NOSTART  0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK  0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */

這裡I2C_FUNC_PROTOCOL_MANGLING 是什麼意思?為什麼定義這些東東?看了注釋也不太理解。求解釋!

 

3. I2C匯流排驅動程式碼分析

   s3c2440的匯流排驅動代碼在i2c-s3c2410.c中。照例先從init看起。

static int __init i2c_adap_s3c_init(void)<br />{<br />return platform_driver_register(&s3c24xx_i2c_driver);<br />}

在init中只是調用了平台驅動註冊函數註冊了一個i2c的平台驅動s3c24xx_i2c_driver。這個驅動是一個platform_driver的結構體變數。注意這裡不是i2c_driver結構體,因為i2c_driver是對裝置的驅動,而這裡對控制器的驅動要使用platform_driver

static struct platform_driver s3c24xx_i2c_driver = {<br />.probe= s3c24xx_i2c_probe,<br />.remove= s3c24xx_i2c_remove,<br />.suspend_late= s3c24xx_i2c_suspend_late,<br />.resume= s3c24xx_i2c_resume,<br />.id_table= s3c24xx_driver_ids,<br />.driver= {<br />.owner= THIS_MODULE,<br />.name= "s3c-i2c",<br />},<br />};

同樣的,重要的函數還是那幾個:probe,remove,suspend_late,resume。再加上一個id_table和device_driver結構體變數。

下面逐個分析:

* probe函數

當調用platform_driver_register函數註冊platform_driver結構體時,probe指標指向的s3c24xx_i2c_probe函數將會被調用。這部分詳細解釋參考本部落格另一篇文章《S3C2410看門狗驅動分析》。細心的朋友可能會發現,在s3c24xx_i2c_driver中,驅動的名字是"s3c-i2c",而在板檔案中可以看到,裝置的名字是"s3c2410-i2c",這兩個名字不一樣,那驅動和裝置是如何match的呢?答案就在於id_table。這個id_table包含了驅動所支援的裝置ID表。在match的時候,判斷這個表中的名字是不是和裝置一致,一致則match成功。這也是為什麼一個驅動可以同時match成功多個裝置的原因。如果只是靠platform_driver-->driver中的名字來匹配的話,那麼驅動和裝置只能是一對一的關係了。

static struct platform_device_id s3c24xx_driver_ids[] = {<br />{<br />.name= "s3c2410-i2c",<br />.driver_data= TYPE_S3C2410,<br />}, {<br />.name= "s3c2440-i2c",<br />.driver_data= TYPE_S3C2440,<br />}, { },<br />};

扯遠了,還是看看probe的代碼吧~

static int s3c24xx_i2c_probe(struct platform_device *pdev)<br />{<br />struct s3c24xx_i2c *i2c;<br />struct s3c2410_platform_i2c *pdata;<br />struct resource *res;<br />int ret;</p><p>pdata = pdev->dev.platform_data;<br />if (!pdata) {<br />dev_err(&pdev->dev, "no platform data/n");<br />return -EINVAL;<br />}<br /> //給s3c24xx_i2c結構體申請空間<br />i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);<br />if (!i2c) {<br />dev_err(&pdev->dev, "no memory for state/n");<br />return -ENOMEM;<br />}<br /> //填充s3c24xx_i2c結構體中各項,包括名稱、所有者、演算法、所屬class等等<br />strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));<br />i2c->adap.owner = THIS_MODULE;<br />i2c->adap.algo = &s3c24xx_i2c_algorithm; //這個下面會重點介紹<br />i2c->adap.retries = 2;<br />i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;<br />i2c->tx_setup = 50;</p><p>spin_lock_init(&i2c->lock);<br />init_waitqueue_head(&i2c->wait);</p><p>/* find the clock and enable it */<br /> // 找到i2c始終並且使能它<br />i2c->dev = &pdev->dev;<br />i2c->clk = clk_get(&pdev->dev, "i2c");<br />if (IS_ERR(i2c->clk)) {<br />dev_err(&pdev->dev, "cannot get clock/n");<br />ret = -ENOENT;<br />goto err_noclk;<br />}</p><p>dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);</p><p>clk_enable(i2c->clk);</p><p>/* map the registers */<br /> /*映射寄存器*/<br />res = platform_get_resource(pdev, IORESOURCE_MEM, 0);<br />if (res == NULL) {<br />dev_err(&pdev->dev, "cannot find IO resource/n");<br />ret = -ENOENT;<br />goto err_clk;<br />}</p><p>i2c->ioarea = request_mem_region(res->start, resource_size(res),<br /> pdev->name);</p><p>if (i2c->ioarea == NULL) {<br />dev_err(&pdev->dev, "cannot request IO/n");<br />ret = -ENXIO;<br />goto err_clk;<br />}</p><p>i2c->regs = ioremap(res->start, resource_size(res));</p><p>if (i2c->regs == NULL) {<br />dev_err(&pdev->dev, "cannot map IO/n");<br />ret = -ENXIO;<br />goto err_ioarea;<br />}</p><p>dev_dbg(&pdev->dev, "registers %p (%p, %p)/n",<br />i2c->regs, i2c->ioarea, res);</p><p>/* setup info block for the i2c core */</p><p>i2c->adap.algo_data = i2c;<br />i2c->adap.dev.parent = &pdev->dev;</p><p>/* initialise the i2c controller */<br /> /*s3c24xx_i2c結構體變數i2c的必要的資訊都填充完了以後,開始進行初始化*/<br />ret = s3c24xx_i2c_init(i2c);<br />if (ret != 0)<br />goto err_iomap;</p><p>/* find the IRQ for this unit (note, this relies on the init call to<br /> * ensure no current IRQs pending<br /> */<br /> //接下來申請中斷<br />i2c->irq = ret = platform_get_irq(pdev, 0);<br />if (ret <= 0) {<br />dev_err(&pdev->dev, "cannot find IRQ/n");<br />goto err_iomap;<br />}</p><p>ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,<br /> dev_name(&pdev->dev), i2c);</p><p>if (ret != 0) {<br />dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq);<br />goto err_iomap;<br />}</p><p>ret = s3c24xx_i2c_register_cpufreq(i2c);<br />if (ret < 0) {<br />dev_err(&pdev->dev, "failed to register cpufreq notifier/n");<br />goto err_irq;<br />}</p><p>/* Note, previous versions of the driver used i2c_add_adapter()<br /> * to add the bus at any number. We now pass the bus number via<br /> * the platform data, so if unset it will now default to always<br /> * being bus 0.<br /> */</p><p>i2c->adap.nr = pdata->bus_num;</p><p> //看到了吧?下面調用了i2c-core中的i2c_add_adapter函數來添加一個i2c控制器<br /> //i2c_add_numbered_adapter和i2c_add_adapter的區別在於前者用來添加一個在CPU內<br /> //部整合的適配器,而後者用來添加一個CPU外部的適配器。顯然這裡應該用前者。<br />ret = i2c_add_numbered_adapter(&i2c->adap);<br />if (ret < 0) {<br />dev_err(&pdev->dev, "failed to add bus to i2c core/n");<br />goto err_cpufreq;<br />}</p><p>platform_set_drvdata(pdev, i2c);</p><p>dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev));<br />return 0;</p><p> err_cpufreq:<br />s3c24xx_i2c_deregister_cpufreq(i2c);</p><p> err_irq:<br />free_irq(i2c->irq, i2c);</p><p> err_iomap:<br />iounmap(i2c->regs);</p><p> err_ioarea:<br />release_resource(i2c->ioarea);<br />kfree(i2c->ioarea);</p><p> err_clk:<br />clk_disable(i2c->clk);<br />clk_put(i2c->clk);</p><p> err_noclk:<br />kfree(i2c);<br />return ret;<br />}

 

*remove函數

這是和probe相反的一個函數,在i2c_adap_s3c_exit時調用。主要功能是登出適配器,釋放中斷,釋放記憶體地區,禁止始終等等。看到上邊代碼中的err_的各個部分了吧? remove是它們的匯總。

 

*suspend函數和resume函數

把這兩個放一起說吧,掛起和恢複函數。掛起時儲存狀態共置標誌位,恢複時重新初始化i2c適配器共置標誌位。

 

Algorithm

 哎呀我去,終於到這了。憋得我難受啊。這裡要重點介紹一下,不僅要知其然,還要知其所以然,這樣我們以後自己寫驅動的時候就有把握了。

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {<br />.master_xfer= s3c24xx_i2c_xfer,<br />.functionality= s3c24xx_i2c_func,<br />};

這裡實現的就是這個s3c24xx_i2c_xfer。這個是控制器能不能動作的關鍵,缺了這個,控制器就是廢銅爛鐵。

static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,<br />struct i2c_msg *msgs, int num)<br />{<br />struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;<br />int retry;<br />int ret;</p><p>for (retry = 0; retry < adap->retries; retry++) {</p><p>ret = s3c24xx_i2c_doxfer(i2c, msgs, num);</p><p>if (ret != -EAGAIN)<br />return ret;</p><p>dev_dbg(i2c->dev, "Retrying transmission (%d)/n", retry);</p><p>udelay(100);<br />}</p><p>return -EREMOTEIO;<br />}

 

完成任務的函數是s3c24xx_i2c_doxfer(),源碼清單如下,static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,<br /> struct i2c_msg *msgs, int num)<br />{<br />unsigned long timeout;<br />int ret;</p><p>if (i2c->suspended)<br />return -EIO;</p><p>ret = s3c24xx_i2c_set_master(i2c);<br />if (ret != 0) {<br />dev_err(i2c->dev, "cannot get bus (error %d)/n", ret);<br />ret = -EAGAIN;<br />goto out;<br />}</p><p>spin_lock_irq(&i2c->lock);</p><p>i2c->msg = msgs;<br />i2c->msg_num = num;<br />i2c->msg_ptr = 0;<br />i2c->msg_idx = 0;<br />i2c->state = STATE_START;</p><p>s3c24xx_i2c_enable_irq(i2c);<br />s3c24xx_i2c_message_start(i2c, msgs);<br />spin_unlock_irq(&i2c->lock);</p><p>timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);</p><p>ret = i2c->msg_idx;</p><p>/* having these next two as dev_err() makes life very<br /> * noisy when doing an i2cdetect */</p><p>if (timeout == 0)<br />dev_dbg(i2c->dev, "timeout/n");<br />else if (ret != num)<br />dev_dbg(i2c->dev, "incomplete xfer (%d)/n", ret);</p><p>/* ensure the stop has been through the bus */</p><p>msleep(1);</p><p> out:<br />return ret;<br />}

上面代碼可以分成幾個部分來看:

  * s3c24xx_i2c_set_master() 這個函數每隔1ms查看一次i2c匯流排狀態,timeout是400ms,如果在這期間匯流排狀態不忙,則返回零。否則返回-ETIMEDOUT

  * 將要發送的訊息和其他資訊付給i2c->msg和其他變數,並將狀態設定為STATE_START

  * s3c24xx_i2c_enable_irq() 使能中斷

  * s3c24xx_i2c_message_start() 重中之重啊。在看代碼之前先來看看2440的datasheet上是怎麼說的吧。

代碼清單如下:static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,<br /> struct i2c_msg *msg)<br />{<br />unsigned int addr = (msg->addr & 0x7f) << 1;<br />unsigned long stat;<br />unsigned long iiccon;</p><p>stat = 0;<br />stat |= S3C2410_IICSTAT_TXRXEN;</p><p>if (msg->flags & I2C_M_RD) {//如果是read data, from slave to masterstat |= S3C2410_IICSTAT_MASTER_RX;<br />addr |= 1;<br />} else<br />stat |= S3C2410_IICSTAT_MASTER_TX;</p><p>if (msg->flags & I2C_M_REV_DIR_ADDR)<br />addr ^= 1;</p><p>/* todo - check for wether ack wanted or not */<br />s3c24xx_i2c_enable_ack(i2c);</p><p>iiccon = readl(i2c->regs + S3C2410_IICCON);<br />writel(stat, i2c->regs + S3C2410_IICSTAT);</p><p>dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS/n", stat, addr);<br />writeb(addr, i2c->regs + S3C2410_IICDS);</p><p>/* delay here to ensure the data byte has gotten onto the bus<br /> * before the transaction is started */</p><p>ndelay(i2c->tx_setup);</p><p>dev_dbg(i2c->dev, "iiccon, %08lx/n", iiccon);<br />writel(iiccon, i2c->regs + S3C2410_IICCON);</p><p>stat |= S3C2410_IICSTAT_START;<br />writel(stat, i2c->regs + S3C2410_IICSTAT);<br />}

 

(今天沒寫完啊,明天繼續~)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.