From patchwork Mon Apr 25 14:33:30 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Glauber X-Patchwork-Id: 614512 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 3qtplr2p4yz9s1h for ; Tue, 26 Apr 2016 00:39:52 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752601AbcDYOjv (ORCPT ); Mon, 25 Apr 2016 10:39:51 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:34152 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754769AbcDYOd6 (ORCPT ); Mon, 25 Apr 2016 10:33:58 -0400 Received: by mail-wm0-f66.google.com with SMTP id n3so25455266wmn.1; Mon, 25 Apr 2016 07:33:57 -0700 (PDT) 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:in-reply-to :references; bh=cXYTNvPXVBTsFYJBeCOz3ez9LWJrOF9lO1SJse6Q2go=; b=IamD9WhUFM1G4bIF6uQ08lgP6YX3yQZuegtMczkEAzVV7ui7m0lOWOgfBRK454Ecm8 sA53UlDBs3c/YGLWbKPGHRnpXH6fvRw1GfFw24lFjBdNkxoR72I9+Rv1+CXVD+33XKJt W6fcnqA/E4zyV73ATCL5Xa//tkMOk76sjbTGiEpBcWq68Ejl+4wnDr8igfC6GYRFt/Eo nEHo7aZUBLHrbbyHICHhLAMi+8Ihp/TQGlk6sNmMQjKGa+2RKNZ4Z8CbcgbP0i52nvPr EYrEM9tG01cX4u2MNU1iBy/Hql3sLKQ5PMzr/cYnijTOm78ChokV0zsob0fs11ezS8aU tMJg== X-Gm-Message-State: AOPr4FXGOeP8F5Jpr42tcDxnVGUdZaUf1O0Tz2ic6X+fjtgcdtcgv1te1xLByHiA1OvTvw== X-Received: by 10.195.3.1 with SMTP id bs1mr37588073wjd.160.1461594836335; Mon, 25 Apr 2016 07:33:56 -0700 (PDT) Received: from wintermute.fritz.box (HSI-KBW-46-223-170-19.hsi.kabel-badenwuerttemberg.de. [46.223.170.19]) by smtp.gmail.com with ESMTPSA id o73sm19084272wme.16.2016.04.25.07.33.55 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 25 Apr 2016 07:33:55 -0700 (PDT) From: Jan Glauber To: Wolfram Sang Cc: linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, David Daney , Jan Glauber Subject: [PATCH v7 01/15] i2c: octeon: Improve error status checking Date: Mon, 25 Apr 2016 16:33:30 +0200 Message-Id: <1461594824-7215-2-git-send-email-jglauber@cavium.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1461594824-7215-1-git-send-email-jglauber@cavium.com> References: <1461594824-7215-1-git-send-email-jglauber@cavium.com> Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Introduce a function that checks for valid status codes depending on the phase of a transmit or receive. Also add all existing status codes and improve error handling for various states. The Octeon TWSI has an "assert acknowledge" bit (TWSI_CTL_AAK) that is required to be set in master receive mode until the last byte is requested. The state check needs to consider if this bit was set. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 129 +++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 23 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 4275007..471d06e 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -55,13 +55,35 @@ #define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */ #define TWSI_CTL_AAK 0x04 /* Assert ACK */ -/* Some status values */ +/* Status values */ +#define STAT_ERROR 0x00 #define STAT_START 0x08 -#define STAT_RSTART 0x10 +#define STAT_REP_START 0x10 #define STAT_TXADDR_ACK 0x18 +#define STAT_TXADDR_NAK 0x20 #define STAT_TXDATA_ACK 0x28 +#define STAT_TXDATA_NAK 0x30 +#define STAT_LOST_ARB_38 0x38 #define STAT_RXADDR_ACK 0x40 +#define STAT_RXADDR_NAK 0x48 #define STAT_RXDATA_ACK 0x50 +#define STAT_RXDATA_NAK 0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST 0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 #define STAT_IDLE 0xF8 /* TWSI_INT values */ @@ -225,6 +247,67 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } +static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) +{ + u8 stat = octeon_i2c_stat_read(i2c); + + switch (stat) { + /* Everything is fine */ + case STAT_IDLE: + case STAT_AD2W_ACK: + case STAT_RXADDR_ACK: + case STAT_TXADDR_ACK: + case STAT_TXDATA_ACK: + return 0; + + /* ACK allowed on pre-terminal bytes only */ + case STAT_RXDATA_ACK: + if (!final_read) + return 0; + return -EIO; + + /* NAK allowed on terminal byte only */ + case STAT_RXDATA_NAK: + if (final_read) + return 0; + return -EIO; + + /* Arbitration lost */ + case STAT_LOST_ARB_38: + case STAT_LOST_ARB_68: + case STAT_LOST_ARB_78: + case STAT_LOST_ARB_B0: + return -EAGAIN; + + /* Being addressed as slave, should back off & listen */ + case STAT_SLAVE_60: + case STAT_SLAVE_70: + case STAT_GENDATA_ACK: + case STAT_GENDATA_NAK: + return -EOPNOTSUPP; + + /* Core busy as slave */ + case STAT_SLAVE_80: + case STAT_SLAVE_88: + case STAT_SLAVE_A0: + case STAT_SLAVE_A8: + case STAT_SLAVE_LOST: + case STAT_SLAVE_NAK: + case STAT_SLAVE_ACK: + return -EOPNOTSUPP; + + case STAT_TXDATA_NAK: + return -EIO; + case STAT_TXADDR_NAK: + case STAT_RXADDR_NAK: + case STAT_AD2W_NAK: + return -ENXIO; + default: + dev_err(i2c->dev, "unhandled state: %d\n", stat); + return -EIO; + } +} + /* calculate and set clock divisors */ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) { @@ -318,7 +401,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) } data = octeon_i2c_stat_read(i2c); - if ((data != STAT_START) && (data != STAT_RSTART)) { + if ((data != STAT_START) && (data != STAT_REP_START)) { dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); return -EIO; } @@ -347,7 +430,6 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, const u8 *data, int length) { int i, result; - u8 tmp; result = octeon_i2c_start(i2c); if (result) @@ -361,14 +443,9 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_stat_read(i2c); - - if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { - dev_err(i2c->dev, - "%s: bad status before write (0x%x)\n", - __func__, tmp); - return -EIO; - } + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; octeon_i2c_data_write(i2c, data[i]); octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); @@ -397,7 +474,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, u16 *rlength, bool recv_len) { int i, result, length = *rlength; - u8 tmp; + bool final_read = false; if (length < 1) return -EINVAL; @@ -413,19 +490,21 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, if (result) return result; + /* address OK ? */ + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; + for (i = 0; i < length; i++) { - tmp = octeon_i2c_stat_read(i2c); - if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { - dev_err(i2c->dev, - "%s: bad status before read (0x%x)\n", - __func__, tmp); - return -EIO; - } + /* for the last byte TWSI_CTL_AAK must not be set */ + if (i + 1 == length) + final_read = true; - if (i + 1 < length) - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); - else + /* clear iflg to allow next event */ + if (final_read) octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + else + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); result = octeon_i2c_wait(i2c); if (result) @@ -441,6 +520,10 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, } length += data[i]; } + + result = octeon_i2c_check_status(i2c, final_read); + if (result) + return result; } *rlength = length; return 0;