#define ICE_DPLL_RCLK_NUM_PER_PF               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
+#define ICE_DPLL_SW_PIN_INPUT_BASE_SFP         4
+#define ICE_DPLL_SW_PIN_INPUT_BASE_QSFP                6
+#define ICE_DPLL_SW_PIN_OUTPUT_BASE            0
+
+#define ICE_DPLL_PIN_SW_INPUT_ABS(in_idx) \
+       (ICE_DPLL_SW_PIN_INPUT_BASE_SFP + (in_idx))
+
+#define ICE_DPLL_PIN_SW_1_INPUT_ABS_IDX \
+       (ICE_DPLL_PIN_SW_INPUT_ABS(ICE_DPLL_PIN_SW_1_IDX))
+
+#define ICE_DPLL_PIN_SW_2_INPUT_ABS_IDX \
+       (ICE_DPLL_PIN_SW_INPUT_ABS(ICE_DPLL_PIN_SW_2_IDX))
+
+#define ICE_DPLL_PIN_SW_OUTPUT_ABS(out_idx) \
+       (ICE_DPLL_SW_PIN_OUTPUT_BASE + (out_idx))
+
+#define ICE_DPLL_PIN_SW_1_OUTPUT_ABS_IDX \
+       (ICE_DPLL_PIN_SW_OUTPUT_ABS(ICE_DPLL_PIN_SW_1_IDX))
+
+#define ICE_DPLL_PIN_SW_2_OUTPUT_ABS_IDX \
+       (ICE_DPLL_PIN_SW_OUTPUT_ABS(ICE_DPLL_PIN_SW_2_IDX))
 
 /**
  * enum ice_dpll_pin_type - enumerate ice pin types:
  * @ICE_DPLL_PIN_TYPE_INPUT: input pin
  * @ICE_DPLL_PIN_TYPE_OUTPUT: output pin
  * @ICE_DPLL_PIN_TYPE_RCLK_INPUT: recovery clock input pin
+ * @ICE_DPLL_PIN_TYPE_SOFTWARE: software controlled SMA/U.FL pins
  */
 enum ice_dpll_pin_type {
        ICE_DPLL_PIN_INVALID,
        ICE_DPLL_PIN_TYPE_INPUT,
        ICE_DPLL_PIN_TYPE_OUTPUT,
        ICE_DPLL_PIN_TYPE_RCLK_INPUT,
+       ICE_DPLL_PIN_TYPE_SOFTWARE,
 };
 
 static const char * const pin_type_name[] = {
        [ICE_DPLL_PIN_TYPE_INPUT] = "input",
        [ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
        [ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
+       [ICE_DPLL_PIN_TYPE_SOFTWARE] = "software",
 };
 
+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 struct dpll_pin_frequency ice_esync_range[] = {
        DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ),
 };
 
+/**
+ * ice_dpll_is_sw_pin - check if given pin shall be controlled by SW
+ * @pf: private board structure
+ * @index: index of a pin as understood by FW
+ * @input: true for input, false for output
+ *
+ * Check if the pin shall be controlled by SW - instead of providing raw access
+ * for pin control. For E810 NIC with dpll there is additional MUX-related logic
+ * between SMA/U.FL pins/connectors and dpll device, best to give user access
+ * with series of wrapper functions as from user perspective they convey single
+ * functionality rather then separated pins.
+ *
+ * Return:
+ * * true - pin controlled by SW
+ * * false - pin not controlled by SW
+ */
+static bool ice_dpll_is_sw_pin(struct ice_pf *pf, u8 index, bool input)
+{
+       if (input && pf->hw.device_id == ICE_DEV_ID_E810C_QSFP)
+               index -= ICE_DPLL_SW_PIN_INPUT_BASE_QSFP -
+                        ICE_DPLL_SW_PIN_INPUT_BASE_SFP;
+
+       if ((input && (index == ICE_DPLL_PIN_SW_1_INPUT_ABS_IDX ||
+                      index == ICE_DPLL_PIN_SW_2_INPUT_ABS_IDX)) ||
+           (!input && (index == ICE_DPLL_PIN_SW_1_OUTPUT_ABS_IDX ||
+                       index == ICE_DPLL_PIN_SW_2_OUTPUT_ABS_IDX)))
+               return true;
+       return false;
+}
+
 /**
  * ice_dpll_is_reset - check if reset is in progress
  * @pf: private board structure
                                      extack, ICE_DPLL_PIN_TYPE_OUTPUT);
 }
 
+/**
+ * ice_dpll_sw_pin_frequency_set - callback to set frequency of SW pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Calls set frequency command for corresponding and active input/output pin.
+ *
+ * Context: Calls a function which acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not active or couldn't get from hw
+ */
+static int
+ice_dpll_sw_pin_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+                             const struct dpll_device *dpll, void *dpll_priv,
+                             u64 frequency, struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *sma = pin_priv;
+       int ret;
+
+       if (!sma->active) {
+               NL_SET_ERR_MSG(extack, "pin is not active");
+               return -EINVAL;
+       }
+       if (sma->direction == DPLL_PIN_DIRECTION_INPUT)
+               ret = ice_dpll_input_frequency_set(NULL, sma->input, dpll,
+                                                  dpll_priv, frequency,
+                                                  extack);
+       else
+               ret = ice_dpll_output_frequency_set(NULL, sma->output, dpll,
+                                                   dpll_priv, frequency,
+                                                   extack);
+
+       return ret;
+}
+
+/**
+ * ice_dpll_sw_pin_frequency_get - callback for get frequency of SW pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Calls get frequency command for corresponding active input/output.
+ *
+ * Context: Calls a function which acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not active or couldn't get from hw
+ */
+static int
+ice_dpll_sw_pin_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+                             const struct dpll_device *dpll, void *dpll_priv,
+                             u64 *frequency, struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *sma = pin_priv;
+       int ret;
+
+       if (!sma->active) {
+               *frequency = 0;
+               return 0;
+       }
+       if (sma->direction == DPLL_PIN_DIRECTION_INPUT) {
+               ret = ice_dpll_input_frequency_get(NULL, sma->input, dpll,
+                                                  dpll_priv, frequency,
+                                                  extack);
+       } else {
+               ret = ice_dpll_output_frequency_get(NULL, sma->output, dpll,
+                                                   dpll_priv, frequency,
+                                                   extack);
+       }
+
+       return ret;
+}
+
 /**
  * ice_dpll_pin_enable - enable a pin on dplls
  * @hw: board private hw structure
        return ret;
 }
 
+/**
+ * ice_dpll_sw_pins_update - update status of all SW pins
+ * @pf: private board struct
+ *
+ * Determine and update pin struct fields (direction/active) of their current
+ * values for all the SW controlled pins.
+ *
+ * Context: Call with pf->dplls.lock held
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_sw_pins_update(struct ice_pf *pf)
+{
+       struct ice_dplls *d = &pf->dplls;
+       struct ice_dpll_pin *p;
+       u8 data = 0;
+       int ret;
+
+       ret = ice_read_sma_ctrl(&pf->hw, &data);
+       if (ret)
+               return ret;
+       /* no change since last check */
+       if (d->sma_data == data)
+               return 0;
+
+       /*
+        * SMA1/U.FL1 vs SMA2/U.FL2 are using different bit scheme to decide
+        * on their direction and if are active
+        */
+       p = &d->sma[ICE_DPLL_PIN_SW_1_IDX];
+       p->active = true;
+       p->direction = DPLL_PIN_DIRECTION_INPUT;
+       if (data & ICE_SMA1_DIR_EN) {
+               p->direction = DPLL_PIN_DIRECTION_OUTPUT;
+               if (data & ICE_SMA1_TX_EN)
+                       p->active = false;
+       }
+
+       p = &d->sma[ICE_DPLL_PIN_SW_2_IDX];
+       p->active = true;
+       p->direction = DPLL_PIN_DIRECTION_INPUT;
+       if ((data & ICE_SMA2_INACTIVE_MASK) == ICE_SMA2_INACTIVE_MASK)
+               p->active = false;
+       else if (data & ICE_SMA2_DIR_EN)
+               p->direction = DPLL_PIN_DIRECTION_OUTPUT;
+
+       p = &d->ufl[ICE_DPLL_PIN_SW_1_IDX];
+       if (!(data & (ICE_SMA1_DIR_EN | ICE_SMA1_TX_EN)))
+               p->active = true;
+       else
+               p->active = false;
+
+       p = &d->ufl[ICE_DPLL_PIN_SW_2_IDX];
+       p->active = (data & ICE_SMA2_DIR_EN) && !(data & ICE_SMA2_UFL2_RX_DIS);
+       d->sma_data = data;
+
+       return 0;
+}
+
 /**
  * ice_dpll_pin_state_update - update pin's state
  * @pf: private board struct
                                        DPLL_PIN_STATE_DISCONNECTED;
                }
                break;
+       case ICE_DPLL_PIN_TYPE_SOFTWARE:
+               ret = ice_dpll_sw_pins_update(pf);
+               if (ret)
+                       goto err;
+               break;
        default:
                return -EINVAL;
        }
                                      extack, ICE_DPLL_PIN_TYPE_INPUT);
 }
 
+/**
+ * ice_dpll_sma_direction_set - set direction of SMA pin
+ * @p: pointer to a pin
+ * @direction: requested direction of the pin
+ * @extack: error reporting
+ *
+ * Wrapper for dpll subsystem callback. Set direction of a SMA pin.
+ *
+ * Context: Call with pf->dplls.lock held
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p,
+                                     enum dpll_pin_direction direction,
+                                     struct netlink_ext_ack *extack)
+{
+       u8 data;
+       int ret;
+
+       if (p->direction == direction && p->active)
+               return 0;
+       ret = ice_read_sma_ctrl(&p->pf->hw, &data);
+       if (ret)
+               return ret;
+
+       switch (p->idx) {
+       case ICE_DPLL_PIN_SW_1_IDX:
+               data &= ~ICE_SMA1_MASK;
+               if (direction == DPLL_PIN_DIRECTION_OUTPUT)
+                       data |= ICE_SMA1_DIR_EN;
+               break;
+       case ICE_DPLL_PIN_SW_2_IDX:
+               if (direction == DPLL_PIN_DIRECTION_INPUT) {
+                       data &= ~ICE_SMA2_DIR_EN;
+               } else {
+                       data &= ~ICE_SMA2_TX_EN;
+                       data |= ICE_SMA2_DIR_EN;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       ret = ice_write_sma_ctrl(&p->pf->hw, data);
+       if (!ret)
+               ret = ice_dpll_pin_state_update(p->pf, p,
+                                               ICE_DPLL_PIN_TYPE_SOFTWARE,
+                                               extack);
+
+       return ret;
+}
+
+/**
+ * ice_dpll_ufl_pin_state_set - set U.FL pin state on dpll device
+ * @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: requested state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Set the state of a pin.
+ *
+ * Context: Acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_ufl_pin_state_set(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)
+{
+       struct ice_dpll_pin *p = pin_priv, *target;
+       struct ice_dpll *d = dpll_priv;
+       enum ice_dpll_pin_type type;
+       struct ice_pf *pf = p->pf;
+       struct ice_hw *hw;
+       bool enable;
+       u8 data;
+       int ret;
+
+       if (ice_dpll_is_reset(pf, extack))
+               return -EBUSY;
+
+       mutex_lock(&pf->dplls.lock);
+       hw = &pf->hw;
+       ret = ice_read_sma_ctrl(hw, &data);
+       if (ret)
+               goto unlock;
+
+       ret = -EINVAL;
+       switch (p->idx) {
+       case ICE_DPLL_PIN_SW_1_IDX:
+               if (state == DPLL_PIN_STATE_CONNECTED) {
+                       data &= ~ICE_SMA1_MASK;
+                       enable = true;
+               } else if (state == DPLL_PIN_STATE_DISCONNECTED) {
+                       data |= ICE_SMA1_TX_EN;
+                       enable = false;
+               } else {
+                       goto unlock;
+               }
+               target = p->output;
+               type = ICE_DPLL_PIN_TYPE_OUTPUT;
+               break;
+       case ICE_DPLL_PIN_SW_2_IDX:
+               if (state == DPLL_PIN_STATE_SELECTABLE) {
+                       data |= ICE_SMA2_DIR_EN;
+                       data &= ~ICE_SMA2_UFL2_RX_DIS;
+                       enable = true;
+               } else if (state == DPLL_PIN_STATE_DISCONNECTED) {
+                       data |= ICE_SMA2_UFL2_RX_DIS;
+                       enable = false;
+               } else {
+                       goto unlock;
+               }
+               target = p->input;
+               type = ICE_DPLL_PIN_TYPE_INPUT;
+               break;
+       default:
+               goto unlock;
+       }
+
+       ret = ice_write_sma_ctrl(hw, data);
+       if (ret)
+               goto unlock;
+       ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_SOFTWARE,
+                                       extack);
+       if (ret)
+               goto unlock;
+
+       if (enable)
+               ret = ice_dpll_pin_enable(hw, target, d->dpll_idx, type, extack);
+       else
+               ret = ice_dpll_pin_disable(hw, target, type, extack);
+       if (!ret)
+               ret = ice_dpll_pin_state_update(pf, target, type, extack);
+
+unlock:
+       mutex_unlock(&pf->dplls.lock);
+
+       return ret;
+}
+
+/**
+ * ice_dpll_sw_pin_state_get - get SW pin state
+ * @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
+ *
+ * Dpll subsystem callback. Check state of a SW pin.
+ *
+ * Context: Acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_sw_pin_state_get(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)
+{
+       struct ice_dpll_pin *p = pin_priv;
+       struct ice_dpll *d = dpll_priv;
+       struct ice_pf *pf = p->pf;
+       int ret = 0;
+
+       if (ice_dpll_is_reset(pf, extack))
+               return -EBUSY;
+       mutex_lock(&pf->dplls.lock);
+       if (!p->active) {
+               *state = DPLL_PIN_STATE_DISCONNECTED;
+               goto unlock;
+       }
+
+       if (p->direction == DPLL_PIN_DIRECTION_INPUT) {
+               ret = ice_dpll_pin_state_update(pf, p->input,
+                                               ICE_DPLL_PIN_TYPE_INPUT,
+                                               extack);
+               if (ret)
+                       goto unlock;
+               *state = p->input->state[d->dpll_idx];
+       } else {
+               ret = ice_dpll_pin_state_update(pf, p->output,
+                                               ICE_DPLL_PIN_TYPE_OUTPUT,
+                                               extack);
+               if (ret)
+                       goto unlock;
+               *state = p->output->state[d->dpll_idx];
+       }
+unlock:
+       mutex_unlock(&pf->dplls.lock);
+
+       return ret;
+}
+
+/**
+ * ice_dpll_sma_pin_state_set - set SMA pin state on dpll device
+ * @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: requested state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Set state of a pin.
+ *
+ * Context: Acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_sma_pin_state_set(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)
+{
+       struct ice_dpll_pin *sma = pin_priv, *target;
+       struct ice_dpll *d = dpll_priv;
+       struct ice_pf *pf = sma->pf;
+       enum ice_dpll_pin_type type;
+       bool enable;
+       int ret;
+
+       if (ice_dpll_is_reset(pf, extack))
+               return -EBUSY;
+
+       mutex_lock(&pf->dplls.lock);
+       if (!sma->active) {
+               ret = ice_dpll_sma_direction_set(sma, sma->direction, extack);
+               if (ret)
+                       goto unlock;
+       }
+       if (sma->direction == DPLL_PIN_DIRECTION_INPUT) {
+               enable = state == DPLL_PIN_STATE_SELECTABLE;
+               target = sma->input;
+               type = ICE_DPLL_PIN_TYPE_INPUT;
+       } else {
+               enable = state == DPLL_PIN_STATE_CONNECTED;
+               target = sma->output;
+               type = ICE_DPLL_PIN_TYPE_OUTPUT;
+       }
+
+       if (enable)
+               ret = ice_dpll_pin_enable(&pf->hw, target, d->dpll_idx, type,
+                                         extack);
+       else
+               ret = ice_dpll_pin_disable(&pf->hw, target, type, extack);
+       if (!ret)
+               ret = ice_dpll_pin_state_update(pf, target, type, extack);
+
+unlock:
+       mutex_unlock(&pf->dplls.lock);
+
+       return ret;
+}
+
 /**
  * ice_dpll_input_prio_get - get dpll's input prio
  * @pin: pointer to a pin
        return ret;
 }
 
+static int
+ice_dpll_sw_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
+                          const struct dpll_device *dpll, void *dpll_priv,
+                          u32 *prio, struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+       struct ice_dpll *d = dpll_priv;
+       struct ice_pf *pf = d->pf;
+
+       mutex_lock(&pf->dplls.lock);
+       if (p->input && p->direction == DPLL_PIN_DIRECTION_INPUT)
+               *prio = d->input_prio[p->input->idx];
+       else
+               *prio = ICE_DPLL_PIN_PRIO_OUTPUT;
+       mutex_unlock(&pf->dplls.lock);
+
+       return 0;
+}
+
+static int
+ice_dpll_sw_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
+                          const struct dpll_device *dpll, void *dpll_priv,
+                          u32 prio, struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+       struct ice_dpll *d = dpll_priv;
+       struct ice_pf *pf = d->pf;
+       int ret;
+
+       if (!p->input || p->direction != DPLL_PIN_DIRECTION_INPUT)
+               return -EINVAL;
+       if (ice_dpll_is_reset(pf, extack))
+               return -EBUSY;
+
+       mutex_lock(&pf->dplls.lock);
+       ret = ice_dpll_hw_input_prio_set(pf, d, p->input, prio, extack);
+       mutex_unlock(&pf->dplls.lock);
+
+       return ret;
+}
+
 /**
  * ice_dpll_input_direction - callback for get input pin direction
  * @pin: pointer to a pin
        return 0;
 }
 
+/**
+ * ice_dpll_pin_sma_direction_set - callback for set SMA pin direction
+ * @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
+ * @direction: requested pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting direction of a SMA pin.
+ *
+ * Context: Acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_pin_sma_direction_set(const struct dpll_pin *pin, void *pin_priv,
+                              const struct dpll_device *dpll, void *dpll_priv,
+                              enum dpll_pin_direction direction,
+                              struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+       struct ice_pf *pf = p->pf;
+       int ret;
+
+       if (ice_dpll_is_reset(pf, extack))
+               return -EBUSY;
+
+       mutex_lock(&pf->dplls.lock);
+       ret = ice_dpll_sma_direction_set(p, direction, extack);
+       mutex_unlock(&pf->dplls.lock);
+
+       return ret;
+}
+
+/**
+ * ice_dpll_pin_sw_direction_get - callback for get SW pin direction
+ * @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
+ * @direction: on success holds pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of a SMA pin.
+ *
+ * Context: Acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_pin_sw_direction_get(const struct dpll_pin *pin, void *pin_priv,
+                             const struct dpll_device *dpll, void *dpll_priv,
+                             enum dpll_pin_direction *direction,
+                             struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+       struct ice_pf *pf = p->pf;
+
+       if (ice_dpll_is_reset(pf, extack))
+               return -EBUSY;
+       mutex_lock(&pf->dplls.lock);
+       *direction = p->direction;
+       mutex_unlock(&pf->dplls.lock);
+
+       return 0;
+}
+
 /**
  * ice_dpll_pin_phase_adjust_get - callback for get pin phase adjust value
  * @pin: pointer to a pin
  * Dpll subsystem callback. Wraps a handler for setting phase adjust on input
  * pin.
  *
- * Context: Calls a function which acquires pf->dplls.lock
+ * Context: Calls a function which acquires and releases pf->dplls.lock
  * Return:
  * * 0 - success
  * * negative - error
                                             ICE_DPLL_PIN_TYPE_OUTPUT);
 }
 
+/**
+ * ice_dpll_sw_phase_adjust_get - callback for get SW pin phase adjust
+ * @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
+ * @phase_adjust: on success holds phase adjust value
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Wraps a handler for getting phase adjust on sw
+ * pin.
+ *
+ * Context: Calls a function which acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_sw_phase_adjust_get(const struct dpll_pin *pin, void *pin_priv,
+                            const struct dpll_device *dpll, void *dpll_priv,
+                            s32 *phase_adjust,
+                            struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+
+       if (p->direction == DPLL_PIN_DIRECTION_INPUT)
+               return ice_dpll_pin_phase_adjust_get(p->input->pin, p->input,
+                                                    dpll, dpll_priv,
+                                                    phase_adjust, extack);
+       else
+               return ice_dpll_pin_phase_adjust_get(p->output->pin, p->output,
+                                                    dpll, dpll_priv,
+                                                    phase_adjust, extack);
+}
+
+/**
+ * ice_dpll_sw_phase_adjust_set - callback for set SW pin phase adjust value
+ * @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
+ * @phase_adjust: phase_adjust to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Wraps a handler for setting phase adjust on output
+ * pin.
+ *
+ * Context: Calls a function which acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_sw_phase_adjust_set(const struct dpll_pin *pin, void *pin_priv,
+                            const struct dpll_device *dpll, void *dpll_priv,
+                            s32 phase_adjust,
+                            struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+
+       if (!p->active) {
+               NL_SET_ERR_MSG(extack, "pin is not active");
+               return -EINVAL;
+       }
+       if (p->direction == DPLL_PIN_DIRECTION_INPUT)
+               return ice_dpll_pin_phase_adjust_set(p->input->pin, p->input,
+                                                    dpll, dpll_priv,
+                                                    phase_adjust, extack,
+                                                    ICE_DPLL_PIN_TYPE_INPUT);
+       else
+               return ice_dpll_pin_phase_adjust_set(p->output->pin, p->output,
+                                                    dpll, dpll_priv,
+                                                    phase_adjust, extack,
+                                                    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
 #define ICE_DPLL_PHASE_OFFSET_DIVIDER  100
 #define ICE_DPLL_PHASE_OFFSET_FACTOR           \
        (DPLL_PHASE_OFFSET_DIVIDER / ICE_DPLL_PHASE_OFFSET_DIVIDER)
                          const struct dpll_device *dpll, void *dpll_priv,
                          s64 *phase_offset, struct netlink_ext_ack *extack)
 {
+       struct ice_dpll_pin *p = pin_priv;
        struct ice_dpll *d = dpll_priv;
        struct ice_pf *pf = d->pf;
 
        mutex_lock(&pf->dplls.lock);
-       if (d->active_input == pin)
+       if (d->active_input == pin || (p->input &&
+                                      d->active_input == p->input->pin))
                *phase_offset = d->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
        else
                *phase_offset = 0;
        return 0;
 }
 
+/**
+ * ice_dpll_sw_esync_set - callback for setting embedded sync on SW pin
+ * @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
+ * @freq: requested embedded sync frequency
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting embedded sync frequency value
+ * on SW pin.
+ *
+ * Context: Calls a function which acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_sw_esync_set(const struct dpll_pin *pin, void *pin_priv,
+                     const struct dpll_device *dpll, void *dpll_priv,
+                     u64 freq, struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+
+       if (!p->active) {
+               NL_SET_ERR_MSG(extack, "pin is not active");
+               return -EINVAL;
+       }
+       if (p->direction == DPLL_PIN_DIRECTION_INPUT)
+               return ice_dpll_input_esync_set(p->input->pin, p->input, dpll,
+                                               dpll_priv, freq, extack);
+       else
+               return ice_dpll_output_esync_set(p->output->pin, p->output,
+                                                dpll, dpll_priv, freq, extack);
+}
+
+/**
+ * ice_dpll_sw_esync_get - callback for getting embedded sync on SW pin
+ * @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
+ * @esync: on success holds embedded sync frequency and properties
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting embedded sync frequency value
+ * of SW pin.
+ *
+ * Context: Calls a function which acquires and releases pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_sw_esync_get(const struct dpll_pin *pin, void *pin_priv,
+                     const struct dpll_device *dpll, void *dpll_priv,
+                     struct dpll_pin_esync *esync,
+                     struct netlink_ext_ack *extack)
+{
+       struct ice_dpll_pin *p = pin_priv;
+
+       if (p->direction == DPLL_PIN_DIRECTION_INPUT)
+               return ice_dpll_input_esync_get(p->input->pin, p->input, dpll,
+                                               dpll_priv, esync, extack);
+       else
+               return ice_dpll_output_esync_get(p->output->pin, p->output,
+                                                dpll, dpll_priv, esync,
+                                                extack);
+}
+
 /**
  * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
  * @pin: pointer to a pin
        .direction_get = ice_dpll_input_direction,
 };
 
+static const struct dpll_pin_ops ice_dpll_pin_sma_ops = {
+       .state_on_dpll_set = ice_dpll_sma_pin_state_set,
+       .state_on_dpll_get = ice_dpll_sw_pin_state_get,
+       .direction_get = ice_dpll_pin_sw_direction_get,
+       .direction_set = ice_dpll_pin_sma_direction_set,
+       .prio_get = ice_dpll_sw_input_prio_get,
+       .prio_set = ice_dpll_sw_input_prio_set,
+       .frequency_get = ice_dpll_sw_pin_frequency_get,
+       .frequency_set = ice_dpll_sw_pin_frequency_set,
+       .phase_adjust_get = ice_dpll_sw_phase_adjust_get,
+       .phase_adjust_set = ice_dpll_sw_phase_adjust_set,
+       .phase_offset_get = ice_dpll_phase_offset_get,
+       .esync_set = ice_dpll_sw_esync_set,
+       .esync_get = ice_dpll_sw_esync_get,
+};
+
+static const struct dpll_pin_ops ice_dpll_pin_ufl_ops = {
+       .state_on_dpll_set = ice_dpll_ufl_pin_state_set,
+       .state_on_dpll_get = ice_dpll_sw_pin_state_get,
+       .direction_get = ice_dpll_pin_sw_direction_get,
+       .frequency_get = ice_dpll_sw_pin_frequency_get,
+       .frequency_set = ice_dpll_sw_pin_frequency_set,
+       .esync_set = ice_dpll_sw_esync_set,
+       .esync_get = ice_dpll_sw_esync_get,
+       .phase_adjust_get = ice_dpll_sw_phase_adjust_get,
+       .phase_adjust_set = ice_dpll_sw_phase_adjust_set,
+       .phase_offset_get = ice_dpll_phase_offset_get,
+};
+
 static const struct dpll_pin_ops ice_dpll_input_ops = {
        .frequency_get = ice_dpll_input_frequency_get,
        .frequency_set = ice_dpll_input_frequency_set,
        int i;
 
        for (i = 0; i < count; i++)
-               dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
+               if (!pins[i].hidden)
+                       dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
 }
 
 /**
        int ret, i;
 
        for (i = 0; i < count; i++) {
-               ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
-               if (ret)
-                       goto unregister_pins;
+               if (!pins[i].hidden) {
+                       ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
+                       if (ret)
+                               goto unregister_pins;
+               }
        }
 
        return 0;
 
 unregister_pins:
        while (--i >= 0)
-               dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
+               if (!pins[i].hidden)
+                       dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
        return ret;
 }
 
                ice_dpll_unregister_pins(de->dpll, outputs,
                                         &ice_dpll_output_ops, num_outputs);
                ice_dpll_release_pins(outputs, num_outputs);
+               if (!pf->dplls.generic) {
+                       ice_dpll_deinit_direct_pins(cgu, pf->dplls.ufl,
+                                                   ICE_DPLL_PIN_SW_NUM,
+                                                   &ice_dpll_pin_ufl_ops,
+                                                   pf->dplls.pps.dpll,
+                                                   pf->dplls.eec.dpll);
+                       ice_dpll_deinit_direct_pins(cgu, pf->dplls.sma,
+                                                   ICE_DPLL_PIN_SW_NUM,
+                                                   &ice_dpll_pin_sma_ops,
+                                                   pf->dplls.pps.dpll,
+                                                   pf->dplls.eec.dpll);
+               }
        }
 }
 
  */
 static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
 {
-       u32 rclk_idx;
-       int ret;
+       int ret, count;
 
        ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
                                        pf->dplls.num_inputs,
                                        pf->dplls.eec.dpll, pf->dplls.pps.dpll);
        if (ret)
                return ret;
