netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4] can: m_can: don't enable transceiver when probing
@ 2024-06-07 10:52 Martin Hundebøll
  2024-06-12 12:07 ` Markus Schneider-Pargmann
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Martin Hundebøll @ 2024-06-07 10:52 UTC (permalink / raw)
  To: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Markus Schneider-Pargmann, Martin Hundebøll, linux-can,
	netdev, linux-kernel

The m_can driver sets and clears the CCCR.INIT bit during probe (both
when testing the NON-ISO bit, and when configuring the chip). After
clearing the CCCR.INIT bit, the transceiver enters normal mode, where it
affects the CAN bus (i.e. it ACKs frames). This can cause troubles when
the m_can node is only used for monitoring the bus, as one cannot setup
listen-only mode before the device is probed.

Rework the probe flow, so that the CCCR.INIT bit is only cleared when
upping the device. First, the tcan4x5x driver is changed to stay in
standby mode during/after probe. This in turn requires changes when
setting bits in the CCCR register, as its CSR and CSA bits are always
high in standby mode.

Signed-off-by: Martin Hundebøll <martin@geanix.com>
---

Changes since v3:
 * Return 'niso' instead of 'err' in case of failure to detect niso
   support in m_can_dev_setup()
 * Fix typo: redunant -> redundant

Changes since v2:
 * Return and propagate error(s) from m_can_cccr_update_bits()

Changes since v1:
 * Implement Markus review comments:
   - Rename m_can_cccr_wait_bits() to m_can_cccr_update_bits()
   - Explicitly set CCCR_INIT bit in m_can_dev_setup()
   - Revert to 5 timeouts/tries to 10
   - Use m_can_config_{en|dis}able() in m_can_niso_supported()
   - Revert move of call to m_can_enable_all_interrupts()
   - Return -EBUSY on failure to enter normal mode
   - Use tcan4x5x_clear_interrupts() in tcan4x5x_can_probe()

 drivers/net/can/m_can/m_can.c         | 169 ++++++++++++++++----------
 drivers/net/can/m_can/tcan4x5x-core.c |  13 +-
 2 files changed, 116 insertions(+), 66 deletions(-)

diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 14b231c4d7ec..7f63f866083e 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -379,38 +379,72 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val)
 	return cdev->ops->read_fifo(cdev, addr_offset, val, 1);
 }
 
-static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable)
+static int m_can_cccr_update_bits(struct m_can_classdev *cdev, u32 mask, u32 val)
 {
-	u32 cccr = m_can_read(cdev, M_CAN_CCCR);
-	u32 timeout = 10;
-	u32 val = 0;
-
-	/* Clear the Clock stop request if it was set */
-	if (cccr & CCCR_CSR)
-		cccr &= ~CCCR_CSR;
-
-	if (enable) {
-		/* enable m_can configuration */
-		m_can_write(cdev, M_CAN_CCCR, cccr | CCCR_INIT);
-		udelay(5);
-		/* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */
-		m_can_write(cdev, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE);
-	} else {
-		m_can_write(cdev, M_CAN_CCCR, cccr & ~(CCCR_INIT | CCCR_CCE));
+	u32 val_before = m_can_read(cdev, M_CAN_CCCR);
+	u32 val_after = (val_before & ~mask) | val;
+	size_t tries = 10;
+
+	if (!(mask & CCCR_INIT) && !(val_before & CCCR_INIT)) {
+		dev_err(cdev->dev,
+			"refusing to configure device when in normal mode\n");
+		return -EBUSY;
 	}
 
-	/* there's a delay for module initialization */
-	if (enable)
-		val = CCCR_INIT | CCCR_CCE;
-
-	while ((m_can_read(cdev, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
-		if (timeout == 0) {
-			netdev_warn(cdev->net, "Failed to init module\n");
-			return;
-		}
-		timeout--;
-		udelay(1);
+	/* The chip should be in standby mode when changing the CCCR register,
+	 * and some chips set the CSR and CSA bits when in standby. Furthermore,
+	 * the CSR and CSA bits should be written as zeros, even when they read
+	 * ones.
+	 */
+	val_after &= ~(CCCR_CSR | CCCR_CSA);
+
+	while (tries--) {
+		u32 val_read;
+
+		/* Write the desired value in each try, as setting some bits in
+		 * the CCCR register require other bits to be set first. E.g.
+		 * setting the NISO bit requires setting the CCE bit first.
+		 */
+		m_can_write(cdev, M_CAN_CCCR, val_after);
+
+		val_read = m_can_read(cdev, M_CAN_CCCR) & ~(CCCR_CSR | CCCR_CSA);
+
+		if (val_read == val_after)
+			return 0;
+
+		usleep_range(1, 5);
 	}
+
+	return -ETIMEDOUT;
+}
+
+static int m_can_config_enable(struct m_can_classdev *cdev)
+{
+	int err;
+
+	/* CCCR_INIT must be set in order to set CCCR_CCE, but access to
+	 * configuration registers should only be enabled when in standby mode,
+	 * where CCCR_INIT is always set.
+	 */
+	err = m_can_cccr_update_bits(cdev, CCCR_CCE, CCCR_CCE);
+	if (err)
+		netdev_err(cdev->net, "failed to enable configuration mode\n");
+
+	return err;
+}
+
+static int m_can_config_disable(struct m_can_classdev *cdev)
+{
+	int err;
+
+	/* Only clear CCCR_CCE, since CCCR_INIT cannot be cleared while in
+	 * standby mode
+	 */
+	err = m_can_cccr_update_bits(cdev, CCCR_CCE, 0);
+	if (err)
+		netdev_err(cdev->net, "failed to disable configuration registers\n");
+
+	return err;
 }
 
 static void m_can_interrupt_enable(struct m_can_classdev *cdev, u32 interrupts)
@@ -1403,7 +1437,9 @@ static int m_can_chip_config(struct net_device *dev)
 	interrupts &= ~(IR_ARA | IR_ELO | IR_DRX | IR_TEFF | IR_TFE | IR_TCF |
 			IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | IR_RF0F);
 
-	m_can_config_endisable(cdev, true);
+	err = m_can_config_enable(cdev);
+	if (err)
+		return err;
 
 	/* RX Buffer/FIFO Element Size 64 bytes data field */
 	m_can_write(cdev, M_CAN_RXESC,
@@ -1521,7 +1557,9 @@ static int m_can_chip_config(struct net_device *dev)
 		    FIELD_PREP(TSCC_TCP_MASK, 0xf) |
 		    FIELD_PREP(TSCC_TSS_MASK, TSCC_TSS_INTERNAL));
 
-	m_can_config_endisable(cdev, false);
+	err = m_can_config_disable(cdev);
+	if (err)
+		return err;
 
 	if (cdev->ops->init)
 		cdev->ops->init(cdev);
@@ -1550,7 +1588,11 @@ static int m_can_start(struct net_device *dev)
 		cdev->tx_fifo_putidx = FIELD_GET(TXFQS_TFQPI_MASK,
 						 m_can_read(cdev, M_CAN_TXFQS));
 
-	return 0;
+	ret = m_can_cccr_update_bits(cdev, CCCR_INIT, 0);
+	if (ret)
+		netdev_err(dev, "failed to enter normal mode\n");
+
+	return ret;
 }
 
 static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
@@ -1599,43 +1641,37 @@ static int m_can_check_core_release(struct m_can_classdev *cdev)
 }
 
 /* Selectable Non ISO support only in version 3.2.x
- * This function checks if the bit is writable.
+ * Return 1 if the bit is writable, 0 if it is not, or negative on error.
  */
-static bool m_can_niso_supported(struct m_can_classdev *cdev)
+static int m_can_niso_supported(struct m_can_classdev *cdev)
 {
-	u32 cccr_reg, cccr_poll = 0;
-	int niso_timeout = -ETIMEDOUT;
-	int i;
+	int ret, niso;
 
-	m_can_config_endisable(cdev, true);
-	cccr_reg = m_can_read(cdev, M_CAN_CCCR);
-	cccr_reg |= CCCR_NISO;
-	m_can_write(cdev, M_CAN_CCCR, cccr_reg);
+	ret = m_can_config_enable(cdev);
+	if (ret)
+		return ret;
 
-	for (i = 0; i <= 10; i++) {
-		cccr_poll = m_can_read(cdev, M_CAN_CCCR);
-		if (cccr_poll == cccr_reg) {
-			niso_timeout = 0;
-			break;
-		}
+	/* First try to set the NISO bit. */
+	niso = m_can_cccr_update_bits(cdev, CCCR_NISO, CCCR_NISO);
 
-		usleep_range(1, 5);
+	/* Then clear the it again. */
+	ret = m_can_cccr_update_bits(cdev, CCCR_NISO, 0);
+	if (ret) {
+		dev_err(cdev->dev, "failed to revert the NON-ISO bit in CCCR\n");
+		return ret;
 	}
 
-	/* Clear NISO */
-	cccr_reg &= ~(CCCR_NISO);
-	m_can_write(cdev, M_CAN_CCCR, cccr_reg);
+	ret = m_can_config_disable(cdev);
+	if (ret)
+		return ret;
 
-	m_can_config_endisable(cdev, false);
-
-	/* return false if time out (-ETIMEDOUT), else return true */
-	return !niso_timeout;
+	return niso == 0;
 }
 
 static int m_can_dev_setup(struct m_can_classdev *cdev)
 {
 	struct net_device *dev = cdev->net;
-	int m_can_version, err;
+	int m_can_version, err, niso;
 
 	m_can_version = m_can_check_core_release(cdev);
 	/* return if unsupported version */
@@ -1684,9 +1720,11 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
 		cdev->can.bittiming_const = &m_can_bittiming_const_31X;
 		cdev->can.data_bittiming_const = &m_can_data_bittiming_const_31X;
 
-		cdev->can.ctrlmode_supported |=
-			(m_can_niso_supported(cdev) ?
-			 CAN_CTRLMODE_FD_NON_ISO : 0);
+		niso = m_can_niso_supported(cdev);
+		if (niso < 0)
+			return niso;
+		if (niso)
+			cdev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO;
 		break;
 	default:
 		dev_err(cdev->dev, "Unsupported version number: %2d",
@@ -1694,21 +1732,26 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
 		return -EINVAL;
 	}
 
-	if (cdev->ops->init)
-		cdev->ops->init(cdev);
-
-	return 0;
+	/* Forcing standby mode should be redundant, as the chip should be in
+	 * standby after a reset. Write the INIT bit anyways, should the chip
+	 * be configured by previous stage.
+	 */
+	return m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
 }
 
 static void m_can_stop(struct net_device *dev)
 {
 	struct m_can_classdev *cdev = netdev_priv(dev);
+	int ret;
 
 	/* disable all interrupts */
 	m_can_disable_all_interrupts(cdev);
 
 	/* Set init mode to disengage from the network */
-	m_can_config_endisable(cdev, true);
+	ret = m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
+	if (ret)
+		netdev_err(dev, "failed to enter standby mode: %pe\n",
+			   ERR_PTR(ret));
 
 	/* set the state as STOPPED */
 	cdev->can.state = CAN_STATE_STOPPED;
diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c
index a42600dac70d..d723206ac7c9 100644
--- a/drivers/net/can/m_can/tcan4x5x-core.c
+++ b/drivers/net/can/m_can/tcan4x5x-core.c
@@ -453,10 +453,17 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
 		goto out_power;
 	}
 
-	ret = tcan4x5x_init(mcan_class);
+	tcan4x5x_check_wake(priv);
+
+	ret = tcan4x5x_write_tcan_reg(mcan_class, TCAN4X5X_INT_EN, 0);
 	if (ret) {
-		dev_err(&spi->dev, "tcan initialization failed %pe\n",
-			ERR_PTR(ret));
+		dev_err(&spi->dev, "Disabling interrupts failed %pe\n", ERR_PTR(ret));
+		goto out_power;
+	}
+
+	ret = tcan4x5x_clear_interrupts(mcan_class);
+	if (ret) {
+		dev_err(&spi->dev, "Clearing interrupts failed %pe\n", ERR_PTR(ret));
 		goto out_power;
 	}
 
-- 
2.44.0


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

* Re: [PATCH v4] can: m_can: don't enable transceiver when probing
  2024-06-07 10:52 [PATCH v4] can: m_can: don't enable transceiver when probing Martin Hundebøll
@ 2024-06-12 12:07 ` Markus Schneider-Pargmann
  2024-06-21  8:32 ` Marc Kleine-Budde
  2024-06-21  9:19 ` Ratheesh Kannoth
  2 siblings, 0 replies; 5+ messages in thread
