Add a common physical (PHY) layer initialization and link notification.
An notification is sent on every link up/down change.

Signed-off-by: Omer Shpigelman <oshpigel...@habana.ai>
Co-developed-by: Abhilash K V <kvabhil...@habana.ai>
Signed-off-by: Abhilash K V <kvabhil...@habana.ai>
Co-developed-by: Andrey Agranovich <aagranov...@habana.ai>
Signed-off-by: Andrey Agranovich <aagranov...@habana.ai>
Co-developed-by: Bharat Jauhari <bjauh...@habana.ai>
Signed-off-by: Bharat Jauhari <bjauh...@habana.ai>
Co-developed-by: David Meriin <dmer...@habana.ai>
Signed-off-by: David Meriin <dmer...@habana.ai>
Co-developed-by: Sagiv Ozeri <soz...@habana.ai>
Signed-off-by: Sagiv Ozeri <soz...@habana.ai>
Co-developed-by: Zvika Yehudai <zyehu...@habana.ai>
Signed-off-by: Zvika Yehudai <zyehu...@habana.ai>
---
 .../ethernet/intel/hbl_cn/common/hbl_cn_phy.c | 201 ++++++++++++++++++
 1 file changed, 201 insertions(+)

diff --git a/drivers/net/ethernet/intel/hbl_cn/common/hbl_cn_phy.c 
b/drivers/net/ethernet/intel/hbl_cn/common/hbl_cn_phy.c
index 0d07cd78221d..6753d54ae2b0 100644
--- a/drivers/net/ethernet/intel/hbl_cn/common/hbl_cn_phy.c
+++ b/drivers/net/ethernet/intel/hbl_cn/common/hbl_cn_phy.c
@@ -4,30 +4,231 @@
  * All Rights Reserved.
  */
 
+#include <linux/firmware.h>
 #include "hbl_cn.h"
 