+       count = pf->dplls.num_inputs;
        if (cgu) {
                ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
-                                               pf->dplls.num_inputs,
+                                               count,
                                                pf->dplls.num_outputs,
                                                &ice_dpll_output_ops,
                                                pf->dplls.eec.dpll,
                                                pf->dplls.pps.dpll);
                if (ret)
                        goto deinit_inputs;
+               count += pf->dplls.num_outputs;
+               if (!pf->dplls.generic) {
+                       ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.sma,
+                                                       count,
+                                                       ICE_DPLL_PIN_SW_NUM,
+                                                       &ice_dpll_pin_sma_ops,
+                                                       pf->dplls.eec.dpll,
+                                                       pf->dplls.pps.dpll);
+                       if (ret)
+                               goto deinit_outputs;
+                       count += ICE_DPLL_PIN_SW_NUM;
+                       ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.ufl,
+                                                       count,
+                                                       ICE_DPLL_PIN_SW_NUM,
+                                                       &ice_dpll_pin_ufl_ops,
+                                                       pf->dplls.eec.dpll,
+                                                       pf->dplls.pps.dpll);
+                       if (ret)
+                               goto deinit_sma;
+                       count += ICE_DPLL_PIN_SW_NUM;
+               }
+       } else {
+               count += pf->dplls.num_outputs + 2 * ICE_DPLL_PIN_SW_NUM;
        }
