Skip to content

Commit

Permalink
Merge branch 'net-atlantic-phy-tunables-from-mac-driver'
Browse files Browse the repository at this point in the history
Igor Russkikh says:

====================
net: atlantic: phy tunables from mac driver

This series implements phy tunables settings via MAC driver callbacks.

AQC 10G devices use integrated MAC+PHY solution, where PHY is fully controlled
by MAC firmware. Therefore, it is not possible to implement separate phy driver
for these.

We use ethtool ops callbacks to implement downshift and EDPC tunables.

v3: fixed flaw in EDPD logic, from Andrew
v2: comments from Andrew
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 6, 2020
2 parents 302af7c + 60db5e4 commit 9b9dda5
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 13 deletions.
53 changes: 53 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,57 @@ static int aq_ethtool_set_priv_flags(struct net_device *ndev, u32 flags)
return ret;
}

static int aq_ethtool_get_phy_tunable(struct net_device *ndev,
const struct ethtool_tunable *tuna, void *data)
{
struct aq_nic_s *aq_nic = netdev_priv(ndev);

switch (tuna->id) {
case ETHTOOL_PHY_EDPD: {
u16 *val = data;

*val = aq_nic->aq_nic_cfg.is_media_detect ? AQ_HW_MEDIA_DETECT_CNT : 0;
break;
}
case ETHTOOL_PHY_DOWNSHIFT: {
u8 *val = data;

*val = (u8)aq_nic->aq_nic_cfg.downshift_counter;
break;
}
default:
return -EOPNOTSUPP;
}

return 0;
}

static int aq_ethtool_set_phy_tunable(struct net_device *ndev,
const struct ethtool_tunable *tuna, const void *data)
{
int err = -EOPNOTSUPP;
struct aq_nic_s *aq_nic = netdev_priv(ndev);

switch (tuna->id) {
case ETHTOOL_PHY_EDPD: {
const u16 *val = data;

err = aq_nic_set_media_detect(aq_nic, *val);
break;
}
case ETHTOOL_PHY_DOWNSHIFT: {
const u8 *val = data;

err = aq_nic_set_downshift(aq_nic, *val);
break;
}
default:
break;
}

return err;
}

const struct ethtool_ops aq_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES,
Expand Down Expand Up @@ -952,4 +1003,6 @@ const struct ethtool_ops aq_ethtool_ops = {
.get_coalesce = aq_ethtool_get_coalesce,
.set_coalesce = aq_ethtool_set_coalesce,
.get_ts_info = aq_ethtool_get_ts_info,
.get_phy_tunable = aq_ethtool_get_phy_tunable,
.set_phy_tunable = aq_ethtool_set_phy_tunable,
};
6 changes: 6 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ struct aq_stats_s {
#define AQ_HW_LED_BLINK 0x2U
#define AQ_HW_LED_DEFAULT 0x0U

#define AQ_HW_MEDIA_DETECT_CNT 6000

enum aq_priv_flags {
AQ_HW_LOOPBACK_DMA_SYS,
AQ_HW_LOOPBACK_PKT_SYS,
Expand Down Expand Up @@ -386,6 +388,10 @@ struct aq_fw_ops {
int (*get_eee_rate)(struct aq_hw_s *self, u32 *rate,
u32 *supported_rates);

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,
Expand Down
50 changes: 50 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_nic.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ int aq_nic_init(struct aq_nic_s *self)
mutex_unlock(&self->fwreq_mutex);
if (err < 0)
goto err_exit;
/* Restore default settings */
aq_nic_set_downshift(self, self->aq_nic_cfg.downshift_counter);
aq_nic_set_media_detect(self, self->aq_nic_cfg.is_media_detect ?
AQ_HW_MEDIA_DETECT_CNT : 0);

err = self->aq_hw_ops->hw_init(self->aq_hw,
aq_nic_get_ndev(self)->dev_addr);
Expand Down Expand Up @@ -1398,6 +1402,52 @@ void aq_nic_release_filter(struct aq_nic_s *self, enum aq_rx_filter_type type,
}
}

int aq_nic_set_downshift(struct aq_nic_s *self, int val)
{
int err = 0;
struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg;

if (!self->aq_fw_ops->set_downshift)
return -EOPNOTSUPP;

if (val > 15) {
netdev_err(self->ndev, "downshift counter should be <= 15\n");
return -EINVAL;
}
cfg->downshift_counter = val;

mutex_lock(&self->fwreq_mutex);
err = self->aq_fw_ops->set_downshift(self->aq_hw, cfg->downshift_counter);
mutex_unlock(&self->fwreq_mutex);

return err;
}

int aq_nic_set_media_detect(struct aq_nic_s *self, int val)
{
struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg;
int err = 0;

if (!self->aq_fw_ops->set_media_detect)
return -EOPNOTSUPP;

if (val > 0 && val != AQ_HW_MEDIA_DETECT_CNT) {
netdev_err(self->ndev, "EDPD on this device could have only fixed value of %d\n",
AQ_HW_MEDIA_DETECT_CNT);
return -EINVAL;
}

mutex_lock(&self->fwreq_mutex);
err = self->aq_fw_ops->set_media_detect(self->aq_hw, !!val);
mutex_unlock(&self->fwreq_mutex);

/* msecs plays no role - configuration is always fixed in PHY */
if (!err)
cfg->is_media_detect = !!val;

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;
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_nic.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ 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;
u8 tcs;
Expand Down Expand Up @@ -195,6 +197,8 @@ int aq_nic_set_link_ksettings(struct aq_nic_s *self,
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 val);
int aq_nic_set_media_detect(struct aq_nic_s *self, int val);
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);
Expand Down
37 changes: 37 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,41 @@ static u32 aq_fw2x_state2_get(struct aq_hw_s *self)
return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
}

static int aq_fw2x_set_downshift(struct aq_hw_s *self, u32 counter)
{
int err = 0;
u32 mpi_opts;
u32 offset;

offset = offsetof(struct hw_atl_utils_settings, downshift_retry_count);
err = hw_atl_write_fwsettings_dwords(self, offset, &counter, 1);
if (err)
return err;

mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
if (counter)
mpi_opts |= HW_ATL_FW2X_CTRL_DOWNSHIFT;
else
mpi_opts &= ~HW_ATL_FW2X_CTRL_DOWNSHIFT;
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);

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;
Expand Down Expand Up @@ -692,6 +727,8 @@ const struct aq_fw_ops aq_fw_2x_ops = {
.enable_ptp = aq_fw3x_enable_ptp,
.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,
Expand Down
13 changes: 13 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,18 @@ int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self,
return 0;
}

static int aq_a2_fw_set_downshift(struct aq_hw_s *self, u32 counter)
{
struct link_options_s link_options;

hw_atl2_shared_buffer_get(self, link_options, link_options);
link_options.downshift = !!counter;
link_options.downshift_retry = counter;
hw_atl2_shared_buffer_write(self, link_options, link_options);

return hw_atl2_shared_buffer_finish_ack(self);
}

const struct aq_fw_ops aq_a2_fw_ops = {
.init = aq_a2_fw_init,
.deinit = aq_a2_fw_deinit,
Expand All @@ -536,4 +548,5 @@ const struct aq_fw_ops aq_a2_fw_ops = {
.set_flow_control = aq_a2_fw_set_flow_control,
.get_flow_control = aq_a2_fw_get_flow_control,
.set_phyloopback = aq_a2_fw_set_phyloopback,
.set_downshift = aq_a2_fw_set_downshift,
};
4 changes: 4 additions & 0 deletions include/linux/ethtool.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,10 @@ struct ethtool_ops {
struct ethtool_fecparam *);
void (*get_ethtool_phy_stats)(struct net_device *,
struct ethtool_stats *, u64 *);
int (*get_phy_tunable)(struct net_device *,
const struct ethtool_tunable *, void *);
int (*set_phy_tunable)(struct net_device *,
const struct ethtool_tunable *, const void *);
};

int ethtool_check_ops(const struct ethtool_ops *ops);
Expand Down
37 changes: 24 additions & 13 deletions net/ethtool/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2459,14 +2459,15 @@ static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna)

