diff options
Diffstat (limited to 'drivers/net/can/m_can/m_can.c')
| -rw-r--r-- | drivers/net/can/m_can/m_can.c | 172 | 
1 files changed, 146 insertions, 26 deletions
| diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 3752520a7d4b..34073cd077e4 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -8,6 +8,7 @@   * https://github.com/linux-can/can-doc/tree/master/m_can   */ +#include <linux/bitfield.h>  #include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/kernel.h> @@ -148,6 +149,16 @@ enum m_can_reg {  #define NBTP_NTSEG2_SHIFT	0  #define NBTP_NTSEG2_MASK	(0x7f << NBTP_NTSEG2_SHIFT) +/* Timestamp Counter Configuration Register (TSCC) */ +#define TSCC_TCP_MASK		GENMASK(19, 16) +#define TSCC_TSS_MASK		GENMASK(1, 0) +#define TSCC_TSS_DISABLE	0x0 +#define TSCC_TSS_INTERNAL	0x1 +#define TSCC_TSS_EXTERNAL	0x2 + +/* Timestamp Counter Value Register (TSCV) */ +#define TSCV_TSC_MASK		GENMASK(15, 0) +  /* Error Counter Register(ECR) */  #define ECR_RP			BIT(15)  #define ECR_REC_SHIFT		8 @@ -302,6 +313,7 @@ enum m_can_reg {  #define RX_BUF_ANMF		BIT(31)  #define RX_BUF_FDF		BIT(21)  #define RX_BUF_BRS		BIT(20) +#define RX_BUF_RXTS_MASK	GENMASK(15, 0)  /* Tx Buffer Element */  /* T0 */ @@ -319,6 +331,7 @@ enum m_can_reg {  /* E1 */  #define TX_EVENT_MM_SHIFT	TX_BUF_MM_SHIFT  #define TX_EVENT_MM_MASK	(0xff << TX_EVENT_MM_SHIFT) +#define TX_EVENT_TXTS_MASK	GENMASK(15, 0)  static inline u32 m_can_read(struct m_can_classdev *cdev, enum m_can_reg reg)  { @@ -413,6 +426,20 @@ static inline void m_can_disable_all_interrupts(struct m_can_classdev *cdev)  	m_can_write(cdev, M_CAN_ILE, 0x0);  } +/* Retrieve internal timestamp counter from TSCV.TSC, and shift it to 32-bit + * width. + */ +static u32 m_can_get_timestamp(struct m_can_classdev *cdev) +{ +	u32 tscv; +	u32 tsc; + +	tscv = m_can_read(cdev, M_CAN_TSCV); +	tsc = FIELD_GET(TSCV_TSC_MASK, tscv); + +	return (tsc << 16); +} +  static void m_can_clean(struct net_device *net)  {  	struct m_can_classdev *cdev = netdev_priv(net); @@ -425,11 +452,33 @@ static void m_can_clean(struct net_device *net)  			putidx = ((m_can_read(cdev, M_CAN_TXFQS) &  				   TXFQS_TFQPI_MASK) >> TXFQS_TFQPI_SHIFT); -		can_free_echo_skb(cdev->net, putidx); +		can_free_echo_skb(cdev->net, putidx, NULL);  		cdev->tx_skb = NULL;  	}  } +/* For peripherals, pass skb to rx-offload, which will push skb from + * napi. For non-peripherals, RX is done in napi already, so push + * directly. timestamp is used to ensure good skb ordering in + * rx-offload and is ignored for non-peripherals. +*/ +static void m_can_receive_skb(struct m_can_classdev *cdev, +			      struct sk_buff *skb, +			      u32 timestamp) +{ +	if (cdev->is_peripheral) { +		struct net_device_stats *stats = &cdev->net->stats; +		int err; + +		err = can_rx_offload_queue_sorted(&cdev->offload, skb, +						  timestamp); +		if (err) +			stats->rx_fifo_errors++; +	} else { +		netif_receive_skb(skb); +	} +} +  static void m_can_read_fifo(struct net_device *dev, u32 rxfs)  {  	struct net_device_stats *stats = &dev->stats; @@ -437,6 +486,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)  	struct canfd_frame *cf;  	struct sk_buff *skb;  	u32 id, fgi, dlc; +	u32 timestamp = 0;  	int i;  	/* calculate the fifo get index for where to read data */ @@ -485,7 +535,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)  	stats->rx_packets++;  	stats->rx_bytes += cf->len; -	netif_receive_skb(skb); +	timestamp = FIELD_GET(RX_BUF_RXTS_MASK, dlc); + +	m_can_receive_skb(cdev, skb, timestamp);  }  static int m_can_do_rx_poll(struct net_device *dev, int quota) @@ -501,9 +553,6 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)  	}  	while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) { -		if (rxfs & RXFS_RFL) -			netdev_warn(dev, "Rx FIFO 0 Message Lost\n"); -  		m_can_read_fifo(dev, rxfs);  		quota--; @@ -519,9 +568,11 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)  static int m_can_handle_lost_msg(struct net_device *dev)  { +	struct m_can_classdev *cdev = netdev_priv(dev);  	struct net_device_stats *stats = &dev->stats;  	struct sk_buff *skb;  	struct can_frame *frame; +	u32 timestamp = 0;  	netdev_err(dev, "msg lost in rxf0\n"); @@ -535,7 +586,10 @@ static int m_can_handle_lost_msg(struct net_device *dev)  	frame->can_id |= CAN_ERR_CRTL;  	frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; -	netif_receive_skb(skb); +	if (cdev->is_peripheral) +		timestamp = m_can_get_timestamp(cdev); + +	m_can_receive_skb(cdev, skb, timestamp);  	return 1;  } @@ -547,6 +601,7 @@ static int m_can_handle_lec_err(struct net_device *dev,  	struct net_device_stats *stats = &dev->stats;  	struct can_frame *cf;  	struct sk_buff *skb; +	u32 timestamp = 0;  	cdev->can.can_stats.bus_error++;  	stats->rx_errors++; @@ -592,7 +647,11 @@ static int m_can_handle_lec_err(struct net_device *dev,  	stats->rx_packets++;  	stats->rx_bytes += cf->len; -	netif_receive_skb(skb); + +	if (cdev->is_peripheral) +		timestamp = m_can_get_timestamp(cdev); + +	m_can_receive_skb(cdev, skb, timestamp);  	return 1;  } @@ -650,6 +709,7 @@ static int m_can_handle_state_change(struct net_device *dev,  	struct sk_buff *skb;  	struct can_berr_counter bec;  	unsigned int ecr; +	u32 timestamp = 0;  	switch (new_state) {  	case CAN_STATE_ERROR_WARNING: @@ -711,7 +771,11 @@ static int m_can_handle_state_change(struct net_device *dev,  	stats->rx_packets++;  	stats->rx_bytes += cf->len; -	netif_receive_skb(skb); + +	if (cdev->is_peripheral) +		timestamp = m_can_get_timestamp(cdev); + +	m_can_receive_skb(cdev, skb, timestamp);  	return 1;  } @@ -776,6 +840,7 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus)  	struct m_can_classdev *cdev = netdev_priv(dev);  	struct can_frame *cf;  	struct sk_buff *skb; +	u32 timestamp = 0;  	/* propagate the error condition to the CAN stack */  	skb = alloc_can_err_skb(dev, &cf); @@ -797,7 +862,11 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus)  		netdev_dbg(dev, "allocation of skb failed\n");  		return 0;  	} -	netif_receive_skb(skb); + +	if (cdev->is_peripheral) +		timestamp = m_can_get_timestamp(cdev); + +	m_can_receive_skb(cdev, skb, timestamp);  	return 1;  } @@ -876,7 +945,7 @@ static int m_can_rx_peripheral(struct net_device *dev)  {  	struct m_can_classdev *cdev = netdev_priv(dev); -	m_can_rx_handler(dev, 1); +	m_can_rx_handler(dev, M_CAN_NAPI_WEIGHT);  	m_can_enable_all_interrupts(cdev); @@ -898,6 +967,29 @@ static int m_can_poll(struct napi_struct *napi, int quota)  	return work_done;  } +/* Echo tx skb and update net stats. Peripherals use rx-offload for + * echo. timestamp is used for peripherals to ensure correct ordering + * by rx-offload, and is ignored for non-peripherals. +*/ +static void m_can_tx_update_stats(struct m_can_classdev *cdev, +				  unsigned int msg_mark, +				  u32 timestamp) +{ +	struct net_device *dev = cdev->net; +	struct net_device_stats *stats = &dev->stats; + +	if (cdev->is_peripheral) +		stats->tx_bytes += +			can_rx_offload_get_echo_skb(&cdev->offload, +						    msg_mark, +						    timestamp, +						    NULL); +	else +		stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); + +	stats->tx_packets++; +} +  static void m_can_echo_tx_event(struct net_device *dev)  {  	u32 txe_count = 0; @@ -907,7 +999,6 @@ static void m_can_echo_tx_event(struct net_device *dev)  	unsigned int msg_mark;  	struct m_can_classdev *cdev = netdev_priv(dev); -	struct net_device_stats *stats = &dev->stats;  	/* read tx event fifo status */  	m_can_txefs = m_can_read(cdev, M_CAN_TXEFS); @@ -917,21 +1008,23 @@ static void m_can_echo_tx_event(struct net_device *dev)  	/* Get and process all sent elements */  	for (i = 0; i < txe_count; i++) { +		u32 txe, timestamp = 0; +  		/* retrieve get index */  		fgi = (m_can_read(cdev, M_CAN_TXEFS) & TXEFS_EFGI_MASK) >>  			TXEFS_EFGI_SHIFT; -		/* get message marker */ -		msg_mark = (m_can_txe_fifo_read(cdev, fgi, 4) & -			    TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; +		/* get message marker, timestamp */ +		txe = m_can_txe_fifo_read(cdev, fgi, 4); +		msg_mark = (txe & TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; +		timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe);  		/* ack txe element */  		m_can_write(cdev, M_CAN_TXEFA, (TXEFA_EFAI_MASK &  						(fgi << TXEFA_EFAI_SHIFT)));  		/* update stats */ -		stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); -		stats->tx_packets++; +		m_can_tx_update_stats(cdev, msg_mark, timestamp);  	}  } @@ -939,7 +1032,6 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)  {  	struct net_device *dev = (struct net_device *)dev_id;  	struct m_can_classdev *cdev = netdev_priv(dev); -	struct net_device_stats *stats = &dev->stats;  	u32 ir;  	if (pm_runtime_suspended(cdev->dev)) @@ -972,8 +1064,12 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)  	if (cdev->version == 30) {  		if (ir & IR_TC) {  			/* Transmission Complete Interrupt*/ -			stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); -			stats->tx_packets++; +			u32 timestamp = 0; + +			if (cdev->is_peripheral) +				timestamp = m_can_get_timestamp(cdev); +			m_can_tx_update_stats(cdev, 0, timestamp); +  			can_led_event(dev, CAN_LED_EVENT_TX);  			netif_wake_queue(dev);  		} @@ -1111,6 +1207,7 @@ static int m_can_set_bittiming(struct net_device *dev)   *		- >= v3.1.x: TX FIFO is used   * - configure mode   * - setup bittiming + * - configure timestamp generation   */  static void m_can_chip_config(struct net_device *dev)  { @@ -1222,6 +1319,10 @@ static void m_can_chip_config(struct net_device *dev)  	/* set bittiming params */  	m_can_set_bittiming(dev); +	/* enable internal timestamp generation, with a prescalar of 16. The +	 * prescalar is applied to the nominal bit timing */ +	m_can_write(cdev, M_CAN_TSCC, FIELD_PREP(TSCC_TCP_MASK, 0xf)); +  	m_can_config_endisable(cdev, false);  	if (cdev->ops->init) @@ -1429,6 +1530,9 @@ static int m_can_close(struct net_device *dev)  		cdev->tx_wq = NULL;  	} +	if (cdev->is_peripheral) +		can_rx_offload_disable(&cdev->offload); +  	close_candev(dev);  	can_led_event(dev, CAN_LED_EVENT_STOP); @@ -1627,6 +1731,9 @@ static int m_can_open(struct net_device *dev)  		goto exit_disable_clks;  	} +	if (cdev->is_peripheral) +		can_rx_offload_enable(&cdev->offload); +  	/* register interrupt handler */  	if (cdev->is_peripheral) {  		cdev->tx_skb = NULL; @@ -1668,6 +1775,8 @@ exit_irq_fail:  	if (cdev->is_peripheral)  		destroy_workqueue(cdev->tx_wq);  out_wq_fail: +	if (cdev->is_peripheral) +		can_rx_offload_disable(&cdev->offload);  	close_candev(dev);  exit_disable_clks:  	m_can_clk_stop(cdev); @@ -1790,11 +1899,6 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,  	}  	class_dev = netdev_priv(net_dev); -	if (!class_dev) { -		dev_err(dev, "Failed to init netdev cdevate"); -		goto out; -	} -  	class_dev->net = net_dev;  	class_dev->dev = dev;  	SET_NETDEV_DEV(net_dev, dev); @@ -1821,15 +1925,22 @@ int m_can_class_register(struct m_can_classdev *cdev)  			return ret;  	} +	if (cdev->is_peripheral) { +		ret = can_rx_offload_add_manual(cdev->net, &cdev->offload, +						M_CAN_NAPI_WEIGHT); +		if (ret) +			goto clk_disable; +	} +  	ret = m_can_dev_setup(cdev);  	if (ret) -		goto clk_disable; +		goto rx_offload_del;  	ret = register_m_can_dev(cdev->net);  	if (ret) {  		dev_err(cdev->dev, "registering %s failed (err=%d)\n",  			cdev->net->name, ret); -		goto clk_disable; +		goto rx_offload_del;  	}  	devm_can_led_init(cdev->net); @@ -1842,6 +1953,13 @@ int m_can_class_register(struct m_can_classdev *cdev)  	/* Probe finished  	 * Stop clocks. They will be reactivated once the M_CAN device is opened  	 */ +	m_can_clk_stop(cdev); + +	return 0; + +rx_offload_del: +	if (cdev->is_peripheral) +		can_rx_offload_del(&cdev->offload);  clk_disable:  	m_can_clk_stop(cdev); @@ -1851,6 +1969,8 @@ EXPORT_SYMBOL_GPL(m_can_class_register);  void m_can_class_unregister(struct m_can_classdev *cdev)  { +	if (cdev->is_peripheral) +		can_rx_offload_del(&cdev->offload);  	unregister_candev(cdev->net);  }  EXPORT_SYMBOL_GPL(m_can_class_unregister); | 
