Implement PM interface of libsas to prepare for SATA PM support. Signed-off-by: Xiangliang Yu <yxlraid@xxxxxxxxx> --- drivers/scsi/mvsas/mv_64xx.c | 2 + drivers/scsi/mvsas/mv_94xx.c | 19 ++++ drivers/scsi/mvsas/mv_defs.h | 1 + drivers/scsi/mvsas/mv_init.c | 7 ++ drivers/scsi/mvsas/mv_sas.c | 246 +++++++++++++++++++++++++++++++++++++++++- drivers/scsi/mvsas/mv_sas.h | 9 ++ 6 files changed, 281 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 8bb0699..bb257cb 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c @@ -780,6 +780,8 @@ const struct mvs_dispatch mvs_64xx_dispatch = { mvs_64xx_iounmap, mvs_64xx_isr, mvs_64xx_isr_status, + NULL, + NULL, mvs_64xx_interrupt_enable, mvs_64xx_interrupt_disable, mvs_read_phy_ctl, diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index 1e4479f..9d547a7 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -602,6 +602,23 @@ static u32 mvs_94xx_isr_status(struct mvs_info *mvi, int irq) return stat; } +static u32 mvs_94xx_read_isr_stat(struct mvs_info *mvi) +{ + void __iomem *regs = mvi->regs; + u32 stat = 0; + + stat = mr32(MVS_INT_STAT); + + return stat; +} + +static void mvs_94xx_write_isr_stat(struct mvs_info *mvi, u32 stat) +{ + void __iomem *regs = mvi->regs; + + mw32(MVS_INT_STAT, stat); +} + static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat) { void __iomem *regs = mvi->regs; @@ -1013,6 +1030,8 @@ const struct mvs_dispatch mvs_94xx_dispatch = { mvs_94xx_iounmap, mvs_94xx_isr, mvs_94xx_isr_status, + mvs_94xx_read_isr_stat, + mvs_94xx_write_isr_stat, mvs_94xx_interrupt_enable, mvs_94xx_interrupt_disable, mvs_read_phy_ctl, diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h index f545194..4a25bb6 100644 --- a/drivers/scsi/mvsas/mv_defs.h +++ b/drivers/scsi/mvsas/mv_defs.h @@ -401,6 +401,7 @@ enum mvs_event_flags { PHY_PLUG_IN = (1U << 0), /* phy plug in */ PHY_PLUG_OUT = (1U << 1), /* phy plug out */ EXP_BRCT_CHG = (1U << 2), /* broadcast change */ + PHY_SNTF_RCVD = (1U << 3), /* pm sntf received */ }; enum mvs_port_type { diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 5ff978b..779f853 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -94,6 +94,12 @@ static struct sas_domain_function_template mvs_transport_ops = { .lldd_port_formed = mvs_port_formed, .lldd_port_deformed = mvs_port_deformed, + .lldd_dev_freeze = mvs_dev_freeze, + .lldd_dev_thaw = mvs_dev_thaw, + .lldd_wait_task_done = mvs_wait_task_done, + .lldd_ata_check_ready = mvs_check_ready, + .lldd_dev_classify = mvs_dev_classify, + .lldd_dev_set = mvs_dev_set, }; static void mvs_phy_init(struct mvs_info *mvi, int phy_id) @@ -251,6 +257,7 @@ static int mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost) mvi->port[i].wide_port_phymap = 0; mvi->port[i].port_attached = 0; INIT_LIST_HEAD(&mvi->port[i].list); + mvi->port[i].sig = 0xFFFFFFFF; } for (i = 0; i < MVS_MAX_DEVICES; i++) { mvi->devices[i].taskfileset = MVS_ID_NOT_MAPPED; diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 6c1f223..537c7d6 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -443,13 +443,15 @@ static int mvs_task_prep_ata(struct mvs_info *mvi, { struct sas_ha_struct *sha = mvi->sas; struct sas_task *task = tei->task; - struct domain_device *dev = task->dev; + struct domain_device *dev = task->dev, *parent = NULL; struct mvs_device *mvi_dev = dev->lldd_dev; struct mvs_cmd_hdr *hdr = tei->hdr; struct asd_sas_port *sas_port = dev->port; - struct sas_phy *sphy = dev->phy; - struct asd_sas_phy *sas_phy = sha->sas_phy[sphy->number]; + struct sas_phy *sphy = NULL; + struct asd_sas_phy *sas_phy = NULL; struct mvs_slot_info *slot; + struct host_to_dev_fis *fis; + struct ata_queued_cmd *qc = NULL; void *buf_prd; u32 tag = tei->tag, hdr_tag; u32 flags, del_q; @@ -464,6 +466,13 @@ static int mvs_task_prep_ata(struct mvs_info *mvi, mvi_dev->device_id); return -EBUSY; } + if (dev->parent) { + parent = dev->parent; + sphy = parent->phy; + } else + sphy = dev->phy; + sas_phy = sha->sas_phy[sphy->number]; + slot = &mvi->slot_info[tag]; slot->tx = mvi->tx_prod; del_q = TXQ_MODE_I | tag | @@ -484,6 +493,19 @@ static int mvs_task_prep_ata(struct mvs_info *mvi, flags |= MCH_ATAPI; } + if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) + flags |= MCH_RESET; + + if (!(task->task_state_flags & SAS_TASK_NEED_DEV_RESET)) { + qc = task->uldd_task; + fis = &task->ata_task.fis; + if ((qc->tf.command == ATA_CMD_PMP_READ) || + (qc->tf.command == ATA_CMD_PMP_WRITE)) + flags |= 0xF; /* pm control port */ + else + flags |= fis->flags & 0xF; + } + hdr->flags = cpu_to_le32(flags); if (task->ata_task.use_ncq && mvs_get_ncq_tag(task, &hdr_tag)) @@ -491,6 +513,13 @@ static int mvs_task_prep_ata(struct mvs_info *mvi, else hdr_tag = tag; + if (!(task->task_state_flags & SAS_TASK_NEED_DEV_RESET)) { + qc = task->uldd_task; + if ((qc->tf.command == ATA_CMD_PMP_READ) || + (qc->tf.command == ATA_CMD_PMP_WRITE)) + hdr_tag = 0; + } + hdr->tags = cpu_to_le32(hdr_tag); hdr->data_len = cpu_to_le32(task->total_xfer_len); @@ -1369,6 +1398,215 @@ void mvs_dev_gone(struct domain_device *dev) mvs_dev_gone_notify(dev); } +void mvs_dev_freeze(struct domain_device *dev) +{ + struct mvs_device *mvi_dev = dev->lldd_dev; + struct mvs_info *mvi = mvi_dev->mvi_info; + + if (!mvi) { + mv_dprintk("mvi is null.\n"); + return; + } + + MVS_CHIP_DISP->interrupt_disable(mvi); +} + +void mvs_dev_thaw(struct domain_device *dev) +{ + struct mvs_device *mvi_dev = dev->lldd_dev; + struct mvs_info *mvi = mvi_dev->mvi_info; + + if (!mvi) { + mv_dprintk("mvi is null.\n"); + return; + } + + MVS_CHIP_DISP->interrupt_enable(mvi); +} + +int mvs_wait_task_done(struct sas_task *task) +{ + struct domain_device *dev = task->dev; + struct mvs_device *mdev = dev->lldd_dev; + struct mvs_slot_info *slot = (struct mvs_slot_info *)task->lldd_task; + u32 slot_idx = slot->slot_tag; + struct mvs_info *mvi = mdev->mvi_info; + unsigned int rx_prod_idx, rx_desc; + int ret = -1; + u32 reg; + + reg = MVS_CHIP_DISP->read_isr_status(mvi); + + rx_prod_idx = mvi->rx_cons; + + mvi->rx_cons = le32_to_cpu(mvi->rx[0]); + if (mvi->rx_cons == 0xFFF) + return ret; + + if (unlikely(mvi->rx_cons == rx_prod_idx)) + mvi->rx_cons = MVS_CHIP_DISP->rx_update(mvi) & RX_RING_SZ_MASK; + + if (reg & 0x1) { + /* free slot resource */ + + task->task_state_flags = SAS_TASK_STATE_DONE; + if (mdev && mdev->running_req) + mdev->running_req--; + if (sas_protocol_ata(task->task_proto)) + mvs_free_reg_set(mvi, mdev); + + mvs_slot_task_free(mvi, task, slot, slot_idx); + + /* clear int register */ + MVS_CHIP_DISP->write_isr_status(mvi, reg & 0x1); + reg = MVS_CHIP_DISP->read_isr_status(mvi); + + rx_desc = le32_to_cpu(mvi->rx[mvi->rx_cons + 1]); + + return 0; + } + + if (mvi->rx_cons == rx_prod_idx) + return ret; + + rx_desc = le32_to_cpu(mvi->rx[mvi->rx_cons + 1]); + if (rx_desc & RXQ_DONE) + ret = 0; + + ret = MVS_CHIP_DISP->read_isr_status(mvi); + if (reg & 0x1) + MVS_CHIP_DISP->write_isr_status(mvi, reg & 0x1); + + return ret; +} + +int mvs_check_ready(struct domain_device *dev) +{ + struct mvs_device *mdev = dev->lldd_dev; + struct mvs_info *mvi = mdev->mvi_info; + struct asd_sas_port *sas_port = dev->port; + struct mvs_port *port = sas_port->lldd_port; + int i = 0; + u32 reg, status = 0; + + while (&(mvi->port[i]) != port) + i++; + + BUG_ON(!mvi); + + reg = MVS_CHIP_DISP->read_port_irq_stat(mvi, i); + if (reg == 0 && test_bit(SAS_DEV_RESET, &dev->state)) { + clear_bit(SAS_DEV_RESET, &dev->state); + return 1; + } + status = mvs_is_phy_ready(mvi, i); + if (!status) { + mv_dprintk("phy is not ready.\n"); + return 1; + } + + if (reg & PHYEV_UNASSOC_FIS) { + reg = readl(UNASSOC_D2H_FIS(i)); + + } else if (reg & PHYEV_SIG_FIS) { + MVS_CHIP_DISP->write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0); + reg = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, i)); + } + + status = (reg >> 16) & 0xFF; + + if (status & 0x80) + return 0; + + return 1; +} + +static u32 mvs_get_dev_sig(struct mvs_info *mvi, int phy, u32 reg) +{ + u32 sig = 0, val; + + if (reg & PHYEV_UNASSOC_FIS) { + + val = readl(UNASSOC_D2H_FIS(phy) + 0xC); + val = val & 0xFF; + sig |= val; + val = readl(UNASSOC_D2H_FIS(phy) + 0x4); + val = val & 0xFFFFFF; + sig |= (val << 8); + + memset(UNASSOC_D2H_FIS(phy), 0, 4); + memset(UNASSOC_D2H_FIS(phy) + 0x4, 0, 4); + memset(UNASSOC_D2H_FIS(phy) + 0x8, 0, 4); + memset(UNASSOC_D2H_FIS(phy) + 0xC, 0, 4); + + } else if (reg & PHYEV_SIG_FIS) { + MVS_CHIP_DISP->write_port_cfg_addr(mvi, phy, PHYR_SATA_SIG3); + val = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, phy)); + val = val & 0xFF; + sig |= val; + MVS_CHIP_DISP->write_port_cfg_addr(mvi, phy, PHYR_SATA_SIG1); + val = cpu_to_le32(MVS_CHIP_DISP->read_port_cfg_data(mvi, phy)); + val = val & 0xFFFFFF; + sig |= (val << 8); + } + + return sig; +} + +int mvs_dev_classify(struct domain_device *dev) +{ + struct mvs_device *mdev = dev->lldd_dev; + struct mvs_info *mvi = mdev->mvi_info; + struct asd_sas_port *sas_port = dev->port; + struct mvs_port *port = sas_port->lldd_port; + int i = 0; + u32 reg, sig; + + if (test_and_clear_bit(SAS_DEV_RESET, &dev->state)) { + sig = port->sig; + port->sig = 0xFFFFFFFF; + + goto out; + } + + while (&mvi->port[i] != port) + i++; + + reg = MVS_CHIP_DISP->read_port_irq_stat(mvi, i); + + port->sig = mvs_get_dev_sig(mvi, i, reg); + sig = port->sig; + port->sig = 0xFFFFFFFF; + + reg = MVS_CHIP_DISP->read_port_irq_stat(mvi, i); + MVS_CHIP_DISP->write_port_irq_stat(mvi, i, reg); + reg = MVS_CHIP_DISP->read_port_irq_stat(mvi, i); +out: + + if (sig == 0x96690101) + return ATA_DEV_PMP; + else if (sig == 0x101) + return ATA_DEV_ATA; + else + return ATA_DEV_UNKNOWN; +} + +void mvs_dev_set(struct domain_device *dev) +{ + struct ata_port *ap; + + if (dev->parent && + (dev->parent->dev_type == SAS_SATA_PM)) + return; + + ap = dev->sata_dev.ap; + + /* add support PM and AN */ + ap->flags |= (ATA_FLAG_PMP | ATA_FLAG_AN); + + return; +} + static void mvs_task_done(struct sas_task *task) { if (!del_timer(&task->slow_task->timer)) @@ -1519,6 +1757,8 @@ int mvs_I_T_nexus_reset(struct domain_device *dev) struct mvs_device * mvi_dev = (struct mvs_device *)dev->lldd_dev; struct mvs_info *mvi = mvi_dev->mvi_info; + set_bit(SAS_DEV_RESET, &dev->state); + if (mvi_dev->dev_status != MVS_DEV_EH) return TMF_RESP_FUNC_COMPLETE; else diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index d6b19dc..090c074 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -113,6 +113,8 @@ struct mvs_dispatch { void (*chip_iounmap)(struct mvs_info *mvi); irqreturn_t (*isr)(struct mvs_info *mvi, int irq, u32 stat); u32 (*isr_status)(struct mvs_info *mvi, int irq); + u32 (*read_isr_status)(struct mvs_info *mvi); + void (*write_isr_status)(struct mvs_info *mvi, u32 stat); void (*interrupt_enable)(struct mvs_info *mvi); void (*interrupt_disable)(struct mvs_info *mvi); @@ -214,6 +216,7 @@ struct mvs_port { u8 port_attached; u8 wide_port_phymap; struct list_head list; + u32 sig; }; struct mvs_phy { @@ -484,5 +487,11 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events); void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st); int mvs_int_rx(struct mvs_info *mvi, bool self_clear); struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi, u8 reg_set); +void mvs_dev_freeze(struct domain_device *dev); +void mvs_dev_thaw(struct domain_device *dev); +int mvs_wait_task_done(struct sas_task *task); +int mvs_check_ready(struct domain_device *dev); +int mvs_dev_classify(struct domain_device *dev); +void mvs_dev_set(struct domain_device *dev); #endif -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html