]> git.proxmox.com Git - qemu.git/blame - hw/esp.c
Small read fix.
[qemu.git] / hw / esp.c
CommitLineData
6f7e9aec
FB
1/*
2 * QEMU ESP emulation
3 *
4e9aec74 4 * Copyright (c) 2005-2006 Fabrice Bellard
6f7e9aec
FB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include "vl.h"
25
26/* debug ESP card */
2f275b8f 27//#define DEBUG_ESP
6f7e9aec
FB
28
29#ifdef DEBUG_ESP
30#define DPRINTF(fmt, args...) \
31do { printf("ESP: " fmt , ##args); } while (0)
9e61bde5
FB
32#define pic_set_irq(irq, level) \
33do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0)
6f7e9aec
FB
34#else
35#define DPRINTF(fmt, args...)
36#endif
37
38#define ESPDMA_REGS 4
39#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
40#define ESP_MAXREG 0x3f
2e5d83bb 41#define TI_BUFSZ 32
4f6200f0 42#define DMA_VER 0xa0000000
9e61bde5
FB
43#define DMA_INTR 1
44#define DMA_INTREN 0x10
2e5d83bb 45#define DMA_WRITE_MEM 0x100
4f6200f0 46#define DMA_LOADED 0x04000000
4e9aec74 47typedef struct ESPState ESPState;
6f7e9aec 48
4e9aec74 49struct ESPState {
6f7e9aec 50 BlockDriverState **bd;
2f275b8f
FB
51 uint8_t rregs[ESP_MAXREG];
52 uint8_t wregs[ESP_MAXREG];
6f7e9aec
FB
53 int irq;
54 uint32_t espdmaregs[ESPDMA_REGS];
2f275b8f 55 uint32_t ti_size;
4f6200f0 56 uint32_t ti_rptr, ti_wptr;
4f6200f0
FB
57 uint8_t ti_buf[TI_BUFSZ];
58 int dma;
2e5d83bb
PB
59 SCSIDevice *scsi_dev[MAX_DISKS];
60 SCSIDevice *current_dev;
4e9aec74 61};
6f7e9aec 62
2f275b8f
FB
63#define STAT_DO 0x00
64#define STAT_DI 0x01
65#define STAT_CD 0x02
66#define STAT_ST 0x03
67#define STAT_MI 0x06
68#define STAT_MO 0x07
69
70#define STAT_TC 0x10
71#define STAT_IN 0x80
72
73#define INTR_FC 0x08
74#define INTR_BS 0x10
75#define INTR_DC 0x20
9e61bde5 76#define INTR_RST 0x80
2f275b8f
FB
77
78#define SEQ_0 0x0
79#define SEQ_CD 0x4
80
81static void handle_satn(ESPState *s)
82{
83 uint8_t buf[32];
84 uint32_t dmaptr, dmalen;
2f275b8f 85 int target;
2e5d83bb 86 int32_t datalen;
2f275b8f 87
2f275b8f 88 dmalen = s->wregs[0] | (s->wregs[1] << 8);
4f6200f0
FB
89 target = s->wregs[4] & 7;
90 DPRINTF("Select with ATN len %d target %d\n", dmalen, target);
91 if (s->dma) {
92 dmaptr = iommu_translate(s->espdmaregs[1]);
2e5d83bb
PB
93 DPRINTF("DMA Direction: %c, addr 0x%8.8x\n",
94 s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', dmaptr);
4f6200f0
FB
95 cpu_physical_memory_read(dmaptr, buf, dmalen);
96 } else {
97 buf[0] = 0;
98 memcpy(&buf[1], s->ti_buf, dmalen);
99 dmalen++;
100 }
2e5d83bb 101
2f275b8f 102 s->ti_size = 0;
4f6200f0
FB
103 s->ti_rptr = 0;
104 s->ti_wptr = 0;
2f275b8f 105
2e5d83bb
PB
106 if (target >= 4 || !s->scsi_dev[target]) {
107 // No such drive
2f275b8f
FB
108 s->rregs[4] = STAT_IN;
109 s->rregs[5] = INTR_DC;
110 s->rregs[6] = SEQ_0;
9e61bde5 111 s->espdmaregs[0] |= DMA_INTR;
2f275b8f
FB
112 pic_set_irq(s->irq, 1);
113 return;
114 }
2e5d83bb
PB
115 s->current_dev = s->scsi_dev[target];
116 datalen = scsi_send_command(s->current_dev, 0, &buf[1]);
117 if (datalen == 0) {
118 s->ti_size = 0;
119 } else {
120 s->rregs[4] = STAT_IN | STAT_TC;
121 if (datalen > 0) {
122 s->rregs[4] |= STAT_DI;
123 s->ti_size = datalen;
124 } else {
125 s->rregs[4] |= STAT_DO;
126 s->ti_size = -datalen;
b9788fc4 127 }
2f275b8f 128 }
2f275b8f
FB
129 s->rregs[5] = INTR_BS | INTR_FC;
130 s->rregs[6] = SEQ_CD;
9e61bde5 131 s->espdmaregs[0] |= DMA_INTR;
2f275b8f
FB
132 pic_set_irq(s->irq, 1);
133}
134
135static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
136{
db59203d 137 uint32_t dmaptr;
2f275b8f 138
db59203d 139 DPRINTF("Transfer status len %d\n", len);
4f6200f0
FB
140 if (s->dma) {
141 dmaptr = iommu_translate(s->espdmaregs[1]);
2e5d83bb
PB
142 DPRINTF("DMA Direction: %c\n",
143 s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r');
4f6200f0
FB
144 cpu_physical_memory_write(dmaptr, buf, len);
145 s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
146 s->rregs[5] = INTR_BS | INTR_FC;
147 s->rregs[6] = SEQ_CD;
4f6200f0
FB
148 } else {
149 memcpy(s->ti_buf, buf, len);
db59203d 150 s->ti_size = len;
4f6200f0
FB
151 s->ti_rptr = 0;
152 s->ti_wptr = 0;
db59203d 153 s->rregs[7] = len;
4f6200f0 154 }
9e61bde5 155 s->espdmaregs[0] |= DMA_INTR;
2f275b8f
FB
156 pic_set_irq(s->irq, 1);
157
158}
4f6200f0 159
2f275b8f
FB
160static const uint8_t okbuf[] = {0, 0};
161
2e5d83bb
PB
162static void esp_command_complete(void *opaque, uint32_t tag, int fail)
163{
164 ESPState *s = (ESPState *)opaque;
165
166 DPRINTF("SCSI Command complete\n");
167 if (s->ti_size != 0)
168 DPRINTF("SCSI command completed unexpectedly\n");
169 s->ti_size = 0;
170 /* ??? Report failures. */
171 if (fail)
172 DPRINTF("Command failed\n");
173 s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
174}
175
2f275b8f
FB
176static void handle_ti(ESPState *s)
177{
db59203d 178 uint32_t dmaptr, dmalen, minlen, len, from, to;
2f275b8f 179 unsigned int i;
2e5d83bb
PB
180 int to_device;
181 uint8_t buf[TARGET_PAGE_SIZE];
2f275b8f 182
2f275b8f 183 dmalen = s->wregs[0] | (s->wregs[1] << 8);
db59203d
PB
184 if (dmalen==0) {
185 dmalen=0x10000;
186 }
187
188 minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
189 DPRINTF("Transfer Information len %d\n", minlen);
4f6200f0
FB
190 if (s->dma) {
191 dmaptr = iommu_translate(s->espdmaregs[1]);
2e5d83bb
PB
192 /* Check if the transfer writes to to reads from the device. */
193 to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
194 DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
195 to_device ? 'r': 'w', dmaptr, s->ti_size);
db59203d
PB
196 from = s->espdmaregs[1];
197 to = from + minlen;
198 for (i = 0; i < minlen; i += len, from += len) {
4f6200f0 199 dmaptr = iommu_translate(s->espdmaregs[1] + i);
db59203d
PB
200 if ((from & TARGET_PAGE_MASK) != (to & TARGET_PAGE_MASK)) {
201 len = TARGET_PAGE_SIZE - (from & ~TARGET_PAGE_MASK);
202 } else {
203 len = to - from;
204 }
205 DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
2e5d83bb
PB
206 s->ti_size -= len;
207 if (to_device) {
208 cpu_physical_memory_read(dmaptr, buf, len);
209 scsi_write_data(s->current_dev, buf, len);
210 } else {
211 scsi_read_data(s->current_dev, buf, len);
212 cpu_physical_memory_write(dmaptr, buf, len);
213 }
4f6200f0 214 }
2e5d83bb
PB
215 if (s->ti_size) {
216 s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
4e9aec74 217 }
db59203d 218 s->rregs[5] = INTR_BS;
4f6200f0 219 s->rregs[6] = 0;
db59203d 220 s->rregs[7] = 0;
9e61bde5 221 s->espdmaregs[0] |= DMA_INTR;
4f6200f0 222 }
2f275b8f
FB
223 pic_set_irq(s->irq, 1);
224}
225
6f7e9aec
FB
226static void esp_reset(void *opaque)
227{
228 ESPState *s = opaque;
2f275b8f 229 memset(s->rregs, 0, ESP_MAXREG);
4e9aec74 230 memset(s->wregs, 0, ESP_MAXREG);
2f275b8f 231 s->rregs[0x0e] = 0x4; // Indicate fas100a
6f7e9aec 232 memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
4e9aec74
PB
233 s->ti_size = 0;
234 s->ti_rptr = 0;
235 s->ti_wptr = 0;
4e9aec74 236 s->dma = 0;
6f7e9aec
FB
237}
238
239static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
240{
241 ESPState *s = opaque;
242 uint32_t saddr;
243
244 saddr = (addr & ESP_MAXREG) >> 2;
9e61bde5 245 DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
6f7e9aec 246 switch (saddr) {
4f6200f0
FB
247 case 2:
248 // FIFO
249 if (s->ti_size > 0) {
250 s->ti_size--;
2e5d83bb
PB
251 if ((s->rregs[4] & 6) == 0) {
252 /* Data in/out. */
253 scsi_read_data(s->current_dev, &s->rregs[2], 0);
254 } else {
255 s->rregs[2] = s->ti_buf[s->ti_rptr++];
256 }
4f6200f0
FB
257 pic_set_irq(s->irq, 1);
258 }
259 if (s->ti_size == 0) {
260 s->ti_rptr = 0;
261 s->ti_wptr = 0;
262 }
263 break;
9e61bde5
FB
264 case 5:
265 // interrupt
266 // Clear status bits except TC
267 s->rregs[4] &= STAT_TC;
268 pic_set_irq(s->irq, 0);
269 s->espdmaregs[0] &= ~DMA_INTR;
270 break;
6f7e9aec
FB
271 default:
272 break;
273 }
2f275b8f 274 return s->rregs[saddr];
6f7e9aec
FB
275}
276
277static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
278{
279 ESPState *s = opaque;
280 uint32_t saddr;
281
282 saddr = (addr & ESP_MAXREG) >> 2;
2f275b8f 283 DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
6f7e9aec 284 switch (saddr) {
4f6200f0
FB
285 case 0:
286 case 1:
287 s->rregs[saddr] = val;
288 break;
289 case 2:
290 // FIFO
2e5d83bb
PB
291 if ((s->rregs[4] & 6) == 0) {
292 uint8_t buf;
293 buf = val & 0xff;
294 s->ti_size--;
295 scsi_write_data(s->current_dev, &buf, 0);
296 } else {
297 s->ti_size++;
298 s->ti_buf[s->ti_wptr++] = val & 0xff;
299 }
4f6200f0 300 break;
6f7e9aec 301 case 3:
4f6200f0 302 s->rregs[saddr] = val;
6f7e9aec 303 // Command
4f6200f0
FB
304 if (val & 0x80) {
305 s->dma = 1;
306 } else {
307 s->dma = 0;
308 }
6f7e9aec
FB
309 switch(val & 0x7f) {
310 case 0:
2f275b8f
FB
311 DPRINTF("NOP (%2.2x)\n", val);
312 break;
313 case 1:
314 DPRINTF("Flush FIFO (%2.2x)\n", val);
9e61bde5 315 //s->ti_size = 0;
2f275b8f 316 s->rregs[5] = INTR_FC;
9e61bde5 317 s->rregs[6] = 0;
6f7e9aec
FB
318 break;
319 case 2:
2f275b8f 320 DPRINTF("Chip reset (%2.2x)\n", val);
6f7e9aec
FB
321 esp_reset(s);
322 break;
323 case 3:
2f275b8f 324 DPRINTF("Bus reset (%2.2x)\n", val);
9e61bde5
FB
325 s->rregs[5] = INTR_RST;
326 if (!(s->wregs[8] & 0x40)) {
327 s->espdmaregs[0] |= DMA_INTR;
328 pic_set_irq(s->irq, 1);
329 }
2f275b8f
FB
330 break;
331 case 0x10:
332 handle_ti(s);
333 break;
334 case 0x11:
335 DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
336 dma_write(s, okbuf, 2);
337 break;
338 case 0x12:
339 DPRINTF("Message Accepted (%2.2x)\n", val);
340 dma_write(s, okbuf, 2);
341 s->rregs[5] = INTR_DC;
342 s->rregs[6] = 0;
6f7e9aec
FB
343 break;
344 case 0x1a:
2f275b8f 345 DPRINTF("Set ATN (%2.2x)\n", val);
6f7e9aec
FB
346 break;
347 case 0x42:
2f275b8f
FB
348 handle_satn(s);
349 break;
350 case 0x43:
351 DPRINTF("Set ATN & stop (%2.2x)\n", val);
352 handle_satn(s);
353 break;
354 default:
4f6200f0 355 DPRINTF("Unhandled ESP command (%2.2x)\n", val);
6f7e9aec
FB
356 break;
357 }
358 break;
359 case 4 ... 7:
6f7e9aec 360 break;
4f6200f0
FB
361 case 8:
362 s->rregs[saddr] = val;
363 break;
364 case 9 ... 10:
365 break;
9e61bde5
FB
366 case 11:
367 s->rregs[saddr] = val & 0x15;
368 break;
369 case 12 ... 15:
4f6200f0
FB
370 s->rregs[saddr] = val;
371 break;
6f7e9aec 372 default:
6f7e9aec
FB
373 break;
374 }
2f275b8f 375 s->wregs[saddr] = val;
6f7e9aec
FB
376}
377
378static CPUReadMemoryFunc *esp_mem_read[3] = {
379 esp_mem_readb,
380 esp_mem_readb,
381 esp_mem_readb,
382};
383
384static CPUWriteMemoryFunc *esp_mem_write[3] = {
385 esp_mem_writeb,
386 esp_mem_writeb,
387 esp_mem_writeb,
388};
389
390static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr)
391{
392 ESPState *s = opaque;
393 uint32_t saddr;
394
395 saddr = (addr & ESPDMA_MAXADDR) >> 2;
4f6200f0
FB
396 DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]);
397
6f7e9aec
FB
398 return s->espdmaregs[saddr];
399}
400
401static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
402{
403 ESPState *s = opaque;
404 uint32_t saddr;
405
406 saddr = (addr & ESPDMA_MAXADDR) >> 2;
4f6200f0 407 DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val);
2f275b8f
FB
408 switch (saddr) {
409 case 0:
9e61bde5 410 if (!(val & DMA_INTREN))
2f275b8f 411 pic_set_irq(s->irq, 0);
4f6200f0
FB
412 if (val & 0x80) {
413 esp_reset(s);
414 } else if (val & 0x40) {
415 val &= ~0x40;
416 } else if (val == 0)
417 val = 0x40;
418 val &= 0x0fffffff;
419 val |= DMA_VER;
2f275b8f 420 break;
4f6200f0 421 case 1:
e4d165c2 422 s->espdmaregs[0] |= DMA_LOADED;
4f6200f0 423 break;
2f275b8f
FB
424 default:
425 break;
426 }
6f7e9aec
FB
427 s->espdmaregs[saddr] = val;
428}
429
430static CPUReadMemoryFunc *espdma_mem_read[3] = {
431 espdma_mem_readl,
432 espdma_mem_readl,
433 espdma_mem_readl,
434};
435
436static CPUWriteMemoryFunc *espdma_mem_write[3] = {
437 espdma_mem_writel,
438 espdma_mem_writel,
439 espdma_mem_writel,
440};
441
442static void esp_save(QEMUFile *f, void *opaque)
443{
444 ESPState *s = opaque;
2f275b8f
FB
445 unsigned int i;
446
447 qemu_put_buffer(f, s->rregs, ESP_MAXREG);
448 qemu_put_buffer(f, s->wregs, ESP_MAXREG);
449 qemu_put_be32s(f, &s->irq);
450 for (i = 0; i < ESPDMA_REGS; i++)
451 qemu_put_be32s(f, &s->espdmaregs[i]);
4f6200f0
FB
452 qemu_put_be32s(f, &s->ti_size);
453 qemu_put_be32s(f, &s->ti_rptr);
454 qemu_put_be32s(f, &s->ti_wptr);
4f6200f0
FB
455 qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
456 qemu_put_be32s(f, &s->dma);
6f7e9aec
FB
457}
458
459static int esp_load(QEMUFile *f, void *opaque, int version_id)
460{
461 ESPState *s = opaque;
2f275b8f 462 unsigned int i;
6f7e9aec
FB
463
464 if (version_id != 1)
465 return -EINVAL;
466
2f275b8f
FB
467 qemu_get_buffer(f, s->rregs, ESP_MAXREG);
468 qemu_get_buffer(f, s->wregs, ESP_MAXREG);
469 qemu_get_be32s(f, &s->irq);
470 for (i = 0; i < ESPDMA_REGS; i++)
471 qemu_get_be32s(f, &s->espdmaregs[i]);
4f6200f0
FB
472 qemu_get_be32s(f, &s->ti_size);
473 qemu_get_be32s(f, &s->ti_rptr);
474 qemu_get_be32s(f, &s->ti_wptr);
4f6200f0
FB
475 qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
476 qemu_get_be32s(f, &s->dma);
2f275b8f 477
6f7e9aec
FB
478 return 0;
479}
480
481void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr)
482{
483 ESPState *s;
484 int esp_io_memory, espdma_io_memory;
2e5d83bb 485 int i;
6f7e9aec
FB
486
487 s = qemu_mallocz(sizeof(ESPState));
488 if (!s)
489 return;
490
491 s->bd = bd;
492 s->irq = irq;
493
494 esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
495 cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory);
496
497 espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s);
498 cpu_register_physical_memory(espdaddr, 16, espdma_io_memory);
499
500 esp_reset(s);
501
502 register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
503 qemu_register_reset(esp_reset, s);
2e5d83bb
PB
504 for (i = 0; i < MAX_DISKS; i++) {
505 if (bs_table[i]) {
506 s->scsi_dev[i] =
507 scsi_disk_init(bs_table[i], esp_command_complete, s);
508 }
509 }
6f7e9aec
FB
510}
511