net: txgbe: support copper NIC with external PHY
authorJiawen Wu <jiawenwu@trustnetic.com>
Wed, 23 Aug 2023 06:19:34 +0000 (14:19 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 25 Aug 2023 06:42:59 +0000 (07:42 +0100)
Wangxun SP chip supports to connect with external PHY (marvell 88x3310),
which links to 10GBASE-T/1000BASE-T/100BASE-T. Add the identification of
media types from subsystem device IDs. For sp_media_copper, register mdio
bus for the external PHY.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/wangxun/Kconfig
drivers/net/ethernet/wangxun/libwx/wx_type.h
drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c

index 39596cd..23cd610 100644 (file)
@@ -41,6 +41,7 @@ config TXGBE
        tristate "Wangxun(R) 10GbE PCI Express adapters support"
        depends on PCI
        depends on COMMON_CLK
+       select MARVELL_10G_PHY
        select REGMAP
        select I2C
        select I2C_DESIGNWARE_PLATFORM
index 50b92cf..c5cbd17 100644 (file)
 #define WX_MAC_WDG_TIMEOUT           0x1100C
 #define WX_MAC_RX_FLOW_CTRL          0x11090
 #define WX_MAC_RX_FLOW_CTRL_RFE      BIT(0) /* receive fc enable */
+/* MDIO Registers */
+#define WX_MSCA                      0x11200
+#define WX_MSCA_RA(v)                FIELD_PREP(U16_MAX, v)
+#define WX_MSCA_PA(v)                FIELD_PREP(GENMASK(20, 16), v)
+#define WX_MSCA_DA(v)                FIELD_PREP(GENMASK(25, 21), v)
+#define WX_MSCC                      0x11204
+#define WX_MSCC_CMD(v)               FIELD_PREP(GENMASK(17, 16), v)
+
+enum WX_MSCA_CMD_value {
+       WX_MSCA_CMD_RSV = 0,
+       WX_MSCA_CMD_WRITE,
+       WX_MSCA_CMD_POST_READ,
+       WX_MSCA_CMD_READ,
+};
+
+#define WX_MSCC_SADDR                BIT(18)
+#define WX_MSCC_BUSY                 BIT(22)
+#define WX_MDIO_CLK(v)               FIELD_PREP(GENMASK(21, 19), v)
 #define WX_MMC_CONTROL               0x11800
 #define WX_MMC_CONTROL_RSTONRD       BIT(2) /* reset on read */
 
@@ -582,6 +600,13 @@ enum wx_mac_type {
        wx_mac_em
 };
 
+enum sp_media_type {
+       sp_media_unknown = 0,
+       sp_media_fiber,
+       sp_media_copper,
+       sp_media_backplane
+};
+
 enum em_mac_type {
        em_mac_type_unknown = 0,
        em_mac_type_mdi,
@@ -829,6 +854,7 @@ struct wx {
        struct wx_bus_info bus;
        struct wx_mac_info mac;
        enum em_mac_type mac_type;
+       enum sp_media_type media_type;
        struct wx_eeprom_info eeprom;
        struct wx_addr_filter_info addr_ctrl;
        struct wx_mac_addr *mac_table;
index 90168aa..3727452 100644 (file)
@@ -285,17 +285,20 @@ static void txgbe_reset_misc(struct wx *wx)
 int txgbe_reset_hw(struct wx *wx)
 {
        int status;
-       u32 val;
 
        /* Call adapter stop to disable tx/rx and clear interrupts */
        status = wx_stop_adapter(wx);
        if (status != 0)
                return status;
 
-       val = WX_MIS_RST_LAN_RST(wx->bus.func);
-       wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
-       WX_WRITE_FLUSH(wx);
-       usleep_range(10, 100);
+       if (wx->media_type != sp_media_copper) {
+               u32 val;
+
+               val = WX_MIS_RST_LAN_RST(wx->bus.func);
+               wr32(wx, WX_MIS_RST, val | rd32(wx, WX_MIS_RST));
+               WX_WRITE_FLUSH(wx);
+               usleep_range(10, 100);
+       }
 
        status = wx_check_flash_load(wx, TXGBE_SPI_ILDR_STATUS_LAN_SW_RST(wx->bus.func));
        if (status != 0)
index 641b818..5c3aed5 100644 (file)
@@ -301,6 +301,49 @@ static void txgbe_down(struct wx *wx)
 }
 
 /**
+ *  txgbe_init_type_code - Initialize the shared code
+ *  @wx: pointer to hardware structure
+ **/
+static void txgbe_init_type_code(struct wx *wx)
+{
+       u8 device_type = wx->subsystem_device_id & 0xF0;
+
+       switch (wx->device_id) {
+       case TXGBE_DEV_ID_SP1000:
+       case TXGBE_DEV_ID_WX1820:
+               wx->mac.type = wx_mac_sp;
+               break;
+       default:
+               wx->mac.type = wx_mac_unknown;
+               break;
+       }
+
+       switch (device_type) {
+       case TXGBE_ID_SFP:
+               wx->media_type = sp_media_fiber;
+               break;
+       case TXGBE_ID_XAUI:
+       case TXGBE_ID_SGMII:
+               wx->media_type = sp_media_copper;
+               break;
+       case TXGBE_ID_KR_KX_KX4:
+       case TXGBE_ID_MAC_XAUI:
+       case TXGBE_ID_MAC_SGMII:
+               wx->media_type = sp_media_backplane;
+               break;
+       case TXGBE_ID_SFI_XAUI:
+               if (wx->bus.func == 0)
+                       wx->media_type = sp_media_fiber;
+               else
+                       wx->media_type = sp_media_copper;
+               break;
+       default:
+               wx->media_type = sp_media_unknown;
+               break;
+       }
+}
+
+/**
  * txgbe_sw_init - Initialize general software structures (struct wx)
  * @wx: board private structure to initialize
  **/
@@ -324,15 +367,7 @@ static int txgbe_sw_init(struct wx *wx)
                return err;
        }
 
-       switch (wx->device_id) {
-       case TXGBE_DEV_ID_SP1000:
-       case TXGBE_DEV_ID_WX1820:
-               wx->mac.type = wx_mac_sp;
-               break;
-       default:
-               wx->mac.type = wx_mac_unknown;
-               break;
-       }
+       txgbe_init_type_code(wx);
 
        /* Set common capability flags and settings */
        wx->max_q_vectors = TXGBE_MAX_MSIX_VECTORS;
index d212454..4159c84 100644 (file)
@@ -161,7 +161,10 @@ static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *confi
 {
        struct txgbe *txgbe = netdev_to_txgbe(to_net_dev(config->dev));
 
-       return &txgbe->xpcs->pcs;
+       if (interface == PHY_INTERFACE_MODE_10GBASER)
+               return &txgbe->xpcs->pcs;
+
+       return NULL;
 }
 
 static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
@@ -244,8 +247,8 @@ static const struct phylink_mac_ops txgbe_mac_ops = {
 
 static int txgbe_phylink_init(struct txgbe *txgbe)
 {
+       struct fwnode_handle *fwnode = NULL;
        struct phylink_config *config;
-       struct fwnode_handle *fwnode;
        struct wx *wx = txgbe->wx;
        phy_interface_t phy_mode;
        struct phylink *phylink;
@@ -256,16 +259,34 @@ static int txgbe_phylink_init(struct txgbe *txgbe)
 
        config->dev = &wx->netdev->dev;
        config->type = PHYLINK_NETDEV;
-       config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
-       phy_mode = PHY_INTERFACE_MODE_10GBASER;
-       __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
-       __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
-       __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
-       fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
+       config->mac_capabilities = MAC_10000FD | MAC_1000FD | MAC_100FD |
+                                  MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
+
+       if (wx->media_type == sp_media_copper) {
+               phy_mode = PHY_INTERFACE_MODE_XAUI;
+               __set_bit(PHY_INTERFACE_MODE_XAUI, config->supported_interfaces);
+       } else {
+               phy_mode = PHY_INTERFACE_MODE_10GBASER;
+               fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
+               __set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
+       }
+
        phylink = phylink_create(config, fwnode, phy_mode, &txgbe_mac_ops);
        if (IS_ERR(phylink))
                return PTR_ERR(phylink);
 
+       if (wx->phydev) {
+               int ret;
+
+               ret = phylink_connect_phy(phylink, wx->phydev);
+               if (ret) {
+                       phylink_destroy(phylink);
+                       return ret;
+               }
+       }
+
        txgbe->phylink = phylink;
 
        return 0;
@@ -626,10 +647,117 @@ static int txgbe_sfp_register(struct txgbe *txgbe)
        return 0;
 }
 
+static int txgbe_phy_read(struct mii_bus *bus, int phy_addr,
+                         int devnum, int regnum)
+{
+       struct wx *wx = bus->priv;
+       u32 val, command;
+       int ret;
+
+       /* setup and write the address cycle command */
+       command = WX_MSCA_RA(regnum) |
+                 WX_MSCA_PA(phy_addr) |
+                 WX_MSCA_DA(devnum);
+       wr32(wx, WX_MSCA, command);
+
+       command = WX_MSCC_CMD(WX_MSCA_CMD_READ) | WX_MSCC_BUSY;
+       wr32(wx, WX_MSCC, command);
+
+       /* wait to complete */
+       ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
+                               100000, false, wx, WX_MSCC);
+       if (ret) {
+               wx_err(wx, "Mdio read c45 command did not complete.\n");
+               return ret;
+       }
+
+       return (u16)rd32(wx, WX_MSCC);
+}
+
+static int txgbe_phy_write(struct mii_bus *bus, int phy_addr,
+                          int devnum, int regnum, u16 value)
+{
+       struct wx *wx = bus->priv;
+       int ret, command;
+       u16 val;
+
+       /* setup and write the address cycle command */
+       command = WX_MSCA_RA(regnum) |
+                 WX_MSCA_PA(phy_addr) |
+                 WX_MSCA_DA(devnum);
+       wr32(wx, WX_MSCA, command);
+
+       command = value | WX_MSCC_CMD(WX_MSCA_CMD_WRITE) | WX_MSCC_BUSY;
+       wr32(wx, WX_MSCC, command);
+
+       /* wait to complete */
+       ret = read_poll_timeout(rd32, val, !(val & WX_MSCC_BUSY), 1000,
+                               100000, false, wx, WX_MSCC);
+       if (ret)
+               wx_err(wx, "Mdio write c45 command did not complete.\n");
+
+       return ret;
+}
+
+static int txgbe_ext_phy_init(struct txgbe *txgbe)
+{
+       struct phy_device *phydev;
+       struct mii_bus *mii_bus;
+       struct pci_dev *pdev;
+       struct wx *wx;
+       int ret = 0;
+
+       wx = txgbe->wx;
+       pdev = wx->pdev;
+
+       mii_bus = devm_mdiobus_alloc(&pdev->dev);
+       if (!mii_bus)
+               return -ENOMEM;
+
+       mii_bus->name = "txgbe_mii_bus";
+       mii_bus->read_c45 = &txgbe_phy_read;
+       mii_bus->write_c45 = &txgbe_phy_write;
+       mii_bus->parent = &pdev->dev;
+       mii_bus->phy_mask = GENMASK(31, 1);
+       mii_bus->priv = wx;
+       snprintf(mii_bus->id, MII_BUS_ID_SIZE, "txgbe-%x",
+                (pdev->bus->number << 8) | pdev->devfn);
+
+       ret = devm_mdiobus_register(&pdev->dev, mii_bus);
+       if (ret) {
+               wx_err(wx, "failed to register MDIO bus: %d\n", ret);
+               return ret;
+       }
+
+       phydev = phy_find_first(mii_bus);
+       if (!phydev) {
+               wx_err(wx, "no PHY found\n");
+               return -ENODEV;
+       }
+
+       phy_attached_info(phydev);
+
+       wx->link = 0;
+       wx->speed = 0;
+       wx->duplex = 0;
+       wx->phydev = phydev;
+
+       ret = txgbe_phylink_init(txgbe);
+       if (ret) {
+               wx_err(wx, "failed to init phylink: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 int txgbe_init_phy(struct txgbe *txgbe)
 {
        int ret;
 
+       if (txgbe->wx->media_type == sp_media_copper)
+               return txgbe_ext_phy_init(txgbe);
+
        ret = txgbe_swnodes_register(txgbe);
        if (ret) {
                wx_err(txgbe->wx, "failed to register software nodes\n");
@@ -691,6 +819,12 @@ err_unregister_swnode:
 
 void txgbe_remove_phy(struct txgbe *txgbe)
 {
+       if (txgbe->wx->media_type == sp_media_copper) {
+               phylink_disconnect_phy(txgbe->phylink);
+               phylink_destroy(txgbe->phylink);
+               return;
+       }
+
        platform_device_unregister(txgbe->sfp_dev);
        platform_device_unregister(txgbe->i2c_dev);
        clkdev_drop(txgbe->clock);