These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / scsi / pm8001 / pm80xx_hwi.c
index 05cce46..eb4fee6 100644 (file)
@@ -309,6 +309,9 @@ static void read_main_config_table(struct pm8001_hba_info *pm8001_ha)
                pm8001_mr32(address, MAIN_INT_VECTOR_TABLE_OFFSET);
        pm8001_ha->main_cfg_tbl.pm80xx_tbl.phy_attr_table_offset =
                pm8001_mr32(address, MAIN_SAS_PHY_ATTR_TABLE_OFFSET);
+       /* read port recover and reset timeout */
+       pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer =
+               pm8001_mr32(address, MAIN_PORT_RECOVERY_TIMER);
 }
 
 /**
@@ -585,6 +588,12 @@ static void update_main_config_table(struct pm8001_hba_info *pm8001_ha)
                pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer);
        pm8001_mw32(address, MAIN_INT_REASSERTION_DELAY,
                pm8001_ha->main_cfg_tbl.pm80xx_tbl.interrupt_reassertion_delay);
+
+       pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer &= 0xffff0000;
+       pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer |=
+                                                       PORT_RECOVERY_TIMEOUT;
+       pm8001_mw32(address, MAIN_PORT_RECOVERY_TIMER,
+                       pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer);
 }
 
 /**
@@ -843,6 +852,7 @@ pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha)
        int rc;
        u32 tag;
        u32 opc = OPC_INB_SET_CONTROLLER_CONFIG;
+       u32 page_code;
 
        memset(&payload, 0, sizeof(struct set_ctrl_cfg_req));
        rc = pm8001_tag_alloc(pm8001_ha, &tag);
@@ -851,8 +861,14 @@ pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha)
 
        circularQ = &pm8001_ha->inbnd_q_tbl[0];
        payload.tag = cpu_to_le32(tag);
+
+       if (IS_SPCV_12G(pm8001_ha->pdev))
+               page_code = THERMAL_PAGE_CODE_7H;
+       else
+               page_code = THERMAL_PAGE_CODE_8H;
+
        payload.cfg_pg[0] = (THERMAL_LOG_ENABLE << 9) |
-                       (THERMAL_ENABLE << 8) | THERMAL_OP_CODE;
+                               (THERMAL_ENABLE << 8) | page_code;
        payload.cfg_pg[1] = (LTEMPHIL << 24) | (RTEMPHIL << 8);
 
        rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
@@ -1251,6 +1267,8 @@ pm80xx_chip_soft_rst(struct pm8001_hba_info *pm8001_ha)
                /* check iButton feature support for motherboard controller */
                if (pm8001_ha->pdev->subsystem_vendor !=
                        PCI_VENDOR_ID_ADAPTEC2 &&
+                       pm8001_ha->pdev->subsystem_vendor !=
+                       PCI_VENDOR_ID_ATTO &&
                        pm8001_ha->pdev->subsystem_vendor != 0) {
                        ibutton0 = pm8001_cr32(pm8001_ha, 0,
                                        MSGU_HOST_SCRATCH_PAD_6);
@@ -1593,6 +1611,13 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
                ts->stat = SAS_OPEN_REJECT;
                ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
                break;
+       case IO_XFER_ERROR_INVALID_SSP_RSP_FRAME:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_INVALID_SSP_RSP_FRAME\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
        case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
                PM8001_IO_DBG(pm8001_ha,
                pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
@@ -2314,6 +2339,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
                ts->resp = SAS_TASK_COMPLETE;
                ts->stat = SAS_OPEN_REJECT;
                ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
        default:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("Unknown status 0x%x\n", status));
@@ -2829,6 +2855,32 @@ static void pm80xx_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
 static int pm80xx_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
        u32 phyId, u32 phy_op);
 
