New upstream version 17.08
[deb_dpdk.git] / drivers / net / i40e / base / i40e_common.c
index 03e94bc..900d379 100644 (file)
@@ -34,7 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
 #include "i40e_type.h"
 #include "i40e_adminq.h"
 #include "i40e_prototype.h"
-#include "i40e_virtchnl.h"
+#include "virtchnl.h"
 
 
 /**
@@ -93,6 +93,7 @@ STATIC enum i40e_status_code i40e_set_mac_type(struct i40e_hw *hw)
 #if defined(INTEGRATED_VF) || defined(VF_DRIVER)
                case I40E_DEV_ID_VF:
                case I40E_DEV_ID_VF_HV:
+               case I40E_DEV_ID_ADAPTIVE_VF:
                        hw->mac.type = I40E_MAC_VF;
                        break;
 #endif
@@ -329,13 +330,15 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
                   void *buffer, u16 buf_len)
 {
        struct i40e_aq_desc *aq_desc = (struct i40e_aq_desc *)desc;
-       u16 len = LE16_TO_CPU(aq_desc->datalen);
        u8 *buf = (u8 *)buffer;
+       u16 len;
        u16 i = 0;
 
        if ((!(mask & hw->debug_mask)) || (desc == NULL))
                return;
 
+       len = LE16_TO_CPU(aq_desc->datalen);
+
        i40e_debug(hw, mask,
                   "AQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
                   LE16_TO_CPU(aq_desc->opcode),
@@ -1295,6 +1298,8 @@ STATIC enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw)
        case I40E_PHY_TYPE_40GBASE_AOC:
        case I40E_PHY_TYPE_10GBASE_AOC:
        case I40E_PHY_TYPE_25GBASE_CR:
+       case I40E_PHY_TYPE_25GBASE_AOC:
+       case I40E_PHY_TYPE_25GBASE_ACC:
                media = I40E_MEDIA_TYPE_DA;
                break;
        case I40E_PHY_TYPE_1000BASE_KX:
@@ -1377,6 +1382,8 @@ enum i40e_status_code i40e_pf_reset(struct i40e_hw *hw)
         * we don't need to do the PF Reset
         */
        if (!cnt) {
+               u32 reg2 = 0;
+
                reg = rd32(hw, I40E_PFGEN_CTRL);
                wr32(hw, I40E_PFGEN_CTRL,
                     (reg | I40E_PFGEN_CTRL_PFSWR_MASK));
@@ -1384,6 +1391,12 @@ enum i40e_status_code i40e_pf_reset(struct i40e_hw *hw)
                        reg = rd32(hw, I40E_PFGEN_CTRL);
                        if (!(reg & I40E_PFGEN_CTRL_PFSWR_MASK))
                                break;
+                       reg2 = rd32(hw, I40E_GLGEN_RSTAT);
+                       if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) {
+                               DEBUGOUT("Core reset upcoming.\n");
+                               DEBUGOUT1("I40E_GLGEN_RSTAT = 0x%x\n", reg2);
+                               return I40E_ERR_NOT_READY;
+                       }
                        i40e_msec_delay(1);
                }
                if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) {
@@ -1689,8 +1702,15 @@ enum i40e_status_code i40e_aq_get_phy_capabilities(struct i40e_hw *hw,
                status = I40E_ERR_UNKNOWN_PHY;
 
        if (report_init) {
-               hw->phy.phy_types = LE32_TO_CPU(abilities->phy_type);
-               hw->phy.phy_types |= ((u64)abilities->phy_type_ext << 32);
+               if (hw->mac.type ==  I40E_MAC_XL710 &&
+                   hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
+                   hw->aq.api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_XL710) {
+                       status = i40e_aq_get_link_info(hw, true, NULL, NULL);
+               } else {
+                       hw->phy.phy_types = LE32_TO_CPU(abilities->phy_type);
+                       hw->phy.phy_types |=
+                                       ((u64)abilities->phy_type_ext << 32);
+               }
        }
 
        return status;
@@ -1952,7 +1972,7 @@ enum i40e_status_code i40e_aq_get_link_info(struct i40e_hw *hw,
        hw_link_info->fec_info = resp->config & (I40E_AQ_CONFIG_FEC_KR_ENA |
                                                 I40E_AQ_CONFIG_FEC_RS_ENA);
        hw_link_info->ext_info = resp->ext_info;
-       hw_link_info->loopback = resp->loopback;
+       hw_link_info->loopback = resp->loopback & I40E_AQ_LOOPBACK_MASK;
        hw_link_info->max_frame_size = LE16_TO_CPU(resp->max_frame_size);
        hw_link_info->pacing = resp->config & I40E_AQ_CONFIG_PACING_MASK;
 
@@ -1983,6 +2003,12 @@ enum i40e_status_code i40e_aq_get_link_info(struct i40e_hw *hw,
             hw->aq.fw_min_ver < 40)) && hw_link_info->phy_type == 0xE)
                hw_link_info->phy_type = I40E_PHY_TYPE_10GBASE_SFPP_CU;
 
+       if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
+           hw->aq.api_min_ver >= 7) {
+               hw->phy.phy_types = LE32_TO_CPU(*(__le32 *)resp->link_type);
+               hw->phy.phy_types |= ((u64)resp->link_type_ext << 32);
+       }
+
        /* save link status information */
        if (link)
                i40e_memcpy(link, hw_link_info, sizeof(*hw_link_info),
@@ -2679,7 +2705,11 @@ enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw,
                                          i40e_aqc_opc_set_switch_config);
        scfg->flags = CPU_TO_LE16(flags);
        scfg->valid_flags = CPU_TO_LE16(valid_flags);
