mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

The descriptor structure is the same in ice, ixgbe and i40e. Move it to common libie header to use it across different driver. Leave device specific adminq commands in separate folders. This lead to a change that need to be done in filling/getting descriptor: - previous: struct specific_desc *cmd; cmd = &desc.params.specific_desc; - now: struct specific_desc *cmd; cmd = libie_aq_raw(&desc); Do this changes across the driver to allow clean build. The casting only have to be done in case of specific descriptors, for generic one union can still be used. Changes beside code moving: - change ICE_ prefix to LIBIE_ prefix (ice_ and libie_ too) - remove shift variables not otherwise needed (in libie_aq_flags) - fill/get descriptor data based on desc.params.raw whenever the descriptor isn't defined in libie - move defines from the libie_aq_sth structure outside - add libie_aq_raw helper and use it instead of explicit casting Reviewed by: Przemek Kitszel <przemyslaw.kitszel@intel.com> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com> Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com> Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel) Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
3831 lines
107 KiB
C
3831 lines
107 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (C) 2022, Intel Corporation. */
|
|
|
|
#include "ice.h"
|
|
#include "ice_lib.h"
|
|
#include "ice_trace.h"
|
|
#include <linux/dpll.h>
|
|
|
|
#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_ESYNC_PULSE_HIGH_PERCENT 25
|
|
#define ICE_DPLL_PIN_GEN_RCLK_FREQ 1953125
|
|
#define ICE_DPLL_PIN_PRIO_OUTPUT 0xff
|
|
#define ICE_DPLL_INPUT_REF_NUM 10
|
|
#define ICE_DPLL_PHASE_OFFSET_PERIOD 2
|
|
#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))
|
|
|
|
#define ICE_SR_PFA_DPLL_DEFAULTS 0x152
|
|
#define ICE_DPLL_PFA_REF_SYNC_TYPE 0x2420
|
|
#define ICE_DPLL_PFA_REF_SYNC_TYPE2 0x2424
|
|
#define ICE_DPLL_PFA_END 0xFFFF
|
|
#define ICE_DPLL_PFA_HEADER_LEN 4
|
|
#define ICE_DPLL_PFA_ENTRY_LEN 3
|
|
#define ICE_DPLL_PFA_MAILBOX_REF_SYNC_PIN_S 4
|
|
#define ICE_DPLL_PFA_MASK_OFFSET 1
|
|
#define ICE_DPLL_PFA_VALUE_OFFSET 2
|
|
|
|
#define ICE_DPLL_E810C_SFP_NC_PINS 2
|
|
#define ICE_DPLL_E810C_SFP_NC_START 4
|
|
|
|
/**
|
|
* enum ice_dpll_pin_type - enumerate ice pin types:
|
|
* @ICE_DPLL_PIN_INVALID: invalid pin type
|
|
* @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: error reporting
|
|
*
|
|
* If reset is in progress, fill extack with error.
|
|
*
|
|
* Return:
|
|
* * false - no reset in progress
|
|
* * true - reset in progress
|
|
*/
|
|
static bool ice_dpll_is_reset(struct ice_pf *pf, struct netlink_ext_ack *extack)
|
|
{
|
|
if (ice_is_reset_in_progress(pf->state)) {
|
|
NL_SET_ERR_MSG(extack, "PF reset in progress");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pin_freq_set - set pin's frequency
|
|
* @pf: private board structure
|
|
* @pin: pointer to a pin
|
|
* @pin_type: type of pin being configured
|
|
* @freq: frequency to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Set requested frequency on a pin.
|
|
*
|
|
* Context: Called under pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error on AQ or wrong pin type given
|
|
*/
|
|
static int
|
|
ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
|
|
enum ice_dpll_pin_type pin_type, const u32 freq,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u8 flags;
|
|
int ret;
|
|
|
|
switch (pin_type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
|
|
ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
|
|
pin->flags[0], freq, 0);
|
|
break;
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
|
|
ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
|
|
0, freq, 0);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (ret) {
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to set pin freq:%u on pin:%u",
|
|
ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status),
|
|
freq, pin->idx);
|
|
return ret;
|
|
}
|
|
pin->freq = freq;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_frequency_set - wrapper for pin callback for set frequency
|
|
* @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: frequency to be set
|
|
* @extack: error reporting
|
|
* @pin_type: type of pin being configured
|
|
*
|
|
* Wraps internal set frequency command on a pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error pin not found or couldn't set in hw
|
|
*/
|
|
static int
|
|
ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_device *dpll, void *dpll_priv,
|
|
const u32 frequency,
|
|
struct netlink_ext_ack *extack,
|
|
enum ice_dpll_pin_type pin_type)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency, extack);
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_frequency_set - input pin callback for set frequency
|
|
* @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: frequency to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Wraps internal set frequency command on a pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error pin not found or couldn't set in hw
|
|
*/
|
|
static int
|
|
ice_dpll_input_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)
|
|
{
|
|
return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
|
|
extack, ICE_DPLL_PIN_TYPE_INPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_frequency_set - output pin callback for set frequency
|
|
* @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: frequency to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Wraps internal set frequency command on a pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error pin not found or couldn't set in hw
|
|
*/
|
|
static int
|
|
ice_dpll_output_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)
|
|
{
|
|
return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
|
|
extack, ICE_DPLL_PIN_TYPE_OUTPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_frequency_get - wrapper for pin callback for get frequency
|
|
* @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
|
|
* @pin_type: type of pin being configured
|
|
*
|
|
* Wraps internal get frequency command of a pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error pin not found or couldn't get from hw
|
|
*/
|
|
static int
|
|
ice_dpll_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,
|
|
enum ice_dpll_pin_type pin_type)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
*frequency = p->freq;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_frequency_get - input pin callback for get frequency
|
|
* @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
|
|
*
|
|
* Wraps internal get frequency command of a input pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error pin not found or couldn't get from hw
|
|
*/
|
|
static int
|
|
ice_dpll_input_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)
|
|
{
|
|
return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
|
|
extack, ICE_DPLL_PIN_TYPE_INPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_frequency_get - output pin callback for get frequency
|
|
* @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
|
|
*
|
|
* Wraps internal get frequency command of a pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error pin not found or couldn't get from hw
|
|
*/
|
|
static int
|
|
ice_dpll_output_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)
|
|
{
|
|
return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
|
|
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
|
|
* @pin: pointer to a pin
|
|
* @dpll_idx: dpll index to connect to output pin
|
|
* @pin_type: type of pin being enabled
|
|
* @extack: error reporting
|
|
*
|
|
* Enable a pin on both dplls. Store current state in pin->flags.
|
|
*
|
|
* Context: Called under pf->dplls.lock
|
|
* Return:
|
|
* * 0 - OK
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
|
|
u8 dpll_idx, enum ice_dpll_pin_type pin_type,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u8 flags = 0;
|
|
int ret;
|
|
|
|
switch (pin_type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
|
|
flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
|
|
flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
|
|
ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
|
|
break;
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_SRC_SEL;
|
|
if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
|
|
flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
|
|
flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
|
|
ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, dpll_idx,
|
|
0, 0);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (ret)
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to enable %s pin:%u",
|
|
ret, ice_aq_str(hw->adminq.sq_last_status),
|
|
pin_type_name[pin_type], pin->idx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pin_disable - disable a pin on dplls
|
|
* @hw: board private hw structure
|
|
* @pin: pointer to a pin
|
|
* @pin_type: type of pin being disabled
|
|
* @extack: error reporting
|
|
*
|
|
* Disable a pin on both dplls. Store current state in pin->flags.
|
|
*
|
|
* Context: Called under pf->dplls.lock
|
|
* Return:
|
|
* * 0 - OK
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
|
|
enum ice_dpll_pin_type pin_type,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u8 flags = 0;
|
|
int ret;
|
|
|
|
switch (pin_type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
|
|
flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
|
|
ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
|
|
break;
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
|
|
flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
|
|
ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (ret)
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to disable %s pin:%u",
|
|
ret, ice_aq_str(hw->adminq.sq_last_status),
|
|
pin_type_name[pin_type], pin->idx);
|
|
|
|
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
|
|
* @pin: structure with pin attributes to be updated
|
|
* @pin_type: type of pin being updated
|
|
* @extack: error reporting
|
|
*
|
|
* Determine pin current state and frequency, then update struct
|
|
* holding the pin info. For input pin states are separated for each
|
|
* dpll, for rclk pins states are separated for each parent.
|
|
*
|
|
* Context: Called under pf->dplls.lock
|
|
* Return:
|
|
* * 0 - OK
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
|
|
enum ice_dpll_pin_type pin_type,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
|
|
int ret;
|
|
|
|
switch (pin_type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, &pin->status,
|
|
NULL, NULL, &pin->flags[0],
|
|
&pin->freq, &pin->phase_adjust);
|
|
if (ret)
|
|
goto err;
|
|
if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
|
|
if (pin->pin) {
|
|
pin->state[pf->dplls.eec.dpll_idx] =
|
|
pin->pin == pf->dplls.eec.active_input ?
|
|
DPLL_PIN_STATE_CONNECTED :
|
|
DPLL_PIN_STATE_SELECTABLE;
|
|
pin->state[pf->dplls.pps.dpll_idx] =
|
|
pin->pin == pf->dplls.pps.active_input ?
|
|
DPLL_PIN_STATE_CONNECTED :
|
|
DPLL_PIN_STATE_SELECTABLE;
|
|
} else {
|
|
pin->state[pf->dplls.eec.dpll_idx] =
|
|
DPLL_PIN_STATE_SELECTABLE;
|
|
pin->state[pf->dplls.pps.dpll_idx] =
|
|
DPLL_PIN_STATE_SELECTABLE;
|
|
}
|
|
} else {
|
|
pin->state[pf->dplls.eec.dpll_idx] =
|
|
DPLL_PIN_STATE_DISCONNECTED;
|
|
pin->state[pf->dplls.pps.dpll_idx] =
|
|
DPLL_PIN_STATE_DISCONNECTED;
|
|
}
|
|
break;
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
|
|
&pin->flags[0], &parent,
|
|
&pin->freq, NULL);
|
|
if (ret)
|
|
goto err;
|
|
|
|
parent &= ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL;
|
|
if (ICE_AQC_GET_CGU_OUT_CFG_OUT_EN & pin->flags[0]) {
|
|
pin->state[pf->dplls.eec.dpll_idx] =
|
|
parent == pf->dplls.eec.dpll_idx ?
|
|
DPLL_PIN_STATE_CONNECTED :
|
|
DPLL_PIN_STATE_DISCONNECTED;
|
|
pin->state[pf->dplls.pps.dpll_idx] =
|
|
parent == pf->dplls.pps.dpll_idx ?
|
|
DPLL_PIN_STATE_CONNECTED :
|
|
DPLL_PIN_STATE_DISCONNECTED;
|
|
} else {
|
|
pin->state[pf->dplls.eec.dpll_idx] =
|
|
DPLL_PIN_STATE_DISCONNECTED;
|
|
pin->state[pf->dplls.pps.dpll_idx] =
|
|
DPLL_PIN_STATE_DISCONNECTED;
|
|
}
|
|
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 (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:
|
|
ret = ice_dpll_sw_pins_update(pf);
|
|
if (ret)
|
|
goto err;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
err:
|
|
if (extack)
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to update %s pin:%u",
|
|
ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status),
|
|
pin_type_name[pin_type], pin->idx);
|
|
else
|
|
dev_err_ratelimited(ice_pf_to_dev(pf),
|
|
"err:%d %s failed to update %s pin:%u\n",
|
|
ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status),
|
|
pin_type_name[pin_type], pin->idx);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_hw_input_prio_set - set input priority value in hardware
|
|
* @pf: board private structure
|
|
* @dpll: ice dpll pointer
|
|
* @pin: ice pin pointer
|
|
* @prio: priority value being set on a dpll
|
|
* @extack: error reporting
|
|
*
|
|
* Internal wrapper for setting the priority in the hardware.
|
|
*
|
|
* Context: Called under pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int
|
|
ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
|
|
struct ice_dpll_pin *pin, const u32 prio,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
int ret;
|
|
|
|
ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
|
|
(u8)prio);
|
|
if (ret)
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to set pin prio:%u on pin:%u",
|
|
ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status),
|
|
prio, pin->idx);
|
|
else
|
|
dpll->input_prio[pin->idx] = prio;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_lock_status_get - get dpll lock status callback
|
|
* @dpll: registered dpll pointer
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @status: on success holds dpll's lock status
|
|
* @status_error: status error value
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback, provides dpll's lock status.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int
|
|
ice_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
enum dpll_lock_status *status,
|
|
enum dpll_lock_status_error *status_error,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
*status = d->dpll_state;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_mode_get - get dpll's working mode
|
|
* @dpll: registered dpll pointer
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @mode: on success holds current working mode of dpll
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Provides working mode of dpll.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int ice_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
enum dpll_mode *mode,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
*mode = d->mode;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_phase_offset_monitor_set - set phase offset monitor state
|
|
* @dpll: registered dpll pointer
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @state: feature state to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Enable/disable phase offset monitor feature of dpll.
|
|
*
|
|
* Context: Acquires and releases pf->dplls.lock
|
|
* Return: 0 - success
|
|
*/
|
|
static int ice_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
|
|
void *dpll_priv,
|
|
enum dpll_feature_state state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (state == DPLL_FEATURE_STATE_ENABLE)
|
|
d->phase_offset_monitor_period = ICE_DPLL_PHASE_OFFSET_PERIOD;
|
|
else
|
|
d->phase_offset_monitor_period = 0;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_phase_offset_monitor_get - get phase offset monitor state
|
|
* @dpll: registered dpll pointer
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @state: on success holds current state of phase offset monitor
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Provides current state of phase offset monitor
|
|
* features on dpll device.
|
|
*
|
|
* Context: Acquires and releases pf->dplls.lock
|
|
* Return: 0 - success
|
|
*/
|
|
static int ice_dpll_phase_offset_monitor_get(const struct dpll_device *dpll,
|
|
void *dpll_priv,
|
|
enum dpll_feature_state *state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (d->phase_offset_monitor_period)
|
|
*state = DPLL_FEATURE_STATE_ENABLE;
|
|
else
|
|
*state = DPLL_FEATURE_STATE_DISABLE;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pin_state_set - set 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
|
|
* @enable: if pin shalll be enabled
|
|
* @extack: error reporting
|
|
* @pin_type: type of a pin
|
|
*
|
|
* Set pin state on a pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - OK or no change required
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_device *dpll, void *dpll_priv,
|
|
bool enable, struct netlink_ext_ack *extack,
|
|
enum ice_dpll_pin_type pin_type)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (enable)
|
|
ret = ice_dpll_pin_enable(&pf->hw, p, d->dpll_idx, pin_type,
|
|
extack);
|
|
else
|
|
ret = ice_dpll_pin_disable(&pf->hw, p, pin_type, extack);
|
|
if (!ret)
|
|
ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_state_set - enable/disable output pin on dpll device
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @dpll: dpll being configured
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @state: state of pin to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Set given state on output type pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - successfully enabled mode
|
|
* * negative - failed to enable mode
|
|
*/
|
|
static int
|
|
ice_dpll_output_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)
|
|
{
|
|
bool enable = state == DPLL_PIN_STATE_CONNECTED;
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_dpll *d = dpll_priv;
|
|
|
|
if (state == DPLL_PIN_STATE_SELECTABLE)
|
|
return -EINVAL;
|
|
if (!enable && p->state[d->dpll_idx] == DPLL_PIN_STATE_DISCONNECTED)
|
|
return 0;
|
|
|
|
return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
|
|
extack, ICE_DPLL_PIN_TYPE_OUTPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_state_set - enable/disable input pin on dpll levice
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @dpll: dpll being configured
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @state: state of pin to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Enables given mode on input type pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - successfully enabled mode
|
|
* * negative - failed to enable mode
|
|
*/
|
|
static int
|
|
ice_dpll_input_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)
|
|
{
|
|
bool enable = state == DPLL_PIN_STATE_SELECTABLE;
|
|
|
|
return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
|
|
extack, ICE_DPLL_PIN_TYPE_INPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pin_state_get - set 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
|
|
* @pin_type: type of questioned pin
|
|
*
|
|
* Determine pin state set it on a pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failed to get state
|
|
*/
|
|
static int
|
|
ice_dpll_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,
|
|
enum ice_dpll_pin_type pin_type)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
ret = ice_dpll_pin_state_update(pf, p, pin_type, extack);
|
|
if (ret)
|
|
goto unlock;
|
|
if (pin_type == ICE_DPLL_PIN_TYPE_INPUT ||
|
|
pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
|
|
*state = p->state[d->dpll_idx];
|
|
ret = 0;
|
|
unlock:
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_state_get - get output 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: on success holds state of the pin
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Check state of a pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failed to get state
|
|
*/
|
|
static int
|
|
ice_dpll_output_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)
|
|
{
|
|
return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
|
|
extack, ICE_DPLL_PIN_TYPE_OUTPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_state_get - get input 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: on success holds state of the pin
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Check state of a input pin.
|
|
*
|
|
* Context: Calls a function which acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failed to get state
|
|
*/
|
|
static int
|
|
ice_dpll_input_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)
|
|
{
|
|
return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
|
|
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
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @dpll: registered dpll pointer
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @prio: on success - returns input priority on dpll
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting priority of a input pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int
|
|
ice_dpll_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);
|
|
*prio = d->input_prio[p->idx];
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_prio_set - set dpll input prio
|
|
* @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
|
|
* @prio: input priority to be set on dpll
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for setting priority of a input pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int
|
|
ice_dpll_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 (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
ret = ice_dpll_hw_input_prio_set(pf, d, p, prio, extack);
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
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
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @dpll: registered dpll pointer
|
|
* @dpll_priv: private data pointer passed on dpll registration
|
|
* @direction: holds input pin direction
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting direction of a input pin.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
*/
|
|
static int
|
|
ice_dpll_input_direction(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)
|
|
{
|
|
*direction = DPLL_PIN_DIRECTION_INPUT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_direction - callback for get output 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: holds output pin direction
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting direction of an output pin.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
*/
|
|
static int
|
|
ice_dpll_output_direction(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)
|
|
{
|
|
*direction = DPLL_PIN_DIRECTION_OUTPUT;
|
|
|
|
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
|
|
* @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 pin phase_adjust value
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting phase adjust value of a pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_pin_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;
|
|
struct ice_pf *pf = p->pf;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
*phase_adjust = p->phase_adjust;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pin_phase_adjust_set - helper for setting a 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
|
|
* @type: type of a pin
|
|
*
|
|
* Helper for dpll subsystem callback. Handler for setting phase adjust value
|
|
* of a pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_pin_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,
|
|
enum ice_dpll_pin_type type)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
u8 flag, flags_en = 0;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
switch (type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
flag = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_DELAY;
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
|
|
flags_en |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN)
|
|
flags_en |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
|
|
ret = ice_aq_set_input_pin_cfg(&pf->hw, p->idx, flag, flags_en,
|
|
0, phase_adjust);
|
|
break;
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
flag = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_PHASE;
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_OUT_EN)
|
|
flag |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
|
|
flag |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
|
|
ret = ice_aq_set_output_pin_cfg(&pf->hw, p->idx, flag, 0, 0,
|
|
phase_adjust);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
if (!ret)
|
|
p->phase_adjust = phase_adjust;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
if (ret)
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to set pin phase_adjust:%d for pin:%u on dpll:%u",
|
|
ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status),
|
|
phase_adjust, p->idx, d->dpll_idx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_phase_adjust_set - callback for set input 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: phase_adjust to be set
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Wraps a handler for setting phase adjust on input
|
|
* pin.
|
|
*
|
|
* Context: Calls a function which acquires and releases pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_input_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)
|
|
{
|
|
return ice_dpll_pin_phase_adjust_set(pin, pin_priv, dpll, dpll_priv,
|
|
phase_adjust, extack,
|
|
ICE_DPLL_PIN_TYPE_INPUT);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_phase_adjust_set - callback for set output 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: 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 pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_output_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)
|
|
{
|
|
return ice_dpll_pin_phase_adjust_set(pin, pin_priv, dpll, dpll_priv,
|
|
phase_adjust, extack,
|
|
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)
|
|
/**
|
|
* ice_dpll_phase_offset_get - callback for get dpll phase shift 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_offset: on success holds pin phase_offset value
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting phase shift value between
|
|
* dpll's input and output.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv,
|
|
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 || (p->input &&
|
|
d->active_input == p->input->pin))
|
|
*phase_offset = d->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
|
|
else if (d->phase_offset_monitor_period)
|
|
*phase_offset = p->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR;
|
|
else
|
|
*phase_offset = 0;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_esync_set - callback for setting embedded sync
|
|
* @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 output pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_output_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;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
u8 flags = 0;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_OUT_EN)
|
|
flags = ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
|
|
if (freq == DPLL_PIN_FREQUENCY_1_HZ) {
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN) {
|
|
ret = 0;
|
|
} else {
|
|
flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
|
|
ret = ice_aq_set_output_pin_cfg(&pf->hw, p->idx, flags,
|
|
0, 0, 0);
|
|
}
|
|
} else {
|
|
if (!(p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)) {
|
|
ret = 0;
|
|
} else {
|
|
flags &= ~ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
|
|
ret = ice_aq_set_output_pin_cfg(&pf->hw, p->idx, flags,
|
|
0, 0, 0);
|
|
}
|
|
}
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_output_esync_get - callback for getting embedded sync config
|
|
* @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 pin properties
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting embedded sync frequency value
|
|
* and capabilities on output pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_output_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;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (!(p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_ABILITY) ||
|
|
p->freq != DPLL_PIN_FREQUENCY_10_MHZ) {
|
|
mutex_unlock(&pf->dplls.lock);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
esync->range = ice_esync_range;
|
|
esync->range_num = ARRAY_SIZE(ice_esync_range);
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN) {
|
|
esync->freq = DPLL_PIN_FREQUENCY_1_HZ;
|
|
esync->pulse = ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT;
|
|
} else {
|
|
esync->freq = 0;
|
|
esync->pulse = 0;
|
|
}
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_esync_set - callback for setting embedded sync
|
|
* @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 input pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_input_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;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
u8 flags_en = 0;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN)
|
|
flags_en = ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
|
|
if (freq == DPLL_PIN_FREQUENCY_1_HZ) {
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN) {
|
|
ret = 0;
|
|
} else {
|
|
flags_en |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
|
|
ret = ice_aq_set_input_pin_cfg(&pf->hw, p->idx, 0,
|
|
flags_en, 0, 0);
|
|
}
|
|
} else {
|
|
if (!(p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)) {
|
|
ret = 0;
|
|
} else {
|
|
flags_en &= ~ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
|
|
ret = ice_aq_set_input_pin_cfg(&pf->hw, p->idx, 0,
|
|
flags_en, 0, 0);
|
|
}
|
|
}
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_esync_get - callback for getting embedded sync config
|
|
* @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 pin properties
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for getting embedded sync frequency value
|
|
* and capabilities on input pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_input_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;
|
|
struct ice_dpll *d = dpll_priv;
|
|
struct ice_pf *pf = d->pf;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
mutex_lock(&pf->dplls.lock);
|
|
if (!(p->status & ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_CAP) ||
|
|
p->freq != DPLL_PIN_FREQUENCY_10_MHZ) {
|
|
mutex_unlock(&pf->dplls.lock);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
esync->range = ice_esync_range;
|
|
esync->range_num = ARRAY_SIZE(ice_esync_range);
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN) {
|
|
esync->freq = DPLL_PIN_FREQUENCY_1_HZ;
|
|
esync->pulse = ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT;
|
|
} else {
|
|
esync->freq = 0;
|
|
esync->pulse = 0;
|
|
}
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
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_input_ref_sync_set - callback for setting reference sync feature
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @ref_pin: pin pointer for reference sync pair
|
|
* @ref_pin_priv: private data pointer of ref_pin
|
|
* @state: requested state for reference sync for pin pair
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for setting reference sync frequency
|
|
* feature for input pin.
|
|
*
|
|
* Context: Acquires and releases pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_input_ref_sync_set(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_pin *ref_pin, void *ref_pin_priv,
|
|
const enum dpll_pin_state state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
struct ice_pf *pf = p->pf;
|
|
u8 flags_en = 0;
|
|
int ret;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
mutex_lock(&pf->dplls.lock);
|
|
|
|
if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN)
|
|
flags_en = ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
|
|
if (state == DPLL_PIN_STATE_CONNECTED)
|
|
flags_en |= ICE_AQC_CGU_IN_CFG_FLG2_REFSYNC_EN;
|
|
ret = ice_aq_set_input_pin_cfg(&pf->hw, p->idx, 0, flags_en, 0, 0);
|
|
if (!ret)
|
|
ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT,
|
|
extack);
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_input_ref_sync_get - callback for getting reference sync config
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @ref_pin: pin pointer for reference sync pair
|
|
* @ref_pin_priv: private data pointer of ref_pin
|
|
* @state: on success holds reference sync state for pin pair
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for setting reference sync frequency
|
|
* feature for input pin.
|
|
*
|
|
* Context: Acquires and releases pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_input_ref_sync_get(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_pin *ref_pin, void *ref_pin_priv,
|
|
enum dpll_pin_state *state,
|
|
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);
|
|
if (p->flags[0] & ICE_AQC_CGU_IN_CFG_FLG2_REFSYNC_EN)
|
|
*state = DPLL_PIN_STATE_CONNECTED;
|
|
else
|
|
*state = DPLL_PIN_STATE_DISCONNECTED;
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ice_dpll_sw_input_ref_sync_set - callback for setting reference sync feature
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @ref_pin: pin pointer for reference sync pair
|
|
* @ref_pin_priv: private data pointer of ref_pin
|
|
* @state: requested state for reference sync for pin pair
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for setting reference sync
|
|
* feature for input pins.
|
|
*
|
|
* Context: Calls a function which acquires and releases pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_sw_input_ref_sync_set(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_pin *ref_pin,
|
|
void *ref_pin_priv,
|
|
const enum dpll_pin_state state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
|
|
return ice_dpll_input_ref_sync_set(pin, p->input, ref_pin, ref_pin_priv,
|
|
state, extack);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_sw_input_ref_sync_get - callback for getting reference sync config
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @ref_pin: pin pointer for reference sync pair
|
|
* @ref_pin_priv: private data pointer of ref_pin
|
|
* @state: on success holds reference sync state for pin pair
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback. Handler for setting reference sync feature for
|
|
* input pins.
|
|
*
|
|
* Context: Calls a function which acquires and releases pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - error
|
|
*/
|
|
static int
|
|
ice_dpll_sw_input_ref_sync_get(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_pin *ref_pin,
|
|
void *ref_pin_priv,
|
|
enum dpll_pin_state *state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv;
|
|
|
|
return ice_dpll_input_ref_sync_get(pin, p->input, ref_pin, ref_pin_priv,
|
|
state, extack);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @parent_pin: pin parent pointer
|
|
* @parent_pin_priv: parent private data pointer passed on pin registration
|
|
* @state: state to be set on pin
|
|
* @extack: error reporting
|
|
*
|
|
* Dpll subsystem callback, set a state of a rclk pin on a parent pin
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int
|
|
ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_pin *parent_pin,
|
|
void *parent_pin_priv,
|
|
enum dpll_pin_state state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
|
|
bool enable = state == DPLL_PIN_STATE_CONNECTED;
|
|
struct ice_pf *pf = p->pf;
|
|
int ret = -EINVAL;
|
|
u32 hw_idx;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
hw_idx = parent->idx - pf->dplls.base_rclk_idx;
|
|
if (hw_idx >= pf->dplls.num_inputs)
|
|
goto unlock;
|
|
|
|
if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
|
|
(!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"pin:%u state:%u on parent:%u already set",
|
|
p->idx, state, parent->idx);
|
|
goto unlock;
|
|
}
|
|
ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
|
|
&p->freq);
|
|
if (ret)
|
|
NL_SET_ERR_MSG_FMT(extack,
|
|
"err:%d %s failed to set pin state:%u for pin:%u on parent:%u",
|
|
ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status),
|
|
state, p->idx, parent->idx);
|
|
unlock:
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
|
|
* @pin: pointer to a pin
|
|
* @pin_priv: private data pointer passed on pin registration
|
|
* @parent_pin: pin parent pointer
|
|
* @parent_pin_priv: pin parent priv data pointer passed on pin registration
|
|
* @state: on success holds pin state on parent pin
|
|
* @extack: error reporting
|
|
*
|
|
* dpll subsystem callback, get a state of a recovered clock pin.
|
|
*
|
|
* Context: Acquires pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - failure
|
|
*/
|
|
static int
|
|
ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
|
|
const struct dpll_pin *parent_pin,
|
|
void *parent_pin_priv,
|
|
enum dpll_pin_state *state,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
|
|
struct ice_pf *pf = p->pf;
|
|
int ret = -EINVAL;
|
|
u32 hw_idx;
|
|
|
|
if (ice_dpll_is_reset(pf, extack))
|
|
return -EBUSY;
|
|
|
|
mutex_lock(&pf->dplls.lock);
|
|
hw_idx = parent->idx - pf->dplls.base_rclk_idx;
|
|
if (hw_idx >= pf->dplls.num_inputs)
|
|
goto unlock;
|
|
|
|
ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
|
|
extack);
|
|
if (ret)
|
|
goto unlock;
|
|
|
|
*state = p->state[hw_idx];
|
|
ret = 0;
|
|
unlock:
|
|
mutex_unlock(&pf->dplls.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct dpll_pin_ops ice_dpll_rclk_ops = {
|
|
.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
|
|
.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
|
|
.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,
|
|
.ref_sync_set = ice_dpll_sw_input_ref_sync_set,
|
|
.ref_sync_get = ice_dpll_sw_input_ref_sync_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,
|
|
.state_on_dpll_get = ice_dpll_input_state_get,
|
|
.state_on_dpll_set = ice_dpll_input_state_set,
|
|
.prio_get = ice_dpll_input_prio_get,
|
|
.prio_set = ice_dpll_input_prio_set,
|
|
.direction_get = ice_dpll_input_direction,
|
|
.phase_adjust_get = ice_dpll_pin_phase_adjust_get,
|
|
.phase_adjust_set = ice_dpll_input_phase_adjust_set,
|
|
.phase_offset_get = ice_dpll_phase_offset_get,
|
|
.esync_set = ice_dpll_input_esync_set,
|
|
.esync_get = ice_dpll_input_esync_get,
|
|
.ref_sync_set = ice_dpll_input_ref_sync_set,
|
|
.ref_sync_get = ice_dpll_input_ref_sync_get,
|
|
};
|
|
|
|
static const struct dpll_pin_ops ice_dpll_output_ops = {
|
|
.frequency_get = ice_dpll_output_frequency_get,
|
|
.frequency_set = ice_dpll_output_frequency_set,
|
|
.state_on_dpll_get = ice_dpll_output_state_get,
|
|
.state_on_dpll_set = ice_dpll_output_state_set,
|
|
.direction_get = ice_dpll_output_direction,
|
|
.phase_adjust_get = ice_dpll_pin_phase_adjust_get,
|
|
.phase_adjust_set = ice_dpll_output_phase_adjust_set,
|
|
.esync_set = ice_dpll_output_esync_set,
|
|
.esync_get = ice_dpll_output_esync_get,
|
|
};
|
|
|
|
static const struct dpll_device_ops ice_dpll_ops = {
|
|
.lock_status_get = ice_dpll_lock_status_get,
|
|
.mode_get = ice_dpll_mode_get,
|
|
};
|
|
|
|
static const struct dpll_device_ops ice_dpll_pom_ops = {
|
|
.lock_status_get = ice_dpll_lock_status_get,
|
|
.mode_get = ice_dpll_mode_get,
|
|
.phase_offset_monitor_set = ice_dpll_phase_offset_monitor_set,
|
|
.phase_offset_monitor_get = ice_dpll_phase_offset_monitor_get,
|
|
};
|
|
|
|
/**
|
|
* ice_generate_clock_id - generates unique clock_id for registering dpll.
|
|
* @pf: board private structure
|
|
*
|
|
* Generates unique (per board) clock_id for allocation and search of dpll
|
|
* devices in Linux dpll subsystem.
|
|
*
|
|
* Return: generated clock id for the board
|
|
*/
|
|
static u64 ice_generate_clock_id(struct ice_pf *pf)
|
|
{
|
|
return pci_get_dsn(pf->pdev);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_notify_changes - notify dpll subsystem about changes
|
|
* @d: pointer do dpll
|
|
*
|
|
* Once change detected appropriate event is submitted to the dpll subsystem.
|
|
*/
|
|
static void ice_dpll_notify_changes(struct ice_dpll *d)
|
|
{
|
|
bool pin_notified = false;
|
|
|
|
if (d->prev_dpll_state != d->dpll_state) {
|
|
d->prev_dpll_state = d->dpll_state;
|
|
dpll_device_change_ntf(d->dpll);
|
|
}
|
|
if (d->prev_input != d->active_input) {
|
|
if (d->prev_input)
|
|
dpll_pin_change_ntf(d->prev_input);
|
|
d->prev_input = d->active_input;
|
|
if (d->active_input) {
|
|
dpll_pin_change_ntf(d->active_input);
|
|
pin_notified = true;
|
|
}
|
|
}
|
|
if (d->prev_phase_offset != d->phase_offset) {
|
|
d->prev_phase_offset = d->phase_offset;
|
|
if (!pin_notified && d->active_input)
|
|
dpll_pin_change_ntf(d->active_input);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_is_pps_phase_monitor - check if dpll capable of phase offset monitor
|
|
* @pf: pf private structure
|
|
*
|
|
* Check if firmware is capable of supporting admin command to provide
|
|
* phase offset monitoring on all the input pins on PPS dpll.
|
|
*
|
|
* Returns:
|
|
* * true - PPS dpll phase offset monitoring is supported
|
|
* * false - PPS dpll phase offset monitoring is not supported
|
|
*/
|
|
static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf)
|
|
{
|
|
struct ice_cgu_input_measure meas[ICE_DPLL_INPUT_REF_NUM];
|
|
int ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas,
|
|
ARRAY_SIZE(meas));
|
|
|
|
if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_ESRCH)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pins_notify_mask - notify dpll subsystem about bulk pin changes
|
|
* @pins: array of ice_dpll_pin pointers registered within dpll subsystem
|
|
* @pin_num: number of pins
|
|
* @phase_offset_ntf_mask: bitmask of pin indexes to notify
|
|
*
|
|
* Iterate over array of pins and call dpll subsystem pin notify if
|
|
* corresponding pin index within bitmask is set.
|
|
*
|
|
* Context: Must be called while pf->dplls.lock is released.
|
|
*/
|
|
static void ice_dpll_pins_notify_mask(struct ice_dpll_pin *pins,
|
|
u8 pin_num,
|
|
u32 phase_offset_ntf_mask)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < pin_num; i++)
|
|
if (phase_offset_ntf_mask & (1 << i))
|
|
dpll_pin_change_ntf(pins[i].pin);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pps_update_phase_offsets - update phase offset measurements
|
|
* @pf: pf private structure
|
|
* @phase_offset_pins_updated: returns mask of updated input pin indexes
|
|
*
|
|
* Read phase offset measurements for PPS dpll device and store values in
|
|
* input pins array. On success phase_offset_pins_updated - fills bitmask of
|
|
* updated input pin indexes, pins shall be notified.
|
|
*
|
|
* Context: Shall be called with pf->dplls.lock being locked.
|
|
* Returns:
|
|
* * 0 - success or no data available
|
|
* * negative - AQ failure
|
|
*/
|
|
static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf,
|
|
u32 *phase_offset_pins_updated)
|
|
{
|
|
struct ice_cgu_input_measure meas[ICE_DPLL_INPUT_REF_NUM];
|
|
struct ice_dpll_pin *p;
|
|
s64 phase_offset, tmp;
|
|
int i, j, ret;
|
|
|
|
*phase_offset_pins_updated = 0;
|
|
ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas,
|
|
ARRAY_SIZE(meas));
|
|
if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) {
|
|
return 0;
|
|
} else if (ret) {
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"failed to get input pin measurements dpll=%d, ret=%d %s\n",
|
|
DPLL_TYPE_PPS, ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status));
|
|
return ret;
|
|
}
|
|
for (i = 0; i < pf->dplls.num_inputs; i++) {
|
|
p = &pf->dplls.inputs[i];
|
|
phase_offset = 0;
|
|
for (j = 0; j < ICE_CGU_INPUT_PHASE_OFFSET_BYTES; j++) {
|
|
tmp = meas[i].phase_offset[j];
|
|
#ifdef __LITTLE_ENDIAN
|
|
phase_offset += tmp << 8 * j;
|
|
#else
|
|
phase_offset += tmp << 8 *
|
|
(ICE_CGU_INPUT_PHASE_OFFSET_BYTES - 1 - j);
|
|
#endif
|
|
}
|
|
phase_offset = sign_extend64(phase_offset, 47);
|
|
if (p->phase_offset != phase_offset) {
|
|
dev_dbg(ice_pf_to_dev(pf),
|
|
"phase offset changed for pin:%d old:%llx, new:%llx\n",
|
|
p->idx, p->phase_offset, phase_offset);
|
|
p->phase_offset = phase_offset;
|
|
*phase_offset_pins_updated |= (1 << i);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_update_state - update dpll state
|
|
* @pf: pf private structure
|
|
* @d: pointer to queried dpll device
|
|
* @init: if function called on initialization of ice dpll
|
|
*
|
|
* Poll current state of dpll from hw and update ice_dpll struct.
|
|
*
|
|
* Context: Called by kworker under pf->dplls.lock
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - AQ failure
|
|
*/
|
|
static int
|
|
ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
|
|
{
|
|
struct ice_dpll_pin *p = NULL;
|
|
int ret;
|
|
|
|
ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
|
|
&d->input_idx, &d->ref_state, &d->eec_mode,
|
|
&d->phase_offset, &d->dpll_state);
|
|
|
|
dev_dbg(ice_pf_to_dev(pf),
|
|
"update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d mode:%d\n",
|
|
d->dpll_idx, d->prev_input_idx, d->input_idx,
|
|
d->dpll_state, d->prev_dpll_state, d->mode);
|
|
if (ret) {
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"update dpll=%d state failed, ret=%d %s\n",
|
|
d->dpll_idx, ret,
|
|
ice_aq_str(pf->hw.adminq.sq_last_status));
|
|
return ret;
|
|
}
|
|
if (init) {
|
|
if (d->dpll_state == DPLL_LOCK_STATUS_LOCKED ||
|
|
d->dpll_state == DPLL_LOCK_STATUS_LOCKED_HO_ACQ)
|
|
d->active_input = pf->dplls.inputs[d->input_idx].pin;
|
|
p = &pf->dplls.inputs[d->input_idx];
|
|
return ice_dpll_pin_state_update(pf, p,
|
|
ICE_DPLL_PIN_TYPE_INPUT, NULL);
|
|
}
|
|
if (d->dpll_state == DPLL_LOCK_STATUS_HOLDOVER ||
|
|
d->dpll_state == DPLL_LOCK_STATUS_UNLOCKED) {
|
|
d->active_input = NULL;
|
|
if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID)
|
|
p = &pf->dplls.inputs[d->input_idx];
|
|
d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
|
|
d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
|
|
if (!p)
|
|
return 0;
|
|
ret = ice_dpll_pin_state_update(pf, p,
|
|
ICE_DPLL_PIN_TYPE_INPUT, NULL);
|
|
} else if (d->input_idx != d->prev_input_idx) {
|
|
if (d->prev_input_idx != ICE_DPLL_PIN_IDX_INVALID) {
|
|
p = &pf->dplls.inputs[d->prev_input_idx];
|
|
ice_dpll_pin_state_update(pf, p,
|
|
ICE_DPLL_PIN_TYPE_INPUT,
|
|
NULL);
|
|
}
|
|
if (d->input_idx != ICE_DPLL_PIN_IDX_INVALID) {
|
|
p = &pf->dplls.inputs[d->input_idx];
|
|
d->active_input = p->pin;
|
|
ice_dpll_pin_state_update(pf, p,
|
|
ICE_DPLL_PIN_TYPE_INPUT,
|
|
NULL);
|
|
}
|
|
d->prev_input_idx = d->input_idx;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_periodic_work - DPLLs periodic worker
|
|
* @work: pointer to kthread_work structure
|
|
*
|
|
* DPLLs periodic worker is responsible for polling state of dpll.
|
|
* Context: Holds pf->dplls.lock
|
|
*/
|
|
static void ice_dpll_periodic_work(struct kthread_work *work)
|
|
{
|
|
struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
|
|
struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
|
|
struct ice_dpll *de = &pf->dplls.eec;
|
|
struct ice_dpll *dp = &pf->dplls.pps;
|
|
u32 phase_offset_ntf = 0;
|
|
int ret = 0;
|
|
|
|
if (ice_is_reset_in_progress(pf->state))
|
|
goto resched;
|
|
mutex_lock(&pf->dplls.lock);
|
|
d->periodic_counter++;
|
|
ret = ice_dpll_update_state(pf, de, false);
|
|
if (!ret)
|
|
ret = ice_dpll_update_state(pf, dp, false);
|
|
if (!ret && dp->phase_offset_monitor_period &&
|
|
d->periodic_counter % dp->phase_offset_monitor_period == 0)
|
|
ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf);
|
|
if (ret) {
|
|
d->cgu_state_acq_err_num++;
|
|
/* stop rescheduling this worker */
|
|
if (d->cgu_state_acq_err_num >
|
|
ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"EEC/PPS DPLLs periodic work disabled\n");
|
|
mutex_unlock(&pf->dplls.lock);
|
|
return;
|
|
}
|
|
}
|
|
mutex_unlock(&pf->dplls.lock);
|
|
ice_dpll_notify_changes(de);
|
|
ice_dpll_notify_changes(dp);
|
|
if (phase_offset_ntf)
|
|
ice_dpll_pins_notify_mask(d->inputs, d->num_inputs,
|
|
phase_offset_ntf);
|
|
|
|
resched:
|
|
/* Run twice a second or reschedule if update failed */
|
|
kthread_queue_delayed_work(d->kworker, &d->work,
|
|
ret ? msecs_to_jiffies(10) :
|
|
msecs_to_jiffies(500));
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_ref_sync_inputs - initialize reference sync pin pairs
|
|
* @pf: pf private structure
|
|
*
|
|
* Read DPLL TLV capabilities and initialize reference sync pin pairs in
|
|
* dpll subsystem.
|
|
*
|
|
* Return:
|
|
* * 0 - success or nothing to do (no ref-sync tlv are present)
|
|
* * negative - AQ failure
|
|
*/
|
|
static int ice_dpll_init_ref_sync_inputs(struct ice_pf *pf)
|
|
{
|
|
struct ice_dpll_pin *inputs = pf->dplls.inputs;
|
|
struct ice_hw *hw = &pf->hw;
|
|
u16 addr, len, end, hdr;
|
|
int ret;
|
|
|
|
ret = ice_get_pfa_module_tlv(hw, &hdr, &len, ICE_SR_PFA_DPLL_DEFAULTS);
|
|
if (ret) {
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"Failed to read PFA dpll defaults TLV ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
end = hdr + len;
|
|
|
|
for (addr = hdr + ICE_DPLL_PFA_HEADER_LEN; addr < end;
|
|
addr += ICE_DPLL_PFA_ENTRY_LEN) {
|
|
unsigned long bit, ul_mask, offset;
|
|
u16 pin, mask, buf;
|
|
bool valid = false;
|
|
|
|
ret = ice_read_sr_word(hw, addr, &buf);
|
|
if (ret)
|
|
return ret;
|
|
|
|
switch (buf) {
|
|
case ICE_DPLL_PFA_REF_SYNC_TYPE:
|
|
case ICE_DPLL_PFA_REF_SYNC_TYPE2:
|
|
{
|
|
u16 mask_addr = addr + ICE_DPLL_PFA_MASK_OFFSET;
|
|
u16 val_addr = addr + ICE_DPLL_PFA_VALUE_OFFSET;
|
|
|
|
ret = ice_read_sr_word(hw, mask_addr, &mask);
|
|
if (ret)
|
|
return ret;
|
|
ret = ice_read_sr_word(hw, val_addr, &pin);
|
|
if (ret)
|
|
return ret;
|
|
if (buf == ICE_DPLL_PFA_REF_SYNC_TYPE)
|
|
pin >>= ICE_DPLL_PFA_MAILBOX_REF_SYNC_PIN_S;
|
|
valid = true;
|
|
break;
|
|
}
|
|
case ICE_DPLL_PFA_END:
|
|
addr = end;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (!valid)
|
|
continue;
|
|
|
|
ul_mask = mask;
|
|
offset = 0;
|
|
for_each_set_bit(bit, &ul_mask, BITS_PER_TYPE(u16)) {
|
|
int i, j;
|
|
|
|
if (hw->device_id == ICE_DEV_ID_E810C_SFP &&
|
|
pin > ICE_DPLL_E810C_SFP_NC_START)
|
|
offset = -ICE_DPLL_E810C_SFP_NC_PINS;
|
|
i = pin + offset;
|
|
j = bit + offset;
|
|
if (i < 0 || j < 0)
|
|
return -ERANGE;
|
|
inputs[i].ref_sync = j;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_release_pins - release pins resources from dpll subsystem
|
|
* @pins: pointer to pins array
|
|
* @count: number of pins
|
|
*
|
|
* Release resources of given pins array in the dpll subsystem.
|
|
*/
|
|
static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
dpll_pin_put(pins[i].pin);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_get_pins - get pins from dpll subsystem
|
|
* @pf: board private structure
|
|
* @pins: pointer to pins array
|
|
* @start_idx: get starts from this pin idx value
|
|
* @count: number of pins
|
|
* @clock_id: clock_id of dpll device
|
|
*
|
|
* Get pins - allocate - in dpll subsystem, store them in pin field of given
|
|
* pins array.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - allocation failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
|
|
int start_idx, int count, u64 clock_id)
|
|
{
|
|
int i, ret;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
|
|
&pins[i].prop);
|
|
if (IS_ERR(pins[i].pin)) {
|
|
ret = PTR_ERR(pins[i].pin);
|
|
goto release_pins;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
release_pins:
|
|
while (--i >= 0)
|
|
dpll_pin_put(pins[i].pin);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_unregister_pins - unregister pins from a dpll
|
|
* @dpll: dpll device pointer
|
|
* @pins: pointer to pins array
|
|
* @ops: callback ops registered with the pins
|
|
* @count: number of pins
|
|
*
|
|
* Unregister pins of a given array of pins from given dpll device registered in
|
|
* dpll subsystem.
|
|
*/
|
|
static void
|
|
ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
|
|
const struct dpll_pin_ops *ops, int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
if (!pins[i].hidden)
|
|
dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_pin_ref_sync_register - register reference sync pins
|
|
* @pins: pointer to pins array
|
|
* @count: number of pins
|
|
*
|
|
* Register reference sync pins in dpll subsystem.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - registration failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_pin_ref_sync_register(struct ice_dpll_pin *pins, int count)
|
|
{
|
|
int ret, i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (!pins[i].hidden && pins[i].ref_sync) {
|
|
int j = pins[i].ref_sync;
|
|
|
|
ret = dpll_pin_ref_sync_pair_add(pins[i].pin,
|
|
pins[j].pin);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_register_pins - register pins with a dpll
|
|
* @dpll: dpll pointer to register pins with
|
|
* @pins: pointer to pins array
|
|
* @ops: callback ops registered with the pins
|
|
* @count: number of pins
|
|
*
|
|
* Register pins of a given array with given dpll in dpll subsystem.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - registration failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
|
|
const struct dpll_pin_ops *ops, int count)
|
|
{
|
|
int ret, i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
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)
|
|
if (!pins[i].hidden)
|
|
dpll_pin_unregister(dpll, pins[i].pin, ops, &pins[i]);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit_direct_pins - deinitialize direct pins
|
|
* @cgu: if cgu is present and controlled by this NIC
|
|
* @pins: pointer to pins array
|
|
* @count: number of pins
|
|
* @ops: callback ops registered with the pins
|
|
* @first: dpll device pointer
|
|
* @second: dpll device pointer
|
|
*
|
|
* If cgu is owned unregister pins from given dplls.
|
|
* Release pins resources to the dpll subsystem.
|
|
*/
|
|
static void
|
|
ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
|
|
const struct dpll_pin_ops *ops,
|
|
struct dpll_device *first,
|
|
struct dpll_device *second)
|
|
{
|
|
if (cgu) {
|
|
ice_dpll_unregister_pins(first, pins, ops, count);
|
|
ice_dpll_unregister_pins(second, pins, ops, count);
|
|
}
|
|
ice_dpll_release_pins(pins, count);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_direct_pins - initialize direct pins
|
|
* @pf: board private structure
|
|
* @cgu: if cgu is present and controlled by this NIC
|
|
* @pins: pointer to pins array
|
|
* @start_idx: on which index shall allocation start in dpll subsystem
|
|
* @count: number of pins
|
|
* @ops: callback ops registered with the pins
|
|
* @first: dpll device pointer
|
|
* @second: dpll device pointer
|
|
*
|
|
* Allocate directly connected pins of a given array in dpll subsystem.
|
|
* If cgu is owned register allocated pins with given dplls.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - registration failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
|
|
struct ice_dpll_pin *pins, int start_idx, int count,
|
|
const struct dpll_pin_ops *ops,
|
|
struct dpll_device *first, struct dpll_device *second)
|
|
{
|
|
int ret;
|
|
|
|
ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
|
|
if (ret)
|
|
return ret;
|
|
if (cgu) {
|
|
ret = ice_dpll_register_pins(first, pins, ops, count);
|
|
if (ret)
|
|
goto release_pins;
|
|
ret = ice_dpll_register_pins(second, pins, ops, count);
|
|
if (ret)
|
|
goto unregister_first;
|
|
}
|
|
|
|
return 0;
|
|
|
|
unregister_first:
|
|
ice_dpll_unregister_pins(first, pins, ops, count);
|
|
release_pins:
|
|
ice_dpll_release_pins(pins, count);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit_rclk_pin - release rclk pin resources
|
|
* @pf: board private structure
|
|
*
|
|
* Deregister rclk pin from parent pins and release resources in dpll subsystem.
|
|
*/
|
|
static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
|
|
{
|
|
struct ice_dpll_pin *rclk = &pf->dplls.rclk;
|
|
struct ice_vsi *vsi = ice_get_main_vsi(pf);
|
|
struct dpll_pin *parent;
|
|
int i;
|
|
|
|
for (i = 0; i < rclk->num_parents; i++) {
|
|
parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
|
|
if (!parent)
|
|
continue;
|
|
dpll_pin_on_pin_unregister(parent, rclk->pin,
|
|
&ice_dpll_rclk_ops, rclk);
|
|
}
|
|
if (WARN_ON_ONCE(!vsi || !vsi->netdev))
|
|
return;
|
|
dpll_netdev_pin_clear(vsi->netdev);
|
|
dpll_pin_put(rclk->pin);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_rclk_pins - initialize recovered clock pin
|
|
* @pf: board private structure
|
|
* @pin: pin to register
|
|
* @start_idx: on which index shall allocation start in dpll subsystem
|
|
* @ops: callback ops registered with the pins
|
|
*
|
|
* Allocate resource for recovered clock pin in dpll subsystem. Register the
|
|
* pin with the parents it has in the info. Register pin with the pf's main vsi
|
|
* netdev.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - registration failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
|
|
int start_idx, const struct dpll_pin_ops *ops)
|
|
{
|
|
struct ice_vsi *vsi = ice_get_main_vsi(pf);
|
|
struct dpll_pin *parent;
|
|
int ret, i;
|
|
|
|
if (WARN_ON((!vsi || !vsi->netdev)))
|
|
return -EINVAL;
|
|
ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
|
|
pf->dplls.clock_id);
|
|
if (ret)
|
|
return ret;
|
|
for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
|
|
parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
|
|
if (!parent) {
|
|
ret = -ENODEV;
|
|
goto unregister_pins;
|
|
}
|
|
ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
|
|
ops, &pf->dplls.rclk);
|
|
if (ret)
|
|
goto unregister_pins;
|
|
}
|
|
dpll_netdev_pin_set(vsi->netdev, pf->dplls.rclk.pin);
|
|
|
|
return 0;
|
|
|
|
unregister_pins:
|
|
while (i) {
|
|
parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
|
|
dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
|
|
&ice_dpll_rclk_ops, &pf->dplls.rclk);
|
|
}
|
|
ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit_pins - deinitialize direct pins
|
|
* @pf: board private structure
|
|
* @cgu: if cgu is controlled by this pf
|
|
*
|
|
* If cgu is owned unregister directly connected pins from the dplls.
|
|
* Release resources of directly connected pins from the dpll subsystem.
|
|
*/
|
|
static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
|
|
{
|
|
struct ice_dpll_pin *outputs = pf->dplls.outputs;
|
|
struct ice_dpll_pin *inputs = pf->dplls.inputs;
|
|
int num_outputs = pf->dplls.num_outputs;
|
|
int num_inputs = pf->dplls.num_inputs;
|
|
struct ice_dplls *d = &pf->dplls;
|
|
struct ice_dpll *de = &d->eec;
|
|
struct ice_dpll *dp = &d->pps;
|
|
|
|
ice_dpll_deinit_rclk_pin(pf);
|
|
if (cgu) {
|
|
ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
|
|
num_inputs);
|
|
ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
|
|
num_inputs);
|
|
}
|
|
ice_dpll_release_pins(inputs, num_inputs);
|
|
if (cgu) {
|
|
ice_dpll_unregister_pins(dp->dpll, outputs,
|
|
&ice_dpll_output_ops, num_outputs);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_pins - init pins and register pins with a dplls
|
|
* @pf: board private structure
|
|
* @cgu: if cgu is present and controlled by this NIC
|
|
*
|
|
* Initialize directly connected pf's pins within pf's dplls in a Linux dpll
|
|
* subsystem.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - initialization failure reason
|
|
*/
|
|
static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
|
|
{
|
|
int ret, count;
|
|
|
|
ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
|
|
pf->dplls.num_inputs,
|
|
&ice_dpll_input_ops,
|
|
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,
|
|
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;
|
|
}
|
|
ret = ice_dpll_pin_ref_sync_register(pf->dplls.inputs,
|
|
pf->dplls.num_inputs);
|
|
if (ret)
|
|
goto deinit_ufl;
|
|
ret = ice_dpll_pin_ref_sync_register(pf->dplls.sma,
|
|
ICE_DPLL_PIN_SW_NUM);
|
|
if (ret)
|
|
goto deinit_ufl;
|
|
} else {
|
|
count += pf->dplls.num_outputs + 2 * ICE_DPLL_PIN_SW_NUM;
|
|
}
|
|
ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, count + pf->hw.pf_id,
|
|
&ice_dpll_rclk_ops);
|
|
if (ret)
|
|
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,
|
|
&ice_dpll_output_ops, pf->dplls.pps.dpll,
|
|
pf->dplls.eec.dpll);
|
|
deinit_inputs:
|
|
ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
|
|
&ice_dpll_input_ops, pf->dplls.pps.dpll,
|
|
pf->dplls.eec.dpll);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit_dpll - deinitialize dpll device
|
|
* @pf: board private structure
|
|
* @d: pointer to ice_dpll
|
|
* @cgu: if cgu is present and controlled by this NIC
|
|
*
|
|
* If cgu is owned unregister the dpll from dpll subsystem.
|
|
* Release resources of dpll device from dpll subsystem.
|
|
*/
|
|
static void
|
|
ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
|
|
{
|
|
if (cgu)
|
|
dpll_device_unregister(d->dpll, d->ops, d);
|
|
dpll_device_put(d->dpll);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_dpll - initialize dpll device in dpll subsystem
|
|
* @pf: board private structure
|
|
* @d: dpll to be initialized
|
|
* @cgu: if cgu is present and controlled by this NIC
|
|
* @type: type of dpll being initialized
|
|
*
|
|
* Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
|
|
* by this NIC, register dpll with the callback ops.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - initialization failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
|
|
enum dpll_type type)
|
|
{
|
|
u64 clock_id = pf->dplls.clock_id;
|
|
int ret;
|
|
|
|
d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
|
|
if (IS_ERR(d->dpll)) {
|
|
ret = PTR_ERR(d->dpll);
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"dpll_device_get failed (%p) err=%d\n", d, ret);
|
|
return ret;
|
|
}
|
|
d->pf = pf;
|
|
if (cgu) {
|
|
const struct dpll_device_ops *ops = &ice_dpll_ops;
|
|
|
|
if (type == DPLL_TYPE_PPS && ice_dpll_is_pps_phase_monitor(pf))
|
|
ops = &ice_dpll_pom_ops;
|
|
ice_dpll_update_state(pf, d, true);
|
|
ret = dpll_device_register(d->dpll, type, ops, d);
|
|
if (ret) {
|
|
dpll_device_put(d->dpll);
|
|
return ret;
|
|
}
|
|
d->ops = ops;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit_worker - deinitialize dpll kworker
|
|
* @pf: board private structure
|
|
*
|
|
* Stop dpll's kworker, release it's resources.
|
|
*/
|
|
static void ice_dpll_deinit_worker(struct ice_pf *pf)
|
|
{
|
|
struct ice_dplls *d = &pf->dplls;
|
|
|
|
kthread_cancel_delayed_work_sync(&d->work);
|
|
kthread_destroy_worker(d->kworker);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_worker - Initialize DPLLs periodic worker
|
|
* @pf: board private structure
|
|
*
|
|
* Create and start DPLLs periodic worker.
|
|
*
|
|
* Context: Shall be called after pf->dplls.lock is initialized.
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - create worker failure
|
|
*/
|
|
static int ice_dpll_init_worker(struct ice_pf *pf)
|
|
{
|
|
struct ice_dplls *d = &pf->dplls;
|
|
struct kthread_worker *kworker;
|
|
|
|
kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
|
|
kworker = kthread_run_worker(0, "ice-dplls-%s",
|
|
dev_name(ice_pf_to_dev(pf)));
|
|
if (IS_ERR(kworker))
|
|
return PTR_ERR(kworker);
|
|
d->kworker = kworker;
|
|
d->cgu_state_acq_err_num = 0;
|
|
kthread_queue_delayed_work(d->kworker, &d->work, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_phase_range_set - initialize phase adjust range helper
|
|
* @range: pointer to phase adjust range struct to be initialized
|
|
* @phase_adj: a value to be used as min(-)/max(+) boundary
|
|
*/
|
|
static void ice_dpll_phase_range_set(struct dpll_pin_phase_adjust_range *range,
|
|
u32 phase_adj)
|
|
{
|
|
range->min = -phase_adj;
|
|
range->max = phase_adj;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_info_pins_generic - initializes generic pins info
|
|
* @pf: board private structure
|
|
* @input: if input pins initialized
|
|
*
|
|
* Init information for generic pins, cache them in PF's pins structures.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - init failure reason
|
|
*/
|
|
static int ice_dpll_init_info_pins_generic(struct ice_pf *pf, bool input)
|
|
{
|
|
struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
|
|
static const char labels[][sizeof("99")] = {
|
|
"0", "1", "2", "3", "4", "5", "6", "7", "8",
|
|
"9", "10", "11", "12", "13", "14", "15" };
|
|
u32 cap = DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
|
|
enum ice_dpll_pin_type pin_type;
|
|
int i, pin_num, ret = -EINVAL;
|
|
struct ice_dpll_pin *pins;
|
|
u32 phase_adj_max;
|
|
|
|
if (input) {
|
|
pin_num = pf->dplls.num_inputs;
|
|
pins = pf->dplls.inputs;
|
|
phase_adj_max = pf->dplls.input_phase_adj_max;
|
|
pin_type = ICE_DPLL_PIN_TYPE_INPUT;
|
|
cap |= DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE;
|
|
} else {
|
|
pin_num = pf->dplls.num_outputs;
|
|
pins = pf->dplls.outputs;
|
|
phase_adj_max = pf->dplls.output_phase_adj_max;
|
|
pin_type = ICE_DPLL_PIN_TYPE_OUTPUT;
|
|
}
|
|
if (pin_num > ARRAY_SIZE(labels))
|
|
return ret;
|
|
|
|
for (i = 0; i < pin_num; i++) {
|
|
pins[i].idx = i;
|
|
pins[i].prop.board_label = labels[i];
|
|
ice_dpll_phase_range_set(&pins[i].prop.phase_range,
|
|
phase_adj_max);
|
|
pins[i].prop.capabilities = cap;
|
|
pins[i].pf = pf;
|
|
ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
|
|
if (ret)
|
|
break;
|
|
if (input && pins[i].freq == ICE_DPLL_PIN_GEN_RCLK_FREQ)
|
|
pins[i].prop.type = DPLL_PIN_TYPE_MUX;
|
|
else
|
|
pins[i].prop.type = DPLL_PIN_TYPE_EXT;
|
|
if (!input)
|
|
continue;
|
|
ret = ice_aq_get_cgu_ref_prio(&pf->hw, de->dpll_idx, i,
|
|
&de->input_prio[i]);
|
|
if (ret)
|
|
break;
|
|
ret = ice_aq_get_cgu_ref_prio(&pf->hw, dp->dpll_idx, i,
|
|
&dp->input_prio[i]);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_info_direct_pins - initializes direct pins info
|
|
* @pf: board private structure
|
|
* @pin_type: type of pins being initialized
|
|
*
|
|
* Init information for directly connected pins, cache them in pf's pins
|
|
* structures.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - init failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_init_info_direct_pins(struct ice_pf *pf,
|
|
enum ice_dpll_pin_type pin_type)
|
|
{
|
|
struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
|
|
int num_pins, i, ret = -EINVAL;
|
|
struct ice_hw *hw = &pf->hw;
|
|
struct ice_dpll_pin *pins;
|
|
unsigned long caps;
|
|
u32 phase_adj_max;
|
|
u8 freq_supp_num;
|
|
bool input;
|
|
|
|
switch (pin_type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
pins = pf->dplls.inputs;
|
|
num_pins = pf->dplls.num_inputs;
|
|
phase_adj_max = pf->dplls.input_phase_adj_max;
|
|
input = true;
|
|
break;
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
pins = pf->dplls.outputs;
|
|
num_pins = pf->dplls.num_outputs;
|
|
phase_adj_max = pf->dplls.output_phase_adj_max;
|
|
input = false;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
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;
|
|
pins[i].idx = i;
|
|
pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
|
|
pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
|
|
if (input) {
|
|
ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
|
|
&de->input_prio[i]);
|
|
if (ret)
|
|
return ret;
|
|
ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
|
|
&dp->input_prio[i]);
|
|
if (ret)
|
|
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);
|
|
pins[i].prop.capabilities = caps;
|
|
ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL);
|
|
if (ret)
|
|
return ret;
|
|
pins[i].prop.freq_supported =
|
|
ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
|
|
pins[i].prop.freq_supported_num = freq_supp_num;
|
|
pins[i].pf = pf;
|
|
}
|
|
if (input)
|
|
ret = ice_dpll_init_ref_sync_inputs(pf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_info_rclk_pin - initializes rclk pin information
|
|
* @pf: board private structure
|
|
*
|
|
* Init information for rclk pin, cache them in pf->dplls.rclk.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - init failure reason
|
|
*/
|
|
static int ice_dpll_init_info_rclk_pin(struct ice_pf *pf)
|
|
{
|
|
struct ice_dpll_pin *pin = &pf->dplls.rclk;
|
|
|
|
pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
|
|
pin->prop.capabilities |= DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
|
|
pin->pf = pf;
|
|
|
|
return ice_dpll_pin_state_update(pf, pin,
|
|
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];
|
|
if (pin->input->ref_sync)
|
|
pin->ref_sync = pin->input->ref_sync - 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
|
|
* @pin_type: type of pins being initialized
|
|
*
|
|
* Wraps functions for pin initialization.
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - init failure reason
|
|
*/
|
|
static int
|
|
ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
|
|
{
|
|
switch (pin_type) {
|
|
case ICE_DPLL_PIN_TYPE_INPUT:
|
|
case ICE_DPLL_PIN_TYPE_OUTPUT:
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit_info - release memory allocated for pins info
|
|
* @pf: board private structure
|
|
*
|
|
* Release memory allocated for pins by ice_dpll_init_info function.
|
|
*/
|
|
static void ice_dpll_deinit_info(struct ice_pf *pf)
|
|
{
|
|
kfree(pf->dplls.inputs);
|
|
kfree(pf->dplls.outputs);
|
|
kfree(pf->dplls.eec.input_prio);
|
|
kfree(pf->dplls.pps.input_prio);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init_info - prepare pf's dpll information structure
|
|
* @pf: board private structure
|
|
* @cgu: if cgu is present and controlled by this NIC
|
|
*
|
|
* Acquire (from HW) and set basic dpll information (on pf->dplls struct).
|
|
*
|
|
* Return:
|
|
* * 0 - success
|
|
* * negative - init failure reason
|
|
*/
|
|
static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
|
|
{
|
|
struct ice_aqc_get_cgu_abilities abilities;
|
|
struct ice_dpll *de = &pf->dplls.eec;
|
|
struct ice_dpll *dp = &pf->dplls.pps;
|
|
struct ice_dplls *d = &pf->dplls;
|
|
struct ice_hw *hw = &pf->hw;
|
|
int ret, alloc_size, i;
|
|
|
|
d->clock_id = ice_generate_clock_id(pf);
|
|
ret = ice_aq_get_cgu_abilities(hw, &abilities);
|
|
if (ret) {
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"err:%d %s failed to read cgu abilities\n",
|
|
ret, ice_aq_str(hw->adminq.sq_last_status));
|
|
return ret;
|
|
}
|
|
|
|
de->dpll_idx = abilities.eec_dpll_idx;
|
|
dp->dpll_idx = abilities.pps_dpll_idx;
|
|
d->num_inputs = abilities.num_inputs;
|
|
d->num_outputs = abilities.num_outputs;
|
|
d->input_phase_adj_max = le32_to_cpu(abilities.max_in_phase_adj) &
|
|
ICE_AQC_GET_CGU_MAX_PHASE_ADJ;
|
|
d->output_phase_adj_max = le32_to_cpu(abilities.max_out_phase_adj) &
|
|
ICE_AQC_GET_CGU_MAX_PHASE_ADJ;
|
|
|
|
alloc_size = sizeof(*d->inputs) * d->num_inputs;
|
|
d->inputs = kzalloc(alloc_size, GFP_KERNEL);
|
|
if (!d->inputs)
|
|
return -ENOMEM;
|
|
|
|
alloc_size = sizeof(*de->input_prio) * d->num_inputs;
|
|
de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
|
|
if (!de->input_prio)
|
|
return -ENOMEM;
|
|
|
|
dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
|
|
if (!dp->input_prio)
|
|
return -ENOMEM;
|
|
|
|
ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
|
|
if (ret)
|
|
goto deinit_info;
|
|
|
|
if (cgu) {
|
|
alloc_size = sizeof(*d->outputs) * d->num_outputs;
|
|
d->outputs = kzalloc(alloc_size, GFP_KERNEL);
|
|
if (!d->outputs) {
|
|
ret = -ENOMEM;
|
|
goto deinit_info;
|
|
}
|
|
|
|
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,
|
|
&pf->dplls.rclk.num_parents);
|
|
if (ret)
|
|
return ret;
|
|
for (i = 0; i < pf->dplls.rclk.num_parents; i++)
|
|
pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
|
|
ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
|
|
if (ret)
|
|
return ret;
|
|
de->mode = DPLL_MODE_AUTOMATIC;
|
|
dp->mode = DPLL_MODE_AUTOMATIC;
|
|
|
|
dev_dbg(ice_pf_to_dev(pf),
|
|
"%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
|
|
__func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
|
|
|
|
return 0;
|
|
|
|
deinit_info:
|
|
dev_err(ice_pf_to_dev(pf),
|
|
"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
|
|
__func__, d->inputs, de->input_prio,
|
|
dp->input_prio, d->outputs);
|
|
ice_dpll_deinit_info(pf);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
|
|
* the dpll device.
|
|
* @pf: board private structure
|
|
*
|
|
* Handles the cleanup work required after dpll initialization, freeing
|
|
* resources and unregistering the dpll, pin and all resources used for
|
|
* handling them.
|
|
*
|
|
* Context: Destroys pf->dplls.lock mutex. Call only if ICE_FLAG_DPLL was set.
|
|
*/
|
|
void ice_dpll_deinit(struct ice_pf *pf)
|
|
{
|
|
bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
|
|
|
|
clear_bit(ICE_FLAG_DPLL, pf->flags);
|
|
if (cgu)
|
|
ice_dpll_deinit_worker(pf);
|
|
|
|
ice_dpll_deinit_pins(pf, cgu);
|
|
ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
|
|
ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
|
|
ice_dpll_deinit_info(pf);
|
|
mutex_destroy(&pf->dplls.lock);
|
|
}
|
|
|
|
/**
|
|
* ice_dpll_init - initialize support for dpll subsystem
|
|
* @pf: board private structure
|
|
*
|
|
* Set up the device dplls, register them and pins connected within Linux dpll
|
|
* subsystem. Allow userspace to obtain state of DPLL and handling of DPLL
|
|
* configuration requests.
|
|
*
|
|
* Context: Initializes pf->dplls.lock mutex.
|
|
*/
|
|
void ice_dpll_init(struct ice_pf *pf)
|
|
{
|
|
bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
|
|
struct ice_dplls *d = &pf->dplls;
|
|
int err = 0;
|
|
|
|
mutex_init(&d->lock);
|
|
err = ice_dpll_init_info(pf, cgu);
|
|
if (err)
|
|
goto err_exit;
|
|
err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu, DPLL_TYPE_EEC);
|
|
if (err)
|
|
goto deinit_info;
|
|
err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu, DPLL_TYPE_PPS);
|
|
if (err)
|
|
goto deinit_eec;
|
|
err = ice_dpll_init_pins(pf, cgu);
|
|
if (err)
|
|
goto deinit_pps;
|
|
if (cgu) {
|
|
err = ice_dpll_init_worker(pf);
|
|
if (err)
|
|
goto deinit_pins;
|
|
}
|
|
set_bit(ICE_FLAG_DPLL, pf->flags);
|
|
|
|
return;
|
|
|
|
deinit_pins:
|
|
ice_dpll_deinit_pins(pf, cgu);
|
|
deinit_pps:
|
|
ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
|
|
deinit_eec:
|
|
ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
|
|
deinit_info:
|
|
ice_dpll_deinit_info(pf);
|
|
err_exit:
|
|
mutex_destroy(&d->lock);
|
|
dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", err);
|
|
}
|