Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 May 2015 13:42:28 +0000 (UTC)
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r283612 - in head/sys/ofed: drivers/net/mlx4 include/linux/mlx4
Message-ID:  <201505271342.t4RDgSUG099180@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: glebius
Date: Wed May 27 13:42:28 2015
New Revision: 283612
URL: https://svnweb.freebsd.org/changeset/base/283612

Log:
  Add SIOCGI2C ioctl support to the driver. Would work only on ConnectX-3
  with fresh firmware. The low level code is based on code provided by
  Mellanox.
  
  Thanks to Mellanox and their distributor Must (http://mustcompany.ru)
  for providing hardware.
  
  In collaboration with:	Andre Melkoumian <andre mellanox.com>
  Reviewed by:		hselasky
  Sponsored by:		Netflix
  Sponsored by:		Nginx, Inc.

Modified:
  head/sys/ofed/drivers/net/mlx4/en_netdev.c
  head/sys/ofed/drivers/net/mlx4/port.c
  head/sys/ofed/include/linux/mlx4/device.h

Modified: head/sys/ofed/drivers/net/mlx4/en_netdev.c
==============================================================================
--- head/sys/ofed/drivers/net/mlx4/en_netdev.c	Wed May 27 13:27:39 2015	(r283611)
+++ head/sys/ofed/drivers/net/mlx4/en_netdev.c	Wed May 27 13:42:28 2015	(r283612)
@@ -1967,6 +1967,29 @@ static int mlx4_en_ioctl(struct ifnet *d
 		mutex_unlock(&mdev->state_lock);
 		VLAN_CAPABILITIES(dev);
 		break;
+	case SIOCGI2C: {
+		struct ifi2creq i2c;
+
+		error = copyin(ifr->ifr_data, &i2c, sizeof(i2c));
+		if (error)
+			break;
+		if (i2c.len > sizeof(i2c.data)) {
+			error = EINVAL;
+			break;
+		}
+		/*
+		 * Note that we ignore i2c.addr here. The driver hardcodes
+		 * the address to 0x50, while standard expects it to be 0xA0.
+		 */
+		error = mlx4_get_module_info(mdev->dev, priv->port,
+		    i2c.offset, i2c.len, i2c.data);
+		if (error < 0) {
+			error = -error;
+			break;
+		}
+		error = copyout(&i2c, ifr->ifr_data, sizeof(i2c));
+		break;
+	}
 	default:
 		error = ether_ioctl(dev, command, data);
 		break;

Modified: head/sys/ofed/drivers/net/mlx4/port.c
==============================================================================
--- head/sys/ofed/drivers/net/mlx4/port.c	Wed May 27 13:27:39 2015	(r283611)
+++ head/sys/ofed/drivers/net/mlx4/port.c	Wed May 27 13:42:28 2015	(r283612)
@@ -1058,3 +1058,160 @@ int mlx4_get_roce_gid_from_slave(struct 
 }
 EXPORT_SYMBOL(mlx4_get_roce_gid_from_slave);
 
+/* Cable Module Info */
+#define MODULE_INFO_MAX_READ 48
+
+#define I2C_ADDR_LOW  0x50
+#define I2C_ADDR_HIGH 0x51
+#define I2C_PAGE_SIZE 256
+
+/* Module Info Data */
+struct mlx4_cable_info {
+	u8	i2c_addr;
+	u8	page_num;
+	__be16	dev_mem_address;
+	__be16	reserved1;
+	__be16	size;
+	__be32	reserved2[2];
+	u8	data[MODULE_INFO_MAX_READ];
+};
+
+enum cable_info_err {
+	CABLE_INF_INV_PORT	= 0x1,
+	CABLE_INF_OP_NOSUP	= 0x2,
+	CABLE_INF_NOT_CONN	= 0x3,
+	CABLE_INF_NO_EEPRM	= 0x4,
+	CABLE_INF_PAGE_ERR	= 0x5,
+	CABLE_INF_INV_ADDR	= 0x6,
+	CABLE_INF_I2C_ADDR	= 0x7,
+	CABLE_INF_QSFP_VIO	= 0x8,
+	CABLE_INF_I2C_BUSY	= 0x9,
+};
+
+#define MAD_STATUS_2_CABLE_ERR(mad_status) ((mad_status >> 8) & 0xFF)
+
+#ifdef DEBUG
+static inline const char *cable_info_mad_err_str(u16 mad_status)
+{
+	u8 err = MAD_STATUS_2_CABLE_ERR(mad_status);
+
+	switch (err) {
+	case CABLE_INF_INV_PORT:
+		return "invalid port selected";
+	case CABLE_INF_OP_NOSUP:
+		return "operation not supported for this port (the port is of type CX4 or internal)";
+	case CABLE_INF_NOT_CONN:
+		return "cable is not connected";
+	case CABLE_INF_NO_EEPRM:
+		return "the connected cable has no EPROM (passive copper cable)";
+	case CABLE_INF_PAGE_ERR:
+		return "page number is greater than 15";
+	case CABLE_INF_INV_ADDR:
+		return "invalid device_address or size (that is, size equals 0 or address+size is greater than 256)";
+	case CABLE_INF_I2C_ADDR:
+		return "invalid I2C slave address";
+	case CABLE_INF_QSFP_VIO:
+		return "at least one cable violates the QSFP specification and ignores the modsel signal";
+	case CABLE_INF_I2C_BUSY:
+		return "I2C bus is constantly busy";
+	}
+	return "Unknown Error";
+}
+#endif /* DEBUG */
+
+/**
+ * mlx4_get_module_info - Read cable module eeprom data
+ * @dev: mlx4_dev.
+ * @port: port number.
+ * @offset: byte offset in eeprom to start reading data from.
+ * @size: num of bytes to read.
+ * @data: output buffer to put the requested data into.
+ *
+ * Reads cable module eeprom data, puts the outcome data into
+ * data pointer paramer.
+ * Returns num of read bytes on success or a negative error
+ * code.
+ */
+int mlx4_get_module_info(struct mlx4_dev *dev, u8 port, u16 offset,
+    u16 size, u8 *data)
+{
+	struct mlx4_cmd_mailbox *inbox, *outbox;
+	struct mlx4_mad_ifc *inmad, *outmad;
+	struct mlx4_cable_info *cable_info;
+	u16 i2c_addr;
+	int ret;
+
+	if (size > MODULE_INFO_MAX_READ)
+		size = MODULE_INFO_MAX_READ;
+
+	inbox = mlx4_alloc_cmd_mailbox(dev);
+	if (IS_ERR(inbox))
+		return PTR_ERR(inbox);
+
+	outbox = mlx4_alloc_cmd_mailbox(dev);
+	if (IS_ERR(outbox)) {
+		mlx4_free_cmd_mailbox(dev, inbox);
+		return PTR_ERR(outbox);
+	}
+
+	inmad = (struct mlx4_mad_ifc *)(inbox->buf);
+	outmad = (struct mlx4_mad_ifc *)(outbox->buf);
+
+	inmad->method = 0x1; /* Get */
+	inmad->class_version = 0x1;
+	inmad->mgmt_class = 0x1;
+	inmad->base_version = 0x1;
+	inmad->attr_id = cpu_to_be16(0xFF60); /* Module Info */
+
+	if (offset < I2C_PAGE_SIZE && offset + size > I2C_PAGE_SIZE)
+		/* Cross pages reads are not allowed
+		 * read until offset 256 in low page
+		 */
+		size -= offset + size - I2C_PAGE_SIZE;
+
+	i2c_addr = I2C_ADDR_LOW;
+	if (offset >= I2C_PAGE_SIZE) {
+		/* Reset offset to high page */
+		i2c_addr = I2C_ADDR_HIGH;
+		offset -= I2C_PAGE_SIZE;
+	}
+
+	cable_info = (struct mlx4_cable_info *)inmad->data;
+	cable_info->dev_mem_address = cpu_to_be16(offset);
+	cable_info->page_num = 0;
+	cable_info->i2c_addr = i2c_addr;
+	cable_info->size = cpu_to_be16(size);
+
+	ret = mlx4_cmd_box(dev, inbox->dma, outbox->dma, port, 3,
+	    MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE);
+	if (ret)
+		goto out;
+
+	if (be16_to_cpu(outmad->status)) {
+		/* Mad returned with bad status */
+		ret = be16_to_cpu(outmad->status);
+#ifdef DEBUG
+		mlx4_warn(dev, "MLX4_CMD_MAD_IFC Get Module info attr(%x) "
+		    "port(%d) i2c_addr(%x) offset(%d) size(%d): Response "
+		    "Mad Status(%x) - %s\n", 0xFF60, port, i2c_addr, offset,
+		    size, ret, cable_info_mad_err_str(ret));
+#endif
+		if (i2c_addr == I2C_ADDR_HIGH &&
+		    MAD_STATUS_2_CABLE_ERR(ret) == CABLE_INF_I2C_ADDR)
+			/* Some SFP cables do not support i2c slave
+			 * address 0x51 (high page), abort silently.
+			 */
+			ret = 0;
+		else
+			ret = -ret;
+		goto out;
+	}
+	cable_info = (struct mlx4_cable_info *)outmad->data;
+	memcpy(data, cable_info->data, size);
+	ret = size;
+out:
+	mlx4_free_cmd_mailbox(dev, inbox);
+	mlx4_free_cmd_mailbox(dev, outbox);
+	return ret;
+}
+EXPORT_SYMBOL(mlx4_get_module_info);

Modified: head/sys/ofed/include/linux/mlx4/device.h
==============================================================================
--- head/sys/ofed/include/linux/mlx4/device.h	Wed May 27 13:27:39 2015	(r283611)
+++ head/sys/ofed/include/linux/mlx4/device.h	Wed May 27 13:42:28 2015	(r283612)
@@ -892,6 +892,26 @@ struct mlx4_init_port_param {
 	u64			si_guid;
 };
 
+#define MAD_IFC_DATA_SZ 192
+/* MAD IFC Mailbox */
+struct mlx4_mad_ifc {
+	u8      base_version;
+	u8      mgmt_class;
+	u8      class_version;
+	u8      method;
+	__be16  status;
+	__be16  class_specific;
+	__be64  tid;
+	__be16  attr_id;
+	__be16  resv;
+	__be32  attr_mod;
+	__be64  mkey;
+	__be16  dr_slid;
+	__be16  dr_dlid;
+	u8      reserved[28];
+	u8      data[MAD_IFC_DATA_SZ];
+} __packed;
+
 #define mlx4_foreach_port(port, dev, type)				\
 	for ((port) = 1; (port) <= (dev)->caps.num_ports; (port)++)	\
 		if ((type) == (dev)->caps.port_mask[(port)])
@@ -1294,4 +1314,7 @@ int mlx4_read_clock(struct mlx4_dev *dev
 int mlx4_get_internal_clock_params(struct mlx4_dev *dev,
 				   struct mlx4_clock_params *params);
 
+int mlx4_get_module_info(struct mlx4_dev *dev, u8 port,
+			u16 offset, u16 size, u8 *data);
+
 #endif /* MLX4_DEVICE_H */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505271342.t4RDgSUG099180>