解決一個(gè)I2C讀寫問題
之前寫關(guān)于I2C相關(guān)的文章
1、問題
今天遇到一個(gè)問題,我們有一個(gè)芯片,I2C讀寫失敗,導(dǎo)致錄音有問題,而且是偶現(xiàn)的。
log提示看到是返回 -6
<3>[??730.336308]??(3)[2085:tinycap]es7243_read?error2,?ret?=?-6.
下面流程找一下這個(gè) -6 是從哪里來的
2、I2c 發(fā)送流程
i2c-core.c
調(diào)用發(fā)送函數(shù)
/**
?*?__i2c_transfer?-?unlocked?flavor?of?i2c_transfer
?*?@adap:?Handle?to?I2C?bus
?*?@msgs:?One?or?more?messages?to?execute?before?STOP?is?issued?to
?*?terminate?the?operation;?each?message?begins?with?a?START.
?*?@num:?Number?of?messages?to?be?executed.
?*
?*?Returns?negative?errno,?else?the?number?of?messages?executed.
?*
?*?Adapter?lock?must?be?held?when?calling?this?function.?No?debug?logging
?*?takes?place.?adap->algo->master_xfer?existence?isn't?checked.
?*/
int?__i2c_transfer(struct?i2c_adapter?*adap,?struct?i2c_msg?*msgs,?int?num)
{
?unsigned?long?orig_jiffies;
?int?ret,?try;
?if?(adap->quirks?&&?i2c_check_for_quirks(adap,?msgs,?num))
??return?-EOPNOTSUPP;
?/*?i2c_trace_msg?gets?enabled?when?tracepoint?i2c_transfer?gets
??*?enabled.??This?is?an?efficient?way?of?keeping?the?for-loop?from
??*?being?executed?when?not?needed.
??*/
?if?(static_key_false(&i2c_trace_msg))?{
??int?i;
??for?(i?=?0;?i????if?(msgs[i].flags?&?I2C_M_RD)
????trace_i2c_read(adap,?&msgs[i],?i);
???else
????trace_i2c_write(adap,?&msgs[i],?i);
?}
?/*?Retry?automatically?on?arbitration?loss?*/
?orig_jiffies?=?jiffies;
?for?(ret?=?0,?try?=?0;?try?<=?adap->retries;?try++)?{
??ret?=?adap->algo->master_xfer(adap,?msgs,?num);
??if?(ret?!=?-EAGAIN)
???break;
??if?(time_after(jiffies,?orig_jiffies?+?adap->timeout))
???break;
?}
?if?(static_key_false(&i2c_trace_msg))?{
??int?i;
??for?(i?=?0;?i????if?(msgs[i].flags?&?I2C_M_RD)
????trace_i2c_reply(adap,?&msgs[i],?i);
??trace_i2c_result(adap,?i,?ret);
?}
?return?ret;
}
EXPORT_SYMBOL(__i2c_transfer);
里面有一句
ret?=?adap->algo->master_xfer(adap,?msgs,?num);
這里需要找到初始化的位置,這種指針初始化的位置,一般都是和平臺(tái)設(shè)備相關(guān)的,這里也是一個(gè)技巧,初學(xué)者看到這類指針一般都會(huì)比較懵。
所以,就開始去找吧。
因?yàn)槲矣玫氖荕TK平臺(tái),所以需要去找到MTK平臺(tái)相關(guān)里面初始化的這個(gè)指針
i2c-mt65xx.c?(D:\source\kernel-4.4\drivers\i2c\busses)?line?755?:??.master_xfer?=?mtk_i2c_transfer,
在文件
i2c-mt65xx.c
這里初始化了
static?const?struct?i2c_algorithm?mtk_i2c_algorithm?=?{
?.master_xfer?=?mtk_i2c_transfer,
?.functionality?=?mtk_i2c_functionality,
};
然后再調(diào)用發(fā)送函數(shù)
static?int?mtk_i2c_transfer(struct?i2c_adapter?*adap,
???????struct?i2c_msg?msgs[],?int?num)
{
?int?ret;
?int?left_num?=?num;
?struct?mtk_i2c?*i2c?=?i2c_get_adapdata(adap);
?ret?=?mtk_i2c_clock_enable(i2c);
?if?(ret)
??return?ret;
?i2c->auto_restart?=?i2c->dev_comp->auto_restart;
?/*?checking?if?we?can?skip?restart?and?optimize?using?WRRD?mode?*/
?if?(i2c->auto_restart?&&?num?==?2)?{
??if?(!(msgs[0].flags?&?I2C_M_RD)?&&?(msgs[1].flags?&?I2C_M_RD)?&&
??????msgs[0].addr?==?msgs[1].addr)?{
???i2c->auto_restart?=?0;
??}
?}
?if?(i2c->auto_restart?&&?num?>=?2?&&?i2c->speed_hz?>?MAX_FS_MODE_SPEED)
??/*?ignore?the?first?restart?irq?after?the?master?code,
???*?otherwise?the?first?transfer?will?be?discarded.
???*/
??i2c->ignore_restart_irq?=?true;
?else
??i2c->ignore_restart_irq?=?false;
?while?(left_num--)?{
??if?(!msgs->buf)?{
???dev_dbg(i2c->dev,?"data?buffer?is?NULL.\n");
???ret?=?-EINVAL;
???goto?err_exit;
??}
??if?(msgs->flags?&?I2C_M_RD)
???i2c->op?=?I2C_MASTER_RD;
??else
???i2c->op?=?I2C_MASTER_WR;
??if?(!i2c->auto_restart)?{
???if?(num?>?1)?{
????/*?combined?two?messages?into?one?transaction?*/
????i2c->op?=?I2C_MASTER_WRRD;
????left_num--;
???}
??}
??/*?len?<=?8:?use?fifo?mode,?len?>?8:?use?DMA?mode.?*/
??ret?=?mtk_i2c_do_transfer(i2c,?msgs,?num,?left_num);
??if?(ret?0)
???goto?err_exit;
??msgs++;
?}
?/*?the?return?value?is?number?of?executed?messages?*/
?ret?=?num;
err_exit:
?mtk_i2c_clock_disable(i2c);
?return?ret;
}
3、返回 值的代碼位置
在文件
i2c-mt65xx.c
中的函數(shù)
static?int?mtk_i2c_do_transfer(struct?mtk_i2c?*i2c,?struct?i2c_msg?*msgs,
??????????int?num,?int?left_num)
{
?bool?dma_en;
?u8?*data_buf;
?u16?data_len;
?u16?read_len;
?u16?addr_reg;
?u16?start_reg;
?u16?control_reg;
?u16?restart_flag?=?0;
?u32?reg_4g_mode;
?dma_addr_t?rpaddr?=?0;
?dma_addr_t?wpaddr?=?0;
?int?ret;
?i2c->irq_stat?=?0;
?if?(i2c->auto_restart)
??restart_flag?=?I2C_RS_TRANSFER;
?reinit_completion(&i2c->msg_complete);
?control_reg?=?readw(i2c->base?+?OFFSET_CONTROL)?&
???~(I2C_CONTROL_DIR_CHANGE?|?I2C_CONTROL_RS);
?if?((i2c->speed_hz?>?400000)?||?(left_num?>=?1))
??control_reg?|=?I2C_CONTROL_RS;
?if?(i2c->op?==?I2C_MASTER_WRRD)
??control_reg?|=?I2C_CONTROL_DIR_CHANGE?|?I2C_CONTROL_RS;
?writew(control_reg,?i2c->base?+?OFFSET_CONTROL);
?/*?set?start?condition?*/
?if?(i2c->speed_hz?<=?100000)
??writew(I2C_ST_START_CON,?i2c->base?+?OFFSET_EXT_CONF);
?else
??writew(I2C_FS_START_CON,?i2c->base?+?OFFSET_EXT_CONF);
?addr_reg?=?msgs->addr?<1;
?if?(i2c->op?==?I2C_MASTER_RD)
??addr_reg?|=?0x1;
?writew(addr_reg,?i2c->base?+?OFFSET_SLAVE_ADDR);
?/*?Clear?interrupt?status?*/
?writew(restart_flag?|?I2C_HS_NACKERR?|?I2C_ACKERR?|
????????I2C_TRANSAC_COMP,?i2c->base?+?OFFSET_INTR_STAT);
?writew(I2C_FIFO_ADDR_CLR,?i2c->base?+?OFFSET_FIFO_ADDR_CLR);
?/*?Enable?interrupt?*/
?writew(restart_flag?|?I2C_HS_NACKERR?|?I2C_ACKERR?|
????????I2C_TRANSAC_COMP,?i2c->base?+?OFFSET_INTR_MASK);
?/*?Set?transfer?and?transaction?len?*/
?if?(i2c->op?==?I2C_MASTER_WRRD)?{
??if?(i2c->dev_comp->aux_len_reg)?{
???writew(msgs->len,?i2c->base?+?OFFSET_TRANSFER_LEN);
???writew((msgs?+?1)->len,?i2c->base?+
??????????OFFSET_TRANSFER_LEN_AUX);
??}?else?{
???writew(msgs->len?|?((msgs?+?1)->len)?<8,
??????????i2c->base?+?OFFSET_TRANSFER_LEN);
??}
??writew(I2C_WRRD_TRANAC_VALUE,?i2c->base?+?OFFSET_TRANSAC_LEN);
?}?else?{
??writew(msgs->len,?i2c->base?+?OFFSET_TRANSFER_LEN);
??writew(num,?i2c->base?+?OFFSET_TRANSAC_LEN);
?}
?/*?Prepare?buffer?data?to?start?transfer?*/
?if?(msgs->len?>?I2C_FIFO_SIZE?||?(i2c->op?==?I2C_MASTER_WRRD?&&
?????(msgs?+?1)->len?>?I2C_FIFO_SIZE))?{
??dma_en?=?true;
??if?(i2c->op?==?I2C_MASTER_RD)?{
???writel(I2C_DMA_INT_FLAG_NONE,?i2c->pdmabase?+?OFFSET_INT_FLAG);
???writel(I2C_DMA_CON_RX,?i2c->pdmabase?+?OFFSET_CON);
???rpaddr?=?dma_map_single(i2c->dev,?msgs->buf,
??????msgs->len,?DMA_FROM_DEVICE);
???if?(dma_mapping_error(i2c->dev,?rpaddr))
????return?-ENOMEM;
???if?(i2c->dev_comp->support_33bits)?{
????reg_4g_mode?=?mtk_i2c_set_4g_mode(rpaddr);
????writel(reg_4g_mode,?i2c->pdmabase?+?OFFSET_RX_4G_MODE);
???}
???writel((u32)rpaddr,?i2c->pdmabase?+?OFFSET_RX_MEM_ADDR);
???writel(msgs->len,?i2c->pdmabase?+?OFFSET_RX_LEN);
??}?else?if?(i2c->op?==?I2C_MASTER_WR)?{
???writel(I2C_DMA_INT_FLAG_NONE,?i2c->pdmabase?+?OFFSET_INT_FLAG);
???writel(I2C_DMA_CON_TX,?i2c->pdmabase?+?OFFSET_CON);
???wpaddr?=?dma_map_single(i2c->dev,?msgs->buf,
??????msgs->len,?DMA_TO_DEVICE);
???if?(dma_mapping_error(i2c->dev,?wpaddr))
????return?-ENOMEM;
???if?(i2c->dev_comp->support_33bits)?{
????reg_4g_mode?=?mtk_i2c_set_4g_mode(wpaddr);
????writel(reg_4g_mode,?i2c->pdmabase?+?OFFSET_TX_4G_MODE);
???}
???writel((u32)wpaddr,?i2c->pdmabase?+?OFFSET_TX_MEM_ADDR);
???writel(msgs->len,?i2c->pdmabase?+?OFFSET_TX_LEN);
??}?else?{
???writel(I2C_DMA_CLR_FLAG,?i2c->pdmabase?+?OFFSET_INT_FLAG);
???writel(I2C_DMA_CLR_FLAG,?i2c->pdmabase?+?OFFSET_CON);
???wpaddr?=?dma_map_single(i2c->dev,?msgs->buf,
??????msgs->len,?DMA_TO_DEVICE);
???if?(dma_mapping_error(i2c->dev,?wpaddr))
????return?-ENOMEM;
???rpaddr?=?dma_map_single(i2c->dev,?(msgs?+?1)->buf,
??????(msgs?+?1)->len,?DMA_FROM_DEVICE);
???if?(dma_mapping_error(i2c->dev,?rpaddr))?{
????dma_unmap_single(i2c->dev,?wpaddr,
???????msgs->len,?DMA_TO_DEVICE);
????return?-ENOMEM;
???}
???if?(i2c->dev_comp->support_33bits)?{
????reg_4g_mode?=?mtk_i2c_set_4g_mode(wpaddr);
????writel(reg_4g_mode,?i2c->pdmabase?+?OFFSET_TX_4G_MODE);
????reg_4g_mode?=?mtk_i2c_set_4g_mode(rpaddr);
????writel(reg_4g_mode,?i2c->pdmabase?+?OFFSET_RX_4G_MODE);
???}
???writel((u32)wpaddr,?i2c->pdmabase?+?OFFSET_TX_MEM_ADDR);
???writel((u32)rpaddr,?i2c->pdmabase?+?OFFSET_RX_MEM_ADDR);
???writel(msgs->len,?i2c->pdmabase?+?OFFSET_TX_LEN);
???writel((msgs?+?1)->len,?i2c->pdmabase?+?OFFSET_RX_LEN);
??}
??if?(i2c->op?!=?I2C_MASTER_RD)
???writel(I2C_DMA_START_EN,?i2c->pdmabase?+?OFFSET_EN);
?}?else?{
??dma_en?=?false;
??if?(i2c->op?!=?I2C_MASTER_RD)?{
???data_buf?=?msgs->buf;
???data_len?=?msgs->len;
???while?(data_len--)
????writew_relaxed(*(data_buf++),?i2c->base?+?OFFSET_DATA_PORT);
??}
?}
?if?(!i2c->auto_restart)?{
??start_reg?=?I2C_TRANSAC_START;
?}?else?{
??start_reg?=?I2C_TRANSAC_START?|?I2C_RS_MUL_TRIG;
??if?(left_num?>=?1)
???start_reg?|=?I2C_RS_MUL_CNFG;
?}
?writew(start_reg,?i2c->base?+?OFFSET_START);
?if?(dma_en?&&?i2c->op?==?I2C_MASTER_RD)
??writel(I2C_DMA_START_EN,?i2c->pdmabase?+?OFFSET_EN);
?ret?=?wait_for_completion_timeout(&i2c->msg_complete,
???????i2c->adap.timeout);
?/*?Clear?interrupt?mask?*/
?writew(~(restart_flag?|?I2C_HS_NACKERR?|?I2C_ACKERR?|
????????I2C_TRANSAC_COMP),?i2c->base?+?OFFSET_INTR_MASK);
?if?(dma_en)?{
??if?(i2c->op?==?I2C_MASTER_WR)?{
???dma_unmap_single(i2c->dev,?wpaddr,
??????msgs->len,?DMA_TO_DEVICE);
??}?else?if?(i2c->op?==?I2C_MASTER_RD)?{
???dma_unmap_single(i2c->dev,?rpaddr,
??????msgs->len,?DMA_FROM_DEVICE);
??}?else?{
???dma_unmap_single(i2c->dev,?wpaddr,?msgs->len,
??????DMA_TO_DEVICE);
???dma_unmap_single(i2c->dev,?rpaddr,?(msgs?+?1)->len,
??????DMA_FROM_DEVICE);
??}
?}
?if?(ret?==?0)?{
??dev_dbg(i2c->dev,?"addr:?%x,?transfer?timeout\n",?msgs->addr);
??mtk_i2c_init_hw(i2c);
??return?-ETIMEDOUT;
?}
?completion_done(&i2c->msg_complete);
?if?(i2c->irq_stat?&?(I2C_HS_NACKERR?|?I2C_ACKERR))?{
??dev_dbg(i2c->dev,?"addr:?%x,?transfer?ACK?error\n",?msgs->addr);
??mtk_i2c_init_hw(i2c);
??return?-ENXIO;
?}
?if?(!dma_en?&&?i2c->op?!=?I2C_MASTER_WR)?{
??data_buf?=?(i2c->op?==?I2C_MASTER_RD)???msgs->buf?:
??????(msgs?+?1)->buf;
??data_len?=?(i2c->op?==?I2C_MASTER_RD)???msgs->len?:
??????(msgs?+?1)->len;
??read_len?=?(readw(i2c->base?+?OFFSET_FIFO_STAT)?>>?4)?&?0xf;
??if?(read_len?==?data_len)?{
???while?(data_len--)
????*(data_buf++)?=?readw_relaxed(i2c->base?+?OFFSET_DATA_PORT);
??}?else?{
???dev_dbg(i2c->dev,?"data_len?%x,?read_len?%x,?fifo?read?len?error\n",
????data_len,?read_len);
???mtk_i2c_init_hw(i2c);
???return?-EIO;
??}
?}
?return?0;
}
下面這段代碼
?if?(i2c->irq_stat?&?(I2C_HS_NACKERR?|?I2C_ACKERR))?{
??dev_dbg(i2c->dev,?"addr:?%x,?transfer?ACK?error\n",?msgs->addr);
??mtk_i2c_init_hw(i2c);
??return?-ENXIO;
?}
宏定義
errno-base.h
#define?ENXIO???6?/*?No?such?device?or?address?*/
表示就是找不到設(shè)備,沒有應(yīng)答
我用邏輯分析儀抓波形,確實(shí)存在沒有應(yīng)答的情況下打印了上面的錯(cuò)誤日志。

解決
問題日志
<6>[?1594.422871]??(3)[1663:AudioInMic][ES7243]:?es7243_pcm_startup()?line:?444?start.
<6>[?1594.432778]??(3)[1663:AudioInMic][ES7243]:?es7243_pcm_startup()?line:?444?start.
<6>[?1594.432902]??(3)[1663:AudioInMic][ES7243]:?es7243_pcm_hw_params()?line:?472?es7243_pcm_hw_params
<6>[?1594.432918]??(3)[1663:AudioInMic][ES7243]:?es7243_set_bias_level()?line:?568?level:1
<6>[?1594.433367]??(3)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x00?val:0x09?ok
<6>[?1594.433741]??(0)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x06?val:0x18?ok
<6>[?1594.434121]??(2)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x07?val:0x80?ok
<6>[?1594.434506]??(0)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x01?val:0x0c?ok
<6>[?1594.434886]??(0)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x02?val:0x10?ok
<6>[?1594.435288]??(2)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x03?val:0x04?ok
<6>[?1594.435682]??(3)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x04?val:0x02?ok
<6>[?1594.436136]??(1)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x05?val:0x1a?ok
<6>[?1594.436574]??(3)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x09?val:0x3f?ok
<6>[?1594.437004]??(0)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x06?val:0x00?ok
<6>[?1594.437424]??(3)[1663:AudioInMic][ES7243]:?es7243_write()?line:?161?es7243_write?reg:0x05?val:0x36?ok
<3>[?1594.437676]??(3)[1663:AudioInMic]es7243_write?13?error->[REG-0x08,val-0x07]
<3>[?1594.437907]??(3)[1663:AudioInMic]es7243_read?error1
<3>[?1594.438220]??(3)[1663:AudioInMic]es7243_write?13?error->[REG-0x01,val-0x8c]
<6>[?1594.438246]??(3)[1663:AudioInMic][ES7243]:?es7243_update_bits()?line:?175?value:12
<3>[?1594.438951]??(3)[1663:AudioInMic]es7243_read?error2,?ret?=?-6.
<6>[?1594.438990]??(3)[1663:AudioInMic][ES7243]:?es7243_pcm_hw_params()?line:?538?es7243_pcm_hw_params538?--?Reg?08?---->?0x0!
<3>[?1594.439477]??(3)[1663:AudioInMic]es7243_read?error2,?ret?=?-6.
<6>[?1594.439510]??(3)[1663:AudioInMic][ES7243]:?es7243_pcm_hw_params()?line:?540?es7243_pcm_hw_params540?--?Reg?05?---->?0x0!
復(fù)現(xiàn)了十幾把,發(fā)現(xiàn)每次都是剛開始I2C可以讀寫,后面再讀寫的時(shí)候,就容易出現(xiàn)I2C操作失敗的情況。
FAE給的答復(fù)是
I2C走線是否有其他干擾 I2C有沒有加RC濾波電路
硬件已經(jīng)確定了,想再修改硬件可能性比較小,也比較麻煩,硬件測(cè)量了I2C波形,也沒有發(fā)現(xiàn)異常。故此,還是想在軟件上做規(guī)避。
我猜測(cè)可能是芯片操作過快,芯片沒有來得及反應(yīng),而且每次操作開始I2C都是可以讀寫的,所以就想在讀寫函數(shù)中阻塞一會(huì)會(huì)來規(guī)避這個(gè)問題。
patch
diff?--git?a/kernel-4.4/sound/soc/codecs/es7243.c?b/kernel-4.4/sound/soc/codecs/es7243.c
index?272f5dbf88..0e6a1e6bcc?100755
---?a/kernel-4.4/sound/soc/codecs/es7243.c
+++?b/kernel-4.4/sound/soc/codecs/es7243.c
@@?-32,6?+32,7?@@
?#include?
?#include?
?#include?
+#include?
?#ifdef?CONFIG_OF
?#include?
?#include?
@@?-127,6?+128,7?@@?static?int?es7243_read(u8?reg,?u8?*rt_value,?struct?i2c_client?*client)
????????read_cmd[0]?=?reg;
????????cmd_len?=?1;
+???????udelay(2000);
????????if?(client->adapter?==?NULL)
????????????????pr_err("es7243_read?client->adapter==NULL\n");
@@?-141,6?+143,7?@@?static?int?es7243_read(u8?reg,?u8?*rt_value,?struct?i2c_client?*client)
????????????????pr_err("es7243_read?error2,?ret?=?%d.\n",?ret);
????????????????return?-1;
????????}
+
????????ES7243_DEBUG("es7243_read?OK\n");
????????return?0;
?}
@@?-153,12?+156,14?@@?static?int?es7243_write(u8?reg,?unsigned?char?value,?struct?i2c_client?*client)
????????write_cmd[0]?=?reg;
????????write_cmd[1]?=?value;
+???????udelay(2000);
????????ret?=?i2c_master_send(client,?write_cmd,?2);
????????if?(ret?!=?2)?{
????????????????pr_err("es7243_write?%x?error->[REG-0x%02x,val-0x%02x]\n",client->addr,reg,value);
????????????????return?-1;
????????}
????????ES7243_DEBUG("es7243_write?reg:0x%.2x?val:0x%.2x?ok?\n",reg,value);
+
????????return?0;
?}
修改后,驗(yàn)證了十幾把,確實(shí)沒有再發(fā)現(xiàn)問題,如果再有問題的話,再分析。

