mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-16 10:20:30 +00:00
ahc_pci.c:
Disable "cache line streaming" for aic7890/91 Rev A chips. I have never seen these chips fail using this feature, but some of Adaptec's regression tests have. Explicitly set "cache line streaming" to on for aic7896/97 chips. This was happening before, but this documents the fact that these chips will not function correctly without CACHETHEEN set. aic7xxx.h: Add new bug types. Fix a typo in a comment. aic7xxx.reg: Add a definition for the SHVALID bit in SSTAT3 for Ultra2/3 chips. This bit inicates whether the bottom most (current) element in the S/G fifo has exhausted its data count. aic7xxx.seq: Be more careful in how we turn off the secondary DMA channel. Being less careful may hang the PCI bus arbitor that negotiates between the two DMA engines. Remove an unecessary and incorrect flag set operation in the overrun case. On Ultra2/3 controllers, clear the dma FIFO before starting to handle an overrun. We don't want any residual bytes from the beginning of the overrun to cause the code that shuts down the DMA engine from hanging because the FIFO is not (and never will be) empty. If the data fifo is empty by the time we notice that a read transaction has completed, there is no need to hit the flush bit on aic7890/91 hardware that will not perform an auto-flush. Skip some cycles by short circuiting the manual flush code in this case. When transitioning out of data phase, make sure that we have the next S/G element loaded for the following reconnect if there is more work to do. The code would do this in most cases before, but there was a small window where the current S/G element could be exhausted before our fetch of the next S/G element completed. Since the S/G fetch is already initiated at this point, it makes sense to just wait for the segment to arrive instead of incuring even more latency by canceling the fetch and initiating it later. Fast path the end of data phase handling for the last S/G segment. In the general case, we might have worked ahead a bit by stuffing the S/G FIFO with additional segments. If we stop before using them all, we need to fixup our location in the S/G stream. Since we can't work past the last S/G segment, no fixups are ever required if we stop somewhere in that final segment. Fix a little buglet in the target mode dma bug handler. We were employing the workaround in all cases instead of only for the chips that require it. Fix the cause of SCB timeouts and possible "lost data" during read operations on the aic7890. When sending a data on any Ultra2/3 controller, the final segment must be marked as such so the FIFO will be flushed and cleaned up correctly when the transfer is ended. We failed to do this for the CDB transfer and so, if the target immediately transfered from command to data phase without an intervening disconnection, the first segment transferred would be any residual bytes from the cdb transfer. The Ultra160 controllers for some reason were not affected by this problem. Many Thanks to Tor Egge for bringing the aic7890 problem to my attention, providing analysis, as well as a mechanism to reproduce the problem.
This commit is contained in:
parent
ab2adc20f2
commit
957790c3e6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=63944
@ -777,8 +777,23 @@ ahc_pci_attach(device_t dev)
|
||||
* some MBs so don't use it.
|
||||
*/
|
||||
dscommand0 &= ~DPARCKEN;
|
||||
/*
|
||||
* We default to using 32byte SCBs
|
||||
* and using cacheline streaming.
|
||||
* If external SCB ram is detected,
|
||||
* we'll switch to using 64 byte SCBs.
|
||||
*/
|
||||
dscommand0 |= CACHETHEN|USCBSIZE32;
|
||||
}
|
||||
/*
|
||||
* Handle chips that must have cache line
|
||||
* streaming (dis/en)abled.
|
||||
*/
|
||||
if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
|
||||
dscommand0 |= CACHETHEN;
|
||||
|
||||
if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
|
||||
dscommand0 &= ~CACHETHEN;
|
||||
|
||||
ahc_outb(ahc, DSCOMMAND0, dscommand0);
|
||||
|
||||
@ -1856,7 +1871,7 @@ ahc_aic7890_setup(device_t dev, struct ahc_probe_config *probe_config)
|
||||
probe_config->features = AHC_AIC7890_FE;
|
||||
probe_config->flags |= AHC_NEWEEPROM_FMT;
|
||||
if (pci_get_revid(dev) == 0)
|
||||
probe_config->bugs |= AHC_AUTOFLUSH_BUG;
|
||||
probe_config->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1897,6 +1912,7 @@ ahc_aic7896_setup(device_t dev, struct ahc_probe_config *probe_config)
|
||||
probe_config->chip = AHC_AIC7896;
|
||||
probe_config->features = AHC_AIC7896_FE;
|
||||
probe_config->flags |= AHC_NEWEEPROM_FMT;
|
||||
probe_config->bugs |= AHC_CACHETHEN_DIS_BUG;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,17 @@ typedef enum {
|
||||
* feature does not work. A manual flush of
|
||||
* the DMA FIFO is required.
|
||||
*/
|
||||
AHC_AUTOFLUSH_BUG = 0x02
|
||||
AHC_AUTOFLUSH_BUG = 0x02,
|
||||
/*
|
||||
* On the aic7890/91 Rev 0 chips, cacheline
|
||||
* streaming does not work.
|
||||
*/
|
||||
AHC_CACHETHEN_BUG = 0x04,
|
||||
/*
|
||||
* On the aic7896/97 chips, cacheline
|
||||
* streaming must be enabled.
|
||||
*/
|
||||
AHC_CACHETHEN_DIS_BUG = 0x08
|
||||
} ahc_bug;
|
||||
|
||||
typedef enum {
|
||||
@ -598,7 +608,7 @@ struct ahc_softc {
|
||||
struct scb_data *scb_data;
|
||||
|
||||
/*
|
||||
* CCBs that have been send to the controller
|
||||
* CCBs that have been sent to the controller
|
||||
*/
|
||||
LIST_HEAD(, ccb_hdr) pending_ccbs;
|
||||
|
||||
|
@ -307,6 +307,7 @@ register SSTAT2 {
|
||||
address 0x00d
|
||||
access_mode RO
|
||||
bit OVERRUN 0x80
|
||||
bit SHVALID 0x40 /* Shaddow Layer non-zero */
|
||||
bit EXP_ACTIVE 0x10 /* SCSI Expander Active */
|
||||
mask SFCNT 0x1f
|
||||
}
|
||||
|
@ -681,7 +681,8 @@ idle_loop:
|
||||
bmov CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
|
||||
mvi CCSGCTL, CCSGEN|CCSGRESET ret;
|
||||
idle_sgfetch_complete:
|
||||
mvi CCSGCTL, CCSGRESET;
|
||||
clr CCSGCTL;
|
||||
test CCSGCTL, CCSGEN jnz .;
|
||||
and CCSGADDR, (CCSGADDR_MAX - 1), SCB_RESIDUAL_SGPTR;
|
||||
idle_sg_avail:
|
||||
if ((ahc->features & AHC_ULTRA2) != 0) {
|
||||
@ -789,14 +790,12 @@ data_phase_loop:
|
||||
*/
|
||||
or SXFRCTL1,BITBUCKET;
|
||||
and DMAPARAMS, ~(HDMAEN|SDMAEN);
|
||||
/* Keep our idle loop from mucking with SG segments */
|
||||
or SCB_RESIDUAL_DATACNT[0], SG_LAST_SEG;
|
||||
if ((ahc->features & AHC_ULTRA2) != 0) {
|
||||
bmov HCNT, ALLONES, 3;
|
||||
or SXFRCTL0, CLRCHN; /* Ensure FIFO empty */
|
||||
} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
|
||||
bmov STCNT, ALLONES, 3;
|
||||
} else {
|
||||
/* XXX Use bcopy? */
|
||||
mvi STCNT[0], 0xFF;
|
||||
mvi STCNT[1], 0xFF;
|
||||
mvi STCNT[2], 0xFF;
|
||||
@ -823,6 +822,9 @@ ultra2_dmafinish:
|
||||
test DFCNTRL, DIRECTION jnz ultra2_dmafifoempty;
|
||||
and DFCNTRL, ~SCSIEN;
|
||||
test DFCNTRL, SCSIEN jnz .;
|
||||
if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
|
||||
test DFSTATUS, FIFOEMP jnz ultra2_dmafifoempty;
|
||||
}
|
||||
ultra2_dmafifoflush:
|
||||
if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
|
||||
/*
|
||||
@ -854,6 +856,33 @@ ultra2_dmahalt:
|
||||
test DFCNTRL, HDMAEN jnz .;
|
||||
|
||||
test SXFRCTL1,BITBUCKET jnz data_phase_finish;
|
||||
|
||||
/*
|
||||
* If, by chance, we stopped before being able
|
||||
* to fetch additional segments for this transfer,
|
||||
* yet the last S/G was completely exhausted,
|
||||
* call our idle loop until it is able to load
|
||||
* another segment. This will allow us to immediately
|
||||
* pickup on the next segment on the next data phase.
|
||||
*
|
||||
* If we happened to stop on the last segment, then
|
||||
* our residual information is still correct from
|
||||
* the idle loop and there is no need to perform
|
||||
* any fixups. Just jump to data_phase_finish.
|
||||
*/
|
||||
ultra2_ensure_sg:
|
||||
test SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
|
||||
/* Record if we've consumed all S/G entries */
|
||||
test SG_CACHE_SHADOW, LAST_SEG_DONE jz data_phase_finish;
|
||||
or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
|
||||
jmp data_phase_finish;
|
||||
|
||||
ultra2_shvalid:
|
||||
test SSTAT2, SHVALID jnz sgptr_fixup;
|
||||
call idle_loop;
|
||||
jmp ultra2_ensure_sg;
|
||||
|
||||
sgptr_fixup:
|
||||
/*
|
||||
* Fixup the residual next S/G pointer. The S/G preload
|
||||
* feature of the chip allows us to load two elements
|
||||
@ -875,19 +904,17 @@ sgptr_fixup_done:
|
||||
clr DATA_COUNT_ODD;
|
||||
test SG_CACHE_SHADOW, ODD_SEG jz . + 2;
|
||||
or DATA_COUNT_ODD, 0x1;
|
||||
clr SCB_RESIDUAL_DATACNT[3];
|
||||
test SG_CACHE_SHADOW, LAST_SEG jz data_phase_finish;
|
||||
or SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG;
|
||||
/* Record if we've consumed all S/G entries */
|
||||
test SG_CACHE_SHADOW, LAST_SEG_DONE jz . + 2;
|
||||
or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
|
||||
clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
|
||||
} else {
|
||||
/* If we are the last SG block, tell the hardware. */
|
||||
test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
|
||||
if ((ahc->flags & AHC_TARGETMODE) != 0) {
|
||||
test SSTAT0, TARGET jz . + 2;
|
||||
test DMAPARAMS, DIRECTION jz dma_mid_sg;
|
||||
test SSTAT0, TARGET jz dma_last_sg;
|
||||
if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) {
|
||||
test DMAPARAMS, DIRECTION jz dma_mid_sg;
|
||||
}
|
||||
}
|
||||
dma_last_sg:
|
||||
and DMAPARAMS, ~WIDEODD;
|
||||
dma_mid_sg:
|
||||
/* Start DMA data transfer. */
|
||||
@ -1071,6 +1098,7 @@ p_command:
|
||||
if ((ahc->features & AHC_ULTRA2) != 0) {
|
||||
bmov HCNT[0], SCB_CDB_LEN, 1;
|
||||
bmov HCNT[1], ALLZEROS, 2;
|
||||
mvi SG_CACHE_PRE, LAST_SEG;
|
||||
} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
|
||||
bmov STCNT[0], SCB_CDB_LEN, 1;
|
||||
bmov STCNT[1], ALLZEROS, 2;
|
||||
|
Loading…
Reference in New Issue
Block a user