1 |
/* |
/* |
2 |
* Copyright (C) 2004-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2004-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_wdc.c,v 1.69 2006/08/30 17:14:25 debug Exp $ |
* $Id: dev_wdc.c,v 1.74 2007/02/16 19:57:56 debug Exp $ |
29 |
* |
* |
30 |
* Standard "wdc" IDE controller. |
* Standard "wdc" IDE controller. |
31 |
*/ |
*/ |
48 |
#define WDC_MAX_SECTORS 512 |
#define WDC_MAX_SECTORS 512 |
49 |
#define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1)) |
#define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1)) |
50 |
|
|
|
/* |
|
|
* INT_DELAY: This is an old hack which only exists because (some versions of) |
|
|
* NetBSD for hpcmips have interrupt problems. These problems are probably not |
|
|
* specific to GXemul, but are also triggered on real hardware. |
|
|
* |
|
|
* See the following URL for more info: |
|
|
* http://mail-index.netbsd.org/port-hpcmips/2004/12/30/0003.html |
|
|
* |
|
|
* NetBSD/malta also bugs out if wdc interrupts come too quickly. Hm. |
|
|
*/ |
|
|
#define INT_DELAY 1 |
|
|
|
|
51 |
extern int quiet_mode; |
extern int quiet_mode; |
52 |
|
|
53 |
/* #define debug fatal */ |
/* #define debug fatal */ |
54 |
|
|
55 |
struct wdc_data { |
struct wdc_data { |
56 |
int irq_nr; |
struct interrupt irq; |
57 |
int addr_mult; |
int addr_mult; |
58 |
int base_drive; |
int base_drive; |
59 |
int data_debug; |
int data_debug; |
68 |
int inbuf_head; |
int inbuf_head; |
69 |
int inbuf_tail; |
int inbuf_tail; |
70 |
|
|
71 |
int delayed_interrupt; |
int int_assert; |
|
int int_asserted; |
|
72 |
|
|
73 |
int write_in_progress; |
int write_in_progress; |
74 |
int write_count; |
int write_count; |
99 |
#define COMMAND_RESET 0x100 |
#define COMMAND_RESET 0x100 |
100 |
|
|
101 |
|
|
102 |
/* |
DEVICE_TICK(wdc) |
|
* dev_wdc_tick(): |
|
|
*/ |
|
|
void dev_wdc_tick(struct cpu *cpu, void *extra) |
|
103 |
{ |
{ |
104 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
|
int old_di = d->delayed_interrupt; |
|
|
|
|
|
if (d->delayed_interrupt) |
|
|
d->delayed_interrupt --; |
|
105 |
|
|
106 |
if (old_di == 1 || d->int_asserted) { |
if (d->int_assert) |
107 |
cpu_interrupt(cpu, d->irq_nr); |
INTERRUPT_ASSERT(d->irq); |
|
d->int_asserted = 1; |
|
|
} |
|
108 |
} |
} |
109 |
|
|
110 |
|
|
307 |
count -= to_read; |
count -= to_read; |
308 |
} |
} |
309 |
|
|
310 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
311 |
} |
} |
312 |
|
|
313 |
|
|
362 |
} |
} |
363 |
|
|
364 |
|
|
|
/* |
|
|
* dev_wdc_altstatus_access(): |
|
|
*/ |
|
365 |
DEVICE_ACCESS(wdc_altstatus) |
DEVICE_ACCESS(wdc_altstatus) |
366 |
{ |
{ |
367 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
412 |
debug("[ wdc: command 0x%02x drive %i, but no disk image ]\n", |
debug("[ wdc: command 0x%02x drive %i, but no disk image ]\n", |
413 |
d->cur_command, d->drive + d->base_drive); |
d->cur_command, d->drive + d->base_drive); |
414 |
d->error |= WDCE_ABRT; |
d->error |= WDCE_ABRT; |
415 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
416 |
return; |
return; |
417 |
} |
} |
418 |
if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive, |
if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive, |
420 |
debug("[ wdc: IDENTIFY drive %i, but it is an ATAPI " |
debug("[ wdc: IDENTIFY drive %i, but it is an ATAPI " |
421 |
"drive ]\n", d->drive + d->base_drive); |
"drive ]\n", d->drive + d->base_drive); |
422 |
d->error |= WDCE_ABRT; |
d->error |= WDCE_ABRT; |
423 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
424 |
return; |
return; |
425 |
} |
} |
426 |
|
|
448 |
case WDCC_IDP: /* Initialize drive parameters */ |
case WDCC_IDP: /* Initialize drive parameters */ |
449 |
debug("[ wdc: IDP drive %i (TODO) ]\n", d->drive); |
debug("[ wdc: IDP drive %i (TODO) ]\n", d->drive); |
450 |
/* TODO */ |
/* TODO */ |
451 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
452 |
break; |
break; |
453 |
|
|
454 |
case SET_FEATURES: |
case SET_FEATURES: |
463 |
default:d->error |= WDCE_ABRT; |
default:d->error |= WDCE_ABRT; |
464 |
} |
} |
465 |
/* TODO: always interrupt? */ |
/* TODO: always interrupt? */ |
466 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
467 |
break; |
break; |
468 |
|
|
469 |
case WDCC_RECAL: |
case WDCC_RECAL: |
470 |
debug("[ wdc: RECAL drive %i ]\n", d->drive); |
debug("[ wdc: RECAL drive %i ]\n", d->drive); |
471 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
472 |
break; |
break; |
473 |
|
|
474 |
case WDCC_IDENTIFY: |
case WDCC_IDENTIFY: |
481 |
wdc_addtoinbuf(d, d->identify_struct[i+1]); |
wdc_addtoinbuf(d, d->identify_struct[i+1]); |
482 |
wdc_addtoinbuf(d, d->identify_struct[i+0]); |
wdc_addtoinbuf(d, d->identify_struct[i+0]); |
483 |
} |
} |
484 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
485 |
break; |
break; |
486 |
|
|
487 |
case WDCC_IDLE_IMMED: |
case WDCC_IDLE_IMMED: |
488 |
debug("[ wdc: IDLE_IMMED drive %i ]\n", d->drive); |
debug("[ wdc: IDLE_IMMED drive %i ]\n", d->drive); |
489 |
/* TODO: interrupt here? */ |
/* TODO: interrupt here? */ |
490 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
491 |
break; |
break; |
492 |
|
|
493 |
case WDCC_SETMULTI: |
case WDCC_SETMULTI: |
494 |
debug("[ wdc: SETMULTI drive %i ]\n", d->drive); |
debug("[ wdc: SETMULTI drive %i ]\n", d->drive); |
495 |
/* TODO: interrupt here? */ |
/* TODO: interrupt here? */ |
496 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
497 |
break; |
break; |
498 |
|
|
499 |
case ATAPI_SOFT_RESET: |
case ATAPI_SOFT_RESET: |
500 |
debug("[ wdc: ATAPI_SOFT_RESET drive %i ]\n", d->drive); |
debug("[ wdc: ATAPI_SOFT_RESET drive %i ]\n", d->drive); |
501 |
/* TODO: interrupt here? */ |
/* TODO: interrupt here? */ |
502 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
503 |
break; |
break; |
504 |
|
|
505 |
case ATAPI_PKT_CMD: |
case ATAPI_PKT_CMD: |
506 |
debug("[ wdc: ATAPI_PKT_CMD drive %i ]\n", d->drive); |
debug("[ wdc: ATAPI_PKT_CMD drive %i ]\n", d->drive); |
507 |
/* TODO: interrupt here? */ |
/* TODO: interrupt here? */ |
508 |
/* d->delayed_interrupt = INT_DELAY; */ |
/* d->int_assert = 1; */ |
509 |
d->atapi_cmd_in_progress = 1; |
d->atapi_cmd_in_progress = 1; |
510 |
d->atapi_phase = PHASE_CMDOUT; |
d->atapi_phase = PHASE_CMDOUT; |
511 |
break; |
break; |
513 |
case WDCC_DIAGNOSE: |
case WDCC_DIAGNOSE: |
514 |
debug("[ wdc: WDCC_DIAGNOSE drive %i: TODO ]\n", d->drive); |
debug("[ wdc: WDCC_DIAGNOSE drive %i: TODO ]\n", d->drive); |
515 |
/* TODO: interrupt here? */ |
/* TODO: interrupt here? */ |
516 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
517 |
d->error = 1; /* No error? */ |
d->error = 1; /* No error? */ |
518 |
break; |
break; |
519 |
|
|
537 |
} |
} |
538 |
|
|
539 |
|
|
|
/* |
|
|
* dev_wdc_access(): |
|
|
*/ |
|
540 |
DEVICE_ACCESS(wdc) |
DEVICE_ACCESS(wdc) |
541 |
{ |
{ |
542 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
607 |
} else |
} else |
608 |
d->atapi_phase = |
d->atapi_phase = |
609 |
PHASE_COMPLETED; |
PHASE_COMPLETED; |
610 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
611 |
} |
} |
612 |
} else { |
} else { |
613 |
#if 0 |
#if 0 |
617 |
((d->inbuf_tail - d->inbuf_head) % 512) |
((d->inbuf_tail - d->inbuf_head) % 512) |
618 |
== 0) |
== 0) |
619 |
#endif |
#endif |
620 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
621 |
} |
} |
622 |
} else { |
} else { |
623 |
int inbuf_len; |
int inbuf_len; |
732 |
exit(1); |
exit(1); |
733 |
} |
} |
734 |
|
|
735 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
736 |
} |
} |
737 |
|
|
738 |
if (( d->write_in_progress == WDCC_WRITEMULTI && |
if (( d->write_in_progress == WDCC_WRITEMULTI && |
766 |
d->write_count -= count; |
d->write_count -= count; |
767 |
d->write_offset += 512 * count; |
d->write_offset += 512 * count; |
768 |
|
|
769 |
d->delayed_interrupt = INT_DELAY; |
d->int_assert = 1; |
770 |
|
|
771 |
if (d->write_count == 0) |
if (d->write_count == 0) |
772 |
d->write_in_progress = 0; |
d->write_in_progress = 0; |
875 |
if (!quiet_mode) |
if (!quiet_mode) |
876 |
debug("[ wdc: read from STATUS: 0x%02x ]\n", |
debug("[ wdc: read from STATUS: 0x%02x ]\n", |
877 |
(int)odata); |
(int)odata); |
878 |
cpu_interrupt_ack(cpu, d->irq_nr); |
INTERRUPT_DEASSERT(d->irq); |
879 |
d->int_asserted = 0; |
d->int_assert = 0; |
|
d->delayed_interrupt = 0; |
|
880 |
} else { |
} else { |
881 |
debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata); |
debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata); |
882 |
wdc_command(cpu, d, idata); |
wdc_command(cpu, d, idata); |
892 |
(int)relative_addr, (int)idata); |
(int)relative_addr, (int)idata); |
893 |
} |
} |
894 |
|
|
895 |
|
/* Assert interrupt, if necessary: */ |
896 |
if (cpu->machine->machine_type != MACHINE_HPCMIPS && |
dev_wdc_tick(cpu, extra); |
|
cpu->machine->machine_type != MACHINE_EVBMIPS && |
|
|
cpu->machine->machine_type != MACHINE_ALGOR && |
|
|
cpu->machine->machine_type != MACHINE_BEBOX) |
|
|
dev_wdc_tick(cpu, extra); |
|
897 |
|
|
898 |
ret: |
ret: |
899 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
919 |
exit(1); |
exit(1); |
920 |
} |
} |
921 |
memset(d, 0, sizeof(struct wdc_data)); |
memset(d, 0, sizeof(struct wdc_data)); |
922 |
d->irq_nr = devinit->irq_nr; |
|
923 |
|
INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); |
924 |
d->addr_mult = devinit->addr_mult; |
d->addr_mult = devinit->addr_mult; |
925 |
d->data_debug = 1; |
d->data_debug = 1; |
926 |
d->io_enabled = 1; |
d->io_enabled = 1; |
963 |
devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access, |
devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access, |
964 |
d, DM_DEFAULT, NULL); |
d, DM_DEFAULT, NULL); |
965 |
|
|
|
if (devinit->machine->machine_type != MACHINE_HPCMIPS && |
|
|
devinit->machine->machine_type != MACHINE_EVBMIPS) |
|
|
tick_shift += 1; |
|
|
|
|
966 |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
967 |
d, tick_shift, 0.0); |
d, tick_shift, 0.0); |
968 |
|
|