Added support for hotplug and wide port. Signed-off-by: Ke Wei <kewei@xxxxxxxxxxx> --- drivers/scsi/mvsas.c | 445 ++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 339 insertions(+), 106 deletions(-) diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c index 3bf009b..e5cf3ad 100755 --- a/drivers/scsi/mvsas.c +++ b/drivers/scsi/mvsas.c @@ -26,6 +26,12 @@ structures. this permits elimination of all the le32_to_cpu() and cpu_to_le32() conversions. + Changelog: + 2008-02-05 0.4 Added support for hotplug and wide port. + 2008-01-22 0.3 Added support for SAS HD and SATA Devices. + 2008-01-09 0.2 detect SAS disk. + 2007-09-95 0.1 rough draft, Initial version. + */ #include <linux/kernel.h> @@ -39,13 +45,13 @@ #include <asm/io.h> #define DRV_NAME "mvsas" -#define DRV_VERSION "0.3" +#define DRV_VERSION "0.4" #define _MV_DUMP 0 #define MVS_DISABLE_NVRAM #define mr32(reg) readl(regs + MVS_##reg) #define mw32(reg,val) writel((val), regs + MVS_##reg) -#define mw32_f(reg,val) do { \ +#define mw32_f(reg,val) do { \ writel((val), regs + MVS_##reg); \ readl(regs + MVS_##reg); \ } while (0) @@ -54,13 +60,19 @@ #define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width) /* offset for D2H FIS in the Received FIS List Structure */ -#define SATA_RECEIVED_D2H_FIS(reg_set) \ +#define SATA_RECEIVED_D2H_FIS(reg_set) \ ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x40) -#define SATA_RECEIVED_PIO_FIS(reg_set) \ +#define SATA_RECEIVED_PIO_FIS(reg_set) \ ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x20) -#define UNASSOC_D2H_FIS(id) \ +#define UNASSOC_D2H_FIS(id) \ ((void *) mvi->rx_fis + 0x100 * id) +#define for_each_phy(__lseq_mask, __mc, __lseq, __rest) \ + for ((__mc) = (__lseq_mask), (__lseq) = 0; \ + (__mc) != 0 && __rest; \ + (++__lseq), (__mc) >>= 1) \ + if (((__mc) & 1)) + /* driver compile-time configuration */ enum driver_configuration { MVS_TX_RING_SZ = 1024, /* TX ring size (12-bit) */ @@ -130,6 +142,7 @@ enum hw_registers { MVS_INT_STAT = 0x150, /* Central int status */ MVS_INT_MASK = 0x154, /* Central int enable */ MVS_INT_STAT_SRS = 0x158, /* SATA register set status */ + MVS_INT_MASK_SRS = 0x15C, /* ports 1-3 follow after this */ MVS_P0_INT_STAT = 0x160, /* port0 interrupt status */ @@ -223,7 +236,7 @@ enum hw_register_bits { /* shl for ports 1-3 */ CINT_PORT_STOPPED = (1U << 16), /* port0 stopped */ - CINT_PORT = (1U << 8), /* port0 event */ + CINT_PORT = (1U << 8), /* port0 event */ CINT_PORT_MASK_OFFSET = 8, CINT_PORT_MASK = (0xFF << CINT_PORT_MASK_OFFSET), @@ -300,6 +313,7 @@ enum hw_register_bits { PHY_READY_MASK = (1U << 20), /* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */ + PHYEV_DEC_ERR = (1U << 24), /* Phy Decoding Error */ PHYEV_UNASSOC_FIS = (1U << 19), /* unassociated FIS rx'd */ PHYEV_AN = (1U << 18), /* SATA async notification */ PHYEV_BIST_ACT = (1U << 17), /* BIST activate FIS */ @@ -501,6 +515,9 @@ enum status_buffer { SB_RFB_MAX = 0x400, /* RFB size*/ }; +enum error_info_rec { + CMD_ISS_STPD = (1U << 31), /* Cmd Issue Stopped */ +}; struct mvs_chip_info { u32 n_phy; @@ -534,6 +551,7 @@ struct mvs_cmd_hdr { struct mvs_slot_info { struct sas_task *task; u32 n_elem; + u32 tx; /* DMA buffer for storing cmd tbl, open addr frame, status buffer, * and PRD table @@ -546,23 +564,28 @@ struct mvs_slot_info { struct mvs_port { struct asd_sas_port sas_port; - u8 taskfileset; + u8 port_attached; + union { + u8 taskfileset; + u8 wide_port_phymap; + }; }; struct mvs_phy { struct mvs_port *port; struct asd_sas_phy sas_phy; - struct sas_identify identify; + struct sas_identify identify; + struct scsi_device *sdev; u64 dev_sas_addr; u64 att_dev_sas_addr; u32 att_dev_info; u32 dev_info; - u32 type; + u32 phy_type; u32 phy_status; u32 irq_status; u32 frame_rcvd_size; u8 frame_rcvd[32]; - u8 wide_port_phymap; + u8 phy_attached; }; struct mvs_info { @@ -610,6 +633,8 @@ struct mvs_queue_task { void *uldd_task; }; +static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata); static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port); static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val); static u32 mvs_read_port(struct mvs_info *mvi, u32 off, u32 off2, u32 port); @@ -624,9 +649,18 @@ static void mvs_write_port_vsr_addr(struct mvs_info *mvi, u32 port, u32 addr); static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port); static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val); static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val); +static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port); + +static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i); +static void mvs_detect_porttype(struct mvs_info *mvi, int i); +static void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st); +static void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port); +static u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port); +static u32 mvs_is_sig_fis_received(u32 irq_status); static int mvs_scan_finished(struct Scsi_Host *, unsigned long); static void mvs_scan_start(struct Scsi_Host *); +static int mvs_sas_slave_alloc(struct scsi_device *scsi_dev); static struct scsi_transport_template *mvs_stt; @@ -656,7 +690,7 @@ static struct scsi_host_template mvs_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, + .slave_alloc = mvs_sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, }; @@ -705,7 +739,8 @@ static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag, offset = len_ct + MVS_OAF_SZ + sizeof(struct mvs_prd) * mvi->slot_info[tag].n_elem; - dev_printk(KERN_DEBUG, &pdev->dev, "+---->Status buffer :\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "+---->Status buffer[%d] :\n", + tag); mvs_hexdump(32, (u8 *) mvi->slot_info[tag].response, (u32) mvi->slot_info[tag].buf_dma + offset); } @@ -789,7 +824,6 @@ static void mvs_hba_cq_dump(struct mvs_info *mvi) #endif } -#if 0 static void mvs_hba_interrupt_enable(struct mvs_info *mvi) { void __iomem *regs = mvi->regs; @@ -809,7 +843,6 @@ static void mvs_hba_interrupt_disable(struct mvs_info *mvi) mw32(GBL_CTL, tmp & ~INT_EN); } -#endif static int mvs_int_rx(struct mvs_info *mvi, bool self_clear); @@ -1002,6 +1035,28 @@ err_out: #endif } +static void mvs_bytes_dmaed(struct mvs_info *mvi, int i) +{ + struct mvs_phy *phy = &mvi->phy[i]; + + if (!phy->phy_attached) + return; + + if (phy->phy_type & PORT_TYPE_SAS) { + struct sas_identify_frame *id; + + id = (struct sas_identify_frame *)phy->frame_rcvd; + id->dev_type = phy->identify.device_type; + id->initiator_bits = SAS_PROTOCOL_ALL; + id->target_bits = phy->identify.target_port_protocols; + } else if (phy->phy_type & PORT_TYPE_SATA) { + /* TODO */ + } + mvi->sas.sas_phy[i]->frame_rcvd_size = phy->frame_rcvd_size; + mvi->sas.notify_port_event(mvi->sas.sas_phy[i], + PORTE_BYTES_DMAED); +} + static int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time) { /* give the phy enabling interrupt event time to come in (1s @@ -1016,34 +1071,79 @@ static int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time) static void mvs_scan_start(struct Scsi_Host *shost) { int i; - struct sas_identify_frame *id; struct mvs_info *mvi = SHOST_TO_SAS_HA(shost)->lldd_ha; for (i = 0; i < mvi->chip->n_phy; ++i) { - struct mvs_phy *phy = &mvi->phy[i]; - id = (struct sas_identify_frame *)phy->frame_rcvd; - if (phy->type & PORT_TYPE_SAS) { - id->dev_type = phy->identify.device_type; - id->initiator_bits = SAS_PROTOCOL_ALL; - id->target_bits = phy->identify.target_port_protocols; - } else if (phy->type & PORT_TYPE_SATA) { - } - mvi->sas.sas_phy[i]->frame_rcvd_size = phy->frame_rcvd_size; - mvi->sas.notify_port_event(mvi->sas.sas_phy[i], - PORTE_BYTES_DMAED); + mvs_bytes_dmaed(mvi, i); } +} + +static int mvs_sas_slave_alloc(struct scsi_device *scsi_dev) +{ + int rc; + + rc = sas_slave_alloc(scsi_dev); + return rc; } static void mvs_int_port(struct mvs_info *mvi, int port_no, u32 events) { struct pci_dev *pdev = mvi->pdev; + struct sas_ha_struct *sas_ha = &mvi->sas; + struct mvs_phy *phy = &mvi->phy[port_no]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + phy->irq_status = mvs_read_port_irq_stat(mvi, port_no); /* * events is port event now , * we need check the interrupt status which belongs to per port. */ dev_printk(KERN_DEBUG, &pdev->dev, - "Port0 = %d", mvs_read_port_irq_stat(mvi, 0)); + "Port %d Event = %X\n", + port_no, phy->irq_status); + + if ((phy->irq_status & PHYEV_POOF) || + (phy->irq_status & PHYEV_DEC_ERR)) { + if (!mvs_is_phy_ready(mvi, port_no)) { + sas_phy_disconnected(sas_phy); + sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL); + } else + mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET, NULL); + } + if (!(phy->irq_status & PHYEV_DEC_ERR)) { + if (phy->irq_status & PHYEV_COMWAKE) { + u32 tmp = mvs_read_port_irq_mask(mvi, port_no); + mvs_write_port_irq_mask(mvi, port_no, + tmp | PHYEV_SIG_FIS); + } + if ((phy->irq_status & PHYEV_SIG_FIS) || + (phy->irq_status & PHYEV_ID_DONE)) { + phy->phy_status = mvs_is_phy_ready(mvi, port_no); + if (phy->phy_status) { + mvs_detect_porttype(mvi, port_no); + + if (phy->phy_type & PORT_TYPE_SATA) { + u32 tmp = mvs_read_port_irq_mask(mvi, + port_no); + tmp &= ~PHYEV_SIG_FIS; + mvs_write_port_irq_mask(mvi, + port_no, tmp); + } + + mvs_update_phyinfo(mvi, port_no, 0); + sas_ha->notify_phy_event(sas_phy, + PHYE_OOB_DONE); + mvs_bytes_dmaed(mvi, port_no); + } else { + dev_printk(KERN_DEBUG, &pdev->dev, + "plugin interrupt but phy is gone\n"); + mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET, + NULL); + } + } + } + mvs_write_port_irq_stat(mvi, port_no, phy->irq_status); } static void mvs_int_sata(struct mvs_info *mvi) @@ -1075,13 +1175,24 @@ static void mvs_slot_free(struct mvs_info *mvi, struct sas_task *task, break; } + slot->task = NULL; mvs_tag_clear(mvi, slot_idx); } static void mvs_slot_err(struct mvs_info *mvi, struct sas_task *task, u32 slot_idx) { - /* FIXME */ + struct mvs_slot_info *slot = &mvi->slot_info[slot_idx]; + u64 err_dw0 = *(u32 *) slot->response; + void __iomem *regs = mvi->regs; + u32 tmp; + + if (err_dw0 & CMD_ISS_STPD) + if (sas_protocol_ata(task->task_proto)) { + tmp = mr32(INT_STAT_SRS); + mw32(INT_STAT_SRS, tmp & 0xFFFF); + } + mvs_hba_sb_dump(mvi, slot_idx, task->task_proto); } @@ -1091,6 +1202,7 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc) struct mvs_slot_info *slot = &mvi->slot_info[slot_idx]; struct sas_task *task = slot->task; struct task_status_struct *tstat = &task->task_status; + struct mvs_port *port = &mvi->port[task->dev->port->id]; bool aborted; spin_lock(&task->task_state_lock); @@ -1108,6 +1220,12 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc) memset(tstat, 0, sizeof(*tstat)); tstat->resp = SAS_TASK_COMPLETE; + + if (unlikely(!port->port_attached)) { + tstat->stat = SAS_PHY_DOWN; + goto out; + } + /* error info record present */ if ((rx_desc & RXQ_ERR) && (*(u64 *) slot->response)) { tstat->stat = SAM_CHECK_COND; @@ -1142,9 +1260,6 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc) case SAS_PROTOCOL_STP: { struct ata_task_resp *resp = (struct ata_task_resp *)tstat->buf; - struct domain_device *dev = task->dev; - struct mvs_port *port = - (struct mvs_port *)dev->port->lldd_port; if ((rx_desc & (RXQ_DONE | RXQ_ERR | RXQ_ATTN)) == RXQ_DONE) @@ -1156,7 +1271,8 @@ static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc) memcpy(&resp->ending_fis[0], SATA_RECEIVED_D2H_FIS(port->taskfileset), sizeof(struct dev_to_host_fis)); - /*mvs_hexdump(16,resp->ending_fis,0);*/ + if (resp->ending_fis[2] & ATA_ERR) + mvs_hexdump(16, resp->ending_fis, 0); break; } @@ -1232,11 +1348,13 @@ static int mvs_int_rx(struct mvs_info *mvi, bool self_clear) if (unlikely(rx_desc & RXQ_DONE)) mvs_slot_complete(mvi, rx_desc); - else if (rx_desc & RXQ_ATTN) { + if (rx_desc & RXQ_ATTN) { attn = true; - dev_printk(KERN_DEBUG, &pdev->dev, "ATTN\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "ATTN %X\n", + rx_desc); } else if (rx_desc & RXQ_ERR) { - dev_printk(KERN_DEBUG, &pdev->dev, "RXQ_ERR\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "RXQ_ERR %X\n", + rx_desc); } } @@ -1269,6 +1387,7 @@ static irqreturn_t mvs_interrupt(int irq, void *opaque) return IRQ_HANDLED; } +#ifdef MVS_DISABLE_MSI static irqreturn_t mvs_msi_interrupt(int irq, void *opaque) { struct mvs_info *mvi = opaque; @@ -1281,10 +1400,12 @@ static irqreturn_t mvs_msi_interrupt(int irq, void *opaque) return IRQ_HANDLED; } +#endif struct mvs_task_exec_info { struct sas_task *task; struct mvs_cmd_hdr *hdr; + struct mvs_port *port; u32 tag; int n_elem; }; @@ -1348,27 +1469,30 @@ err_out: return rc; } -#if 0 static void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port) { void __iomem *regs = mvi->regs; u32 tmp, offs; + u8 *tfs = &port->taskfileset; - if (port->taskfileset == MVS_ID_NOT_MAPPED) + if (*tfs == MVS_ID_NOT_MAPPED) return; - offs = 1U << ((port->taskfileset & 0x0f) + PCS_EN_SATA_REG_SHIFT); - if (port->taskfileset < 16) { + offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT); + if (*tfs < 16) { tmp = mr32(PCS); - mw32(PCS, tmp | ~offs); + mw32(PCS, tmp & ~offs); } else { tmp = mr32(CTL); - mw32(CTL, tmp | ~offs); + mw32(CTL, tmp & ~offs); } - port->taskfileset = MVS_ID_NOT_MAPPED; + tmp = mr32(INT_STAT_SRS) & (1U << *tfs); + if (tmp) + mw32(INT_STAT_SRS, tmp); + + *tfs = MVS_ID_NOT_MAPPED; } -#endif static u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port) { @@ -1392,6 +1516,9 @@ static u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port) mw32(PCS, tmp | offs); else mw32(CTL, tmp | offs); + tmp = mr32(INT_STAT_SRS) & (1U << i); + if (tmp) + mw32(INT_STAT_SRS, tmp); return 0; } } @@ -1419,7 +1546,7 @@ static int mvs_task_prep_ata(struct mvs_info *mvi, struct mvs_slot_info *slot; struct scatterlist *sg; struct mvs_prd *buf_prd; - struct mvs_port *port = (struct mvs_port *)sas_port->lldd_port; + struct mvs_port *port = tei->port; u32 tag = tei->tag; u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT); void *buf_tmp; @@ -1432,7 +1559,7 @@ static int mvs_task_prep_ata(struct mvs_info *mvi, return -EBUSY; slot = &mvi->slot_info[tag]; - + slot->tx = mvi->tx_prod; mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag | (TXQ_CMD_STP << TXQ_CMD_SHIFT) | (sas_port->phy_mask << TXQ_PHY_SHIFT) | @@ -1530,8 +1657,8 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, struct mvs_task_exec_info *tei) { struct sas_task *task = tei->task; - struct asd_sas_port *sas_port = task->dev->port; struct mvs_cmd_hdr *hdr = tei->hdr; + struct mvs_port *port = tei->port; struct mvs_slot_info *slot; struct scatterlist *sg; struct mvs_prd *buf_prd; @@ -1545,9 +1672,11 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, slot = &mvi->slot_info[tag]; + slot->tx = mvi->tx_prod; mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag | - (TXQ_CMD_SSP << TXQ_CMD_SHIFT) | - (sas_port->phy_mask << TXQ_PHY_SHIFT)); + (TXQ_CMD_SSP << TXQ_CMD_SHIFT) | + (port->wide_port_phymap << TXQ_PHY_SHIFT)); + flags = MCH_RETRY; if (task->ssp_task.enable_first_burst) { flags |= MCH_FBURST; @@ -1642,11 +1771,12 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags) { - struct mvs_info *mvi = task->dev->port->ha->lldd_ha; + struct domain_device *dev = task->dev; + struct mvs_info *mvi = dev->port->ha->lldd_ha; struct pci_dev *pdev = mvi->pdev; + void __iomem *regs = mvi->regs; struct mvs_task_exec_info tei; struct sas_task *t = task; - void __iomem *regs = mvi->regs; u32 tag = 0xdeadbeef, rc, n_elem = 0; unsigned long flags; u32 n = num, pass = 0; @@ -1654,6 +1784,15 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags) spin_lock_irqsave(&mvi->lock, flags); do { + tei.port = &mvi->port[dev->port->id]; + + if (!tei.port->port_attached) { + struct task_status_struct *ts = &t->task_status; + ts->stat = SAS_PHY_DOWN; + t->task_done(t); + rc = 0; + goto exec_exit; + } if (!sas_protocol_ata(t->task_proto)) { if (t->num_scatter) { n_elem = pci_map_sg(mvi->pdev, t->scatter, @@ -1724,11 +1863,12 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags) err_out_tag: mvs_tag_free(mvi, tag); err_out: - dev_printk(KERN_ERR, &pdev->dev, "mvsas exec failed[%d]!\n", pass); + dev_printk(KERN_ERR, &pdev->dev, "mvsas exec failed[%d]!\n", rc); if (!sas_protocol_ata(t->task_proto)) if (n_elem) pci_unmap_sg(mvi->pdev, t->scatter, n_elem, t->data_dir); +exec_exit: if (pass) mw32(TX_PROD_IDX, (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1)); spin_unlock_irqrestore(&mvi->lock, flags); @@ -1751,6 +1891,7 @@ static int mvs_task_abort(struct sas_task *task) /*FIXME*/ rc = TMF_RESP_FUNC_COMPLETE; + switch (task->task_proto) { case SAS_PROTOCOL_SMP: dev_printk(KERN_DEBUG, &pdev->dev, "SMP Abort! "); @@ -1823,11 +1964,10 @@ static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata) { struct mvs_info *mvi = sas_phy->ha->lldd_ha; - void __iomem *reg; int rc = 0, phy_id = sas_phy->id; u32 tmp; - reg = mvi->regs + MVS_P0_SER_CTLSTAT + (phy_id * 4); + tmp = mvs_read_phy_ctl(mvi, phy_id); switch (func) { case PHY_FUNC_SET_LINK_RATE:{ @@ -1837,7 +1977,6 @@ static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, lrmin = (rates->minimum_linkrate << 8); lrmax = (rates->maximum_linkrate << 12); - tmp = readl(reg); if (lrmin) { tmp &= ~(0xf << 8); tmp |= lrmin; @@ -1846,19 +1985,18 @@ static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, tmp &= ~(0xf << 12); tmp |= lrmax; } - writel(tmp, reg); + mvs_write_phy_ctl(mvi, phy_id, tmp); break; } case PHY_FUNC_HARD_RESET: - tmp = readl(reg); if (tmp & PHY_RST_HARD) break; - writel(tmp | PHY_RST_HARD, reg); + mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST_HARD); break; case PHY_FUNC_LINK_RESET: - writel(readl(reg) | PHY_RST, reg); + mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST); break; case PHY_FUNC_DISABLE: @@ -2127,12 +2265,10 @@ static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val) mvs_write_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port, val); } -#if 0 static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port) { return mvs_read_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port); } -#endif static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val) { @@ -2211,18 +2347,14 @@ static void mvs_detect_porttype(struct mvs_info *mvi, int i) u32 reg; struct mvs_phy *phy = &mvi->phy[i]; - /* enable auto port detection */ - mw32(GBL_PORT_TYPE, MODE_AUTO_DET_EN); - msleep(100); - /* TODO check & save device type */ reg = mr32(GBL_PORT_TYPE); if (reg & MODE_SAS_SATA & (1 << i)) { - phy->type = PORT_TYPE_SAS; + phy->phy_type |= PORT_TYPE_SAS; phy->identify.target_port_protocols = SAS_PROTOCOL_SSP; } else { - phy->type = PORT_TYPE_SATA; + phy->phy_type |= PORT_TYPE_SATA; phy->identify.target_port_protocols = SAS_PROTOCOL_STP; } @@ -2250,23 +2382,65 @@ static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf) return (void *)s; } -static u32 mvs_is_sig_fis_received(struct mvs_info *mvi, int i) +static u32 mvs_is_sig_fis_received(u32 irq_status) +{ + return irq_status & PHYEV_SIG_FIS; +} + +static void mvs_update_wideport(struct mvs_info *mvi, int i) +{ + struct mvs_phy *phy = &mvi->phy[i]; + struct mvs_port *port = phy->port; + int j, no; + + for_each_phy(port->wide_port_phymap, no, j, mvi->chip->n_phy) { + mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT); + mvs_write_port_cfg_data(mvi, no , port->wide_port_phymap); + } else { + mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT); + mvs_write_port_cfg_data(mvi, no , 0); + } +} + +static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i) { u32 tmp; + struct mvs_phy *phy = &mvi->phy[i]; + struct mvs_port *port; - tmp = mvs_read_port_irq_stat(mvi, i) & PHYEV_SIG_FIS; - if (tmp) - mvs_write_port_irq_stat(mvi, i, PHYEV_SIG_FIS); + tmp = mvs_read_phy_ctl(mvi, i); - return tmp; + if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) { + if (!phy->port) + phy->phy_attached = 1; + return tmp; + } + + port = phy->port; + if (port) { + if (phy->phy_type & PORT_TYPE_SAS) { + port->wide_port_phymap &= ~(1U << i); + if (!port->wide_port_phymap) + port->port_attached = 0; + mvs_update_wideport(mvi, i); + } else if (phy->phy_type & PORT_TYPE_SATA) { + mvs_free_reg_set(mvi, phy->port); + port->port_attached = 0; + } + phy->port = NULL; + phy->phy_attached = 0; + phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA); + } + return 0; } -static void __devinit mvs_update_phyinfo(struct mvs_info *mvi, int i) +static void mvs_update_phyinfo(struct mvs_info *mvi, int i, + int get_st) { struct mvs_phy *phy = &mvi->phy[i]; - u32 tmp; - u64 tmp64; struct pci_dev *pdev = mvi->pdev; + u32 tmp, j; + u64 tmp64; mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY); phy->dev_info = mvs_read_port_cfg_data(mvi, i); @@ -2277,20 +2451,23 @@ static void __devinit mvs_update_phyinfo(struct mvs_info *mvi, int i) mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO); phy->dev_sas_addr |= mvs_read_port_cfg_data(mvi, i); - phy->phy_status = mvs_read_phy_ctl(mvi, i); - - /* FIXME Update Wide Port info */ - phy->port = &mvi->port[i]; - phy->port->sas_port.lldd_port = phy->port; - phy->port->taskfileset = MVS_ID_NOT_MAPPED; + if (get_st) { + phy->irq_status = mvs_read_port_irq_stat(mvi, i); + phy->phy_status = mvs_is_phy_ready(mvi, i); + } - if (phy->phy_status & PHY_READY_MASK) { + if (phy->phy_status) { u32 phy_st; struct asd_sas_phy *sas_phy = mvi->sas.sas_phy[i]; mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT); phy_st = mvs_read_port_cfg_data(mvi, i); + sas_phy->linkrate = + (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >> + PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET; + + /* Updated attached_sas_addr */ mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI); phy->att_dev_sas_addr = (u64) mvs_read_port_cfg_data(mvi, i) << 32; @@ -2298,36 +2475,57 @@ static void __devinit mvs_update_phyinfo(struct mvs_info *mvi, int i) mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO); phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i); - /*Updated attached_sas_addr */ - tmp64 = phy->att_dev_sas_addr; dev_printk(KERN_DEBUG, &pdev->dev, "phy[%d] Get Attached Address 0x%llX ," " SAS Address 0x%llX\n", - i, tmp64, phy->dev_sas_addr); - tmp64 = cpu_to_be64(tmp64); + i, phy->att_dev_sas_addr, phy->dev_sas_addr); + dev_printk(KERN_DEBUG, &pdev->dev, + "Rate = %x , type = %d\n", + sas_phy->linkrate, phy->phy_type); + +#if 1 + /* + * If the device is capable of supporting a wide port + * on its phys, it may configure the phys as a wide port. + */ + if (phy->phy_type & PORT_TYPE_SAS) + for (j = 0; j < mvi->chip->n_phy && j != i; ++j) { + if ((mvi->phy[j].phy_attached) && + (mvi->phy[j].phy_type & PORT_TYPE_SAS)) + if (phy->att_dev_sas_addr == + mvi->phy[j].att_dev_sas_addr - 1) { + phy->att_dev_sas_addr = + mvi->phy[j].att_dev_sas_addr; + break; + } + } + +#endif + + tmp64 = cpu_to_be64(phy->att_dev_sas_addr); memcpy(sas_phy->attached_sas_addr, &tmp64, SAS_ADDR_SIZE); - if (phy->type & PORT_TYPE_SAS) { + if (phy->phy_type & PORT_TYPE_SAS) { mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO); phy->att_dev_info = mvs_read_port_cfg_data(mvi, i); phy->identify.device_type = phy->att_dev_info & PORT_DEV_TYPE_MASK; - sas_phy->linkrate = - (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >> - PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET; if (phy_st & PHY_OOB_DTCTD) sas_phy->oob_mode = SAS_OOB_MODE; phy->frame_rcvd_size = sizeof(struct sas_identify_frame); - } else if (phy->type & PORT_TYPE_SATA) { - if (mvs_is_sig_fis_received(mvi, i)) { + } else if (phy->phy_type & PORT_TYPE_SATA) { + if (mvs_is_sig_fis_received(phy->irq_status)) { if (phy_st & PHY_OOB_DTCTD) sas_phy->oob_mode = SATA_OOB_MODE; phy->frame_rcvd_size = sizeof(struct dev_to_host_fis); mvs_get_d2h_reg(mvi, i, (void *)sas_phy->frame_rcvd); + } else { + dev_printk(KERN_DEBUG, &pdev->dev, + "No sig fis\n"); } } /* workaround for HW phy decoding error on 1.5g disk drive */ @@ -2342,7 +2540,28 @@ static void __devinit mvs_update_phyinfo(struct mvs_info *mvi, int i) mvs_write_port_vsr_data(mvi, i, tmp); } - phy->irq_status = mvs_read_port_irq_stat(mvi, i); + if (get_st) + mvs_write_port_irq_stat(mvi, i, phy->irq_status); +} + +static void mvs_port_formed(struct asd_sas_phy *sas_phy) +{ + struct sas_ha_struct *sas_ha = sas_phy->ha; + struct mvs_info *mvi = sas_ha->lldd_ha; + struct asd_sas_port *sas_port = sas_phy->port; + struct mvs_phy *phy = sas_phy->lldd_phy; + struct mvs_port *port = &mvi->port[sas_port->id]; + unsigned long flags; + + spin_lock_irqsave(&mvi->lock, flags); + port->port_attached = 1; + phy->port = port; + if (phy->phy_type & PORT_TYPE_SAS) { + port->wide_port_phymap = sas_port->phy_mask; + mvs_update_wideport(mvi, sas_phy->id); + } else if (phy->phy_type & PORT_TYPE_SATA) + port->taskfileset = MVS_ID_NOT_MAPPED; + spin_unlock_irqrestore(&mvi->lock, flags); } static int __devinit mvs_hw_init(struct mvs_info *mvi) @@ -2431,6 +2650,9 @@ static int __devinit mvs_hw_init(struct mvs_info *mvi) mw32(RX_LO, mvi->rx_dma); mw32(RX_HI, (mvi->rx_dma >> 16) >> 16); + /* enable auto port detection */ + mw32(GBL_PORT_TYPE, MODE_AUTO_DET_EN); + msleep(100); /* init and reset phys */ for (i = 0; i < mvi->chip->n_phy; i++) { /* FIXME: is this the correct dword order? */ @@ -2460,10 +2682,12 @@ static int __devinit mvs_hw_init(struct mvs_info *mvi) mvs_write_port_irq_stat(mvi, i, tmp); /* set phy int mask */ - tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS; + tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS | + PHYEV_ID_DONE | PHYEV_DEC_ERR; mvs_write_port_irq_mask(mvi, i, tmp); - mvs_update_phyinfo(mvi, i); + msleep(100); + mvs_update_phyinfo(mvi, i, 1); mvs_enable_xmt(mvi, i); } @@ -2500,11 +2724,10 @@ static int __devinit mvs_hw_init(struct mvs_info *mvi) mw32(TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN); mw32(RX_CFG, MVS_RX_RING_SZ | RX_EN); /* enable CMD/CMPL_Q/RESP mode */ - mw32(PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN | PCS_CMD_EN | - ((mvi->flags & MVF_MSI) ? PCS_SELF_CLEAR : 0)); + mw32(PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN | PCS_CMD_EN); /* re-enable interrupts globally */ - mw32(GBL_CTL, INT_EN); + mvs_hba_interrupt_enable(mvi); /* enable completion queue interrupt */ tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM); @@ -2556,10 +2779,16 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev, if (rc) goto err_out_mvi; +#ifdef MVS_DISABLE_MSI if (!pci_enable_msi(pdev)) { + u32 tmp; + void __iomem *regs = mvi->regs; mvi->flags |= MVF_MSI; irq_handler = mvs_msi_interrupt; + tmp = mr32(PCS); + mw32(PCS, tmp | PCS_SELF_CLEAR); } +#endif rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, mvi); if (rc) @@ -2603,15 +2832,18 @@ static void __devexit mvs_pci_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); - sas_unregister_ha(&mvi->sas); - sas_remove_host(mvi->shost); - scsi_remove_host(mvi->shost); - - free_irq(pdev->irq, mvi); - if (mvi->flags & MVF_MSI) - pci_disable_msi(pdev); - mvs_free(mvi); - pci_release_regions(pdev); + if (mvi) { + sas_unregister_ha(&mvi->sas); + mvs_hba_interrupt_disable(mvi); + sas_remove_host(mvi->shost); + scsi_remove_host(mvi->shost); + + free_irq(pdev->irq, mvi); + if (mvi->flags & MVF_MSI) + pci_disable_msi(pdev); + mvs_free(mvi); + pci_release_regions(pdev); + } pci_disable_device(pdev); } @@ -2619,6 +2851,7 @@ static struct sas_domain_function_template mvs_transport_ops = { .lldd_execute_task = mvs_task_exec, .lldd_control_phy = mvs_phy_control, .lldd_abort_task = mvs_task_abort, + .lldd_port_formed = mvs_port_formed }; static struct pci_device_id __devinitdata mvs_pci_table[] = { -- 1.5.4.rc4 - 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