这个玩意开始单纯的以为默认支持所有的工作模式,都知道它有两个片选,默认CS0是用作flash不作变动,我们操作就只有操作CS1, 先前一顿折腾都没出来,用自带的SPI-TEST函数去测试,一直报错无效参数无效参数,人都麻了。后来无可奈何,转到内核中去添加打印信息一步一步找错看错在那里,(注意:添加内核模块看打印信息,在内核配置菜单中选为M,我开始一直是*,弄半天没打印信息),后来打印纠错发现是默认MT7688中他喵的只支持半双工工作模式,最离谱的是我去问商家售后技术,他还说支持全双工,我把驱动函数发出去,他哑口无言,说需要自己配置修改,我就开始了我的死磕之路。今天刚弄出来,太开心了,具体一些后续补充,先上我的核心代码。
spi半双工的代码在openwrt-21.02/build_dir/target-mipsel_24kc_musl/linux-ramips_mt76x8/linux-5.4.171/drivers/spi目录下的 spi-mt7621.c,每人的板子及源码可能不同需要自己寻找,它原先自带的主要看 transfer_one_message函数(自带的):
static int mt7621_spi_transfer_one_message(struct spi_controller *master,
struct spi_message *m)
{
struct mt7621_spi *rs = spi_controller_get_devdata(master);
struct spi_device *spi = m->spi;
unsigned int speed = spi->max_speed_hz;
struct spi_transfer *t = NULL;
int status = 0;
mt7621_spi_wait_till_ready(rs);
list_for_each_entry(t, &m->transfers, transfer_list)
if (t->speed_hz < speed)
speed = t->speed_hz;
if (mt7621_spi_prepare(spi, speed)) {
status = -EIO;
goto msg_done;
}
/* Assert CS */
mt7621_spi_set_cs(spi, 1);
m->actual_length = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
if ((t->rx_buf) && (t->tx_buf)) {
/*
* This controller will shift some extra data out
* of spi_opcode if (mosi_bit_cnt > 0) &&
* (cmd_bit_cnt == 0). So the claimed full-duplex
* support is broken since we have no way to read
* the MISO value during that bit.
*/
status = -EIO;
goto msg_done;
} else if (t->rx_buf) {
mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf);
} else if (t->tx_buf) {
mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf);
}
m->actual_length += t->len;
}
/* Flush data and deassert CS */
mt7621_spi_flush(rs);
mt7621_spi_set_cs(spi, 0);
msg_done:
m->status = status;
spi_finalize_current_message(master);
return 0;
}
上图代码就是自带的半双工,用来操作flash的CSO,但是这个函数 static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
struct spi_message *m) 是spi发送传递信息的关键,所以把这个函数更改为half_duplex,如下图:
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){
printk("-----> cs = 0 00000<******\n");
return mt7621_spi_transfer_half_duplex(master, m); //可执行half
}
else if(cs == 1){
printk("-----> cs = 1 11111<-----\n"); //调试时的信息,看是否片选为CS1
return mt7621_spi_transfer_full_duplex(master, m);
}
return 0;
}
此时就需要添加full_duplex的函数了,此函数原型参考:https://blog.youkuaiyun.com/zzZhangYiLong/article/details/120697009:,
#define ONETXRX 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); //spi MASTER 模式寄存器第十位 置1,
mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
}
static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
struct spi_message *m)
{
struct mt7621_spi *rs = spi_master_get_devdata(master);
struct spi_device *spi = m->spi;
unsigned int speed = spi->max_speed_hz;
struct spi_transfer *t = NULL;
int status = 0;
int i, j,k,len = 0;
int total=0; //add
int rx_len = 0;
u32 txdata[64] = { 0 };
u32 rxdata[64] = { 0 };
u32 val = 0;
mt7621_spi_reset_full(rs);
mt7621_spi_wait_till_ready(rs);
printk(">>>>> mt7621_spi_transfer_full_duplex <<<<<\n");
list_for_each_entry(t, &m->transfers, transfer_list)
{
const u8 *buf = t->tx_buf;
if (t->rx_buf)
rx_len += t->len;
if (!buf)
continue;
for (i = 0; i < t->len; i++, len++)
txdata[len / 4] |= buf[i] << (8 * (len & 3));
if (speed > t->speed_hz)
speed = t->speed_hz;
}
if (mt7621_spi_prepare(spi, speed)) {
status = -EIO;
goto msg_done;
}
total=len;
while(total>ONETXRX)
{
for (i = 0;i < ONETXRX; i += 4,j += 4)
mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, txdata[j / 4]);
val |= ONETXRX * 8;
val |= (ONETXRX * 8) << 12;
mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
mt7621_spi_write_half_duplex(rs, t->len, t->rx_buf); ///********************/
mt7621_spi_set_cs(spi, 1);
mt7621_spi_reset_full(rs); //add 每个地方都加了这个函数后才能正常操作
val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
mt7621_spi_read_half_duplex(rs, t->len, t->tx_buf); // ********************
val |= SPI_CTL_START;
mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
mt7621_spi_wait_till_ready(rs);
mt7621_spi_set_cs(spi, 0);
mt7621_spi_reset_full(rs); //add
for (i = 0; i < ONETXRX; i += 4,k+=4)
rxdata[k / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
total-=ONETXRX;
}
if(total>0)
{
for (i = 0;i < total; i += 4,j += 4)
mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, txdata[j / 4]);
val |= total * 8;
val |= (total * 8) << 12;
mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
mt7621_spi_set_cs(spi, 1);
mt7621_spi_reset_full(rs); //add
val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
val |= SPI_CTL_START;
mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
mt7621_spi_wait_till_ready(rs);
mt7621_spi_set_cs(spi, 0);
mt7621_spi_reset_full(rs); //add
for (i = 0; i < total; i += 4,k+=4)
rxdata[k / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
}
m->actual_length = rx_len;
len = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
u8 *buf = t->rx_buf;
if (!buf)
continue;
for (i = 0; i < t->len; i++, len++)
buf[i] = rxdata[len / 4] >> (8 * (len & 3));
}
msg_done:
m->status = status;
spi_finalize_current_message(master);
return 0;
}
其中static void mt7621_spi_reset_full(struct mt7621_spi *rs) 这个函数个人觉得至关重要,他将master中的第10位置1了 , 可查看7688datesheet发现,关于半双工和全双工的描述在SPI_MASTER的BIT10,此处图片引用,自己难得去截数据手册的图嘞,MT7688 坑爹的 SPI Master 半双工全双工问题_WindLOR的博客-优快云博客_mt7688 spi
将其置1后才是full duplex mode。突然想到还有一个spi.c中修改地方不确定,我是报错了所以修改后能正常运行:
static int __spi_validate(struct spi_device *spi, struct spi_message *message)
{
struct spi_controller *ctlr = spi->controller;
struct spi_transfer *xfer;
int w_size;
if (list_empty(&message->transfers)) {
printk("---> message->transfers empty\n");
return -EINVAL;
}
/* If an SPI controller does not support toggling the CS line on each
* transfer (indicated by the SPI_CS_WORD flag) or we are using a GPIO
* for the CS line, we can emulate the CS-per-word hardware function by
* splitting transfers into one-word transfers and ensuring that
* cs_change is set for each transfer.
*/
if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
spi->cs_gpiod ||
gpio_is_valid(spi->cs_gpio))) {
size_t maxsize;
int ret;
maxsize = (spi->bits_per_word + 7) / 8;
/* spi_split_transfers_maxsize() requires message->spi */
message->spi = spi;
ret = spi_split_transfers_maxsize(ctlr, message, maxsize,
GFP_KERNEL);
printk("---> spi_split_transfers_maxsize = %d\n", ret);
if (ret)
return ret;
list_for_each_entry(xfer, &message->transfers, transfer_list) {
/* don't change cs_change on the last entry in the list */
if (list_is_last(&xfer->transfer_list, &message->transfers))
break;
xfer->cs_change = 1;
}
}
/* Half-duplex links include original MicroWire, and ones with
* only one data pin like SPI_3WIRE (switches direction) or where
* either MOSI or MISO is missing. They can also be caused by
* software limitations.
*/
//printk("--> ctlr->flags=0x%x\n", ctlr->flags);
if ((ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX) ||
(spi->mode & SPI_3WIRE)) {
unsigned flags = ctlr->flags;
list_for_each_entry(xfer, &message->transfers, transfer_list) {
/*if (xfer->rx_buf && xfer->tx_buf) {
printk("--> xfer->rx_buf && xfer->tx_buf\n");
return -EINVAL;
}*/ 我运行时这里会报错,屏蔽后执行全双工没问题
if ((flags & SPI_CONTROLLER_NO_TX) && xfer->tx_buf) {
printk("--> (flags & SPI_CONTROLLER_NO_TX) && xfer->tx_buf\n");
return -EINVAL;
}
if ((flags & SPI_CONTROLLER_NO_RX) && xfer->rx_buf) {
printk("--> (flags & SPI_CONTROLLER_NO_RX) && xfer->rx_buf\n");
return -EINVAL;
}
}
}
至此,大致的一个修改步骤及内容到此结束,期间真的花费了很多时间和精力,如果有人也在调试openwrt和lora设备的,会发现网上的资源真的很少很少,找到也不全,所以自己有成功的案例分享出来仅供参考,希望能给大家提供一些帮助,我大部分的参考博客即为文字两个链接,内网外网找一堆,就这两个有用,最终也是自己综合起来也算死磕出来了,因为自己说实话是根本不咋懂驱动函数的,第一次做这个东西。
今天刚调试出结果就想着分享出来给大家,自己知道这个有多烦人,可能有些细节没写到,后面会完善了,不过关键核心代码我是贴出了,如果有帮助,希望不仅浏览,再点个赞,给我一点鼓励,如果那里写的有问题欢迎评论探讨指教,谢谢大家~!