From patchwork Thu Feb 28 18:17:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 159418 Delivered-To: patch@linaro.org Received: by 2002:ac9:18c7:0:0:0:0:0 with SMTP id i7csp984508oce; Thu, 28 Feb 2019 10:29:48 -0800 (PST) X-Google-Smtp-Source: APXvYqyaRPFGnv2umlaW+SdoNTIV2HocGXeEMZ5Pa7h7Q22hBqNwAMzHcQtPjoV1+ORSFYVw6RjJ X-Received: by 2002:a25:bacd:: with SMTP id a13mr762205ybk.175.1551378588349; Thu, 28 Feb 2019 10:29:48 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1551378588; cv=none; d=google.com; s=arc-20160816; b=truS9VMXI6dubJ9qA/+Ht3S6IG/WdY0NwBe0DPSSB2S+EcRHh8c4sP/3HhxkM69FfQ /wxXVGYM/oHKA4NW/7koh+Z02VktUHXv1REYCsUex1zwDZrLDW7/C+FgpbU4Iq5iWNJW x3o7VVnohSlv2MUGOsyZEuv83oDVIk7rRbePG119poP4LlOGUxkmD+aA3zoxj9HWoOIB u0Z/5nPg3WOJhWdYeWtFNdx0nJ2Vr4FTxRGD4kfF8EuE2jVlUqNQU+2iLsXNB+pq23Z2 zmZFzGwvF5zqA5kGLaN/Jv9MyjGn4n4oFvswdVqAtjtp5ciV6PyaIrPOggVbx+Oto6z2 /gfg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:subject:references:in-reply-to :message-id:date:to:from:dkim-signature; bh=vCzpvVmLyXpfuQofAv3zcqXy6IBz4pj9h28tnU3AUe8=; b=d5SOJixOeRnA/uOYf/gR09kE7P3LXuyK27CV+5Ccl1xn8KnYTW7NG0FjVvvpuojlHI 80pM2zbS27bRjN7JnSLoAoyd5SSwH1vQ/6uyMROcAtZVzYvNXlrflkC60vFK4A9ndIJQ UCWFTglR+DRhr0cEiZzRCooMw9QJNHteu6vR61INRdcKZPD/fr5E8oE5t7jHywzgPvVf CFWLwm1WLYPEszwf85hZNQj8JbEoJWvGBplU5iLdNy861Ou2kLyO6HWCvea2Z9eWjSMj IdUjV9pN+OuRpHJyF/ha6uPdvSJRv69cVYfTNMWFyBPqEld7bq9L3J9yWtoIQyFlYBz1 23ow== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b="e/hqWnla"; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org" Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id s22si11737376ywa.41.2019.02.28.10.29.48 for (version=TLS1 cipher=AES128-SHA bits=128/128); Thu, 28 Feb 2019 10:29:48 -0800 (PST) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b="e/hqWnla"; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org" Received: from localhost ([127.0.0.1]:44642 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzQRH-0000aq-Oi for patch@linaro.org; Thu, 28 Feb 2019 13:29:47 -0500 Received: from eggs.gnu.org ([209.51.188.92]:46639) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzQFe-0000VP-BZ for qemu-devel@nongnu.org; Thu, 28 Feb 2019 13:18:00 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzQFV-0002Hv-JO for qemu-devel@nongnu.org; Thu, 28 Feb 2019 13:17:44 -0500 Received: from mail-oi1-x241.google.com ([2607:f8b0:4864:20::241]:37584) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gzQFV-0002C2-95 for qemu-devel@nongnu.org; Thu, 28 Feb 2019 13:17:37 -0500 Received: by mail-oi1-x241.google.com with SMTP id w66so17270046oia.4 for ; Thu, 28 Feb 2019 10:17:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=vCzpvVmLyXpfuQofAv3zcqXy6IBz4pj9h28tnU3AUe8=; b=e/hqWnla9W0W7pOCamG7EeP2X+6p1IA8pt9r5lnC45s7voge1XPwxqav+j6vnpgUAb tflakcl/XjIwMIXo3KrsbFZl5xUkkgudkcvg6IskwRCDK0cLgsutC4uYCqnippP45qDb yWxt+yj/7jE/pPPS9h9dXuSnczKaOkRo4PLBX/T2xY1XDlP4SldTA5K2cplUW64GM+h0 k1lRmmS6auPucXHFG16UMkU5OxwelbgV/oZLZ5iECIaSBzGqSDV1/eI0kfRLxggSc952 iVuew9SDUEdKcBnr0byhIcEnMDOvpn2gs22mNK2pT62aXwTL/4Gd2s+B6siJHs7+hcf1 77gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=vCzpvVmLyXpfuQofAv3zcqXy6IBz4pj9h28tnU3AUe8=; b=h6LGgJ0jHsfyjRpPNratyAntRAAgGObaFZrD07SLdyCSQttmVyhDRZ0IEY+EZFD8ld djKJTA0slEq8CmKP0+knzKl7c3WS2eeOToPJkAH+PE53KbHaQPcQJUVL9aSr67dmDh1G ooKD5+N6DKx9bDozVP4XHct8Ghv/U9aj70V0+7sz15agcrACFkPYHUGptmP0vh1yfn3b Y7aVUpU/RljkF8GLJmOIl35NhbpUMe1U/Isot4qP9DIcxYmm1mx6CfNDtJbTuP9WYPK6 clMbcA+HA6ybqHedOYU4cxO04VEFCiDa5wnkINqzVhUtozaKADlDCt5pH1QijFOvtvWM xaZw== X-Gm-Message-State: AHQUAuZMuPov1xTxKa5T1CnXvohrESez6D5u3QSh6GkQKE5MF4Z12Zng ibNb5wIfC7pDrnTGDxY87A== X-Received: by 2002:aca:55d0:: with SMTP id j199mr699345oib.110.1551377844392; Thu, 28 Feb 2019 10:17:24 -0800 (PST) Received: from serve.minyard.net (serve.minyard.net. [2001:470:b8f6:1b::1]) by smtp.gmail.com with ESMTPSA id n25sm8805196otj.76.2019.02.28.10.17.16 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 28 Feb 2019 10:17:19 -0800 (PST) Received: from t430.minyard.net (unknown [IPv6:2001:470:b8f6:1b:b89e:c7bf:12ab:ad14]) by serve.minyard.net (Postfix) with ESMTPA id 4C5781808FB; Thu, 28 Feb 2019 18:17:14 +0000 (UTC) Received: by t430.minyard.net (Postfix, from userid 1000) id 21941302AF5; Thu, 28 Feb 2019 12:17:14 -0600 (CST) From: minyard@acm.org To: Peter Maydell Date: Thu, 28 Feb 2019 12:17:02 -0600 Message-Id: <20190228181710.2477-12-minyard@acm.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190228181710.2477-1-minyard@acm.org> References: <20190228181710.2477-1-minyard@acm.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:4864:20::241 Subject: [Qemu-devel] [PULL 11/19] i2c:pm_smbus: Fix pm_smbus handling of I2C block read X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Corey Minyard , QEMU Developers , minyard@acm.org Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: "Qemu-devel" From: Corey Minyard The I2C block read function of pm_smbus was completely broken. It required doing some direct I2C handling because it didn't have a defined size, the OS code just reads bytes until it marks the transaction finished. This also required adjusting how the AMIBIOS workaround code worked, the I2C block mode was setting STS_HOST_BUSY during a transaction, so that bit could no longer be used to inform the host status read code to start the transaction. Create a explicit bool for that operation. Also, don't read the next byte from the device in byte-by-byte mode unless the OS is actually clearing the byte done bit. Just assuming that's what the OS is doing is a bad idea. Signed-off-by: Corey Minyard --- hw/i2c/pm_smbus.c | 86 ++++++++++++++++++++++++++++++--------- include/hw/i2c/pm_smbus.h | 6 +++ 2 files changed, 73 insertions(+), 19 deletions(-) -- 2.17.1 diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 6cfb7eb1b3..81d2a425ec 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -118,19 +118,30 @@ static void smb_transaction(PMSMBus *s) } break; case PROT_I2C_BLOCK_READ: - if (read) { - int xfersize = s->smb_data0; - if (xfersize > sizeof(s->smb_data)) { - xfersize = sizeof(s->smb_data); - } - ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data, - xfersize, false, true); - goto data8; - } else { - /* The manual says the behavior is undefined, just set DEV_ERR. */ + /* According to the Linux i2c-i801 driver: + * NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading. + * However if SPD Write Disable is set (Lynx Point and later), + * the read will fail if we don't set the R/#W bit. + * So at least Linux may or may not set the read bit here. + * So just ignore the read bit for this command. + */ + if (i2c_start_transfer(bus, addr, 0)) { goto error; } - break; + ret = i2c_send(bus, s->smb_data1); + if (ret) { + goto error; + } + if (i2c_start_transfer(bus, addr, 1)) { + goto error; + } + s->in_i2c_block_read = true; + s->smb_blkdata = i2c_recv(s->smbus); + s->op_done = false; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + goto out; + case PROT_BLOCK_DATA: if (read) { ret = smbus_read_block(bus, addr, cmd, s->smb_data, @@ -208,6 +219,7 @@ static void smb_transaction_start(PMSMBus *s) { if (s->smb_ctl & CTL_INTREN) { smb_transaction(s); + s->start_transaction_on_status_read = false; } else { /* Do not execute immediately the command; it will be * executed when guest will read SMB_STAT register. This @@ -217,6 +229,7 @@ static void smb_transaction_start(PMSMBus *s) * checking for status. If STS_HOST_BUSY doesn't get * set, it gets stuck. */ s->smb_stat |= STS_HOST_BUSY; + s->start_transaction_on_status_read = true; } } @@ -226,19 +239,38 @@ smb_irq_value(PMSMBus *s) return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN); } +static bool +smb_byte_by_byte(PMSMBus *s) +{ + if (s->op_done) { + return false; + } + if (s->in_i2c_block_read) { + return true; + } + return !(s->smb_auxctl & AUX_BLK); +} + static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, unsigned width) { PMSMBus *s = opaque; + uint8_t clear_byte_done; SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx " val=0x%02" PRIx64 "\n", addr, val); switch(addr) { case SMBHSTSTS: + clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; s->smb_stat &= ~(val & ~STS_HOST_BUSY); - if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) { + if (clear_byte_done && smb_byte_by_byte(s)) { uint8_t read = s->smb_addr & 0x01; + if (s->in_i2c_block_read) { + /* See comment below PROT_I2C_BLOCK_READ above. */ + read = 1; + } + s->smb_index++; if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; @@ -268,12 +300,23 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, s->smb_stat |= STS_BYTE_DONE; } else if (s->smb_ctl & CTL_LAST_BYTE) { s->op_done = true; - s->smb_blkdata = s->smb_data[s->smb_index]; + if (s->in_i2c_block_read) { + s->in_i2c_block_read = false; + s->smb_blkdata = i2c_recv(s->smbus); + i2c_nack(s->smbus); + i2c_end_transfer(s->smbus); + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + } s->smb_index = 0; s->smb_stat |= STS_INTR; s->smb_stat &= ~STS_HOST_BUSY; } else { - s->smb_blkdata = s->smb_data[s->smb_index]; + if (s->in_i2c_block_read) { + s->smb_blkdata = i2c_recv(s->smbus); + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + } s->smb_stat |= STS_BYTE_DONE; } } @@ -284,6 +327,10 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, if (!s->op_done) { s->smb_index = 0; s->op_done = true; + if (s->in_i2c_block_read) { + s->in_i2c_block_read = false; + i2c_end_transfer(s->smbus); + } } smb_transaction_start(s); } @@ -337,8 +384,9 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) switch(addr) { case SMBHSTSTS: val = s->smb_stat; - if (s->smb_stat & STS_HOST_BUSY) { + if (s->start_transaction_on_status_read) { /* execute command now */ + s->start_transaction_on_status_read = false; s->smb_stat &= ~STS_HOST_BUSY; smb_transaction(s); } @@ -359,10 +407,10 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_data1; break; case SMBBLKDAT: - if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { - s->smb_index = 0; - } - if (s->smb_auxctl & AUX_BLK) { + if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) { + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { + s->smb_index = 0; + } val = s->smb_data[s->smb_index++]; if (!s->op_done && s->smb_index == s->smb_data0) { s->op_done = true; diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 6dd5b7040b..7bcca97672 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -33,6 +33,12 @@ typedef struct PMSMBus { /* Set on block transfers after the last byte has been read, so the INTR bit can be set at the right time. */ bool op_done; + + /* Set during an I2C block read, so we know how to handle data. */ + bool in_i2c_block_read; + + /* Used to work around a bug in AMIBIOS, see smb_transaction_start() */ + bool start_transaction_on_status_read; } PMSMBus; void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk);