linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/6] TXGBE PHYLINK support
@ 2023-04-11  9:27 Jiawen Wu
  2023-04-11  9:27 ` [PATCH net-next v2 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
                   ` (5 more replies)
  0 siblings, 6 replies; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:27 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, mengyuanlou, Jiawen Wu

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

v1 -> v2:
- add comments to indicate GPIO lines
- add I2C write operation support
- modify GPIO direction functions
- rename functions related to PHY interface
- add condition on interface changing to re-config PCS
- add to set advertise and fix to get status for 1000BASE-X mode
- other redundant codes remove

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

 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 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  58 +-
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 980 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_phy.h    |  10 +
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   | 157 +++
 9 files changed, 1219 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] 21+ messages in thread

* [PATCH net-next v2 1/6] net: txgbe: Add software nodes to support phylink
  2023-04-11  9:27 [PATCH net-next v2 0/6] TXGBE PHYLINK support Jiawen Wu
@ 2023-04-11  9:27 ` Jiawen Wu
  2023-04-11 12:51   ` Andrew Lunn
  2023-04-11  9:27 ` [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:27 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, 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    | 86 +++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_phy.h    | 10 +++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   | 42 +++++++++
 6 files changed, 158 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 32f952d93009..97bce855bc60 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 5b8a121fb496..5a5e7620e9f8 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";
@@ -513,6 +514,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;
@@ -663,10 +665,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);
@@ -694,6 +707,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);
@@ -724,6 +739,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..86d5e0647d5e
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -0,0 +1,86 @@
+// 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;
+
+	/* GPIO 0: tx fault
+	 * GPIO 1: tx disable
+	 * GPIO 2: sfp module absent
+	 * GPIO 3: rx signal lost
+	 * GPIO 4: rate select, 1G(0) 10G(1)
+	 * GPIO 5: rate select, 1G(0) 10G(1)
+	 */
+	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] 21+ messages in thread

* [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-11  9:27 [PATCH net-next v2 0/6] TXGBE PHYLINK support Jiawen Wu
  2023-04-11  9:27 ` [PATCH net-next v2 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
@ 2023-04-11  9:27 ` Jiawen Wu
  2023-04-11  9:32   ` Jiawen Wu
  2023-04-11  9:27 ` [PATCH net-next v2 3/6] net: txgbe: Add SFP module identify Jiawen Wu
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:27 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, mengyuanlou, Jiawen Wu

Implement I2C bus driver to send and receive I2C messages.

This I2C license the IP of Synopsys Designware, but without interrupt
support on the hardware design. It seems that polling mode needs to be
added in Synopsys Designware I2C driver. But currently it can only be
driven by this I2C bus master driver.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/ethernet/wangxun/Kconfig          |   1 +
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 153 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  23 +++
 3 files changed, 177 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 86d5e0647d5e..2721da1625e0 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"
 
@@ -67,6 +70,142 @@ 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));
+	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_i2c_poll_intr(struct wx *wx, u16 intr)
+{
+	u16 val;
+
+	return read_poll_timeout(rd32, val, (val & intr) == intr,
+				 100, 1000, false, wx,
+				 TXGBE_I2C_RAW_INTR_STAT);
+}
+
+static int txgbe_read_i2c_bytes(struct wx *wx, u8 dev_addr, struct i2c_msg *msg)
+{
+	int err, i;
+
+	txgbe_i2c_start(wx, msg->addr);
+
+	for (i = 0; i < msg->len; i++) {
+		/* wait tx empty */
+		err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_TEMP);
+		if (err)
+			return err;
+
+		/* read data */
+		wr32(wx, TXGBE_I2C_DATA_CMD,
+		     (dev_addr + i) | TXGBE_I2C_DATA_CMD_STOP);
+		wr32(wx, TXGBE_I2C_DATA_CMD,
+		     TXGBE_I2C_DATA_CMD_READ | TXGBE_I2C_DATA_CMD_STOP);
+
+		/* wait for read complete */
+		err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_RFUL);
+		if (err)
+			return err;
+
+		msg->buf[i] = 0xFF & rd32(wx, TXGBE_I2C_DATA_CMD);
+	}
+
+	return 0;
+}
+
+static int txgbe_write_i2c_bytes(struct wx *wx, struct i2c_msg *msg)
+{
+	int err, i;
+
+	txgbe_i2c_start(wx, msg->addr);
+
+	for (i = 0; i < msg->len; i++) {
+		/* wait tx empty */
+		err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_TEMP);
+		if (err)
+			return err;
+
+		/* write data */
+		wr32(wx, TXGBE_I2C_DATA_CMD, msg->buf[i]);
+		if (i == (msg->len - 1))
+			wr32(wx, TXGBE_I2C_DATA_CMD, TXGBE_I2C_DATA_CMD_STOP);
+	}
+
+	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[0];
+	int i, ret;
+
+	for (i = 0; i < num_msgs; i++) {
+		if (msg[i].flags & I2C_M_RD)
+			ret = txgbe_read_i2c_bytes(wx, dev_addr, &msg[i]);
+		else
+			ret = txgbe_write_i2c_bytes(wx, &msg[i]);
+
+		if (ret)
+			return ret;
+	}
+
+	return num_msgs;
+}
+
+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;
@@ -77,10 +216,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..6c02af196157 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -55,6 +55,28 @@
 #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)
+#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
+
 /* Part Number String Length */
 #define TXGBE_PBANUM_LENGTH                     32
 
@@ -139,6 +161,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] 21+ messages in thread

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

Register SFP platform device to get modules information.

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

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 2721da1625e0..dd5ecfad56c1 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>
@@ -206,6 +207,25 @@ 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;
+
+	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;
@@ -222,6 +242,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:
@@ -232,6 +258,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 6c02af196157..a7d6f47cbe05 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -162,6 +162,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] 21+ messages in thread

* [PATCH net-next v2 4/6] net: txgbe: Support GPIO to SFP socket
  2023-04-11  9:27 [PATCH net-next v2 0/6] TXGBE PHYLINK support Jiawen Wu
                   ` (2 preceding siblings ...)
  2023-04-11  9:27 ` [PATCH net-next v2 3/6] net: txgbe: Add SFP module identify Jiawen Wu
@ 2023-04-11  9:27 ` Jiawen Wu
  2023-04-11  9:27 ` [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
  2023-04-11  9:27 ` [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
  5 siblings, 0 replies; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:27 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, 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 +
 .../net/ethernet/wangxun/txgbe/txgbe_main.c   |  20 +-
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 227 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  27 +++
 6 files changed, 262 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 97bce855bc60..d151d6f79022 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_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 5a5e7620e9f8..d8108ab30818 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -82,6 +82,8 @@ static int txgbe_enumerate_functions(struct wx *wx)
  **/
 static void txgbe_irq_enable(struct wx *wx, bool queues)
 {
+	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 +131,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 +162,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 dd5ecfad56c1..42e66db6e9ff 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>
@@ -207,6 +210,224 @@ 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);
+	struct txgbe *txgbe;
+	int val;
+
+	val = rd32m(wx, WX_GPIO_EXT, BIT(offset));
+
+	txgbe = (struct txgbe *)wx->priv;
+	txgbe->gpio_orig &= ~BIT(offset);
+	txgbe->gpio_orig |= val;
+
+	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)
+{
+	struct wx *wx = gpiochip_get_data(chip);
+
+	wr32m(wx, WX_GPIO_DDR, BIT(offset), 0);
+
+	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;
+
+	mask = BIT(offset) | BIT(offset - 1);
+	if (val)
+		wr32m(wx, WX_GPIO_DR, mask, mask);
+	else
+		wr32m(wx, WX_GPIO_DR, mask, 0);
+
+	wr32m(wx, WX_GPIO_DDR, BIT(offset), BIT(offset));
+
+	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);
+
+	wr32m(wx, WX_GPIO_INTEN, BIT(hwirq), BIT(hwirq));
+	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 gpioirq;
+	u32 gpio;
+
+	chained_irq_enter(chip, desc);
+
+	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);
+
+	/* 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;
+	txgbe->gpio_orig = 0;
+
+	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;
@@ -242,6 +463,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 a7d6f47cbe05..58b0054ae59c 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)
@@ -162,7 +187,9 @@ struct txgbe {
 	struct wx *wx;
 	struct txgbe_nodes nodes;
 	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] 21+ messages in thread

* [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs
  2023-04-11  9:27 [PATCH net-next v2 0/6] TXGBE PHYLINK support Jiawen Wu
                   ` (3 preceding siblings ...)
  2023-04-11  9:27 ` [PATCH net-next v2 4/6] net: txgbe: Support GPIO to SFP socket Jiawen Wu
@ 2023-04-11  9:27 ` Jiawen Wu
  2023-04-11 10:36   ` Russell King (Oracle)
                     ` (2 more replies)
  2023-04-11  9:27 ` [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
  5 siblings, 3 replies; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:27 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, 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    | 377 ++++++++++++++++++
 .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  59 +++
 3 files changed, 437 insertions(+)

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 42e66db6e9ff..123fa7ed9039 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>
 
@@ -74,6 +76,375 @@ 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 int txgbe_pcs_validate(struct phylink_pcs *pcs,
+			      unsigned long *supported,
+			      const struct phylink_link_state *state)
+{
+	/* When in 802.3z mode, we must have AN enabled */
+	if (phy_interface_mode_is_8023z(state->interface) &&
+	    !phylink_test(state->advertising, Autoneg))
+		return -EINVAL;
+
+	return 0;
+}
+
+static void txgbe_pma_config_10gbaser(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_1000basex(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_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(0) |
+		  TXGBE_MII_AN_CTRL_INTR_EN);
+	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 void txgbe_setup_adv(struct txgbe *txgbe, phy_interface_t interface,
+			    const unsigned long *advertising)
+{
+	int adv;
+
+	adv = phylink_mii_c22_pcs_encode_advertisement(interface,
+						       advertising);
+	if (adv > 0)
+		mdiodev_c45_modify(txgbe->mdiodev, MDIO_MMD_VEND2, MII_ADVERTISE,
+				   0xffff, adv);
+}
+
+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;
+
+	if (interface == txgbe->interface)
+		goto out;
+
+	/* 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_10gbaser(txgbe);
+		break;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		txgbe_pma_config_1000basex(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");
+
+	txgbe->interface = interface;
+
+out:
+	if (interface == PHY_INTERFACE_MODE_1000BASEX) {
+		txgbe_setup_adv(txgbe, interface, advertising);
+		txgbe_set_an37_ability(txgbe);
+	}
+
+	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, an_intr;
+
+	/* Reset link state */
+	state->link = false;
+
+	lpa = pcs_read(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;
+	}
+
+	/* Clear AN complete interrupt */
+	an_intr = pcs_read(txgbe, MDIO_MMD_VEND2, TXGBE_MII_AN_INTR);
+	if (an_intr & TXGBE_MII_AN_INTR_CL37_CMPLT) {
+		an_intr &= ~TXGBE_MII_AN_INTR_CL37_CMPLT;
+		pcs_write(txgbe, MDIO_MMD_VEND2, TXGBE_MII_AN_INTR, an_intr);
+	}
+
+	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);
+
+	mdiodev_c45_modify(txgbe->mdiodev, MDIO_MMD_VEND2, MDIO_CTRL1,
+			   BMCR_ANRESTART, BMCR_ANRESTART);
+}
+
+static const struct phylink_pcs_ops txgbe_pcs_ops = {
+	.pcs_validate = txgbe_pcs_validate,
+	.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);
@@ -457,6 +828,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);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 58b0054ae59c..d83225b4e34e 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 */
@@ -102,6 +105,59 @@
 #define TXGBE_I2C_SCL_STUCK_TIMEOUT             0x149AC
 #define TXGBE_I2C_SDA_STUCK_TIMEOUT             0x149B0
 
+/************************************** 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)
+#define TXGBE_MII_AN_CTRL_INTR_EN               BIT(0)
+#define TXGBE_MII_AN_INTR                       0x8002
+#define TXGBE_MII_AN_INTR_CL37_CMPLT            BIT(0)
+/* 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
 
@@ -186,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;
+	phy_interface_t interface;
 	u32 gpio_orig;
 };
 
-- 
2.27.0


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

* [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer
  2023-04-11  9:27 [PATCH net-next v2 0/6] TXGBE PHYLINK support Jiawen Wu
                   ` (4 preceding siblings ...)
  2023-04-11  9:27 ` [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
@ 2023-04-11  9:27 ` Jiawen Wu
  2023-04-11 10:44   ` Russell King (Oracle)
  5 siblings, 1 reply; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:27 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, 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   |  19 ++-
 .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 111 +++++++++++++++++-
 .../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 d8108ab30818..f640ff1a084e 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>
 
@@ -204,7 +205,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);
@@ -213,24 +214,16 @@ 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(0));
 	rd32(wx, WX_PX_IC(1));
 	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)
@@ -264,7 +257,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);
@@ -295,8 +287,11 @@ 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);
+	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 123fa7ed9039..84dc3e850036 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"
@@ -445,6 +446,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);
@@ -728,7 +821,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);
 
@@ -744,6 +839,12 @@ 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 |
+		    TXGBE_PX_MISC_ETH_AN)) {
+		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));
@@ -834,6 +935,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);
@@ -862,6 +969,8 @@ int txgbe_init_phy(struct txgbe *txgbe)
 
 void txgbe_remove_phy(struct txgbe *txgbe)
 {
+	if (txgbe->phylink)
+		phylink_destroy(txgbe->phylink);
 	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 d83225b4e34e..45fb887aaf6d 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] 21+ messages in thread

* RE: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-11  9:27 ` [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
@ 2023-04-11  9:32   ` Jiawen Wu
  2023-04-11 10:08     ` Jarkko Nikula
  0 siblings, 1 reply; 21+ messages in thread
From: Jiawen Wu @ 2023-04-11  9:32 UTC (permalink / raw)
  To: netdev, linux; +Cc: linux-i2c, linux-gpio, mengyuanlou, jarkko.nikula

+Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>

> -----Original Message-----
> From: Jiawen Wu <jiawenwu@trustnetic.com>
> Sent: Tuesday, April 11, 2023 5:27 PM
> To: netdev@vger.kernel.org; linux@armlinux.org.uk
> Cc: linux-i2c@vger.kernel.org; linux-gpio@vger.kernel.org;
> mengyuanlou@net-swift.com; Jiawen Wu <jiawenwu@trustnetic.com>
> Subject: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master
> driver
> 
> Implement I2C bus driver to send and receive I2C messages.
> 
> This I2C license the IP of Synopsys Designware, but without interrupt
> support on the hardware design. It seems that polling mode needs to be
> added in Synopsys Designware I2C driver. But currently it can only be
> driven by this I2C bus master driver.
> 
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> ---
>  drivers/net/ethernet/wangxun/Kconfig          |   1 +
>  .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 153
> ++++++++++++++++++
>  .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  23 +++
>  3 files changed, 177 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 86d5e0647d5e..2721da1625e0 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"
> 
> @@ -67,6 +70,142 @@ 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));
> +	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_i2c_poll_intr(struct wx *wx, u16 intr)
> +{
> +	u16 val;
> +
> +	return read_poll_timeout(rd32, val, (val & intr) == intr,
> +				 100, 1000, false, wx,
> +				 TXGBE_I2C_RAW_INTR_STAT);
> +}
> +
> +static int txgbe_read_i2c_bytes(struct wx *wx, u8 dev_addr, struct
> i2c_msg *msg)
> +{
> +	int err, i;
> +
> +	txgbe_i2c_start(wx, msg->addr);
> +
> +	for (i = 0; i < msg->len; i++) {
> +		/* wait tx empty */
> +		err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_TEMP);
> +		if (err)
> +			return err;
> +
> +		/* read data */
> +		wr32(wx, TXGBE_I2C_DATA_CMD,
> +		     (dev_addr + i) | TXGBE_I2C_DATA_CMD_STOP);
> +		wr32(wx, TXGBE_I2C_DATA_CMD,
> +		     TXGBE_I2C_DATA_CMD_READ |
> TXGBE_I2C_DATA_CMD_STOP);
> +
> +		/* wait for read complete */
> +		err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_RFUL);
> +		if (err)
> +			return err;
> +
> +		msg->buf[i] = 0xFF & rd32(wx, TXGBE_I2C_DATA_CMD);
> +	}
> +
> +	return 0;
> +}
> +
> +static int txgbe_write_i2c_bytes(struct wx *wx, struct i2c_msg *msg)
> +{
> +	int err, i;
> +
> +	txgbe_i2c_start(wx, msg->addr);
> +
> +	for (i = 0; i < msg->len; i++) {
> +		/* wait tx empty */
> +		err = txgbe_i2c_poll_intr(wx, TXGBE_I2C_INTR_STAT_TEMP);
> +		if (err)
> +			return err;
> +
> +		/* write data */
> +		wr32(wx, TXGBE_I2C_DATA_CMD, msg->buf[i]);
> +		if (i == (msg->len - 1))
> +			wr32(wx, TXGBE_I2C_DATA_CMD,
> TXGBE_I2C_DATA_CMD_STOP);
> +	}
> +
> +	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[0];
> +	int i, ret;
> +
> +	for (i = 0; i < num_msgs; i++) {
> +		if (msg[i].flags & I2C_M_RD)
> +			ret = txgbe_read_i2c_bytes(wx, dev_addr,
&msg[i]);
> +		else
> +			ret = txgbe_write_i2c_bytes(wx, &msg[i]);
> +
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return num_msgs;
> +}
> +
> +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;
> @@ -77,10 +216,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..6c02af196157 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
> @@ -55,6 +55,28 @@
>  #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)
> +#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
> +
>  /* Part Number String Length */
>  #define TXGBE_PBANUM_LENGTH                     32
> 
> @@ -139,6 +161,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	[flat|nested] 21+ messages in thread

* Re: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-11  9:32   ` Jiawen Wu
@ 2023-04-11 10:08     ` Jarkko Nikula
  2023-04-13 16:28       ` Wolfram Sang
  0 siblings, 1 reply; 21+ messages in thread
From: Jarkko Nikula @ 2023-04-11 10:08 UTC (permalink / raw)
  To: Jiawen Wu, netdev, linux; +Cc: linux-i2c, linux-gpio, mengyuanlou

Hi

On 4/11/23 12:32, Jiawen Wu wrote:
> +Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> 
>> -----Original Message-----
>> From: Jiawen Wu <jiawenwu@trustnetic.com>
>> Sent: Tuesday, April 11, 2023 5:27 PM
>> To: netdev@vger.kernel.org; linux@armlinux.org.uk
>> Cc: linux-i2c@vger.kernel.org; linux-gpio@vger.kernel.org;
>> mengyuanlou@net-swift.com; Jiawen Wu <jiawenwu@trustnetic.com>
>> Subject: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master
>> driver
>>
>> Implement I2C bus driver to send and receive I2C messages.
>>
>> This I2C license the IP of Synopsys Designware, but without interrupt
>> support on the hardware design. It seems that polling mode needs to be
>> added in Synopsys Designware I2C driver. But currently it can only be
>> driven by this I2C bus master driver.
>>
>> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
>> ---
>>   drivers/net/ethernet/wangxun/Kconfig          |   1 +
>>   .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 153
>> ++++++++++++++++++
>>   .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  23 +++
>>   3 files changed, 177 insertions(+)
>>
Looks like your use case has similarities with the commit 17631e8ca2d3 
("i2c: designware: Add driver support for AMD NAVI GPU").

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

* Re: [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs
  2023-04-11  9:27 ` [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
@ 2023-04-11 10:36   ` Russell King (Oracle)
  2023-04-11 11:03   ` Simon Horman
  2023-04-17 14:47   ` kernel test robot
  2 siblings, 0 replies; 21+ messages in thread
From: Russell King (Oracle) @ 2023-04-11 10:36 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux-i2c, linux-gpio, mengyuanlou

On Tue, Apr 11, 2023 at 05:27:24PM +0800, Jiawen Wu wrote:
> +static void txgbe_set_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(0) |
> +               TXGBE_MII_AN_CTRL_INTR_EN);
> +     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;

	val |= BMCR_ANENABLE;

> +     pcs_write(txgbe, MDIO_MMD_VEND2, MDIO_CTRL1, val);
> +}

> +static void txgbe_setup_adv(struct txgbe *txgbe, phy_interface_t interface,
> +			    const unsigned long *advertising)

Please return an int.

> +{
> +	int adv;
> +
> +	adv = phylink_mii_c22_pcs_encode_advertisement(interface,
> +						       advertising);

	if (adv < 0)
		return adv;

> +	if (adv > 0)
> +		mdiodev_c45_modify(txgbe->mdiodev, MDIO_MMD_VEND2, MII_ADVERTISE,
> +				   0xffff, adv);

	return mdiodev_c45_modify_changed(txgbe->mdiodev, MDIO_MMD_VEND2,
					  MII_ADVERTISE, 0xffff, adv);

> +}
> +
> +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;
> +
> +	if (interface == txgbe->interface)
> +		goto out;
> +
> +	/* 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_10gbaser(txgbe);
> +		break;
> +	case PHY_INTERFACE_MODE_1000BASEX:
> +		txgbe_pma_config_1000basex(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");
> +
> +	txgbe->interface = interface;
> +
> +out:
> +	if (interface == PHY_INTERFACE_MODE_1000BASEX) {
> +		txgbe_setup_adv(txgbe, interface, advertising);

		ret = txgbe_setup_adv(txgbe, interface, advertising);
		if (ret < 0)
			return ret;

> +		txgbe_set_an37_ability(txgbe);
> +	}
> +
> +	return ret;

... and then this will propagate whether the advertisement has changed,
which will then cause...

> +static void txgbe_pcs_an_restart(struct phylink_pcs *pcs)
> +{
> +	struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
> +
> +	mdiodev_c45_modify(txgbe->mdiodev, MDIO_MMD_VEND2, MDIO_CTRL1,
> +			   BMCR_ANRESTART, BMCR_ANRESTART);
> +}

to be called whenever the advertisement changes (which is why you
then don't need to do it in txgbe_set_an37_ability().)

-- 
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] 21+ messages in thread

* Re: [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer
  2023-04-11  9:27 ` [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
@ 2023-04-11 10:44   ` Russell King (Oracle)
  2023-04-11 12:43     ` Andrew Lunn
  0 siblings, 1 reply; 21+ messages in thread
From: Russell King (Oracle) @ 2023-04-11 10:44 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux-i2c, linux-gpio, mengyuanlou

On Tue, Apr 11, 2023 at 05:27:25PM +0800, Jiawen Wu wrote:
> 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   |  19 ++-
>  .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 111 +++++++++++++++++-
>  .../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"

I wonder if a small helper would be useful in txgbe_type.h:

static inline struct txgbe *netdev_to_txgbe(struct net_device *netdev)
{
	struct wx *wx = netdev_priv(netdev);

	return wx->priv;
}

> +static int txgbe_nway_reset(struct net_device *netdev)
> +{
> +	struct wx *wx = netdev_priv(netdev);
> +	struct txgbe *txgbe;
> +
> +	txgbe = (struct txgbe *)wx->priv;

Then all of these can be simply:

	struct txgbe *txgbe = netdev_to_txgbe(netdev);

> +	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 d8108ab30818..f640ff1a084e 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>
>  
> @@ -204,7 +205,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;

Personal choice I guess, but normally we tend to rely on compilers
accepting the implicit cast from void * to whatever struct pointer
in the kernel.

>  
>  	wx_control_hw(wx, true);
>  	wx_configure_vectors(wx);
> @@ -213,24 +214,16 @@ 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(0));
>  	rd32(wx, WX_PX_IC(1));
>  	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)
> @@ -264,7 +257,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);
> @@ -295,8 +287,11 @@ 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);
> +	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 123fa7ed9039..84dc3e850036 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"
> @@ -445,6 +446,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;

	struct txgbr *txgbe = netdev_to_txgbe(to_net_dev(config->dev));

> +
> +	return &txgbe->pcs;
> +}
> +

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] 21+ messages in thread

* Re: [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs
  2023-04-11  9:27 ` [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
  2023-04-11 10:36   ` Russell King (Oracle)
@ 2023-04-11 11:03   ` Simon Horman
  2023-04-17 14:47   ` kernel test robot
  2 siblings, 0 replies; 21+ messages in thread
From: Simon Horman @ 2023-04-11 11:03 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, linux-i2c, linux-gpio, mengyuanlou

On Tue, Apr 11, 2023 at 05:27:24PM +0800, Jiawen Wu wrote:
> Register MDIO bus for PCS layer, support 10GBASE-R and 1000BASE-X
> interfaces to the controller.

...

> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c

...

> +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;
> +
> +	if (interface == txgbe->interface)
> +		goto out;

Hi Jiawen,

The out label returns 'ret', but it is not initialised here.

Reported by clang-16 as:

drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:270:6: error: variable 'ret' is used uninitialized whenever 'if' condition is true [-Werror,-Wsometimes-uninitialized]
        if (interface == txgbe->interface)
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:326:9: note: uninitialized use occurs here
        return ret;
               ^~~
drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:270:2: note: remove the 'if' if its condition is always false
        if (interface == txgbe->interface)
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:268:9: note: initialize the variable 'ret' to silence this warning
        int ret, val;
               ^
                = 0
1 error generated.

...

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

* Re: [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer
  2023-04-11 10:44   ` Russell King (Oracle)
@ 2023-04-11 12:43     ` Andrew Lunn
  0 siblings, 0 replies; 21+ messages in thread
From: Andrew Lunn @ 2023-04-11 12:43 UTC (permalink / raw)
  To: Russell King (Oracle)
  Cc: Jiawen Wu, netdev, linux-i2c, linux-gpio, mengyuanlou

> > @@ -204,7 +205,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;
> 
> Personal choice I guess, but normally we tend to rely on compilers
> accepting the implicit cast from void * to whatever struct pointer
> in the kernel.

Davem used to strongly push back against a cast when the pointer was a
void *. Please remove the cast.

     Andrew

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

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

On Tue, Apr 11, 2023 at 05:27:20PM +0800, Jiawen Wu wrote:
> Register software nodes for GPIO, I2C, SFP and PHYLINK. Define the
> device properties.
> 
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

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

* Re: [PATCH net-next v2 3/6] net: txgbe: Add SFP module identify
  2023-04-11  9:27 ` [PATCH net-next v2 3/6] net: txgbe: Add SFP module identify Jiawen Wu
@ 2023-04-11 12:52   ` Andrew Lunn
  0 siblings, 0 replies; 21+ messages in thread
From: Andrew Lunn @ 2023-04-11 12:52 UTC (permalink / raw)
  To: Jiawen Wu; +Cc: netdev, linux, linux-i2c, linux-gpio, mengyuanlou

On Tue, Apr 11, 2023 at 05:27:22PM +0800, Jiawen Wu wrote:
> Register SFP platform device to get modules information.
> 
> Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

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

* Re: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-11 10:08     ` Jarkko Nikula
@ 2023-04-13 16:28       ` Wolfram Sang
  2023-04-14 11:04         ` Jiawen Wu
  0 siblings, 1 reply; 21+ messages in thread
From: Wolfram Sang @ 2023-04-13 16:28 UTC (permalink / raw)
  To: Jarkko Nikula
  Cc: Jiawen Wu, netdev, linux, linux-i2c, linux-gpio, mengyuanlou

[-- Attachment #1: Type: text/plain, Size: 916 bytes --]


> > > Implement I2C bus driver to send and receive I2C messages.
> > > 
> > > This I2C license the IP of Synopsys Designware, but without interrupt
> > > support on the hardware design. It seems that polling mode needs to be
> > > added in Synopsys Designware I2C driver. But currently it can only be
> > > driven by this I2C bus master driver.
> > > 
> > > Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> > > ---
> > >   drivers/net/ethernet/wangxun/Kconfig          |   1 +
> > >   .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 153
> > > ++++++++++++++++++
> > >   .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  23 +++
> > >   3 files changed, 177 insertions(+)
> > > 
> Looks like your use case has similarities with the commit 17631e8ca2d3
> ("i2c: designware: Add driver support for AMD NAVI GPU").

Yes, can you please check if you can't use the current i2c designware
driver?


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* RE: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-13 16:28       ` Wolfram Sang
@ 2023-04-14 11:04         ` Jiawen Wu
  2023-04-15 15:11           ` Andrew Lunn
  2023-04-17  3:17           ` Jiawen Wu
  0 siblings, 2 replies; 21+ messages in thread
From: Jiawen Wu @ 2023-04-14 11:04 UTC (permalink / raw)
  To: 'Wolfram Sang', 'Jarkko Nikula'
  Cc: netdev, linux, linux-i2c, linux-gpio, mengyuanlou

On Friday, April 14, 2023 12:29 AM, Wolfram Sang wrote:
> > > > Implement I2C bus driver to send and receive I2C messages.
> > > >
> > > > This I2C license the IP of Synopsys Designware, but without interrupt
> > > > support on the hardware design. It seems that polling mode needs to be
> > > > added in Synopsys Designware I2C driver. But currently it can only be
> > > > driven by this I2C bus master driver.
> > > >
> > > > Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> > > > ---
> > > >   drivers/net/ethernet/wangxun/Kconfig          |   1 +
> > > >   .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 153
> > > > ++++++++++++++++++
> > > >   .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  23 +++
> > > >   3 files changed, 177 insertions(+)
> > > >
> > Looks like your use case has similarities with the commit 17631e8ca2d3
> > ("i2c: designware: Add driver support for AMD NAVI GPU").
> 
> Yes, can you please check if you can't use the current i2c designware
> driver?

Hi Jarkko & Wolfram,

I read the i2c designware driver code, and found that 'dev->ss_hcnt' can
only be obtained by i2c_dw_acpi_configure() or calculated by clock rate.

I don't quite understand how to get the clock rate. I tried to add a software
node of clock with property ("clock-frequency", 100000) and referenced by
I2C node. But it didn't work.

Can I deliver 'dev->ss_hcnt' via platform data? Or how should I fill in the
software node?



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

* Re: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-14 11:04         ` Jiawen Wu
@ 2023-04-15 15:11           ` Andrew Lunn
  2023-04-17  1:46             ` Jiawen Wu
  2023-04-17  3:17           ` Jiawen Wu
  1 sibling, 1 reply; 21+ messages in thread
From: Andrew Lunn @ 2023-04-15 15:11 UTC (permalink / raw)
  To: Jiawen Wu
  Cc: 'Wolfram Sang', 'Jarkko Nikula',
	netdev, linux, linux-i2c, linux-gpio, mengyuanlou

> I don't quite understand how to get the clock rate. I tried to add a software
> node of clock with property ("clock-frequency", 100000) and referenced by
> I2C node. But it didn't work.

I've not spent the time to fully understand the code, so i could be
very wrong....

From what you said above, you clock is fixed? So maybe you can do
something like:

mfld_get_clk_rate_khz()

https://elixir.bootlin.com/linux/latest/source/drivers/i2c/busses/i2c-designware-pcidrv.c#L97

How are you instantiating the driver? Can you add to
i2_designware_pci_ids[]?

    Andrew

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

* RE: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-15 15:11           ` Andrew Lunn
@ 2023-04-17  1:46             ` Jiawen Wu
  0 siblings, 0 replies; 21+ messages in thread
From: Jiawen Wu @ 2023-04-17  1:46 UTC (permalink / raw)
  To: 'Andrew Lunn'
  Cc: 'Wolfram Sang', 'Jarkko Nikula',
	netdev, linux, linux-i2c, linux-gpio, mengyuanlou

On Saturday, April 15, 2023 11:11 PM, Andrew Lunn wrote:
> > I don't quite understand how to get the clock rate. I tried to add a software
> > node of clock with property ("clock-frequency", 100000) and referenced by
> > I2C node. But it didn't work.
> 
> I've not spent the time to fully understand the code, so i could be
> very wrong....
> 
> From what you said above, you clock is fixed? So maybe you can do
> something like:
> 
> mfld_get_clk_rate_khz()
> 
> https://elixir.bootlin.com/linux/latest/source/drivers/i2c/busses/i2c-designware-pcidrv.c#L97
> 
> How are you instantiating the driver? Can you add to
> i2_designware_pci_ids[]?
> 
>     Andrew
> 

There is no PCI ID for our I2C device, so I register the platform I2C device.


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

* RE: [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver
  2023-04-14 11:04         ` Jiawen Wu
  2023-04-15 15:11           ` Andrew Lunn
@ 2023-04-17  3:17           ` Jiawen Wu
  1 sibling, 0 replies; 21+ messages in thread
From: Jiawen Wu @ 2023-04-17  3:17 UTC (permalink / raw)
  To: 'Wolfram Sang', 'Jarkko Nikula'
  Cc: netdev, linux, linux-i2c, linux-gpio, mengyuanlou

On Friday, April 14, 2023 7:05 PM, Jiawen Wu wrote:
> On Friday, April 14, 2023 12:29 AM, Wolfram Sang wrote:
> > > > > Implement I2C bus driver to send and receive I2C messages.
> > > > >
> > > > > This I2C license the IP of Synopsys Designware, but without interrupt
> > > > > support on the hardware design. It seems that polling mode needs to be
> > > > > added in Synopsys Designware I2C driver. But currently it can only be
> > > > > driven by this I2C bus master driver.
> > > > >
> > > > > Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
> > > > > ---
> > > > >   drivers/net/ethernet/wangxun/Kconfig          |   1 +
> > > > >   .../net/ethernet/wangxun/txgbe/txgbe_phy.c    | 153
> > > > > ++++++++++++++++++
> > > > >   .../net/ethernet/wangxun/txgbe/txgbe_type.h   |  23 +++
> > > > >   3 files changed, 177 insertions(+)
> > > > >
> > > Looks like your use case has similarities with the commit 17631e8ca2d3
> > > ("i2c: designware: Add driver support for AMD NAVI GPU").
> >
> > Yes, can you please check if you can't use the current i2c designware
> > driver?
> 
> Hi Jarkko & Wolfram,
> 
> I read the i2c designware driver code, and found that 'dev->ss_hcnt' can
> only be obtained by i2c_dw_acpi_configure() or calculated by clock rate.
> 
> I don't quite understand how to get the clock rate. I tried to add a software
> node of clock with property ("clock-frequency", 100000) and referenced by
> I2C node. But it didn't work.
> 
> Can I deliver 'dev->ss_hcnt' via platform data? Or how should I fill in the
> software node?
> 

The above question is in the case of platform driver.

Moreover, why 'dev->fs_hcnt' and 'dev->fs_lcont' must be set when I use the
standard mode? Should it be set only if I2C_MAX_FAST_MODE_* ?



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

* Re: [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs
  2023-04-11  9:27 ` [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
  2023-04-11 10:36   ` Russell King (Oracle)
  2023-04-11 11:03   ` Simon Horman
@ 2023-04-17 14:47   ` kernel test robot
  2 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2023-04-17 14:47 UTC (permalink / raw)
  To: Jiawen Wu, netdev, linux
  Cc: llvm, oe-kbuild-all, linux-i2c, linux-gpio, mengyuanlou, Jiawen Wu

Hi Jiawen,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Jiawen-Wu/net-txgbe-Add-software-nodes-to-support-phylink/20230411-173314
patch link:    https://lore.kernel.org/r/20230411092725.104992-6-jiawenwu%40trustnetic.com
patch subject: [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs
config: riscv-randconfig-r042-20230417 (https://download.01.org/0day-ci/archive/20230417/202304172223.PoHEDYCs-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project 9638da200e00bd069e6dd63604e14cbafede9324)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install riscv cross compiling tool for clang build
        # apt-get install binutils-riscv64-linux-gnu
        # https://github.com/intel-lab-lkp/linux/commit/5903347a21d42b4f2d632e08e04890d7f638a947
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Jiawen-Wu/net-txgbe-Add-software-nodes-to-support-phylink/20230411-173314
        git checkout 5903347a21d42b4f2d632e08e04890d7f638a947
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=riscv olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/net/ethernet/wangxun/txgbe/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304172223.PoHEDYCs-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:269:6: warning: variable 'ret' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (interface == txgbe->interface)
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:325:9: note: uninitialized use occurs here
           return ret;
                  ^~~
   drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:269:2: note: remove the 'if' if its condition is always false
           if (interface == txgbe->interface)
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c:267:9: note: initialize the variable 'ret' to silence this warning
           int ret, val;
                  ^
                   = 0
   1 warning generated.


vim +269 drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c

   259	
   260	static int txgbe_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
   261				    phy_interface_t interface,
   262				    const unsigned long *advertising,
   263				    bool permit_pause_to_mac)
   264	{
   265		struct txgbe *txgbe = container_of(pcs, struct txgbe, pcs);
   266		struct wx *wx = txgbe->wx;
   267		int ret, val;
   268	
 > 269		if (interface == txgbe->interface)
   270			goto out;
   271	
   272		/* Wait xpcs power-up good */
   273		ret = read_poll_timeout(pcs_read, val,
   274					(val & TXGBE_PCS_DIG_STS_PSEQ_ST) ==
   275					TXGBE_PCS_DIG_STS_PSEQ_ST_GOOD,
   276					10000, 1000000, false,
   277					txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_STS);
   278		if (ret < 0) {
   279			wx_err(wx, "xpcs power-up timeout.\n");
   280			return ret;
   281		}
   282	
   283		/* Disable xpcs AN-73 */
   284		pcs_write(txgbe, MDIO_MMD_AN, MDIO_CTRL1, 0);
   285	
   286		/* Disable PHY MPLLA for eth mode change(after ECO) */
   287		txgbe_ephy_write(txgbe, TXGBE_SUP_DIG_MPLLA_OVRD_IN_0, 0x243A);
   288		WX_WRITE_FLUSH(wx);
   289		usleep_range(1000, 2000);
   290	
   291		/* Set the eth change_mode bit first in mis_rst register
   292		 * for corresponding LAN port
   293		 */
   294		wr32(wx, TXGBE_MIS_RST, TXGBE_MIS_RST_LAN_ETH_MODE(wx->bus.func));
   295	
   296		switch (interface) {
   297		case PHY_INTERFACE_MODE_10GBASER:
   298			txgbe_pma_config_10gbaser(txgbe);
   299			break;
   300		case PHY_INTERFACE_MODE_1000BASEX:
   301			txgbe_pma_config_1000basex(txgbe);
   302			break;
   303		default:
   304			break;
   305		}
   306	
   307		pcs_write(txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_CTRL1,
   308			  TXGBE_PCS_DIG_CTRL1_VR_RST | TXGBE_PCS_DIG_CTRL1_EN_VSMMD1);
   309		/* wait phy initialization done */
   310		ret = read_poll_timeout(pcs_read, val,
   311					!(val & TXGBE_PCS_DIG_CTRL1_VR_RST),
   312					100000, 10000000, false,
   313					txgbe, MDIO_MMD_PCS, TXGBE_PCS_DIG_CTRL1);
   314		if (ret < 0)
   315			wx_err(wx, "PHY initialization timeout.\n");
   316	
   317		txgbe->interface = interface;
   318	
   319	out:
   320		if (interface == PHY_INTERFACE_MODE_1000BASEX) {
   321			txgbe_setup_adv(txgbe, interface, advertising);
   322			txgbe_set_an37_ability(txgbe);
   323		}
   324	
   325		return ret;
   326	}
   327	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

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

end of thread, other threads:[~2023-04-17 14:47 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-11  9:27 [PATCH net-next v2 0/6] TXGBE PHYLINK support Jiawen Wu
2023-04-11  9:27 ` [PATCH net-next v2 1/6] net: txgbe: Add software nodes to support phylink Jiawen Wu
2023-04-11 12:51   ` Andrew Lunn
2023-04-11  9:27 ` [PATCH net-next v2 2/6] net: txgbe: Implement I2C bus master driver Jiawen Wu
2023-04-11  9:32   ` Jiawen Wu
2023-04-11 10:08     ` Jarkko Nikula
2023-04-13 16:28       ` Wolfram Sang
2023-04-14 11:04         ` Jiawen Wu
2023-04-15 15:11           ` Andrew Lunn
2023-04-17  1:46             ` Jiawen Wu
2023-04-17  3:17           ` Jiawen Wu
2023-04-11  9:27 ` [PATCH net-next v2 3/6] net: txgbe: Add SFP module identify Jiawen Wu
2023-04-11 12:52   ` Andrew Lunn
2023-04-11  9:27 ` [PATCH net-next v2 4/6] net: txgbe: Support GPIO to SFP socket Jiawen Wu
2023-04-11  9:27 ` [PATCH net-next v2 5/6] net: txgbe: Implement phylink pcs Jiawen Wu
2023-04-11 10:36   ` Russell King (Oracle)
2023-04-11 11:03   ` Simon Horman
2023-04-17 14:47   ` kernel test robot
2023-04-11  9:27 ` [PATCH net-next v2 6/6] net: txgbe: Support phylink MAC layer Jiawen Wu
2023-04-11 10:44   ` Russell King (Oracle)
2023-04-11 12:43     ` Andrew Lunn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).