diff options
Diffstat (limited to 'drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c')
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c | 227 |
1 files changed, 223 insertions, 4 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c index 0ffc33bd67d0..85628acbcc1d 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c @@ -7,6 +7,7 @@ #include "aq_hw.h" #include "aq_hw_utils.h" +#include "aq_nic.h" #include "hw_atl/hw_atl_llh.h" #include "hw_atl2_utils.h" #include "hw_atl2_llh.h" @@ -15,15 +16,29 @@ #define AQ_A2_FW_READ_TRY_MAX 1000 #define hw_atl2_shared_buffer_write(HW, ITEM, VARIABLE) \ +{\ + BUILD_BUG_ON_MSG((offsetof(struct fw_interface_in, ITEM) % \ + sizeof(u32)) != 0,\ + "Unaligned write " # ITEM);\ + BUILD_BUG_ON_MSG((sizeof(VARIABLE) % sizeof(u32)) != 0,\ + "Unaligned write length " # ITEM);\ hw_atl2_mif_shared_buf_write(HW,\ (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\ - (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32)) + (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\ +} #define hw_atl2_shared_buffer_get(HW, ITEM, VARIABLE) \ +{\ + BUILD_BUG_ON_MSG((offsetof(struct fw_interface_in, ITEM) % \ + sizeof(u32)) != 0,\ + "Unaligned get " # ITEM);\ + BUILD_BUG_ON_MSG((sizeof(VARIABLE) % sizeof(u32)) != 0,\ + "Unaligned get length " # ITEM);\ hw_atl2_mif_shared_buf_get(HW, \ (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\ (u32 *)&(VARIABLE), \ - sizeof(VARIABLE) / sizeof(u32)) + sizeof(VARIABLE) / sizeof(u32));\ +} /* This should never be used on non atomic fields, * treat any > u32 read as non atomic. @@ -32,7 +47,9 @@ {\ BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \ sizeof(u32)) != 0,\ - "Non aligned read " # ITEM);\ + "Unaligned read " # ITEM);\ + BUILD_BUG_ON_MSG((sizeof(VARIABLE) % sizeof(u32)) != 0,\ + "Unaligned read length " # ITEM);\ BUILD_BUG_ON_MSG(sizeof(VARIABLE) > sizeof(u32),\ "Non atomic read " # ITEM);\ hw_atl2_mif_shared_buf_read(HW, \ @@ -41,10 +58,18 @@ } #define hw_atl2_shared_buffer_read_safe(HW, ITEM, DATA) \ +({\ + BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \ + sizeof(u32)) != 0,\ + "Unaligned read_safe " # ITEM);\ + BUILD_BUG_ON_MSG((sizeof(((struct fw_interface_out *)0)->ITEM) % \ + sizeof(u32)) != 0,\ + "Unaligned read_safe length " # ITEM);\ hw_atl2_shared_buffer_read_block((HW), \ (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\ sizeof(((struct fw_interface_out *)0)->ITEM) / sizeof(u32),\ - (DATA)) + (DATA));\ +}) static int hw_atl2_shared_buffer_read_block(struct aq_hw_s *self, u32 offset, u32 dwords, void *data) @@ -135,6 +160,67 @@ static void a2_link_speed_mask2fw(u32 speed, link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G); link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M); link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M); + + link_options->rate_1G_hd = !!(speed & AQ_NIC_RATE_1G_HALF); + link_options->rate_100M_hd = !!(speed & AQ_NIC_RATE_100M_HALF); + link_options->rate_10M_hd = !!(speed & AQ_NIC_RATE_10M_HALF); +} + +static u32 a2_fw_dev_to_eee_mask(struct device_link_caps_s *device_link_caps) +{ + u32 rate = 0; + + if (device_link_caps->eee_10G) + rate |= AQ_NIC_RATE_EEE_10G; + if (device_link_caps->eee_5G) + rate |= AQ_NIC_RATE_EEE_5G; + if (device_link_caps->eee_2P5G) + rate |= AQ_NIC_RATE_EEE_2G5; + if (device_link_caps->eee_1G) + rate |= AQ_NIC_RATE_EEE_1G; + if (device_link_caps->eee_100M) + rate |= AQ_NIC_RATE_EEE_100M; + + return rate; +} + +static u32 a2_fw_lkp_to_mask(struct lkp_link_caps_s *lkp_link_caps) +{ + u32 rate = 0; + + if (lkp_link_caps->rate_10G) + rate |= AQ_NIC_RATE_10G; + if (lkp_link_caps->rate_5G) + rate |= AQ_NIC_RATE_5G; + if (lkp_link_caps->rate_N5G) + rate |= AQ_NIC_RATE_5GSR; + if (lkp_link_caps->rate_2P5G) + rate |= AQ_NIC_RATE_2G5; + if (lkp_link_caps->rate_1G) + rate |= AQ_NIC_RATE_1G; + if (lkp_link_caps->rate_1G_hd) + rate |= AQ_NIC_RATE_1G_HALF; + if (lkp_link_caps->rate_100M) + rate |= AQ_NIC_RATE_100M; + if (lkp_link_caps->rate_100M_hd) + rate |= AQ_NIC_RATE_100M_HALF; + if (lkp_link_caps->rate_10M) + rate |= AQ_NIC_RATE_10M; + if (lkp_link_caps->rate_10M_hd) + rate |= AQ_NIC_RATE_10M_HALF; + + if (lkp_link_caps->eee_10G) + rate |= AQ_NIC_RATE_EEE_10G; + if (lkp_link_caps->eee_5G) + rate |= AQ_NIC_RATE_EEE_5G; + if (lkp_link_caps->eee_2P5G) + rate |= AQ_NIC_RATE_EEE_2G5; + if (lkp_link_caps->eee_1G) + rate |= AQ_NIC_RATE_EEE_1G; + if (lkp_link_caps->eee_100M) + rate |= AQ_NIC_RATE_EEE_100M; + + return rate; } static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed) @@ -149,6 +235,26 @@ static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed) return hw_atl2_shared_buffer_finish_ack(self); } +static void aq_a2_fw_set_mpi_flow_control(struct aq_hw_s *self, + struct link_options_s *link_options) +{ + u32 flow_control = self->aq_nic_cfg->fc.req; + + link_options->pause_rx = !!(flow_control & AQ_NIC_FC_RX); + link_options->pause_tx = !!(flow_control & AQ_NIC_FC_TX); +} + +static void aq_a2_fw_upd_eee_rate_bits(struct aq_hw_s *self, + struct link_options_s *link_options, + u32 eee_speeds) +{ + link_options->eee_10G = !!(eee_speeds & AQ_NIC_RATE_EEE_10G); + link_options->eee_5G = !!(eee_speeds & AQ_NIC_RATE_EEE_5G); + link_options->eee_2P5G = !!(eee_speeds & AQ_NIC_RATE_EEE_2G5); + link_options->eee_1G = !!(eee_speeds & AQ_NIC_RATE_EEE_1G); + link_options->eee_100M = !!(eee_speeds & AQ_NIC_RATE_EEE_100M); +} + static int aq_a2_fw_set_state(struct aq_hw_s *self, enum hal_atl_utils_fw_state_e state) { @@ -159,6 +265,9 @@ static int aq_a2_fw_set_state(struct aq_hw_s *self, switch (state) { case MPI_INIT: link_options.link_up = 1U; + aq_a2_fw_upd_eee_rate_bits(self, &link_options, + self->aq_nic_cfg->eee_speeds); + aq_a2_fw_set_mpi_flow_control(self, &link_options); break; case MPI_DEINIT: link_options.link_up = 0U; @@ -176,6 +285,7 @@ static int aq_a2_fw_set_state(struct aq_hw_s *self, static int aq_a2_fw_update_link_status(struct aq_hw_s *self) { + struct lkp_link_caps_s lkp_link_caps; struct link_status_s link_status; hw_atl2_shared_buffer_read(self, link_status, link_status); @@ -202,6 +312,15 @@ static int aq_a2_fw_update_link_status(struct aq_hw_s *self) default: self->aq_link_status.mbps = 0; } + self->aq_link_status.full_duplex = link_status.duplex; + + hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps); + + self->aq_link_status.lp_link_speed_msk = + a2_fw_lkp_to_mask(&lkp_link_caps); + self->aq_link_status.lp_flow_control = + ((lkp_link_caps.pause_rx) ? AQ_NIC_FC_RX : 0) | + ((lkp_link_caps.pause_tx) ? AQ_NIC_FC_TX : 0); return 0; } @@ -260,6 +379,53 @@ static int aq_a2_fw_update_stats(struct aq_hw_s *self) return 0; } +static int aq_a2_fw_get_phy_temp(struct aq_hw_s *self, int *temp) +{ + struct phy_health_monitor_s phy_health_monitor; + + hw_atl2_shared_buffer_read_safe(self, phy_health_monitor, + &phy_health_monitor); + + *temp = (int8_t)phy_health_monitor.phy_temperature * 1000; + return 0; +} + +static int aq_a2_fw_get_mac_temp(struct aq_hw_s *self, int *temp) +{ + /* There's only one temperature sensor on A2, use it for + * both MAC and PHY. + */ + return aq_a2_fw_get_phy_temp(self, temp); +} + +static int aq_a2_fw_set_eee_rate(struct aq_hw_s *self, u32 speed) +{ + struct link_options_s link_options; + + hw_atl2_shared_buffer_get(self, link_options, link_options); + + aq_a2_fw_upd_eee_rate_bits(self, &link_options, speed); + + hw_atl2_shared_buffer_write(self, link_options, link_options); + + return hw_atl2_shared_buffer_finish_ack(self); +} + +static int aq_a2_fw_get_eee_rate(struct aq_hw_s *self, u32 *rate, + u32 *supported_rates) +{ + struct device_link_caps_s device_link_caps; + struct lkp_link_caps_s lkp_link_caps; + + hw_atl2_shared_buffer_read(self, device_link_caps, device_link_caps); + hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps); + + *supported_rates = a2_fw_dev_to_eee_mask(&device_link_caps); + *rate = a2_fw_lkp_to_mask(&lkp_link_caps); + + return 0; +} + static int aq_a2_fw_renegotiate(struct aq_hw_s *self) { struct link_options_s link_options; @@ -280,6 +446,52 @@ static int aq_a2_fw_renegotiate(struct aq_hw_s *self) return err; } +static int aq_a2_fw_set_flow_control(struct aq_hw_s *self) +{ + struct link_options_s link_options; + + hw_atl2_shared_buffer_get(self, link_options, link_options); + + aq_a2_fw_set_mpi_flow_control(self, &link_options); + + hw_atl2_shared_buffer_write(self, link_options, link_options); + + return hw_atl2_shared_buffer_finish_ack(self); +} + +static u32 aq_a2_fw_get_flow_control(struct aq_hw_s *self, u32 *fcmode) +{ + struct link_status_s link_status; + + hw_atl2_shared_buffer_read(self, link_status, link_status); + + *fcmode = ((link_status.pause_rx) ? AQ_NIC_FC_RX : 0) | + ((link_status.pause_tx) ? AQ_NIC_FC_TX : 0); + return 0; +} + +static int aq_a2_fw_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable) +{ + struct link_options_s link_options; + + hw_atl2_shared_buffer_get(self, link_options, link_options); + + switch (mode) { + case AQ_HW_LOOPBACK_PHYINT_SYS: + link_options.internal_loopback = enable; + break; + case AQ_HW_LOOPBACK_PHYEXT_SYS: + link_options.external_loopback = enable; + break; + default: + return -EINVAL; + } + + hw_atl2_shared_buffer_write(self, link_options, link_options); + + return hw_atl2_shared_buffer_finish_ack(self); +} + u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self) { struct version_s version; @@ -317,4 +529,11 @@ const struct aq_fw_ops aq_a2_fw_ops = { .set_state = aq_a2_fw_set_state, .update_link_status = aq_a2_fw_update_link_status, .update_stats = aq_a2_fw_update_stats, + .get_mac_temp = aq_a2_fw_get_mac_temp, + .get_phy_temp = aq_a2_fw_get_phy_temp, + .set_eee_rate = aq_a2_fw_set_eee_rate, + .get_eee_rate = aq_a2_fw_get_eee_rate, + .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, }; |