A71CH driver, UID as serial number

- Basic driver for A71CH. Resets device, negotiates transmission sizes,
and fetches chip UID.
- On EVT2 boards, probe A71CH and use UID as serial#.
- Add support for SMBus-style block reads to the i2c-gpio driver.

Change-Id: Ib47fb3f4a6e8d4a024877bf30916a4b195c7187e
diff --git a/arch/arm/dts/mt8516-coral.dts b/arch/arm/dts/mt8516-coral.dts
index 1d193d0..ce13f2d 100644
--- a/arch/arm/dts/mt8516-coral.dts
+++ b/arch/arm/dts/mt8516-coral.dts
@@ -49,6 +49,45 @@
 		regulator-boot-on;
 		regulator-always-on;
 	};
+
+	i2c0: i2c@11009000 {
+		status = "okay";
+		compatible = "i2c-gpio";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c0_pins>;
+		gpios = <&gpio 58 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>,
+		        <&gpio 59 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	i2c1: i2c@1100a000 {
+		status = "okay";
+		compatible = "i2c-gpio";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c1_pins>;
+		gpios = <&gpio 52 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>,
+		        <&gpio 53 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+	};
+
+	i2c2: i2c@1100b000 {
+		status = "okay";
+		compatible = "i2c-gpio";
+		pinctrl-names = "default";
+		pinctrl-0 = <&i2c2_pins>;
+		gpios = <&gpio 60 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>,
+		        <&gpio 61 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+		i2c-gpio,delay-us = <5>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		a71ch: a71ch@49 {
+			compatible = "nxp,a71ch";
+			reg = <0x49>;
+		};
+	};
 };
 
 &mmc0 {
@@ -98,6 +137,31 @@
 			groups = "uart0_0_rxd_txd";
 		};
 	};
+
+	i2c0_pins: i2c0 {
+		mux {
+			function = "i2c_gpio";
+			groups = "i2c0";
+		};
+	};
+
+	i2c1_pins: i2c1 {
+		mux {
+			function = "i2c_gpio";
+			groups = "i2c1";
+		};
+	};
+
+	i2c2_pins: i2c2 {
+		mux {
+			function = "i2c_gpio";
+			groups = "i2c2";
+		};
+		conf_a71ch_reset {
+			pins = "EINT23";
+			output-high;
+		};
+	};
 };
 
 &uart0 {
diff --git a/board/mediatek/mt8516-coral/mt8516-coral.c b/board/mediatek/mt8516-coral/mt8516-coral.c
index c302da9..c36765f 100644
--- a/board/mediatek/mt8516-coral/mt8516-coral.c
+++ b/board/mediatek/mt8516-coral/mt8516-coral.c
@@ -5,7 +5,9 @@
 
 #include <common.h>
 #include <dm.h>
+#include <i2c.h>
 #include <mmc.h>
+#include "../../../drivers/crypto/nxp/a71ch.h"
 #include "../../../drivers/watchdog/mtk_wdt.h"
 
 #define WDT_DRIVER_NAME "mtk_wdt"
@@ -55,6 +57,8 @@
 		}
 	}
 
+	// Set a default serial number.
+	// If a crypto chip is detected, override it.
 	env_set("serial#", "0123456789ABCDEF");
 
 	mmc = find_mmc_device(mmc_dev);
@@ -62,6 +66,28 @@
 		if (!mmc_init(mmc) && mmc->capacity >= 7000000000) {
 			printf("EVT2 detected\n");
 			env_set("evt2_board", "1");
+
+			// Try to detect A71CH on the I2C bus.
+			// This usually fails on the first try, so retry a little.
+			int retries = 3;
+			struct udevice *devp;
+			do {
+				ret = i2c_get_chip_for_busnum(2, 0x49, 0, &devp);
+				retries--;
+				udelay(10000);
+			} while (retries && ret);
+
+			if (!ret) {
+				// Retrieve the UID of the A71CH,
+				// and convert it into a string.
+				u8* uid = a71ch_uid(devp);
+				char uid_str[(A71CH_UID_LEN*2)+1];
+				memset(uid_str, 0, sizeof(uid_str));
+				for (int i = 0; i < A71CH_UID_LEN; i++) {
+					sprintf(uid_str+(i*2), "%02x", uid[i]);
+				}
+				env_set("serial#", uid_str);
+			}
 		}
 	}
 
