* [patch v5 repost 1/1] i2c: add master driver for mellanox systems
From: vadimp @ 2016-11-16 7:08 UTC (permalink / raw)
To: wsa; +Cc: linux-i2c, linux-kernel, jiri, Vadim Pasternak, Michael Shych
From: Vadim Pasternak <vadimp@mellanox.com>
Device driver for Mellanox I2C controller logic, implemented in Lattice
CPLD device.
Device supports:
- Master mode
- One physical bus
- Polling mode
The Kconfig currently controlling compilation of this code is:
drivers/i2c/busses/Kconfig:config I2C_MLXCPLD
Signed-off-by: Michael Shych <michaelsh@mellanox.com>
Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Vladimir Zapolskiy <vz@mleia.com>
---
v4->v5:
Comments pointed out by Vladimir:
- Remove "default n" from Kconfig;
- Fix the comments for timeout and pool time;
- Optimize error flow in mlxcpld_i2c_probe;
v3->v4:
Comments pointed out by Vladimir:
- Set default to no in Kconfig;
- Make mlxcpld_i2c_plat_dev static and add empty line before the
declaration;
- In function mlxcpld_i2c_invalid_len remove (msg->len < 0), since len is
unsigned;
- Remove unused symbol mlxcpld_i2c_plat_dev;
- Remove extra spaces in comments to mlxcpld_i2c_check_msg_params;
- Remove unnecessary round braces in mlxcpld_i2c_set_transf_data;
- Remove the assignment of 'i' variable in mlxcpld_i2c_wait_for_tc;
- Add extra line in mlxcpld_i2c_xfer;
- Move assignment of the adapter's fields retries and nr inside
mlxcpld_i2c_adapter declaration;
v2->v3:
Comments pointed out by Vladimir:
- Use tab symbol as indentation in Kconfig
- Add the Kconfig section preserving the alphabetical order - added
within "Other I2C/SMBus bus drivers" after I2C_ELEKTOR (but after this
sections others are not follow alphabetical);
- Change license to dual;
- Replace ADRR with ADDR in macros;
- Remove unused macros: MLXCPLD_LPCI2C_LPF_DFLT,
MLXCPLD_LPCI2C_HALF_CYC_100, MLXCPLD_LPCI2C_I2C_HOLD_100,
MLXCPLD_LPCI2C_HALF_CYC_REG, MLXCPLD_LPCI2C_I2C_HOLD_REG;
- Fix checkpatch warnings (**/ and the end of comment);
- Add empty line before structures mlxcpld_i2c_regs,
mlxcpld_i2c_curr_transf, mlxcpld_i2c_priv;
- Remove unused structure mlxcpld_i2c_regs;
- Remove from mlxcpld_i2c_priv the next fields:
retr_num, poll_time, block_sz, xfer_to; use instead macros
respectively: MLXCPLD_I2C_RETR_NUM, MLXCPLD_I2C_POLL_TIME,
MLXCPLD_I2C_DATA_REG_SZ, MLXCPLD_I2C_XFER_TO;
- In mlxcpld_i2c_invalid_len remove unnecessary else;
- Optimize mlxcpld_i2c_set_transf_data;
- mlxcpld_i2c_reset - add empty lines after/before mutex
lock/unlock;
- mlxcpld_i2c_wait_for_free - cover case timeout is equal
MLXCPLD_I2C_XFER_TO;
- mlxcpld_i2c_wait_for_tc:
- Do not assign err in declaration (also err is removed);
- Insert empty line before case MLXCPLD_LPCI2C_ACK_IND;
- inside case MLXCPLD_LPCI2C_ACK_IND - avoid unnecessary
indentation;
- Remove case MLXCPLD_LPCI2C_ERR_IND and remove this macro;
- Add empty lines in mlxcpld_i2c_xfer before/after mutex_lock/
mutex_unlock;
- In mlxcpld_i2c_probe add emtpy line after platform_set_drvdata;
- Replace platfrom handle pdev in mlxcpld_i2c_priv with the pointer
to the structure device;
- Place assignment of base_addr near the others;
- Enclose e-mail with <>;
Fixes added by Vadim:
- Change structure description format according to
Documentation/kernel-documentation.rst guideline;
- mlxcpld_i2c_wait_for_tc: return error if status reaches default case;
v1->v2
Fixes added by Vadim:
- Put new record in Makefile in alphabetic order;
- Remove http://www.mellanox.com from MAINTAINERS record;
---
Documentation/i2c/busses/i2c-mlxcpld | 47 +++
MAINTAINERS | 8 +
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-mlxcpld.c | 551 +++++++++++++++++++++++++++++++++++
5 files changed, 618 insertions(+)
create mode 100644 Documentation/i2c/busses/i2c-mlxcpld
create mode 100644 drivers/i2c/busses/i2c-mlxcpld.c
diff --git a/Documentation/i2c/busses/i2c-mlxcpld b/Documentation/i2c/busses/i2c-mlxcpld
new file mode 100644
index 0000000..0f8678a
--- /dev/null
+++ b/Documentation/i2c/busses/i2c-mlxcpld
@@ -0,0 +1,47 @@
+Driver i2c-mlxcpld
+
+Author: Michael Shych <michaelsh@mellanox.com>
+
+This is a for Mellanox I2C controller logic, implemented in Lattice CPLD
+device.
+Device supports:
+ - Master mode.
+ - One physical bus.
+ - Polling mode.
+
+This controller is equipped within the next Mellanox systems:
+"msx6710", "msx6720", "msb7700", "msn2700", "msx1410", "msn2410", "msb7800",
+"msn2740", "msn2100".
+
+The next transaction types are supported:
+ - Receive Byte/Block.
+ - Send Byte/Block.
+ - Read Byte/Block.
+ - Write Byte/Block.
+
+Registers:
+CTRL 0x1 - control reg.
+ Resets all the registers.
+HALF_CYC 0x4 - cycle reg.
+ Configure the width of I2C SCL half clock cycle (in 4 LPC_CLK
+ units).
+I2C_HOLD 0x5 - hold reg.
+ OE (output enable) is delayed by value set to this register
+ (in LPC_CLK units)
+CMD 0x6 - command reg.
+ Bit 7(lsb), 0 = write, 1 = read.
+ Bits [6:0] - the 7bit Address of the I2C device.
+ It should be written last as it triggers an I2C transaction.
+NUM_DATA 0x7 - data size reg.
+ Number of address bytes to write in read transaction
+NUM_ADDR 0x8 - address reg.
+ Number of address bytes to write in read transaction.
+STATUS 0x9 - status reg.
+ Bit 0 - transaction is completed.
+ Bit 4 - ACK/NACK.
+DATAx 0xa - 0x54 - 68 bytes data buffer regs.
+ For write transaction address is specified in four first bytes
+ (DATA1 - DATA4), data starting from DATA4.
+ For read transactions address is send in separate transaction and
+ specified in four first bytes (DATA0 - DATA3). Data is reading
+ starting from DATA0.
diff --git a/MAINTAINERS b/MAINTAINERS
index 411e3b8..26d05f8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7881,6 +7881,14 @@ W: http://www.mellanox.com
Q: http://patchwork.ozlabs.org/project/netdev/list/
F: drivers/net/ethernet/mellanox/mlxsw/
+MELLANOX MLXCPLD I2C DRIVER
+M: Vadim Pasternak <vadimp@mellanox.com>
+M: Michael Shych <michaelsh@mellanox.com>
+L: linux-i2c@vger.kernel.org
+S: Supported
+F: drivers/i2c/busses/i2c-mlxcpld.c
+F: Documentation/i2c/busses/i2c-mlxcpld
+
MELLANOX MLXCPLD LED DRIVER
M: Vadim Pasternak <vadimp@mellanox.com>
L: linux-leds@vger.kernel.org
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d252276..6399cea 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1150,6 +1150,17 @@ config I2C_ELEKTOR
This support is also available as a module. If so, the module
will be called i2c-elektor.
+config I2C_MLXCPLD
+ tristate "Mellanox I2C driver"
+ depends on X86_64
+ help
+ This exposes the Mellanox platform I2C busses to the linux I2C layer
+ for X86 based systems.
+ Controller is implemented as CPLD logic.
+
+ This driver can also be built as a module. If so, the module will be
+ called as i2c-mlxcpld.
+
config I2C_PCA_ISA
tristate "PCA9564/PCA9665 on an ISA bus"
depends on ISA
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 29764cc..645bf08 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -116,6 +116,7 @@ obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
+obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c
new file mode 100644
index 0000000..f7a6f3a
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mlxcpld.c
@@ -0,0 +1,551 @@
+/*
+ * drivers/i2c/busses/i2c-mlxcpld.c
+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+/* General defines */
+#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000
+#define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld"
+#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD)
+#define MLXCPLD_I2C_BUS_NUM 1
+#define MLXCPLD_I2C_DATA_REG_SZ 36
+#define MLXCPLD_I2C_MAX_ADDR_LEN 4
+#define MLXCPLD_I2C_RETR_NUM 2
+#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
+#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */
+
+/* LPC I2C registers */
+#define MLXCPLD_LPCI2C_LPF_REG 0x0
+#define MLXCPLD_LPCI2C_CTRL_REG 0x1
+#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4
+#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5
+#define MLXCPLD_LPCI2C_CMD_REG 0x6
+#define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7
+#define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8
+#define MLXCPLD_LPCI2C_STATUS_REG 0x9
+#define MLXCPLD_LPCI2C_DATA_REG 0xa
+
+/* LPC I2C masks and parametres */
+#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1
+#define MLXCPLD_LPCI2C_TRANS_END 0x1
+#define MLXCPLD_LPCI2C_STATUS_NACK 0x10
+#define MLXCPLD_LPCI2C_NO_IND 0
+#define MLXCPLD_LPCI2C_ACK_IND 1
+#define MLXCPLD_LPCI2C_NACK_IND 2
+
+/**
+ * struct mlxcpld_i2c_curr_transf - current transaction parameters:
+ * @cmd: command;
+ * @addr_width: address width;
+ * @data_len: data length;
+ * @cmd: command register;
+ * @msg_num: message number;
+ * @msg: pointer to message buffer;
+ */
+
+struct mlxcpld_i2c_curr_transf {
+ u8 cmd;
+ u8 addr_width;
+ u8 data_len;
+ u8 msg_num;
+ struct i2c_msg *msg;
+};
+
+/**
+ * struct mlxcpld_i2c_priv - private controller data:
+ * @lpc_gen_dec_reg: register space;
+ * @adap: i2c adapter;
+ * @dev_id: device id;
+ * @base_addr: base IO address;
+ * @lock: bus access lock;
+ * @xfer: current i2c transfer block;
+ * @dev: device handle;
+ */
+
+struct mlxcpld_i2c_priv {
+ struct i2c_adapter adap;
+ u16 dev_id;
+ u16 base_addr;
+ struct mutex lock;
+ struct mlxcpld_i2c_curr_transf xfer;
+ struct device *dev;
+};
+
+static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr)
+{
+ int i, nbyte, ndword;
+
+ nbyte = len % 4;
+ ndword = len / 4;
+ for (i = 0; i < ndword; i++)
+ outl(*((u32 *)data + i), addr + i * 4);
+ ndword *= 4;
+ addr += ndword;
+ data += ndword;
+ for (i = 0; i < nbyte; i++)
+ outb(*(data + i), addr + i);
+}
+
+static void mlxcpld_i2c_lpc_read_buf(u8 *data, u8 len, u32 addr)
+{
+ int i, nbyte, ndword;
+
+ nbyte = len % 4;
+ ndword = len / 4;
+ for (i = 0; i < ndword; i++)
+ *((u32 *)data + i) = inl(addr + i * 4);
+ ndword *= 4;
+ addr += ndword;
+ data += ndword;
+ for (i = 0; i < nbyte; i++)
+ *(data + i) = inb(addr + i);
+}
+
+static void mlxcpld_i2c_read_comm(struct mlxcpld_i2c_priv *priv, u8 offs,
+ u8 *data, u8 datalen)
+{
+ u32 addr = priv->base_addr + offs;
+
+ switch (datalen) {
+ case 1:
+ *(data) = inb(addr);
+ break;
+ case 2:
+ *((u16 *)data) = inw(addr);
+ break;
+ case 3:
+ *((u16 *)data) = inw(addr);
+ *(data + 2) = inb(addr + 2);
+ break;
+ case 4:
+ *((u32 *)data) = inl(addr);
+ break;
+ default:
+ mlxcpld_i2c_lpc_read_buf(data, datalen, addr);
+ break;
+ }
+}
+
+static void mlxcpld_i2c_write_comm(struct mlxcpld_i2c_priv *priv, u8 offs,
+ u8 *data, u8 datalen)
+{
+ u32 addr = priv->base_addr + offs;
+
+ switch (datalen) {
+ case 1:
+ outb(*(data), addr);
+ break;
+ case 2:
+ outw(*((u16 *)data), addr);
+ break;
+ case 3:
+ outw(*((u16 *)data), addr);
+ outb(*(data + 2), addr + 2);
+ break;
+ case 4:
+ outl(*((u32 *)data), addr);
+ break;
+ default:
+ mlxcpld_i2c_lpc_write_buf(data, datalen, addr);
+ break;
+ }
+}
+
+/* Check validity of current i2c message and all transfer.
+ * Calculate also coomon length of all i2c messages in transfer.
+ */
+static int mlxcpld_i2c_invalid_len(struct mlxcpld_i2c_priv *priv,
+ const struct i2c_msg *msg, u8 *comm_len)
+{
+ u8 max_len = msg->flags == I2C_M_RD ? MLXCPLD_I2C_DATA_REG_SZ -
+ MLXCPLD_I2C_MAX_ADDR_LEN : MLXCPLD_I2C_DATA_REG_SZ;
+
+ if (msg->len > max_len)
+ return -EINVAL;
+
+ *comm_len += msg->len;
+ if (*comm_len > MLXCPLD_I2C_DATA_REG_SZ)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Check validity of received i2c messages parameters.
+ * Returns 0 if OK, other - in case of invalid parameters
+ * or common length of data that should be passed to CPLD
+ */
+static int mlxcpld_i2c_check_msg_params(struct mlxcpld_i2c_priv *priv,
+ struct i2c_msg *msgs, int num,
+ u8 *comm_len)
+{
+ int i;
+
+ if (!num) {
+ dev_err(priv->dev, "Incorrect 0 num of messages\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(msgs[0].addr > 0x7f)) {
+ dev_err(priv->dev, "Invalid address 0x%03x\n",
+ msgs[0].addr);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num; ++i) {
+ if (unlikely(!msgs[i].buf)) {
+ dev_err(priv->dev, "Invalid buf in msg[%d]\n",
+ i);
+ return -EINVAL;
+ }
+ if (unlikely(msgs[0].addr != msgs[i].addr)) {
+ dev_err(priv->dev, "Invalid addr in msg[%d]\n",
+ i);
+ return -EINVAL;
+ }
+ if (unlikely(mlxcpld_i2c_invalid_len(priv, &msgs[i],
+ comm_len))) {
+ dev_err(priv->dev, "Invalid len %d msg[%d], addr 0x%x, lag %u\n",
+ msgs[i].len, i, msgs[i].addr, msgs[i].flags);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Check if transfer is completed and status of operation.
+ * Returns 0 - transfer completed (both ACK or NACK),
+ * negative - transfer isn't finished.
+ */
+static int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status)
+{
+ u8 val;
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1);
+
+ if (val & MLXCPLD_LPCI2C_TRANS_END) {
+ if (val & MLXCPLD_LPCI2C_STATUS_NACK)
+ /* The slave is unable to accept the data. No such
+ * slave, command not understood, or unable to accept
+ * any more data.
+ */
+ *status = MLXCPLD_LPCI2C_NACK_IND;
+ else
+ *status = MLXCPLD_LPCI2C_ACK_IND;
+ return 0;
+ }
+ *status = MLXCPLD_LPCI2C_NO_IND;
+
+ return -EIO;
+}
+
+static void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv,
+ struct i2c_msg *msgs, int num,
+ u8 comm_len)
+{
+ priv->xfer.msg = msgs;
+ priv->xfer.msg_num = num;
+
+ /*
+ * All upper layers currently are never use transfer with more than
+ * 2 messages. Actually, it's also not so relevant in Mellanox systems
+ * because of HW limitation. Max size of transfer is o more than 20B
+ * in current x86 LPCI2C bridge.
+ */
+ priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD;
+
+ if (priv->xfer.cmd == I2C_M_RD && comm_len != msgs[0].len) {
+ priv->xfer.addr_width = msgs[0].len;
+ priv->xfer.data_len = comm_len - priv->xfer.addr_width;
+ } else {
+ priv->xfer.addr_width = 0;
+ priv->xfer.data_len = comm_len;
+ }
+}
+
+/* Reset CPLD LPCI2C block */
+static void mlxcpld_i2c_reset(struct mlxcpld_i2c_priv *priv)
+{
+ u8 val;
+
+ mutex_lock(&priv->lock);
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1);
+ val &= ~MLXCPLD_LPCI2C_RST_SEL_MASK;
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1);
+
+ mutex_unlock(&priv->lock);
+}
+
+/* Make sure the CPLD is ready to start transmitting.
+ * Return 0 if it is, -EBUSY if it is not.
+ */
+static int mlxcpld_i2c_check_busy(struct mlxcpld_i2c_priv *priv)
+{
+ u8 val;
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1);
+
+ if (val & MLXCPLD_LPCI2C_TRANS_END)
+ return 0;
+
+ return -EIO;
+}
+
+static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv)
+{
+ int timeout = 0;
+
+ do {
+ if (!mlxcpld_i2c_check_busy(priv))
+ break;
+ usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
+ timeout += MLXCPLD_I2C_POLL_TIME;
+ } while (timeout <= MLXCPLD_I2C_XFER_TO);
+
+ if (timeout > MLXCPLD_I2C_XFER_TO)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/*
+ * Wait for master transfer to complete.
+ * It puts current process to sleep until we get interrupt or timeout expires.
+ * Returns the number of transferred or read bytes or error (<0).
+ */
+static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
+{
+ int status, i, timeout = 0;
+ u8 datalen;
+
+ do {
+ usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
+ if (!mlxcpld_i2c_check_status(priv, &status))
+ break;
+ timeout += MLXCPLD_I2C_POLL_TIME;
+ } while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO);
+
+ switch (status) {
+ case MLXCPLD_LPCI2C_NO_IND:
+ return -ETIMEDOUT;
+
+ case MLXCPLD_LPCI2C_ACK_IND:
+ if (priv->xfer.cmd != I2C_M_RD)
+ return (priv->xfer.addr_width + priv->xfer.data_len);
+
+ if (priv->xfer.msg_num == 1)
+ i = 0;
+ else
+ i = 1;
+
+ if (!priv->xfer.msg[i].buf)
+ return -EINVAL;
+
+ /*
+ * Actual read data len will be always the same as
+ * requested len. 0xff (line pull-up) will be returned
+ * if slave has no data to return. Thus don't read
+ * MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD.
+ */
+ datalen = priv->xfer.data_len;
+
+ mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG,
+ priv->xfer.msg[i].buf, datalen);
+
+ return datalen;
+
+ case MLXCPLD_LPCI2C_NACK_IND:
+ return -EAGAIN;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv)
+{
+ int i, len = 0;
+ u8 cmd;
+
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG,
+ &priv->xfer.data_len, 1);
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG,
+ &priv->xfer.addr_width, 1);
+
+ for (i = 0; i < priv->xfer.msg_num; i++) {
+ if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) {
+ /* Don't write to CPLD buffer in read transaction */
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_DATA_REG +
+ len, priv->xfer.msg[i].buf,
+ priv->xfer.msg[i].len);
+ len += priv->xfer.msg[i].len;
+ }
+ }
+
+ /* Set target slave address with command for master transfer.
+ * It should be latest executed function before CPLD transaction.
+ */
+ cmd = (priv->xfer.msg[0].addr << 1) | priv->xfer.cmd;
+ mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CMD_REG, &cmd, 1);
+}
+
+/* Generic lpc-i2c transfer.
+ * Returns the number of processed messages or error (<0).
+ */
+static int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap);
+ u8 comm_len = 0;
+ int err;
+
+ err = mlxcpld_i2c_check_msg_params(priv, msgs, num, &comm_len);
+ if (err) {
+ dev_err(priv->dev, "Incorrect message\n");
+ return err;
+ }
+
+ /* Check bus state */
+ if (mlxcpld_i2c_wait_for_free(priv)) {
+ dev_err(priv->dev, "LPCI2C bridge is busy\n");
+
+ /*
+ * Usually it means something serious has happened.
+ * We can not have unfinished previous transfer
+ * so it doesn't make any sense to try to stop it.
+ * Probably we were not able to recover from the
+ * previous error.
+ * The only reasonable thing - is soft reset.
+ */
+ mlxcpld_i2c_reset(priv);
+ if (mlxcpld_i2c_check_busy(priv)) {
+ dev_err(priv->dev, "LPCI2C bridge is busy after reset\n");
+ return -EIO;
+ }
+ }
+
+ mlxcpld_i2c_set_transf_data(priv, msgs, num, comm_len);
+
+ mutex_lock(&priv->lock);
+
+ /* Do real transfer. Can't fail */
+ mlxcpld_i2c_xfer_msg(priv);
+
+ /* Wait for transaction complete */
+ err = mlxcpld_i2c_wait_for_tc(priv);
+
+ mutex_unlock(&priv->lock);
+
+ return err < 0 ? err : num;
+}
+
+static u32 mlxcpld_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm mlxcpld_i2c_algo = {
+ .master_xfer = mlxcpld_i2c_xfer,
+ .functionality = mlxcpld_i2c_func
+};
+
+static struct i2c_adapter mlxcpld_i2c_adapter = {
+ .owner = THIS_MODULE,
+ .name = "i2c-mlxcpld",
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .algo = &mlxcpld_i2c_algo,
+ .retries = MLXCPLD_I2C_RETR_NUM,
+ .nr = MLXCPLD_I2C_BUS_NUM,
+};
+
+static int mlxcpld_i2c_probe(struct platform_device *pdev)
+{
+ struct mlxcpld_i2c_priv *priv;
+ int err;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct mlxcpld_i2c_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+
+ priv->dev = &pdev->dev;
+
+ /* Register with i2c layer */
+ mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO);
+ priv->adap = mlxcpld_i2c_adapter;
+ priv->adap.dev.parent = &pdev->dev;
+ priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
+ i2c_set_adapdata(&priv->adap, priv);
+
+ err = i2c_add_numbered_adapter(&priv->adap);
+ if (err)
+ mutex_destroy(&priv->lock);
+
+ return err;
+}
+
+static int mlxcpld_i2c_remove(struct platform_device *pdev)
+{
+ struct mlxcpld_i2c_priv *priv = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&priv->adap);
+ mutex_destroy(&priv->lock);
+
+ return 0;
+}
+
+static struct platform_driver mlxcpld_i2c_driver = {
+ .probe = mlxcpld_i2c_probe,
+ .remove = mlxcpld_i2c_remove,
+ .driver = {
+ .name = MLXCPLD_I2C_DEVICE_NAME,
+ },
+};
+
+module_platform_driver(mlxcpld_i2c_driver);
+
+MODULE_AUTHOR("Michael Shych <michaels@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox I2C-CPLD controller driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:i2c-mlxcpld");
--
2.1.4
^ permalink raw reply related
* Kernel Oops while loading I2C SSDT overlay with Designware I2C adapter
From: David Scully @ 2016-11-15 22:01 UTC (permalink / raw)
To: linux-i2c@vger.kernel.org
Hello,
I am using a platform based on the Minnowboard Turbot (Intel Atom SoC) that contains a TLV320AIC3104 connected via I2C. I am also using Linux kernel 4.8.3 which supports SSDT Overlays for augmenting open-ended hardware configurations for ACPI (see Documentation/acpi/ssdt-overlays.txt).
I have written an overlay based on a mix of the DSDT for Lenovo 100s and the example in the ssdt-overlay documentation which binds the audio codec to an i2c interface using the Synopsys Designware I2C adapter. I have attempted to load the driver via the EFI method as well as the configfs method in the documentation, but both cause an immediate kernel oops. The overlay does not cause an oops if I target bus not controlled by the DesignWare adapter.
SSDT Overlay: https://gist.github.com/anonymous/eddfbb85aebcda2d547ee61873fa7117
Lenovo-100s DSDT: http://noraisin.net/lenovo-100s/dsdt.dsl
If I'm interpreting the disassembly correctly, I believe that the problem is that the aic31xx_i2c_probe's i2c_client argument is null for the i2c_adapter pointer.
Can anyone help me track this down?
Thanks,
David Scully
Relevant CONFIGs:
CONFIG_ACPI_CONFIGFS=y
CONFIG_SND_SOC_TLV320AIC31XX=m
uname -a:
Linux intel-corei7-64 4.8.3-yocto-standard #8 SMP PREEMPT Fri Nov 11 12:06:35 PST 2016 x86_64 x86_64 x86_64 GNU/Linux
Decoded decodecode output:
All code
========
0: 00 00 add %al,(%rax)
2: 31 d2 xor %edx,%edx
4: 31 c9 xor %ecx,%ecx
6: 48 c7 c6 c0 ae 3d a0 mov $0xffffffffa03daec0,%rsi
d: 4c 89 e7 mov %r12,%rdi
10: 48 89 c3 mov %rax,%rbx
13: e8 e1 d0 22 e1 callq 0xffffffffe122d0f9
18: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
1e: 48 89 c2 mov %rax,%rdx
21: 48 89 43 18 mov %rax,0x18(%rbx)
25: 0f 87 c5 00 00 00 ja 0xf0
2b:* 49 8b 46 18 mov 0x18(%r14),%rax <-- trapping instruction
2f: 4c 89 6b 10 mov %r13,0x10(%rbx)
33: 49 89 9c 24 c0 00 00 mov %rbx,0xc0(%r12)
3a: 00
3b: 89 .byte 0x89
Code starting with the faulting instruction
===========================================
0: 49 8b 46 18 mov 0x18(%r14),%rax
4: 4c 89 6b 10 mov %r13,0x10(%rbx)
8: 49 89 9c 24 c0 00 00 mov %rbx,0xc0(%r12)
f: 00
10: 89 .byte 0x89
root@intel-corei7-64:/config/acpi/table# cat ~/audio.aml > my_ssdt/aml
[ 570.860263] ACPI: Host-directed Dynamic ACPI Table Load:
[ 570.866751] ACPI: SSDT 0xFFFF8800756CE180 000081 (v01 Vendor Accel 00000003 INTL 20150515)
[ 570.881027] hpet: number irqs doesn't agree with number of timers
root@intel-corei7-64:/config/acpi/table# [ 570.904275] BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
[ 570.913176] IP: [<ffffffffa03d6512>] aic31xx_i2c_probe+0x62/0x150 [snd_soc_tlv320aic31xx]
[ 570.922459] PGD 0
[ 570.924744] Oops: 0000 [#1] PREEMPT SMP
[ 570.929087] Modules linked in: snd_soc_tlv320aic31xx(+) snd_soc_core ctr ccm igb arc4 intel_rapl intel_soc_dts_iosf intel_powerclamp coretemp rt2800usb rt2x00usb rt2800lib rt2x00lib snd_hda_codec_hdmis
[ 570.974555] CPU: 2 PID: 199 Comm: kworker/2:3 Not tainted 4.8.3-yocto-standard #8
[ 570.983026] Workqueue: events acpi_table_events_fn
[ 570.988460] task: ffff880079b20000 task.stack: ffff8800741a0000
[ 570.995163] RIP: 0010:[<ffffffffa03d6512>] [<ffffffffa03d6512>] aic31xx_i2c_probe+0x62/0x150 [snd_soc_tlv320aic31xx]
[ 571.007197] RSP: 0018:ffff8800741a39c0 EFLAGS: 00010287
[ 571.013212] RAX: ffff880072656c00 RBX: ffff880074af7218 RCX: ffff880074983600
[ 571.021293] RDX: ffff880072656c00 RSI: 0000000000000287 RDI: ffff880072657a68
[ 571.032026] RBP: ffff8800741a39e8 R08: ffff880075401c00 R09: ffff880074983600
[ 571.042740] R10: ffff880074983038 R11: ffff8800756c8ef8 R12: ffff880072657800
[ 571.053421] R13: ffff880072657820 R14: 0000000000000000 R15: ffffffffa03d64b0
[ 571.064051] FS: 0000000000000000(0000) GS:ffff880078100000(0000) knlGS:0000000000000000
[ 571.075740] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 571.084770] CR2: 0000000000000018 CR3: 0000000001e06000 CR4: 00000000001006e0
[ 571.095332] Stack:
[ 571.100058] 00000000fffffffe 0000000000000000 ffff880072657820 ffff880072657804
[ 571.110937] ffff880072657800 ffff8800741a3a20 ffffffff8176752d ffff880072657820
[ 571.121813] 0000000000000000 ffffffffa03db758 0000000000000024 0000000000000000
[ 571.132683] Call Trace:
[ 571.137914] [<ffffffff8176752d>] i2c_device_probe+0xfd/0x1a0
[ 571.146853] [<ffffffff815e81d2>] driver_probe_device+0x202/0x2c0
[ 571.156142] [<ffffffff815e83ac>] __device_attach_driver+0x7c/0xb0
[ 571.165481] [<ffffffff815e8330>] ? __driver_attach+0xa0/0xa0
[ 571.174275] [<ffffffff815e61ab>] bus_for_each_drv+0x5b/0x90
[ 571.182982] [<ffffffff815e7ebb>] __device_attach+0xbb/0x110
[ 571.191625] [<ffffffff815e8423>] device_initial_probe+0x13/0x20
[ 571.200586] [<ffffffff815e7252>] bus_probe_device+0x92/0xa0
[ 571.209088] [<ffffffff815e5159>] device_add+0x359/0x590
[ 571.217122] [<ffffffff815e53aa>] device_register+0x1a/0x20
[ 571.225369] [<ffffffff8176a35a>] i2c_new_device+0x18a/0x1f0
[ 571.233656] [<ffffffff8176a445>] acpi_i2c_register_device+0x25/0x70
[ 571.242692] [<ffffffff8176a599>] acpi_i2c_notify+0x89/0xa0
[ 571.250819] [<ffffffff81098fca>] notifier_call_chain+0x4a/0x70
[ 571.259286] [<ffffffff81099347>] __blocking_notifier_call_chain+0x47/0x60
[ 571.268780] [<ffffffff81099376>] blocking_notifier_call_chain+0x16/0x20
[ 571.278063] [<ffffffff81473aff>] acpi_default_enumeration+0x63/0x6c
[ 571.286932] [<ffffffff8147503d>] acpi_bus_attach+0x148/0x159
[ 571.295121] [<ffffffff81475005>] acpi_bus_attach+0x110/0x159
[ 571.303268] [<ffffffff81475005>] acpi_bus_attach+0x110/0x159
[ 571.311390] [<ffffffff81475005>] acpi_bus_attach+0x110/0x159
[ 571.319462] [<ffffffff81475131>] acpi_bus_scan+0x5c/0x67
[ 571.327083] [<ffffffff814755c0>] acpi_table_events_fn+0x21/0x31
[ 571.335358] [<ffffffff8109290e>] process_one_work+0x1de/0x4e0
[ 571.343413] [<ffffffff81092c58>] worker_thread+0x48/0x4e0
[ 571.351039] [<ffffffff81092c10>] ? process_one_work+0x4e0/0x4e0
[ 571.359203] [<ffffffff81092c10>] ? process_one_work+0x4e0/0x4e0
[ 571.367316] [<ffffffff810982e9>] kthread+0xc9/0xe0
[ 571.374123] [<ffffffff819d184f>] ret_from_fork+0x1f/0x40
[ 571.381482] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
[ 571.389610] Code: 00 00 31 d2 31 c9 48 c7 c6 c0 ae 3d a0 4c 89 e7 48 89 c3 e8 e1 d0 22 e1 48 3d 00 f0 ff ff 48 89 c2 48 89 43 18 0f 87 c5 00 00 00 <49> 8b 46 18 4c 89 6b 10 49 89 9c 24 c0 00 00 00 89
[ 571.415686] RIP [<ffffffffa03d6512>] aic31xx_i2c_probe+0x62/0x150 [snd_soc_tlv320aic31xx]
[ 571.426520] RSP <ffff8800741a39c0>
[ 571.431991] CR2: 0000000000000018
[ 571.437253] ---[ end trace 614a27e632791261 ]---
[ 571.444021] BUG: unable to handle kernel NULL pointer dereference at 0000000000000009
[ 571.454047] IP: [<ffffffff810b5fab>] __wake_up_common+0x2b/0x80
[ 571.461947] PGD 0
[ 571.465451] Oops: 0000 [#2] PREEMPT SMP
[ 571.470992] Modules linked in: snd_soc_tlv320aic31xx(+) snd_soc_core ctr ccm igb arc4 intel_rapl intel_soc_dts_iosf intel_powerclamp coretemp rt2800usb rt2x00usb rt2800lib rt2x00lib snd_hda_codec_hdmis
[ 571.523019] CPU: 2 PID: 199 Comm: kworker/2:3 Tainted: G D 4.8.3-yocto-standard #8
[ 571.534327] task: ffff880079b20000 task.stack: ffff8800741a0000
[ 571.542548] RIP: 0010:[<ffffffff810b5fab>] [<ffffffff810b5fab>] __wake_up_common+0x2b/0x80
[ 571.553580] RSP: 0018:ffff8800741a3e48 EFLAGS: 00010096
[ 571.561242] RAX: 0000000000000282 RBX: ffff8800741a3f18 RCX: 0000000000000000
[ 571.570986] RDX: 0000000000000009 RSI: 0000000000000003 RDI: ffff8800741a3f18
[ 571.580750] RBP: ffff8800741a3e80 R08: 0000000000000000 R09: 0000000000000000
[ 571.590520] R10: ffffffff81e05200 R11: 0000000000000308 R12: ffff8800741a3f20
[ 571.600291] R13: 0000000000000282 R14: 0000000000000046 R15: 0000000000000003
[ 571.610068] FS: 0000000000000000(0000) GS:ffff880078100000(0000) knlGS:0000000000000000
[ 571.620977] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 571.629261] CR2: 0000000000000028 CR3: 0000000001e06000 CR4: 00000000001006e0
[ 571.639136] Stack:
[ 571.643250] 0000000100000000 0000000000000000 ffff8800741a3f18 ffff8800741a3f10
[ 571.653469] 0000000000000282 0000000000000046 0000000000000009 ffff8800741a3e90
[ 571.663623] ffffffff810b6063 ffff8800741a3eb8 ffffffff810b6a67 ffff880079b20610
[ 571.673753] Call Trace:
[ 571.678246] [<ffffffff810b6063>] __wake_up_locked+0x13/0x20
[ 571.686347] [<ffffffff810b6a67>] complete+0x37/0x50
[ 571.693665] [<ffffffff81077d2d>] mm_release+0xbd/0x140
[ 571.701268] [<ffffffff8107e224>] do_exit+0x4e4/0xb20
[ 571.708638] [<ffffffff819d29d7>] rewind_stack_do_exit+0x17/0x20
[ 571.717043] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
[ 571.725541] Code: 0f 1f 44 00 00 55 48 89 e5 41 57 41 89 f7 41 56 41 55 41 54 4c 8d 67 08 53 48 83 ec 10 89 55 cc 48 8b 57 08 4c 89 45 d0 49 39 d4 <48> 8b 32 74 40 48 8d 42 e8 41 89 ce 4c 8d 6e e8 8b
[ 571.752398] RIP [<ffffffff810b5fab>] __wake_up_common+0x2b/0x80
[ 571.760820] RSP <ffff8800741a3e48>
[ 571.766359] CR2: 0000000000000009
[ 571.771636] ---[ end trace 614a27e632791262 ]---
[ 571.778350] Fixing recursive fault but reboot is needed!
[ 571.785817] BUG: scheduling while atomic: kworker/2:3/199/0x00000003
[ 571.794447] Modules linked in: snd_soc_tlv320aic31xx(+) snd_soc_core ctr ccm igb arc4 intel_rapl intel_soc_dts_iosf intel_powerclamp coretemp rt2800usb rt2x00usb rt2800lib rt2x00lib snd_hda_codec_hdmis
[ 571.847407] CPU: 2 PID: 199 Comm: kworker/2:3 Tainted: G D 4.8.3-yocto-standard #8
[ 571.858904] 0000000000000000 ffff8800741a3e60 ffffffff813f29fa 00000000000171c0
[ 571.868977] ffff8800781171c0 ffff8800741a3e70 ffffffff8109e05f ffff8800741a3ec0
[ 571.879034] ffffffff819cd099 ffffffff81144e66 ffff880079b20000 ffff8800741a3ee8
[ 571.889078] Call Trace:
[ 571.893497] [<ffffffff813f29fa>] dump_stack+0x4d/0x63
[ 571.900942] [<ffffffff8109e05f>] __schedule_bug+0x4f/0x70
[ 571.908783] [<ffffffff819cd099>] __schedule+0x469/0x610
[ 571.916396] [<ffffffff81144e66>] ? printk+0x48/0x50
[ 571.923583] [<ffffffff819cd27c>] schedule+0x3c/0x90
[ 571.930743] [<ffffffff8107e69a>] do_exit+0x95a/0xb20
[ 571.937986] [<ffffffff819d29d7>] rewind_stack_do_exit+0x17/0x20
[ 571.946324] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
[ 571.954778] BUG: unable to handle kernel paging request at ffffffffffffffd8
[ 571.964252] IP: [<ffffffff810987c1>] kthread_data+0x11/0x20
[ 571.972167] PGD 1e07067 PUD 1e09067 PMD 0
[ 571.978406] Oops: 0000 [#3] PREEMPT SMP
[ 571.984266] Modules linked in: snd_soc_tlv320aic31xx(+) snd_soc_core ctr ccm igb arc4 intel_rapl intel_soc_dts_iosf intel_powerclamp coretemp rt2800usb rt2x00usb rt2800lib rt2x00lib snd_hda_codec_hdmis
[ 572.037565] CPU: 2 PID: 199 Comm: kworker/2:3 Tainted: G D W 4.8.3-yocto-standard #8
[ 572.049133] task: ffff880079b20000 task.stack: ffff8800741a0000
[ 572.057574] RIP: 0010:[<ffffffff810987c1>] [<ffffffff810987c1>] kthread_data+0x11/0x20
[ 572.068374] RSP: 0018:ffff8800741a3e68 EFLAGS: 00010002
[ 572.076095] RAX: 0000000000000000 RBX: 00000000000171c0 RCX: 0000000000000002
[ 572.085842] RDX: ffff880075405000 RSI: ffff880079b20080 RDI: ffff880079b20000
[ 572.095551] RBP: ffff8800741a3e70 R08: 00000000000000c0 R09: 0000000000000000
[ 572.105217] R10: 0000000000000000 R11: 00000000ce248c14 R12: ffff8800781171c0
[ 572.114853] R13: 0000000000000000 R14: ffff880079b20000 R15: 0000000000000000
[ 572.124449] FS: 0000000000000000(0000) GS:ffff880078100000(0000) knlGS:0000000000000000
[ 572.135138] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 572.143197] CR2: 0000000000000028 CR3: 0000000001e06000 CR4: 00000000001006e0
[ 572.152808] Stack:
[ 572.156656] ffffffff8109407e ffff8800741a3ec0 ffffffff819ccff9 ffffffff81144e66
[ 572.166641] ffff880079b20000 ffff8800741a3ee8 ffff8800741a4000 0000000000000009
[ 572.176655] 0000000000000000 0000000000000046 0000000000000009 ffff8800741a3ed8
[ 572.186691] Call Trace:
[ 572.191149] [<ffffffff8109407e>] ? wq_worker_sleeping+0xe/0x80
[ 572.199555] [<ffffffff819ccff9>] __schedule+0x3c9/0x610
[ 572.207272] [<ffffffff81144e66>] ? printk+0x48/0x50
[ 572.214592] [<ffffffff819cd27c>] schedule+0x3c/0x90
[ 572.221872] [<ffffffff8107e69a>] do_exit+0x95a/0xb20
[ 572.229212] [<ffffffff819d29d7>] rewind_stack_do_exit+0x17/0x20
[ 572.237624] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
[ 572.246140] Code: b6 3b f7 7e 0f 85 56 ff ff ff e8 b3 89 f6 ff e9 4c ff ff ff 66 0f 1f 44 00 00 0f 1f 44 00 00 48 8b 87 68 04 00 00 55 48 89 e5 5d <48> 8b 40 d8 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f
[ 572.272875] RIP [<ffffffff810987c1>] kthread_data+0x11/0x20
[ 572.280865] RSP <ffff8800741a3e68>
[ 572.286355] CR2: ffffffffffffffd8
[ 572.291589] ---[ end trace 614a27e632791263 ]---
[ 572.298258] Fixing recursive fault but reboot is needed!
[ 572.305693] BUG: scheduling while atomic: kworker/2:3/199/0x00000003
[ 572.314294] Modules linked in: snd_soc_tlv320aic31xx(+) snd_soc_core ctr ccm igb arc4 intel_rapl intel_soc_dts_iosf intel_powerclamp coretemp rt2800usb rt2x00usb rt2800lib rt2x00lib snd_hda_codec_hdmis
[ 572.367213] CPU: 2 PID: 199 Comm: kworker/2:3 Tainted: G D W 4.8.3-yocto-standard #8
[ 572.378698] 0000000000000000 ffff8800741a3e60 ffffffff813f29fa 00000000000171c0
[ 572.388749] ffff8800781171c0 ffff8800741a3e70 ffffffff8109e05f ffff8800741a3ec0
[ 572.398787] ffffffff819cd099 ffffffff81144e66 ffff880079b20000 ffff8800741a3ee8
[ 572.408804] Call Trace:
[ 572.413204] [<ffffffff813f29fa>] dump_stack+0x4d/0x63
[ 572.420639] [<ffffffff8109e05f>] __schedule_bug+0x4f/0x70
[ 572.428444] [<ffffffff819cd099>] __schedule+0x469/0x610
[ 572.436011] [<ffffffff81144e66>] ? printk+0x48/0x50
[ 572.443172] [<ffffffff819cd27c>] schedule+0x3c/0x90
[ 572.450314] [<ffffffff8107e69a>] do_exit+0x95a/0xb20
[ 572.457550] [<ffffffff819d29d7>] rewind_stack_do_exit+0x17/0x20
[ 572.465884] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
[ 593.488004] INFO: rcu_preempt detected stalls on CPUs/tasks:
[ 593.500628] 2-...: (3 GPs behind) idle=a1b/140000000000000/0 softirq=3893/3894 fqs=5250
[ 593.516219] (detected by 3, t=21002 jiffies, g=3909, c=3908, q=364)
[ 593.529707] Task dump for CPU 2:
[ 593.539404] kworker/2:3 D ffff880079b28000 0 199 2 0x00000008
[ 593.553523] ffff880074c61483 ffff88007548c400 0000000000000000 ffff8800741a3858
[ 593.568045] ffffffff814f6370 0000000000000003 ffff880074c63730 ffff8800741a3960
[ 593.582553] ffffffff81460e71 ffff8800741a38a8 ffffffff814f6157 ffffffff00000200
[ 593.597148] Call Trace:
[ 593.606038] [<ffffffff814f6370>] ? drm_fb_helper_cfb_imageblit+0x30/0x40
[ 593.620027] [<ffffffff81460e71>] ? bit_putcs+0x2d1/0x520
[ 593.632349] [<ffffffff814f6157>] ? drm_fb_helper_dirty.isra.14+0xc7/0xe0
[ 593.646159] [<ffffffff814f6157>] ? drm_fb_helper_dirty.isra.14+0xc7/0xe0
[ 593.659643] [<ffffffff814f6370>] ? drm_fb_helper_cfb_imageblit+0x30/0x40
[ 593.672884] [<ffffffff8146127a>] ? soft_cursor+0x1ba/0x230
[ 593.684557] [<ffffffff81460a73>] ? bit_cursor+0x633/0x670
[ 593.695916] [<ffffffff813ff3c7>] ? __const_udelay+0x27/0x30
[ 593.707310] [<ffffffff81460440>] ? update_attr.isra.2+0x90/0x90
[ 593.718955] [<ffffffff8145c2cc>] ? fbcon_cursor+0x13c/0x1d0
[ 593.730189] [<ffffffff8109924a>] ? atomic_notifier_call_chain+0x3a/0x50
[ 593.742646] [<ffffffff814c8865>] ? vt_console_print+0x225/0x3b0
[ 593.754208] [<ffffffff810b8222>] ? up+0x32/0x50
[ 593.764140] [<ffffffff810bf25b>] ? wake_up_klogd+0x3b/0x50
[ 593.775160] [<ffffffff810bf796>] ? console_unlock+0x526/0x5d0
[ 593.786526] [<ffffffff810bfa83>] ? vprintk_emit+0x243/0x470
[ 593.797690] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
[ 593.809407] [<ffffffff810bfe1f>] ? vprintk_default+0x1f/0x30
[ 593.820760] [<ffffffff81144e66>] ? printk+0x48/0x50
[ 593.831196] [<ffffffff810e9d62>] ? __module_text_address+0x12/0x60
[ 593.843148] [<ffffffff810ed605>] ? is_module_text_address+0x15/0x30
[ 593.855136] [<ffffffff81096886>] ? __kernel_text_address+0x56/0x70
[ 593.866887] [<ffffffff8101e35b>] ? print_context_stack+0x7b/0x100
[ 593.878403] [<ffffffff8101d9b8>] ? dump_trace+0x118/0x320
[ 593.889055] [<ffffffff8101e68b>] ? show_trace_log_lvl+0x4b/0x60
[ 593.900224] [<ffffffff8101dcd8>] ? show_stack_log_lvl+0x118/0x1b0
[ 593.911484] [<ffffffff8101e725>] ? show_stack+0x25/0x50
[ 593.921672] [<ffffffff810cee84>] ? rcu_note_context_switch+0x244/0x410
[ 593.933353] [<ffffffff819d1477>] ? _raw_spin_lock+0x27/0x30
[ 593.943846] [<ffffffff819ccc9f>] ? __schedule+0x6f/0x610
[ 593.953925] [<ffffffff81144e66>] ? printk+0x48/0x50
[ 593.963454] [<ffffffff819cd27c>] ? schedule+0x3c/0x90
[ 593.973123] [<ffffffff8107e69a>] ? do_exit+0x95a/0xb20
[ 593.982979] [<ffffffff819d29d7>] ? rewind_stack_do_exit+0x17/0x20
[ 593.993988] [<ffffffff81098220>] ? kthread_worker_fn+0x170/0x170
^ permalink raw reply
* Re: [PATCH v4 07/14] ARM: dts: koelsch: use demuxer for I2C4
From: Simon Horman @ 2016-11-15 17:44 UTC (permalink / raw)
To: Geert Uytterhoeven; +Cc: Wolfram Sang, Linux-Renesas, Linux I2C
In-Reply-To: <CAMuHMdXzqakz6RYXFMOe5ps0SPsxDWHbOV024ts_E_qufVOutQ@mail.gmail.com>
On Tue, Nov 15, 2016 at 04:45:01PM +0100, Geert Uytterhoeven wrote:
> Hi Wolfram,
>
> On Sun, Nov 6, 2016 at 9:20 PM, Wolfram Sang
> <wsa+renesas@sang-engineering.com> wrote:
> > From: Simon Horman <horms+renesas@verge.net.au>
> >
> > Make it possible to fallback to GPIO for I2C4 on the EXIO-E connector.
> >
> > This is based on reference work for the I2C0 core of the lager/r8a7790
> > by Wolfram Sang.
> >
> > Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
> > [wsa: rebased and fixed aliases]
> > Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> > ---
> > arch/arm/boot/dts/r8a7791-koelsch.dts | 35 +++++++++++++++++++++++++++++++++++
> > 1 file changed, 35 insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
> > index 8d653920957a4b..442d1512a8fa9c 100644
> > --- a/arch/arm/boot/dts/r8a7791-koelsch.dts
> > +++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
>
> > @@ -476,6 +501,11 @@
> > function = "i2c2";
> > };
> >
> > + i2c4_pins: i2c4 {
> > + groups = "i2c4";
> > + function = "i2c4";
> > + };
> > +
> > du_pins: du {
> > groups = "du_rgb888", "du_sync", "du_disp", "du_clk_out_0";
> > function = "du";
> > @@ -732,6 +762,11 @@
> > clock-frequency = <100000>;
> > };
> >
> > +&i2c4 {
> > + pinctrl-0 = <&i2c4_pins>;
> > + pinctrl-names = "i2c-exio4";
> > +};
>
> i2c4 shares pins with vin0. Hence enabling the former breaks the latter:
>
> sh-pfc e6060000.pfc: pin GP_4_13 already requested by
> e6520000.i2c; cannot claim for e6ef0000.video
> sh-pfc e6060000.pfc: pin-141 (e6ef0000.video) status -22
> sh-pfc e6060000.pfc: could not request pin 141 (GP_4_13) from
> group vin0_data24 on device sh-pfc
> rcar-vin e6ef0000.video: Error applying setting, reverse things back
> rcar-vin: probe of e6ef0000.video failed with error -22
>
> There may be similar issues on other boards. Haven't checked yet.
Thanks, I will drop this patch for now.
I checked the boot logs of other boards with similar patches and didn't see
anything there.
^ permalink raw reply
* [PATCH v2 2/2] i2c: Add Tegra BPMP I2C proxy driver
From: Thierry Reding @ 2016-11-15 16:23 UTC (permalink / raw)
To: Wolfram Sang
Cc: Stephen Warren, Alexandre Courbot, Jon Hunter, linux-i2c,
linux-tegra
In-Reply-To: <20161115162350.32405-1-thierry.reding@gmail.com>
From: Shardar Shariff Md <smohammed@nvidia.com>
Add Tegra BPMP I2C driver. The BPMP is the boot and power management
processor embedded in Tegra SoCs. In newer SoC versions, access to one
of the I2C busses goes via the BPMP, requiring a different "proxy" I2C
driver that accesses the bus via the real I2C driver embedded in the
BPMP firmware.
Signed-off-by: Shardar Shariff Md <smohammed@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/i2c/busses/Kconfig | 13 ++
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-tegra-bpmp.c | 365 ++++++++++++++++++++++++++++++++++++
3 files changed, 379 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-tegra-bpmp.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 6d94e2ec5b4f..db4aac696486 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -909,6 +909,19 @@ config I2C_TEGRA
If you say yes to this option, support will be included for the
I2C controller embedded in NVIDIA Tegra SOCs
+config I2C_TEGRA_BPMP
+ tristate "NVIDIA Tegra BPMP I2C controller"
+ depends on TEGRA_BPMP
+ select I2C_ALGO_BUSCLEAR
+ select I2C_ALGOBIT
+ help
+ If you say yes to this option, support will be included for the I2C
+ controller embedded in NVIDIA Tegra SoCs accessed via the BPMP.
+
+ This I2C driver is a 'virtual' I2C driver. The real driver is part
+ of the BPMP firmware, and this driver merely communicates with that
+ real driver.
+
config I2C_UNIPHIER
tristate "UniPhier FIFO-less I2C controller"
depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 29764cc20a44..9dacd88669ae 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_I2C_ST) += i2c-st.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
+obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c
new file mode 100644
index 000000000000..480251cce238
--- /dev/null
+++ b/drivers/i2c/busses/i2c-tegra-bpmp.c
@@ -0,0 +1,365 @@
+/*
+ * drivers/i2c/busses/i2c-tegra-bpmp.c
+ *
+ * Copyright (c) 2016 NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Shardar Shariff Md <smohammed@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <soc/tegra/bpmp-abi.h>
+#include <soc/tegra/bpmp.h>
+
+/*
+ * Serialized I2C message header size is 6 bytes and includes address, flags
+ * and length
+ */
+#define SERIALI2C_HDR_SIZE 6
+
+struct tegra_bpmp_i2c {
+ struct i2c_adapter adapter;
+ struct device *dev;
+
+ struct tegra_bpmp *bpmp;
+ unsigned int bus;
+};
+
+/*
+ * Linux flags are translated to BPMP defined I2C flags that are used in BPMP
+ * firmware I2C driver to avoid any issues in future if Linux I2C flags are
+ * changed.
+ */
+static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
+{
+ if (flags & I2C_M_TEN) {
+ *out |= SERIALI2C_TEN;
+ flags &= ~I2C_M_TEN;
+ }
+
+ if (flags & I2C_M_RD) {
+ *out |= SERIALI2C_RD;
+ flags &= ~I2C_M_RD;
+ }
+
+ if (flags & I2C_M_STOP) {
+ *out |= SERIALI2C_STOP;
+ flags &= ~I2C_M_STOP;
+ }
+
+ if (flags & I2C_M_NOSTART) {
+ *out |= SERIALI2C_NOSTART;
+ flags &= ~I2C_M_NOSTART;
+ }
+
+ if (flags & I2C_M_REV_DIR_ADDR) {
+ *out |= SERIALI2C_REV_DIR_ADDR;
+ flags &= ~I2C_M_REV_DIR_ADDR;
+ }
+
+ if (flags & I2C_M_IGNORE_NAK) {
+ *out |= SERIALI2C_IGNORE_NAK;
+ flags &= ~I2C_M_IGNORE_NAK;
+ }
+
+ if (flags & I2C_M_NO_RD_ACK) {
+ *out |= SERIALI2C_NO_RD_ACK;
+ flags &= ~I2C_M_NO_RD_ACK;
+ }
+
+ if (flags & I2C_M_RECV_LEN) {
+ *out |= SERIALI2C_RECV_LEN;
+ flags &= ~I2C_M_RECV_LEN;
+ }
+
+ return (flags != 0) ? -EINVAL : 0;
+}
+
+/**
+ * The serialized I2C format is simply the following:
+ * [addr little-endian][flags little-endian][len little-endian][data if write]
+ * [addr little-endian][flags little-endian][len little-endian][data if write]
+ * ...
+ *
+ * The flags are translated from Linux kernel representation to seriali2c
+ * representation. Any undefined flag being set causes an error.
+ *
+ * The data is there only for writes. Reads have the data transferred in the
+ * other direction, and thus data is not present.
+ *
+ * See deserialize_i2c documentation for the data format in the other direction.
+ */
+static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
+ struct mrq_i2c_request *request,
+ struct i2c_msg *msgs,
+ unsigned int num)
+{
+ char *buf = request->xfer.data_buf;
+ unsigned int i, j, pos = 0;
+ int err;
+
+ for (i = 0; i < num; i++) {
+ struct i2c_msg *msg = &msgs[i];
+ u16 flags = 0;
+
+ err = tegra_bpmp_xlate_flags(msg->flags, &flags);
+ if (err < 0)
+ return err;
+
+ buf[pos++] = msg->addr & 0xff;
+ buf[pos++] = (msg->addr & 0xff00) >> 8;
+ buf[pos++] = flags & 0xff;
+ buf[pos++] = (flags & 0xff00) >> 8;
+ buf[pos++] = msg->len & 0xff;
+ buf[pos++] = (msg->len & 0xff00) >> 8;
+
+ if ((flags & SERIALI2C_RD) == 0) {
+ for (j = 0; j < msg->len; j++)
+ buf[pos++] = msg->buf[j];
+ }
+ }
+
+ request->xfer.data_size = pos;
+
+ return 0;
+}
+
+/**
+ * The data in the BPMP -> CPU direction is composed of sequential blocks for
+ * those messages that have I2C_M_RD. So, for example, if you have:
+ *
+ * - !I2C_M_RD, len == 5, data == a0 01 02 03 04
+ * - !I2C_M_RD, len == 1, data == a0
+ * - I2C_M_RD, len == 2, data == [uninitialized buffer 1]
+ * - !I2C_M_RD, len == 1, data == a2
+ * - I2C_M_RD, len == 2, data == [uninitialized buffer 2]
+ *
+ * ...then the data in the BPMP -> CPU direction would be 4 bytes total, and
+ * would contain 2 bytes that will go to uninitialized buffer 1, and 2 bytes
+ * that will go to uninitialized buffer 2.
+ */
+static int tegra_bpmp_i2c_deserialize(struct tegra_bpmp_i2c *i2c, char *buf,
+ size_t size, struct i2c_msg *msgs,
+ unsigned int num)
+{
+ size_t len = 0, pos = 0;
+ unsigned int i;
+
+ for (i = 0; i < num; i++)
+ if (msgs[i].flags & I2C_M_RD)
+ len += msgs[i].len;
+
+ if (len != size)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) {
+ memcpy(msgs[i].buf, buf + pos, msgs[i].len);
+ pos += msgs[i].len;
+ }
+ }
+
+ return 0;
+}
+
+static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
+{
+ size_t tx_len = 0, rx_len = 0;
+ unsigned int i;
+
+ for (i = 0; i < num; i++)
+ if (!(msgs[i].flags & I2C_M_RD))
+ tx_len += SERIALI2C_HDR_SIZE + msgs[i].len;
+
+ if (tx_len > TEGRA_I2C_IPC_MAX_IN_BUF_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++)
+ if ((msgs[i].flags & I2C_M_RD))
+ rx_len += msgs[i].len;
+
+ if (rx_len > TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
+ struct mrq_i2c_request *request,
+ struct mrq_i2c_response *response)
+{
+ struct tegra_bpmp_message msg;
+ int err;
+
+ request->cmd = CMD_I2C_XFER;
+ request->xfer.bus_id = i2c->bus;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_I2C;
+ msg.tx.data = request;
+ msg.tx.size = sizeof(*request);
+ msg.rx.data = response;
+ msg.rx.size = sizeof(*response);
+
+ if (irqs_disabled())
+ err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
+ else
+ err = tegra_bpmp_transfer(i2c->bpmp, &msg);
+
+ return err;
+}
+
+static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
+ struct mrq_i2c_response response;
+ struct mrq_i2c_request request;
+ int err;
+
+ err = tegra_bpmp_i2c_msg_len_check(msgs, num);
+ if (err < 0) {
+ dev_err(i2c->dev, "unsupported message length\n");
+ return err;
+ }
+
+ /* TODO: move this somewhere else */
+ memset(&request, 0, sizeof(request));
+ memset(&response, 0, sizeof(response));
+
+ err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
+ if (err < 0) {
+ dev_err(i2c->dev, "failed to serialize message: %d\n", err);
+ return err;
+ }
+
+ err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response);
+ if (err < 0) {
+ dev_err(i2c->dev, "failed to transfer message: %d\n", err);
+ return err;
+ }
+
+ err = tegra_bpmp_i2c_deserialize(i2c, response.xfer.data_buf,
+ response.xfer.data_size,
+ msgs, num);
+ if (err < 0) {
+ dev_err(i2c->dev, "failed to deserialize message: %d\n", err);
+ return err;
+ }
+
+ return num;
+}
+
+static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
+}
+
+static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
+ .master_xfer = tegra_bpmp_i2c_xfer,
+ .functionality = tegra_bpmp_i2c_func,
+};
+
+static int tegra_bpmp_i2c_probe(struct platform_device *pdev)
+{
+ struct tegra_bpmp_i2c *i2c;
+ u32 value;
+ int err;
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->dev = &pdev->dev;
+
+ i2c->bpmp = dev_get_drvdata(pdev->dev.parent);
+ if (!i2c->bpmp)
+ return -ENODEV;
+
+ err = of_property_read_u32(pdev->dev.of_node, "nvidia,bpmp-bus-id",
+ &value);
+ if (err < 0)
+ return err;
+
+ i2c->bus = value;
+
+ i2c_set_adapdata(&i2c->adapter, i2c);
+ i2c->adapter.owner = THIS_MODULE;
+ i2c->adapter.class = I2C_CLASS_HWMON;
+ strlcpy(i2c->adapter.name, "Tegra BPMP I2C adapter",
+ sizeof(i2c->adapter.name));
+ i2c->adapter.algo = &tegra_bpmp_i2c_algo;
+ i2c->adapter.dev.parent = &pdev->dev;
+ i2c->adapter.dev.of_node = pdev->dev.of_node;
+
+ platform_set_drvdata(pdev, i2c);
+
+ err = i2c_add_adapter(&i2c->adapter);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to add I2C adapter: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_bpmp_i2c_remove(struct platform_device *pdev)
+{
+ struct tegra_bpmp_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adapter);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_bpmp_i2c_of_match[] = {
+ { .compatible = "nvidia,tegra186-bpmp-i2c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_bpmp_i2c_of_match);
+
+static struct platform_driver tegra_bpmp_i2c_driver = {
+ .driver = {
+ .name = "tegra-bpmp-i2c",
+ .of_match_table = tegra_bpmp_i2c_of_match,
+ },
+ .probe = tegra_bpmp_i2c_probe,
+ .remove = tegra_bpmp_i2c_remove,
+};
+
+static int __init tegra_bpmp_i2c_init_driver(void)
+{
+ return platform_driver_register(&tegra_bpmp_i2c_driver);
+}
+subsys_initcall(tegra_bpmp_i2c_init_driver);
+
+static void __exit tegra_bpmp_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&tegra_bpmp_i2c_driver);
+}
+module_exit(tegra_bpmp_i2c_exit_driver);
+
+MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus contoller driver");
+MODULE_AUTHOR("Shardar Shariff Md <smohammed@nvidia.com>");
+MODULE_AUTHOR("Juha-Matti Tilli");
+MODULE_LICENSE("GPL v2");
--
2.10.2
^ permalink raw reply related
* [PATCH v2 1/2] dt-bindings: Add Tegra186 BPMP I2C binding
From: Thierry Reding @ 2016-11-15 16:23 UTC (permalink / raw)
To: Wolfram Sang
Cc: Stephen Warren, Alexandre Courbot, Jon Hunter, linux-i2c,
linux-tegra
In-Reply-To: <20161115162350.32405-1-thierry.reding@gmail.com>
From: Stephen Warren <swarren@nvidia.com>
In Tegra186, the BPMP (Boot and Power Management Processor) owns certain
HW devices, such as the I2C controller for the power management I2C bus.
Software running on other CPUs must perform IPC to the BPMP in order to
execute transactions on that I2C bus. This binding describes an I2C bus
that is accessed in such a fashion.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Tom Warren <twarren@nvidia.com>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
.../bindings/i2c/nvidia,tegra186-bpmp-i2c.txt | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/nvidia,tegra186-bpmp-i2c.txt
diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra186-bpmp-i2c.txt b/Documentation/devicetree/bindings/i2c/nvidia,tegra186-bpmp-i2c.txt
new file mode 100644
index 000000000000..ab240e10debc
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra186-bpmp-i2c.txt
@@ -0,0 +1,42 @@
+NVIDIA Tegra186 BPMP I2C controller
+
+In Tegra186, the BPMP (Boot and Power Management Processor) owns certain HW
+devices, such as the I2C controller for the power management I2C bus. Software
+running on other CPUs must perform IPC to the BPMP in order to execute
+transactions on that I2C bus. This binding describes an I2C bus that is
+accessed in such a fashion.
+
+The BPMP I2C node must be located directly inside the main BPMP node. See
+../firmware/nvidia,tegra186-bpmp.txt for details of the BPMP binding.
+
+This node represents an I2C controller. See ../i2c/i2c.txt for details of the
+core I2C binding.
+
+Required properties:
+- compatible:
+ Array of strings.
+ One of:
+ - "nvidia,tegra186-bpmp-i2c".
+- #address-cells: Address cells for I2C device address.
+ Single-cell integer.
+ Must be <1>.
+- #size-cells:
+ Single-cell integer.
+ Must be <0>.
+- nvidia,bpmp-bus-id:
+ Single-cell integer.
+ Indicates the I2C bus number this DT node represent, as defined by the
+ BPMP firmware.
+
+Example:
+
+bpmp {
+ ...
+
+ i2c {
+ compatible = "nvidia,tegra186-bpmp-i2c";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ nvidia,bpmp-bus-id = <5>;
+ };
+};
--
2.10.2
^ permalink raw reply related
* [PATCH v2 0/2] i2c: Add Tegra BPMP I2C proxy driver
From: Thierry Reding @ 2016-11-15 16:23 UTC (permalink / raw)
To: Wolfram Sang
Cc: Stephen Warren, Alexandre Courbot, Jon Hunter,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA
From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Hi Wolfram,
This series of patches depends on two branches in the Tegra tree, so I
think the easiest would be if you Acked-by'd this so that I can manage
the dependencies in the Tegra tree. The DT binding has already been
reviewed and I've tested the driver in my development tree for a while
and it's working great.
Thierry
Shardar Shariff Md (1):
i2c: Add Tegra BPMP I2C proxy driver
Stephen Warren (1):
dt-bindings: Add Tegra186 BPMP I2C binding
.../bindings/i2c/nvidia,tegra186-bpmp-i2c.txt | 42 +++
drivers/i2c/busses/Kconfig | 13 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-tegra-bpmp.c | 365 +++++++++++++++++++++
4 files changed, 421 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/nvidia,tegra186-bpmp-i2c.txt
create mode 100644 drivers/i2c/busses/i2c-tegra-bpmp.c
--
2.10.2
^ permalink raw reply
* Re: [PATCH v4 07/14] ARM: dts: koelsch: use demuxer for I2C4
From: Geert Uytterhoeven @ 2016-11-15 15:45 UTC (permalink / raw)
To: Wolfram Sang; +Cc: Linux-Renesas, Simon Horman, Linux I2C, Simon Horman
In-Reply-To: <20161106202032.5227-8-wsa+renesas@sang-engineering.com>
Hi Wolfram,
On Sun, Nov 6, 2016 at 9:20 PM, Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
> From: Simon Horman <horms+renesas@verge.net.au>
>
> Make it possible to fallback to GPIO for I2C4 on the EXIO-E connector.
>
> This is based on reference work for the I2C0 core of the lager/r8a7790
> by Wolfram Sang.
>
> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
> [wsa: rebased and fixed aliases]
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
> arch/arm/boot/dts/r8a7791-koelsch.dts | 35 +++++++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
> diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
> index 8d653920957a4b..442d1512a8fa9c 100644
> --- a/arch/arm/boot/dts/r8a7791-koelsch.dts
> +++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
> @@ -476,6 +501,11 @@
> function = "i2c2";
> };
>
> + i2c4_pins: i2c4 {
> + groups = "i2c4";
> + function = "i2c4";
> + };
> +
> du_pins: du {
> groups = "du_rgb888", "du_sync", "du_disp", "du_clk_out_0";
> function = "du";
> @@ -732,6 +762,11 @@
> clock-frequency = <100000>;
> };
>
> +&i2c4 {
> + pinctrl-0 = <&i2c4_pins>;
> + pinctrl-names = "i2c-exio4";
> +};
i2c4 shares pins with vin0. Hence enabling the former breaks the latter:
sh-pfc e6060000.pfc: pin GP_4_13 already requested by
e6520000.i2c; cannot claim for e6ef0000.video
sh-pfc e6060000.pfc: pin-141 (e6ef0000.video) status -22
sh-pfc e6060000.pfc: could not request pin 141 (GP_4_13) from
group vin0_data24 on device sh-pfc
rcar-vin e6ef0000.video: Error applying setting, reverse things back
rcar-vin: probe of e6ef0000.video failed with error -22
There may be similar issues on other boards. Haven't checked yet.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Jan Glauber @ 2016-11-15 13:03 UTC (permalink / raw)
To: Steven J. Hill
Cc: Wolfram Sang, linux-i2c, linux-mips, Paul Burton, David Daney
In-Reply-To: <99500824-4c63-b769-ad66-c136529b14b2@cavium.com>
On Mon, Nov 14, 2016 at 01:53:40PM -0600, Steven J. Hill wrote:
> On 11/14/2016 12:50 PM, Jan Glauber wrote:
> >
> > Since time is running out for 4.9 (or might have already if you're not
> > going to send another pull request) I'm going for the safe option
> > to fix the Octeon i2c problems, which is:
> >
> > 1. Reverting the readq_poll_timeout patch since it is broken
> > 2. Apply Patch #2 from Paul
> > 3. Add a small fix for the recovery that makes Paul's patch
> > work on ThunderX
> >
> > I'll try to come up with a better solution for 4.10. My plan is to get rid
> > of the polling-around-interrupt thing completely, but for that we need more
> > time to make it work on Octeon.
> >
> > Please consider for 4.9.
> >
> Hey Jan.
>
> This does not work on Octeon 71xx platforms. I will look at it more
> closely tomorrow.
Paul, can you confirm this? It doesn't make sense for me, since patches #1
and #3 are unlikely to break anything... And patch #2 worked for you.
--Jan
> Steve
^ permalink raw reply
* Re: [PATCH v4 1/2] i2c: aspeed: added driver for Aspeed I2C
From: Joel Stanley @ 2016-11-15 11:23 UTC (permalink / raw)
To: Brendan Higgins
Cc: Wolfram Sang, Rob Herring, Mark Rutland, linux-i2c, devicetree,
OpenBMC Maillist
In-Reply-To: <1478311099-6771-2-git-send-email-brendanhiggins@google.com>
On Sat, Nov 5, 2016 at 12:28 PM, Brendan Higgins
<brendanhiggins@google.com> wrote:
> Added initial master and slave support for Aspeed I2C controller.
> Supports fourteen busses present in ast24xx and ast25xx BMC SoCs by
> Aspeed.
>
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
I've tested this on an OpenPower Palmetto BMC system and on the Qemu
machine for the same board.
Reviewed-by: Joel Stanley <joel@jms.id.au>
Cheers,
Joel
> ---
> Changes for v2:
> - Added single module_init (multiple was breaking some builds).
> Changes for v3:
> - Removed "bus" device tree param; now extracted from bus address offset
> Changes for v4:
> - I2C adapter number is now generated dynamically unless specified in alias.
> ---
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-aspeed.c | 807 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 818 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-aspeed.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index d252276..b6caa5d 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -1009,6 +1009,16 @@ config I2C_RCAR
> This driver can also be built as a module. If so, the module
> will be called i2c-rcar.
>
> +config I2C_ASPEED
> + tristate "Aspeed AST2xxx SoC I2C Controller"
> + depends on ARCH_ASPEED
> + help
> + If you say yes to this option, support will be included for the
> + Aspeed AST2xxx SoC I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-aspeed.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 29764cc..826e780 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -99,6 +99,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
> obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> +obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
> new file mode 100644
> index 0000000..88e078a
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-aspeed.c
> @@ -0,0 +1,807 @@
> +/*
> + * I2C adapter for the ASPEED I2C bus.
> + *
> + * Copyright (C) 2012-2020 ASPEED Technology Inc.
> + * Copyright 2016 IBM Corporation
> + * Copyright 2016 Google, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/slab.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +
> +/* I2C Register */
> +#define ASPEED_I2C_FUN_CTRL_REG 0x00
> +#define ASPEED_I2C_AC_TIMING_REG1 0x04
> +#define ASPEED_I2C_AC_TIMING_REG2 0x08
> +#define ASPEED_I2C_INTR_CTRL_REG 0x0c
> +#define ASPEED_I2C_INTR_STS_REG 0x10
> +#define ASPEED_I2C_CMD_REG 0x14
> +#define ASPEED_I2C_DEV_ADDR_REG 0x18
> +#define ASPEED_I2C_BYTE_BUF_REG 0x20
> +#define ASPEED_I2C_OFFSET_START 0x40
> +#define ASPEED_I2C_OFFSET_INCREMENT 0x40
> +
> +#define ASPEED_I2C_NUM_BUS 14
> +
> +/* Global Register Definition */
> +/* 0x00 : I2C Interrupt Status Register */
> +/* 0x08 : I2C Interrupt Target Assignment */
> +
> +/* Device Register Definition */
> +/* 0x00 : I2CD Function Control Register */
> +#define ASPEED_I2CD_MULTI_MASTER_DIS BIT(15)
> +#define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8)
> +#define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7)
> +#define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6)
> +#define ASPEED_I2CD_SLAVE_EN BIT(1)
> +#define ASPEED_I2CD_MASTER_EN BIT(0)
> +
> +/* 0x08 : I2CD Clock and AC Timing Control Register #2 */
> +#define ASPEED_NO_TIMEOUT_CTRL 0
> +
> +
> +/* 0x0c : I2CD Interrupt Control Register &
> + * 0x10 : I2CD Interrupt Status Register
> + *
> + * These share bit definitions, so use the same values for the enable &
> + * status bits.
> + */
> +#define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14)
> +#define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13)
> +#define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7)
> +#define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6)
> +#define ASPEED_I2CD_INTR_ABNORMAL BIT(5)
> +#define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4)
> +#define ASPEED_I2CD_INTR_ARBIT_LOSS BIT(3)
> +#define ASPEED_I2CD_INTR_RX_DONE BIT(2)
> +#define ASPEED_I2CD_INTR_TX_NAK BIT(1)
> +#define ASPEED_I2CD_INTR_TX_ACK BIT(0)
> +
> +/* 0x14 : I2CD Command/Status Register */
> +#define ASPEED_I2CD_SCL_LINE_STS BIT(18)
> +#define ASPEED_I2CD_SDA_LINE_STS BIT(17)
> +#define ASPEED_I2CD_BUS_BUSY_STS BIT(16)
> +#define ASPEED_I2CD_BUS_RECOVER_CMD BIT(11)
> +
> +/* Command Bit */
> +#define ASPEED_I2CD_M_STOP_CMD BIT(5)
> +#define ASPEED_I2CD_M_S_RX_CMD_LAST BIT(4)
> +#define ASPEED_I2CD_M_RX_CMD BIT(3)
> +#define ASPEED_I2CD_S_TX_CMD BIT(2)
> +#define ASPEED_I2CD_M_TX_CMD BIT(1)
> +#define ASPEED_I2CD_M_START_CMD BIT(0)
> +
> +/* 0x18 : I2CD Slave Device Address Register */
> +#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
> +
> +enum aspeed_i2c_slave_state {
> + ASPEED_I2C_SLAVE_START,
> + ASPEED_I2C_SLAVE_READ_REQUESTED,
> + ASPEED_I2C_SLAVE_READ_PROCESSED,
> + ASPEED_I2C_SLAVE_WRITE_REQUESTED,
> + ASPEED_I2C_SLAVE_WRITE_RECEIVED,
> + ASPEED_I2C_SLAVE_STOP,
> +};
> +
> +struct aspeed_i2c_bus {
> + struct i2c_adapter adap;
> + struct device *dev;
> + void __iomem *base;
> + spinlock_t lock;
> + struct completion cmd_complete;
> + int irq;
> + /* Transaction state. */
> + struct i2c_msg *msg;
> + int msg_pos;
> + u32 cmd_err;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + struct i2c_client *slave;
> + enum aspeed_i2c_slave_state slave_state;
> +#endif
> +};
> +
> +struct aspeed_i2c_controller {
> + struct device *dev;
> + void __iomem *base;
> + int irq;
> + struct irq_domain *irq_domain;
> +};
> +
> +static inline void aspeed_i2c_write(struct aspeed_i2c_bus *bus, u32 val,
> + u32 reg)
> +{
> + writel(val, bus->base + reg);
> +}
> +
> +static inline u32 aspeed_i2c_read(struct aspeed_i2c_bus *bus, u32 reg)
> +{
> + return readl(bus->base + reg);
> +}
> +
> +static u8 aspeed_i2c_recover_bus(struct aspeed_i2c_bus *bus)
> +{
> + u32 command;
> + unsigned long time_left;
> + unsigned long flags;
> + int ret = 0;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> + /* Bus is idle: no recovery needed. */
> + if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> + (command & ASPEED_I2CD_SCL_LINE_STS))
> + goto out;
> +
> + dev_dbg(bus->dev, "bus hung (state %x), attempting recovery\n",
> + command);
> +
> + /* Bus held: put bus in stop state. */
> + if ((command & ASPEED_I2CD_SDA_LINE_STS) &&
> + !(command & ASPEED_I2CD_SCL_LINE_STS)) {
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (time_left == 0)
> + ret = -ETIMEDOUT;
> + else if (bus->cmd_err)
> + ret = -EIO;
> + /* Bus error. */
> + } else if (!(command & ASPEED_I2CD_SDA_LINE_STS)) {
> + aspeed_i2c_write(bus, ASPEED_I2CD_BUS_RECOVER_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (time_left == 0)
> + ret = -ETIMEDOUT;
> + else if (bus->cmd_err)
> + ret = -EIO;
> + /* Recovery failed. */
> + else if (!(aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> + ASPEED_I2CD_SDA_LINE_STS))
> + ret = -EIO;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return ret;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
> +{
> + bool irq_handled = true;
> + u32 command;
> + u32 irq_status;
> + u32 status_ack = 0;
> + u8 value;
> + struct i2c_client *slave = bus->slave;
> +
> + spin_lock(&bus->lock);
> + if (!slave) {
> + irq_handled = false;
> + goto out;
> + }
> + command = aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG);
> + irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> +
> + /* Slave was requested, restart state machine. */
> + if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
> + status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH;
> + bus->slave_state = ASPEED_I2C_SLAVE_START;
> + }
> + /* Slave is not currently active, irq was for someone else. */
> + if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> + irq_handled = false;
> + goto out;
> + }
> +
> + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
> + irq_status, command);
> +
> + /* Slave was sent something. */
> + if (irq_status & ASPEED_I2CD_INTR_RX_DONE) {
> + value = aspeed_i2c_read(bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> + /* Handle address frame. */
> + if (bus->slave_state == ASPEED_I2C_SLAVE_START) {
> + if (value & 0x1)
> + bus->slave_state =
> + ASPEED_I2C_SLAVE_READ_REQUESTED;
> + else
> + bus->slave_state =
> + ASPEED_I2C_SLAVE_WRITE_REQUESTED;
> + }
> + status_ack |= ASPEED_I2CD_INTR_RX_DONE;
> + }
> +
> + /* Slave was asked to stop. */
> + if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
> + status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + }
> + if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
> + status_ack |= ASPEED_I2CD_INTR_TX_NAK;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + }
> +
> + if (bus->slave_state == ASPEED_I2C_SLAVE_READ_REQUESTED) {
> + if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
> + dev_err(bus->dev, "Unexpected ACK on read request.\n");
> + bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
> +
> + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
> + aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
> + status_ack |= ASPEED_I2CD_INTR_TX_ACK;
> + if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
> + dev_err(bus->dev,
> + "Expected ACK after processed read.\n");
> + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
> + aspeed_i2c_write(bus, value, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_S_TX_CMD, ASPEED_I2C_CMD_REG);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_REQUESTED) {
> + bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
> + i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_WRITE_RECEIVED) {
> + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
> + } else if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
> + i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
> + }
> +
> + if (status_ack != irq_status)
> + dev_err(bus->dev,
> + "irq handled != irq. expected %x, but was %x\n",
> + irq_status, status_ack);
> + aspeed_i2c_write(bus, status_ack, ASPEED_I2C_INTR_STS_REG);
> +
> +out:
> + spin_unlock(&bus->lock);
> + return irq_handled;
> +}
> +#endif
> +
> +static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
> +{
> + const u32 errs = ASPEED_I2CD_INTR_ARBIT_LOSS |
> + ASPEED_I2CD_INTR_ABNORMAL |
> + ASPEED_I2CD_INTR_SCL_TIMEOUT |
> + ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> + ASPEED_I2CD_INTR_TX_NAK;
> + u32 irq_status;
> +
> + spin_lock(&bus->lock);
> + irq_status = aspeed_i2c_read(bus, ASPEED_I2C_INTR_STS_REG);
> + bus->cmd_err = irq_status & errs;
> +
> + dev_dbg(bus->dev, "master irq status 0x%08x\n", irq_status);
> +
> + /* No message to transfer. */
> + if (bus->cmd_err ||
> + (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) ||
> + (irq_status & ASPEED_I2CD_INTR_BUS_RECOVER_DONE)) {
> + complete(&bus->cmd_complete);
> + goto out;
> + } else if (!bus->msg || bus->msg_pos >= bus->msg->len)
> + goto out;
> +
> + if ((bus->msg->flags & I2C_M_RD) &&
> + (irq_status & ASPEED_I2CD_INTR_RX_DONE)) {
> + bus->msg->buf[bus->msg_pos++] = aspeed_i2c_read(
> + bus, ASPEED_I2C_BYTE_BUF_REG) >> 8;
> + if (bus->msg_pos + 1 < bus->msg->len)
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD,
> + ASPEED_I2C_CMD_REG);
> + else if (bus->msg_pos < bus->msg->len)
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_RX_CMD |
> + ASPEED_I2CD_M_S_RX_CMD_LAST,
> + ASPEED_I2C_CMD_REG);
> + } else if (!(bus->msg->flags & I2C_M_RD) &&
> + (irq_status & ASPEED_I2CD_INTR_TX_ACK)) {
> + aspeed_i2c_write(bus, bus->msg->buf[bus->msg_pos++],
> + ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_TX_CMD, ASPEED_I2C_CMD_REG);
> + }
> +
> + /* Transmission complete: notify caller. */
> + if (bus->msg_pos >= bus->msg->len)
> + complete(&bus->cmd_complete);
> +out:
> + aspeed_i2c_write(bus, irq_status, ASPEED_I2C_INTR_STS_REG);
> + spin_unlock(&bus->lock);
> + return true;
> +}
> +
> +static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
> +{
> + struct aspeed_i2c_bus *bus = dev_id;
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + if (aspeed_i2c_slave_irq(bus)) {
> + dev_dbg(bus->dev, "irq handled by slave.\n");
> + return IRQ_HANDLED;
> + }
> +#endif
> + if (aspeed_i2c_master_irq(bus)) {
> + dev_dbg(bus->dev, "irq handled by master.\n");
> + return IRQ_HANDLED;
> + }
> + dev_err(bus->dev, "irq not handled properly!\n");
> + return IRQ_HANDLED;
> +}
> +
> +static int aspeed_i2c_master_single_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msg)
> +{
> + struct aspeed_i2c_bus *bus = adap->algo_data;
> + unsigned long flags;
> + u8 slave_addr;
> + u32 command = ASPEED_I2CD_M_START_CMD | ASPEED_I2CD_M_TX_CMD;
> + int ret = msg->len;
> + unsigned long time_left;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + bus->msg = msg;
> + bus->msg_pos = 0;
> + slave_addr = msg->addr << 1;
> + if (msg->flags & I2C_M_RD) {
> + slave_addr |= 1;
> + command |= ASPEED_I2CD_M_RX_CMD;
> + if (msg->len == 1)
> + command |= ASPEED_I2CD_M_S_RX_CMD_LAST;
> + }
> + aspeed_i2c_write(bus, slave_addr, ASPEED_I2C_BYTE_BUF_REG);
> + aspeed_i2c_write(bus, command, ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ * msg->len);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (bus->cmd_err)
> + ret = -EIO;
> + bus->msg = NULL;
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + return ret;
> +}
> +
> +static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs, int num)
> +{
> + struct aspeed_i2c_bus *bus = adap->algo_data;
> + int ret;
> + int i;
> + unsigned long flags;
> + unsigned long time_left;
> +
> + /* If bus is busy, attempt recovery. We assume a single master
> + * environment.
> + */
> + if (aspeed_i2c_read(bus, ASPEED_I2C_CMD_REG) &
> + ASPEED_I2CD_BUS_BUSY_STS) {
> + ret = aspeed_i2c_recover_bus(bus);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < num; i++) {
> + ret = aspeed_i2c_master_single_xfer(adap, &msgs[i]);
> + if (ret < 0)
> + break;
> + /* TODO: Support other forms of I2C protocol mangling. */
> + if (msgs[i].flags & I2C_M_STOP) {
> + spin_lock_irqsave(&bus->lock, flags);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD,
> + ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete,
> + bus->adap.timeout * HZ);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> + }
> + }
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + aspeed_i2c_write(bus, ASPEED_I2CD_M_STOP_CMD, ASPEED_I2C_CMD_REG);
> + reinit_completion(&bus->cmd_complete);
> + spin_unlock_irqrestore(&bus->lock, flags);
> +
> + time_left = wait_for_completion_interruptible_timeout(
> + &bus->cmd_complete, bus->adap.timeout * HZ);
> + if (time_left == 0)
> + return -ETIMEDOUT;
> +
> + /* If nothing went wrong, return number of messages transferred. */
> + if (ret < 0)
> + return ret;
> + else
> + return i;
> +}
> +
> +static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
> +}
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int aspeed_i2c_reg_slave(struct i2c_client *client)
> +{
> + struct aspeed_i2c_bus *bus;
> + unsigned long flags;
> + u32 addr_reg_val;
> + u32 func_ctrl_reg_val;
> +
> + bus = client->adapter->algo_data;
> + spin_lock_irqsave(&bus->lock, flags);
> + if (bus->slave) {
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return -EINVAL;
> + }
> +
> + /* Set slave addr. */
> + addr_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_DEV_ADDR_REG);
> + addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK;
> + addr_reg_val |= client->addr & ASPEED_I2CD_DEV_ADDR_MASK;
> + aspeed_i2c_write(bus, addr_reg_val, ASPEED_I2C_DEV_ADDR_REG);
> +
> + /* Switch from master mode to slave mode. */
> + func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> + func_ctrl_reg_val &= ~ASPEED_I2CD_MASTER_EN;
> + func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
> + aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> + bus->slave = client;
> + bus->slave_state = ASPEED_I2C_SLAVE_STOP;
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return 0;
> +}
> +
> +static int aspeed_i2c_unreg_slave(struct i2c_client *client)
> +{
> + struct aspeed_i2c_bus *bus = client->adapter->algo_data;
> + unsigned long flags;
> + u32 func_ctrl_reg_val;
> +
> + spin_lock_irqsave(&bus->lock, flags);
> + if (!bus->slave) {
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return -EINVAL;
> + }
> +
> + /* Switch from slave mode to master mode. */
> + func_ctrl_reg_val = aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG);
> + func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
> + func_ctrl_reg_val |= ASPEED_I2CD_MASTER_EN;
> + aspeed_i2c_write(bus, func_ctrl_reg_val, ASPEED_I2C_FUN_CTRL_REG);
> +
> + bus->slave = NULL;
> + spin_unlock_irqrestore(&bus->lock, flags);
> + return 0;
> +}
> +#endif
> +
> +static const struct i2c_algorithm aspeed_i2c_algo = {
> + .master_xfer = aspeed_i2c_master_xfer,
> + .functionality = aspeed_i2c_functionality,
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + .reg_slave = aspeed_i2c_reg_slave,
> + .unreg_slave = aspeed_i2c_unreg_slave,
> +#endif
> +};
> +
> +static u32 aspeed_i2c_get_clk_reg_val(u32 divider_ratio)
> +{
> + unsigned int inc = 0, div;
> + u32 scl_low, scl_high, data;
> +
> + for (div = 0; divider_ratio >= 16; div++) {
> + inc |= (divider_ratio & 1);
> + divider_ratio >>= 1;
> + }
> + divider_ratio += inc;
> + scl_low = (divider_ratio >> 1) - 1;
> + scl_high = divider_ratio - scl_low - 2;
> + data = 0x77700300 | (scl_high << 16) | (scl_low << 12) | div;
> + return data;
> +}
> +
> +static int aspeed_i2c_init_clk(struct aspeed_i2c_bus *bus,
> + struct platform_device *pdev)
> +{
> + struct clk *pclk;
> + u32 clk_freq;
> + u32 divider_ratio;
> + int ret;
> +
> + pclk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(pclk)) {
> + dev_err(&pdev->dev, "clk_get failed\n");
> + return PTR_ERR(pclk);
> + }
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "clock-frequency", &clk_freq);
> + if (ret < 0) {
> + dev_err(&pdev->dev,
> + "Could not read clock-frequency property\n");
> + clk_freq = 100000;
> + }
> + divider_ratio = clk_get_rate(pclk) / clk_freq;
> + /* We just need the clock rate, we don't actually use the clk object. */
> + devm_clk_put(&pdev->dev, pclk);
> +
> + /* Set AC Timing */
> + if (clk_freq / 1000 > 400) {
> + aspeed_i2c_write(bus, aspeed_i2c_read(bus,
> + ASPEED_I2C_FUN_CTRL_REG) |
> + ASPEED_I2CD_M_HIGH_SPEED_EN |
> + ASPEED_I2CD_M_SDA_DRIVE_1T_EN |
> + ASPEED_I2CD_SDA_DRIVE_1T_EN,
> + ASPEED_I2C_FUN_CTRL_REG);
> +
> + aspeed_i2c_write(bus, 0x3, ASPEED_I2C_AC_TIMING_REG2);
> + aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> + ASPEED_I2C_AC_TIMING_REG1);
> + } else {
> + aspeed_i2c_write(bus, aspeed_i2c_get_clk_reg_val(divider_ratio),
> + ASPEED_I2C_AC_TIMING_REG1);
> + aspeed_i2c_write(bus, ASPEED_NO_TIMEOUT_CTRL,
> + ASPEED_I2C_AC_TIMING_REG2);
> + }
> +
> + return 0;
> +}
> +
> +static void noop(struct irq_data *data) { }
> +
> +static struct irq_chip aspeed_i2c_irqchip = {
> + .name = "ast-i2c",
> + .irq_unmask = noop,
> + .irq_mask = noop,
> +};
> +
> +static int aspeed_i2c_probe_bus(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_bus *bus;
> + struct aspeed_i2c_controller *controller =
> + dev_get_drvdata(pdev->dev.parent);
> + struct resource *res;
> + int ret, irq;
> + u32 hwirq;
> +
> + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
> + if (!bus)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + bus->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(bus->base))
> + return PTR_ERR(bus->base);
> +
> + bus->irq = platform_get_irq(pdev, 0);
> + if (bus->irq < 0)
> + return -ENXIO;
> + ret = of_property_read_u32(pdev->dev.of_node, "interrupts", &hwirq);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "no I2C 'interrupts' property\n");
> + return -ENXIO;
> + }
> + irq = irq_create_mapping(controller->irq_domain, hwirq);
> + irq_set_chip_data(irq, controller);
> + irq_set_chip_and_handler(irq, &aspeed_i2c_irqchip, handle_simple_irq);
> + ret = devm_request_irq(&pdev->dev, bus->irq, aspeed_i2c_bus_irq,
> + 0, dev_name(&pdev->dev), bus);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request interrupt\n");
> + return -ENXIO;
> + }
> +
> + /* Initialize the I2C adapter */
> + spin_lock_init(&bus->lock);
> + init_completion(&bus->cmd_complete);
> + bus->adap.owner = THIS_MODULE;
> + bus->adap.retries = 0;
> + bus->adap.timeout = 5;
> + bus->adap.algo = &aspeed_i2c_algo;
> + bus->adap.algo_data = bus;
> + bus->adap.dev.parent = &pdev->dev;
> + bus->adap.dev.of_node = pdev->dev.of_node;
> + snprintf(bus->adap.name, sizeof(bus->adap.name), "Aspeed i2c");
> +
> + bus->dev = &pdev->dev;
> +
> + /* reset device: disable master & slave functions */
> + aspeed_i2c_write(bus, 0, ASPEED_I2C_FUN_CTRL_REG);
> +
> + ret = aspeed_i2c_init_clk(bus, pdev);
> + if (ret < 0)
> + return ret;
> +
> + /* Enable Master Mode */
> + aspeed_i2c_write(bus, aspeed_i2c_read(bus, ASPEED_I2C_FUN_CTRL_REG) |
> + ASPEED_I2CD_MASTER_EN |
> + ASPEED_I2CD_MULTI_MASTER_DIS, ASPEED_I2C_FUN_CTRL_REG);
> +
> + /* Set interrupt generation of I2C controller */
> + aspeed_i2c_write(bus, ASPEED_I2CD_INTR_SDA_DL_TIMEOUT |
> + ASPEED_I2CD_INTR_BUS_RECOVER_DONE |
> + ASPEED_I2CD_INTR_SCL_TIMEOUT |
> + ASPEED_I2CD_INTR_ABNORMAL |
> + ASPEED_I2CD_INTR_NORMAL_STOP |
> + ASPEED_I2CD_INTR_ARBIT_LOSS |
> + ASPEED_I2CD_INTR_RX_DONE |
> + ASPEED_I2CD_INTR_TX_NAK |
> + ASPEED_I2CD_INTR_TX_ACK,
> + ASPEED_I2C_INTR_CTRL_REG);
> +
> + ret = i2c_add_adapter(&bus->adap);
> + if (ret < 0)
> + return -ENXIO;
> +
> + platform_set_drvdata(pdev, bus);
> +
> + dev_info(bus->dev, "i2c bus %d registered, irq %d\n",
> + bus->adap.nr, bus->irq);
> +
> + return 0;
> +}
> +
> +static int aspeed_i2c_remove_bus(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
> +
> + i2c_del_adapter(&bus->adap);
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_bus_of_table[] = {
> + { .compatible = "aspeed,ast2400-i2c-bus", },
> + { .compatible = "aspeed,ast2500-i2c-bus", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
> +
> +static struct platform_driver aspeed_i2c_bus_driver = {
> + .probe = aspeed_i2c_probe_bus,
> + .remove = aspeed_i2c_remove_bus,
> + .driver = {
> + .name = "ast-i2c-bus",
> + .of_match_table = aspeed_i2c_bus_of_table,
> + },
> +};
> +
> +static void aspeed_i2c_controller_irq(struct irq_desc *desc)
> +{
> + struct aspeed_i2c_controller *c = irq_desc_get_handler_data(desc);
> + unsigned long p, status;
> + unsigned int bus_irq;
> +
> + status = readl(c->base);
> + for_each_set_bit(p, &status, ASPEED_I2C_NUM_BUS) {
> + bus_irq = irq_find_mapping(c->irq_domain, p);
> + generic_handle_irq(bus_irq);
> + }
> +}
> +
> +static int aspeed_i2c_probe_controller(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_controller *controller;
> + struct device_node *np;
> + struct resource *res;
> +
> + controller = kzalloc(sizeof(*controller), GFP_KERNEL);
> + if (!controller)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + controller->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(controller->base))
> + return PTR_ERR(controller->base);
> +
> + controller->irq = platform_get_irq(pdev, 0);
> + if (controller->irq < 0)
> + return -ENXIO;
> +
> + controller->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
> + ASPEED_I2C_NUM_BUS, &irq_domain_simple_ops, NULL);
> + if (!controller->irq_domain)
> + return -ENXIO;
> + controller->irq_domain->name = "ast-i2c-domain";
> +
> + irq_set_chained_handler_and_data(controller->irq,
> + aspeed_i2c_controller_irq, controller);
> +
> + controller->dev = &pdev->dev;
> +
> + platform_set_drvdata(pdev, controller);
> +
> + dev_info(controller->dev, "i2c controller registered, irq %d\n",
> + controller->irq);
> +
> + for_each_child_of_node(pdev->dev.of_node, np) {
> + of_platform_device_create(np, NULL, &pdev->dev);
> + of_node_put(np);
> + }
> +
> + return 0;
> +}
> +
> +static int aspeed_i2c_remove_controller(struct platform_device *pdev)
> +{
> + struct aspeed_i2c_controller *controller = platform_get_drvdata(pdev);
> +
> + irq_domain_remove(controller->irq_domain);
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_i2c_controller_of_table[] = {
> + { .compatible = "aspeed,ast2400-i2c-controller", },
> + { .compatible = "aspeed,ast2500-i2c-controller", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_i2c_controller_of_table);
> +
> +static struct platform_driver aspeed_i2c_controller_driver = {
> + .probe = aspeed_i2c_probe_controller,
> + .remove = aspeed_i2c_remove_controller,
> + .driver = {
> + .name = "ast-i2c-controller",
> + .of_match_table = aspeed_i2c_controller_of_table,
> + },
> +};
> +
> +static int __init aspeed_i2c_driver_init(void)
> +{
> + int ret;
> +
> + ret = platform_driver_register(&aspeed_i2c_controller_driver);
> + if (ret < 0)
> + return ret;
> + return platform_driver_register(&aspeed_i2c_bus_driver);
> +}
> +module_init(aspeed_i2c_driver_init);
> +
> +static void __exit aspeed_i2c_driver_exit(void)
> +{
> + platform_driver_unregister(&aspeed_i2c_bus_driver);
> + platform_driver_unregister(&aspeed_i2c_controller_driver);
> +}
> +module_exit(aspeed_i2c_driver_exit);
> +
> +MODULE_AUTHOR("Brendan Higgins <brendanhiggins@google.com>");
> +MODULE_DESCRIPTION("Aspeed I2C Bus Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.8.0.rc3.226.g39d4020
>
^ permalink raw reply
* Re: [PATCH v4 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
From: Joel Stanley @ 2016-11-15 11:16 UTC (permalink / raw)
To: Rob Herring
Cc: Brendan Higgins, Wolfram Sang, Mark Rutland,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, OpenBMC Maillist
In-Reply-To: <20161114155731.p4kxmefqevplphtd@rob-hp-laptop>
On Tue, Nov 15, 2016 at 2:27 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Fri, Nov 04, 2016 at 06:58:19PM -0700, Brendan Higgins wrote:
>> Added device tree binding documentation for Aspeed I2C controller and
>> busses.
>>
>> Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes for v2:
>> - None
>> Changes for v3:
>> - Removed reference to "bus" device tree param
>> Changes for v4:
>> - None
>> ---
>> .../devicetree/bindings/i2c/i2c-aspeed.txt | 61 ++++++++++++++++++++++
>> 1 file changed, 61 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Thanks Rob.
Acked-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
Cheers,
Joel
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCHv7 08/11] i2c: match vendorless strings on the internal string length
From: Kieran Bingham @ 2016-11-15 9:49 UTC (permalink / raw)
To: Wolfram Sang
Cc: Lee Jones, linux-i2c, linux-kernel, Javier Martinez Canillas,
sameo
In-Reply-To: <20161114203208.GA4207@katana>
On 14/11/16 22:27, Wolfram Sang wrote:
> On Mon, Nov 07, 2016 at 12:47:43PM +0000, Kieran Bingham wrote:
>> If a user provides a shortened string to match a device to the sysfs i2c
>> interface it will match on the first string that contains that string
>> prefix.
>>
>> for example:
>> echo a 0x68 > /sys/bus/i2c/devices/i2c-2/new_device
>> will match as3711, as3722, and ak8975 incorrectly.
>>
>> Correct this by using sysfs_streq to match the string exactly
>>
>> Signed-off-by: Kieran Bingham <kieran@bingham.xyz>
>
> Any objections that I squash this directly into patch 2? It is more like
> a bugfix, no?
No objection
Acked-by: Kieran Bingham <kieran@bingham.xyz>
--
Regards
Kieran Bingham
^ permalink raw reply
* Re: [PATCHv7 07/11] i2c: match dt-style device names from sysfs interface
From: Kieran Bingham @ 2016-11-15 9:48 UTC (permalink / raw)
To: Wolfram Sang
Cc: Lee Jones, linux-i2c, linux-kernel, Javier Martinez Canillas,
sameo
In-Reply-To: <20161114194953.GB3603@katana>
On 14/11/16 22:27, Wolfram Sang wrote:
> On Mon, Nov 07, 2016 at 12:47:42PM +0000, Kieran Bingham wrote:
>> A user can choose to instantiate a device on an i2c bus using the sysfs
>> interface by providing a string and address to match and communicate
>> with the device on the bus. Presently this string is only matched
>> against the old i2c device id style strings, even in the presence of
>> full device tree compatible strings with vendor prefixes.
>>
>> Providing a vendor-prefixed string to the sysfs interface will not match
>> against the device tree of_match_device() calls as there is no device
>> tree node to parse from the sysfs interface.
>>
>> Convert i2c_of_match_device_strip_vendor() such that it can match both
>
> The function name here is the old one...
>
>> vendor prefixed and stripped compatible strings on the sysfs interface.
>>
>> Signed-off-by: Kieran Bingham <kieran@bingham.xyz>
>
> ... and in patch 2, the sentence "remove this function if all drivers
> are converted" is obsolete, too, since we need this function always for
> sysfs.
>
> This make me wonder if we shouldn't squash this patch also in into patch
> 2 (like I suggested for the next one), and create a best-of-all-worlds
> commit message from these three patches?
>
> Opinions?
That's fine with me - My main reason for keeping them separate during
posting was so that the changes I had made could be seen - but yes - I
think they probably are eligible for squashing.
--
Regards
Kieran Bingham
^ permalink raw reply
* re:
From: Apply @ 2016-11-15 4:40 UTC (permalink / raw)
To: Recipients
Do you need loan?we offer all kinds of loan from minimum amount of $5,000 to maximum of $2,000,000 if you are interested contact us via:internationalloanplc1@gmail.com with the information below:
Full Name:
Country:
Loan Amount:
Loan Duration:
Mobile phone number:
Sex:
Thanks,
Dr Scott.
^ permalink raw reply
* Re: [PATCH] i2c: mux: fix up dependencies
From: Peter Rosin @ 2016-11-14 13:59 UTC (permalink / raw)
To: Linus Walleij, Wolfram Sang, linux-i2c; +Cc: stable, Jonathan Cameron
In-Reply-To: <1479128586-17052-1-git-send-email-linus.walleij@linaro.org>
On 2016-11-14 14:03, Linus Walleij wrote:
> We get the following build error from UM Linux after adding
> an entry to drivers/iio/gyro/Kconfig that issues "select I2C_MUX":
>
> ERROR: "devm_ioremap_resource"
> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
> ERROR: "of_address_to_resource"
> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
>
> It appears that the I2C mux core code actually only requires
> CONFIG_OF, but depends on HAS_IOMEM for historical reasons,
> while CONFIG_I2C_MUX_REG does *not* have a direct dependency
> on HAS_IOMEM.
>
> This creates a situation where a allyesconfig or allmodconfig
> for UM Linux will select I2C_MUX, and will implicitly enable
> I2C_MUX_REG as well, and the compilation will fail for the
> register driver.
>
> Fix this up by making I2C_MUX_REG depend on HAS_IOMEM and
> the I2C_MUX depend on OF.
>
> Cc: stable@vger.kernel.org
> Reported-by: kbuild test robot <fengguang.wu@intel.com>
> Reported-by: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
> Cc: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
> Cc: Peter Rosin <peda@axentia.se>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> drivers/i2c/Kconfig | 2 +-
> drivers/i2c/muxes/Kconfig | 1 +
> 2 files changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index d223650a97e4..b9e378cfe1d1 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -59,7 +59,7 @@ config I2C_CHARDEV
>
> config I2C_MUX
> tristate "I2C bus multiplexing support"
> - depends on HAS_IOMEM
> + depends on OF
It is not obvious to me that the i2c mux is dependent on
CONFIG_OF. Sure, there are unconditional calls to things like
of_property_read_u32 etc, but those are meant to be compiled
away for the !CONFIG_OF case. I think. The code predates me,
but that's how I read it...
So, I agree the HAS_IOMEM is probably hysterical remains from
some point when i2c-mux was perhaps a specific thing that actually
controlled the mux using IOMEM from code under that config option,
and it should probably be moved to where it is needed (below).
But don't add "depends on OF", or am I missing something?
Cheers,
Peter
> help
> Say Y here if you want the I2C core to support the ability to
> handle multiplexed I2C bus topologies, by presenting each
> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> index e280c8ecc0b5..96de9ce5669b 100644
> --- a/drivers/i2c/muxes/Kconfig
> +++ b/drivers/i2c/muxes/Kconfig
> @@ -63,6 +63,7 @@ config I2C_MUX_PINCTRL
>
> config I2C_MUX_REG
> tristate "Register-based I2C multiplexer"
> + depends on HAS_IOMEM
> help
> If you say yes to this option, support will be included for a
> register based I2C multiplexer. This driver provides access to
>
^ permalink raw reply
* Re: [PATCHv7 07/11] i2c: match dt-style device names from sysfs interface
From: Wolfram Sang @ 2016-11-14 22:27 UTC (permalink / raw)
To: Kieran Bingham
Cc: Lee Jones, linux-i2c, linux-kernel, Javier Martinez Canillas,
sameo
In-Reply-To: <1478522866-29620-8-git-send-email-kieran@bingham.xyz>
[-- Attachment #1: Type: text/plain, Size: 1231 bytes --]
On Mon, Nov 07, 2016 at 12:47:42PM +0000, Kieran Bingham wrote:
> A user can choose to instantiate a device on an i2c bus using the sysfs
> interface by providing a string and address to match and communicate
> with the device on the bus. Presently this string is only matched
> against the old i2c device id style strings, even in the presence of
> full device tree compatible strings with vendor prefixes.
>
> Providing a vendor-prefixed string to the sysfs interface will not match
> against the device tree of_match_device() calls as there is no device
> tree node to parse from the sysfs interface.
>
> Convert i2c_of_match_device_strip_vendor() such that it can match both
The function name here is the old one...
> vendor prefixed and stripped compatible strings on the sysfs interface.
>
> Signed-off-by: Kieran Bingham <kieran@bingham.xyz>
... and in patch 2, the sentence "remove this function if all drivers
are converted" is obsolete, too, since we need this function always for
sysfs.
This make me wonder if we shouldn't squash this patch also in into patch
2 (like I suggested for the next one), and create a best-of-all-worlds
commit message from these three patches?
Opinions?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCHv7 08/11] i2c: match vendorless strings on the internal string length
From: Wolfram Sang @ 2016-11-14 22:27 UTC (permalink / raw)
To: Kieran Bingham
Cc: Lee Jones, linux-i2c, linux-kernel, Javier Martinez Canillas,
sameo
In-Reply-To: <1478522866-29620-9-git-send-email-kieran@bingham.xyz>
[-- Attachment #1: Type: text/plain, Size: 576 bytes --]
On Mon, Nov 07, 2016 at 12:47:43PM +0000, Kieran Bingham wrote:
> If a user provides a shortened string to match a device to the sysfs i2c
> interface it will match on the first string that contains that string
> prefix.
>
> for example:
> echo a 0x68 > /sys/bus/i2c/devices/i2c-2/new_device
> will match as3711, as3722, and ak8975 incorrectly.
>
> Correct this by using sysfs_streq to match the string exactly
>
> Signed-off-by: Kieran Bingham <kieran@bingham.xyz>
Any objections that I squash this directly into patch 2? It is more like
a bugfix, no?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Steven J. Hill @ 2016-11-14 19:53 UTC (permalink / raw)
To: Jan Glauber, Wolfram Sang; +Cc: linux-i2c, linux-mips, Paul Burton, David Daney
In-Reply-To: <1479149445-4663-1-git-send-email-jglauber@cavium.com>
On 11/14/2016 12:50 PM, Jan Glauber wrote:
>
> Since time is running out for 4.9 (or might have already if you're not
> going to send another pull request) I'm going for the safe option
> to fix the Octeon i2c problems, which is:
>
> 1. Reverting the readq_poll_timeout patch since it is broken
> 2. Apply Patch #2 from Paul
> 3. Add a small fix for the recovery that makes Paul's patch
> work on ThunderX
>
> I'll try to come up with a better solution for 4.10. My plan is to get rid
> of the polling-around-interrupt thing completely, but for that we need more
> time to make it work on Octeon.
>
> Please consider for 4.9.
>
Hey Jan.
This does not work on Octeon 71xx platforms. I will look at it more
closely tomorrow.
Steve
^ permalink raw reply
* [PATCH v2 3/3] i2c: octeon: thunderx: TWSI software reset in recovery
From: Jan Glauber @ 2016-11-14 18:50 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, linux-mips, Paul Burton, David Daney, Jan Glauber
In-Reply-To: <1479149445-4663-1-git-send-email-jglauber@cavium.com>
I've seen i2c recovery reporting long loops of:
[ 1035.887818] i2c i2c-4: SCL is stuck low, exit recovery
[ 1037.999748] i2c i2c-4: SCL is stuck low, exit recovery
[ 1040.111694] i2c i2c-4: SCL is stuck low, exit recovery
...
Add a TWSI software reset which clears the status and
STA,STP,IFLG in SW_TWSI_EOP_TWSI_CTL.
With this the recovery works fine and above message is not seen.
Signed-off-by: Jan Glauber <jglauber@cavium.com>
---
drivers/i2c/busses/i2c-octeon-core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
index 54a9c14..c3b63c1 100644
--- a/drivers/i2c/busses/i2c-octeon-core.c
+++ b/drivers/i2c/busses/i2c-octeon-core.c
@@ -760,6 +760,9 @@ static void octeon_i2c_prepare_recovery(struct i2c_adapter *adap)
struct octeon_i2c *i2c = i2c_get_adapdata(adap);
octeon_i2c_hlc_disable(i2c);
+ octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0);
+ /* wait for software reset to settle */
+ udelay(5);
/*
* Bring control register to a good state regardless
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/3] i2c: octeon: Fix waiting for operation completion
From: Jan Glauber @ 2016-11-14 18:50 UTC (permalink / raw)
To: Wolfram Sang
Cc: linux-i2c, linux-mips, Paul Burton, David Daney, Jan Glauber,
Peter Swain
In-Reply-To: <1479149445-4663-1-git-send-email-jglauber@cavium.com>
From: Paul Burton <paul.burton@imgtec.com>
Commit 1bb1ff3e7c74 ("i2c: octeon: Improve performance if interrupt is
early") modified octeon_i2c_wait() & octeon_i2c_hlc_wait() to attempt to
check for a valid bit being clear & if not to sleep for a while then try
again before waiting on a waitqueue which may time out. However it does
so by sleeping within a function called as the condition provided to
wait_event_timeout() which seems to cause strange behaviour, with the
system hanging during boot with the condition being checked constantly &
the timeout not seeming to have any effect.
Fix this by instead checking for the valid bit being clear in the
octeon_i2c(_hlc)_wait() functions & sleeping there if that condition is
not met, then calling the wait_event_timeout with a condition that does
not sleep.
Tested on a Rhino Labs UTM-8 with Octeon CN7130.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Signed-off-by: Jan Glauber <jglauber@cavium.com>
Cc: David Daney <david.daney@cavium.com>
Cc: Jan Glauber <jglauber@cavium.com>
[jglauber@cavium.com: removed unused variable]
Cc: Peter Swain <pswain@cavium.com>
Cc: Wolfram Sang <wsa@the-dreams.de>
Cc: linux-i2c@vger.kernel.org
---
drivers/i2c/busses/i2c-octeon-core.c | 59 +++++++++---------------------------
1 file changed, 15 insertions(+), 44 deletions(-)
diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
index 5e63b17..54a9c14 100644
--- a/drivers/i2c/busses/i2c-octeon-core.c
+++ b/drivers/i2c/busses/i2c-octeon-core.c
@@ -36,24 +36,6 @@ static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c)
return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG);
}
-static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first)
-{
- if (octeon_i2c_test_iflg(i2c))
- return true;
-
- if (*first) {
- *first = false;
- return false;
- }
-
- /*
- * IRQ has signaled an event but IFLG hasn't changed.
- * Sleep and retry once.
- */
- usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
- return octeon_i2c_test_iflg(i2c);
-}
-
/**
* octeon_i2c_wait - wait for the IFLG to be set
* @i2c: The struct octeon_i2c
@@ -63,7 +45,6 @@ static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first)
static int octeon_i2c_wait(struct octeon_i2c *i2c)
{
long time_left;
- bool first = true;
/*
* Some chip revisions don't assert the irq in the interrupt
@@ -80,8 +61,13 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
}
i2c->int_enable(i2c);
- time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first),
- i2c->adap.timeout);
+ time_left = i2c->adap.timeout;
+ if (!octeon_i2c_test_iflg(i2c)) {
+ usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
+ time_left = wait_event_timeout(i2c->queue,
+ octeon_i2c_test_iflg(i2c),
+ time_left);
+ }
i2c->int_disable(i2c);
if (i2c->broken_irq_check && !time_left &&
@@ -99,26 +85,8 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c)
{
- return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
-}
-
-static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first)
-{
/* check if valid bit is cleared */
- if (octeon_i2c_hlc_test_valid(i2c))
- return true;
-
- if (*first) {
- *first = false;
- return false;
- }
-
- /*
- * IRQ has signaled an event but valid bit isn't cleared.
- * Sleep and retry once.
- */
- usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
- return octeon_i2c_hlc_test_valid(i2c);
+ return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
}
static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c)
@@ -176,7 +144,6 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
*/
static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
{
- bool first = true;
int time_left;
/*
@@ -194,9 +161,13 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
}
i2c->hlc_int_enable(i2c);
- time_left = wait_event_timeout(i2c->queue,
- octeon_i2c_hlc_test_ready(i2c, &first),
- i2c->adap.timeout);
+ time_left = i2c->adap.timeout;
+ if (!octeon_i2c_hlc_test_valid(i2c)) {
+ usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
+ time_left = wait_event_timeout(i2c->queue,
+ octeon_i2c_hlc_test_valid(i2c),
+ time_left);
+ }
i2c->hlc_int_disable(i2c);
if (!time_left)
octeon_i2c_hlc_int_clear(i2c);
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/3] Revert "i2c: octeon: thunderx: Limit register access retries"
From: Jan Glauber @ 2016-11-14 18:50 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, linux-mips, Paul Burton, David Daney, Jan Glauber
In-Reply-To: <1479149445-4663-1-git-send-email-jglauber@cavium.com>
This reverts commit 70121f7f3725 ("i2c: octeon: thunderx: Limit register access retries").
Using readq_poll_timeout instead of __raw_readq triggers the following
debug warning:
[ 78.871568] ipmi_ssif: Trying hotmod-specified SSIF interface at i2c address 0x12, adapter Cavium ThunderX i2c adapter at 0000:01:09.4, slave address 0x0
[ 78.886107] do not call blocking ops when !TASK_RUNNING; state=2 set at [<fffffc00080e0088>] prepare_to_wait_event+0x58/0x10c
[ 78.897436] ------------[ cut here ]------------
[ 78.902050] WARNING: CPU: 6 PID: 2235 at kernel/sched/core.c:7718 __might_sleep+0x80/0x88
[...]
[ 79.133553] [<fffffc00080c3aac>] __might_sleep+0x80/0x88
[ 79.138862] [<fffffc0000e30138>] octeon_i2c_test_iflg+0x4c/0xbc [i2c_thunderx]
[ 79.146077] [<fffffc0000e30958>] octeon_i2c_test_ready+0x18/0x70 [i2c_thunderx]
[ 79.153379] [<fffffc0000e30b04>] octeon_i2c_wait+0x154/0x1a4 [i2c_thunderx]
[ 79.160334] [<fffffc0000e310bc>] octeon_i2c_xfer+0xf4/0xf60 [i2c_thunderx]
Signed-off-by: Jan Glauber <jglauber@cavium.com>
---
drivers/i2c/busses/i2c-octeon-core.c | 4 +---
drivers/i2c/busses/i2c-octeon-core.h | 27 +++++++++++----------------
2 files changed, 12 insertions(+), 19 deletions(-)
diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
index 419b54b..5e63b17 100644
--- a/drivers/i2c/busses/i2c-octeon-core.c
+++ b/drivers/i2c/busses/i2c-octeon-core.c
@@ -381,9 +381,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
if (result)
return result;
- data[i] = octeon_i2c_data_read(i2c, &result);
- if (result)
- return result;
+ data[i] = octeon_i2c_data_read(i2c);
if (recv_len && i == 0) {
if (data[i] > I2C_SMBUS_BLOCK_MAX + 1)
return -EPROTO;
diff --git a/drivers/i2c/busses/i2c-octeon-core.h b/drivers/i2c/busses/i2c-octeon-core.h
index 1db7c83..87151ea 100644
--- a/drivers/i2c/busses/i2c-octeon-core.h
+++ b/drivers/i2c/busses/i2c-octeon-core.h
@@ -5,7 +5,6 @@
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/io.h>
-#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -145,9 +144,9 @@ static inline void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8
u64 tmp;
__raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI(i2c));
-
- readq_poll_timeout(i2c->twsi_base + SW_TWSI(i2c), tmp, tmp & SW_TWSI_V,
- I2C_OCTEON_EVENT_WAIT, i2c->adap.timeout);
+ do {
+ tmp = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ } while ((tmp & SW_TWSI_V) != 0);
}
#define octeon_i2c_ctl_write(i2c, val) \
@@ -164,28 +163,24 @@ static inline void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8
*
* The I2C core registers are accessed indirectly via the SW_TWSI CSR.
*/
-static inline int octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg,
- int *error)
+static inline u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg)
{
u64 tmp;
- int ret;
__raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI(i2c));
+ do {
+ tmp = __raw_readq(i2c->twsi_base + SW_TWSI(i2c));
+ } while ((tmp & SW_TWSI_V) != 0);
- ret = readq_poll_timeout(i2c->twsi_base + SW_TWSI(i2c), tmp,
- tmp & SW_TWSI_V, I2C_OCTEON_EVENT_WAIT,
- i2c->adap.timeout);
- if (error)
- *error = ret;
return tmp & 0xFF;
}
#define octeon_i2c_ctl_read(i2c) \
- octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL, NULL)
-#define octeon_i2c_data_read(i2c, error) \
- octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA, error)
+ octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL)
+#define octeon_i2c_data_read(i2c) \
+ octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA)
#define octeon_i2c_stat_read(i2c) \
- octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT, NULL)
+ octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT)
/**
* octeon_i2c_read_int - read the TWSI_INT register
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/3] i2c: octeon: thunder: Fix i2c not working on Octeon
From: Jan Glauber @ 2016-11-14 18:50 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, linux-mips, Paul Burton, David Daney, Jan Glauber
Hi Wolfram,
Since time is running out for 4.9 (or might have already if you're not
going to send another pull request) I'm going for the safe option
to fix the Octeon i2c problems, which is:
1. Reverting the readq_poll_timeout patch since it is broken
2. Apply Patch #2 from Paul
3. Add a small fix for the recovery that makes Paul's patch
work on ThunderX
I'll try to come up with a better solution for 4.10. My plan is to get rid
of the polling-around-interrupt thing completely, but for that we need more
time to make it work on Octeon.
Please consider for 4.9.
thanks,
Jan
------------
Jan Glauber (2):
Revert "i2c: octeon: thunderx: Limit register access retries"
i2c: octeon: thunderx: TWSI software reset in recovery
Paul Burton (1):
i2c: octeon: Fix waiting for operation completion
drivers/i2c/busses/i2c-octeon-core.c | 66 +++++++++++-------------------------
drivers/i2c/busses/i2c-octeon-core.h | 27 ++++++---------
2 files changed, 30 insertions(+), 63 deletions(-)
--
1.9.1
^ permalink raw reply
* Re: [PATCH v2] i2c: mux: fix up dependencies
From: Peter Rosin @ 2016-11-14 14:39 UTC (permalink / raw)
To: Linus Walleij, Wolfram Sang, linux-i2c; +Cc: stable, Jonathan Cameron
In-Reply-To: <1479134057-30653-1-git-send-email-linus.walleij@linaro.org>
On 2016-11-14 15:34, Linus Walleij wrote:
> We get the following build error from UM Linux after adding
> an entry to drivers/iio/gyro/Kconfig that issues "select I2C_MUX":
>
> ERROR: "devm_ioremap_resource"
> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
> ERROR: "of_address_to_resource"
> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
>
> It appears that the I2C mux core code depends on HAS_IOMEM
> for historical reasons, while CONFIG_I2C_MUX_REG does *not*
> have a direct dependency on HAS_IOMEM.
>
> This creates a situation where a allyesconfig or allmodconfig
> for UM Linux will select I2C_MUX, and will implicitly enable
> I2C_MUX_REG as well, and the compilation will fail for the
> register driver.
>
> Fix this up by making I2C_MUX_REG depend on HAS_IOMEM and
> removing the dependency from I2C_MUX.
>
> Cc: stable@vger.kernel.org
> Reported-by: kbuild test robot <fengguang.wu@intel.com>
> Reported-by: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
> Cc: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
> Cc: Peter Rosin <peda@axentia.se>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Peter Rosin <peda@axentia.se>
Thanks!
Cheers,
Peter
^ permalink raw reply
* Re: [PATCH v4 3/3] dt-bindings: i2c: pxa: Update the documentation for the Armada 3700
From: Rob Herring @ 2016-11-14 17:28 UTC (permalink / raw)
To: Romain Perier
Cc: Wolfram Sang, linux-i2c, devicetree, Ian Campbell, Pawel Moll,
Mark Rutland, Kumar Gala, Jason Cooper, Andrew Lunn,
Sebastian Hesselbarth, Gregory Clement, linux-arm-kernel,
Thomas Petazzoni, Nadav Haklai, Omri Itach, Shadi Ammouri,
Yahuda Yitschak, Hanna Hawa, Neta Zur Hershkovits, Igal Liberman
In-Reply-To: <20161109115715.2557-4-romain.perier@free-electrons.com>
On Wed, Nov 09, 2016 at 12:57:15PM +0100, Romain Perier wrote:
> This commit documents the compatible string to have the compatibility for
> the I2C unit found in the Armada 3700.
>
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
> ---
>
> Changes in v2:
> - Fixed wrong compatible string, it should be "marvell,armada-3700-i2c"
> and not "marvell,armada-3700".
>
> Documentation/devicetree/bindings/i2c/i2c-pxa.txt | 1 +
> 1 file changed, 1 insertion(+)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v2] i2c: mux: fix up dependencies
From: Wolfram Sang @ 2016-11-14 17:24 UTC (permalink / raw)
To: Linus Walleij; +Cc: linux-i2c, stable, Jonathan Cameron, Peter Rosin
In-Reply-To: <1479134057-30653-1-git-send-email-linus.walleij@linaro.org>
[-- Attachment #1: Type: text/plain, Size: 1209 bytes --]
On Mon, Nov 14, 2016 at 03:34:17PM +0100, Linus Walleij wrote:
> We get the following build error from UM Linux after adding
> an entry to drivers/iio/gyro/Kconfig that issues "select I2C_MUX":
>
> ERROR: "devm_ioremap_resource"
> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
> ERROR: "of_address_to_resource"
> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
>
> It appears that the I2C mux core code depends on HAS_IOMEM
> for historical reasons, while CONFIG_I2C_MUX_REG does *not*
> have a direct dependency on HAS_IOMEM.
>
> This creates a situation where a allyesconfig or allmodconfig
> for UM Linux will select I2C_MUX, and will implicitly enable
> I2C_MUX_REG as well, and the compilation will fail for the
> register driver.
>
> Fix this up by making I2C_MUX_REG depend on HAS_IOMEM and
> removing the dependency from I2C_MUX.
>
> Cc: stable@vger.kernel.org
> Reported-by: kbuild test robot <fengguang.wu@intel.com>
> Reported-by: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
> Cc: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
> Cc: Peter Rosin <peda@axentia.se>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Applied to for-current, thanks!
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v2] i2c: mux: fix up dependencies
From: Jonathan Cameron @ 2016-11-14 17:13 UTC (permalink / raw)
To: Peter Rosin, Linus Walleij, Wolfram Sang, linux-i2c; +Cc: stable
In-Reply-To: <6094de61-8745-fbff-e516-db95392aa142@axentia.se>
On 14 November 2016 14:39:54 GMT+00:00, Peter Rosin <peda@axentia.se> wrote:
>On 2016-11-14 15:34, Linus Walleij wrote:
>> We get the following build error from UM Linux after adding
>> an entry to drivers/iio/gyro/Kconfig that issues "select I2C_MUX":
>>
>> ERROR: "devm_ioremap_resource"
>> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
>> ERROR: "of_address_to_resource"
>> [drivers/i2c/muxes/i2c-mux-reg.ko] undefined!
>>
>> It appears that the I2C mux core code depends on HAS_IOMEM
>> for historical reasons, while CONFIG_I2C_MUX_REG does *not*
>> have a direct dependency on HAS_IOMEM.
>>
>> This creates a situation where a allyesconfig or allmodconfig
>> for UM Linux will select I2C_MUX, and will implicitly enable
>> I2C_MUX_REG as well, and the compilation will fail for the
>> register driver.
>>
>> Fix this up by making I2C_MUX_REG depend on HAS_IOMEM and
>> removing the dependency from I2C_MUX.
>>
>> Cc: stable@vger.kernel.org
>> Reported-by: kbuild test robot <fengguang.wu@intel.com>
>> Reported-by: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
>> Cc: Jonathan Cameron <jic23@jic23.retrosnub.co.uk>
>> Cc: Peter Rosin <peda@axentia.se>
>> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
>
>Acked-by: Peter Rosin <peda@axentia.se>
>
>Thanks!
Thanks for sorting this.
Acked-by: Jonathan Cameron <jic23@kernel.org>
>
>Cheers,
>Peter
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox