Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c223da68 authored by Appana Durga Kedareswara rao's avatar Appana Durga Kedareswara rao Committed by Marc Kleine-Budde
Browse files

can: xilinx_can: Add support for CANFD FD frames



CANFD IP supports both CAN and CAN FD frames,
Existing driver supports only CAN frames, This patch
adds support for CAN FD frames.

Signed-off-by: default avatarNaga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
Signed-off-by: default avatarAppana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 8dce7ea4
Loading
Loading
Loading
Loading
+214 −16
Original line number Diff line number Diff line
@@ -50,6 +50,10 @@ enum xcan_reg {
	XCAN_AFR_OFFSET		= 0x60, /* Acceptance Filter */

	/* only on CAN FD cores */
	XCAN_F_BRPR_OFFSET	= 0x088, /* Data Phase Baud Rate
					  * Prescalar
					  */
	XCAN_F_BTR_OFFSET	= 0x08C, /* Data Phase Bit Timing */
	XCAN_TRR_OFFSET		= 0x0090, /* TX Buffer Ready Request */
	XCAN_AFR_EXT_OFFSET	= 0x00E0, /* Acceptance Filter */
	XCAN_FSR_OFFSET		= 0x00E8, /* RX FIFO Status */
@@ -62,6 +66,8 @@ enum xcan_reg {
#define XCAN_FRAME_DLC_OFFSET(frame_base)	((frame_base) + 0x04)
#define XCAN_FRAME_DW1_OFFSET(frame_base)	((frame_base) + 0x08)
#define XCAN_FRAME_DW2_OFFSET(frame_base)	((frame_base) + 0x0C)
#define XCANFD_FRAME_DW_OFFSET(frame_base, n)	(((frame_base) + 0x08) + \
						 ((n) * XCAN_CANFD_FRAME_SIZE))

#define XCAN_CANFD_FRAME_SIZE		0x48
#define XCAN_TXMSG_FRAME_OFFSET(n)	(XCAN_TXMSG_BASE_OFFSET + \
@@ -120,6 +126,8 @@ enum xcan_reg {
#define XCAN_FSR_FL_MASK		0x00003F00 /* RX Fill Level */
#define XCAN_FSR_IRI_MASK		0x00000080 /* RX Increment Read Index */
#define XCAN_FSR_RI_MASK		0x0000001F /* RX Read Index */
#define XCAN_DLCR_EDL_MASK		0x08000000 /* EDL Mask in DLC */
#define XCAN_DLCR_BRS_MASK		0x04000000 /* BRS Mask in DLC */

/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
#define XCAN_BTR_SJW_SHIFT		7  /* Synchronous jump width */
@@ -133,6 +141,7 @@ enum xcan_reg {

/* CAN frame length constants */
#define XCAN_FRAME_MAX_DATA_LEN		8
#define XCANFD_DW_BYTES			4
#define XCAN_TIMEOUT			(1 * HZ)

/* TX-FIFO-empty interrupt available */
@@ -213,6 +222,7 @@ static const struct can_bittiming_const xcan_bittiming_const = {
	.brp_inc = 1,
};

/* AXI CANFD Arbitration Bittiming constants as per AXI CANFD 1.0 spec */
static const struct can_bittiming_const xcan_bittiming_const_canfd = {
	.name = DRIVER_NAME,
	.tseg1_min = 1,
@@ -225,6 +235,20 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd = {
	.brp_inc = 1,
};

/* AXI CANFD Data Bittiming constants as per AXI CANFD 1.0 specs */
static struct can_bittiming_const xcan_data_bittiming_const_canfd = {
	.name = DRIVER_NAME,
	.tseg1_min = 1,
	.tseg1_max = 16,
	.tseg2_min = 1,
	.tseg2_max = 8,
	.sjw_max = 8,
	.brp_min = 1,
	.brp_max = 256,
	.brp_inc = 1,
};

/* AXI CANFD 2.0 Arbitration Bittiming constants as per AXI CANFD 2.0 spec */
static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
	.name = DRIVER_NAME,
	.tseg1_min = 1,
@@ -237,6 +261,19 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
	.brp_inc = 1,
};

/* AXI CANFD 2.0 Data Bittiming constants as per AXI CANFD 2.0 spec */
static struct can_bittiming_const xcan_data_bittiming_const_canfd2 = {
	.name = DRIVER_NAME,
	.tseg1_min = 1,
	.tseg1_max = 32,
	.tseg2_min = 1,
	.tseg2_max = 16,
	.sjw_max = 16,
	.brp_min = 1,
	.brp_max = 256,
	.brp_inc = 1,
};

/**
 * xcan_write_reg_le - Write a value to the device register little endian
 * @priv:	Driver private data structure
@@ -351,6 +388,7 @@ static int xcan_set_bittiming(struct net_device *ndev)
{
	struct xcan_priv *priv = netdev_priv(ndev);
	struct can_bittiming *bt = &priv->can.bittiming;
	struct can_bittiming *dbt = &priv->can.data_bittiming;
	u32 btr0, btr1;
	u32 is_config_mode;

@@ -380,6 +418,24 @@ static int xcan_set_bittiming(struct net_device *ndev)
	priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
	priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);

	if (priv->devtype.cantype == XAXI_CANFD ||
	    priv->devtype.cantype == XAXI_CANFD_2_0) {
		/* Setting Baud Rate prescalar value in F_BRPR Register */
		btr0 = dbt->brp - 1;

		/* Setting Time Segment 1 in BTR Register */
		btr1 = dbt->prop_seg + bt->phase_seg1 - 1;

		/* Setting Time Segment 2 in BTR Register */
		btr1 |= (dbt->phase_seg2 - 1) << priv->devtype.btr_ts2_shift;

		/* Setting Synchronous jump width in BTR Register */
		btr1 |= (dbt->sjw - 1) << priv->devtype.btr_sjw_shift;

		priv->write_reg(priv, XCAN_F_BRPR_OFFSET, btr0);
		priv->write_reg(priv, XCAN_F_BTR_OFFSET, btr1);
	}

	netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
		   priv->read_reg(priv, XCAN_BRPR_OFFSET),
		   priv->read_reg(priv, XCAN_BTR_OFFSET));
@@ -499,7 +555,8 @@ static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb,
			     int frame_offset)
{
	u32 id, dlc, data[2] = {0, 0};
	struct can_frame *cf = (struct can_frame *)skb->data;
	struct canfd_frame *cf = (struct canfd_frame *)skb->data;
	u32 ramoff, dwindex = 0, i;

	/* Watch carefully on the bit sequence */
	if (cf->can_id & CAN_EFF_FLAG) {
@@ -528,28 +585,46 @@ static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb,
			id |= XCAN_IDR_SRR_MASK;
	}

	dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT;

	if (cf->can_dlc > 0)
		data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
	if (cf->can_dlc > 4)
		data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
	dlc = can_len2dlc(cf->len) << XCAN_DLCR_DLC_SHIFT;
	if (can_is_canfd_skb(skb)) {
		if (cf->flags & CANFD_BRS)
			dlc |= XCAN_DLCR_BRS_MASK;
		dlc |= XCAN_DLCR_EDL_MASK;
	}

	priv->write_reg(priv, XCAN_FRAME_ID_OFFSET(frame_offset), id);
	/* If the CAN frame is RTR frame this write triggers transmission
	 * (not on CAN FD)
	 */
	priv->write_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_offset), dlc);
	if (priv->devtype.cantype == XAXI_CANFD ||
	    priv->devtype.cantype == XAXI_CANFD_2_0) {
		for (i = 0; i < cf->len; i += 4) {
			ramoff = XCANFD_FRAME_DW_OFFSET(frame_offset, dwindex) +
					(dwindex * XCANFD_DW_BYTES);
			priv->write_reg(priv, ramoff,
					be32_to_cpup((__be32 *)(cf->data + i)));
			dwindex++;
		}
	} else {
		if (cf->len > 0)
			data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
		if (cf->len > 4)
			data[1] = be32_to_cpup((__be32 *)(cf->data + 4));

		if (!(cf->can_id & CAN_RTR_FLAG)) {
		priv->write_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_offset),
			priv->write_reg(priv,
					XCAN_FRAME_DW1_OFFSET(frame_offset),
					data[0]);
			/* If the CAN frame is Standard/Extended frame this
			 * write triggers transmission (not on CAN FD)
			 */
		priv->write_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_offset),
			priv->write_reg(priv,
					XCAN_FRAME_DW2_OFFSET(frame_offset),
					data[1]);
		}
	}
}