-
+       if (hw->flags & I40E_HW_FLAG_802_1AD_CAPABLE) {
+               scfg->switch_tag = CPU_TO_LE16(hw->switch_tag);
+               scfg->first_tag = CPU_TO_LE16(hw->first_tag);
+               scfg->second_tag = CPU_TO_LE16(hw->second_tag);
+       }
        status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
 
        return status;
@@ -2825,6 +2855,10 @@ enum i40e_status_code i40e_update_link_info(struct i40e_hw *hw)
                if (status)
                        return status;
 
+               hw->phy.link_info.req_fec_info =
+                       abilities.fec_cfg_curr_mod_ext_info &
+                       (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS);
+
                i40e_memcpy(hw->phy.link_info.module_type, &abilities.module_type,
                        sizeof(hw->phy.link_info.module_type), I40E_NONDMA_TO_NONDMA);
        }
@@ -5523,7 +5557,7 @@ enum i40e_status_code i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
        }
 
        if (mac_addr)
-               i40e_memcpy(cmd->mac, mac_addr, I40E_ETH_LENGTH_OF_ADDRESS,
+               i40e_memcpy(cmd->mac, mac_addr, ETH_ALEN,
                            I40E_NONDMA_TO_NONDMA);
 
        cmd->etype = CPU_TO_LE16(ethtype);
@@ -6654,24 +6688,38 @@ enum i40e_status_code i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr,
        u16 temp_addr;
        u8 port_num;
        u32 i;
-
-       temp_addr = I40E_PHY_LED_PROV_REG_1;
-       i = rd32(hw, I40E_PFGEN_PORTNUM);
-       port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
-       phy_addr = i40e_get_phy_address(hw, port_num);
-
-       for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++,
-            temp_addr++) {
-               status = i40e_read_phy_register_clause45(hw,
+       u32 reg_val_aq;
+
+       if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
+               status =
+                     i40e_aq_get_phy_register(hw,
+                                              I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                              I40E_PHY_COM_REG_PAGE,
+                                              I40E_PHY_LED_PROV_REG_1,
+                                              &reg_val_aq, NULL);
+               if (status)
+                       return status;
+               *val = (u16)reg_val_aq;
+       } else {
+               temp_addr = I40E_PHY_LED_PROV_REG_1;
+               i = rd32(hw, I40E_PFGEN_PORTNUM);
+               port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
+               phy_addr = i40e_get_phy_address(hw, port_num);
+
+               for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++,
+                    temp_addr++) {
+                       status =
+                        i40e_read_phy_register_clause45(hw,
                                                         I40E_PHY_COM_REG_PAGE,
                                                         temp_addr, phy_addr,
                                                         &reg_val);
-               if (status)
-                       return status;
-               *val = reg_val;
-               if (reg_val & I40E_PHY_LED_LINK_MODE_MASK) {
-                       *led_addr = temp_addr;
-                       break;
+                       if (status)
+                               return status;
+                       *val = reg_val;
+                       if (reg_val & I40E_PHY_LED_LINK_MODE_MASK) {
+                               *led_addr = temp_addr;
+                               break;
+                       }
                }
        }
        return status;
@@ -6689,51 +6737,115 @@ enum i40e_status_code i40e_led_set_phy(struct i40e_hw *hw, bool on,
                                       u16 led_addr, u32 mode)
 {
        enum i40e_status_code status = I40E_SUCCESS;
-       u16 led_ctl = 0;
-       u16 led_reg = 0;
+       u32 led_ctl = 0;
+       u32 led_reg = 0;
        u8 phy_addr = 0;
        u8 port_num;
        u32 i;
 
-       i = rd32(hw, I40E_PFGEN_PORTNUM);
-       port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
-       phy_addr = i40e_get_phy_address(hw, port_num);
-       status = i40e_read_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE,
-                                                led_addr, phy_addr, &led_reg);
+       if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
+               status =
+                     i40e_aq_get_phy_register(hw,
+                                              I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                              I40E_PHY_COM_REG_PAGE,
+                                              I40E_PHY_LED_PROV_REG_1,
+                                              &led_reg, NULL);
+       } else {
+               i = rd32(hw, I40E_PFGEN_PORTNUM);
+               port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
+               phy_addr = i40e_get_phy_address(hw, port_num);
+               status = i40e_read_phy_register_clause45(hw,
+                                                        I40E_PHY_COM_REG_PAGE,
+                                                        led_addr, phy_addr,
+                                                        (u16 *)&led_reg);
+       }
        if (status)
                return status;
        led_ctl = led_reg;
        if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) {
                led_reg = 0;
-               status = i40e_write_phy_register_clause45(hw,
-                                                         I40E_PHY_COM_REG_PAGE,
-                                                         led_addr, phy_addr,
-                                                         led_reg);
+               if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
+                   hw->aq.api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_XL710) {
+                       status = i40e_aq_set_phy_register(hw,
+                                       I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                       I40E_PHY_COM_REG_PAGE,
+                                       I40E_PHY_LED_PROV_REG_1,
+                                       led_reg, NULL);
+               } else {
+                       status = i40e_write_phy_register_clause45(hw,
+                                                       I40E_PHY_COM_REG_PAGE,
+                                                       led_addr, phy_addr,
+                                                       (u16)led_reg);
+               }
                if (status)
                        return status;
        }
