1 |
/* |
/* |
2 |
* Copyright (C) 2004-2005 Anders Gavare. All rights reserved. |
* Copyright (C) 2004-2006 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.56 2005/11/23 23:31:36 debug Exp $ |
* $Id: dev_wdc.c,v 1.68 2006/08/14 17:45:47 debug Exp $ |
29 |
* |
* |
30 |
* Standard "wdc" IDE controller. |
* Standard "wdc" IDE controller. |
31 |
*/ |
*/ |
66 |
|
|
67 |
struct wdc_data { |
struct wdc_data { |
68 |
int irq_nr; |
int irq_nr; |
69 |
|
int addr_mult; |
70 |
int base_drive; |
int base_drive; |
71 |
int data_debug; |
int data_debug; |
72 |
|
int io_enabled; |
73 |
|
|
74 |
/* Cached values: */ |
/* Cached values: */ |
75 |
int cyls[2]; |
int cyls[2]; |
103 |
int atapi_phase; |
int atapi_phase; |
104 |
struct scsi_transfer *atapi_st; |
struct scsi_transfer *atapi_st; |
105 |
int atapi_len; |
int atapi_len; |
106 |
int atapi_received; |
size_t atapi_received; |
107 |
|
|
108 |
unsigned char identify_struct[512]; |
unsigned char identify_struct[512]; |
109 |
}; |
}; |
131 |
|
|
132 |
|
|
133 |
/* |
/* |
134 |
|
* wdc_set_io_enabled(): |
135 |
|
* |
136 |
|
* Set io_enabled to zero to disable the I/O registers temporarily (e.g. |
137 |
|
* used by PCI code in NetBSD to detect whether multiple controllers collide |
138 |
|
* in I/O space). |
139 |
|
* |
140 |
|
* Return value is old contents of the io_enabled variable. |
141 |
|
*/ |
142 |
|
int wdc_set_io_enabled(struct wdc_data *d, int io_enabled) |
143 |
|
{ |
144 |
|
int old = d->io_enabled; |
145 |
|
d->io_enabled = io_enabled; |
146 |
|
return old; |
147 |
|
} |
148 |
|
|
149 |
|
|
150 |
|
/* |
151 |
* wdc_addtoinbuf(): |
* wdc_addtoinbuf(): |
152 |
* |
* |
153 |
* Write to the inbuf at its head, read at its tail. |
* Write to the inbuf at its head, read at its tail. |
230 |
/* 27-46: Model number */ |
/* 27-46: Model number */ |
231 |
if (diskimage_getname(cpu->machine, d->drive + d->base_drive, |
if (diskimage_getname(cpu->machine, d->drive + d->base_drive, |
232 |
DISKIMAGE_IDE, namebuf, sizeof(namebuf))) { |
DISKIMAGE_IDE, namebuf, sizeof(namebuf))) { |
233 |
int i; |
size_t i; |
234 |
for (i=0; i<sizeof(namebuf); i++) |
for (i=0; i<sizeof(namebuf); i++) |
235 |
if (namebuf[i] == 0) { |
if (namebuf[i] == 0) { |
236 |
for (; i<sizeof(namebuf); i++) |
for (; i<sizeof(namebuf); i++) |
387 |
/* |
/* |
388 |
* dev_wdc_altstatus_access(): |
* dev_wdc_altstatus_access(): |
389 |
*/ |
*/ |
390 |
int dev_wdc_altstatus_access(struct cpu *cpu, struct memory *mem, |
DEVICE_ACCESS(wdc_altstatus) |
|
uint64_t relative_addr, unsigned char *data, size_t len, |
|
|
int writeflag, void *extra) |
|
391 |
{ |
{ |
392 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
393 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
419 |
*/ |
*/ |
420 |
void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata) |
void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata) |
421 |
{ |
{ |
422 |
int i; |
size_t i; |
423 |
|
|
424 |
d->cur_command = idata; |
d->cur_command = idata; |
425 |
d->atapi_cmd_in_progress = 0; |
d->atapi_cmd_in_progress = 0; |
535 |
d->atapi_phase = PHASE_CMDOUT; |
d->atapi_phase = PHASE_CMDOUT; |
536 |
break; |
break; |
537 |
|
|
538 |
|
case WDCC_DIAGNOSE: |
539 |
|
debug("[ wdc: WDCC_DIAGNOSE drive %i: TODO ]\n", d->drive); |
540 |
|
/* TODO: interrupt here? */ |
541 |
|
d->delayed_interrupt = INT_DELAY; |
542 |
|
d->error = 1; /* No error? */ |
543 |
|
break; |
544 |
|
|
545 |
/* Unsupported commands, without warning: */ |
/* Unsupported commands, without warning: */ |
546 |
case WDCC_SEC_SET_PASSWORD: |
case WDCC_SEC_SET_PASSWORD: |
547 |
case WDCC_SEC_UNLOCK: |
case WDCC_SEC_UNLOCK: |
565 |
/* |
/* |
566 |
* dev_wdc_access(): |
* dev_wdc_access(): |
567 |
*/ |
*/ |
568 |
int dev_wdc_access(struct cpu *cpu, struct memory *mem, |
DEVICE_ACCESS(wdc) |
|
uint64_t relative_addr, unsigned char *data, size_t len, |
|
|
int writeflag, void *extra) |
|
569 |
{ |
{ |
570 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
571 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
572 |
int i; |
int i; |
573 |
|
|
574 |
|
relative_addr /= d->addr_mult; |
575 |
|
|
576 |
|
if (!d->io_enabled) |
577 |
|
goto ret; |
578 |
|
|
579 |
if (writeflag == MEM_WRITE) { |
if (writeflag == MEM_WRITE) { |
580 |
if (relative_addr == wd_data) |
if (relative_addr == wd_data) |
581 |
idata = memory_readmax64(cpu, data, len); |
idata = memory_readmax64(cpu, data, len); |
590 |
|
|
591 |
case wd_data: /* 0: data */ |
case wd_data: /* 0: data */ |
592 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
593 |
odata = 0; |
odata = wdc_get_inbuf(d); |
|
|
|
|
odata += wdc_get_inbuf(d); |
|
594 |
|
|
595 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) { |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) { |
596 |
if (len >= 2) |
if (len >= 2) |
609 |
} |
} |
610 |
|
|
611 |
if (d->data_debug) { |
if (d->data_debug) { |
612 |
char *s = "0x%04llx ]\n"; |
char *s = "0x%04"PRIx64" ]\n"; |
613 |
if (len == 1) |
if (len == 1) |
614 |
s = "0x%02llx ]\n"; |
s = "0x%02"PRIx64" ]\n"; |
615 |
if (len == 4) |
if (len == 4) |
616 |
s = "0x%08llx ]\n"; |
s = "0x%08"PRIx64" ]\n"; |
617 |
if (len == 8) |
if (len == 8) |
618 |
s = "0x%016llx ]\n"; |
s = "0x%016"PRIx64" ]\n"; |
619 |
debug("[ wdc: read from DATA: "); |
debug("[ wdc: read from DATA: "); |
620 |
debug(s, (long long)odata); |
debug(s, (uint64_t) odata); |
621 |
} |
} |
622 |
|
|
623 |
if (d->atapi_cmd_in_progress) { |
if (d->atapi_cmd_in_progress) { |
650 |
} else { |
} else { |
651 |
int inbuf_len; |
int inbuf_len; |
652 |
if (d->data_debug) { |
if (d->data_debug) { |
653 |
char *s = "0x%04llx ]\n"; |
char *s = "0x%04"PRIx64" ]\n"; |
654 |
if (len == 1) |
if (len == 1) |
655 |
s = "0x%02llx ]\n"; |
s = "0x%02"PRIx64" ]\n"; |
656 |
if (len == 4) |
if (len == 4) |
657 |
s = "0x%08llx ]\n"; |
s = "0x%08"PRIx64" ]\n"; |
658 |
if (len == 8) |
if (len == 8) |
659 |
s = "0x%016llx ]\n"; |
s = "0x%016"PRIx64" ]\n"; |
660 |
debug("[ wdc: write to DATA: "); |
debug("[ wdc: write to DATA: "); |
661 |
debug(s, (long long)idata); |
debug(s, (uint64_t) idata); |
662 |
} |
} |
663 |
if (!d->write_in_progress && |
if (!d->write_in_progress && |
664 |
!d->atapi_cmd_in_progress) { |
!d->atapi_cmd_in_progress) { |
770 |
inbuf_len % 512 == 0) ) { |
inbuf_len % 512 == 0) ) { |
771 |
int count = (d->write_in_progress == |
int count = (d->write_in_progress == |
772 |
WDCC_WRITEMULTI)? d->write_count : 1; |
WDCC_WRITEMULTI)? d->write_count : 1; |
773 |
unsigned char buf[512 * count]; |
unsigned char *buf = malloc(512 * count); |
774 |
unsigned char *b = buf; |
unsigned char *b = buf; |
775 |
|
|
776 |
|
if (buf == NULL) { |
777 |
|
fprintf(stderr, "out of memory\n"); |
778 |
|
exit(1); |
779 |
|
} |
780 |
|
|
781 |
if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) { |
if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) { |
782 |
b = d->inbuf + d->inbuf_tail; |
b = d->inbuf + d->inbuf_tail; |
783 |
d->inbuf_tail = (d->inbuf_tail + 512 |
d->inbuf_tail = (d->inbuf_tail + 512 |
798 |
|
|
799 |
if (d->write_count == 0) |
if (d->write_count == 0) |
800 |
d->write_in_progress = 0; |
d->write_in_progress = 0; |
801 |
|
|
802 |
|
free(buf); |
803 |
} |
} |
804 |
} |
} |
805 |
break; |
break; |
807 |
case wd_error: /* 1: error (r), precomp (w) */ |
case wd_error: /* 1: error (r), precomp (w) */ |
808 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
809 |
odata = d->error; |
odata = d->error; |
810 |
debug("[ wdc: read from ERROR: 0x%02x ]\n", |
debug("[ wdc: read from ERROR: 0x%02x ]\n", (int)odata); |
|
(int)odata); |
|
811 |
/* TODO: is the error value cleared on read? */ |
/* TODO: is the error value cleared on read? */ |
812 |
d->error = 0; |
d->error = 0; |
813 |
} else { |
} else { |
921 |
(int)relative_addr, (int)idata); |
(int)relative_addr, (int)idata); |
922 |
} |
} |
923 |
|
|
924 |
|
|
925 |
if (cpu->machine->machine_type != MACHINE_HPCMIPS && |
if (cpu->machine->machine_type != MACHINE_HPCMIPS && |
926 |
cpu->machine->machine_type != MACHINE_EVBMIPS && |
cpu->machine->machine_type != MACHINE_EVBMIPS && |
927 |
|
cpu->machine->machine_type != MACHINE_ALGOR && |
928 |
cpu->machine->machine_type != MACHINE_BEBOX) |
cpu->machine->machine_type != MACHINE_BEBOX) |
929 |
dev_wdc_tick(cpu, extra); |
dev_wdc_tick(cpu, extra); |
930 |
|
|
931 |
|
ret: |
932 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
933 |
if (relative_addr == wd_data) |
if (relative_addr == wd_data) |
934 |
memory_writemax64(cpu, data, len, odata); |
memory_writemax64(cpu, data, len, odata); |
940 |
} |
} |
941 |
|
|
942 |
|
|
943 |
/* |
DEVINIT(wdc) |
|
* devinit_wdc(): |
|
|
*/ |
|
|
int devinit_wdc(struct devinit *devinit) |
|
944 |
{ |
{ |
945 |
struct wdc_data *d; |
struct wdc_data *d; |
946 |
uint64_t alt_status_addr; |
uint64_t alt_status_addr; |
952 |
exit(1); |
exit(1); |
953 |
} |
} |
954 |
memset(d, 0, sizeof(struct wdc_data)); |
memset(d, 0, sizeof(struct wdc_data)); |
955 |
d->irq_nr = devinit->irq_nr; |
d->irq_nr = devinit->irq_nr; |
956 |
|
d->addr_mult = devinit->addr_mult; |
957 |
d->data_debug = 1; |
d->data_debug = 1; |
958 |
|
d->io_enabled = 1; |
959 |
|
|
960 |
d->inbuf = zeroed_alloc(WDC_INBUF_SIZE); |
d->inbuf = zeroed_alloc(WDC_INBUF_SIZE); |
961 |
|
|
966 |
|
|
967 |
alt_status_addr = devinit->addr + 0x206; |
alt_status_addr = devinit->addr + 0x206; |
968 |
|
|
969 |
/* Special hack for pcic/hpcmips: TODO: Fix */ |
/* Special hacks for individual machines: */ |
970 |
if (devinit->addr == 0x14000180) |
switch (devinit->machine->machine_type) { |
971 |
alt_status_addr = 0x14000386; |
case MACHINE_MACPPC: |
972 |
|
alt_status_addr = devinit->addr + 0x160; |
973 |
|
break; |
974 |
|
case MACHINE_HPCMIPS: |
975 |
|
/* TODO: Fix */ |
976 |
|
if (devinit->addr == 0x14000180) |
977 |
|
alt_status_addr = 0x14000386; |
978 |
|
break; |
979 |
|
case MACHINE_IQ80321: |
980 |
|
alt_status_addr = devinit->addr + 0x402; |
981 |
|
break; |
982 |
|
} |
983 |
|
|
984 |
/* Get disk geometries: */ |
/* Get disk geometries: */ |
985 |
for (i=0; i<2; i++) |
for (i=0; i<2; i++) |
992 |
memory_device_register(devinit->machine->memory, "wdc_altstatus", |
memory_device_register(devinit->machine->memory, "wdc_altstatus", |
993 |
alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL); |
alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL); |
994 |
memory_device_register(devinit->machine->memory, devinit->name, |
memory_device_register(devinit->machine->memory, devinit->name, |
995 |
devinit->addr, DEV_WDC_LENGTH, dev_wdc_access, d, DM_DEFAULT, |
devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access, |
996 |
NULL); |
d, DM_DEFAULT, NULL); |
997 |
|
|
998 |
if (devinit->machine->machine_type != MACHINE_HPCMIPS && |
if (devinit->machine->machine_type != MACHINE_HPCMIPS && |
999 |
devinit->machine->machine_type != MACHINE_EVBMIPS) |
devinit->machine->machine_type != MACHINE_EVBMIPS) |
1000 |
tick_shift += 1; |
tick_shift += 1; |
1001 |
|
|
1002 |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
1003 |
d, tick_shift); |
d, tick_shift, 0.0); |
1004 |
|
|
1005 |
|
devinit->return_ptr = d; |
1006 |
|
|
1007 |
return 1; |
return 1; |
1008 |
} |
} |