diff options
Diffstat (limited to 'sim/ppc/hw_ide.c')
-rw-r--r-- | sim/ppc/hw_ide.c | 869 |
1 files changed, 869 insertions, 0 deletions
diff --git a/sim/ppc/hw_ide.c b/sim/ppc/hw_ide.c new file mode 100644 index 0000000..00d54b3 --- /dev/null +++ b/sim/ppc/hw_ide.c @@ -0,0 +1,869 @@ +/* This file is part of the program psim. + + Copyright (C) 1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_IDE_C_ +#define _HW_IDE_C_ + +#include "device_table.h" + + + +/* DEVICE + + + ide - Integrated Disk Electronics + + + DESCRIPTION + + + This device models the primary/secondary <<ide>> controller + described in the [CHRPIO] document. + + The controller has separate independant interrupt outputs for each + <<ide>> bus. + + + PROPERTIES + + + reg = ... (required) + + The <<reg>> property is described in the document [CHRPIO]. + + + ready-delay = <integer> (optional) + + If present, this specifies the time that the <<ide>> device takes + to complete an I/O operation. + + + disk@?/ide-byte-count = <integer> (optional) + + disk@?/ide-sector-count = <integer> (optional) + + disk@?/ide-head-count = <integer> (optional) + + The <<ide>> device checks each child (disk device) node to see if + it has the above properties. If present, these values will be used + to compute the <<LBA>> address in <<CHS>> addressing mode. + + + EXAMPLES + + + Enable tracing: + + | -t ide-device \ + + + Attach the <<ide>> device to the <<pci>> bus at slot one. Specify + legacy I/O addresses: + + | -o '/phb/ide@1/assigned-addresses \ + | ni0,0,10,1f0 8 \ + | ni0,0,14,3f8 8 \ + | ni0,0,18,170 8 \ + | ni0,0,1c,378 8 \ + | ni0,0,20,200 8' \ + | -o '/phb@0x80000000/ide@1/reg \ + | 1 0 \ + | i0,0,10,0 8 \ + | i0,0,18,0 8 \ + | i0,0,14,6 1 \ + | i0,0,1c,6 1 \ + | i0,0,20,0 8' \ + + Note: the fouth and fifth reg entries specify that the register is + at an offset into the address specified by the base register + (<<assigned-addresses>>); Apart from restrictions placed by the + <<pci>> specification, no restrictions are placed on the number of + base registers specified by the <<assigned-addresses>> property. + + Attach a <<disk>> to the primary and a <<cdrom>> to the secondary + <<ide>> controller. + + | -o '/phb@0x80000000/ide@1/disk@0/file "zero' \ + | -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \ + + Connect the two interrupt outputs (a and b) to a <<glue>> device to + allow testing of the interrupt port. In a real simulation they + would be wired to the interrupt controller. + + | -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \ + | -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \ + | -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' + + + BUGS + + + While the DMA registers are present, DMA support has not yet been + implemented. + + The number of supported commands is very limited. + + The standards documents appear to be vague on how to specify the + <<unit-address>> of disk devices devices being attached to the + <<ide>> controller. I've chosen to use integers with devices zero + and one going to the primary controller while two and three are + connected to the secondary controller. + + + REFERENCES + + + [CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference + Platform: I/O Device Reference. http://chrp.apple.com/???. + + [SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications + and Programming. Friedhelm Schmidt (translated by Michael + Schultz). ISBN 0-201-42284-0. Addison-Wesley Publishing Company. + + + */ + + + +typedef enum _io_direction { + is_read, + is_write, +} io_direction; + + +enum { + nr_ide_controllers = 2, + nr_ide_drives_per_controller = 2, + nr_fifo_entries = 8192, +}; + +enum { + /* command register block - read */ + ide_data_reg, + ide_error_reg, /*ide_feature_reg*/ + ide_sector_count_reg, + ide_sector_number_reg, + ide_cylinder_reg0, + ide_cylinder_reg1, + ide_drive_head_reg, + ide_status_reg, /*ide_command_reg*/ + /* command register block - write */ + ide_feature_reg, /*ide_error_reg*/ + ide_command_reg, /*ide_status_reg*/ + /* control register block - read */ + ide_alternate_status_reg, /*ide_control_reg*/ + ide_control_reg, /*ide_alternate_status_reg*/ + /* dma register block */ + ide_dma_command_reg, + ide_dma_unused_1_reg, + ide_dma_status_reg, + ide_dma_unused_3_reg, + ide_dma_prd_table_address_reg0, + ide_dma_prd_table_address_reg1, + ide_dma_prd_table_address_reg2, + ide_dma_prd_table_address_reg3, + nr_ide_registers, +}; + + +typedef enum _ide_states { + idle_state, + busy_loaded_state, + busy_drained_state, + busy_dma_state, + busy_command_state, + loading_state, + draining_state, +} ide_states; + +static const char * +ide_state_name(ide_states state) +{ + switch (state) { + case idle_state: return "idle"; + case busy_loaded_state: return "busy_loaded_state"; + case busy_drained_state: return "busy_drained_state"; + case busy_dma_state: return "busy_dma_state"; + case busy_command_state: return "busy_command_state"; + case loading_state: return "loading_state"; + case draining_state: return "draining_state"; + default: return "illegal-state"; + } +} + +typedef struct _ide_geometry { + int head; + int sector; + int byte; +} ide_geometry; + +typedef struct _ide_drive { + int nr; + device *device; + ide_geometry geometry; + ide_geometry default_geometry; +} ide_drive; + +typedef struct _ide_controller { + int nr; + ide_states state; + unsigned8 reg[nr_ide_registers]; + unsigned8 fifo[nr_fifo_entries]; + int fifo_pos; + int fifo_size; + ide_drive *current_drive; + int current_byte; + int current_transfer; + ide_drive drive[nr_ide_drives_per_controller]; + device *me; + event_entry_tag event_tag; + int is_interrupting; + signed64 ready_delay; +} ide_controller; + + + +static void +set_interrupt(device *me, + ide_controller *controller) +{ + if ((controller->reg[ide_control_reg] & 0x2) == 0) { + DTRACE(ide, ("controller %d - interrupt set\n", controller->nr)); + device_interrupt_event(me, controller->nr, 1, NULL, 0); + controller->is_interrupting = 1; + } +} + + +static void +clear_interrupt(device *me, + ide_controller *controller) +{ + if (controller->is_interrupting) { + DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr)); + device_interrupt_event(me, controller->nr, 0, NULL, 0); + controller->is_interrupting = 0; + } +} + + +static void +do_event(void *data) +{ + ide_controller *controller = data; + device *me = controller->me; + controller->event_tag = 0; + switch (controller->state) { + case busy_loaded_state: + case busy_drained_state: + if (controller->current_transfer > 0) { + controller->state = (controller->state == busy_loaded_state + ? loading_state : draining_state); + } + else { + controller->state = idle_state; + } + set_interrupt(me, controller); + break; + default: + device_error(me, "controller %d - unexpected event", controller->nr); + break; + } +} + + +static void +schedule_ready_event(device *me, + ide_controller *controller) +{ + if (controller->event_tag != 0) + device_error(me, "controller %d - attempting to schedule multiple events", + controller->nr); + controller->event_tag = + device_event_queue_schedule(me, controller->ready_delay, + do_event, controller); +} + + +static void +do_fifo_read(device *me, + ide_controller *controller, + void *dest, + int nr_bytes) +{ + if (controller->state != draining_state) + device_error(me, "controller %d - reading fifo when not ready (%s)", + controller->nr, + ide_state_name(controller->state)); + if (controller->fifo_pos + nr_bytes > controller->fifo_size) + device_error(me, "controller %d - fifo underflow", controller->nr); + if (nr_bytes > 0) { + memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes); + controller->fifo_pos += nr_bytes; + } + if (controller->fifo_pos == controller->fifo_size) { + controller->current_transfer -= 1; + if (controller->current_transfer > 0 + && controller->current_drive != NULL) { + DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n", + controller->nr, + controller->current_drive->nr, + controller->fifo_size, + controller->current_byte)); + if (device_io_read_buffer(controller->current_drive->device, + controller->fifo, + 0, controller->current_byte, + controller->fifo_size, + NULL, 0) + != controller->fifo_size) + device_error(me, "controller %d - disk %s io read error", + controller->nr, + device_path(controller->current_drive->device)); + } + controller->state = busy_drained_state; + controller->fifo_pos = 0; + controller->current_byte += controller->fifo_size; + schedule_ready_event(me, controller); + } +} + + +static void +do_fifo_write(device *me, + ide_controller *controller, + const void *source, + int nr_bytes) +{ + if (controller->state != loading_state) + device_error(me, "controller %d - writing fifo when not ready (%s)", + controller->nr, + ide_state_name(controller->state)); + if (controller->fifo_pos + nr_bytes > controller->fifo_size) + device_error(me, "controller %d - fifo overflow", controller->nr); + if (nr_bytes > 0) { + memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes); + controller->fifo_pos += nr_bytes; + } + if (controller->fifo_pos == controller->fifo_size) { + if (controller->current_transfer > 0 + && controller->current_drive != NULL) { + DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n", + controller->nr, + controller->current_drive->nr, + controller->fifo_size, + controller->current_byte)); + if (device_io_write_buffer(controller->current_drive->device, + controller->fifo, + 0, controller->current_byte, + controller->fifo_size, + NULL, 0) + != controller->fifo_size) + device_error(me, "controller %d - disk %s io write error", + controller->nr, + device_path(controller->current_drive->device)); + } + controller->current_transfer -= 1; + controller->fifo_pos = 0; + controller->current_byte += controller->fifo_size; + controller->state = busy_loaded_state; + schedule_ready_event(me, controller); + } +} + + +static void +setup_fifo(device *me, + ide_controller *controller, + int is_simple, + int is_with_disk, + io_direction direction) +{ + /* find the disk */ + if (is_with_disk) { + int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0; + controller->current_drive = &controller->drive[drive_nr]; + } + else { + controller->current_drive = NULL; + } + + /* number of transfers */ + if (is_simple) + controller->current_transfer = 1; + else { + int sector_count = controller->reg[ide_sector_count_reg]; + if (sector_count == 0) + controller->current_transfer = 256; + else + controller->current_transfer = sector_count; + } + + /* the transfer size */ + if (controller->current_drive == NULL) + controller->fifo_size = 512; + else + controller->fifo_size = controller->current_drive->geometry.byte; + + /* empty the fifo */ + controller->fifo_pos = 0; + + /* the starting address */ + if (controller->current_drive == NULL) + controller->current_byte = 0; + else if (controller->reg[ide_drive_head_reg] & 0x40) { + /* LBA addressing mode */ + controller->current_byte = controller->fifo_size + * (((controller->reg[ide_drive_head_reg] & 0xf) << 24) + | (controller->reg[ide_cylinder_reg1] << 16) + | (controller->reg[ide_cylinder_reg0] << 8) + | (controller->reg[ide_sector_number_reg])); + } + else if (controller->current_drive->geometry.head != 0 + && controller->current_drive->geometry.sector != 0) { + /* CHS addressing mode */ + int head_nr = controller->reg[ide_drive_head_reg] & 0xf; + int cylinder_nr = ((controller->reg[ide_cylinder_reg1] << 8) + | controller->reg[ide_cylinder_reg0]); + int sector_nr = controller->reg[ide_sector_number_reg]; + controller->current_byte = controller->fifo_size + * ((cylinder_nr * controller->current_drive->geometry.head + head_nr) + * controller->current_drive->geometry.sector + sector_nr - 1); + } + else + device_error(me, "controller %d:%d - CHS addressing disabled", + controller->nr, controller->current_drive->nr); + DTRACE(ide, ("controller %ld:%ld - transfer (%s) %ld blocks of %ld bytes from 0x%lx\n", + (long)controller->nr, + controller->current_drive == NULL ? -1L : (long)controller->current_drive->nr, + direction == is_read ? "read" : "write", + (long)controller->current_transfer, + (long)controller->fifo_size, + (unsigned long)controller->current_byte)); + switch (direction) { + case is_read: + /* force a primeing read */ + controller->current_transfer += 1; + controller->state = draining_state; + controller->fifo_pos = controller->fifo_size; + do_fifo_read(me, controller, NULL, 0); + break; + case is_write: + controller->state = loading_state; + break; + } +} + + +static void +do_command(device *me, + ide_controller *controller, + int command) +{ + if (controller->state != idle_state) + device_error(me, "controller %d - command when not idle", controller->nr); + switch (command) { + case 0x20: case 0x21: /* read-sectors */ + setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_read); + break; + case 0x30: case 0x31: /* write */ + setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_write); + break; + } +} + +static unsigned8 +get_status(device *me, + ide_controller *controller) +{ + switch (controller->state) { + case loading_state: + case draining_state: + return 0x08; /* data req */ + case busy_loaded_state: + case busy_drained_state: + return 0x80; /* busy */ + case idle_state: + return 0x40; /* drive ready */ + default: + device_error(me, "internal error"); + return 0; + } +} + + +/* The address presented to the IDE controler is decoded and then + mapped onto a controller:reg pair */ + +enum { + nr_address_blocks = 6, +}; + +typedef struct _address_block { + int space; + unsigned_word base_addr; + unsigned_word bound_addr; + int controller; + int base_reg; +} address_block; + +typedef struct _address_decoder { + address_block block[nr_address_blocks]; +} address_decoder; + +static void +decode_address(device *me, + address_decoder *decoder, + int space, + unsigned_word address, + int *controller, + int *reg, + io_direction direction) +{ + int i; + for (i = 0; i < nr_address_blocks; i++) { + if (space == decoder->block[i].space + && address >= decoder->block[i].base_addr + && address <= decoder->block[i].bound_addr) { + *controller = decoder->block[i].controller; + *reg = (address + - decoder->block[i].base_addr + + decoder->block[i].base_reg); + if (direction == is_write) { + switch (*reg) { + case ide_error_reg: *reg = ide_feature_reg; break; + case ide_status_reg: *reg = ide_command_reg; break; + case ide_alternate_status_reg: *reg = ide_control_reg; break; + default: break; + } + } + return; + } + } + device_error(me, "address %d:0x%lx invalid", + space, (unsigned long)address); +} + + +static void +build_address_decoder(device *me, + address_decoder *decoder) +{ + int reg; + for (reg = 1; reg < 6; reg++) { + reg_property_spec unit; + int space; + unsigned_word address; + unsigned size; + /* find and decode the reg property */ + if (!device_find_reg_array_property(me, "reg", reg, &unit)) + device_error(me, "missing or invalid reg entry %d", reg); + device_address_to_attach_address(device_parent(me), &unit.address, + &space, &address, me); + device_size_to_attach_size(device_parent(me), &unit.size, &size, me); + /* insert it into the address decoder */ + switch (reg) { + case 1: + case 2: + /* command register block */ + if (size != 8) + device_error(me, "reg entry %d must have a size of 8", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + size - 1; + decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; + decoder->block[reg-1].base_reg = ide_data_reg; + DTRACE(ide, ("controller %d command register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + break; + case 3: + case 4: + /* control register block */ + if (size != 1) + device_error(me, "reg entry %d must have a size of 1", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + size - 1; + decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers; + decoder->block[reg-1].base_reg = ide_alternate_status_reg; + DTRACE(ide, ("controller %d control register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + break; + case 5: + /* dma register block */ + if (size != 8) + device_error(me, "reg entry %d must have a size of 8", reg); + decoder->block[reg-1].space = space; + decoder->block[reg-1].base_addr = address; + decoder->block[reg-1].bound_addr = address + 4 - 1; + decoder->block[reg-1].base_reg = ide_dma_command_reg; + decoder->block[reg-1].controller = 0; + DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", + decoder->block[reg-1].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg-1].base_addr, + (unsigned long)decoder->block[reg-1].bound_addr)); + decoder->block[reg].space = space; + decoder->block[reg].base_addr = address + 4; + decoder->block[reg].bound_addr = address + 8 - 1; + decoder->block[reg].controller = 1; + decoder->block[reg].base_reg = ide_dma_command_reg; + DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n", + decoder->block[reg].controller, + decoder->block[reg-1].space, + (unsigned long)decoder->block[reg].base_addr, + (unsigned long)decoder->block[reg].bound_addr)); + break; + default: + device_error(me, "internal error - bad switch"); + break; + } + } +} + + + +typedef struct _hw_ide_device { + ide_controller controller[nr_ide_controllers]; + address_decoder decoder; +} hw_ide_device; + + +static void +hw_ide_init_address(device *me) +{ + hw_ide_device *ide = device_data(me); + int controller; + int drive; + + /* zero some things */ + for (controller = 0; controller < nr_ide_controllers; controller++) { + memset(&ide->controller[controller], 0, sizeof(ide_controller)); + for (drive = 0; drive < nr_ide_drives_per_controller; drive++) { + ide->controller[controller].drive[drive].nr = drive; + } + ide->controller[controller].me = me; + if (device_find_property(me, "ready-delay") != NULL) + ide->controller[controller].ready_delay = + device_find_integer_property(me, "ready-delay"); + } + + /* attach this device to its parent */ + generic_device_init_address(me); + + /* determine our own address map */ + build_address_decoder(me, &ide->decoder); + +} + + +static void +hw_ide_attach_address(device *me, + attach_type type, + int space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + device *client) /*callback/default*/ +{ + hw_ide_device *ide = (hw_ide_device*)device_data(me); + int controller_nr = addr / nr_ide_drives_per_controller; + int drive_nr = addr % nr_ide_drives_per_controller; + ide_controller *controller; + ide_drive *drive; + if (controller_nr >= nr_ide_controllers) + device_error(me, "no controller for disk %s", + device_path(client)); + + controller = &ide->controller[controller_nr]; + drive = &controller->drive[drive_nr]; + drive->device = client; + if (device_find_property(client, "ide-byte-count") != NULL) + drive->geometry.byte = device_find_integer_property(client, "ide-byte-count"); + else + drive->geometry.byte = 512; + if (device_find_property(client, "ide-sector-count") != NULL) + drive->geometry.sector = device_find_integer_property(client, "ide-sector-count"); + if (device_find_property(client, "ide-head-count") != NULL) + drive->geometry.head = device_find_integer_property(client, "ide-head-count"); + drive->default_geometry = drive->geometry; + DTRACE(ide, ("controller %d:%d %s byte-count %d, sector-count %d, head-count %d\n", + controller_nr, + drive->nr, + device_path(client), + drive->geometry.byte, + drive->geometry.sector, + drive->geometry.head)); +} + + +static unsigned +hw_ide_io_read_buffer(device *me, + void *dest, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_ide_device *ide = (hw_ide_device *)device_data(me); + int control_nr; + int reg; + ide_controller *controller; + + /* find the interface */ + decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_read); + controller = & ide->controller[control_nr]; + + /* process the transfer */ + memset(dest, 0, nr_bytes); + switch (reg) { + case ide_data_reg: + do_fifo_read(me, controller, dest, nr_bytes); + break; + case ide_status_reg: + *(unsigned8*)dest = get_status(me, controller); + clear_interrupt(me, controller); + break; + case ide_alternate_status_reg: + *(unsigned8*)dest = get_status(me, controller); + break; + case ide_error_reg: + case ide_sector_count_reg: + case ide_sector_number_reg: + case ide_cylinder_reg0: + case ide_cylinder_reg1: + case ide_drive_head_reg: + case ide_control_reg: + case ide_dma_command_reg: + case ide_dma_status_reg: + case ide_dma_prd_table_address_reg0: + case ide_dma_prd_table_address_reg1: + case ide_dma_prd_table_address_reg2: + case ide_dma_prd_table_address_reg3: + *(unsigned8*)dest = controller->reg[reg]; + break; + default: + device_error(me, "bus-error at address 0x%lx", addr); + break; + } + return nr_bytes; +} + + +static unsigned +hw_ide_io_write_buffer(device *me, + const void *source, + int space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + hw_ide_device *ide = (hw_ide_device *)device_data(me); + int control_nr; + int reg; + ide_controller *controller; + + /* find the interface */ + decode_address(me, &ide->decoder, space, addr, &control_nr, ®, is_write); + controller = &ide->controller[control_nr]; + + /* process the access */ + switch (reg) { + case ide_data_reg: + do_fifo_write(me, controller, source, nr_bytes); + break; + case ide_command_reg: + do_command(me, controller, *(unsigned8*)source); + break; + case ide_control_reg: + controller->reg[reg] = *(unsigned8*)source; + /* possibly cancel interrupts */ + if ((controller->reg[reg] & 0x02) == 0x02) + clear_interrupt(me, controller); + break; + case ide_feature_reg: + case ide_sector_count_reg: + case ide_sector_number_reg: + case ide_cylinder_reg0: + case ide_cylinder_reg1: + case ide_drive_head_reg: + case ide_dma_command_reg: + case ide_dma_status_reg: + case ide_dma_prd_table_address_reg0: + case ide_dma_prd_table_address_reg1: + case ide_dma_prd_table_address_reg2: + case ide_dma_prd_table_address_reg3: + controller->reg[reg] = *(unsigned8*)source; + break; + default: + device_error(me, "bus-error at 0x%lx", addr); + break; + } + return nr_bytes; +} + + +static const device_interrupt_port_descriptor hw_ide_interrupt_ports[] = { + { "a", 0, 0 }, + { "b", 1, 0 }, + { "c", 2, 0 }, + { "d", 3, 0 }, + { NULL } +}; + + + +static device_callbacks const hw_ide_callbacks = { + { hw_ide_init_address, }, + { hw_ide_attach_address, }, /* attach */ + { hw_ide_io_read_buffer, hw_ide_io_write_buffer, }, + { NULL, }, /* DMA */ + { NULL, NULL, hw_ide_interrupt_ports }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, + generic_device_address_to_attach_address, + generic_device_size_to_attach_size }, +}; + + +static void * +hw_ide_create(const char *name, + const device_unit *unit_address, + const char *args) +{ + hw_ide_device *ide = ZALLOC(hw_ide_device); + return ide; +} + + +const device_descriptor hw_ide_device_descriptor[] = { + { "ide", hw_ide_create, &hw_ide_callbacks }, + { NULL, }, +}; + +#endif /* _HW_IDE_ */ |