-       rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
-       ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
+       ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, count + pf->hw.pf_id,
                                      &ice_dpll_rclk_ops);
        if (ret)
-               goto deinit_outputs;
+               goto deinit_ufl;
 
        return 0;
+deinit_ufl:
+       ice_dpll_deinit_direct_pins(cgu, pf->dplls.ufl,
+                                   ICE_DPLL_PIN_SW_NUM,
+                                   &ice_dpll_pin_ufl_ops,
+                                   pf->dplls.pps.dpll, pf->dplls.eec.dpll);
+deinit_sma:
+       ice_dpll_deinit_direct_pins(cgu, pf->dplls.sma,
+                                   ICE_DPLL_PIN_SW_NUM,
+                                   &ice_dpll_pin_sma_ops,
+                                   pf->dplls.pps.dpll, pf->dplls.eec.dpll);
 deinit_outputs:
        ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
                                    pf->dplls.num_outputs,
        default:
                return -EINVAL;
        }
-       if (num_pins != ice_cgu_get_num_pins(hw, input))
+       if (num_pins != ice_cgu_get_num_pins(hw, input)) {
+               pf->dplls.generic = true;
                return ice_dpll_init_info_pins_generic(pf, input);
+       }
 
        for (i = 0; i < num_pins; i++) {
                caps = 0;
                                return ret;
                        caps |= (DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
                                 DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE);
+                       if (ice_dpll_is_sw_pin(pf, i, true))
+                               pins[i].hidden = true;
                } else {
                        ret = ice_cgu_get_output_pin_state_caps(hw, i, &caps);
                        if (ret)
                                return ret;
+                       if (ice_dpll_is_sw_pin(pf, i, false))
+                               pins[i].hidden = true;
                }
                ice_dpll_phase_range_set(&pins[i].prop.phase_range,
                                         phase_adj_max);
                                         ICE_DPLL_PIN_TYPE_RCLK_INPUT, NULL);
 }
 
