Commit 2f5e6395 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-pse-pd-add-new-pse-c33-features'

parents 73d4d645 a87e699c
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -24,6 +24,21 @@ definitions:
    name: module-fw-flash-status
    type: enum
    entries: [ started, in_progress, completed, error ]
  -
    name: c33-pse-ext-state
    enum-name:
    type: enum
    name-prefix: ethtool-c33-pse-ext-state-
    entries:
        - none
        - error-condition
        - mr-mps-valid
        - mr-pse-enable
        - option-detect-ted
        - option-vport-lim
        - ovld-detected
        - power-not-available
        - short-detected

attribute-sets:
  -
@@ -924,6 +939,15 @@ attribute-sets:
      -
        name: power-mode
        type: u8
  -
    name: c33-pse-pw-limit
    attributes:
      -
        name: min
        type: u32
      -
        name: max
        type: u32
  -
    name: pse
    attributes:
@@ -955,6 +979,33 @@ attribute-sets:
        name: c33-pse-pw-d-status
        type: u32
        name-prefix: ethtool-a-
      -
        name: c33-pse-pw-class
        type: u32
        name-prefix: ethtool-a-
      -
        name: c33-pse-actual-pw
        type: u32
        name-prefix: ethtool-a-
      -
        name: c33-pse-ext-state
        type: u32
        name-prefix: ethtool-a-
        enum: c33-pse-ext-state
      -
        name: c33-pse-ext-substate
        type: u32
        name-prefix: ethtool-a-
      -
        name: c33-pse-avail-pw-limit
        type: u32
        name-prefix: ethtool-a-
      -
        name: c33-pse-pw-limit-ranges
        name-prefix: ethtool-a-
        type: nest
        multi-attr: true
        nested-attributes: c33-pse-pw-limit
  -
    name: rss
    attributes:
@@ -1672,6 +1723,12 @@ operations:
            - c33-pse-admin-state
            - c33-pse-admin-control
            - c33-pse-pw-d-status
            - c33-pse-pw-class
            - c33-pse-actual-pw
            - c33-pse-ext-state
            - c33-pse-ext-substate
            - c33-pse-avail-pw-limit
            - c33-pse-pw-limit-ranges
      dump: *pse-get-op
    -
      name: pse-set
@@ -1685,6 +1742,7 @@ operations:
            - header
            - podl-pse-admin-control
            - c33-pse-admin-control
            - c33-pse-avail-pw-limit
    -
      name: rss-get
      doc: Get RSS params.
+76 −11
Original line number Diff line number Diff line
@@ -1730,7 +1730,7 @@ Request contents:

Kernel response contents:

  ======================================  ======  =============================
  ==========================================  ======  =============================
  ``ETHTOOL_A_PSE_HEADER``                    nested  reply header
  ``ETHTOOL_A_PODL_PSE_ADMIN_STATE``             u32  Operational state of the PoDL
                                                      PSE functions
@@ -1740,7 +1740,18 @@ Kernel response contents:
                                                      PSE functions.
  ``ETHTOOL_A_C33_PSE_PW_D_STATUS``              u32  power detection status of the
                                                      PoE PSE.
  ======================================  ======  =============================
  ``ETHTOOL_A_C33_PSE_PW_CLASS``                 u32  power class of the PoE PSE.
  ``ETHTOOL_A_C33_PSE_ACTUAL_PW``                u32  actual power drawn on the
                                                      PoE PSE.
  ``ETHTOOL_A_C33_PSE_EXT_STATE``                u32  power extended state of the
                                                      PoE PSE.
  ``ETHTOOL_A_C33_PSE_EXT_SUBSTATE``             u32  power extended substatus of
                                                      the PoE PSE.
  ``ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT``           u32  currently configured power
                                                      limit of the PoE PSE.
  ``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES``       nested  Supported power limit
                                                      configuration ranges.
  ==========================================  ======  =============================

