diff options
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/sata_mv.c | 55 |
1 files changed, 40 insertions, 15 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 1c53c8a..ea7af1f 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -398,8 +398,8 @@ struct mv_port_priv { dma_addr_t crqb_dma; struct mv_crpb *crpb; dma_addr_t crpb_dma; - struct mv_sg *sg_tbl; - dma_addr_t sg_tbl_dma; + struct mv_sg *sg_tbl[MV_MAX_Q_DEPTH]; + dma_addr_t sg_tbl_dma[MV_MAX_Q_DEPTH]; unsigned int req_idx; unsigned int resp_idx; @@ -483,6 +483,10 @@ static void mv_edma_cfg(struct mv_port_priv *pp, struct mv_host_priv *hpriv, void __iomem *port_mmio, int want_ncq); static int __mv_stop_dma(struct ata_port *ap); +/* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below + * because we have to allow room for worst case splitting of + * PRDs for 64K boundaries in mv_fill_sg(). + */ static struct scsi_host_template mv5_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -1107,6 +1111,7 @@ static void mv_port_free_dma_mem(struct ata_port *ap) { struct mv_host_priv *hpriv = ap->host->private_data; struct mv_port_priv *pp = ap->private_data; + int tag; if (pp->crqb) { dma_pool_free(hpriv->crqb_pool, pp->crqb, pp->crqb_dma); @@ -1116,9 +1121,18 @@ static void mv_port_free_dma_mem(struct ata_port *ap) dma_pool_free(hpriv->crpb_pool, pp->crpb, pp->crpb_dma); pp->crpb = NULL; } - if (pp->sg_tbl) { - dma_pool_free(hpriv->sg_tbl_pool, pp->sg_tbl, pp->sg_tbl_dma); - pp->sg_tbl = NULL; + /* + * For GEN_I, there's no NCQ, so we have only a single sg_tbl. + * For later hardware, we have one unique sg_tbl per NCQ tag. + */ + for (tag = 0; tag < MV_MAX_Q_DEPTH; ++tag) { + if (pp->sg_tbl[tag]) { + if (tag == 0 || !IS_GEN_I(hpriv)) + dma_pool_free(hpriv->sg_tbl_pool, + pp->sg_tbl[tag], + pp->sg_tbl_dma[tag]); + pp->sg_tbl[tag] = NULL; + } } } @@ -1139,7 +1153,7 @@ static int mv_port_start(struct ata_port *ap) struct mv_port_priv *pp; void __iomem *port_mmio = mv_ap_base(ap); unsigned long flags; - int rc; + int tag, rc; pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); if (!pp) @@ -1160,10 +1174,21 @@ static int mv_port_start(struct ata_port *ap) goto out_port_free_dma_mem; memset(pp->crpb, 0, MV_CRPB_Q_SZ); - pp->sg_tbl = dma_pool_alloc(hpriv->sg_tbl_pool, GFP_KERNEL, - &pp->sg_tbl_dma); - if (!pp->sg_tbl) - goto out_port_free_dma_mem; + /* + * For GEN_I, there's no NCQ, so we only allocate a single sg_tbl. + * For later hardware, we need one unique sg_tbl per NCQ tag. + */ + for (tag = 0; tag < MV_MAX_Q_DEPTH; ++tag) { + if (tag == 0 || !IS_GEN_I(hpriv)) { + pp->sg_tbl[tag] = dma_pool_alloc(hpriv->sg_tbl_pool, + GFP_KERNEL, &pp->sg_tbl_dma[tag]); + if (!pp->sg_tbl[tag]) + goto out_port_free_dma_mem; + } else { + pp->sg_tbl[tag] = pp->sg_tbl[0]; + pp->sg_tbl_dma[tag] = pp->sg_tbl_dma[0]; + } + } spin_lock_irqsave(&ap->host->lock, flags); @@ -1214,7 +1239,7 @@ static void mv_fill_sg(struct ata_queued_cmd *qc) struct mv_sg *mv_sg, *last_sg = NULL; unsigned int si; - mv_sg = pp->sg_tbl; + mv_sg = pp->sg_tbl[qc->tag]; for_each_sg(qc->sg, sg, qc->n_elem, si) { dma_addr_t addr = sg_dma_address(sg); u32 sg_len = sg_dma_len(sg); @@ -1284,9 +1309,9 @@ static void mv_qc_prep(struct ata_queued_cmd *qc) in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK; pp->crqb[in_index].sg_addr = - cpu_to_le32(pp->sg_tbl_dma & 0xffffffff); + cpu_to_le32(pp->sg_tbl_dma[qc->tag] & 0xffffffff); pp->crqb[in_index].sg_addr_hi = - cpu_to_le32((pp->sg_tbl_dma >> 16) >> 16); + cpu_to_le32((pp->sg_tbl_dma[qc->tag] >> 16) >> 16); pp->crqb[in_index].ctrl_flags = cpu_to_le16(flags); cw = &pp->crqb[in_index].ata_cmd[0]; @@ -1377,8 +1402,8 @@ static void mv_qc_prep_iie(struct ata_queued_cmd *qc) in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK; crqb = (struct mv_crqb_iie *) &pp->crqb[in_index]; - crqb->addr = cpu_to_le32(pp->sg_tbl_dma & 0xffffffff); - crqb->addr_hi = cpu_to_le32((pp->sg_tbl_dma >> 16) >> 16); + crqb->addr = cpu_to_le32(pp->sg_tbl_dma[qc->tag] & 0xffffffff); + crqb->addr_hi = cpu_to_le32((pp->sg_tbl_dma[qc->tag] >> 16) >> 16); crqb->flags = cpu_to_le32(flags); tf = &qc->tf; |