linux6.1 配置mcp2518fd驱动

说明

mcp2518fd适配,它是一个spi转can芯片。芯片勘误上说是spi速率最多10m,不然内部存储会有问题。

硬件

只用一个中断脚的硬件原理图配置:

alt text

三个中断脚都用的硬件原理图配置:

alt text

步骤

开启设备树和内核选项。

&spi0 {
       status = "okay";
       max-freq = <5000000>;
       pinctrl-names = "default";
       pinctrl-0 = <&spi0m0_csn0 &spi0m0_pins>;

    mcp251xfd_0: mcp251xfd_0@0 {
        status = "okay";
               compatible = "microchip,mcp2518fd";
               reg = <0>;
               pinctrl-names = "default";
               pinctrl-0 = <&pinctrl_mcp251xfd>;
               clocks = <&spi_clk_mcp251xfd_40M>;
               spi-max-frequency = <5000000>;   // 原来是 10000000
               interrupts-extended = <&gpio0 RK_PD3 IRQ_TYPE_LEVEL_LOW>;
               // 用RX中断脚的情况
               // microchip,rx-int-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_LOW>;
               vdd-supply = <&vcc_ext3v3_ext5v0>;
               xceiver-supply = <&vcc_ext3v3_ext5v0>;
    };

};

这样其实就可以了,但是为了在只用一个中断脚的情况下防止那两个中断脚有问题,可以直接配置为GPIO。

index 185202bad4cd..a1d4ff823fb3 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -592,8 +592,15 @@ static int mcp251xfd_chip_rx_int_enable(const struct mcp251xfd_priv *priv)
 {
        u32 val;

-       if (!priv->rx_int)
-               return 0;
+       if (!priv->rx_int) {
+               /* 未使用 RX-INT GPIO 时,也要把 PIN0/PIN1 配置为 GPIO 输入,
+                * 避免它们停留在复位默认的特殊功能模式(SOF/RXINT)。
+                * 否则 RX 中断可能被分流到 GPIO1,导致 nINT 收不到 RX 中断。
+                */
+               val = MCP251XFD_REG_IOCON_PM1 | MCP251XFD_REG_IOCON_PM0 |
+                       MCP251XFD_REG_IOCON_TRIS1 | MCP251XFD_REG_IOCON_TRIS0;
+               return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
+       }

        /* Configure GPIOs:
         * - PIN0: GPIO Input
(END)

解决spi转can的can不插连接线然后直接发送,内核中断报错的问题

diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
index 1665f78abb5c..185202bad4cd 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -25,6 +25,10 @@

 #define DEVICE_NAME "mcp251xfd"

+/* IRQ 错误恢复:连续恢复失败退避上限,超过后停止自动恢复,
+ * 防止 SPI 恢复风暴打满 CPU(见 mcp251xfd_irq 的 out_fail 路径) */
+#define MCP251XFD_RECOVER_RETRY_MAX 10
+
 static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp2517fd = {
        .quirks = MCP251XFD_QUIRK_MAB_NO_WARN | MCP251XFD_QUIRK_CRC_REG |
                MCP251XFD_QUIRK_CRC_RX | MCP251XFD_QUIRK_CRC_TX |
@@ -1474,7 +1478,16 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)

                if (!(intf_pending)) {
                        can_rx_offload_threaded_irq_finish(&priv->offload);
-                       return handled;
+
+                       /* 虚假中断:SPI 读正常但芯片没有待处理的中断
+                        * (INT 引脚毛刺/电平噪声,常见于 SPI 传输期间)。
+                        * 返回 IRQ_HANDLED,防止 spurious 计数累积导致
+                        * "nobody cared" 禁用中断。真中断下次仍会触发。
+                        */
+                       dev_info_once(priv->ndev->dev.parent,
+                                     "spurious IRQ: no pending ints (intf=0x%08x).\n",
+                                     priv->regs_status.intf);
+                       return IRQ_HANDLED;
                }

                /* Some interrupts must be ACKed in the
@@ -1582,10 +1595,47 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)
        netdev_err(priv->ndev, "IRQ handler returned %d (intf=0x%08x).\n",
                   err, priv->regs_status.intf);
        mcp251xfd_dump(priv);
-       mcp251xfd_chip_interrupts_disable(priv);
-       mcp251xfd_timestamp_stop(priv);

-       return handled;
+       /* SPI 通信失败时,芯片的中断标志可能无法清除,INT 引脚会一直
+        * 保持低电平,导致 GPIO 电平中断风暴;而每次都返回 IRQ_NONE
+        * 会让内核累积 spurious 计数,最终 "nobody cared" 禁用中断。
+        * 这里软复位芯片恢复 SPI 状态机,并返回 IRQ_HANDLED 保住中断。
+        *
+        * 连续失败超过上限后停止自动恢复(总线持续异常时避免 SPI
+        * 恢复风暴打满 CPU),仅保住中断不被禁用。
+        */
+       if (priv->recover_fail_cnt >= MCP251XFD_RECOVER_RETRY_MAX)
+               return IRQ_HANDLED;
+
+       err = mcp251xfd_chip_start(priv);
+       if (!err) {
+               int i;
+
+               /* chip_start() 已重置 TX/RX ring,旧的 echo skb 不再有
+                * 对应的 TEF 条目,必须全部释放,防止 skb 泄漏和
+                * can_get_echo_skb() 统计/释放错乱。
+                */
+               for (i = 0; i < priv->tx->obj_num; i++)
+                       can_free_echo_skb(priv->ndev, i, NULL);
+
+               err = mcp251xfd_chip_interrupts_enable(priv);
+       }
+       if (err) {
+               priv->recover_fail_cnt++;
+               netdev_err(priv->ndev,
+                          "Failed to recover chip after IRQ error (%d, cnt=%u).\n",
+                          err, priv->recover_fail_cnt);
+
+               /* 恢复失败:停队列并上报上层状态,避免"假运行" */
+               can_change_state(priv->ndev, NULL, CAN_STATE_BUS_OFF,
+                                CAN_STATE_BUS_OFF);
+               netif_stop_queue(priv->ndev);
+       } else {
+               priv->recover_fail_cnt = 0;
+               netif_wake_queue(priv->ndev);
+       }
+
+       return IRQ_HANDLED;
 }

 static int mcp251xfd_open(struct net_device *ndev)
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
index b98ded7098a5..376b088010da 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
@@ -659,6 +659,17 @@ struct mcp251xfd_priv {

        struct mcp251xfd_devtype_data devtype_data;
        struct can_berr_counter bec;
+
+       /* IRQ 错误恢复:连续失败计数(超过 MCP251XFD_RECOVER_RETRY_MAX
+        * 后停止自动恢复,防止恢复风暴)。
+        *
+        * 并发边界:恢复路径只在 mcp251xfd_irq(threaded)中执行,而
+        * mcp251xfd_open() 在 request_threaded_irq() 之前完成 chip_start,
+        * mcp251xfd_stop() 在 chip_stop() 之前 free_irq(),因此与
+        * open/stop 天然互斥。若将来启用 restart-ms(can_restart → do_set_mode
+        * → chip_start),需在此加锁串行化。
+        */
+       u8 recover_fail_cnt;
 };

 #define MCP251XFD_IS(_model) \
posted @ 2026-08-22 14:20  杨旭0324  阅读(25)  评论(0)    收藏  举报