URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [or1ksim/] [peripheral/] [atadevice.c] - Rev 1748
Go to most recent revision | Compare with Previous | Blame | View Log
/* atadevice.c -- ATA Device simulation. Simulates a harddisk, using a local streams. Copyright (C) 2002 Richard Herveille, rherveille@opencores.org Copyright (C) 2008 Embecosm Limited Contributor Jeremy Bennett <jeremy.bennett@embecosm.com> This file is part of Or1ksim, the OpenRISC 1000 Architectural Simulator. 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 3 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, see <http://www.gnu.org/licenses/>. */ /* This program is commented throughout in a fashion suitable for processing with Doxygen. */ /* Autoconf and/or portability configuration */ #include "config.h" #include "port.h" /* System includes */ #include <byteswap.h> /* Package includes */ #include "atadevice.h" #include "atadevice-cmdi.h" #include "atacmd.h" #include "debug.h" #include "abstract.h" DEFAULT_DEBUG_CHANNEL (ata); /* mandatory commands: - execute device diagnostics (done) - flush cache - identify device (done) - initialize device parameters (done) - read dma - read multiple - read sector(s) (done) - read verify sector(s) - seek - set features - set multiple mode - write dma - write multiple - write sector(s) optional commands: - download microcode - nop - read buffer - write buffer prohibited commands: - device reset */ /* Use a file to simulate a hard-disk */ static FILE * open_file (uint32_t * size, const char *filename) { FILE *fp; unsigned long n; // TODO: /* check if a file with name 'filename' already exists */ if (!(fp = fopen (filename, "rb+"))) if (!(fp = fopen (filename, "wb+"))) { ERR ("ata_open_file, cannot open hd-file %s\n", filename); return NULL; } else { /* TODO create a file 'size' large */ /* create a file 'size' large */ TRACE ("ata_device; generating a new hard disk file.\n"); TRACE ("This may take a while, depending on the requested size.\n"); for (n = 0; n < *size; n++) fputc (0, fp); } else /* file already exist */ fprintf (stderr, "file %s already exists. Using existing file.\n", filename); TRACE ("requested filesize was: %d (MBytes).\n", *size); /* get the size of the file. This is also the size of the harddisk */ fseek (fp, 0, SEEK_END); *size = ftell (fp); TRACE ("actual filesize is: %d (MBytes).\n", *size >> 20); return fp; } /* Use a the local filesystem as a hard-disk */ static FILE * open_local (void) { // TODO: FIXME ("Device type LOCAL is not yet supported. Defaulting to device type NO_CONNECT."); return NULL; } static void ata_device_init (struct ata_device * device, int dev) { /* set DeviceID */ device->internals.dev = dev; /* generate stream for hd_simulation */ switch (device->conf.type) { case TYPE_NO_CONNECT: TRACE ("ata_device, using type NO_CONNECT.\n"); device->conf.stream = NULL; break; case TYPE_FILE: TRACE ("ata_device, using device type FILE.\n"); device->conf.stream = open_file (&device->conf.size, device->conf.file); break; case TYPE_LOCAL: TRACE ("ata_device, using device type LOCAL.\n"); device->conf.stream = open_local (); break; default: ERR ("Illegal device-type. Defaulting to type NO_CONNECT.\n"); device->conf.stream = NULL; break; } device->conf.size_sect = device->conf.size / BYTES_PER_SECTOR; } /* D E V I C E S _ I N I T */ void ata_devices_init (struct ata_devices * devices) { ata_device_init (&devices->device[0], 0); if (devices->device[0].conf.type) ata_device_init (&devices->device[1], ATA_DHR_DEV); else ata_device_init (&devices->device[1], 0); } /* D E V I C E S _ H W _ R E S E T */ static void ata_device_hw_reset (struct ata_device * device, int reset_signal, int daspo, int pdiagi, int daspi) { /* check ata-device state */ if (device->internals.state == ATA_STATE_HW_RST) { if (!reset_signal) { /* hardware reset finished */ /* set sectors_per_track & heads_per_cylinders */ device->internals.sectors_per_track = device->conf.sectors; device->internals.heads_per_cylinder = device->conf.heads; /* set device1 input signals */ device->sigs.pdiagi = pdiagi; device->sigs.daspi = daspi; ata_execute_device_diagnostics_cmd (device); /* clear busy bit */ device->regs.status &= ~ATA_SR_BSY; /* set DRDY bit, when not a PACKET device */ if (!device->conf.packet) device->regs.status |= ATA_SR_DRDY; /* set new state */ device->internals.state = ATA_STATE_IDLE; } } else if (reset_signal) { /* hardware reset signal asserted, stop what we are doing */ /* negate bus signals */ device->sigs.iordy = 0; device->sigs.intrq = 0; device->sigs.dmarq = 0; device->sigs.pdiago = 0; device->sigs.daspo = daspo; /* set busy bit */ device->regs.status |= ATA_SR_BSY; /* set new state */ device->internals.state = ATA_STATE_HW_RST; } } /* power-on and hardware reset */ void ata_devices_hw_reset (struct ata_devices * devices, int reset_signal) { /* display debug information */ TRACE (reset_signal ? "Starting reset\n" : "Finishing reset\n"); /* find device 0 */ if ((devices->device[0].conf.stream) && (devices->device[1].conf.stream)) { /* this one is simple, device0 is device0 */ /* 1) handle device1 first */ ata_device_hw_reset (&devices->device[1], reset_signal, 1, /* assert dasp, this is device1 */ 0, /* negate pdiag input, no more devices */ 0); /* negate dasp input, no more devices */ /* 2) Then handle device0 */ ata_device_hw_reset (&devices->device[0], reset_signal, 0, devices->device[1].sigs.pdiago, devices->device[1].sigs.daspo); } else if (devices->device[0].conf.stream) { /* device0 is device0, there's no device1 */ ata_device_hw_reset (&devices->device[0], reset_signal, 0, /* negate dasp, this is device0 */ 0, /* negate pdiag input, there's no device1 */ 0); /* negate dasp input, there's no device1 */ } else if (devices->device[1].conf.stream) { /* device1 is (logical) device0, there's no (physical) device0 */ ata_device_hw_reset (&devices->device[1], reset_signal, 0, /* negate dasp, this is device0 */ 0, /* negate pdiag input, there's no device1 */ 0); /* negate dasp input, there's no device1 */ } else { /* no devices connected */ TRACE ("ata_device_hw_reset, no devices connected.\n"); } } /* D E V I C E S _ D O _ C O N T R O L _ R E G I S T E R Handles - software reset - Interrupt enable bit */ static void ata_device_do_control_register (struct ata_device * device) { /* TODO respond to new values of nIEN */ /* if device not selected, respond to new values of nIEN & SRST */ /* check if SRST bit is set */ if (device->regs.device_control & ATA_DCR_RST) { if (device->internals.state == ATA_STATE_IDLE) { /* start software reset */ /* negate bus signals */ device->sigs.pdiago = 0; device->sigs.intrq = 0; device->sigs.iordy = 0; device->sigs.dmarq = 0; /* set busy bit */ device->regs.status |= ATA_SR_BSY; /* set new state */ device->internals.state = ATA_STATE_SW_RST; /* display debug information */ TRACE ("ata_device_sw_reset initiated.\n"); } } else if (device->internals.state == ATA_STATE_SW_RST) { /* are we doing a software reset ?? */ /* SRST bit cleared, end of software reset */ /*execute device diagnostics */ ata_execute_device_diagnostics_cmd (device); /* clear busy bit */ device->regs.status &= ~ATA_SR_BSY; /* set DRDY bit, when not a PACKET device */ if (!device->conf.packet) device->regs.status |= ATA_SR_DRDY; /* set new state */ device->internals.state = ATA_STATE_IDLE; /* display debug information */ TRACE ("ata_device_sw_reset done.\n"); } /* <else> We are doing a hardware reset (or similar) ignore command */ } /* D E V I C E S _ D O _ C O M M A N D _ R E G I S T E R */ static void ata_device_do_command_register (struct ata_device * device) { /* check BSY & DRQ */ if ((device->regs.status & ATA_SR_BSY) || (device->regs.status & ATA_SR_DRQ)) if (device->regs.command != DEVICE_RESET) WARN ("ata_device_write, writing a command while BSY or DRQ asserted.\n"); /* check if device selected */ if ((device->regs.device_head & ATA_DHR_DEV) == device->internals.dev) ata_device_execute_cmd (device); else { /* if not selected, only respond to EXECUTE DEVICE DIAGNOSTICS */ if (device->regs.command == EXECUTE_DEVICE_DIAGNOSTICS) ata_device_execute_cmd (device); } } /* Return contents of status register in pretty for */ static const char * ata_pretty_status (uint8_t status) { static char str[50]; str[0] = '\0'; if (status & ATA_SR_BSY) strcat (str, "BUSY"); if (status & ATA_SR_DRDY) strcat (str, "| READY"); if (status & ATA_SR_DF) strcat (str, "| FAULT"); if (status & ATA_SR_DSC) strcat (str, "| SEEKC"); if (status & ATA_SR_DRQ) strcat (str, "| DRQ"); if (status & ATA_SR_COR) strcat (str, "| COR"); if (status & ATA_SR_IDX) strcat (str, "| IDX"); if (status & ATA_SR_ERR) strcat (str, "| ERROR"); return str; } /* D E V I C E S _ R E A D */ /* Read from devices */ short ata_devices_read (struct ata_devices * devices, char adr) { struct ata_device *device; /* check for no connected devices */ if ((!devices->device[0].conf.stream) && (!devices->device[1].conf.stream)) ERR ("ata_devices_read, no ata devices connected.\n"); else { /* check if both device0 and device1 are connected */ if ((devices->device[0].conf.stream) && (devices->device[1].conf.stream)) { /* get the current active device */ if (devices->device[1].regs.device_head & ATA_DHR_DEV) device = &devices->device[1]; else device = &devices->device[0]; } else { /* only one device connected */ if (devices->device[1].conf.stream) device = &devices->device[1]; else device = &devices->device[0]; } /* return data provided by selected device */ switch (adr) { case ATA_ASR: TRACE ("read alternate status register: %s\n", ata_pretty_status (device->regs.status)); if ((device->regs.device_head & ATA_DHR_DEV) == device->internals.dev) return device->regs.status; else { TRACE ("device0 responds for device1, asr = 0x00\n"); return 0; // return 0 when device0 responds for device1 } case ATA_CHR: TRACE ("cylinder_high register read, value = 0x%02X\n", device->regs.cylinder_high); return device->regs.cylinder_high; case ATA_CLR: TRACE ("cylinder_low register read, value = 0x%02X\n", device->regs.cylinder_low); return device->regs.cylinder_low; case ATA_DR: if (!device->regs.status & ATA_SR_DRQ) { TRACE ("data register read, while DRQ bit negated\n"); return 0; } else { uint16_t val = LE16 (*device->internals.dbuf_ptr++); TRACE ("data register read, value = 0x%04X, cnt = %3d\n", val, device->internals.dbuf_cnt); if (!--device->internals.dbuf_cnt) { device->regs.status &= ~ATA_SR_DRQ; if (device->internals.end_t_func) device->internals.end_t_func (device); } return val; } case ATA_DHR: TRACE ("device_head register read, value = 0x%02X\n", device->regs.device_head); return device->regs.device_head; case ATA_ERR: TRACE ("error register read, value = 0x%02X\n", device->regs.error); return device->regs.error; case ATA_SCR: TRACE ("sectorcount register read, value = 0x%02X\n", device->regs.sector_count); return device->regs.sector_count; case ATA_SNR: TRACE ("sectornumber register read, value = 0x%02X\n", device->regs.sector_number); return device->regs.sector_number; case ATA_SR: TRACE ("read status register: %s\n", ata_pretty_status (device->regs.status)); if ((device->regs.device_head & ATA_DHR_DEV) == device->internals.dev) { device->sigs.intrq = 0; return device->regs.status; } TRACE ("device0 responds for device1, sr = 0x00\n"); return 0; // return 0 when device0 responds for device1 // case ATA_DA : // return device -> regs.status; } } return 0; } /* D E V I C E S _ W R I T E */ /* write to a single device */ static void ata_device_write (struct ata_device * device, char adr, short value) { switch (adr) { case ATA_CR: /*display debug information */ TRACE ("command register written, value = 0x%02X\n", value); device->regs.command = value; /* check command register settings and execute command */ ata_device_do_command_register (device); break; case ATA_CHR: /*display debug information */ TRACE ("cylinder high register written, value = 0x%02X\n", value); device->regs.cylinder_high = value; break; case ATA_CLR: /*display debug information */ TRACE ("cylinder low register written, value = 0x%02X\n", value); device->regs.cylinder_low = value; break; case ATA_DR: /*display debug information */ if (!device->regs.status & ATA_SR_DRQ) { TRACE ("data register write, while DRQ bit negated\n"); break; } else { TRACE ("data register write, value = 0x%04X, cnt = %3d\n", value, device->internals.dbuf_cnt); *device->internals.dbuf_ptr++ = LE16 (value); if (!--device->internals.dbuf_cnt) { device->regs.status &= ~ATA_SR_DRQ; if (device->internals.end_t_func) device->internals.end_t_func (device); } } device->regs.dataport_i = value; break; case ATA_DCR: /*display debug information */ TRACE ("device control register written, value = 0x%02X\n", value); device->regs.device_control = value; ata_device_do_control_register (device); break; case ATA_DHR: /*display debug information */ TRACE ("device head register written, value = 0x%02X\n", value); device->regs.device_head = value; break; case ATA_FR: /*display debug information */ TRACE ("features register written, value = 0x%02X\n", value); device->regs.features = value; break; case ATA_SCR: /*display debug information */ TRACE ("sectorcount register written, value = 0x%02X\n", value); device->regs.sector_count = value; break; case ATA_SNR: /*display debug information */ TRACE ("sectornumber register written, value = 0x%02X\n", value); device->regs.sector_number = value; break; } //endcase } /* Write to devices */ void ata_devices_write (struct ata_devices * devices, char adr, short value) { /* check for no connected devices */ if (!devices->device[0].conf.stream && !devices->device[1].conf.stream) ERR ("ata_devices_write, no ata devices connected.\n"); else { /* first device */ if (devices->device[0].conf.stream) ata_device_write (&devices->device[0], adr, value); /* second device */ if (devices->device[1].conf.stream) ata_device_write (&devices->device[1], adr, value); } }
Go to most recent revision | Compare with Previous | Blame | View Log