-       status = i40e_read_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE,
-                                                led_addr, phy_addr, &led_reg);
+       if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
+               status =
+                     i40e_aq_get_phy_register(hw,
+                                              I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                              I40E_PHY_COM_REG_PAGE,
+                                              I40E_PHY_LED_PROV_REG_1,
+                                              &led_reg, NULL);
+       } else {
+               status = i40e_read_phy_register_clause45(hw,
+                                                        I40E_PHY_COM_REG_PAGE,
+                                                        led_addr, phy_addr,
+                                                        (u16 *)&led_reg);
+       }
        if (status)
                goto restore_config;
        if (on)
                led_reg = I40E_PHY_LED_MANUAL_ON;
        else
                led_reg = 0;
-       status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE,
-                                                 led_addr, phy_addr, led_reg);
+
+       if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
+               status =
+                     i40e_aq_set_phy_register(hw,
+                                              I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                              I40E_PHY_COM_REG_PAGE,
+                                              I40E_PHY_LED_PROV_REG_1,
+                                              led_reg, NULL);
+       } else {
+               status =
+                   i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE,
+                                                    led_addr, phy_addr,
+                                                    (u16)led_reg);
+       }
        if (status)
                goto restore_config;
        if (mode & I40E_PHY_LED_MODE_ORIG) {
                led_ctl = (mode & I40E_PHY_LED_MODE_MASK);
-               status = i40e_write_phy_register_clause45(hw,
-                                                I40E_PHY_COM_REG_PAGE,
-                                                led_addr, phy_addr, led_ctl);
+               if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
+                       status = i40e_aq_set_phy_register(hw,
+                                       I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                       I40E_PHY_COM_REG_PAGE,
+                                       I40E_PHY_LED_PROV_REG_1,
+                                       led_ctl, NULL);
+               } else {
+                       status = i40e_write_phy_register_clause45(hw,
+                                                        I40E_PHY_COM_REG_PAGE,
+                                                        led_addr, phy_addr,
+                                                        (u16)led_ctl);
+               }
        }
        return status;
 restore_config:
