summaryrefslogtreecommitdiffstats
path: root/drivers/net/fsl_enetc_mdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/fsl_enetc_mdio.c')
-rw-r--r--drivers/net/fsl_enetc_mdio.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c
new file mode 100644
index 0000000000..773d4afd52
--- /dev/null
+++ b/drivers/net/fsl_enetc_mdio.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ENETC ethernet controller driver
+ * Copyright 2019 NXP
+ */
+
+#include <common.h>
+#include <net.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+#include <io.h>
+#include <linux/mdio.h>
+
+#include "fsl_enetc.h"
+
+static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
+{
+ int to = 10000;
+
+ while ((enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) &&
+ --to)
+ cpu_relax();
+}
+
+int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+ int reg)
+{
+ if (devad == MDIO_DEVAD_NONE)
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
+ else
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
+ enetc_mdio_wait_bsy(priv);
+
+ if (devad == MDIO_DEVAD_NONE) {
+ enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
+ (addr << 5) | reg);
+ } else {
+ enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
+ enetc_mdio_wait_bsy(priv);
+
+ enetc_write(priv, ENETC_MDIO_STAT, reg);
+ enetc_mdio_wait_bsy(priv);
+
+ enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
+ (addr << 5) | devad);
+ }
+
+ enetc_mdio_wait_bsy(priv);
+ if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER)
+ return ENETC_MDIO_READ_ERR;
+
+ return enetc_read(priv, ENETC_MDIO_DATA);
+}
+
+int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
+ int reg, u16 val)
+{
+ if (devad == MDIO_DEVAD_NONE)
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
+ else
+ enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
+ enetc_mdio_wait_bsy(priv);
+
+ if (devad != MDIO_DEVAD_NONE) {
+ enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
+ enetc_write(priv, ENETC_MDIO_STAT, reg);
+ } else {
+ enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg);
+ }
+ enetc_mdio_wait_bsy(priv);
+
+ enetc_write(priv, ENETC_MDIO_DATA, val);
+ enetc_mdio_wait_bsy(priv);
+
+ return 0;
+}
+
+static int enetc_mdio_read(struct mii_bus *bus, int addr, int reg)
+{
+ struct enetc_mdio_priv *priv = bus->priv;
+
+ return enetc_mdio_read_priv(priv, addr, MDIO_DEVAD_NONE, reg);
+}
+
+static int enetc_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+ struct enetc_mdio_priv *priv = bus->priv;
+
+ return enetc_mdio_write_priv(priv, addr, MDIO_DEVAD_NONE, reg, val);
+}
+
+static int enetc_mdio_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct enetc_mdio_priv *priv;
+
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->regs_base = pci_iomap(pdev, 0);
+ if (!priv->regs_base) {
+ dev_err(&pdev->dev, "failed to map BAR0\n");
+ return -EINVAL;
+ }
+
+ priv->regs_base += ENETC_MDIO_BASE;
+
+ priv->bus.read = enetc_mdio_read;
+ priv->bus.write = enetc_mdio_write;
+ priv->bus.parent = &pdev->dev;
+ priv->bus.priv = priv;
+
+ return mdiobus_register(&priv->bus);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(enetc_mdio_pci_tbl) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
+ { },
+};
+
+static struct pci_driver enetc_mdio_driver = {
+ .name = "fsl_enetc_mdio",
+ .id_table = enetc_mdio_pci_tbl,
+ .probe = enetc_mdio_probe,
+};
+device_pci_driver(enetc_mdio_driver);