From patchwork Fri Mar 18 08:46:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Glauber X-Patchwork-Id: 599365 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 3qRJpb5Wjhz9sR9 for ; Fri, 18 Mar 2016 19:50:47 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753985AbcCRIr6 (ORCPT ); Fri, 18 Mar 2016 04:47:58 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:35834 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753966AbcCRIrE (ORCPT ); Fri, 18 Mar 2016 04:47:04 -0400 Received: by mail-wm0-f66.google.com with SMTP id l124so4325475wmf.2; Fri, 18 Mar 2016 01:47:03 -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:in-reply-to:references; bh=/tYY0iDq7MN2FM97WChhIJpWT1Igm2B+c6me1lUISa8=; b=TNP5z0WoVGRQc1LGRujSvg1v5wV0qT56ZwqAVpGRh7AFUaOgCt8SuSf7rArl4RQ/LW EaYg+dv/fhZxx6NOW3a3NwlKEYibvgJa8hHPwVaLajBtOV2nGnvTqi3jrTwd1rqjc92w xiJcSntt7t01dFWu1JTl4K1ZbsJJ3C0vAlpsWl8piRaSthOlQIn3hJsQGcMaXy9dFYMa QM/sL5KwkHoUH+6NlFpUIs8FGTrmsxtbxhXTnFK54GSMATP8zgHNe8+SPGm9KS0pZPtB jNvTqX/2OYmgc9hBc6oEZBFtTBKhNcWYPuknmIT/33sfB38AvHyReJvWiuXSJNdh65Ca lGNQ== X-Gm-Message-State: AD7BkJLIa2XxIYnccRl1Yznf+DMw+9aFwsz5inP0TOV49wUTzhiG9HE/sBkUf3lEb8JIUQ== X-Received: by 10.194.60.44 with SMTP id e12mr14507110wjr.137.1458290822138; Fri, 18 Mar 2016 01:47:02 -0700 (PDT) Received: from wintermute.fritz.box (HSI-KBW-109-193-047-197.hsi7.kabel-badenwuerttemberg.de. [109.193.47.197]) by smtp.gmail.com with ESMTPSA id jo6sm11221214wjb.48.2016.03.18.01.47.01 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 18 Mar 2016 01:47:01 -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 v4 12/14] i2c-octeon: Split the driver into two parts Date: Fri, 18 Mar 2016 09:46:37 +0100 Message-Id: X-Mailer: git-send-email 1.9.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Move common functionality into a separate file in preparation of the re-use from the ThunderX i2c driver. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile | 3 +- drivers/i2c/busses/i2c-cavium.c | 822 +++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-cavium.h | 195 +++++++ drivers/i2c/busses/i2c-octeon-core.c | 983 +---------------------------------- 4 files changed, 1020 insertions(+), 983 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3405286..282f781 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,8 @@ obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o +i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.c b/drivers/i2c/busses/i2c-cavium.c new file mode 100644 index 0000000..9ad1f23 --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.c @@ -0,0 +1,822 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +static int reset_how; + +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_dis(i2c); + wake_up(&i2c->queue); + + return IRQ_HANDLED; +} + +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + +#define I2C_OCTEON_IFLG_WAIT 80 /* microseconds */ + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + long time_left; + int first = 1; + + if (i2c->broken_irq_mode) { + /* + * Some chip revisions seem to not assert the irq in + * the interrupt controller. So we must poll for the + * IFLG change. + */ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->int_en(i2c); + time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, &first), + i2c->adap.timeout); + i2c->int_dis(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + if (!time_left) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Cleanup low-level state & enable high-level. + * Returns -EAGAIN if low-level state could not be cleaned. + */ +static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) +{ + int try = 0, ret = 0; + u64 val; + + if (i2c->hlc_enabled) + return 0; + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_read_ctl(i2c) & (TWSI_CTL_STA | TWSI_CTL_STP); + if (!val) + break; + + /* clear IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + ret = -EAGAIN; + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_CE | TWSI_CTL_AAK | TWSI_CTL_ENAB); + return ret; +} + +static int octeon_i2c_lost_arb(u8 code, int final_read) +{ + switch (code) { + /* 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 -EIO; + + /* 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 -EIO; + + /* ACK allowed on pre-terminal bytes only */ + case STAT_RXDATA_ACK: + if (!final_read) + return 0; + return -EAGAIN; + + /* NAK allowed on terminal byte only */ + case STAT_RXDATA_NAK: + if (final_read) + return 0; + return -EAGAIN; + case STAT_TXDATA_NAK: + case STAT_TXADDR_NAK: + case STAT_RXADDR_NAK: + case STAT_AD2W_NAK: + return -EAGAIN; + } + return 0; +} + +static int check_arb(struct octeon_i2c *i2c, int final_read) +{ + return octeon_i2c_lost_arb(octeon_i2c_read_sw(i2c, + SW_TWSI_EOP_TWSI_STAT), final_read); +} + +/* send STOP to the bus */ +static void octeon_i2c_stop(struct octeon_i2c *i2c) +{ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STP); +} + +/** + * octeon_i2c_unblock - unblock the bus + * @i2c: The struct octeon_i2c + * + * If there was a reset while a device was driving 0 to bus, bus is blocked. + * We toggle it free manually by some clock cycles and send a stop. + */ +static void octeon_i2c_unblock(struct octeon_i2c *i2c) +{ + int state, i; + + octeon_i2c_disable_hlc(i2c); + dev_dbg(i2c->dev, "%s\n", __func__); + + /* cycle 8+1 clocks with SDA high */ + for (i = 0; i < 9; i++) { + octeon_i2c_write_int(i2c, 0); + udelay(5); + state = __raw_readq(i2c->twsi_base + TWSI_INT); + if (state & (TWSI_INT_SDA | TWSI_INT_SCL)) + break; + octeon_i2c_write_int(i2c, TWSI_INT_SCL_OVR); + udelay(5); + } + /* hand-crank a STOP */ + octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR); + udelay(5); + octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR); + udelay(5); + octeon_i2c_write_int(i2c, 0); +} + +/* + * TWSI state seems stuck. Not sure if it's TWSI-engine state or something + * else on bus. The initial _stop() is always harmless, it just resets state + * machine, does not _transmit_ STOP unless engine was active. + */ +static int start_unstick(struct octeon_i2c *i2c) +{ + octeon_i2c_stop(i2c); + + /* + * Response is escalated over successive calls, + * as EAGAIN provokes retries from i2c/core. + */ + switch (reset_how++ % 4) { + case 0: + /* just the stop above */ + break; + case 1: + /* + * Controller refused to send start flag. May be a + * client is holding SDA low? Let's try to free it. + */ + octeon_i2c_unblock(i2c); + break; + case 2: + /* re-init our TWSI hardware */ + octeon_i2c_init_lowlevel(i2c); + break; + default: + /* retry in caller */ + reset_how = 0; + return -EAGAIN; + } + return 0; +} + +/** + * octeon_i2c_start - send START to the bus + * @i2c: The struct octeon_i2c + * @first: first msg in combined operation? + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_start(struct octeon_i2c *i2c, int first) +{ + int result; + u8 data; + + octeon_i2c_disable_hlc(i2c); + + while (1) { + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STA); + + result = octeon_i2c_wait(i2c); + data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + + switch (data) { + case STAT_START: + case STAT_RSTART: + if (!first) + return -EAGAIN; + reset_how = 0; + return 0; + case STAT_RXADDR_ACK: + if (first) + return -EAGAIN; + return start_unstick(i2c); + /* + * case STAT_IDLE: + * case STAT_ERROR: + */ + default: + if (!first) + return -EAGAIN; + start_unstick(i2c); + } + } + return 0; +} + +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) +{ + u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); + + return (val & SW_TWSI_V) == 0; +} + +static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) +{ + /* clear ST/TS events, listen for neither */ + octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); +} + +/** + * octeon_i2c_hlc_wait - wait for an HLC operation to complete + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) +{ + int time_left; + + if (i2c->broken_irq_mode) { + /* + * Some cn38xx boards did not assert the irq in + * the interrupt controller. So we must poll for the + * IFLG change. + */ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_ready(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->hlc_int_en(i2c); + time_left = wait_event_interruptible_timeout(i2c->queue, + octeon_i2c_hlc_test_ready(i2c), + i2c->adap.timeout); + i2c->hlc_int_dis(i2c); + if (!time_left) + octeon_i2c_hlc_int_clear(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_hlc_test_ready(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + if (time_left < 0) { + dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); + return time_left; + } + return 0; +} + +/** + * octeon_i2c_read - receive data from the bus via low-level controller + * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the location to store the data + * @rlength: Length of the data + * @phase: which phase of a combined operation. + * @recv_len: flag for length byte + * + * The address is sent over the bus, then the data is read. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, + u16 *rlength, bool first, bool last, bool recv_len) +{ + u8 ctl = TWSI_CTL_ENAB | TWSI_CTL_AAK; + int i, result, length = *rlength; + u8 tmp; + + if (length < 1) + return -EINVAL; + + result = octeon_i2c_start(i2c, first); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target << 1) | 1); + + for (i = 0; i < length; ) { + tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + result = octeon_i2c_lost_arb(tmp, !(ctl & TWSI_CTL_AAK)); + if (result) + return result; + + switch (tmp) { + case STAT_RXDATA_ACK: + case STAT_RXDATA_NAK: + data[i++] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + } + + + /* NAK last recv'd byte, as a no-more-please */ + if (last && i == length - 1) + ctl &= ~TWSI_CTL_AAK; + + /* clr iflg to allow next event */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, ctl); + result = octeon_i2c_wait(i2c); + if (result) + return result; + if (recv_len && i == 0) { + if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(i2c->dev, + "%s: read len > I2C_SMBUS_BLOCK_MAX %d\n", + __func__, data[i]); + return -EPROTO; + } + length += data[i]; + } + } + *rlength = length; + return 0; +} + +/** + * octeon_i2c_write - send data to the bus via low-level controller + * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the data to be sent + * @length: Length of the data + * @last: is last msg in combined operation? + * + * The address is sent over the bus, then the data. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length, int first, int last) +{ + int i, result; + + result = octeon_i2c_start(i2c, first); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + result = check_arb(i2c, false); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + result = check_arb(i2c, false); + if (result) + return result; + } + + return 0; +} + +/* high-level-controller pure read of up to 8 bytes */ +static int octeon_i2c_simple_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_enable_hlc(i2c); + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64) (msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64) (msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10; + else + cmd |= SW_TWSI_OP_7; + + octeon_i2c_hlc_int_clear(i2c); + __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) + msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; + + if (msgs[0].len > 4) { + cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); + for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) + msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; + } + +err: + return ret; +} + +/* high-level-controller pure write of up to 8 bytes */ +static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_enable_hlc(i2c); + octeon_i2c_hlc_int_clear(i2c); + + ret = check_arb(i2c, false); + if (ret) + goto err; + + cmd = SW_TWSI_V | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64) (msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64) (msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10; + else + cmd |= SW_TWSI_OP_7; + + for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) + cmd |= (u64)msgs[0].buf[j] << (8 * i); + + if (msgs[0].len > 4) { + u64 ext = 0; + + for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) + ext |= (u64) msgs[0].buf[j] << (8 * i); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); + } + + writeqflush(cmd, i2c->twsi_base + SW_TWSI); + + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + ret = check_arb(i2c, false); + +err: + return ret; +} + +/* high-level-controller composite write+read, msg0=addr, msg1=data */ +static int octeon_i2c_ia_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_enable_hlc(i2c); + + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10_IA; + else + cmd |= SW_TWSI_OP_7_IA; + + if (msgs[0].len == 2) { + u64 ext = 0; + + cmd |= SW_TWSI_EIA; + ext = (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + cmd |= (u64) msgs[0].buf[1] << SW_TWSI_IA_SHIFT; + __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + } else + cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + + octeon_i2c_hlc_int_clear(i2c); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); + + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) + msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; + + if (msgs[1].len > 4) { + cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); + for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) + msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; + } + +err: + return ret; +} + +/* high-level-controller composite write+write, m[0]len<=2, m[1]len<=8 */ +static int octeon_i2c_ia_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + bool set_ext = false; + int i, j, ret = 0; + u64 cmd, ext = 0; + + octeon_i2c_enable_hlc(i2c); + + cmd = SW_TWSI_V | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64) (msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64) (msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10_IA; + else + cmd |= SW_TWSI_OP_7_IA; + + if (msgs[0].len == 2) { + cmd |= SW_TWSI_EIA; + ext |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + set_ext = true; + cmd |= (u64) msgs[0].buf[1] << SW_TWSI_IA_SHIFT; + } else + cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + + for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) + cmd |= (u64) msgs[1].buf[j] << (8 * i); + + if (msgs[1].len > 4) { + for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) + ext |= (u64)msgs[1].buf[j] << (8 * i); + set_ext = true; + } + if (set_ext) + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); + + octeon_i2c_hlc_int_clear(i2c); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); + + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = octeon_i2c_read_sw64(i2c, SW_TWSI_EOP_TWSI_STAT); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + ret = octeon_i2c_lost_arb(cmd, false); + +err: + return ret; +} + +/** + * octeon_i2c_xfer - The driver's master_xfer function + * @adap: Pointer to the i2c_adapter structure + * @msgs: Pointer to the messages to be processed + * @num: Length of the MSGS array + * + * Returns the number of messages processed, or a negative errno on failure. + */ +int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + int i, ret = 0; + + if (num == 1) { + if (msgs[0].len > 0 && msgs[0].len <= 8) { + if (msgs[0].flags & I2C_M_RD) + ret = octeon_i2c_simple_read(i2c, msgs); + else + ret = octeon_i2c_simple_write(i2c, msgs); + goto out; + } + } else if (num == 2) { + if ((msgs[0].flags & I2C_M_RD) == 0 && + (msgs[1].flags & I2C_M_RECV_LEN) == 0 && + msgs[0].len > 0 && msgs[0].len <= 2 && + msgs[1].len > 0 && msgs[1].len <= 8 && + msgs[0].addr == msgs[1].addr) { + if (msgs[1].flags & I2C_M_RD) + ret = octeon_i2c_ia_read(i2c, msgs); + else + ret = octeon_i2c_ia_write(i2c, msgs); + goto out; + } + } + + for (i = 0; ret == 0 && i < num; i++) { + struct i2c_msg *pmsg = &msgs[i]; + bool last = (i == (num - 1)); + + dev_dbg(i2c->dev, + "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num); + if (pmsg->flags & I2C_M_RD) + ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, + &pmsg->len, !i, last, + pmsg->flags & I2C_M_RECV_LEN); + else + ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, + pmsg->len, !i, last); + } + octeon_i2c_stop(i2c); +out: + return (ret != 0) ? ret : num; +} + +/* calculate and set clock divisors */ +void octeon_i2c_set_clock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* + * An mdiv value of less than 2 seems to not work well + * with ds1337 RTCs, so we constrain it to larger values. + */ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* + * For given ndiv and mdiv values check the + * two closest thp values. + */ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); +} + +int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) +{ + u8 status = 0; + int tries; + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries && status != STAT_IDLE; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + } + + if (status != STAT_IDLE) { + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", + __func__, status); + return -EIO; + } + + /* toggle twice to force both teardowns */ + octeon_i2c_enable_hlc(i2c); + octeon_i2c_disable_hlc(i2c); + return 0; +} diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h new file mode 100644 index 0000000..7f78bf0 --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.h @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets */ +#define SW_TWSI 0x00 +#define TWSI_INT 0x10 +#define SW_TWSI_EXT 0x18 + +/* Controller command patterns */ +#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIA BIT_ULL(61) /* Extended internal address */ +#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ + +/* Controller opcode word (bits 60:57) */ +#define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA (1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ + +/* Controller extended opcode word (bits 34:32) */ +#define SW_TWSI_EOP_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CLKCTL (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) + +/* Controller command and status bits */ +#define TWSI_CTL_CE 0x80 /* High level controller enable */ +#define TWSI_CTL_ENAB 0x40 /* Bus enable */ +#define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */ +#define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */ +#define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */ +#define TWSI_CTL_AAK 0x04 /* Assert ACK */ + +/* Some status values */ +#define STAT_ERROR 0x00 +#define STAT_START 0x08 +#define STAT_RSTART 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 */ +#define TWSI_INT_ST_INT BIT_ULL(0) +#define TWSI_INT_TS_INT BIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) +#define TWSI_INT_CORE_EN BIT_ULL(6) +#define TWSI_INT_SDA_OVR BIT_ULL(8) +#define TWSI_INT_SCL_OVR BIT_ULL(9) +#define TWSI_INT_SDA BIT_ULL(10) +#define TWSI_INT_SCL BIT_ULL(11) + +struct octeon_i2c { + wait_queue_head_t queue; + struct i2c_adapter adap; + int irq; + int hlc_irq; /* For cn7890 only */ + u32 twsi_freq; + int sys_freq; + void __iomem *twsi_base; + struct device *dev; + bool hlc_enabled; + bool broken_irq_mode; + bool broken_irq_check; + void (*int_en)(struct octeon_i2c *); + void (*int_dis)(struct octeon_i2c *); + void (*hlc_int_en)(struct octeon_i2c *); + void (*hlc_int_dis)(struct octeon_i2c *); + atomic_t int_en_cnt; + atomic_t hlc_int_en_cnt; +}; + +static inline void writeqflush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + +/** + * octeon_i2c_write_sw - write an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * @data: Value to be written + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static inline void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u32 data) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); +} + +/** + * octeon_i2c_read_sw64 - read an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static inline u64 octeon_i2c_read_sw64(struct octeon_i2c *i2c, u64 eop_reg) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI); + } while ((tmp & SW_TWSI_V) != 0); + + return tmp; +} + +/** + * octeon_i2c_read_sw - read lower bits of an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static inline u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +{ + return octeon_i2c_read_sw64(i2c, eop_reg) & 0xFF; +} + +/** + * octeon_i2c_write_int - write the TWSI_INT register + * @i2c: The struct octeon_i2c + * @data: Value to be written + */ +static inline void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) +{ + writeqflush(data, i2c->twsi_base + TWSI_INT); +} + +static inline u64 octeon_i2c_read_ctl(struct octeon_i2c *i2c) +{ + return octeon_i2c_read_sw64(i2c, SW_TWSI_EOP_TWSI_CTL); +} + +static inline int octeon_i2c_test_iflg(struct octeon_i2c *i2c) +{ + return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; +} + +/* Prototypes */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id); +int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c); +void octeon_i2c_set_clock(struct octeon_i2c *i2c); diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c index 444e8ed..68cb75d 100644 --- a/drivers/i2c/busses/i2c-octeon-core.c +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -24,182 +24,10 @@ #include #include +#include "i2c-cavium.h" #define DRV_NAME "i2c-octeon" -/* Register offsets */ -#define SW_TWSI 0x00 -#define TWSI_INT 0x10 -#define SW_TWSI_EXT 0x18 - -/* Controller command patterns */ -#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ -#define SW_TWSI_EIA BIT_ULL(61) /* Extended internal address */ -#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ -#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ -#define SW_TWSI_SIZE_SHIFT 52 -#define SW_TWSI_ADDR_SHIFT 40 -#define SW_TWSI_IA_SHIFT 32 /* Internal address */ - -/* Controller opcode word (bits 60:57) */ -#define SW_TWSI_OP_SHIFT 57 -#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_7_IA (1ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ - -/* Controller extended opcode word (bits 34:32) */ -#define SW_TWSI_EOP_SHIFT 32 -#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_CLKCTL (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) - -/* Controller command and status bits */ -#define TWSI_CTL_CE 0x80 /* High level controller enable */ -#define TWSI_CTL_ENAB 0x40 /* Bus enable */ -#define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */ -#define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */ -#define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */ -#define TWSI_CTL_AAK 0x04 /* Assert ACK */ - -/* Some status values */ -#define STAT_ERROR 0x00 -#define STAT_START 0x08 -#define STAT_RSTART 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 */ -#define TWSI_INT_ST_INT BIT_ULL(0) -#define TWSI_INT_TS_INT BIT_ULL(1) -#define TWSI_INT_CORE_INT BIT_ULL(2) -#define TWSI_INT_ST_EN BIT_ULL(4) -#define TWSI_INT_TS_EN BIT_ULL(5) -#define TWSI_INT_CORE_EN BIT_ULL(6) -#define TWSI_INT_SDA_OVR BIT_ULL(8) -#define TWSI_INT_SCL_OVR BIT_ULL(9) -#define TWSI_INT_SDA BIT_ULL(10) -#define TWSI_INT_SCL BIT_ULL(11) - -struct octeon_i2c { - wait_queue_head_t queue; - struct i2c_adapter adap; - int irq; - int hlc_irq; /* For cn7890 only */ - u32 twsi_freq; - int sys_freq; - void __iomem *twsi_base; - struct device *dev; - bool hlc_enabled; - bool broken_irq_mode; - bool broken_irq_check; - void (*int_en)(struct octeon_i2c *); - void (*int_dis)(struct octeon_i2c *); - void (*hlc_int_en)(struct octeon_i2c *); - void (*hlc_int_dis)(struct octeon_i2c *); - atomic_t int_en_cnt; - atomic_t hlc_int_en_cnt; -}; - -static int reset_how; - -static void writeqflush(u64 val, void __iomem *addr) -{ - __raw_writeq(val, addr); - __raw_readq(addr); /* wait for write to land */ -} - -/** - * octeon_i2c_write_sw - write an I2C core register - * @i2c: The struct octeon_i2c - * @eop_reg: Register selector - * @data: Value to be written - * - * The I2C core registers are accessed indirectly via the SW_TWSI CSR. - */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u32 data) -{ - u64 tmp; - - __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI); - do { - tmp = __raw_readq(i2c->twsi_base + SW_TWSI); - } while ((tmp & SW_TWSI_V) != 0); -} - -/** - * octeon_i2c_read_sw64 - read an I2C core register - * @i2c: The struct octeon_i2c - * @eop_reg: Register selector - * - * Returns the data. - * - * The I2C core registers are accessed indirectly via the SW_TWSI CSR. - */ -static u64 octeon_i2c_read_sw64(struct octeon_i2c *i2c, u64 eop_reg) -{ - u64 tmp; - - __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI); - do { - tmp = __raw_readq(i2c->twsi_base + SW_TWSI); - } while ((tmp & SW_TWSI_V) != 0); - - return tmp; -} - -/** - * octeon_i2c_read_sw - read lower bits of an I2C core register - * @i2c: The struct octeon_i2c - * @eop_reg: Register selector - * - * Returns the data. - * - * The I2C core registers are accessed indirectly via the SW_TWSI CSR. - */ -static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) -{ - return octeon_i2c_read_sw64(i2c, eop_reg) & 0xFF; -} - -/** - * octeon_i2c_write_int - write the TWSI_INT register - * @i2c: The struct octeon_i2c - * @data: Value to be written - */ -static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) -{ - writeqflush(data, i2c->twsi_base + TWSI_INT); -} - /** * octeon_i2c_int_enable - enable the CORE interrupt * @i2c: The struct octeon_i2c @@ -219,15 +47,6 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } -static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) -{ - if (!i2c->hlc_enabled) - return; - - i2c->hlc_enabled = false; - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); -} - /** * octeon_i2c_int_enable78 - enable the CORE interrupt * @i2c: The struct octeon_i2c @@ -280,49 +99,6 @@ static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) __octeon_i2c_irq_disable(&i2c->hlc_int_en_cnt, i2c->hlc_irq); } -/** - * octeon_i2c_unblock - unblock the bus - * @i2c: The struct octeon_i2c - * - * If there was a reset while a device was driving 0 to bus, bus is blocked. - * We toggle it free manually by some clock cycles and send a stop. - */ -static void octeon_i2c_unblock(struct octeon_i2c *i2c) -{ - int state, i; - - octeon_i2c_disable_hlc(i2c); - dev_dbg(i2c->dev, "%s\n", __func__); - - /* cycle 8+1 clocks with SDA high */ - for (i = 0; i < 9; i++) { - octeon_i2c_write_int(i2c, 0); - udelay(5); - state = __raw_readq(i2c->twsi_base + TWSI_INT); - if (state & (TWSI_INT_SDA | TWSI_INT_SCL)) - break; - octeon_i2c_write_int(i2c, TWSI_INT_SCL_OVR); - udelay(5); - } - /* hand-crank a STOP */ - octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR); - udelay(5); - octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR); - udelay(5); - octeon_i2c_write_int(i2c, 0); -} - -/* interrupt service routine */ -static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) -{ - struct octeon_i2c *i2c = dev_id; - - i2c->int_dis(i2c); - wake_up(&i2c->queue); - - return IRQ_HANDLED; -} - /* HLC interrupt service routine */ static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) { @@ -334,768 +110,11 @@ static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) return IRQ_HANDLED; } -static u64 octeon_i2c_read_ctl(struct octeon_i2c *i2c) -{ - return octeon_i2c_read_sw64(i2c, SW_TWSI_EOP_TWSI_CTL); -} - -static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) -{ - return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; -} - -#define I2C_OCTEON_IFLG_WAIT 80 /* microseconds */ - -/* - * Wait-helper which addresses the delayed-IFLAG problem by re-polling for - * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, - * but none found. Skip this re-poll on the first (non-wakeup) call. - */ -static int poll_iflg(struct octeon_i2c *i2c, int *first_p) -{ - int iflg = octeon_i2c_test_iflg(i2c); - - if (iflg) - return 1; - if (*first_p) - *first_p = 0; - else { - usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); - iflg = octeon_i2c_test_iflg(i2c); - } - return iflg; -} - -/** - * octeon_i2c_wait - wait for the IFLG to be set - * @i2c: The struct octeon_i2c - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_wait(struct octeon_i2c *i2c) -{ - long time_left; - int first = 1; - - if (i2c->broken_irq_mode) { - /* - * Some chip revisions seem to not assert the irq in - * the interrupt controller. So we must poll for the - * IFLG change. - */ - u64 end = get_jiffies_64() + i2c->adap.timeout; - - while (!octeon_i2c_test_iflg(i2c) && - time_before64(get_jiffies_64(), end)) - udelay(50); - - return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; - } - - i2c->int_en(i2c); - time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, &first), - i2c->adap.timeout); - i2c->int_dis(i2c); - - if (time_left <= 0 && i2c->broken_irq_check && - octeon_i2c_test_iflg(i2c)) { - dev_err(i2c->dev, - "broken irq connection detected, switching to polling mode.\n"); - i2c->broken_irq_mode = true; - return 0; - } - if (!time_left) { - dev_dbg(i2c->dev, "%s: timeout\n", __func__); - return -ETIMEDOUT; - } - - return 0; -} - -/* - * Cleanup low-level state & enable high-level. - * Returns -EAGAIN if low-level state could not be cleaned. - */ -static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) -{ - int try = 0, ret = 0; - u64 val; - - if (i2c->hlc_enabled) - return 0; - i2c->hlc_enabled = true; - - while (1) { - val = octeon_i2c_read_ctl(i2c) & (TWSI_CTL_STA | TWSI_CTL_STP); - if (!val) - break; - - /* clear IFLG event */ - if (val & TWSI_CTL_IFLG) - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); - - if (try++ > 100) { - pr_err("%s: giving up\n", __func__); - ret = -EAGAIN; - break; - } - - /* spin until any start/stop has finished */ - udelay(10); - } - - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_CE | TWSI_CTL_AAK | TWSI_CTL_ENAB); - return ret; -} - -static int octeon_i2c_lost_arb(u8 code, int final_read) -{ - switch (code) { - /* 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 -EIO; - - /* 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 -EIO; - - /* ACK allowed on pre-terminal bytes only */ - case STAT_RXDATA_ACK: - if (!final_read) - return 0; - return -EAGAIN; - - /* NAK allowed on terminal byte only */ - case STAT_RXDATA_NAK: - if (final_read) - return 0; - return -EAGAIN; - case STAT_TXDATA_NAK: - case STAT_TXADDR_NAK: - case STAT_RXADDR_NAK: - case STAT_AD2W_NAK: - return -EAGAIN; - } - return 0; -} - -static int check_arb(struct octeon_i2c *i2c, int final_read) -{ - return octeon_i2c_lost_arb(octeon_i2c_read_sw(i2c, - SW_TWSI_EOP_TWSI_STAT), final_read); -} - -/* send STOP to the bus */ -static void octeon_i2c_stop(struct octeon_i2c *i2c) -{ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STP); -} - -static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) -{ - u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); - - return (val & SW_TWSI_V) == 0; -} - static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) { octeon_i2c_write_int(i2c, TWSI_INT_ST_EN); } -static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) -{ - /* clear ST/TS events, listen for neither */ - octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); -} - -/** - * octeon_i2c_hlc_wait - wait for an HLC operation to complete - * @i2c: The struct octeon_i2c - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) -{ - int time_left; - - if (i2c->broken_irq_mode) { - /* - * Some cn38xx boards did not assert the irq in - * the interrupt controller. So we must poll for the - * IFLG change. - */ - u64 end = get_jiffies_64() + i2c->adap.timeout; - - while (!octeon_i2c_hlc_test_ready(i2c) && - time_before64(get_jiffies_64(), end)) - udelay(50); - - return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; - } - - i2c->hlc_int_en(i2c); - time_left = wait_event_interruptible_timeout(i2c->queue, - octeon_i2c_hlc_test_ready(i2c), - i2c->adap.timeout); - i2c->hlc_int_dis(i2c); - if (!time_left) - octeon_i2c_hlc_int_clear(i2c); - - if (time_left <= 0 && i2c->broken_irq_check && - octeon_i2c_hlc_test_ready(i2c)) { - dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); - i2c->broken_irq_mode = true; - return 0; - } - - if (!time_left) { - dev_dbg(i2c->dev, "%s: timeout\n", __func__); - return -ETIMEDOUT; - } - if (time_left < 0) { - dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); - return time_left; - } - return 0; -} - -/* high-level-controller pure read of up to 8 bytes */ -static int octeon_i2c_simple_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) -{ - int i, j, ret = 0; - u64 cmd; - - octeon_i2c_enable_hlc(i2c); - cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; - /* SIZE */ - cmd |= (u64) (msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; - /* A */ - cmd |= (u64) (msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10; - else - cmd |= SW_TWSI_OP_7; - - octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); - - ret = octeon_i2c_hlc_wait(i2c); - if (ret) - goto err; - - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); - if ((cmd & SW_TWSI_R) == 0) - return -EAGAIN; - - for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) - msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; - - if (msgs[0].len > 4) { - cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); - for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) - msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; - } - -err: - return ret; -} - -/* high-level-controller pure write of up to 8 bytes */ -static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) -{ - int i, j, ret = 0; - u64 cmd; - - octeon_i2c_enable_hlc(i2c); - octeon_i2c_hlc_int_clear(i2c); - - ret = check_arb(i2c, false); - if (ret) - goto err; - - cmd = SW_TWSI_V | SW_TWSI_SOVR; - /* SIZE */ - cmd |= (u64) (msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; - /* A */ - cmd |= (u64) (msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10; - else - cmd |= SW_TWSI_OP_7; - - for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) - cmd |= (u64)msgs[0].buf[j] << (8 * i); - - if (msgs[0].len > 4) { - u64 ext = 0; - - for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) - ext |= (u64) msgs[0].buf[j] << (8 * i); - writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); - } - - writeqflush(cmd, i2c->twsi_base + SW_TWSI); - - ret = octeon_i2c_hlc_wait(i2c); - if (ret) - goto err; - - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); - if ((cmd & SW_TWSI_R) == 0) - return -EAGAIN; - - ret = check_arb(i2c, false); - -err: - return ret; -} - -/* high-level-controller composite write+read, msg0=addr, msg1=data */ -static int octeon_i2c_ia_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) -{ - int i, j, ret = 0; - u64 cmd; - - octeon_i2c_enable_hlc(i2c); - - cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; - /* SIZE */ - cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; - /* A */ - cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10_IA; - else - cmd |= SW_TWSI_OP_7_IA; - - if (msgs[0].len == 2) { - u64 ext = 0; - - cmd |= SW_TWSI_EIA; - ext = (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - cmd |= (u64) msgs[0].buf[1] << SW_TWSI_IA_SHIFT; - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); - } else - cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - - octeon_i2c_hlc_int_clear(i2c); - writeqflush(cmd, i2c->twsi_base + SW_TWSI); - - ret = octeon_i2c_hlc_wait(i2c); - if (ret) - goto err; - - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); - if ((cmd & SW_TWSI_R) == 0) - return -EAGAIN; - - for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) - msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; - - if (msgs[1].len > 4) { - cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); - for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) - msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; - } - -err: - return ret; -} - -/* high-level-controller composite write+write, m[0]len<=2, m[1]len<=8 */ -static int octeon_i2c_ia_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) -{ - bool set_ext = false; - int i, j, ret = 0; - u64 cmd, ext = 0; - - octeon_i2c_enable_hlc(i2c); - - cmd = SW_TWSI_V | SW_TWSI_SOVR; - /* SIZE */ - cmd |= (u64) (msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; - /* A */ - cmd |= (u64) (msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10_IA; - else - cmd |= SW_TWSI_OP_7_IA; - - if (msgs[0].len == 2) { - cmd |= SW_TWSI_EIA; - ext |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - set_ext = true; - cmd |= (u64) msgs[0].buf[1] << SW_TWSI_IA_SHIFT; - } else - cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - - for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) - cmd |= (u64) msgs[1].buf[j] << (8 * i); - - if (msgs[1].len > 4) { - for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) - ext |= (u64)msgs[1].buf[j] << (8 * i); - set_ext = true; - } - if (set_ext) - writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); - - octeon_i2c_hlc_int_clear(i2c); - writeqflush(cmd, i2c->twsi_base + SW_TWSI); - - ret = octeon_i2c_hlc_wait(i2c); - if (ret) - goto err; - - cmd = octeon_i2c_read_sw64(i2c, SW_TWSI_EOP_TWSI_STAT); - if ((cmd & SW_TWSI_R) == 0) - return -EAGAIN; - ret = octeon_i2c_lost_arb(cmd, false); - -err: - return ret; -} - -/* calculate and set clock divisors */ -static void octeon_i2c_set_clock(struct octeon_i2c *i2c) -{ - int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; - int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; - - for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { - /* - * An mdiv value of less than 2 seems to not work well - * with ds1337 RTCs, so we constrain it to larger values. - */ - for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { - /* - * For given ndiv and mdiv values check the - * two closest thp values. - */ - tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; - tclk *= (1 << ndiv_idx); - thp_base = (i2c->sys_freq / (tclk * 2)) - 1; - - for (inc = 0; inc <= 1; inc++) { - thp_idx = thp_base + inc; - if (thp_idx < 5 || thp_idx > 0xff) - continue; - - foscl = i2c->sys_freq / (2 * (thp_idx + 1)); - foscl = foscl / (1 << ndiv_idx); - foscl = foscl / (mdiv_idx + 1) / 10; - diff = abs(foscl - i2c->twsi_freq); - if (diff < delta_hz) { - delta_hz = diff; - thp = thp_idx; - mdiv = mdiv_idx; - ndiv = ndiv_idx; - } - } - } - } - octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); -} - -static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) -{ - u8 status = 0; - int tries; - - /* reset controller */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); - - for (tries = 10; tries && status != STAT_IDLE; tries--) { - udelay(1); - status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); - } - - if (status != STAT_IDLE) { - dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", - __func__, status); - return -EIO; - } - - /* toggle twice to force both teardowns */ - octeon_i2c_enable_hlc(i2c); - octeon_i2c_disable_hlc(i2c); - return 0; -} - -/* - * TWSI state seems stuck. Not sure if it's TWSI-engine state or something - * else on bus. The initial _stop() is always harmless, it just resets state - * machine, does not _transmit_ STOP unless engine was active. - */ -static int start_unstick(struct octeon_i2c *i2c) -{ - octeon_i2c_stop(i2c); - - /* - * Response is escalated over successive calls, - * as EAGAIN provokes retries from i2c/core. - */ - switch (reset_how++ % 4) { - case 0: - /* just the stop above */ - break; - case 1: - /* - * Controller refused to send start flag. May be a - * client is holding SDA low? Let's try to free it. - */ - octeon_i2c_unblock(i2c); - break; - case 2: - /* re-init our TWSI hardware */ - octeon_i2c_init_lowlevel(i2c); - break; - default: - /* retry in caller */ - reset_how = 0; - return -EAGAIN; - } - return 0; -} - -/** - * octeon_i2c_start - send START to the bus - * @i2c: The struct octeon_i2c - * @first: first msg in combined operation? - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_start(struct octeon_i2c *i2c, int first) -{ - int result; - u8 data; - - octeon_i2c_disable_hlc(i2c); - - while (1) { - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STA); - - result = octeon_i2c_wait(i2c); - data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); - - switch (data) { - case STAT_START: - case STAT_RSTART: - if (!first) - return -EAGAIN; - reset_how = 0; - return 0; - case STAT_RXADDR_ACK: - if (first) - return -EAGAIN; - return start_unstick(i2c); - /* - * case STAT_IDLE: - * case STAT_ERROR: - */ - default: - if (!first) - return -EAGAIN; - start_unstick(i2c); - } - } - return 0; -} - -/** - * octeon_i2c_read - receive data from the bus via low-level controller - * @i2c: The struct octeon_i2c - * @target: Target address - * @data: Pointer to the location to store the data - * @rlength: Length of the data - * @phase: which phase of a combined operation. - * @recv_len: flag for length byte - * - * The address is sent over the bus, then the data is read. - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, - u16 *rlength, bool first, bool last, bool recv_len) -{ - u8 ctl = TWSI_CTL_ENAB | TWSI_CTL_AAK; - int i, result, length = *rlength; - u8 tmp; - - if (length < 1) - return -EINVAL; - - result = octeon_i2c_start(i2c, first); - if (result) - return result; - - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target << 1) | 1); - - for (i = 0; i < length; ) { - tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); - result = octeon_i2c_lost_arb(tmp, !(ctl & TWSI_CTL_AAK)); - if (result) - return result; - - switch (tmp) { - case STAT_RXDATA_ACK: - case STAT_RXDATA_NAK: - data[i++] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); - } - - - /* NAK last recv'd byte, as a no-more-please */ - if (last && i == length - 1) - ctl &= ~TWSI_CTL_AAK; - - /* clr iflg to allow next event */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, ctl); - result = octeon_i2c_wait(i2c); - if (result) - return result; - if (recv_len && i == 0) { - if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { - dev_err(i2c->dev, - "%s: read len > I2C_SMBUS_BLOCK_MAX %d\n", - __func__, data[i]); - return -EPROTO; - } - length += data[i]; - } - } - *rlength = length; - return 0; -} - -/** - * octeon_i2c_write - send data to the bus via low-level controller - * @i2c: The struct octeon_i2c - * @target: Target address - * @data: Pointer to the data to be sent - * @length: Length of the data - * @last: is last msg in combined operation? - * - * The address is sent over the bus, then the data. - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_write(struct octeon_i2c *i2c, int target, - const u8 *data, int length, int first, int last) -{ - int i, result; - - result = octeon_i2c_start(i2c, first); - if (result) - return result; - - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - - for (i = 0; i < length; i++) { - result = check_arb(i2c, false); - if (result) - return result; - - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - result = check_arb(i2c, false); - if (result) - return result; - } - - return 0; -} - -/** - * octeon_i2c_xfer - The driver's master_xfer function - * @adap: Pointer to the i2c_adapter structure - * @msgs: Pointer to the messages to be processed - * @num: Length of the MSGS array - * - * Returns the number of messages processed, or a negative errno on failure. - */ -static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, - int num) -{ - struct octeon_i2c *i2c = i2c_get_adapdata(adap); - int i, ret = 0; - - if (num == 1) { - if (msgs[0].len > 0 && msgs[0].len <= 8) { - if (msgs[0].flags & I2C_M_RD) - ret = octeon_i2c_simple_read(i2c, msgs); - else - ret = octeon_i2c_simple_write(i2c, msgs); - goto out; - } - } else if (num == 2) { - if ((msgs[0].flags & I2C_M_RD) == 0 && - (msgs[1].flags & I2C_M_RECV_LEN) == 0 && - msgs[0].len > 0 && msgs[0].len <= 2 && - msgs[1].len > 0 && msgs[1].len <= 8 && - msgs[0].addr == msgs[1].addr) { - if (msgs[1].flags & I2C_M_RD) - ret = octeon_i2c_ia_read(i2c, msgs); - else - ret = octeon_i2c_ia_write(i2c, msgs); - goto out; - } - } - - for (i = 0; ret == 0 && i < num; i++) { - struct i2c_msg *pmsg = &msgs[i]; - bool last = (i == (num - 1)); - - dev_dbg(i2c->dev, - "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n", - pmsg->flags & I2C_M_RD ? "read" : "write", - pmsg->len, pmsg->addr, i + 1, num); - if (pmsg->flags & I2C_M_RD) - ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, - &pmsg->len, !i, last, - pmsg->flags & I2C_M_RECV_LEN); - else - ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, - pmsg->len, !i, last); - } - octeon_i2c_stop(i2c); -out: - return (ret != 0) ? ret : num; -} - static u32 octeon_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |