Skip to contentSkip to conversation

i2c: Adding support for Intel iSMT SMBus 2.0 host controller

PATCH In review Bill E Brown 5 participants 27 messages Dec 7, 2012 – Mar 5, 2013 19 messages not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH v4 1/1 i2c: Adding support for Intel iSMT SMBus 2.0 host controller

From: Bill Brown <[email protected]>

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

---
 Documentation/i2c/busses/i2c-ismt |   36 ++
 drivers/i2c/busses/Kconfig        |   10 +
 drivers/i2c/busses/Makefile       |    1 +
 drivers/i2c/busses/i2c-ismt.c     |  911 +++++++++++++++++++++++++++++++++++++
 4 files changed, 958 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/i2c/busses/i2c-ismt
 create mode 100644 drivers/i2c/busses/i2c-ismt.c
Documentation/i2c/busses/i2c-ismt new file +36 -0
new file mode 100644
index 0000000..ed6375c
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Available bus frequency settings:
+  0  no change
+  1  80 kHz
+  2  100 kHz
+  3  400 kHz
+  4  1 MHz
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci will show
+something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Device 0xc59
+  00:13.1 System peripheral: Intel Corporation Device 0xc5a
+
drivers/i2c/busses/Kconfig +10 -0
index 7244c8b..64d5756 100644
@@ -119,6 +119,16 @@ config I2C_ISCH
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-isch.
 
+config I2C_ISMT
+	tristate "Intel iSMT SMBus Controller"
+	depends on PCI
+	help
+	  If you say yes to this option, support will be included for the Intel
+	  iSMT SMBus host controller interface.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called i2c-ismt.
+
 config I2C_PIIX4
 	tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
 	depends on PCI
drivers/i2c/busses/Makefile +1 -0
index ce3c2be..527a1f1 100644
@@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882)	+= i2c-amd756-s4882.o
 obj-$(CONFIG_I2C_AMD8111)	+= i2c-amd8111.o
 obj-$(CONFIG_I2C_I801)		+= i2c-i801.o
 obj-$(CONFIG_I2C_ISCH)		+= i2c-isch.o
+obj-$(CONFIG_I2C_ISMT)		+= i2c-ismt.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_NFORCE2_S4985)	+= i2c-nforce2-s4985.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
drivers/i2c/busses/i2c-ismt.c new file +911 -0

911 lines not on this page · raw message

Signature · 1 line
1.6.2.5

A message that is not in the archive (sent off-list or not yet received): 1354840604-8160-1-git-send-email-bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org

#2 lore raw
Quoted 11 lines

-----Original Message-----
From: [email protected] [mailto:linux-i2c- [email protected]] On Behalf Of Bill E Brown
Sent: Thursday, December 06, 2012 5:37 PM
To: Wolfram Sang
Cc: Jean Delvare; Heasley, Seth; [email protected]; Brown, Bill E
Subject: [PATCH v4] i2c: Adding support for Intel iSMT SMBus 2.0 host
controller

Quoted 9 lines

From: Bill Brown <[email protected]>

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Forgot to run checkpatch on v3, please use v4.

Bill

Quoted 1,046 lines
---
 Documentation/i2c/busses/i2c-ismt |   36 ++
 drivers/i2c/busses/Kconfig        |   10 +
 drivers/i2c/busses/Makefile       |    1 +
 drivers/i2c/busses/i2c-ismt.c     |  911
+++++++++++++++++++++++++++++++++++++
 4 files changed, 958 insertions(+), 0 deletions(-)  create mode 100644

Documentation/i2c/busses/i2c-ismt create mode 100644 drivers/i2c/busses/i2c-ismt.c

Documentation/i2c/busses/i2c-ismt new file +16 -0
new file mode 100644
index 0000000..ed6375c
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by
+the BIOS and never needs to be changed.  However, some SMBus analyzers
+are too slow for monitoring the bus during debug, thus the need for this

module parameter.

Documentation/i2c/busses/i2c-ismt new file +20 -0
+Available bus frequency settings:
+  0  no change
+  1  80 kHz
+  2  100 kHz
+  3  400 kHz
+  4  1 MHz
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0
+controllers targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci
+will show something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Device 0xc59
+  00:13.1 System peripheral: Intel Corporation Device 0xc5a
+
drivers/i2c/busses/Kconfig index renamed from drivers/i2c/busses/Kconfig +0 -0

7244c8b..64d5756 100644

drivers/i2c/busses/Kconfig +10 -0
@@ -119,6 +119,16 @@ config I2C_ISCH
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-isch.
 
+config I2C_ISMT
+	tristate "Intel iSMT SMBus Controller"
+	depends on PCI
+	help
+	  If you say yes to this option, support will be included for the Intel
+	  iSMT SMBus host controller interface.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called i2c-ismt.
+
 config I2C_PIIX4
 	tristate "Intel PIIX4 and compatible

(ATI/AMD/Serverworks/Broadcom/SMSC)"

drivers/i2c/busses/Kconfig +0 -0
 	depends on PCI
drivers/i2c/busses/Makefile index renamed from drivers/i2c/busses/Makefile +0 -0

ce3c2be..527a1f1 100644

drivers/i2c/busses/Makefile +0 -0
@@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882)	+= i2c-

amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o

drivers/i2c/busses/Makefile +1 -0
+obj-$(CONFIG_I2C_ISMT)		+= i2c-ismt.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_NFORCE2_S4985)	+= i2c-nforce2-s4985.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
drivers/i2c/busses/i2c-ismt.c new renamed from drivers/i2c/busses/i2c-ismt.c +0 -0

file mode 100644 index 0000000..17b1bdd

drivers/i2c/busses/i2c-ismt.c new file +15 -0
@@ -0,0 +1,911 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2012 Intel Corporation. All rights reserved.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * BSD LICENSE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright

+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.

+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */

drivers/i2c/busses/i2c-ismt.c new file +33 -0
+
+/*
+ *  Supports the SMBus Message Transport (SMT) in the Intel Atom
+Processor
+ *  S12xx Product Family.
+ *
+ *  Features supported by this driver:
+ *  Hardware PEC                     yes
+ *  Block buffer                     yes
+ *  Block process call transaction   no
+ *  Slave mode                       no
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+
+/* PCI Address Constants */
+#define SMBBAR		0
+
+/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */
+#define PCI_DEVICE_ID_INTEL_S1200_SMB_SMT0	0x0c59
+#define PCI_DEVICE_ID_INTEL_S1200_SMB_SMT1	0x0c5a
+
+#define ISMT_DESC_ENTRIES	32	/* number of descriptor entries */
+#define ISMT_MAX_RETRIES	3	/* number of SMBus retries to

attempt */

drivers/i2c/busses/i2c-ismt.c new file +5 -0
+
+/* Hardware Descriptor Constants - Control Field */
+#define ISMT_DESC_CWRL	0x01	/* Command/Write Length */
+#define ISMT_DESC_BLK	0X04	/* Perform Block Transaction */
+#define ISMT_DESC_FAIR	0x08	/* Set fairness flag upon successful

arbit. */

drivers/i2c/busses/i2c-ismt.c new file +20 -0
+#define ISMT_DESC_PEC	0x10	/* Packet Error Code */
+#define ISMT_DESC_I2C	0x20	/* I2C Enable */
+#define ISMT_DESC_INT	0x40	/* Interrupt */
+#define ISMT_DESC_SOE	0x80	/* Stop On Error */
+
+/* Hardware Descriptor Constants - Status Field */
+#define ISMT_DESC_SCS	0x01	/* Success */
+#define ISMT_DESC_DLTO	0x04	/* Data Low Time Out */
+#define ISMT_DESC_NAK	0x08	/* NAK Received */
+#define ISMT_DESC_CRC	0x10	/* CRC Error */
+#define ISMT_DESC_CLTO	0x20	/* Clock Low Time Out */
+#define ISMT_DESC_COL	0x40	/* Collisions */
+#define ISMT_DESC_LPR	0x80	/* Large Packet Received */
+
+/* Macros */
+#define ISMT_DESC_ADDR_RW(addr, rw) (((addr) << 1) | (rw))
+
+/* iSMT General Register address offsets (SMBBAR + <addr>) */
+#define ISMT_GR_GCTRL		0x000	/* General Control */
+#define ISMT_GR_SMTICL		0x008	/* SMT Interrupt Cause

Location */

drivers/i2c/busses/i2c-ismt.c new file +7 -0
+#define ISMT_GR_ERRINTMSK	0x010	/* Error Interrupt Mask */
+#define ISMT_GR_ERRAERMSK	0x014	/* Error AER Mask */
+#define ISMT_GR_ERRSTS		0x018	/* Error Status */
+#define ISMT_GR_ERRINFO		0x01c	/* Error Information */
+
+/* iSMT Master Registers */
+#define ISMT_MSTR_MDBA		0x100	/* Master Descriptor Base

Address */

drivers/i2c/busses/i2c-ismt.c new file +16 -0
+#define ISMT_MSTR_MCTRL		0x108	/* Master Control */
+#define ISMT_MSTR_MSTS		0x10c	/* Master Status */
+#define ISMT_MSTR_MDS		0x110	/* Master Descriptor Size */
+#define ISMT_MSTR_RPOLICY	0x114	/* Retry Policy */
+
+/* iSMT Miscellaneous Registers */
+#define ISMT_SPGT	0x300	/* SMBus PHY Global Timing */
+
+/* General Control Register (GCTRL) bit definitions */
+#define ISMT_GCTRL_TRST	0x04	/* Target Reset */
+#define ISMT_GCTRL_KILL	0x08	/* Kill */
+#define ISMT_GCTRL_SRST	0x40	/* Soft Reset */
+
+/* Master Control Register (MCTRL) bit definitions */
+#define ISMT_MCTRL_SS	0x01		/* Start/Stop */
+#define ISMT_MCTRL_MEIE	0x10		/* Master Error Interrupt

Enable */
+#define ISMT_MCTRL_FMHP 0x00ff0000 /* Firmware Master Head Ptr
(FMHP) */

drivers/i2c/busses/i2c-ismt.c new file +3 -0
+
+/* Master Status Register (MSTS) bit definitions */
+#define ISMT_MSTS_HMTP	0xff0000	/* HW Master Tail Pointer

(HMTP) */
+#define ISMT_MSTS_MIS 0x20 /* Master Interrupt Status
(MIS) */
+#define ISMT_MSTS_MEIS 0x10 /* Master Error Int Status
(MEIS) */

drivers/i2c/busses/i2c-ismt.c new file +4 -0
+#define ISMT_MSTS_IP	0x01		/* In Progress */
+
+/* Master Descriptor Size (MDS) bit definitions */
+#define ISMT_MDS_MASK	0xff	/* Master Descriptor Size mask

(MDS) */

drivers/i2c/busses/i2c-ismt.c new file +3 -0
+
+/* SMBus PHY Global Timing Register (SPGT) bit definitions */
+#define ISMT_SPGT_SPD_MASK	0xc0000000	/* SMBus Speed

mask */

drivers/i2c/busses/i2c-ismt.c new file +29 -0
+#define ISMT_SPGT_SPD_80K	0x00		/* 80 kHz */
+#define ISMT_SPGT_SPD_100K	(0x1 << 30)	/* 100 kHz */
+#define ISMT_SPGT_SPD_400K	(0x2 << 30)	/* 400 kHz */
+#define ISMT_SPGT_SPD_1M	(0x3 << 30)	/* 1 MHz */
+
+
+/* MSI Control Register (MSICTL) bit definitions */
+#define ISMT_MSICTL_MSIE	0x01	/* MSI Enable */
+
+/* iSMT Hardware Descriptor */
+struct ismt_desc {
+	u8 tgtaddr_rw;	/* target address & r/w bit */
+	u8 wr_len_cmd;	/* write length in bytes or a command */
+	u8 rd_len;	/* read length */
+	u8 control;	/* control bits */
+	u8 status;	/* status bits */
+	u8 retry;	/* collision retry and retry count */
+	u8 rxbytes;	/* received bytes */
+	u8 txbytes;	/* transmitted bytes */
+	u32 dptr_low;	/* lower 32 bit of the data pointer */
+	u32 dptr_high;	/* upper 32 bit of the data pointer */
+} __packed;
+
+struct ismt_priv {
+	struct i2c_adapter adapter;
+	void *smba;				/* PCI BAR */
+	struct pci_dev *pci_dev;
+	struct ismt_desc *hw;			/* descriptor virt base addr */
+	dma_addr_t io_rng_dma;			/* descriptor HW

base addr */

drivers/i2c/busses/i2c-ismt.c new file +3 -0
+	u8 head;				/* ring buffer head pointer */
+	struct completion cmp;			/* interrupt completion */
+	u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 3];	/* temp R/W data

buffer */

drivers/i2c/busses/i2c-ismt.c new file +1 -0
+	bool using_msi;				/* type of interrupt

flag */

drivers/i2c/busses/i2c-ismt.c new file +6 -0
+};
+
+/**
+ * ismt_ids - PCI device IDs supported by this driver  */ static const
+DEFINE_PCI_DEVICE_TABLE(ismt_ids) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,

PCI_DEVICE_ID_INTEL_S1200_SMB_SMT0) },

drivers/i2c/busses/i2c-ismt.c new file +1 -0
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,

PCI_DEVICE_ID_INTEL_S1200_SMB_SMT1) },

drivers/i2c/busses/i2c-ismt.c new file +240 -0
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, ismt_ids);
+
+/* Bus speed control bits for slow debuggers - refer to the docs for
+usage */ static unsigned int bus_speed; module_param(bus_speed, uint,
+S_IRUGO); MODULE_PARM_DESC(bus_speed, "Bus Speed");
+
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+/**
+ * ismt_desc_dump() - dump the contents of a descriptor for debug
+purposes
+ * @priv: iSMT private data
+ */
+static void ismt_desc_dump(struct ismt_priv *priv) {
+	struct device *dev = &priv->pci_dev->dev;
+	struct ismt_desc *desc = &priv->hw[priv->head];
+
+	dev_dbg(dev, "Dump of the descriptor struct:  0x%X\n", priv->head);
+	dev_dbg(dev, "\ttgtaddr_rw=0x%02X\n", desc->tgtaddr_rw);
+	dev_dbg(dev, "\twr_len_cmd=0x%02X\n", desc->wr_len_cmd);
+	dev_dbg(dev, "\trd_len=    0x%02X\n", desc->rd_len);
+	dev_dbg(dev, "\tcontrol=   0x%02X\n", desc->control);
+	dev_dbg(dev, "\tstatus=    0x%02X\n", desc->status);
+	dev_dbg(dev, "\tretry=     0x%02X\n", desc->retry);
+	dev_dbg(dev, "\trxbytes=   0x%02X\n", desc->rxbytes);
+	dev_dbg(dev, "\ttxbytes=   0x%02X\n", desc->txbytes);
+	dev_dbg(dev, "\tdptr_low=  0x%08X\n", desc->dptr_low);
+	dev_dbg(dev, "\tdptr_high= 0x%08X\n", desc->dptr_high); }
+
+/**
+ * ismt_gen_reg_dump() - dump the iSMT General Registers
+ * @priv: iSMT private data
+ */
+static void ismt_gen_reg_dump(struct ismt_priv *priv) {
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT General Registers\n");
+	dev_dbg(dev, "  GCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_GCTRL,
+		readl(priv->smba + ISMT_GR_GCTRL));
+	dev_dbg(dev, "  SMTICL... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_GR_SMTICL,
+		readq(priv->smba + ISMT_GR_SMTICL));
+	dev_dbg(dev, "  ERRINTMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINTMSK,
+		readl(priv->smba + ISMT_GR_ERRINTMSK));
+	dev_dbg(dev, "  ERRAERMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRAERMSK,
+		readl(priv->smba + ISMT_GR_ERRAERMSK));
+	dev_dbg(dev, "  ERRSTS... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRSTS,
+		readl(priv->smba + ISMT_GR_ERRSTS));
+	dev_dbg(dev, "  ERRINFO.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINFO,
+		readl(priv->smba + ISMT_GR_ERRINFO)); }
+
+/**
+ * ismt_mstr_reg_dump() - dump the iSMT Master Registers
+ * @priv: iSMT private data
+ */
+static void ismt_mstr_reg_dump(struct ismt_priv *priv) {
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT Master Registers\n");
+	dev_dbg(dev, "  MDBA..... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_MSTR_MDBA,
+		readq(priv->smba + ISMT_MSTR_MDBA));
+	dev_dbg(dev, "  MCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MCTRL,
+		readl(priv->smba + ISMT_MSTR_MCTRL));
+	dev_dbg(dev, "  MSTS..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MSTS,
+		readl(priv->smba + ISMT_MSTR_MSTS));
+	dev_dbg(dev, "  MDS...... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MDS,
+		readl(priv->smba + ISMT_MSTR_MDS));
+	dev_dbg(dev, "  RPOLICY.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_RPOLICY,
+		readl(priv->smba + ISMT_MSTR_RPOLICY));
+	dev_dbg(dev, "  SPGT..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_SPGT,
+		readl(priv->smba + ISMT_SPGT));
+}
+#endif
+
+/**
+ * ismt_submit_desc() - add a descriptor to the ring
+ * @priv: iSMT private data
+ */
+static void ismt_submit_desc(struct ismt_priv *priv) {
+	uint fmhp;
+	uint val;
+
+	ismt_desc_dump(priv);
+	ismt_gen_reg_dump(priv);
+	ismt_mstr_reg_dump(priv);
+
+	/* Set the FMHP (Firmware Master Head Pointer)*/
+	fmhp = ((priv->head + 1) % ISMT_DESC_ENTRIES) << 16;
+	val = readl(priv->smba + ISMT_MSTR_MCTRL);
+	writel((val & ~ISMT_MCTRL_FMHP) | fmhp,
+	       priv->smba + ISMT_MSTR_MCTRL);
+
+	/* Set the start bit */
+	val = readl(priv->smba + ISMT_MSTR_MCTRL);
+	writel(val | ISMT_MCTRL_SS,
+	       priv->smba + ISMT_MSTR_MCTRL);
+}
+
+/**
+ * ismt_process_desc() - handle the completion of the descriptor
+ * @desc: the iSMT hardware descriptor
+ * @data: data buffer from the upper layer
+ * @dma_buffer: temporary buffer for the DMA engine
+ * @size: SMBus transaction type
+ */
+static int ismt_process_desc(const struct ismt_desc *desc,
+			     union i2c_smbus_data *data,
+			     u8 *dma_buffer, int size)
+{
+	if (likely(desc->status & ISMT_DESC_SCS)) {
+		if (size != I2C_SMBUS_QUICK)
+			memcpy(data, dma_buffer, sizeof(*data));
+		return 0;
+	}
+
+	if (likely(desc->status & ISMT_DESC_NAK))
+		return -ENXIO;
+
+	if (desc->status & ISMT_DESC_CRC)
+		return -EBADMSG;
+
+	if (desc->status & ISMT_DESC_COL)
+		return -EAGAIN;
+
+	if (desc->status & ISMT_DESC_LPR)
+		return -EPROTO;
+
+	if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO))
+		return -ETIMEDOUT;
+
+	return -EIO;
+}
+
+/**
+ * ismt_access() - process an SMBus command
+ * @adap: the i2c host adapter
+ * @addr: address of the i2c/SMBus target
+ * @flags: command options
+ * @read_write: read from or write to device
+ * @command: the i2c/SMBus command to issue
+ * @size: SMBus transaction type
+ * @data: read/write data buffer
+ */
+static int ismt_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data *data) {
+	int ret;
+	dma_addr_t dma_addr = 0; /* address of the data buffer */
+	u8 dma_size = 0;
+	enum dma_data_direction dma_direction = 0;
+	struct ismt_desc *desc;
+	struct ismt_priv *priv = i2c_get_adapdata(adap);
+	struct device *dev = &priv->pci_dev->dev;
+
+	desc = &priv->hw[priv->head];
+
+	/* Initialize the descriptor */
+	memset(desc, 0, sizeof(struct ismt_desc));
+	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
+
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if (size != I2C_SMBUS_QUICK) {
+		memcpy(priv->dma_buffer + 1, data, sizeof(*data));
+		priv->dma_buffer[0] = command;
+	}
+
+	/* Initialize common control bits */
+	if (likely(priv->using_msi))
+		desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR;
+	else
+		desc->control = ISMT_DESC_FAIR;
+
+	if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK)
+	    && (size != I2C_SMBUS_I2C_BLOCK_DATA))
+		desc->control |= ISMT_DESC_PEC;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		dev_dbg(dev, "I2C_SMBUS_QUICK\n");
+		break;
+
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Send Byte
+			 * The command field contains the write data
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  WRITE\n");
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+		} else {
+			/* Receive Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  READ\n");
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = 1;
+		}
+
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Write Byte
+			 * Command plus 1 data byte
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  WRITE\n");
+			desc->wr_len_cmd = 2;
+			dma_size = 2;
+			dma_direction = DMA_TO_DEVICE;
+		} else {
+			/* Read Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  READ\n");
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+			desc->rd_len = 1;
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+		}
+
+		break;
+
+	case I2C_SMBUS_WORD_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* Write Word */
+			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:

WRITE\n");

drivers/i2c/busses/i2c-ismt.c new file +6 -0
+			desc->wr_len_cmd = 3;
+			dma_size = 3;
+			dma_direction = DMA_TO_DEVICE;
+		} else {
+			/* Read Word */
+			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:

READ\n");

drivers/i2c/busses/i2c-ismt.c new file +21 -0
+			desc->wr_len_cmd = command;
+			desc->control |= ISMT_DESC_CWRL;
+			desc->rd_len = 2;
+			dma_size = 2;
+			dma_direction = DMA_FROM_DEVICE;
+		}
+
+		break;
+
+	case I2C_SMBUS_PROC_CALL:
+		dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n");
+		desc->wr_len_cmd = 3;
+		desc->rd_len = 2;
+		dma_size = 3;
+		dma_direction = DMA_BIDIRECTIONAL;
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* Block Write */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:

