[PATCH] net: ksz9477: add support for KSZ9893 GbE switch

Karsten Wiese karsten.wiese at protechna.com
Wed Jun 28 16:54:45 CEST 2023


Copy and tweak the required code from the linux kernel.
Only the KSZ9893 has been tested.

Signed-off-by: Karsten Wiese <karsten.wiese at protechna.com>

---
 drivers/net/ksz9477.c | 103 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 89 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c
index 6b59b5fcd2..43baa69961 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -16,6 +16,10 @@
 
 #include <asm-generic/gpio.h>
 
+/* Used with variable features to indicate capabilities. */
+#define NEW_XMII			BIT(1)
+#define IS_9893				BIT(2)
+
 /* Global registers */
 
 /* Chip ID */
@@ -41,6 +45,13 @@
 #define PORT_RMII_SEL			0x1
 #define PORT_GMII_SEL			0x2
 #define PORT_MII_SEL			0x3
+/* S1 */
+#define PORT_MII_1000MBIT_S1		BIT(6)
+/* S1 */
+#define PORT_MII_SEL_S1			0x0
+#define PORT_RMII_SEL_S1		0x1
+#define PORT_GMII_SEL_S1		0x2
+#define PORT_RGMII_SEL_S1		0x3
 
 /* Port MSTP State Register */
 #define REG_PORT_MSTP_STATE		0x0b04
@@ -62,6 +73,8 @@
 
 struct ksz_dsa_priv {
 	struct udevice *dev;
+
+	u32 features;			/* chip specific features */
 };
 
 static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val)
@@ -284,6 +297,60 @@ U_BOOT_DRIVER(ksz_mdio) = {
 	.plat_auto	= sizeof(struct mdio_perdev_priv),
 };
 
+static void ksz9477_set_gbit(struct ksz_dsa_priv *priv, bool gbit, u8 *data)
+{
+	if (priv->features & NEW_XMII) {
+		if (gbit)
+			*data &= ~PORT_MII_NOT_1GBIT;
+		else
+			*data |= PORT_MII_NOT_1GBIT;
+	} else {
+		if (gbit)
+			*data |= PORT_MII_1000MBIT_S1;
+		else
+			*data &= ~PORT_MII_1000MBIT_S1;
+	}
+}
+
+static void ksz9477_set_xmii(struct ksz_dsa_priv *priv, int mode, u8 *data)
+{
+	u8 xmii;
+
+	if (priv->features & NEW_XMII) {
+		switch (mode) {
+		case 0:
+			xmii = PORT_MII_SEL;
+			break;
+		case 1:
+			xmii = PORT_RMII_SEL;
+			break;
+		case 2:
+			xmii = PORT_GMII_SEL;
+			break;
+		default:
+			xmii = PORT_RGMII_SEL;
+			break;
+		}
+	} else {
+		switch (mode) {
+		case 0:
+			xmii = PORT_MII_SEL_S1;
+			break;
+		case 1:
+			xmii = PORT_RMII_SEL_S1;
+			break;
+		case 2:
+			xmii = PORT_GMII_SEL_S1;
+			break;
+		default:
+			xmii = PORT_RGMII_SEL_S1;
+			break;
+		}
+	}
+	*data &= ~PORT_MII_SEL_M;
+	*data |= xmii;
+}
+
 static int ksz_port_setup(struct udevice *dev, int port,
 			  phy_interface_t interface)
 {
@@ -293,9 +360,11 @@ static int ksz_port_setup(struct udevice *dev, int port,
 	dev_dbg(dev, "%s P%d %s\n", __func__, port + 1,
 		(port == pdata->cpu_port) ? "cpu" : "");
 
+	struct ksz_dsa_priv *priv = dev_get_priv(dev);
 	if (port != pdata->cpu_port) {
-		/* phy port: config errata and leds */
-		ksz_phy_errata_setup(dev, port);
+		if (priv->features & NEW_XMII)
+			/* phy port: config errata and leds */
+			ksz_phy_errata_setup(dev, port);
 	} else {
 		/* cpu port: configure MAC interface mode */
 		ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
@@ -303,24 +372,20 @@ static int ksz_port_setup(struct udevice *dev, int port,
 			phy_string_for_interface(interface));
 		switch (interface) {
 		case PHY_INTERFACE_MODE_MII:
-			data8 &= ~PORT_MII_SEL_M;
-			data8 |= PORT_MII_SEL;
-			data8 |= PORT_MII_NOT_1GBIT;
+			ksz9477_set_xmii(priv, 0, &data8);
+			ksz9477_set_gbit(priv, false, &data8);
 			break;
 		case PHY_INTERFACE_MODE_RMII:
-			data8 &= ~PORT_MII_SEL_M;
-			data8 |= PORT_RMII_SEL;
-			data8 |= PORT_MII_NOT_1GBIT;
+			ksz9477_set_xmii(priv, 1, &data8);
+			ksz9477_set_gbit(priv, false, &data8);
 			break;
 		case PHY_INTERFACE_MODE_GMII:
-			data8 &= ~PORT_MII_SEL_M;
-			data8 |= PORT_GMII_SEL;
-			data8 &= ~PORT_MII_NOT_1GBIT;
+			ksz9477_set_xmii(priv, 2, &data8);
+			ksz9477_set_gbit(priv, true, &data8);
 			break;
 		default:
-			data8 &= ~PORT_MII_SEL_M;
-			data8 |= PORT_RGMII_SEL;
-			data8 &= ~PORT_MII_NOT_1GBIT;
+			ksz9477_set_xmii(priv, 3, &data8);
+			ksz9477_set_gbit(priv, true, &data8);
 			data8 &= ~PORT_RGMII_ID_IG_ENABLE;
 			data8 &= ~PORT_RGMII_ID_EG_ENABLE;
 			if (interface == PHY_INTERFACE_MODE_RGMII_ID ||
@@ -329,6 +394,8 @@ static int ksz_port_setup(struct udevice *dev, int port,
 			if (interface == PHY_INTERFACE_MODE_RGMII_ID ||
 			    interface == PHY_INTERFACE_MODE_RGMII_TXID)
 				data8 |= PORT_RGMII_ID_EG_ENABLE;
+			if (priv->features & IS_9893)
+				data8 &= ~PORT_MII_MAC_MODE;
 			break;
 		}
 		ksz_write8(dev, PORT_CTRL_ADDR(port, REG_PORT_XMII_CTRL_1), data8);
@@ -479,10 +546,17 @@ static int ksz_i2c_probe(struct udevice *dev)
 	case 0x00989700:
 		puts("KSZ9897S: ");
 		break;
+	case 0x00989300:
+		puts("KSZ9893R: ");
+		break;
 	default:
 		dev_err(dev, "invalid chip id: 0x%08x\n", id);
 		return -EINVAL;
 	}
+	if ((id & 0xf00) == 0x300)
+		priv->features |= IS_9893;
+	else
+		priv->features |= NEW_XMII;
 
 	/* probe mdio bus */
 	ret = ksz_probe_mdio(dev);
@@ -503,6 +577,7 @@ static const struct udevice_id ksz_i2c_ids[] = {
 	{ .compatible = "microchip,ksz9897" },
 	{ .compatible = "microchip,ksz9477" },
 	{ .compatible = "microchip,ksz9567" },
+	{ .compatible = "microchip,ksz9893" },
 	{ }
 };
 
-- 
2.25.1




More information about the U-Boot mailing list