Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH] phy: phy-can-transceiver: Add suspend operation for tcan1043
@ 2026-06-30 12:28 Thomas Richard (TI)
  2026-06-30 12:40 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Thomas Richard (TI) @ 2026-06-30 12:28 UTC (permalink / raw)
  To: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong
  Cc: Thomas Petazzoni, Gregory CLEMENT, richard.genoud, Udit Kumar,
	Abhash Kumar, linux-can, linux-phy, linux-kernel,
	Thomas Richard (TI)

Add suspend operation for tcan1043. It switches the PHY in Sleep mode, the
lowest power mode of the device. If a bus wake-up pattern or a local
wake-up event occurs, the PHY transitions to Standby mode, set its internal
WAKERQ flag and set the INH output high. In Sleep mode INH is floating.

The WAKERQ flag prevents transition to Go-to-Sleep mode. The only way to
clear it is to switch to Normal mode. So to reach Sleep mode, we firstly
switch to Normal mode, then to Go-to-Sleep mode.

Suspend sequence (PHY is off):

    Standby -> Normal -> Go-to-Sleep -> Sleep

Suspend sequence (PHY is on):

    Normal -> Go-to-Sleep -> Sleep

Signed-off-by: Thomas Richard (TI) <thomas.richard@bootlin.com>
---
 drivers/phy/phy-can-transceiver.c | 56 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 7 deletions(-)

diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index 75dc49e75ca0..2bca1a173fcc 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com
  *
  */
+#include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
@@ -12,25 +13,28 @@
 #include <linux/module.h>
 #include <linux/mux/consumer.h>
 
+struct can_transceiver_phy {
+	struct phy *generic_phy;
+	struct gpio_desc *silent_gpio;
+	struct gpio_desc *standby_gpio;
+	struct gpio_desc *enable_gpio;
+	struct can_transceiver_priv *priv;
+};
+
 struct can_transceiver_data {
 	u32 flags;
 #define CAN_TRANSCEIVER_STB_PRESENT	BIT(0)
 #define CAN_TRANSCEIVER_EN_PRESENT	BIT(1)
 #define CAN_TRANSCEIVER_DUAL_CH		BIT(2)
 #define CAN_TRANSCEIVER_SILENT_PRESENT	BIT(3)
+	int	(*suspend)(struct can_transceiver_phy *phy);
 };
 
-struct can_transceiver_phy {
-	struct phy *generic_phy;
-	struct gpio_desc *silent_gpio;
-	struct gpio_desc *standby_gpio;
-	struct gpio_desc *enable_gpio;
-	struct can_transceiver_priv *priv;
-};
 
 struct can_transceiver_priv {
 	struct mux_state *mux_state;
 	int num_ch;
+	const struct can_transceiver_data *data;
 	struct can_transceiver_phy can_transceiver_phy[] __counted_by(num_ch);
 };
 
@@ -76,12 +80,28 @@ static const struct phy_ops can_transceiver_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
+static int tcan1043_suspend(struct can_transceiver_phy *phy)
+{
+	/* Switch to Normal mode, it clears WAKERQ */
+	gpiod_set_value_cansleep(phy->standby_gpio, 0);
+	gpiod_set_value_cansleep(phy->enable_gpio, 1);
+
+	/* Switch to Go-to-Sleep mode */
+	gpiod_set_value_cansleep(phy->standby_gpio, 1);
+
+	/* Wait transition to Sleep mode */
+	fsleep(5);
+
+	return 0;
+}
+
 static const struct can_transceiver_data tcan1042_drvdata = {
 	.flags = CAN_TRANSCEIVER_STB_PRESENT,
 };
 
 static const struct can_transceiver_data tcan1043_drvdata = {
 	.flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT,
+	.suspend = tcan1043_suspend,
 };
 
 static const struct can_transceiver_data tja1048_drvdata = {
@@ -115,6 +135,26 @@ static struct phy *can_transceiver_phy_xlate(struct device *dev,
 	return priv->can_transceiver_phy[idx].generic_phy;
 }
 
+static int can_transceiver_phy_suspend(struct device *dev)
+{
+	struct can_transceiver_priv *priv = dev_get_drvdata(dev);
+	const struct can_transceiver_data *data = priv->data;
+	int ret, i;
+
+	for (i = 0; i < priv->num_ch; i++) {
+		if (data->suspend) {
+			ret = data->suspend(&priv->can_transceiver_phy[i]);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(can_transceiver_phy_pm_ops,
+				can_transceiver_phy_suspend, NULL);
+
 static int can_transceiver_phy_probe(struct platform_device *pdev)
 {
 	struct phy_provider *phy_provider;
@@ -145,6 +185,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	priv->num_ch = num_ch;
+	priv->data = drvdata;
 	platform_set_drvdata(pdev, priv);
 
 	mux_state = devm_mux_state_get_optional(dev, NULL);
@@ -247,6 +288,7 @@ static struct platform_driver can_transceiver_phy_driver = {
 	.driver = {
 		.name = "can-transceiver-phy",
 		.of_match_table = can_transceiver_phy_ids,
+		.pm = pm_sleep_ptr(&can_transceiver_phy_pm_ops),
 	},
 };
 

---
base-commit: a25d586f7f9e7f22e7e0350053c15924d28ed6ee
change-id: 20260618-phy-can-transceiver-suspend-resume-support-de9f32852fb8

Best regards,
-- 
Thomas Richard (TI) <thomas.richard@bootlin.com>


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-06-30 12:40 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-30 12:28 [PATCH] phy: phy-can-transceiver: Add suspend operation for tcan1043 Thomas Richard (TI)
2026-06-30 12:40 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox