[SCSI] mvsas: Add new macros and functions
Xiangliang Yu [Tue, 24 May 2011 14:36:02 +0000 (22:36 +0800)]
Add new macros: MVS_SOFT_RESET, MVS_HARD_RESET, MVS_PHY_TUNE,
MVS_COMMAND_ACTIVE, EXP_BRCT_CHG, MVS_MAX_SG
Add new member sg_width in struct mvs_chip_info
Use macros rather than magic number
Add new functions: mvs_fill_ssp_resp_iu, mvs_set_sense,
mvs_94xx_clear_srs_irq, mvs_94xx_phy_set_link_rate

Signed-off-by: Xiangliang Yu <yuxiangl@marvell.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>

drivers/scsi/mvsas/mv_64xx.c
drivers/scsi/mvsas/mv_94xx.c
drivers/scsi/mvsas/mv_94xx.h
drivers/scsi/mvsas/mv_defs.h
drivers/scsi/mvsas/mv_init.c
drivers/scsi/mvsas/mv_sas.c
drivers/scsi/mvsas/mv_sas.h

index 96bde34..702c767 100644 (file)
@@ -48,7 +48,7 @@ static void __devinit mvs_64xx_enable_xmt(struct mvs_info *mvi, int phy_id)
        u32 tmp;
 
        tmp = mr32(MVS_PCS);
-       if (mvi->chip->n_phy <= 4)
+       if (mvi->chip->n_phy <= MVS_SOC_PORTS)
                tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT);
        else
                tmp |= 1 << (phy_id + PCS_EN_PORT_XMT_SHIFT2);
@@ -95,7 +95,7 @@ static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
        u32 reg, tmp;
 
        if (!(mvi->flags & MVF_FLAG_SOC)) {
-               if (phy_id < 4)
+               if (phy_id < MVS_SOC_PORTS)
                        pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &reg);
                else
                        pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &reg);
@@ -104,13 +104,13 @@ static void mvs_64xx_stp_reset(struct mvs_info *mvi, u32 phy_id)
                reg = mr32(MVS_PHY_CTL);
 
        tmp = reg;
-       if (phy_id < 4)
+       if (phy_id < MVS_SOC_PORTS)
                tmp |= (1U << phy_id) << PCTL_LINK_OFFS;
        else
-               tmp |= (1U << (phy_id - 4)) << PCTL_LINK_OFFS;
+               tmp |= (1U << (phy_id - MVS_SOC_PORTS)) << PCTL_LINK_OFFS;
 
        if (!(mvi->flags & MVF_FLAG_SOC)) {
-               if (phy_id < 4) {
+               if (phy_id < MVS_SOC_PORTS) {
                        pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp);
                        mdelay(10);
                        pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, reg);
@@ -133,9 +133,9 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard)
        tmp &= ~PHYEV_RDY_CH;
        mvs_write_port_irq_stat(mvi, phy_id, tmp);
        tmp = mvs_read_phy_ctl(mvi, phy_id);
-       if (hard == 1)
+       if (hard == MVS_HARD_RESET)
                tmp |= PHY_RST_HARD;
-       else if (hard == 0)
+       else if (hard == MVS_SOFT_RESET)
                tmp |= PHY_RST;
        mvs_write_phy_ctl(mvi, phy_id, tmp);
        if (hard) {
@@ -346,7 +346,7 @@ static int __devinit mvs_64xx_init(struct mvs_info *mvi)
 
                mvs_64xx_enable_xmt(mvi, i);
 
-               mvs_64xx_phy_reset(mvi, i, 1);
+               mvs_64xx_phy_reset(mvi, i, MVS_HARD_RESET);
                msleep(500);
                mvs_64xx_detect_porttype(mvi, i);
        }
@@ -661,7 +661,7 @@ void mvs_64xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
                tmp |= lrmax;
        }
        mvs_write_phy_ctl(mvi, phy_id, tmp);
-       mvs_64xx_phy_reset(mvi, phy_id, 1);
+       mvs_64xx_phy_reset(mvi, phy_id, MVS_HARD_RESET);
 }
 
 static void mvs_64xx_clear_active_cmds(struct mvs_info *mvi)
index 5b25f1b..a0ec4aa 100644 (file)
@@ -389,7 +389,7 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
        mvs_phy_hacks(mvi);
 
        /* set LED blink when IO*/
-       mw32(MVS_PA_VSR_ADDR, 0x00000030);
+       mw32(MVS_PA_VSR_ADDR, VSR_PHY_ACT_LED);
        tmp = mr32(MVS_PA_VSR_PORT);
        tmp &= 0xFFFF00FF;
        tmp |= 0x00003300;
@@ -419,7 +419,7 @@ static int __devinit mvs_94xx_init(struct mvs_info *mvi)
                mvs_94xx_config_reg_from_hba(mvi, i);
                mvs_94xx_phy_enable(mvi, i);
 
-               mvs_94xx_phy_reset(mvi, i, 1);
+               mvs_94xx_phy_reset(mvi, i, PHY_RST_HARD);
                msleep(500);
                mvs_94xx_detect_porttype(mvi, i);
        }
@@ -585,10 +585,48 @@ static irqreturn_t mvs_94xx_isr(struct mvs_info *mvi, int irq, u32 stat)
 static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
 {
        u32 tmp;
-       mvs_cw32(mvi, 0x300 + (slot_idx >> 3), 1 << (slot_idx % 32));
-       do {
-               tmp = mvs_cr32(mvi, 0x300 + (slot_idx >> 3));
-       } while (tmp & 1 << (slot_idx % 32));
+       tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
+       if (tmp && 1 << (slot_idx % 32)) {
+               mv_printk("command active %08X,  slot [%x].\n", tmp, slot_idx);
+               mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
+                       1 << (slot_idx % 32));
+               do {
+                       tmp = mvs_cr32(mvi,
+                               MVS_COMMAND_ACTIVE + (slot_idx >> 3));
+               } while (tmp & 1 << (slot_idx % 32));
+       }
+}
+
+void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all)
+{
+       void __iomem *regs = mvi->regs;
+       u32 tmp;
+
+       if (clear_all) {
+               tmp = mr32(MVS_INT_STAT_SRS_0);
+               if (tmp) {
+                       mv_dprintk("check SRS 0 %08X.\n", tmp);
+                       mw32(MVS_INT_STAT_SRS_0, tmp);
+               }
+               tmp = mr32(MVS_INT_STAT_SRS_1);
+               if (tmp) {
+                       mv_dprintk("check SRS 1 %08X.\n", tmp);
+                       mw32(MVS_INT_STAT_SRS_1, tmp);
+               }
+       } else {
+               if (reg_set > 31)
+                       tmp = mr32(MVS_INT_STAT_SRS_1);
+               else
+                       tmp = mr32(MVS_INT_STAT_SRS_0);
+
+               if (tmp & (1 << (reg_set % 32))) {
+                       mv_dprintk("register set 0x%x was stopped.\n", reg_set);
+                       if (reg_set > 31)
+                               mw32(MVS_INT_STAT_SRS_1, 1 << (reg_set % 32));
+                       else
+                               mw32(MVS_INT_STAT_SRS_0, 1 << (reg_set % 32));
+               }
+       }
 }
 
 static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
