nxp: i2c driver support.

NXP I2C driver support for NXP SoC(s).

Signed-off-by: York Sun <york.sun@nxp.com>
Signed-off-by: Pankaj Gupta <pankaj.gupta@nxp.com>
Change-Id: I234b76f9fa1b30dd13aa087001411370cc6c8dd0
diff --git a/drivers/nxp/i2c/i2c.c b/drivers/nxp/i2c/i2c.c
new file mode 100644
index 0000000..9281409
--- /dev/null
+++ b/drivers/nxp/i2c/i2c.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2016-2020 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include "i2c.h"
+#include <nxp_timer.h>
+
+static uintptr_t g_nxp_i2c_addr;
+
+void i2c_init(uintptr_t nxp_i2c_addr)
+{
+	struct ls_i2c *ccsr_i2c = (void *)nxp_i2c_addr;
+
+	g_nxp_i2c_addr = nxp_i2c_addr;
+	/* Presume workaround for erratum a009203 applied */
+	i2c_out(&ccsr_i2c->cr, I2C_CR_DIS);
+	i2c_out(&ccsr_i2c->fd, I2C_FD_CONSERV);
+	i2c_out(&ccsr_i2c->sr, I2C_SR_RST);
+	i2c_out(&ccsr_i2c->cr, I2C_CR_EN);
+}
+
+static int wait_for_state(struct ls_i2c *ccsr_i2c,
+			  unsigned char state, unsigned char mask)
+{
+	unsigned char sr;
+	uint64_t start_time = get_timer_val(0);
+	uint64_t timer;
+
+	do {
+		sr = i2c_in(&ccsr_i2c->sr);
+		if (sr & I2C_SR_AL) {
+			i2c_out(&ccsr_i2c->sr, sr);
+			WARN("I2C arbitration lost\n");
+			return -EIO;
+		}
+		if ((sr & mask) == state) {
+			return (int)sr;
+		}
+
+		timer = get_timer_val(start_time);
+		if (timer > I2C_TIMEOUT)
+			break;
+		mdelay(1);
+	} while (1);
+	WARN("I2C: Timeout waiting for state 0x%x, sr = 0x%x\n", state, sr);
+
+	return -ETIMEDOUT;
+}
+
+static int tx_byte(struct ls_i2c *ccsr_i2c, unsigned char c)
+{
+	int ret;
+
+	i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
+	i2c_out(&ccsr_i2c->dr, c);
+	ret = wait_for_state(ccsr_i2c, I2C_SR_IF, I2C_SR_IF);
+	if (ret < 0) {
+		WARN("%s: state error\n", __func__);
+		return ret;
+	}
+	if (ret & I2C_SR_RX_NAK) {
+		WARN("%s: nodev\n", __func__);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int gen_stop(struct ls_i2c *ccsr_i2c)
+{
+	unsigned char cr;
+	int ret;
+
+	cr = i2c_in(&ccsr_i2c->cr);
+	cr &= ~(I2C_CR_MA | I2C_CR_TX);
+	i2c_out(&ccsr_i2c->cr, cr);
+	ret = wait_for_state(ccsr_i2c, I2C_SR_IDLE, I2C_SR_BB);
+	if (ret < 0) {
+		WARN("I2C: Generating stop failed.\n");
+	}
+	return ret;
+}
+
+static int i2c_write_addr(struct ls_i2c *ccsr_i2c, unsigned char chip,
+			  int addr, int alen)
+{
+	int ret;
+	unsigned char cr;
+
+	if (alen != 1) {
+		WARN("I2C: Unsupported address len [%d]\n", alen);
+		return -EIO;
+	}
+
+	if (i2c_in(&ccsr_i2c->ad) == (chip << 1)) {
+		WARN("I2C: slave address same as self\n");
+		return -ENODEV;
+	}
+	i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
+	ret = wait_for_state(ccsr_i2c, I2C_SR_IDLE, I2C_SR_BB);
+	if (ret < 0) {
+		return ret;
+	}
+
+	cr = i2c_in(&ccsr_i2c->cr);
+	cr |= I2C_CR_MA;
+	i2c_out(&ccsr_i2c->cr, cr);
+	ret = wait_for_state(ccsr_i2c, I2C_SR_BB, I2C_SR_BB);
+	if (ret < 0) {
+		return ret;
+	}
+
+	VERBOSE("Before writing chip %d\n", chip);
+	cr |= I2C_CR_TX | I2C_CR_TX_NAK;
+	i2c_out(&ccsr_i2c->cr, cr);
+	ret = tx_byte(ccsr_i2c, chip << 1);
+	if (ret < 0) {
+		gen_stop(ccsr_i2c);
+		return ret;
+	}
+
+	VERBOSE("Before writing addr\n");
+	while (alen--) {
+		ret = tx_byte(ccsr_i2c, (addr >> (alen << 3)) & 0xff);
+		if (ret < 0) {
+			gen_stop(ccsr_i2c);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int read_data(struct ls_i2c *ccsr_i2c, unsigned char chip,
+		     unsigned char *buf, int len)
+{
+	int i;
+	int ret;
+	unsigned char cr;
+
+	cr = i2c_in(&ccsr_i2c->cr);
+	cr &= ~(I2C_CR_TX | I2C_CR_TX_NAK);
+	if (len == 1) {
+		cr |= I2C_CR_TX_NAK;
+	}
+	i2c_out(&ccsr_i2c->cr, cr);
+	i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
+	i2c_in(&ccsr_i2c->dr);	/* dummy read */
+	for (i = 0; i < len; i++) {
+		ret = wait_for_state(ccsr_i2c, I2C_SR_IF, I2C_SR_IF);
+		if (ret < 0) {
+			gen_stop(ccsr_i2c);
+			return ret;
+		}
+		if (i == (len - 1)) {
+			gen_stop(ccsr_i2c);
+		} else if (i == (len - 2)) {
+			/* Updating the command to send
+			 * No ACK.
+			 */
+			cr = i2c_in(&ccsr_i2c->cr);
+			cr |= I2C_CR_TX_NAK;
+			i2c_out(&ccsr_i2c->cr, cr);
+		}
+		i2c_out(&ccsr_i2c->sr, I2C_SR_IF);
+		buf[i] = i2c_in(&ccsr_i2c->dr);
+	}
+
+	return 0;
+}
+
+static int write_data(struct ls_i2c *ccsr_i2c, unsigned char chip,
+		      const unsigned char *buf, int len)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < len; i++) {
+		ret = tx_byte(ccsr_i2c, buf[i]);
+		if (ret < 0) {
+			break;
+		}
+	}
+	ret = gen_stop(ccsr_i2c);
+
+	return ret;
+}
+
+
+int i2c_read(unsigned char chip, int addr, int alen,
+	     unsigned char *buf, int len)
+{
+	int ret;
+	unsigned char cr;
+	struct ls_i2c *ccsr_i2c = (void *)g_nxp_i2c_addr;
+
+	ret = i2c_write_addr(ccsr_i2c, chip, addr, alen);
+	if (ret < 0) {
+		gen_stop(ccsr_i2c);
+		return ret;
+	}
+
+	cr = i2c_in(&ccsr_i2c->cr);
+	cr |= I2C_CR_RSTA;
+	i2c_out(&ccsr_i2c->cr, cr);
+
+	ret = tx_byte(ccsr_i2c, (chip << 1) | 1);
+	if (ret < 0) {
+		gen_stop(ccsr_i2c);
+		return ret;
+	}
+
+	return read_data(ccsr_i2c, chip, buf, len);
+}
+
+int i2c_write(unsigned char chip, int addr, int alen,
+	      const unsigned char *buf, int len)
+{
+	int ret;
+	struct ls_i2c *ccsr_i2c = (void *)g_nxp_i2c_addr;
+
+	ret = i2c_write_addr(ccsr_i2c, chip, addr, alen);
+	if (ret < 0) {
+		return ret;
+	}
+
+	return write_data(ccsr_i2c, chip, buf, len);
+}
+
+int i2c_probe_chip(unsigned char chip)
+{
+	int ret;
+	struct ls_i2c *ccsr_i2c = (void *)g_nxp_i2c_addr;
+
+	ret = i2c_write_addr(ccsr_i2c, chip, 0, 0);
+	if (ret < 0) {
+		WARN("write addr failed\n");
+		return ret;
+	}
+
+	ret = gen_stop(ccsr_i2c);
+	if (ret < 0) {
+		WARN("I2C: Probe not complete.\n");
+	}
+
+	return ret;
+}
diff --git a/drivers/nxp/i2c/i2c.h b/drivers/nxp/i2c/i2c.h
new file mode 100644
index 0000000..925bbc0
--- /dev/null
+++ b/drivers/nxp/i2c/i2c.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016-2020 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#ifndef I2C_H
+#define I2C_H
+
+#include <lib/mmio.h>
+
+#define I2C_TIMEOUT	1000	/* ms */
+
+#define I2C_FD_CONSERV	0x7e
+#define I2C_CR_DIS	(1 << 7)
+#define I2C_CR_EN	(0 << 7)
+#define I2C_CR_MA	(1 << 5)
+#define I2C_CR_TX	(1 << 4)
+#define I2C_CR_TX_NAK	(1 << 3)
+#define I2C_CR_RSTA	(1 << 2)
+#define I2C_SR_BB	(1 << 5)
+#define I2C_SR_IDLE	(0 << 5)
+#define I2C_SR_AL	(1 << 4)
+#define I2C_SR_IF	(1 << 1)
+#define I2C_SR_RX_NAK	(1 << 0)
+#define I2C_SR_RST	(I2C_SR_AL | I2C_SR_IF)
+
+#define I2C_GLITCH_EN	0x8
+
+#define i2c_in(a)	mmio_read_8((uintptr_t)(a))
+#define i2c_out(a, v)	mmio_write_8((uintptr_t)(a), (v))
+
+struct ls_i2c {
+	unsigned char ad;	/* I2c Bus Address Register */
+	unsigned char fd;	/* I2c Bus Frequency Dividor Register */
+	unsigned char cr;	/* I2c Bus Control Register */
+	unsigned char sr;	/* I2c Bus Status Register */
+	unsigned char dr;	/* I2C Bus Data I/O Register */
+	unsigned char ic;	/* I2C Bus Interrupt Config Register */
+	unsigned char dbg;	/* I2C Bus Debug Register */
+};
+
+void i2c_init(uintptr_t nxp_i2c_addr);
+int i2c_read(unsigned char chip, int addr, int alen,
+	     unsigned char *buf, int len);
+int i2c_write(unsigned char chip, int addr, int alen,
+	      const unsigned char *buf, int len);
+int i2c_probe_chip(unsigned char chip);
+
+#endif /* I2C_H */
diff --git a/drivers/nxp/i2c/i2c.mk b/drivers/nxp/i2c/i2c.mk
new file mode 100644
index 0000000..ae89115
--- /dev/null
+++ b/drivers/nxp/i2c/i2c.mk
@@ -0,0 +1,25 @@
+#
+# Copyright 2020 NXP
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+ifeq (${ADD_I2C},)
+
+ADD_I2C			:= 1
+I2C_DRIVERS_PATH        := ${PLAT_DRIVERS_PATH}/i2c
+
+I2C_SOURCES		+= $(I2C_DRIVERS_PATH)/i2c.c
+PLAT_INCLUDES		+= -I$(I2C_DRIVERS_PATH)
+
+ifeq (${BL_COMM_I2C_NEEDED},yes)
+BL_COMMON_SOURCES	+= ${I2C_SOURCES}
+else
+ifeq (${BL2_I2C_NEEDED},yes)
+BL2_SOURCES		+= ${I2C_SOURCES}
+endif
+ifeq (${BL31_I2C_NEEDED},yes)
+BL31_SOURCES		+= ${I2C_SOURCES}
+endif
+endif
+endif