]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
can: m_can: Support pinctrl wakeup state
authorMarkus Schneider-Pargmann (TI.com) <msp@baylibre.com>
Wed, 1 Oct 2025 14:30:22 +0000 (16:30 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Fri, 17 Oct 2025 09:02:28 +0000 (11:02 +0200)
TI AM62x SoC requires a wakeup flag being set in pinctrl when mcan pins
act as a wakeup source. Add support to select the wakeup state if WOL
is enabled.

Signed-off-by: Markus Schneider-Pargmann (TI.com) <msp@baylibre.com>
Link: https://patch.msgid.link/20251001-topic-mcan-wakeup-source-v6-12-v10-4-4ab508ac5d1e@baylibre.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/m_can/m_can.c
drivers/net/can/m_can/m_can.h

index 10b5862b488018fc10b139d8717c0bb6155f3eb3..8569596ae830a62553bc866d4ed781b4e7748db0 100644 (file)
@@ -2265,7 +2265,26 @@ static int m_can_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
                return ret;
        }
 
+       if (!IS_ERR_OR_NULL(cdev->pinctrl_state_wakeup)) {
+               if (wol_enable)
+                       ret = pinctrl_select_state(cdev->pinctrl, cdev->pinctrl_state_wakeup);
+               else
+                       ret = pinctrl_pm_select_default_state(cdev->dev);
+
+               if (ret) {
+                       netdev_err(cdev->net, "Failed to select pinctrl state %pE\n",
+                                  ERR_PTR(ret));
+                       goto err_wakeup_enable;
+               }
+       }
+
        return 0;
+
+err_wakeup_enable:
+       /* Revert wakeup enable */
+       device_set_wakeup_enable(cdev->dev, !wol_enable);
+
+       return ret;
 }
 
 static const struct ethtool_ops m_can_ethtool_ops_coalescing = {
@@ -2393,6 +2412,42 @@ int m_can_class_get_clocks(struct m_can_classdev *cdev)
 }
 EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
 
+static bool m_can_class_wakeup_pinctrl_enabled(struct m_can_classdev *class_dev)
+{
+       return device_may_wakeup(class_dev->dev) && class_dev->pinctrl_state_wakeup;
+}
+
+static int m_can_class_parse_pinctrl(struct m_can_classdev *class_dev)
+{
+       struct device *dev = class_dev->dev;
+       int ret;
+
+       class_dev->pinctrl = devm_pinctrl_get(dev);
+       if (IS_ERR(class_dev->pinctrl)) {
+               ret = PTR_ERR(class_dev->pinctrl);
+               class_dev->pinctrl = NULL;
+
+               if (ret == -ENODEV)
+                       return 0;
+
+               return dev_err_probe(dev, ret, "Failed to get pinctrl\n");
+       }
+
+       class_dev->pinctrl_state_wakeup =
+               pinctrl_lookup_state(class_dev->pinctrl, "wakeup");
+       if (IS_ERR(class_dev->pinctrl_state_wakeup)) {
+               ret = PTR_ERR(class_dev->pinctrl_state_wakeup);
+               class_dev->pinctrl_state_wakeup = NULL;
+
+               if (ret == -ENODEV)
+                       return 0;
+
+               return dev_err_probe(dev, ret, "Failed to lookup pinctrl wakeup state\n");
+       }
+
+       return 0;
+}
+
 struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
                                                int sizeof_priv)
 {
@@ -2434,7 +2489,15 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
        m_can_of_parse_mram(class_dev, mram_config_vals);
        spin_lock_init(&class_dev->tx_handling_spinlock);
 
+       ret = m_can_class_parse_pinctrl(class_dev);
+       if (ret)
+               goto err_free_candev;
+
        return class_dev;
+
+err_free_candev:
+       free_candev(net_dev);
+       return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
 
@@ -2563,7 +2626,8 @@ int m_can_class_suspend(struct device *dev)
                cdev->can.state = CAN_STATE_SLEEPING;
        }
 
-       pinctrl_pm_select_sleep_state(dev);
+       if (!m_can_class_wakeup_pinctrl_enabled(cdev))
+               pinctrl_pm_select_sleep_state(dev);
 
        return ret;
 }
@@ -2575,7 +2639,8 @@ int m_can_class_resume(struct device *dev)
        struct net_device *ndev = cdev->net;
        int ret = 0;
 
-       pinctrl_pm_select_default_state(dev);
+       if (!m_can_class_wakeup_pinctrl_enabled(cdev))
+               pinctrl_pm_select_default_state(dev);
 
        if (netif_running(ndev)) {
                ret = m_can_clk_start(cdev);
index 7b7600697c6bc52f821730d0a373adb713155831..f2f89687bbd2118eb800ec21a05a7b9aeb201f45 100644 (file)
@@ -129,6 +129,9 @@ struct m_can_classdev {
        struct mram_cfg mcfg[MRAM_CFG_NUM];
 
        struct hrtimer hrtimer;
+
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *pinctrl_state_wakeup;
 };
 
 struct m_can_classdev *m_can_class_allocate_dev(struct device *dev, int sizeof_priv);