lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Thu, 16 Jan 2020 11:14:53 +0200
From:   Alexandru Ardelean <alexandru.ardelean@...log.com>
To:     <netdev@...r.kernel.org>, <devicetree@...r.kernel.org>,
        <linux-kernel@...r.kernel.org>
CC:     <davem@...emloft.net>, <andrew@...n.ch>, <f.fainelli@...il.com>,
        <hkallweit1@...il.com>,
        Alexandru Ardelean <alexandru.ardelean@...log.com>
Subject: [PATCH 3/4] net: phy: adin: implement support for 1588 start-of-packet indication

The ADIN1300 & ADIN1200 PHYs support detection of IEEE 1588 time stamp
packets. This mechanism can be used to signal the MAC via a pulse-signal
when the PHY detects such a packet.
This reduces the time when the MAC can check these packets and can improve
the accuracy of the PTP algorithm.

The PHYs support configurable delays for when the signaling happens to the
MAC. These delays would typically get adjusted using a userspace phytool to
identify the best value for the setup. That values can then be added in the
system configuration via device-tree or ACPI and read as an array of 3
elements.

For the RX delays, the units are in MII clock cycles, while for TX delays
the units are in 8 nano-second intervals.

The indication of either RX or TX must use one of 4 pins from the device:
LED_0, LINK_ST, GP_CLK and INT_N.

The driver will make sure that TX SOP & RX SOP will not use the same pin.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@...log.com>
---
 drivers/net/phy/adin.c | 201 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)

diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c
index 11ffeaa665a1..4247fe5d813a 100644
--- a/drivers/net/phy/adin.c
+++ b/drivers/net/phy/adin.c
@@ -69,6 +69,42 @@
 #define ADIN1300_CLOCK_STOP_REG			0x9400
 #define ADIN1300_LPI_WAKE_ERR_CNT_REG		0xa000
 
+#define ADIN1300_SOP_CTRL			0x9428
+#define   ADIN1300_SOP_N_8_CYCM_1		GENMASK(6, 4)
+#define   ADIN1300_SOP_NCYC_EN			BIT(3)
+#define   ADIN1300_SOP_SFD_EN			BIT(2)
+#define   ADIN1300_SOP_RX_EN			BIT(1)
+#define   ADIN1300_SOP_TX_EN			BIT(0)
+
+enum {
+	SOP_DELAY_10,
+	SOP_DELAY_100,
+	SOP_DELAY_1000,
+	__SOP_DELAY_MAX,
+};
+
+#define ADIN1300_SOP_RX_DELAY			0x9429
+#define   ADIN1300_SOP_RX_10_DEL_NCYC_MSK	GENMASK(15, 11)
+#define   ADIN1300_SOP_RX_10_DEL_NCYC_SEL(x)	\
+		FIELD_PREP(ADIN1300_SOP_RX_10_DEL_NCYC_MSK, x)
+#define   ADIN1300_SOP_RX_100_DEL_NCYC_MSK	GENMASK(10, 6)
+#define   ADIN1300_SOP_RX_100_DEL_NCYC_SEL(x)	\
+		FIELD_PREP(ADIN1300_SOP_RX_100_DEL_NCYC_MSK, x)
+#define   ADIN1300_SOP_RX_1000_DEL_NCYC_MSK	GENMASK(5, 0)
+#define   ADIN1300_SOP_RX_1000_DEL_NCYC_SEL(x)	\
+		FIELD_PREP(ADIN1300_SOP_RX_1000_DEL_NCYC_MSK, x)
+
+#define ADIN1300_SOP_TX_DELAY			0x942a
+#define   ADIN1300_SOP_TX_10_DEL_N_8_NS_MSK	GENMASK(12, 8)
+#define   ADIN1300_SOP_TX_10_DEL_N_8_NS_SEL(x)	\
+		FIELD_PREP(ADIN1300_SOP_TX_10_DEL_N_8_NS_MSK, x)
+#define   ADIN1300_SOP_TX_100_DEL_N_8_NS_MSK	GENMASK(7, 4)
+#define   ADIN1300_SOP_TX_100_DEL_N_8_NS_SEL(x)	\
+		FIELD_PREP(ADIN1300_SOP_TX_100_DEL_N_8_NS_MSK, x)
+#define   ADIN1300_SOP_TX_1000_DEL_N_8_NS_MSK	GENMASK(3, 0)
+#define   ADIN1300_SOP_TX_1000_DEL_N_8_NS_SEL(x)\
+		FIELD_PREP(ADIN1300_SOP_TX_1000_DEL_N_8_NS_MSK, x)
+
 #define ADIN1300_GE_SOFT_RESET_REG		0xff0c
 #define   ADIN1300_GE_SOFT_RESET		BIT(0)
 
