Thread (7 messages) 7 messages, 4 authors, 2025-08-08

RE: [PATCH v8 iwl-next] ice: add recovery clock and clock 1588 control for E825c

From: "Kubalewski, Arkadiusz" <arkadiusz.kubalewski@intel.com>
Date: 2025-08-05 14:30:50
Also in: intel-wired-lan

quoted hunk ↗ jump to hunk
From: Nitka, Grzegorz <redacted>
Sent: Thursday, July 24, 2025 2:28 PM

From: Przemyslaw Korba <redacted>

Add control for E825 input pins: phy clock recovery and clock 1588.
E825 does not provide control over platform level DPLL but it
provides control over PHY clock recovery, and PTP/timestamp driven
inputs for platform level DPLL.

Introduce a software controlled layer of abstraction to:
- create a DPLL of type EEC for E825c,
- create recovered clock pin for each PF, and control them through
writing to registers,
- create pin to control clock 1588 for PF0, and control it through
writing to registers.

Usage example:
- to get EEC PLL info
$ ynl --family dpll --dump device-get
[{'clock-id': 0,
 'id': 6,
 'lock-status': 'locked',
 'mode': 'manual',
 'mode-supported': ['manual'],
 'module-name': 'ice',
 'type': 'eec'},
...
 ]

- to get 1588 and rclk pins info
(note: in the output below, pin id=31 is a representor for 1588 input,
while pins 32..35 corresponds to PHY clock inputs to SyncE  module)
$ ynl --family dpll --dump pin-get
[{'board-label': 'CLK_IN_0',
 'capabilities': set(),
 'clock-id': 0,
 'id': 27,
 'module-name': 'ice',
 'parent-device': [{'direction': 'input',
                    'parent-id': 6,
                    'state': 'connected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'mux'},
{'board-label': 'CLK_IN_1',
 'capabilities': set(),
 'clock-id': 0,
 'id': 28,
 'module-name': 'ice',
 'parent-device': [{'direction': 'input',
                    'parent-id': 6,
                    'state': 'connected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'mux'},
{'board-label': 'pin_1588',
 'capabilities': {'state-can-change'},
 'clock-id': 0,
 'id': 31,
 'module-name': 'ice',
 'parent-pin': [{'parent-id': 27, 'state': 'disconnected'},
                {'parent-id': 28, 'state': 'disconnected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'synce-eth-port'},
{'capabilities': {'state-can-change'},
 'clock-id': 0,
 'id': 32,
 'module-name': 'ice',
 'parent-pin': [{'parent-id': 27, 'state': 'disconnected'},
                {'parent-id': 28, 'state': 'disconnected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'synce-eth-port'},
{'capabilities': {'state-can-change'},
 'clock-id': 0,
 'id': 33,
 'module-name': 'ice',
 'parent-pin': [{'parent-id': 27, 'state': 'disconnected'},
                {'parent-id': 28, 'state': 'disconnected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'synce-eth-port'},
{'capabilities': {'state-can-change'},
 'clock-id': 0,
 'id': 34,
 'module-name': 'ice',
 'parent-pin': [{'parent-id': 27, 'state': 'disconnected'},
                {'parent-id': 28, 'state': 'disconnected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'synce-eth-port'},
{'capabilities': {'state-can-change'},
 'clock-id': 0,
 'id': 35,
 'module-name': 'ice',
 'parent-pin': [{'parent-id': 27, 'state': 'disconnected'},
                {'parent-id': 28, 'state': 'disconnected'}],
 'phase-adjust-max': 0,
 'phase-adjust-min': 0,
 'type': 'synce-eth-port'}]

- to set PHY0 clock as SyncE module input
$ ynl --family dpll --do pin-set --json '{"id":32,"parent-pin":\
   {"parent-id":27, "state":"connected"}}'

- to set 1588 Main Timer as source into SyncE module
$ ynl --family dpll --do pin-set --json '{"id":31,"parent-pin":\
   {"parent-id":27, "state":"connected"}}'

Reviewed-by: Milena Olech <redacted>
Co-developed-by: Grzegorz Nitka <redacted>
Signed-off-by: Grzegorz Nitka <redacted>
Signed-off-by: Przemyslaw Korba <redacted>
---
v7->v8:
- rebased
- removed unrelated changes
- change pin_1588 type to DPLL_PIN_TYPE_EXT
- use ICE_SYNCE_CLK_NUM to determine the number of rclk pins
---
drivers/net/ethernet/intel/ice/ice_dpll.c   | 852 ++++++++++++++++++--
drivers/net/ethernet/intel/ice/ice_dpll.h   |  24 +
drivers/net/ethernet/intel/ice/ice_lib.c    |   3 +
drivers/net/ethernet/intel/ice/ice_ptp_hw.c |  34 +-
drivers/net/ethernet/intel/ice/ice_ptp_hw.h |   1 +
drivers/net/ethernet/intel/ice/ice_tspll.h  |   7 +
drivers/net/ethernet/intel/ice/ice_type.h   |   6 +
7 files changed, 869 insertions(+), 58 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c
b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 53b54e395a2e..f89dec5e532e 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -9,6 +9,7 @@
#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
#define ICE_DPLL_PIN_IDX_INVALID		0xff
#define ICE_DPLL_RCLK_NUM_PER_PF		1
+#define ICE_DPLL_PIN_1588_NUM			1
#define ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT	25
#define ICE_DPLL_PIN_GEN_RCLK_FREQ		1953125
#define ICE_DPLL_PIN_PRIO_OUTPUT		0xff
@@ -74,6 +75,7 @@ static const char * const pin_type_name[] = {
static const char * const ice_dpll_sw_pin_sma[] = { "SMA1", "SMA2" };
static const char * const ice_dpll_sw_pin_ufl[] = { "U.FL1", "U.FL2" };
+static const char ice_dpll_pin_1588[] = "pin_1588";

static const struct dpll_pin_frequency ice_esync_range[] = {
	DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ),
@@ -528,6 +530,117 @@ ice_dpll_pin_disable(struct ice_hw *hw, struct
ice_dpll_pin *pin,
	return ret;
}

+/**
+ * ice_dpll_rclk_update_e825c - updates the state of rclk pin on e825c
device
+ * @pf: private board struct
+ * @pin: pointer to a pin
+ *
+ * Update struct holding pin states info, states are separate for each
parent
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int ice_dpll_rclk_update_e825c(struct ice_pf *pf,
+				      struct ice_dpll_pin *pin)
+{
+	u8 rclk_bits;
+	int err;
+	u32 reg;
+
+	if (pf->dplls.rclk.num_parents > ICE_SYNCE_CLK_NUM)
+		return -EINVAL;
+
+	err = ice_read_cgu_reg(&pf->hw, ICE_CGU_R10, &reg);
+	if (err)
+		return err;
+	rclk_bits = FIELD_GET(ICE_CGU_R10_SYNCE_S_REF_CLK, reg);
+	SET_PIN_STATE(pin, ICE_SYNCE_CLK0, rclk_bits ==
+		     (pf->ptp.port.port_num +
ICE_CGU_BYPASS_MUX_OFFSET_E825C));
+
+	err = ice_read_cgu_reg(&pf->hw, ICE_CGU_R11, &reg);
+	if (err)
+		return err;
+	rclk_bits = FIELD_GET(ICE_CGU_R11_SYNCE_S_BYP_CLK, reg);
+	SET_PIN_STATE(pin, ICE_SYNCE_CLK1, rclk_bits ==
+		     (pf->ptp.port.port_num +
ICE_CGU_BYPASS_MUX_OFFSET_E825C));
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_update - updates the state of rclk pin on a device
+ * @pf: private board struct
+ * @pin: pointer to a pin
+ * @port_num: port number
+ *
+ * Update struct holding pin states info, states are separate for each
parent
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int ice_dpll_rclk_update(struct ice_pf *pf, struct ice_dpll_pin
*pin,
+				u8 port_num)
+{
+	int ret;
+
+	for (u8 parent = 0; parent < pf->dplls.rclk.num_parents; parent++) {
+		ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &parent, &port_num,
+						 &pin->flags[parent], NULL);
+		if (ret)
+			return ret;
+		SET_PIN_STATE(pin, parent,
+			      ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
+			      pin->flags[parent]);
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_update_pin_1588_e825c - updates the state of clock 1588 pin
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @parent: clock source identifier
+ *
+ * Update struct holding pin states info, states are separate for each
parent
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int ice_dpll_update_pin_1588_e825c(struct ice_hw *hw,
+					  struct ice_dpll_pin *pin,
+					  enum ice_synce_clk parent)
+{
+	u8 bits_clk;
+	int err;
+	u32 reg;
+
+	switch (parent) {
+	case ICE_SYNCE_CLK0:
+		err = ice_read_cgu_reg(hw, ICE_CGU_R10, &reg);
+		if (err)
+			return err;
+		bits_clk = FIELD_GET(ICE_CGU_R10_SYNCE_S_REF_CLK, reg);
+		break;
+	case ICE_SYNCE_CLK1:
+		err = ice_read_cgu_reg(hw, ICE_CGU_R11, &reg);
+		if (err)
+			return err;
+		bits_clk = FIELD_GET(ICE_CGU_R11_SYNCE_S_BYP_CLK, reg);
+		break;
+	default:
+		return -EINVAL;
+	}
+	SET_PIN_STATE(pin, parent, bits_clk == ICE_CGU_NCOCLK);
+
+	return 0;
+}
+
/**
 * ice_dpll_sw_pins_update - update status of all SW pins
 * @pf: private board struct
@@ -668,22 +781,14 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct
ice_dpll_pin *pin,
		}
		break;
	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
-		for (parent = 0; parent < pf->dplls.rclk.num_parents;
-		     parent++) {
-			u8 p = parent;
-
-			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
-							 &port_num,
-							 &pin->flags[parent],
-							 NULL);
+		if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) {
+			ret = ice_dpll_rclk_update_e825c(pf, pin);
+			if (ret)
+				goto err;
+		} else {
+			ret = ice_dpll_rclk_update(pf, pin, port_num);
			if (ret)
				goto err;
-			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
-			    pin->flags[parent])
-				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
-			else
-				pin->state[parent] =
-					DPLL_PIN_STATE_DISCONNECTED;
		}
		break;
	case ICE_DPLL_PIN_TYPE_SOFTWARE:
@@ -1021,6 +1126,33 @@ ice_dpll_pin_state_get(const struct dpll_pin *pin,
void *pin_priv,
	return ret;
}

+/**
+ * ice_dpll_pin_state_get_e825c - update e825c device pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Set pin state of e825c device to connected.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ */
+static int ice_dpll_pin_state_get_e825c(const struct dpll_pin *pin,
+					void *pin_priv,
+					const struct dpll_device *dpll,
+					void *dpll_priv,
+					enum dpll_pin_state *state,
+					struct netlink_ext_ack *extack)
+{
+	*state = DPLL_PIN_STATE_CONNECTED;
+
+	return 0;
+}
+
/**
 * ice_dpll_output_state_get - get output pin state on dpll device
 * @pin: pointer to a pin
@@ -1842,6 +1974,228 @@ ice_dpll_phase_offset_get(const struct dpll_pin
*pin, void *pin_priv,
	return 0;
}

+/**
+ * ice_dpll_cfg_bypass_mux_e825c - check if the given port recovered clock
+ * or clock 1588 is set active
+ * @hw: Pointer to the HW struct
+ * @ena: true to enable the reference, false if disable
+ * @port_num: Number of the port
+ * @output: Output pin, we have two in E825C
+ * @clock_1588: true to enable 1588 reference, false to recover from port
+ *
+ * Dpll subsystem callback. Handler for setting the correct registers to
+ * enable a functionality on e825c device.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_cfg_bypass_mux_e825c(struct ice_hw *hw, bool ena,
+			      u32 port_num, enum ice_synce_clk output,
+			      bool clock_1588)
There is now new file ice_tspll.c, where this code could belong, then just
provide interface for this code.
+{
+	u8 first_mux;
+	int err;
+	u32 r10;
+
+	err = ice_read_cgu_reg(hw, ICE_CGU_R10, &r10);
+	if (err)
+		return err;
+
+	if (!ena)
+		first_mux = ICE_CGU_NET_REF_CLK0;
+	else if (clock_1588)
+		first_mux = ICE_CGU_NCOCLK;
+	else
+		first_mux = port_num + ICE_CGU_BYPASS_MUX_OFFSET_E825C;
+
+	r10 &= ~(ICE_CGU_R10_SYNCE_DCK_RST | ICE_CGU_R10_SYNCE_DCK2_RST);
+
+	switch (output) {
+	case ICE_SYNCE_CLK0:
+		r10 &= ~(ICE_CGU_R10_SYNCE_ETHCLKO_SEL |
+			 ICE_CGU_R10_SYNCE_ETHDIV_LOAD |
+			 ICE_CGU_R10_SYNCE_S_REF_CLK);
+		r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_S_REF_CLK, first_mux);
+		if (clock_1588)
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHCLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP0);
+		else
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHCLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP0_DIV);
+		break;
+	case ICE_SYNCE_CLK1:
+	{
+		u32 val;
+
+		err = ice_read_cgu_reg(hw, ICE_CGU_R11, &val);
+		if (err)
+			return err;
+		val &= ~ICE_CGU_R11_SYNCE_S_BYP_CLK;
+		val |= FIELD_PREP(ICE_CGU_R11_SYNCE_S_BYP_CLK, first_mux);
+		err = ice_write_cgu_reg(hw, ICE_CGU_R11, val);
+		if (err)
+			return err;
+		r10 &= ~(ICE_CGU_R10_SYNCE_CLKODIV_LOAD |
+			 ICE_CGU_R10_SYNCE_CLKO_SEL);
+		if (clock_1588)
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP1);
+		else
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP1_DIV);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	err = ice_write_cgu_reg(hw, ICE_CGU_R10, r10);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_get_div_e825c - get the divider for the given speed
+ * @link_speed: link speed of the port
+ * @divider: output value, calculated divider
+ *
+ * Dpll subsystem callback. Handler for setting the divider on e825c
device.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int ice_dpll_get_div_e825c(u16 link_speed, u8 *divider)
+{
+	switch (link_speed) {
+	case ICE_AQ_LINK_SPEED_100GB:
+	case ICE_AQ_LINK_SPEED_50GB:
+	case ICE_AQ_LINK_SPEED_25GB:
+		*divider = 10;
+		break;
+	case ICE_AQ_LINK_SPEED_40GB:
+	case ICE_AQ_LINK_SPEED_10GB:
+		*divider = 4;
+		break;
+	case ICE_AQ_LINK_SPEED_5GB:
+	case ICE_AQ_LINK_SPEED_2500MB:
+	case ICE_AQ_LINK_SPEED_1000MB:
+		*divider = 2;
+		break;
+	case ICE_AQ_LINK_SPEED_100MB:
+		*divider = 1;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_cfg_synce_ethdiv_e825c - set the divider on the mux
+ * @hw: Pointer to the HW struct
+ * @output: Output pin, we have two in E825C
+ *
+ * Dpll subsystem callback. Set the correct divider for RCLKA or RCLKB.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int ice_dpll_cfg_synce_ethdiv_e825c(struct ice_hw *hw,
+					   enum ice_synce_clk output)
Ditto.

Thank you!

[..]
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help