WRITE\n");

drivers/i2c/busses/i2c-ismt.c new file +7 -0
+			dma_size = data->block[0] + 1;
+			dma_direction = DMA_TO_DEVICE;
+			desc->wr_len_cmd = dma_size;
+			desc->control |= ISMT_DESC_BLK;
+		} else {
+			/* Block Read */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:

READ\n");

drivers/i2c/busses/i2c-ismt.c new file +5 -0
+			dma_size = I2C_SMBUS_BLOCK_MAX;
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = dma_size;
+			desc->wr_len_cmd = command;
+			desc->control |= (ISMT_DESC_BLK |

ISMT_DESC_CWRL);

drivers/i2c/busses/i2c-ismt.c new file +37 -0
+		}
+
+		break;
+
+	default:
+		dev_err(dev, "Unsupported transaction %d\n",
+			size);
+		return -EOPNOTSUPP;
+	}
+
+	/* map the data buffer */
+	if (dma_size != 0) {
+		dev_dbg(dev, " dev=%p\n", dev);
+		dev_dbg(dev, " data=%p\n", data);
+		dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer);
+		dev_dbg(dev, " dma_size=%d\n", dma_size);
+		dev_dbg(dev, " dma_direction=%d\n", dma_direction);
+
+		dma_addr = dma_map_single(&priv->pci_dev->dev,
+				      priv->dma_buffer,
+				      dma_size,
+				      dma_direction);
+
+		dev_dbg(dev, " dma_addr = 0x%016llX\n",
+			dma_addr);
+
+		desc->dptr_low = lower_32_bits(dma_addr);
+		desc->dptr_high = upper_32_bits(dma_addr);
+	}
+
+	INIT_COMPLETION(priv->cmp);
+
+	/* Add the descriptor */
+	ismt_submit_desc(priv);
+
+	/* Now we wait for interrupt completion, 1s */
+	ret = wait_for_completion_interruptible_timeout(&priv->cmp,

HZ*1);

drivers/i2c/busses/i2c-ismt.c new file +4 -0
+
+	/* unmap the data buffer */
+	if (dma_size != 0)
+		dma_unmap_single(&adap->dev, dma_addr, dma_size,

dma_direction);

drivers/i2c/busses/i2c-ismt.c new file +98 -0
+
+	if (likely(ret > 0))
+		/* do any post processing of the descriptor here */
+		ret = ismt_process_desc(desc, data, priv->dma_buffer, size);
+	else if (ret == 0) {
+		dev_err(dev, "completion wait timed out\n");
+		ret = -ETIMEDOUT;
+	} else {
+		dev_err(dev, "completion wait interrupted\n");
+		ret = -EIO;
+	}
+
+	/* Update the ring pointer */
+	priv->head++;
+	priv->head %= ISMT_DESC_ENTRIES;
+
+	return ret;
+}
+
+/**
+ * ismt_func() - report which i2c commands are supported by this
+adapter
+ * @adap: the i2c host adapter
+ */
+static u32 ismt_func(struct i2c_adapter *adap) {
+	return I2C_FUNC_SMBUS_QUICK		|
+		I2C_FUNC_SMBUS_BYTE		|
+		I2C_FUNC_SMBUS_BYTE_DATA	|
+		I2C_FUNC_SMBUS_WORD_DATA	|
+		I2C_FUNC_SMBUS_PROC_CALL	|
+		I2C_FUNC_SMBUS_BLOCK_DATA	|
+		I2C_FUNC_SMBUS_PEC;
+}
+
+/**
+ * smbus_algorithm - the adapter algorithm and supported functionality
+ * @smbus_xfer: the adapter algorithm
+ * @functionality: functionality supported by the adapter  */ static
+const struct i2c_algorithm smbus_algorithm = {
+	.smbus_xfer	= ismt_access,
+	.functionality	= ismt_func,
+};
+
+/**
+ * ismt_handle_isr() - interrupt handler bottom half
+ * @priv: iSMT private data
+ */
+static irqreturn_t ismt_handle_isr(struct ismt_priv *priv) {
+	complete(&priv->cmp);
+
+	return IRQ_HANDLED;
+}
+
+
+/**
+ * ismt_do_interrupt() - IRQ interrupt handler
+ * @vec: interrupt vector
+ * @data: iSMT private data
+ */
+static irqreturn_t ismt_do_interrupt(int vec, void *data) {
+	u32 val;
+	struct ismt_priv *priv = data;
+
+	/*
+	 * check to see it's our interrupt, return IRQ_NONE if not ours
+	 * since we are sharing interrupt
+	 */
+	val = readl(priv->smba + ISMT_MSTR_MSTS);
+
+	if (!(val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS)))
+		return IRQ_NONE;
+	else
+		writel(val | ISMT_MSTS_MIS | ISMT_MSTS_MEIS,
+		       priv->smba + ISMT_MSTR_MSTS);
+
+	return ismt_handle_isr(priv);
+}
+
+/**
+ * ismt_do_msi_interrupt() - MSI interrupt handler
+ * @vec: interrupt vector
+ * @data: iSMT private data
+ */
+static irqreturn_t ismt_do_msi_interrupt(int vec, void *data) {
+	return ismt_handle_isr(data);
+}
+
+/**
+ * ismt_hw_init() - initialize the iSMT hardware
+ * @priv: iSMT private data
+ */
+static void __devinit ismt_hw_init(struct ismt_priv *priv) {
+	u32 val;
+	struct device *dev = &priv->pci_dev->dev;
+
+	/* initialize the Master Descriptor Base Address (MDBA) */
+	writel(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA);
+	writel(priv->io_rng_dma >> 32, priv->smba + ISMT_MSTR_MDBA +

4);

drivers/i2c/busses/i2c-ismt.c new file +25 -0
+
+	/* initialize the Master Control Register (MCTRL) */
+	writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL);
+
+	/* initialize the Master Status Register (MSTS) */
+	writel(0, priv->smba + ISMT_MSTR_MSTS);
+
+	/* initialize the Master Descriptor Size (MDS) */
+	val = readl(priv->smba + ISMT_MSTR_MDS);
+	writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1),
+		priv->smba + ISMT_MSTR_MDS);
+
+	/*
+	 * Set the SMBus speed (could use this for slow HW debuggers)
+	 */
+
+	val = readl(priv->smba + ISMT_SPGT);
+
+	switch (bus_speed) {
+	case 0:
+		break;
+
+	case 1:
+		dev_dbg(dev, "Setting SMBus clock to 80kHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) |

ISMT_SPGT_SPD_80K),

drivers/i2c/busses/i2c-ismt.c new file +6 -0
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 2:
+		dev_dbg(dev, "Setting SMBus clock to 100kHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) |

ISMT_SPGT_SPD_100K),

drivers/i2c/busses/i2c-ismt.c new file +6 -0
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 3:
+		dev_dbg(dev, "Setting SMBus clock to 400kHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) |

ISMT_SPGT_SPD_400K),

drivers/i2c/busses/i2c-ismt.c new file +6 -0
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 4:
+		dev_dbg(dev, "Setting SMBus clock to 1MHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) |

ISMT_SPGT_SPD_1M),

drivers/i2c/busses/i2c-ismt.c new file +5 -0
+			priv->smba + ISMT_SPGT);
+		break;
+
+	default:
+		dev_dbg(dev, "Invalid SMBus clock speed, only 1-4 are

valid\n");

drivers/i2c/busses/i2c-ismt.c new file +19 -0
+		break;
+	}
+}
+
+/**
+ * ismt_dev_init() - initialize the iSMT data structures
+ * @priv: iSMT private data
+ */
+static int __devinit ismt_dev_init(struct ismt_priv *priv) {
+	/* allocate memory for the descriptor */
+	priv->hw = dmam_alloc_coherent(&priv->pci_dev->dev,
+				       (ISMT_DESC_ENTRIES
+					       * sizeof(struct ismt_desc)),
+				       &priv->io_rng_dma,
+				       GFP_KERNEL);
+	if (!priv->hw)
+		return -ENOMEM;
+
+	memset(priv->hw, 0, (ISMT_DESC_ENTRIES * sizeof(struct

ismt_desc)));

drivers/i2c/busses/i2c-ismt.c new file +20 -0
+
+	priv->head = 0;
+	init_completion(&priv->cmp);
+
+	return 0;
+}
+
+/**
+ * ismt_int_init() - initialize interrupts
+ * @priv: iSMT private data
+ */
+static int __devinit ismt_int_init(struct ismt_priv *priv) {
+	int err;
+
+	/* Try using MSI interrupts */
+	err = pci_enable_msi(priv->pci_dev);
+
+	if (err) {
+		dev_warn(&priv->pci_dev->dev,
+			 "Unable to use MSI interrupts, falling back to

legacy");

drivers/i2c/busses/i2c-ismt.c new file +73 -0
+		goto intx;
+	}
+
+	err = devm_request_irq(&priv->pci_dev->dev,
+			       priv->pci_dev->irq,
+			       ismt_do_msi_interrupt,
+			       0,
+			       "ismt-msi",
+			       priv);
+
+	if (err) {
+		pci_disable_msi(priv->pci_dev);
+		goto intx;
+	}
+
+	priv->using_msi = true;
+	goto done;
+
+	/* Try using legacy interrupts */
+intx:
+	err = devm_request_irq(&priv->pci_dev->dev,
+			       priv->pci_dev->irq,
+			       ismt_do_interrupt,
+			       IRQF_SHARED,
+			       "ismt-intx",
+			       priv);
+	if (err) {
+		dev_err(&priv->pci_dev->dev, "no usable interrupts\n");
+		return -ENODEV;
+	}
+
+	priv->using_msi = false;
+
+done:
+	return 0;
+}
+
+static struct pci_driver ismt_driver;
+
+/**
+ * ismt_probe() - probe for iSMT devices
+ * @pdev: PCI-Express device
+ * @id: PCI-Express device ID
+ */
+static int __devinit
+ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
+	int err;
+	struct ismt_priv *priv;
+	unsigned long start, len;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	pci_set_drvdata(pdev, priv);
+	i2c_set_adapdata(&priv->adapter, priv);
+	priv->adapter.owner = THIS_MODULE;
+
+	priv->adapter.class = I2C_CLASS_HWMON;
+
+	priv->adapter.algo = &smbus_algorithm;
+
+	/* set up the sysfs linkage to our parent device */
+	priv->adapter.dev.parent = &pdev->dev;
+
+	/* number of retries on lost arbitration */
+	priv->adapter.retries = ISMT_MAX_RETRIES;
+
+	priv->pci_dev = pdev;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to enable SMBus PCI device

(%d)\n",

drivers/i2c/busses/i2c-ismt.c new file +45 -0
+			err);
+		return err;
+	}
+
+	/* enable bus mastering */
+	pci_set_master(pdev);
+
+	/* Determine the address of the SMBus area */
+	start = pci_resource_start(pdev, SMBBAR);
+	len = pci_resource_len(pdev, SMBBAR);
+	if (!start || !len) {
+		dev_err(&pdev->dev,
+			"SMBus base address uninitialized, upgrade BIOS\n");
+		return -ENODEV;
+	}
+
+	snprintf(priv->adapter.name, sizeof(priv->adapter.name),
+		 "SMBus iSMT adapter at %lx", start);
+
+	dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start);
+	dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len);
+
+	err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]);
+	if (err) {
+		dev_err(&pdev->dev, "ACPI resource conflict!\n");
+		return err;
+	}
+
+	err = pci_request_region(pdev, SMBBAR, ismt_driver.name);
+	if (err) {
+		dev_err(&pdev->dev,
+			"Failed to request SMBus region 0x%lx-0x%lx\n",
+			start, start + len);
+		return err;
+	}
+
+	priv->smba = pcim_iomap(pdev, SMBBAR, len);
+	if (!priv->smba) {
+		dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n");
+		err = -ENODEV;
+		goto fail;
+	}
+
+	if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
+	    (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0))

{

drivers/i2c/busses/i2c-ismt.c new file +2 -0
+		if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
+		   (pci_set_consistent_dma_mask(pdev,

DMA_BIT_MASK(32)) != 0)) {

drivers/i2c/busses/i2c-ismt.c new file +1 -0
+			dev_warn(&pdev->dev, "pci_set_dma_mask fail

%p\n",

drivers/i2c/busses/i2c-ismt.c new file +18 -0
+				 pdev);
+			goto fail;
+		}
+	}
+
+	err = ismt_dev_init(priv);
+	if (err)
+		goto fail;
+
+	ismt_hw_init(priv);
+
+	err = ismt_int_init(priv);
+	if (err)
+		goto fail;
+
+	err = i2c_add_adapter(&priv->adapter);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to add SMBus iSMT

adapter\n");