-       status = i40e_write_phy_register_clause45(hw, I40E_PHY_COM_REG_PAGE,
-                                                 led_addr, phy_addr, led_ctl);
+       if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
+               status =
+                     i40e_aq_set_phy_register(hw,
+                                              I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+                                              I40E_PHY_COM_REG_PAGE,
+                                              I40E_PHY_LED_PROV_REG_1,
+                                              led_ctl, NULL);
+       } else {
+               status =
+                       i40e_write_phy_register_clause45(hw,
+                                                        I40E_PHY_COM_REG_PAGE,
+                                                        led_addr, phy_addr,
+                                                        (u16)led_ctl);
+       }
        return status;
 }
 #endif /* PF_DRIVER */
@@ -6863,6 +6975,76 @@ do_retry:
        if (status || use_register)
                wr32(hw, reg_addr, reg_val);
 }
+
+/**
+ * i40e_aq_set_phy_register
+ * @hw: pointer to the hw struct
+ * @phy_select: select which phy should be accessed
+ * @dev_addr: PHY device address
+ * @reg_addr: PHY register address
+ * @reg_val: new register value
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Write the external PHY register.
+ **/
+enum i40e_status_code i40e_aq_set_phy_register(struct i40e_hw *hw,
+                               u8 phy_select, u8 dev_addr,
+                               u32 reg_addr, u32 reg_val,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_phy_register_access *cmd =
+               (struct i40e_aqc_phy_register_access *)&desc.params.raw;
+       enum i40e_status_code status;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_set_phy_register);
+
+       cmd->phy_interface = phy_select;
+       cmd->dev_addres = dev_addr;
+       cmd->reg_address = reg_addr;
+       cmd->reg_value = reg_val;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       return status;
+}
+
+/**
+ * i40e_aq_get_phy_register
+ * @hw: pointer to the hw struct
+ * @phy_select: select which phy should be accessed
+ * @dev_addr: PHY device address
+ * @reg_addr: PHY register address
+ * @reg_val: read register value
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Read the external PHY register.
+ **/
+enum i40e_status_code i40e_aq_get_phy_register(struct i40e_hw *hw,
+                               u8 phy_select, u8 dev_addr,
+                               u32 reg_addr, u32 *reg_val,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_phy_register_access *cmd =
+               (struct i40e_aqc_phy_register_access *)&desc.params.raw;
+       enum i40e_status_code status;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_get_phy_register);
+
+       cmd->phy_interface = phy_select;
+       cmd->dev_addres = dev_addr;
+       cmd->reg_address = reg_addr;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+       if (!status)
+               *reg_val = cmd->reg_value;
+
+       return status;
+}
+
 #ifdef VF_DRIVER
 
 /**
@@ -6879,7 +7061,7 @@ do_retry:
  * completion before returning.
  **/
 enum i40e_status_code i40e_aq_send_msg_to_pf(struct i40e_hw *hw,
-                               enum i40e_virtchnl_ops v_opcode,
+                               enum virtchnl_ops v_opcode,
                                enum i40e_status_code v_retval,
                                u8 *msg, u16 msglen,
                                struct i40e_asq_cmd_details *cmd_details)
@@ -6918,9 +7100,9 @@ enum i40e_status_code i40e_aq_send_msg_to_pf(struct i40e_hw *hw,
  * with appropriate information.
  **/
 void i40e_vf_parse_hw_config(struct i40e_hw *hw,
-                            struct i40e_virtchnl_vf_resource *msg)
+                            struct virtchnl_vf_resource *msg)
 {
-       struct i40e_virtchnl_vsi_resource *vsi_res;
+       struct virtchnl_vsi_resource *vsi_res;
        int i;
 
        vsi_res = &msg->vsi_res[0];
@@ -6930,19 +7112,17 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw,
        hw->dev_caps.num_tx_qp = msg->num_queue_pairs;
        hw->dev_caps.num_msix_vectors_vf = msg->max_vectors;
        hw->dev_caps.dcb = msg->vf_offload_flags &
-                          I40E_VIRTCHNL_VF_OFFLOAD_L2;
-       hw->dev_caps.fcoe = (msg->vf_offload_flags &
-                            I40E_VIRTCHNL_VF_OFFLOAD_FCOE) ? 1 : 0;
+                          VIRTCHNL_VF_OFFLOAD_L2;
        hw->dev_caps.iwarp = (msg->vf_offload_flags &
-                             I40E_VIRTCHNL_VF_OFFLOAD_IWARP) ? 1 : 0;
+                             VIRTCHNL_VF_OFFLOAD_IWARP) ? 1 : 0;
        for (i = 0; i < msg->num_vsis; i++) {
-               if (vsi_res->vsi_type == I40E_VSI_SRIOV) {
+               if (vsi_res->vsi_type == VIRTCHNL_VSI_SRIOV) {
                        i40e_memcpy(hw->mac.perm_addr,
                                    vsi_res->default_mac_addr,
-                                   I40E_ETH_LENGTH_OF_ADDRESS,
+                                   ETH_ALEN,
                                    I40E_NONDMA_TO_NONDMA);
                        i40e_memcpy(hw->mac.addr, vsi_res->default_mac_addr,
-                                   I40E_ETH_LENGTH_OF_ADDRESS,
+                                   ETH_ALEN,
                                    I40E_NONDMA_TO_NONDMA);
                }
                vsi_res++;
@@ -6959,7 +7139,7 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw,
  **/
 enum i40e_status_code i40e_vf_reset(struct i40e_hw *hw)
 {
-       return i40e_aq_send_msg_to_pf(hw, I40E_VIRTCHNL_OP_RESET_VF,
+       return i40e_aq_send_msg_to_pf(hw, VIRTCHNL_OP_RESET_VF,
                                      I40E_SUCCESS, NULL, 0, NULL);
 }
 #endif /* VF_DRIVER */
@@ -7261,6 +7441,165 @@ i40e_find_segment_in_package(u32 segment_type,
        return NULL;
 }
 
+/* Get section table in profile */
+#define I40E_SECTION_TABLE(profile, sec_tbl)                           \
+       do {                                                            \
+               struct i40e_profile_segment *p = (profile);             \
+               u32 count;                                              \
+               u32 *nvm;                                               \
+               count = p->device_table_count;                          \
+               nvm = (u32 *)&p->device_table[count];                   \
+               sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \
+       } while (0)
+
+/* Get section header in profile */
+#define I40E_SECTION_HEADER(profile, offset)                           \
+       (struct i40e_profile_section_header *)((u8 *)(profile) + (offset))
+
+/**
+ * i40e_find_section_in_profile
+ * @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE)
+ * @profile: pointer to the i40e segment header to be searched
+ *
+ * This function searches i40e segment for a particular section type. On
+ * success it returns a pointer to the section header, otherwise it will
+ * return NULL.
+ **/
+struct i40e_profile_section_header *
+i40e_find_section_in_profile(u32 section_type,
+                            struct i40e_profile_segment *profile)
+{
+       struct i40e_profile_section_header *sec;
+       struct i40e_section_table *sec_tbl;
+       u32 sec_off;
+       u32 i;
+
+       if (profile->header.type != SEGMENT_TYPE_I40E)
+               return NULL;
+
+       I40E_SECTION_TABLE(profile, sec_tbl);
+
+       for (i = 0; i < sec_tbl->section_count; i++) {
+               sec_off = sec_tbl->section_offset[i];
+               sec = I40E_SECTION_HEADER(profile, sec_off);
+               if (sec->section.type == section_type)
+                       return sec;
+       }
+
+       return NULL;
+}
+
+/**
+ * i40e_ddp_exec_aq_section - Execute generic AQ for DDP
+ * @hw: pointer to the hw struct
+ * @aq: command buffer containing all data to execute AQ
+ **/
+STATIC enum
+i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw,
+                                         struct i40e_profile_aq_section *aq)
+{
+       enum i40e_status_code status;
+       struct i40e_aq_desc desc;
+       u8 *msg = NULL;
+       u16 msglen;
+
+       i40e_fill_default_direct_cmd_desc(&desc, aq->opcode);
+       desc.flags |= CPU_TO_LE16(aq->flags);
+       i40e_memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw),
+                   I40E_NONDMA_TO_NONDMA);
+
+       msglen = aq->datalen;
+       if (msglen) {
+               desc.flags |= CPU_TO_LE16((u16)(I40E_AQ_FLAG_BUF |
+                                               I40E_AQ_FLAG_RD));
+               if (msglen > I40E_AQ_LARGE_BUF)
+                       desc.flags |= CPU_TO_LE16((u16)I40E_AQ_FLAG_LB);
+               desc.datalen = CPU_TO_LE16(msglen);
+               msg = &aq->data[0];
+       }
+
+       status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL);
+
+       if (status != I40E_SUCCESS) {
+               i40e_debug(hw, I40E_DEBUG_PACKAGE,
+                          "unable to exec DDP AQ opcode %u, error %d\n",
+                          aq->opcode, status);
+               return status;
+       }
+
+       /* copy returned desc to aq_buf */
+       i40e_memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw),
+                   I40E_NONDMA_TO_NONDMA);
+
+       return I40E_SUCCESS;
+}
+
+/**
+ * i40e_validate_profile
+ * @hw: pointer to the hardware structure
+ * @profile: pointer to the profile segment of the package to be validated
+ * @track_id: package tracking id
+ * @rollback: flag if the profile is for rollback.
+ *
+ * Validates supported devices and profile's sections.
+ */
+STATIC enum i40e_status_code
+i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
+                     u32 track_id, bool rollback)
+{
+       struct i40e_profile_section_header *sec = NULL;
+       enum i40e_status_code status = I40E_SUCCESS;
+       struct i40e_section_table *sec_tbl;
+       u32 vendor_dev_id;
+       u32 dev_cnt;
+       u32 sec_off;
+       u32 i;
+
+       if (track_id == I40E_DDP_TRACKID_INVALID) {
+               i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n");
+               return I40E_NOT_SUPPORTED;
+       }
+
+       dev_cnt = profile->device_table_count;
+       for (i = 0; i < dev_cnt; i++) {
+               vendor_dev_id = profile->device_table[i].vendor_dev_id;
+               if ((vendor_dev_id >> 16) == I40E_INTEL_VENDOR_ID &&
+                   hw->device_id == (vendor_dev_id & 0xFFFF))
+                       break;
+       }
+       if (dev_cnt && (i == dev_cnt)) {
+               i40e_debug(hw, I40E_DEBUG_PACKAGE,
+                          "Device doesn't support DDP\n");
+               return I40E_ERR_DEVICE_NOT_SUPPORTED;
+       }
+
+       I40E_SECTION_TABLE(profile, sec_tbl);
+
+       /* Validate sections types */
+       for (i = 0; i < sec_tbl->section_count; i++) {
+               sec_off = sec_tbl->section_offset[i];
+               sec = I40E_SECTION_HEADER(profile, sec_off);
+               if (rollback) {
+                       if (sec->section.type == SECTION_TYPE_MMIO ||
+                           sec->section.type == SECTION_TYPE_AQ ||
+                           sec->section.type == SECTION_TYPE_RB_AQ) {
+                               i40e_debug(hw, I40E_DEBUG_PACKAGE,
+                                          "Not a roll-back package\n");
+                               return I40E_NOT_SUPPORTED;
+                       }
+               } else {
+                       if (sec->section.type == SECTION_TYPE_RB_AQ ||
+                           sec->section.type == SECTION_TYPE_RB_MMIO) {
+                               i40e_debug(hw, I40E_DEBUG_PACKAGE,
+                                          "Not an original package\n");
+                               return I40E_NOT_SUPPORTED;
+                       }
+               }
+       }
+
+       return status;
+}
+
 /**
  * i40e_write_profile
  * @hw: pointer to the hardware structure
@@ -7276,52 +7615,99 @@ i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
        enum i40e_status_code status = I40E_SUCCESS;
        struct i40e_section_table *sec_tbl;
        struct i40e_profile_section_header *sec = NULL;
-       u32 dev_cnt;
-       u32 vendor_dev_id;
-       u32 *nvm;
+       struct i40e_profile_aq_section *ddp_aq;
        u32 section_size = 0;
        u32 offset = 0, info = 0;
+       u32 sec_off;
        u32 i;
 
-       if (!track_id) {
-               i40e_debug(hw, I40E_DEBUG_PACKAGE, "Track_id can't be 0.");
-               return I40E_NOT_SUPPORTED;
-       }
+       status = i40e_validate_profile(hw, profile, track_id, false);
+       if (status)
+               return status;
 
-       dev_cnt = profile->device_table_count;
+       I40E_SECTION_TABLE(profile, sec_tbl);
 
-       for (i = 0; i < dev_cnt; i++) {
-               vendor_dev_id = profile->device_table[i].vendor_dev_id;
-               if ((vendor_dev_id >> 16) == I40E_INTEL_VENDOR_ID)
-                       if (hw->device_id == (vendor_dev_id & 0xFFFF))
+       for (i = 0; i < sec_tbl->section_count; i++) {
+               sec_off = sec_tbl->section_offset[i];
+               sec = I40E_SECTION_HEADER(profile, sec_off);
+               /* Process generic admin command */
+               if (sec->section.type == SECTION_TYPE_AQ) {
+                       ddp_aq = (struct i40e_profile_aq_section *)&sec[1];
+                       status = i40e_ddp_exec_aq_section(hw, ddp_aq);
+                       if (status) {
+                               i40e_debug(hw, I40E_DEBUG_PACKAGE,
+                                          "Failed to execute aq: section %d, opcode %u\n",
+                                          i, ddp_aq->opcode);
                                break;
+                       }
+                       sec->section.type = SECTION_TYPE_RB_AQ;
+               }
+
+               /* Skip any non-mmio sections */
+               if (sec->section.type != SECTION_TYPE_MMIO)
+                       continue;
+
+               section_size = sec->section.size +
+                       sizeof(struct i40e_profile_section_header);
+
+               /* Write MMIO section */
+               status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
+                                          track_id, &offset, &info, NULL);
+               if (status) {
+                       i40e_debug(hw, I40E_DEBUG_PACKAGE,
+                                  "Failed to write profile: section %d, offset %d, info %d\n",
+                                  i, offset, info);
+                       break;
+               }
        }
