Commit 6787f5fae0fb9e3923b8c316780645c3e1d81df2
1 parent
b8174937
SPARC SCSI fixes.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2158 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
31 additions
and
8 deletions
hw/esp.c
| ... | ... | @@ -61,7 +61,11 @@ struct ESPState { |
| 61 | 61 | int cmdlen; |
| 62 | 62 | int do_cmd; |
| 63 | 63 | |
| 64 | + /* The amount of data left in the current DMA transfer. */ | |
| 64 | 65 | uint32_t dma_left; |
| 66 | + /* The size of the current DMA transfer. Zero if no transfer is in | |
| 67 | + progress. */ | |
| 68 | + uint32_t dma_counter; | |
| 65 | 69 | uint8_t *async_buf; |
| 66 | 70 | uint32_t async_len; |
| 67 | 71 | void *dma_opaque; |
| ... | ... | @@ -92,7 +96,7 @@ static int get_cmd(ESPState *s, uint8_t *buf) |
| 92 | 96 | uint32_t dmalen; |
| 93 | 97 | int target; |
| 94 | 98 | |
| 95 | - dmalen = s->wregs[0] | (s->wregs[1] << 8); | |
| 99 | + dmalen = s->rregs[0] | (s->rregs[1] << 8); | |
| 96 | 100 | target = s->wregs[4] & 7; |
| 97 | 101 | DPRINTF("get_cmd: len %d target %d\n", dmalen, target); |
| 98 | 102 | if (s->dma) { |
| ... | ... | @@ -137,6 +141,7 @@ static void do_cmd(ESPState *s, uint8_t *buf) |
| 137 | 141 | if (datalen != 0) { |
| 138 | 142 | s->rregs[4] = STAT_IN | STAT_TC; |
| 139 | 143 | s->dma_left = 0; |
| 144 | + s->dma_counter = 0; | |
| 140 | 145 | if (datalen > 0) { |
| 141 | 146 | s->rregs[4] |= STAT_DI; |
| 142 | 147 | scsi_read_data(s->current_dev, 0); |
| ... | ... | @@ -198,6 +203,8 @@ static void esp_dma_done(ESPState *s) |
| 198 | 203 | s->rregs[5] = INTR_BS; |
| 199 | 204 | s->rregs[6] = 0; |
| 200 | 205 | s->rregs[7] = 0; |
| 206 | + s->rregs[0] = 0; | |
| 207 | + s->rregs[1] = 0; | |
| 201 | 208 | espdma_raise_irq(s->dma_opaque); |
| 202 | 209 | } |
| 203 | 210 | |
| ... | ... | @@ -232,17 +239,25 @@ static void esp_do_dma(ESPState *s) |
| 232 | 239 | s->dma_left -= len; |
| 233 | 240 | s->async_buf += len; |
| 234 | 241 | s->async_len -= len; |
| 242 | + if (to_device) | |
| 243 | + s->ti_size += len; | |
| 244 | + else | |
| 245 | + s->ti_size -= len; | |
| 235 | 246 | if (s->async_len == 0) { |
| 236 | 247 | if (to_device) { |
| 237 | 248 | // ti_size is negative |
| 238 | - s->ti_size += len; | |
| 239 | 249 | scsi_write_data(s->current_dev, 0); |
| 240 | 250 | } else { |
| 241 | - s->ti_size -= len; | |
| 242 | 251 | scsi_read_data(s->current_dev, 0); |
| 252 | + /* If there is still data to be read from the device then | |
| 253 | + complete the DMA operation immeriately. Otherwise defer | |
| 254 | + until the scsi layer has completed. */ | |
| 255 | + if (s->dma_left == 0 && s->ti_size > 0) { | |
| 256 | + esp_dma_done(s); | |
| 257 | + } | |
| 243 | 258 | } |
| 244 | - } | |
| 245 | - if (s->dma_left == 0) { | |
| 259 | + } else { | |
| 260 | + /* Partially filled a scsi buffer. Complete immediately. */ | |
| 246 | 261 | esp_dma_done(s); |
| 247 | 262 | } |
| 248 | 263 | } |
| ... | ... | @@ -269,8 +284,13 @@ static void esp_command_complete(void *opaque, int reason, uint32_t tag, |
| 269 | 284 | DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); |
| 270 | 285 | s->async_len = arg; |
| 271 | 286 | s->async_buf = scsi_get_buf(s->current_dev, 0); |
| 272 | - if (s->dma_left) | |
| 287 | + if (s->dma_left) { | |
| 273 | 288 | esp_do_dma(s); |
| 289 | + } else if (s->dma_counter != 0 && s->ti_size <= 0) { | |
| 290 | + /* If this was the last part of a DMA transfer then the | |
| 291 | + completion interrupt is deferred to here. */ | |
| 292 | + esp_dma_done(s); | |
| 293 | + } | |
| 274 | 294 | } |
| 275 | 295 | } |
| 276 | 296 | |
| ... | ... | @@ -278,10 +298,11 @@ static void handle_ti(ESPState *s) |
| 278 | 298 | { |
| 279 | 299 | uint32_t dmalen, minlen; |
| 280 | 300 | |
| 281 | - dmalen = s->wregs[0] | (s->wregs[1] << 8); | |
| 301 | + dmalen = s->rregs[0] | (s->rregs[1] << 8); | |
| 282 | 302 | if (dmalen==0) { |
| 283 | 303 | dmalen=0x10000; |
| 284 | 304 | } |
| 305 | + s->dma_counter = dmalen; | |
| 285 | 306 | |
| 286 | 307 | if (s->do_cmd) |
| 287 | 308 | minlen = (dmalen < 32) ? dmalen : 32; |
| ... | ... | @@ -366,7 +387,6 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
| 366 | 387 | switch (saddr) { |
| 367 | 388 | case 0: |
| 368 | 389 | case 1: |
| 369 | - s->rregs[saddr] = val; | |
| 370 | 390 | s->rregs[4] &= ~STAT_TC; |
| 371 | 391 | break; |
| 372 | 392 | case 2: |
| ... | ... | @@ -388,6 +408,9 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
| 388 | 408 | // Command |
| 389 | 409 | if (val & 0x80) { |
| 390 | 410 | s->dma = 1; |
| 411 | + /* Reload DMA counter. */ | |
| 412 | + s->rregs[0] = s->wregs[0]; | |
| 413 | + s->rregs[1] = s->wregs[1]; | |
| 391 | 414 | } else { |
| 392 | 415 | s->dma = 0; |
| 393 | 416 | } | ... | ... |