drivers/i2c/busses/i2c-ismt.c new file +64 -1
+		err = -ENODEV;
+		goto fail;
+	}
+	return 0;
+
+fail:
+	pci_release_region(pdev, SMBBAR);
+	return err;
+}
+
+/**
+ * ismt_remove() - release driver resources
+ * @pdev: PCI-Express device
+ */
+static void __devexit ismt_remove(struct pci_dev *pdev) {
+	struct ismt_priv *priv = pci_get_drvdata(pdev);
+
+	i2c_del_adapter(&priv->adapter);
+	pci_release_region(pdev, SMBBAR);
+}
+
+/**
+ * ismt_suspend() - place the device in suspend
+ * @pdev: PCI-Express device
+ * @mesg: PM message
+ */
+#ifdef CONFIG_PM
+static int ismt_suspend(struct pci_dev *pdev, pm_message_t mesg) {
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
+	return 0;
+}
+
+/**
+ * ismt_resume() - PCI resume code
+ * @pdev: PCI-Express device
+ */
+static int ismt_resume(struct pci_dev *pdev) {
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	return pci_enable_device(pdev);
+}
+
+#else
+
+#define ismt_suspend NULL
+#define ismt_resume NULL
+
+#endif
+
+static struct pci_driver ismt_driver = {
+	.name = "ismt_smbus",
+	.id_table = ismt_ids,
+	.probe = ismt_probe,
+	.remove = __devexit_p(ismt_remove),
+	.suspend = ismt_suspend,
+	.resume = ismt_resume,
+};
+
+module_pci_driver(ismt_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Bill E. Brown <[email protected]>");
+MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver");
--

1.6.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to [email protected] More majordomo info at
http://vger.kernel.org/majordomo-info.html

#3 lore raw
In reply to a message not in the archive

Hi Bill,

Quoted 10 lines · Bill E Brown

From: Bill Brown <[email protected]>

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Quoted 8 lines
---
 Documentation/i2c/busses/i2c-ismt |   36 ++
 drivers/i2c/busses/Kconfig        |   10 +
 drivers/i2c/busses/Makefile       |    1 +
 drivers/i2c/busses/i2c-ismt.c     |  911 +++++++++++++++++++++++++++++++++++++
 4 files changed, 958 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/i2c/busses/i2c-ismt
 create mode 100644 drivers/i2c/busses/i2c-ismt.c

Looks much better and nearly ready for upstream inclusion. I have a few minor concerns remaining, see my comments in-line:

Quoted 18 lines
Documentation/i2c/busses/i2c-ismt new file +12 -0
new file mode 100644
index 0000000..ed6375c
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>
+
+
+Module Parameters
+-----------------
+

Documentation/i2c/busses/i2c-ismt line 22

Quoted 10 lines
Documentation/i2c/busses/i2c-ismt new file +10 -0
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Available bus frequency settings:
+  0  no change
+  1  80 kHz
+  2  100 kHz
+  3  400 kHz
+  4  1 MHz

I'm fine with having a module parameter for this, but not so with using arbitrary values to represent the different speeds. While we don't (yet) have a standard module parameter for this, several drivers implementing this feature use actual frequency numbers and I'd prefer that you do the same. See for example drivers i2c-eg20t, i2c-stu300 and i2c-viperboard. Or i2c-diolan-u2c, although that one uses Hz instead of kHz as the unit.

Documentation/i2c/busses/i2c-ismt line 35

Quoted 13 lines
Documentation/i2c/busses/i2c-ismt new file +13 -0
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci will show
+something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Device 0xc59
+  00:13.1 System peripheral: Intel Corporation Device 0xc5a

Actually, as these PCI device IDs have been added to the pci.ids database meanwhile, you'd rather see:

00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0 00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1

Documentation/i2c/busses/i2c-ismt line 36

Quoted 1 line
Documentation/i2c/busses/i2c-ismt new file +1 -0
+

No blank line at end of file please, git would complain.

drivers/i2c/busses/Kconfig config I2C_ISCH · line 124

Quoted 11 lines
drivers/i2c/busses/Kconfig +3 -0
index 7244c8b..64d5756 100644
@@ -119,6 +119,16 @@ config I2C_ISCH
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-isch.
 
+config I2C_ISMT
+	tristate "Intel iSMT SMBus Controller"
+	depends on PCI

As the controller is integrated into x86 CPUs, I think you should make the driver depend on X86.

Quoted 36 lines
drivers/i2c/busses/Kconfig +7 -0
+	help
+	  If you say yes to this option, support will be included for the Intel
+	  iSMT SMBus host controller interface.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called i2c-ismt.
+
 config I2C_PIIX4
 	tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
 	depends on PCI
(...)
drivers/i2c/busses/i2c-ismt.c new file +18 -0
new file mode 100644
index 0000000..17b1bdd
@@ -0,0 +1,911 @@
(...)
+/*
+ *  Supports the SMBus Message Transport (SMT) in the Intel Atom Processor
+ *  S12xx Product Family.
+ *
+ *  Features supported by this driver:
+ *  Hardware PEC                     yes
+ *  Block buffer                     yes
+ *  Block process call transaction   no
+ *  Slave mode                       no
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>

drivers/i2c/busses/i2c-ismt.c line 80

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+
+/* PCI Address Constants */
+#define SMBBAR		0
+
+/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */
+#define PCI_DEVICE_ID_INTEL_S1200_SMB_SMT0	0x0c59
+#define PCI_DEVICE_ID_INTEL_S1200_SMB_SMT1	0x0c5a

I don't like "SMB" being used for SMBus as it is ambiguous. Plus, SMBus and "SMT" are somewhat redundant. So I would suggest the following names:
PCI_DEVICE_ID_INTEL_S1200_SMT0 PCI_DEVICE_ID_INTEL_S1200_SMT1

drivers/i2c/busses/i2c-ismt.c line 176

Quoted 10 lines

(...)

drivers/i2c/busses/i2c-ismt.c new file +9 -0
+struct ismt_priv {
+	struct i2c_adapter adapter;
+	void *smba;				/* PCI BAR */
+	struct pci_dev *pci_dev;
+	struct ismt_desc *hw;			/* descriptor virt base addr */
+	dma_addr_t io_rng_dma;			/* descriptor HW base addr */
+	u8 head;				/* ring buffer head pointer */
+	struct completion cmp;			/* interrupt completion */
+	u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 3];	/* temp R/W data buffer */

Why + 3? Also, are there no alignment constraints for DMA buffers?

Quoted 8 lines
drivers/i2c/busses/i2c-ismt.c new file +8 -0
+	bool using_msi;				/* type of interrupt flag */
+};
+
+/**
+ * ismt_ids - PCI device IDs supported by this driver
+ */
+static const DEFINE_PCI_DEVICE_TABLE(ismt_ids) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMB_SMT0) },

drivers/i2c/busses/i2c-ismt.c line 194

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMB_SMT1) },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, ismt_ids);
+
+/* Bus speed control bits for slow debuggers - refer to the docs for usage */
+static unsigned int bus_speed;
+module_param(bus_speed, uint, S_IRUGO);
+MODULE_PARM_DESC(bus_speed, "Bus Speed");

"Bus speed in kHz (0 = BIOS default)"

Quoted 29 lines
drivers/i2c/busses/i2c-ismt.c new file +29 -0
+
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+/**
+ * ismt_desc_dump() - dump the contents of a descriptor for debug purposes
+ * @priv: iSMT private data
+ */
+static void ismt_desc_dump(struct ismt_priv *priv)
+{
+	struct device *dev = &priv->pci_dev->dev;
+	struct ismt_desc *desc = &priv->hw[priv->head];
+
+	dev_dbg(dev, "Dump of the descriptor struct:  0x%X\n", priv->head);
+	dev_dbg(dev, "\ttgtaddr_rw=0x%02X\n", desc->tgtaddr_rw);
+	dev_dbg(dev, "\twr_len_cmd=0x%02X\n", desc->wr_len_cmd);
+	dev_dbg(dev, "\trd_len=    0x%02X\n", desc->rd_len);
+	dev_dbg(dev, "\tcontrol=   0x%02X\n", desc->control);
+	dev_dbg(dev, "\tstatus=    0x%02X\n", desc->status);
+	dev_dbg(dev, "\tretry=     0x%02X\n", desc->retry);
+	dev_dbg(dev, "\trxbytes=   0x%02X\n", desc->rxbytes);
+	dev_dbg(dev, "\ttxbytes=   0x%02X\n", desc->txbytes);
+	dev_dbg(dev, "\tdptr_low=  0x%08X\n", desc->dptr_low);
+	dev_dbg(dev, "\tdptr_high= 0x%08X\n", desc->dptr_high);
+}
+
+/**
+ * ismt_gen_reg_dump() - dump the iSMT General Registers
+ * @priv: iSMT private data
+ */
+static void ismt_gen_reg_dump(struct ismt_priv *priv)

drivers/i2c/busses/i2c-ismt.c line 233

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT General Registers\n");
+	dev_dbg(dev, "  GCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_GCTRL,
+		readl(priv->smba + ISMT_GR_GCTRL));
+	dev_dbg(dev, "  SMTICL... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_GR_SMTICL,
+		readq(priv->smba + ISMT_GR_SMTICL));

Function readq() doesn't exist on 32-bit x86, causing a build failure.

Quoted 16 lines
drivers/i2c/busses/i2c-ismt.c new file +16 -0
+	dev_dbg(dev, "  ERRINTMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINTMSK,
+		readl(priv->smba + ISMT_GR_ERRINTMSK));
+	dev_dbg(dev, "  ERRAERMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRAERMSK,
+		readl(priv->smba + ISMT_GR_ERRAERMSK));
+	dev_dbg(dev, "  ERRSTS... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRSTS,
+		readl(priv->smba + ISMT_GR_ERRSTS));
+	dev_dbg(dev, "  ERRINFO.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINFO,
+		readl(priv->smba + ISMT_GR_ERRINFO));
+}
+
+/**
+ * ismt_mstr_reg_dump() - dump the iSMT Master Registers

drivers/i2c/busses/i2c-ismt.c line 259

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ * @priv: iSMT private data
+ */
+static void ismt_mstr_reg_dump(struct ismt_priv *priv)
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT Master Registers\n");
+	dev_dbg(dev, "  MDBA..... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_MSTR_MDBA,
+		readq(priv->smba + ISMT_MSTR_MDBA));

Same problem here.

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+	dev_dbg(dev, "  MCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MCTRL,
+		readl(priv->smba + ISMT_MSTR_MCTRL));
+	dev_dbg(dev, "  MSTS..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MSTS,
+		readl(priv->smba + ISMT_MSTR_MSTS));

drivers/i2c/busses/i2c-ismt.c line 275

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	dev_dbg(dev, "  MDS...... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MDS,
+		readl(priv->smba + ISMT_MSTR_MDS));
+	dev_dbg(dev, "  RPOLICY.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_RPOLICY,
+		readl(priv->smba + ISMT_MSTR_RPOLICY));
+	dev_dbg(dev, "  SPGT..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_SPGT,
+		readl(priv->smba + ISMT_SPGT));
+}

Originally you had an #else here with stubs for when debugging was disabled. I don't understand why you removed them, as this will cause a build failure if neither CONFIG_DYNAMIC_DEBUG nor CONFIG_I2C_DEBUG_BUS is set.

Quoted 30 lines
drivers/i2c/busses/i2c-ismt.c new file +30 -0
+#endif
+
+/**
+ * ismt_submit_desc() - add a descriptor to the ring
+ * @priv: iSMT private data
+ */
+static void ismt_submit_desc(struct ismt_priv *priv)
+{
+	uint fmhp;
+	uint val;
+
+	ismt_desc_dump(priv);
+	ismt_gen_reg_dump(priv);
+	ismt_mstr_reg_dump(priv);
+
+	/* Set the FMHP (Firmware Master Head Pointer)*/
+	fmhp = ((priv->head + 1) % ISMT_DESC_ENTRIES) << 16;
+	val = readl(priv->smba + ISMT_MSTR_MCTRL);
+	writel((val & ~ISMT_MCTRL_FMHP) | fmhp,
+	       priv->smba + ISMT_MSTR_MCTRL);
+
+	/* Set the start bit */
+	val = readl(priv->smba + ISMT_MSTR_MCTRL);
+	writel(val | ISMT_MCTRL_SS,
+	       priv->smba + ISMT_MSTR_MCTRL);
+}
+
+/**
+ * ismt_process_desc() - handle the completion of the descriptor
+ * @desc: the iSMT hardware descriptor

drivers/i2c/busses/i2c-ismt.c line 315

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ * @data: data buffer from the upper layer
+ * @dma_buffer: temporary buffer for the DMA engine
+ * @size: SMBus transaction type
+ */
+static int ismt_process_desc(const struct ismt_desc *desc,
+			     union i2c_smbus_data *data,
+			     u8 *dma_buffer, int size)
+{
+	if (likely(desc->status & ISMT_DESC_SCS)) {
+		if (size != I2C_SMBUS_QUICK)

This condition seems incomplete, as far as I can see the memcpy is only needed for read transactions, not write transactions.

drivers/i2c/busses/i2c-ismt.c line 316

Quoted 1 line
drivers/i2c/busses/i2c-ismt.c new file +1 -0
+			memcpy(data, dma_buffer, sizeof(*data));

Why not limit the size of the copy?

Quoted 43 lines
drivers/i2c/busses/i2c-ismt.c new file +43 -0
+		return 0;
+	}
+
+	if (likely(desc->status & ISMT_DESC_NAK))
+		return -ENXIO;
+
+	if (desc->status & ISMT_DESC_CRC)
+		return -EBADMSG;
+
+	if (desc->status & ISMT_DESC_COL)
+		return -EAGAIN;
+
+	if (desc->status & ISMT_DESC_LPR)
+		return -EPROTO;
+
+	if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO))
+		return -ETIMEDOUT;
+
+	return -EIO;
+}
+
+/**
+ * ismt_access() - process an SMBus command
+ * @adap: the i2c host adapter
+ * @addr: address of the i2c/SMBus target
+ * @flags: command options
+ * @read_write: read from or write to device
+ * @command: the i2c/SMBus command to issue
+ * @size: SMBus transaction type
+ * @data: read/write data buffer
+ */
+static int ismt_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data *data)
+{
+	int ret;
+	dma_addr_t dma_addr = 0; /* address of the data buffer */
+	u8 dma_size = 0;
+	enum dma_data_direction dma_direction = 0;
+	struct ismt_desc *desc;
+	struct ismt_priv *priv = i2c_get_adapdata(adap);
+	struct device *dev = &priv->pci_dev->dev;
+

drivers/i2c/busses/i2c-ismt.c line 369

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	desc = &priv->hw[priv->head];
+
+	/* Initialize the descriptor */
+	memset(desc, 0, sizeof(struct ismt_desc));
+	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
+
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if (size != I2C_SMBUS_QUICK) {
+		memcpy(priv->dma_buffer + 1, data, sizeof(*data));

Here too, this memcpy seems unneeded for read transactions, and for writes, you could limit the size.

Quoted 88 lines
drivers/i2c/busses/i2c-ismt.c new file +88 -0
+		priv->dma_buffer[0] = command;
+	}
+
+	/* Initialize common control bits */
+	if (likely(priv->using_msi))
+		desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR;
+	else
+		desc->control = ISMT_DESC_FAIR;
+
+	if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK)
+	    && (size != I2C_SMBUS_I2C_BLOCK_DATA))
+		desc->control |= ISMT_DESC_PEC;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		dev_dbg(dev, "I2C_SMBUS_QUICK\n");
+		break;
+
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Send Byte
+			 * The command field contains the write data
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  WRITE\n");
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+		} else {
+			/* Receive Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  READ\n");
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = 1;
+		}
+
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Write Byte
+			 * Command plus 1 data byte
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  WRITE\n");
+			desc->wr_len_cmd = 2;
+			dma_size = 2;
+			dma_direction = DMA_TO_DEVICE;
+		} else {
+			/* Read Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  READ\n");
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+			desc->rd_len = 1;
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+		}
+
+		break;
+
+	case I2C_SMBUS_WORD_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* Write Word */
+			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:  WRITE\n");
+			desc->wr_len_cmd = 3;
+			dma_size = 3;
+			dma_direction = DMA_TO_DEVICE;
+		} else {
+			/* Read Word */
+			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:  READ\n");
+			desc->wr_len_cmd = command;
+			desc->control |= ISMT_DESC_CWRL;
+			desc->rd_len = 2;
+			dma_size = 2;
+			dma_direction = DMA_FROM_DEVICE;
+		}
+
+		break;
+
+	case I2C_SMBUS_PROC_CALL:
+		dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n");
+		desc->wr_len_cmd = 3;
+		desc->rd_len = 2;
+		dma_size = 3;
+		dma_direction = DMA_BIDIRECTIONAL;
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {

drivers/i2c/busses/i2c-ismt.c line 467

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+			/* Block Write */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  WRITE\n");
+			dma_size = data->block[0] + 1;
+			dma_direction = DMA_TO_DEVICE;
+			desc->wr_len_cmd = dma_size;
+			desc->control |= ISMT_DESC_BLK;
+		} else {
+			/* Block Read */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  READ\n");
+			dma_size = I2C_SMBUS_BLOCK_MAX;

If I'm not mistaken, you're missing a + 1 here. I2C_SMBUS_BLOCK_MAX is the maximum block length value returned by the slave, but there's one leading byte needed for the command too.

Quoted 13 lines
drivers/i2c/busses/i2c-ismt.c new file +13 -0
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = dma_size;
+			desc->wr_len_cmd = command;
+			desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL);
+		}
+
+		break;
+
+	default:
+		dev_err(dev, "Unsupported transaction %d\n",
+			size);
+		return -EOPNOTSUPP;
+	}

drivers/i2c/busses/i2c-ismt.c line 490

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+
+	/* map the data buffer */
+	if (dma_size != 0) {
+		dev_dbg(dev, " dev=%p\n", dev);
+		dev_dbg(dev, " data=%p\n", data);
+		dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer);
+		dev_dbg(dev, " dma_size=%d\n", dma_size);
+		dev_dbg(dev, " dma_direction=%d\n", dma_direction);
+
+		dma_addr = dma_map_single(&priv->pci_dev->dev,

Just "dev" will do.

Quoted 21 lines
drivers/i2c/busses/i2c-ismt.c new file +21 -0
+				      priv->dma_buffer,
+				      dma_size,
+				      dma_direction);
+
+		dev_dbg(dev, " dma_addr = 0x%016llX\n",
+			dma_addr);
+
+		desc->dptr_low = lower_32_bits(dma_addr);
+		desc->dptr_high = upper_32_bits(dma_addr);
+	}
+
+	INIT_COMPLETION(priv->cmp);
+
+	/* Add the descriptor */
+	ismt_submit_desc(priv);
+
+	/* Now we wait for interrupt completion, 1s */
+	ret = wait_for_completion_interruptible_timeout(&priv->cmp, HZ*1);
+
+	/* unmap the data buffer */
+	if (dma_size != 0)

drivers/i2c/busses/i2c-ismt.c line 521

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+		dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction);
+
+	if (likely(ret > 0))
+		/* do any post processing of the descriptor here */
+		ret = ismt_process_desc(desc, data, priv->dma_buffer, size);
+	else if (ret == 0) {
+		dev_err(dev, "completion wait timed out\n");
+		ret = -ETIMEDOUT;
+	} else {
+		dev_err(dev, "completion wait interrupted\n");

I did not notice during my initial review... Why did you make it interruptible in the first place? At least 2 i2c bus drivers have moved to the non-interruptible flavor in the past. See for example commits 4b723a471050a8b80f7fa86e76f01f4c711b3443 and b7af349b175af45f9d87b3bf3f0a221e1831ed39. Given how short SMBus transactions are typically, letting them be interrupted seems more trouble than is worth.

Quoted 13 lines
drivers/i2c/busses/i2c-ismt.c new file +13 -0
+		ret = -EIO;
+	}
+
+	/* Update the ring pointer */
+	priv->head++;
+	priv->head %= ISMT_DESC_ENTRIES;
+
+	return ret;
+}
+
+/**
+ * ismt_func() - report which i2c commands are supported by this adapter
+ * @adap: the i2c host adapter

drivers/i2c/busses/i2c-ismt.c line 544

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ */
+static u32 ismt_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_QUICK		|
+		I2C_FUNC_SMBUS_BYTE		|
+		I2C_FUNC_SMBUS_BYTE_DATA	|
+		I2C_FUNC_SMBUS_WORD_DATA	|
+		I2C_FUNC_SMBUS_PROC_CALL	|
+		I2C_FUNC_SMBUS_BLOCK_DATA	|
+		I2C_FUNC_SMBUS_PEC;

Nitpicking: can you please align the I2C_... by using seven spaces instead of one tab?

Quoted 8 lines
drivers/i2c/busses/i2c-ismt.c new file +8 -0
+}
+
+(...)
+/**
+ * ismt_do_msi_interrupt() - MSI interrupt handler
+ * @vec: interrupt vector
+ * @data: iSMT private data
+ */

drivers/i2c/busses/i2c-ismt.c line 608

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+static irqreturn_t ismt_do_msi_interrupt(int vec, void *data)
+{
+	return ismt_handle_isr(data);
+}
+
+/**
+ * ismt_hw_init() - initialize the iSMT hardware
+ * @priv: iSMT private data
+ */
+static void __devinit ismt_hw_init(struct ismt_priv *priv)

All __dev* markers are going away meanwhile, so you should remove them from your code to prevent build failures in future kernels.

