* [PATCH v4] i2c: Adding support for Intel iSMT SMBus 2.0 host controller @ 2012-12-07 0:36 Bill E Brown [not found] ` <1354840604-8160-1-git-send-email-bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Bill E Brown @ 2012-12-07 0:36 UTC (permalink / raw) To: Wolfram Sang Cc: Jean Delvare, Seth Heasley, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown From: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> 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. Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> --- 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 diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 0000000..ed6375c --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> + + +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 + diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 7244c8b..64d5756 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -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 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ce3c2be..527a1f1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -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 diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 0000000..17b1bdd --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -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. + */ + +/* + * 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 */ + +/* 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. */ +#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 */ +#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 */ +#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) */ + +/* 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) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#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 */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 3]; /* temp R/W data buffer */ + 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) }, + { 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"); + +#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"); + 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) { + /* 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; + 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(&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); + + /* unmap the data buffer */ + if (dma_size != 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"); + 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); + + /* 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), + 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; + } +} + +/** + * 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))); + + 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"); + 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", + 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)) { + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0)) { + dev_warn(&pdev->dev, "pci_set_dma_mask fail %p\n", + 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; + 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 <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>"); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); -- 1.6.2.5 ^ permalink raw reply related [flat|nested] 27+ messages in thread
[parent not found: <1354840604-8160-1-git-send-email-bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>]
* RE: [PATCH v4] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1354840604-8160-1-git-send-email-bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> @ 2012-12-07 0:34 ` Brown, Bill E 2012-12-18 14:03 ` Jean Delvare 1 sibling, 0 replies; 27+ messages in thread From: Brown, Bill E @ 2012-12-07 0:34 UTC (permalink / raw) To: Brown, Bill E, Wolfram Sang Cc: Jean Delvare, Heasley, Seth, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org > -----Original Message----- > From: linux-i2c-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-i2c- > owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bill E Brown > Sent: Thursday, December 06, 2012 5:37 PM > To: Wolfram Sang > Cc: Jean Delvare; Heasley, Seth; linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; Brown, Bill E > Subject: [PATCH v4] i2c: Adding support for Intel iSMT SMBus 2.0 host > controller > > From: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > 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 > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > --- > 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 > > diff --git a/Documentation/i2c/busses/i2c-ismt > b/Documentation/i2c/busses/i2c-ismt > new file mode 100644 > index 0000000..ed6375c > --- /dev/null > +++ b/Documentation/i2c/busses/i2c-ismt > @@ -0,0 +1,36 @@ > +Kernel driver i2c-ismt > + > +Supported adapters: > + * Intel S12xx series SOCs > + > +Authors: > + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > + > + > +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 > + > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index > 7244c8b..64d5756 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > @@ -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 > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index > ce3c2be..527a1f1 100644 > --- a/drivers/i2c/busses/Makefile > +++ b/drivers/i2c/busses/Makefile > @@ -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 > diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new > file mode 100644 index 0000000..17b1bdd > --- /dev/null > +++ b/drivers/i2c/busses/i2c-ismt.c > @@ -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. > + */ > + > +/* > + * 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 */ > + > +/* 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. */ > +#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 */ > +#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 */ > +#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) */ > + > +/* 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) */ > +#define ISMT_MSTS_IP 0x01 /* In Progress */ > + > +/* Master Descriptor Size (MDS) bit definitions */ > +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask > (MDS) */ > + > +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ > +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed > mask */ > +#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 */ > + u8 head; /* ring buffer head pointer */ > + struct completion cmp; /* interrupt completion */ > + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 3]; /* temp R/W data > buffer */ > + 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) }, > + { 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"); > + > +#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"); > + 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) { > + /* 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; > + 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(&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); > + > + /* unmap the data buffer */ > + if (dma_size != 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"); > + 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); > + > + /* 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), > + 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; > + } > +} > + > +/** > + * 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))); > + > + 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"); > + 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", > + 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)) > { > + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || > + (pci_set_consistent_dma_mask(pdev, > DMA_BIT_MASK(32)) != 0)) { > + dev_warn(&pdev->dev, "pci_set_dma_mask fail > %p\n", > + 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; > + 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 <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>"); > +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 majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at > http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v4] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1354840604-8160-1-git-send-email-bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> 2012-12-07 0:34 ` Brown, Bill E @ 2012-12-18 14:03 ` Jean Delvare [not found] ` <20121218150337.5b861ae3-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 1 sibling, 1 reply; 27+ messages in thread From: Jean Delvare @ 2012-12-18 14:03 UTC (permalink / raw) To: Bill E Brown; +Cc: Wolfram Sang, Seth Heasley, linux-i2c-u79uwXL29TY76Z2rM5mHXA Hi Bill, On Thu, 6 Dec 2012 17:36:44 -0700, Bill E Brown wrote: > From: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > 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. > > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > --- > 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: > diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt > new file mode 100644 > index 0000000..ed6375c > --- /dev/null > +++ b/Documentation/i2c/busses/i2c-ismt > @@ -0,0 +1,36 @@ > +Kernel driver i2c-ismt > + > +Supported adapters: > + * Intel S12xx series SOCs > + > +Authors: > + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > + > + > +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 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. > + > + > +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 > + No blank line at end of file please, git would complain. > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > index 7244c8b..64d5756 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > @@ -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. > + 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 > (...) > diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c > new file mode 100644 > index 0000000..17b1bdd > --- /dev/null > +++ b/drivers/i2c/busses/i2c-ismt.c > @@ -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> > +#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 > (...) > +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? > + 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) }, > + { 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)" > + > +#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)); Function readq() doesn't exist on 32-bit x86, causing a build failure. > + 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)); Same problem here. > + 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)); > +} 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. > +#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) This condition seems incomplete, as far as I can see the memcpy is only needed for read transactions, not write transactions. > + memcpy(data, dma_buffer, sizeof(*data)); Why not limit the size of the copy? > + 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)); Here too, this memcpy seems unneeded for read transactions, and for writes, you could limit the size. > + 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) { > + /* 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. > + 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(&priv->pci_dev->dev, Just "dev" will do. > + 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) > + 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. > + 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; Nitpicking: can you please align the I2C_... by using seven spaces instead of one tab? > +} > + > +(...) > +/** > + * 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) All __dev* markers are going away meanwhile, so you should remove them from your code to prevent build failures in future kernels. > +{ > + 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) > + */ > + > + 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. > + break; > + > + case 1: > + dev_dbg(dev, "Setting SMBus clock to 80kHz\n"); A space between the number and "kHz" would improve readability. > + 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; > + } > +} > + > + (...) > +/** > + * 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. > + if (err) { > + dev_warn(&priv->pci_dev->dev, > + "Unable to use MSI interrupts, falling back to legacy"); Missing trailing "\n". > + 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. > + 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); > + 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. > + 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. > + 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; > + 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. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20121218150337.5b861ae3-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20121218150337.5b861ae3-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-01-28 19:43 ` Neil Horman [not found] ` <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-01-28 19:43 UTC (permalink / raw) To: linux-i2c-u79uwXL29TY76Z2rM5mHXA Cc: Neil Horman, Bill Brown, Seth Heasley, Jean Delvare 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. Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> --- 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 diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 0000000..aa3b60f --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> + + +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 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bdca511..0bb7ff4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -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 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 6181f3f..23c9d55 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -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 diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 0000000..5ad4680 --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,932 @@ +/* + * 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. + */ + +/* + * 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> + +#include <asm-generic/io-64-nonatomic-lo-hi.h> + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a + +#define ISMT_DESC_ENTRIES 32 /* number of descriptor entries */ +#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */ + +/* 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. */ +#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 */ +#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 */ +#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) */ + +/* 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) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#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 */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */ + 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_SMT0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_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 in KHz (0 = BIOS default)"); + +/** + * 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)); +} + +/** + * 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, + char read_write) +{ + if ((read_write == I2C_SMBUS_READ) && + (desc->status & ISMT_DESC_SCS)) { + if (size != I2C_SMBUS_QUICK) + memcpy(data, dma_buffer, + min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX)); + 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 ((read_write == I2C_SMBUS_WRITE) && + (size != I2C_SMBUS_QUICK)) { + memcpy(priv->dma_buffer + 1, data, + min(sizeof(*data), (size_t)I2C_SMBUS_BLOCK_MAX)); + 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) { + /* 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; + 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; + } + + /* do any post processing of the descriptor here */ + ret = ismt_process_desc(desc, data, priv->dma_buffer, size, read_write); + + /* 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 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) + */ + + val = readl(priv->smba + ISMT_SPGT); + + switch (bus_speed) { + case 0: + break; + + case 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + 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"); + 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"); + 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"); + 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; + } + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + 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); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int 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))); + + priv->head = 0; + init_completion(&priv->cmp); + + return 0; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int 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\n"); + 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 +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"); + 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); + 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; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void 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 = ismt_remove, + .suspend = ismt_suspend, + .resume = ismt_resume, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>"); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 27+ messages in thread
[parent not found: <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>]
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> @ 2013-01-29 14:05 ` Jean Delvare [not found] ` <20130129150551.498b2a9b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-01-29 14:29 ` Jean Delvare ` (2 subsequent siblings) 3 siblings, 1 reply; 27+ messages in thread From: Jean Delvare @ 2013-01-29 14:05 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley Hi Neil, On Mon, 28 Jan 2013 14:43:52 -0500, Neil Horman wrote: > 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. > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> > > --- > 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. > 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 > > diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt > new file mode 100644 > index 0000000..aa3b60f > --- /dev/null > +++ b/Documentation/i2c/busses/i2c-ismt > @@ -0,0 +1,36 @@ > +Kernel driver i2c-ismt > + > +Supported adapters: > + * Intel S12xx series SOCs > + > +Authors: > + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > + > + > +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. > +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 ;) > + > + > +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 > (...) > diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c > new file mode 100644 > index 0000000..5ad4680 > --- /dev/null > +++ b/drivers/i2c/busses/i2c-ismt.c > @@ -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> > +#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. > (...) > +/* 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". > (...) > +/** > + * 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)); 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’ > + 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)); 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’ > + 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) > +{ > + 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? > + 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(). > + 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 ((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? > + 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. > + 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; > + } > + This blank line could go away. There are more occurrences of this in this function. > + 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) { > + /* 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. > + 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; > + } > + > + /* 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. > + > + /* 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) > + */ > + > + 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... > + 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... > + 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... > + 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. > + 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. > + break; > + } > + > + switch (val & ISMT_SPGT_SPD_MASK) { > + case ISMT_SPGT_SPD_80K: > + bus_speed = 80; > + break; > + case ISMT_SPGT_SPD_100K: > + 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". > +} > + > (...) > +/** > + * 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"); > + 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. > + 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; > +} -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130129150551.498b2a9b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130129150551.498b2a9b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-01-29 15:32 ` Neil Horman [not found] ` <20130129153253.GA14044-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 2013-01-29 16:57 ` Heasley, Seth 1 sibling, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-01-29 15:32 UTC (permalink / raw) To: Jean Delvare; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Tue, Jan 29, 2013 at 03:05:51PM +0100, Jean Delvare wrote: > Hi Neil, > > On Mon, 28 Jan 2013 14:43:52 -0500, Neil Horman wrote: > > 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. > > > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> > > > > --- > > 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. > 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. > > + > > +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. > > +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. > > + > > +#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> > > + > > +#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. > There are two build warnings left on 32-bit, see below. > > > (...) > > +/* 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 > > (...) > > +/** > > + * 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)); > > 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. > > + 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)); > > 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. > > + 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) > > +{ > > + 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? > > + 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. > 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. > > + > > +/** > > + * 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 ((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. > > > + 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. > > + 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; > > + } > > + > > This blank line could go away. There are more occurrences of this in > this function. > Ok > > + > > + 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.) > > 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. > > + 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; > > + } > > + > > + /* 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. > > + > > + /* 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) > > + */ > > + > > + 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... > > > + 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... > > > + 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... > > > + 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. > > > + 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 > > + break; > > + } > > + > > + switch (val & ISMT_SPGT_SPD_MASK) { > > + case ISMT_SPGT_SPD_80K: > > + bus_speed = 80; > > + break; > > + case ISMT_SPGT_SPD_100K: > > + 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. > Ah, you're right, thanks. > Also note that the proper casing is "kHz". > > Give me a day or two, and I'll have these fixed up. Neil ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130129153253.GA14044-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org>]
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130129153253.GA14044-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> @ 2013-01-29 22:10 ` Jean Delvare [not found] ` <20130129231028.41b04fc9-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Jean Delvare @ 2013-01-29 22:10 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Tue, 29 Jan 2013 10:32:53 -0500, Neil Horman wrote: > On Tue, Jan 29, 2013 at 03:05:51PM +0100, Jean Delvare wrote: > > On Mon, 28 Jan 2013 14:43:52 -0500, Neil Horman wrote: > > > + 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. > > > 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. 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. > > > +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 ((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. 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. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130129231028.41b04fc9-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130129231028.41b04fc9-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-01-30 14:15 ` Neil Horman [not found] ` <20130130141504.GA2968-0o1r3XBGOEbbgkc5XkKeNuvMHUBZFtU3YPYVAmT7z5s@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-01-30 14:15 UTC (permalink / raw) To: Jean Delvare; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Tue, Jan 29, 2013 at 11:10:28PM +0100, Jean Delvare wrote: > On Tue, 29 Jan 2013 10:32:53 -0500, Neil Horman wrote: > > On Tue, Jan 29, 2013 at 03:05:51PM +0100, Jean Delvare wrote: > > > On Mon, 28 Jan 2013 14:43:52 -0500, Neil Horman wrote: > > > > + 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. > > > > > 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. > > 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. > > > > +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 ((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. > > 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? > 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. > 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 > -- > Jean Delvare > ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130130141504.GA2968-0o1r3XBGOEbbgkc5XkKeNuvMHUBZFtU3YPYVAmT7z5s@public.gmane.org>]
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130130141504.GA2968-0o1r3XBGOEbbgkc5XkKeNuvMHUBZFtU3YPYVAmT7z5s@public.gmane.org> @ 2013-01-30 21:55 ` Jean Delvare 0 siblings, 0 replies; 27+ messages in thread From: Jean Delvare @ 2013-01-30 21:55 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Wed, 30 Jan 2013 09:15:04 -0500, Neil Horman wrote: > On Tue, Jan 29, 2013 at 11:10:28PM +0100, Jean Delvare wrote: > > 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. > (...) > 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. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
* RE: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130129150551.498b2a9b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-01-29 15:32 ` Neil Horman @ 2013-01-29 16:57 ` Heasley, Seth [not found] ` <DEC3922D1904474A8862C7BFF516EA9A59B47EB2-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org> 1 sibling, 1 reply; 27+ messages in thread From: Heasley, Seth @ 2013-01-29 16:57 UTC (permalink / raw) To: Jean Delvare, Neil Horman Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Brown, Bill E Hi Jean, >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 ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <DEC3922D1904474A8862C7BFF516EA9A59B47EB2-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org>]
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <DEC3922D1904474A8862C7BFF516EA9A59B47EB2-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org> @ 2013-01-29 18:52 ` Jean Delvare 0 siblings, 0 replies; 27+ messages in thread From: Jean Delvare @ 2013-01-29 18:52 UTC (permalink / raw) To: Heasley, Seth Cc: Neil Horman, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Brown, Bill E On Tue, 29 Jan 2013 16:57:16 +0000, Heasley, Seth wrote: > Hi Jean, > > >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 Excellent, thanks Seth! -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v5] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-01-29 14:05 ` Jean Delvare @ 2013-01-29 14:29 ` Jean Delvare 2013-02-01 19:48 ` [PATCH v6] " Neil Horman 2013-02-04 19:54 ` [PATCH v7] " Neil Horman 3 siblings, 0 replies; 27+ messages in thread From: Jean Delvare @ 2013-01-29 14:29 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley 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, -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v6] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-01-29 14:05 ` Jean Delvare 2013-01-29 14:29 ` Jean Delvare @ 2013-02-01 19:48 ` Neil Horman [not found] ` <1359748083-24423-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-02-04 19:54 ` [PATCH v7] " Neil Horman 3 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-02-01 19:48 UTC (permalink / raw) To: linux-i2c-u79uwXL29TY76Z2rM5mHXA Cc: Neil Horman, Bill Brown, Seth Heasley, Jean Delvare 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. Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> --- 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 diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 0000000..7373558 --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> + + +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 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bdca511..0bb7ff4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -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 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 6181f3f..23c9d55 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -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 diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 0000000..db53d6b --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,949 @@ +/* + * 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. + */ + +/* + * 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> + +#include <asm-generic/io-64-nonatomic-lo-hi.h> + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a + +#define ISMT_DESC_ENTRIES 32 /* number of descriptor entries */ +#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */ + +/* 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. */ +#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 */ +#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 */ +#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) */ + +/* 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) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#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 */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */ + 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_SMT0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_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 in kHz (0 = BIOS default)"); + +/** + * __ismt_desc_dump() - dump the contents of a specific descriptor + */ +static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc) +{ + + dev_dbg(dev, "Descriptor struct: %p\n", desc); + 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_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); + __ismt_desc_dump(dev, desc); +} + +/** + * 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, + (long unsigned int)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, + (long unsigned int)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)); +} + +/** + * 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 + * @priv: ismt_priv struct holding our dma buffer + * @size: SMBus transaction type + * @read_write flag to indicate if this is a read or write + * @dma_size size of the dma transfer + */ +static int ismt_process_desc(const struct ismt_desc *desc, + union i2c_smbus_data *data, + struct ismt_priv *priv, int size, + char read_write) +{ + 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; + } + 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); + + /* 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) { + /* 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; + 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; + } + + /* 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); + 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; + } + + /* 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; +} + +/** + * 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 ismt_hw_init(struct ismt_priv *priv) +{ + u32 val; + struct device *dev = &priv->pci_dev->dev; + + /* initialize the Master Descriptor Base Address (MDBA) */ + writeq(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA); + + /* 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 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K), + priv->smba + ISMT_SPGT); + break; + + case 100: + dev_dbg(dev, "Setting SMBus clock to 100 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K), + priv->smba + ISMT_SPGT); + break; + + case 400: + dev_dbg(dev, "Setting SMBus clock to 400 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K), + priv->smba + ISMT_SPGT); + break; + + case 1000: + dev_dbg(dev, "Setting SMBus clock to 1000 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M), + priv->smba + ISMT_SPGT); + break; + + default: + dev_warn(dev, "Invalid SMBus clock speed, only 0, 80, 100, 400, and 1000 are valid\n"); + break; + } + + val = readl(priv->smba + ISMT_SPGT); + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + 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); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int 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))); + + priv->head = 0; + init_completion(&priv->cmp); + + return 0; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int 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\n"); + 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 +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"); + 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); + 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; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void 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 = ismt_remove, + .suspend = ismt_suspend, + .resume = ismt_resume, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>"); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 27+ messages in thread
[parent not found: <1359748083-24423-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>]
* Re: [PATCH v6] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1359748083-24423-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> @ 2013-02-04 9:47 ` Jean Delvare [not found] ` <20130204104744.5f83541e-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Jean Delvare @ 2013-02-04 9:47 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley Hi Neil, On Fri, 1 Feb 2013 14:48:03 -0500, Neil Horman wrote: > 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. > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> > > --- > 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: > +/** > + * 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, > + (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. > + 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, > + (long unsigned int)readq(priv->smba + ISMT_MSTR_MDBA)); Same here. > + 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 > + * @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. > + * @dma_size size of the dma transfer This function has no parameter named dma_size. > + */ > +static int ismt_process_desc(const struct ismt_desc *desc, > + union i2c_smbus_data *data, > + struct ismt_priv *priv, int size, > + char read_write) > +{ > + 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. > + } > + 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; > +} > (...) > + /* 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. > + > + /* 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; > + } > + > + /* 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. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130204104744.5f83541e-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v6] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130204104744.5f83541e-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-02-04 17:19 ` Jean Delvare [not found] ` <20130204181902.06c247ad-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Jean Delvare @ 2013-02-04 17:19 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Mon, 4 Feb 2013 10:47:44 +0100, Jean Delvare wrote: > 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.orig/i2c-ismt.c +++ i2c-ismt/i2c-ismt.c @@ -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. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130204181902.06c247ad-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v6] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130204181902.06c247ad-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-02-04 17:28 ` Neil Horman [not found] ` <20130204172851.GA24749-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-02-04 17:28 UTC (permalink / raw) To: Jean Delvare; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Mon, Feb 04, 2013 at 06:19:02PM +0100, Jean Delvare wrote: > On Mon, 4 Feb 2013 10:47:44 +0100, Jean Delvare wrote: > > 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: > 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 > --- > i2c-ismt.c | 35 +++++++++++++++++++++++++---------- > 1 file changed, 25 insertions(+), 10 deletions(-) > > --- i2c-ismt.orig/i2c-ismt.c > +++ i2c-ismt/i2c-ismt.c > @@ -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. > > -- > Jean Delvare > ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130204172851.GA24749-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org>]
* Re: [PATCH v6] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130204172851.GA24749-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> @ 2013-02-05 7:21 ` Jean Delvare 0 siblings, 0 replies; 27+ messages in thread From: Jean Delvare @ 2013-02-05 7:21 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Mon, 4 Feb 2013 12:28:51 -0500, Neil Horman wrote: > 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. > I'll test out this patch in a bit here, make > the other requested corrections and repost shortly. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> ` (2 preceding siblings ...) 2013-02-01 19:48 ` [PATCH v6] " Neil Horman @ 2013-02-04 19:54 ` Neil Horman [not found] ` <1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 3 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-02-04 19:54 UTC (permalink / raw) To: linux-i2c-u79uwXL29TY76Z2rM5mHXA Cc: Neil Horman, Bill Brown, Seth Heasley, Jean Delvare 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. Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> --- 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 diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 0000000..7373558 --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> + + +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 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bdca511..0bb7ff4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -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 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 6181f3f..23c9d55 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -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 diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 0000000..3f7a9cb --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,963 @@ +/* + * 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. + */ + +/* + * 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> + +#include <asm-generic/io-64-nonatomic-lo-hi.h> + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a + +#define ISMT_DESC_ENTRIES 32 /* number of descriptor entries */ +#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */ + +/* 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. */ +#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 */ +#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 */ +#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) */ + +/* 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) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#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 */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */ + 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_SMT0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_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 in kHz (0 = BIOS default)"); + +/** + * __ismt_desc_dump() - dump the contents of a specific descriptor + */ +static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc) +{ + + dev_dbg(dev, "Descriptor struct: %p\n", desc); + 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_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); + __ismt_desc_dump(dev, desc); +} + +/** + * 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%016llX\n", + priv->smba + ISMT_GR_SMTICL, + (long long unsigned int)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%016llX\n", + priv->smba + ISMT_MSTR_MDBA, + (long long unsigned int)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)); +} + +/** + * 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 + * @priv: ismt_priv struct holding our dma buffer + * @size: SMBus transaction type + * @read_write: flag to indicate if this is a read or write + */ +static int ismt_process_desc(const struct ismt_desc *desc, + union i2c_smbus_data *data, + struct ismt_priv *priv, int size, + char read_write) +{ + 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 (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; + } + + 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); + + /* 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; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->byte; + } 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; + 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"); + 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; + 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: + 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; + 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"); + 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); + } + 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; + goto out; + } + + /* 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; +} + +/** + * 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 ismt_hw_init(struct ismt_priv *priv) +{ + u32 val; + struct device *dev = &priv->pci_dev->dev; + + /* initialize the Master Descriptor Base Address (MDBA) */ + writeq(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA); + + /* 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 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K), + priv->smba + ISMT_SPGT); + break; + + case 100: + dev_dbg(dev, "Setting SMBus clock to 100 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K), + priv->smba + ISMT_SPGT); + break; + + case 400: + dev_dbg(dev, "Setting SMBus clock to 400 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K), + priv->smba + ISMT_SPGT); + break; + + case 1000: + dev_dbg(dev, "Setting SMBus clock to 1000 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M), + priv->smba + ISMT_SPGT); + break; + + default: + dev_warn(dev, "Invalid SMBus clock speed, only 0, 80, 100, 400, and 1000 are valid\n"); + break; + } + + val = readl(priv->smba + ISMT_SPGT); + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + 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); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int 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))); + + priv->head = 0; + init_completion(&priv->cmp); + + return 0; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int 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\n"); + 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 +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"); + 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); + 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; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void 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 = ismt_remove, + .suspend = ismt_suspend, + .resume = ismt_resume, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>"); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 27+ messages in thread
[parent not found: <1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>]
* RE: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> @ 2013-02-05 1:13 ` Heasley, Seth [not found] ` <DEC3922D1904474A8862C7BFF516EA9A59B4A8B8-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org> 2013-02-05 16:41 ` Jean Delvare 2013-02-10 15:02 ` Wolfram Sang 2 siblings, 1 reply; 27+ messages in thread From: Heasley, Seth @ 2013-02-05 1:13 UTC (permalink / raw) To: Neil Horman, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Cc: Brown, Bill E, Jean Delvare Hi Neil, >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 ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <DEC3922D1904474A8862C7BFF516EA9A59B4A8B8-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org>]
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <DEC3922D1904474A8862C7BFF516EA9A59B4A8B8-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org> @ 2013-02-05 7:16 ` Jean Delvare [not found] ` <20130205081642.5349bd1a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Jean Delvare @ 2013-02-05 7:16 UTC (permalink / raw) To: Heasley, Seth Cc: Neil Horman, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Brown, Bill E On Tue, 5 Feb 2013 01:13:56 +0000, Heasley, Seth wrote: > Hi Neil, > > >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! -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130205081642.5349bd1a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130205081642.5349bd1a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-02-05 14:52 ` Neil Horman [not found] ` <20130205145230.GA4511-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-02-05 14:52 UTC (permalink / raw) To: Jean Delvare Cc: Heasley, Seth, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Brown, Bill E On Tue, Feb 05, 2013 at 08:16:42AM +0100, Jean Delvare wrote: > On Tue, 5 Feb 2013 01:13:56 +0000, Heasley, Seth wrote: > > Hi Neil, > > > > >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! > 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 ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130205145230.GA4511-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org>]
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130205145230.GA4511-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> @ 2013-02-05 16:41 ` Jean Delvare 0 siblings, 0 replies; 27+ messages in thread From: Jean Delvare @ 2013-02-05 16:41 UTC (permalink / raw) To: Neil Horman Cc: Heasley, Seth, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Brown, Bill E, Wolfram Sang On Tue, 5 Feb 2013 09:52:30 -0500, Neil Horman wrote: > On Tue, Feb 05, 2013 at 08:16:42AM +0100, Jean Delvare wrote: > > On Tue, 5 Feb 2013 01:13:56 +0000, Heasley, Seth wrote: > > > Hi Neil, > > > > > > >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! > > > 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. -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-02-05 1:13 ` Heasley, Seth @ 2013-02-05 16:41 ` Jean Delvare 2013-02-10 15:02 ` Wolfram Sang 2 siblings, 0 replies; 27+ messages in thread From: Jean Delvare @ 2013-02-05 16:41 UTC (permalink / raw) To: Neil Horman; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Mon, 4 Feb 2013 14:54:10 -0500, Neil Horman wrote: > 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. > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > CC: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > CC: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> > (...) Reviewed-by: Jean Delvare <khali-PUYAD+kWke1g9hUCZPvPmw@public.gmane.org> -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-02-05 1:13 ` Heasley, Seth 2013-02-05 16:41 ` Jean Delvare @ 2013-02-10 15:02 ` Wolfram Sang [not found] ` <20130210150205.GB6282-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org> 2 siblings, 1 reply; 27+ messages in thread From: Wolfram Sang @ 2013-02-10 15:02 UTC (permalink / raw) To: Neil Horman Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley, Jean Delvare On Mon, Feb 04, 2013 at 02:54:10PM -0500, Neil Horman wrote: > 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. > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> Thanks, applied to for-next! ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130210150205.GB6282-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org>]
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130210150205.GB6282-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org> @ 2013-03-05 14:22 ` Neil Horman [not found] ` <20130305142225.GA23095-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Neil Horman @ 2013-03-05 14:22 UTC (permalink / raw) To: Wolfram Sang Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley, Jean Delvare On Sun, Feb 10, 2013 at 04:02:05PM +0100, Wolfram Sang wrote: > On Mon, Feb 04, 2013 at 02:54:10PM -0500, Neil Horman wrote: > > 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. > > > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > 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 ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130305142225.GA23095-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org>]
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130305142225.GA23095-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> @ 2013-03-05 14:42 ` Jean Delvare [not found] ` <20130305154253.317bd23a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 0 siblings, 1 reply; 27+ messages in thread From: Jean Delvare @ 2013-03-05 14:42 UTC (permalink / raw) To: Neil Horman Cc: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley Hi Neil, On Tue, 5 Mar 2013 09:22:25 -0500, Neil Horman wrote: > On Sun, Feb 10, 2013 at 04:02:05PM +0100, Wolfram Sang wrote: > > On Mon, Feb 04, 2013 at 02:54:10PM -0500, Neil Horman wrote: > > > 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. > > > > > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > > > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > > > 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. 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 -- Jean Delvare ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <20130305154253.317bd23a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>]
* Re: [PATCH v7] i2c: Adding support for Intel iSMT SMBus 2.0 host controller [not found] ` <20130305154253.317bd23a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> @ 2013-03-05 16:09 ` Neil Horman 0 siblings, 0 replies; 27+ messages in thread From: Neil Horman @ 2013-03-05 16:09 UTC (permalink / raw) To: Jean Delvare Cc: Wolfram Sang, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Bill Brown, Seth Heasley On Tue, Mar 05, 2013 at 03:42:53PM +0100, Jean Delvare wrote: > Hi Neil, > > On Tue, 5 Mar 2013 09:22:25 -0500, Neil Horman wrote: > > On Sun, Feb 10, 2013 at 04:02:05PM +0100, Wolfram Sang wrote: > > > On Mon, Feb 04, 2013 at 02:54:10PM -0500, Neil Horman wrote: > > > > 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. > > > > > > > > Signed-off-by: Neil Horman <nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> > > > > Signed-off-by: Bill Brown <bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > > > Tested-by: Seth Heasley <seth.heasley-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> > > > > > > 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. > > 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 > -- > Jean Delvare > ^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2013-03-05 16:09 UTC | newest] Thread overview: 27+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-12-07 0:36 [PATCH v4] i2c: Adding support for Intel iSMT SMBus 2.0 host controller Bill E Brown [not found] ` <1354840604-8160-1-git-send-email-bill.e.brown-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> 2012-12-07 0:34 ` Brown, Bill E 2012-12-18 14:03 ` Jean Delvare [not found] ` <20121218150337.5b861ae3-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-01-28 19:43 ` [PATCH v5] " Neil Horman [not found] ` <1359402232-21369-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-01-29 14:05 ` Jean Delvare [not found] ` <20130129150551.498b2a9b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-01-29 15:32 ` Neil Horman [not found] ` <20130129153253.GA14044-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 2013-01-29 22:10 ` Jean Delvare [not found] ` <20130129231028.41b04fc9-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-01-30 14:15 ` Neil Horman [not found] ` <20130130141504.GA2968-0o1r3XBGOEbbgkc5XkKeNuvMHUBZFtU3YPYVAmT7z5s@public.gmane.org> 2013-01-30 21:55 ` Jean Delvare 2013-01-29 16:57 ` Heasley, Seth [not found] ` <DEC3922D1904474A8862C7BFF516EA9A59B47EB2-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org> 2013-01-29 18:52 ` Jean Delvare 2013-01-29 14:29 ` Jean Delvare 2013-02-01 19:48 ` [PATCH v6] " Neil Horman [not found] ` <1359748083-24423-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-02-04 9:47 ` Jean Delvare [not found] ` <20130204104744.5f83541e-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-02-04 17:19 ` Jean Delvare [not found] ` <20130204181902.06c247ad-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-02-04 17:28 ` Neil Horman [not found] ` <20130204172851.GA24749-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 2013-02-05 7:21 ` Jean Delvare 2013-02-04 19:54 ` [PATCH v7] " Neil Horman [not found] ` <1360007650-26272-1-git-send-email-nhorman-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org> 2013-02-05 1:13 ` Heasley, Seth [not found] ` <DEC3922D1904474A8862C7BFF516EA9A59B4A8B8-P5GAC/sN6hmkrb+BlOpmy7fspsVTdybXVpNB7YpNyf8@public.gmane.org> 2013-02-05 7:16 ` Jean Delvare [not found] ` <20130205081642.5349bd1a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-02-05 14:52 ` Neil Horman [not found] ` <20130205145230.GA4511-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 2013-02-05 16:41 ` Jean Delvare 2013-02-05 16:41 ` Jean Delvare 2013-02-10 15:02 ` Wolfram Sang [not found] ` <20130210150205.GB6282-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org> 2013-03-05 14:22 ` Neil Horman [not found] ` <20130305142225.GA23095-B26myB8xz7F8NnZeBjwnZQMhkBWG/bsMQH7oEaQurus@public.gmane.org> 2013-03-05 14:42 ` Jean Delvare [not found] ` <20130305154253.317bd23a-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org> 2013-03-05 16:09 ` Neil Horman
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).