From: Markus Schneider-Pargmann @ 2024-06-12 12:07 UTC (permalink / raw)
  To: Martin Hundebøll
  Cc: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-can, netdev, linux-kernel

Hi Martin,

On Fri, Jun 07, 2024 at 12:52:08PM GMT, Martin Hundebøll wrote:
> The m_can driver sets and clears the CCCR.INIT bit during probe (both
> when testing the NON-ISO bit, and when configuring the chip). After
> clearing the CCCR.INIT bit, the transceiver enters normal mode, where it
> affects the CAN bus (i.e. it ACKs frames). This can cause troubles when
> the m_can node is only used for monitoring the bus, as one cannot setup
> listen-only mode before the device is probed.
> 
> Rework the probe flow, so that the CCCR.INIT bit is only cleared when
> upping the device. First, the tcan4x5x driver is changed to stay in
> standby mode during/after probe. This in turn requires changes when
> setting bits in the CCCR register, as its CSR and CSA bits are always
> high in standby mode.
> 
> Signed-off-by: Martin Hundebøll <martin@geanix.com>

Reviewed-by: Markus Schneider-Pargmann <msp@baylibre.com>
Tested-by: Markus Schneider-Pargmann <msp@baylibre.com>

I tested on a beaglebone black with tcan attached in loopback mode.
Everything seemed to work fine in my small test.