Quoted 17 lines
drivers/i2c/busses/i2c-ismt.c new file +17 -0
+{
+	u32 val;
+	struct device *dev = &priv->pci_dev->dev;
+
+	/* initialize the Master Descriptor Base Address (MDBA) */
+	writel(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA);
+	writel(priv->io_rng_dma >> 32, priv->smba + ISMT_MSTR_MDBA + 4);
+
+	/* initialize the Master Control Register (MCTRL) */
+	writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL);
+
+	/* initialize the Master Status Register (MSTS) */
+	writel(0, priv->smba + ISMT_MSTR_MSTS);
+
+	/* initialize the Master Descriptor Size (MDS) */
+	val = readl(priv->smba + ISMT_MSTR_MDS);
+	writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1),

drivers/i2c/busses/i2c-ismt.c line 635

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+		priv->smba + ISMT_MSTR_MDS);
+
+	/*
+	 * Set the SMBus speed (could use this for slow HW debuggers)
+	 */
+
+	val = readl(priv->smba + ISMT_SPGT);
+
+	switch (bus_speed) {
+	case 0:

Maybe you could set bus_speed here based on the register value you read. And log the value as well. Rationale: it might be useful for the user (or for support / debugging) to know the actual clock speed.

drivers/i2c/busses/i2c-ismt.c line 639

Quoted 4 lines
drivers/i2c/busses/i2c-ismt.c new file +4 -0
+		break;
+
+	case 1:
+		dev_dbg(dev, "Setting SMBus clock to 80kHz\n");

A space between the number and "kHz" would improve readability.

Quoted 30 lines
drivers/i2c/busses/i2c-ismt.c new file +30 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 2:
+		dev_dbg(dev, "Setting SMBus clock to 100kHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 3:
+		dev_dbg(dev, "Setting SMBus clock to 400kHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 4:
+		dev_dbg(dev, "Setting SMBus clock to 1MHz\n");
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	default:
+		dev_dbg(dev, "Invalid SMBus clock speed, only 1-4 are valid\n");
+		break;
+	}
+}
+
+ (...)
+/**

drivers/i2c/busses/i2c-ismt.c line 701

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ * ismt_int_init() - initialize interrupts
+ * @priv: iSMT private data
+ */
+static int __devinit ismt_int_init(struct ismt_priv *priv)
+{
+	int err;
+
+	/* Try using MSI interrupts */
+	err = pci_enable_msi(priv->pci_dev);
+

No blank line between command and error testing, please.

drivers/i2c/busses/i2c-ismt.c line 704

Quoted 3 lines
drivers/i2c/busses/i2c-ismt.c new file +3 -0
+	if (err) {
+		dev_warn(&priv->pci_dev->dev,
+			 "Unable to use MSI interrupts, falling back to legacy");

Missing trailing "\n".

drivers/i2c/busses/i2c-ismt.c line 714

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+		goto intx;
+	}
+
+	err = devm_request_irq(&priv->pci_dev->dev,
+			       priv->pci_dev->irq,
+			       ismt_do_msi_interrupt,
+			       0,
+			       "ismt-msi",
+			       priv);
+

No blank line here either.

Quoted 101 lines
drivers/i2c/busses/i2c-ismt.c new file +101 -0
+	if (err) {
+		pci_disable_msi(priv->pci_dev);
+		goto intx;
+	}
+
+	priv->using_msi = true;
+	goto done;
+
+	/* Try using legacy interrupts */
+intx:
+	err = devm_request_irq(&priv->pci_dev->dev,
+			       priv->pci_dev->irq,
+			       ismt_do_interrupt,
+			       IRQF_SHARED,
+			       "ismt-intx",
+			       priv);
+	if (err) {
+		dev_err(&priv->pci_dev->dev, "no usable interrupts\n");
+		return -ENODEV;
+	}
+
+	priv->using_msi = false;
+
+done:
+	return 0;
+}
+
+static struct pci_driver ismt_driver;
+
+/**
+ * ismt_probe() - probe for iSMT devices
+ * @pdev: PCI-Express device
+ * @id: PCI-Express device ID
+ */
+static int __devinit
+ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int err;
+	struct ismt_priv *priv;
+	unsigned long start, len;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	pci_set_drvdata(pdev, priv);
+	i2c_set_adapdata(&priv->adapter, priv);
+	priv->adapter.owner = THIS_MODULE;
+
+	priv->adapter.class = I2C_CLASS_HWMON;
+
+	priv->adapter.algo = &smbus_algorithm;
+
+	/* set up the sysfs linkage to our parent device */
+	priv->adapter.dev.parent = &pdev->dev;
+
+	/* number of retries on lost arbitration */
+	priv->adapter.retries = ISMT_MAX_RETRIES;
+
+	priv->pci_dev = pdev;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to enable SMBus PCI device (%d)\n",
+			err);
+		return err;
+	}
+
+	/* enable bus mastering */
+	pci_set_master(pdev);
+
+	/* Determine the address of the SMBus area */
+	start = pci_resource_start(pdev, SMBBAR);
+	len = pci_resource_len(pdev, SMBBAR);
+	if (!start || !len) {
+		dev_err(&pdev->dev,
+			"SMBus base address uninitialized, upgrade BIOS\n");
+		return -ENODEV;
+	}
+
+	snprintf(priv->adapter.name, sizeof(priv->adapter.name),
+		 "SMBus iSMT adapter at %lx", start);
+
+	dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start);
+	dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len);
+
+	err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]);
+	if (err) {
+		dev_err(&pdev->dev, "ACPI resource conflict!\n");
+		return err;
+	}
+
+	err = pci_request_region(pdev, SMBBAR, ismt_driver.name);
+	if (err) {
+		dev_err(&pdev->dev,
+			"Failed to request SMBus region 0x%lx-0x%lx\n",
+			start, start + len);
+		return err;
+	}
+
+	priv->smba = pcim_iomap(pdev, SMBBAR, len);

drivers/i2c/busses/i2c-ismt.c line 825

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	if (!priv->smba) {
+		dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n");
+		err = -ENODEV;
+		goto fail;
+	}
+
+	if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
+	    (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) {
+		if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
+		   (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {

One more space for perfect alignment.

drivers/i2c/busses/i2c-ismt.c line 826

Quoted 1 line
drivers/i2c/busses/i2c-ismt.c new file +1 -0
+			dev_warn(&pdev->dev, "pci_set_dma_mask fail %p\n",

With a "goto fail" that's more than a dev_warn, that's a dev_err.

Quoted 19 lines
drivers/i2c/busses/i2c-ismt.c new file +19 -0
+				 pdev);
+			goto fail;
+		}
+	}
+
+	err = ismt_dev_init(priv);
+	if (err)
+		goto fail;
+
+	ismt_hw_init(priv);
+
+	err = ismt_int_init(priv);
+	if (err)
+		goto fail;
+
+	err = i2c_add_adapter(&priv->adapter);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n");
+		err = -ENODEV;

drivers/i2c/busses/i2c-ismt.c line 854

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +9 -0
+		goto fail;
+	}
+	return 0;
+
+fail:
+	pci_release_region(pdev, SMBBAR);
+	return err;
+}
+
(...)

Please resubmit with these last issues fixed and I'll be happy to approve this new driver for upstream inclusion.

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#5 lore raw

PATCH v5 1/1 i2c: Adding support for Intel iSMT SMBus 2.0 host controller

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

---
Forgive the latency in this reply, Jean, Bill has gone on sabbatical, so I finished this up on behalf of Intel.

Change notes for V5)
* Remove __devexit, __devinit macros

* Convert module parameter to use units of khz

* Update Description to reflect current pci db

* Improve KConfig dependencies

* Improve PCI device id names

* Reduce dma buffer size to appropriate value and check mapping result

* Fix build failure resulting from missing readq definition

* Limit use of memcpy and copy length in ismt_process_desc

* Fix off by one error in dma_size

* Convert driver to uninterruptible wait on dma transfers

* Add spped sync with bus_speed module option

* Misc cleanups and fixes

* Fix FTBFS when CONFIG_DYNAMIC_DEBUG is off

---
 Documentation/i2c/busses/i2c-ismt |  36 ++
 drivers/i2c/busses/Kconfig        |  10 +
 drivers/i2c/busses/Makefile       |   1 +
 drivers/i2c/busses/i2c-ismt.c     | 932 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 979 insertions(+)
 create mode 100644 Documentation/i2c/busses/i2c-ismt
 create mode 100644 drivers/i2c/busses/i2c-ismt.c
Documentation/i2c/busses/i2c-ismt new file +36 -0
new file mode 100644
index 0000000..aa3b60f
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Specify the bus speed in units of KHz.
+Available bus frequency settings:
+  0  no change
+  80 kHz
+  100 kHz
+  400 kHz
+  1000 KHz
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci will show
+something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0
+  00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1
drivers/i2c/busses/Kconfig +10 -0
index bdca511..0bb7ff4 100644
@@ -121,6 +121,16 @@ config I2C_ISCH
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-isch.
 
+config I2C_ISMT
+	tristate "Intel iSMT SMBus Controller"
+	depends on PCI && X86
+	help
+	  If you say yes to this option, support will be included for the Intel
+	  iSMT SMBus host controller interface.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called i2c-ismt.
+
 config I2C_PIIX4
 	tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
 	depends on PCI
drivers/i2c/busses/Makefile +1 -0
index 6181f3f..23c9d55 100644
@@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882)	+= i2c-amd756-s4882.o
 obj-$(CONFIG_I2C_AMD8111)	+= i2c-amd8111.o
 obj-$(CONFIG_I2C_I801)		+= i2c-i801.o
 obj-$(CONFIG_I2C_ISCH)		+= i2c-isch.o
+obj-$(CONFIG_I2C_ISMT)		+= i2c-ismt.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_NFORCE2_S4985)	+= i2c-nforce2-s4985.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
drivers/i2c/busses/i2c-ismt.c new file +932 -0

932 lines not on this page · raw message

Signature · 1 line
1.7.11.7

A message that is not in the archive (sent off-list or not yet received): 1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org

#7 lore raw

Hi Neil,

Quoted 13 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Quoted 3 lines

---
Forgive the latency in this reply, Jean, Bill has gone on sabbatical, so I finished this up on behalf of Intel.

No problem, thanks for stepping in and sending this updated version. I'm afraid we'll need one more round though (partly my fault, sorry about that), see my comments in-line.

BTW, when I asked Bill several month ago for a datasheet for this part, he told me it wasn't available publicly yet. Has the situation changed meanwhile? There is an issue with SMBus block reads, as you'll see below. Without a datasheet I can't verify how it can be solved, so you'll have to do it.

Quoted 48 lines

Change notes for V5)
* Remove __devexit, __devinit macros

* Convert module parameter to use units of khz

* Update Description to reflect current pci db

* Improve KConfig dependencies

* Improve PCI device id names

* Reduce dma buffer size to appropriate value and check mapping result

* Fix build failure resulting from missing readq definition

* Limit use of memcpy and copy length in ismt_process_desc

* Fix off by one error in dma_size

* Convert driver to uninterruptible wait on dma transfers

* Add spped sync with bus_speed module option

* Misc cleanups and fixes

* Fix FTBFS when CONFIG_DYNAMIC_DEBUG is off

---
 Documentation/i2c/busses/i2c-ismt |  36 ++
 drivers/i2c/busses/Kconfig        |  10 +
 drivers/i2c/busses/Makefile       |   1 +
 drivers/i2c/busses/i2c-ismt.c     | 932 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 979 insertions(+)
 create mode 100644 Documentation/i2c/busses/i2c-ismt
 create mode 100644 drivers/i2c/busses/i2c-ismt.c
Documentation/i2c/busses/i2c-ismt new file +7 -0
new file mode 100644
index 0000000..aa3b60f
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>

Documentation/i2c/busses/i2c-ismt line 17

Quoted 10 lines
Documentation/i2c/busses/i2c-ismt new file +10 -0
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Specify the bus speed in units of KHz.

"in kHz" should do.

Documentation/i2c/busses/i2c-ismt line 23

Quoted 6 lines
Documentation/i2c/busses/i2c-ismt new file +6 -0
+Available bus frequency settings:
+  0  no change
+  80 kHz
+  100 kHz
+  400 kHz
+  1000 KHz

Should still be a lowercase "k" even if it's a big number ;)

Quoted 34 lines
Documentation/i2c/busses/i2c-ismt new file +13 -0
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci will show
+something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0
+  00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1
(...)
drivers/i2c/busses/i2c-ismt.c new file +13 -0
new file mode 100644
index 0000000..5ad4680
@@ -0,0 +1,932 @@
(...)
+/*
+ *  Supports the SMBus Message Transport (SMT) in the Intel Atom Processor
+ *  S12xx Product Family.
+ *
+ *  Features supported by this driver:
+ *  Hardware PEC                     yes
+ *  Block buffer                     yes
+ *  Block process call transaction   no
+ *  Slave mode                       no
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>

drivers/i2c/busses/i2c-ismt.c line 75

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+
+#include <asm-generic/io-64-nonatomic-lo-hi.h>

This should fix the build issue on X86_32. This also means that you should now be able to write register value ISMT_MSTR_MDBA in ismt_hw_init() using writeq(), as was done originally, instead of two writel(). This would be both more efficient and more consistent.

There are two build warnings left on 32-bit, see below.

drivers/i2c/busses/i2c-ismt.c line 196

Quoted 5 lines

(...)

drivers/i2c/busses/i2c-ismt.c new file +4 -0
+/* Bus speed control bits for slow debuggers - refer to the docs for usage */
+static unsigned int bus_speed;
+module_param(bus_speed, uint, S_IRUGO);
+MODULE_PARM_DESC(bus_speed, "Bus Speed in KHz (0 = BIOS default)");

Proper casing is "kHz".

Quoted 6 lines

(...)

drivers/i2c/busses/i2c-ismt.c new file +5 -0
+/**
+ * ismt_gen_reg_dump() - dump the iSMT General Registers
+ * @priv: iSMT private data
+ */
+static void ismt_gen_reg_dump(struct ismt_priv *priv)

drivers/i2c/busses/i2c-ismt.c line 234

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT General Registers\n");
+	dev_dbg(dev, "  GCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_GCTRL,
+		readl(priv->smba + ISMT_GR_GCTRL));
+	dev_dbg(dev, "  SMTICL... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_GR_SMTICL,
+		readq(priv->smba + ISMT_GR_SMTICL));

drivers/i2c/busses/i2c-ismt.c: In function ‘ismt_gen_reg_dump’:
drivers/i2c/busses/i2c-ismt.c:232: warning: format ‘%016lX’ expects type ‘long unsigned int’, but argument 5 has type ‘__u64’

Quoted 16 lines
drivers/i2c/busses/i2c-ismt.c new file +16 -0
+	dev_dbg(dev, "  ERRINTMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINTMSK,
+		readl(priv->smba + ISMT_GR_ERRINTMSK));
+	dev_dbg(dev, "  ERRAERMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRAERMSK,
+		readl(priv->smba + ISMT_GR_ERRAERMSK));
+	dev_dbg(dev, "  ERRSTS... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRSTS,
+		readl(priv->smba + ISMT_GR_ERRSTS));
+	dev_dbg(dev, "  ERRINFO.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINFO,
+		readl(priv->smba + ISMT_GR_ERRINFO));
+}
+
+/**
+ * ismt_mstr_reg_dump() - dump the iSMT Master Registers

drivers/i2c/busses/i2c-ismt.c line 260

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ * @priv: iSMT private data
+ */
+static void ismt_mstr_reg_dump(struct ismt_priv *priv)
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT Master Registers\n");
+	dev_dbg(dev, "  MDBA..... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_MSTR_MDBA,
+		readq(priv->smba + ISMT_MSTR_MDBA));

drivers/i2c/busses/i2c-ismt.c: In function ‘ismt_mstr_reg_dump’:
drivers/i2c/busses/i2c-ismt.c:258: warning: format ‘%016lX’ expects type ‘long unsigned int’, but argument 5 has type ‘__u64’

Quoted 22 lines
drivers/i2c/busses/i2c-ismt.c new file +21 -0
+	dev_dbg(dev, "  MCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MCTRL,
+		readl(priv->smba + ISMT_MSTR_MCTRL));
+	dev_dbg(dev, "  MSTS..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MSTS,
+		readl(priv->smba + ISMT_MSTR_MSTS));
+	dev_dbg(dev, "  MDS...... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MDS,
+		readl(priv->smba + ISMT_MSTR_MDS));
+	dev_dbg(dev, "  RPOLICY.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_RPOLICY,
+		readl(priv->smba + ISMT_MSTR_RPOLICY));
+	dev_dbg(dev, "  SPGT..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_SPGT,
+		readl(priv->smba + ISMT_SPGT));
+}
+
(...)
+/**
+ * ismt_process_desc() - handle the completion of the descriptor
+ * @desc: the iSMT hardware descriptor
+ * @data: data buffer from the upper layer

drivers/i2c/busses/i2c-ismt.c line 316

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ * @dma_buffer: temporary buffer for the DMA engine
+ * @size: SMBus transaction type
+ */
+static int ismt_process_desc(const struct ismt_desc *desc,
+			     union i2c_smbus_data *data,
+			     u8 *dma_buffer, int size,
+			     char read_write)
+{
+	if ((read_write == I2C_SMBUS_READ) &&
+	    (desc->status & ISMT_DESC_SCS)) {

You added the "read_write == I2C_SMBUS_READ" test in the wrong place. You should add it below, to limit the memcpy to read transactions. Putting it here instead will cause all write transactions to be reported as having failed. Which brings a question... you did not test the updated driver, did you?

drivers/i2c/busses/i2c-ismt.c line 319

Quoted 3 lines
drivers/i2c/busses/i2c-ismt.c new file +3 -0
+		if (size != I2C_SMBUS_QUICK)
+			memcpy(data, dma_buffer,
+			       min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX));

This computation looks wrong. We already know that sizeof(*data) == sizeof(union i2c_smbus_data) > I2C_SMBUS_BLOCK_MAX, so the min() above will always return I2C_SMBUS_BLOCK_MAX. Which may be insufficient as the first byte holds the length, and at the same time it will be more than needed in most cases.

My original suggestion to limit the size of the copy was meant as a run-time optimization, i.e. copying exactly the number of bytes that were received from the slave. That would be 1 or 2 for non-block reads, and the block length for block reads. Unfortunately I'm not sure is the block length is available. See my comment about this below in ismt_access().

Quoted 43 lines
drivers/i2c/busses/i2c-ismt.c new file +43 -0
+		return 0;
+	}
+
+	if (likely(desc->status & ISMT_DESC_NAK))
+		return -ENXIO;
+
+	if (desc->status & ISMT_DESC_CRC)
+		return -EBADMSG;
+
+	if (desc->status & ISMT_DESC_COL)
+		return -EAGAIN;
+
+	if (desc->status & ISMT_DESC_LPR)
+		return -EPROTO;
+
+	if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO))
+		return -ETIMEDOUT;
+
+	return -EIO;
+}
+
+/**
+ * ismt_access() - process an SMBus command
+ * @adap: the i2c host adapter
+ * @addr: address of the i2c/SMBus target
+ * @flags: command options
+ * @read_write: read from or write to device
+ * @command: the i2c/SMBus command to issue
+ * @size: SMBus transaction type
+ * @data: read/write data buffer
+ */
+static int ismt_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data *data)
+{
+	int ret;
+	dma_addr_t dma_addr = 0; /* address of the data buffer */
+	u8 dma_size = 0;
+	enum dma_data_direction dma_direction = 0;
+	struct ismt_desc *desc;
+	struct ismt_priv *priv = i2c_get_adapdata(adap);
+	struct device *dev = &priv->pci_dev->dev;
+

drivers/i2c/busses/i2c-ismt.c line 372

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	desc = &priv->hw[priv->head];
+
+	/* Initialize the descriptor */
+	memset(desc, 0, sizeof(struct ismt_desc));
+	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
+
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if ((read_write == I2C_SMBUS_WRITE) &&
+	    (size != I2C_SMBUS_QUICK)) {

If I read the code below properly, this can be skipped for size == I2C_SMBUS_BYTE too, right?

drivers/i2c/busses/i2c-ismt.c line 374

Quoted 2 lines
drivers/i2c/busses/i2c-ismt.c new file +2 -0
+		memcpy(priv->dma_buffer + 1, data,
+		       min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX));

Thinking about it some more, this is wrong for block transactions. Sorry for not spotting it before. For block transactions, data->block[0] is used for the length, so you can't just copy the union i2c_smbus_data blindly to dma_buffer. You have to start copying from data->block[1], and data->block[0] could be used to limit the length of the memcpy. For non-block writes, length can only be 1 or 2 so you could limit it to 2.

Quoted 25 lines
drivers/i2c/busses/i2c-ismt.c new file +25 -0
+		priv->dma_buffer[0] = command;
+	}
+
+	/* Initialize common control bits */
+	if (likely(priv->using_msi))
+		desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR;
+	else
+		desc->control = ISMT_DESC_FAIR;
+
+	if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK)
+	    && (size != I2C_SMBUS_I2C_BLOCK_DATA))
+		desc->control |= ISMT_DESC_PEC;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		dev_dbg(dev, "I2C_SMBUS_QUICK\n");
+		break;
+
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Send Byte
+			 * The command field contains the write data
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  WRITE\n");

