xref: /openbmc/linux/drivers/net/can/usb/etas_es58x/es581_4.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
11dfb6005SVincent Mailhol // SPDX-License-Identifier: GPL-2.0
21dfb6005SVincent Mailhol 
31dfb6005SVincent Mailhol /* Driver for ETAS GmbH ES58X USB CAN(-FD) Bus Interfaces.
41dfb6005SVincent Mailhol  *
51dfb6005SVincent Mailhol  * File es581_4.c: Adds support to ETAS ES581.4.
61dfb6005SVincent Mailhol  *
71dfb6005SVincent Mailhol  * Copyright (c) 2019 Robert Bosch Engineering and Business Solutions. All rights reserved.
81dfb6005SVincent Mailhol  * Copyright (c) 2020 ETAS K.K.. All rights reserved.
9*8fd9323eSVincent Mailhol  * Copyright (c) 2020-2022 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
101dfb6005SVincent Mailhol  */
111dfb6005SVincent Mailhol 
12*8fd9323eSVincent Mailhol #include <asm/unaligned.h>
131dfb6005SVincent Mailhol #include <linux/kernel.h>
14330c6d3bSVincent Mailhol #include <linux/units.h>
151dfb6005SVincent Mailhol 
161dfb6005SVincent Mailhol #include "es58x_core.h"
171dfb6005SVincent Mailhol #include "es581_4.h"
181dfb6005SVincent Mailhol 
191dfb6005SVincent Mailhol /**
201dfb6005SVincent Mailhol  * es581_4_sizeof_rx_tx_msg() - Calculate the actual length of the
211dfb6005SVincent Mailhol  *	structure of a rx or tx message.
221dfb6005SVincent Mailhol  * @msg: message of variable length, must have a dlc field.
231dfb6005SVincent Mailhol  *
241dfb6005SVincent Mailhol  * Even if RTR frames have actually no payload, the ES58X devices
251dfb6005SVincent Mailhol  * still expect it. Must be a macro in order to accept several types
261dfb6005SVincent Mailhol  * (struct es581_4_tx_can_msg and struct es581_4_rx_can_msg) as an
271dfb6005SVincent Mailhol  * input.
281dfb6005SVincent Mailhol  *
291dfb6005SVincent Mailhol  * Return: length of the message.
301dfb6005SVincent Mailhol  */
311dfb6005SVincent Mailhol #define es581_4_sizeof_rx_tx_msg(msg)				\
321dfb6005SVincent Mailhol 	offsetof(typeof(msg), data[can_cc_dlc2len((msg).dlc)])
331dfb6005SVincent Mailhol 
es581_4_get_msg_len(const union es58x_urb_cmd * urb_cmd)341dfb6005SVincent Mailhol static u16 es581_4_get_msg_len(const union es58x_urb_cmd *urb_cmd)
351dfb6005SVincent Mailhol {
361dfb6005SVincent Mailhol 	return get_unaligned_le16(&urb_cmd->es581_4_urb_cmd.msg_len);
371dfb6005SVincent Mailhol }
381dfb6005SVincent Mailhol 
es581_4_echo_msg(struct es58x_device * es58x_dev,const struct es581_4_urb_cmd * es581_4_urb_cmd)391dfb6005SVincent Mailhol static int es581_4_echo_msg(struct es58x_device *es58x_dev,
401dfb6005SVincent Mailhol 			    const struct es581_4_urb_cmd *es581_4_urb_cmd)
411dfb6005SVincent Mailhol {
421dfb6005SVincent Mailhol 	struct net_device *netdev;
431dfb6005SVincent Mailhol 	const struct es581_4_bulk_echo_msg *bulk_echo_msg;
441dfb6005SVincent Mailhol 	const struct es581_4_echo_msg *echo_msg;
451dfb6005SVincent Mailhol 	u64 *tstamps = es58x_dev->timestamps;
461dfb6005SVincent Mailhol 	u16 msg_len;
471dfb6005SVincent Mailhol 	u32 first_packet_idx, packet_idx;
481dfb6005SVincent Mailhol 	unsigned int dropped = 0;
491dfb6005SVincent Mailhol 	int i, num_element, ret;
501dfb6005SVincent Mailhol 
511dfb6005SVincent Mailhol 	bulk_echo_msg = &es581_4_urb_cmd->bulk_echo_msg;
521dfb6005SVincent Mailhol 	msg_len = get_unaligned_le16(&es581_4_urb_cmd->msg_len) -
531dfb6005SVincent Mailhol 	    sizeof(bulk_echo_msg->channel_no);
541dfb6005SVincent Mailhol 	num_element = es58x_msg_num_element(es58x_dev->dev,
551dfb6005SVincent Mailhol 					    bulk_echo_msg->echo_msg, msg_len);
561dfb6005SVincent Mailhol 	if (num_element <= 0)
571dfb6005SVincent Mailhol 		return num_element;
581dfb6005SVincent Mailhol 
591dfb6005SVincent Mailhol 	ret = es58x_get_netdev(es58x_dev, bulk_echo_msg->channel_no,
601dfb6005SVincent Mailhol 			       ES581_4_CHANNEL_IDX_OFFSET, &netdev);
611dfb6005SVincent Mailhol 	if (ret)
621dfb6005SVincent Mailhol 		return ret;
631dfb6005SVincent Mailhol 
641dfb6005SVincent Mailhol 	echo_msg = &bulk_echo_msg->echo_msg[0];
651dfb6005SVincent Mailhol 	first_packet_idx = get_unaligned_le32(&echo_msg->packet_idx);
661dfb6005SVincent Mailhol 	packet_idx = first_packet_idx;
671dfb6005SVincent Mailhol 	for (i = 0; i < num_element; i++) {
681dfb6005SVincent Mailhol 		u32 tmp_idx;
691dfb6005SVincent Mailhol 
701dfb6005SVincent Mailhol 		echo_msg = &bulk_echo_msg->echo_msg[i];
711dfb6005SVincent Mailhol 		tmp_idx = get_unaligned_le32(&echo_msg->packet_idx);
721dfb6005SVincent Mailhol 		if (tmp_idx == packet_idx - 1) {
731dfb6005SVincent Mailhol 			if (net_ratelimit())
741dfb6005SVincent Mailhol 				netdev_warn(netdev,
751dfb6005SVincent Mailhol 					    "Received echo packet idx %u twice\n",
761dfb6005SVincent Mailhol 					    packet_idx - 1);
771dfb6005SVincent Mailhol 			dropped++;
781dfb6005SVincent Mailhol 			continue;
791dfb6005SVincent Mailhol 		}
801dfb6005SVincent Mailhol 		if (tmp_idx != packet_idx) {
811dfb6005SVincent Mailhol 			netdev_err(netdev, "Echo packet idx jumped from %u to %u\n",
821dfb6005SVincent Mailhol 				   packet_idx - 1, echo_msg->packet_idx);
831dfb6005SVincent Mailhol 			return -EBADMSG;
841dfb6005SVincent Mailhol 		}
851dfb6005SVincent Mailhol 
861dfb6005SVincent Mailhol 		tstamps[i] = get_unaligned_le64(&echo_msg->timestamp);
871dfb6005SVincent Mailhol 		packet_idx++;
881dfb6005SVincent Mailhol 	}
891dfb6005SVincent Mailhol 
901dfb6005SVincent Mailhol 	netdev->stats.tx_dropped += dropped;
911dfb6005SVincent Mailhol 	return es58x_can_get_echo_skb(netdev, first_packet_idx,
921dfb6005SVincent Mailhol 				      tstamps, num_element - dropped);
931dfb6005SVincent Mailhol }
941dfb6005SVincent Mailhol 
es581_4_rx_can_msg(struct es58x_device * es58x_dev,const struct es581_4_urb_cmd * es581_4_urb_cmd,u16 msg_len)951dfb6005SVincent Mailhol static int es581_4_rx_can_msg(struct es58x_device *es58x_dev,
961dfb6005SVincent Mailhol 			      const struct es581_4_urb_cmd *es581_4_urb_cmd,
971dfb6005SVincent Mailhol 			      u16 msg_len)
981dfb6005SVincent Mailhol {
991dfb6005SVincent Mailhol 	const struct device *dev = es58x_dev->dev;
1001dfb6005SVincent Mailhol 	struct net_device *netdev;
1011dfb6005SVincent Mailhol 	int pkts, num_element, channel_no, ret;
1021dfb6005SVincent Mailhol 
1031dfb6005SVincent Mailhol 	num_element = es58x_msg_num_element(dev, es581_4_urb_cmd->rx_can_msg,
1041dfb6005SVincent Mailhol 					    msg_len);
1051dfb6005SVincent Mailhol 	if (num_element <= 0)
1061dfb6005SVincent Mailhol 		return num_element;
1071dfb6005SVincent Mailhol 
1081dfb6005SVincent Mailhol 	channel_no = es581_4_urb_cmd->rx_can_msg[0].channel_no;
1091dfb6005SVincent Mailhol 	ret = es58x_get_netdev(es58x_dev, channel_no,
1101dfb6005SVincent Mailhol 			       ES581_4_CHANNEL_IDX_OFFSET, &netdev);
1111dfb6005SVincent Mailhol 	if (ret)
1121dfb6005SVincent Mailhol 		return ret;
1131dfb6005SVincent Mailhol 
1141dfb6005SVincent Mailhol 	if (!netif_running(netdev)) {
1151dfb6005SVincent Mailhol 		if (net_ratelimit())
1161dfb6005SVincent Mailhol 			netdev_info(netdev,
1171dfb6005SVincent Mailhol 				    "%s: %s is down, dropping %d rx packets\n",
1181dfb6005SVincent Mailhol 				    __func__, netdev->name, num_element);
1191dfb6005SVincent Mailhol 		netdev->stats.rx_dropped += num_element;
1201dfb6005SVincent Mailhol 		return 0;
1211dfb6005SVincent Mailhol 	}
1221dfb6005SVincent Mailhol 
1231dfb6005SVincent Mailhol 	for (pkts = 0; pkts < num_element; pkts++) {
1241dfb6005SVincent Mailhol 		const struct es581_4_rx_can_msg *rx_can_msg =
1251dfb6005SVincent Mailhol 		    &es581_4_urb_cmd->rx_can_msg[pkts];
1261dfb6005SVincent Mailhol 		u64 tstamp = get_unaligned_le64(&rx_can_msg->timestamp);
1271dfb6005SVincent Mailhol 		canid_t can_id = get_unaligned_le32(&rx_can_msg->can_id);
1281dfb6005SVincent Mailhol 
1291dfb6005SVincent Mailhol 		if (channel_no != rx_can_msg->channel_no)
1301dfb6005SVincent Mailhol 			return -EBADMSG;
1311dfb6005SVincent Mailhol 
1321dfb6005SVincent Mailhol 		ret = es58x_rx_can_msg(netdev, tstamp, rx_can_msg->data,
1331dfb6005SVincent Mailhol 				       can_id, rx_can_msg->flags,
1341dfb6005SVincent Mailhol 				       rx_can_msg->dlc);
1351dfb6005SVincent Mailhol 		if (ret)
1361dfb6005SVincent Mailhol 			break;
1371dfb6005SVincent Mailhol 	}
1381dfb6005SVincent Mailhol 
1391dfb6005SVincent Mailhol 	return ret;
1401dfb6005SVincent Mailhol }
1411dfb6005SVincent Mailhol 
es581_4_rx_err_msg(struct es58x_device * es58x_dev,const struct es581_4_rx_err_msg * rx_err_msg)1421dfb6005SVincent Mailhol static int es581_4_rx_err_msg(struct es58x_device *es58x_dev,
1431dfb6005SVincent Mailhol 			      const struct es581_4_rx_err_msg *rx_err_msg)
1441dfb6005SVincent Mailhol {
1451dfb6005SVincent Mailhol 	struct net_device *netdev;
1461dfb6005SVincent Mailhol 	enum es58x_err error = get_unaligned_le32(&rx_err_msg->error);
1471dfb6005SVincent Mailhol 	int ret;
1481dfb6005SVincent Mailhol 
1491dfb6005SVincent Mailhol 	ret = es58x_get_netdev(es58x_dev, rx_err_msg->channel_no,
1501dfb6005SVincent Mailhol 			       ES581_4_CHANNEL_IDX_OFFSET, &netdev);
1511dfb6005SVincent Mailhol 	if (ret)
1521dfb6005SVincent Mailhol 		return ret;
1531dfb6005SVincent Mailhol 
1541dfb6005SVincent Mailhol 	return es58x_rx_err_msg(netdev, error, 0,
1551dfb6005SVincent Mailhol 				get_unaligned_le64(&rx_err_msg->timestamp));
1561dfb6005SVincent Mailhol }
1571dfb6005SVincent Mailhol 
es581_4_rx_event_msg(struct es58x_device * es58x_dev,const struct es581_4_rx_event_msg * rx_event_msg)1581dfb6005SVincent Mailhol static int es581_4_rx_event_msg(struct es58x_device *es58x_dev,
1591dfb6005SVincent Mailhol 				const struct es581_4_rx_event_msg *rx_event_msg)
1601dfb6005SVincent Mailhol {
1611dfb6005SVincent Mailhol 	struct net_device *netdev;
1621dfb6005SVincent Mailhol 	enum es58x_event event = get_unaligned_le32(&rx_event_msg->event);
1631dfb6005SVincent Mailhol 	int ret;
1641dfb6005SVincent Mailhol 
1651dfb6005SVincent Mailhol 	ret = es58x_get_netdev(es58x_dev, rx_event_msg->channel_no,
1661dfb6005SVincent Mailhol 			       ES581_4_CHANNEL_IDX_OFFSET, &netdev);
1671dfb6005SVincent Mailhol 	if (ret)
1681dfb6005SVincent Mailhol 		return ret;
1691dfb6005SVincent Mailhol 
1701dfb6005SVincent Mailhol 	return es58x_rx_err_msg(netdev, 0, event,
1711dfb6005SVincent Mailhol 				get_unaligned_le64(&rx_event_msg->timestamp));
1721dfb6005SVincent Mailhol }
1731dfb6005SVincent Mailhol 
es581_4_rx_cmd_ret_u32(struct es58x_device * es58x_dev,const struct es581_4_urb_cmd * es581_4_urb_cmd,enum es58x_ret_type ret_type)1741dfb6005SVincent Mailhol static int es581_4_rx_cmd_ret_u32(struct es58x_device *es58x_dev,
1751dfb6005SVincent Mailhol 				  const struct es581_4_urb_cmd *es581_4_urb_cmd,
1761dfb6005SVincent Mailhol 				  enum es58x_ret_type ret_type)
1771dfb6005SVincent Mailhol {
1781dfb6005SVincent Mailhol 	struct net_device *netdev;
1791dfb6005SVincent Mailhol 	const struct es581_4_rx_cmd_ret *rx_cmd_ret;
1801dfb6005SVincent Mailhol 	u16 msg_len = get_unaligned_le16(&es581_4_urb_cmd->msg_len);
1811dfb6005SVincent Mailhol 	int ret;
1821dfb6005SVincent Mailhol 
1831dfb6005SVincent Mailhol 	ret = es58x_check_msg_len(es58x_dev->dev,
1841dfb6005SVincent Mailhol 				  es581_4_urb_cmd->rx_cmd_ret, msg_len);
1851dfb6005SVincent Mailhol 	if (ret)
1861dfb6005SVincent Mailhol 		return ret;
1871dfb6005SVincent Mailhol 
1881dfb6005SVincent Mailhol 	rx_cmd_ret = &es581_4_urb_cmd->rx_cmd_ret;
1891dfb6005SVincent Mailhol 
1901dfb6005SVincent Mailhol 	ret = es58x_get_netdev(es58x_dev, rx_cmd_ret->channel_no,
1911dfb6005SVincent Mailhol 			       ES581_4_CHANNEL_IDX_OFFSET, &netdev);
1921dfb6005SVincent Mailhol 	if (ret)
1931dfb6005SVincent Mailhol 		return ret;
1941dfb6005SVincent Mailhol 
1951dfb6005SVincent Mailhol 	return es58x_rx_cmd_ret_u32(netdev, ret_type,
1961dfb6005SVincent Mailhol 				    get_unaligned_le32(&rx_cmd_ret->rx_cmd_ret_le32));
1971dfb6005SVincent Mailhol }
1981dfb6005SVincent Mailhol 
es581_4_tx_ack_msg(struct es58x_device * es58x_dev,const struct es581_4_urb_cmd * es581_4_urb_cmd)1991dfb6005SVincent Mailhol static int es581_4_tx_ack_msg(struct es58x_device *es58x_dev,
2001dfb6005SVincent Mailhol 			      const struct es581_4_urb_cmd *es581_4_urb_cmd)
2011dfb6005SVincent Mailhol {
2021dfb6005SVincent Mailhol 	struct net_device *netdev;
2031dfb6005SVincent Mailhol 	const struct es581_4_tx_ack_msg *tx_ack_msg;
2041dfb6005SVincent Mailhol 	u16 msg_len = get_unaligned_le16(&es581_4_urb_cmd->msg_len);
2051dfb6005SVincent Mailhol 	int ret;
2061dfb6005SVincent Mailhol 
2071dfb6005SVincent Mailhol 	tx_ack_msg = &es581_4_urb_cmd->tx_ack_msg;
2081dfb6005SVincent Mailhol 	ret = es58x_check_msg_len(es58x_dev->dev, *tx_ack_msg, msg_len);
2091dfb6005SVincent Mailhol 	if (ret)
2101dfb6005SVincent Mailhol 		return ret;
2111dfb6005SVincent Mailhol 
2121dfb6005SVincent Mailhol 	if (tx_ack_msg->rx_cmd_ret_u8 != ES58X_RET_U8_OK)
2131dfb6005SVincent Mailhol 		return es58x_rx_cmd_ret_u8(es58x_dev->dev,
2141dfb6005SVincent Mailhol 					   ES58X_RET_TYPE_TX_MSG,
2151dfb6005SVincent Mailhol 					   tx_ack_msg->rx_cmd_ret_u8);
2161dfb6005SVincent Mailhol 
2171dfb6005SVincent Mailhol 	ret = es58x_get_netdev(es58x_dev, tx_ack_msg->channel_no,
2181dfb6005SVincent Mailhol 			       ES581_4_CHANNEL_IDX_OFFSET, &netdev);
2191dfb6005SVincent Mailhol 	if (ret)
2201dfb6005SVincent Mailhol 		return ret;
2211dfb6005SVincent Mailhol 
2221dfb6005SVincent Mailhol 	return es58x_tx_ack_msg(netdev,
2231dfb6005SVincent Mailhol 				get_unaligned_le16(&tx_ack_msg->tx_free_entries),
2241dfb6005SVincent Mailhol 				ES58X_RET_U32_OK);
2251dfb6005SVincent Mailhol }
2261dfb6005SVincent Mailhol 
es581_4_dispatch_rx_cmd(struct es58x_device * es58x_dev,const struct es581_4_urb_cmd * es581_4_urb_cmd)2271dfb6005SVincent Mailhol static int es581_4_dispatch_rx_cmd(struct es58x_device *es58x_dev,
2281dfb6005SVincent Mailhol 				   const struct es581_4_urb_cmd *es581_4_urb_cmd)
2291dfb6005SVincent Mailhol {
2301dfb6005SVincent Mailhol 	const struct device *dev = es58x_dev->dev;
2311dfb6005SVincent Mailhol 	u16 msg_len = get_unaligned_le16(&es581_4_urb_cmd->msg_len);
2321dfb6005SVincent Mailhol 	enum es581_4_rx_type rx_type = es581_4_urb_cmd->rx_can_msg[0].rx_type;
2331dfb6005SVincent Mailhol 	int ret = 0;
2341dfb6005SVincent Mailhol 
2351dfb6005SVincent Mailhol 	switch (rx_type) {
2361dfb6005SVincent Mailhol 	case ES581_4_RX_TYPE_MESSAGE:
2371dfb6005SVincent Mailhol 		return es581_4_rx_can_msg(es58x_dev, es581_4_urb_cmd, msg_len);
2381dfb6005SVincent Mailhol 
2391dfb6005SVincent Mailhol 	case ES581_4_RX_TYPE_ERROR:
2401dfb6005SVincent Mailhol 		ret = es58x_check_msg_len(dev, es581_4_urb_cmd->rx_err_msg,
2411dfb6005SVincent Mailhol 					  msg_len);
2421dfb6005SVincent Mailhol 		if (ret < 0)
2431dfb6005SVincent Mailhol 			return ret;
2441dfb6005SVincent Mailhol 		return es581_4_rx_err_msg(es58x_dev,
2451dfb6005SVincent Mailhol 					  &es581_4_urb_cmd->rx_err_msg);
2461dfb6005SVincent Mailhol 
2471dfb6005SVincent Mailhol 	case ES581_4_RX_TYPE_EVENT:
2481dfb6005SVincent Mailhol 		ret = es58x_check_msg_len(dev, es581_4_urb_cmd->rx_event_msg,
2491dfb6005SVincent Mailhol 					  msg_len);
2501dfb6005SVincent Mailhol 		if (ret < 0)
2511dfb6005SVincent Mailhol 			return ret;
2521dfb6005SVincent Mailhol 		return es581_4_rx_event_msg(es58x_dev,
2531dfb6005SVincent Mailhol 					    &es581_4_urb_cmd->rx_event_msg);
2541dfb6005SVincent Mailhol 
2551dfb6005SVincent Mailhol 	default:
2561dfb6005SVincent Mailhol 		dev_err(dev, "%s: Unknown rx_type 0x%02X\n", __func__, rx_type);
2571dfb6005SVincent Mailhol 		return -EBADRQC;
2581dfb6005SVincent Mailhol 	}
2591dfb6005SVincent Mailhol }
2601dfb6005SVincent Mailhol 
es581_4_handle_urb_cmd(struct es58x_device * es58x_dev,const union es58x_urb_cmd * urb_cmd)2611dfb6005SVincent Mailhol static int es581_4_handle_urb_cmd(struct es58x_device *es58x_dev,
2621dfb6005SVincent Mailhol 				  const union es58x_urb_cmd *urb_cmd)
2631dfb6005SVincent Mailhol {
2641dfb6005SVincent Mailhol 	const struct es581_4_urb_cmd *es581_4_urb_cmd;
2651dfb6005SVincent Mailhol 	struct device *dev = es58x_dev->dev;
2661dfb6005SVincent Mailhol 	u16 msg_len = es581_4_get_msg_len(urb_cmd);
2671dfb6005SVincent Mailhol 	int ret;
2681dfb6005SVincent Mailhol 
2691dfb6005SVincent Mailhol 	es581_4_urb_cmd = &urb_cmd->es581_4_urb_cmd;
2701dfb6005SVincent Mailhol 
2711dfb6005SVincent Mailhol 	if (es581_4_urb_cmd->cmd_type != ES581_4_CAN_COMMAND_TYPE) {
2721dfb6005SVincent Mailhol 		dev_err(dev, "%s: Unknown command type (0x%02X)\n",
2731dfb6005SVincent Mailhol 			__func__, es581_4_urb_cmd->cmd_type);
2741dfb6005SVincent Mailhol 		return -EBADRQC;
2751dfb6005SVincent Mailhol 	}
2761dfb6005SVincent Mailhol 
2771dfb6005SVincent Mailhol 	switch ((enum es581_4_cmd_id)es581_4_urb_cmd->cmd_id) {
2781dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_SET_BITTIMING:
2791dfb6005SVincent Mailhol 		return es581_4_rx_cmd_ret_u32(es58x_dev, es581_4_urb_cmd,
2801dfb6005SVincent Mailhol 					      ES58X_RET_TYPE_SET_BITTIMING);
2811dfb6005SVincent Mailhol 
2821dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_ENABLE_CHANNEL:
2831dfb6005SVincent Mailhol 		return es581_4_rx_cmd_ret_u32(es58x_dev, es581_4_urb_cmd,
2841dfb6005SVincent Mailhol 					      ES58X_RET_TYPE_ENABLE_CHANNEL);
2851dfb6005SVincent Mailhol 
2861dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_TX_MSG:
2871dfb6005SVincent Mailhol 		return es581_4_tx_ack_msg(es58x_dev, es581_4_urb_cmd);
2881dfb6005SVincent Mailhol 
2891dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_RX_MSG:
2901dfb6005SVincent Mailhol 		return es581_4_dispatch_rx_cmd(es58x_dev, es581_4_urb_cmd);
2911dfb6005SVincent Mailhol 
2921dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_RESET_RX:
2931dfb6005SVincent Mailhol 		ret = es581_4_rx_cmd_ret_u32(es58x_dev, es581_4_urb_cmd,
2941dfb6005SVincent Mailhol 					     ES58X_RET_TYPE_RESET_RX);
2951dfb6005SVincent Mailhol 		return ret;
2961dfb6005SVincent Mailhol 
2971dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_RESET_TX:
2981dfb6005SVincent Mailhol 		ret = es581_4_rx_cmd_ret_u32(es58x_dev, es581_4_urb_cmd,
2991dfb6005SVincent Mailhol 					     ES58X_RET_TYPE_RESET_TX);
3001dfb6005SVincent Mailhol 		return ret;
3011dfb6005SVincent Mailhol 
3021dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_DISABLE_CHANNEL:
3031dfb6005SVincent Mailhol 		return es581_4_rx_cmd_ret_u32(es58x_dev, es581_4_urb_cmd,
3041dfb6005SVincent Mailhol 					      ES58X_RET_TYPE_DISABLE_CHANNEL);
3051dfb6005SVincent Mailhol 
3061dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_TIMESTAMP:
3071dfb6005SVincent Mailhol 		ret = es58x_check_msg_len(dev, es581_4_urb_cmd->timestamp,
3081dfb6005SVincent Mailhol 					  msg_len);
3091dfb6005SVincent Mailhol 		if (ret < 0)
3101dfb6005SVincent Mailhol 			return ret;
3111dfb6005SVincent Mailhol 		es58x_rx_timestamp(es58x_dev,
3121dfb6005SVincent Mailhol 				   get_unaligned_le64(&es581_4_urb_cmd->timestamp));
3131dfb6005SVincent Mailhol 		return 0;
3141dfb6005SVincent Mailhol 
3151dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_ECHO:
3161dfb6005SVincent Mailhol 		return es581_4_echo_msg(es58x_dev, es581_4_urb_cmd);
3171dfb6005SVincent Mailhol 
3181dfb6005SVincent Mailhol 	case ES581_4_CMD_ID_DEVICE_ERR:
3191dfb6005SVincent Mailhol 		ret = es58x_check_msg_len(dev, es581_4_urb_cmd->rx_cmd_ret_u8,
3201dfb6005SVincent Mailhol 					  msg_len);
3211dfb6005SVincent Mailhol 		if (ret)
3221dfb6005SVincent Mailhol 			return ret;
3231dfb6005SVincent Mailhol 		return es58x_rx_cmd_ret_u8(dev, ES58X_RET_TYPE_DEVICE_ERR,
3241dfb6005SVincent Mailhol 					   es581_4_urb_cmd->rx_cmd_ret_u8);
3251dfb6005SVincent Mailhol 
3261dfb6005SVincent Mailhol 	default:
3271dfb6005SVincent Mailhol 		dev_warn(dev, "%s: Unexpected command ID: 0x%02X\n",
3281dfb6005SVincent Mailhol 			 __func__, es581_4_urb_cmd->cmd_id);
3291dfb6005SVincent Mailhol 		return -EBADRQC;
3301dfb6005SVincent Mailhol 	}
3311dfb6005SVincent Mailhol }
3321dfb6005SVincent Mailhol 
es581_4_fill_urb_header(union es58x_urb_cmd * urb_cmd,u8 cmd_type,u8 cmd_id,u8 channel_idx,u16 msg_len)3331dfb6005SVincent Mailhol static void es581_4_fill_urb_header(union es58x_urb_cmd *urb_cmd, u8 cmd_type,
3341dfb6005SVincent Mailhol 				    u8 cmd_id, u8 channel_idx, u16 msg_len)
3351dfb6005SVincent Mailhol {
3361dfb6005SVincent Mailhol 	struct es581_4_urb_cmd *es581_4_urb_cmd = &urb_cmd->es581_4_urb_cmd;
3371dfb6005SVincent Mailhol 
3381dfb6005SVincent Mailhol 	es581_4_urb_cmd->SOF = cpu_to_le16(es581_4_param.tx_start_of_frame);
3391dfb6005SVincent Mailhol 	es581_4_urb_cmd->cmd_type = cmd_type;
3401dfb6005SVincent Mailhol 	es581_4_urb_cmd->cmd_id = cmd_id;
3411dfb6005SVincent Mailhol 	es581_4_urb_cmd->msg_len = cpu_to_le16(msg_len);
3421dfb6005SVincent Mailhol }
3431dfb6005SVincent Mailhol 
es581_4_tx_can_msg(struct es58x_priv * priv,const struct sk_buff * skb)3441dfb6005SVincent Mailhol static int es581_4_tx_can_msg(struct es58x_priv *priv,
3451dfb6005SVincent Mailhol 			      const struct sk_buff *skb)
3461dfb6005SVincent Mailhol {
3471dfb6005SVincent Mailhol 	struct es58x_device *es58x_dev = priv->es58x_dev;
3481dfb6005SVincent Mailhol 	union es58x_urb_cmd *urb_cmd = priv->tx_urb->transfer_buffer;
3491dfb6005SVincent Mailhol 	struct es581_4_urb_cmd *es581_4_urb_cmd = &urb_cmd->es581_4_urb_cmd;
3501dfb6005SVincent Mailhol 	struct can_frame *cf = (struct can_frame *)skb->data;
3511dfb6005SVincent Mailhol 	struct es581_4_tx_can_msg *tx_can_msg;
3521dfb6005SVincent Mailhol 	u16 msg_len;
3531dfb6005SVincent Mailhol 	int ret;
3541dfb6005SVincent Mailhol 
3551dfb6005SVincent Mailhol 	if (can_is_canfd_skb(skb))
3561dfb6005SVincent Mailhol 		return -EMSGSIZE;
3571dfb6005SVincent Mailhol 
3581dfb6005SVincent Mailhol 	if (priv->tx_can_msg_cnt == 0) {
3597fcecf51SVincent Mailhol 		msg_len = sizeof(es581_4_urb_cmd->bulk_tx_can_msg.num_can_msg);
3601dfb6005SVincent Mailhol 		es581_4_fill_urb_header(urb_cmd, ES581_4_CAN_COMMAND_TYPE,
3611dfb6005SVincent Mailhol 					ES581_4_CMD_ID_TX_MSG,
3621dfb6005SVincent Mailhol 					priv->channel_idx, msg_len);
3631dfb6005SVincent Mailhol 		es581_4_urb_cmd->bulk_tx_can_msg.num_can_msg = 0;
3641dfb6005SVincent Mailhol 	} else {
3651dfb6005SVincent Mailhol 		msg_len = es581_4_get_msg_len(urb_cmd);
3661dfb6005SVincent Mailhol 	}
3671dfb6005SVincent Mailhol 
3681dfb6005SVincent Mailhol 	ret = es58x_check_msg_max_len(es58x_dev->dev,
3691dfb6005SVincent Mailhol 				      es581_4_urb_cmd->bulk_tx_can_msg,
3701dfb6005SVincent Mailhol 				      msg_len + sizeof(*tx_can_msg));
3711dfb6005SVincent Mailhol 	if (ret)
3721dfb6005SVincent Mailhol 		return ret;
3731dfb6005SVincent Mailhol 
3741dfb6005SVincent Mailhol 	/* Fill message contents. */
375f4f5247dSVincent Mailhol 	tx_can_msg = (typeof(tx_can_msg))&es581_4_urb_cmd->raw_msg[msg_len];
3761dfb6005SVincent Mailhol 	put_unaligned_le32(es58x_get_raw_can_id(cf), &tx_can_msg->can_id);
3771dfb6005SVincent Mailhol 	put_unaligned_le32(priv->tx_head, &tx_can_msg->packet_idx);
3781dfb6005SVincent Mailhol 	put_unaligned_le16((u16)es58x_get_flags(skb), &tx_can_msg->flags);
3791dfb6005SVincent Mailhol 	tx_can_msg->channel_no = priv->channel_idx + ES581_4_CHANNEL_IDX_OFFSET;
3801dfb6005SVincent Mailhol 	tx_can_msg->dlc = can_get_cc_dlc(cf, priv->can.ctrlmode);
3811dfb6005SVincent Mailhol 
3821dfb6005SVincent Mailhol 	memcpy(tx_can_msg->data, cf->data, cf->len);
3831dfb6005SVincent Mailhol 
3841dfb6005SVincent Mailhol 	/* Calculate new sizes. */
3851dfb6005SVincent Mailhol 	es581_4_urb_cmd->bulk_tx_can_msg.num_can_msg++;
3861dfb6005SVincent Mailhol 	msg_len += es581_4_sizeof_rx_tx_msg(*tx_can_msg);
3871dfb6005SVincent Mailhol 	priv->tx_urb->transfer_buffer_length = es58x_get_urb_cmd_len(es58x_dev,
3881dfb6005SVincent Mailhol 								     msg_len);
3891dfb6005SVincent Mailhol 	es581_4_urb_cmd->msg_len = cpu_to_le16(msg_len);
3901dfb6005SVincent Mailhol 
3911dfb6005SVincent Mailhol 	return 0;
3921dfb6005SVincent Mailhol }
3931dfb6005SVincent Mailhol 
es581_4_set_bittiming(struct es58x_priv * priv)3941dfb6005SVincent Mailhol static int es581_4_set_bittiming(struct es58x_priv *priv)
3951dfb6005SVincent Mailhol {
3961dfb6005SVincent Mailhol 	struct es581_4_tx_conf_msg tx_conf_msg = { 0 };
3971dfb6005SVincent Mailhol 	struct can_bittiming *bt = &priv->can.bittiming;
3981dfb6005SVincent Mailhol 
3991dfb6005SVincent Mailhol 	tx_conf_msg.bitrate = cpu_to_le32(bt->bitrate);
4001dfb6005SVincent Mailhol 	/* bt->sample_point is in tenth of percent. Convert it to percent. */
4011dfb6005SVincent Mailhol 	tx_conf_msg.sample_point = cpu_to_le32(bt->sample_point / 10U);
4021dfb6005SVincent Mailhol 	tx_conf_msg.samples_per_bit = cpu_to_le32(ES58X_SAMPLES_PER_BIT_ONE);
4031dfb6005SVincent Mailhol 	tx_conf_msg.bit_time = cpu_to_le32(can_bit_time(bt));
4041dfb6005SVincent Mailhol 	tx_conf_msg.sjw = cpu_to_le32(bt->sjw);
4051dfb6005SVincent Mailhol 	tx_conf_msg.sync_edge = cpu_to_le32(ES58X_SYNC_EDGE_SINGLE);
4061dfb6005SVincent Mailhol 	tx_conf_msg.physical_layer =
4071dfb6005SVincent Mailhol 	    cpu_to_le32(ES58X_PHYSICAL_LAYER_HIGH_SPEED);
4081dfb6005SVincent Mailhol 	tx_conf_msg.echo_mode = cpu_to_le32(ES58X_ECHO_ON);
4091dfb6005SVincent Mailhol 	tx_conf_msg.channel_no = priv->channel_idx + ES581_4_CHANNEL_IDX_OFFSET;
4101dfb6005SVincent Mailhol 
4111dfb6005SVincent Mailhol 	return es58x_send_msg(priv->es58x_dev, ES581_4_CAN_COMMAND_TYPE,
4121dfb6005SVincent Mailhol 			      ES581_4_CMD_ID_SET_BITTIMING, &tx_conf_msg,
4131dfb6005SVincent Mailhol 			      sizeof(tx_conf_msg), priv->channel_idx);
4141dfb6005SVincent Mailhol }
4151dfb6005SVincent Mailhol 
es581_4_enable_channel(struct es58x_priv * priv)4161dfb6005SVincent Mailhol static int es581_4_enable_channel(struct es58x_priv *priv)
4171dfb6005SVincent Mailhol {
4181dfb6005SVincent Mailhol 	int ret;
4191dfb6005SVincent Mailhol 	u8 msg = priv->channel_idx + ES581_4_CHANNEL_IDX_OFFSET;
4201dfb6005SVincent Mailhol 
4211dfb6005SVincent Mailhol 	ret = es581_4_set_bittiming(priv);
4221dfb6005SVincent Mailhol 	if (ret)
4231dfb6005SVincent Mailhol 		return ret;
4241dfb6005SVincent Mailhol 
4251dfb6005SVincent Mailhol 	return es58x_send_msg(priv->es58x_dev, ES581_4_CAN_COMMAND_TYPE,
4261dfb6005SVincent Mailhol 			      ES581_4_CMD_ID_ENABLE_CHANNEL, &msg, sizeof(msg),
4271dfb6005SVincent Mailhol 			      priv->channel_idx);
4281dfb6005SVincent Mailhol }
4291dfb6005SVincent Mailhol 
es581_4_disable_channel(struct es58x_priv * priv)4301dfb6005SVincent Mailhol static int es581_4_disable_channel(struct es58x_priv *priv)
4311dfb6005SVincent Mailhol {
4321dfb6005SVincent Mailhol 	u8 msg = priv->channel_idx + ES581_4_CHANNEL_IDX_OFFSET;
4331dfb6005SVincent Mailhol 
4341dfb6005SVincent Mailhol 	return es58x_send_msg(priv->es58x_dev, ES581_4_CAN_COMMAND_TYPE,
4351dfb6005SVincent Mailhol 			      ES581_4_CMD_ID_DISABLE_CHANNEL, &msg, sizeof(msg),
4361dfb6005SVincent Mailhol 			      priv->channel_idx);
4371dfb6005SVincent Mailhol }
4381dfb6005SVincent Mailhol 
es581_4_reset_device(struct es58x_device * es58x_dev)4391dfb6005SVincent Mailhol static int es581_4_reset_device(struct es58x_device *es58x_dev)
4401dfb6005SVincent Mailhol {
4411dfb6005SVincent Mailhol 	return es58x_send_msg(es58x_dev, ES581_4_CAN_COMMAND_TYPE,
4421dfb6005SVincent Mailhol 			      ES581_4_CMD_ID_RESET_DEVICE,
4431dfb6005SVincent Mailhol 			      ES58X_EMPTY_MSG, 0, ES58X_CHANNEL_IDX_NA);
4441dfb6005SVincent Mailhol }
4451dfb6005SVincent Mailhol 
es581_4_get_timestamp(struct es58x_device * es58x_dev)4461dfb6005SVincent Mailhol static int es581_4_get_timestamp(struct es58x_device *es58x_dev)
4471dfb6005SVincent Mailhol {
4481dfb6005SVincent Mailhol 	return es58x_send_msg(es58x_dev, ES581_4_CAN_COMMAND_TYPE,
4491dfb6005SVincent Mailhol 			      ES581_4_CMD_ID_TIMESTAMP,
4501dfb6005SVincent Mailhol 			      ES58X_EMPTY_MSG, 0, ES58X_CHANNEL_IDX_NA);
4511dfb6005SVincent Mailhol }
4521dfb6005SVincent Mailhol 
4531dfb6005SVincent Mailhol /* Nominal bittiming constants for ES581.4 as specified in the
4541dfb6005SVincent Mailhol  * microcontroller datasheet: "Stellaris(R) LM3S5B91 Microcontroller"
4551dfb6005SVincent Mailhol  * table 17-4 "CAN Protocol Ranges" from Texas Instruments.
4561dfb6005SVincent Mailhol  */
4571dfb6005SVincent Mailhol static const struct can_bittiming_const es581_4_bittiming_const = {
4581dfb6005SVincent Mailhol 	.name = "ES581.4",
4591dfb6005SVincent Mailhol 	.tseg1_min = 1,
4601dfb6005SVincent Mailhol 	.tseg1_max = 8,
4611dfb6005SVincent Mailhol 	.tseg2_min = 1,
4621dfb6005SVincent Mailhol 	.tseg2_max = 8,
4631dfb6005SVincent Mailhol 	.sjw_max = 4,
4641dfb6005SVincent Mailhol 	.brp_min = 1,
4651dfb6005SVincent Mailhol 	.brp_max = 128,
4661dfb6005SVincent Mailhol 	.brp_inc = 1
4671dfb6005SVincent Mailhol };
4681dfb6005SVincent Mailhol 
4691dfb6005SVincent Mailhol const struct es58x_parameters es581_4_param = {
4701dfb6005SVincent Mailhol 	.bittiming_const = &es581_4_bittiming_const,
4711dfb6005SVincent Mailhol 	.data_bittiming_const = NULL,
4721dfb6005SVincent Mailhol 	.tdc_const = NULL,
473330c6d3bSVincent Mailhol 	.bitrate_max = 1 * MEGA /* BPS */,
474330c6d3bSVincent Mailhol 	.clock = {.freq = 50 * MEGA /* Hz */},
4751dfb6005SVincent Mailhol 	.ctrlmode_supported = CAN_CTRLMODE_CC_LEN8_DLC,
4761dfb6005SVincent Mailhol 	.tx_start_of_frame = 0xAFAF,
4771dfb6005SVincent Mailhol 	.rx_start_of_frame = 0xFAFA,
4781dfb6005SVincent Mailhol 	.tx_urb_cmd_max_len = ES581_4_TX_URB_CMD_MAX_LEN,
4791dfb6005SVincent Mailhol 	.rx_urb_cmd_max_len = ES581_4_RX_URB_CMD_MAX_LEN,
4801dfb6005SVincent Mailhol 	/* Size of internal device TX queue is 330.
4811dfb6005SVincent Mailhol 	 *
4821dfb6005SVincent Mailhol 	 * However, we witnessed some ES58X_ERR_PROT_CRC errors from
4831dfb6005SVincent Mailhol 	 * the device and thus, echo_skb_max was lowered to the
4841dfb6005SVincent Mailhol 	 * empirical value of 75 which seems stable and then rounded
4851dfb6005SVincent Mailhol 	 * down to become a power of two.
4861dfb6005SVincent Mailhol 	 *
4871dfb6005SVincent Mailhol 	 * Root cause of those ES58X_ERR_PROT_CRC errors is still
4881dfb6005SVincent Mailhol 	 * unclear.
4891dfb6005SVincent Mailhol 	 */
4901dfb6005SVincent Mailhol 	.fifo_mask = 63, /* echo_skb_max = 64 */
4911dfb6005SVincent Mailhol 	.dql_min_limit = CAN_FRAME_LEN_MAX * 50, /* Empirical value. */
4921dfb6005SVincent Mailhol 	.tx_bulk_max = ES581_4_TX_BULK_MAX,
4931dfb6005SVincent Mailhol 	.urb_cmd_header_len = ES581_4_URB_CMD_HEADER_LEN,
4941dfb6005SVincent Mailhol 	.rx_urb_max = ES58X_RX_URBS_MAX,
4951dfb6005SVincent Mailhol 	.tx_urb_max = ES58X_TX_URBS_MAX
4961dfb6005SVincent Mailhol };
4971dfb6005SVincent Mailhol 
4981dfb6005SVincent Mailhol const struct es58x_operators es581_4_ops = {
4991dfb6005SVincent Mailhol 	.get_msg_len = es581_4_get_msg_len,
5001dfb6005SVincent Mailhol 	.handle_urb_cmd = es581_4_handle_urb_cmd,
5011dfb6005SVincent Mailhol 	.fill_urb_header = es581_4_fill_urb_header,
5021dfb6005SVincent Mailhol 	.tx_can_msg = es581_4_tx_can_msg,
5031dfb6005SVincent Mailhol 	.enable_channel = es581_4_enable_channel,
5041dfb6005SVincent Mailhol 	.disable_channel = es581_4_disable_channel,
5051dfb6005SVincent Mailhol 	.reset_device = es581_4_reset_device,
5061dfb6005SVincent Mailhol 	.get_timestamp = es581_4_get_timestamp
5071dfb6005SVincent Mailhol };
508