+static void hw_event_port_recover(struct pm8001_hba_info *pm8001_ha,
+                                       void *piomb)
+{
+       struct hw_event_resp *pPayload = (struct hw_event_resp *)(piomb + 4);
+       u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate);
+       u8 phy_id = (u8)((phyid_npip_portstate & 0xFF0000) >> 16);
+       u32 lr_status_evt_portid =
+               le32_to_cpu(pPayload->lr_status_evt_portid);
+       u8 deviceType = pPayload->sas_identify.dev_type;
+       u8 link_rate = (u8)((lr_status_evt_portid & 0xF0000000) >> 28);
+       struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+       u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF);
+       struct pm8001_port *port = &pm8001_ha->port[port_id];
+
+       if (deviceType == SAS_END_DEVICE) {
+               pm80xx_chip_phy_ctl_req(pm8001_ha, phy_id,
+                                       PHY_NOTIFY_ENABLE_SPINUP);
+       }
+
+       port->wide_port_phymap |= (1U << phy_id);
+       pm8001_get_lrate_mode(phy, link_rate);
+       phy->sas_phy.oob_mode = SAS_OOB_MODE;
+       phy->phy_state = PHY_STATE_LINK_UP_SPCV;
+       phy->phy_attached = 1;
+}
+
 /**
  * hw_event_sas_phy_up -FW tells me a SAS phy up event.
  * @pm8001_ha: our hba card information
@@ -2856,6 +2908,7 @@ hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
        unsigned long flags;
        u8 deviceType = pPayload->sas_identify.dev_type;
        port->port_state = portstate;
+       port->wide_port_phymap |= (1U << phy_id);
        phy->phy_state = PHY_STATE_LINK_UP_SPCV;
        PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
                "portid:%d; phyid:%d; linkrate:%d; "
@@ -2981,7 +3034,6 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
        struct pm8001_port *port = &pm8001_ha->port[port_id];
        struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
        port->port_state = portstate;
-       phy->phy_type = 0;
        phy->identify.device_type = 0;
        phy->phy_attached = 0;
        memset(&phy->dev_sas_addr, 0, SAS_ADDR_SIZE);
@@ -2993,9 +3045,13 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
                        pm8001_printk(" PortInvalid portID %d\n", port_id));
                PM8001_MSG_DBG(pm8001_ha,
                        pm8001_printk(" Last phy Down and port invalid\n"));
-               port->port_attached = 0;
-               pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
-                       port_id, phy_id, 0, 0);
+               if (phy->phy_type & PORT_TYPE_SATA) {
+                       phy->phy_type = 0;
+                       port->port_attached = 0;
+                       pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+                                       port_id, phy_id, 0, 0);
+               }
+               sas_phy_disconnected(&phy->sas_phy);
                break;
        case PORT_IN_RESET:
                PM8001_MSG_DBG(pm8001_ha,
@@ -3003,22 +3059,26 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
                break;
        case PORT_NOT_ESTABLISHED:
                PM8001_MSG_DBG(pm8001_ha,
-                       pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n"));
+                       pm8001_printk(" Phy Down and PORT_NOT_ESTABLISHED\n"));
                port->port_attached = 0;
                break;
        case PORT_LOSTCOMM:
                PM8001_MSG_DBG(pm8001_ha,
-                       pm8001_printk(" phy Down and PORT_LOSTCOMM\n"));
+                       pm8001_printk(" Phy Down and PORT_LOSTCOMM\n"));
                PM8001_MSG_DBG(pm8001_ha,
                        pm8001_printk(" Last phy Down and port invalid\n"));
-               port->port_attached = 0;
-               pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
-                       port_id, phy_id, 0, 0);
+               if (phy->phy_type & PORT_TYPE_SATA) {
+                       port->port_attached = 0;
+                       phy->phy_type = 0;
+                       pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+                                       port_id, phy_id, 0, 0);
+               }
+               sas_phy_disconnected(&phy->sas_phy);
                break;
        default:
                port->port_attached = 0;
                PM8001_MSG_DBG(pm8001_ha,
-                       pm8001_printk(" phy Down and(default) = 0x%x\n",
+                       pm8001_printk(" Phy Down and(default) = 0x%x\n",
                        portstate));
                break;
 
@@ -3084,7 +3144,7 @@ static int mpi_thermal_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
  */
 static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
 {
-       unsigned long flags;
+       unsigned long flags, i;
        struct hw_event_resp *pPayload =
                (struct hw_event_resp *)(piomb + 4);
        u32 lr_status_evt_portid =
@@ -3097,9 +3157,9 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
                (u16)((lr_status_evt_portid & 0x00FFFF00) >> 8);
        u8 status =
                (u8)((lr_status_evt_portid & 0x0F000000) >> 24);
-
        struct sas_ha_struct *sas_ha = pm8001_ha->sas;
        struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+       struct pm8001_port *port = &pm8001_ha->port[port_id];
        struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
        PM8001_MSG_DBG(pm8001_ha,
                pm8001_printk("portid:%d phyid:%d event:0x%x status:0x%x\n",
@@ -3125,7 +3185,9 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
        case HW_EVENT_PHY_DOWN:
                PM8001_MSG_DBG(pm8001_ha,
                        pm8001_printk("HW_EVENT_PHY_DOWN\n"));
-               sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+               if (phy->phy_type & PORT_TYPE_SATA)
+                       sas_ha->notify_phy_event(&phy->sas_phy,
+                               PHYE_LOSS_OF_SIGNAL);
                phy->phy_attached = 0;
                phy->phy_state = 0;
                hw_event_phy_down(pm8001_ha, piomb);
@@ -3169,9 +3231,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
                        pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n"));
                pm80xx_hw_event_ack_req(pm8001_ha, 0,
                        HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0);
-               sas_phy_disconnected(sas_phy);
-               phy->phy_attached = 0;
-               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
                break;
        case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
                PM8001_MSG_DBG(pm8001_ha,
@@ -3179,9 +3238,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
                pm80xx_hw_event_ack_req(pm8001_ha, 0,
                        HW_EVENT_LINK_ERR_DISPARITY_ERROR,
                        port_id, phy_id, 0, 0);
-               sas_phy_disconnected(sas_phy);
-               phy->phy_attached = 0;
-               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
                break;
        case HW_EVENT_LINK_ERR_CODE_VIOLATION:
                PM8001_MSG_DBG(pm8001_ha,
@@ -3189,9 +3245,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
                pm80xx_hw_event_ack_req(pm8001_ha, 0,
                        HW_EVENT_LINK_ERR_CODE_VIOLATION,
                        port_id, phy_id, 0, 0);
-               sas_phy_disconnected(sas_phy);
-               phy->phy_attached = 0;
-               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
                break;
        case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH:
                PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
@@ -3199,9 +3252,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
                pm80xx_hw_event_ack_req(pm8001_ha, 0,
                        HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH,
                        port_id, phy_id, 0, 0);
-               sas_phy_disconnected(sas_phy);
-               phy->phy_attached = 0;
-               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
                break;
        case HW_EVENT_MALFUNCTION:
                PM8001_MSG_DBG(pm8001_ha,
@@ -3257,13 +3307,19 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb)
                pm80xx_hw_event_ack_req(pm8001_ha, 0,
                        HW_EVENT_PORT_RECOVERY_TIMER_TMO,
                        port_id, phy_id, 0, 0);
-               sas_phy_disconnected(sas_phy);
-               phy->phy_attached = 0;
-               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+                       if (port->wide_port_phymap & (1 << i)) {
+                               phy = &pm8001_ha->phy[i];
+                               sas_ha->notify_phy_event(&phy->sas_phy,
+                                               PHYE_LOSS_OF_SIGNAL);
+                               port->wide_port_phymap &= ~(1 << i);
+                       }
+               }
                break;
        case HW_EVENT_PORT_RECOVER:
                PM8001_MSG_DBG(pm8001_ha,
                        pm8001_printk("HW_EVENT_PORT_RECOVER\n"));
+               hw_event_port_recover(pm8001_ha, piomb);
                break;
        case HW_EVENT_PORT_RESET_COMPLETE:
                PM8001_MSG_DBG(pm8001_ha,
@@ -4522,6 +4578,38 @@ void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha,
        }
        PM8001_INIT_DBG(pm8001_ha, pm8001_printk("phy settings completed\n"));
 }
+
+void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha,
+               u32 phy, u32 length, u32 *buf)
+{
+       u32 tag, opc;
+       int rc, i;
+       struct set_phy_profile_req payload;
+       struct inbound_queue_table *circularQ;
+
+       memset(&payload, 0, sizeof(payload));
+
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               PM8001_INIT_DBG(pm8001_ha, pm8001_printk("Invalid tag"));
+
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       opc = OPC_INB_SET_PHY_PROFILE;
+
+       payload.tag = cpu_to_le32(tag);
+       payload.ppc_phyid = (((SAS_PHY_ANALOG_SETTINGS_PAGE & 0xF) << 8)
+                               | (phy & 0xFF));
+
+       for (i = 0; i < length; i++)
+               payload.reserved[i] = cpu_to_le32(*(buf + i));
+
+       rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0);
+       if (rc)
+               pm8001_tag_free(pm8001_ha, tag);
+
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("PHY %d settings applied", phy));
+}
 const struct pm8001_dispatch pm8001_80xx_dispatch = {
        .name                   = "pmc80xx",
        .chip_init              = pm80xx_chip_init,