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 \