@@ -104,6 +140,21 @@
 #define ADIN1300_RMII_20_BITS			0x0004
 #define ADIN1300_RMII_24_BITS			0x0005
 
+#define ADIN1300_GE_IO_GP_CLK_OR_CNTRL		0xff3d
+#define ADIN1300_GE_IO_GP_OUT_OR_CNTRL		0xff3e
+#define ADIN1300_GE_IO_INT_N_OR_CNTRL		0xff3f
+#define ADIN1300_GE_IO_LED_A_OR_CNTRL		0xff41
+
+/* Common definitions for ADIN1300_GE_IO_* regs */
+enum ge_io_fn {
+	GE_IO_FN_DFLT,
+	GE_IO_FN_LINK_STATUS,
+	GE_IO_FN_TX_SOP_IND,
+	GE_IO_FN_RX_SOP_IND,
+	GE_IO_FN_CRS,
+	GE_IO_FN_COL,
+};
+
 /**
  * struct adin_cfg_reg_map - map a config value to aregister value
  * @cfg		value in device configuration
@@ -172,6 +223,13 @@ static const struct adin_map adin_hw_stats[] = {
 	{ "false_carrier_events_count",		0x9414 },
 };
 
+static const struct adin_map adin_ge_io_pins[] = {
+	{ "gp_clk",	ADIN1300_GE_IO_GP_CLK_OR_CNTRL,	GENMASK(2, 0) },
+	{ "link_st",	ADIN1300_GE_IO_GP_OUT_OR_CNTRL,	GENMASK(2, 0) },
+	{ "int_n",	ADIN1300_GE_IO_INT_N_OR_CNTRL,	GENMASK(2, 0) },
+	{ "led_0",	ADIN1300_GE_IO_LED_A_OR_CNTRL,	GENMASK(3, 0) },
+};
+
 /**
  * struct adin_priv - ADIN PHY driver private data
  * stats		statistic counters for the PHY
@@ -381,6 +439,145 @@ static int adin_set_edpd(struct phy_device *phydev, u16 tx_interval)
 			  val);
 }
 
+static int adin_set_pin_function(struct phy_device *phydev,
+				 const char *prop_name,
+				 enum ge_io_fn ge_io_fn,
+				 int *selected_pin)
+{
+	struct device *dev = &phydev->mdio.dev;
+	const struct adin_map *p;
+	const char *pin_name;
+	int i, rc;
+
+	rc = device_property_read_string(dev, prop_name, &pin_name);
+	if (rc) {
+		phydev_err(phydev, "Could not get property '%s', error: %d\n",
+			   prop_name, rc);
+		return rc;
+	}
+
+	p = NULL;
+	for (i = 0; i < ARRAY_SIZE(adin_ge_io_pins); i++) {
+		if (strcmp(adin_ge_io_pins[i].string, pin_name) == 0) {
+			if (selected_pin)
+				*selected_pin = i;
+			p = &adin_ge_io_pins[i];
+			break;
+		}
+	}
+
+	if (!p) {
+		phydev_err(phydev, "Invalid pin name %s\n", pin_name);
+		return -EINVAL;
+	}
+
+	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, p->val1, p->val2,
+			      ge_io_fn);
+}
+
+static int adin_config_1588_sop_rx(struct phy_device *phydev, int *rx_sop_pin)
+{
+	struct device *dev = &phydev->mdio.dev;
+	u8 values[__SOP_DELAY_MAX];
+	u16 val;
+	int rc;
+
+	rc = device_property_read_u8_array(dev,
+					   "adi,1588-rx-sop-delays-cycles",
+					   values, ARRAY_SIZE(values));
+	if (rc)
+		return 0;
+
+	rc = adin_set_pin_function(phydev, "adi,1588-rx-sop-pin-name",
+				   GE_IO_FN_RX_SOP_IND, rx_sop_pin);
+	if (rc)
+		return rc;
+
+	val = ADIN1300_SOP_RX_10_DEL_NCYC_SEL(values[SOP_DELAY_10]) |
+	      ADIN1300_SOP_RX_100_DEL_NCYC_SEL(values[SOP_DELAY_100]) |
+	      ADIN1300_SOP_RX_1000_DEL_NCYC_SEL(values[SOP_DELAY_1000]);
+
+	rc = phy_write_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_SOP_RX_DELAY, val);
+	if (rc < 0) {
+		adin_set_pin_function(phydev, "adi,1588-rx-sop-pin-name",
+				      GE_IO_FN_DFLT, NULL);
+		return rc;
+	}
+
+	return 1;
+}
+
+static int adin_config_1588_sop_tx(struct phy_device *phydev, int rx_sop_pin)
+{
+	struct device *dev = &phydev->mdio.dev;
+	u8 values[__SOP_DELAY_MAX];
+	int tx_sop_pin = -1;
+	u16 val;
+	int i, rc;
+
+	rc = device_property_read_u8_array(dev,
+					   "adi,1588-tx-sop-delays-ns",
+					   values, ARRAY_SIZE(values));
+	if (rc)
+		return 0;
+
+	for (i = 0; i < __SOP_DELAY_MAX; i++) {
+		if (values[i] % 8 != 0) {
+			phydev_err(phydev,
+				   "SOP TX delays must be multiples of 8\n");
+			return -EINVAL;
+		}
+	}
+
+	val = ADIN1300_SOP_TX_10_DEL_N_8_NS_SEL(values[SOP_DELAY_10] / 8) |
+	      ADIN1300_SOP_TX_100_DEL_N_8_NS_SEL(values[SOP_DELAY_100] / 8) |
+	      ADIN1300_SOP_TX_1000_DEL_N_8_NS_SEL(values[SOP_DELAY_1000] / 8);
+
+	rc = adin_set_pin_function(phydev, "adi,1588-tx-sop-pin-name",
+				   GE_IO_FN_TX_SOP_IND, &tx_sop_pin);
+	if (rc)
+		return rc;
+
+	if (rx_sop_pin == tx_sop_pin) {
+		phydev_err(phydev,
+			   "TX SOP can't use the same pin as RX SOP\n");
+		return -EFAULT;
+	}
+
+	rc = phy_write_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_SOP_TX_DELAY, val);
+	if (rc < 0) {
+		adin_set_pin_function(phydev, "adi,1588-tx-sop-pin-name",
+				      GE_IO_FN_DFLT, NULL);
+		return rc;
+	}
+
+	return 1;
+}
+
+static int adin_config_1588_sop(struct phy_device *phydev)
+{
+	int rx_sop_pin = -1;
+	u16 val = 0;
+	int rc;
+
+	rc = adin_config_1588_sop_rx(phydev, &rx_sop_pin);
+	if (rc < 0)
+		return rc;
+	if (rc == 1)
+		val |= ADIN1300_SOP_RX_EN;
+
+	rc = adin_config_1588_sop_tx(phydev, rx_sop_pin);
+	if (rc < 0)
+		return rc;
+	if (rc == 1)
+		val |= ADIN1300_SOP_TX_EN;
+
+	if (val)
+		val |= ADIN1300_SOP_SFD_EN;
+
+	return phy_write_mmd(phydev, MDIO_MMD_VEND1, ADIN1300_SOP_CTRL, val);
+}
+
 static int adin_get_tunable(struct phy_device *phydev,
 			    struct ethtool_tunable *tuna, void *data)
 {
@@ -429,6 +626,10 @@ static int adin_config_init(struct phy_device *phydev)
 	if (rc < 0)
 		return rc;
 
+	rc = adin_config_1588_sop(phydev);
+	if (rc < 0)
+		return rc;
+
 	phydev_dbg(phydev, "PHY is using mode '%s'\n",
 		   phy_modes(phydev->interface));
 
-- 
2.20.1

Powered by blists - more mailing lists