Added support for hotplug and wide port.

Signed-off-by: Ke Wei <[EMAIL PROTECTED]>
---
 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 [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to