[PATCH 2/2] can: esd_usb: Add support for esd CAN-USB/3

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add support for esd CAN-USB/3 and CAN FD to esd_usb.

Signed-off-by: Frank Jungclaus <frank.jungclaus@xxxxxx>
---
 drivers/net/can/usb/esd_usb.c | 282 ++++++++++++++++++++++++++++++----
 1 file changed, 249 insertions(+), 33 deletions(-)

diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
index e24fa48b9b42..48cf5e88d216 100644
--- a/drivers/net/can/usb/esd_usb.c
+++ b/drivers/net/can/usb/esd_usb.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * CAN driver for esd electronics gmbh CAN-USB/2 and CAN-USB/Micro
+ * CAN driver for esd electronics gmbh CAN-USB/2, CAN-USB/3 and CAN-USB/Micro
  *
  * Copyright (C) 2010-2012 esd electronic system design gmbh, Matthias Fuchs <socketcan@xxxxxx>
  * Copyright (C) 2022-2023 esd electronics gmbh, Frank Jungclaus <frank.jungclaus@xxxxxx>
@@ -18,17 +18,19 @@
 
 MODULE_AUTHOR("Matthias Fuchs <socketcan@xxxxxx>");
 MODULE_AUTHOR("Frank Jungclaus <frank.jungclaus@xxxxxx>");
-MODULE_DESCRIPTION("CAN driver for esd electronics gmbh CAN-USB/2 and CAN-USB/Micro interfaces");
+MODULE_DESCRIPTION("CAN driver for esd electronics gmbh CAN-USB/2, CAN-USB/3 and CAN-USB/Micro interfaces");
 MODULE_LICENSE("GPL v2");
 
 /* USB vendor and product ID */
 #define USB_ESDGMBH_VENDOR_ID	0x0ab4
 #define USB_CANUSB2_PRODUCT_ID	0x0010
 #define USB_CANUSBM_PRODUCT_ID	0x0011
+#define USB_CANUSB3_PRODUCT_ID	0x0014
 
 /* CAN controller clock frequencies */
 #define ESD_USB2_CAN_CLOCK	60000000
 #define ESD_USBM_CAN_CLOCK	36000000
+#define ESD_USB3_CAN_CLOCK	80000000
 
 /* Maximum number of CAN nets */
 #define ESD_USB_MAX_NETS	2
@@ -43,6 +45,9 @@ MODULE_LICENSE("GPL v2");
 
 /* esd CAN message flags - dlc field */
 #define ESD_DLC_RTR		0x10
+#define ESD_DLC_NO_BRS		0x10
+#define ESD_DLC_ESI		0x20
+#define ESD_DLC_FD		0x80
 
 /* esd CAN message flags - id field */
 #define ESD_EXTID		0x20000000
@@ -72,6 +77,28 @@ MODULE_LICENSE("GPL v2");
 #define ESD_USB2_BRP_INC	1
 #define ESD_USB2_3_SAMPLES	0x00800000
 
+/* Bit timing CAN-USB/3 */
+#define ESD_USB3_TSEG1_MIN	1
+#define ESD_USB3_TSEG1_MAX	256
+#define ESD_USB3_TSEG2_MIN	1
+#define ESD_USB3_TSEG2_MAX	128
+#define ESD_USB3_SJW_MAX	128
+#define ESD_USB3_BRP_MIN	1
+#define ESD_USB3_BRP_MAX	1024
+#define ESD_USB3_BRP_INC	1
+/* Bit timing CAN-USB/3, data phase */
+#define ESD_USB3_DATA_TSEG1_MIN	1
+#define ESD_USB3_DATA_TSEG1_MAX	32
+#define ESD_USB3_DATA_TSEG2_MIN	1
+#define ESD_USB3_DATA_TSEG2_MAX	16
+#define ESD_USB3_DATA_SJW_MAX	8
+#define ESD_USB3_DATA_BRP_MIN	1
+#define ESD_USB3_DATA_BRP_MAX	32
+#define ESD_USB3_DATA_BRP_INC	1
+
+/* Transmitter Delay Compensation */
+#define ESD_TDC_MODE_AUTO	0
+
 /* esd IDADD message */
 #define ESD_ID_ENABLE		0x80
 #define ESD_MAX_ID_SEGMENT	64