-       if (i == dev_cnt) {
-               i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support DDP");
-               return I40E_ERR_DEVICE_NOT_SUPPORTED;
-       }
+       return status;
+}
 
-       nvm = (u32 *)&profile->device_table[dev_cnt];
-       sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1];
+/**
+ * i40e_rollback_profile
+ * @hw: pointer to the hardware structure
+ * @profile: pointer to the profile segment of the package to be removed
+ * @track_id: package tracking id
+ *
+ * Rolls back previously loaded package.
+ */
+enum i40e_status_code
+i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
+                     u32 track_id)
+{
+       struct i40e_profile_section_header *sec = NULL;
+       enum i40e_status_code status = I40E_SUCCESS;
+       struct i40e_section_table *sec_tbl;
+       u32 offset = 0, info = 0;
+       u32 section_size = 0;
+       u32 sec_off;
+       int i;
 
-       for (i = 0; i < sec_tbl->section_count; i++) {
-               sec = (struct i40e_profile_section_header *)((u8 *)profile +
-                                            sec_tbl->section_offset[i]);
+       status = i40e_validate_profile(hw, profile, track_id, true);
+       if (status)
+               return status;
 
-               /* Skip 'AQ', 'note' and 'name' sections */
-               if (sec->section.type != SECTION_TYPE_MMIO)
+       I40E_SECTION_TABLE(profile, sec_tbl);
+
+       /* For rollback write sections in reverse */
+       for (i = sec_tbl->section_count - 1; i >= 0; i--) {
+               sec_off = sec_tbl->section_offset[i];
+               sec = I40E_SECTION_HEADER(profile, sec_off);
+
+               /* Skip any non-rollback sections */
+               if (sec->section.type != SECTION_TYPE_RB_MMIO)
                        continue;
 
                section_size = sec->section.size +
                        sizeof(struct i40e_profile_section_header);
 
-               /* Write profile */
+               /* Write roll-back MMIO section */
                status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
                                           track_id, &offset, &info, NULL);
                if (status) {
                        i40e_debug(hw, I40E_DEBUG_PACKAGE,
-                                  "Failed to write profile: offset %d, info %d",
-                                  offset, info);
+                                  "Failed to write profile: section %d, offset %d, info %d\n",
+                                  i, offset, info);
                        break;
                }
        }
@@ -7359,9 +7745,10 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
        pinfo->track_id = track_id;
        pinfo->version = profile->version;
        pinfo->op = I40E_DDP_ADD_TRACKID;
-       memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE);
+       i40e_memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE,
+                   I40E_NONDMA_TO_NONDMA);
 
        status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end,
-                               track_id, &offset, &info, NULL);
+                                  track_id, &offset, &info, NULL);
        return status;
 }