/**
 * xcan_start_xmit_fifo - Starts the transmission (FIFO mode)
@@ -724,6 +799,113 @@ static int xcan_rx(struct net_device *ndev, int frame_base)
	return 1;
}

/**
 * xcanfd_rx -  Is called from CAN isr to complete the received
 *		frame  processing
 * @ndev:	Pointer to net_device structure
 * @frame_base:	Register offset to the frame to be read
 *
 * This function is invoked from the CAN isr(poll) to process the Rx frames. It
 * does minimal processing and invokes "netif_receive_skb" to complete further
 * processing.
 * Return: 1 on success and 0 on failure.
 */
static int xcanfd_rx(struct net_device *ndev, int frame_base)
{
	struct xcan_priv *priv = netdev_priv(ndev);
	struct net_device_stats *stats = &ndev->stats;
	struct canfd_frame *cf;
	struct sk_buff *skb;
	u32 id_xcan, dlc, data[2] = {0, 0}, dwindex = 0, i, fsr, readindex;

	fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
	if (fsr & XCAN_FSR_FL_MASK) {
		readindex = fsr & XCAN_FSR_RI_MASK;
		id_xcan = priv->read_reg(priv,
					 XCAN_FRAME_ID_OFFSET(frame_base));
		dlc = priv->read_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_base));
		if (dlc & XCAN_DLCR_EDL_MASK)
			skb = alloc_canfd_skb(ndev, &cf);
		else
			skb = alloc_can_skb(ndev, (struct can_frame **)&cf);

		if (unlikely(!skb)) {
			stats->rx_dropped++;
			return 0;
		}

		/* Change Xilinx CANFD data length format to socketCAN data
		 * format
		 */
		if (dlc & XCAN_DLCR_EDL_MASK)
			cf->len = can_dlc2len((dlc & XCAN_DLCR_DLC_MASK) >>
					  XCAN_DLCR_DLC_SHIFT);
		else
			cf->len = get_can_dlc((dlc & XCAN_DLCR_DLC_MASK) >>
						  XCAN_DLCR_DLC_SHIFT);

		/* Change Xilinx CAN ID format to socketCAN ID format */
		if (id_xcan & XCAN_IDR_IDE_MASK) {
			/* The received frame is an Extended format frame */
			cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
			cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
					XCAN_IDR_ID2_SHIFT;
			cf->can_id |= CAN_EFF_FLAG;
			if (id_xcan & XCAN_IDR_RTR_MASK)
				cf->can_id |= CAN_RTR_FLAG;
		} else {
			/* The received frame is a standard format frame */
			cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
					XCAN_IDR_ID1_SHIFT;
			if (!(dlc & XCAN_DLCR_EDL_MASK) && (id_xcan &
						XCAN_IDR_SRR_MASK))
				cf->can_id |= CAN_RTR_FLAG;
		}

		/* Check the frame received is FD or not*/
		if (dlc & XCAN_DLCR_EDL_MASK) {
			for (i = 0; i < cf->len; i += 4) {
				if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
					data[0] = priv->read_reg(priv,
					(XCAN_RXMSG_2_FRAME_OFFSET(readindex) +
					(dwindex * XCANFD_DW_BYTES)));
				else
					data[0] = priv->read_reg(priv,
					(XCAN_RXMSG_FRAME_OFFSET(readindex) +
						(dwindex * XCANFD_DW_BYTES)));
				*(__be32 *)(cf->data + i) =
						cpu_to_be32(data[0]);
				dwindex++;
			}
		} else {
			for (i = 0; i < cf->len; i += 4) {
				if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
					data[0] = priv->read_reg(priv,
						XCAN_RXMSG_2_FRAME_OFFSET(readindex) + i);
				else
					data[0] = priv->read_reg(priv,
						XCAN_RXMSG_FRAME_OFFSET(readindex) + i);
				*(__be32 *)(cf->data + i) =
						cpu_to_be32(data[0]);
			}
		}
		/* Update FSR Register so that next packet will save to
		 * buffer
		 */
		fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
		fsr |= XCAN_FSR_IRI_MASK;
		priv->write_reg(priv, XCAN_FSR_OFFSET, fsr);
		fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
		stats->rx_bytes += cf->len;
		stats->rx_packets++;
		netif_receive_skb(skb);

		return 1;
	}
	/* If FSR Register is not updated with fill level */
	return 0;
}

