+static int
+hns3_reinit_dev(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ int ret;
+
+ ret = hns3_cmd_init(hw);
+ if (ret) {
+ hns3_err(hw, "Failed to init cmd: %d", ret);
+ return ret;
+ }
+
+ ret = hns3_reset_all_queues(hns);
+ if (ret) {
+ hns3_err(hw, "Failed to reset all queues: %d", ret);
+ goto err_init;
+ }
+
+ ret = hns3_init_hardware(hns);
+ if (ret) {
+ hns3_err(hw, "Failed to init hardware: %d", ret);
+ goto err_init;
+ }
+
+ ret = hns3_enable_hw_error_intr(hns, true);
+ if (ret) {
+ hns3_err(hw, "fail to enable hw error interrupts: %d",
+ ret);
+ goto err_mac_init;
+ }
+ hns3_info(hw, "Reset done, driver initialization finished.");
+
+ return 0;
+
+err_mac_init:
+ hns3_uninit_umv_space(hw);
+err_init:
+ hns3_cmd_uninit(hw);
+
+ return ret;
+}
+
+static bool
+is_pf_reset_done(struct hns3_hw *hw)
+{
+ uint32_t val, reg, reg_bit;
+
+ switch (hw->reset.level) {
+ case HNS3_IMP_RESET:
+ reg = HNS3_GLOBAL_RESET_REG;
+ reg_bit = HNS3_IMP_RESET_BIT;
+ break;
+ case HNS3_GLOBAL_RESET:
+ reg = HNS3_GLOBAL_RESET_REG;
+ reg_bit = HNS3_GLOBAL_RESET_BIT;
+ break;
+ case HNS3_FUNC_RESET:
+ reg = HNS3_FUN_RST_ING;
+ reg_bit = HNS3_FUN_RST_ING_B;
+ break;
+ case HNS3_FLR_RESET:
+ default:
+ hns3_err(hw, "Wait for unsupported reset level: %d",
+ hw->reset.level);
+ return true;
+ }
+ val = hns3_read_dev(hw, reg);
+ if (hns3_get_bit(val, reg_bit))
+ return false;
+ else
+ return true;
+}
+
+bool
+hns3_is_reset_pending(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ enum hns3_reset_level reset;
+
+ hns3_check_event_cause(hns, NULL);
+ reset = hns3_get_reset_level(hns, &hw->reset.pending);
+ if (hw->reset.level != HNS3_NONE_RESET && hw->reset.level < reset) {
+ hns3_warn(hw, "High level reset %d is pending", reset);
+ return true;
+ }
+ reset = hns3_get_reset_level(hns, &hw->reset.request);
+ if (hw->reset.level != HNS3_NONE_RESET && hw->reset.level < reset) {
+ hns3_warn(hw, "High level reset %d is request", reset);
+ return true;
+ }
+ return false;
+}
+
+static int
+hns3_wait_hardware_ready(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ struct hns3_wait_data *wait_data = hw->reset.wait_data;
+ struct timeval tv;
+
+ if (wait_data->result == HNS3_WAIT_SUCCESS)
+ return 0;
+ else if (wait_data->result == HNS3_WAIT_TIMEOUT) {
+ gettimeofday(&tv, NULL);
+ hns3_warn(hw, "Reset step4 hardware not ready after reset time=%ld.%.6ld",
+ tv.tv_sec, tv.tv_usec);
+ return -ETIME;
+ } else if (wait_data->result == HNS3_WAIT_REQUEST)
+ return -EAGAIN;
+
+ wait_data->hns = hns;
+ wait_data->check_completion = is_pf_reset_done;
+ wait_data->end_ms = (uint64_t)HNS3_RESET_WAIT_CNT *
+ HNS3_RESET_WAIT_MS + get_timeofday_ms();
+ wait_data->interval = HNS3_RESET_WAIT_MS * USEC_PER_MSEC;
+ wait_data->count = HNS3_RESET_WAIT_CNT;
+ wait_data->result = HNS3_WAIT_REQUEST;
+ rte_eal_alarm_set(wait_data->interval, hns3_wait_callback, wait_data);
+ return -EAGAIN;
+}
+
+static int
+hns3_func_reset_cmd(struct hns3_hw *hw, int func_id)
+{
+ struct hns3_cmd_desc desc;
+ struct hns3_reset_cmd *req = (struct hns3_reset_cmd *)desc.data;
+
+ hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_RST_TRIGGER, false);
+ hns3_set_bit(req->mac_func_reset, HNS3_CFG_RESET_FUNC_B, 1);
+ req->fun_reset_vfid = func_id;
+
+ return hns3_cmd_send(hw, &desc, 1);
+}
+
+static int
+hns3_imp_reset_cmd(struct hns3_hw *hw)
+{
+ struct hns3_cmd_desc desc;
+
+ hns3_cmd_setup_basic_desc(&desc, 0xFFFE, false);
+ desc.data[0] = 0xeedd;
+
+ return hns3_cmd_send(hw, &desc, 1);
+}
+
+static void
+hns3_msix_process(struct hns3_adapter *hns, enum hns3_reset_level reset_level)
+{
+ struct hns3_hw *hw = &hns->hw;
+ struct timeval tv;
+ uint32_t val;
+
+ gettimeofday(&tv, NULL);
+ if (hns3_read_dev(hw, HNS3_GLOBAL_RESET_REG) ||
+ hns3_read_dev(hw, HNS3_FUN_RST_ING)) {
+ hns3_warn(hw, "Don't process msix during resetting time=%ld.%.6ld",
+ tv.tv_sec, tv.tv_usec);
+ return;
+ }
+
+ switch (reset_level) {
+ case HNS3_IMP_RESET:
+ hns3_imp_reset_cmd(hw);
+ hns3_warn(hw, "IMP Reset requested time=%ld.%.6ld",
+ tv.tv_sec, tv.tv_usec);
+ break;
+ case HNS3_GLOBAL_RESET:
+ val = hns3_read_dev(hw, HNS3_GLOBAL_RESET_REG);
+ hns3_set_bit(val, HNS3_GLOBAL_RESET_BIT, 1);
+ hns3_write_dev(hw, HNS3_GLOBAL_RESET_REG, val);
+ hns3_warn(hw, "Global Reset requested time=%ld.%.6ld",
+ tv.tv_sec, tv.tv_usec);
+ break;
+ case HNS3_FUNC_RESET:
+ hns3_warn(hw, "PF Reset requested time=%ld.%.6ld",
+ tv.tv_sec, tv.tv_usec);
+ /* schedule again to check later */
+ hns3_atomic_set_bit(HNS3_FUNC_RESET, &hw->reset.pending);
+ hns3_schedule_reset(hns);
+ break;
+ default:
+ hns3_warn(hw, "Unsupported reset level: %d", reset_level);
+ return;
+ }
+ hns3_atomic_clear_bit(reset_level, &hw->reset.request);
+}
+
+static enum hns3_reset_level
+hns3_get_reset_level(struct hns3_adapter *hns, uint64_t *levels)
+{
+ struct hns3_hw *hw = &hns->hw;
+ enum hns3_reset_level reset_level = HNS3_NONE_RESET;
+
+ /* Return the highest priority reset level amongst all */
+ if (hns3_atomic_test_bit(HNS3_IMP_RESET, levels))
+ reset_level = HNS3_IMP_RESET;
+ else if (hns3_atomic_test_bit(HNS3_GLOBAL_RESET, levels))
+ reset_level = HNS3_GLOBAL_RESET;
+ else if (hns3_atomic_test_bit(HNS3_FUNC_RESET, levels))
+ reset_level = HNS3_FUNC_RESET;
+ else if (hns3_atomic_test_bit(HNS3_FLR_RESET, levels))
+ reset_level = HNS3_FLR_RESET;
+
+ if (hw->reset.level != HNS3_NONE_RESET && reset_level < hw->reset.level)
+ return HNS3_NONE_RESET;
+
+ return reset_level;
+}
+
+static int
+hns3_prepare_reset(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ uint32_t reg_val;
+ int ret;
+
+ switch (hw->reset.level) {
+ case HNS3_FUNC_RESET:
+ ret = hns3_func_reset_cmd(hw, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * After performaning pf reset, it is not necessary to do the
+ * mailbox handling or send any command to firmware, because
+ * any mailbox handling or command to firmware is only valid
+ * after hns3_cmd_init is called.
+ */
+ rte_atomic16_set(&hw->reset.disable_cmd, 1);
+ hw->reset.stats.request_cnt++;
+ break;
+ case HNS3_IMP_RESET:
+ reg_val = hns3_read_dev(hw, HNS3_VECTOR0_OTER_EN_REG);
+ hns3_write_dev(hw, HNS3_VECTOR0_OTER_EN_REG, reg_val |
+ BIT(HNS3_VECTOR0_IMP_RESET_INT_B));
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int
+hns3_set_rst_done(struct hns3_hw *hw)
+{
+ struct hns3_pf_rst_done_cmd *req;
+ struct hns3_cmd_desc desc;
+
+ req = (struct hns3_pf_rst_done_cmd *)desc.data;
+ hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_PF_RST_DONE, false);
+ req->pf_rst_done |= HNS3_PF_RESET_DONE_BIT;
+ return hns3_cmd_send(hw, &desc, 1);
+}
+
+static int
+hns3_stop_service(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ struct rte_eth_dev *eth_dev;
+
+ eth_dev = &rte_eth_devices[hw->data->port_id];
+ if (hw->adapter_state == HNS3_NIC_STARTED)
+ rte_eal_alarm_cancel(hns3_service_handler, eth_dev);
+ hw->mac.link_status = ETH_LINK_DOWN;
+
+ hns3_set_rxtx_function(eth_dev);
+ rte_wmb();
+ /* Disable datapath on secondary process. */
+ hns3_mp_req_stop_rxtx(eth_dev);
+ rte_delay_ms(hw->tqps_num);
+
+ rte_spinlock_lock(&hw->lock);
+ if (hns->hw.adapter_state == HNS3_NIC_STARTED ||
+ hw->adapter_state == HNS3_NIC_STOPPING) {
+ hns3_do_stop(hns);
+ hw->reset.mbuf_deferred_free = true;
+ } else
+ hw->reset.mbuf_deferred_free = false;
+
+ /*
+ * It is cumbersome for hardware to pick-and-choose entries for deletion
+ * from table space. Hence, for function reset software intervention is
+ * required to delete the entries
+ */
+ if (rte_atomic16_read(&hw->reset.disable_cmd) == 0)
+ hns3_configure_all_mc_mac_addr(hns, true);
+ rte_spinlock_unlock(&hw->lock);
+
+ return 0;
+}
+
+static int
+hns3_start_service(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ struct rte_eth_dev *eth_dev;
+
+ if (hw->reset.level == HNS3_IMP_RESET ||
+ hw->reset.level == HNS3_GLOBAL_RESET)
+ hns3_set_rst_done(hw);
+ eth_dev = &rte_eth_devices[hw->data->port_id];
+ hns3_set_rxtx_function(eth_dev);
+ hns3_mp_req_start_rxtx(eth_dev);
+ if (hw->adapter_state == HNS3_NIC_STARTED)
+ hns3_service_handler(eth_dev);
+
+ return 0;
+}
+
+static int
+hns3_restore_conf(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ int ret;
+
+ ret = hns3_configure_all_mac_addr(hns, false);
+ if (ret)
+ return ret;
+
+ ret = hns3_configure_all_mc_mac_addr(hns, false);
+ if (ret)
+ goto err_mc_mac;
+
+ ret = hns3_dev_promisc_restore(hns);
+ if (ret)
+ goto err_promisc;
+
+ ret = hns3_restore_vlan_table(hns);
+ if (ret)
+ goto err_promisc;
+
+ ret = hns3_restore_vlan_conf(hns);
+ if (ret)
+ goto err_promisc;
+
+ ret = hns3_restore_all_fdir_filter(hns);
+ if (ret)
+ goto err_promisc;
+
+ if (hns->hw.adapter_state == HNS3_NIC_STARTED) {
+ ret = hns3_do_start(hns, false);
+ if (ret)
+ goto err_promisc;
+ hns3_info(hw, "hns3 dev restart successful!");
+ } else if (hw->adapter_state == HNS3_NIC_STOPPING)
+ hw->adapter_state = HNS3_NIC_CONFIGURED;
+ return 0;
+
+err_promisc:
+ hns3_configure_all_mc_mac_addr(hns, true);
+err_mc_mac:
+ hns3_configure_all_mac_addr(hns, true);
+ return ret;
+}
+
+static void
+hns3_reset_service(void *param)
+{
+ struct hns3_adapter *hns = (struct hns3_adapter *)param;
+ struct hns3_hw *hw = &hns->hw;
+ enum hns3_reset_level reset_level;
+ struct timeval tv_delta;
+ struct timeval tv_start;
+ struct timeval tv;
+ uint64_t msec;
+ int ret;
+
+ /*
+ * The interrupt is not triggered within the delay time.
+ * The interrupt may have been lost. It is necessary to handle
+ * the interrupt to recover from the error.
+ */
+ if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_DEFERRED) {
+ rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_REQUESTED);
+ hns3_err(hw, "Handling interrupts in delayed tasks");
+ hns3_interrupt_handler(&rte_eth_devices[hw->data->port_id]);
+ reset_level = hns3_get_reset_level(hns, &hw->reset.pending);
+ if (reset_level == HNS3_NONE_RESET) {
+ hns3_err(hw, "No reset level is set, try IMP reset");
+ hns3_atomic_set_bit(HNS3_IMP_RESET, &hw->reset.pending);
+ }
+ }
+ rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_NONE);
+
+ /*
+ * Check if there is any ongoing reset in the hardware. This status can
+ * be checked from reset_pending. If there is then, we need to wait for
+ * hardware to complete reset.
+ * a. If we are able to figure out in reasonable time that hardware
+ * has fully resetted then, we can proceed with driver, client
+ * reset.
+ * b. else, we can come back later to check this status so re-sched
+ * now.
+ */
+ reset_level = hns3_get_reset_level(hns, &hw->reset.pending);
+ if (reset_level != HNS3_NONE_RESET) {
+ gettimeofday(&tv_start, NULL);
+ ret = hns3_reset_process(hns, reset_level);
+ gettimeofday(&tv, NULL);
+ timersub(&tv, &tv_start, &tv_delta);
+ msec = tv_delta.tv_sec * MSEC_PER_SEC +
+ tv_delta.tv_usec / USEC_PER_MSEC;
+ if (msec > HNS3_RESET_PROCESS_MS)
+ hns3_err(hw, "%d handle long time delta %" PRIx64
+ " ms time=%ld.%.6ld",
+ hw->reset.level, msec,
+ tv.tv_sec, tv.tv_usec);
+ if (ret == -EAGAIN)
+ return;
+ }
+
+ /* Check if we got any *new* reset requests to be honored */
+ reset_level = hns3_get_reset_level(hns, &hw->reset.request);
+ if (reset_level != HNS3_NONE_RESET)
+ hns3_msix_process(hns, reset_level);
+}
+