+/**
+ * ice_dpll_init_info_sw_pins - initializes software controlled pin information
+ * @pf: board private structure
+ *
+ * Init information for software controlled pins, cache them in
+ * pf->dplls.sma and pf->dplls.ufl.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info_sw_pins(struct ice_pf *pf)
+{
+       u8 freq_supp_num, pin_abs_idx, input_idx_offset = 0;
+       struct ice_dplls *d = &pf->dplls;
+       struct ice_dpll_pin *pin;
+       u32 phase_adj_max, caps;
+       int i, ret;
+
+       if (pf->hw.device_id == ICE_DEV_ID_E810C_QSFP)
+               input_idx_offset = ICE_E810_RCLK_PINS_NUM;
+       phase_adj_max = max(d->input_phase_adj_max, d->output_phase_adj_max);
+       caps = DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+       for (i = 0; i < ICE_DPLL_PIN_SW_NUM; i++) {
+               pin = &d->sma[i];
+               pin->idx = i;
+               pin->prop.type = DPLL_PIN_TYPE_EXT;
+               pin_abs_idx = ICE_DPLL_PIN_SW_INPUT_ABS(i) + input_idx_offset;
+               pin->prop.freq_supported =
+                       ice_cgu_get_pin_freq_supp(&pf->hw, pin_abs_idx,
+                                                 true, &freq_supp_num);
+               pin->prop.freq_supported_num = freq_supp_num;
+               pin->prop.capabilities =
+                       (DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE |
+                        DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
+                        caps);
+               pin->pf = pf;
+               pin->prop.board_label = ice_dpll_sw_pin_sma[i];
+               pin->input = &d->inputs[pin_abs_idx];
+               pin->output = &d->outputs[ICE_DPLL_PIN_SW_OUTPUT_ABS(i)];
+               ice_dpll_phase_range_set(&pin->prop.phase_range, phase_adj_max);
+       }
+       for (i = 0; i < ICE_DPLL_PIN_SW_NUM; i++) {
+               pin = &d->ufl[i];
+               pin->idx = i;
+               pin->prop.type = DPLL_PIN_TYPE_EXT;
+               pin->prop.capabilities = caps;
+               pin->pf = pf;
+               pin->prop.board_label = ice_dpll_sw_pin_ufl[i];
+               if (i == ICE_DPLL_PIN_SW_1_IDX) {
+                       pin->direction = DPLL_PIN_DIRECTION_OUTPUT;
+                       pin_abs_idx = ICE_DPLL_PIN_SW_OUTPUT_ABS(i);
+                       pin->prop.freq_supported =
+                               ice_cgu_get_pin_freq_supp(&pf->hw, pin_abs_idx,
+                                                         false,
+                                                         &freq_supp_num);
+                       pin->prop.freq_supported_num = freq_supp_num;
+                       pin->input = NULL;
+                       pin->output = &d->outputs[pin_abs_idx];
+               } else if (i == ICE_DPLL_PIN_SW_2_IDX) {
+                       pin->direction = DPLL_PIN_DIRECTION_INPUT;
+                       pin_abs_idx = ICE_DPLL_PIN_SW_INPUT_ABS(i) +
+                                     input_idx_offset;
+                       pin->output = NULL;
+                       pin->input = &d->inputs[pin_abs_idx];
+                       pin->prop.freq_supported =
+                               ice_cgu_get_pin_freq_supp(&pf->hw, pin_abs_idx,
+                                                         true, &freq_supp_num);
+                       pin->prop.freq_supported_num = freq_supp_num;
+                       pin->prop.capabilities =
+                               (DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
+                                caps);
+               }
+               ice_dpll_phase_range_set(&pin->prop.phase_range, phase_adj_max);
+       }
+       ret = ice_dpll_pin_state_update(pf, pin, ICE_DPLL_PIN_TYPE_SOFTWARE,
+                                       NULL);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
 /**
  * ice_dpll_init_pins_info - init pins info wrapper
  * @pf: board private structure
                return ice_dpll_init_info_direct_pins(pf, pin_type);
        case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
                return ice_dpll_init_info_rclk_pin(pf);
+       case ICE_DPLL_PIN_TYPE_SOFTWARE:
+               return ice_dpll_init_info_sw_pins(pf);
        default:
                return -EINVAL;
        }
                ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
                if (ret)
                        goto deinit_info;
+               ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_SOFTWARE);
+               if (ret)
+                       goto deinit_info;
        }
 
        ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,