Takashi Iwai d3b2ab
From 35364f5b41a4917fe94a3f393d149b63ec583297 Mon Sep 17 00:00:00 2001
Takashi Iwai d3b2ab
From: Jimmy Assarsson <extja@kvaser.com>
Takashi Iwai d3b2ab
Date: Mon, 10 Oct 2022 20:52:28 +0200
Takashi Iwai d3b2ab
Subject: [PATCH] can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device
Takashi Iwai d3b2ab
Git-commit: 35364f5b41a4917fe94a3f393d149b63ec583297
Takashi Iwai d3b2ab
Patch-mainline: v6.2-rc1
Takashi Iwai d3b2ab
References: git-fixes
Takashi Iwai d3b2ab
Takashi Iwai d3b2ab
Use the CMD_GET_CAPABILITIES_REQ command to query the device for certain
Takashi Iwai d3b2ab
capabilities. We are only interested in LISTENONLY mode and wither the
Takashi Iwai d3b2ab
device reports CAN error counters.
Takashi Iwai d3b2ab
Takashi Iwai d3b2ab
Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices")
Takashi Iwai d3b2ab
Reported-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Takashi Iwai d3b2ab
Tested-by: Anssi Hannula <anssi.hannula@bitwise.fi>
Takashi Iwai d3b2ab
Signed-off-by: Jimmy Assarsson <extja@kvaser.com>
Takashi Iwai d3b2ab
Link: https://lore.kernel.org/all/20221010185237.319219-3-extja@kvaser.com
Takashi Iwai d3b2ab
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Takashi Iwai d3b2ab
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai d3b2ab
Takashi Iwai d3b2ab
---
Takashi Iwai d3b2ab
 .../net/can/usb/kvaser_usb/kvaser_usb_leaf.c  | 144 +++++++++++++++++-
Takashi Iwai d3b2ab
 1 file changed, 143 insertions(+), 1 deletion(-)
Takashi Iwai d3b2ab
Takashi Iwai d3b2ab
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Takashi Iwai d3b2ab
index 50f2ac8319ff..c87b13dc1048 100644
Takashi Iwai d3b2ab
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Takashi Iwai d3b2ab
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
Takashi Iwai d3b2ab
@@ -74,6 +74,8 @@
Takashi Iwai d3b2ab
 #define CMD_TX_ACKNOWLEDGE		50
Takashi Iwai d3b2ab
 #define CMD_CAN_ERROR_EVENT		51
Takashi Iwai d3b2ab
 #define CMD_FLUSH_QUEUE_REPLY		68
Takashi Iwai d3b2ab
+#define CMD_GET_CAPABILITIES_REQ	95
Takashi Iwai d3b2ab
+#define CMD_GET_CAPABILITIES_RESP	96
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
 #define CMD_LEAF_LOG_MESSAGE		106
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
@@ -83,6 +85,8 @@
Takashi Iwai d3b2ab
 #define KVASER_USB_LEAF_SWOPTION_FREQ_32_MHZ_CLK BIT(5)
Takashi Iwai d3b2ab
 #define KVASER_USB_LEAF_SWOPTION_FREQ_24_MHZ_CLK BIT(6)
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
+#define KVASER_USB_LEAF_SWOPTION_EXT_CAP BIT(12)
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
 /* error factors */
Takashi Iwai d3b2ab
 #define M16C_EF_ACKE			BIT(0)
Takashi Iwai d3b2ab
 #define M16C_EF_CRCE			BIT(1)
