From patchwork Thu Nov 10 02:56:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: tnhuynh@apm.com X-Patchwork-Id: 693041 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3tDnkq5RMkz9t2T for ; Thu, 10 Nov 2016 13:56:51 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=apm.com header.i=@apm.com header.b="iy5B6pXN"; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753183AbcKJC4t (ORCPT ); Wed, 9 Nov 2016 21:56:49 -0500 Received: from mail-pf0-f170.google.com ([209.85.192.170]:33237 "EHLO mail-pf0-f170.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751582AbcKJC4s (ORCPT ); Wed, 9 Nov 2016 21:56:48 -0500 Received: by mail-pf0-f170.google.com with SMTP id d2so137602368pfd.0 for ; Wed, 09 Nov 2016 18:56:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=apm.com; s=apm; h=from:to:cc:subject:date:message-id; bh=xISNSfnAFNKEkPJx7viRfLyIzw9xlgDk2CpmHTywJ6s=; b=iy5B6pXN4EnzCrBFDEQzxrfvrg5370ETzOXwgZYIK5PKMzj6VqbNpwYiZPxZ2RKnN6 u1q2qXWWjLvC7Xh9cD8+7Xe38bM3kBHwxFObJwJ4Eeel4HyYvwQMfQC5SX7M/Lpz4Kei bc4zj0sI0hpGadg+fFXdpa6bNW9Nkx7TWzK/o= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=xISNSfnAFNKEkPJx7viRfLyIzw9xlgDk2CpmHTywJ6s=; b=lLAARMfF9SN74iHtX9hNLdpahe3K/2XR1ZzKgz5hFjitiqnNXHCOUymydO6w0XwOhU 3LzId+h18nrMi0F1sqhhRkHHLCoEc5PNqKzYF+u+OLUeXgC20C4XjqoAviHTL8yIcuhN /At6U2/6EU/mE6N2ZdYLB38ooczoZzFDKPByeAr7suJ3l8HLAEaWPZBmEKWDxDnf6dyY rhe7Csdt91pw4kNDrFH++csbpa44mFfukFnANQllNlp6iV6amjq8J9PiCJGC35D3lZ2a ATbD5+QcZcqmWPs96k51mIe+fuDVUlO2SoudEv6DPmT5E6FYSzHcaPssUogxcXsRCNj9 gxwg== X-Gm-Message-State: ABUngvf1jUrzNmK7q7mKg17Z3ExsBi2SORriclOXg9RMmOPIyTYmH7gaSvXt1vsSMUCSzK98 X-Received: by 10.98.38.199 with SMTP id m190mr5431100pfm.45.1478746607876; Wed, 09 Nov 2016 18:56:47 -0800 (PST) Received: from localhost.localdomain ([118.69.219.197]) by smtp.gmail.com with ESMTPSA id 186sm2319239pfv.61.2016.11.09.18.56.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 09 Nov 2016 18:56:46 -0800 (PST) From: tnhuynh@apm.com To: Jarkko Nikula , Andy Shevchenko , Mika Westerberg , Wolfram Sang , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Loc Ho , Thang Nguyen , Phong Vo , patches@apm.com, Tin Huynh Subject: [PATCH v4] i2c: designware: Implement support for SMBus block read and write Date: Thu, 10 Nov 2016 09:56:33 +0700 Message-Id: <1478746593-10905-1-git-send-email-tnhuynh@apm.com> X-Mailer: git-send-email 1.7.1 Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org From: Tin Huynh Free and Open IPMI use SMBUS BLOCK Read/Write to support SSIF protocol. However, I2C Designware Core Driver doesn't handle the case at the moment. The below patch supports this feature. Signed-off-by: Tin Huynh Acked-by: Jarkko Nikula Reviewed-by: Mika Westerberg Reviewed-by: Andy Shevchenko --- Change from V3: - Correct coding conventions - Make clean Change from V2: - Change subject of email - Add a helper function to handle length byte receiving Change from V1: - Remove empty lines - Add flags variable to make clean code - Change DW_DEFAULT_FUNCTIONALITY in i2c-designware-pcidrv.c --- drivers/i2c/busses/i2c-designware-core.c | 46 +++++++++++++++++++++++++-- drivers/i2c/busses/i2c-designware-pcidrv.c | 1 + drivers/i2c/busses/i2c-designware-platdrv.c | 1 + 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 1fe93c4..c91d1b4 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -543,6 +543,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) intr_mask = DW_IC_INTR_DEFAULT_MASK; for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + u32 flags = msgs[dev->msg_write_idx].flags; + /* * if target address has changed, we need to * reprogram the target address in the i2c @@ -588,8 +590,15 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) * detected from the registers so we set it always * when writing/reading the last byte. */ + + /* + * i2c-core.c always sets the buffer length of + * I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will + * be adjusted when receiving the first byte. + * Thus we can't stop the transaction here. + */ if (dev->msg_write_idx == dev->msgs_num - 1 && - buf_len == 1) + buf_len == 1 && !(flags & I2C_M_RECV_LEN)) cmd |= BIT(9); if (need_restart) { @@ -614,7 +623,12 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) dev->tx_buf = buf; dev->tx_buf_len = buf_len; - if (buf_len > 0) { + /* + * Because we don't know the buffer length in the + * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop + * the transaction here. + */ + if (buf_len > 0 || flags & I2C_M_RECV_LEN) { /* more bytes to be written */ dev->status |= STATUS_WRITE_IN_PROGRESS; break; @@ -635,6 +649,24 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) dw_writel(dev, intr_mask, DW_IC_INTR_MASK); } +static u8 +i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len) +{ + struct i2c_msg *msgs = dev->msgs; + u32 flags = msgs[dev->msg_read_idx].flags; + + /* + * Adjust the buffer length and mask the flag + * after receiving the first byte. + */ + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; + dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); + msgs[dev->msg_read_idx].len = len; + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + + return len; +} + static void i2c_dw_read(struct dw_i2c_dev *dev) { @@ -659,7 +691,15 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) rx_valid = dw_readl(dev, DW_IC_RXFLR); for (; len > 0 && rx_valid > 0; len--, rx_valid--) { - *buf++ = dw_readl(dev, DW_IC_DATA_CMD); + u32 flags = msgs[dev->msg_read_idx].flags; + + *buf = dw_readl(dev, DW_IC_DATA_CMD); + /* Ensure length byte is a valid value */ + if (flags & I2C_M_RECV_LEN && + *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { + len = i2c_dw_recv_len(dev, *buf); + } + buf++; dev->rx_outstanding--; } diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 96f8230..8ffe2da 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -75,6 +75,7 @@ struct dw_pci_controller { I2C_FUNC_SMBUS_BYTE | \ I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_BLOCK_DATA | \ I2C_FUNC_SMBUS_I2C_BLOCK) /* Merrifield HCNT/LCNT/SDA hold time */ diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 0b42a12..886fb62 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -220,6 +220,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |