[PATCH 3/6] net: Add IPQ40xx MDIO driver

Robert Marko robert.marko at sartura.hr
Thu Oct 8 22:05:11 CEST 2020


This adds the driver for the IPQ40xx built-in MDIO.
This will be needed to support future PHY driver.

Signed-off-by: Robert Marko <robert.marko at sartura.hr>
Cc: Luka Perkov <luka.perkov at sartura.hr>
---
 MAINTAINERS                |   1 +
 drivers/net/Kconfig        |   7 ++
 drivers/net/Makefile       |   1 +
 drivers/net/mdio-ipq4019.c | 146 +++++++++++++++++++++++++++++++++++++
 4 files changed, 155 insertions(+)
 create mode 100644 drivers/net/mdio-ipq4019.c

diff --git a/MAINTAINERS b/MAINTAINERS
index aa6cbb965d..5f1a815c88 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -242,6 +242,7 @@ F:	include/dt-bindings/reset/qcom,ipq4019-reset.h
 F:	drivers/reset/reset-ipq4019.c
 F:	drivers/phy/phy-qcom-ipq4019-usb.c
 F:	drivers/spi/spi-qup.c
+F:	drivers/net/mdio-ipq4019.c
 
 ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K
 M:	Stefan Roese <sr at denx.de>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index a0d2d21a55..09a4688d69 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -701,6 +701,13 @@ config MDIO_MUX_I2CREG
 	  an I2C chip.  The board it was developed for uses a mux controlled by
 	  on-board FPGA which in turn is accessed as a chip over I2C.
 
+config MDIO_IPQ4019
+	bool "Qualcomm IPQ4019 MDIO interface support"
+	depends on DM_MDIO
+	help
+	  This driver supports the MDIO interface found in Qualcomm
+	  IPQ40xx series Soc-s.
+
 config MVMDIO
 	bool "Marvell MDIO interface support"
 	depends on DM_MDIO
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 03f01921ea..9cd4b36f57 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_LAN91C96) += lan91c96.o
 obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o
 obj-$(CONFIG_MACB) += macb.o
 obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
+obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
 obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o
 obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o
 obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o
diff --git a/drivers/net/mdio-ipq4019.c b/drivers/net/mdio-ipq4019.c
new file mode 100644
index 0000000000..bc68e1d506
--- /dev/null
+++ b/drivers/net/mdio-ipq4019.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm IPQ4019 MDIO driver
+ *
+ * Copyright (c) 2020 Sartura Ltd.
+ *
+ * Author: Luka Kovacic <luka.kovacic at sartura.hr>
+ * Author: Robert Marko <robert.marko at sartura.hr>
+ *
+ * Based on Linux driver
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <miiphy.h>
+#include <phy.h>
+
+#define MDIO_MODE_REG               0x40
+#define MDIO_ADDR_REG               0x44
+#define MDIO_DATA_WRITE_REG         0x48
+#define MDIO_DATA_READ_REG          0x4c
+#define MDIO_CMD_REG                0x50
+#define MDIO_CMD_ACCESS_BUSY        BIT(16)
+#define MDIO_CMD_ACCESS_START       BIT(8)
+#define MDIO_CMD_ACCESS_CODE_READ   0
+#define MDIO_CMD_ACCESS_CODE_WRITE  1
+
+/* 0 = Clause 22, 1 = Clause 45 */
+#define MDIO_MODE_BIT               BIT(8)
+
+#define IPQ4019_MDIO_TIMEOUT    10000
+#define IPQ4019_MDIO_SLEEP      10
+
+struct ipq4019_mdio_priv {
+	phys_addr_t mdio_base;
+};
+
+static int ipq4019_mdio_wait_busy(struct ipq4019_mdio_priv *priv)
+{
+	unsigned int busy;
+
+	return readl_poll_sleep_timeout(priv->mdio_base + MDIO_CMD_REG, busy,
+				  (busy & MDIO_CMD_ACCESS_BUSY) == 0, IPQ4019_MDIO_SLEEP,
+				  IPQ4019_MDIO_TIMEOUT);
+}
+
+int ipq4019_mdio_read(struct udevice *dev, int addr, int devad, int reg)
+{
+	struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
+	unsigned int cmd;
+
+	if (ipq4019_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	/* Issue the phy address and reg */
+	writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);
+
+	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
+
+	/* Issue read command */
+	writel(cmd, priv->mdio_base + MDIO_CMD_REG);
+
+	/* Wait read complete */
+	if (ipq4019_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	/* Read and return data */
+	return readl(priv->mdio_base + MDIO_DATA_READ_REG);
+}
+
+int ipq4019_mdio_write(struct udevice *dev, int addr, int devad,
+					  int reg, u16 val)
+{
+	struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
+	unsigned int cmd;
+
+	if (ipq4019_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	/* Issue the phy addreass and reg */
+	writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);
+
+	/* Issue write data */
+	writel(val, priv->mdio_base + MDIO_DATA_WRITE_REG);
+
+	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
+
+	/* Issue write command */
+	writel(cmd, priv->mdio_base + MDIO_CMD_REG);
+
+	/* Wait for write complete */
+
+	if (ipq4019_mdio_wait_busy(priv))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static const struct mdio_ops ipq4019_mdio_ops = {
+	.read = ipq4019_mdio_read,
+	.write = ipq4019_mdio_write,
+};
+
+static int ipq4019_mdio_bind(struct udevice *dev)
+{
+	if (ofnode_valid(dev->node))
+		device_set_name(dev, ofnode_get_name(dev->node));
+
+	return 0;
+}
+
+static int ipq4019_mdio_probe(struct udevice *dev)
+{
+	struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
+	unsigned int data;
+
+	priv->mdio_base = dev_read_addr(dev);
+	if (priv->mdio_base == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	/* Enter Clause 22 mode */
+	data = readl(priv->mdio_base + MDIO_MODE_REG);
+	data &= ~MDIO_MODE_BIT;
+	writel(data, priv->mdio_base + MDIO_MODE_REG);
+
+	return 0;
+}
+
+static const struct udevice_id ipq4019_mdio_ids[] = {
+	{ .compatible = "qcom,ipq4019-mdio", },
+	{ }
+};
+
+U_BOOT_DRIVER(ipq4019_mdio) = {
+	.name           = "ipq4019_mdio",
+	.id             = UCLASS_MDIO,
+	.of_match       = ipq4019_mdio_ids,
+	.bind           = ipq4019_mdio_bind,
+	.probe          = ipq4019_mdio_probe,
+	.ops            = &ipq4019_mdio_ops,
+	.priv_auto_alloc_size   = sizeof(struct ipq4019_mdio_priv),
+};
-- 
2.28.0



More information about the U-Boot mailing list