Takashi Iwai d3b2ab
@@ -278,6 +282,28 @@ struct leaf_cmd_log_message {
Takashi Iwai d3b2ab
 	u8 data[8];
Takashi Iwai d3b2ab
 } __packed;
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
+/* Sub commands for cap_req and cap_res */
Takashi Iwai d3b2ab
+#define KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE 0x02
Takashi Iwai d3b2ab
+#define KVASER_USB_LEAF_CAP_CMD_ERR_REPORT 0x05
Takashi Iwai d3b2ab
+struct kvaser_cmd_cap_req {
Takashi Iwai d3b2ab
+	__le16 padding0;
Takashi Iwai d3b2ab
+	__le16 cap_cmd;
Takashi Iwai d3b2ab
+	__le16 padding1;
Takashi Iwai d3b2ab
+	__le16 channel;
Takashi Iwai d3b2ab
+} __packed;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+/* Status codes for cap_res */
Takashi Iwai d3b2ab
+#define KVASER_USB_LEAF_CAP_STAT_OK 0x00
Takashi Iwai d3b2ab
+#define KVASER_USB_LEAF_CAP_STAT_NOT_IMPL 0x01
Takashi Iwai d3b2ab
+#define KVASER_USB_LEAF_CAP_STAT_UNAVAIL 0x02
Takashi Iwai d3b2ab
+struct kvaser_cmd_cap_res {
Takashi Iwai d3b2ab
+	__le16 padding;
Takashi Iwai d3b2ab
+	__le16 cap_cmd;
Takashi Iwai d3b2ab
+	__le16 status;
Takashi Iwai d3b2ab
+	__le32 mask;
Takashi Iwai d3b2ab
+	__le32 value;
Takashi Iwai d3b2ab
+} __packed;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
 struct kvaser_cmd {
Takashi Iwai d3b2ab
 	u8 len;
Takashi Iwai d3b2ab
 	u8 id;
Takashi Iwai d3b2ab
@@ -295,6 +321,8 @@ struct kvaser_cmd {
Takashi Iwai d3b2ab
 			struct leaf_cmd_chip_state_event chip_state_event;
Takashi Iwai d3b2ab
 			struct leaf_cmd_error_event error_event;
Takashi Iwai d3b2ab
 			struct leaf_cmd_log_message log_message;
Takashi Iwai d3b2ab
+			struct kvaser_cmd_cap_req cap_req;
Takashi Iwai d3b2ab
+			struct kvaser_cmd_cap_res cap_res;
Takashi Iwai d3b2ab
 		} __packed leaf;
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
 		union {
Takashi Iwai d3b2ab
@@ -324,6 +352,7 @@ static const u8 kvaser_usb_leaf_cmd_sizes_leaf[] = {
Takashi Iwai d3b2ab
 	[CMD_LEAF_LOG_MESSAGE]		= kvaser_fsize(u.leaf.log_message),
Takashi Iwai d3b2ab
 	[CMD_CHIP_STATE_EVENT]		= kvaser_fsize(u.leaf.chip_state_event),
Takashi Iwai d3b2ab
 	[CMD_CAN_ERROR_EVENT]		= kvaser_fsize(u.leaf.error_event),
Takashi Iwai d3b2ab
+	[CMD_GET_CAPABILITIES_RESP]	= kvaser_fsize(u.leaf.cap_res),
Takashi Iwai d3b2ab
 	/* ignored events: */
Takashi Iwai d3b2ab
 	[CMD_FLUSH_QUEUE_REPLY]		= CMD_SIZE_ANY,
Takashi Iwai d3b2ab
 };
Takashi Iwai d3b2ab
@@ -606,6 +635,9 @@ static void kvaser_usb_leaf_get_software_info_leaf(struct kvaser_usb *dev,
Takashi Iwai d3b2ab
 	dev->fw_version = le32_to_cpu(softinfo->fw_version);
Takashi Iwai d3b2ab
 	dev->max_tx_urbs = le16_to_cpu(softinfo->max_outstanding_tx);
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
+	if (sw_options & KVASER_USB_LEAF_SWOPTION_EXT_CAP)
Takashi Iwai d3b2ab
+		dev->card_data.capabilities |= KVASER_USB_CAP_EXT_CAP;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
 	if (dev->driver_info->quirks & KVASER_USB_QUIRK_IGNORE_CLK_FREQ) {
Takashi Iwai d3b2ab
 		/* Firmware expects bittiming parameters calculated for 16MHz
Takashi Iwai d3b2ab
 		 * clock, regardless of the actual clock
Takashi Iwai d3b2ab
@@ -693,6 +725,116 @@ static int kvaser_usb_leaf_get_card_info(struct kvaser_usb *dev)
Takashi Iwai d3b2ab
 	return 0;
Takashi Iwai d3b2ab
 }
Takashi Iwai d3b2ab
 
Takashi Iwai d3b2ab
+static int kvaser_usb_leaf_get_single_capability(struct kvaser_usb *dev,
Takashi Iwai d3b2ab
+						 u16 cap_cmd_req, u16 *status)
Takashi Iwai d3b2ab
+{
Takashi Iwai d3b2ab
+	struct kvaser_usb_dev_card_data *card_data = &dev->card_data;
Takashi Iwai d3b2ab
+	struct kvaser_cmd *cmd;
Takashi Iwai d3b2ab
+	u32 value = 0;
Takashi Iwai d3b2ab
+	u32 mask = 0;
Takashi Iwai d3b2ab
+	u16 cap_cmd_res;
Takashi Iwai d3b2ab
+	int err;
Takashi Iwai d3b2ab
+	int i;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
Takashi Iwai d3b2ab
+	if (!cmd)
Takashi Iwai d3b2ab
+		return -ENOMEM;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	cmd->id = CMD_GET_CAPABILITIES_REQ;
Takashi Iwai d3b2ab
+	cmd->u.leaf.cap_req.cap_cmd = cpu_to_le16(cap_cmd_req);
Takashi Iwai d3b2ab
+	cmd->len = CMD_HEADER_LEN + sizeof(struct kvaser_cmd_cap_req);
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	err = kvaser_usb_send_cmd(dev, cmd, cmd->len);
Takashi Iwai d3b2ab
+	if (err)
Takashi Iwai d3b2ab
+		goto end;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	err = kvaser_usb_leaf_wait_cmd(dev, CMD_GET_CAPABILITIES_RESP, cmd);
Takashi Iwai d3b2ab
+	if (err)
Takashi Iwai d3b2ab
+		goto end;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	*status = le16_to_cpu(cmd->u.leaf.cap_res.status);
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	if (*status != KVASER_USB_LEAF_CAP_STAT_OK)
Takashi Iwai d3b2ab
+		goto end;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	cap_cmd_res = le16_to_cpu(cmd->u.leaf.cap_res.cap_cmd);
Takashi Iwai d3b2ab
+	switch (cap_cmd_res) {
Takashi Iwai d3b2ab
+	case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE:
Takashi Iwai d3b2ab
+	case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT:
Takashi Iwai d3b2ab
+		value = le32_to_cpu(cmd->u.leaf.cap_res.value);
Takashi Iwai d3b2ab
+		mask = le32_to_cpu(cmd->u.leaf.cap_res.mask);
Takashi Iwai d3b2ab
+		break;
Takashi Iwai d3b2ab
+	default:
Takashi Iwai d3b2ab
+		dev_warn(&dev->intf->dev, "Unknown capability command %u\n",
Takashi Iwai d3b2ab
+			 cap_cmd_res);
Takashi Iwai d3b2ab
+		break;
Takashi Iwai d3b2ab
+	}
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	for (i = 0; i < dev->nchannels; i++) {
Takashi Iwai d3b2ab
+		if (BIT(i) & (value & mask)) {
Takashi Iwai d3b2ab
+			switch (cap_cmd_res) {
Takashi Iwai d3b2ab
+			case KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE:
Takashi Iwai d3b2ab
+				card_data->ctrlmode_supported |=
Takashi Iwai d3b2ab
+						CAN_CTRLMODE_LISTENONLY;
Takashi Iwai d3b2ab
+				break;
Takashi Iwai d3b2ab
+			case KVASER_USB_LEAF_CAP_CMD_ERR_REPORT:
Takashi Iwai d3b2ab
+				card_data->capabilities |=
Takashi Iwai d3b2ab
+						KVASER_USB_CAP_BERR_CAP;
Takashi Iwai d3b2ab
+				break;
Takashi Iwai d3b2ab
+			}
Takashi Iwai d3b2ab
+		}
Takashi Iwai d3b2ab
+	}
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+end:
Takashi Iwai d3b2ab
+	kfree(cmd);
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	return err;
Takashi Iwai d3b2ab
+}
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+static int kvaser_usb_leaf_get_capabilities_leaf(struct kvaser_usb *dev)
Takashi Iwai d3b2ab
+{
Takashi Iwai d3b2ab
+	int err;
Takashi Iwai d3b2ab
+	u16 status;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	if (!(dev->card_data.capabilities & KVASER_USB_CAP_EXT_CAP)) {
Takashi Iwai d3b2ab
+		dev_info(&dev->intf->dev,
Takashi Iwai d3b2ab
+			 "No extended capability support. Upgrade device firmware.\n");
Takashi Iwai d3b2ab
+		return 0;
Takashi Iwai d3b2ab
+	}
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	err = kvaser_usb_leaf_get_single_capability(dev,
Takashi Iwai d3b2ab
+						    KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE,
Takashi Iwai d3b2ab
+						    &status);
Takashi Iwai d3b2ab
+	if (err)
Takashi Iwai d3b2ab
+		return err;
Takashi Iwai d3b2ab
+	if (status)
Takashi Iwai d3b2ab
+		dev_info(&dev->intf->dev,
Takashi Iwai d3b2ab
+			 "KVASER_USB_LEAF_CAP_CMD_LISTEN_MODE failed %u\n",
Takashi Iwai d3b2ab
+			 status);
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	err = kvaser_usb_leaf_get_single_capability(dev,
Takashi Iwai d3b2ab
+						    KVASER_USB_LEAF_CAP_CMD_ERR_REPORT,
Takashi Iwai d3b2ab
+						    &status);
Takashi Iwai d3b2ab
+	if (err)
Takashi Iwai d3b2ab
+		return err;
Takashi Iwai d3b2ab
+	if (status)
Takashi Iwai d3b2ab
+		dev_info(&dev->intf->dev,
Takashi Iwai d3b2ab
+			 "KVASER_USB_LEAF_CAP_CMD_ERR_REPORT failed %u\n",
Takashi Iwai d3b2ab
+			 status);
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	return 0;
Takashi Iwai d3b2ab
+}
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+static int kvaser_usb_leaf_get_capabilities(struct kvaser_usb *dev)
Takashi Iwai d3b2ab
+{
Takashi Iwai d3b2ab
+	int err = 0;
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	if (dev->driver_info->family == KVASER_LEAF)
Takashi Iwai d3b2ab
+		err = kvaser_usb_leaf_get_capabilities_leaf(dev);
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
+	return err;
Takashi Iwai d3b2ab
+}
Takashi Iwai d3b2ab
+
Takashi Iwai d3b2ab
 static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev,
Takashi Iwai d3b2ab
 					   const struct kvaser_cmd *cmd)
Takashi Iwai d3b2ab
 {
Takashi Iwai d3b2ab
@@ -1486,7 +1628,7 @@ const struct kvaser_usb_dev_ops kvaser_usb_leaf_dev_ops = {
Takashi Iwai d3b2ab
 	.dev_get_software_info = kvaser_usb_leaf_get_software_info,
Takashi Iwai d3b2ab
 	.dev_get_software_details = NULL,
Takashi Iwai d3b2ab
 	.dev_get_card_info = kvaser_usb_leaf_get_card_info,
Takashi Iwai d3b2ab
-	.dev_get_capabilities = NULL,
Takashi Iwai d3b2ab
+	.dev_get_capabilities = kvaser_usb_leaf_get_capabilities,
Takashi Iwai d3b2ab
 	.dev_set_opt_mode = kvaser_usb_leaf_set_opt_mode,
Takashi Iwai d3b2ab
 	.dev_start_chip = kvaser_usb_leaf_start_chip,
Takashi Iwai d3b2ab
 	.dev_stop_chip = kvaser_usb_leaf_stop_chip,
Takashi Iwai d3b2ab
-- 
Takashi Iwai d3b2ab
2.35.3
Takashi Iwai d3b2ab