All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marc Kleine-Budde <mkl@pengutronix.de>
To: Dong Aisheng <b29396@freescale.com>, linux-can@vger.kernel.org
Cc: netdev@vger.kernel.org, wg@grandegger.com, devicetree@vger.kernel.org
Subject: Re: [PATCH 2/3] can: m_can: add bus error handling
Date: Tue, 01 Jul 2014 12:37:07 +0200	[thread overview]
Message-ID: <53B28F53.7010403@pengutronix.de> (raw)
In-Reply-To: <1403863246-18822-3-git-send-email-b29396@freescale.com>

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

On 06/27/2014 12:00 PM, Dong Aisheng wrote:
> Add bus error, state change, lost message handling mechanism.
> 
> Signed-off-by: Dong Aisheng <b29396@freescale.com>
> ---
>  drivers/net/can/m_can.c |  271 +++++++++++++++++++++++++++++++++++++++++------
>  1 files changed, 240 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/net/can/m_can.c b/drivers/net/can/m_can.c
> index 61e9a8e..e4aed71 100644
> --- a/drivers/net/can/m_can.c
> +++ b/drivers/net/can/m_can.c
> @@ -83,6 +83,18 @@ enum m_can_reg {
>  	M_CAN_TXEFA	= 0xf8,
>  };
>  
> +/* m_can lec values */
> +enum m_can_lec_type {
> +	LEC_NO_ERROR = 0,
> +	LEC_STUFF_ERROR,
> +	LEC_FORM_ERROR,
> +	LEC_ACK_ERROR,
> +	LEC_BIT1_ERROR,
> +	LEC_BIT0_ERROR,
> +	LEC_CRC_ERROR,
> +	LEC_UNUSED,
> +};
> +
>  /* CC Control Register(CCCR) */
>  #define CCCR_CCE	BIT(1)
>  #define CCCR_INIT	BIT(0)
> @@ -97,6 +109,19 @@ enum m_can_reg {
>  #define BTR_SJW_SHIFT		0
>  #define BTR_SJW_MASK		0xf
>  
> +/* Error Counter Register(ECR) */
> +#define ECR_RP			BIT(15)
> +#define ECR_REC_SHIFT		8
> +#define ECR_REC_MASK		(0x7f << ECR_REC_SHIFT)
> +#define ECR_TEC_SHIFT		0
> +#define ECR_TEC_MASK		0xff
> +
> +/* Protocol Status Register(PSR) */
> +#define PSR_BO		BIT(7)
> +#define PSR_EW		BIT(6)
> +#define PSR_EP		BIT(5)
> +#define PSR_LEC_MASK	0x7
> +
>  /* Interrupt Register(IR) */
>  #define IR_ALL_INT	0xffffffff
>  #define IR_STE		BIT(31)
> @@ -131,10 +156,11 @@ enum m_can_reg {
>  #define IR_RF0F		BIT(2)
>  #define IR_RF0W		BIT(1)
>  #define IR_RF0N		BIT(0)
> -#define IR_ERR_ALL	(IR_STE	| IR_FOE | IR_ACKE | IR_BE | IR_CRCE | \
> -		IR_WDI | IR_BO | IR_EW | IR_EP | IR_ELO | IR_BEU | \
> -		IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | IR_RF1L | \
> -		IR_RF0L)
> +#define IR_ERR_STATE	(IR_BO | IR_EW | IR_EP)
> +#define IR_ERR_BUS	(IR_STE	| IR_FOE | IR_ACKE | IR_BE | IR_CRCE | \
> +		IR_WDI | IR_ELO | IR_BEU | IR_BEC | IR_TOO | IR_MRAF | \
> +		IR_TSW | IR_TEFL | IR_RF1L | IR_RF0L)
> +#define IR_ERR_ALL	(IR_ERR_STATE | IR_ERR_BUS)
>  
>  /* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */
>  #define RXFC_FWM_OFF	24
> @@ -320,12 +346,175 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
>  	return num_rx_pkts;
>  }
>  
> +static int m_can_handle_lost_msg(struct net_device *dev)
> +{
> +	struct net_device_stats *stats = &dev->stats;
> +	struct sk_buff *skb;
> +	struct can_frame *frame;
> +
> +	netdev_err(dev, "msg lost in rxf0\n");
> +
> +	skb = alloc_can_err_skb(dev, &frame);
> +	if (unlikely(!skb))
> +		return 0;

Please rearrange this function differently, so that the stats are
updated, even if alloc_can_err_skb() fails.

> +
> +	frame->can_id |= CAN_ERR_CRTL;
> +	frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +	stats->rx_errors++;
> +	stats->rx_over_errors++;
> +
> +	netif_receive_skb(skb);
> +
> +	return 1;
> +}
> +
> +static int m_can_handle_bus_err(struct net_device *dev,
> +				enum m_can_lec_type lec_type)
> +{
> +	struct m_can_priv *priv = netdev_priv(dev);
> +	struct net_device_stats *stats = &dev->stats;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +
> +	/* early exit if no lec update */
> +	if (lec_type == LEC_UNUSED)
> +		return 0;
> +
> +	/* propagate the error condition to the CAN stack */
> +	skb = alloc_can_err_skb(dev, &cf);
> +	if (unlikely(!skb))
> +		return 0;

same here

> +
> +	/*
> +	 * check for 'last error code' which tells us the
> +	 * type of the last error to occur on the CAN bus
> +	 */
> +	priv->can.can_stats.bus_error++;
> +	stats->rx_errors++;
> +	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +	cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +
> +	switch (lec_type) {
> +	case LEC_STUFF_ERROR:
> +		netdev_dbg(dev, "stuff error\n");
> +		cf->data[2] |= CAN_ERR_PROT_STUFF;
> +		break;
> +	case LEC_FORM_ERROR:
> +		netdev_dbg(dev, "form error\n");
> +		cf->data[2] |= CAN_ERR_PROT_FORM;
> +		break;
> +	case LEC_ACK_ERROR:
> +		netdev_dbg(dev, "ack error\n");
> +		cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
> +				CAN_ERR_PROT_LOC_ACK_DEL);
> +		break;
> +	case LEC_BIT1_ERROR:
> +		netdev_dbg(dev, "bit1 error\n");
> +		cf->data[2] |= CAN_ERR_PROT_BIT1;
> +		break;
> +	case LEC_BIT0_ERROR:
> +		netdev_dbg(dev, "bit0 error\n");
> +		cf->data[2] |= CAN_ERR_PROT_BIT0;
> +		break;
> +	case LEC_CRC_ERROR:
> +		netdev_dbg(dev, "CRC error\n");
> +		cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> +				CAN_ERR_PROT_LOC_CRC_DEL);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	netif_receive_skb(skb);
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;
> +
> +	return 1;
> +}
> +
> +static int m_can_get_berr_counter(const struct net_device *dev,
> +				  struct can_berr_counter *bec)
> +{
> +	struct m_can_priv *priv = netdev_priv(dev);
> +	unsigned int ecr;

This function might be called, even if the interface is down. You might
have to enable the clock(s) here.

> +	ecr = m_can_read(priv, M_CAN_ECR);
> +	bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT;
> +	bec->txerr = ecr & ECR_TEC_MASK;
> +
> +	return 0;
> +}
> +
> +static int m_can_handle_state_change(struct net_device *dev,
> +				enum can_state new_state)
> +{
> +	struct m_can_priv *priv = netdev_priv(dev);
> +	struct net_device_stats *stats = &dev->stats;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	struct can_berr_counter bec;
> +	unsigned int ecr;
> +
> +	/* propagate the error condition to the CAN stack */
> +	skb = alloc_can_err_skb(dev, &cf);
> +	if (unlikely(!skb))
> +		return 0;

Please rearrange the function, so that the stats and more important
bus-off are handled correctly, even if this fails.

> +
> +	m_can_get_berr_counter(dev, &bec);
> +
> +	switch (new_state) {
> +	case CAN_STATE_ERROR_ACTIVE:
> +		/* error warning state */
> +		priv->can.can_stats.error_warning++;
> +		priv->can.state = CAN_STATE_ERROR_WARNING;
> +		cf->can_id |= CAN_ERR_CRTL;
> +		cf->data[1] = (bec.txerr > bec.rxerr) ?
> +			CAN_ERR_CRTL_TX_WARNING :
> +			CAN_ERR_CRTL_RX_WARNING;
> +		cf->data[6] = bec.txerr;
> +		cf->data[7] = bec.rxerr;
> +		break;
> +	case CAN_STATE_ERROR_PASSIVE:
> +		/* error passive state */
> +		priv->can.can_stats.error_passive++;
> +		priv->can.state = CAN_STATE_ERROR_PASSIVE;
> +		cf->can_id |= CAN_ERR_CRTL;
> +		ecr = m_can_read(priv, M_CAN_ECR);
> +		if (ecr & ECR_RP)
> +			cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> +		if (bec.txerr > 127)
> +			cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +		cf->data[6] = bec.txerr;
> +		cf->data[7] = bec.rxerr;
> +		break;
> +	case CAN_STATE_BUS_OFF:
> +		/* bus-off state */
> +		priv->can.state = CAN_STATE_BUS_OFF;
> +		cf->can_id |= CAN_ERR_BUSOFF;
> +		/*
> +		 * disable all interrupts in bus-off mode to ensure that
> +		 * the CPU is not hogged down
> +		 */
> +		m_can_enable_all_interrupts(priv, false);
> +		can_bus_off(dev);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	netif_receive_skb(skb);
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;

don't acces "cf" after netif_receive_sb();

> +
> +	return 1;
> +}
> +
>  static int m_can_poll(struct napi_struct *napi, int quota)
>  {
>  	struct net_device *dev = napi->dev;
>  	struct m_can_priv *priv = netdev_priv(dev);
>  	u32 work_done = 0;
> -	u32 irqstatus;
> +	u32 irqstatus, psr;
>  
>  	irqstatus = m_can_read(priv, M_CAN_IR);
>  	if (irqstatus)
> @@ -337,6 +526,48 @@ static int m_can_poll(struct napi_struct *napi, int quota)
>  	if (!irqstatus)
>  		goto end;
>  
> +	psr = m_can_read(priv, M_CAN_PSR);
> +	if (irqstatus & IR_ERR_STATE) {
> +		if ((psr & PSR_EW) &&
> +			(priv->can.state != CAN_STATE_ERROR_WARNING)) {
> +			netdev_dbg(dev, "entered error warning state\n");
> +			work_done += m_can_handle_state_change(dev,
> +					CAN_STATE_ERROR_WARNING);
> +		}
> +
> +		if ((psr & PSR_EP) &&
> +			(priv->can.state != CAN_STATE_ERROR_PASSIVE)) {
> +			netdev_dbg(dev, "entered error warning state\n");
> +			work_done += m_can_handle_state_change(dev,
> +					CAN_STATE_ERROR_PASSIVE);
> +		}
> +
> +		if ((psr & PSR_BO) &&
> +			(priv->can.state != CAN_STATE_BUS_OFF)) {
> +			netdev_dbg(dev, "entered error warning state\n");
> +			work_done += m_can_handle_state_change(dev,
> +					CAN_STATE_BUS_OFF);
> +		}

You might want to push this into a seperate function.

> +	}
> +
> +	if (irqstatus & IR_ERR_BUS) {
> +		if (irqstatus & IR_RF0L)
> +			work_done += m_can_handle_lost_msg(dev);
> +
> +		/* handle lec errors on the bus */
> +		if (psr & LEC_UNUSED)
> +			work_done += m_can_handle_bus_err(dev,
> +					psr & LEC_UNUSED);
> +
> +		/* other unproccessed error interrupts */
> +		if (irqstatus & IR_WDI)
> +			netdev_err(dev, "Message RAM Watchdog event due to missing READY\n");
> +		if (irqstatus & IR_TOO)
> +			netdev_err(dev, "Timeout reached\n");
> +		if (irqstatus & IR_MRAF)
> +			netdev_err(dev, "Message RAM access failure occurred\n");

same here
> +	}
> +
>  	if (irqstatus & IR_RF0N)
>  		/* handle events corresponding to receive message objects */
>  		work_done += m_can_do_rx_poll(dev, (quota - work_done));
> @@ -369,31 +600,18 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
>  	if (ir & IR_ALL_INT)
>  		m_can_write(priv, M_CAN_IR, ir);
>  
> -	if (ir & IR_ERR_ALL) {
> -		netdev_dbg(dev, "bus error\n");
> -		/* TODO: handle bus error */
> -	}
> -
> -	/* save irqstatus for later using */
> -	priv->irqstatus = ir;
> -
>  	/*
>  	 * schedule NAPI in case of
>  	 * - rx IRQ
> -	 * - state change IRQ(TODO)
> -	 * - bus error IRQ and bus error reporting (TODO)
> +	 * - state change IRQ
> +	 * - bus error IRQ and bus error reporting
>  	 */
> -	if (ir & IR_RF0N) {
> +	if ((ir & IR_RF0N) || (ir & IR_ERR_ALL)) {
> +		priv->irqstatus = ir;
>  		m_can_enable_all_interrupts(priv, false);
>  		napi_schedule(&priv->napi);
>  	}
>  
> -	/* FIFO overflow */
> -	if (ir & IR_RF0L) {
> -		dev->stats.rx_over_errors++;
> -		dev->stats.rx_errors++;
> -	}
> -
>  	/* transmission complete interrupt */
>  	if (ir & IR_TC) {
>  		netdev_dbg(dev, "tx complete\n");
> @@ -446,7 +664,6 @@ static int m_can_set_bittiming(struct net_device *dev)
>   * - setup bittiming
>   * - TODO:
>   *   1) other working modes support like monitor, loopback...
> - *   2) lec error status report enable
>   */
>  static void m_can_chip_config(struct net_device *dev)
>  {
> @@ -515,14 +732,6 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
>  	return 0;
>  }
>  
> -static int m_can_get_berr_counter(const struct net_device *dev,
> -				  struct can_berr_counter *bec)
> -{
> -	/* TODO */
> -
> -	return 0;
> -}
> -
>  static void free_m_can_dev(struct net_device *dev)
>  {
>  	free_candev(dev);
> 

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 242 bytes --]

  reply	other threads:[~2014-07-01 10:37 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-06-27 10:00 [PATCH 0/3] can: m_can: add Bosch M_CAN controller support Dong Aisheng
2014-06-27 10:00 ` Dong Aisheng
2014-06-27 10:00 ` [PATCH 1/3] " Dong Aisheng
2014-06-27 10:00   ` Dong Aisheng
2014-06-27 12:35   ` Mark Rutland
2014-06-30  8:03     ` Dong Aisheng
2014-06-27 18:03   ` Oliver Hartkopp
2014-06-30  8:26     ` Dong Aisheng
2014-06-30  8:26       ` Dong Aisheng
2014-07-02 17:54       ` Oliver Hartkopp
2014-07-02 19:13         ` Marc Kleine-Budde
2014-07-03  3:48           ` Dong Aisheng
2014-07-03  7:12             ` Marc Kleine-Budde
2014-07-03  8:48               ` Dong Aisheng
     [not found]                 ` <20140703084803.GA11012-KgLukfWpBlCctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>
2014-07-03  9:04                   ` Marc Kleine-Budde
2014-07-03  9:09                     ` Dong Aisheng
2014-07-03  9:20                       ` Marc Kleine-Budde
2014-07-03 10:39                         ` Dong Aisheng
2014-07-01 10:29   ` Marc Kleine-Budde
2014-07-02  6:20     ` Dong Aisheng
2014-07-02  6:20       ` Dong Aisheng
2014-07-02  7:57       ` Marc Kleine-Budde
2014-07-02  6:33         ` Dong Aisheng
2014-07-02  6:33           ` Dong Aisheng
2014-07-01 10:33   ` Marc Kleine-Budde
2014-06-27 10:00 ` [PATCH 2/3] can: m_can: add bus error handling Dong Aisheng
2014-06-27 10:00   ` Dong Aisheng
2014-07-01 10:37   ` Marc Kleine-Budde [this message]
2014-07-02  6:31     ` Dong Aisheng
2014-07-02  6:31       ` Dong Aisheng
2014-06-27 10:00 ` [PATCH 3/3] can: m_can: add loopback and monitor mode support Dong Aisheng
2014-06-27 10:00   ` Dong Aisheng
2014-07-01 10:38   ` Marc Kleine-Budde
2014-07-02  6:32     ` Dong Aisheng
2014-07-02  6:32       ` Dong Aisheng

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53B28F53.7010403@pengutronix.de \
    --to=mkl@pengutronix.de \
    --cc=b29396@freescale.com \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-can@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=wg@grandegger.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.