am62 requires a wakeup flag being set in pinctrl when mcan pins acts as
a wakeup source. Add support to select the wakeup state if WOL is
enabled.
Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
---
drivers/net/can/m_can/m_can.c | 69 +++++++++++++++++++++++++++++++++++++++++--
drivers/net/can/m_can/m_can.h | 3 ++
2 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index e08fae5ddf5efa8345670dd50d50954ec5d52b29..a1fa4b2f6b6cc94e5e10259cca53bd931ab238c8 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -2249,7 +2249,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 = {
@@ -2377,6 +2396,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_setup_optional_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)
{
@@ -2418,7 +2473,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_setup_optional_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);
@@ -2533,7 +2596,8 @@ int m_can_class_suspend(struct device *dev)
m_can_clk_stop(cdev);
}
- pinctrl_pm_select_sleep_state(dev);
+ if (!m_can_class_wakeup_pinctrl_enabled(cdev))
+ pinctrl_pm_select_sleep_state(dev);
cdev->can.state = CAN_STATE_SLEEPING;
@@ -2547,7 +2611,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);
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index bd4746c63af3f0a032910644dfd48a9ebb3a6168..583c7f1d005d61b3fc8587697388522993ff11a8 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -128,6 +128,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);
--
2.50.1
On Aug 20, 2025 at 14:42:28 +0200, Markus Schneider-Pargmann wrote:
> am62 requires a wakeup flag being set in pinctrl when mcan pins acts as
Let's call it "TI AM62x SoC" or TI K3 SoCs? This commit goes into a driver so let's not assume
everyone knows what am62 means ;)
Also nit: s/"mcan pins acts"/"mcan pins act"/
> a wakeup source. Add support to select the wakeup state if WOL is
> enabled.
>
> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
> ---
> drivers/net/can/m_can/m_can.c | 69 +++++++++++++++++++++++++++++++++++++++++--
> drivers/net/can/m_can/m_can.h | 3 ++
> 2 files changed, 70 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> index e08fae5ddf5efa8345670dd50d50954ec5d52b29..a1fa4b2f6b6cc94e5e10259cca53bd931ab238c8 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -2249,7 +2249,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 = {
> @@ -2377,6 +2396,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_setup_optional_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)
> {
> @@ -2418,7 +2473,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_setup_optional_pinctrl(class_dev);
optional makes it sound a little confusing IMO, might make sense to call
it something like m_can_class_configure_pinctrl or m_can_class_setup_wakeup_pinctrl
> + 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);
>
> @@ -2533,7 +2596,8 @@ int m_can_class_suspend(struct device *dev)
> m_can_clk_stop(cdev);
> }
>
> - pinctrl_pm_select_sleep_state(dev);
> + if (!m_can_class_wakeup_pinctrl_enabled(cdev))
> + pinctrl_pm_select_sleep_state(dev);
>
> cdev->can.state = CAN_STATE_SLEEPING;
>
> @@ -2547,7 +2611,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);
>
> cdev->can.state = CAN_STATE_ERROR_ACTIVE;
>
> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> index bd4746c63af3f0a032910644dfd48a9ebb3a6168..583c7f1d005d61b3fc8587697388522993ff11a8 100644
> --- a/drivers/net/can/m_can/m_can.h
> +++ b/drivers/net/can/m_can/m_can.h
> @@ -128,6 +128,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);
>
> --
> 2.50.1
>
--
Best regards,
Dhruva Gole
Texas Instruments Incorporated
On 9/4/25 04:05, Dhruva Gole wrote:
> On Aug 20, 2025 at 14:42:28 +0200, Markus Schneider-Pargmann wrote:
>> am62 requires a wakeup flag being set in pinctrl when mcan pins acts as
>
> Let's call it "TI AM62x SoC" or TI K3 SoCs? This commit goes into a driver so let's not assume
> everyone knows what am62 means ;)
>
> Also nit: s/"mcan pins acts"/"mcan pins act"/
>
>> a wakeup source. Add support to select the wakeup state if WOL is
>> enabled.
>>
>> Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
>> ---
>> drivers/net/can/m_can/m_can.c | 69 +++++++++++++++++++++++++++++++++++++++++--
>> drivers/net/can/m_can/m_can.h | 3 ++
>> 2 files changed, 70 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>> index e08fae5ddf5efa8345670dd50d50954ec5d52b29..a1fa4b2f6b6cc94e5e10259cca53bd931ab238c8 100644
>> --- a/drivers/net/can/m_can/m_can.c
>> +++ b/drivers/net/can/m_can/m_can.c
>> @@ -2249,7 +2249,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 = {
>> @@ -2377,6 +2396,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_setup_optional_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)
>> {
>> @@ -2418,7 +2473,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_setup_optional_pinctrl(class_dev);
>
> optional makes it sound a little confusing IMO, might make sense to call
> it something like m_can_class_configure_pinctrl or m_can_class_setup_wakeup_pinctrl
I agree it should be changed to something different. Maybe you could add
a comment or add in the commit message denoting that configuring the
pinctrl is optional?
>
>> + 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);
>>
>> @@ -2533,7 +2596,8 @@ int m_can_class_suspend(struct device *dev)
>> m_can_clk_stop(cdev);
>> }
>>
>> - pinctrl_pm_select_sleep_state(dev);
>> + if (!m_can_class_wakeup_pinctrl_enabled(cdev))
>> + pinctrl_pm_select_sleep_state(dev);
>>
>> cdev->can.state = CAN_STATE_SLEEPING;
>>
>> @@ -2547,7 +2611,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);
>>
>> cdev->can.state = CAN_STATE_ERROR_ACTIVE;
>>
>> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
>> index bd4746c63af3f0a032910644dfd48a9ebb3a6168..583c7f1d005d61b3fc8587697388522993ff11a8 100644
>> --- a/drivers/net/can/m_can/m_can.h
>> +++ b/drivers/net/can/m_can/m_can.h
>> @@ -128,6 +128,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);
>>
>> --
>> 2.50.1
>>
>
© 2016 - 2026 Red Hat, Inc.