drivers/i2c/busses/i2c-ismt.c line 409

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+		} else {
+			/* Receive Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  READ\n");
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = 1;
+		}
+

This blank line could go away. There are more occurrences of this in this function.

Quoted 53 lines
drivers/i2c/busses/i2c-ismt.c new file +53 -0
+		break;
+
+	case I2C_SMBUS_BYTE_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Write Byte
+			 * Command plus 1 data byte
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  WRITE\n");
+			desc->wr_len_cmd = 2;
+			dma_size = 2;
+			dma_direction = DMA_TO_DEVICE;
+		} else {
+			/* Read Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  READ\n");
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+			desc->rd_len = 1;
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+		}
+
+		break;
+
+	case I2C_SMBUS_WORD_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* Write Word */
+			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:  WRITE\n");
+			desc->wr_len_cmd = 3;
+			dma_size = 3;
+			dma_direction = DMA_TO_DEVICE;
+		} else {
+			/* Read Word */
+			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:  READ\n");
+			desc->wr_len_cmd = command;
+			desc->control |= ISMT_DESC_CWRL;
+			desc->rd_len = 2;
+			dma_size = 2;
+			dma_direction = DMA_FROM_DEVICE;
+		}
+
+		break;
+
+	case I2C_SMBUS_PROC_CALL:
+		dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n");
+		desc->wr_len_cmd = 3;
+		desc->rd_len = 2;
+		dma_size = 3;
+		dma_direction = DMA_BIDIRECTIONAL;
+		break;
+
+	case I2C_SMBUS_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {

drivers/i2c/busses/i2c-ismt.c line 472

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+			/* Block Write */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  WRITE\n");
+			dma_size = data->block[0] + 1;
+			dma_direction = DMA_TO_DEVICE;
+			desc->wr_len_cmd = dma_size;
+			desc->control |= ISMT_DESC_BLK;
+		} else {
+			/* Block Read */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  READ\n");
+			dma_size = I2C_SMBUS_BLOCK_MAX + 1;

I am the one who asked for the + 1 here, now I realize it might not be correct. It really depends on what the hardware is returning on SMBus block reads.

What you are supposed to return to the caller, per Linux' i2c API, is the block length in data->block[0] and that many bytes in data->block[1..n]. Question is, what do you get from the hardware in dma_buffer? If you get the block length followed by the data then "dma_size = I2C_SMBUS_BLOCK_MAX + 1" is correct and the code in ismt_process_desc() is almost correct as well (you only have to get the memcpy size right.)

OTOH if the hardware gives your only the data bytes in dma_buffer, then the "+ 1" above is wrong, and we also face an issue with retrieving the block length. Where is the hardware making it available to us? I certainly hope it does.

Quoted 48 lines
drivers/i2c/busses/i2c-ismt.c new file +48 -0
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = dma_size;
+			desc->wr_len_cmd = command;
+			desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL);
+		}
+
+		break;
+
+	default:
+		dev_err(dev, "Unsupported transaction %d\n",
+			size);
+		return -EOPNOTSUPP;
+	}
+
+	/* map the data buffer */
+	if (dma_size != 0) {
+		dev_dbg(dev, " dev=%p\n", dev);
+		dev_dbg(dev, " data=%p\n", data);
+		dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer);
+		dev_dbg(dev, " dma_size=%d\n", dma_size);
+		dev_dbg(dev, " dma_direction=%d\n", dma_direction);
+
+		dma_addr = dma_map_single(dev,
+				      priv->dma_buffer,
+				      dma_size,
+				      dma_direction);
+
+		if (dma_mapping_error(dev, dma_addr)) {
+			dev_err(dev, "Error in mapping dma buffer %p\n", priv->dma_buffer);
+			return -EIO;
+		}
+
+		dev_dbg(dev, " dma_addr = 0x%016llX\n",
+			dma_addr);
+
+		desc->dptr_low = lower_32_bits(dma_addr);
+		desc->dptr_high = upper_32_bits(dma_addr);
+	}
+
+	INIT_COMPLETION(priv->cmp);
+
+	/* Add the descriptor */
+	ismt_submit_desc(priv);
+
+	/* Now we wait for interrupt completion, 1s */
+	ret = wait_for_completion_timeout(&priv->cmp, HZ*1);
+
+	/* unmap the data buffer */

drivers/i2c/busses/i2c-ismt.c line 530

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	if (dma_size != 0)
+		dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction);
+
+	if (unlikely(!ret)) {
+		dev_err(dev, "completion wait timed out\n");
+		ret = -ETIMEDOUT;
+	} 
+
+	/* do any post processing of the descriptor here */
+	ret = ismt_process_desc(desc, data, priv->dma_buffer, size, read_write);

There's something wrong here, the "ret = -ETIMEDOUT" above is overwritten by this call so the error code is lost. Bill's code did not have the problem, not sure why you changed it.

Quoted 35 lines
drivers/i2c/busses/i2c-ismt.c new file +34 -0
+
+	/* Update the ring pointer */
+	priv->head++;
+	priv->head %= ISMT_DESC_ENTRIES;
+
+	return ret;
+}
+
(...)
+/**
+ * ismt_hw_init() - initialize the iSMT hardware
+ * @priv: iSMT private data
+ */
+static void ismt_hw_init(struct ismt_priv *priv)
+{
+	u32 val;
+	struct device *dev = &priv->pci_dev->dev;
+
+	/* initialize the Master Descriptor Base Address (MDBA) */
+	writel(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA);
+	writel(priv->io_rng_dma >> 32, priv->smba + ISMT_MSTR_MDBA + 4);
+
+	/* initialize the Master Control Register (MCTRL) */
+	writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL);
+
+	/* initialize the Master Status Register (MSTS) */
+	writel(0, priv->smba + ISMT_MSTR_MSTS);
+
+	/* initialize the Master Descriptor Size (MDS) */
+	val = readl(priv->smba + ISMT_MSTR_MDS);
+	writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1),
+		priv->smba + ISMT_MSTR_MDS);
+
+	/*
+	 * Set the SMBus speed (could use this for slow HW debuggers)

drivers/i2c/busses/i2c-ismt.c line 646

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	 */
+
+	val = readl(priv->smba + ISMT_SPGT);
+
+	switch (bus_speed) {
+	case 0:
+		break;
+
+	case 80:
+		dev_dbg(dev, "Setting SMBus clock to 80 kHz\n");

You did add a space here between "80" and "kHz" as I asked, but...

drivers/i2c/busses/i2c-ismt.c line 652

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 100:
+		dev_dbg(dev, "Setting SMBus clock to 100kHz\n");

... not here...

drivers/i2c/busses/i2c-ismt.c line 658

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 400:
+		dev_dbg(dev, "Setting SMBus clock to 400kHz\n");

... nor here...

drivers/i2c/busses/i2c-ismt.c line 664

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 1000:
+		dev_dbg(dev, "Setting SMBus clock to 1MHz\n");

... nor here.

drivers/i2c/busses/i2c-ismt.c line 670

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	default:
+		dev_dbg(dev, "Invalid SMBus clock speed, only 1-4 are valid\n");

Valid values are now 80, 100, 400 and 1000. This is more than a debug message BTW, this should be a warning IMHO.

Quoted 8 lines
drivers/i2c/busses/i2c-ismt.c new file +8 -0
+		break;
+	}
+
+	switch (val & ISMT_SPGT_SPD_MASK) {
+	case ISMT_SPGT_SPD_80K:
+		bus_speed = 80;
+		break;
+	case ISMT_SPGT_SPD_100K:

drivers/i2c/busses/i2c-ismt.c line 688

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+		bus_speed = 100;
+		break;
+	case ISMT_SPGT_SPD_400K:
+		bus_speed = 400;
+		break;
+	case ISMT_SPGT_SPD_1M:
+		bus_speed = 1000;
+		break;
+	}
+	dev_dbg(dev, "SMBus clock is running at %d KHz\n", bus_speed);

This is incorrect. You changed the value of register ISMT_SPGT based on the value of bus_speed, however you did not update "val" accordingly. So if the user forced a specific speed, the value you are printing is the speed at which the bus _was_ running before.

So you must update val prior to writing the new value to the register.

Also note that the proper casing is "kHz".

Quoted 77 lines
drivers/i2c/busses/i2c-ismt.c new file +76 -0
+}
+
(...)
+/**
+ * ismt_probe() - probe for iSMT devices
+ * @pdev: PCI-Express device
+ * @id: PCI-Express device ID
+ */
+static int
+ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int err;
+	struct ismt_priv *priv;
+	unsigned long start, len;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	pci_set_drvdata(pdev, priv);
+	i2c_set_adapdata(&priv->adapter, priv);
+	priv->adapter.owner = THIS_MODULE;
+
+	priv->adapter.class = I2C_CLASS_HWMON;
+
+	priv->adapter.algo = &smbus_algorithm;
+
+	/* set up the sysfs linkage to our parent device */
+	priv->adapter.dev.parent = &pdev->dev;
+
+	/* number of retries on lost arbitration */
+	priv->adapter.retries = ISMT_MAX_RETRIES;
+
+	priv->pci_dev = pdev;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to enable SMBus PCI device (%d)\n",
+			err);
+		return err;
+	}
+
+	/* enable bus mastering */
+	pci_set_master(pdev);
+
+	/* Determine the address of the SMBus area */
+	start = pci_resource_start(pdev, SMBBAR);
+	len = pci_resource_len(pdev, SMBBAR);
+	if (!start || !len) {
+		dev_err(&pdev->dev,
+			"SMBus base address uninitialized, upgrade BIOS\n");
+		return -ENODEV;
+	}
+
+	snprintf(priv->adapter.name, sizeof(priv->adapter.name),
+		 "SMBus iSMT adapter at %lx", start);
+
+	dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start);
+	dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len);
+
+	err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]);
+	if (err) {
+		dev_err(&pdev->dev, "ACPI resource conflict!\n");
+		return err;
+	}
+
+	err = pci_request_region(pdev, SMBBAR, ismt_driver.name);
+	if (err) {
+		dev_err(&pdev->dev,
+			"Failed to request SMBus region 0x%lx-0x%lx\n",
+			start, start + len);
+		return err;
+	}
+
+	priv->smba = pcim_iomap(pdev, SMBBAR, len);
+	if (!priv->smba) {
+		dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n");

drivers/i2c/busses/i2c-ismt.c line 848

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+		err = -ENODEV;
+		goto fail;
+	}
+
+	if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
+	    (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) {
+		if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) ||
+		    (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {
+			dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n",
+				 pdev);

One less space before "pdev" for perfect alignment.

Quoted 26 lines
drivers/i2c/busses/i2c-ismt.c new file +26 -0
+			goto fail;
+		}
+	}
+
+	err = ismt_dev_init(priv);
+	if (err)
+		goto fail;
+
+	ismt_hw_init(priv);
+
+	err = ismt_int_init(priv);
+	if (err)
+		goto fail;
+
+	err = i2c_add_adapter(&priv->adapter);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n");
+		err = -ENODEV;
+		goto fail;
+	}
+	return 0;
+
+fail:
+	pci_release_region(pdev, SMBBAR);
+	return err;
+}
Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#9 lore raw
Quoted 16 lines · Jean Delvare

Hi Neil,

Quoted 13 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Quoted 8 lines
Quoted 3 lines

---
Forgive the latency in this reply, Jean, Bill has gone on sabbatical, so I finished this up on behalf of Intel.

No problem, thanks for stepping in and sending this updated version. I'm afraid we'll need one more round though (partly my fault, sorry about that), see my comments in-line.

Understood, and no problem, I'll square it up.

Quoted 6 lines

BTW, when I asked Bill several month ago for a datasheet for this part, he told me it wasn't available publicly yet. Has the situation changed meanwhile? There is an issue with SMBus block reads, as you'll see below. Without a datasheet I can't verify how it can be solved, so you'll have to do it.

I'm afraid not (at least not that I know of), the only reason that I was able to post this was because most of the changes were fairly trivial. I'll see if I can't push to get a datasheet released however, either publically, or to myself, so that I can verify the issue.

Quoted 12 lines

Documentation/i2c/busses/i2c-ismt line 17

Quoted 9 lines
Documentation/i2c/busses/i2c-ismt new file +9 -0
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Specify the bus speed in units of KHz.

"in kHz" should do.

No problem.

Quoted 9 lines

Documentation/i2c/busses/i2c-ismt line 23

Quoted 6 lines
Documentation/i2c/busses/i2c-ismt new file +6 -0
+Available bus frequency settings:
+  0  no change
+  80 kHz
+  100 kHz
+  400 kHz
+  1000 KHz

Should still be a lowercase "k" even if it's a big number ;)

Check.

Quoted 10 lines
Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
Quoted 9 lines

drivers/i2c/busses/i2c-ismt.c line 75

Quoted 3 lines
drivers/i2c/busses/i2c-ismt.c new file +3 -0
+#include <linux/interrupt.h>
+
+#include <asm-generic/io-64-nonatomic-lo-hi.h>

This should fix the build issue on X86_32. This also means that you should now be able to write register value ISMT_MSTR_MDBA in ismt_hw_init() using writeq(), as was done originally, instead of two writel(). This would be both more efficient and more consistent.

Understood, I'll put that back.

Quoted 10 lines

There are two build warnings left on 32-bit, see below.

drivers/i2c/busses/i2c-ismt.c line 196

Quoted 5 lines

(...)

drivers/i2c/busses/i2c-ismt.c new file +4 -0
+/* Bus speed control bits for slow debuggers - refer to the docs for usage */
+static unsigned int bus_speed;
+module_param(bus_speed, uint, S_IRUGO);
+MODULE_PARM_DESC(bus_speed, "Bus Speed in KHz (0 = BIOS default)");

Proper casing is "kHz".

yup

Quoted 11 lines
Quoted 11 lines

(...)

drivers/i2c/busses/i2c-ismt.c new file +10 -0
+/**
+ * ismt_gen_reg_dump() - dump the iSMT General Registers
+ * @priv: iSMT private data
+ */
+static void ismt_gen_reg_dump(struct ismt_priv *priv)
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT General Registers\n");
+	dev_dbg(dev, "  GCTRL.... : (0x%p)=0x%X\n",
Quoted 9 lines

drivers/i2c/busses/i2c-ismt.c line 234

Quoted 5 lines
drivers/i2c/busses/i2c-ismt.c new file +5 -0
+		priv->smba + ISMT_GR_GCTRL,
+		readl(priv->smba + ISMT_GR_GCTRL));
+	dev_dbg(dev, "  SMTICL... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_GR_SMTICL,
+		readq(priv->smba + ISMT_GR_SMTICL));

drivers/i2c/busses/i2c-ismt.c: In function ‘ismt_gen_reg_dump’:
drivers/i2c/busses/i2c-ismt.c:232: warning: format ‘%016lX’ expects type ‘long unsigned int’, but argument 5 has type ‘__u64’

Doh! My bad, I'll fix that up.

