uboot: Import latest patches from u-boot-2023.07.y-kkit

Adds enough support for 6393X switchcore to enable netbooting from a
single port.
This commit is contained in:
Tobias Waldekranz
2023-10-30 11:24:18 +01:00
committed by Joachim Wiberg
parent 97747a95ea
commit c385e2e9c2
6 changed files with 1183 additions and 13 deletions
@@ -0,0 +1,156 @@
From 4348261902b60f93c217f07069a2b5613a933c17 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 17 Oct 2023 13:15:58 +0200
Subject: [PATCH 1/5] net: mv88e6xxx: Add reset-gpios support
If the switch's RESETn signal is under the CPUs control, release it
before starting the probe.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/mv88e6xxx.c | 68 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 61 insertions(+), 7 deletions(-)
diff --git a/drivers/net/mv88e6xxx.c b/drivers/net/mv88e6xxx.c
index 64e860e324..1bf7cfd6a6 100644
--- a/drivers/net/mv88e6xxx.c
+++ b/drivers/net/mv88e6xxx.c
@@ -33,6 +33,8 @@
#include <miiphy.h>
#include <net/dsa.h>
+#include <asm-generic/gpio.h>
+
/* Device addresses */
#define DEVADDR_PHY(p) (p)
#define DEVADDR_SERDES 0x0F
@@ -146,6 +148,8 @@ struct mv88e6xxx_priv {
int port_reg_base; /* Base of the switch port registers */
u8 global1; /* Offset of Switch Global 1 registers */
u8 global2; /* Offset of Switch Global 2 registers */
+
+ struct gpio_desc reset;
};
/* Wait for the current SMI indirect command to complete */
@@ -675,6 +679,37 @@ static int mv88e6xxx_probe_mdio(struct udevice *dev)
return ret;
}
+static int mv88e6xxx_probe_reset(struct udevice *dev)
+{
+ struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+ int err;
+
+ if (!CONFIG_IS_ENABLED(DM_GPIO))
+ return 0;
+
+ err = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset, GPIOD_IS_OUT);
+ if (err && (err != -ENOENT))
+ return err;
+
+ if (dm_gpio_is_valid(&priv->reset)) {
+ dm_gpio_set_value(&priv->reset, 0);
+ mdelay(10);
+ }
+
+ return 0;
+}
+
+static void mv88e6xxx_remove_reset(struct udevice *dev)
+{
+ struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+
+ if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&priv->reset)) {
+ dm_gpio_set_value(&priv->reset, 1);
+ dm_gpio_free(dev, &priv->reset);
+ }
+}
+
static int mv88e6xxx_probe(struct udevice *dev)
{
struct dsa_pdata *dsa_pdata = dev_get_uclass_plat(dev);
@@ -684,17 +719,22 @@ static int mv88e6xxx_probe(struct udevice *dev)
if (ofnode_valid(dev_ofnode(dev)) &&
!ofnode_is_enabled(dev_ofnode(dev))) {
dev_dbg(dev, "switch disabled\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err;
}
+ ret = mv88e6xxx_probe_reset(dev);
+ if (ret)
+ goto err;
+
/* probe internal mdio bus */
ret = mv88e6xxx_probe_mdio(dev);
if (ret)
- return ret;
+ goto err_remove_reset;
ret = mv88e6xxx_priv_reg_offs_pre_init(dev);
if (ret)
- return ret;
+ goto err_remove_reset;
dev_dbg(dev, "ID=0x%x PORT_BASE=0x%02x GLOBAL1=0x%02x GLOBAL2=0x%02x\n",
priv->id, priv->port_reg_base, priv->global1, priv->global2);
@@ -716,28 +756,40 @@ static int mv88e6xxx_probe(struct udevice *dev)
priv->port_count = 7;
break;
default:
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_remove_reset;
}
ret = mv88e6xxx_switch_reset(dev);
if (ret < 0)
- return ret;
+ goto err_remove_reset;
if (mv88e6xxx_6352_family(dev)) {
val = mv88e6xxx_get_cmode(dev, dsa_pdata->cpu_port);
if (val < 0)
- return val;
+ goto err_remove_reset;
/* initialize serdes */
if (val == PORT_REG_STATUS_CMODE_100BASE_X ||
val == PORT_REG_STATUS_CMODE_1000BASE_X ||
val == PORT_REG_STATUS_CMODE_SGMII) {
ret = mv88e6xxx_serdes_init(dev);
if (ret < 0)
- return ret;
+ goto err_remove_reset;
}
}
return 0;
+
+err_remove_reset:
+ mv88e6xxx_remove_reset(dev);
+err:
+ return ret;
+}
+
+static int mv88e6xxx_remove(struct udevice *dev)
+{
+ mv88e6xxx_remove_reset(dev);
+ return 0;
}
static const struct udevice_id mv88e6xxx_ids[] = {
@@ -750,6 +802,8 @@ U_BOOT_DRIVER(mv88e6xxx) = {
.id = UCLASS_DSA,
.of_match = mv88e6xxx_ids,
.probe = mv88e6xxx_probe,
+ .remove = mv88e6xxx_remove,
.ops = &mv88e6xxx_dsa_ops,
.priv_auto = sizeof(struct mv88e6xxx_priv),
+ .flags = DM_FLAG_OS_PREPARE,
};
--
2.34.1
@@ -0,0 +1,181 @@
From d87aea2f0d0acfa0d54adb20a980256c0ba95023 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 17 Oct 2023 13:20:19 +0200
Subject: [PATCH 2/5] net: mv88e6xxx: Support clause 45 addressing on internal
MDIO bus
This is needed to access SERDES registers on newer chips, like the
6393X.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/mv88e6xxx.c | 113 +++++++++++++++++++++++++++++++++++++---
1 file changed, 106 insertions(+), 7 deletions(-)
diff --git a/drivers/net/mv88e6xxx.c b/drivers/net/mv88e6xxx.c
index 1bf7cfd6a6..eddac332e0 100644
--- a/drivers/net/mv88e6xxx.c
+++ b/drivers/net/mv88e6xxx.c
@@ -114,6 +114,9 @@
#define SMI_CMD_CLAUSE_22 BIT(12)
#define SMI_CMD_CLAUSE_22_OP_READ (2 << 10)
#define SMI_CMD_CLAUSE_22_OP_WRITE (1 << 10)
+#define SMI_CMD_CLAUSE_45_OP_WADDR (0 << 10)
+#define SMI_CMD_CLAUSE_45_OP_WDATA (1 << 10)
+#define SMI_CMD_CLAUSE_45_OP_RDATA (3 << 10)
#define SMI_CMD_ADDR_SHIFT 5
#define SMI_CMD_ADDR_MASK 0x1f
#define SMI_CMD_REG_SHIFT 0
@@ -126,6 +129,18 @@
(SMI_BUSY | SMI_CMD_CLAUSE_22 | SMI_CMD_CLAUSE_22_OP_WRITE) | \
(((addr) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \
(((reg) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT)
+#define XSMI_CMD_WADDR(port, dev) \
+ (SMI_BUSY | SMI_CMD_CLAUSE_45_OP_WADDR) | \
+ (((port) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \
+ (((dev) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT)
+#define XSMI_CMD_WDATA(port, dev) \
+ (SMI_BUSY | SMI_CMD_CLAUSE_45_OP_WDATA) | \
+ (((port) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \
+ (((dev) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT)
+#define XSMI_CMD_RDATA(port, dev) \
+ (SMI_BUSY | SMI_CMD_CLAUSE_45_OP_RDATA) | \
+ (((port) & SMI_CMD_ADDR_MASK) << SMI_CMD_ADDR_SHIFT) | \
+ (((dev) & SMI_CMD_REG_MASK) << SMI_CMD_REG_SHIFT)
/* ID register values for different switch models */
#define PORT_SWITCH_ID_6020 0x0200
@@ -273,7 +288,41 @@ static int mv88e6xxx_phy_wait(struct udevice *dev)
return -ETIMEDOUT;
}
-static int mv88e6xxx_phy_read_indirect(struct udevice *dev, int phyad, int devad, int reg)
+static int mv88e6xxx_phy_read_indirect_c45(struct udevice *dev, int phyad, int devad, int reg)
+{
+ struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+ int res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_DATA, reg);
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_CMD,
+ XSMI_CMD_WADDR(phyad, devad));
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_phy_wait(dev);
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_CMD,
+ XSMI_CMD_RDATA(phyad, devad));
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_phy_wait(dev);
+ if (res < 0)
+ return res;
+
+ return mv88e6xxx_reg_read(dev, priv->global2,
+ GLOBAL2_REG_PHY_DATA);
+}
+
+static int mv88e6xxx_phy_read_indirect_c22(struct udevice *dev, int phyad, int reg)
{
struct mv88e6xxx_priv *priv = dev_get_priv(dev);
int res;
@@ -293,8 +342,16 @@ static int mv88e6xxx_phy_read_indirect(struct udevice *dev, int phyad, int devad
GLOBAL2_REG_PHY_DATA);
}
-static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad,
- int devad, int reg, u16 data)
+static int mv88e6xxx_phy_read_indirect(struct udevice *dev, int phyad, int devad, int reg)
+{
+ if (devad == MDIO_DEVAD_NONE)
+ return mv88e6xxx_phy_read_indirect_c22(dev, phyad, reg);
+
+ return mv88e6xxx_phy_read_indirect_c45(dev, phyad, devad, reg);
+}
+
+static int mv88e6xxx_phy_write_indirect_c22(struct udevice *dev, int phyad,
+ int reg, u16 data)
{
struct mv88e6xxx_priv *priv = dev_get_priv(dev);
int res;
@@ -315,6 +372,50 @@ static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad,
return mv88e6xxx_phy_wait(dev);
}
+static int mv88e6xxx_phy_write_indirect_c45(struct udevice *dev, int phyad,
+ int devad, int reg, u16 data)
+{
+ struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+ int res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_DATA, reg);
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_CMD,
+ XSMI_CMD_WADDR(phyad, devad));
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_phy_wait(dev);
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_DATA, data);
+ if (res < 0)
+ return res;
+
+ res = mv88e6xxx_reg_write(dev, priv->global2,
+ GLOBAL2_REG_PHY_CMD,
+ XSMI_CMD_WDATA(phyad, devad));
+ if (res < 0)
+ return res;
+
+ return mv88e6xxx_phy_wait(dev);
+}
+
+static int mv88e6xxx_phy_write_indirect(struct udevice *dev, int phyad,
+ int devad, int reg, u16 data)
+{
+ if (devad == MDIO_DEVAD_NONE)
+ return mv88e6xxx_phy_write_indirect_c22(dev, phyad, reg, data);
+
+ return mv88e6xxx_phy_write_indirect_c45(dev, phyad, devad, reg, data);
+}
+
/* Wrapper function to make calls to phy_read_indirect simpler */
static int mv88e6xxx_phy_read(struct udevice *dev, int phy, int reg)
{
@@ -493,15 +594,13 @@ static int mv88e6xxx_priv_reg_offs_pre_init(struct udevice *dev)
static int mv88e6xxx_mdio_read(struct udevice *dev, int addr, int devad, int reg)
{
- return mv88e6xxx_phy_read_indirect(dev->parent, DEVADDR_PHY(addr),
- MDIO_DEVAD_NONE, reg);
+ return mv88e6xxx_phy_read_indirect(dev->parent, DEVADDR_PHY(addr), devad, reg);
}
static int mv88e6xxx_mdio_write(struct udevice *dev, int addr, int devad,
int reg, u16 val)
{
- return mv88e6xxx_phy_write_indirect(dev->parent, DEVADDR_PHY(addr),
- MDIO_DEVAD_NONE, reg, val);
+ return mv88e6xxx_phy_write_indirect(dev->parent, DEVADDR_PHY(addr), devad, reg, val);
}
static const struct mdio_ops mv88e6xxx_mdio_ops = {
--
2.34.1
@@ -0,0 +1,216 @@
From c801299f06f0e5c95bd597fe0ce4ad6c3df9a444 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 17 Oct 2023 13:22:00 +0200
Subject: [PATCH 3/5] net: mv88e6xxx: Add support for 6393X
Only built-in copper PHYs are supported as access ports. Support
10GBASE-R SERDES connection to CPU.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/net/mv88e6xxx.c | 134 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 130 insertions(+), 4 deletions(-)
diff --git a/drivers/net/mv88e6xxx.c b/drivers/net/mv88e6xxx.c
index eddac332e0..4a4a3ae0b1 100644
--- a/drivers/net/mv88e6xxx.c
+++ b/drivers/net/mv88e6xxx.c
@@ -74,6 +74,7 @@
#define PORT_REG_STATUS_SPEED_1000 2
#define PORT_REG_STATUS_CMODE_MASK 0xF
+#define PORT_REG_STATUS_CMODE_10GBASE_R 0xd
#define PORT_REG_STATUS_CMODE_SGMII 0xa
#define PORT_REG_STATUS_CMODE_1000BASE_X 0x9
#define PORT_REG_STATUS_CMODE_100BASE_X 0x8
@@ -155,6 +156,7 @@
#define PORT_SWITCH_ID_6250 0x2500
#define PORT_SWITCH_ID_6320 0x1150
#define PORT_SWITCH_ID_6352 0x3520
+#define PORT_SWITCH_ID_6393 0x3930
struct mv88e6xxx_priv {
int smi_addr;
@@ -475,6 +477,17 @@ static bool mv88e6xxx_6352_family(struct udevice *dev)
return false;
}
+static bool mv88e6xxx_6393_family(struct udevice *dev)
+{
+ struct mv88e6xxx_priv *priv = dev_get_priv(dev);
+
+ switch (priv->id) {
+ case PORT_SWITCH_ID_6393:
+ return true;
+ }
+ return false;
+}
+
static int mv88e6xxx_get_cmode(struct udevice *dev, u8 port)
{
int res;
@@ -529,7 +542,7 @@ static int mv88e6xxx_switch_reset(struct udevice *dev)
return 0;
}
-static int mv88e6xxx_serdes_init(struct udevice *dev)
+static int mv88e6352_serdes_init(struct udevice *dev)
{
int val;
@@ -549,6 +562,107 @@ static int mv88e6xxx_serdes_init(struct udevice *dev)
return 0;
}
+static int mv88e6393_serdes_erratum_4_6(struct udevice *dev, int port)
+{
+ int val;
+
+ /* mv88e6393x family errata 4.6:
+ * Cannot clear PwrDn bit on SERDES if device is configured CPU_MGD
+ * mode or P0_mode is configured for [x]MII.
+ * Workaround: Set SERDES register 4.F002 bit 5=0 and bit 15=1.
+ *
+ * It seems that after this workaround the SERDES is automatically
+ * powered up (the bit is cleared), so power it down.
+ */
+ val = mv88e6xxx_phy_read_indirect(dev, port, MDIO_MMD_PHYXS, 0xf002);
+ if (val < 0)
+ return val;
+
+ val &= ~BIT(5);
+ val |= MDIO_CTRL1_RESET;
+ return mv88e6xxx_phy_write_indirect(dev, port, MDIO_MMD_PHYXS, 0xf002, val);
+}
+
+static int mv88e6393_serdes_erratum_5_2(struct udevice *dev, int port, int cmode)
+{
+ static const struct {
+ u16 dev, reg, val, mask;
+ } fixes[] = {
+ { MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff },
+ { MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff },
+ { MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff },
+ { MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f },
+ { MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 },
+ { MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff },
+ { MDIO_MMD_PHYXS, 0xf002, 0x8000, 0x8000 }
+ };
+ int val, i;
+
+ /* mv88e6393x family errata 5.2:
+ * For optimal signal integrity the following sequence should be applied
+ * to SERDES operating in 10G mode. These registers only apply to 10G
+ * operation and have no effect on other speeds.
+ */
+ if (cmode != PORT_REG_STATUS_CMODE_10GBASE_R)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(fixes); ++i) {
+ val = mv88e6xxx_phy_read_indirect(dev, port, fixes[i].dev, fixes[i].reg);
+ if (val < 0)
+ return val;
+
+ val &= ~fixes[i].mask;
+ val |= fixes[i].val;
+
+ val = mv88e6xxx_phy_write_indirect(dev, port, fixes[i].dev, fixes[i].reg, val);
+ if (val < 0)
+ return val;
+ }
+
+ return 0;
+}
+
+static int mv88e6393_serdes_init(struct udevice *dev, int port, int cmode)
+{
+ u16 base;
+ int val;
+
+ val = mv88e6393_serdes_erratum_4_6(dev, port);
+ if (val < 0)
+ return val;
+
+ val = mv88e6393_serdes_erratum_5_2(dev, port, cmode);
+ if (val < 0)
+ return val;
+
+ switch (cmode) {
+ case PORT_REG_STATUS_CMODE_10GBASE_R:
+ base = 0x1000;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ val = mv88e6xxx_phy_read_indirect(dev, port, MDIO_MMD_PHYXS, base);
+ if (val < 0)
+ return val;
+
+ val &= ~MDIO_CTRL1_LPOWER;
+ val |= MDIO_CTRL1_RESET;
+
+ return mv88e6xxx_phy_write_indirect(dev, port, MDIO_MMD_PHYXS, base, val);
+}
+
+static int mv88e6xxx_serdes_init(struct udevice *dev, int cpu_port, int cmode)
+{
+ if (mv88e6xxx_6352_family(dev))
+ return mv88e6352_serdes_init(dev);
+ else if (mv88e6xxx_6393_family(dev))
+ return mv88e6393_serdes_init(dev, cpu_port, cmode);
+
+ return -EOPNOTSUPP;
+}
+
/*
* This function is used to pre-configure the required register
* offsets, so that the indirect register access to the PHY registers
@@ -569,6 +683,15 @@ static int mv88e6xxx_priv_reg_offs_pre_init(struct udevice *dev)
*/
priv->port_reg_base = 0x10;
priv->id = mv88e6xxx_get_switch_id(dev);
+ if (priv->id == 0xfff0) {
+ /*
+ * Look for a newer device in which ports start at
+ * offset 0 (6390/6393 and compatible switches).
+ */
+ priv->port_reg_base = 0;
+ priv->id = mv88e6xxx_get_switch_id(dev);
+ }
+
if (priv->id != 0xfff0) {
priv->global1 = 0x1B;
priv->global2 = 0x1C;
@@ -844,6 +967,7 @@ static int mv88e6xxx_probe(struct udevice *dev)
case PORT_SWITCH_ID_6176:
case PORT_SWITCH_ID_6240:
case PORT_SWITCH_ID_6352:
+ case PORT_SWITCH_ID_6393:
priv->port_count = 11;
break;
case PORT_SWITCH_ID_6020:
@@ -863,15 +987,17 @@ static int mv88e6xxx_probe(struct udevice *dev)
if (ret < 0)
goto err_remove_reset;
- if (mv88e6xxx_6352_family(dev)) {
+ if (mv88e6xxx_6352_family(dev) ||
+ mv88e6xxx_6393_family(dev)) {
val = mv88e6xxx_get_cmode(dev, dsa_pdata->cpu_port);
if (val < 0)
goto err_remove_reset;
/* initialize serdes */
if (val == PORT_REG_STATUS_CMODE_100BASE_X ||
val == PORT_REG_STATUS_CMODE_1000BASE_X ||
- val == PORT_REG_STATUS_CMODE_SGMII) {
- ret = mv88e6xxx_serdes_init(dev);
+ val == PORT_REG_STATUS_CMODE_SGMII ||
+ val == PORT_REG_STATUS_CMODE_10GBASE_R) {
+ ret = mv88e6xxx_serdes_init(dev, dsa_pdata->cpu_port, val);
if (ret < 0)
goto err_remove_reset;
}
--
2.34.1
@@ -0,0 +1,54 @@
From 5013dadfd3ba92eba930f8ea0f88c0f34b8c55a5 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Fri, 20 Oct 2023 13:19:41 +0200
Subject: [PATCH 4/5] i2c: pcf8575: Properly address chip
These devices hold a simple shift register that is accessed without
any "register" setup.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
drivers/gpio/pcf8575_gpio.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/pcf8575_gpio.c b/drivers/gpio/pcf8575_gpio.c
index d5930d941f..bc0275b901 100644
--- a/drivers/gpio/pcf8575_gpio.c
+++ b/drivers/gpio/pcf8575_gpio.c
@@ -49,9 +49,15 @@ static int pcf8575_i2c_write(struct udevice *dev, unsigned int word)
{
struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
u8 buf[2] = { word & 0xff, word >> 8, };
+ struct i2c_msg wmsg = {
+ .addr = chip->chip_addr,
+ .flags = 0,
+ .buf = buf,
+ .len = dev_get_driver_data(dev),
+ };
int ret;
- ret = dm_i2c_write(dev, 0, buf, dev_get_driver_data(dev));
+ ret = dm_i2c_xfer(dev, &wmsg, 1);
if (ret)
printf("%s i2c write failed to addr %x\n", __func__,
chip->chip_addr);
@@ -63,9 +69,15 @@ static int pcf8575_i2c_read(struct udevice *dev)
{
struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
u8 buf[2] = {0x00, 0x00};
+ struct i2c_msg rmsg = {
+ .addr = chip->chip_addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = dev_get_driver_data(dev),
+ };
int ret;
- ret = dm_i2c_read(dev, 0, buf, dev_get_driver_data(dev));
+ ret = dm_i2c_xfer(dev, &rmsg, 1);
if (ret) {
printf("%s i2c read failed from addr %x\n", __func__,
chip->chip_addr);
--
2.34.1
@@ -0,0 +1,576 @@
From c0495a607652aef38a4d4480ebdb6dd45e3dd9b9 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Tue, 24 Oct 2023 22:23:44 +0200
Subject: [PATCH 5/5] arm64: mvebu: a8k: Add eFuse support
Add support for reading and burning LD and HD fuses. Use a specialized
commandline implementation over the standard "fuse" command because:
- We want to reference fuses by the die and module they belong to,
rather than a plain integer bank ID.
- Our lines are wider than 32 bits, meaning that we would have to use
similar tactics as arch/arm/mach-mvebu/efuse.c in order to support
fuse programming, which is cumbersome.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
arch/arm/mach-mvebu/Kconfig | 7 +
arch/arm/mach-mvebu/armada8k/Makefile | 2 +
arch/arm/mach-mvebu/armada8k/efuse.c | 516 ++++++++++++++++++++++++++
3 files changed, 525 insertions(+)
create mode 100644 arch/arm/mach-mvebu/armada8k/efuse.c
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index ac484c73f6..45584b4282 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -421,6 +421,13 @@ config MVEBU_EFUSE_VHV_GPIO_ACTIVE_LOW
bool "VHV_Enable GPIO is Active Low"
depends on MVEBU_EFUSE_VHV_GPIO != ""
+config MVEBU_8K_EFUSE
+ bool "Enable eFuse support"
+ depends on ARMADA_8K
+ select HEX_DUMP
+ help
+ Enable support for reading and writing eFuses on mvebu SoCs.
+
config SECURED_MODE_IMAGE
bool "Build image for trusted boot"
default false
diff --git a/arch/arm/mach-mvebu/armada8k/Makefile b/arch/arm/mach-mvebu/armada8k/Makefile
index 0a4756717a..c1be059b85 100644
--- a/arch/arm/mach-mvebu/armada8k/Makefile
+++ b/arch/arm/mach-mvebu/armada8k/Makefile
@@ -3,3 +3,5 @@
# Copyright (C) 2016 Stefan Roese <sr@denx.de>
obj-y = cpu.o cache_llc.o dram.o
+
+obj-$(CONFIG_MVEBU_8K_EFUSE) += efuse.o
diff --git a/arch/arm/mach-mvebu/armada8k/efuse.c b/arch/arm/mach-mvebu/armada8k/efuse.c
new file mode 100644
index 0000000000..5438d4466d
--- /dev/null
+++ b/arch/arm/mach-mvebu/armada8k/efuse.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015-2016 Reinhard Pfau <reinhard.pfau@gdsys.cc>
+ */
+
+#include <command.h>
+#include <common.h>
+#include <errno.h>
+#include <hexdump.h>
+
+#include <asm/io.h>
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+enum mvebu_efuse_mod {
+ MVEBU_EFUSE_MOD_LD0,
+ MVEBU_EFUSE_MOD_LD1,
+ MVEBU_EFUSE_MOD_HD,
+};
+
+struct mvebu_efuse_ld {
+ u32 array[256 / 32];
+#define MVEBU_EFUSE_LD_PARITY_BIT BIT(31)
+ u32 burn;
+};
+
+struct mvebu_efuse_hd_line {
+ u32 array[2];
+#define MVEBU_EFUSE_HD_ECC_MASK 0xfe000000
+ u32 lock;
+ u32 pad;
+};
+
+struct mvebu_efuse_hd {
+#define MVEBU_EFUSE_HD_LINES 64
+ struct mvebu_efuse_hd_line line[MVEBU_EFUSE_HD_LINES];
+};
+
+struct mvebu_efuse_die {
+ union {
+ struct {
+ u32 resvd0;
+ u32 resvd1;
+ u32 ctrl;
+ };
+
+ u8 pad_ctrl[0xf00];
+ };
+
+ union {
+ struct mvebu_efuse_ld ld;
+ u8 pad_ld[0x100];
+ };
+
+ union {
+ struct mvebu_efuse_hd hd;
+ };
+};
+
+static const struct mvebu_efuse_die __iomem *mvebu_efuse_dies[] = {
+ (void __iomem *)0xf06f8000,
+ (void __iomem *)0xf2400000,
+};
+
+static void mvebu_efuse_program_set(const struct mvebu_efuse_die __iomem *die,
+ bool enable)
+{
+ if (enable)
+ setbits_le32(&die->ctrl, BIT(31));
+ else
+ clrbits_le32(&die->ctrl, BIT(31));
+}
+
+static void mvebu_efuse_secure_set(const struct mvebu_efuse_die __iomem *die,
+ bool enable)
+{
+ if (enable)
+ setbits_le32(&die->ctrl, BIT(7));
+ else
+ clrbits_le32(&die->ctrl, BIT(7));
+}
+
+static void mvebu_efuse_ld_select(const struct mvebu_efuse_die __iomem *die,
+ bool ld1)
+{
+ if (ld1)
+ setbits_le32(&die->ctrl, BIT(6));
+ else
+ clrbits_le32(&die->ctrl, BIT(6));
+}
+
+static void mvebu_efuse_ld_read(const struct mvebu_efuse_die __iomem *die,
+ bool ld1, struct mvebu_efuse_ld *ld)
+{
+ size_t i;
+
+ mvebu_efuse_ld_select(die, ld1);
+
+ for (i = 0; i < ARRAY_SIZE(ld->array); i++) {
+ ld->array[i] = readl(&die->ld.array[i]);
+ if (i & 1)
+ ld->array[i] &= ~MVEBU_EFUSE_LD_PARITY_BIT;
+ }
+}
+
+static int mvebu_efuse_ld_write(const struct mvebu_efuse_die __iomem *die,
+ bool ld1, bool secure, const struct mvebu_efuse_ld *ld)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(ld->array); i++)
+ if ((i & 1) && (ld->array[i] & MVEBU_EFUSE_LD_PARITY_BIT))
+ return -EINVAL;
+
+ mvebu_efuse_ld_select(die, ld1);
+ mvebu_efuse_program_set(die, true);
+ mvebu_efuse_secure_set(die, secure);
+
+ for (i = 0; i < ARRAY_SIZE(ld->array); i++)
+ writel(ld->array[i], &die->ld.array[i]);
+
+ writel(0, &die->ld.burn);
+ mdelay(1);
+
+ mvebu_efuse_program_set(die, false);
+ return 0;
+}
+
+static int mvebu_efuse_hd_read_line(const struct mvebu_efuse_die __iomem *die,
+ u8 line, struct mvebu_efuse_hd_line *hdl)
+{
+ if (line >= ARRAY_SIZE(die->hd.line))
+ return -ERANGE;
+
+ hdl->array[0] = readl(&die->hd.line[line].array[0]);
+ hdl->array[1] = readl(&die->hd.line[line].array[1]);
+ hdl->array[1] &= ~MVEBU_EFUSE_HD_ECC_MASK;
+ hdl->lock = readl(&die->hd.line[line].lock);
+ return 0;
+}
+
+static int mvebu_efuse_hd_write_line(const struct mvebu_efuse_die __iomem *die,
+ u8 line, const struct mvebu_efuse_hd_line *hdl)
+{
+ if (line >= ARRAY_SIZE(die->hd.line))
+ return -ERANGE;
+
+ if (hdl->array[1] & MVEBU_EFUSE_HD_ECC_MASK)
+ return -EINVAL;
+
+ mvebu_efuse_program_set(die, true);
+
+ writel(hdl->array[0], &die->hd.line[line].array[0]);
+ writel(hdl->array[1], &die->hd.line[line].array[1]);
+ writel(0, &die->hd.line[line].lock);
+ mdelay(1);
+
+ mvebu_efuse_program_set(die, false);
+ return 0;
+}
+
+
+/*
+ * Commandline interface
+ */
+
+static int mvefuse_die(const char *diestr,
+ const struct mvebu_efuse_die __iomem **diep)
+{
+ if (!strcmp(diestr, "ap")) {
+ *diep = mvebu_efuse_dies[0];
+ } else if (!strcmp(diestr, "cp0")) {
+ *diep = mvebu_efuse_dies[1];
+ } else {
+ printf("\"%s\" is not a valid die\n", diestr);
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+static int mvefuse_mod(const char *fuse, enum mvebu_efuse_mod *modp)
+{
+ if (!strcmp(fuse, "ld0")) {
+ *modp = MVEBU_EFUSE_MOD_LD0;
+ } else if (!strcmp(fuse, "ld1")) {
+ *modp = MVEBU_EFUSE_MOD_LD1;
+ } else if (!strncmp(fuse, "hd", 2)) {
+ *modp = MVEBU_EFUSE_MOD_HD;
+ } else {
+ printf("\"%s\" is not a valid fuse\n", fuse);
+ return CMD_RET_USAGE;
+ }
+
+ return 0;
+}
+
+#define MVEFUSE_LINE_ALL 0xff
+
+static int mvefuse_line(const char *fuse, u8 *linep)
+{
+ unsigned long line;
+
+ if (!strcmp(fuse, "hd")) {
+ *linep = MVEFUSE_LINE_ALL;
+ return 0;
+ }
+
+ line = simple_strtoul(&fuse[2], NULL, 0);
+ if (line >= MVEBU_EFUSE_HD_LINES) {
+ printf("\"%s\" is not valid HD fuse\n", fuse);
+ return CMD_RET_USAGE;
+ }
+
+ *linep = line;
+ return 0;
+}
+
+static int mvefuse_parse(char * const *argv,
+ const struct mvebu_efuse_die __iomem **diep,
+ enum mvebu_efuse_mod *modp,
+ u8 *linep)
+{
+ int err;
+
+ err = mvefuse_die(argv[0], diep);
+ if (err)
+ return err;
+
+ err = mvefuse_mod(argv[1], modp);
+ if (err)
+ return err;
+
+ if (*modp != MVEBU_EFUSE_MOD_HD)
+ return 0;
+
+ return mvefuse_line(argv[1], linep);
+}
+
+static int do_mvefuse_md_ld(const struct mvebu_efuse_die __iomem *die,
+ bool ld1)
+{
+ struct mvebu_efuse_ld ld;
+
+ mvebu_efuse_ld_read(die, ld1, &ld);
+
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 16, 4,
+ ld.array, sizeof(ld.array), false);
+ return 0;
+}
+
+static int do_mvefuse_md_hd_line(const struct mvebu_efuse_die __iomem *die,
+ u8 line)
+{
+ struct mvebu_efuse_hd_line hdl;
+ int err;
+
+ err = mvebu_efuse_hd_read_line(die, line, &hdl);
+ if (err) {
+ printf("Unable to read HD line %u: %d\n", line, err);
+ return CMD_RET_FAILURE;
+ }
+
+ printf("%08x %08x %s\n", hdl.array[0], hdl.array[1],
+ hdl.lock ? "[LOCKED]" : "");
+ return 0;
+}
+
+static int do_mvefuse_md_hd_all(const struct mvebu_efuse_die __iomem *die)
+{
+ u8 line;
+ int err;
+
+ for (line = 0; line < MVEBU_EFUSE_HD_LINES; line++) {
+ printf("Line %02u: ", line);
+ err = do_mvefuse_md_hd_line(die, line);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int do_mvefuse_md(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const *argv)
+{
+ const struct mvebu_efuse_die __iomem *die;
+ enum mvebu_efuse_mod mod;
+ u8 line;
+ int err;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ err = mvefuse_parse(&argv[1], &die, &mod, &line);
+ if (err)
+ return err;
+
+ switch (mod) {
+ case MVEBU_EFUSE_MOD_LD0:
+ return do_mvefuse_md_ld(die, false);
+ case MVEBU_EFUSE_MOD_LD1:
+ return do_mvefuse_md_ld(die, true);
+ case MVEBU_EFUSE_MOD_HD:
+ if (line == MVEFUSE_LINE_ALL)
+ return do_mvefuse_md_hd_all(die);
+ else
+ return do_mvefuse_md_hd_line(die, line);
+ }
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_mvefuse_mw_ld(const struct mvebu_efuse_die __iomem *die,
+ bool ld1, int argc, char *const *argv,
+ bool interactive)
+{
+ struct mvebu_efuse_ld ld;
+ bool secure = false;
+ size_t i;
+ int err;
+
+ mvebu_efuse_ld_read(die, ld1, &ld);
+
+ switch (argc) {
+ case 9:
+ if (!strcmp(argv[8], "lock"))
+ secure = true;
+ else
+ return CMD_RET_USAGE;
+
+ /* fallthrough */
+ case 8:
+ for (i = 0; i < ARRAY_SIZE(ld.array); i++)
+ ld.array[i] |= hextoul(argv[i], NULL);
+
+ break;
+
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ if (interactive) {
+ printf("The following values will be written%s into %s:\n",
+ secure ? ", and PERMANENTLY LOCKED," : "",
+ ld1 ? "LD1" : "LD0");
+
+ print_hex_dump("", DUMP_PREFIX_OFFSET, 16, 4,
+ ld.array, sizeof(ld.array), false);
+
+ puts("WARNING: This operation is irreversible\n"
+ "Are you sure you want to continue? (y/N)\n");
+
+ if (!confirm_yesno()) {
+ puts("Programming aborted\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ err = mvebu_efuse_ld_write(die, ld1, secure, &ld);
+ if (err) {
+ printf("Failed to program %s: %d\n", ld1 ? "LD1" : "LD0", err);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_mvefuse_mw_hd_line(const struct mvebu_efuse_die __iomem *die,
+ u8 line, int argc, char *const *argv,
+ bool interactive)
+{
+ struct mvebu_efuse_hd_line hdl;
+ int err;
+
+ mvebu_efuse_hd_read_line(die, line, &hdl);
+ if (hdl.lock) {
+ printf("Unable to program LOCKED line HD%u\n", line);
+ return CMD_RET_FAILURE;
+ }
+
+ switch (argc) {
+ case 3:
+ /* In contrast to the LD fuse array, HD fuse lines are
+ * always locked when programmed, make sure the user
+ * is aware of this by requiring the "lock" keyword to
+ * be specified. */
+ if (strcmp(argv[2], "lock"))
+ return CMD_RET_USAGE;
+
+ hdl.array[0] |= hextoul(argv[0], NULL);
+ hdl.array[1] |= hextoul(argv[1], NULL);
+ break;
+
+ default:
+ return CMD_RET_USAGE;
+ }
+
+ if (interactive) {
+ printf("The following values will be PERMANENTLY written into HD%u:\n",
+ line);
+
+ printf("%08x %08x\n", hdl.array[0], hdl.array[1]);
+
+ puts("WARNING: This operation is irreversible\n"
+ "Are you sure you want to continue? (y/N)\n");
+
+ if (!confirm_yesno()) {
+ puts("Programming aborted\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ err = mvebu_efuse_hd_write_line(die, line, &hdl);
+ if (err) {
+ printf("Failed to program HD%u: %d\n", line, err);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_mvefuse_mw(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const *argv)
+{
+ const struct mvebu_efuse_die __iomem *die;
+ enum mvebu_efuse_mod mod;
+ bool interactive = true;
+ u8 line;
+ int err;
+
+ /* Pop off "md" */
+ argv++, argc--;
+
+ if (argc && (!strcmp("-y", argv[0]))) {
+ interactive = false;
+ argv++, argc--;
+ }
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ err = mvefuse_parse(argv, &die, &mod, &line);
+ if (err)
+ return err;
+
+ /* Pop off <die> and <fuse> */
+ argv++, argc--;
+ argv++, argc--;
+
+ switch (mod) {
+ case MVEBU_EFUSE_MOD_LD0:
+ return do_mvefuse_mw_ld(die, false, argc, argv, interactive);
+ case MVEBU_EFUSE_MOD_LD1:
+ return do_mvefuse_mw_ld(die, true, argc, argv, interactive);
+ case MVEBU_EFUSE_MOD_HD:
+ if (line == MVEFUSE_LINE_ALL)
+ return CMD_RET_USAGE;
+ else
+ return do_mvefuse_mw_hd_line(die, line, argc, argv, interactive);
+ }
+
+ return CMD_RET_FAILURE;
+}
+
+static int do_mvefuse_unlocked(struct cmd_tbl *cmdtp, int flag,
+ int argc, char *const argv[])
+{
+ const struct mvebu_efuse_die __iomem *die;
+ struct mvebu_efuse_hd_line hdl;
+ enum mvebu_efuse_mod mod;
+ u8 line;
+ int err;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ err = mvefuse_parse(&argv[1], &die, &mod, &line);
+ if (err)
+ return err;
+
+ if (mod != MVEBU_EFUSE_MOD_HD) {
+ printf("Only HD fuse lines have individual lock bits\n");
+ return CMD_RET_FAILURE;
+ }
+
+ if (line == MVEFUSE_LINE_ALL) {
+ printf("Only an individual fuse line can be tested\n");
+ return CMD_RET_FAILURE;
+ }
+
+ err = mvebu_efuse_hd_read_line(die, line, &hdl);
+ if (err) {
+ printf("Unable to read HD%u: %d\n", line, err);
+ return CMD_RET_FAILURE;
+ }
+
+ return !!hdl.lock;
+}
+
+U_BOOT_CMD_WITH_SUBCMDS(
+ mvefuse, "Marvell eFuse Access",
+ "md <die> <fuse> - Dump the contents of <fuse> from <die>\n"
+ "mvefuse mw [-y] <die> <fuse> <words...> [lock] - Program <words..> into\n"
+ " <fuse> in <die>, optionally permanently locking it\n"
+ "mvefuse unlocked <die> hd<0-63> - Test if an HD fuse line on <die> is \n"
+ " unlocked\n"
+ "\n"
+ "<die>: ap|cp0\n"
+ "<fuse>: ld0|ld1|hd<0-63>\n",
+
+ U_BOOT_SUBCMD_MKENT(md, 3, 1, do_mvefuse_md),
+ U_BOOT_SUBCMD_MKENT(mw, 12, 1, do_mvefuse_mw),
+ U_BOOT_SUBCMD_MKENT(unlocked, 3, 1, do_mvefuse_unlocked));
--
2.34.1
-13
View File
@@ -1,13 +0,0 @@
Index: uboot-2023.07.02/arch/arm/dts/cn9130-crb.dtsi
===================================================================
--- uboot-2023.07.02.orig/arch/arm/dts/cn9130-crb.dtsi
+++ uboot-2023.07.02/arch/arm/dts/cn9130-crb.dtsi
@@ -239,7 +239,7 @@
&cp0_eth0 {
status = "okay";
- phy-mode = "sfi";
+ phy-mode = "10gbase-r";
};
&cp0_eth1 {