针对MCIMX6G2CVM05AB imx6ul启动时CAN偶发通信失败的问题,以下是逐步解决方案:
1. 增加超时和延迟
在flexcan_chip_disable中延长等待时间,确保硬件有足够时间响应。
修改驱动代码(flexcan.c):
static void flexcan_chip_disable(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->regs;
u32 mcr;
int timeout = 1000; // 将超时次数从默认值(如100)增加到1000
mcr = flexcan_read(®s->mcr);
mcr |= FLEXCAN_MCR_MDIS;
flexcan_write(mcr, ®s->mcr);
while (timeout-- > 0) {
mcr = flexcan_read(®s->mcr);
if (mcr & FLEXCAN_MCR_LPM_ACK)
break;
udelay(20); // 增加每次循环的延迟,如从10us调整到20us
}
if (timeout <= 0)
dev_err(priv->dev, "FlexCAN: Disable timeout, MCR=0x%08xn", mcr);
// 其他清理代码...
}
2. 添加内存屏障
确保寄存器写入操作完成后再读取状态。
在写入MCR后添加内存屏障:
mcr |= FLEXCAN_MCR_MDIS;
flexcan_write(mcr, ®s->mcr);
wmb(); // 插入写内存屏障,确保写入完成
3. 检查时钟配置
确认禁用期间时钟未被关闭。在设备树中检查CAN时钟配置,确保没有启用过早关闭时钟的电源管理策略。
设备树检查:
&can1 {
clocks = <&clks IMX6UL_CLK_CAN1_IPG>,
<&clks IMX6UL_CLK_CAN1_SERIAL>;
clock-names = "ipg", "per";
// 确保没有使用不合理的时钟门控配置
};
4. 强制复位模块
在初始化前执行硬件复位,确保模块状态正确。
在驱动初始化函数中添加复位:
static int flexcan_probe(struct platform_device *pdev)
{
// ...其他初始化代码
reset_control_assert(priv->rst); // 断言复位
udelay(10);
reset_control_deassert(priv->rst); // 释放复位
// 继续初始化...
}
5. 查阅勘误表及更新驱动
检查i.MX6UL的芯片勘误表,确认是否存在FlexCAN问题。例如,NXP可能建议在禁用模块前执行特定操作。
参考勘误表建议:
- 确保在进入MDIS模式前停止所有CAN传输。
- 禁用中断并清空接收缓冲区。
6. 调试信息增强
在失败时记录更多寄存器状态,帮助定位问题。
添加调试输出:
if (timeout <= 0) {
dev_err(priv->dev, "FlexCAN disable failed, MCR=0x%08x CTRL=0x%08xn",
flexcan_read(®s->mcr), flexcan_read(®s->ctrl));
}
7. 电源和复位信号检查
使用示波器测量CAN控制器的电源和复位信号:
- 电源纹波:确保在启动期间VDD_CAN稳定,无跌落。
- 复位信号:检查复位引脚在启动时是否有毛刺或延迟。
8. 热复位 vs 冷复位测试
通过软件复位与断电重启对比,确定是否与电源周期相关:
echo 1 > /sys/class/remoteproc/remoteproc0/recovery # 触发软件复位(示例)
9. 驱动清理流程
确保在模块禁用时彻底释放资源,避免残留状态影响后续初始化:
- 关闭所有CAN中断。
- 清空接收/发送FIFO。
- 释放DMA通道(如果使用)。
10. 更新驱动版本
检查NXP官方Linux内核的最新flexcan驱动版本,合并修复补丁。例如,某些版本可能已修复LPM_ACK检测问题。
总结:通过增加超时、优化时序、确保时钟稳定、强制复位和增强调试,可解决大部分偶发性禁用失败问题。若问题仍存,需结合硬件信号分析和芯片勘误表进一步排查。建议优先实施步骤1、2、4,并在失败时捕获寄存器状态以缩小问题范围。
针对MCIMX6G2CVM05AB imx6ul启动时CAN偶发通信失败的问题,以下是逐步解决方案:
1. 增加超时和延迟
在flexcan_chip_disable中延长等待时间,确保硬件有足够时间响应。
修改驱动代码(flexcan.c):
static void flexcan_chip_disable(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->regs;
u32 mcr;
int timeout = 1000; // 将超时次数从默认值(如100)增加到1000
mcr = flexcan_read(®s->mcr);
mcr |= FLEXCAN_MCR_MDIS;
flexcan_write(mcr, ®s->mcr);
while (timeout-- > 0) {
mcr = flexcan_read(®s->mcr);
if (mcr & FLEXCAN_MCR_LPM_ACK)
break;
udelay(20); // 增加每次循环的延迟,如从10us调整到20us
}
if (timeout <= 0)
dev_err(priv->dev, "FlexCAN: Disable timeout, MCR=0x%08xn", mcr);
// 其他清理代码...
}
2. 添加内存屏障
确保寄存器写入操作完成后再读取状态。
在写入MCR后添加内存屏障:
mcr |= FLEXCAN_MCR_MDIS;
flexcan_write(mcr, ®s->mcr);
wmb(); // 插入写内存屏障,确保写入完成
3. 检查时钟配置
确认禁用期间时钟未被关闭。在设备树中检查CAN时钟配置,确保没有启用过早关闭时钟的电源管理策略。
设备树检查:
&can1 {
clocks = <&clks IMX6UL_CLK_CAN1_IPG>,
<&clks IMX6UL_CLK_CAN1_SERIAL>;
clock-names = "ipg", "per";
// 确保没有使用不合理的时钟门控配置
};
4. 强制复位模块
在初始化前执行硬件复位,确保模块状态正确。
在驱动初始化函数中添加复位:
static int flexcan_probe(struct platform_device *pdev)
{
// ...其他初始化代码
reset_control_assert(priv->rst); // 断言复位
udelay(10);
reset_control_deassert(priv->rst); // 释放复位
// 继续初始化...
}
5. 查阅勘误表及更新驱动
检查i.MX6UL的芯片勘误表,确认是否存在FlexCAN问题。例如,NXP可能建议在禁用模块前执行特定操作。
参考勘误表建议:
- 确保在进入MDIS模式前停止所有CAN传输。
- 禁用中断并清空接收缓冲区。
6. 调试信息增强
在失败时记录更多寄存器状态,帮助定位问题。
添加调试输出:
if (timeout <= 0) {
dev_err(priv->dev, "FlexCAN disable failed, MCR=0x%08x CTRL=0x%08xn",
flexcan_read(®s->mcr), flexcan_read(®s->ctrl));
}
7. 电源和复位信号检查
使用示波器测量CAN控制器的电源和复位信号:
- 电源纹波:确保在启动期间VDD_CAN稳定,无跌落。
- 复位信号:检查复位引脚在启动时是否有毛刺或延迟。
8. 热复位 vs 冷复位测试
通过软件复位与断电重启对比,确定是否与电源周期相关:
echo 1 > /sys/class/remoteproc/remoteproc0/recovery # 触发软件复位(示例)
9. 驱动清理流程
确保在模块禁用时彻底释放资源,避免残留状态影响后续初始化:
- 关闭所有CAN中断。
- 清空接收/发送FIFO。
- 释放DMA通道(如果使用)。
10. 更新驱动版本
检查NXP官方Linux内核的最新flexcan驱动版本,合并修复补丁。例如,某些版本可能已修复LPM_ACK检测问题。
总结:通过增加超时、优化时序、确保时钟稳定、强制复位和增强调试,可解决大部分偶发性禁用失败问题。若问题仍存,需结合硬件信号分析和芯片勘误表进一步排查。建议优先实施步骤1、2、4,并在失败时捕获寄存器状态以缩小问题范围。
举报