Re: [PATCH v6] i2c: Adding support for Intel iSMT SMBus 2.0 host controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxx>
> Signed-off-by: Bill Brown <bill.e.brown@xxxxxxxxx>
> Tested-by: Seth Heasley <seth.heasley@xxxxxxxxx>
> CC: Seth Heasley <seth.heasley@xxxxxxxxx>
> CC: Jean Delvare <khali@xxxxxxxxxxxx>
> 
> ---
> 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
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux