23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
|
* |
|
26 |
* |
* |
27 |
* $Id: dev_wdc.c,v 1.37 2005/05/27 07:29:25 debug Exp $ |
* |
28 |
* |
* $Id: dev_wdc.c,v 1.39 2005/09/27 23:55:44 debug Exp $ |
29 |
* Standard IDE controller. |
* |
30 |
|
* Standard "wdc" IDE controller. |
31 |
*/ |
*/ |
32 |
|
|
33 |
#include <stdio.h> |
#include <stdio.h> |
35 |
#include <string.h> |
#include <string.h> |
36 |
|
|
37 |
#include "console.h" |
#include "console.h" |
|
#include "cop0.h" |
|
38 |
#include "cpu.h" |
#include "cpu.h" |
39 |
#include "devices.h" |
#include "device.h" |
40 |
#include "diskimage.h" |
#include "diskimage.h" |
41 |
#include "machine.h" |
#include "machine.h" |
42 |
#include "memory.h" |
#include "memory.h" |
44 |
|
|
45 |
#include "wdcreg.h" |
#include "wdcreg.h" |
46 |
|
|
47 |
|
#define DEV_WDC_LENGTH 8 |
48 |
#define WDC_TICK_SHIFT 14 |
#define WDC_TICK_SHIFT 14 |
49 |
#define WDC_INBUF_SIZE (512*257) |
#define WDC_INBUF_SIZE (512*257) |
50 |
|
|
51 |
/* INT_DELAY=2 to be safe, 1 is faster but maybe buggy. */ |
/* |
52 |
|
* INT_DELAY=2 to be safe, 1 is faster but maybe buggy. 0 is fastest. |
53 |
|
* |
54 |
|
* The only reason for this delay to exist is because (some versions of) |
55 |
|
* NetBSD for hpcmips have interrupt problems. These problems are not only |
56 |
|
* triggered inside the emulator, but also on real hardware. Using the |
57 |
|
* delay is an ugly (but working) work-around. |
58 |
|
*/ |
59 |
#define INT_DELAY 1 |
#define INT_DELAY 1 |
60 |
|
|
61 |
extern int quiet_mode; |
extern int quiet_mode; |
62 |
|
|
63 |
/* #define debug fatal */ |
/* #define debug fatal */ |
|
/* #define DATA_DEBUG */ |
|
64 |
|
|
65 |
struct wdc_data { |
struct wdc_data { |
66 |
int irq_nr; |
int irq_nr; |
67 |
int base_drive; |
int base_drive; |
68 |
|
int data_debug; |
69 |
|
|
70 |
int delayed_interrupt; |
/* Cached values: */ |
71 |
|
int cyls[2]; |
72 |
|
int heads[2]; |
73 |
|
int sectors_per_track[2]; |
74 |
|
|
75 |
unsigned char identify_struct[512]; |
unsigned char identify_struct[512]; |
76 |
|
|
78 |
int inbuf_head; |
int inbuf_head; |
79 |
int inbuf_tail; |
int inbuf_tail; |
80 |
|
|
81 |
|
int delayed_interrupt; |
82 |
int write_in_progress; |
int write_in_progress; |
83 |
int write_count; |
int write_count; |
84 |
int64_t write_offset; |
int64_t write_offset; |
154 |
static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d) |
static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d) |
155 |
{ |
{ |
156 |
uint64_t total_size; |
uint64_t total_size; |
157 |
int cyls, heads, sectors_per_track; |
char namebuf[40]; |
158 |
|
|
159 |
total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive, |
total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive, |
160 |
DISKIMAGE_IDE); |
DISKIMAGE_IDE); |
161 |
|
|
|
diskimage_getchs(cpu->machine, d->drive + d->base_drive, |
|
|
DISKIMAGE_IDE, &cyls, &heads, §ors_per_track); |
|
|
|
|
162 |
memset(d->identify_struct, 0, sizeof(d->identify_struct)); |
memset(d->identify_struct, 0, sizeof(d->identify_struct)); |
163 |
|
|
164 |
/* Offsets are in 16-bit WORDS! High byte, then low. */ |
/* Offsets are in 16-bit WORDS! High byte, then low. */ |
168 |
d->identify_struct[2 * 0 + 1] = 1 << 6; |
d->identify_struct[2 * 0 + 1] = 1 << 6; |
169 |
|
|
170 |
/* 1: nr of cylinders */ |
/* 1: nr of cylinders */ |
171 |
d->identify_struct[2 * 1 + 0] = (cyls >> 8); |
d->identify_struct[2 * 1 + 0] = (d->cyls[d->drive] >> 8); |
172 |
d->identify_struct[2 * 1 + 1] = cyls & 255; |
d->identify_struct[2 * 1 + 1] = d->cyls[d->drive] & 255; |
173 |
|
|
174 |
/* 3: nr of heads */ |
/* 3: nr of heads */ |
175 |
d->identify_struct[2 * 3 + 0] = heads >> 8; |
d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8; |
176 |
d->identify_struct[2 * 3 + 1] = heads; |
d->identify_struct[2 * 3 + 1] = d->heads[d->drive]; |
177 |
|
|
178 |
/* 6: sectors per track */ |
/* 6: sectors per track */ |
179 |
d->identify_struct[2 * 6 + 0] = sectors_per_track >> 8; |
d->identify_struct[2 * 6 + 0] = d->sectors_per_track[d->drive] >> 8; |
180 |
d->identify_struct[2 * 6 + 1] = sectors_per_track; |
d->identify_struct[2 * 6 + 1] = d->sectors_per_track[d->drive]; |
181 |
|
|
182 |
/* 10-19: Serial number */ |
/* 10-19: Serial number */ |
183 |
memcpy(&d->identify_struct[2 * 10], "S/N 1234-5678 ", 20); |
memcpy(&d->identify_struct[2 * 10], "S/N 1234-5678 ", 20); |
186 |
memcpy(&d->identify_struct[2 * 23], "VER 1.0 ", 8); |
memcpy(&d->identify_struct[2 * 23], "VER 1.0 ", 8); |
187 |
|
|
188 |
/* 27-46: Model number */ |
/* 27-46: Model number */ |
189 |
memcpy(&d->identify_struct[2 * 27], |
if (diskimage_getname(cpu->machine, d->drive + d->base_drive, |
190 |
"Fake GXemul IDE disk ", 40); |
DISKIMAGE_IDE, namebuf, sizeof(namebuf))) { |
191 |
/* TODO: Use the diskimage's filename instead? */ |
int i; |
192 |
|
for (i=0; i<sizeof(namebuf); i++) |
193 |
|
if (namebuf[i] == 0) { |
194 |
|
for (; i<sizeof(namebuf); i++) |
195 |
|
namebuf[i] = ' '; |
196 |
|
break; |
197 |
|
} |
198 |
|
memcpy(&d->identify_struct[2 * 27], namebuf, 40); |
199 |
|
} else |
200 |
|
memcpy(&d->identify_struct[2 * 27], |
201 |
|
"Fake GXemul IDE disk ", 40); |
202 |
|
|
203 |
/* 47: max sectors per multitransfer */ |
/* 47: max sectors per multitransfer */ |
204 |
d->identify_struct[2 * 47 + 0] = 0x80; |
d->identify_struct[2 * 47 + 0] = 0x80; |
220 |
|
|
221 |
|
|
222 |
/* |
/* |
223 |
|
* wdc__read(): |
224 |
|
*/ |
225 |
|
void wdc__read(struct cpu *cpu, struct wdc_data *d) |
226 |
|
{ |
227 |
|
unsigned char buf[512]; |
228 |
|
int i, cyl = d->cyl_hi * 256+ d->cyl_lo; |
229 |
|
int count = d->seccnt? d->seccnt : 256; |
230 |
|
uint64_t offset = 512 * (d->sector - 1 |
231 |
|
+ d->head * d->sectors_per_track[d->drive] + |
232 |
|
d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl); |
233 |
|
|
234 |
|
#if 0 |
235 |
|
/* LBA: */ |
236 |
|
if (d->lba) |
237 |
|
offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8) |
238 |
|
+ d->sector); |
239 |
|
printf("WDC read from offset %lli\n", (long long)offset); |
240 |
|
#endif |
241 |
|
|
242 |
|
while (count > 0) { |
243 |
|
diskimage_access(cpu->machine, d->drive + d->base_drive, |
244 |
|
DISKIMAGE_IDE, 0, offset, buf, 512); |
245 |
|
/* TODO: result code */ |
246 |
|
for (i=0; i<512; i++) |
247 |
|
wdc_addtoinbuf(d, buf[i]); |
248 |
|
offset += 512; |
249 |
|
count --; |
250 |
|
} |
251 |
|
|
252 |
|
d->delayed_interrupt = INT_DELAY; |
253 |
|
} |
254 |
|
|
255 |
|
|
256 |
|
/* |
257 |
|
* wdc__write(): |
258 |
|
*/ |
259 |
|
void wdc__write(struct cpu *cpu, struct wdc_data *d) |
260 |
|
{ |
261 |
|
int cyl = d->cyl_hi * 256+ d->cyl_lo; |
262 |
|
int count = d->seccnt? d->seccnt : 256; |
263 |
|
uint64_t offset = 512 * (d->sector - 1 |
264 |
|
+ d->head * d->sectors_per_track[d->drive] + |
265 |
|
d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl); |
266 |
|
#if 0 |
267 |
|
/* LBA: */ |
268 |
|
if (d->lba) |
269 |
|
offset = 512 * (((d->head & 0xf) << 24) + |
270 |
|
(cyl << 8) + d->sector); |
271 |
|
printf("WDC write to offset %lli\n", (long long)offset); |
272 |
|
#endif |
273 |
|
|
274 |
|
d->write_in_progress = 1; |
275 |
|
d->write_count = count; |
276 |
|
d->write_offset = offset; |
277 |
|
|
278 |
|
/* TODO: result code? */ |
279 |
|
} |
280 |
|
|
281 |
|
|
282 |
|
/* |
283 |
* status_byte(): |
* status_byte(): |
284 |
|
* |
285 |
|
* Return a reasonable status byte corresponding to the controller's current |
286 |
|
* state. |
287 |
*/ |
*/ |
288 |
static int status_byte(struct wdc_data *d, struct cpu *cpu) |
static int status_byte(struct wdc_data *d, struct cpu *cpu) |
289 |
{ |
{ |
290 |
int odata = 0; |
int odata = 0; |
291 |
|
|
292 |
|
/* |
293 |
|
* Modern versions of OpenBSD wants WDCS_DSC. (Thanks to Alexander |
294 |
|
* Yurchenko for noticing this.) |
295 |
|
*/ |
296 |
if (diskimage_exist(cpu->machine, d->drive + d->base_drive, |
if (diskimage_exist(cpu->machine, d->drive + d->base_drive, |
297 |
DISKIMAGE_IDE)) |
DISKIMAGE_IDE)) |
298 |
odata |= WDCS_DRDY; |
odata |= WDCS_DRDY | WDCS_DSC; |
299 |
if (d->inbuf_head != d->inbuf_tail) |
if (d->inbuf_head != d->inbuf_tail) |
300 |
odata |= WDCS_DRQ; |
odata |= WDCS_DRQ; |
301 |
if (d->write_in_progress) |
if (d->write_in_progress) |
303 |
if (d->error) |
if (d->error) |
304 |
odata |= WDCS_ERR; |
odata |= WDCS_ERR; |
305 |
|
|
|
#if 0 |
|
|
/* |
|
|
* TODO: Is this correct behaviour? |
|
|
* |
|
|
* NetBSD/cobalt seems to want it, but Linux on MobilePro does not. |
|
|
*/ |
|
|
if (!diskimage_exist(cpu->machine, d->drive + d->base_drive, |
|
|
DISKIMAGE_IDE)) |
|
|
odata = 0xff; |
|
|
#endif |
|
|
|
|
306 |
return odata; |
return odata; |
307 |
} |
} |
308 |
|
|
345 |
{ |
{ |
346 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
347 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
348 |
int i, cyls, heads, sectors_per_track; |
int i; |
349 |
|
|
350 |
idata = memory_readmax64(cpu, data, len); |
idata = memory_readmax64(cpu, data, len); |
351 |
|
|
365 |
odata += (wdc_get_inbuf(d) << 24); |
odata += (wdc_get_inbuf(d) << 24); |
366 |
} |
} |
367 |
|
|
368 |
#ifdef DATA_DEBUG |
if (d->data_debug) |
369 |
debug("[ wdc: read from DATA: 0x%04x ]\n", odata); |
debug("[ wdc: read from DATA: 0x%04x ]\n", |
370 |
#endif |
(int)odata); |
|
|
|
371 |
if (d->inbuf_tail != d->inbuf_head) |
if (d->inbuf_tail != d->inbuf_head) |
372 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
|
|
|
373 |
} else { |
} else { |
374 |
int inbuf_len; |
int inbuf_len; |
375 |
#ifdef DATA_DEBUG |
if (d->data_debug) |
376 |
debug("[ wdc: write to DATA (len=%i): 0x%08lx ]\n", |
debug("[ wdc: write to DATA (len=%i): " |
377 |
(int)len, (long)idata); |
"0x%08lx ]\n", (int)len, (long)idata); |
|
#endif |
|
378 |
if (!d->write_in_progress) { |
if (!d->write_in_progress) { |
379 |
fatal("[ wdc: write to DATA, but not " |
fatal("[ wdc: write to DATA, but not " |
380 |
"expecting any? (len=%i): 0x%08lx ]\n", |
"expecting any? (len=%i): 0x%08lx ]\n", |
401 |
|
|
402 |
#if 0 |
#if 0 |
403 |
if ((inbuf_len % (512 * d->write_count)) == 0) { |
if ((inbuf_len % (512 * d->write_count)) == 0) { |
404 |
#endif |
#else |
405 |
if ((inbuf_len % 512) == 0) { |
if ((inbuf_len % 512) == 0) { |
406 |
|
#endif |
407 |
int count = 1; /* d->write_count; */ |
int count = 1; /* d->write_count; */ |
408 |
unsigned char *buf = malloc(count * 512); |
unsigned char *buf = malloc(count * 512); |
409 |
if (buf == NULL) { |
if (buf == NULL) { |
433 |
case wd_error: /* 1: error (r), precomp (w) */ |
case wd_error: /* 1: error (r), precomp (w) */ |
434 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
435 |
odata = d->error; |
odata = d->error; |
436 |
debug("[ wdc: read from ERROR: 0x%02x ]\n", odata); |
debug("[ wdc: read from ERROR: 0x%02x ]\n", |
437 |
|
(int)odata); |
438 |
/* TODO: is the error value cleared on read? */ |
/* TODO: is the error value cleared on read? */ |
439 |
d->error = 0; |
d->error = 0; |
440 |
} else { |
} else { |
441 |
d->precomp = idata; |
d->precomp = idata; |
442 |
debug("[ wdc: write to PRECOMP: 0x%02x ]\n", idata); |
debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata); |
443 |
} |
} |
444 |
break; |
break; |
445 |
|
|
446 |
case wd_seccnt: /* 2: sector count */ |
case wd_seccnt: /* 2: sector count */ |
447 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
448 |
odata = d->seccnt; |
odata = d->seccnt; |
449 |
debug("[ wdc: read from SECCNT: 0x%02x ]\n", odata); |
debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata); |
450 |
} else { |
} else { |
451 |
d->seccnt = idata; |
d->seccnt = idata; |
452 |
debug("[ wdc: write to SECCNT: 0x%02x ]\n", idata); |
debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata); |
453 |
} |
} |
454 |
break; |
break; |
455 |
|
|
456 |
case wd_sector: /* 3: first sector */ |
case wd_sector: /* 3: first sector */ |
457 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
458 |
odata = d->sector; |
odata = d->sector; |
459 |
debug("[ wdc: read from SECTOR: 0x%02x ]\n", odata); |
debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata); |
460 |
} else { |
} else { |
461 |
d->sector = idata; |
d->sector = idata; |
462 |
debug("[ wdc: write to SECTOR: 0x%02x ]\n", idata); |
debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata); |
463 |
} |
} |
464 |
break; |
break; |
465 |
|
|
466 |
case wd_cyl_lo: /* 4: cylinder low */ |
case wd_cyl_lo: /* 4: cylinder low */ |
467 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
468 |
odata = d->cyl_lo; |
odata = d->cyl_lo; |
469 |
debug("[ wdc: read from CYL_LO: 0x%02x ]\n", odata); |
debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata); |
470 |
} else { |
} else { |
471 |
d->cyl_lo = idata; |
d->cyl_lo = idata; |
472 |
debug("[ wdc: write to CYL_LO: 0x%02x ]\n", idata); |
debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata); |
473 |
} |
} |
474 |
break; |
break; |
475 |
|
|
476 |
case wd_cyl_hi: /* 5: cylinder low */ |
case wd_cyl_hi: /* 5: cylinder low */ |
477 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
478 |
odata = d->cyl_hi; |
odata = d->cyl_hi; |
479 |
debug("[ wdc: read from CYL_HI: 0x%02x ]\n", odata); |
debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata); |
480 |
} else { |
} else { |
481 |
d->cyl_hi = idata; |
d->cyl_hi = idata; |
482 |
debug("[ wdc: write to CYL_HI: 0x%02x ]\n", idata); |
debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata); |
483 |
} |
} |
484 |
break; |
break; |
485 |
|
|
504 |
case wd_command: /* 7: command or status */ |
case wd_command: /* 7: command or status */ |
505 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
506 |
odata = status_byte(d, cpu); |
odata = status_byte(d, cpu); |
|
#if 1 |
|
507 |
if (!quiet_mode) |
if (!quiet_mode) |
508 |
debug("[ wdc: read from STATUS: 0x%02x ]\n", |
debug("[ wdc: read from STATUS: 0x%02x ]\n", |
509 |
odata); |
(int)odata); |
|
#endif |
|
|
|
|
510 |
cpu_interrupt_ack(cpu, d->irq_nr); |
cpu_interrupt_ack(cpu, d->irq_nr); |
511 |
d->delayed_interrupt = 0; |
d->delayed_interrupt = 0; |
512 |
} else { |
} else { |
513 |
debug("[ wdc: write to COMMAND: 0x%02x ]\n", idata); |
debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata); |
514 |
d->cur_command = idata; |
d->cur_command = idata; |
515 |
|
|
516 |
/* TODO: Is this correct behaviour? */ |
/* TODO: Is this correct behaviour? */ |
523 |
|
|
524 |
/* Handle the command: */ |
/* Handle the command: */ |
525 |
switch (d->cur_command) { |
switch (d->cur_command) { |
|
case WDCC_READ: |
|
|
debug("[ wdc: READ from drive %i, head %i, " |
|
|
"cylinder %i, sector %i, nsecs %i ]\n", |
|
|
d->drive, d->head, d->cyl_hi*256+d->cyl_lo, |
|
|
d->sector, d->seccnt); |
|
|
/* TODO: HAHA! This should be removed |
|
|
quickly */ |
|
|
diskimage_getchs(cpu->machine, d->drive + |
|
|
d->base_drive, DISKIMAGE_IDE, &cyls, |
|
|
&heads, §ors_per_track); |
|
|
|
|
|
{ |
|
|
unsigned char buf[512*256]; |
|
|
int cyl = d->cyl_hi * 256+ d->cyl_lo; |
|
|
int count = d->seccnt? d->seccnt : 256; |
|
|
uint64_t offset = 512 * (d->sector - 1 |
|
|
+ d->head * sectors_per_track + |
|
|
heads*sectors_per_track*cyl); |
|
526 |
|
|
527 |
#if 0 |
case WDCC_READ: |
528 |
/* LBA: */ |
if (!quiet_mode) |
529 |
if (d->lba) |
debug("[ wdc: READ from drive %i, head" |
530 |
offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8) + d->sector); |
" %i, cyl %i, sector %i, nsecs %i " |
531 |
printf("WDC read from offset %lli\n", (long long)offset); |
"]\n", d->drive, d->head, |
532 |
#endif |
d->cyl_hi*256+d->cyl_lo, d->sector, |
533 |
diskimage_access(cpu->machine, |
d->seccnt); |
534 |
d->drive + d->base_drive, |
wdc__read(cpu, d); |
|
DISKIMAGE_IDE, 0, |
|
|
offset, buf, 512 * count); |
|
|
/* TODO: result code */ |
|
|
for (i=0; i<512 * count; i++) |
|
|
wdc_addtoinbuf(d, buf[i]); |
|
|
} |
|
|
d->delayed_interrupt = INT_DELAY; |
|
535 |
break; |
break; |
|
case WDCC_WRITE: |
|
|
debug("[ wdc: WRITE to drive %i, head %i, " |
|
|
"cylinder %i, sector %i, nsecs %i ]\n", |
|
|
d->drive, d->head, d->cyl_hi*256+d->cyl_lo, |
|
|
d->sector, d->seccnt); |
|
|
/* TODO: HAHA! This should be removed |
|
|
quickly */ |
|
|
diskimage_getchs(cpu->machine, d->drive + |
|
|
d->base_drive, DISKIMAGE_IDE, &cyls, |
|
|
&heads, §ors_per_track); |
|
|
{ |
|
|
int cyl = d->cyl_hi * 256+ d->cyl_lo; |
|
|
int count = d->seccnt? d->seccnt : 256; |
|
|
uint64_t offset = 512 * (d->sector - 1 |
|
|
+ d->head * sectors_per_track + |
|
|
heads*sectors_per_track*cyl); |
|
|
|
|
|
#if 0 |
|
|
/* LBA: */ |
|
|
if (d->lba) |
|
|
offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8) + d->sector); |
|
|
printf("WDC write to offset %lli\n", (long long)offset); |
|
|
#endif |
|
|
|
|
|
d->write_in_progress = 1; |
|
|
d->write_count = count; |
|
|
d->write_offset = offset; |
|
536 |
|
|
537 |
/* TODO: result code */ |
case WDCC_WRITE: |
538 |
} |
if (!quiet_mode) |
539 |
/* TODO: Really interrupt here? */ |
debug("[ wdc: WRITE to drive %i, head" |
540 |
#if 0 |
" %i, cyl %i, sector %i, nsecs %i" |
541 |
d->delayed_interrupt = INT_DELAY; |
" ]\n", d->drive, d->head, |
542 |
#endif |
d->cyl_hi*256+d->cyl_lo, d->sector, |
543 |
|
d->seccnt); |
544 |
|
wdc__write(cpu, d); |
545 |
break; |
break; |
546 |
|
|
547 |
case WDCC_IDP: /* Initialize drive parameters */ |
case WDCC_IDP: /* Initialize drive parameters */ |
550 |
/* TODO */ |
/* TODO */ |
551 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
552 |
break; |
break; |
553 |
|
|
554 |
case SET_FEATURES: |
case SET_FEATURES: |
555 |
fatal("[ wdc: SET_FEATURES drive %i (TODO), " |
fatal("[ wdc: SET_FEATURES drive %i (TODO), " |
556 |
"feature 0x%02x ]\n", d->drive, d->precomp); |
"feature 0x%02x ]\n", d->drive, d->precomp); |
567 |
/* TODO: always interrupt? */ |
/* TODO: always interrupt? */ |
568 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
569 |
break; |
break; |
570 |
|
|
571 |
case WDCC_RECAL: |
case WDCC_RECAL: |
572 |
debug("[ wdc: RECAL drive %i ]\n", d->drive); |
debug("[ wdc: RECAL drive %i ]\n", d->drive); |
573 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
574 |
break; |
break; |
575 |
|
|
576 |
case WDCC_IDENTIFY: |
case WDCC_IDENTIFY: |
577 |
debug("[ wdc: IDENTIFY drive %i ]\n", d->drive); |
debug("[ wdc: IDENTIFY drive %i ]\n", d->drive); |
578 |
wdc_initialize_identify_struct(cpu, d); |
wdc_initialize_identify_struct(cpu, d); |
586 |
} |
} |
587 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
588 |
break; |
break; |
589 |
|
|
590 |
case WDCC_IDLE_IMMED: |
case WDCC_IDLE_IMMED: |
591 |
debug("[ wdc: IDLE_IMMED drive %i ]\n", |
debug("[ wdc: IDLE_IMMED drive %i ]\n", |
592 |
d->drive); |
d->drive); |
593 |
/* TODO: interrupt here? */ |
/* TODO: interrupt here? */ |
594 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
595 |
break; |
break; |
596 |
|
|
597 |
|
/* Unsupported commands, without warning: */ |
598 |
|
case ATAPI_IDENTIFY_DEVICE: |
599 |
|
case WDCC_SEC_SET_PASSWORD: |
600 |
|
case WDCC_SEC_UNLOCK: |
601 |
|
case WDCC_SEC_ERASE_PREPARE: |
602 |
|
case WDCC_SEC_ERASE_UNIT: |
603 |
|
case WDCC_SEC_FREEZE_LOCK: |
604 |
|
case WDCC_SEC_DISABLE_PASSWORD: |
605 |
|
d->error |= WDCE_ABRT; |
606 |
|
break; |
607 |
|
|
608 |
default: |
default: |
609 |
/* TODO */ |
/* TODO */ |
610 |
d->error |= WDCE_ABRT; |
d->error |= WDCE_ABRT; |
611 |
|
|
612 |
fatal("[ wdc: unknown command 0x%02x (" |
fatal("[ wdc: WARNING! Unimplemented command " |
613 |
"drive %i, head %i, cylinder %i, sector %i," |
"0x%02x (drive %i, head %i, cyl %i, sector" |
614 |
" nsecs %i) ]\n", d->cur_command, d->drive, |
" %i, nsecs %i) ]\n", d->cur_command, |
615 |
d->head, d->cyl_hi*256+d->cyl_lo, |
d->drive, d->head, d->cyl_hi*256+d->cyl_lo, |
616 |
d->sector, d->seccnt); |
d->sector, d->seccnt); |
|
exit(1); |
|
617 |
} |
} |
618 |
} |
} |
619 |
break; |
break; |
635 |
|
|
636 |
|
|
637 |
/* |
/* |
638 |
* dev_wdc_init(): |
* devinit_wdc(): |
|
* |
|
|
* base_drive should be 0 for the primary device, and 2 for the secondary. |
|
639 |
*/ |
*/ |
640 |
void dev_wdc_init(struct machine *machine, struct memory *mem, |
int devinit_wdc(struct devinit *devinit) |
|
uint64_t baseaddr, int irq_nr, int base_drive) |
|
641 |
{ |
{ |
642 |
struct wdc_data *d; |
struct wdc_data *d; |
643 |
uint64_t alt_status_addr; |
uint64_t alt_status_addr; |
644 |
|
int i; |
645 |
|
|
646 |
d = malloc(sizeof(struct wdc_data)); |
d = malloc(sizeof(struct wdc_data)); |
647 |
if (d == NULL) { |
if (d == NULL) { |
649 |
exit(1); |
exit(1); |
650 |
} |
} |
651 |
memset(d, 0, sizeof(struct wdc_data)); |
memset(d, 0, sizeof(struct wdc_data)); |
652 |
d->irq_nr = irq_nr; |
d->irq_nr = devinit->irq_nr; |
653 |
d->base_drive = base_drive; |
|
654 |
|
/* base_drive = 0 for the primary controller, 2 for the secondary. */ |
655 |
|
d->base_drive = 0; |
656 |
|
if ((devinit->addr & 0xfff) == 0x170) |
657 |
|
d->base_drive = 2; |
658 |
|
|
659 |
alt_status_addr = baseaddr + 0x206; |
alt_status_addr = devinit->addr + 0x206; |
660 |
|
|
661 |
/* Special hack for pcic/hpcmips: TODO: Fix */ |
/* Special hack for pcic/hpcmips: TODO: Fix */ |
662 |
if (baseaddr == 0x14000180) |
if (devinit->addr == 0x14000180) |
663 |
alt_status_addr = 0x14000386; |
alt_status_addr = 0x14000386; |
664 |
|
|
665 |
memory_device_register(mem, "wdc_altstatus", alt_status_addr, 2, |
/* Get disk geometries: */ |
666 |
dev_wdc_altstatus_access, d, MEM_DEFAULT, NULL); |
for (i=0; i<2; i++) |
667 |
memory_device_register(mem, "wdc", baseaddr, DEV_WDC_LENGTH, |
if (diskimage_exist(devinit->machine, d->base_drive +i, |
668 |
dev_wdc_access, d, MEM_DEFAULT, NULL); |
DISKIMAGE_IDE)) |
669 |
|
diskimage_getchs(devinit->machine, d->base_drive + i, |
670 |
|
DISKIMAGE_IDE, &d->cyls[i], &d->heads[i], |
671 |
|
&d->sectors_per_track[i]); |
672 |
|
|
673 |
|
memory_device_register(devinit->machine->memory, "wdc_altstatus", |
674 |
|
alt_status_addr, 2, dev_wdc_altstatus_access, d, MEM_DEFAULT, NULL); |
675 |
|
memory_device_register(devinit->machine->memory, devinit->name, |
676 |
|
devinit->addr, DEV_WDC_LENGTH, dev_wdc_access, d, MEM_DEFAULT, |
677 |
|
NULL); |
678 |
|
|
679 |
machine_add_tickfunction(machine, dev_wdc_tick, |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
680 |
d, WDC_TICK_SHIFT); |
d, WDC_TICK_SHIFT); |
681 |
|
|
682 |
|
return 1; |
683 |
} |
} |
684 |
|
|