/**
 * xcan_current_error_state - Get current error state from HW
 * @ndev:	Pointer to net_device structure
@@ -1035,6 +1217,9 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota)

	while ((frame_offset = xcan_rx_fifo_get_next_frame(priv)) >= 0 &&
	       (work_done < quota)) {
		if (xcan_rx_int_mask(priv) & XCAN_IXR_RXOK_MASK)
			work_done += xcanfd_rx(ndev, frame_offset);
		else
			work_done += xcan_rx(ndev, frame_offset);

		if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI)
@@ -1577,6 +1762,19 @@ static int xcan_probe(struct platform_device *pdev)
	priv->can.do_get_berr_counter = xcan_get_berr_counter;
	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
					CAN_CTRLMODE_BERR_REPORTING;

	if (devtype->cantype == XAXI_CANFD)
		priv->can.data_bittiming_const =
			&xcan_data_bittiming_const_canfd;

	if (devtype->cantype == XAXI_CANFD_2_0)
		priv->can.data_bittiming_const =
			&xcan_data_bittiming_const_canfd2;

	if (devtype->cantype == XAXI_CANFD ||
	    devtype->cantype == XAXI_CANFD_2_0)
		priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;

	priv->reg_base = addr;
	priv->tx_max = tx_max;
	priv->devtype = *devtype;