Hello.
There appears to be a hardware bug in that it chokes on scatterlist
if the last item is larger than 164 bytes.
The patch that follows fixes my problem on 2.6.22.
I can't think of a way to avoid second pass over scatterlist without
duplicating code (ata_qc_prep() and ata_fill_sg() from libata-core.c).
--- a/drivers/ata/sata_promise.c2007-07-09 03:32:17.0 +0400
+++ b/drivers/ata/sata_promise.c2007-10-27 17:20:03.0 +0400
@@ -531,6 +531,80 @@
memcpy(buf+31, cdb, cdb_len);
}
+/**
+ * pdc_qc_prep - Fill PCI IDE PRD table
+ * @qc: Metadata associated with taskfile to be transferred
+ *
+ * Fill PCI IDE PRD (scatter-gather) table with segments
+ * associated with the current disk command.
+ * Make sure hardware does not choke on it.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ *
+ */
+static void pdc_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct scatterlist *sg;
+ unsigned int idx;
+ const u32 SG_COUNT_ASIC_BUG = 41*4;
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ WARN_ON(qc->__sg == NULL);
+ WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
+
+ idx = 0;
+ ata_for_each_sg(sg, qc) {
+ u32 addr, offset;
+ u32 sg_len, len;
+
+ /* determine if physical DMA addr spans 64K boundary.
+* Note h/w doesn't support 64-bit, so we unconditionally
+* truncate dma_addr_t to u32.
+*/
+ addr = (u32) sg_dma_address(sg);
+ sg_len = sg_dma_len(sg);
+
+ while (sg_len) {
+ offset = addr & 0x;
+ len = sg_len;
+ if ((offset + sg_len) > 0x1)
+ len = 0x1 - offset;
+
+ ap->prd[idx].addr = cpu_to_le32(addr);
+ ap->prd[idx].flags_len = cpu_to_le32(len & 0x);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+
+ idx++;
+ sg_len -= len;
+ addr += len;
+ }
+ }
+
+ if (idx) {
+ u32 len = ap->prd[idx - 1].flags_len;
+ if (len > SG_COUNT_ASIC_BUG) {
+ u32 addr, len;
+
+ VPRINTK("Last PRD split\n");
+
+ len = le32_to_cpu(ap->prd[idx - 1].flags_len) -
SG_COUNT_ASIC_BUG;
+ addr = le32_to_cpu(ap->prd[idx - 1].addr);
+ ap->prd[idx - 1].flags_len = cpu_to_le32(len);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len);
+
+ ap->prd[idx].flags_len = cpu_to_le32(SG_COUNT_ASIC_BUG);
+ ap->prd[idx].addr = cpu_to_le32(addr + len);
+ idx++;
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr + len,
SG_COUNT_ASIC_BUG);
+ }
+ ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+ }
+}
+
static void pdc_qc_prep(struct ata_queued_cmd *qc)
{
struct pdc_port_priv *pp = qc->ap->private_data;
@@ -540,7 +614,7 @@
switch (qc->tf.protocol) {
case ATA_PROT_DMA:
- ata_qc_prep(qc);
+ pdc_qc_prep(qc);
/* fall through */
case ATA_PROT_NODATA:
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html