@@ -95,6 +122,21 @@ MODULE_LICENSE("GPL v2");
 #define MAX_RX_URBS		4
 #define MAX_TX_URBS		16 /* must be power of 2 */
 
+/* Modes for NTCAN_BAUDRATE_X */
+#define ESD_BAUDRATE_MODE_DISABLE	0 /* remove from bus */
+#define ESD_BAUDRATE_MODE_INDEX		1 /* ESD (CiA) bit rate idx */
+#define ESD_BAUDRATE_MODE_BTR_CTRL	2 /* BTR values (Controller)*/
+#define ESD_BAUDRATE_MODE_BTR_CANONICAL	3 /* BTR values (Canonical) */
+#define ESD_BAUDRATE_MODE_NUM		4 /* numerical bit rate */
+#define ESD_BAUDRATE_MODE_AUTOBAUD	5 /* autobaud */
+
+/* Flags for NTCAN_BAUDRATE_X */
+#define ESD_BAUDRATE_FLAG_FD	0x0001 /* enable CAN FD Mode */
+#define ESD_BAUDRATE_FLAG_LOM	0x0002 /* enable Listen Only mode */
+#define ESD_BAUDRATE_FLAG_STM	0x0004 /* enable Self test mode */
+#define ESD_BAUDRATE_FLAG_TRS	0x0008 /* enable Triple Sampling */
+#define ESD_BAUDRATE_FLAG_TXP	0x0010 /* enable Transmit Pause */
+
 struct header_msg {
 	u8 len; /* len is always the total message length in 32bit words */
 	u8 cmd;
@@ -129,6 +171,7 @@ struct rx_msg {
 	__le32 id; /* upper 3 bits contain flags */
 	union {
 		u8 data[8];
+		u8 data_fd[64];
 		struct {
 			u8 status; /* CAN Controller Status */
 			u8 ecc;    /* Error Capture Register */
@@ -144,8 +187,11 @@ struct tx_msg {
 	u8 net;
 	u8 dlc;
 	u32 hnd;	/* opaque handle, not used by device */
-	__le32 id; /* upper 3 bits contain flags */
-	u8 data[8];
+	__le32 id;	/* upper 3 bits contain flags */
+	union {
+		u8 data[8];
+		u8 data_fd[64];
+	};
 };
 
 struct tx_done_msg {
@@ -165,12 +211,37 @@ struct id_filter_msg {
 	__le32 mask[ESD_MAX_ID_SEGMENT + 1];
 };
 
+struct baudrate_x_cfg {
+	__le16 brp;	/* bit rate pre-scaler */
+	__le16 tseg1;	/* TSEG1 register */
+	__le16 tseg2;	/* TSEG2 register */
+	__le16 sjw;	/* SJW register */
+};
+
+struct tdc_cfg {
+	u8 tdc_mode;	/* transmitter Delay Compensation mode  */
+	u8 ssp_offset;	/* secondary Sample Point offset in mtq */
+	s8 ssp_shift;	/* secondary Sample Point shift in mtq */
+	u8 tdc_filter;	/* Transmitter Delay Compensation */
+};
+
+struct baudrate_x {
+	__le16 mode;	/* mode word */
+	__le16 flags;	/* control flags */
+	struct tdc_cfg tdc;	/* TDC configuration */
+	struct baudrate_x_cfg arb;	/* bit rate during arbitration phase  */
+	struct baudrate_x_cfg data;	/* bit rate during data phase */
+};
+
 struct set_baudrate_msg {
 	u8 len;
 	u8 cmd;
 	u8 net;
 	u8 rsvd;
-	__le32 baud;
+	union {
+		__le32 baud;
+		struct baudrate_x baud_x;
+	};
 };
 
 /* Main message type used between library and application */
@@ -188,6 +259,7 @@ union __packed esd_usb_msg {
 static struct usb_device_id esd_usb_table[] = {
 	{USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB2_PRODUCT_ID)},
 	{USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSBM_PRODUCT_ID)},
+	{USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB3_PRODUCT_ID)},
 	{}
 };
 MODULE_DEVICE_TABLE(usb, esd_usb_table);
@@ -326,11 +398,13 @@ static void esd_usb_rx_event(struct esd_usb_net_priv *priv,
 static void esd_usb_rx_can_msg(struct esd_usb_net_priv *priv,
 			       union esd_usb_msg *msg)
 {
+	bool is_canfd = msg->rx.dlc & ESD_DLC_FD ? true : false;
 	struct net_device_stats *stats = &priv->netdev->stats;
 	struct can_frame *cf;
+	struct canfd_frame *cfd;
 	struct sk_buff *skb;
-	int i;
 	u32 id;
+	u8 len;
 
 	if (!netif_device_present(priv->netdev))
 		return;
@@ -340,27 +414,42 @@ static void esd_usb_rx_can_msg(struct esd_usb_net_priv *priv,
 	if (id & ESD_EVENT) {
 		esd_usb_rx_event(priv, msg);
 	} else {
-		skb = alloc_can_skb(priv->netdev, &cf);
+		if (is_canfd) {
+			skb = alloc_canfd_skb(priv->netdev, &cfd);
+		} else {
+			skb = alloc_can_skb(priv->netdev, &cf);
+			cfd = (struct canfd_frame *)cf;
+		}
+
 		if (skb == NULL) {
 			stats->rx_dropped++;
 			return;
 		}
 
-		cf->can_id = id & ESD_IDMASK;
-		can_frame_set_cc_len(cf, msg->rx.dlc & ~ESD_DLC_RTR,
-				     priv->can.ctrlmode);
-
-		if (id & ESD_EXTID)
-			cf->can_id |= CAN_EFF_FLAG;
+		cfd->can_id = id & ESD_IDMASK;
 
-		if (msg->rx.dlc & ESD_DLC_RTR) {
-			cf->can_id |= CAN_RTR_FLAG;
+		if (is_canfd) {
+			/* masking by 0x0F is already done within can_fd_dlc2len() */
+			cfd->len = can_fd_dlc2len(msg->rx.dlc);
+			len = cfd->len;
+			if ((msg->rx.dlc & ESD_DLC_NO_BRS) == 0)
+				cfd->flags |= CANFD_BRS;
+			if (msg->rx.dlc & ESD_DLC_ESI)
+				cfd->flags |= CANFD_ESI;
 		} else {
-			for (i = 0; i < cf->len; i++)
-				cf->data[i] = msg->rx.data[i];
-
-			stats->rx_bytes += cf->len;
+			can_frame_set_cc_len(cf, msg->rx.dlc & ~ESD_DLC_RTR, priv->can.ctrlmode);
+			len = cf->len;
+			if (msg->rx.dlc & ESD_DLC_RTR) {
+				cf->can_id |= CAN_RTR_FLAG;
+				len = 0;
+			}
 		}
+
+		if (id & ESD_EXTID)
+			cfd->can_id |= CAN_EFF_FLAG;
+
+		memcpy(cfd->data, msg->rx.data_fd, len);
+		stats->rx_bytes += len;
 		stats->rx_packets++;
 
 		netif_rx(skb);
@@ -735,7 +824,7 @@ static netdev_tx_t esd_usb_start_xmit(struct sk_buff *skb,
 	struct esd_usb *dev = priv->usb;
 	struct esd_tx_urb_context *context = NULL;
 	struct net_device_stats *stats = &netdev->stats;
-	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
 	union esd_usb_msg *msg;
 	struct urb *urb;
 	u8 *buf;
@@ -768,19 +857,28 @@ static netdev_tx_t esd_usb_start_xmit(struct sk_buff *skb,
 	msg->hdr.len = 3; /* minimal length */
 	msg->hdr.cmd = CMD_CAN_TX;
 	msg->tx.net = priv->index;
-	msg->tx.dlc = can_get_cc_dlc(cf, priv->can.ctrlmode);
-	msg->tx.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
 
-	if (cf->can_id & CAN_RTR_FLAG)
-		msg->tx.dlc |= ESD_DLC_RTR;
+	if (can_is_canfd_skb(skb)) {
+		msg->tx.dlc = can_fd_len2dlc(cfd->len);
+		msg->tx.dlc |= ESD_DLC_FD;
+
+		if ((cfd->flags & CANFD_BRS) == 0)
+			msg->tx.dlc |= ESD_DLC_NO_BRS;
+	} else {
+		msg->tx.dlc = can_get_cc_dlc((struct can_frame *)cfd, priv->can.ctrlmode);
+
+		if (cfd->can_id & CAN_RTR_FLAG)
+			msg->tx.dlc |= ESD_DLC_RTR;
+	}
 
-	if (cf->can_id & CAN_EFF_FLAG)
+	msg->tx.id = cpu_to_le32(cfd->can_id & CAN_ERR_MASK);
+
+	if (cfd->can_id & CAN_EFF_FLAG)
 		msg->tx.id |= cpu_to_le32(ESD_EXTID);
 
-	for (i = 0; i < cf->len; i++)
-		msg->tx.data[i] = cf->data[i];
+	memcpy(msg->tx.data_fd, cfd->data, cfd->len);
 
-	msg->hdr.len += (cf->len + 3) >> 2;
+	msg->hdr.len += (cfd->len + 3) >> 2;
 
 	for (i = 0; i < MAX_TX_URBS; i++) {
 		if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
@@ -966,6 +1064,108 @@ static int esd_usb2_set_bittiming(struct net_device *netdev)
 	return err;
 }
 
+static const struct can_bittiming_const esd_usb3_bittiming_const = {
+	.name = "esd_usb3",
+	.tseg1_min = ESD_USB3_TSEG1_MIN,
+	.tseg1_max = ESD_USB3_TSEG1_MAX,
+	.tseg2_min = ESD_USB3_TSEG2_MIN,
+	.tseg2_max = ESD_USB3_TSEG2_MAX,
+	.sjw_max = ESD_USB3_SJW_MAX,
+	.brp_min = ESD_USB3_BRP_MIN,
+	.brp_max = ESD_USB3_BRP_MAX,
+	.brp_inc = ESD_USB3_BRP_INC,
+};
+
+static const struct can_bittiming_const esd_usb3_data_bittiming_const = {
+	.name = "esd_usb3",
+	.tseg1_min = ESD_USB3_DATA_TSEG1_MIN,
+	.tseg1_max = ESD_USB3_DATA_TSEG1_MAX,
+	.tseg2_min = ESD_USB3_DATA_TSEG2_MIN,
+	.tseg2_max = ESD_USB3_DATA_TSEG2_MAX,
+	.sjw_max = ESD_USB3_DATA_SJW_MAX,
+	.brp_min = ESD_USB3_DATA_BRP_MIN,
+	.brp_max = ESD_USB3_DATA_BRP_MAX,
+	.brp_inc = ESD_USB3_DATA_BRP_INC,
+};
+
+static int esd_usb3_set_bittiming(struct net_device *netdev)
+{
+	struct esd_usb_net_priv *priv = netdev_priv(netdev);
+	struct can_bittiming *bt   = &priv->can.bittiming;
+	struct can_bittiming *d_bt = &priv->can.data_bittiming;
+	union esd_usb_msg *msg;
+	int err;
+	u16 mode;
+	u16 flags = 0;
+	u16 brp, tseg1, tseg2, sjw;
+	u16 d_brp, d_tseg1, d_tseg2, d_sjw;
+
+	msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	/* Canonical is the most reasonable mode for SocketCAN on CAN-USB/3 ... */
+	mode = ESD_BAUDRATE_MODE_BTR_CANONICAL;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+		flags |= ESD_BAUDRATE_FLAG_LOM;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		flags |= ESD_BAUDRATE_FLAG_TRS;
+
+	brp = bt->brp & (ESD_USB3_BRP_MAX - 1);
+	sjw = bt->sjw & (ESD_USB3_SJW_MAX - 1);
+	tseg1 = (bt->prop_seg + bt->phase_seg1) & (ESD_USB3_TSEG1_MAX - 1);
+	tseg2 = bt->phase_seg2 & (ESD_USB3_TSEG2_MAX - 1);
+
+	msg->setbaud.baud_x.arb.brp = cpu_to_le16(brp);
+	msg->setbaud.baud_x.arb.sjw = cpu_to_le16(sjw);
+	msg->setbaud.baud_x.arb.tseg1 = cpu_to_le16(tseg1);
+	msg->setbaud.baud_x.arb.tseg2 = cpu_to_le16(tseg2);
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
+		d_brp = d_bt->brp & (ESD_USB3_DATA_BRP_MAX - 1);
+		d_sjw = d_bt->sjw & (ESD_USB3_DATA_SJW_MAX - 1);
+		d_tseg1 = (d_bt->prop_seg + d_bt->phase_seg1) & (ESD_USB3_DATA_TSEG1_MAX - 1);
+		d_tseg2 = d_bt->phase_seg2 & (ESD_USB3_DATA_TSEG2_MAX - 1);
+		flags |= ESD_BAUDRATE_FLAG_FD;
+	} else {
+		d_brp = 0;
+		d_sjw = 0;
+		d_tseg1 = 0;
+		d_tseg2 = 0;
+	}
+
+	msg->setbaud.baud_x.data.brp = cpu_to_le16(d_brp);
+	msg->setbaud.baud_x.data.sjw = cpu_to_le16(d_sjw);
+	msg->setbaud.baud_x.data.tseg1 = cpu_to_le16(d_tseg1);
+	msg->setbaud.baud_x.data.tseg2 = cpu_to_le16(d_tseg2);
+	msg->setbaud.baud_x.mode = cpu_to_le16(mode);
+	msg->setbaud.baud_x.flags = cpu_to_le16(flags);
+	msg->setbaud.baud_x.tdc.tdc_mode = ESD_TDC_MODE_AUTO;
+	msg->setbaud.baud_x.tdc.ssp_offset = 0;
+	msg->setbaud.baud_x.tdc.ssp_shift = 0;
+	msg->setbaud.baud_x.tdc.tdc_filter = 0;
+
+	msg->hdr.len = 7;
+	msg->hdr.cmd = CMD_SETBAUD;
+
+	msg->setbaud.net = priv->index;
+	msg->setbaud.rsvd = 0;
+
+	netdev_info(netdev,
+		    "ctrlmode=%#x/%#x, esd-net=%u, esd-mode=%#x, esd-flg=%#x, arb: brp=%u, ts1=%u, ts2=%u, sjw=%u, data: dbrp=%u, dts1=%u, dts2=%u dsjw=%u\n",
+		    priv->can.ctrlmode, priv->can.ctrlmode_supported,
+		    priv->index, mode, flags,
+		    brp, tseg1, tseg2, sjw,
+		    d_brp, d_tseg1, d_tseg2, d_sjw);
+
+	err = esd_usb_send_msg(priv->usb, msg);
+
+	kfree(msg);
+	return err;
+}
+
 static int esd_usb_get_berr_counter(const struct net_device *netdev,
 				    struct can_berr_counter *bec)
 {
@@ -1023,16 +1223,32 @@ static int esd_usb_probe_one_net(struct usb_interface *intf, int index)
 		CAN_CTRLMODE_CC_LEN8_DLC |
 		CAN_CTRLMODE_BERR_REPORTING;
 
-	if (le16_to_cpu(dev->udev->descriptor.idProduct) ==
-	    USB_CANUSBM_PRODUCT_ID)
+	switch (le16_to_cpu(dev->udev->descriptor.idProduct)) {
+	case USB_CANUSB3_PRODUCT_ID:
+		priv->can.clock.freq = ESD_USB3_CAN_CLOCK;
+		priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+		priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
+		priv->can.bittiming_const = &esd_usb3_bittiming_const;
+		priv->can.data_bittiming_const = &esd_usb3_data_bittiming_const;
+		priv->can.do_set_bittiming = esd_usb3_set_bittiming;
+		priv->can.do_set_data_bittiming = esd_usb3_set_bittiming;
+		break;
+
+	case USB_CANUSBM_PRODUCT_ID:
 		priv->can.clock.freq = ESD_USBM_CAN_CLOCK;
-	else {
+		priv->can.bittiming_const = &esd_usb2_bittiming_const;
+		priv->can.do_set_bittiming = esd_usb2_set_bittiming;
+		break;
+
+	case USB_CANUSB2_PRODUCT_ID:
+	default:
 		priv->can.clock.freq = ESD_USB2_CAN_CLOCK;
 		priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+		priv->can.bittiming_const = &esd_usb2_bittiming_const;
+		priv->can.do_set_bittiming = esd_usb2_set_bittiming;
+		break;
 	}
 
-	priv->can.bittiming_const = &esd_usb2_bittiming_const;
-	priv->can.do_set_bittiming = esd_usb2_set_bittiming;
 	priv->can.do_set_mode = esd_usb_set_mode;
 	priv->can.do_get_berr_counter = esd_usb_get_berr_counter;
 
-- 
2.25.1




[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux