All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/6] TXGBE PHYLINK support
@ 2023-04-03  6:45 Jiawen Wu
  2023-04-03  6:45 ` [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
                   ` (5 more replies)
  0 siblings, 6 replies; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

Implement I2C, SFP, GPIO and PHYLINK to setup TXGBE link and switch link
rate based on optical module information.

Jiawen Wu (6):
  net: txgbe: Add software nodes to support phylink
  net: txgbe: Implement I2C bus master driver
  net: txgbe: Add SFP module identify
  net: txgbe: Support GPIO to SFP socket
  net: txgbe: Implement phylink pcs
  net: txgbe: Support phylink MAC layer

 .../device_drivers/ethernet/wangxun/txgbe.rst |  47 +
 drivers/net/ethernet/wangxun/Kconfig          |   5 +
 drivers/net/ethernet/wangxun/libwx/wx_lib.c   |   3 +-
 drivers/net/ethernet/wangxun/libwx/wx_type.h  |   3 +
 drivers/net/ethernet/wangxun/txgbe/Makefile   |   1 +
 .../ethernet/wangxun/txgbe/txgbe_ethtool.c    |  34 +
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c |   1 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  61 +-
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 934 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_phy.h    |  10 +
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   | 156 +++
 11 files changed, 1223 insertions(+), 32 deletions(-)
 create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
 create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h

-- 
2.27.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink
  2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
@ 2023-04-03  6:45 ` Jiawen Wu
  2023-04-03 12:35   ` Andrew Lunn
  2023-04-03  6:45 ` [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

Register software nodes for GPIO, I2C, SFP and PHYLINK. Define the
device properties.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/ethernet/wangxun/libwx/wx_type.h  |  1 +
 drivers/net/ethernet/wangxun/txgbe/Makefile   |  1 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   | 19 ++++-
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 79 +++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_phy.h    | 10 +++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   | 42 ++++++++++
 6 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
 create mode 100644 drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h

diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 2bec5b1bc196..072aa2bd3fdc 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -611,6 +611,7 @@ enum wx_isb_idx {
 
 struct wx {
 	u8 __iomem *hw_addr;
+	void *priv;
 	struct pci_dev *pdev;
 	struct net_device *netdev;
 	struct wx_bus_info bus;
diff --git a/drivers/net/ethernet/wangxun/txgbe/Makefile b/drivers/net/ethernet/wangxun/txgbe/Makefile
index 6db14a2cb2d0..7507f762edfe 100644
--- a/drivers/net/ethernet/wangxun/txgbe/Makefile
+++ b/drivers/net/ethernet/wangxun/txgbe/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_TXGBE) += txgbe.o
 
 txgbe-objs := txgbe_main.o \
               txgbe_hw.o \
+              txgbe_phy.o \
               txgbe_ethtool.o
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 843a88bc416f..319d56720c06 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -15,6 +15,7 @@
 #include "../libwx/wx_hw.h"
 #include "txgbe_type.h"
 #include "txgbe_hw.h"
+#include "txgbe_phy.h"
 #include "txgbe_ethtool.h"
 
 char txgbe_driver_name[] = "txgbe";
@@ -512,6 +513,7 @@ static int txgbe_probe(struct pci_dev *pdev,
 	struct net_device *netdev;
 	int err, expected_gts;
 	struct wx *wx = NULL;
+	struct txgbe *txgbe;
 
 	u16 eeprom_verh = 0, eeprom_verl = 0, offset = 0;
 	u16 eeprom_cfg_blkh = 0, eeprom_cfg_blkl = 0;
@@ -662,10 +664,21 @@ static int txgbe_probe(struct pci_dev *pdev,
 			 "0x%08x", etrack_id);
 	}
 
-	err = register_netdev(netdev);
+	txgbe = devm_kzalloc(&pdev->dev, sizeof(*txgbe), GFP_KERNEL);
+	if (!txgbe)
+		return -ENOMEM;
+
+	txgbe->wx = wx;
+	wx->priv = txgbe;
+
+	err = txgbe_init_phy(txgbe);
 	if (err)
 		goto err_release_hw;
 
+	err = register_netdev(netdev);
+	if (err)
+		goto err_remove_phy;
+
 	pci_set_drvdata(pdev, wx);
 
 	netif_tx_stop_all_queues(netdev);
@@ -693,6 +706,8 @@ static int txgbe_probe(struct pci_dev *pdev,
 
 	return 0;
 
+err_remove_phy:
+	txgbe_remove_phy(txgbe);
 err_release_hw:
 	wx_clear_interrupt_scheme(wx);
 	wx_control_hw(wx, false);
@@ -723,6 +738,8 @@ static void txgbe_remove(struct pci_dev *pdev)
 	netdev = wx->netdev;
 	unregister_netdev(netdev);
 
+	txgbe_remove_phy((struct txgbe *)wx->priv);
+
 	pci_release_selected_regions(pdev,
 				     pci_select_bars(pdev, IORESOURCE_MEM));
 
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
new file mode 100644
index 000000000000..163acb7e515e
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
+
+#include <linux/gpio/property.h>
+#include <linux/pci.h>
+
+#include "../libwx/wx_type.h"
+#include "txgbe_type.h"
+#include "txgbe_phy.h"
+
+static int txgbe_swnodes_register(struct txgbe *txgbe)
+{
+	struct txgbe_nodes *nodes = &txgbe->nodes;
+	struct pci_dev *pdev = txgbe->wx->pdev;
+	struct software_node *swnodes;
+	u32 id;
+
+	id = (pdev->bus->number << 8) | pdev->devfn;
+
+	snprintf(nodes->gpio_name, sizeof(nodes->gpio_name), "txgbe_gpio-%x", id);
+	snprintf(nodes->i2c_name, sizeof(nodes->i2c_name), "txgbe_i2c-%x", id);
+	snprintf(nodes->sfp_name, sizeof(nodes->sfp_name), "txgbe_sfp-%x", id);
+	snprintf(nodes->phylink_name, sizeof(nodes->phylink_name), "txgbe_phylink-%x", id);
+
+	swnodes = nodes->swnodes;
+
+	nodes->gpio_props[0] = PROPERTY_ENTRY_STRING("pinctrl-names", "default");
+	swnodes[SWNODE_GPIO] = NODE_PROP(nodes->gpio_name, nodes->gpio_props);
+	nodes->gpio0_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 0, GPIO_ACTIVE_HIGH);
+	nodes->gpio1_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 1, GPIO_ACTIVE_HIGH);
+	nodes->gpio2_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 2, GPIO_ACTIVE_LOW);
+	nodes->gpio3_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 3, GPIO_ACTIVE_HIGH);
+	nodes->gpio4_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 4, GPIO_ACTIVE_HIGH);
+	nodes->gpio5_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 5, GPIO_ACTIVE_HIGH);
+
+	nodes->i2c_props[0] = PROPERTY_ENTRY_STRING("pinctrl-names", "default");
+	swnodes[SWNODE_I2C] = NODE_PROP(nodes->i2c_name, nodes->i2c_props);
+	nodes->i2c_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_I2C]);
+
+	nodes->sfp_props[0] = PROPERTY_ENTRY_STRING("compatible", "sff,sfp");
+	nodes->sfp_props[1] = PROPERTY_ENTRY_REF_ARRAY("i2c-bus", nodes->i2c_ref);
+	nodes->sfp_props[2] = PROPERTY_ENTRY_REF_ARRAY("tx-fault-gpios", nodes->gpio0_ref);
+	nodes->sfp_props[3] = PROPERTY_ENTRY_REF_ARRAY("tx-disable-gpios", nodes->gpio1_ref);
+	nodes->sfp_props[4] = PROPERTY_ENTRY_REF_ARRAY("mod-def0-gpios", nodes->gpio2_ref);
+	nodes->sfp_props[5] = PROPERTY_ENTRY_REF_ARRAY("los-gpios", nodes->gpio3_ref);
+	nodes->sfp_props[6] = PROPERTY_ENTRY_REF_ARRAY("rate-select1-gpios", nodes->gpio4_ref);
+	nodes->sfp_props[7] = PROPERTY_ENTRY_REF_ARRAY("rate-select0-gpios", nodes->gpio5_ref);
+	swnodes[SWNODE_SFP] = NODE_PROP(nodes->sfp_name, nodes->sfp_props);
+	nodes->sfp_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_SFP]);
+
+	nodes->phylink_props[0] = PROPERTY_ENTRY_STRING("managed", "in-band-status");
+	nodes->phylink_props[1] = PROPERTY_ENTRY_REF_ARRAY("sfp", nodes->sfp_ref);
+	swnodes[SWNODE_PHYLINK] = NODE_PROP(nodes->phylink_name, nodes->phylink_props);
+
+	nodes->group[SWNODE_GPIO] = &swnodes[SWNODE_GPIO];
+	nodes->group[SWNODE_I2C] = &swnodes[SWNODE_I2C];
+	nodes->group[SWNODE_SFP] = &swnodes[SWNODE_SFP];
+	nodes->group[SWNODE_PHYLINK] = &swnodes[SWNODE_PHYLINK];
+
+	return software_node_register_node_group(nodes->group);
+}
+
+int txgbe_init_phy(struct txgbe *txgbe)
+{
+	int ret;
+
+	ret = txgbe_swnodes_register(txgbe);
+	if (ret) {
+		wx_err(txgbe->wx, "failed to register software nodes\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+void txgbe_remove_phy(struct txgbe *txgbe)
+{
+	software_node_unregister_node_group(txgbe->nodes.group);
+}
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h
new file mode 100644
index 000000000000..1ab592124986
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
+
+#ifndef _TXGBE_PHY_H_
+#define _TXGBE_PHY_H_
+
+int txgbe_init_phy(struct txgbe *txgbe);
+void txgbe_remove_phy(struct txgbe *txgbe);
+
+#endif /* _TXGBE_NODE_H_ */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 63a1c733718d..d30684378f4e 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -4,6 +4,8 @@
 #ifndef _TXGBE_TYPE_H_
 #define _TXGBE_TYPE_H_
 
+#include <linux/property.h>
+
 /* Device IDs */
 #define TXGBE_DEV_ID_SP1000                     0x1001
 #define TXGBE_DEV_ID_WX1820                     0x2001
@@ -99,4 +101,44 @@
 
 extern char txgbe_driver_name[];
 
+#define NODE_PROP(_NAME, _PROP)			\
+	(const struct software_node) {		\
+		.name = _NAME,			\
+		.properties = _PROP,		\
+	}
+
+enum txgbe_swnodes {
+	SWNODE_GPIO = 0,
+	SWNODE_I2C,
+	SWNODE_SFP,
+	SWNODE_PHYLINK,
+	SWNODE_MAX
+};
+
+struct txgbe_nodes {
+	char gpio_name[32];
+	char i2c_name[32];
+	char sfp_name[32];
+	char phylink_name[32];
+	struct property_entry gpio_props[1];
+	struct property_entry i2c_props[1];
+	struct property_entry sfp_props[8];
+	struct property_entry phylink_props[2];
+	struct software_node_ref_args i2c_ref[1];
+	struct software_node_ref_args gpio0_ref[1];
+	struct software_node_ref_args gpio1_ref[1];
+	struct software_node_ref_args gpio2_ref[1];
+	struct software_node_ref_args gpio3_ref[1];
+	struct software_node_ref_args gpio4_ref[1];
+	struct software_node_ref_args gpio5_ref[1];
+	struct software_node_ref_args sfp_ref[1];
+	struct software_node swnodes[SWNODE_MAX];
+	const struct software_node *group[SWNODE_MAX + 1];
+};
+
+struct txgbe {
+	struct wx *wx;
+	struct txgbe_nodes nodes;
+};
+
 #endif /* _TXGBE_TYPE_H_ */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
  2023-04-03  6:45 ` [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
@ 2023-04-03  6:45 ` Jiawen Wu
  2023-04-03 12:52   ` Andrew Lunn
  2023-04-03  6:45 ` [PATCH net-next 3/6] net: txgbe: Add SFP module identify Jiawen Wu
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

I2C bus is integrated in Wangxun 10Gb ethernet chip. Implement I2C bus
driver to receive I2C messages.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/ethernet/wangxun/Kconfig          |   1 +
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 141 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  26 ++++
 3 files changed, 168 insertions(+)

diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig
index c9d88673d306..8cbf0dd48a2c 100644
--- a/drivers/net/ethernet/wangxun/Kconfig
+++ b/drivers/net/ethernet/wangxun/Kconfig
@@ -41,6 +41,7 @@ config TXGBE
 	tristate "Wangxun(R) 10GbE PCI Express adapters support"
 	depends on PCI
 	select LIBWX
+	select I2C
 	help
 	  This driver supports Wangxun(R) 10GbE PCI Express family of
 	  adapters.
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 163acb7e515e..f8a4b211f4e8 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -2,9 +2,12 @@
 /* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
 
 #include <linux/gpio/property.h>
+#include <linux/iopoll.h>
+#include <linux/i2c.h>
 #include <linux/pci.h>
 
 #include "../libwx/wx_type.h"
+#include "../libwx/wx_hw.h"
 #include "txgbe_type.h"
 #include "txgbe_phy.h"
 
@@ -60,6 +63,130 @@ static int txgbe_swnodes_register(struct txgbe *txgbe)
 	return software_node_register_node_group(nodes->group);
 }
 
+static void txgbe_i2c_start(struct wx *wx, u16 dev_addr)
+{
+	wr32(wx, TXGBE_I2C_ENABLE, 0);
+
+	wr32(wx, TXGBE_I2C_CON,
+	     (TXGBE_I2C_CON_MASTER_MODE |
+	      TXGBE_I2C_CON_SPEED(1) |
+	      TXGBE_I2C_CON_RESTART_EN |
+	      TXGBE_I2C_CON_SLAVE_DISABLE));
+	/* Default addr is 0xA0 ,bit 0 is configure for read/write! */
+	wr32(wx, TXGBE_I2C_TAR, dev_addr);
+	wr32(wx, TXGBE_I2C_SS_SCL_HCNT, 600);
+	wr32(wx, TXGBE_I2C_SS_SCL_LCNT, 600);
+	wr32(wx, TXGBE_I2C_RX_TL, 0); /* 1byte for rx full signal */
+	wr32(wx, TXGBE_I2C_TX_TL, 4);
+	wr32(wx, TXGBE_I2C_SCL_STUCK_TIMEOUT, 0xFFFFFF);
+	wr32(wx, TXGBE_I2C_SDA_STUCK_TIMEOUT, 0xFFFFFF);
+
+	wr32(wx, TXGBE_I2C_INTR_MASK, 0);
+	wr32(wx, TXGBE_I2C_ENABLE, 1);
+}
+
+static int txgbe_read_i2c_bytes(struct wx *wx, u8 addr, u16 len, u8 *buf)
+{
+	int err, i;
+	u16 val;
+
+	for (i = 0; i < len; i++) {
+		/* wait tx empty */
+		err = read_poll_timeout(rd32, val,
+					(val & TXGBE_I2C_INTR_STAT_TEMP) ==
+					TXGBE_I2C_INTR_STAT_TEMP,
+					100, 1000, false, wx,
+					TXGBE_I2C_RAW_INTR_STAT);
+		if (err != 0)
+			return err;
+
+		/* read data */
+		wr32(wx, TXGBE_I2C_DATA_CMD, (addr + i) | TXGBE_I2C_DATA_CMD_STOP);
+		wr32(wx, TXGBE_I2C_DATA_CMD, TXGBE_I2C_DATA_CMD_READ);
+
+		/* wait for read complete */
+		err = read_poll_timeout(rd32, val,
+					(val & TXGBE_I2C_INTR_STAT_RFUL) ==
+					TXGBE_I2C_INTR_STAT_RFUL,
+					100, 1000, false, wx,
+					TXGBE_I2C_RAW_INTR_STAT);
+		if (err != 0)
+			return err;
+
+		buf[i] = 0xFF & rd32(wx, TXGBE_I2C_DATA_CMD);
+	}
+
+	return 0;
+}
+
+static int txgbe_i2c_xfer(struct i2c_adapter *i2c_adap,
+			  struct i2c_msg *msg, int num_msgs)
+{
+	struct wx *wx = i2c_get_adapdata(i2c_adap);
+	u8 *dev_addr = msg[0].buf;
+	bool read = false;
+	int i, ret;
+	u8 *buf;
+	u16 len;
+
+	txgbe_i2c_start(wx, msg[0].addr);
+
+	for (i = 0; i < num_msgs; i++) {
+		if (msg[i].flags & I2C_M_RD) {
+			read = true;
+			len = msg[i].len;
+			buf = msg[i].buf;
+		}
+	}
+
+	if (!read) {
+		wx_err(wx, "I2C write not supported\n");
+		return num_msgs;
+	}
+
+	ret = txgbe_read_i2c_bytes(wx, *dev_addr, len, buf);
+	if (!ret)
+		ret = num_msgs;
+
+	return ret;
+}
+
+static u32 txgbe_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm txgbe_i2c_algo = {
+	.master_xfer = txgbe_i2c_xfer,
+	.functionality = txgbe_i2c_func,
+};
+
+static int txgbe_i2c_adapter_add(struct txgbe *txgbe)
+{
+	struct pci_dev *pdev = txgbe->wx->pdev;
+	struct i2c_adapter *i2c_adap;
+	int ret;
+
+	i2c_adap = devm_kzalloc(&pdev->dev, sizeof(*i2c_adap), GFP_KERNEL);
+	if (!i2c_adap)
+		return -ENOMEM;
+
+	i2c_adap->owner = THIS_MODULE;
+	i2c_adap->algo = &txgbe_i2c_algo;
+	i2c_adap->dev.parent = &pdev->dev;
+	i2c_adap->dev.fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_I2C]);
+	strscpy(i2c_adap->name, "txgbe_i2c", sizeof(i2c_adap->name));
+
+	i2c_set_adapdata(i2c_adap, txgbe->wx);
+	ret = i2c_add_adapter(i2c_adap);
+	if (ret)
+		return ret;
+
+	txgbe->i2c_adap = i2c_adap;
+
+	return 0;
+}
+
 int txgbe_init_phy(struct txgbe *txgbe)
 {
 	int ret;
@@ -70,10 +197,24 @@ int txgbe_init_phy(struct txgbe *txgbe)
 		return ret;
 	}
 
+	ret = txgbe_i2c_adapter_add(txgbe);
+	if (ret) {
+		wx_err(txgbe->wx, "failed to init i2c interface: %d\n", ret);
+		goto err;
+	}
+
 	return 0;
+
+err:
+	txgbe_remove_phy(txgbe);
+
+	return ret;
 }
 
 void txgbe_remove_phy(struct txgbe *txgbe)
 {
+	if (txgbe->i2c_adap)
+		i2c_del_adapter(txgbe->i2c_adap);
+
 	software_node_unregister_node_group(txgbe->nodes.group);
 }
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index d30684378f4e..de488609f713 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -55,6 +55,31 @@
 #define TXGBE_TS_CTL                            0x10300
 #define TXGBE_TS_CTL_EVAL_MD                    BIT(31)
 
+/* I2C registers */
+#define TXGBE_I2C_CON                           0x14900 /* I2C Control */
+#define TXGBE_I2C_CON_SLAVE_DISABLE             BIT(6)
+#define TXGBE_I2C_CON_RESTART_EN                BIT(5)
+#define TXGBE_I2C_CON_SPEED(_v)                 FIELD_PREP(GENMASK(2, 1), _v)
+#define TXGBE_I2C_CON_MASTER_MODE               BIT(0)
+#define TXGBE_I2C_TAR                           0x14904 /* I2C Target Address */
+#define TXGBE_I2C_DATA_CMD                      0x14910 /* I2C Rx/Tx Data Buf and Cmd */
+#define TXGBE_I2C_DATA_CMD_STOP                 BIT(9)
+#define TXGBE_I2C_DATA_CMD_READ                 (BIT(8) | TXGBE_I2C_DATA_CMD_STOP)
+#define TXGBE_I2C_SS_SCL_HCNT                   0x14914
+#define TXGBE_I2C_SS_SCL_LCNT                   0x14918
+#define TXGBE_I2C_INTR_MASK                     0x14930 /* I2C Interrupt Mask */
+#define TXGBE_I2C_RAW_INTR_STAT                 0x14934 /* I2C Raw Interrupt Status */
+#define TXGBE_I2C_INTR_STAT_RFUL                BIT(2)
+#define TXGBE_I2C_INTR_STAT_TEMP                BIT(4)
+#define TXGBE_I2C_RX_TL                         0x14938 /* I2C Receive FIFO Threshold */
+#define TXGBE_I2C_TX_TL                         0x1493C /* I2C TX FIFO Threshold */
+#define TXGBE_I2C_ENABLE                        0x1496C /* I2C Enable */
+#define TXGBE_I2C_SCL_STUCK_TIMEOUT             0x149AC
+#define TXGBE_I2C_SDA_STUCK_TIMEOUT             0x149B0
+
+#define TXGBE_I2C_SLAVE_ADDR                    (0xA0 >> 1)
+#define TXGBE_I2C_EEPROM_DEV_ADDR               0xA0
+
 /* Part Number String Length */
 #define TXGBE_PBANUM_LENGTH                     32
 
@@ -139,6 +164,7 @@ struct txgbe_nodes {
 struct txgbe {
 	struct wx *wx;
 	struct txgbe_nodes nodes;
+	struct i2c_adapter *i2c_adap;
 };
 
 #endif /* _TXGBE_TYPE_H_ */
-- 
2.27.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next 3/6] net: txgbe: Add SFP module identify
  2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
  2023-04-03  6:45 ` [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
  2023-04-03  6:45 ` [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
@ 2023-04-03  6:45 ` Jiawen Wu
  2023-04-03 12:58   ` Andrew Lunn
  2023-04-03  6:45 ` [PATCH net-next 4/6] net: txgbe: Support GPIO to SFP socket Jiawen Wu
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

Register SFP platform device to get modules information.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 .../device_drivers/ethernet/wangxun/txgbe.rst | 47 +++++++++++++++++++
 drivers/net/ethernet/wangxun/Kconfig          |  1 +
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 29 ++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  1 +
 4 files changed, 78 insertions(+)

diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
index d052ef40fe36..9eb05a2ef110 100644
--- a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
+++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
@@ -11,9 +11,56 @@ Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd.
 Contents
 ========
 
+- Identifying Adapter
 - Support
 
 
+Identifying Adapter
+===================
+The driver is compatible with WangXun Sapphire Dual ports Ethernet Adapters.
+
+SFP+ Devices with Pluggable Optics
+----------------------------------
+The following is a list of 3rd party SFP+ modules that have been tested and verified.
+
++----------+----------------------+----------------------+
+| Supplier | Type                 | Part Numbers         |
++==========+======================+======================+
+| ACCELINK | SFP+                 | RTXM228-551          |
++----------+----------------------+----------------------+
+| Avago	   | SFP+                 | SFBR-7701SDZ         |
++----------+----------------------+----------------------+
+| BOYANG   | SFP+                 | OMXD30000            |
++----------+----------------------+----------------------+
+| F-tone   | SFP+                 | FTCS-851X-02D        |
++----------+----------------------+----------------------+
+| FS       | SFP+                 | SFP-10GSR-85         |
++----------+----------------------+----------------------+
+| Finisar  | SFP+                 | FTLX8574D3BCL        |
++----------+----------------------+----------------------+
+| Hisense  | SFP+                 | LTF8502-BC+          |
++----------+----------------------+----------------------+
+| HGTECH   | SFP+                 | MTRS-01X11-G         |
++----------+----------------------+----------------------+
+| HP       | SFP+                 | SR SFP+ 456096-001   |
++----------+----------------------+----------------------+
+| Huawei   | SFP+                 | AFBR-709SMZ          |
++----------+----------------------+----------------------+
+| Intel    | SFP+                 | FTLX8571D3BCV-IT     |
++----------+----------------------+----------------------+
+| JDSU     | SFP+                 | PLRXPL-SC-S43        |
++----------+----------------------+----------------------+
+| SONT     | SFP+                 | XP-8G10-01           |
++----------+----------------------+----------------------+
+| Trixon   | SFP+                 | TPS-TGM3-85DCR       |
++----------+----------------------+----------------------+
+
+Laser turns off for SFP+ when ifconfig ethX down
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"ifconfig ethX down" turns off the laser for SFP+ fiber adapters.
+"ifconfig ethX up" turns on the laser.
+
+
 Support
 =======
 If you got any problem, contact Wangxun support team via nic-support@net-swift.com
diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig
index 8cbf0dd48a2c..c5b62918db78 100644
--- a/drivers/net/ethernet/wangxun/Kconfig
+++ b/drivers/net/ethernet/wangxun/Kconfig
@@ -42,6 +42,7 @@ config TXGBE
 	depends on PCI
 	select LIBWX
 	select I2C
+	select SFP
 	help
 	  This driver supports Wangxun(R) 10GbE PCI Express family of
 	  adapters.
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index f8a4b211f4e8..dac9dfd001f0 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
 
+#include <linux/platform_device.h>
 #include <linux/gpio/property.h>
 #include <linux/iopoll.h>
 #include <linux/i2c.h>
@@ -187,6 +188,26 @@ static int txgbe_i2c_adapter_add(struct txgbe *txgbe)
 	return 0;
 }
 
+static int txgbe_sfp_register(struct txgbe *txgbe)
+{
+	struct pci_dev *pdev = txgbe->wx->pdev;
+	struct platform_device_info info;
+	struct platform_device *sfp_dev;
+
+	memset(&info, 0, sizeof(info));
+	info.parent = &pdev->dev;
+	info.fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_SFP]);
+	info.name = "sfp";
+	info.id = (pdev->bus->number << 8) | pdev->devfn;
+	sfp_dev = platform_device_register_full(&info);
+	if (IS_ERR(sfp_dev))
+		return PTR_ERR(sfp_dev);
+
+	txgbe->sfp_dev = sfp_dev;
+
+	return 0;
+}
+
 int txgbe_init_phy(struct txgbe *txgbe)
 {
 	int ret;
@@ -203,6 +224,12 @@ int txgbe_init_phy(struct txgbe *txgbe)
 		goto err;
 	}
 
+	ret = txgbe_sfp_register(txgbe);
+	if (ret) {
+		wx_err(txgbe->wx, "failed to register sfp\n");
+		goto err;
+	}
+
 	return 0;
 
 err:
@@ -213,6 +240,8 @@ int txgbe_init_phy(struct txgbe *txgbe)
 
 void txgbe_remove_phy(struct txgbe *txgbe)
 {
+	if (txgbe->sfp_dev)
+		platform_device_unregister(txgbe->sfp_dev);
 	if (txgbe->i2c_adap)
 		i2c_del_adapter(txgbe->i2c_adap);
 
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index de488609f713..75a4e7b8cc51 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -165,6 +165,7 @@ struct txgbe {
 	struct wx *wx;
 	struct txgbe_nodes nodes;
 	struct i2c_adapter *i2c_adap;
+	struct platform_device *sfp_dev;
 };
 
 #endif /* _TXGBE_TYPE_H_ */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next 4/6] net: txgbe: Support GPIO to SFP socket
  2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
                   ` (2 preceding siblings ...)
  2023-04-03  6:45 ` [PATCH net-next 3/6] net: txgbe: Add SFP module identify Jiawen Wu
@ 2023-04-03  6:45 ` Jiawen Wu
  2023-04-03 13:16   ` Andrew Lunn
  2023-04-03  6:45 ` [PATCH net-next 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
  2023-04-03  6:45 ` [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
  5 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

Register GPIO chip and handle GPIO IRQ for SFP socket.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/ethernet/wangxun/Kconfig          |   2 +
 drivers/net/ethernet/wangxun/libwx/wx_lib.c   |   3 +-
 drivers/net/ethernet/wangxun/libwx/wx_type.h  |   2 +
 drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c |   1 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  22 +-
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 216 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  26 +++
 7 files changed, 253 insertions(+), 19 deletions(-)

diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig
index c5b62918db78..d9cccdad8a53 100644
--- a/drivers/net/ethernet/wangxun/Kconfig
+++ b/drivers/net/ethernet/wangxun/Kconfig
@@ -40,6 +40,8 @@ config NGBE
 config TXGBE
 	tristate "Wangxun(R) 10GbE PCI Express adapters support"
 	depends on PCI
+	select GPIOLIB_IRQCHIP
+	select GPIOLIB
 	select LIBWX
 	select I2C
 	select SFP
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index eb89a274083e..dff0d573ee33 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -1348,7 +1348,8 @@ void wx_free_irq(struct wx *wx)
 		free_irq(entry->vector, q_vector);
 	}
 
-	free_irq(wx->msix_entries[vector].vector, wx);
+	if (wx->mac.type == wx_mac_em)
+		free_irq(wx->msix_entries[vector].vector, wx);
 }
 EXPORT_SYMBOL(wx_free_irq);
 
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 072aa2bd3fdc..89bba827edf2 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -79,7 +79,9 @@
 #define WX_GPIO_INTMASK              0x14834
 #define WX_GPIO_INTTYPE_LEVEL        0x14838
 #define WX_GPIO_POLARITY             0x1483C
+#define WX_GPIO_INTSTATUS            0x14844
 #define WX_GPIO_EOI                  0x1484C
+#define WX_GPIO_EXT                  0x14850
 
 /*********************** Transmit DMA registers **************************/
 /* transmit global control */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
index ebc46f3be056..b87034e57140 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
@@ -256,6 +256,7 @@ int txgbe_validate_eeprom_checksum(struct wx *wx, u16 *checksum_val)
 static void txgbe_reset_misc(struct wx *wx)
 {
 	wx_reset_misc(wx);
+	wr32(wx, WX_GPIO_DDR, 0x32);
 	txgbe_init_thermal_sensor_thresh(wx);
 }
 
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 319d56720c06..caaefc20afb9 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -82,6 +82,10 @@ static int txgbe_enumerate_functions(struct wx *wx)
  **/
 static void txgbe_irq_enable(struct wx *wx, bool queues)
 {
+	wr32(wx, WX_GPIO_INTEN, TXGBE_GPIOBIT_0 | TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
+	wr32(wx, WX_GPIO_INTTYPE_LEVEL, TXGBE_GPIOBIT_0 | TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
+	wr32(wx, WX_PX_MISC_IEN, TXGBE_PX_MISC_IEN_MASK);
+
 	/* unmask interrupt */
 	wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
 	if (queues)
@@ -129,17 +133,6 @@ static irqreturn_t txgbe_intr(int __always_unused irq, void *data)
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t txgbe_msix_other(int __always_unused irq, void *data)
-{
-	struct wx *wx = data;
-
-	/* re-enable the original interrupt state */
-	if (netif_running(wx->netdev))
-		txgbe_irq_enable(wx, false);
-
-	return IRQ_HANDLED;
-}
-
 /**
  * txgbe_request_msix_irqs - Initialize MSI-X interrupts
  * @wx: board private structure
@@ -171,13 +164,6 @@ static int txgbe_request_msix_irqs(struct wx *wx)
 		}
 	}
 
-	err = request_irq(wx->msix_entries[vector].vector,
-			  txgbe_msix_other, 0, netdev->name, wx);
-	if (err) {
-		wx_err(wx, "request_irq for msix_other failed: %d\n", err);
-		goto free_queue_irqs;
-	}
-
 	return 0;
 
 free_queue_irqs:
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index dac9dfd001f0..fe51485a9b68 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -2,6 +2,9 @@
 /* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
 
 #include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/gpio/driver.h>
 #include <linux/gpio/property.h>
 #include <linux/iopoll.h>
 #include <linux/i2c.h>
@@ -188,6 +191,213 @@ static int txgbe_i2c_adapter_add(struct txgbe *txgbe)
 	return 0;
 }
 
+static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct wx *wx = gpiochip_get_data(chip);
+	int val, dir;
+
+	dir = chip->get_direction(chip, offset);
+	if (dir == GPIO_LINE_DIRECTION_IN)
+		val = rd32m(wx, WX_GPIO_EXT, BIT(offset));
+	else
+		val = rd32m(wx, WX_GPIO_DR, BIT(offset));
+
+	return !!(val & BIT(offset));
+}
+
+static int txgbe_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+	struct wx *wx = gpiochip_get_data(chip);
+	u32 val;
+
+	val = rd32(wx, WX_GPIO_DDR);
+	if (BIT(offset) & val)
+		return GPIO_LINE_DIRECTION_OUT;
+
+	return GPIO_LINE_DIRECTION_IN;
+}
+
+static int txgbe_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
+{
+	return 0;
+}
+
+static int txgbe_gpio_direction_out(struct gpio_chip *chip, unsigned int offset,
+				    int val)
+{
+	struct wx *wx = gpiochip_get_data(chip);
+	u32 mask;
+	int dir;
+
+	dir = chip->get_direction(chip, offset);
+	if (dir == GPIO_LINE_DIRECTION_IN)
+		return 0;
+
+	mask = BIT(offset) | BIT(offset - 1);
+	if (val)
+		wr32m(wx, WX_GPIO_DR, mask, mask);
+	else
+		wr32m(wx, WX_GPIO_DR, mask, 0);
+
+	return 0;
+}
+
+static void txgbe_gpio_irq_ack(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+	struct wx *wx = gpiochip_get_data(gc);
+
+	wr32(wx, WX_GPIO_EOI, BIT(hwirq));
+}
+
+static void txgbe_gpio_irq_mask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+	struct wx *wx = gpiochip_get_data(gc);
+
+	gpiochip_disable_irq(gc, hwirq);
+
+	wr32m(wx, WX_GPIO_INTMASK, BIT(hwirq), BIT(hwirq));
+}
+
+static void txgbe_gpio_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+	struct wx *wx = gpiochip_get_data(gc);
+
+	gpiochip_enable_irq(gc, hwirq);
+
+	wr32m(wx, WX_GPIO_INTMASK, BIT(hwirq), 0);
+}
+
+static int txgbe_gpio_set_type(struct irq_data *d, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+	struct wx *wx = gpiochip_get_data(gc);
+	u32 level, polarity;
+
+	level = rd32(wx, WX_GPIO_INTTYPE_LEVEL);
+	polarity = rd32(wx, WX_GPIO_POLARITY);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_BOTH:
+		level |= BIT(hwirq);
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		level |= BIT(hwirq);
+		polarity |= BIT(hwirq);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		level |= BIT(hwirq);
+		polarity &= ~BIT(hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		level &= ~BIT(hwirq);
+		polarity |= BIT(hwirq);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		level &= ~BIT(hwirq);
+		polarity &= ~BIT(hwirq);
+		break;
+	}
+
+	if (type & IRQ_TYPE_LEVEL_MASK)
+		irq_set_handler_locked(d, handle_level_irq);
+	else if (type & IRQ_TYPE_EDGE_BOTH)
+		irq_set_handler_locked(d, handle_edge_irq);
+
+	wr32(wx, WX_GPIO_INTTYPE_LEVEL, level);
+	if (type != IRQ_TYPE_EDGE_BOTH)
+		wr32(wx, WX_GPIO_POLARITY, polarity);
+
+	return 0;
+}
+
+static const struct irq_chip txgbe_gpio_irq_chip = {
+	.name = "txgbe_gpio_irq",
+	.irq_ack = txgbe_gpio_irq_ack,
+	.irq_mask = txgbe_gpio_irq_mask,
+	.irq_unmask = txgbe_gpio_irq_unmask,
+	.irq_set_type = txgbe_gpio_set_type,
+	.flags = IRQCHIP_IMMUTABLE,
+	GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static void txgbe_irq_handler(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct wx *wx = irq_desc_get_handler_data(desc);
+	struct txgbe *txgbe = (struct txgbe *)wx->priv;
+	struct gpio_chip *gc = txgbe->gpio;
+	irq_hw_number_t hwirq;
+	unsigned long val;
+
+	chained_irq_enter(chip, desc);
+
+	val = rd32(wx, WX_GPIO_INTSTATUS);
+	for_each_set_bit(hwirq, &val, gc->ngpio)
+		generic_handle_domain_irq(gc->irq.domain, hwirq);
+
+	chained_irq_exit(chip, desc);
+
+	/* unmask interrupt */
+	if (netif_running(wx->netdev))
+		wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
+}
+
+static int txgbe_gpio_init(struct txgbe *txgbe)
+{
+	struct gpio_irq_chip *girq;
+	struct wx *wx = txgbe->wx;
+	struct pci_dev *pdev;
+	struct gpio_chip *gc;
+	int ret;
+
+	pdev = wx->pdev;
+
+	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
+	if (!gc)
+		return -ENOMEM;
+
+	gc->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "txgbe_gpio-%x",
+				   (pdev->bus->number << 8) | pdev->devfn);
+	gc->base = -1;
+	gc->ngpio = 6;
+	gc->owner = THIS_MODULE;
+	gc->parent = &pdev->dev;
+	gc->fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_GPIO]);
+	gc->get = txgbe_gpio_get;
+	gc->get_direction = txgbe_gpio_get_direction;
+	gc->direction_input = txgbe_gpio_direction_in;
+	gc->direction_output = txgbe_gpio_direction_out;
+	gc->can_sleep = false;
+
+	girq = &gc->irq;
+	gpio_irq_chip_set_chip(girq, &txgbe_gpio_irq_chip);
+	girq->parent_handler = txgbe_irq_handler;
+	girq->parent_handler_data = wx;
+	girq->num_parents = 1;
+	girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents),
+				     GFP_KERNEL);
+	if (!girq->parents)
+		return -ENOMEM;
+	girq->parents[0] = wx->msix_entries[wx->num_q_vectors].vector;
+	girq->default_type = IRQ_TYPE_NONE;
+	girq->handler = handle_bad_irq;
+
+	ret = devm_gpiochip_add_data(&pdev->dev, gc, wx);
+	if (ret)
+		return ret;
+
+	txgbe->gpio = gc;
+
+	return 0;
+}
+
 static int txgbe_sfp_register(struct txgbe *txgbe)
 {
 	struct pci_dev *pdev = txgbe->wx->pdev;
@@ -224,6 +434,12 @@ int txgbe_init_phy(struct txgbe *txgbe)
 		goto err;
 	}
 
+	ret = txgbe_gpio_init(txgbe);
+	if (ret) {
+		wx_err(txgbe->wx, "failed to init gpio\n");
+		goto err;
+	}
+
 	ret = txgbe_sfp_register(txgbe);
 	if (ret) {
 		wx_err(txgbe->wx, "failed to register sfp\n");
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 75a4e7b8cc51..ea2b39252edf 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -55,6 +55,31 @@
 #define TXGBE_TS_CTL                            0x10300
 #define TXGBE_TS_CTL_EVAL_MD                    BIT(31)
 
+/* GPIO register bit */
+#define TXGBE_GPIOBIT_0                         BIT(0) /* I:tx fault */
+#define TXGBE_GPIOBIT_1                         BIT(1) /* O:tx disabled */
+#define TXGBE_GPIOBIT_2                         BIT(2) /* I:sfp module absent */
+#define TXGBE_GPIOBIT_3                         BIT(3) /* I:rx signal lost */
+#define TXGBE_GPIOBIT_4                         BIT(4) /* O:rate select, 1G(0) 10G(1) */
+#define TXGBE_GPIOBIT_5                         BIT(5) /* O:rate select, 1G(0) 10G(1) */
+
+/* Extended Interrupt Enable Set */
+#define TXGBE_PX_MISC_ETH_LKDN                  BIT(8)
+#define TXGBE_PX_MISC_DEV_RST                   BIT(10)
+#define TXGBE_PX_MISC_ETH_EVENT                 BIT(17)
+#define TXGBE_PX_MISC_ETH_LK                    BIT(18)
+#define TXGBE_PX_MISC_ETH_AN                    BIT(19)
+#define TXGBE_PX_MISC_INT_ERR                   BIT(20)
+#define TXGBE_PX_MISC_GPIO                      BIT(26)
+#define TXGBE_PX_MISC_IEN_MASK ( \
+				TXGBE_PX_MISC_ETH_LKDN | \
+				TXGBE_PX_MISC_DEV_RST | \
+				TXGBE_PX_MISC_ETH_EVENT | \
+				TXGBE_PX_MISC_ETH_LK | \
+				TXGBE_PX_MISC_ETH_AN | \
+				TXGBE_PX_MISC_INT_ERR | \
+				TXGBE_PX_MISC_GPIO)
+
 /* I2C registers */
 #define TXGBE_I2C_CON                           0x14900 /* I2C Control */
 #define TXGBE_I2C_CON_SLAVE_DISABLE             BIT(6)
@@ -165,6 +190,7 @@ struct txgbe {
 	struct wx *wx;
 	struct txgbe_nodes nodes;
 	struct i2c_adapter *i2c_adap;
+	struct gpio_chip *gpio;
 	struct platform_device *sfp_dev;
 };
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next 5/6] net: txgbe: Implement phylink pcs
  2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
                   ` (3 preceding siblings ...)
  2023-04-03  6:45 ` [PATCH net-next 4/6] net: txgbe: Support GPIO to SFP socket Jiawen Wu
@ 2023-04-03  6:45 ` Jiawen Wu
  2023-04-03 13:47   ` Russell King (Oracle)
  2023-04-03  6:45 ` [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
  5 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

Register MDIO bus for PCS layer, support 10GBASE-R and 1000BASE-X
interfaces to the controller.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/ethernet/wangxun/Kconfig          |   1 +
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 371 +++++++++++++++++-
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  56 +++
 3 files changed, 423 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig
index d9cccdad8a53..9e374e9c3d9c 100644
--- a/drivers/net/ethernet/wangxun/Kconfig
+++ b/drivers/net/ethernet/wangxun/Kconfig
@@ -42,6 +42,7 @@ config TXGBE
 	depends on PCI
 	select GPIOLIB_IRQCHIP
 	select GPIOLIB
+	select PHYLINK
 	select LIBWX
 	select I2C
 	select SFP
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index fe51485a9b68..a00651ba021f 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -6,7 +6,9 @@
 #include <linux/gpio/machine.h>
 #include <linux/gpio/driver.h>
 #include <linux/gpio/property.h>
+#include <linux/phylink.h>
 #include <linux/iopoll.h>
+#include <linux/mdio.h>
 #include <linux/i2c.h>
 #include <linux/pci.h>
 
@@ -67,6 +69,344 @@ static int txgbe_swnodes_register(struct txgbe *txgbe)
 	return software_node_register_node_group(nodes->group);
 }
 
+static int pcs_read(struct txgbe *txgbe, int dev, u32 reg)
+{
+	return mdiodev_c45_read(txgbe->mdiodev, dev, reg);
+}
+
+static int pcs_write(struct txgbe *txgbe, int dev, u32 reg, u16 val)
+{
+	return mdiodev_c45_write(txgbe->mdiodev, dev, reg, val);
+}
+
+static int pma_read(struct txgbe *txgbe, u32 reg)
+{
+	return pcs_read(txgbe, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
+}
+
+static int pma_write(struct txgbe *txgbe, u32 reg, u16 val)
+{
+	return pcs_write(txgbe, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
+}
+
+static int txgbe_pcs_read(struct mii_bus *bus, int addr, int devnum, int regnum)
+{
+	struct wx *wx  = bus->priv;
+	u32 offset, val;
+
+	offset = devnum << 16 | regnum;
+
+	/* Set the LAN port indicator to IDA_ADDR */
+	wr32(wx, TXGBE_XPCS_IDA_ADDR, offset);
+
+	/* Read the data from IDA_DATA register */
+	val = rd32(wx, TXGBE_XPCS_IDA_DATA);
+
+	return (u16)val;
+}
+
+static int txgbe_pcs_write(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val)
+{
+	struct wx *wx = bus->priv;
+	u32 offset;
+
+	offset = devnum << 16 | regnum;
+
+	/* Set the LAN port indicator to IDA_ADDR */
+	wr32(wx, TXGBE_XPCS_IDA_ADDR, offset);
+
+	/* Write the data to IDA_DATA register */
+	wr32(wx, TXGBE_XPCS_IDA_DATA, val);
+
+	return 0;
+}
+
+static void txgbe_ephy_write(struct txgbe *txgbe, u32 addr, u32 data)
+{
+	struct wx *wx = txgbe->wx;
+
+	/* Set the LAN port indicator to IDA_ADDR */
+	wr32(wx, TXGBE_ETHPHY_IDA_ADDR, addr);
+
+	/* Write the data to IDA_DATA register */
+	wr32(wx, TXGBE_ETHPHY_IDA_DATA, data);
+}
+
+static void txgbe_pma_config_10gbr(struct txgbe *txgbe)
+{
+	u16 val;
+
+	pcs_write(txgbe, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
+	val = pcs_read(txgbe, MDIO_MMD_PMAPMD, MDIO_CTRL1);
+	val |= MDIO_CTRL1_SPEED10G;
+	pcs_write(txgbe, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
+
+	pma_write(txgbe, TXGBE_MPLLA_CTL0, 0x21);
+	pma_write(txgbe, TXGBE_MPLLA_CTL3, 0);
+	val = pma_read(txgbe, TXGBE_TX_GENCTRL1);
+	val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTRL1_VBOOST_LVL);
+	pma_write(txgbe, TXGBE_TX_GENCTRL1, val);
+	pma_write(txgbe, TXGBE_MISC_CTL0, 0xCF00);
+	pma_write(txgbe, TXGBE_VCO_CAL_LD0, 0x549);
+	pma_write(txgbe, TXGBE_VCO_CAL_REF0, 0x29);
+	pma_write(txgbe, TXGBE_TX_RATE_CTL, 0);
+	pma_write(txgbe, TXGBE_RX_RATE_CTL, 0);
+	pma_write(txgbe, TXGBE_TX_GEN_CTL2, 0x300);
+	pma_write(txgbe, TXGBE_RX_GEN_CTL2, 0x300);
+	pma_write(txgbe, TXGBE_MPLLA_CTL2, 0x600);
+
+	pma_write(txgbe, TXGBE_RX_EQ_CTL0, 0x45);
+	val = pma_read(txgbe, TXGBE_RX_EQ_ATTN_CTL);
+	val &= ~TXGBE_RX_EQ_ATTN_LVL0;
+	pma_write(txgbe, TXGBE_RX_EQ_ATTN_CTL, val);
+	pma_write(txgbe, TXGBE_DFE_TAP_CTL0, 0xBE);
+	val = pma_read(txgbe, TXGBE_AFE_DFE_ENABLE);
+	val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
+	pma_write(txgbe, TXGBE_AFE_DFE_ENABLE, val);
+	val = pma_read(txgbe, TXGBE_RX_EQ_CTL4);
+	val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
+	pma_write(txgbe, TXGBE_RX_EQ_CTL4, val);
+}
+
+static void txgbe_pma_config_1000bx(struct txgbe *txgbe)
+{
+	u16 val;
+
+	pcs_write(txgbe, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
+	pcs_write(txgbe, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0);
+	pcs_write(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1,
+		  MDIO_PMA_CTRL1_SPEED1000 | MDIO_CTRL1_FULLDPLX);
+
+	val = pma_read(txgbe, TXGBE_TX_GENCTRL1);
+	val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTRL1_VBOOST_LVL);
+	val &= ~TXGBE_TX_GENCTRL1_VBOOST_EN0;
+	pma_write(txgbe, TXGBE_TX_GENCTRL1, val);
+	pma_write(txgbe, TXGBE_MISC_CTL0, 0xCF00);
+
+	pma_write(txgbe, TXGBE_RX_EQ_CTL0, 0x7706);
+	val = pma_read(txgbe, TXGBE_RX_EQ_ATTN_CTL);
+	val &= ~TXGBE_RX_EQ_ATTN_LVL0;
+	pma_write(txgbe, TXGBE_RX_EQ_ATTN_CTL, val);
+	pma_write(txgbe, TXGBE_DFE_TAP_CTL0, 0);
+	val = pma_read(txgbe, TXGBE_RX_GEN_CTL3);
+	val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
+	pma_write(txgbe, TXGBE_RX_EQ_ATTN_CTL, val);
+
+	pma_write(txgbe, TXGBE_MPLLA_CTL0, 0x20);
+	pma_write(txgbe, TXGBE_MPLLA_CTL3, 0x46);
+	pma_write(txgbe, TXGBE_VCO_CAL_LD0, 0x540);
+	pma_write(txgbe, TXGBE_VCO_CAL_REF0, 0x2A);
+	pma_write(txgbe, TXGBE_AFE_DFE_ENABLE, 0);
+	pma_write(txgbe, TXGBE_RX_EQ_CTL4, 0x10);
+	pma_write(txgbe, TXGBE_TX_RATE_CTL, 0x3);
+	pma_write(txgbe, TXGBE_RX_RATE_CTL, 0x3);
+	pma_write(txgbe, TXGBE_TX_GEN_CTL2, 0x100);
+	pma_write(txgbe, TXGBE_RX_GEN_CTL2, 0x100);
+	pma_write(txgbe, TXGBE_MPLLA_CTL2, 0x200);
+	pcs_write(txgbe, MDIO_MMD_VEND2, TXGBE_MII_AN_CTRL, TXGBE_MII_AN_CTRL_MII);
+}
+
+static void txgbe_set_sgmii_an37_ability(struct txgbe *txgbe)
+{
+	u16 val;
+
+	pcs_write(txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_CTRL1,
+		  TXGBE_PCS_DIG_CTRL1_EN_VSMMD1 |
+		  TXGBE_PCS_DIG_CTRL1_CLS7_BP |
+		  TXGBE_PCS_DIG_CTRL1_BYP_PWRUP);
+	pcs_write(txgbe, MDIO_MMD_VEND2, TXGBE_MII_AN_CTRL,
+		  TXGBE_MII_AN_CTRL_MII |
+		  TXGBE_MII_AN_CTRL_TXCFG |
+		  TXGBE_MII_AN_CTRL_PCS_MODE(2));
+	pcs_write(txgbe, MDIO_MMD_VEND2, TXGBE_MII_DIG_CTRL1,
+		  TXGBE_MII_DIG_CTRL1_MAC_AUTOSW);
+	val = pcs_read(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1);
+	val |= BMCR_ANRESTART | BMCR_ANENABLE;
+	pcs_write(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1, val);
+}
+
+static int txgbe_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+			    phy_interface_t interface,
+			    const unsigned long *advertising,
+			    bool permit_pause_to_mac)
+{
+	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
+	struct wx *wx = txgbe->wx;
+	int ret, val;
+
+	/* Wait xpcs power-up good */
+	ret = read_poll_timeout(pcs_read, val,
+				(val & TXGBE_PCS_DIG_STS_PSEQ_ST) ==
+				TXGBE_PCS_DIG_STS_PSEQ_ST_GOOD,
+				10000, 1000000, false,
+				txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_STS);
+	if (ret < 0) {
+		wx_err(wx, "xpcs power-up timeout.\n");
+		return ret;
+	}
+
+	/* Disable xpcs AN-73 */
+	pcs_write(txgbe, MDIO_MMD_AN, MDIO_CTRL1, 0);
+
+	/* Disable PHY MPLLA for eth mode change(after ECO) */
+	txgbe_ephy_write(txgbe, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x243A);
+	WX_WRITE_FLUSH(wx);
+	usleep_range(1000, 2000);
+
+	/* Set the eth change_mode bit first in mis_rst register
+	 * for corresponding LAN port
+	 */
+	wr32(wx, TXGBE_MIS_RST, TXGBE_MIS_RST_LAN_ETH_MODE(wx->bus.func));
+
+	switch (interface) {
+	case PHY_INTERFACE_MODE_10GBASER:
+		txgbe_pma_config_10gbr(txgbe);
+		break;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		txgbe_pma_config_1000bx(txgbe);
+		txgbe_set_sgmii_an37_ability(txgbe);
+		break;
+	default:
+		break;
+	}
+
+	pcs_write(txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_CTRL1,
+		  TXGBE_PCS_DIG_CTRL1_VR_RST | TXGBE_PCS_DIG_CTRL1_EN_VSMMD1);
+	/* wait phy initialization done */
+	ret = read_poll_timeout(pcs_read, val,
+				!(val & TXGBE_PCS_DIG_CTRL1_VR_RST),
+				100000, 10000000, false,
+				txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_CTRL1);
+	if (ret < 0)
+		wx_err(wx, "PHY initialization timeout.\n");
+
+	return ret;
+}
+
+static void txgbe_pcs_get_state_10gbr(struct txgbe *txgbe,
+				      struct phylink_link_state *state)
+{
+	int ret;
+
+	state->link = false;
+
+	ret = pcs_read(txgbe, MDIO_MMD_PCS, MDIO_STAT1);
+	if (ret < 0)
+		return;
+
+	if (ret & MDIO_STAT1_LSTATUS)
+		state->link = true;
+
+	if (state->link) {
+		state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
+		state->duplex = DUPLEX_FULL;
+		state->speed = SPEED_10000;
+	}
+}
+
+static void txgbe_pcs_get_state_1000bx(struct txgbe *txgbe,
+				       struct phylink_link_state *state)
+{
+	int lpa, bmsr;
+
+	/* For C37 1000BASEX mode */
+	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+			      state->advertising)) {
+		/* Reset link state */
+		state->link = false;
+
+		/* Poll for link jitter */
+		read_poll_timeout(pcs_read, lpa, lpa,
+				  100, 50000, false, txgbe,
+				  MDIO_MMD_VEND2, MII_LPA);
+
+		if (lpa < 0 || lpa & LPA_RFAULT) {
+			wx_err(txgbe->wx, "read pcs lpa error: %d\n", lpa);
+			return;
+		}
+
+		bmsr = pcs_read(txgbe, MDIO_MMD_VEND2, MII_BMSR);
+		if (bmsr < 0) {
+			wx_err(txgbe->wx, "read pcs lpa error: %d\n", bmsr);
+			return;
+		}
+
+		phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
+	}
+}
+
+static void txgbe_pcs_get_state(struct phylink_pcs *pcs,
+				struct phylink_link_state *state)
+{
+	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
+
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_10GBASER:
+		txgbe_pcs_get_state_10gbr(txgbe, state);
+		return;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		txgbe_pcs_get_state_1000bx(txgbe, state);
+		return;
+	default:
+		return;
+	}
+}
+
+static void txgbe_pcs_an_restart(struct phylink_pcs *pcs)
+{
+	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
+	int ret;
+
+	ret = pcs_read(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1);
+	if (ret >= 0) {
+		ret |= BMCR_ANRESTART;
+		pcs_write(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
+	}
+}
+
+static const struct phylink_pcs_ops txgbe_pcs_ops = {
+	.pcs_config = txgbe_pcs_config,
+	.pcs_get_state = txgbe_pcs_get_state,
+	.pcs_an_restart = txgbe_pcs_an_restart,
+};
+
+static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
+{
+	struct mdio_device *mdiodev;
+	struct wx *wx = txgbe->wx;
+	struct mii_bus *mii_bus;
+	struct pci_dev *pdev;
+	int ret = 0;
+
+	pdev = wx->pdev;
+
+	mii_bus = devm_mdiobus_alloc(&pdev->dev);
+	if (!mii_bus)
+		return -ENOMEM;
+
+	mii_bus->name = "txgbe_pcs_mdio_bus";
+	mii_bus->read_c45 = &txgbe_pcs_read;
+	mii_bus->write_c45 = &txgbe_pcs_write;
+	mii_bus->parent = &pdev->dev;
+	mii_bus->phy_mask = ~0;
+	mii_bus->priv = wx;
+	snprintf(mii_bus->id, MII_BUS_ID_SIZE, "txgbe_pcs-%x",
+		 (pdev->bus->number << 8) | pdev->devfn);
+
+	ret = devm_mdiobus_register(&pdev->dev, mii_bus);
+	if (ret)
+		return ret;
+
+	mdiodev = mdio_device_create(mii_bus, 0);
+	if (IS_ERR(mdiodev))
+		return PTR_ERR(mdiodev);
+
+	txgbe->mdiodev = mdiodev;
+	txgbe->pcs.ops = &txgbe_pcs_ops;
+
+	return 0;
+}
+
 static void txgbe_i2c_start(struct wx *wx, u16 dev_addr)
 {
 	wr32(wx, TXGBE_I2C_ENABLE, 0);
@@ -197,10 +537,15 @@ static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset)
 	int val, dir;
 
 	dir = chip->get_direction(chip, offset);
-	if (dir == GPIO_LINE_DIRECTION_IN)
+	if (dir == GPIO_LINE_DIRECTION_IN) {
+		struct txgbe *txgbe = (struct txgbe *)wx->priv;
+
 		val = rd32m(wx, WX_GPIO_EXT, BIT(offset));
-	else
+		txgbe->gpio_orig &= ~BIT(offset);
+		txgbe->gpio_orig |= val;
+	} else {
 		val = rd32m(wx, WX_GPIO_DR, BIT(offset));
+	}
 
 	return !!(val & BIT(offset));
 }
@@ -334,12 +679,19 @@ static void txgbe_irq_handler(struct irq_desc *desc)
 	struct txgbe *txgbe = (struct txgbe *)wx->priv;
 	struct gpio_chip *gc = txgbe->gpio;
 	irq_hw_number_t hwirq;
-	unsigned long val;
+	unsigned long gpioirq;
+	u32 gpio;
 
 	chained_irq_enter(chip, desc);
 
-	val = rd32(wx, WX_GPIO_INTSTATUS);
-	for_each_set_bit(hwirq, &val, gc->ngpio)
+	gpioirq = rd32(wx, WX_GPIO_INTSTATUS);
+
+	/* workaround for hysteretic gpio interrupts */
+	gpio = rd32(wx, WX_GPIO_EXT);
+	if (!gpioirq)
+		gpioirq = txgbe->gpio_orig ^ gpio;
+
+	for_each_set_bit(hwirq, &gpioirq, gc->ngpio)
 		generic_handle_domain_irq(gc->irq.domain, hwirq);
 
 	chained_irq_exit(chip, desc);
@@ -358,6 +710,7 @@ static int txgbe_gpio_init(struct txgbe *txgbe)
 	int ret;
 
 	pdev = wx->pdev;
+	txgbe->gpio_orig = 0;
 
 	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
 	if (!gc)
@@ -428,6 +781,12 @@ int txgbe_init_phy(struct txgbe *txgbe)
 		return ret;
 	}
 
+	ret = txgbe_mdio_pcs_init(txgbe);
+	if (ret) {
+		wx_err(txgbe->wx, "failed to init mdio pcs: %d\n", ret);
+		goto err;
+	}
+
 	ret = txgbe_i2c_adapter_add(txgbe);
 	if (ret) {
 		wx_err(txgbe->wx, "failed to init i2c interface: %d\n", ret);
@@ -456,6 +815,8 @@ int txgbe_init_phy(struct txgbe *txgbe)
 
 void txgbe_remove_phy(struct txgbe *txgbe)
 {
+	if (txgbe->mdiodev)
+		mdio_device_free(txgbe->mdiodev);
 	if (txgbe->sfp_dev)
 		platform_device_unregister(txgbe->sfp_dev);
 	if (txgbe->i2c_adap)
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index ea2b39252edf..d121edadfd09 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -5,6 +5,7 @@
 #define _TXGBE_TYPE_H_
 
 #include <linux/property.h>
+#include <linux/phylink.h>
 
 /* Device IDs */
 #define TXGBE_DEV_ID_SP1000                     0x1001
@@ -43,6 +44,8 @@
 
 /**************** SP Registers ****************************/
 /* chip control Registers */
+#define TXGBE_MIS_RST                           0x1000C
+#define TXGBE_MIS_RST_LAN_ETH_MODE(_i)          BIT((_i) + 29)
 #define TXGBE_MIS_PRB_CTL                       0x10010
 #define TXGBE_MIS_PRB_CTL_LAN_UP(_i)            BIT(1 - (_i))
 /* FMGR Registers */
@@ -105,6 +108,56 @@
 #define TXGBE_I2C_SLAVE_ADDR                    (0xA0 >> 1)
 #define TXGBE_I2C_EEPROM_DEV_ADDR               0xA0
 
+/************************************** ETH PHY ******************************/
+#define TXGBE_XPCS_IDA_ADDR                     0x13000
+#define TXGBE_XPCS_IDA_DATA                     0x13004
+#define TXGBE_ETHPHY_IDA_ADDR                   0x13008
+#define TXGBE_ETHPHY_IDA_DATA                   0x1300C
+/* PHY Registers */
+#define TXGBE_SUP_DIG_MPLLA_OVRD_IN_0           0x4
+/* Vendor Specific PCS MMD Registers */
+#define TXGBE_PCS_DIG_CTRL1                     0x8000
+#define TXGBE_PCS_DIG_CTRL1_VR_RST              BIT(15)
+#define TXGBE_PCS_DIG_CTRL1_EN_VSMMD1           BIT(13)
+#define TXGBE_PCS_DIG_CTRL1_CLS7_BP             BIT(12)
+#define TXGBE_PCS_DIG_CTRL1_BYP_PWRUP           BIT(1)
+#define TXGBE_PCS_DIG_STS                       0x8010
+#define TXGBE_PCS_DIG_STS_PSEQ_ST               GENMASK(4, 2)
+#define TXGBE_PCS_DIG_STS_PSEQ_ST_GOOD          FIELD_PREP(GENMASK(4, 2), 0x4)
+/* Vendor Specific MII MMD Standard Registers */
+#define TXGBE_MII_DIG_CTRL1                     0x8000
+#define TXGBE_MII_DIG_CTRL1_MAC_AUTOSW          BIT(9)
+#define TXGBE_MII_AN_CTRL                       0x8001
+#define TXGBE_MII_AN_CTRL_MII                   BIT(8)
+#define TXGBE_MII_AN_CTRL_TXCFG                 BIT(3)
+#define TXGBE_MII_AN_CTRL_PCS_MODE(_v)          FIELD_PREP(GENMASK(2, 1), _v)
+/* Vendor Specific PMA MMD Registers */
+#define TXGBE_PMA_MMD                           0x8020
+#define TXGBE_TX_GENCTRL1                       0x11
+#define TXGBE_TX_GENCTRL1_VBOOST_LVL            GENMASK(10, 8)
+#define TXGBE_TX_GENCTRL1_VBOOST_EN0            BIT(4)
+#define TXGBE_TX_GEN_CTL2                       0x12
+#define TXGBE_TX_RATE_CTL                       0x14
+#define TXGBE_RX_GEN_CTL2                       0x32
+#define TXGBE_RX_GEN_CTL3                       0x33
+#define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0           GENMASK(2, 0)
+#define TXGBE_RX_RATE_CTL                       0x34
+#define TXGBE_RX_EQ_ATTN_CTL                    0x37
+#define TXGBE_RX_EQ_ATTN_LVL0                   GENMASK(2, 0)
+#define TXGBE_RX_EQ_CTL0                        0x38
+#define TXGBE_RX_EQ_CTL4                        0x3C
+#define TXGBE_RX_EQ_CTL4_CONT_ADAPT0            BIT(0)
+#define TXGBE_AFE_DFE_ENABLE                    0x3D
+#define TXGBE_DFE_EN_0                          BIT(4)
+#define TXGBE_AFE_EN_0                          BIT(0)
+#define TXGBE_DFE_TAP_CTL0                      0x3E
+#define TXGBE_MPLLA_CTL0                        0x51
+#define TXGBE_MPLLA_CTL2                        0x53
+#define TXGBE_MPLLA_CTL3                        0x57
+#define TXGBE_MISC_CTL0                         0x70
+#define TXGBE_VCO_CAL_LD0                       0x72
+#define TXGBE_VCO_CAL_REF0                      0x76
+
 /* Part Number String Length */
 #define TXGBE_PBANUM_LENGTH                     32
 
@@ -189,9 +242,12 @@ struct txgbe_nodes {
 struct txgbe {
 	struct wx *wx;
 	struct txgbe_nodes nodes;
+	struct mdio_device *mdiodev;
+	struct phylink_pcs pcs;
 	struct i2c_adapter *i2c_adap;
 	struct gpio_chip *gpio;
 	struct platform_device *sfp_dev;
+	u32 gpio_orig;
 };
 
 #endif /* _TXGBE_TYPE_H_ */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer
  2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
                   ` (4 preceding siblings ...)
  2023-04-03  6:45 ` [PATCH net-next 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
@ 2023-04-03  6:45 ` Jiawen Wu
  2023-04-03 13:22   ` Andrew Lunn
  5 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-03  6:45 UTC (permalink / raw)
  To: netdev, linux; +Cc: mengyuanlou, Jiawen Wu

Add phylink support to Wangxun 10Gb Ethernet controller, for the 10GBASE-R
and 1000BASE-X interfaces.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 .../ethernet/wangxun/txgbe/txgbe_ethtool.c    |  34 ++++++
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  20 ++--
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 110 +++++++++++++++++-
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |   5 +
 4 files changed, 156 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
index d914e9a05404..43ca84c90637 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
@@ -6,11 +6,45 @@
 #include <linux/netdevice.h>
 
 #include "../libwx/wx_ethtool.h"
+#include "../libwx/wx_type.h"
+#include "txgbe_type.h"
 #include "txgbe_ethtool.h"
 
+static int txgbe_nway_reset(struct net_device *netdev)
+{
+	struct wx *wx = netdev_priv(netdev);
+	struct txgbe *txgbe;
+
+	txgbe = (struct txgbe *)wx->priv;
+	return phylink_ethtool_nway_reset(txgbe->phylink);
+}
+
+static int txgbe_get_link_ksettings(struct net_device *netdev,
+				    struct ethtool_link_ksettings *cmd)
+{
+	struct wx *wx = netdev_priv(netdev);
+	struct txgbe *txgbe;
+
+	txgbe = (struct txgbe *)wx->priv;
+	return phylink_ethtool_ksettings_get(txgbe->phylink, cmd);
+}
+
+static int txgbe_set_link_ksettings(struct net_device *netdev,
+				    const struct ethtool_link_ksettings *cmd)
+{
+	struct wx *wx = netdev_priv(netdev);
+	struct txgbe *txgbe;
+
+	txgbe = (struct txgbe *)wx->priv;
+	return phylink_ethtool_ksettings_set(txgbe->phylink, cmd);
+}
+
 static const struct ethtool_ops txgbe_ethtool_ops = {
 	.get_drvinfo		= wx_get_drvinfo,
+	.nway_reset		= txgbe_nway_reset,
 	.get_link		= ethtool_op_get_link,
+	.get_link_ksettings	= txgbe_get_link_ksettings,
+	.set_link_ksettings	= txgbe_set_link_ksettings,
 };
 
 void txgbe_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index caaefc20afb9..e033ed8409ce 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -7,6 +7,7 @@
 #include <linux/netdevice.h>
 #include <linux/string.h>
 #include <linux/etherdevice.h>
+#include <linux/phylink.h>
 #include <net/ip.h>
 #include <linux/if_vlan.h>
 
@@ -206,7 +207,7 @@ static int txgbe_request_irq(struct wx *wx)
 
 static void txgbe_up_complete(struct wx *wx)
 {
-	u32 reg;
+	struct txgbe *txgbe = (struct txgbe *)wx->priv;
 
 	wx_control_hw(wx, true);
 	wx_configure_vectors(wx);
@@ -215,23 +216,15 @@ static void txgbe_up_complete(struct wx *wx)
 	smp_mb__before_atomic();
 	wx_napi_enable_all(wx);
 
+	phylink_start(txgbe->phylink);
+
 	/* clear any pending interrupts, may auto mask */
 	rd32(wx, WX_PX_IC);
 	rd32(wx, WX_PX_MISC_IC);
 	txgbe_irq_enable(wx, true);
 
-	/* Configure MAC Rx and Tx when link is up */
-	reg = rd32(wx, WX_MAC_RX_CFG);
-	wr32(wx, WX_MAC_RX_CFG, reg);
-	wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
-	reg = rd32(wx, WX_MAC_WDG_TIMEOUT);
-	wr32(wx, WX_MAC_WDG_TIMEOUT, reg);
-	reg = rd32(wx, WX_MAC_TX_CFG);
-	wr32(wx, WX_MAC_TX_CFG, (reg & ~WX_MAC_TX_CFG_SPEED_MASK) | WX_MAC_TX_CFG_SPEED_10G);
-
 	/* enable transmits */
 	netif_tx_start_all_queues(wx->netdev);
-	netif_carrier_on(wx->netdev);
 }
 
 static void txgbe_reset(struct wx *wx)
@@ -265,7 +258,6 @@ static void txgbe_disable_device(struct wx *wx)
 		wx_disable_rx_queue(wx, wx->rx_ring[i]);
 
 	netif_tx_stop_all_queues(netdev);
-	netif_carrier_off(netdev);
 	netif_tx_disable(netdev);
 
 	wx_irq_disable(wx);
@@ -296,8 +288,12 @@ static void txgbe_disable_device(struct wx *wx)
 
 static void txgbe_down(struct wx *wx)
 {
+	struct txgbe *txgbe = (struct txgbe *)wx->priv;
+
 	txgbe_disable_device(wx);
 	txgbe_reset(wx);
+	if (txgbe->phylink)
+		phylink_stop(txgbe->phylink);
 
 	wx_clean_all_tx_rings(wx);
 	wx_clean_all_rx_rings(wx);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index a00651ba021f..fee7fe175734 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -13,6 +13,7 @@
 #include <linux/pci.h>
 
 #include "../libwx/wx_type.h"
+#include "../libwx/wx_lib.h"
 #include "../libwx/wx_hw.h"
 #include "txgbe_type.h"
 #include "txgbe_phy.h"
@@ -407,6 +408,98 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe)
 	return 0;
 }
 
+static struct phylink_pcs *txgbe_phylink_mac_select(struct phylink_config *config,
+						    phy_interface_t interface)
+{
+	struct wx *wx = netdev_priv(to_net_dev(config->dev));
+	struct txgbe *txgbe = (struct txgbe *)wx->priv;
+
+	return &txgbe->pcs;
+}
+
+static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
+			     const struct phylink_link_state *state)
+{
+}
+
+static void txgbe_mac_link_down(struct phylink_config *config,
+				unsigned int mode, phy_interface_t interface)
+{
+	struct wx *wx = netdev_priv(to_net_dev(config->dev));
+
+	wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
+}
+
+static void txgbe_mac_link_up(struct phylink_config *config,
+			      struct phy_device *phy,
+			      unsigned int mode, phy_interface_t interface,
+			      int speed, int duplex,
+			      bool tx_pause, bool rx_pause)
+{
+	struct wx *wx = netdev_priv(to_net_dev(config->dev));
+	u32 txcfg, rxcfg, wdg;
+
+	txcfg = rd32(wx, WX_MAC_TX_CFG);
+	txcfg &= ~WX_MAC_TX_CFG_SPEED_MASK;
+
+	switch (speed) {
+	case SPEED_10000:
+		txcfg |= WX_MAC_TX_CFG_SPEED_10G;
+		break;
+	case SPEED_1000:
+	case SPEED_100:
+	case SPEED_10:
+		txcfg |= WX_MAC_TX_CFG_SPEED_1G;
+		break;
+	default:
+		break;
+	}
+
+	wr32(wx, WX_MAC_TX_CFG, txcfg | WX_MAC_TX_CFG_TE);
+
+	/* Re configure MAC Rx */
+	rxcfg = rd32(wx, WX_MAC_RX_CFG);
+	wr32(wx, WX_MAC_RX_CFG, rxcfg);
+	wr32(wx, WX_MAC_PKT_FLT, WX_MAC_PKT_FLT_PR);
+	wdg = rd32(wx, WX_MAC_WDG_TIMEOUT);
+	wr32(wx, WX_MAC_WDG_TIMEOUT, wdg);
+}
+
+static const struct phylink_mac_ops txgbe_mac_ops = {
+	.mac_select_pcs = txgbe_phylink_mac_select,
+	.mac_config = txgbe_mac_config,
+	.mac_link_down = txgbe_mac_link_down,
+	.mac_link_up = txgbe_mac_link_up,
+};
+
+static int txgbe_phylink_init(struct txgbe *txgbe)
+{
+	struct phylink_config *config;
+	struct fwnode_handle *fwnode;
+	struct wx *wx = txgbe->wx;
+	phy_interface_t phy_mode;
+	struct phylink *phylink;
+
+	config = devm_kzalloc(&wx->pdev->dev, sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return -ENOMEM;
+
+	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);
+	fwnode = software_node_fwnode(txgbe->nodes.group[SWNODE_PHYLINK]);
+	phylink = phylink_create(config, fwnode, phy_mode, &txgbe_mac_ops);
+	if (IS_ERR(phylink))
+		return PTR_ERR(phylink);
+
+	txgbe->phylink = phylink;
+
+	return 0;
+}
+
 static void txgbe_i2c_start(struct wx *wx, u16 dev_addr)
 {
 	wr32(wx, TXGBE_I2C_ENABLE, 0);
@@ -680,7 +773,9 @@ static void txgbe_irq_handler(struct irq_desc *desc)
 	struct gpio_chip *gc = txgbe->gpio;
 	irq_hw_number_t hwirq;
 	unsigned long gpioirq;
-	u32 gpio;
+	u32 gpio, eicr, reg;
+
+	eicr = wx_misc_isb(wx, WX_ISB_MISC);
 
 	chained_irq_enter(chip, desc);
 
@@ -696,6 +791,11 @@ static void txgbe_irq_handler(struct irq_desc *desc)
 
 	chained_irq_exit(chip, desc);
 
+	if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN)) {
+		reg = rd32(wx, TXGBE_CFG_PORT_ST);
+		phylink_mac_change(txgbe->phylink, !!(reg & TXGBE_CFG_PORT_ST_LINK_UP));
+	}
+
 	/* unmask interrupt */
 	if (netif_running(wx->netdev))
 		wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
@@ -787,6 +887,12 @@ int txgbe_init_phy(struct txgbe *txgbe)
 		goto err;
 	}
 
+	ret = txgbe_phylink_init(txgbe);
+	if (ret) {
+		wx_err(txgbe->wx, "failed to init phylink\n");
+		goto err;
+	}
+
 	ret = txgbe_i2c_adapter_add(txgbe);
 	if (ret) {
 		wx_err(txgbe->wx, "failed to init i2c interface: %d\n", ret);
@@ -815,6 +921,8 @@ int txgbe_init_phy(struct txgbe *txgbe)
 
 void txgbe_remove_phy(struct txgbe *txgbe)
 {
+	if (txgbe->phylink)
+		phylink_destroy(txgbe->phylink);
 	if (txgbe->mdiodev)
 		mdio_device_free(txgbe->mdiodev);
 	if (txgbe->sfp_dev)
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index d121edadfd09..ad3e426e49a0 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -83,6 +83,10 @@
 				TXGBE_PX_MISC_INT_ERR | \
 				TXGBE_PX_MISC_GPIO)
 
+/* Port cfg registers */
+#define TXGBE_CFG_PORT_ST                       0x14404
+#define TXGBE_CFG_PORT_ST_LINK_UP               BIT(0)
+
 /* I2C registers */
 #define TXGBE_I2C_CON                           0x14900 /* I2C Control */
 #define TXGBE_I2C_CON_SLAVE_DISABLE             BIT(6)
@@ -244,6 +248,7 @@ struct txgbe {
 	struct txgbe_nodes nodes;
 	struct mdio_device *mdiodev;
 	struct phylink_pcs pcs;
+	struct phylink *phylink;
 	struct i2c_adapter *i2c_adap;
 	struct gpio_chip *gpio;
 	struct platform_device *sfp_dev;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink
  2023-04-03  6:45 ` [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
@ 2023-04-03 12:35   ` Andrew Lunn
  0 siblings, 0 replies; 22+ messages in thread
From: Andrew Lunn @ 2023-04-03 12:35 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

> +	nodes->gpio_props[0] = PROPERTY_ENTRY_STRING("pinctrl-names", "default");
> +	swnodes[SWNODE_GPIO] = NODE_PROP(nodes->gpio_name, nodes->gpio_props);
> +	nodes->gpio0_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 0, GPIO_ACTIVE_HIGH);
> +	nodes->gpio1_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 1, GPIO_ACTIVE_HIGH);
> +	nodes->gpio2_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 2, GPIO_ACTIVE_LOW);
> +	nodes->gpio3_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 3, GPIO_ACTIVE_HIGH);
> +	nodes->gpio4_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 4, GPIO_ACTIVE_HIGH);
> +	nodes->gpio5_ref[0] = SOFTWARE_NODE_REFERENCE(&swnodes[SWNODE_GPIO], 5, GPIO_ACTIVE_HIGH);

Please could you add some comments indicating what these 6 GPIOs
are. Or use more descriptive names than gpioX_ref.

> +int txgbe_init_phy(struct txgbe *txgbe)
> +{
> +	int ret;
> +
> +	ret = txgbe_swnodes_register(txgbe);
> +	if (ret) {
> +		wx_err(txgbe->wx, "failed to register software nodes\n");
> +		return ret;
> +	}
> +
> +	return 0;


I assume this function is going to be extended with later patches?
Otherwise you can simply it.

	  Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-03  6:45 ` [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
@ 2023-04-03 12:52   ` Andrew Lunn
  2023-04-04  2:47     ` Jiawen Wu
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-04-03 12:52 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

On Mon, Apr 03, 2023 at 02:45:24PM +0800, Jiawen Wu wrote:
> I2C bus is integrated in Wangxun 10Gb ethernet chip. Implement I2C bus
> driver to receive I2C messages.

Please Cc: the i2c mailing list for comments. They know more about I2C
than the netdev people.

Is the I2C bus master your own IP, or have you licensed a core? Or
using the open cores i2C bus master? I just want to make sure there is
not already a linux driver for this.
 
> +static void txgbe_i2c_start(struct wx *wx, u16 dev_addr)
> +{
> +	wr32(wx, TXGBE_I2C_ENABLE, 0);
> +
> +	wr32(wx, TXGBE_I2C_CON,
> +	     (TXGBE_I2C_CON_MASTER_MODE |
> +	      TXGBE_I2C_CON_SPEED(1) |
> +	      TXGBE_I2C_CON_RESTART_EN |
> +	      TXGBE_I2C_CON_SLAVE_DISABLE));
> +	/* Default addr is 0xA0 ,bit 0 is configure for read/write! */

A generic I2C bus master should not care about that address is being
read/write. For the SFP use case, 0xa0 will be used most of the time,
plus 0xa2 for diagnostics. But when the SFP contains a copper PHY,
other addresses will be used as well.

> +static int txgbe_i2c_xfer(struct i2c_adapter *i2c_adap,
> +			  struct i2c_msg *msg, int num_msgs)
> +{
> +	struct wx *wx = i2c_get_adapdata(i2c_adap);
> +	u8 *dev_addr = msg[0].buf;
> +	bool read = false;
> +	int i, ret;
> +	u8 *buf;
> +	u16 len;
> +
> +	txgbe_i2c_start(wx, msg[0].addr);
> +
> +	for (i = 0; i < num_msgs; i++) {
> +		if (msg[i].flags & I2C_M_RD) {
> +			read = true;
> +			len = msg[i].len;
> +			buf = msg[i].buf;
> +		}
> +	}
> +
> +	if (!read) {
> +		wx_err(wx, "I2C write not supported\n");
> +		return num_msgs;
> +	}

Write is not supported at all? Is this a hardware limitation?  I think
-EOPNOTSUPP is required here, and you need to ensure the code using
the I2C bus master has quirks to not try to write.

> +#define TXGBE_I2C_SLAVE_ADDR                    (0xA0 >> 1)
> +#define TXGBE_I2C_EEPROM_DEV_ADDR               0xA0

These two do not appear to be used? I guess you took your hard coded
SFP i2c bus master and generalised it? Please clean up dead code like
this.

	Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 3/6] net: txgbe: Add SFP module identify
  2023-04-03  6:45 ` [PATCH net-next 3/6] net: txgbe: Add SFP module identify Jiawen Wu
@ 2023-04-03 12:58   ` Andrew Lunn
  0 siblings, 0 replies; 22+ messages in thread
From: Andrew Lunn @ 2023-04-03 12:58 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

> +Identifying Adapter
> +===================
> +The driver is compatible with WangXun Sapphire Dual ports Ethernet Adapters.
> +
> +SFP+ Devices with Pluggable Optics
> +----------------------------------
> +The following is a list of 3rd party SFP+ modules that have been tested and verified.
> +
> ++----------+----------------------+----------------------+
> +| Supplier | Type                 | Part Numbers         |
> ++==========+======================+======================+
> +| ACCELINK | SFP+                 | RTXM228-551          |
> ++----------+----------------------+----------------------+
> +| Avago	   | SFP+                 | SFBR-7701SDZ         |
> ++----------+----------------------+----------------------+

Tab issue.

> +| BOYANG   | SFP+                 | OMXD30000            |
> ++----------+----------------------+----------------------+
> +| F-tone   | SFP+                 | FTCS-851X-02D        |
> ++----------+----------------------+----------------------+
> +| FS       | SFP+                 | SFP-10GSR-85         |
> ++----------+----------------------+----------------------+
> +| Finisar  | SFP+                 | FTLX8574D3BCL        |
> ++----------+----------------------+----------------------+
> +| Hisense  | SFP+                 | LTF8502-BC+          |
> ++----------+----------------------+----------------------+
> +| HGTECH   | SFP+                 | MTRS-01X11-G         |
> ++----------+----------------------+----------------------+
> +| HP       | SFP+                 | SR SFP+ 456096-001   |
> ++----------+----------------------+----------------------+
> +| Huawei   | SFP+                 | AFBR-709SMZ          |
> ++----------+----------------------+----------------------+
> +| Intel    | SFP+                 | FTLX8571D3BCV-IT     |
> ++----------+----------------------+----------------------+
> +| JDSU     | SFP+                 | PLRXPL-SC-S43        |
> ++----------+----------------------+----------------------+
> +| SONT     | SFP+                 | XP-8G10-01           |
> ++----------+----------------------+----------------------+
> +| Trixon   | SFP+                 | TPS-TGM3-85DCR       |
> ++----------+----------------------+----------------------+

This does not make much sense, now that the generic SFP driver is
being used. If you want to have a such a list, move it into the
generic SFP documentation.

I assume you have retested all these using the generic code?

Russell will be interested in the contents of the EEPROM for these. 

> +Laser turns off for SFP+ when ifconfig ethX down
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +"ifconfig ethX down" turns off the laser for SFP+ fiber adapters.
> +"ifconfig ethX up" turns on the laser.

This also not specific to your device, the generic SFP code will do
that.

> +static int txgbe_sfp_register(struct txgbe *txgbe)
> +{
> +	struct pci_dev *pdev = txgbe->wx->pdev;
> +	struct platform_device_info info;
> +	struct platform_device *sfp_dev;
> +
> +	memset(&info, 0, sizeof(info));

I _think_ the memset can be replace by

struct platform_device_info info = {};

       Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 4/6] net: txgbe: Support GPIO to SFP socket
  2023-04-03  6:45 ` [PATCH net-next 4/6] net: txgbe: Support GPIO to SFP socket Jiawen Wu
@ 2023-04-03 13:16   ` Andrew Lunn
  0 siblings, 0 replies; 22+ messages in thread
From: Andrew Lunn @ 2023-04-03 13:16 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

Please Cc: The GPIO mailing list, they know more about GPIO drivers
than the netdev people.

> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> index eb89a274083e..dff0d573ee33 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> @@ -1348,7 +1348,8 @@ void wx_free_irq(struct wx *wx)
>  		free_irq(entry->vector, q_vector);
>  	}
>  
> -	free_irq(wx->msix_entries[vector].vector, wx);
> +	if (wx->mac.type == wx_mac_em)
> +		free_irq(wx->msix_entries[vector].vector, wx);
>  }
>  EXPORT_SYMBOL(wx_free_irq);
>  
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
> index 072aa2bd3fdc..89bba827edf2 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
> @@ -79,7 +79,9 @@
>  #define WX_GPIO_INTMASK              0x14834
>  #define WX_GPIO_INTTYPE_LEVEL        0x14838
>  #define WX_GPIO_POLARITY             0x1483C
> +#define WX_GPIO_INTSTATUS            0x14844
>  #define WX_GPIO_EOI                  0x1484C
> +#define WX_GPIO_EXT                  0x14850
>  
>  /*********************** Transmit DMA registers **************************/
>  /* transmit global control */
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
> index ebc46f3be056..b87034e57140 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c
> @@ -256,6 +256,7 @@ int txgbe_validate_eeprom_checksum(struct wx *wx, u16 *checksum_val)
>  static void txgbe_reset_misc(struct wx *wx)
>  {
>  	wx_reset_misc(wx);
> +	wr32(wx, WX_GPIO_DDR, 0x32);

No magic numbers, use #define's.

DDR is Data Direction Register? So are you configuring some of the
pins as outputs? Is that needed? The SFP driver will request how it
wants the pins configuring, so there should not be any need to hard
code it here.

>  	txgbe_init_thermal_sensor_thresh(wx);
>  }
>  
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> index 319d56720c06..caaefc20afb9 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> @@ -82,6 +82,10 @@ static int txgbe_enumerate_functions(struct wx *wx)
>   **/
>  static void txgbe_irq_enable(struct wx *wx, bool queues)
>  {
> +	wr32(wx, WX_GPIO_INTEN, TXGBE_GPIOBIT_0 | TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
> +	wr32(wx, WX_GPIO_INTTYPE_LEVEL, TXGBE_GPIOBIT_0 | TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);

Again, the SFP code will ask that interrupts are enabled for the GPIOs
it is interested in. There should not be any need to hard code
this. Disable all interrupts until they are requested.

> +	wr32(wx, WX_PX_MISC_IEN, TXGBE_PX_MISC_IEN_MASK);
> +
>  	/* unmask interrupt */
>  	wx_intr_enable(wx, TXGBE_INTR_MISC(wx));
>  	if (queues)
> @@ -129,17 +133,6 @@ static irqreturn_t txgbe_intr(int __always_unused irq, void *data)
>  	return IRQ_HANDLED;
>  }
>  
> +static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct wx *wx = gpiochip_get_data(chip);
> +	int val, dir;
> +
> +	dir = chip->get_direction(chip, offset);
> +	if (dir == GPIO_LINE_DIRECTION_IN)
> +		val = rd32m(wx, WX_GPIO_EXT, BIT(offset));
> +	else
> +		val = rd32m(wx, WX_GPIO_DR, BIT(offset));
> +
> +	return !!(val & BIT(offset));
> +}
> +
> +static int txgbe_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct wx *wx = gpiochip_get_data(chip);
> +	u32 val;
> +
> +	val = rd32(wx, WX_GPIO_DDR);
> +	if (BIT(offset) & val)
> +		return GPIO_LINE_DIRECTION_OUT;
> +
> +	return GPIO_LINE_DIRECTION_IN;
> +}
> +
> +static int txgbe_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
> +{
> +	return 0;
> +}

This is where the write to DDR should probably happen?

> +
> +static int txgbe_gpio_direction_out(struct gpio_chip *chip, unsigned int offset,
> +				    int val)
> +{
> +	struct wx *wx = gpiochip_get_data(chip);
> +	u32 mask;
> +	int dir;
> +
> +	dir = chip->get_direction(chip, offset);
> +	if (dir == GPIO_LINE_DIRECTION_IN)
> +		return 0;

Don't assume in by default.

> +	mask = BIT(offset) | BIT(offset - 1);
> +	if (val)
> +		wr32m(wx, WX_GPIO_DR, mask, mask);
> +	else
> +		wr32m(wx, WX_GPIO_DR, mask, 0);
> +
> +	return 0;
> +}
> +
> +static void txgbe_gpio_irq_ack(struct irq_data *d)
> +{
> +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +	irq_hw_number_t hwirq = irqd_to_hwirq(d);
> +	struct wx *wx = gpiochip_get_data(gc);
> +
> +	wr32(wx, WX_GPIO_EOI, BIT(hwirq));
> +}
> +
> +static void txgbe_gpio_irq_mask(struct irq_data *d)
> +{
> +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +	irq_hw_number_t hwirq = irqd_to_hwirq(d);
> +	struct wx *wx = gpiochip_get_data(gc);
> +
> +	gpiochip_disable_irq(gc, hwirq);
> +
> +	wr32m(wx, WX_GPIO_INTMASK, BIT(hwirq), BIT(hwirq));
> +}
> +
> +static void txgbe_gpio_irq_unmask(struct irq_data *d)
> +{
> +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +	irq_hw_number_t hwirq = irqd_to_hwirq(d);
> +	struct wx *wx = gpiochip_get_data(gc);
> +
> +	gpiochip_enable_irq(gc, hwirq);
> +
> +	wr32m(wx, WX_GPIO_INTMASK, BIT(hwirq), 0);
> +}
> +
> +static int txgbe_gpio_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +	irq_hw_number_t hwirq = irqd_to_hwirq(d);
> +	struct wx *wx = gpiochip_get_data(gc);
> +	u32 level, polarity;
> +
> +	level = rd32(wx, WX_GPIO_INTTYPE_LEVEL);
> +	polarity = rd32(wx, WX_GPIO_POLARITY);
> +
> +	switch (type) {
> +	case IRQ_TYPE_EDGE_BOTH:
> +		level |= BIT(hwirq);
> +		break;
> +	case IRQ_TYPE_EDGE_RISING:
> +		level |= BIT(hwirq);
> +		polarity |= BIT(hwirq);
> +		break;
> +	case IRQ_TYPE_EDGE_FALLING:
> +		level |= BIT(hwirq);
> +		polarity &= ~BIT(hwirq);
> +		break;
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		level &= ~BIT(hwirq);
> +		polarity |= BIT(hwirq);
> +		break;
> +	case IRQ_TYPE_LEVEL_LOW:
> +		level &= ~BIT(hwirq);
> +		polarity &= ~BIT(hwirq);
> +		break;
> +	}

You have two bits, level and priority, so 4 states. But you have
handling 5 different types? Please return -EOPNOTSUPP for whatever you
don't support.

> +
> +	if (type & IRQ_TYPE_LEVEL_MASK)
> +		irq_set_handler_locked(d, handle_level_irq);
> +	else if (type & IRQ_TYPE_EDGE_BOTH)
> +		irq_set_handler_locked(d, handle_edge_irq);

I think this is part of the answer to my question above. Please
re-write this code to make it more obvious.

	 Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer
  2023-04-03  6:45 ` [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
@ 2023-04-03 13:22   ` Andrew Lunn
  2023-04-03 13:51     ` Russell King (Oracle)
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-04-03 13:22 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

> @@ -215,23 +216,15 @@ static void txgbe_up_complete(struct wx *wx)
>  	smp_mb__before_atomic();
>  	wx_napi_enable_all(wx);
>  
> +	phylink_start(txgbe->phylink);
> +

>  static void txgbe_down(struct wx *wx)
>  {
> +	struct txgbe *txgbe = (struct txgbe *)wx->priv;
> +
>  	txgbe_disable_device(wx);
>  	txgbe_reset(wx);
> +	if (txgbe->phylink)
> +		phylink_stop(txgbe->phylink);

You uncoditionally phylink_start(). Does this need to be conditional?

> +static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
> +			     const struct phylink_link_state *state)
> +{
> +}

That is very likely to be wrong. Lets see what Russell says.

     Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 5/6] net: txgbe: Implement phylink pcs
  2023-04-03  6:45 ` [PATCH net-next 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
@ 2023-04-03 13:47   ` Russell King (Oracle)
  2023-04-10  7:32     ` Jiawen Wu
  0 siblings, 1 reply; 22+ messages in thread
From: Russell King (Oracle) @ 2023-04-03 13:47 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, mengyuanlou

On Mon, Apr 03, 2023 at 02:45:27PM +0800, Jiawen Wu wrote:
> +static void txgbe_set_sgmii_an37_ability(struct txgbe *txgbe)
> +{
> +	u16 val;
> +
> +	pcs_write(txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_CTRL1,
> +		  TXGBE_PCS_DIG_CTRL1_EN_VSMMD1 |
> +		  TXGBE_PCS_DIG_CTRL1_CLS7_BP |
> +		  TXGBE_PCS_DIG_CTRL1_BYP_PWRUP);
> +	pcs_write(txgbe, MDIO_MMD_VEND2, TXGBE_MII_AN_CTRL,
> +		  TXGBE_MII_AN_CTRL_MII |
> +		  TXGBE_MII_AN_CTRL_TXCFG |
> +		  TXGBE_MII_AN_CTRL_PCS_MODE(2));
> +	pcs_write(txgbe, MDIO_MMD_VEND2, TXGBE_MII_DIG_CTRL1,
> +		  TXGBE_MII_DIG_CTRL1_MAC_AUTOSW);
> +	val = pcs_read(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1);
> +	val |= BMCR_ANRESTART | BMCR_ANENABLE;
> +	pcs_write(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1, val);

Does this really set the hardware up for *CISCO SGMII* or does it
set it up for *1000BASE-X*?

Hint: SGMII is *not* just another name for *1000BASE-X*. SGMII is a
modification of IEEE 802.3 1000BASE-X by Cisco to support 10M and
100M speeds. Please do NOT use SGMII as an inter-changeable term for
1000BASE-X, even if everyone else in industry commits that crime. It
is *incorrect* and an abuse of the term.

> +}
> +
> +static int txgbe_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
> +			    phy_interface_t interface,
> +			    const unsigned long *advertising,
> +			    bool permit_pause_to_mac)
> +{
> +	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
> +	struct wx *wx = txgbe->wx;
> +	int ret, val;
> +
> +	/* Wait xpcs power-up good */
> +	ret = read_poll_timeout(pcs_read, val,
> +				(val & TXGBE_PCS_DIG_STS_PSEQ_ST) ==
> +				TXGBE_PCS_DIG_STS_PSEQ_ST_GOOD,
> +				10000, 1000000, false,
> +				txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_STS);
> +	if (ret < 0) {
> +		wx_err(wx, "xpcs power-up timeout.\n");
> +		return ret;
> +	}
> +
> +	/* Disable xpcs AN-73 */
> +	pcs_write(txgbe, MDIO_MMD_AN, MDIO_CTRL1, 0);
> +
> +	/* Disable PHY MPLLA for eth mode change(after ECO) */
> +	txgbe_ephy_write(txgbe, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x243A);
> +	WX_WRITE_FLUSH(wx);
> +	usleep_range(1000, 2000);

Is all this really appropriate for when pcs_config() gets called to
modify the advertisement? I think, maybe, you probably want to make
this conditional on the interface mode changing?

On that note, I don't see anywhere where you set the advertisement.

> +static void txgbe_pcs_get_state_10gbr(struct txgbe *txgbe,
> +				      struct phylink_link_state *state)
> +{
> +	int ret;
> +
> +	state->link = false;
> +
> +	ret = pcs_read(txgbe, MDIO_MMD_PCS, MDIO_STAT1);
> +	if (ret < 0)
> +		return;
> +
> +	if (ret & MDIO_STAT1_LSTATUS)
> +		state->link = true;
> +
> +	if (state->link) {
> +		state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
> +		state->duplex = DUPLEX_FULL;
> +		state->speed = SPEED_10000;
> +	}
> +}
> +
> +static void txgbe_pcs_get_state_1000bx(struct txgbe *txgbe,

I think you're using "bx" here as a shortened 1000base-x, but there
is an official 1000BASE-BX which makes this a little confusing.
Please either use 1000b_x or spell it out as 1000basex.

> +				       struct phylink_link_state *state)
> +{
> +	int lpa, bmsr;
> +
> +	/* For C37 1000BASEX mode */
> +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> +			      state->advertising)) {
> +		/* Reset link state */
> +		state->link = false;
> +
> +		/* Poll for link jitter */
> +		read_poll_timeout(pcs_read, lpa, lpa,
> +				  100, 50000, false, txgbe,
> +				  MDIO_MMD_VEND2, MII_LPA);

What jitter are you referring to? If the link is down (and thus this
register reads zero), why do we have to spin here for 50ms each time?

> +
> +		if (lpa < 0 || lpa & LPA_RFAULT) {
> +			wx_err(txgbe->wx, "read pcs lpa error: %d\n", lpa);
> +			return;
> +		}
> +
> +		bmsr = pcs_read(txgbe, MDIO_MMD_VEND2, MII_BMSR);
> +		if (bmsr < 0) {
> +			wx_err(txgbe->wx, "read pcs lpa error: %d\n", bmsr);
> +			return;
> +		}
> +
> +		phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
> +	}