Quoted 21 lines
Quoted 21 lines
drivers/i2c/busses/i2c-ismt.c new file +21 -0
+	dev_dbg(dev, "  ERRINTMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINTMSK,
+		readl(priv->smba + ISMT_GR_ERRINTMSK));
+	dev_dbg(dev, "  ERRAERMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRAERMSK,
+		readl(priv->smba + ISMT_GR_ERRAERMSK));
+	dev_dbg(dev, "  ERRSTS... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRSTS,
+		readl(priv->smba + ISMT_GR_ERRSTS));
+	dev_dbg(dev, "  ERRINFO.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINFO,
+		readl(priv->smba + ISMT_GR_ERRINFO));
+}
+
+/**
+ * ismt_mstr_reg_dump() - dump the iSMT Master Registers
+ * @priv: iSMT private data
+ */
+static void ismt_mstr_reg_dump(struct ismt_priv *priv)
+{
+	struct device *dev = &priv->pci_dev->dev;
Quoted 9 lines

drivers/i2c/busses/i2c-ismt.c line 260

Quoted 5 lines
drivers/i2c/busses/i2c-ismt.c new file +5 -0
+
+	dev_dbg(dev, "Dump of the iSMT Master Registers\n");
+	dev_dbg(dev, "  MDBA..... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_MSTR_MDBA,
+		readq(priv->smba + ISMT_MSTR_MDBA));

drivers/i2c/busses/i2c-ismt.c: In function ‘ismt_mstr_reg_dump’:
drivers/i2c/busses/i2c-ismt.c:258: warning: format ‘%016lX’ expects type ‘long unsigned int’, but argument 5 has type ‘__u64’

Ditto.

Quoted 30 lines
Quoted 30 lines
drivers/i2c/busses/i2c-ismt.c new file +29 -0
+	dev_dbg(dev, "  MCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MCTRL,
+		readl(priv->smba + ISMT_MSTR_MCTRL));
+	dev_dbg(dev, "  MSTS..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MSTS,
+		readl(priv->smba + ISMT_MSTR_MSTS));
+	dev_dbg(dev, "  MDS...... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MDS,
+		readl(priv->smba + ISMT_MSTR_MDS));
+	dev_dbg(dev, "  RPOLICY.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_RPOLICY,
+		readl(priv->smba + ISMT_MSTR_RPOLICY));
+	dev_dbg(dev, "  SPGT..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_SPGT,
+		readl(priv->smba + ISMT_SPGT));
+}
+
(...)
+/**
+ * ismt_process_desc() - handle the completion of the descriptor
+ * @desc: the iSMT hardware descriptor
+ * @data: data buffer from the upper layer
+ * @dma_buffer: temporary buffer for the DMA engine
+ * @size: SMBus transaction type
+ */
+static int ismt_process_desc(const struct ismt_desc *desc,
+			     union i2c_smbus_data *data,
+			     u8 *dma_buffer, int size,
+			     char read_write)
+{
Quoted 9 lines

drivers/i2c/busses/i2c-ismt.c line 316

Quoted 2 lines
drivers/i2c/busses/i2c-ismt.c new file +2 -0
+	if ((read_write == I2C_SMBUS_READ) &&
+	    (desc->status & ISMT_DESC_SCS)) {

You added the "read_write == I2C_SMBUS_READ" test in the wrong place. You should add it below, to limit the memcpy to read transactions. Putting it here instead will cause all write transactions to be reported as having failed. Which brings a question... you did not test the updated driver, did you?

I passed it over to intel for testing, and Seth, who is copied, gave it a thumbs up. Seth, can you elaborate?

Quoted 10 lines

drivers/i2c/busses/i2c-ismt.c line 319

Quoted 3 lines
drivers/i2c/busses/i2c-ismt.c new file +3 -0
+		if (size != I2C_SMBUS_QUICK)
+			memcpy(data, dma_buffer,
+			       min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX));

This computation looks wrong. We already know that sizeof(*data) == sizeof(union i2c_smbus_data) > I2C_SMBUS_BLOCK_MAX, so the min() above will always return I2C_SMBUS_BLOCK_MAX. Which may be insufficient as the first byte holds the length, and at the same time it will be more than needed in most cases.

Gah! Sorry, you're right, I wasn't paying attention and though that data was just an allocated buffer, I need to fix that.

Quoted 7 lines

My original suggestion to limit the size of the copy was meant as a run-time optimization, i.e. copying exactly the number of bytes that were received from the slave. That would be 1 or 2 for non-block reads, and the block length for block reads. Unfortunately I'm not sure is the block length is available. See my comment about this below in ismt_access().

We don't currently have the dma_size, no, but the only calling function computes it (either from the block header or sets it appropriately for non-block transfers). We can pass that into the process_desc function and fix this.

Quoted 28 lines
Quoted 28 lines
drivers/i2c/busses/i2c-ismt.c new file +28 -0
+
+/**
+ * ismt_access() - process an SMBus command
+ * @adap: the i2c host adapter
+ * @addr: address of the i2c/SMBus target
+ * @flags: command options
+ * @read_write: read from or write to device
+ * @command: the i2c/SMBus command to issue
+ * @size: SMBus transaction type
+ * @data: read/write data buffer
+ */
+static int ismt_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data *data)
+{
+	int ret;
+	dma_addr_t dma_addr = 0; /* address of the data buffer */
+	u8 dma_size = 0;
+	enum dma_data_direction dma_direction = 0;
+	struct ismt_desc *desc;
+	struct ismt_priv *priv = i2c_get_adapdata(adap);
+	struct device *dev = &priv->pci_dev->dev;
+
+	desc = &priv->hw[priv->head];
+
+	/* Initialize the descriptor */
+	memset(desc, 0, sizeof(struct ismt_desc));
+	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
Quoted 8 lines

drivers/i2c/busses/i2c-ismt.c line 372

Quoted 5 lines
drivers/i2c/busses/i2c-ismt.c new file +5 -0
+
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if ((read_write == I2C_SMBUS_WRITE) &&
+	    (size != I2C_SMBUS_QUICK)) {

If I read the code below properly, this can be skipped for size == I2C_SMBUS_BYTE too, right?

I don't think so. The command field contains the data, and that is right below the memcpy in that if clause. We can certainly optimize this however, given that I need to fix the memcpy sizes anyway.

Quoted 12 lines

drivers/i2c/busses/i2c-ismt.c line 374

Quoted 3 lines
drivers/i2c/busses/i2c-ismt.c new file +2 -0
+		memcpy(priv->dma_buffer + 1, data,
+		       min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX));

Thinking about it some more, this is wrong for block transactions. Sorry for not spotting it before. For block transactions, data->block[0] is used for the length, so you can't just copy the union i2c_smbus_data blindly to dma_buffer. You have to start copying from data->block[1], and data->block[0] could be used to limit the length of the memcpy. For non-block writes, length can only be 1 or 2 so you could limit it to 2.

understood.

Quoted 30 lines
Quoted 30 lines
drivers/i2c/busses/i2c-ismt.c new file +30 -0
+		priv->dma_buffer[0] = command;
+	}
+
+	/* Initialize common control bits */
+	if (likely(priv->using_msi))
+		desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR;
+	else
+		desc->control = ISMT_DESC_FAIR;
+
+	if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK)
+	    && (size != I2C_SMBUS_I2C_BLOCK_DATA))
+		desc->control |= ISMT_DESC_PEC;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		dev_dbg(dev, "I2C_SMBUS_QUICK\n");
+		break;
+
+	case I2C_SMBUS_BYTE:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/*
+			 * Send Byte
+			 * The command field contains the write data
+			 */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  WRITE\n");
+			desc->control |= ISMT_DESC_CWRL;
+			desc->wr_len_cmd = command;
+		} else {
+			/* Receive Byte */
+			dev_dbg(dev, "I2C_SMBUS_BYTE:  READ\n");
Quoted 9 lines

drivers/i2c/busses/i2c-ismt.c line 409

Quoted 5 lines
drivers/i2c/busses/i2c-ismt.c new file +5 -0
+			dma_size = 1;
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = 1;
+		}
+

This blank line could go away. There are more occurrences of this in this function.

Ok

Quoted 26 lines

drivers/i2c/busses/i2c-ismt.c line 472

Quoted 13 lines
drivers/i2c/busses/i2c-ismt.c new file +13 -0
+
+	case I2C_SMBUS_BLOCK_DATA:
+		if (read_write == I2C_SMBUS_WRITE) {
+			/* Block Write */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  WRITE\n");
+			dma_size = data->block[0] + 1;
+			dma_direction = DMA_TO_DEVICE;
+			desc->wr_len_cmd = dma_size;
+			desc->control |= ISMT_DESC_BLK;
+		} else {
+			/* Block Read */
+			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  READ\n");
+			dma_size = I2C_SMBUS_BLOCK_MAX + 1;

I am the one who asked for the + 1 here, now I realize it might not be correct. It really depends on what the hardware is returning on SMBus block reads.

What you are supposed to return to the caller, per Linux' i2c API, is the block length in data->block[0] and that many bytes in data->block[1..n]. Question is, what do you get from the hardware in dma_buffer? If you get the block length followed by the data then "dma_size = I2C_SMBUS_BLOCK_MAX + 1" is correct and the code in ismt_process_desc() is almost correct as well (you only have to get the memcpy size right.)

Quoted 5 lines

OTOH if the hardware gives your only the data bytes in dma_buffer, then the "+ 1" above is wrong, and we also face an issue with retrieving the block length. Where is the hardware making it available to us? I certainly hope it does.

It better, I'll speak with Intel and see if I can't get the datasheet for this device, and make the appropriate fix here.

Quoted 54 lines
Quoted 54 lines
drivers/i2c/busses/i2c-ismt.c new file +54 -0
+			dma_direction = DMA_FROM_DEVICE;
+			desc->rd_len = dma_size;
+			desc->wr_len_cmd = command;
+			desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL);
+		}
+
+		break;
+
+	default:
+		dev_err(dev, "Unsupported transaction %d\n",
+			size);
+		return -EOPNOTSUPP;
+	}
+
+	/* map the data buffer */
+	if (dma_size != 0) {
+		dev_dbg(dev, " dev=%p\n", dev);
+		dev_dbg(dev, " data=%p\n", data);
+		dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer);
+		dev_dbg(dev, " dma_size=%d\n", dma_size);
+		dev_dbg(dev, " dma_direction=%d\n", dma_direction);
+
+		dma_addr = dma_map_single(dev,
+				      priv->dma_buffer,
+				      dma_size,
+				      dma_direction);
+
+		if (dma_mapping_error(dev, dma_addr)) {
+			dev_err(dev, "Error in mapping dma buffer %p\n", priv->dma_buffer);
+			return -EIO;
+		}
+
+		dev_dbg(dev, " dma_addr = 0x%016llX\n",
+			dma_addr);
+
+		desc->dptr_low = lower_32_bits(dma_addr);
+		desc->dptr_high = upper_32_bits(dma_addr);
+	}
+
+	INIT_COMPLETION(priv->cmp);
+
+	/* Add the descriptor */
+	ismt_submit_desc(priv);
+
+	/* Now we wait for interrupt completion, 1s */
+	ret = wait_for_completion_timeout(&priv->cmp, HZ*1);
+
+	/* unmap the data buffer */
+	if (dma_size != 0)
+		dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction);
+
+	if (unlikely(!ret)) {
+		dev_err(dev, "completion wait timed out\n");
+		ret = -ETIMEDOUT;
Quoted 9 lines

drivers/i2c/busses/i2c-ismt.c line 530

Quoted 4 lines
drivers/i2c/busses/i2c-ismt.c new file +4 -0
+	} 
+
+	/* do any post processing of the descriptor here */
+	ret = ismt_process_desc(desc, data, priv->dma_buffer, size, read_write);

There's something wrong here, the "ret = -ETIMEDOUT" above is overwritten by this call so the error code is lost. Bill's code did not have the problem, not sure why you changed it.

I changed it because you asked for this to be modified to the uninterruptible version of the completion api. I see what you mean though, I didn't handle the ETIMEDOUT error case properly.

Quoted 75 lines
Quoted 35 lines
drivers/i2c/busses/i2c-ismt.c new file +34 -0
+
+	/* Update the ring pointer */
+	priv->head++;
+	priv->head %= ISMT_DESC_ENTRIES;
+
+	return ret;
+}
+
(...)
+/**
+ * ismt_hw_init() - initialize the iSMT hardware
+ * @priv: iSMT private data
+ */
+static void ismt_hw_init(struct ismt_priv *priv)
+{
+	u32 val;
+	struct device *dev = &priv->pci_dev->dev;
+
+	/* initialize the Master Descriptor Base Address (MDBA) */
+	writel(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA);
+	writel(priv->io_rng_dma >> 32, priv->smba + ISMT_MSTR_MDBA + 4);
+
+	/* initialize the Master Control Register (MCTRL) */
+	writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL);
+
+	/* initialize the Master Status Register (MSTS) */
+	writel(0, priv->smba + ISMT_MSTR_MSTS);
+
+	/* initialize the Master Descriptor Size (MDS) */
+	val = readl(priv->smba + ISMT_MSTR_MDS);
+	writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1),
+		priv->smba + ISMT_MSTR_MDS);
+
+	/*
+	 * Set the SMBus speed (could use this for slow HW debuggers)

drivers/i2c/busses/i2c-ismt.c line 646

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	 */
+
+	val = readl(priv->smba + ISMT_SPGT);
+
+	switch (bus_speed) {
+	case 0:
+		break;
+
+	case 80:
+		dev_dbg(dev, "Setting SMBus clock to 80 kHz\n");

You did add a space here between "80" and "kHz" as I asked, but...

drivers/i2c/busses/i2c-ismt.c line 652

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 100:
+		dev_dbg(dev, "Setting SMBus clock to 100kHz\n");

... not here...

drivers/i2c/busses/i2c-ismt.c line 658

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 400:
+		dev_dbg(dev, "Setting SMBus clock to 400kHz\n");

... nor here...

drivers/i2c/busses/i2c-ismt.c line 664

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	case 1000:
+		dev_dbg(dev, "Setting SMBus clock to 1MHz\n");

... nor here.

Quoted 10 lines

drivers/i2c/busses/i2c-ismt.c line 670

Quoted 6 lines
drivers/i2c/busses/i2c-ismt.c new file +6 -0
+		writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M),
+			priv->smba + ISMT_SPGT);
+		break;
+
+	default:
+		dev_dbg(dev, "Invalid SMBus clock speed, only 1-4 are valid\n");

Valid values are now 80, 100, 400 and 1000. This is more than a debug message BTW, this should be a warning IMHO.

Ok

Quoted 24 lines
Quoted 8 lines
drivers/i2c/busses/i2c-ismt.c new file +8 -0
+		break;
+	}
+
+	switch (val & ISMT_SPGT_SPD_MASK) {
+	case ISMT_SPGT_SPD_80K:
+		bus_speed = 80;
+		break;
+	case ISMT_SPGT_SPD_100K:

drivers/i2c/busses/i2c-ismt.c line 688

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+		bus_speed = 100;
+		break;
+	case ISMT_SPGT_SPD_400K:
+		bus_speed = 400;
+		break;
+	case ISMT_SPGT_SPD_1M:
+		bus_speed = 1000;
+		break;
+	}
+	dev_dbg(dev, "SMBus clock is running at %d KHz\n", bus_speed);

This is incorrect. You changed the value of register ISMT_SPGT based on the value of bus_speed, however you did not update "val" accordingly. So if the user forced a specific speed, the value you are printing is the speed at which the bus _was_ running before.

Quoted 2 lines

So you must update val prior to writing the new value to the register.

Ah, you're right, thanks.

Quoted 3 lines

Also note that the proper casing is "kHz".

Give me a day or two, and I'll have these fixed up. Neil

A message that is not in the archive (sent off-list or not yet received): 20130129153253.GA14044-B26myB8xz7F8NnZeBjwnZQMhkBWG/[email protected]

#11 lore raw
Quoted 22 lines · Neil Horman
Quoted 10 lines · Jean Delvare

drivers/i2c/busses/i2c-ismt.c line 319

Quoted 3 lines · Neil Horman
drivers/i2c/busses/i2c-ismt.c new file +3 -0
+		if (size != I2C_SMBUS_QUICK)
+			memcpy(data, dma_buffer,
+			       min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX));

This computation looks wrong. We already know that sizeof(*data) == sizeof(union i2c_smbus_data) > I2C_SMBUS_BLOCK_MAX, so the min() above will always return I2C_SMBUS_BLOCK_MAX. Which may be insufficient as the first byte holds the length, and at the same time it will be more than needed in most cases.

Gah! Sorry, you're right, I wasn't paying attention and though that data was just an allocated buffer, I need to fix that.

Quoted 6 lines

My original suggestion to limit the size of the copy was meant as a run-time optimization, i.e. copying exactly the number of bytes that were received from the slave. That would be 1 or 2 for non-block reads, and the block length for block reads. Unfortunately I'm not sure is the block length is available. See my comment about this below in ismt_access().

Quoted 3 lines

We don't currently have the dma_size, no, but the only calling function computes it (either from the block header or sets it appropriately for non-block transfers). We can pass that into the process_desc function and fix this.

This won't fix it, unless the hardware happens to update dma_size when receiving a block from a slave. If not then dma_size will always be I2C_SMBUS_BLOCK_MAX, while what we need is the number of bytes actually received.

