diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/scsi/pm8001 | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/scsi/pm8001')
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm8001_defs.h | 6 | ||||
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm8001_hwi.c | 5 | ||||
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm8001_init.c | 220 | ||||
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm8001_sas.c | 19 | ||||
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm8001_sas.h | 18 | ||||
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm80xx_hwi.c | 146 | ||||
-rw-r--r-- | kernel/drivers/scsi/pm8001/pm80xx_hwi.h | 5 |
7 files changed, 364 insertions, 55 deletions
diff --git a/kernel/drivers/scsi/pm8001/pm8001_defs.h b/kernel/drivers/scsi/pm8001/pm8001_defs.h index 74a4bb9af..199527dba 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_defs.h +++ b/kernel/drivers/scsi/pm8001/pm8001_defs.h @@ -49,13 +49,17 @@ enum chip_flavors { chip_8019, chip_8074, chip_8076, - chip_8077 + chip_8077, + chip_8006, + chip_8070, + chip_8072 }; enum phy_speed { PHY_SPEED_15 = 0x01, PHY_SPEED_30 = 0x02, PHY_SPEED_60 = 0x04, + PHY_SPEED_120 = 0x08, }; enum data_direction { diff --git a/kernel/drivers/scsi/pm8001/pm8001_hwi.c b/kernel/drivers/scsi/pm8001/pm8001_hwi.c index 96dcc097a..04e67a190 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_hwi.c +++ b/kernel/drivers/scsi/pm8001/pm8001_hwi.c @@ -2642,6 +2642,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)); @@ -3263,6 +3264,10 @@ void pm8001_get_lrate_mode(struct pm8001_phy *phy, u8 link_rate) struct sas_phy *sas_phy = phy->sas_phy.phy; switch (link_rate) { + case PHY_SPEED_120: + phy->sas_phy.linkrate = SAS_LINK_RATE_12_0_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_12_0_GBPS; + break; case PHY_SPEED_60: phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS; phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; diff --git a/kernel/drivers/scsi/pm8001/pm8001_init.c b/kernel/drivers/scsi/pm8001/pm8001_init.c index 65555916d..062ab34b8 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_init.c +++ b/kernel/drivers/scsi/pm8001/pm8001_init.c @@ -57,6 +57,9 @@ static const struct pm8001_chip_info pm8001_chips[] = { [chip_8074] = {0, 8, &pm8001_80xx_dispatch,}, [chip_8076] = {0, 16, &pm8001_80xx_dispatch,}, [chip_8077] = {0, 16, &pm8001_80xx_dispatch,}, + [chip_8006] = {0, 16, &pm8001_80xx_dispatch,}, + [chip_8070] = {0, 8, &pm8001_80xx_dispatch,}, + [chip_8072] = {0, 16, &pm8001_80xx_dispatch,}, }; static int pm8001_id; @@ -78,7 +81,6 @@ static struct scsi_host_template pm8001_sht = { .change_queue_depth = sas_change_queue_depth, .bios_param = sas_bios_param, .can_queue = 1, - .cmd_per_lun = 1, .this_id = -1, .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, @@ -88,7 +90,6 @@ static struct scsi_host_template pm8001_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = pm8001_host_attrs, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -480,7 +481,8 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, #ifdef PM8001_USE_TASKLET /* Tasklet for non msi-x interrupt handler */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) + || (pm8001_ha->chip_id == chip_8001)) tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet, (unsigned long)&(pm8001_ha->irq_vector[0])); else @@ -634,6 +636,11 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) payload.minor_function = 0; payload.length = 128; } + } else if ((pm8001_ha->chip_id == chip_8070 || + pm8001_ha->chip_id == chip_8072) && + pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ATTO) { + payload.minor_function = 4; + payload.length = 4096; } else { payload.minor_function = 1; payload.length = 4096; @@ -660,6 +667,11 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) else if (deviceid == 0x0042) pm8001_ha->sas_addr[j] = payload.func_specific[0x010 + i]; + } else if ((pm8001_ha->chip_id == chip_8070 || + pm8001_ha->chip_id == chip_8072) && + pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ATTO) { + pm8001_ha->sas_addr[j] = + payload.func_specific[0x010 + i]; } else pm8001_ha->sas_addr[j] = payload.func_specific[0x804 + i]; @@ -720,6 +732,153 @@ static int pm8001_get_phy_settings_info(struct pm8001_hba_info *pm8001_ha) return 0; } +struct pm8001_mpi3_phy_pg_trx_config { + u32 LaneLosCfg; + u32 LanePgaCfg1; + u32 LanePisoCfg1; + u32 LanePisoCfg2; + u32 LanePisoCfg3; + u32 LanePisoCfg4; + u32 LanePisoCfg5; + u32 LanePisoCfg6; + u32 LaneBctCtrl; +}; + +/** + * pm8001_get_internal_phy_settings : Retrieves the internal PHY settings + * @pm8001_ha : our adapter + * @phycfg : PHY config page to populate + */ +static +void pm8001_get_internal_phy_settings(struct pm8001_hba_info *pm8001_ha, + struct pm8001_mpi3_phy_pg_trx_config *phycfg) +{ + phycfg->LaneLosCfg = 0x00000132; + phycfg->LanePgaCfg1 = 0x00203949; + phycfg->LanePisoCfg1 = 0x000000FF; + phycfg->LanePisoCfg2 = 0xFF000001; + phycfg->LanePisoCfg3 = 0xE7011300; + phycfg->LanePisoCfg4 = 0x631C40C0; + phycfg->LanePisoCfg5 = 0xF8102036; + phycfg->LanePisoCfg6 = 0xF74A1000; + phycfg->LaneBctCtrl = 0x00FB33F8; +} + +/** + * pm8001_get_external_phy_settings : Retrieves the external PHY settings + * @pm8001_ha : our adapter + * @phycfg : PHY config page to populate + */ +static +void pm8001_get_external_phy_settings(struct pm8001_hba_info *pm8001_ha, + struct pm8001_mpi3_phy_pg_trx_config *phycfg) +{ + phycfg->LaneLosCfg = 0x00000132; + phycfg->LanePgaCfg1 = 0x00203949; + phycfg->LanePisoCfg1 = 0x000000FF; + phycfg->LanePisoCfg2 = 0xFF000001; + phycfg->LanePisoCfg3 = 0xE7011300; + phycfg->LanePisoCfg4 = 0x63349140; + phycfg->LanePisoCfg5 = 0xF8102036; + phycfg->LanePisoCfg6 = 0xF80D9300; + phycfg->LaneBctCtrl = 0x00FB33F8; +} + +/** + * pm8001_get_phy_mask : Retrieves the mask that denotes if a PHY is int/ext + * @pm8001_ha : our adapter + * @phymask : The PHY mask + */ +static +void pm8001_get_phy_mask(struct pm8001_hba_info *pm8001_ha, int *phymask) +{ + switch (pm8001_ha->pdev->subsystem_device) { + case 0x0070: /* H1280 - 8 external 0 internal */ + case 0x0072: /* H12F0 - 16 external 0 internal */ + *phymask = 0x0000; + break; + + case 0x0071: /* H1208 - 0 external 8 internal */ + case 0x0073: /* H120F - 0 external 16 internal */ + *phymask = 0xFFFF; + break; + + case 0x0080: /* H1244 - 4 external 4 internal */ + *phymask = 0x00F0; + break; + + case 0x0081: /* H1248 - 4 external 8 internal */ + *phymask = 0x0FF0; + break; + + case 0x0082: /* H1288 - 8 external 8 internal */ + *phymask = 0xFF00; + break; + + default: + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Unknown subsystem device=0x%.04x", + pm8001_ha->pdev->subsystem_device)); + } +} + +/** + * pm8001_set_phy_settings_ven_117c_12Gb : Configure ATTO 12Gb PHY settings + * @pm8001_ha : our adapter + */ +static +int pm8001_set_phy_settings_ven_117c_12G(struct pm8001_hba_info *pm8001_ha) +{ + struct pm8001_mpi3_phy_pg_trx_config phycfg_int; + struct pm8001_mpi3_phy_pg_trx_config phycfg_ext; + int phymask = 0; + int i = 0; + + memset(&phycfg_int, 0, sizeof(phycfg_int)); + memset(&phycfg_ext, 0, sizeof(phycfg_ext)); + + pm8001_get_internal_phy_settings(pm8001_ha, &phycfg_int); + pm8001_get_external_phy_settings(pm8001_ha, &phycfg_ext); + pm8001_get_phy_mask(pm8001_ha, &phymask); + + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + if (phymask & (1 << i)) {/* Internal PHY */ + pm8001_set_phy_profile_single(pm8001_ha, i, + sizeof(phycfg_int) / sizeof(u32), + (u32 *)&phycfg_int); + + } else { /* External PHY */ + pm8001_set_phy_profile_single(pm8001_ha, i, + sizeof(phycfg_ext) / sizeof(u32), + (u32 *)&phycfg_ext); + } + } + + return 0; +} + +/** + * pm8001_configure_phy_settings : Configures PHY settings based on vendor ID. + * @pm8001_ha : our hba. + */ +static int pm8001_configure_phy_settings(struct pm8001_hba_info *pm8001_ha) +{ + switch (pm8001_ha->pdev->subsystem_vendor) { + case PCI_VENDOR_ID_ATTO: + if (pm8001_ha->pdev->device == 0x0042) /* 6Gb */ + return 0; + else + return pm8001_set_phy_settings_ven_117c_12G(pm8001_ha); + + case PCI_VENDOR_ID_ADAPTEC2: + case 0: + return 0; + + default: + return pm8001_get_phy_settings_info(pm8001_ha); + } +} + #ifdef PM8001_USE_MSIX /** * pm8001_setup_msix - enable MSI-X interrupt @@ -792,7 +951,7 @@ static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) pdev = pm8001_ha->pdev; #ifdef PM8001_USE_MSIX - if (pdev->msix_cap) + if (pdev->msix_cap && pci_msi_enabled()) return pm8001_setup_msix(pm8001_ha); else { PM8001_INIT_DBG(pm8001_ha, @@ -803,6 +962,8 @@ static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) intx: /* initialize the INT-X interrupt */ + pm8001_ha->irq_vector[0].irq_id = 0; + pm8001_ha->irq_vector[0].drv_inst = pm8001_ha; rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED, DRV_NAME, SHOST_TO_SAS_HA(pm8001_ha->shost)); return rc; @@ -902,12 +1063,9 @@ static int pm8001_pci_probe(struct pci_dev *pdev, pm8001_init_sas_add(pm8001_ha); /* phy setting support for motherboard controller */ - if (pdev->subsystem_vendor != PCI_VENDOR_ID_ADAPTEC2 && - pdev->subsystem_vendor != 0) { - rc = pm8001_get_phy_settings_info(pm8001_ha); - if (rc) - goto err_out_shost; - } + if (pm8001_configure_phy_settings(pm8001_ha)) + goto err_out_shost; + pm8001_post_sas_ha_init(shost, chip); rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); if (rc) @@ -937,10 +1095,10 @@ static void pm8001_pci_remove(struct pci_dev *pdev) struct pm8001_hba_info *pm8001_ha; int i, j; pm8001_ha = sha->lldd_ha; + scsi_remove_host(pm8001_ha->shost); sas_unregister_ha(sha); sas_remove_host(pm8001_ha->shost); list_del(&pm8001_ha->list); - scsi_remove_host(pm8001_ha->shost); PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF); PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha); @@ -956,7 +1114,8 @@ static void pm8001_pci_remove(struct pci_dev *pdev) #endif #ifdef PM8001_USE_TASKLET /* For non-msix and msix interrupts */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) || + (pm8001_ha->chip_id == chip_8001)) tasklet_kill(&pm8001_ha->tasklet[0]); else for (j = 0; j < PM8001_MAX_MSIX_VEC; j++) @@ -1005,7 +1164,8 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) #endif #ifdef PM8001_USE_TASKLET /* For non-msix and msix interrupts */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) || + (pm8001_ha->chip_id == chip_8001)) tasklet_kill(&pm8001_ha->tasklet[0]); else for (j = 0; j < PM8001_MAX_MSIX_VEC; j++) @@ -1074,7 +1234,8 @@ static int pm8001_pci_resume(struct pci_dev *pdev) goto err_out_disable; #ifdef PM8001_USE_TASKLET /* Tasklet for non msi-x interrupt handler */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) || + (pm8001_ha->chip_id == chip_8001)) tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet, (unsigned long)&(pm8001_ha->irq_vector[0])); else @@ -1087,6 +1248,19 @@ static int pm8001_pci_resume(struct pci_dev *pdev) for (i = 1; i < pm8001_ha->number_of_intr; i++) PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i); } + + /* Chip documentation for the 8070 and 8072 SPCv */ + /* states that a 500ms minimum delay is required */ + /* before issuing commands. Otherwise, the firmare */ + /* will enter an unrecoverable state. */ + + if (pm8001_ha->chip_id == chip_8070 || + pm8001_ha->chip_id == chip_8072) { + mdelay(500); + } + + /* Spin up the PHYs */ + pm8001_ha->flags = PM8001F_RUN_TIME; for (i = 0; i < pm8001_ha->chip->n_phy; i++) { pm8001_ha->phy[i].enable_completion = &completion; @@ -1108,6 +1282,8 @@ err_out_enable: */ static struct pci_device_id pm8001_pci_table[] = { { PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001 }, + { PCI_VDEVICE(PMC_Sierra, 0x8006), chip_8006 }, + { PCI_VDEVICE(ADAPTEC2, 0x8006), chip_8006 }, { PCI_VDEVICE(ATTO, 0x0042), chip_8001 }, /* Support for SPC/SPCv/SPCve controllers */ { PCI_VDEVICE(ADAPTEC2, 0x8001), chip_8001 }, @@ -1163,6 +1339,20 @@ static struct pci_device_id pm8001_pci_table[] = { PCI_VENDOR_ID_ADAPTEC2, 0x0808, 0, 0, chip_8077 }, { PCI_VENDOR_ID_ADAPTEC2, 0x8074, PCI_VENDOR_ID_ADAPTEC2, 0x0404, 0, 0, chip_8074 }, + { PCI_VENDOR_ID_ATTO, 0x8070, + PCI_VENDOR_ID_ATTO, 0x0070, 0, 0, chip_8070 }, + { PCI_VENDOR_ID_ATTO, 0x8070, + PCI_VENDOR_ID_ATTO, 0x0071, 0, 0, chip_8070 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0072, 0, 0, chip_8072 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0073, 0, 0, chip_8072 }, + { PCI_VENDOR_ID_ATTO, 0x8070, + PCI_VENDOR_ID_ATTO, 0x0080, 0, 0, chip_8070 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0081, 0, 0, chip_8072 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0082, 0, 0, chip_8072 }, {} /* terminate list */ }; @@ -1218,7 +1408,7 @@ MODULE_AUTHOR("Anand Kumar Santhanam <AnandKumar.Santhanam@pmcs.com>"); MODULE_AUTHOR("Sangeetha Gnanasekaran <Sangeetha.Gnanasekaran@pmcs.com>"); MODULE_AUTHOR("Nikith Ganigarakoppal <Nikith.Ganigarakoppal@pmcs.com>"); MODULE_DESCRIPTION( - "PMC-Sierra PM8001/8081/8088/8089/8074/8076/8077 " + "PMC-Sierra PM8001/8006/8081/8088/8089/8074/8076/8077/8070/8072 " "SAS/SATA controller driver"); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/scsi/pm8001/pm8001_sas.c b/kernel/drivers/scsi/pm8001/pm8001_sas.c index b93f289b4..949198c01 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_sas.c +++ b/kernel/drivers/scsi/pm8001/pm8001_sas.c @@ -790,6 +790,7 @@ pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha, ccb->device = pm8001_dev; ccb->ccb_tag = ccb_tag; ccb->task = task; + ccb->n_elem = 0; res = PM8001_CHIP_DISP->task_abort(pm8001_ha, pm8001_dev, flag, task_tag, ccb_tag); @@ -975,19 +976,27 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev) phy = sas_get_local_phy(dev); if (dev_is_sata(dev)) { - DECLARE_COMPLETION_ONSTACK(completion_setstate); if (scsi_is_sas_phy_local(phy)) { rc = 0; goto out; } rc = sas_phy_reset(phy, 1); + if (rc) { + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("phy reset failed for device %x\n" + "with rc %d\n", pm8001_dev->device_id, rc)); + rc = TMF_RESP_FUNC_FAILED; + goto out; + } msleep(2000); rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , dev, 1, 0); - pm8001_dev->setds_completion = &completion_setstate; - rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, - pm8001_dev, 0x01); - wait_for_completion(&completion_setstate); + if (rc) { + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("task abort failed %x\n" + "with rc %d\n", pm8001_dev->device_id, rc)); + rc = TMF_RESP_FUNC_FAILED; + } } else { rc = sas_phy_reset(phy, 1); msleep(2000); diff --git a/kernel/drivers/scsi/pm8001/pm8001_sas.h b/kernel/drivers/scsi/pm8001/pm8001_sas.h index 8dd8b7840..6628cc383 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_sas.h +++ b/kernel/drivers/scsi/pm8001/pm8001_sas.h @@ -58,7 +58,7 @@ #include "pm8001_defs.h" #define DRV_NAME "pm80xx" -#define DRV_VERSION "0.1.37" +#define DRV_VERSION "0.1.38" #define PM8001_FAIL_LOGGING 0x01 /* Error message logging */ #define PM8001_INIT_LOGGING 0x02 /* driver init logging */ #define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */ @@ -106,7 +106,9 @@ do { \ #define DEV_IS_EXPANDER(type) ((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE)) #define IS_SPCV_12G(dev) ((dev->device == 0X8074) \ || (dev->device == 0X8076) \ - || (dev->device == 0X8077)) + || (dev->device == 0X8077) \ + || (dev->device == 0X8070) \ + || (dev->device == 0X8072)) #define PM8001_NAME_LENGTH 32/* generic length of strings */ extern struct list_head hba_list; @@ -241,7 +243,7 @@ struct pm8001_chip_info { struct pm8001_port { struct asd_sas_port sas_port; u8 port_attached; - u8 wide_port_phymap; + u16 wide_port_phymap; u8 port_state; struct list_head list; }; @@ -569,6 +571,14 @@ struct pm8001_fw_image_header { #define NCQ_READ_LOG_FLAG 0x80000000 #define NCQ_ABORT_ALL_FLAG 0x40000000 #define NCQ_2ND_RLE_FLAG 0x20000000 + +/* Device states */ +#define DS_OPERATIONAL 0x01 +#define DS_PORT_IN_RESET 0x02 +#define DS_IN_RECOVERY 0x03 +#define DS_IN_ERROR 0x04 +#define DS_NON_OPERATIONAL 0x07 + /** * brief param structure for firmware flash update. */ @@ -700,6 +710,8 @@ int pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha); int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha, u32 length, u8 *buf); +void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha, + u32 phy, u32 length, u32 *buf); int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); ssize_t pm80xx_get_fatal_dump(struct device *cdev, struct device_attribute *attr, char *buf); diff --git a/kernel/drivers/scsi/pm8001/pm80xx_hwi.c b/kernel/drivers/scsi/pm8001/pm80xx_hwi.c index 05cce463a..eb4fee61d 100644 --- a/kernel/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/kernel/drivers/scsi/pm8001/pm80xx_hwi.c @@ -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, diff --git a/kernel/drivers/scsi/pm8001/pm80xx_hwi.h b/kernel/drivers/scsi/pm8001/pm80xx_hwi.h index 9970a3857..7a443bad6 100644 --- a/kernel/drivers/scsi/pm8001/pm80xx_hwi.h +++ b/kernel/drivers/scsi/pm8001/pm80xx_hwi.h @@ -177,7 +177,8 @@ /* Thermal related */ #define THERMAL_ENABLE 0x1 #define THERMAL_LOG_ENABLE 0x1 -#define THERMAL_OP_CODE 0x6 +#define THERMAL_PAGE_CODE_7H 0x6 +#define THERMAL_PAGE_CODE_8H 0x7 #define LTEMPHIL 70 #define RTEMPHIL 100 @@ -1174,7 +1175,7 @@ typedef struct SASProtocolTimerConfig SASProtocolTimerConfig_t; #define IO_XFER_ERROR_INTERNAL_CRC_ERROR 0x54 #define MPI_IO_RQE_BUSY_FULL 0x55 #define IO_XFER_ERR_EOB_DATA_OVERRUN 0x56 -#define IO_XFR_ERROR_INVALID_SSP_RSP_FRAME 0x57 +#define IO_XFER_ERROR_INVALID_SSP_RSP_FRAME 0x57 #define IO_OPEN_CNX_ERROR_OPEN_PREEMPTED 0x58 #define MPI_ERR_IO_RESOURCE_UNAVAILABLE 0x1004 |