| 1 | /* This file is part of the program psim. |
| 2 | |
| 3 | Copyright (C) 1996, Andrew Cagney <cagney@highland.com.au> |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; either version 3 of the License, or |
| 8 | (at your option) any later version. |
| 9 | |
| 10 | This program is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License |
| 16 | along with this program; if not, see <http://www.gnu.org/licenses/>. |
| 17 | |
| 18 | */ |
| 19 | |
| 20 | |
| 21 | #ifndef _HW_IDE_C_ |
| 22 | #define _HW_IDE_C_ |
| 23 | |
| 24 | #include "device_table.h" |
| 25 | |
| 26 | |
| 27 | |
| 28 | /* DEVICE |
| 29 | |
| 30 | |
| 31 | ide - Integrated Disk Electronics |
| 32 | |
| 33 | |
| 34 | DESCRIPTION |
| 35 | |
| 36 | |
| 37 | This device models the primary/secondary <<ide>> controller |
| 38 | described in the [CHRPIO] document. |
| 39 | |
| 40 | The controller has separate independant interrupt outputs for each |
| 41 | <<ide>> bus. |
| 42 | |
| 43 | |
| 44 | PROPERTIES |
| 45 | |
| 46 | |
| 47 | reg = ... (required) |
| 48 | |
| 49 | The <<reg>> property is described in the document [CHRPIO]. |
| 50 | |
| 51 | |
| 52 | ready-delay = <integer> (optional) |
| 53 | |
| 54 | If present, this specifies the time that the <<ide>> device takes |
| 55 | to complete an I/O operation. |
| 56 | |
| 57 | |
| 58 | disk@?/ide-byte-count = <integer> (optional) |
| 59 | |
| 60 | disk@?/ide-sector-count = <integer> (optional) |
| 61 | |
| 62 | disk@?/ide-head-count = <integer> (optional) |
| 63 | |
| 64 | The <<ide>> device checks each child (disk device) node to see if |
| 65 | it has the above properties. If present, these values will be used |
| 66 | to compute the <<LBA>> address in <<CHS>> addressing mode. |
| 67 | |
| 68 | |
| 69 | EXAMPLES |
| 70 | |
| 71 | |
| 72 | Enable tracing: |
| 73 | |
| 74 | | -t ide-device \ |
| 75 | |
| 76 | |
| 77 | Attach the <<ide>> device to the <<pci>> bus at slot one. Specify |
| 78 | legacy I/O addresses: |
| 79 | |
| 80 | | -o '/phb/ide@1/assigned-addresses \ |
| 81 | | ni0,0,10,1f0 8 \ |
| 82 | | ni0,0,14,3f8 8 \ |
| 83 | | ni0,0,18,170 8 \ |
| 84 | | ni0,0,1c,378 8 \ |
| 85 | | ni0,0,20,200 8' \ |
| 86 | | -o '/phb@0x80000000/ide@1/reg \ |
| 87 | | 1 0 \ |
| 88 | | i0,0,10,0 8 \ |
| 89 | | i0,0,18,0 8 \ |
| 90 | | i0,0,14,6 1 \ |
| 91 | | i0,0,1c,6 1 \ |
| 92 | | i0,0,20,0 8' \ |
| 93 | |
| 94 | Note: the fouth and fifth reg entries specify that the register is |
| 95 | at an offset into the address specified by the base register |
| 96 | (<<assigned-addresses>>); Apart from restrictions placed by the |
| 97 | <<pci>> specification, no restrictions are placed on the number of |
| 98 | base registers specified by the <<assigned-addresses>> property. |
| 99 | |
| 100 | Attach a <<disk>> to the primary and a <<cdrom>> to the secondary |
| 101 | <<ide>> controller. |
| 102 | |
| 103 | | -o '/phb@0x80000000/ide@1/disk@0/file "zero' \ |
| 104 | | -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \ |
| 105 | |
| 106 | Connect the two interrupt outputs (a and b) to a <<glue>> device to |
| 107 | allow testing of the interrupt port. In a real simulation they |
| 108 | would be wired to the interrupt controller. |
| 109 | |
| 110 | | -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \ |
| 111 | | -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \ |
| 112 | | -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' |
| 113 | |
| 114 | |
| 115 | BUGS |
| 116 | |
| 117 | |
| 118 | While the DMA registers are present, DMA support has not yet been |
| 119 | implemented. |
| 120 | |
| 121 | The number of supported commands is very limited. |
| 122 | |
| 123 | The standards documents appear to be vague on how to specify the |
| 124 | <<unit-address>> of disk devices devices being attached to the |
| 125 | <<ide>> controller. I've chosen to use integers with devices zero |
| 126 | and one going to the primary controller while two and three are |
| 127 | connected to the secondary controller. |
| 128 | |
| 129 | |
| 130 | REFERENCES |
| 131 | |
| 132 | |
| 133 | [CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference |
| 134 | Platform: I/O Device Reference. http://chrp.apple.com/???. |
| 135 | |
| 136 | [SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications |
| 137 | and Programming. Friedhelm Schmidt (translated by Michael |
| 138 | Schultz). ISBN 0-201-42284-0. Addison-Wesley Publishing Company. |
| 139 | |
| 140 | |
| 141 | */ |
| 142 | |
| 143 | |
| 144 | |
| 145 | typedef enum _io_direction { |
| 146 | is_read, |
| 147 | is_write, |
| 148 | } io_direction; |
| 149 | |
| 150 | |
| 151 | enum { |
| 152 | nr_ide_controllers = 2, |
| 153 | nr_ide_drives_per_controller = 2, |
| 154 | nr_fifo_entries = 8192, |
| 155 | }; |
| 156 | |
| 157 | enum { |
| 158 | /* command register block - read */ |
| 159 | ide_data_reg, |
| 160 | ide_error_reg, /*ide_feature_reg*/ |
| 161 | ide_sector_count_reg, |
| 162 | ide_sector_number_reg, |
| 163 | ide_cylinder_reg0, |
| 164 | ide_cylinder_reg1, |
| 165 | ide_drive_head_reg, |
| 166 | ide_status_reg, /*ide_command_reg*/ |
| 167 | /* command register block - write */ |
| 168 | ide_feature_reg, /*ide_error_reg*/ |
| 169 | ide_command_reg, /*ide_status_reg*/ |
| 170 | /* control register block - read */ |
| 171 | ide_alternate_status_reg, /*ide_control_reg*/ |
| 172 | ide_control_reg, /*ide_alternate_status_reg*/ |
| 173 | /* dma register block */ |
| 174 | ide_dma_command_reg, |
| 175 | ide_dma_unused_1_reg, |
| 176 | ide_dma_status_reg, |
| 177 | ide_dma_unused_3_reg, |
| 178 | ide_dma_prd_table_address_reg0, |
| 179 | ide_dma_prd_table_address_reg1, |
| 180 | ide_dma_prd_table_address_reg2, |
| 181 | ide_dma_prd_table_address_reg3, |
| 182 | nr_ide_registers, |
| 183 | }; |
| 184 | |
| 185 | |
| 186 | typedef enum _ide_states { |
| 187 | idle_state, |
| 188 | busy_loaded_state, |
| 189 | busy_drained_state, |
| 190 | busy_dma_state, |
| 191 | busy_command_state, |
| 192 | loading_state, |
| 193 | draining_state, |
| 194 | } ide_states; |
| 195 | |
| 196 | static const char * |
| 197 | ide_state_name(ide_states state) |
| 198 | { |
| 199 | switch (state) { |
| 200 | case idle_state: return "idle"; |
| 201 | case busy_loaded_state: return "busy_loaded_state"; |
| 202 | case busy_drained_state: return "busy_drained_state"; |
| 203 | case busy_dma_state: return "busy_dma_state"; |
| 204 | case busy_command_state: return "busy_command_state"; |
| 205 | case loading_state: return "loading_state"; |
| 206 | case draining_state: return "draining_state"; |
| 207 | default: return "illegal-state"; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | typedef struct _ide_geometry { |
| 212 | int head; |
| 213 | int sector; |
| 214 | int byte; |
| 215 | } ide_geometry; |
| 216 | |
| 217 | typedef struct _ide_drive { |
| 218 | int nr; |
| 219 | device *device; |
| 220 | ide_geometry geometry; |
| 221 | ide_geometry default_geometry; |
| 222 | } ide_drive; |
| 223 | |
| 224 | typedef struct _ide_controller { |
| 225 | int nr; |
| 226 | ide_states state; |
| 227 | unsigned8 reg[nr_ide_registers]; |
| 228 | unsigned8 fifo[nr_fifo_entries]; |
| 229 | int fifo_pos; |
| 230 | int fifo_size; |
| 231 | ide_drive *current_drive; |
| 232 | int current_byte; |
| 233 | int current_transfer; |
| 234 | ide_drive drive[nr_ide_drives_per_controller]; |
| 235 | device *me; |
| 236 | event_entry_tag event_tag; |
| 237 | int is_interrupting; |
| 238 | signed64 ready_delay; |
| 239 | } ide_controller; |
| 240 | |
| 241 | |
| 242 | |
| 243 | static void |
| 244 | set_interrupt(device *me, |
| 245 | ide_controller *controller) |
| 246 | { |
| 247 | if ((controller->reg[ide_control_reg] & 0x2) == 0) { |
| 248 | DTRACE(ide, ("controller %d - interrupt set\n", controller->nr)); |
| 249 | device_interrupt_event(me, controller->nr, 1, NULL, 0); |
| 250 | controller->is_interrupting = 1; |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | |
| 255 | static void |
| 256 | clear_interrupt(device *me, |
| 257 | ide_controller *controller) |
| 258 | { |
| 259 | if (controller->is_interrupting) { |
| 260 | DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr)); |
| 261 | device_interrupt_event(me, controller->nr, 0, NULL, 0); |
| 262 | controller->is_interrupting = 0; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | |
| 267 | static void |
| 268 | do_event(void *data) |
| 269 | { |
| 270 | ide_controller *controller = data; |
| 271 | device *me = controller->me; |
| 272 | controller->event_tag = 0; |
| 273 | switch (controller->state) { |
| 274 | case busy_loaded_state: |
| 275 | case busy_drained_state: |
| 276 | if (controller->current_transfer > 0) { |
| 277 | controller->state = (controller->state == busy_loaded_state |
| 278 | ? loading_state : draining_state); |
| 279 | } |
| 280 | else { |
| 281 | controller->state = idle_state; |
| 282 | } |
| 283 | set_interrupt(me, controller); |
| 284 | break; |
| 285 | default: |
| 286 | device_error(me, "controller %d - unexpected event", controller->nr); |
| 287 | break; |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | |
| 292 | static void |
| 293 | schedule_ready_event(device *me, |
| 294 | ide_controller *controller) |
| 295 | { |
| 296 | if (controller->event_tag != 0) |
| 297 | device_error(me, "controller %d - attempting to schedule multiple events", |
| 298 | controller->nr); |
| 299 | controller->event_tag = |
| 300 | device_event_queue_schedule(me, controller->ready_delay, |
| 301 | do_event, controller); |
| 302 | } |
| 303 | |
| 304 | |
| 305 | static void |
| 306 | do_fifo_read(device *me, |
| 307 | ide_controller *controller, |
| 308 | void *dest, |
| 309 | int nr_bytes) |
| 310 | { |
| 311 | if (controller->state != draining_state) |
| 312 | device_error(me, "controller %d - reading fifo when not ready (%s)", |
| 313 | controller->nr, |
| 314 | ide_state_name(controller->state)); |
| 315 | if (controller->fifo_pos + nr_bytes > controller->fifo_size) |
| 316 | device_error(me, "controller %d - fifo underflow", controller->nr); |
| 317 | if (nr_bytes > 0) { |
| 318 | memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes); |
| 319 | controller->fifo_pos += nr_bytes; |
| 320 | } |
| 321 | if (controller->fifo_pos == controller->fifo_size) { |
| 322 | controller->current_transfer -= 1; |
| 323 | if (controller->current_transfer > 0 |
| 324 | && controller->current_drive != NULL) { |
| 325 | DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n", |
| 326 | controller->nr, |
| 327 | controller->current_drive->nr, |
| 328 | controller->fifo_size, |
| 329 | controller->current_byte)); |
| 330 | if (device_io_read_buffer(controller->current_drive->device, |
| 331 | controller->fifo, |
| 332 | 0, controller->current_byte, |
| 333 | controller->fifo_size, |
| 334 | NULL, 0) |
| 335 | != controller->fifo_size) |
| 336 | device_error(me, "controller %d - disk %s io read error", |
| 337 | controller->nr, |
| 338 | device_path(controller->current_drive->device)); |
| 339 | } |
| 340 | controller->state = busy_drained_state; |
| 341 | controller->fifo_pos = 0; |
| 342 | controller->current_byte += controller->fifo_size; |
| 343 | schedule_ready_event(me, controller); |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | |
| 348 | static void |
| 349 | do_fifo_write(device *me, |
| 350 | ide_controller *controller, |
| 351 | const void *source, |
| 352 | int nr_bytes) |
| 353 | { |
| 354 | if (controller->state != loading_state) |
| 355 | device_error(me, "controller %d - writing fifo when not ready (%s)", |
| 356 | controller->nr, |
| 357 | ide_state_name(controller->state)); |
| 358 | if (controller->fifo_pos + nr_bytes > controller->fifo_size) |
| 359 | device_error(me, "controller %d - fifo overflow", controller->nr); |
| 360 | if (nr_bytes > 0) { |
| 361 | memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes); |
| 362 | controller->fifo_pos += nr_bytes; |
| 363 | } |
| 364 | if (controller->fifo_pos == controller->fifo_size) { |
| 365 | if (controller->current_transfer > 0 |
| 366 | && controller->current_drive != NULL) { |
| 367 | DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n", |
| 368 | controller->nr, |
| 369 | controller->current_drive->nr, |
| 370 | controller->fifo_size, |
| 371 | controller->current_byte)); |
| 372 | if (device_io_write_buffer(controller->current_drive->device, |
| 373 | controller->fifo, |
| 374 | 0, controller->current_byte, |
| 375 | controller->fifo_size, |
| 376 | NULL, 0) |
| 377 | != controller->fifo_size) |
| 378 | device_error(me, "controller %d - disk %s io write error", |
| 379 | controller->nr, |
| 380 | device_path(controller->current_drive->device)); |
| 381 | } |
| 382 | controller->current_transfer -= 1; |
| 383 | controller->fifo_pos = 0; |
| 384 | controller->current_byte += controller->fifo_size; |
| 385 | controller->state = busy_loaded_state; |
| 386 | schedule_ready_event(me, controller); |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | |
| 391 | static void |
| 392 | setup_fifo(device *me, |
| 393 | ide_controller *controller, |
| 394 | int is_simple, |
| 395 | int is_with_disk, |
| 396 | io_direction direction) |
| 397 | { |
| 398 | /* find the disk */ |
| 399 | if (is_with_disk) { |
| 400 | int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0; |
| 401 | controller->current_drive = &controller->drive[drive_nr]; |
| 402 | } |
| 403 | else { |
| 404 | controller->current_drive = NULL; |
| 405 | } |
| 406 | |
| 407 | /* number of transfers */ |
| 408 | if (is_simple) |
| 409 | controller->current_transfer = 1; |
| 410 | else { |
| 411 | int sector_count = controller->reg[ide_sector_count_reg]; |
| 412 | if (sector_count == 0) |
| 413 | controller->current_transfer = 256; |
| 414 | else |
| 415 | controller->current_transfer = sector_count; |
| 416 | } |
| 417 | |
| 418 | /* the transfer size */ |
| 419 | if (controller->current_drive == NULL) |
| 420 | controller->fifo_size = 512; |
| 421 | else |
| 422 | controller->fifo_size = controller->current_drive->geometry.byte; |
| 423 | |
| 424 | /* empty the fifo */ |
| 425 | controller->fifo_pos = 0; |
| 426 | |
| 427 | /* the starting address */ |
| 428 | if (controller->current_drive == NULL) |
| 429 | controller->current_byte = 0; |
| 430 | else if (controller->reg[ide_drive_head_reg] & 0x40) { |
| 431 | /* LBA addressing mode */ |
| 432 | controller->current_byte = controller->fifo_size |
| 433 | * (((controller->reg[ide_drive_head_reg] & 0xf) << 24) |
| 434 | | (controller->reg[ide_cylinder_reg1] << 16) |
| 435 | | (controller->reg[ide_cylinder_reg0] << 8) |
| 436 | | (controller->reg[ide_sector_number_reg])); |
| 437 | } |
| 438 | else if (controller->current_drive->geometry.head != 0 |
| 439 | && controller->current_drive->geometry.sector != 0) { |
| 440 | /* CHS addressing mode */ |
| 441 | int head_nr = controller->reg[ide_drive_head_reg] & 0xf; |
| 442 | int cylinder_nr = ((controller->reg[ide_cylinder_reg1] << 8) |
| 443 | | controller->reg[ide_cylinder_reg0]); |
| 444 | int sector_nr = controller->reg[ide_sector_number_reg]; |
| 445 | controller->current_byte = controller->fifo_size |
| 446 | * ((cylinder_nr * controller->current_drive->geometry.head + head_nr) |
| 447 | * controller->current_drive->geometry.sector + sector_nr - 1); |
| 448 | } |
| 449 | else |
| 450 | device_error(me, "controller %d:%d - CHS addressing disabled", |
| 451 | controller->nr, controller->current_drive->nr); |
| 452 | DTRACE(ide, ("controller %ld:%ld - transfer (%s) %ld blocks of %ld bytes from 0x%lx\n", |
| 453 | (long)controller->nr, |
| 454 | controller->current_drive == NULL ? -1L : (long)controller->current_drive->nr, |
| 455 | direction == is_read ? "read" : "write", |
| 456 | (long)controller->current_transfer, |
| 457 | (long)controller->fifo_size, |
| 458 | (unsigned long)controller->current_byte)); |
| 459 | switch (direction) { |
| 460 | case is_read: |
| 461 | /* force a primeing read */ |
| 462 | controller->current_transfer += 1; |
| 463 | controller->state = draining_state; |
| 464 | controller->fifo_pos = controller->fifo_size; |
| 465 | do_fifo_read(me, controller, NULL, 0); |
| 466 | break; |
| 467 | case is_write: |
| 468 | controller->state = loading_state; |
| 469 | break; |
| 470 | } |
| 471 | } |
| 472 | |
| 473 | |
| 474 | static void |
| 475 | do_command(device *me, |
| 476 | ide_controller *controller, |
| 477 | int command) |
| 478 | { |
| 479 | if (controller->state != idle_state) |
| 480 | device_error(me, "controller %d - command when not idle", controller->nr); |
| 481 | switch (command) { |
| 482 | case 0x20: case 0x21: /* read-sectors */ |
| 483 | setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_read); |
| 484 | break; |
| 485 | case 0x30: case 0x31: /* write */ |
| 486 | setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_write); |
| 487 | break; |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | static unsigned8 |
| 492 | get_status(device *me, |
| 493 | ide_controller *controller) |
| 494 | { |
| 495 | switch (controller->state) { |
| 496 | case loading_state: |
| 497 | case draining_state: |
| 498 | return 0x08; /* data req */ |
| 499 | case busy_loaded_state: |
| 500 | case busy_drained_state: |
| 501 | return 0x80; /* busy */ |
| 502 | case idle_state: |
| 503 | return 0x40; /* drive ready */ |
| 504 | default: |
| 505 | device_error(me, "internal error"); |
| 506 | return 0; |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | |
| 511 | /* The address presented to the IDE controler is decoded and then |
| 512 | mapped onto a controller:reg pair */ |
| 513 | |
| 514 | enum { |
| 515 | nr_address_blocks = 6, |
| 516 | }; |
| 517 | |
| 518 | typedef struct _address_block { |
| 519 | int space; |
| 520 | unsigned_word base_addr; |
| 521 | unsigned_word bound_addr; |
| 522 | int controller; |
| 523 | int base_reg; |
| 524 | } address_block; |
| 525 | |
| 526 | typedef struct _address_decoder { |
| 527 | address_block block[nr_address_blocks]; |
| 528 | } address_decoder; |
| 529 | |
| 530 | static void |
| 531 | decode_address(device *me, |
| 532 | address_decoder *decoder, |
| 533 | int space, |
| 534 | unsigned_word address, |
| 535 | int *controller, |
| 536 | int *reg, |
| 537 | io_direction direction) |
| 538 | { |
| 539 | int i; |
| 540 | for (i = 0; i < nr_address_blocks; i++) { |
| 541 | if (space == decoder->block[i].space |
| 542 | && address >= decoder->block[i].base_addr |
| 543 | && address <= decoder->block[i].bound_addr) { |
| 544 | *controller = decoder->block[i].controller; |
| 545 | *reg = (address |
| 546 | - decoder->block[i].base_addr |
| 547 | + decoder->block[i].base_reg); |
| 548 | if (direction == is_write) { |
| 549 | switch (*reg) { |
| 550 | case ide_error_reg: *reg = ide_feature_reg; break; |
| 551 | case ide_status_reg: *reg = ide_command_reg; break; |
| 552 | case ide_alternate_status_reg: *reg = ide_control_reg; break; |
| 553 | default: break; |
| 554 | } |
| 555 | } |
| 556 | return; |
| 557 | } |
| 558 | } |
| 559 | device_error(me, "address %d:0x%lx invalid", |
| 560 | space, (unsigned long)address); |
| 561 | } |
| 562 | |
| 563 | |
| 564 | static void |
| 565 | build_address_decoder(device *me, |
| 566 | address_decoder *decoder) |
| 567 | { |
| 568 | int reg; |
| 569 | for (reg = 1; reg < 6; reg++) { |
| 570 | reg_property_spec unit; |
| 571 | int space; |
| 572 | unsigned_word address; |
| 573 | unsigned size; |
| 574 | /* find and decode the reg property */ |
| 575 | if (!device_find_reg_array_property(me, "reg", reg, &unit)) |
| 576 | device_error(me, "missing or invalid reg entry %d", reg); |
| 577 | device_address_to_attach_address(device_parent(me), &unit.address, |
| 578 | &space, &address, me); |
| 579 | device_size_to_attach_size(device_parent(me), &unit.size, &size, me); |
| 580 | /* insert it into the address decoder */ |
| 581 | switch (reg) { |
| 582 | case 1: |
| 583 | case 2: |
| 584 | /* command register block */ |
| 585 | if (size != 8) |
| 586 | device_error(me, "reg entry %d must have a size of 8", reg); |
| 587 | decoder->block[reg-1].space = space; |
| 588 | decoder->block[reg-1].base_addr = address; |
| 589 | decoder->block[reg-1].bound_addr = address + size - 1; |
| 590 | decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; |
| 591 | decoder->block[reg-1].base_reg = ide_data_reg; |
| 592 | DTRACE(ide, ("controller %d command register block at %d:0x%lx..0x%lx\n", |
| 593 | decoder->block[reg-1].controller, |
| 594 | decoder->block[reg-1].space, |
| 595 | (unsigned long)decoder->block[reg-1].base_addr, |
| 596 | (unsigned long)decoder->block[reg-1].bound_addr)); |
| 597 | break; |
| 598 | case 3: |
| 599 | case 4: |
| 600 | /* control register block */ |
| 601 | if (size != 1) |
| 602 | device_error(me, "reg entry %d must have a size of 1", reg); |
| 603 | decoder->block[reg-1].space = space; |
| 604 | decoder->block[reg-1].base_addr = address; |
| 605 | decoder->block[reg-1].bound_addr = address + size - 1; |
| 606 | decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; |
| 607 | decoder->block[reg-1].base_reg = ide_alternate_status_reg; |
| 608 | DTRACE(ide, ("controller %d control register block at %d:0x%lx..0x%lx\n", |
| 609 | decoder->block[reg-1].controller, |
| 610 | decoder->block[reg-1].space, |
| 611 | (unsigned long)decoder->block[reg-1].base_addr, |
| 612 | (unsigned long)decoder->block[reg-1].bound_addr)); |
| 613 | break; |
| 614 | case 5: |
| 615 | /* dma register block */ |
| 616 | if (size != 8) |
| 617 | device_error(me, "reg entry %d must have a size of 8", reg); |
| 618 | decoder->block[reg-1].space = space; |
| 619 | decoder->block[reg-1].base_addr = address; |
| 620 | decoder->block[reg-1].bound_addr = address + 4 - 1; |
| 621 | decoder->block[reg-1].base_reg = ide_dma_command_reg; |
| 622 | decoder->block[reg-1].controller = 0; |
| 623 | DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", |
| 624 | decoder->block[reg-1].controller, |
| 625 | decoder->block[reg-1].space, |
| 626 | (unsigned long)decoder->block[reg-1].base_addr, |
| 627 | (unsigned long)decoder->block[reg-1].bound_addr)); |
| 628 | decoder->block[reg].space = space; |
| 629 | decoder->block[reg].base_addr = address + 4; |
| 630 | decoder->block[reg].bound_addr = address + 8 - 1; |
| 631 | decoder->block[reg].controller = 1; |
| 632 | decoder->block[reg].base_reg = ide_dma_command_reg; |
| 633 | DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", |
| 634 | decoder->block[reg].controller, |
| 635 | decoder->block[reg-1].space, |
| 636 | (unsigned long)decoder->block[reg].base_addr, |
| 637 | (unsigned long)decoder->block[reg].bound_addr)); |
| 638 | break; |
| 639 | default: |
| 640 | device_error(me, "internal error - bad switch"); |
| 641 | break; |
| 642 | } |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | |
| 647 | |
| 648 | typedef struct _hw_ide_device { |
| 649 | ide_controller controller[nr_ide_controllers]; |
| 650 | address_decoder decoder; |
| 651 | } hw_ide_device; |
| 652 | |
| 653 | |
| 654 | static void |
| 655 | hw_ide_init_address(device *me) |
| 656 | { |
| 657 | hw_ide_device *ide = device_data(me); |
| 658 | int controller; |
| 659 | int drive; |
| 660 | |
| 661 | /* zero some things */ |
| 662 | for (controller = 0; controller < nr_ide_controllers; controller++) { |
| 663 | memset(&ide->controller[controller], 0, sizeof(ide_controller)); |
| 664 | for (drive = 0; drive < nr_ide_drives_per_controller; drive++) { |
| 665 | ide->controller[controller].drive[drive].nr = drive; |
| 666 | } |
| 667 | ide->controller[controller].me = me; |
| 668 | if (device_find_property(me, "ready-delay") != NULL) |
| 669 | ide->controller[controller].ready_delay = |
| 670 | device_find_integer_property(me, "ready-delay"); |
| 671 | } |
| 672 | |
| 673 | /* attach this device to its parent */ |
| 674 | generic_device_init_address(me); |
| 675 | |
| 676 | /* determine our own address map */ |
| 677 | build_address_decoder(me, &ide->decoder); |
| 678 | |
| 679 | } |
| 680 | |
| 681 | |
| 682 | static void |
| 683 | hw_ide_attach_address(device *me, |
| 684 | attach_type type, |
| 685 | int space, |
| 686 | unsigned_word addr, |
| 687 | unsigned nr_bytes, |
| 688 | access_type access, |
| 689 | device *client) /*callback/default*/ |
| 690 | { |
| 691 | hw_ide_device *ide = (hw_ide_device*)device_data(me); |
| 692 | int controller_nr = addr / nr_ide_drives_per_controller; |
| 693 | int drive_nr = addr % nr_ide_drives_per_controller; |
| 694 | ide_controller *controller; |
| 695 | ide_drive *drive; |
| 696 | if (controller_nr >= nr_ide_controllers) |
| 697 | device_error(me, "no controller for disk %s", |
| 698 | device_path(client)); |
| 699 | |
| 700 | controller = &ide->controller[controller_nr]; |
| 701 | drive = &controller->drive[drive_nr]; |
| 702 | drive->device = client; |
| 703 | if (device_find_property(client, "ide-byte-count") != NULL) |
| 704 | drive->geometry.byte = device_find_integer_property(client, "ide-byte-count"); |
| 705 | else |
| 706 | drive->geometry.byte = 512; |
| 707 | if (device_find_property(client, "ide-sector-count") != NULL) |
| 708 | drive->geometry.sector = device_find_integer_property(client, "ide-sector-count"); |
| 709 | if (device_find_property(client, "ide-head-count") != NULL) |
| 710 | drive->geometry.head = device_find_integer_property(client, "ide-head-count"); |
| 711 | drive->default_geometry = drive->geometry; |
| 712 | DTRACE(ide, ("controller %d:%d %s byte-count %d, sector-count %d, head-count %d\n", |
| 713 | controller_nr, |
| 714 | drive->nr, |
| 715 | device_path(client), |
| 716 | drive->geometry.byte, |
| 717 | drive->geometry.sector, |
| 718 | drive->geometry.head)); |
| 719 | } |
| 720 | |
| 721 | |
| 722 | static unsigned |
| 723 | hw_ide_io_read_buffer(device *me, |
| 724 | void *dest, |
| 725 | int space, |
| 726 | unsigned_word addr, |
| 727 | unsigned nr_bytes, |
| 728 | cpu *processor, |
| 729 | unsigned_word cia) |
| 730 | { |
| 731 | hw_ide_device *ide = (hw_ide_device *)device_data(me); |
| 732 | int control_nr; |
| 733 | int reg; |
| 734 | ide_controller *controller; |
| 735 | |
| 736 | /* find the interface */ |
| 737 | decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_read); |
| 738 | controller = & ide->controller[control_nr]; |
| 739 | |
| 740 | /* process the transfer */ |
| 741 | memset(dest, 0, nr_bytes); |
| 742 | switch (reg) { |
| 743 | case ide_data_reg: |
| 744 | do_fifo_read(me, controller, dest, nr_bytes); |
| 745 | break; |
| 746 | case ide_status_reg: |
| 747 | *(unsigned8*)dest = get_status(me, controller); |
| 748 | clear_interrupt(me, controller); |
| 749 | break; |
| 750 | case ide_alternate_status_reg: |
| 751 | *(unsigned8*)dest = get_status(me, controller); |
| 752 | break; |
| 753 | case ide_error_reg: |
| 754 | case ide_sector_count_reg: |
| 755 | case ide_sector_number_reg: |
| 756 | case ide_cylinder_reg0: |
| 757 | case ide_cylinder_reg1: |
| 758 | case ide_drive_head_reg: |
| 759 | case ide_control_reg: |
| 760 | case ide_dma_command_reg: |
| 761 | case ide_dma_status_reg: |
| 762 | case ide_dma_prd_table_address_reg0: |
| 763 | case ide_dma_prd_table_address_reg1: |
| 764 | case ide_dma_prd_table_address_reg2: |
| 765 | case ide_dma_prd_table_address_reg3: |
| 766 | *(unsigned8*)dest = controller->reg[reg]; |
| 767 | break; |
| 768 | default: |
| 769 | device_error(me, "bus-error at address 0x%lx", addr); |
| 770 | break; |
| 771 | } |
| 772 | return nr_bytes; |
| 773 | } |
| 774 | |
| 775 | |
| 776 | static unsigned |
| 777 | hw_ide_io_write_buffer(device *me, |
| 778 | const void *source, |
| 779 | int space, |
| 780 | unsigned_word addr, |
| 781 | unsigned nr_bytes, |
| 782 | cpu *processor, |
| 783 | unsigned_word cia) |
| 784 | { |
| 785 | hw_ide_device *ide = (hw_ide_device *)device_data(me); |
| 786 | int control_nr; |
| 787 | int reg; |
| 788 | ide_controller *controller; |
| 789 | |
| 790 | /* find the interface */ |
| 791 | decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_write); |
| 792 | controller = &ide->controller[control_nr]; |
| 793 | |
| 794 | /* process the access */ |
| 795 | switch (reg) { |
| 796 | case ide_data_reg: |
| 797 | do_fifo_write(me, controller, source, nr_bytes); |
| 798 | break; |
| 799 | case ide_command_reg: |
| 800 | do_command(me, controller, *(unsigned8*)source); |
| 801 | break; |
| 802 | case ide_control_reg: |
| 803 | controller->reg[reg] = *(unsigned8*)source; |
| 804 | /* possibly cancel interrupts */ |
| 805 | if ((controller->reg[reg] & 0x02) == 0x02) |
| 806 | clear_interrupt(me, controller); |
| 807 | break; |
| 808 | case ide_feature_reg: |
| 809 | case ide_sector_count_reg: |
| 810 | case ide_sector_number_reg: |
| 811 | case ide_cylinder_reg0: |
| 812 | case ide_cylinder_reg1: |
| 813 | case ide_drive_head_reg: |
| 814 | case ide_dma_command_reg: |
| 815 | case ide_dma_status_reg: |
| 816 | case ide_dma_prd_table_address_reg0: |
| 817 | case ide_dma_prd_table_address_reg1: |
| 818 | case ide_dma_prd_table_address_reg2: |
| 819 | case ide_dma_prd_table_address_reg3: |
| 820 | controller->reg[reg] = *(unsigned8*)source; |
| 821 | break; |
| 822 | default: |
| 823 | device_error(me, "bus-error at 0x%lx", addr); |
| 824 | break; |
| 825 | } |
| 826 | return nr_bytes; |
| 827 | } |
| 828 | |
| 829 | |
| 830 | static const device_interrupt_port_descriptor hw_ide_interrupt_ports[] = { |
| 831 | { "a", 0, 0 }, |
| 832 | { "b", 1, 0 }, |
| 833 | { "c", 2, 0 }, |
| 834 | { "d", 3, 0 }, |
| 835 | { NULL } |
| 836 | }; |
| 837 | |
| 838 | |
| 839 | |
| 840 | static device_callbacks const hw_ide_callbacks = { |
| 841 | { hw_ide_init_address, }, |
| 842 | { hw_ide_attach_address, }, /* attach */ |
| 843 | { hw_ide_io_read_buffer, hw_ide_io_write_buffer, }, |
| 844 | { NULL, }, /* DMA */ |
| 845 | { NULL, NULL, hw_ide_interrupt_ports }, /* interrupt */ |
| 846 | { generic_device_unit_decode, |
| 847 | generic_device_unit_encode, |
| 848 | generic_device_address_to_attach_address, |
| 849 | generic_device_size_to_attach_size }, |
| 850 | }; |
| 851 | |
| 852 | |
| 853 | static void * |
| 854 | hw_ide_create(const char *name, |
| 855 | const device_unit *unit_address, |
| 856 | const char *args) |
| 857 | { |
| 858 | hw_ide_device *ide = ZALLOC(hw_ide_device); |
| 859 | return ide; |
| 860 | } |
| 861 | |
| 862 | |
| 863 | const device_descriptor hw_ide_device_descriptor[] = { |
| 864 | { "ide", hw_ide_create, &hw_ide_callbacks }, |
| 865 | { NULL, }, |
| 866 | }; |
| 867 | |
| 868 | #endif /* _HW_IDE_ */ |