Quoted 25 lines
Quoted 17 lines
Quoted 17 lines
drivers/i2c/busses/i2c-ismt.c new file +17 -0
+static int ismt_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data *data)
+{
+	int ret;
+	dma_addr_t dma_addr = 0; /* address of the data buffer */
+	u8 dma_size = 0;
+	enum dma_data_direction dma_direction = 0;
+	struct ismt_desc *desc;
+	struct ismt_priv *priv = i2c_get_adapdata(adap);
+	struct device *dev = &priv->pci_dev->dev;
+
+	desc = &priv->hw[priv->head];
+
+	/* Initialize the descriptor */
+	memset(desc, 0, sizeof(struct ismt_desc));
+	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
Quoted 8 lines

drivers/i2c/busses/i2c-ismt.c line 372

Quoted 5 lines
drivers/i2c/busses/i2c-ismt.c new file +5 -0
+
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if ((read_write == I2C_SMBUS_WRITE) &&
+	    (size != I2C_SMBUS_QUICK)) {

If I read the code below properly, this can be skipped for size == I2C_SMBUS_BYTE too, right?

Quoted 3 lines

I don't think so. The command field contains the data, and that is right below the memcpy in that if clause. We can certainly optimize this however, given that I need to fix the memcpy sizes anyway.

Precisely, I'm not sure the priv->dma_buffer[0] = command is needed in this specific case, because later on we have:

			desc->control |= ISMT_DESC_CWRL;
			desc->wr_len_cmd = command;

and dma_size isn't set, which suggests that priv->dma_buffer won't even be used. Honestly this "presetting" of priv->dma_buffer causes more confusion than it helps, and performance-wise it makes little sense, so I wouldn't object to you exploding it to the relevant code paths in the switch statement.

I'll take a look at the datasheet tomorrow to check if I got it right or you did. Or neither of us ^^ I'll also look for test hardware, I don't have that at home obviously, but at work maybe.

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#13 lore raw
Quoted 23 lines · Jean Delvare
Quoted 22 lines · Neil Horman
Quoted 10 lines · Jean Delvare

Review

Quoted 3 lines · Neil Horman
(quoted diff) +3 -0
+		if (size != I2C_SMBUS_QUICK)
+			memcpy(data, dma_buffer,
+			       min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX));

This computation looks wrong. We already know that sizeof(*data) == sizeof(union i2c_smbus_data) > I2C_SMBUS_BLOCK_MAX, so the min() above will always return I2C_SMBUS_BLOCK_MAX. Which may be insufficient as the first byte holds the length, and at the same time it will be more than needed in most cases.

Gah! Sorry, you're right, I wasn't paying attention and though that data was just an allocated buffer, I need to fix that.

Quoted 6 lines

My original suggestion to limit the size of the copy was meant as a run-time optimization, i.e. copying exactly the number of bytes that were received from the slave. That would be 1 or 2 for non-block reads, and the block length for block reads. Unfortunately I'm not sure is the block length is available. See my comment about this below in ismt_access().

Quoted 9 lines
Quoted 3 lines

We don't currently have the dma_size, no, but the only calling function computes it (either from the block header or sets it appropriately for non-block transfers). We can pass that into the process_desc function and fix this.

This won't fix it, unless the hardware happens to update dma_size when receiving a block from a slave. If not then dma_size will always be I2C_SMBUS_BLOCK_MAX, while what we need is the number of bytes actually received.

I'm sorry, I misspoke. Its the rxbytes field in the descriptor that we need to use. I believe that field gets updated by the firmware on dmas from the device to the cpu.

Quoted 29 lines
Quoted 25 lines
Quoted 17 lines
Quoted 17 lines
(quoted diff) +17 -0
+static int ismt_access(struct i2c_adapter *adap, u16 addr,
+		       unsigned short flags, char read_write, u8 command,
+		       int size, union i2c_smbus_data *data)
+{
+	int ret;
+	dma_addr_t dma_addr = 0; /* address of the data buffer */
+	u8 dma_size = 0;
+	enum dma_data_direction dma_direction = 0;
+	struct ismt_desc *desc;
+	struct ismt_priv *priv = i2c_get_adapdata(adap);
+	struct device *dev = &priv->pci_dev->dev;
+
+	desc = &priv->hw[priv->head];
+
+	/* Initialize the descriptor */
+	memset(desc, 0, sizeof(struct ismt_desc));
+	desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if ((read_write == I2C_SMBUS_WRITE) &&
+	    (size != I2C_SMBUS_QUICK)) {

If I read the code below properly, this can be skipped for size == I2C_SMBUS_BYTE too, right?

Quoted 3 lines

I don't think so. The command field contains the data, and that is right below the memcpy in that if clause. We can certainly optimize this however, given that I need to fix the memcpy sizes anyway.

Quoted 6 lines

Precisely, I'm not sure the priv->dma_buffer[0] = command is needed in this specific case, because later on we have:

			desc->control |= ISMT_DESC_CWRL;
			desc->wr_len_cmd = command;

Yeah, and I cant quite glean from the datasheet what the requirement is. Seth, do you have any insight on this?

Quoted 6 lines

and dma_size isn't set, which suggests that priv->dma_buffer won't even be used. Honestly this "presetting" of priv->dma_buffer causes more confusion than it helps, and performance-wise it makes little sense, so I wouldn't object to you exploding it to the relevant code paths in the switch statement.

I might do that. I'm geting my hands on a system with this hardware in it, so I can experiment a bit with the best way to do it.

Quoted 4 lines

I'll take a look at the datasheet tomorrow to check if I got it right or you did. Or neither of us ^^ I'll also look for test hardware, I don't have that at home obviously, but at work maybe.

I'll have some tomorrow. I've got all your trivial changes fixed up, and will hopefully be able to sort out the convoluted stuff with you and Seth in the next few days

Neil

Quoted 3 lines
Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): 20130130141504.GA2968-0o1r3XBGOEbbgkc5XkKeNuvMHUBZFtU3YPYVAmT7z5s@public.gmane.org

#15 lore raw
Quoted 9 lines · Neil Horman
Quoted 4 lines · Jean Delvare

This won't fix it, unless the hardware happens to update dma_size when receiving a block from a slave. If not then dma_size will always be I2C_SMBUS_BLOCK_MAX, while what we need is the number of bytes actually received.

I'm sorry, I misspoke. Its the rxbytes field in the descriptor that we need to use. I believe that field gets updated by the firmware on dmas from the device to the cpu.

I couldn't find a description of the hardware descriptor in the datasheets, so I'm afraid I can't help here. If it is really there then I'd be grateful if you can point me to the right file and page.

Quoted 4 lines

(...)
I'll have some tomorrow. I've got all your trivial changes fixed up, and will hopefully be able to sort out the convoluted stuff with you and Seth in the next few days

Great. I'll be happy to test whatever you have then.

Signature · 1 line
Jean Delvare
#16 lore raw
In reply to a message not in the archive

Hi Jean,

Quoted 4 lines

BTW, when I asked Bill several months ago for a datasheet for this part, he told me it wasn't available publicly yet. Has the situation changed meanwhile? There is an issue with SMBus block reads, as you'll see below. Without a datasheet I can't verify how it can be solved, so you'll have to do it.

The datasheet is now publicly available:

http://www.intel.com/content/www/us/en/processors/atom/atom-processor-s1200-datasheet-vol-1.html
http://www.intel.com/content/www/us/en/processors/atom/atom-processor-s1200-datasheet-vol-2.html
http://www.intel.com/content/www/us/en/processors/atom/atom-processor-s1200-spec-update.html

Regards,
-Seth

A message that is not in the archive (sent off-list or not yet received): DEC3922D1904474A8862C7BFF516EA9A59B47EB2-P5GAC/[email protected]

#18 lore raw
Quoted 7 lines · Seth

Hi Jean,

Quoted 4 lines

BTW, when I asked Bill several months ago for a datasheet for this part, he told me it wasn't available publicly yet. Has the situation changed meanwhile? There is an issue with SMBus block reads, as you'll see below. Without a datasheet I can't verify how it can be solved, so you'll have to do it.

Quoted 5 lines

The datasheet is now publicly available:

http://www.intel.com/content/www/us/en/processors/atom/atom-processor-s1200-datasheet-vol-1.html
http://www.intel.com/content/www/us/en/processors/atom/atom-processor-s1200-datasheet-vol-2.html
http://www.intel.com/content/www/us/en/processors/atom/atom-processor-s1200-spec-update.html

Excellent, thanks Seth!

Signature · 1 line
Jean Delvare
#19 lore raw
In reply to a message not in the archive

Oh, I forgot... checkpatch has the following to say:

WARNING: line over 80 characters
#699: FILE: drivers/i2c/busses/i2c-ismt.c:501:
+ dev_err(dev, "Error in mapping dma buffer %p\n", priv->dma_buffer);

ERROR: trailing whitespace
#725: FILE: drivers/i2c/busses/i2c-ismt.c:527:
+^I} $

WARNING: line over 80 characters
#1044: FILE: drivers/i2c/busses/i2c-ismt.c:846:
+ (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) {

total: 1 errors, 2 warnings, 991 lines checked

Please fix these before resubmitting.

Thanks,

Signature · 1 line
Jean Delvare
#20 lore raw

PATCH v6 1/1 i2c: Adding support for Intel iSMT SMBus 2.0 host controller

In reply to a message not in the archive

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

---
General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

I've setup a system with this SMBus device on it, and managed to successfully preform both read and write operations with it. Specifically I used i2cget to check byte and word reads, i2cset to check byte, word and block writes, and i2cdump to check block reads. Seth is also looking into getting documentation for the dma descriptor format, but using the additional descriptor debug dumping statements I added I was able to verify experimentally that the rxbytes value is the correct one to use in the run time limitation of the read side memcpy.

Change notes for V5)
* Remove __devexit, __devinit macros

* Convert module parameter to use units of khz

* Update Description to reflect current pci db

* Improve KConfig dependencies

* Improve PCI device id names

* Reduce dma buffer size to appropriate value and check mapping result

* Fix build failure resulting from missing readq definition

* Limit use of memcpy and copy length in ismt_process_desc

* Fix off by one error in dma_size

* Convert driver to uninterruptible wait on dma transfers

* Add spped sync with bus_speed module option

* Misc cleanups and fixes

* Fix FTBFS when CONFIG_DYNAMIC_DEBUG is off

Change notes for v6)

* More Misc typos/grammatical fixes/etc

* Fixed some build warnings

* Fixed some checkpatch errors

* Fixed read check for memcpy

* updated memcpy's to limit read/write length to amount of data returned by hw

* Fixed ETIMEOUT return code handling in ismt_access

* Fixed updating of speed in bus_speed module paramter

* Enhanced descriptor debug dumping to aid in validation

---
 Documentation/i2c/busses/i2c-ismt |  36 ++
 drivers/i2c/busses/Kconfig        |  10 +
 drivers/i2c/busses/Makefile       |   1 +
 drivers/i2c/busses/i2c-ismt.c     | 949 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 996 insertions(+)
 create mode 100644 Documentation/i2c/busses/i2c-ismt
 create mode 100644 drivers/i2c/busses/i2c-ismt.c
Documentation/i2c/busses/i2c-ismt new file +36 -0
new file mode 100644
index 0000000..7373558
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Specify the bus speed in kHz.
+Available bus frequency settings:
+  0  no change
+  80 kHz
+  100 kHz
+  400 kHz
+  1000 kHz
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci will show
+something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0
+  00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1
drivers/i2c/busses/Kconfig +10 -0
index bdca511..0bb7ff4 100644
@@ -121,6 +121,16 @@ config I2C_ISCH
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-isch.
 
+config I2C_ISMT
+	tristate "Intel iSMT SMBus Controller"
+	depends on PCI && X86
+	help
+	  If you say yes to this option, support will be included for the Intel
+	  iSMT SMBus host controller interface.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called i2c-ismt.
+
 config I2C_PIIX4
 	tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
 	depends on PCI
drivers/i2c/busses/Makefile +1 -0
index 6181f3f..23c9d55 100644
@@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882)	+= i2c-amd756-s4882.o
 obj-$(CONFIG_I2C_AMD8111)	+= i2c-amd8111.o
 obj-$(CONFIG_I2C_I801)		+= i2c-i801.o
 obj-$(CONFIG_I2C_ISCH)		+= i2c-isch.o
+obj-$(CONFIG_I2C_ISMT)		+= i2c-ismt.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_NFORCE2_S4985)	+= i2c-nforce2-s4985.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
drivers/i2c/busses/i2c-ismt.c new file +949 -0

949 lines not on this page · raw message

Signature · 1 line
1.7.11.7

A message that is not in the archive (sent off-list or not yet received): 1359748083-24423-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org

#22 lore raw

Hi Neil,

Quoted 13 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Quoted 6 lines

---
General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

I am still trying to get my hands on a test machine. I am told we have a few, however they are currently either not setup or reserved.

Until then, a few remaining comments:

Quoted 5 lines
drivers/i2c/busses/i2c-ismt.c new file +5 -0
+/**
+ * ismt_gen_reg_dump() - dump the iSMT General Registers
+ * @priv: iSMT private data
+ */
+static void ismt_gen_reg_dump(struct ismt_priv *priv)

drivers/i2c/busses/i2c-ismt.c line 243

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT General Registers\n");
+	dev_dbg(dev, "  GCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_GCTRL,
+		readl(priv->smba + ISMT_GR_GCTRL));
+	dev_dbg(dev, "  SMTICL... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_GR_SMTICL,
+		(long unsigned int)readq(priv->smba + ISMT_GR_SMTICL));

On 32-bit systems, a long unsigned int can only hold 4 bytes, not 8, so the top 32 bits of the value returned by readq() will be lost. I think you have to use format 0x%016llX and cast to long long unsigned int.

Quoted 16 lines
drivers/i2c/busses/i2c-ismt.c new file +16 -0
+	dev_dbg(dev, "  ERRINTMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINTMSK,
+		readl(priv->smba + ISMT_GR_ERRINTMSK));
+	dev_dbg(dev, "  ERRAERMSK : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRAERMSK,
+		readl(priv->smba + ISMT_GR_ERRAERMSK));
+	dev_dbg(dev, "  ERRSTS... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRSTS,
+		readl(priv->smba + ISMT_GR_ERRSTS));
+	dev_dbg(dev, "  ERRINFO.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_GR_ERRINFO,
+		readl(priv->smba + ISMT_GR_ERRINFO));
+}
+
+/**
+ * ismt_mstr_reg_dump() - dump the iSMT Master Registers

drivers/i2c/busses/i2c-ismt.c line 269

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+ * @priv: iSMT private data
+ */
+static void ismt_mstr_reg_dump(struct ismt_priv *priv)
+{
+	struct device *dev = &priv->pci_dev->dev;
+
+	dev_dbg(dev, "Dump of the iSMT Master Registers\n");
+	dev_dbg(dev, "  MDBA..... : (0x%p)=0x%016lX\n",
+		priv->smba + ISMT_MSTR_MDBA,
+		(long unsigned int)readq(priv->smba + ISMT_MSTR_MDBA));

Same here.

Quoted 15 lines
drivers/i2c/busses/i2c-ismt.c new file +15 -0
+	dev_dbg(dev, "  MCTRL.... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MCTRL,
+		readl(priv->smba + ISMT_MSTR_MCTRL));
+	dev_dbg(dev, "  MSTS..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MSTS,
+		readl(priv->smba + ISMT_MSTR_MSTS));
+	dev_dbg(dev, "  MDS...... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_MDS,
+		readl(priv->smba + ISMT_MSTR_MDS));
+	dev_dbg(dev, "  RPOLICY.. : (0x%p)=0x%X\n",
+		priv->smba + ISMT_MSTR_RPOLICY,
+		readl(priv->smba + ISMT_MSTR_RPOLICY));
+	dev_dbg(dev, "  SPGT..... : (0x%p)=0x%X\n",
+		priv->smba + ISMT_SPGT,
+		readl(priv->smba + ISMT_SPGT));

drivers/i2c/busses/i2c-ismt.c line 318

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +8 -0
+}
 
(...)
+/**
+ * ismt_process_desc() - handle the completion of the descriptor
+ * @desc: the iSMT hardware descriptor
+ * @data: data buffer from the upper layer
+ * @priv: ismt_priv struct holding our dma buffer
+ * @size: SMBus transaction type
+ * @read_write flag to indicate if this is a read or write

Missing colon after parameter name.

Quoted 1 line

+ * @dma_size size of the dma transfer

This function has no parameter named dma_size.

Quoted 6 lines

+ */

drivers/i2c/busses/i2c-ismt.c new file +5 -0
+static int ismt_process_desc(const struct ismt_desc *desc,
+			     union i2c_smbus_data *data,
+			     struct ismt_priv *priv, int size,
+			     char read_write)
+{

drivers/i2c/busses/i2c-ismt.c line 335

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	u8 *dma_buffer = priv->dma_buffer;
+
+	dev_dbg(&priv->pci_dev->dev, "Processing completed descriptor\n");
+	__ismt_desc_dump(&priv->pci_dev->dev, desc);
+
+	if (desc->status & ISMT_DESC_SCS) {
+		if ((size != I2C_SMBUS_QUICK) &&
+		    (read_write == I2C_SMBUS_READ)) {
+			memcpy(&data->block[1], dma_buffer, desc->rxbytes);
+			data->block[0] = desc->rxbytes;

This looks good for block reads. But for receive byte, read byte and read word transactions, this can't be correct. You have to copy the contents of dma_buffer to data->byte or data->word for these transactions.

Quoted 20 lines
drivers/i2c/busses/i2c-ismt.c new file +20 -0
+		}
+		return 0;
+	}
+
+	if (likely(desc->status & ISMT_DESC_NAK))
+		return -ENXIO;
+
+	if (desc->status & ISMT_DESC_CRC)
+		return -EBADMSG;
+
+	if (desc->status & ISMT_DESC_COL)
+		return -EAGAIN;
+
+	if (desc->status & ISMT_DESC_LPR)
+		return -EPROTO;
+
+	if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO))
+		return -ETIMEDOUT;
+
+	return -EIO;

drivers/i2c/busses/i2c-ismt.c line 416

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +8 -0
+}
 
(...)
+	/* Create a temporary buffer for the DMA transaction */
+	/* and insert the command at the beginning of the buffer */
+	if ((read_write == I2C_SMBUS_WRITE) &&
+	    (size > I2C_SMBUS_BYTE)) {
+		memcpy(&priv->dma_buffer[1], &data->block[1], dma_size);
+		priv->dma_buffer[0] = command;
+	}

Again this looks good for block writes, but not for send byte, write byte and write word transactions. data->block is only relevant for block transactions, and &data->block[1] is off by one compared to &data->byte and &data->word.

We did not discuss it yet but you should also pay attention to I2C_SMBUS_PROC_CALL. It is the only bidirectional transaction, so the value of read_write is irrelevant there. So I'm reasonably certain your code doesn't implement it properly. Either fix it or drop support altogether. I can't remember this transaction type ever being used by any slave device in practice, and it can always be added back later if really needed.

Quoted 45 lines
drivers/i2c/busses/i2c-ismt.c new file +45 -0
+
+	/* map the data buffer */
+	if (dma_size != 0) {
+		dev_dbg(dev, " dev=%p\n", dev);
+		dev_dbg(dev, " data=%p\n", data);
+		dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer);
+		dev_dbg(dev, " dma_size=%d\n", dma_size);
+		dev_dbg(dev, " dma_direction=%d\n", dma_direction);
+
+		dma_addr = dma_map_single(dev,
+				      priv->dma_buffer,
+				      dma_size,
+				      dma_direction);
+
+		if (dma_mapping_error(dev, dma_addr)) {
+			dev_err(dev, "Error in mapping dma buffer %p\n",
+				priv->dma_buffer);
+			return -EIO;
+		}
+
+		dev_dbg(dev, " dma_addr = 0x%016llX\n",
+			dma_addr);
+
+		desc->dptr_low = lower_32_bits(dma_addr);
+		desc->dptr_high = upper_32_bits(dma_addr);
+	}
+
+	INIT_COMPLETION(priv->cmp);
+
+	/* Add the descriptor */
+	ismt_submit_desc(priv);
+
+	/* Now we wait for interrupt completion, 1s */
+	ret = wait_for_completion_timeout(&priv->cmp, HZ*1);
+
+	/* unmap the data buffer */
+	if (dma_size != 0)
+		dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction);
+
+	if (unlikely(!ret)) {
+		dev_err(dev, "completion wait timed out\n");
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+

drivers/i2c/busses/i2c-ismt.c line 552

Quoted 10 lines
drivers/i2c/busses/i2c-ismt.c new file +10 -0
+	/* do any post processing of the descriptor here */
+	ret = ismt_process_desc(desc, data, priv, size, read_write);
+
+out:
+	/* Update the ring pointer */
+	priv->head++;
+	priv->head %= ISMT_DESC_ENTRIES;
+
+	return ret;
+}

I have backported your code to kernel 3.0 and I'll get back to you when I finally get a chance to test it.

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#24 lore raw
Quoted 2 lines · Jean Delvare

I have backported your code to kernel 3.0 and I'll get back to you when I finally get a chance to test it.

OK, I am done with the backport and testing. The test system I have access to appears to have a single slave on the second iSMT channel, at address 0x54. I have no idea what this device is so my testing is fairly limited, in particular I can't test writes. But at least I could test the quick command and read byte transactions. I believe the following fix is needed on top of your patch to make non-quick, non-block transactions right:

---
 i2c-ismt.c |   35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)