@@ -596,12 +634,10 @@ static void mvs_94xx_issue_stop(struct mvs_info *mvi, enum mvs_port_type type,
 {
        void __iomem *regs = mvi->regs;
        u32 tmp;
+       mvs_94xx_clear_srs_irq(mvi, 0, 1);
 
-       if (type == PORT_TYPE_SATA) {
-               tmp = mr32(MVS_INT_STAT_SRS_0) | (1U << tfs);
-               mw32(MVS_INT_STAT_SRS_0, tmp);
-       }
-       mw32(MVS_INT_STAT, CINT_CI_STOP);
+       tmp = mr32(MVS_INT_STAT);
+       mw32(MVS_INT_STAT, tmp | CINT_CI_STOP);
        tmp = mr32(MVS_PCS) | 0xFF00;
        mw32(MVS_PCS, tmp);
 }
@@ -794,7 +830,18 @@ static void mvs_94xx_fix_phy_info(struct mvs_info *mvi, int i,
 void mvs_94xx_phy_set_link_rate(struct mvs_info *mvi, u32 phy_id,
                        struct sas_phy_linkrates *rates)
 {
-       /* TODO */
+       u32 lrmax = 0;
+       u32 tmp;
+
+       tmp = mvs_read_phy_ctl(mvi, phy_id);
+       lrmax = (rates->maximum_linkrate - SAS_LINK_RATE_1_5_GBPS) << 12;
+
+       if (lrmax) {
+               tmp &= ~(0x3 << 12);
+               tmp |= lrmax;
+       }
+       mvs_write_phy_ctl(mvi, phy_id, tmp);
+       mvs_94xx_phy_reset(mvi, phy_id, PHY_RST_HARD);
 }
 
 static void mvs_94xx_clear_active_cmds(struct mvs_info *mvi)
@@ -893,15 +940,6 @@ void mvs_94xx_fix_dma(struct mvs_info *mvi, u32 phy_mask,
        }
 }
 
-/*
- * FIXME JEJB: temporary nop clear_srs_irq to make 94xx still work
- * with 64xx fixes
- */
-static void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set,
-                                  u8 clear_all)
-{
-}
-
 static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
 {
        void __iomem *regs = mvi->regs;
index 723fd0a..6fc2c10 100644 (file)
@@ -109,6 +109,7 @@ enum hw_registers {
        MVS_P4_VSR_DATA         = 0x254, /* phy4 VSR data */
        MVS_PA_VSR_ADDR         = 0x290, /* All port VSR addr */
        MVS_PA_VSR_PORT         = 0x294, /* All port VSR data */
+       MVS_COMMAND_ACTIVE      = 0x300,
 };
 
 enum pci_cfg_registers {
@@ -132,6 +133,7 @@ enum sas_sata_vsp_regs {
        VSR_PHY_MODE9           = 0x09 * 4, /* Test */
        VSR_PHY_MODE10          = 0x0A * 4, /* Power */
        VSR_PHY_MODE11          = 0x0B * 4, /* Phy Mode */
+       VSR_PHY_ACT_LED         = 0x0C * 4, /* Activity LED control */
 
        VSR_PHY_FFE_CONTROL     = 0x10C,
        VSR_PHY_DFE_UPDATE_CRTL = 0x110,
index e39629d..1927e1b 100644 (file)
@@ -395,9 +395,10 @@ enum mvs_info_flags {
 };
 
 enum mvs_event_flags {
-       PHY_PLUG_EVENT  = (3U),
+       PHY_PLUG_EVENT          = (3U),
        PHY_PLUG_IN             = (1U << 0),    /* phy plug in */
        PHY_PLUG_OUT            = (1U << 1),    /* phy plug out */
+       EXP_BRCT_CHG            = (1U << 2),    /* broadcast change */
 };
 
 enum mvs_port_type {
index a7815f9..bf7d90c 100644 (file)
@@ -39,15 +39,15 @@ int interrupt_coalescing = 0x80;
 static struct scsi_transport_template *mvs_stt;
 struct kmem_cache *mvs_task_list_cache;
 static const struct mvs_chip_info mvs_chips[] = {
-       [chip_6320] =   { 1, 2, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
-       [chip_6440] =   { 1, 4, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
-       [chip_6485] =   { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, },
-       [chip_9180] =   { 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
-       [chip_9480] =   { 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
-       [chip_9445] =   { 1, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
-       [chip_9485] =   { 2, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
-       [chip_1300] =   { 1, 4, 0x400, 17, 16,  9, &mvs_64xx_dispatch, },
-       [chip_1320] =   { 2, 4, 0x800, 17, 64,  9, &mvs_94xx_dispatch, },
+       [chip_6320] =   { 1, 2, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
+       [chip_6440] =   { 1, 4, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
+       [chip_6485] =   { 1, 8, 0x800, 33, 32, 6, 10, &mvs_64xx_dispatch, },
+       [chip_9180] =   { 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
+       [chip_9480] =   { 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
+       [chip_9445] =   { 1, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
+       [chip_9485] =   { 2, 4, 0x800, 17, 64, 8, 11, &mvs_94xx_dispatch, },
+       [chip_1300] =   { 1, 4, 0x400, 17, 16, 6,  9, &mvs_64xx_dispatch, },
+       [chip_1320] =   { 2, 4, 0x800, 17, 64, 8,  9, &mvs_94xx_dispatch, },
 };
 
 struct device_attribute *mvst_host_attrs[];
@@ -466,7 +466,7 @@ static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost,
        ((struct mvs_prv_info *)sha->lldd_ha)->n_host = core_nr;
 
        shost->transportt = mvs_stt;
-       shost->max_id = 128;
+       shost->max_id = MVS_MAX_DEVICES;
        shost->max_lun = ~0;
        shost->max_channel = 1;
        shost->max_cmd_len = 16;
@@ -512,6 +512,7 @@ static void  __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
                can_queue = MVS_CHIP_SLOT_SZ;
 
        sha->lldd_queue_size = can_queue;
+       shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG);
        shost->can_queue = can_queue;
        mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE;
        sha->core.shost = mvi->shost;
index 7bd0ee3..38b4791 100644 (file)
@@ -203,12 +203,12 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
                tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
                if (tmp & PHY_RST_HARD)
                        break;
-               MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
+               MVS_CHIP_DISP->phy_reset(mvi, phy_id, MVS_HARD_RESET);
                break;
 
        case PHY_FUNC_LINK_RESET:
                MVS_CHIP_DISP->phy_enable(mvi, phy_id);
-               MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
+               MVS_CHIP_DISP->phy_reset(mvi, phy_id, MVS_SOFT_RESET);
                break;
 
        case PHY_FUNC_DISABLE:
@@ -1758,12 +1758,63 @@ static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task,
        return stat;
 }
 
+void mvs_set_sense(u8 *buffer, int len, int d_sense,
+               int key, int asc, int ascq)
+{
+       memset(buffer, 0, len);
+
+       if (d_sense) {
+               /* Descriptor format */
+               if (len < 4) {
+                       mv_printk("Length %d of sense buffer too small to "
+                               "fit sense %x:%x:%x", len, key, asc, ascq);
+               }
+
+               buffer[0] = 0x72;               /* Response Code        */
+               if (len > 1)
+                       buffer[1] = key;        /* Sense Key */
+               if (len > 2)
+                       buffer[2] = asc;        /* ASC  */
+               if (len > 3)
+                       buffer[3] = ascq;       /* ASCQ */
+       } else {
+               if (len < 14) {
+                       mv_printk("Length %d of sense buffer too small to "
+                               "fit sense %x:%x:%x", len, key, asc, ascq);
+               }
+
+               buffer[0] = 0x70;               /* Response Code        */
+               if (len > 2)
+                       buffer[2] = key;        /* Sense Key */
+               if (len > 7)
+                       buffer[7] = 0x0a;       /* Additional Sense Length */
+               if (len > 12)
+                       buffer[12] = asc;       /* ASC */
+               if (len > 13)
+                       buffer[13] = ascq; /* ASCQ */
+       }
+
+       return;
+}
+
+void mvs_fill_ssp_resp_iu(struct ssp_response_iu *iu,
+                               u8 key, u8 asc, u8 asc_q)
+{
+       iu->datapres = 2;
+       iu->response_data_len = 0;
+       iu->sense_data_len = 17;
+       iu->status = 02;
+       mvs_set_sense(iu->sense_data, 17, 0,
+                       key, asc, asc_q);
+}
+
 static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
                         u32 slot_idx)
 {
        struct mvs_slot_info *slot = &mvi->slot_info[slot_idx];
        int stat;
        u32 err_dw0 = le32_to_cpu(*(u32 *) (slot->response));
+       u32 err_dw1 = le32_to_cpu(*((u32 *)slot->response + 1));
        u32 tfs = 0;
        enum mvs_port_type type = PORT_TYPE_SAS;
 
@@ -1775,8 +1826,19 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task,
        stat = SAM_STAT_CHECK_CONDITION;
        switch (task->task_proto) {
        case SAS_PROTOCOL_SSP:
+       {
                stat = SAS_ABORTED_TASK;
+               if ((err_dw0 & NO_DEST) || err_dw1 & bit(31)) {
+                       struct ssp_response_iu *iu = slot->response +
+                               sizeof(struct mvs_err_info);
+                       mvs_fill_ssp_resp_iu(iu, NOT_READY, 0x04, 01);
+                       sas_ssp_task_response(mvi->dev, task, iu);
+                       stat = SAM_STAT_CHECK_CONDITION;
+               }
+               if (err_dw1 & bit(31))
+                       mv_printk("reuse same slot, retry command.\n");
                break;
+       }
        case SAS_PROTOCOL_SMP:
                stat = SAM_STAT_CHECK_CONDITION;
                break;
@@ -1974,13 +2036,13 @@ static void mvs_work_queue(struct work_struct *work)
        struct mvs_wq *mwq = container_of(dw, struct mvs_wq, work_q);
        struct mvs_info *mvi = mwq->mvi;
        unsigned long flags;
+       u32 phy_no = (unsigned long) mwq->data;
+       struct sas_ha_struct *sas_ha = mvi->sas;
+       struct mvs_phy *phy = &mvi->phy[phy_no];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
        spin_lock_irqsave(&mvi->lock, flags);
        if (mwq->handler & PHY_PLUG_EVENT) {
-               u32 phy_no = (unsigned long) mwq->data;
-               struct sas_ha_struct *sas_ha = mvi->sas;
-               struct mvs_phy *phy = &mvi->phy[phy_no];
-               struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
                if (phy->phy_event & PHY_PLUG_OUT) {
                        u32 tmp;
@@ -2002,6 +2064,11 @@ static void mvs_work_queue(struct work_struct *work)
                                mv_dprintk("phy%d Attached Device\n", phy_no);
                        }
                }
+       } else if (mwq->handler & EXP_BRCT_CHG) {
+               phy->phy_event &= ~EXP_BRCT_CHG;
+               sas_ha->notify_port_event(sas_phy,
+                               PORTE_BROADCAST_RCVD);
+               mv_dprintk("phy%d Got Broadcast Change\n", phy_no);
        }
        list_del(&mwq->entry);
        spin_unlock_irqrestore(&mvi->lock, flags);
@@ -2037,7 +2104,7 @@ static void mvs_sig_time_out(unsigned long tphy)
                if (&mvi->phy[phy_no] == phy) {
                        mv_dprintk("Get signature time out, reset phy %d\n",
                                phy_no+mvi->id*mvi->chip->n_phy);
-                       MVS_CHIP_DISP->phy_reset(mvi, phy_no, 1);
+                       MVS_CHIP_DISP->phy_reset(mvi, phy_no, MVS_HARD_RESET);
                }
        }
 }
@@ -2045,9 +2112,7 @@ static void mvs_sig_time_out(unsigned long tphy)
 void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
 {
        u32 tmp;
-       struct sas_ha_struct *sas_ha = mvi->sas;
        struct mvs_phy *phy = &mvi->phy[phy_no];
-       struct asd_sas_phy *sas_phy = &phy->sas_phy;
 
        phy->irq_status = MVS_CHIP_DISP->read_port_irq_stat(mvi, phy_no);
        mv_dprintk("port %d ctrl sts=0x%X.\n", phy_no+mvi->id*mvi->chip->n_phy,
@@ -2086,7 +2151,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
                                                        phy_no);
                                else
                                        MVS_CHIP_DISP->phy_reset(mvi,
-                                                       phy_no, 0);
+                                                       phy_no, MVS_SOFT_RESET);
                                return;
                        }
                }
@@ -2118,14 +2183,14 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
                        }
                        mvs_update_phyinfo(mvi, phy_no, 0);
                        if (phy->phy_type & PORT_TYPE_SAS) {
-                               MVS_CHIP_DISP->phy_reset(mvi, phy_no, 2);
+                               MVS_CHIP_DISP->phy_reset(mvi, phy_no, MVS_PHY_TUNE);
                                mdelay(10);
                        }
 
                        mvs_bytes_dmaed(mvi, phy_no);
                        /* whether driver is going to handle hot plug */
                        if (phy->phy_event & PHY_PLUG_OUT) {
-                               mvs_port_notify_formed(sas_phy, 0);
+                               mvs_port_notify_formed(&phy->sas_phy, 0);
                                phy->phy_event &= ~PHY_PLUG_OUT;
                        }
                } else {
@@ -2135,9 +2200,8 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events)
        } else if (phy->irq_status & PHYEV_BROAD_CH) {
                mv_dprintk("port %d broadcast change.\n",
                        phy_no + mvi->id*mvi->chip->n_phy);
-               /* exception for Samsung disk drive*/
-               mdelay(1000);
-               sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+               mvs_handle_event(mvi, (void *)(unsigned long)phy_no,
+                               EXP_BRCT_CHG);
        }
        MVS_CHIP_DISP->write_port_irq_stat(mvi, phy_no, phy->irq_status);
 }
index 4f8caaf..428b00a 100644 (file)
@@ -96,6 +96,11 @@ enum dev_status {
        MVS_DEV_EH      = 0x1,
 };
 
+enum dev_reset {
+       MVS_SOFT_RESET  = 0,
+       MVS_HARD_RESET  = 1,
+       MVS_PHY_TUNE    = 2,
+};
 
 struct mvs_info;
 
@@ -176,9 +181,11 @@ struct mvs_chip_info {
        u32             fis_offs;
        u32             fis_count;
        u32             srs_sz;
+       u32             sg_width;
        u32             slot_width;
        const struct mvs_dispatch *dispatch;
 };
+#define MVS_MAX_SG             (1U << mvi->chip->sg_width)
 #define MVS_CHIP_SLOT_SZ       (1U << mvi->chip->slot_width)
 #define MVS_RX_FISL_SZ         \
        (mvi->chip->fis_offs + (mvi->chip->fis_count * 0x100))