+static void port_reset_state(struct hbl_cn_port *cn_port)
+{
+       cn_port->pcs_link = false;
+       cn_port->eq_pcs_link = false;
+       cn_port->auto_neg_resolved = false;
+       cn_port->auto_neg_skipped = false;
+       cn_port->phy_fw_tuned = false;
+       cn_port->retry_cnt = 0;
+       cn_port->pcs_remote_fault_seq_cnt = 0;
+       cn_port->pcs_link_restore_cnt = 0;
+       cn_port->correctable_errors_cnt = 0;
+       cn_port->uncorrectable_errors_cnt = 0;
+}
+
+static u32 get_data_rate(struct hbl_cn_port *cn_port)
+{
+       struct hbl_cn_device *hdev = cn_port->hdev;
+       u32 port, speed, data_rate;
+
+       port = cn_port->port;
+       speed = cn_port->speed;
+
+       switch (speed) {
+       case SPEED_10000:
+               data_rate = NIC_DR_10;
+               break;
+       case SPEED_25000:
+               data_rate = NIC_DR_25;
+               break;
+       case SPEED_50000:
+               data_rate = NIC_DR_50;
+               break;
+       case SPEED_100000:
+               data_rate = NIC_DR_50;
+               break;
+       case SPEED_200000:
+               data_rate = NIC_DR_100;
+               break;
+       case SPEED_400000:
+               data_rate = NIC_DR_100;
+               break;
+       default:
+               data_rate = NIC_DR_50;
+               dev_err(hdev->dev, "unknown port %d speed, continue with 50 
GHz\n", port);
+               break;
+       }
+
+       dev_dbg(hdev->dev, "port %d, speed %d data rate %d\n", port, speed, 
data_rate);
+
+       return data_rate;
+}
+
 void hbl_cn_phy_set_port_status(struct hbl_cn_port *cn_port, bool up)
 {
+       struct hbl_cn_device *hdev = cn_port->hdev;
+       struct hbl_cn_asic_port_funcs *port_funcs;
+       struct hbl_en_aux_ops *aux_ops;
+       struct hbl_aux_dev *aux_dev;
+       u32 port = cn_port->port;
+       bool is_ibdev;
+       int rc;
+
+       aux_dev = &hdev->en_aux_dev;
+       aux_ops = aux_dev->aux_ops;
+       port_funcs = hdev->asic_funcs->port_funcs;
+       is_ibdev = hbl_cn_is_ibdev(hdev);
+
+       port_funcs->set_port_status(cn_port, up);
+
+       if (cn_port->eth_enable) {
+               if (aux_ops->set_port_status)
+                       aux_ops->set_port_status(aux_dev, port, up);
+       } else {
+               if (hdev->ctx)
+                       dev_info(hdev->dev, "Card %u Port %u: link %s\n",
+                                hdev->card_location, port, up ? "up" : "down");
+               else
+                       dev_dbg(hdev->dev, "Card %u Port %u: link %s\n",
+                               hdev->card_location, port, up ? "up" : "down");
+       }
+
+       /* IB flow. User polls for IB events.
+        *  - internal ports: Enqueue link event in EQ dispatcher. IB event 
would be dispatched in
+        *                    response.
+        *  - external ports: Do not enqueue. hbl IB driver dispatches IB 
events from netdev
+        *                    notifier chain handler.
+        * non-IB flow. User polls for EQ events.
+        *  - internal ports: Enqueue link event in EQ dispatcher.
+        *  - external ports: Enqueue link event in EQ dispatcher.
+        */
+       if (!is_ibdev || !cn_port->eth_enable) {
+               if (hdev->has_eq) {
+                       rc = hbl_cn_eq_dispatcher_enqueue_bcast(cn_port, 
&cn_port->link_eqe);
+                       if (rc)
+                               dev_dbg_ratelimited(hdev->dev,
+                                                   "Port %d, failed to 
dispatch link event %s, %d\n",
+                                                   port, up ? "up" : "down", 
rc);
+               }
+       }
+
+       cn_port->port_toggle_cnt++;
+
+       /* The FEC counters are relevant during the time that link is UP, hence 
reset them here */
+       if (up) {
+               cn_port->correctable_errors_cnt = 0;
+               cn_port->uncorrectable_errors_cnt = 0;
+       }
+
+       if (hdev->pldm) {
+               dev_dbg(hdev->dev, "%s: port %u\n", __func__, port);
+               msleep(1000);
+       }
 }
 
 int hbl_cn_phy_init(struct hbl_cn_port *cn_port)
 {
+       struct hbl_cn_device *hdev = cn_port->hdev;
+       struct hbl_cn_asic_port_funcs *port_funcs;
+       int rc;
+
+       port_funcs = hdev->asic_funcs->port_funcs;
+
+       /* If mac_loopback is enabled on this port, move the port status to UP 
state */
+       if (cn_port->mac_loopback) {
+               cn_port->pcs_link = true;
+               hbl_cn_phy_set_port_status(cn_port, true);
+               return 0;
+       }
+
+       if (!hdev->phy_config_fw) {
+               /* If EQ is supported, it will take care of setting the port 
status */
+               if (!hdev->has_eq) {
+                       cn_port->pcs_link = true;
+                       hbl_cn_phy_set_port_status(cn_port, true);
+               }
+
+               return 0;
+       }
+
+       cn_port->data_rate = get_data_rate(cn_port);
+
+       rc = port_funcs->phy_port_power_up(cn_port);
+       if (rc) {
+               dev_err(hdev->dev, "ASIC specific phy port power-up failed, 
%d\n", rc);
+               return rc;
+       }
+
+       port_funcs->phy_port_start_stop(cn_port, true);
+
+       queue_delayed_work(cn_port->wq, &cn_port->link_status_work, 
msecs_to_jiffies(1));
+
        return 0;
 }
 
+/* This function does not change the port link status in order to avoid 
unnecessary netdev actions
+ * and prints. Hence it should be done from outside.
+ */
 void hbl_cn_phy_fini(struct hbl_cn_port *cn_port)
 {
+       struct hbl_cn_device *hdev = cn_port->hdev;
+       struct hbl_cn_asic_port_funcs *port_funcs;
+
+       /* This is done before the check because we support setting mac 
loopback for a specific port
+        * and this function might be called when cn_port->mac_loopback is true 
(during the port
+        * reset after setting mac loopback), but the link status work was 
scheduled before (when
+        * the port was opened w/o mac loopback).
+        */
+       cancel_delayed_work_sync(&cn_port->link_status_work);
+
+       port_funcs = hdev->asic_funcs->port_funcs;
+
+       if (!hdev->phy_config_fw || cn_port->mac_loopback) {
+               cn_port->pcs_link = false;
+               cn_port->eq_pcs_link = false;
+               return;
+       }
+
+       port_reset_state(cn_port);
+       port_funcs->phy_port_start_stop(cn_port, false);
 }
 
 void hbl_cn_phy_port_reconfig(struct hbl_cn_port *cn_port)
 {
+       struct hbl_cn_device *hdev = cn_port->hdev;
+       struct hbl_cn_asic_port_funcs *port_funcs;
+
+       port_funcs = hdev->asic_funcs->port_funcs;
+
+       port_funcs->phy_port_reconfig(cn_port);
+
+       port_reset_state(cn_port);
 }
 
 int hbl_cn_phy_has_binary_fw(struct hbl_cn_device *hdev)
 {
+       struct hbl_cn_asic_funcs *asic_funcs = hdev->asic_funcs;
+       const struct firmware *fw;
+       const char *fw_name;
+       int rc;
+
+       fw_name = asic_funcs->get_phy_fw_name();
+
+       rc = request_firmware(&fw, fw_name, hdev->dev);
+       if (rc) {
+               dev_err(hdev->dev, "Firmware file %s is not found!\n", fw_name);
+               return rc;
+       }
+
+       release_firmware(fw);
+
        return 0;
 }
 
 void hbl_cn_phy_set_fw_polarity(struct hbl_cn_device *hdev)
 {
+       struct hbl_cn_cpucp_info *cpucp_info;
+
+       if (hdev->skip_phy_pol_cfg)
+               return;
+
+       cpucp_info = hdev->cpucp_info;
+
+       hdev->pol_tx_mask = cpucp_info->pol_tx_mask[0];
+       hdev->pol_rx_mask = cpucp_info->pol_rx_mask[0];
 }
-- 
2.34.1

Reply via email to