i2c-ismt.c +25 -10
@@ -329,10 +329,23 @@ static int ismt_process_desc(const struc
 	__ismt_desc_dump(&priv->pci_dev->dev, desc);
 
 	if (desc->status & ISMT_DESC_SCS) {
-		if ((size != I2C_SMBUS_QUICK) &&
-		    (read_write == I2C_SMBUS_READ)) {
+		if (read_write == I2C_SMBUS_WRITE &&
+		    size != I2C_SMBUS_PROC_CALL)
+			return 0;
+
+		switch (size) {
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			data->byte = dma_buffer[0];
+			break;
+		case I2C_SMBUS_WORD_DATA:
+		case I2C_SMBUS_PROC_CALL:
+			data->word = dma_buffer[0] | (dma_buffer[1] << 8);
+			break;
+		case I2C_SMBUS_BLOCK_DATA:
 			memcpy(&data->block[1], dma_buffer, desc->rxbytes);
 			data->block[0] = desc->rxbytes;
+			break;
 		}
 		return 0;
 	}
@@ -426,6 +439,8 @@ static int ismt_access(struct i2c_adapte
 			desc->wr_len_cmd = 2;
 			dma_size = 2;
 			dma_direction = DMA_TO_DEVICE;
+			priv->dma_buffer[0] = command;
+			priv->dma_buffer[1] = data->byte;
 		} else {
 			/* Read Byte */
 			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  READ\n");
@@ -444,6 +459,9 @@ static int ismt_access(struct i2c_adapte
 			desc->wr_len_cmd = 3;
 			dma_size = 3;
 			dma_direction = DMA_TO_DEVICE;
+			priv->dma_buffer[0] = command;
+			priv->dma_buffer[1] = data->word & 0xff;
+			priv->dma_buffer[2] = data->word >> 8;
 		} else {
 			/* Read Word */
 			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:  READ\n");
@@ -461,6 +479,9 @@ static int ismt_access(struct i2c_adapte
 		desc->rd_len = 2;
 		dma_size = 3;
 		dma_direction = DMA_BIDIRECTIONAL;
+		priv->dma_buffer[0] = command;
+		priv->dma_buffer[1] = data->word & 0xff;
+		priv->dma_buffer[2] = data->word >> 8;
 		break;
 
 	case I2C_SMBUS_BLOCK_DATA:
@@ -471,6 +492,8 @@ static int ismt_access(struct i2c_adapte
 			dma_direction = DMA_TO_DEVICE;
 			desc->wr_len_cmd = dma_size;
 			desc->control |= ISMT_DESC_BLK;
+			priv->dma_buffer[0] = command;
+			memcpy(&priv->dma_buffer[1], &data->block[1], dma_size);
 		} else {
 			/* Block Read */
 			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  READ\n");
@@ -488,14 +511,6 @@ static int ismt_access(struct i2c_adapte
 		return -EOPNOTSUPP;
 	}
 
-	/* Create a temporary buffer for the DMA transaction */
-	/* and insert the command at the beginning of the buffer */
-	if ((read_write == I2C_SMBUS_WRITE) &&
-	    (size > I2C_SMBUS_BYTE)) {
-		memcpy(&priv->dma_buffer[1], &data->block[1], dma_size);
-		priv->dma_buffer[0] = command;
-	}
-
 	/* map the data buffer */
 	if (dma_size != 0) {
 		dev_dbg(dev, " dev=%p\n", dev);

Maybe that's what Seth was missing. Seth, can you please try this patch of mine and let us know if it helps? Warning: I was only able to test the quick, receive byte and read byte transactions.

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#26 lore raw
Quoted 4 lines · Jean Delvare
Quoted 2 lines · Jean Delvare

I have backported your code to kernel 3.0 and I'll get back to you when I finally get a chance to test it.

Quoted 8 lines

OK, I am done with the backport and testing. The test system I have access to appears to have a single slave on the second iSMT channel, at address 0x54. I have no idea what this device is so my testing is fairly limited, in particular I can't test writes. But at least I could test the quick command and read byte transactions. I believe the following fix is needed on top of your patch to make non-quick, non-block transactions right:

I've got the same device on my system here. Seth, can you elaborate on what it is. FWIW, I took a risk, read a block from that device and wrote the same block back to it with my v6 driver, with dyamic debug enabled, and the reported printk's indicated it succeded. I'll test out this patch in a bit here, make the other requested corrections and repost shortly.
Neil

Quoted 93 lines
---
 i2c-ismt.c |   35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)
i2c-ismt.c +25 -10
@@ -329,10 +329,23 @@ static int ismt_process_desc(const struc
 	__ismt_desc_dump(&priv->pci_dev->dev, desc);
 
 	if (desc->status & ISMT_DESC_SCS) {
-		if ((size != I2C_SMBUS_QUICK) &&
-		    (read_write == I2C_SMBUS_READ)) {
+		if (read_write == I2C_SMBUS_WRITE &&
+		    size != I2C_SMBUS_PROC_CALL)
+			return 0;
+
+		switch (size) {
+		case I2C_SMBUS_BYTE:
+		case I2C_SMBUS_BYTE_DATA:
+			data->byte = dma_buffer[0];
+			break;
+		case I2C_SMBUS_WORD_DATA:
+		case I2C_SMBUS_PROC_CALL:
+			data->word = dma_buffer[0] | (dma_buffer[1] << 8);
+			break;
+		case I2C_SMBUS_BLOCK_DATA:
 			memcpy(&data->block[1], dma_buffer, desc->rxbytes);
 			data->block[0] = desc->rxbytes;
+			break;
 		}
 		return 0;
 	}
@@ -426,6 +439,8 @@ static int ismt_access(struct i2c_adapte
 			desc->wr_len_cmd = 2;
 			dma_size = 2;
 			dma_direction = DMA_TO_DEVICE;
+			priv->dma_buffer[0] = command;
+			priv->dma_buffer[1] = data->byte;
 		} else {
 			/* Read Byte */
 			dev_dbg(dev, "I2C_SMBUS_BYTE_DATA:  READ\n");
@@ -444,6 +459,9 @@ static int ismt_access(struct i2c_adapte
 			desc->wr_len_cmd = 3;
 			dma_size = 3;
 			dma_direction = DMA_TO_DEVICE;
+			priv->dma_buffer[0] = command;
+			priv->dma_buffer[1] = data->word & 0xff;
+			priv->dma_buffer[2] = data->word >> 8;
 		} else {
 			/* Read Word */
 			dev_dbg(dev, "I2C_SMBUS_WORD_DATA:  READ\n");
@@ -461,6 +479,9 @@ static int ismt_access(struct i2c_adapte
 		desc->rd_len = 2;
 		dma_size = 3;
 		dma_direction = DMA_BIDIRECTIONAL;
+		priv->dma_buffer[0] = command;
+		priv->dma_buffer[1] = data->word & 0xff;
+		priv->dma_buffer[2] = data->word >> 8;
 		break;
 
 	case I2C_SMBUS_BLOCK_DATA:
@@ -471,6 +492,8 @@ static int ismt_access(struct i2c_adapte
 			dma_direction = DMA_TO_DEVICE;
 			desc->wr_len_cmd = dma_size;
 			desc->control |= ISMT_DESC_BLK;
+			priv->dma_buffer[0] = command;
+			memcpy(&priv->dma_buffer[1], &data->block[1], dma_size);
 		} else {
 			/* Block Read */
 			dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA:  READ\n");
@@ -488,14 +511,6 @@ static int ismt_access(struct i2c_adapte
 		return -EOPNOTSUPP;
 	}
 
-	/* Create a temporary buffer for the DMA transaction */
-	/* and insert the command at the beginning of the buffer */
-	if ((read_write == I2C_SMBUS_WRITE) &&
-	    (size > I2C_SMBUS_BYTE)) {
-		memcpy(&priv->dma_buffer[1], &data->block[1], dma_size);
-		priv->dma_buffer[0] = command;
-	}
-
 	/* map the data buffer */
 	if (dma_size != 0) {
 		dev_dbg(dev, " dev=%p\n", dev);

Maybe that's what Seth was missing. Seth, can you please try this patch of mine and let us know if it helps? Warning: I was only able to test the quick, receive byte and read byte transactions.

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): 20130204172851.GA24749-B26myB8xz7F8NnZeBjwnZQMhkBWG/[email protected]

#28 lore raw
Quoted 4 lines · Neil Horman

I've got the same device on my system here. Seth, can you elaborate on what it is. FWIW, I took a risk, read a block from that device and wrote the same block back to it with my v6 driver, with dyamic debug enabled, and the reported printk's indicated it succeded.

That doesn't mean a thing, unfortunately. It is possible that the write did not work, if you attempted to write the same data as was already present, you can't tell the difference between success and failure. Some slave SMBus devices will ack all transactions regardless of whether they support these transactions or not. As long as we don't know what is this slave device at 0x54, it's impossible to tell if the driver really works. I am glad Seth could test it with a different, known slave device.

Quoted 2 lines

I'll test out this patch in a bit here, make
the other requested corrections and repost shortly.

Signature · 1 line
Jean Delvare
#29 lore raw

PATCH v7 1/1 i2c: Adding support for Intel iSMT SMBus 2.0 host controller

In reply to a message not in the archive

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

---
General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

I've setup a system with this SMBus device on it, and managed to successfully preform both read and write operations with it. Specifically I used i2cget to check byte and word reads, i2cset to check byte, word and block writes, and i2cdump to check block reads. Seth is also looking into getting documentation for the dma descriptor format, but using the additional descriptor debug dumping statements I added I was able to verify experimentally that the rxbytes value is the correct one to use in the run time limitation of the read side memcpy.

Jean, I've also tested your patch using the same methodoloy you and I have posted previously, and with your patch I concur, It seems to work correctly to the device at bus address 0x54

Change notes for V5)
* Remove __devexit, __devinit macros

* Convert module parameter to use units of khz

* Update Description to reflect current pci db

* Improve KConfig dependencies

* Improve PCI device id names

* Reduce dma buffer size to appropriate value and check mapping result

* Fix build failure resulting from missing readq definition

* Limit use of memcpy and copy length in ismt_process_desc

* Fix off by one error in dma_size

* Convert driver to uninterruptible wait on dma transfers

* Add spped sync with bus_speed module option

* Misc cleanups and fixes

* Fix FTBFS when CONFIG_DYNAMIC_DEBUG is off

Change notes for v6)

* More Misc typos/grammatical fixes/etc

* Fixed some build warnings

* Fixed some checkpatch errors

* Fixed read check for memcpy

* updated memcpy's to limit read/write length to amount of data returned by hw

* Fixed ETIMEOUT return code handling in ismt_access

* Fixed updating of speed in bus_speed module paramter

* Enhanced descriptor debug dumping to aid in validation

Change notes for v7)

* Few final typos

* Fixed the printing or readq values in debug code

* Added Jeans patch to fix quick and byte reads/writes

---
 Documentation/i2c/busses/i2c-ismt |  36 ++
 drivers/i2c/busses/Kconfig        |  10 +
 drivers/i2c/busses/Makefile       |   1 +
 drivers/i2c/busses/i2c-ismt.c     | 963 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 1010 insertions(+)
 create mode 100644 Documentation/i2c/busses/i2c-ismt
 create mode 100644 drivers/i2c/busses/i2c-ismt.c
Documentation/i2c/busses/i2c-ismt new file +36 -0
new file mode 100644
index 0000000..7373558
@@ -0,0 +1,36 @@
+Kernel driver i2c-ismt
+
+Supported adapters:
+  * Intel S12xx series SOCs
+
+Authors:
+	Bill Brown <[email protected]>
+
+
+Module Parameters
+-----------------
+
+* bus_speed (unsigned int)
+Allows changing of the bus speed.  Normally, the bus speed is set by the BIOS
+and never needs to be changed.  However, some SMBus analyzers are too slow for
+monitoring the bus during debug, thus the need for this module parameter.
+Specify the bus speed in kHz.
+Available bus frequency settings:
+  0  no change
+  80 kHz
+  100 kHz
+  400 kHz
+  1000 kHz
+
+
+Description
+-----------
+
+The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers
+targeted primarily at the microserver and storage markets.
+
+The S12xx series contain a pair of PCI functions.  An output of lspci will show
+something similar to the following:
+
+  00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0
+  00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1
drivers/i2c/busses/Kconfig +10 -0
index bdca511..0bb7ff4 100644
@@ -121,6 +121,16 @@ config I2C_ISCH
 	  This driver can also be built as a module. If so, the module
 	  will be called i2c-isch.
 
+config I2C_ISMT
+	tristate "Intel iSMT SMBus Controller"
+	depends on PCI && X86
+	help
+	  If you say yes to this option, support will be included for the Intel
+	  iSMT SMBus host controller interface.
+
+	  This driver can also be built as a module.  If so, the module will be
+	  called i2c-ismt.
+
 config I2C_PIIX4
 	tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)"
 	depends on PCI
drivers/i2c/busses/Makefile +1 -0
index 6181f3f..23c9d55 100644
@@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882)	+= i2c-amd756-s4882.o
 obj-$(CONFIG_I2C_AMD8111)	+= i2c-amd8111.o
 obj-$(CONFIG_I2C_I801)		+= i2c-i801.o
 obj-$(CONFIG_I2C_ISCH)		+= i2c-isch.o
+obj-$(CONFIG_I2C_ISMT)		+= i2c-ismt.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_NFORCE2_S4985)	+= i2c-nforce2-s4985.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
drivers/i2c/busses/i2c-ismt.c new file +963 -0

963 lines not on this page · raw message

Signature · 1 line
1.7.11.7

A message that is not in the archive (sent off-list or not yet received): 1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org

#31 lore raw

Hi Neil,

Quoted 5 lines

General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

Whatever issues I was having with the previous patch, they appear to be resolved. I was able to do testing similar to yours, albeit using an external slave device. I'm not seeing the device at 0x54 on my system. But reads and writes both worked.

-Seth

A message that is not in the archive (sent off-list or not yet received): DEC3922D1904474A8862C7BFF516EA9A59B4A8B8-P5GAC/[email protected]

#33 lore raw
Quoted 9 lines · Seth

Hi Neil,

Quoted 5 lines

General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

Whatever issues I was having with the previous patch, they appear to be resolved. I was able to do testing similar to yours, albeit using an external slave device. I'm not seeing the device at 0x54 on my system. But reads and writes both worked.

Great news, thanks for testing and reporting!

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#35 lore raw
Quoted 9 lines · Jean Delvare
Quoted 8 lines · Seth

Hi Neil,

Quoted 5 lines

General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

Quoted 4 lines
Quoted 1 line

Whatever issues I was having with the previous patch, they appear to be resolved. I was able to do testing similar to yours, albeit using an external slave device. I'm not seeing the device at 0x54 on my system. But reads and writes both worked.

Great news, thanks for testing and reporting!

Yes, thank you Seth. I think that puts version 7 at tested by all three of us, and working. Jean, any other fixups you feel are needed?

Neil

A message that is not in the archive (sent off-list or not yet received): 20130205145230.GA4511-B26myB8xz7F8NnZeBjwnZQMhkBWG/[email protected]

#37 lore raw
Quoted 10 lines · Neil Horman
Quoted 9 lines · Jean Delvare
Quoted 8 lines · Seth

Hi Neil,

Quoted 5 lines

General note - Seth had mentioned to me that he was unable to get writes to work on his system. I'm not sure of the details, but I've done some rudimentary read/write testing here on my system, and it seems to work well (for the limited testing I can do). Seth if you could please provide details of your testing, or give this patch a try, I would appreciate it.

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Whatever issues I was having with the previous patch, they appear to be resolved. I was able to do testing similar to yours, albeit using an external slave device. I'm not seeing the device at 0x54 on my system. But reads and writes both worked.

Great news, thanks for testing and reporting!

Yes, thank you Seth. I think that puts version 7 at tested by all three of us, and working. Jean, any other fixups you feel are needed?

No, it looks good now. Wolfram, please queue this new driver for kernel 3.9.

I have also made the driver available as a standalone source file at:
http://khali.linux-fr.org/devel/misc/i2c-ismt/
Testers can be pointed to this location. This version of the driver has some compatibility code to build at least down to kernel version 3.0.

Signature · 1 line
Jean Delvare
Reviewed-by
#38 lore raw
In reply to a message not in the archive
Quoted 7 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Quoted 6 lines

(...)

Signature · 1 line
Jean Delvare
Applied
#39 lore raw
In reply to a message not in the archive
Quoted 10 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Thanks, applied to for-next!

A message that is not in the archive (sent off-list or not yet received): 20130210150205.GB6282-8EAEigeeuNG034pCzgS/[email protected]

#41 lore raw
Quoted 5 lines · Wolfram Sang
Quoted 4 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

Quoted 10 lines
Quoted 6 lines

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Thanks, applied to for-next!

Wolfram-
Hey, sorry to bother you, but I just wanted to check on this patch. I
see its accepted in patchwork, but I haven't seen it show up in your git tree yet, and I was really hoping this would make 3.9. Since we're in the -rc run for that release, i wanted to check with you to see if you were planning to push it.

Thanks!
Neil

A message that is not in the archive (sent off-list or not yet received): 20130305142225.GA23095-B26myB8xz7F8NnZeBjwnZQMhkBWG/[email protected]

#43 lore raw

Hi Neil,

Quoted 16 lines · Neil Horman
Quoted 5 lines · Wolfram Sang
Quoted 4 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

Quoted 10 lines
Quoted 6 lines

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Thanks, applied to for-next!

Quoted 6 lines

Wolfram-
Hey, sorry to bother you, but I just wanted to check on this patch. I
see its accepted in patchwork, but I haven't seen it show up in your git tree yet, and I was really hoping this would make 3.9. Since we're in the -rc run for that release, i wanted to check with you to see if you were planning to push it.

Your driver is already in 3.9, see:

http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/log/drivers/i2c/busses/i2c-ismt.c

Signature · 1 line
Jean Delvare

A message that is not in the archive (sent off-list or not yet received): [email protected]

#45 lore raw
Quoted 26 lines · Jean Delvare

Hi Neil,

Quoted 16 lines · Neil Horman
Quoted 5 lines · Wolfram Sang
Quoted 4 lines · Neil Horman

The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus.

Quoted 10 lines
Quoted 6 lines

The iSMT hardware can act as both a master and a target, although this driver only supports being a master.

Thanks, applied to for-next!

Quoted 6 lines

Wolfram-
Hey, sorry to bother you, but I just wanted to check on this patch. I
see its accepted in patchwork, but I haven't seen it show up in your git tree yet, and I was really hoping this would make 3.9. Since we're in the -rc run for that release, i wanted to check with you to see if you were planning to push it.

Quoted 4 lines

Your driver is already in 3.9, see:

http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/log/drivers/i2c/busses/i2c-ismt.c

Crud, I was on the wrong branch. Sorry for the noise! Neil

Quoted 3 lines
Signature · 1 line
Jean Delvare