diff -ur rockbox-svn-trunk/firmware/drivers/ata.c rockbox-19581-dma/firmware/drivers/ata.c --- rockbox-svn-trunk/firmware/drivers/ata.c 2008-12-24 13:34:52.465000000 -0500 +++ rockbox-19581-dma/firmware/drivers/ata.c 2008-12-24 15:00:37.725600000 -0500 @@ -1,3 +1,8 @@ +#define HAVE_ATA_DMA +#undef HAVE_DMA_CACHE_COHERENCY +#define ATA_INTERRUPT_POLLED 1 +#define ATA_INTERRUPT ATA_INTERRUPT_POLLED +//Writes enabled in ata_transfer_sectors /*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ @@ -47,6 +52,7 @@ #define CONTROL_nIEN 0x02 #define CONTROL_SRST 0x04 +/* STATUS_RDY is prerequisite for all commands below */ #define CMD_READ_SECTORS 0x20 #define CMD_WRITE_SECTORS 0x30 #define CMD_WRITE_SECTORS_EXT 0x34 @@ -60,6 +66,12 @@ #define CMD_SLEEP 0xE6 #define CMD_SET_FEATURES 0xEF #define CMD_SECURITY_FREEZE_LOCK 0xF5 +#ifdef HAVE_ATA_DMA +#define CMD_READ_DMA 0xC8 +#define CMD_READ_DMA_EXT 0x25 +#define CMD_WRITE_DMA 0xCA +#define CMD_WRITE_DMA_EXT 0x35 +#endif #define Q_SLEEP 0 #define Q_CLOSE 1 @@ -187,26 +199,60 @@ static int phys_sector_mult = 1; #endif +#ifdef HAVE_ATA_DMA +static int dma = 1; +#endif + static int ata_power_on(void); static int perform_soft_reset(void); static int set_multiple_mode(int sectors); static int set_features(void); -STATICIRAM ICODE_ATTR int wait_for_bsy(void) +/* STATICIRAM */ ICODE_ATTR bool ata_wait_cfg(void) { - long timeout = current_tick + HZ*30; + long timeout; + + timeout = current_tick + HZ*5; do { - if (!(ATA_STATUS & STATUS_BSY)) - return 1; + if (IDE0_CFG & 8) + return true; last_disk_activity = current_tick; yield(); } while (TIME_BEFORE(current_tick, timeout)); - return 0; /* timeout */ + lcd_putsxy(20,20,"F"); + lcd_update(); + + return false; /* timeout */ } + +/* Wait for not busy or timeout, return last status read + (if STATUS_BSY, timed out, else not busy) + */ +STATICIRAM ICODE_ATTR unsigned char ata_wait_status(long timeout) +{ + unsigned char status; + timeout += current_tick; + + do + { + status = ATA_STATUS; + if (!(status & STATUS_BSY)) + return status; + last_disk_activity = current_tick; + yield(); + } while (TIME_BEFORE(current_tick, timeout)); + + return status; /* timeout */ +} + +/* FIXME Temporary functions */ +#define wait_for_bsy() ((ata_wait_status(HZ*30) & STATUS_BSY) == 0) +#define ata_wait_r4c() wait_for_rdy() + STATICIRAM ICODE_ATTR int wait_for_rdy(void) { long timeout; @@ -235,13 +281,6 @@ return (ATA_ALT_STATUS & (STATUS_BSY|STATUS_DRQ)) == STATUS_DRQ; } -STATICIRAM ICODE_ATTR int wait_for_end_of_transfer(void) -{ - if (!wait_for_bsy()) - return 0; - return (ATA_ALT_STATUS & (STATUS_RDY|STATUS_DRQ)) == STATUS_RDY; -} - #if (CONFIG_LED == LED_REAL) /* Conditionally block LED access for the ATA driver, so the LED can be * (mis)used for other purposes */ @@ -291,16 +330,52 @@ } #endif /* !ATA_OPTIMIZED_READING */ -#ifdef MAX_PHYS_SECTOR_SIZE -static int _read_sectors(unsigned long start, - int incount, - void* inbuf) +#ifndef ATA_OPTIMIZED_WRITING +STATICIRAM ICODE_ATTR void copy_write_sectors(const unsigned char* buf, + int wordcount) +{ + if ( (unsigned long)buf & 1) + { /* not 16-bit aligned, copy byte by byte */ + unsigned short tmp = 0; + const unsigned char* bufend = buf + wordcount*2; + do + { +#if defined(SWAP_WORDS) || defined(ROCKBOX_LITTLE_ENDIAN) + tmp = (unsigned short) *buf++; + tmp |= (unsigned short) *buf++ << 8; + SET_16BITREG(ATA_DATA, tmp); #else -int ata_read_sectors(IF_MV2(int drive,) - unsigned long start, - int incount, - void* inbuf) + tmp = (unsigned short) *buf++ << 8; + tmp |= (unsigned short) *buf++; + SET_16BITREG(ATA_DATA, tmp); +#endif + } while (buf < bufend); /* tail loop is faster */ + } + else + { /* 16-bit aligned, can do faster copy */ + unsigned short* wbuf = (unsigned short*)buf; + unsigned short* wbufend = wbuf + wordcount; + do + { +#ifdef SWAP_WORDS + SET_16BITREG(ATA_DATA, swap16(*wbuf)); +#else + SET_16BITREG(ATA_DATA, *wbuf); #endif + } while (++wbuf < wbufend); /* tail loop is faster */ + } +} +#endif /* !ATA_OPTIMIZED_WRITING */ + +/* Maximum transfer size is 0x100 without LBA48, and fat.c deals with that. + Yes, this could loop or use LBA48 for size reasons. + For now, it doesn't, for code size and reliability reasons. + */ +int ata_transfer_sectors(IF_MV2(int drive,) + unsigned long start, + int incount, + void* inbuf, + bool write) { int ret = 0; long timeout; @@ -308,6 +383,30 @@ void* buf; long spinup_start; + /* Flags are set in cmdidx for write, LBA48 and DMA. + cmdidx is used as an index to cmds[], giving the ATA command to use. + */ + int cmdidx; +#define IS_WRITE 1 + static const unsigned char cmds[] = { + CMD_READ_MULTIPLE, CMD_WRITE_MULTIPLE +#ifdef HAVE_LBA48 + , CMD_READ_MULTIPLE_EXT, CMD_WRITE_MULTIPLE_EXT +#define USE_LBA48 2 +#endif +#ifdef HAVE_ATA_DMA + , CMD_READ_DMA, CMD_WRITE_DMA +#ifdef HAVE_LBA48 + , CMD_READ_DMA_EXT, CMD_WRITE_DMA_EXT +#define USE_DMA 4 +#else +#define USE_DMA 2 +#endif +#endif + }; + + //if (write) return 0; + #ifndef MAX_PHYS_SECTOR_SIZE #ifdef HAVE_MULTIVOLUME (void)drive; /* unused for now */ @@ -315,11 +414,30 @@ mutex_lock(&ata_mtx); #endif - if (start + incount > total_sectors) { + /* Verify parameters */ + count = incount - 1; + /* Not 1 to 256 */ + if ((count & ~0xFF) || + /* Integer overflow in next check */ + start + (unsigned long)count < start || + /* Read past end of device */ + start + (unsigned long)incount > total_sectors) { ret = -1; goto error; } + /* bool is 0 or 1 and maps to IS_WRITE flag */ + cmdidx = write; + +#ifdef HAVE_LBA48 + /* Only use LBA48 if needed. Assume total_sectors check would + prevent attempt to use LBA48 if it isn't available. + (Standard: "28-bit and 48-bit commands may be intermixed") + */ + if (start + count > 0x0FFFFFFF) + cmdidx |= USE_LBA48; +#endif + last_disk_activity = current_tick; spinup_start = current_tick; @@ -339,48 +457,68 @@ goto error; } } + /* TODO: Are we guaranteed to be ready here? */ } timeout = current_tick + READ_TIMEOUT; - SET_REG(ATA_SELECT, ata_device); - if (!wait_for_rdy()) + /* HI0: Host_Idle */ + + /* When multiple devices are supported, + make sure the right one is selected here. + No point in re-selecting the same device. + If selection changes, wait for ready. + */ +retry: + IDE0_CFG |= 8; + if (!ata_wait_r4c()) { ret = -3; goto error; } - retry: +#ifdef HAVE_ATA_DMA + /* If DMA is supported and parameters are appropriate for DMA, use it. */ + if (dma && ata_dma_setup(inbuf, incount, cmdidx & IS_WRITE)) { + cmdidx |= USE_DMA; +#ifndef HAVE_DMA_CACHE_COHERENCY + /* Read: If unflushed, old data may overwrite new on invalidate. + Write: Make sure up to date data is present in RAM. + */ + flush_icache(); +#endif + } +#endif + buf = inbuf; count = incount; - while (TIME_BEFORE(current_tick, timeout)) { + if (TIME_BEFORE(current_tick, timeout)) { ret = 0; last_disk_activity = current_tick; + /* HI3: Write_Parameters State */ + #ifdef HAVE_LBA48 - if (lba48) + if (cmdidx & USE_LBA48) { + SET_REG(ATA_SELECT, SELECT_LBA | ata_device); + + /* Load previous register values for LBA48 */ SET_REG(ATA_NSECTOR, count >> 8); - SET_REG(ATA_NSECTOR, count & 0xff); SET_REG(ATA_SECTOR, (start >> 24) & 0xff); /* 31:24 */ - SET_REG(ATA_SECTOR, start & 0xff); /* 7:0 */ SET_REG(ATA_LCYL, 0); /* 39:32 */ - SET_REG(ATA_LCYL, (start >> 8) & 0xff); /* 15:8 */ SET_REG(ATA_HCYL, 0); /* 47:40 */ - SET_REG(ATA_HCYL, (start >> 16) & 0xff); /* 23:16 */ - SET_REG(ATA_SELECT, SELECT_LBA | ata_device); - SET_REG(ATA_COMMAND, CMD_READ_MULTIPLE_EXT); } else #endif - { - SET_REG(ATA_NSECTOR, count & 0xff); /* 0 means 256 sectors */ - SET_REG(ATA_SECTOR, start & 0xff); - SET_REG(ATA_LCYL, (start >> 8) & 0xff); - SET_REG(ATA_HCYL, (start >> 16) & 0xff); SET_REG(ATA_SELECT, ((start >> 24) & 0xf) | SELECT_LBA | ata_device); - SET_REG(ATA_COMMAND, CMD_READ_MULTIPLE); - } + + SET_REG(ATA_NSECTOR, count & 0xff); + SET_REG(ATA_SECTOR, start & 0xff); + SET_REG(ATA_LCYL, (start >> 8) & 0xff); + SET_REG(ATA_HCYL, (start >> 16) & 0xff); + /* HI4: Write_command */ + SET_REG(ATA_COMMAND, cmds[cmdidx]); /* wait at least 400ns between writing command and reading status */ __asm__ volatile ("nop"); @@ -389,25 +527,50 @@ __asm__ volatile ("nop"); __asm__ volatile ("nop"); +#ifdef HAVE_ATA_DMA + if (cmdidx & USE_DMA) { + if (!ata_dma_finish()) + ret = -7; + +#ifndef HAVE_DMA_CACHE_COHERENCY + /* Read: Invalidate, because new data is present in RAM. + Write: RAM unchanged, so no need to do anything. + */ + if (!(cmdidx & IS_WRITE)) + flush_icache(); +#endif + } + else +#endif while (count) { int sectors; int wordcount; int status; - if (!wait_for_start_of_transfer()) { - /* We have timed out waiting for RDY and/or DRQ, possibly - because the hard drive is shaking and has problems reading - the data. We have two options: - 1) Wait some more - 2) Perform a soft reset and try again. - - We choose alternative 2. + if (!(cmdidx & IS_WRITE)) + ata_wait_cfg(); + + /* HPIOI1: Check_Status || HPIOO0: Check_Status || HDMA0: Check_Status */ + + status = ata_wait_status(5*HZ); + + if ((status & (STATUS_BSY|STATUS_DRQ)) != STATUS_DRQ) { + /* STATUS_BSY means timeout. Since we don't want to wait + longer, perform a soft reset and try again. + + For reads, STATUS_DRQ should be set, even if there is + an error and the transfer should proceed. Errors are + checked after the transfer. + + For writes, loss of STATUS_DRQ would be premature and + imply an error. */ - perform_soft_reset(); ret = -5; - goto retry; + break; } + /* HPIOI2: Transfer_Data || HPIOO1: Transfer_Data State: */ + if (spinup) { spinup_time = current_tick - spinup_start; spinup = false; @@ -415,9 +578,6 @@ poweroff = false; } - /* read the status register exactly once per loop */ - status = ATA_STATUS; - if (count >= multisectors ) sectors = multisectors; else @@ -425,7 +585,10 @@ wordcount = sectors * SECTOR_SIZE / 2; - copy_read_sectors(buf, wordcount); + if (cmdidx & IS_WRITE) + copy_write_sectors(buf, wordcount); + else + copy_read_sectors(buf, wordcount); /* "Device errors encountered during READ MULTIPLE commands are @@ -433,26 +596,48 @@ but the DRQ bit is still set to one and the data transfer shall take place, including transfer of corrupted data, if any." -- ATA specification - */ - if ( status & (STATUS_BSY | STATUS_ERR | STATUS_DF) ) { - perform_soft_reset(); + */ + if ( status & (STATUS_ERR | STATUS_DF) ) { ret = -6; - goto retry; + break; } buf += sectors * SECTOR_SIZE; /* Advance one chunk of sectors */ count -= sectors; last_disk_activity = current_tick; - } - if(!ret && !wait_for_end_of_transfer()) { + /* "wait one PIO transfer cycle time before + reading the Status register" */ + (void)ATA_ALT_STATUS; + if (cmdidx & IS_WRITE) + ata_wait_cfg(); + + /* If more is left to transfer go to HPIOI1: Check_Status + because nIEN == 1 + */ + } /* while (count) */ + + /* For writes: + We go back to HPIOO0: Check_Status and wait for final status. + + For reads: + Transfer finished, go to HI0: Host_Idle + Spec: "The host may read the Status register." + No waiting for STATUS_BSY to clear is needed, but no harm done. + + Errors from while (count) loop come here also. + */ + if (ret != 0 || + (ata_wait_status(5*HZ) & + (STATUS_BSY|STATUS_RDY|STATUS_DF|STATUS_DRQ|STATUS_ERR)) != + STATUS_RDY) { perform_soft_reset(); - ret = -4; + if (ret == 0) + ret = -4; goto retry; } - break; - } + } /* if (TIME_BEFORE(current_tick, timeout)) */ error: ata_led(false); @@ -460,45 +645,30 @@ mutex_unlock(&ata_mtx); #endif + /* if success, HI0: Host_Idle + if failure, unknown state + */ return ret; } -#ifndef ATA_OPTIMIZED_WRITING -STATICIRAM ICODE_ATTR void copy_write_sectors(const unsigned char* buf, - int wordcount) -{ - if ( (unsigned long)buf & 1) - { /* not 16-bit aligned, copy byte by byte */ - unsigned short tmp = 0; - const unsigned char* bufend = buf + wordcount*2; - do - { -#if defined(SWAP_WORDS) || defined(ROCKBOX_LITTLE_ENDIAN) - tmp = (unsigned short) *buf++; - tmp |= (unsigned short) *buf++ << 8; - SET_16BITREG(ATA_DATA, tmp); -#else - tmp = (unsigned short) *buf++ << 8; - tmp |= (unsigned short) *buf++; - SET_16BITREG(ATA_DATA, tmp); -#endif - } while (buf < bufend); /* tail loop is faster */ - } - else - { /* 16-bit aligned, can do faster copy */ - unsigned short* wbuf = (unsigned short*)buf; - unsigned short* wbufend = wbuf + wordcount; - do - { -#ifdef SWAP_WORDS - SET_16BITREG(ATA_DATA, swap16(*wbuf)); +/* TODO: Make this a preprocessor define? */ +#ifdef MAX_PHYS_SECTOR_SIZE +static int _read_sectors(unsigned long start, + int incount, + void* inbuf) #else - SET_16BITREG(ATA_DATA, *wbuf); +int ata_read_sectors(IF_MV2(int drive,) + unsigned long start, + int incount, + void* inbuf) #endif - } while (++wbuf < wbufend); /* tail loop is faster */ - } +{ + return ata_transfer_sectors(IF_MV2(int drive,) + start, + incount, + inbuf, + false); } -#endif /* !ATA_OPTIMIZED_WRITING */ #ifdef MAX_PHYS_SECTOR_SIZE static int _write_sectors(unsigned long start, @@ -511,110 +681,11 @@ const void* buf) #endif { - int i; - int ret = 0; - long spinup_start; - -#ifndef MAX_PHYS_SECTOR_SIZE -#ifdef HAVE_MULTIVOLUME - (void)drive; /* unused for now */ -#endif - mutex_lock(&ata_mtx); -#endif - - if (start + count > total_sectors) - panicf("Writing past end of disk"); - - last_disk_activity = current_tick; - spinup_start = current_tick; - - ata_led(true); - - if ( sleeping ) { - spinup = true; - if (poweroff) { - if (ata_power_on()) { - ret = -1; - goto error; - } - } - else { - if (perform_soft_reset()) { - ret = -1; - goto error; - } - } - } - - SET_REG(ATA_SELECT, ata_device); - if (!wait_for_rdy()) - { - ret = -2; - goto error; - } - -#ifdef HAVE_LBA48 - if (lba48) - { - SET_REG(ATA_NSECTOR, count >> 8); - SET_REG(ATA_NSECTOR, count & 0xff); - SET_REG(ATA_SECTOR, (start >> 24) & 0xff); /* 31:24 */ - SET_REG(ATA_SECTOR, start & 0xff); /* 7:0 */ - SET_REG(ATA_LCYL, 0); /* 39:32 */ - SET_REG(ATA_LCYL, (start >> 8) & 0xff); /* 15:8 */ - SET_REG(ATA_HCYL, 0); /* 47:40 */ - SET_REG(ATA_HCYL, (start >> 16) & 0xff); /* 23:16 */ - SET_REG(ATA_SELECT, SELECT_LBA | ata_device); - SET_REG(ATA_COMMAND, CMD_WRITE_SECTORS_EXT); - } - else -#endif - { - SET_REG(ATA_NSECTOR, count & 0xff); /* 0 means 256 sectors */ - SET_REG(ATA_SECTOR, start & 0xff); - SET_REG(ATA_LCYL, (start >> 8) & 0xff); - SET_REG(ATA_HCYL, (start >> 16) & 0xff); - SET_REG(ATA_SELECT, ((start >> 24) & 0xf) | SELECT_LBA | ata_device); - SET_REG(ATA_COMMAND, CMD_WRITE_SECTORS); - } - - for (i=0; i= 5us */ +#endif + sleep(1); /* HSR0: Set_SRST : wait >= 5us */ +#if ATA_INTERRUPT + SET_REG(ATA_CONTROL, 0 ); +#else SET_REG(ATA_CONTROL, CONTROL_nIEN); - sleep(1); /* >2ms */ +#endif + sleep(1); /* HSR1: Clear_wait : wait >= 2ms */ + /* HSR2: Check_status */ + /* This little sucker can take up to 30 seconds */ retry_count = 8; do @@ -1175,6 +1256,7 @@ { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */ { 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */ { 82, 6, 0xaa, 0 }, /* enable read look-ahead */ + { 83, 14, 0x03, 0x40 | 2 }, /* UDMA2 */ }; int i; int pio_mode = 2; @@ -1289,6 +1371,12 @@ sleep(HZ/4); /* allow voltage to build up */ } +#if ATA_INTERRUPT + SET_REG(ATA_CONTROL, 0 ); +#else + SET_REG(ATA_CONTROL, CONTROL_nIEN); +#endif + /* first try, hard reset at cold start only */ rc = init_and_check(coldstart); diff -ur rockbox-svn-trunk/firmware/target/arm/ata-pp5020.c rockbox-19581-dma/firmware/target/arm/ata-pp5020.c --- rockbox-svn-trunk/firmware/target/arm/ata-pp5020.c 2008-12-24 13:34:48.765000000 -0500 +++ rockbox-19581-dma/firmware/target/arm/ata-pp5020.c 2008-12-24 14:03:34.900400000 -0500 @@ -25,6 +25,10 @@ #include "system.h" #include "ata-target.h" +#define IDE_DMA_CONTROL (*(volatile unsigned long *)(0xc3000400)) +#define IDE_DMA_LENGTH (*(volatile unsigned long *)(0xc3000408)) +#define IDE_DMA_ADDR (*(volatile unsigned long *)(0xc300040C)) + void ata_reset() { @@ -44,6 +48,7 @@ void ata_device_init() { +#if 0 /* From ipod-ide.c:ipod_ide_register() */ IDE0_CFG |= (1<<5); #ifdef IPOD_NANO @@ -54,4 +59,57 @@ IDE0_PRI_TIMING0 = 0x10; IDE0_PRI_TIMING1 = 0x80002150; +#endif + ata_set_pio_timings(0); + // FIXME wait ready + IDE_DMA_CONTROL |= 2; + IDE_DMA_CONTROL &= ~1; + IDE0_CFG &= ~0x8010; + IDE0_CFG |= 0x20; +} + +static const unsigned long pio80mhz[] = { + 0xC293, 0x43A2, 0x11A1, 0x7232, 0x3131 +}; + +void ata_set_pio_timings(int pio_mode) { + (*(volatile unsigned long *)(0x600060C4)) = 0xC0000000; /* 80 Mhz */ + IDE0_CFG &= ~0x10000000; + IDE0_PRI_TIMING0 = pio80mhz[pio_mode]; + IDE0_PRI_TIMING1 = 0x80003371; /* Fixme UDMA 2 */ + IDE0_CFG |= 0x20000000; /* >= 50 Mhz */ +} + +bool ata_dma_setup(void *addr, unsigned long sectors, bool write) { + if (sectors == 1 || ((unsigned long)addr & 3)) + return false; + + /* cpu_boost(true); unneeded for UltraDMA 2 (33) apparently */ + + IDE_DMA_CONTROL |= 2; + IDE_DMA_LENGTH = (sectors << 9) - 4; + IDE_DMA_ADDR = (unsigned long)addr + 0x10000000; + + if (write) + IDE_DMA_CONTROL &= ~8; + else + IDE_DMA_CONTROL |= 8; + + IDE0_CFG |= 0x8000; + return true; +} + +/* STATICIRAM */ ICODE_ATTR bool ata_wait_cfg(void); + +bool ata_dma_finish(void) { + IDE_DMA_CONTROL |= 1; /* Also works if at end of setup */ + + ata_wait_cfg(); + + IDE0_CFG &= ~0x8000; + IDE_DMA_CONTROL &= ~0x80000001; + + /* cpu_boost(false); unneeded for UltraDMA 2 (33) apparently */ + + return true; } diff -ur rockbox-svn-trunk/firmware/target/arm/ata-target.h rockbox-19581-dma/firmware/target/arm/ata-target.h --- rockbox-svn-trunk/firmware/target/arm/ata-target.h 2008-12-24 13:34:48.610000000 -0500 +++ rockbox-19581-dma/firmware/target/arm/ata-target.h 2008-12-24 14:03:34.900400000 -0500 @@ -82,3 +82,9 @@ void ata_enable(bool on); bool ata_is_coldstart(void); void ata_device_init(void); + +#define ATA_SET_DEVICE_FEATURES +void ata_set_pio_timings(int mode); + +bool ata_dma_finish(void); +bool ata_dma_setup(void *addr, unsigned long len, bool write); diff -ur rockbox-svn-trunk/firmware/target/arm/ipod/power-ipod.c rockbox-19581-dma/firmware/target/arm/ipod/power-ipod.c --- rockbox-svn-trunk/firmware/target/arm/ipod/power-ipod.c 2008-12-24 13:34:45.940000000 -0500 +++ rockbox-19581-dma/firmware/target/arm/ipod/power-ipod.c 2008-12-24 14:03:34.916000000 -0500 @@ -98,6 +98,7 @@ { GPO32_VAL &= ~0x40000000; DEV_EN |= DEV_IDE0; + ata_device_init(); } else { diff -ur rockbox-svn-trunk/firmware/target/arm/system-pp502x.c rockbox-19581-dma/firmware/target/arm/system-pp502x.c --- rockbox-svn-trunk/firmware/target/arm/system-pp502x.c 2008-12-24 13:34:48.735000000 -0500 +++ rockbox-19581-dma/firmware/target/arm/system-pp502x.c 2008-12-24 14:03:34.916000000 -0500 @@ -319,7 +319,7 @@ DEV_INIT2 = 0x40000000; /* reset all allowed devices */ - DEV_RS = 0x3ffffef8; + DEV_RS = 0x3ffffef8 & ~DEV_IDE0; DEV_RS2 = 0xffffffff; DEV_RS = 0x00000000; DEV_RS2 = 0x00000000;