Don't we also want to read the status of the link when autoneg is
disabled? phylink_mii_c22_pcs_decode_state() will deal with this
for you if you just read the LPA and BMSR registers.

> +}
> +
> +static void txgbe_pcs_get_state(struct phylink_pcs *pcs,
> +				struct phylink_link_state *state)
> +{
> +	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
> +
> +	switch (state->interface) {
> +	case PHY_INTERFACE_MODE_10GBASER:
> +		txgbe_pcs_get_state_10gbr(txgbe, state);
> +		return;
> +	case PHY_INTERFACE_MODE_1000BASEX:
> +		txgbe_pcs_get_state_1000bx(txgbe, state);
> +		return;
> +	default:
> +		return;
> +	}
> +}
> +
> +static void txgbe_pcs_an_restart(struct phylink_pcs *pcs)
> +{
> +	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
> +	int ret;
> +
> +	ret = pcs_read(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1);
> +	if (ret >= 0) {
> +		ret |= BMCR_ANRESTART;
> +		pcs_write(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1, ret);
> +	}

We also have mdiodev_modify() which can be used to set BMCR_ANRESTART,
but you'd need pcs_modify() to wrap it... but I don't understand why
not just use the mdiodev_* accessors directly along with:

	struct mdio_device *mdiodev = txgbe->mdiodev;

at the start of the function? It's probably slightly more efficient
in terms of produced code.

> @@ -197,10 +537,15 @@ static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset)
>  	int val, dir;
>  
>  	dir = chip->get_direction(chip, offset);
> -	if (dir == GPIO_LINE_DIRECTION_IN)
> +	if (dir == GPIO_LINE_DIRECTION_IN) {
> +		struct txgbe *txgbe = (struct txgbe *)wx->priv;
> +
>  		val = rd32m(wx, WX_GPIO_EXT, BIT(offset));
> -	else
> +		txgbe->gpio_orig &= ~BIT(offset);
> +		txgbe->gpio_orig |= val;
> +	} else {
>  		val = rd32m(wx, WX_GPIO_DR, BIT(offset));
> +	}
>  
>  	return !!(val & BIT(offset));
>  }
> @@ -334,12 +679,19 @@ static void txgbe_irq_handler(struct irq_desc *desc)
>  	struct txgbe *txgbe = (struct txgbe *)wx->priv;
>  	struct gpio_chip *gc = txgbe->gpio;
>  	irq_hw_number_t hwirq;
> -	unsigned long val;
> +	unsigned long gpioirq;
> +	u32 gpio;
>  
>  	chained_irq_enter(chip, desc);
>  
> -	val = rd32(wx, WX_GPIO_INTSTATUS);
> -	for_each_set_bit(hwirq, &val, gc->ngpio)
> +	gpioirq = rd32(wx, WX_GPIO_INTSTATUS);
> +
> +	/* workaround for hysteretic gpio interrupts */
> +	gpio = rd32(wx, WX_GPIO_EXT);
> +	if (!gpioirq)
> +		gpioirq = txgbe->gpio_orig ^ gpio;
> +
> +	for_each_set_bit(hwirq, &gpioirq, gc->ngpio)
>  		generic_handle_domain_irq(gc->irq.domain, hwirq);
>  
>  	chained_irq_exit(chip, desc);
> @@ -358,6 +710,7 @@ static int txgbe_gpio_init(struct txgbe *txgbe)
>  	int ret;
>  
>  	pdev = wx->pdev;
> +	txgbe->gpio_orig = 0;

What has all this GPIO fiddling got to do with the addition of PCS
support? Shoudln't this be in a different patch?

>  
>  	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
>  	if (!gc)
> @@ -428,6 +781,12 @@ int txgbe_init_phy(struct txgbe *txgbe)
>  		return ret;
>  	}
>  
> +	ret = txgbe_mdio_pcs_init(txgbe);
> +	if (ret) {
> +		wx_err(txgbe->wx, "failed to init mdio pcs: %d\n", ret);
> +		goto err;
> +	}
> +
>  	ret = txgbe_i2c_adapter_add(txgbe);
>  	if (ret) {
>  		wx_err(txgbe->wx, "failed to init i2c interface: %d\n", ret);
> @@ -456,6 +815,8 @@ int txgbe_init_phy(struct txgbe *txgbe)
>  
>  void txgbe_remove_phy(struct txgbe *txgbe)
>  {
> +	if (txgbe->mdiodev)
> +		mdio_device_free(txgbe->mdiodev);

Wasn't the mdiodev allocated using a devm_* function? Won't this lead to
a double-free?

Thanks.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer
  2023-04-03 13:22   ` Andrew Lunn
@ 2023-04-03 13:51     ` Russell King (Oracle)
  0 siblings, 0 replies; 22+ messages in thread
From: Russell King (Oracle) @ 2023-04-03 13:51 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: Jiawen Wu, netdev, mengyuanlou

On Mon, Apr 03, 2023 at 03:22:03PM +0200, Andrew Lunn wrote:
> > +static void txgbe_mac_config(struct phylink_config *config, unsigned int mode,
> > +			     const struct phylink_link_state *state)
> > +{
> > +}
> 
> That is very likely to be wrong. Lets see what Russell says.

I don't think that's a problem if the MAC is just something that
pumps data into the PCS at whatever rate the PCS wants.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-03 12:52   ` Andrew Lunn
@ 2023-04-04  2:47     ` Jiawen Wu
  2023-04-04 14:18       ` Andrew Lunn
  0 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-04  2:47 UTC (permalink / raw)
  To: 'Andrew Lunn'; +Cc: netdev, linux, mengyuanlou

On Monday, April 3, 2023 8:53 PM, Andrew Lunn wrote:
> On Mon, Apr 03, 2023 at 02:45:24PM +0800, Jiawen Wu wrote:
> > I2C bus is integrated in Wangxun 10Gb ethernet chip. Implement I2C bus
> > driver to receive I2C messages.
> 
> Please Cc: the i2c mailing list for comments. They know more about I2C than
the
> netdev people.
> 
> Is the I2C bus master your own IP, or have you licensed a core? Or using the
open
> cores i2C bus master? I just want to make sure there is not already a linux
driver for
> this.
> 

I use the I2C core driver, and implement my own i2c_algorithm. I think it needs
to configure my registers to realize the function.

> > +static void txgbe_i2c_start(struct wx *wx, u16 dev_addr) {
> > +	wr32(wx, TXGBE_I2C_ENABLE, 0);
> > +
> > +	wr32(wx, TXGBE_I2C_CON,
> > +	     (TXGBE_I2C_CON_MASTER_MODE |
> > +	      TXGBE_I2C_CON_SPEED(1) |
> > +	      TXGBE_I2C_CON_RESTART_EN |
> > +	      TXGBE_I2C_CON_SLAVE_DISABLE));
> > +	/* Default addr is 0xA0 ,bit 0 is configure for read/write! */
> 
> A generic I2C bus master should not care about that address is being
read/write.
> For the SFP use case, 0xa0 will be used most of the time, plus 0xa2 for
diagnostics.
> But when the SFP contains a copper PHY, other addresses will be used as well.
> 

Yes, this comment is redundant. The address will be set to 'msg[0].addr'.

> > +static int txgbe_i2c_xfer(struct i2c_adapter *i2c_adap,
> > +			  struct i2c_msg *msg, int num_msgs) {
> > +	struct wx *wx = i2c_get_adapdata(i2c_adap);
> > +	u8 *dev_addr = msg[0].buf;
> > +	bool read = false;
> > +	int i, ret;
> > +	u8 *buf;
> > +	u16 len;
> > +
> > +	txgbe_i2c_start(wx, msg[0].addr);
> > +
> > +	for (i = 0; i < num_msgs; i++) {
> > +		if (msg[i].flags & I2C_M_RD) {
> > +			read = true;
> > +			len = msg[i].len;
> > +			buf = msg[i].buf;
> > +		}
> > +	}
> > +
> > +	if (!read) {
> > +		wx_err(wx, "I2C write not supported\n");
> > +		return num_msgs;
> > +	}
> 
> Write is not supported at all? Is this a hardware limitation?  I think
-EOPNOTSUPP
> is required here, and you need to ensure the code using the I2C bus master has
> quirks to not try to write.

It is supported. False testing leads to false perceptions, I'll fix it.

> 
> > +#define TXGBE_I2C_SLAVE_ADDR                    (0xA0 >> 1)
> > +#define TXGBE_I2C_EEPROM_DEV_ADDR               0xA0
> 
> These two do not appear to be used? I guess you took your hard coded SFP i2c
bus
> master and generalised it? Please clean up dead code like this.
> 
> 	Andrew
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-04  2:47     ` Jiawen Wu
@ 2023-04-04 14:18       ` Andrew Lunn
  2023-04-06  6:59         ` Jiawen Wu
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-04-04 14:18 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

On Tue, Apr 04, 2023 at 10:47:28AM +0800, Jiawen Wu wrote:
> On Monday, April 3, 2023 8:53 PM, Andrew Lunn wrote:
> > On Mon, Apr 03, 2023 at 02:45:24PM +0800, Jiawen Wu wrote:
> > > I2C bus is integrated in Wangxun 10Gb ethernet chip. Implement I2C bus
> > > driver to receive I2C messages.
> > 
> > Please Cc: the i2c mailing list for comments. They know more about I2C than
> the
> > netdev people.
> > 
> > Is the I2C bus master your own IP, or have you licensed a core? Or using the
> open
> > cores i2C bus master? I just want to make sure there is not already a linux
> driver for
> > this.
> > 
> 
> I use the I2C core driver, and implement my own i2c_algorithm. I think it needs
> to configure my registers to realize the function.

I had a quick look, and it appears the hardware is not an open-cores
derived I2C bus master.

As i tried to say, sometimes you just license an I2C bus master,
rather than develop one from scratch. And if it was a licensed IP
core, there is likely to be an existing driver.

> > > +	if (!read) {
> > > +		wx_err(wx, "I2C write not supported\n");
> > > +		return num_msgs;
> > > +	}
> > 
> > Write is not supported at all? Is this a hardware limitation?  I think
> -EOPNOTSUPP
> > is required here, and you need to ensure the code using the I2C bus master has
> > quirks to not try to write.
> 
> It is supported. False testing leads to false perceptions, I'll fix it.

Great. It would be odd not having write support.

	Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-04 14:18       ` Andrew Lunn
@ 2023-04-06  6:59         ` Jiawen Wu
  2023-04-07  2:03           ` Andrew Lunn
  0 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-06  6:59 UTC (permalink / raw)
  To: 'Andrew Lunn'; +Cc: netdev, linux, mengyuanlou

> > > Is the I2C bus master your own IP, or have you licensed a core?
> > > Or using the open cores i2C bus master? I just want to make sure
> > > there is not already a linux driver for this.
> > >
> >
> > I use the I2C core driver, and implement my own i2c_algorithm.
> > I think it needs to configure my registers to realize the function.
> 
> I had a quick look, and it appears the hardware is not an open-cores
> derived I2C bus master.
> 
> As i tried to say, sometimes you just license an I2C bus master,
> rather than develop one from scratch. And if it was a licensed IP
> core, there is likely to be an existing driver.
> 

By consulting with hardware designers, our I2C licensed the IP of
Synopsys.
But I can't use the I2C_DESIGNWARE_CORE directly, because IRQ is not
supported in our I2C.


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-06  6:59         ` Jiawen Wu
@ 2023-04-07  2:03           ` Andrew Lunn
  0 siblings, 0 replies; 22+ messages in thread
From: Andrew Lunn @ 2023-04-07  2:03 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, mengyuanlou

> By consulting with hardware designers, our I2C licensed the IP of
> Synopsys.
> But I can't use the I2C_DESIGNWARE_CORE directly, because IRQ is not
> supported in our I2C.

When you repost this patch and Cc: the i2c list, please point this
out. Please also Cc: the Designware I2C maintainers. They might have
some ideas how the driver can be used in polled mode.

Ideally, you want to use the existing driver, since it has hardware
work around, optimisations, and probably less bugs because it is used
and tested a lot.

And it is not too unusual for interrupt support to be missing. The
ocores driver can be used without interrupts. And there is sometimes a
polled mode supported so you can for example use the I2C bus to talk
to the power controller to turn the power at the end of shutdown when
nearly everything has stop working.

	Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next 5/6] net: txgbe: Implement phylink pcs
  2023-04-03 13:47   ` Russell King (Oracle)
@ 2023-04-10  7:32     ` Jiawen Wu
  2023-04-10 10:40       ` Russell King (Oracle)
  0 siblings, 1 reply; 22+ messages in thread
From: Jiawen Wu @ 2023-04-10  7:32 UTC (permalink / raw)
  To: 'Russell King (Oracle)'; +Cc: netdev, mengyuanlou

> > +				       struct phylink_link_state *state)
> > +{
> > +	int lpa, bmsr;
> > +
> > +	/* For C37 1000BASEX mode */
> > +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> > +			      state->advertising)) {
> > +		/* Reset link state */
> > +		state->link = false;
> > +
> > +		/* Poll for link jitter */
> > +		read_poll_timeout(pcs_read, lpa, lpa,
> > +				  100, 50000, false, txgbe,
> > +				  MDIO_MMD_VEND2, MII_LPA);
> 
> What jitter are you referring to? If the link is down (and thus this
> register reads zero), why do we have to spin here for 50ms each time?
> 

I found that when the last interrupt arrives, the link status is often
still down, but it will become up after a while. It is about 30ms on my
test.
I can't find a good way to solve this problem at the moment, so just
delay some time.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next 5/6] net: txgbe: Implement phylink pcs
  2023-04-10  7:32     ` Jiawen Wu
@ 2023-04-10 10:40       ` Russell King (Oracle)
  2023-04-11  2:34         ` Jiawen Wu
  2023-04-11  6:32         ` Jiawen Wu
  0 siblings, 2 replies; 22+ messages in thread