diff --git a/configs/mt8516_coral_defconfig b/configs/mt8516_coral_defconfig
index 69ad008..e329cd2 100644
--- a/configs/mt8516_coral_defconfig
+++ b/configs/mt8516_coral_defconfig
@@ -92,3 +92,8 @@
 CONFIG_SYS_PROMPT="u-boot=> "
 CONFIG_CMD_ECHO=y
 CONFIG_CMD_IMPORTENV=y
+CONFIG_CMD_I2C=y
+CONFIG_DM_GPIO=y
+CONFIG_DM_I2C=y
+CONFIG_DM_I2C_GPIO=y
+CONFIG_NXP_A71CH=y
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 1ea116b..c3161ff 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -1,5 +1,6 @@
 menu "Hardware crypto devices"
 
 source drivers/crypto/fsl/Kconfig
+source drivers/crypto/nxp/Kconfig
 
 endmenu
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index efbd1d3..c77bf6f 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -6,3 +6,4 @@
 obj-$(CONFIG_EXYNOS_ACE_SHA)	+= ace_sha.o
 obj-y += rsa_mod_exp/
 obj-y += fsl/
+obj-y += nxp/
diff --git a/drivers/crypto/nxp/Kconfig b/drivers/crypto/nxp/Kconfig
new file mode 100644
index 0000000..8078a50
--- /dev/null
+++ b/drivers/crypto/nxp/Kconfig
@@ -0,0 +1,5 @@
+config NXP_A71CH
+	bool "NXP A71CH Support"
+	depends on DM_I2C
+	help
+	  Enables NXP's A71CH Security Module
diff --git a/drivers/crypto/nxp/Makefile b/drivers/crypto/nxp/Makefile
new file mode 100644
index 0000000..cf98402
--- /dev/null
+++ b/drivers/crypto/nxp/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_NXP_A71CH) += a71ch.o
diff --git a/drivers/crypto/nxp/a71ch.c b/drivers/crypto/nxp/a71ch.c
new file mode 100644
index 0000000..b46e2df
--- /dev/null
+++ b/drivers/crypto/nxp/a71ch.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for NXP A71CH
+ * Protocol documentation from https://www.nxp.com/docs/en/supporting-information/AN12207.pdf
+ * Copyright (C) 2020 Google LLC
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/byteorder.h>
+#include "a71ch.h"
+
+// #define LOG_I2C 1
+
+#define PCB_STATUS               (0x7)
+#define PCB_SOFT_RESET           (0x1F)
+#define PCB_READ_ANSWER_TO_RESET (0x2F)
+#define PCB_PARAMETER_EXCHANGE   (0x3F)
+#define PCB_DATA_M2S             (0x0)
+#define PCB_DATA_S2M             (0x2)
+
+#define S2M_SHIFT     (6)
+#define M2S_SHIFT     (2)
+#define M2S_NEG_SHIFT (4)
+#define SEQ_CTR_SHIFT (4)
+#define M2S_MORE      (0x80)
+
+#define APDU_SUCCESS  (0x9000)
+
+#define NRETRY_SRST   (3)
+#define NRETRY_RATR   (3)
+#define NRETRY_PE     (3)
+#define NRETRY_STATUS (3)
+#define NRETRY_M2S    (3)
+
+#define DELAY_RESET_MS      (10)
+#define DELAY_READ_BLOCK_MS (1)
+
+#define BLOCK_SIZE    (32)
+
+#define STATUS_MASK (0xF)
+#define M2S_MASK    (0x3)
+#define S2M_MASK    (0x3)
+
+#define SEQ_CTR_MAX (7)
+
+struct a71ch {
+    int s2m;
+    int m2s;
+    u8 seq_ctr;
+    u8 uid[A71CH_UID_LEN];
+};
+
+enum {
+    s2m_31Bytes = 0,
+    s2m_63Bytes,
+    s2m_127Bytes,
+    s2m_254Bytes
+};
+enum {
+    m2s_32Bytes = 0,
+    m2s_64Bytes,
+    m2s_128Bytes,
+    m2s_255Bytes
+};
+
+enum {
+    STATUS_OKAY = 0x0,
+    STATUS_BUSY = 0x1
+};
+
+static int a71ch_m2s_to_bytes(int m2s) {
+    switch (m2s) {
+        case m2s_32Bytes:
+            return 32;
+        case m2s_64Bytes:
+            return 64;
+        case m2s_128Bytes:
+            return 128;
+        case m2s_255Bytes:
+            return 255;
+        default:
+            return 32;
+    }
+}
+
+static int a71ch_read_block(struct udevice *dev, u8* wr_buf, int wr_len, u8* rd_buf, int tries) {
+    int ret = -1;
+    struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+
+    mdelay(DELAY_READ_BLOCK_MS);
+
+#if defined(LOG_I2C)
+    printf("Tx(%d): ", wr_len);
+    for (int i = 0; i < wr_len; i++) {
+        printf("0x%x ", wr_buf[i]);
+    }
+    printf("\n");
+#endif
+
+    do {
+      struct i2c_msg msgs[2];
+      msgs[0].addr = chip->chip_addr;
+      msgs[0].flags = 0;
+      msgs[0].len = wr_len;
+      msgs[0].buf = wr_buf;
+
+      msgs[1].addr = chip->chip_addr;
+      msgs[1].flags = I2C_M_RD | I2C_M_RECV_LEN;
+      msgs[1].buf = rd_buf;
+      msgs[1].len = BLOCK_SIZE;
+
+      ret = dm_i2c_xfer(dev, msgs, 2);
+      tries--;
+    } while (tries && ret);
+
+#if defined(LOG_I2C)
+    printf("Rx(%d): ", rd_buf[0]);
+    for (int i = 1; i <= rd_buf[0]; i++) {
+        printf("0x%x ", rd_buf[i]);
+    }
+    printf("\n");
+#endif
+
+    return ret;
+}
+
+static int a71ch_write(struct udevice *dev, u8 *wr_buf, int wr_len, int tries) {
+    int ret = -1;
+    struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+
+#if defined(LOG_I2C)
+    printf("Tx(%d): ", wr_len);
+    for (int i = 0; i < wr_len; i++) {
+        printf("0x%x ", wr_buf[i]);
+    }
+    printf("\n");
+#endif
+
+    do {
+      struct i2c_msg msgs[1];
+      msgs[0].addr = chip->chip_addr;
+      msgs[0].flags = 0;
+      msgs[0].len = wr_len;
+      msgs[0].buf = wr_buf;
+
+      ret = dm_i2c_xfer(dev, msgs, 1);
+      tries--;
+    } while (tries && ret);
+    return ret;
+}
+
+static int a71ch_get_status(struct udevice *dev, u8 *status) {
+    int ret;
+
+    if (status == NULL) {
+        return -EINVAL;
+    }
+    u8 wr_buf[] = { PCB_STATUS };
+    u8 rd_buf[BLOCK_SIZE] = {};
+
+    ret = a71ch_read_block(dev, wr_buf, sizeof(wr_buf), rd_buf, NRETRY_STATUS);
+    if (ret) {
+        return ret;
+    }
+
+    *status = ((rd_buf[1] >> 4) & STATUS_MASK);
+    return 0;
+}
+
+static int a71ch_soft_reset(struct udevice *dev) {
+    int ret;
+
+    u8 wr_buf[] = { PCB_SOFT_RESET };
+    u8 rd_buf[BLOCK_SIZE] = {};
+    ret = a71ch_read_block(dev, wr_buf, sizeof(wr_buf), rd_buf, NRETRY_SRST);
+    if (ret)
+        return ret;
+
+    mdelay(DELAY_RESET_MS);
+
+    if (rd_buf[1]) {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static int a71ch_read_answer_to_reset(struct udevice *dev) {
+    int ret;
+
+    u8 wr_buf[] = { PCB_READ_ANSWER_TO_RESET };
+    u8 rd_buf[BLOCK_SIZE] = {};
+    ret = a71ch_read_block(dev, wr_buf, sizeof(wr_buf), rd_buf, NRETRY_RATR);
+    if (ret)
+        return ret;
+
+    return 0;
+}
+
+static int a71ch_parameter_exchange(struct udevice *dev) {
+    struct a71ch *priv = dev_get_priv(dev);
+    int ret;
+
+    u8 wr_buf[] = { PCB_PARAMETER_EXCHANGE | (s2m_31Bytes << S2M_SHIFT) };
+    u8 rd_buf[BLOCK_SIZE];
+
+    int retries = NRETRY_PE;
+    do {
+        ret = a71ch_read_block(dev, wr_buf, sizeof(wr_buf), rd_buf, NRETRY_PE);
+        if (ret)
+            return ret;
+
+        u8 px_val = rd_buf[1];
+        u8 s2m = (px_val >> S2M_SHIFT) & S2M_MASK;
+        u8 m2s = (px_val >> M2S_SHIFT) & M2S_MASK;
+        u8 m2s_neg = (~px_val >> M2S_NEG_SHIFT) & M2S_MASK;
+
+        if ((m2s == m2s_neg) && (s2m == s2m_31Bytes)) {
+            ret = 0;
+            priv->m2s = m2s;
+            priv->s2m = s2m;
+        } else {
+            ret = -EINVAL;
+        }
+    } while (retries && ret);
+
+    return ret;
+}
+
+static int a71ch_data_exchange(struct udevice *dev, u8 *data, int data_len, u8 *out_buf, int out_len) {
+    struct a71ch *priv = dev_get_priv(dev);
+    int data_remaining = data_len;
+    int ret;
+    // Leave space for the control byte and length byte.
+    int max_packet_len = a71ch_m2s_to_bytes(priv->m2s) - 2;
+    u8 wr_buf[BLOCK_SIZE];
+    u8 rd_buf[BLOCK_SIZE];
+    while (data_remaining) {
+        wr_buf[0] = PCB_DATA_M2S | (priv->seq_ctr++ << SEQ_CTR_SHIFT);
+        if (priv->seq_ctr > SEQ_CTR_MAX) {
+            priv->seq_ctr = 0;
+        }
+        if (data_remaining > max_packet_len) {
+            wr_buf[0] |= M2S_MORE;
+        }
+        u8 packet_len = (data_remaining > max_packet_len) ? max_packet_len : data_remaining;
+        wr_buf[1] = packet_len;
+        memcpy(&wr_buf[2], data + (data_len - data_remaining), packet_len);
+        // +2 is for control byte, packet len
+        ret = a71ch_write(dev, wr_buf, packet_len + 2, NRETRY_M2S);
+        u8 status;
+        do {
+            ret = a71ch_get_status(dev, &status);
+            if (ret == -EREMOTEIO) {
+                udelay(1000);
+                continue;
+            }
+            if (status == STATUS_BUSY) {
+                udelay(1000);
+                continue;
+            } else if (status == STATUS_OKAY) {
+                ret = 0;
+            } else {
+                ret = -1;
+                return ret;
+            }
+        } while (status != STATUS_OKAY);
+        data_remaining -= packet_len;
+    }
+    // Sending done, read back.
+    // NOTE: We don't currently support responses longer than one packet,
+    // as our use case doesn't need them.
+    wr_buf[0] = PCB_DATA_S2M;
+    ret = a71ch_read_block(dev, wr_buf, 1, rd_buf, 1);
+    // recv_len is one less than the actual size received, for the returned control byte.
+    int recv_len = rd_buf[0] - 1;
+    if (out_len != recv_len) {
+        printf("out_len and recv_len don't match.");
+        return -EINVAL;
+    }
+    memcpy(out_buf, &rd_buf[2], out_len);
+    u16 apdu_ret = ntohs((u16)rd_buf[recv_len]);
+    ret = (apdu_ret != APDU_SUCCESS);
+    return ret;
+}
+
+static int a71ch_gp_select(struct udevice *dev) {
+    int ret;
+    // Breakdown of APDU:
+    // [0] - CLA_ISO7816
+    // [1] - INS_SELECT
+    // [2] - P1 Offset
+    // [3] - P2 Offset
+    // [4] - Length
+    // [5:9] - "A71CH"
+    // [10] - Expected return length
+    u8 gp_select_data[] = { 0x00, 0xA4, 0x04, 0x00, 0x05, 0x61, 0x37, 0x31, 0x63, 0x68, 0x00 };
+    int gp_select_len = sizeof(gp_select_data);
+    u8 gp_select_out[4];
+    ret = a71ch_data_exchange(dev, gp_select_data, gp_select_len, gp_select_out, sizeof(gp_select_out));
+    return ret;
+}
+
+static int a71ch_get_uid(struct udevice *dev) {
+    int ret;
+    struct a71ch *priv = dev_get_priv(dev);
+    // Breakdown of APDU:
+    // [0] - CLA_A71CH
+    // [1] - A71CH_INS_GET_MODULE
+    // [2] - P1
+    // [3] - P2 (Unique ID)
+    u8 get_uid_data[] = { 0x80, 0x91, 0x0, 0x1 };
+    int get_uid_data_len = sizeof(get_uid_data);
+    u8 get_uid_out[20];
+    ret = a71ch_data_exchange(dev, get_uid_data, get_uid_data_len, get_uid_out, sizeof(get_uid_out));
+    if (!ret) {
+        memcpy(priv->uid, get_uid_out, 18);
+    }
+    return ret;
+}
+
+static int a71ch_probe(struct udevice *dev) {
+    int ret;
+    struct a71ch *priv = dev_get_priv(dev);
+    memset(priv, 0, sizeof(*priv));
+
+    ret = a71ch_soft_reset(dev);
+    if (ret) {
+        printf("A71CH: Soft reset failed.\n");
+        return -ENOENT;
+    }
+
+    ret = a71ch_read_answer_to_reset(dev);
+    if (ret) {
+        printf("A71CH: Read answer to reset failed.\n");
+        return -ENOENT;
+    }
+
+    u8 status;
+    ret = a71ch_get_status(dev, &status);
+    if (ret) {
+        printf("A71CH: Get status failed.\n");
+        return -ENOENT;
+    }
+
+    ret = a71ch_parameter_exchange(dev);
+    if (ret) {
+        printf("A71CH: Parameter exchange failed.\n");
+        return -ENOENT;
+    }
+
+    ret = a71ch_gp_select(dev);
+    if (ret) {
+        printf("A71CH: GP Select failed.\n");
+        return -ENOENT;
+    }
+
+    ret = a71ch_get_uid(dev);
+    if (ret) {
+        printf("A71CH: Get UID failed.\n");
+        return -ENOENT;
+    }
+
+    return 0;
+}
+
+u8* a71ch_uid(struct udevice *dev) {
+    struct a71ch *priv = dev_get_priv(dev);
+    return priv->uid;
+}
+
+static const struct udevice_id a71ch_ids[] = {
+    { .compatible = "nxp,a71ch" },
+    { }
+};
+
+U_BOOT_DRIVER(a71ch) = {
+    .name = "a71ch",
+    .id = UCLASS_I2C_GENERIC,
+    .of_match = a71ch_ids,
+    .probe = a71ch_probe,
+    .priv_auto_alloc_size = sizeof(struct a71ch),
+};
diff --git a/drivers/crypto/nxp/a71ch.h b/drivers/crypto/nxp/a71ch.h
new file mode 100644
index 0000000..67d473c
--- /dev/null
+++ b/drivers/crypto/nxp/a71ch.h
@@ -0,0 +1,9 @@
+#ifndef DRIVERS_CRYPTO_NXP_A71CH_
+#define DRIVERS_CRYPTO_NXP_A71CH_
+
+#include <asm/types.h>
+
+#define A71CH_UID_LEN (18)
+u8 *a71ch_uid(struct udevice *dev);
+
+#endif  // DRIVERS_CRYPTO_NXP_A71CH_
diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c
index 4e8fa21..86aa034 100644
--- a/drivers/i2c/i2c-gpio.c
+++ b/drivers/i2c/i2c-gpio.c
@@ -254,14 +254,39 @@
 static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
 {
 	struct i2c_gpio_bus *bus = dev_get_priv(dev);
+	struct gpio_desc *scl = &bus->gpios[PIN_SCL];
+	struct gpio_desc *sda = &bus->gpios[PIN_SDA];
+	unsigned int delay = bus->udelay;
 	int ret;
 
 	for (; nmsgs > 0; nmsgs--, msg++) {
 		bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
 
 		if (msg->flags & I2C_M_RD) {
-			ret = i2c_gpio_read_data(bus, msg->addr, msg->buf,
-						 msg->len);
+			if (msg->flags & I2C_M_RECV_LEN) {
+				int len = 0;
+				uchar* buffer = msg->buf;
+				udelay(250);
+
+				// Read back the length byte.
+				// If it's longer than the buffer
+				// length (minus one byte, for itself), bail out.
+				len = *buffer++ = i2c_gpio_read_byte(scl, sda, delay, 0);
+				if (len > msg->len - 1) {
+					ret = -1;
+					break;
+				}
+
+				// Read back bytes until we've got them all.
+				while (len-- > 0)
+					*buffer++ = i2c_gpio_read_byte(scl, sda, delay, len == 0);
+
+				i2c_gpio_send_stop(scl, sda, delay);
+				ret = 0;
+			} else {
+				ret = i2c_gpio_read_data(bus, msg->addr, msg->buf,
+				                         msg->len);
+			}
 		} else {
 			ret = i2c_gpio_write_data(bus, msg->addr, msg->buf,
 						  msg->len, next_is_read);
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c
index 829b30e..8299f22 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8516.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c
@@ -345,12 +345,24 @@
 				   119, 120, };
 static int mt8516_msdc0_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, };
 
+static int mt8516_i2c0_gpio_pins[] = { 58, 59 };
+static int mt8516_i2c0_gpio_funcs[] = { 0, 0 };
+static int mt8516_i2c1_gpio_pins[] = { 52, 53 };
+static int mt8516_i2c1_gpio_funcs[] = { 0, 0 };
+static int mt8516_i2c2_gpio_pins[] = { 60, 61, 23 };
+static int mt8516_i2c2_gpio_funcs[] = { 0, 0, 0 };
+static const char *const mt8516_i2c_gpio_groups[] = { "i2c0", "i2c1", "i2c2" };
+
 static const struct mtk_group_desc mt8516_groups[] = {
 	PINCTRL_PIN_GROUP("uart0_0_rxd_txd", mt8516_uart0_0_rxd_txd),
 	PINCTRL_PIN_GROUP("uart1_0_rxd_txd", mt8516_uart1_0_rxd_txd),
 	PINCTRL_PIN_GROUP("uart2_0_rxd_txd", mt8516_uart2_0_rxd_txd),
 
 	PINCTRL_PIN_GROUP("msdc0", mt8516_msdc0),
+
+	PINCTRL_PIN_GROUP("i2c0", mt8516_i2c0_gpio),
+	PINCTRL_PIN_GROUP("i2c1", mt8516_i2c1_gpio),
+	PINCTRL_PIN_GROUP("i2c2", mt8516_i2c2_gpio),
 };
 
 static const char *const mt8516_msdc_groups[] = { "msdc0" };
@@ -358,6 +370,7 @@
 static const struct mtk_function_desc mt8516_functions[] = {
 	{"uart", mt8516_uart_groups, ARRAY_SIZE(mt8516_uart_groups)},
 	{"msdc", mt8516_msdc_groups, ARRAY_SIZE(mt8516_msdc_groups)},
+	{"i2c_gpio", mt8516_i2c_gpio_groups, ARRAY_SIZE(mt8516_i2c_gpio_groups)},
 };
 
 static struct mtk_pinctrl_soc mt8516_data = {
diff --git a/include/configs/mt8516-coral.h b/include/configs/mt8516-coral.h
index 01de927..e7b5e31 100644
--- a/include/configs/mt8516-coral.h
+++ b/include/configs/mt8516-coral.h
@@ -43,6 +43,7 @@
 #define CONFIG_SYS_MMC_ENV_DEV		0
 #define CONFIG_SYS_MMC_ENV_PART		2
 #define CONFIG_ENV_OFFSET		0
+#define CONFIG_ENV_OVERWRITE		1
 
 #ifdef CONFIG_MTK_ANDROID
 #define MMCBOOT \