[PATCH 9/9] net: ksz9477: add support for KSZ GbE switches using SPI bus

Romain Naour romain.naour at smile.fr
Tue Oct 8 09:54:32 CEST 2024


The Microchip KSZ Gigabit Ethernet Switches support
SGMII/RGMII/MII/RMII with register access via SPI, I2C, or MDIO.

Since this driver is now able to check the underlying bus type,
handle the case when the SPI bus is used.

The SPI bus is only used for 8/16/32 wide access of registers.

Reword Kconfig option to include SPI bus support.

Signed-off-by: Romain Naour <romain.naour at smile.fr>
---
 drivers/net/Kconfig   |   6 +--
 drivers/net/ksz9477.c | 103 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index e7d0ddfe25a..39379428b85 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -511,11 +511,11 @@ config KS8851_MLL
 	  The Microchip KS8851 parallel bus external ethernet interface chip.
 
 config KSZ9477
-	bool "Microchip KSZ9477 I2C controller driver"
-	depends on DM_DSA && DM_I2C
+	bool "Microchip KSZ9477 controller driver"
+	depends on DM_DSA && (DM_I2C || DM_SPI)
 	help
 	  This driver implements a DSA switch driver for the KSZ9477 family
-	  of GbE switches using the I2C interface.
+	  of GbE switches using the I2C or SPI interface.
 
 config LITEETH
 	bool "LiteX LiteEth Ethernet MAC"
diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c
index 1190b591fcb..7ebbe197660 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -14,6 +14,9 @@
 #if CONFIG_IS_ENABLED(DM_I2C)
 # include <i2c.h>
 #endif
+#if CONFIG_IS_ENABLED(DM_SPI)
+# include <spi.h>
+#endif
 #include <net/dsa.h>
 
 #include <asm-generic/gpio.h>
@@ -73,6 +76,14 @@
 #define MMD_SETUP(mode, dev)		(((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev))
 #define REG_PORT_PHY_MMD_INDEX_DATA	0x011C
 
+/* SPI specific define (opcodes) */
+#define KSZ_SPI_OP_RD			3
+#define KSZ_SPI_OP_WR			2
+
+#define KSZ9477_SPI_ADDR_SHIFT		24
+#define KSZ9477_SPI_ADDR_ALIGN		3
+#define KSZ9477_SPI_TURNAROUND_SHIFT	5
+
 /**
  * struct ksz_phy_ops - low-level KSZ bus operations
  */
@@ -124,6 +135,92 @@ static struct ksz_phy_ops phy_i2c_ops = {
 };
 #endif
 
+#if CONFIG_IS_ENABLED(DM_SPI)
+/**
+ * ksz_spi_xfer() - only used for 8/16/32 bits bus access
+ *
+ * @dev:	The SPI slave device which will be sending/receiving the data.
+ * @reg:	register address.
+ * @out:	Pointer to a string of bits to send out.  The bits are
+ *		held in a byte array and are sent MSB first.
+ * @in:		Pointer to a string of bits that will be filled in.
+ * @len:	number of bytes to read.
+ *
+ * Return: 0 on success, not 0 on failure
+ */
+static int ksz_spi_xfer(struct udevice *dev, u32 reg, const u8 *out,
+			u8 *in, u16 len)
+{
+	int ret;
+	u32 addr = 0;
+	u8 opcode;
+
+	if (in && out) {
+		printf("%s: can't do full duplex\n", __func__);
+		return -EINVAL;
+	}
+
+	if (len > 4 || len == 0) {
+		printf("%s: only 8/16/32 bits bus access supported\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	ret = dm_spi_claim_bus(dev);
+	if (ret < 0) {
+		printf("%s: could not claim bus\n", __func__);
+		return ret;
+	}
+
+	opcode = (in ? KSZ_SPI_OP_RD : KSZ_SPI_OP_WR);
+
+	/* The actual device address space is 16 bits (A15 - A0),
+	 * so the values of address bits A23 - A16 in the SPI
+	 * command/address phase are “don't care”.
+	 */
+	addr |= opcode << (KSZ9477_SPI_ADDR_SHIFT + KSZ9477_SPI_TURNAROUND_SHIFT);
+	addr |= reg << KSZ9477_SPI_TURNAROUND_SHIFT;
+
+	addr = __swab32(addr);
+
+	ret = dm_spi_xfer(dev, 32, &addr, NULL, SPI_XFER_BEGIN);
+	if (ret) {
+		printf("%s ERROR: dm_spi_xfer addr (%u)\n", __func__, ret);
+		goto release_bus;
+	}
+
+	ret = dm_spi_xfer(dev, len * 8, out, in, SPI_XFER_END);
+	if (ret) {
+		printf("%s ERROR: dm_spi_xfer data (%u)\n", __func__, ret);
+		goto release_bus;
+	}
+
+release_bus:
+	/* If an error occurred, release the chip by deasserting the CS */
+	if (ret < 0)
+		dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
+
+	dm_spi_release_bus(dev);
+
+	return ret;
+}
+
+static inline int ksz_spi_read(struct udevice *dev, u32 reg, u8 *val, int len)
+{
+	return ksz_spi_xfer(dev, reg, NULL, val, len);
+}
+
+static inline int ksz_spi_write(struct udevice *dev, u32 reg, u8 *val, int len)
+{
+	return ksz_spi_xfer(dev, reg, val, NULL, len);
+}
+
+static struct ksz_phy_ops phy_spi_ops = {
+	.read = ksz_spi_read,
+	.write = ksz_spi_write,
+};
+#endif
+
 static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val)
 {
 	struct ksz_dsa_priv *priv = dev_get_priv(dev);
@@ -602,6 +699,12 @@ static int ksz_probe(struct udevice *dev)
 		}
 		break;
 	}
+#endif
+#if CONFIG_IS_ENABLED(DM_SPI)
+	case UCLASS_SPI: {
+		ksz_ops_register(dev, &phy_spi_ops);
+		break;
+	}
 #endif
 	default:
 		dev_err(dev, "invalid parent bus (%s)\n",
-- 
2.45.0



More information about the U-Boot mailing list