static int get_phy_tunable(struct net_device *dev, void __user *useraddr)
{
int ret;
struct ethtool_tunable tuna;
struct phy_device *phydev = dev->phydev;
struct ethtool_tunable tuna;
bool phy_drv_tunable;
void *data;
int ret;

if (!(phydev && phydev->drv && phydev->drv->get_tunable))
phy_drv_tunable = phydev && phydev->drv && phydev->drv->get_tunable;
if (!phy_drv_tunable && !dev->ethtool_ops->get_phy_tunable)
return -EOPNOTSUPP;

if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
return -EFAULT;
ret = ethtool_phy_tunable_valid(&tuna);
Expand All @@ -2475,9 +2476,13 @@ static int get_phy_tunable(struct net_device *dev, void __user *useraddr)
data = kmalloc(tuna.len, GFP_USER);
if (!data)
return -ENOMEM;
mutex_lock(&phydev->lock);
ret = phydev->drv->get_tunable(phydev, &tuna, data);
mutex_unlock(&phydev->lock);
if (phy_drv_tunable) {
mutex_lock(&phydev->lock);
ret = phydev->drv->get_tunable(phydev, &tuna, data);
mutex_unlock(&phydev->lock);
} else {
ret = dev->ethtool_ops->get_phy_tunable(dev, &tuna, data);
}
if (ret)
goto out;
useraddr += sizeof(tuna);
Expand All @@ -2493,12 +2498,14 @@ static int get_phy_tunable(struct net_device *dev, void __user *useraddr)

static int set_phy_tunable(struct net_device *dev, void __user *useraddr)
{
int ret;
struct ethtool_tunable tuna;
struct phy_device *phydev = dev->phydev;
struct ethtool_tunable tuna;
bool phy_drv_tunable;
void *data;
int ret;

if (!(phydev && phydev->drv && phydev->drv->set_tunable))
phy_drv_tunable = phydev && phydev->drv && phydev->drv->get_tunable;
if (!phy_drv_tunable && !dev->ethtool_ops->set_phy_tunable)
return -EOPNOTSUPP;
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
return -EFAULT;
Expand All @@ -2509,9 +2516,13 @@ static int set_phy_tunable(struct net_device *dev, void __user *useraddr)
data = memdup_user(useraddr, tuna.len);
if (IS_ERR(data))
return PTR_ERR(data);
mutex_lock(&phydev->lock);
ret = phydev->drv->set_tunable(phydev, &tuna, data);
mutex_unlock(&phydev->lock);
if (phy_drv_tunable) {
mutex_lock(&phydev->lock);
ret = phydev->drv->set_tunable(phydev, &tuna, data);
mutex_unlock(&phydev->lock);
} else {
ret = dev->ethtool_ops->set_phy_tunable(dev, &tuna, data);
}

kfree(data);
return ret;
Expand Down

0 comments on commit 9b9dda5

Please sign in to comment.