diff mbox series

[net-next,3/3] net: atlantic: implement media detect feature via phy tunables

Message ID 20200929161307.542-4-irusskikh@marvell.com
State New
Headers show
Series net: atlantic: phy tunables from mac driver | expand

Commit Message

Igor Russkikh Sept. 29, 2020, 4:13 p.m. UTC
Mediadetect is another name for the EDPD (energy detect power down).
This feature allows device to save extra power when no link is available.

PHY goes into the extreme power saving mode and only periodically wakes up
and checks for the link.

The feature may increase linkup time.

Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
---
 .../net/ethernet/aquantia/atlantic/aq_ethtool.c  | 14 ++++++++++++++
 drivers/net/ethernet/aquantia/atlantic/aq_hw.h   |  2 ++
 drivers/net/ethernet/aquantia/atlantic/aq_nic.c  | 16 ++++++++++++++++
 drivers/net/ethernet/aquantia/atlantic/aq_nic.h  |  2 ++
 .../aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c | 15 +++++++++++++++
 5 files changed, 49 insertions(+)

Comments

Andrew Lunn Sept. 29, 2020, 5:18 p.m. UTC | #1
> @@ -923,6 +923,12 @@ static int aq_ethtool_get_phy_tunable(struct net_device *ndev,

>  	struct aq_nic_s *aq_nic = netdev_priv(ndev);

>  

>  	switch (tuna->id) {

> +	case ETHTOOL_PHY_EDPD: {

> +		u16 *val = data;

> +

> +		*val = (u16)aq_nic->aq_nic_cfg.is_media_detect;

> +		break;

> +	}

>  	case ETHTOOL_PHY_DOWNSHIFT: {

>  		u8 *val = data;

>  

> @@ -943,6 +949,14 @@ static int aq_ethtool_set_phy_tunable(struct net_device *ndev,

>  	struct aq_nic_s *aq_nic = netdev_priv(ndev);

>  

>  	switch (tuna->id) {

> +	case ETHTOOL_PHY_EDPD: {

> +		const u16 *val = data;

> +

> +		/* msecs plays no role - configuration is always fixed in PHY */

> +		aq_nic->aq_nic_cfg.is_media_detect = *val ? 1 : 0;


This is the wrong usage of the API:

include/uapi/linux/ethtool.h:

* The interval units for TX wake-up are in milliseconds, since this should
 * cover a reasonable range of intervals:
 *  - from 1 millisecond, which does not sound like much of a power-saver
 *  - to ~65 seconds which is quite a lot to wait for a link to come up when
 *    plugging a cable
 */

I guess your PHY is not hard coded to 1 millisecond? Please return the
real value. And the set call should really only allow 0, or the value
the PHY is using.

    Andrew
Igor Russkikh Sept. 30, 2020, 8:37 a.m. UTC | #2
>>  	switch (tuna->id) {
>> +	case ETHTOOL_PHY_EDPD: {
>> +		const u16 *val = data;
>> +
>> +		/* msecs plays no role - configuration is always fixed in
> PHY */
>> +		aq_nic->aq_nic_cfg.is_media_detect = *val ? 1 : 0;
> 
> This is the wrong usage of the API:
> 
> include/uapi/linux/ethtool.h:
> 
> * The interval units for TX wake-up are in milliseconds, since this should
>  * cover a reasonable range of intervals:
>  *  - from 1 millisecond, which does not sound like much of a power-saver
>  *  - to ~65 seconds which is quite a lot to wait for a link to come up
> when
>  *    plugging a cable
>  */
> 
> I guess your PHY is not hard coded to 1 millisecond? Please return the
> real value. And the set call should really only allow 0, or the value
> the PHY is using.

The problem here is that FW interface only allows us to switch this mode on or
off. We can't control the interval value for this device.
Thus, we only can enable it, or disable. Basically ignoring the interval value.

Igor
Andrew Lunn Sept. 30, 2020, 2:22 p.m. UTC | #3
> The problem here is that FW interface only allows us to switch this mode on or

> off. We can't control the interval value for this device.

> Thus, we only can enable it, or disable. Basically ignoring the interval value.


Hi Igor

Since this is your own PHY, not some magical black box, i assume you
actually know what value it is using? It probably even lists it in the
data sheet.

So just hard code that value in the driver. That has got to be better
than saying the incorrect value of 1ms.

   Andrew
Igor Russkikh Oct. 1, 2020, 10:18 a.m. UTC | #4
Hi Andrew,

> Since this is your own PHY, not some magical black box, i assume you

> actually know what value it is using? It probably even lists it in the

> data sheet.

> 

> So just hard code that value in the driver. That has got to be better

> than saying the incorrect value of 1ms.


You mean always return that value in get_, and ignore what we get in set_ ?
That could be done, will investigate.

Thanks for the review,
  Igor
Andrew Lunn Oct. 1, 2020, 12:56 p.m. UTC | #5
On Thu, Oct 01, 2020 at 01:18:06PM +0300, Igor Russkikh wrote:
> Hi Andrew,

> 

> > Since this is your own PHY, not some magical black box, i assume you

> > actually know what value it is using? It probably even lists it in the

> > data sheet.

> > 

> > So just hard code that value in the driver. That has got to be better

> > than saying the incorrect value of 1ms.

> 

> You mean always return that value in get_, and ignore what we get in set_ ?

> That could be done, will investigate.


You should validate the set as well. Only two values are valid, 0 to
turn the feature off, and whatever the firmware is hard coded with.

This is where extack would be very useful. When the user initially
tries to set it to 42, you can return -EINVAL, plus a message listing
the acceptable values. The best you can do at the moment is write the
text to the kernel log.

    Andrew
diff mbox series

Patch

diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
index 1a6732e6bf54..7c38f3ab073a 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
@@ -923,6 +923,12 @@  static int aq_ethtool_get_phy_tunable(struct net_device *ndev,
 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
 
 	switch (tuna->id) {
+	case ETHTOOL_PHY_EDPD: {
+		u16 *val = data;
+
+		*val = (u16)aq_nic->aq_nic_cfg.is_media_detect;
+		break;
+	}
 	case ETHTOOL_PHY_DOWNSHIFT: {
 		u8 *val = data;
 
@@ -943,6 +949,14 @@  static int aq_ethtool_set_phy_tunable(struct net_device *ndev,
 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
 
 	switch (tuna->id) {
+	case ETHTOOL_PHY_EDPD: {
+		const u16 *val = data;
+
+		/* msecs plays no role - configuration is always fixed in PHY */
+		aq_nic->aq_nic_cfg.is_media_detect = *val ? 1 : 0;
+		err = aq_nic_set_media_detect(aq_nic);
+		break;
+	}
 	case ETHTOOL_PHY_DOWNSHIFT: {
 		const u8 *val = data;
 
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
index a17077b0dd49..77a01cf2530e 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
@@ -388,6 +388,8 @@  struct aq_fw_ops {
 
 	int (*set_downshift)(struct aq_hw_s *self, u32 counter);
 
+	int (*set_media_detect)(struct aq_hw_s *self, bool enable);
+
 	u32 (*get_link_capabilities)(struct aq_hw_s *self);
 
 	int (*send_macsec_req)(struct aq_hw_s *self,
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
index f02d193cf609..a0e858c14769 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
@@ -405,6 +405,7 @@  int aq_nic_init(struct aq_nic_s *self)
 	mutex_unlock(&self->fwreq_mutex);
 	if (err < 0)
 		goto err_exit;
+	aq_nic_set_media_detect(self);
 	aq_nic_set_downshift(self);
 
 	err = self->aq_hw_ops->hw_init(self->aq_hw,
@@ -1422,6 +1423,21 @@  int aq_nic_set_downshift(struct aq_nic_s *self)
 	return err;
 }
 
+int aq_nic_set_media_detect(struct aq_nic_s *self)
+{
+	struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg;
+	int err = 0;
+
+	if (!self->aq_fw_ops->set_media_detect)
+		return -EOPNOTSUPP;
+
+	mutex_lock(&self->fwreq_mutex);
+	err = self->aq_fw_ops->set_media_detect(self->aq_hw, cfg->is_media_detect);
+	mutex_unlock(&self->fwreq_mutex);
+
+	return err;
+}
+
 int aq_nic_setup_tc_mqprio(struct aq_nic_s *self, u32 tcs, u8 *prio_tc_map)
 {
 	struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg;
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h
index 0ab29890cb4c..9414a164bdcc 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h
@@ -62,6 +62,7 @@  struct aq_nic_cfg_s {
 	bool is_lro;
 	bool is_qos;
 	bool is_ptp;
+	bool is_media_detect;
 	int downshift_counter;
 	enum aq_tc_mode tc_mode;
 	u32 priv_flags;
@@ -197,6 +198,7 @@  struct aq_nic_cfg_s *aq_nic_get_cfg(struct aq_nic_s *self);
 u32 aq_nic_get_fw_version(struct aq_nic_s *self);
 int aq_nic_set_loopback(struct aq_nic_s *self);
 int aq_nic_set_downshift(struct aq_nic_s *self);
+int aq_nic_set_media_detect(struct aq_nic_s *self);
 int aq_nic_update_interrupt_moderation_settings(struct aq_nic_s *self);
 void aq_nic_shutdown(struct aq_nic_s *self);
 u8 aq_nic_reserve_filter(struct aq_nic_s *self, enum aq_rx_filter_type type);
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
index 09500a95380b..ee0c22d04935 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
@@ -633,6 +633,20 @@  static int aq_fw2x_set_downshift(struct aq_hw_s *self, u32 counter)
 	return err;
 }
 
+static int aq_fw2x_set_media_detect(struct aq_hw_s *self, bool on)
+{
+	u32 enable;
+	u32 offset;
+
+	if (self->fw_ver_actual < HW_ATL_FW_VER_MEDIA_CONTROL)
+		return -EOPNOTSUPP;
+
+	offset = offsetof(struct hw_atl_utils_settings, media_detect);
+	enable = on;
+
+	return hw_atl_write_fwsettings_dwords(self, offset, &enable, 1);
+}
+
 static u32 aq_fw2x_get_link_capabilities(struct aq_hw_s *self)
 {
 	int err = 0;
@@ -714,6 +728,7 @@  const struct aq_fw_ops aq_fw_2x_ops = {
 	.led_control        = aq_fw2x_led_control,
 	.set_phyloopback    = aq_fw2x_set_phyloopback,
 	.set_downshift      = aq_fw2x_set_downshift,
+	.set_media_detect   = aq_fw2x_set_media_detect,
 	.adjust_ptp         = aq_fw3x_adjust_ptp,
 	.get_link_capabilities = aq_fw2x_get_link_capabilities,
 	.send_macsec_req    = aq_fw2x_send_macsec_req,