]> Gentwo Git Trees - linux/.git/commitdiff
fbnic: Add SW shim for MDIO interface to PMD and PCS
authorAlexander Duyck <alexanderduyck@fb.com>
Fri, 21 Nov 2025 16:40:45 +0000 (08:40 -0800)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 27 Nov 2025 09:41:31 +0000 (10:41 +0100)
In order for us to support a PCS device we need to add an MDIO bus to allow
the drivers to have access to the registers for the device.  This change
adds such an interface.

The interface will consist of 2 PHY addrs, the first one consisting of a
PMD and PCS, and the second just being a PCS. There is a need for 2 PHYs
addrs due to the fact that in order to support the 50GBase-CR2 mode we will
need to access and configure the PCS vendor registers and RSFEC registers
from the second lane identical to the first.

Signed-off-by: Alexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/176374324532.959489.15389723111560978054.stgit@ahduyck-xeon-server.home.arpa
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/meta/fbnic/Makefile
drivers/net/ethernet/meta/fbnic/fbnic.h
drivers/net/ethernet/meta/fbnic/fbnic_mac.h
drivers/net/ethernet/meta/fbnic/fbnic_mdio.c [new file with mode: 0644]
drivers/net/ethernet/meta/fbnic/fbnic_pci.c

index 15e8ff649615918ddd60977448492ec065d29676..72c41af65364ba921adf0928a28f52df90bcca5a 100644 (file)
@@ -21,6 +21,7 @@ fbnic-y := fbnic_csr.o \
           fbnic_pci.o \
           fbnic_phylink.o \
           fbnic_rpc.o \
+          fbnic_mdio.o \
           fbnic_time.o \
           fbnic_tlv.o \
           fbnic_txrx.o \
index fac1283d0ade63845e8ddff992240b93a5db9d65..779a083b9215905f2cd39287af6d3c45a0037685 100644 (file)
@@ -95,6 +95,9 @@ struct fbnic_dev {
        u64 prev_firmware_time;
 
        struct fbnic_fw_log fw_log;
+
+       /* MDIO bus for PHYs */
+       struct mii_bus *mdio_bus;
 };
 
 /* Reserve entry 0 in the MSI-X "others" array until we have filled all
@@ -204,6 +207,8 @@ void fbnic_dbg_exit(void);
 
 void fbnic_rpc_reset_valid_entries(struct fbnic_dev *fbd);
 
+int fbnic_mdiobus_create(struct fbnic_dev *fbd);
+
 void fbnic_csr_get_regs(struct fbnic_dev *fbd, u32 *data, u32 *regs_version);
 int fbnic_csr_regs_len(struct fbnic_dev *fbd);
 
index 9b4fb0586dff163de5ce26c9c3881c028965505f..f08fe8b7c497aedb45514e9b2fb8b98bf470b21c 100644 (file)
@@ -56,6 +56,7 @@ enum {
        FBNIC_AUI_50GAUI1       = 2,    /* 53.125GBd    53.125   * 1 */
        FBNIC_AUI_100GAUI2      = 3,    /* 106.25GBd    53.125   * 2 */
        FBNIC_AUI_UNKNOWN       = 4,