When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies
the operational state of the PoDL PSE functions.  The operational state of the
@@ -1772,6 +1783,46 @@ The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_PW_D_STATUS`` implementing
.. kernel-doc:: include/uapi/linux/ethtool.h
    :identifiers: ethtool_c33_pse_pw_d_status

When set, the optional ``ETHTOOL_A_C33_PSE_PW_CLASS`` attribute identifies
the power class of the C33 PSE. It depends on the class negotiated between
the PSE and the PD. This option is corresponding to ``IEEE 802.3-2022``
30.9.1.1.8 aPSEPowerClassification.

When set, the optional ``ETHTOOL_A_C33_PSE_ACTUAL_PW`` attribute identifies
This option is corresponding to ``IEEE 802.3-2022`` 30.9.1.1.23 aPSEActualPower.
Actual power is reported in mW.

When set, the optional ``ETHTOOL_A_C33_PSE_EXT_STATE`` attribute identifies
the extended error state of the C33 PSE. Possible values are:

.. kernel-doc:: include/uapi/linux/ethtool.h
    :identifiers: ethtool_c33_pse_ext_state

When set, the optional ``ETHTOOL_A_C33_PSE_EXT_SUBSTATE`` attribute identifies
the extended error state of the C33 PSE. Possible values are:
Possible values are:

.. kernel-doc:: include/uapi/linux/ethtool.h
    :identifiers: ethtool_c33_pse_ext_substate_class_num_events
		  ethtool_c33_pse_ext_substate_error_condition
		  ethtool_c33_pse_ext_substate_mr_pse_enable
		  ethtool_c33_pse_ext_substate_option_detect_ted
		  ethtool_c33_pse_ext_substate_option_vport_lim
		  ethtool_c33_pse_ext_substate_ovld_detected
		  ethtool_c33_pse_ext_substate_pd_dll_power_type
		  ethtool_c33_pse_ext_substate_power_not_available
		  ethtool_c33_pse_ext_substate_short_detected

When set, the optional ``ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT`` attribute
identifies the C33 PSE power limit in mW.

When set the optional ``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES`` nested attribute
identifies the C33 PSE power limit ranges through
``ETHTOOL_A_C33_PSE_PWR_VAL_LIMIT_RANGE_MIN`` and
``ETHTOOL_A_C33_PSE_PWR_VAL_LIMIT_RANGE_MAX``.
If the controller works with fixed classes, the min and max values will be
equal.

PSE_SET
=======

@@ -1783,6 +1834,8 @@ Request contents:
  ``ETHTOOL_A_PSE_HEADER``                nested  request header
  ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL``       u32  Control PoDL PSE Admin state
  ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL``        u32  Control PSE Admin state
  ``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT``      u32  Control PoE PSE available
                                                  power limit
  ======================================  ======  =============================

When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used
@@ -1793,6 +1846,18 @@ to control PoDL PSE Admin functions. This option is implementing
The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` implementing
``IEEE 802.3-2022`` 30.9.1.2.1 acPSEAdminControl.

When set, the optional ``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT`` attribute is
used to control the available power value limit for C33 PSE in milliwatts.
This attribute corresponds  to the `pse_available_power` variable described in
``IEEE 802.3-2022`` 33.2.4.4 Variables  and `pse_avail_pwr` in 145.2.5.4
Variables, which are described in power classes.

It was decided to use milliwatts for this interface to unify it with other
power monitoring interfaces, which also use milliwatts, and to align with
various existing products that document power consumption in watts rather than
classes. If power limit configuration based on classes is needed, the
conversion can be done in user space, for example by ethtool.

RSS_GET
=======

+316 −1
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ enum {
	PD692X0_MSG_SET_PORT_PARAM,
	PD692X0_MSG_GET_PORT_STATUS,
	PD692X0_MSG_DOWNLOAD_CMD,
	PD692X0_MSG_GET_PORT_CLASS,
	PD692X0_MSG_GET_PORT_MEAS,
	PD692X0_MSG_GET_PORT_PARAM,

	/* add new message above here */
	PD692X0_MSG_CNT
@@ -134,7 +137,7 @@ static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = {
	[PD692X0_MSG_SET_PORT_PARAM] = {
		.key = PD692X0_KEY_CMD,
		.sub = {0x05, 0xc0},
		.data = {   0, 0xff, 0xff, 0xff,
		.data = { 0xf, 0xff, 0xff, 0xff,
			 0x4e, 0x4e, 0x4e, 0x4e},
	},
	[PD692X0_MSG_GET_PORT_STATUS] = {
@@ -149,6 +152,24 @@ static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = {
		.data = {0x16, 0x16, 0x99, 0x4e,
			 0x4e, 0x4e, 0x4e, 0x4e},
	},
	[PD692X0_MSG_GET_PORT_CLASS] = {
		.key = PD692X0_KEY_REQ,
		.sub = {0x05, 0xc4},
		.data = {0x4e, 0x4e, 0x4e, 0x4e,
			 0x4e, 0x4e, 0x4e, 0x4e},
	},
	[PD692X0_MSG_GET_PORT_MEAS] = {
		.key = PD692X0_KEY_REQ,
		.sub = {0x05, 0xc5},
		.data = {0x4e, 0x4e, 0x4e, 0x4e,
			 0x4e, 0x4e, 0x4e, 0x4e},
	},
	[PD692X0_MSG_GET_PORT_PARAM] = {
		.key = PD692X0_KEY_REQ,
		.sub = {0x05, 0xc0},
		.data = {0x4e, 0x4e, 0x4e, 0x4e,
			 0x4e, 0x4e, 0x4e, 0x4e},
	},
};

static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo)
@@ -435,6 +456,184 @@ static int pd692x0_pi_is_enabled(struct pse_controller_dev *pcdev, int id)
	}
}

struct pd692x0_pse_ext_state_mapping {
	u32 status_code;
	enum ethtool_c33_pse_ext_state pse_ext_state;
	u32 pse_ext_substate;
};

static const struct pd692x0_pse_ext_state_mapping
pd692x0_pse_ext_state_map[] = {
	{0x06, ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_HIGH_VOLTAGE},
	{0x07, ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_LOW_VOLTAGE},
	{0x08, ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_PSE_ENABLE_DISABLE_PIN_ACTIVE},
	{0x0C, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_NON_EXISTING_PORT},
	{0x11, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNDEFINED_PORT},
	{0x12, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT},
	{0x1B, ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_DET_IN_PROCESS},
	{0x1C, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
	{0x1E, ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_DETECTED_UNDERLOAD},
	{0x1F, ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_OVLD_DETECTED_OVERLOAD},
	{0x20, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_BUDGET_EXCEEDED},
	{0x21, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT},
	{0x22, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_CONFIG_CHANGE},
	{0x24, ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_VOLTAGE_INJECTION},
	{0x25, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
	{0x34, ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_SHORT_DETECTED_SHORT_CONDITION},
	{0x35, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP},
	{0x36, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP},
	{0x37, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
	{0x3C, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PORT_PW_LIMIT_EXCEEDS_CONTROLLER_BUDGET},
	{0x3D, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PD_REQUEST_EXCEEDS_PORT_LIMIT},
	{0x41, ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_HW_PW_LIMIT},
	{0x43, ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS},
	{0xA7, ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_CONNECTION_CHECK_ERROR},
	{0xA8, ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID,
		ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_CONNECTION_OPEN},
	{ /* sentinel */ }
};

static void
pd692x0_get_ext_state(struct ethtool_c33_pse_ext_state_info *c33_ext_state_info,
		      u32 status_code)
{
	const struct pd692x0_pse_ext_state_mapping *ext_state_map;

	ext_state_map = pd692x0_pse_ext_state_map;
	while (ext_state_map->status_code) {
		if (ext_state_map->status_code == status_code) {
			c33_ext_state_info->c33_pse_ext_state = ext_state_map->pse_ext_state;
			c33_ext_state_info->__c33_pse_ext_substate = ext_state_map->pse_ext_substate;
			return;
		}
		ext_state_map++;
	}
}

struct pd692x0_class_pw {
	int class;
	int class_cfg_value;
	int class_pw;
	int max_added_class_pw;
};

#define PD692X0_CLASS_PW_TABLE_SIZE 4
/* 4/2 pairs class configuration power table in compliance mode.
 * Need to be arranged in ascending order of power support.
 */
static const struct pd692x0_class_pw
pd692x0_class_pw_table[PD692X0_CLASS_PW_TABLE_SIZE] = {
	{.class = 3, .class_cfg_value = 0x3, .class_pw = 15000, .max_added_class_pw = 3100},
	{.class = 4, .class_cfg_value = 0x2, .class_pw = 30000, .max_added_class_pw = 8000},
	{.class = 6, .class_cfg_value = 0x1, .class_pw = 60000, .max_added_class_pw = 5000},
	{.class = 8, .class_cfg_value = 0x0, .class_pw = 90000, .max_added_class_pw = 7500},
};

static int pd692x0_pi_get_pw_from_table(int op_mode, int added_pw)
{
	const struct pd692x0_class_pw *pw_table;
	int i;

	pw_table = pd692x0_class_pw_table;
	for (i = 0; i < PD692X0_CLASS_PW_TABLE_SIZE; i++, pw_table++) {
		if (pw_table->class_cfg_value == op_mode)
			return pw_table->class_pw + added_pw * 100;
	}

	return -ERANGE;
}

static int pd692x0_pi_set_pw_from_table(struct device *dev,
					struct pd692x0_msg *msg, int pw)
{
	const struct pd692x0_class_pw *pw_table;
	int i;

	pw_table = pd692x0_class_pw_table;
	if (pw < pw_table->class_pw) {
		dev_err(dev,
			"Power limit %dmW not supported. Ranges minimal available: [%d-%d]\n",
			pw,
			pw_table->class_pw,
			pw_table->class_pw + pw_table->max_added_class_pw);
		return -ERANGE;
	}

	for (i = 0; i < PD692X0_CLASS_PW_TABLE_SIZE; i++, pw_table++) {
		if (pw > (pw_table->class_pw + pw_table->max_added_class_pw))
			continue;

		if (pw < pw_table->class_pw) {
			dev_err(dev,
				"Power limit %dmW not supported. Ranges availables: [%d-%d] or [%d-%d]\n",
				pw,
				(pw_table - 1)->class_pw,
				(pw_table - 1)->class_pw + (pw_table - 1)->max_added_class_pw,
				pw_table->class_pw,
				pw_table->class_pw + pw_table->max_added_class_pw);
			return -ERANGE;
		}

		msg->data[2] = pw_table->class_cfg_value;
		msg->data[3] = (pw - pw_table->class_pw) / 100;
		return 0;
	}

	pw_table--;
	dev_warn(dev,
		 "Power limit %dmW not supported. Set to highest power limit %dmW\n",
		 pw, pw_table->class_pw + pw_table->max_added_class_pw);
	msg->data[2] = pw_table->class_cfg_value;
	msg->data[3] = pw_table->max_added_class_pw / 100;
	return 0;
}

static int
pd692x0_pi_get_pw_ranges(struct pse_control_status *st)
{
	const struct pd692x0_class_pw *pw_table;
	int i;

	pw_table = pd692x0_class_pw_table;
	st->c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE,
					  sizeof(struct ethtool_c33_pse_pw_limit_range),
					  GFP_KERNEL);
	if (!st->c33_pw_limit_ranges)
		return -ENOMEM;

	for (i = 0; i < PD692X0_CLASS_PW_TABLE_SIZE; i++, pw_table++) {
		st->c33_pw_limit_ranges[i].min = pw_table->class_pw;
		st->c33_pw_limit_ranges[i].max = pw_table->class_pw + pw_table->max_added_class_pw;
	}

	st->c33_pw_limit_nb_ranges = i;
	return 0;
}

static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev,
				      unsigned long id,
				      struct netlink_ext_ack *extack,
@@ -442,6 +641,7 @@ static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev,
{
	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
	struct pd692x0_msg msg, buf = {0};
	u32 class;
	int ret;

	ret = pd692x0_fw_unavailable(priv);
@@ -471,6 +671,36 @@ static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev,

	priv->admin_state[id] = status->c33_admin_state;

	pd692x0_get_ext_state(&status->c33_ext_state_info, buf.sub[0]);
	status->c33_actual_pw = (buf.data[0] << 4 | buf.data[1]) * 100;

	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM];
	msg.sub[2] = id;
	memset(&buf, 0, sizeof(buf));
	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
	if (ret < 0)
		return ret;

	ret = pd692x0_pi_get_pw_from_table(buf.data[0], buf.data[1]);
	if (ret < 0)
		return ret;
	status->c33_avail_pw_limit = ret;

	memset(&buf, 0, sizeof(buf));
	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_CLASS];
	msg.sub[2] = id;
	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
	if (ret < 0)
		return ret;

	class = buf.data[3] >> 4;
	if (class <= 8)
		status->c33_pw_class = class;

	ret = pd692x0_pi_get_pw_ranges(status);
	if (ret < 0)
		return ret;

	return 0;
}

@@ -749,12 +979,97 @@ static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
	return ret;
}

static int pd692x0_pi_get_voltage(struct pse_controller_dev *pcdev, int id)
{
	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
	struct pd692x0_msg msg, buf = {0};
	int ret;

	ret = pd692x0_fw_unavailable(priv);
	if (ret)
		return ret;

	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_MEAS];
	msg.sub[2] = id;
	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
	if (ret < 0)
		return ret;

	/* Convert 0.1V unit to uV */
	return (buf.sub[0] << 8 | buf.sub[1]) * 100000;
}

static int pd692x0_pi_get_current_limit(struct pse_controller_dev *pcdev,
					int id)
{
	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
	struct pd692x0_msg msg, buf = {0};
	int mW, uV, uA, ret;
	s64 tmp_64;

	msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM];
	msg.sub[2] = id;
	ret = pd692x0_sendrecv_msg(priv, &msg, &buf);
	if (ret < 0)
		return ret;

	ret = pd692x0_pi_get_pw_from_table(buf.data[2], buf.data[3]);
	if (ret < 0)
		return ret;
	mW = ret;

	ret = pd692x0_pi_get_voltage(pcdev, id);
	if (ret < 0)
		return ret;
	uV = ret;

	tmp_64 = mW;
	tmp_64 *= 1000000000ull;
	/* uA = mW * 1000000000 / uV */
	uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
	return uA;
}

static int pd692x0_pi_set_current_limit(struct pse_controller_dev *pcdev,
					int id, int max_uA)
{
	struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
	struct device *dev = &priv->client->dev;
	struct pd692x0_msg msg, buf = {0};
	int uV, ret, mW;
	s64 tmp_64;

	ret = pd692x0_fw_unavailable(priv);
	if (ret)
		return ret;

	ret = pd692x0_pi_get_voltage(pcdev, id);
	if (ret < 0)
		return ret;
	uV = ret;

	msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM];
	msg.sub[2] = id;
	tmp_64 = uV;
	tmp_64 *= max_uA;
	/* mW = uV * uA / 1000000000 */
	mW = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000);
	ret = pd692x0_pi_set_pw_from_table(dev, &msg, mW);
	if (ret)
		return ret;

	return pd692x0_sendrecv_msg(priv, &msg, &buf);
}

static const struct pse_controller_ops pd692x0_ops = {
	.setup_pi_matrix = pd692x0_setup_pi_matrix,
	.ethtool_get_status = pd692x0_ethtool_get_status,
	.pi_enable = pd692x0_pi_enable,
	.pi_disable = pd692x0_pi_disable,
	.pi_is_enabled = pd692x0_pi_is_enabled,
	.pi_get_voltage = pd692x0_pi_get_voltage,
	.pi_get_current_limit = pd692x0_pi_get_current_limit,
	.pi_set_current_limit = pd692x0_pi_set_current_limit,
};

#define PD692X0_FW_LINE_MAX_SZ 0xff
+161 −11
Original line number Diff line number Diff line
@@ -265,10 +265,113 @@ static int pse_pi_disable(struct regulator_dev *rdev)
	return ret;
}

static int _pse_pi_get_voltage(struct regulator_dev *rdev)
{
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
	const struct pse_controller_ops *ops;
	int id;

	ops = pcdev->ops;
	if (!ops->pi_get_voltage)
		return -EOPNOTSUPP;

	id = rdev_get_id(rdev);
	return ops->pi_get_voltage(pcdev, id);
}

static int pse_pi_get_voltage(struct regulator_dev *rdev)
{
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
	int ret;

	mutex_lock(&pcdev->lock);
	ret = _pse_pi_get_voltage(rdev);
	mutex_unlock(&pcdev->lock);

	return ret;
}

static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev,
				   int id,
				   struct netlink_ext_ack *extack,
				   struct pse_control_status *status);

static int pse_pi_get_current_limit(struct regulator_dev *rdev)
{
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
	const struct pse_controller_ops *ops;
	struct netlink_ext_ack extack = {};
	struct pse_control_status st = {};
	int id, uV, ret;
	s64 tmp_64;

	ops = pcdev->ops;
	id = rdev_get_id(rdev);
	mutex_lock(&pcdev->lock);
	if (ops->pi_get_current_limit) {
		ret = ops->pi_get_current_limit(pcdev, id);
		goto out;
	}

	/* If pi_get_current_limit() callback not populated get voltage
	 * from pi_get_voltage() and power limit from ethtool_get_status()
	 *  to calculate current limit.
	 */
	ret = _pse_pi_get_voltage(rdev);
	if (!ret) {
		dev_err(pcdev->dev, "Voltage null\n");
		ret = -ERANGE;
		goto out;
	}
	if (ret < 0)
		goto out;
	uV = ret;

	ret = _pse_ethtool_get_status(pcdev, id, &extack, &st);
	if (ret)
		goto out;

	if (!st.c33_avail_pw_limit) {
		ret = -ENODATA;
		goto out;
	}

	tmp_64 = st.c33_avail_pw_limit;
	tmp_64 *= 1000000000ull;
	/* uA = mW * 1000000000 / uV */
	ret = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);

out:
	mutex_unlock(&pcdev->lock);
	return ret;
}

static int pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA,
				    int max_uA)
{
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
	const struct pse_controller_ops *ops;
	int id, ret;

	ops = pcdev->ops;
	if (!ops->pi_set_current_limit)
		return -EOPNOTSUPP;

	id = rdev_get_id(rdev);
	mutex_lock(&pcdev->lock);
	ret = ops->pi_set_current_limit(pcdev, id, max_uA);
	mutex_unlock(&pcdev->lock);

	return ret;
}

static const struct regulator_ops pse_pi_ops = {
	.is_enabled = pse_pi_is_enabled,
	.enable = pse_pi_enable,
	.disable = pse_pi_disable,
	.get_voltage = pse_pi_get_voltage,
	.get_current_limit = pse_pi_get_current_limit,
	.set_current_limit = pse_pi_set_current_limit,
};

static int
@@ -298,7 +401,9 @@ devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev,
	rdesc->ops = &pse_pi_ops;
	rdesc->owner = pcdev->owner;

	rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
	rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS |
						 REGULATOR_CHANGE_CURRENT;
	rinit_data->constraints.max_uA = MAX_PI_CURRENT;
	rinit_data->supply_regulator = "vpwr";

	rconfig.dev = pcdev->dev;
@@ -626,6 +731,23 @@ struct pse_control *of_pse_control_get(struct device_node *node)
}
EXPORT_SYMBOL_GPL(of_pse_control_get);

static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev,
				   int id,
				   struct netlink_ext_ack *extack,
				   struct pse_control_status *status)
{
	const struct pse_controller_ops *ops;

	ops = pcdev->ops;
	if (!ops->ethtool_get_status) {
		NL_SET_ERR_MSG(extack,
			       "PSE driver does not support status report");
		return -EOPNOTSUPP;
	}

	return ops->ethtool_get_status(pcdev, id, extack, status);
}

/**
 * pse_ethtool_get_status - get status of PSE control
 * @psec: PSE control pointer
@@ -638,19 +760,10 @@ int pse_ethtool_get_status(struct pse_control *psec,
			   struct netlink_ext_ack *extack,
			   struct pse_control_status *status)
{
	const struct pse_controller_ops *ops;
	int err;

	ops = psec->pcdev->ops;

	if (!ops->ethtool_get_status) {
		NL_SET_ERR_MSG(extack,
			       "PSE driver does not support status report");
		return -EOPNOTSUPP;
	}

	mutex_lock(&psec->pcdev->lock);
	err = ops->ethtool_get_status(psec->pcdev, psec->id, extack, status);
	err = _pse_ethtool_get_status(psec->pcdev, psec->id, extack, status);
	mutex_unlock(&psec->pcdev->lock);

	return err;
@@ -732,6 +845,43 @@ int pse_ethtool_set_config(struct pse_control *psec,
}
EXPORT_SYMBOL_GPL(pse_ethtool_set_config);

/**
 * pse_ethtool_set_pw_limit - set PSE control power limit
 * @psec: PSE control pointer
 * @extack: extack for reporting useful error messages
 * @pw_limit: power limit value in mW
 *
 * Return: 0 on success and failure value on error
 */
int pse_ethtool_set_pw_limit(struct pse_control *psec,
			     struct netlink_ext_ack *extack,
			     const unsigned int pw_limit)
{
	int uV, uA, ret;
	s64 tmp_64;

	ret = regulator_get_voltage(psec->ps);
	if (!ret) {
		NL_SET_ERR_MSG(extack,
			       "Can't calculate the current, PSE voltage read is 0");
		return -ERANGE;
	}
	if (ret < 0) {
		NL_SET_ERR_MSG(extack,
			       "Error reading PSE voltage");
		return ret;
	}
	uV = ret;

	tmp_64 = pw_limit;
	tmp_64 *= 1000000000ull;
	/* uA = mW * 1000000000 / uV */
	uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);

	return regulator_set_current_limit(psec->ps, 0, uA);
}
EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit);

bool pse_has_podl(struct pse_control *psec)
{
	return psec->pcdev->types & ETHTOOL_PSE_PODL;
+20 −0
Original line number Diff line number Diff line
@@ -1273,4 +1273,24 @@ struct ethtool_forced_speed_map {

void
ethtool_forced_speed_maps_init(struct ethtool_forced_speed_map *maps, u32 size);

/* C33 PSE extended state and substate. */
struct ethtool_c33_pse_ext_state_info {
	enum ethtool_c33_pse_ext_state c33_pse_ext_state;
	union {
		enum ethtool_c33_pse_ext_substate_error_condition error_condition;
		enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable;
		enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted;
		enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim;
		enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected;
		enum ethtool_c33_pse_ext_substate_power_not_available power_not_available;
		enum ethtool_c33_pse_ext_substate_short_detected short_detected;
		u32 __c33_pse_ext_substate;
	};
};

struct ethtool_c33_pse_pw_limit_range {
	u32 min;
	u32 max;
};
#endif /* _LINUX_ETHTOOL_H */
Loading