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.cb/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, structice_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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, structice_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! [..]