+       __FBNIC_AUI_MAX__
 };
 
 #define FBNIC_AUI_MODE_R2      (FBNIC_AUI_LAUI2)
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c
new file mode 100644 (file)
index 0000000..709041f
--- /dev/null
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) Meta Platforms, Inc. and affiliates. */
+
+#include <linux/mdio.h>
+#include <linux/pcs/pcs-xpcs.h>
+
+#include "fbnic.h"
+#include "fbnic_netdev.h"
+
+#define DW_VENDOR              BIT(15)
+#define FBNIC_PCS_VENDOR       BIT(9)
+#define FBNIC_PCS_ZERO_MASK    (DW_VENDOR - FBNIC_PCS_VENDOR)
+
+static int
+fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum)
+{
+       u8 aui = FBNIC_AUI_UNKNOWN;
+       struct fbnic_net *fbn;
+       int ret = 0;
+
+       /* We don't need a second PMD, just one can handle both lanes */
+       if (addr)
+               return 0;
+
+       if (fbd->netdev) {
+               fbn = netdev_priv(fbd->netdev);
+               if (fbn->aui < FBNIC_AUI_UNKNOWN)
+                       aui = fbn->aui;
+       }
+
+       switch (regnum) {
+       case MDIO_DEVID1:
+               ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16;
+               break;
+       case MDIO_DEVID2:
+               ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff;
+               break;
+       case MDIO_DEVS1:
+               ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
+               break;
+       case MDIO_STAT2:
+               ret = MDIO_STAT2_DEVPRST_VAL;
+               break;
+       case MDIO_PMA_RXDET:
+               /* If training isn't complete default to 0 */
+               if (fbd->pmd_state != FBNIC_PMD_SEND_DATA)
+                       break;
+               /* Report either 1 or 2 lanes detected depending on config */
+               ret = (MDIO_PMD_RXDET_GLOBAL | MDIO_PMD_RXDET_0) |
+                     ((aui & FBNIC_AUI_MODE_R2) *
+                      (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2));
+               break;
+       default:
+               break;
+       }
+
+       dev_dbg(fbd->dev,
+               "SWMII PMD Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
+               addr, regnum, ret);
+
+       return ret;
+}
+
+static int
+fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum)
+{
+       int ret, offset = 0;
+
+       /* We will need access to both PCS instances to get config info */
+       if (addr >= 2)
+               return 0;
+
+       /* Report 0 for reserved registers */
+       if (regnum & FBNIC_PCS_ZERO_MASK)
+               return 0;
+
+       /* Intercept and return correct ID for PCS */
+       if (regnum == MDIO_DEVID1)
+               return DW_XPCS_ID >> 16;
+       if (regnum == MDIO_DEVID2)
+               return DW_XPCS_ID & 0xffff;
+       if (regnum == MDIO_DEVS1)
+               return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
+
+       /* Swap vendor page bit for FBNIC PCS vendor page bit */
+       if (regnum & DW_VENDOR)
+               offset ^= DW_VENDOR | FBNIC_PCS_VENDOR;
+
+       ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset));
+
+       dev_dbg(fbd->dev,
+               "SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
+               addr, regnum, ret);
+
+       return ret;
+}
+
+static int
+fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
+{
+       struct fbnic_dev *fbd = bus->priv;
+
+       if (devnum == MDIO_MMD_PMAPMD)
+               return fbnic_mdio_read_pmd(fbd, addr, regnum);
+
+       if (devnum == MDIO_MMD_PCS)
+               return fbnic_mdio_read_pcs(fbd, addr, regnum);
+
+       return 0;
+}
+
+static void
+fbnic_mdio_write_pmd(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
+{
+       dev_dbg(fbd->dev,
+               "SWMII PMD Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
+               addr, regnum, val);
+}
+
+static void
+fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
+{
+       dev_dbg(fbd->dev,
+               "SWMII PCS Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
+               addr, regnum, val);
+
+       /* Allow access to both halves of PCS for 50R2 config */
+       if (addr > 2)
+               return;
+
+       /* Skip write for reserved registers */
+       if (regnum & FBNIC_PCS_ZERO_MASK)
+               return;
+
+       /* Swap vendor page bit for FBNIC PCS vendor page bit */
+       if (regnum & DW_VENDOR)
+               regnum ^= DW_VENDOR | FBNIC_PCS_VENDOR;
+
+       fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val);
+}
+
+static int
+fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum,
+                    int regnum, u16 val)
+{
+       struct fbnic_dev *fbd = bus->priv;
+
+       if (devnum == MDIO_MMD_PMAPMD)
+               fbnic_mdio_write_pmd(fbd, addr, regnum, val);
+
+       if (devnum == MDIO_MMD_PCS)
+               fbnic_mdio_write_pcs(fbd, addr, regnum, val);
+
+       return 0;
+}
+
+/**
+ * fbnic_mdiobus_create - Create an MDIO bus to allow interfacing w/ PHYs
+ * @fbd: Pointer to FBNIC device structure to populate bus on
+ *
+ * Initialize an MDIO bus and place a pointer to it on the fbd struct. This bus
+ * will be used to interface with the PMA/PMD and PCS.
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int fbnic_mdiobus_create(struct fbnic_dev *fbd)
+{
+       struct mii_bus *bus;
+       int err;
+
+       bus = devm_mdiobus_alloc(fbd->dev);
+       if (!bus)
+               return -ENOMEM;
+
+       bus->name = "fbnic_mii_bus";
+       bus->read_c45 = &fbnic_mdio_read_c45;
+       bus->write_c45 = &fbnic_mdio_write_c45;
+
+       /* Disable PHY auto probing. We will add PCS manually */
+       bus->phy_mask = ~0;
+
+       bus->parent = fbd->dev;
+       bus->priv = fbd;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(fbd->dev));
+
+       err = devm_mdiobus_register(fbd->dev, bus);
+       if (err) {
+               dev_err(fbd->dev, "Failed to create MDIO bus: %d\n", err);
+               return err;
+       }
+
+       fbd->mdio_bus = bus;
+
+       return 0;
+}
index dea5367d219013a5a42f0a169a7dc9d6a5445681..861d98099c449f8b927b6457f8e6c2f62b3b34ea 100644 (file)
@@ -339,6 +339,9 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto init_failure_mode;
        }
 
+       if (fbnic_mdiobus_create(fbd))
+               goto init_failure_mode;
+
        netdev = fbnic_netdev_alloc(fbd);
        if (!netdev) {
                dev_err(&pdev->dev, "Netdev allocation failed\n");