summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/scsi/pm8001
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/scsi/pm8001
parentf93b97fd65072de626c074dbe099a1fff05ce060 (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.h6
-rw-r--r--kernel/drivers/scsi/pm8001/pm8001_hwi.c5
-rw-r--r--kernel/drivers/scsi/pm8001/pm8001_init.c220
-rw-r--r--kernel/drivers/scsi/pm8001/pm8001_sas.c19
-rw-r--r--kernel/drivers/scsi/pm8001/pm8001_sas.h18
-rw-r--r--kernel/drivers/scsi/pm8001/pm80xx_hwi.c146
-rw-r--r--kernel/drivers/scsi/pm8001/pm80xx_hwi.h5
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