From: Russell King (Oracle) @ 2023-04-10 10:40 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, mengyuanlou

On Mon, Apr 10, 2023 at 03:32:12PM +0800, Jiawen Wu wrote:
> > > +				       struct phylink_link_state *state)
> > > +{
> > > +	int lpa, bmsr;
> > > +
> > > +	/* For C37 1000BASEX mode */
> > > +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> > > +			      state->advertising)) {
> > > +		/* Reset link state */
> > > +		state->link = false;
> > > +
> > > +		/* Poll for link jitter */
> > > +		read_poll_timeout(pcs_read, lpa, lpa,
> > > +				  100, 50000, false, txgbe,
> > > +				  MDIO_MMD_VEND2, MII_LPA);
> > 
> > What jitter are you referring to? If the link is down (and thus this
> > register reads zero), why do we have to spin here for 50ms each time?
> > 
> 
> I found that when the last interrupt arrives, the link status is often
> still down, but it will become up after a while. It is about 30ms on my
> test.

It is normal for the first read of the BMSR to report that the link is
down after it has come up. This is so that software can see if the link
has failed since it last read the BMSR. Phylink knows this, and will
re-request the link state via the pcs_get_state() callback
appropriately.

Is it reporting that the link is down after the second read of the
link status after the interrupt?

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next 5/6] net: txgbe: Implement phylink pcs
  2023-04-10 10:40       ` Russell King (Oracle)
@ 2023-04-11  2:34         ` Jiawen Wu
  2023-04-11  6:32         ` Jiawen Wu
  1 sibling, 0 replies; 22+ messages in thread
From: Jiawen Wu @ 2023-04-11  2:34 UTC (permalink / raw)
  To: 'Russell King (Oracle)'; +Cc: netdev, mengyuanlou

On Monday, April 10, 2023 6:41 PM, Russell King (Oracle) wrote:
> On Mon, Apr 10, 2023 at 03:32:12PM +0800, Jiawen Wu wrote:
> > > > +				       struct phylink_link_state
*state)
> > > > +{
> > > > +	int lpa, bmsr;
> > > > +
> > > > +	/* For C37 1000BASEX mode */
> > > > +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> > > > +			      state->advertising)) {
> > > > +		/* Reset link state */
> > > > +		state->link = false;
> > > > +
> > > > +		/* Poll for link jitter */
> > > > +		read_poll_timeout(pcs_read, lpa, lpa,
> > > > +				  100, 50000, false, txgbe,
> > > > +				  MDIO_MMD_VEND2, MII_LPA);
> > >
> > > What jitter are you referring to? If the link is down (and thus
this
> > > register reads zero), why do we have to spin here for 50ms each
time?
> > >
> >
> > I found that when the last interrupt arrives, the link status is
often
> > still down, but it will become up after a while. It is about 30ms on
my
> > test.
> 
> It is normal for the first read of the BMSR to report that the link is
> down after it has come up. This is so that software can see if the
link
> has failed since it last read the BMSR. Phylink knows this, and will
> re-request the link state via the pcs_get_state() callback
> appropriately.
> 
> Is it reporting that the link is down after the second read of the
> link status after the interrupt?
> 
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
> 

The link status bit of bmsr will report 1 after the second read. But
auto-negotiation looks like it will take some time to complete.
In my test case, 10000baseSR/1000baseX supported SFP on enp4s0f0, and
1000baseX supported SFP on enp4s0f1, to connect each other by optical
fiber.
Here is the log from probe the module and then change the speed with
enp4s0f0.

root@roy-System-Product-Name:~/net-next# modprobe txgbe
root@roy-System-Product-Name:~/net-next# dmesg -c
[63894.334069] txgbe 0000:04:00.0: 16.000 Gb/s available PCIe bandwidth,
limited by 5.0 GT/s PCIe x4 link at 0000:02:02.0 (capable of 32.000 Gb/s
with 5.0 GT/s PCIe x8 link)
[63894.337488] txgbe 0000:04:00.0 enp4s0f0: renamed from eth0
[63894.338181] sfp sfp.1024: Host maximum power 1.0W
[63894.351172] txgbe 0000:04:00.0 enp4s0f0: 30:09:f9:21:b9:5b
[63894.407234] txgbe 0000:04:00.0 enp4s0f0: configuring for
inband/10gbase-r link mode
[63894.889942] txgbe 0000:04:00.1: 16.000 Gb/s available PCIe bandwidth,
limited by 5.0 GT/s PCIe x4 link at 0000:02:02.0 (capable of 32.000 Gb/s
with 5.0 GT/s PCIe x8 link)
[63894.891155] txgbe 0000:04:00.1 enp4s0f1: renamed from eth0
[63894.906564] txgbe 0000:04:00.1 enp4s0f1: 30:09:f9:21:b9:5c
[63894.957750] txgbe 0000:04:00.1 enp4s0f1: configuring for
inband/10gbase-r link mode
[63895.208990] txgbe 0000:04:00.1 enp4s0f1: switched to
inband/1000base-x link mode
[63895.361265] txgbe 0000:04:00.1: [1] lpa=0x0, bmsr=0x189
[63895.361274] txgbe 0000:04:00.1: [2] lpa=0x0, bmsr=0x189
[63895.361282] txgbe 0000:04:00.1: [1] lpa=0x0, bmsr=0x189
[63895.361290] txgbe 0000:04:00.1: [2] lpa=0x0, bmsr=0x189
root@roy-System-Product-Name:~/net-next# ethtool -s enp4s0f0 advertise
0x20000000000
root@roy-System-Product-Name:~/net-next# dmesg -c
[63927.627708] txgbe 0000:04:00.1: [1] lpa=0x0, bmsr=0x189
[63927.627723] txgbe 0000:04:00.1: [2] lpa=0x0, bmsr=0x18d
[63927.627731] txgbe 0000:04:00.1: [1] lpa=0x0, bmsr=0x18d
[63927.627738] txgbe 0000:04:00.1: [2] lpa=0x0, bmsr=0x18d
[63927.647128] txgbe 0000:04:00.0: [1] lpa=0x0, bmsr=0x189
[63927.647144] txgbe 0000:04:00.0: [1] lpa=0x0, bmsr=0x18d
[63927.647148] txgbe 0000:04:00.0: [2] lpa=0x0, bmsr=0x18d
[63927.647154] txgbe 0000:04:00.0: [2] lpa=0x0, bmsr=0x18d
[63927.647162] txgbe 0000:04:00.0: [1] lpa=0x0, bmsr=0x18d
[63927.647168] txgbe 0000:04:00.0: [1] lpa=0x0, bmsr=0x18d
[63927.647172] txgbe 0000:04:00.0: [2] lpa=0x0, bmsr=0x18d
[63927.647177] txgbe 0000:04:00.0: [2] lpa=0x0, bmsr=0x18d
root@roy-System-Product-Name:~/net-next# ethtool enp4s0f0
Settings for enp4s0f0:
        Supported ports: [ FIBRE ]
        Supported link modes:   1000baseX/Full
                                10000baseSR/Full
        Supported pause frame use: Symmetric Receive-only
        Supports auto-negotiation: Yes
        Supported FEC modes: Not reported
        Advertised link modes:  1000baseX/Full
        Advertised pause frame use: No
        Advertised auto-negotiation: Yes
        Advertised FEC modes: Not reported
        Link partner advertised link modes:  1000baseX/Full
        Link partner advertised pause frame use: Symmetric Receive-only
        Link partner advertised auto-negotiation: Yes
        Link partner advertised FEC modes: Not reported
        Speed: 1000Mb/s
        Duplex: Full
        Port: FIBRE
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: on
        Link detected: no
root@roy-System-Product-Name:~/net-next# dmesg -c
[63980.779785] txgbe 0000:04:00.0: [1] lpa=0x41a0, bmsr=0x1ad
[63980.779801] txgbe 0000:04:00.0: [2] lpa=0x41a0, bmsr=0x1ad

PS: "[1]" print for the first read, and "[2]" for the second.

By the way, I can't use the command "ethtool -s enp4s0f0 speed 1000
duplex full autoneg on" to set link, and the log shows "Cannot set new
settings: Invalid argument".




^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next 5/6] net: txgbe: Implement phylink pcs
  2023-04-10 10:40       ` Russell King (Oracle)
  2023-04-11  2:34         ` Jiawen Wu
@ 2023-04-11  6:32         ` Jiawen Wu
  1 sibling, 0 replies; 22+ messages in thread
From: Jiawen Wu @ 2023-04-11  6:32 UTC (permalink / raw)
  To: 'Russell King (Oracle)'; +Cc: netdev, mengyuanlou

> It is normal for the first read of the BMSR to report that the link is
> down after it has come up. This is so that software can see if the
link
> has failed since it last read the BMSR. Phylink knows this, and will
> re-request the link state via the pcs_get_state() callback
> appropriately.
> 
> Is it reporting that the link is down after the second read of the
> link status after the interrupt?
> 
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
> 

I find out where the problem is.
I should to enable the AN interrupt, and call phylink_mac_change() when
interrupt happend.
Thanks for the tips!!


^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2023-04-11  6:33 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-03  6:45 [PATCH net-next 0/6] TXGBE PHYLINK support Jiawen Wu
2023-04-03  6:45 ` [PATCH net-next 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
2023-04-03 12:35   ` Andrew Lunn
2023-04-03  6:45 ` [PATCH net-next 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
2023-04-03 12:52   ` Andrew Lunn
2023-04-04  2:47     ` Jiawen Wu
2023-04-04 14:18       ` Andrew Lunn
2023-04-06  6:59         ` Jiawen Wu
2023-04-07  2:03           ` Andrew Lunn
2023-04-03  6:45 ` [PATCH net-next 3/6] net: txgbe: Add SFP module identify Jiawen Wu
2023-04-03 12:58   ` Andrew Lunn
2023-04-03  6:45 ` [PATCH net-next 4/6] net: txgbe: Support GPIO to SFP socket Jiawen Wu
2023-04-03 13:16   ` Andrew Lunn
2023-04-03  6:45 ` [PATCH net-next 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
2023-04-03 13:47   ` Russell King (Oracle)
2023-04-10  7:32     ` Jiawen Wu
2023-04-10 10:40       ` Russell King (Oracle)
2023-04-11  2:34         ` Jiawen Wu
2023-04-11  6:32         ` Jiawen Wu
2023-04-03  6:45 ` [PATCH net-next 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
2023-04-03 13:22   ` Andrew Lunn
2023-04-03 13:51     ` Russell King (Oracle)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.