mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 04:33:00 +02:00
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:
committed by
Joachim Wiberg
parent
97747a95ea
commit
c385e2e9c2
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
Reference in New Issue
Block a user