MT7688 坑爹的 SPI Master 半雙工全雙工問題

MTK的東西便宜是真的便宜,好用也相對比較好用,但是總有那麼幾個地方,讓人用着心裏就窩火,就MT76x8來說,第一個窩火的地方就是啓動跳線選擇,非得把串口用作啓動跳線,導致調試起來非常麻煩,第二個就是本文要說的SPI問題,此問題分析和測試了很久,主要是關於spi 半雙工和全雙工的問題。

首先,來看下datesheet關於SPI Master的描述:

一共就這麼幾個寄存器:

關於半雙工和全雙工的描述在SPI_MASTER的BIT10

僅僅只有more_buf_mode=1時,全雙工纔有效;

關於more_buf_mode的描述如下:

即當more_buf_mode = 0時,spi控制器的傳輸數據域最多有2個字節,等於1時,最多有8個字節;此處爲什麼寫傳輸數據域,不是最多發送字節數,繼續看關於more_buf_mode的描述

等於0,即只能工作於半雙工模式;等於1,可以選擇半雙工,也可以全雙工;此處我們只分析1的情況,因爲0傳輸的數據是在太少了;

等於1 及 半雙工時:SPI_OP_ADDR 寄存器也用作了數據寄存器,加上D0-D8幾個,一共9個寄存器,因此最多一次發送36個字節,接收時將循環覆蓋D0-D7;

等於1 及 全雙工時,D0-D3用於發送,D4-D7用於接收;

以上文檔關於寄存器及功能的描述,非常正常,看不出什麼問題所在。

下面,將結合代碼以及邏輯分析儀來繼續深入查探虛實:

SPI Master驅動代碼位於drivers/spi/mt-7621.c

static int mt7621_spi_transfer_one_message(struct spi_master *master,
					   struct spi_message *m)
{
	struct spi_device *spi = m->spi;
	int cs = spi->chip_select;

	return mt7621_spi_transfer_half_duplex(master, m);
}

代碼中只實現了半雙工模式,而我們現在是要分析全雙工,因此需要修改驅動,配置爲全雙工,MT7688的SPI控制器只有一個,但是有2個片選,SPI_CS0用於系統SPI flash,這個我們不能動;另一個SPI_CS1 我們可以用來測試全雙工;

兩個片選,複用一個控制器,複用一個驅動,因此我們可以根據片選的情況來確定傳輸方式,給CS1配置爲全雙工傳輸;

static int mt7621_spi_transfer_one_message(struct spi_master *master,
					   struct spi_message *m)
{
	struct spi_device *spi = m->spi;
	int cs = spi->chip_select;
	if(cs == 0)
		return mt7621_spi_transfer_half_duplex(master, m);
	if(cs == 1)
		return mt7621_spi_transfer_full_duplex(master, m);
}

具體的full_deplex函數就不貼了,其實主要就是修改SPI_MASTER的BIT10,重寫一個reset函數用於full_deplex就行了,還有就是注意full_deplex 的if (WARN_ON(rx_len > 16)) ,一次傳輸長度的判斷,全雙工應該是最多16個字節

static void mt7621_spi_reset_full(struct mt7621_spi *rs)
{
	u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);

	master |= 7 << 29;
	master |= 1 << 2;
	//master &= ~(1 << 10);
        master |= (1 << 10);

	mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
}

至此,驅動已沒什麼好分析的了。

最後就剩下SPI應用層使用了,有兩種方式:

第一,直接write;第二,ioctl方式

我想既然是全雙工,肯定是想要得到回覆的數據了,因此選用ioctl方式,其主要就是這麼一個結構體

struct spi_ioc_transfer tr ={
        .tx_buf = (unsigned long) TxBuf,
	.rx_buf = (unsigned long) RxBuf,
	.len =len,
	.delay_usecs = delay,
};

txbuf和rxbuf的長度要相等,len爲單個tx或者rx的長度,delay爲兩次msg傳輸的間隔

可是運行後,通過邏輯分析儀抓到的波形,跟發送的數據卻對不上,此處不便貼邏輯分析儀的圖,以文字方式描述一下,

其區別在於,在每發送的字節後面都多發了個0x00,

這個0x00明顯不是應用的數據,看驅動,也沒有發現主動會多發數據的地方,那麼這應該就是spi master自己發出去的;

換種方式再次驗證一下猜想,將rx_buf = NULL,即只發不讀,相當於於直接write,此後再抓波形,發現0x00沒有了;

應用換成直接write,抓到的波形跟rx_buf=NULL的結果一樣;

到此我們可以肯定多發的0x00是控制器自己發出的;

再來一次確定性的驗證:應用直接read,只讀數據我們看看有沒有波形,結果抓到的數據發送全部是0x00,這個00肯定就是控制器爲了讀取spi的數據,自動發送的,這是由spi的傳輸機制來決定的,在SCK的跳變處發送或者讀取數據;

到此,我們可以看出MT7688的SPI控制器的“特殊”之處。

當然這個特點,對於使用LCD等只需要接受數據的slave來說,沒有影響;但是對於 其他有雙向數據交互的設備,將導致數據錯亂;剛好我也是因爲在使用spi加密芯片時,發現此問題的;

對於雙向交互的設備來說,MT7688只能使用spi-gpio來模擬了,其限制在於時鐘速度沒有硬件SPI快,只適合低速設備。

總結,此問題搞了幾天,也算是一個教訓,光看文檔和代碼是看不出問題的,通過邏輯分析儀直接就可以看出,再結合代碼測試,定位問題所在。因此結合各種調試工具,可以加快分析問題的速度。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章