Best
Markus

> ---
> 
> Changes since v3:
>  * Return 'niso' instead of 'err' in case of failure to detect niso
>    support in m_can_dev_setup()
>  * Fix typo: redunant -> redundant
> 
> Changes since v2:
>  * Return and propagate error(s) from m_can_cccr_update_bits()
> 
> Changes since v1:
>  * Implement Markus review comments:
>    - Rename m_can_cccr_wait_bits() to m_can_cccr_update_bits()
>    - Explicitly set CCCR_INIT bit in m_can_dev_setup()
>    - Revert to 5 timeouts/tries to 10
>    - Use m_can_config_{en|dis}able() in m_can_niso_supported()
>    - Revert move of call to m_can_enable_all_interrupts()
>    - Return -EBUSY on failure to enter normal mode
>    - Use tcan4x5x_clear_interrupts() in tcan4x5x_can_probe()
> 
>  drivers/net/can/m_can/m_can.c         | 169 ++++++++++++++++----------
>  drivers/net/can/m_can/tcan4x5x-core.c |  13 +-
>  2 files changed, 116 insertions(+), 66 deletions(-)
> 
> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> index 14b231c4d7ec..7f63f866083e 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -379,38 +379,72 @@ m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val)
>  	return cdev->ops->read_fifo(cdev, addr_offset, val, 1);
>  }
>  
> -static void m_can_config_endisable(struct m_can_classdev *cdev, bool enable)
> +static int m_can_cccr_update_bits(struct m_can_classdev *cdev, u32 mask, u32 val)
>  {
> -	u32 cccr = m_can_read(cdev, M_CAN_CCCR);
> -	u32 timeout = 10;
> -	u32 val = 0;
> -
> -	/* Clear the Clock stop request if it was set */
> -	if (cccr & CCCR_CSR)
> -		cccr &= ~CCCR_CSR;
> -
> -	if (enable) {
> -		/* enable m_can configuration */
> -		m_can_write(cdev, M_CAN_CCCR, cccr | CCCR_INIT);
> -		udelay(5);
> -		/* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */
> -		m_can_write(cdev, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE);
> -	} else {
> -		m_can_write(cdev, M_CAN_CCCR, cccr & ~(CCCR_INIT | CCCR_CCE));
> +	u32 val_before = m_can_read(cdev, M_CAN_CCCR);
> +	u32 val_after = (val_before & ~mask) | val;
> +	size_t tries = 10;
> +
> +	if (!(mask & CCCR_INIT) && !(val_before & CCCR_INIT)) {
> +		dev_err(cdev->dev,
> +			"refusing to configure device when in normal mode\n");
> +		return -EBUSY;
>  	}
>  
> -	/* there's a delay for module initialization */
> -	if (enable)
> -		val = CCCR_INIT | CCCR_CCE;
> -
> -	while ((m_can_read(cdev, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE)) != val) {
> -		if (timeout == 0) {
> -			netdev_warn(cdev->net, "Failed to init module\n");
> -			return;
> -		}
> -		timeout--;
> -		udelay(1);
> +	/* The chip should be in standby mode when changing the CCCR register,
> +	 * and some chips set the CSR and CSA bits when in standby. Furthermore,
> +	 * the CSR and CSA bits should be written as zeros, even when they read
> +	 * ones.
> +	 */
> +	val_after &= ~(CCCR_CSR | CCCR_CSA);
> +
> +	while (tries--) {
> +		u32 val_read;
> +
> +		/* Write the desired value in each try, as setting some bits in
> +		 * the CCCR register require other bits to be set first. E.g.
> +		 * setting the NISO bit requires setting the CCE bit first.
> +		 */
> +		m_can_write(cdev, M_CAN_CCCR, val_after);
> +
> +		val_read = m_can_read(cdev, M_CAN_CCCR) & ~(CCCR_CSR | CCCR_CSA);
> +
> +		if (val_read == val_after)
> +			return 0;
> +
> +		usleep_range(1, 5);
>  	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int m_can_config_enable(struct m_can_classdev *cdev)
> +{
> +	int err;
> +
> +	/* CCCR_INIT must be set in order to set CCCR_CCE, but access to
> +	 * configuration registers should only be enabled when in standby mode,
> +	 * where CCCR_INIT is always set.
> +	 */
> +	err = m_can_cccr_update_bits(cdev, CCCR_CCE, CCCR_CCE);
> +	if (err)
> +		netdev_err(cdev->net, "failed to enable configuration mode\n");
> +
> +	return err;
> +}
> +
> +static int m_can_config_disable(struct m_can_classdev *cdev)
> +{
> +	int err;
> +
> +	/* Only clear CCCR_CCE, since CCCR_INIT cannot be cleared while in
> +	 * standby mode
> +	 */
> +	err = m_can_cccr_update_bits(cdev, CCCR_CCE, 0);
> +	if (err)
> +		netdev_err(cdev->net, "failed to disable configuration registers\n");
> +
> +	return err;
>  }
>  
>  static void m_can_interrupt_enable(struct m_can_classdev *cdev, u32 interrupts)
> @@ -1403,7 +1437,9 @@ static int m_can_chip_config(struct net_device *dev)
>  	interrupts &= ~(IR_ARA | IR_ELO | IR_DRX | IR_TEFF | IR_TFE | IR_TCF |
>  			IR_HPM | IR_RF1F | IR_RF1W | IR_RF1N | IR_RF0F);
>  
> -	m_can_config_endisable(cdev, true);
> +	err = m_can_config_enable(cdev);
> +	if (err)
> +		return err;
>  
>  	/* RX Buffer/FIFO Element Size 64 bytes data field */
>  	m_can_write(cdev, M_CAN_RXESC,
> @@ -1521,7 +1557,9 @@ static int m_can_chip_config(struct net_device *dev)
>  		    FIELD_PREP(TSCC_TCP_MASK, 0xf) |
>  		    FIELD_PREP(TSCC_TSS_MASK, TSCC_TSS_INTERNAL));
>  
> -	m_can_config_endisable(cdev, false);
> +	err = m_can_config_disable(cdev);
> +	if (err)
> +		return err;
>  
>  	if (cdev->ops->init)
>  		cdev->ops->init(cdev);
> @@ -1550,7 +1588,11 @@ static int m_can_start(struct net_device *dev)
>  		cdev->tx_fifo_putidx = FIELD_GET(TXFQS_TFQPI_MASK,
>  						 m_can_read(cdev, M_CAN_TXFQS));
>  
> -	return 0;
> +	ret = m_can_cccr_update_bits(cdev, CCCR_INIT, 0);
> +	if (ret)
> +		netdev_err(dev, "failed to enter normal mode\n");
> +
> +	return ret;
>  }
>  
>  static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
> @@ -1599,43 +1641,37 @@ static int m_can_check_core_release(struct m_can_classdev *cdev)
>  }
>  
>  /* Selectable Non ISO support only in version 3.2.x
> - * This function checks if the bit is writable.
> + * Return 1 if the bit is writable, 0 if it is not, or negative on error.
>   */
> -static bool m_can_niso_supported(struct m_can_classdev *cdev)
> +static int m_can_niso_supported(struct m_can_classdev *cdev)
>  {
> -	u32 cccr_reg, cccr_poll = 0;
> -	int niso_timeout = -ETIMEDOUT;
> -	int i;
> +	int ret, niso;
>  
> -	m_can_config_endisable(cdev, true);
> -	cccr_reg = m_can_read(cdev, M_CAN_CCCR);
> -	cccr_reg |= CCCR_NISO;
> -	m_can_write(cdev, M_CAN_CCCR, cccr_reg);
> +	ret = m_can_config_enable(cdev);
> +	if (ret)
> +		return ret;
>  
> -	for (i = 0; i <= 10; i++) {
> -		cccr_poll = m_can_read(cdev, M_CAN_CCCR);
> -		if (cccr_poll == cccr_reg) {
> -			niso_timeout = 0;
> -			break;
> -		}
> +	/* First try to set the NISO bit. */
> +	niso = m_can_cccr_update_bits(cdev, CCCR_NISO, CCCR_NISO);
>  
> -		usleep_range(1, 5);
> +	/* Then clear the it again. */
> +	ret = m_can_cccr_update_bits(cdev, CCCR_NISO, 0);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to revert the NON-ISO bit in CCCR\n");
> +		return ret;
>  	}
>  
> -	/* Clear NISO */
> -	cccr_reg &= ~(CCCR_NISO);
> -	m_can_write(cdev, M_CAN_CCCR, cccr_reg);
> +	ret = m_can_config_disable(cdev);
> +	if (ret)
> +		return ret;
>  
> -	m_can_config_endisable(cdev, false);
> -
> -	/* return false if time out (-ETIMEDOUT), else return true */
> -	return !niso_timeout;
> +	return niso == 0;
>  }
>  
>  static int m_can_dev_setup(struct m_can_classdev *cdev)
>  {
>  	struct net_device *dev = cdev->net;
> -	int m_can_version, err;
> +	int m_can_version, err, niso;
>  
>  	m_can_version = m_can_check_core_release(cdev);
>  	/* return if unsupported version */
> @@ -1684,9 +1720,11 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
>  		cdev->can.bittiming_const = &m_can_bittiming_const_31X;
>  		cdev->can.data_bittiming_const = &m_can_data_bittiming_const_31X;
>  
> -		cdev->can.ctrlmode_supported |=
> -			(m_can_niso_supported(cdev) ?
> -			 CAN_CTRLMODE_FD_NON_ISO : 0);
> +		niso = m_can_niso_supported(cdev);
> +		if (niso < 0)
> +			return niso;
> +		if (niso)
> +			cdev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO;
>  		break;
>  	default:
>  		dev_err(cdev->dev, "Unsupported version number: %2d",
> @@ -1694,21 +1732,26 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
>  		return -EINVAL;
>  	}
>  
> -	if (cdev->ops->init)
> -		cdev->ops->init(cdev);
> -
> -	return 0;
> +	/* Forcing standby mode should be redundant, as the chip should be in
> +	 * standby after a reset. Write the INIT bit anyways, should the chip
> +	 * be configured by previous stage.
> +	 */
> +	return m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
>  }
>  
>  static void m_can_stop(struct net_device *dev)
>  {
>  	struct m_can_classdev *cdev = netdev_priv(dev);
> +	int ret;
>  
>  	/* disable all interrupts */
>  	m_can_disable_all_interrupts(cdev);
>  
>  	/* Set init mode to disengage from the network */
> -	m_can_config_endisable(cdev, true);
> +	ret = m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
> +	if (ret)
> +		netdev_err(dev, "failed to enter standby mode: %pe\n",
> +			   ERR_PTR(ret));
>  
>  	/* set the state as STOPPED */
>  	cdev->can.state = CAN_STATE_STOPPED;
> diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c
> index a42600dac70d..d723206ac7c9 100644
> --- a/drivers/net/can/m_can/tcan4x5x-core.c
> +++ b/drivers/net/can/m_can/tcan4x5x-core.c
> @@ -453,10 +453,17 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
>  		goto out_power;
>  	}
>  
> -	ret = tcan4x5x_init(mcan_class);
> +	tcan4x5x_check_wake(priv);
> +
> +	ret = tcan4x5x_write_tcan_reg(mcan_class, TCAN4X5X_INT_EN, 0);
>  	if (ret) {
> -		dev_err(&spi->dev, "tcan initialization failed %pe\n",
> -			ERR_PTR(ret));
> +		dev_err(&spi->dev, "Disabling interrupts failed %pe\n", ERR_PTR(ret));
> +		goto out_power;
> +	}
> +
> +	ret = tcan4x5x_clear_interrupts(mcan_class);
> +	if (ret) {
> +		dev_err(&spi->dev, "Clearing interrupts failed %pe\n", ERR_PTR(ret));
>  		goto out_power;
>  	}
>  
> -- 
> 2.44.0
> 

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

* Re: [PATCH v4] can: m_can: don't enable transceiver when probing
  2024-06-07 10:52 [PATCH v4] can: m_can: don't enable transceiver when probing Martin Hundebøll
  2024-06-12 12:07 ` Markus Schneider-Pargmann
@ 2024-06-21  8:32 ` Marc Kleine-Budde
  2024-06-21  9:19 ` Ratheesh Kannoth
  2 siblings, 0 replies; 5+ messages in thread
From: Marc Kleine-Budde @ 2024-06-21  8:32 UTC (permalink / raw)
  To: Martin Hundebøll
  Cc: Chandrasekar Ramakrishnan, Vincent Mailhol, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Markus Schneider-Pargmann, linux-can, netdev, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1161 bytes --]

On 07.06.2024 12:52:08, Martin Hundebøll wrote:
> The m_can driver sets and clears the CCCR.INIT bit during probe (both
> when testing the NON-ISO bit, and when configuring the chip). After
> clearing the CCCR.INIT bit, the transceiver enters normal mode, where it
> affects the CAN bus (i.e. it ACKs frames). This can cause troubles when
> the m_can node is only used for monitoring the bus, as one cannot setup
> listen-only mode before the device is probed.
> 
> Rework the probe flow, so that the CCCR.INIT bit is only cleared when
> upping the device. First, the tcan4x5x driver is changed to stay in
> standby mode during/after probe. This in turn requires changes when
> setting bits in the CCCR register, as its CSR and CSA bits are always
> high in standby mode.
> 
> Signed-off-by: Martin Hundebøll <martin@geanix.com>

Applied to linux-can-next.

Thanks,
Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde          |
Embedded Linux                   | https://www.pengutronix.de |
Vertretung Nürnberg              | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-9   |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v4] can: m_can: don't enable transceiver when probing
  2024-06-07 10:52 [PATCH v4] can: m_can: don't enable transceiver when probing Martin Hundebøll
  2024-06-12 12:07 ` Markus Schneider-Pargmann
  2024-06-21  8:32 ` Marc Kleine-Budde
@ 2024-06-21  9:19 ` Ratheesh Kannoth
  2024-06-21  9:27   ` Marc Kleine-Budde
  2 siblings, 1 reply; 5+ messages in thread
From: Ratheesh Kannoth @ 2024-06-21  9:19 UTC (permalink / raw)
  To: Martin Hundebøll
  Cc: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Markus Schneider-Pargmann, linux-can, netdev, linux-kernel

On 2024-06-07 at 16:22:08, Martin Hundebøll (martin@geanix.com) wrote:
>
> -		usleep_range(1, 5);
> +	/* Then clear the it again. */
> +	ret = m_can_cccr_update_bits(cdev, CCCR_NISO, 0);
> +	if (ret) {
> +		dev_err(cdev->dev, "failed to revert the NON-ISO bit in CCCR\n");
> +		return ret;
>  	}
>
> -	/* Clear NISO */
> -	cccr_reg &= ~(CCCR_NISO);
> -	m_can_write(cdev, M_CAN_CCCR, cccr_reg);
> +	ret = m_can_config_disable(cdev);
> +	if (ret)
> +		return ret;
if ret != 0, then the function returns "true", right ?
as indicated by the below comment. But as i understand,
this is an error case and should return "false"
> -	/* return false if time out (-ETIMEDOUT), else return true */
> -	return !niso_timeout;
> +	return niso == 0;
>  }
>
>

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

* Re: [PATCH v4] can: m_can: don't enable transceiver when probing
  2024-06-21  9:19 ` Ratheesh Kannoth
@ 2024-06-21  9:27   ` Marc Kleine-Budde
  0 siblings, 0 replies; 5+ messages in thread
From: Marc Kleine-Budde @ 2024-06-21  9:27 UTC (permalink / raw)
  To: Ratheesh Kannoth
  Cc: Martin Hundebøll, Chandrasekar Ramakrishnan, Vincent Mailhol,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Markus Schneider-Pargmann, linux-can, netdev, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1322 bytes --]

On 21.06.2024 14:49:08, Ratheesh Kannoth wrote:
> On 2024-06-07 at 16:22:08, Martin Hundebøll (martin@geanix.com) wrote:
> >
> > -		usleep_range(1, 5);
> > +	/* Then clear the it again. */
> > +	ret = m_can_cccr_update_bits(cdev, CCCR_NISO, 0);
> > +	if (ret) {
> > +		dev_err(cdev->dev, "failed to revert the NON-ISO bit in CCCR\n");
> > +		return ret;
> >  	}
> >
> > -	/* Clear NISO */
> > -	cccr_reg &= ~(CCCR_NISO);
> > -	m_can_write(cdev, M_CAN_CCCR, cccr_reg);
> > +	ret = m_can_config_disable(cdev);
> > +	if (ret)
> > +		return ret;

> if ret != 0, then the function returns "true", right ?
> as indicated by the below comment. But as i understand,
> this is an error case and should return "false"

AFAICS: This patch changes the return type to int. It Returns 1 if the
bit is writable, 0 if it is not, or negative on error. The caller has
been adopted, too.

Marc

> > -	/* return false if time out (-ETIMEDOUT), else return true */
> > -	return !niso_timeout;
> > +	return niso == 0;
> >  }
> >
> >
> 
> 

-- 
Pengutronix e.K.                 | Marc Kleine-Budde          |
Embedded Linux                   | https://www.pengutronix.de |
Vertretung Nürnberg              | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-9   |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

end of thread, other threads:[~2024-06-21  9:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-07 10:52 [PATCH v4] can: m_can: don't enable transceiver when probing Martin Hundebøll
2024-06-12 12:07 ` Markus Schneider-Pargmann
2024-06-21  8:32 ` Marc Kleine-Budde
2024-06-21  9:19 ` Ratheesh Kannoth
2024-06-21  9:27   ` Marc Kleine-Budde

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).