/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 by Alan Korr * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include "ata.h" #include "kernel.h" #include "thread.h" #include "led.h" #include "sh7034.h" #include "system.h" #include "debug.h" #include "panic.h" #include "usb.h" #include "power.h" #include "string.h" /* Define one of USE_STANDBY, USE_SLEEP or USE_POWEROFF */ #define USE_SLEEP /* We can only use power off on the recorder */ #if !defined(ARCHOS_RECORDER) && defined(USE_POWEROFF) #undef USE_POWEROFF #define USE_SLEEP #endif #define SECTOR_SIZE 512 #define ATA_DATA (*((volatile unsigned short*)0x06104100)) #define ATA_ERROR (*((volatile unsigned char*)0x06100101)) #define ATA_FEATURE ATA_ERROR #define ATA_NSECTOR (*((volatile unsigned char*)0x06100102)) #define ATA_SECTOR (*((volatile unsigned char*)0x06100103)) #define ATA_LCYL (*((volatile unsigned char*)0x06100104)) #define ATA_HCYL (*((volatile unsigned char*)0x06100105)) #define ATA_SELECT (*((volatile unsigned char*)0x06100106)) #define ATA_COMMAND (*((volatile unsigned char*)0x06100107)) #define ATA_STATUS (*((volatile unsigned char*)0x06100107)) #define ATA_CONTROL1 ((volatile unsigned char*)0x06200206) #define ATA_CONTROL2 ((volatile unsigned char*)0x06200306) #define ATA_CONTROL (*ata_control) #define ATA_ALT_STATUS ATA_CONTROL #define SELECT_DEVICE1 0x10 #define SELECT_LBA 0x40 #define STATUS_BSY 0x80 #define STATUS_RDY 0x40 #define STATUS_DF 0x20 #define STATUS_DRQ 0x08 #define STATUS_ERR 0x01 #define CONTROL_nIEN 0x02 #define CONTROL_SRST 0x04 #define CMD_READ_SECTORS 0x20 #define CMD_WRITE_SECTORS 0x30 #define CMD_READ_MULTIPLE 0xC4 #define CMD_WRITE_MULTIPLE 0xC5 #define CMD_SET_MULTIPLE_MODE 0xC6 #define CMD_STANDBY_IMMEDIATE 0xE0 #define CMD_STANDBY 0xE2 #define CMD_IDENTIFY 0xEC #define CMD_SLEEP 0xE6 #define CMD_SECURITY_FREEZE_LOCK 0xF5 #define Q_SLEEP 0 #define READ_TIMEOUT 5*HZ static struct mutex ata_mtx; char ata_device; /* device 0 (master) or 1 (slave) */ int ata_io_address; /* 0x300 or 0x200, only valid on recorder */ static volatile unsigned char* ata_control; bool old_recorder = false; static bool sleeping = false; static int sleep_timeout = 5*HZ; static char ata_stack[DEFAULT_STACK_SIZE]; static char ata_thread_name[] = "ata"; static struct event_queue ata_queue; static bool initialized = false; static bool delayed_write = false; static unsigned char delayed_sector[SECTOR_SIZE]; static int delayed_sector_num; static long last_user_activity = -1; long last_disk_activity = -1; static int multisectors; /* number of supported multisectors */ static unsigned short identify_info[SECTOR_SIZE]; #ifdef USE_POWEROFF static int ata_power_on(void); #endif static int perform_soft_reset(void); static int wait_for_bsy(void) __attribute__ ((section (".icode"))); static int wait_for_bsy(void) { int timeout = current_tick + HZ*10; while (TIME_BEFORE(current_tick, timeout) && (ATA_ALT_STATUS & STATUS_BSY)) yield(); if (TIME_BEFORE(current_tick, timeout)) return 1; else return 0; /* timeout */ } static int wait_for_rdy(void) __attribute__ ((section (".icode"))); static int wait_for_rdy(void) { int timeout; if (!wait_for_bsy()) return 0; timeout = current_tick + HZ*10; while (TIME_BEFORE(current_tick, timeout) && !(ATA_ALT_STATUS & STATUS_RDY)) yield(); if (TIME_BEFORE(current_tick, timeout)) return STATUS_RDY; else return 0; /* timeout */ } static int wait_for_start_of_transfer(void) __attribute__ ((section (".icode"))); static int wait_for_start_of_transfer(void) { if (!wait_for_bsy()) return 0; return (ATA_ALT_STATUS & (STATUS_BSY|STATUS_DRQ)) == STATUS_DRQ; } static int wait_for_end_of_transfer(void) __attribute__ ((section (".icode"))); static int wait_for_end_of_transfer(void) { if (!wait_for_bsy()) return 0; return (ATA_ALT_STATUS & (STATUS_RDY|STATUS_DRQ)) == STATUS_RDY; } int ata_read_sectors(unsigned long start, int count, void* buf) __attribute__ ((section (".icode"))); int ata_read_sectors(unsigned long start, int incount, void* inbuf) { int ret = 0; int timeout; int count; void* buf; last_disk_activity = current_tick; mutex_lock(&ata_mtx); if ( sleeping ) { #ifdef USE_POWEROFF if (ata_power_on()) { mutex_unlock(&ata_mtx); return -1; } #else #ifdef USE_SLEEP if (perform_soft_reset()) { mutex_unlock(&ata_mtx); return -1; } #endif #endif sleeping = false; } if (!wait_for_rdy()) { mutex_unlock(&ata_mtx); return -2; } led(true); timeout = current_tick + READ_TIMEOUT; retry: buf = inbuf; count = incount; while (TIME_BEFORE(current_tick, timeout)) { if ( count == 256 ) ATA_NSECTOR = 0; /* 0 means 256 sectors */ else ATA_NSECTOR = (unsigned char)count; ATA_SECTOR = start & 0xff; ATA_LCYL = (start >> 8) & 0xff; ATA_HCYL = (start >> 16) & 0xff; ATA_SELECT = ((start >> 24) & 0xf) | SELECT_LBA | ata_device; ATA_COMMAND = CMD_READ_MULTIPLE; while (count) { int j; int sectors; int wordcount; if (!wait_for_start_of_transfer()) { ret = -4; goto retry; } if ( ATA_ALT_STATUS & (STATUS_ERR | STATUS_DF) ) goto retry; /* if destination address is odd, use byte copying, otherwise use word copying */ if (count >= multisectors ) sectors = multisectors; else sectors = count; wordcount = sectors * SECTOR_SIZE / 2; if ( (unsigned int)buf & 1 ) { for (j=0; j < wordcount; j++) { unsigned short tmp = SWAB16(ATA_DATA); ((unsigned char*)buf)[j*2] = tmp >> 8; ((unsigned char*)buf)[j*2+1] = tmp & 0xff; } } else { for (j=0; j < wordcount; j++) ((unsigned short*)buf)[j] = SWAB16(ATA_DATA); } #ifdef USE_INTERRUPT /* reading the status register clears the interrupt */ j = ATA_STATUS; #endif buf += sectors * SECTOR_SIZE; /* Advance one chunk of sectors */ count -= sectors; } if(!wait_for_end_of_transfer()) { ret = -3; goto retry; } ret = 0; break; } led(false); mutex_unlock(&ata_mtx); if ( delayed_write ) ata_flush(); last_disk_activity = current_tick; return ret; } int ata_write_sectors(unsigned long start, int count, void* buf) { int i; last_disk_activity = current_tick; mutex_lock(&ata_mtx); if ( sleeping ) { #ifdef USE_POWEROFF if (ata_power_on()) { mutex_unlock(&ata_mtx); return -1; } #else #ifdef USE_SLEEP if (perform_soft_reset()) { mutex_unlock(&ata_mtx); return -1; } #endif #endif sleeping = false; } if (!wait_for_rdy()) { mutex_unlock(&ata_mtx); return -2; } led(true); if ( count == 256 ) ATA_NSECTOR = 0; /* 0 means 256 sectors */ else ATA_NSECTOR = (unsigned char)count; ATA_SECTOR = start & 0xff; ATA_LCYL = (start >> 8) & 0xff; ATA_HCYL = (start >> 16) & 0xff; ATA_SELECT = ((start >> 24) & 0xf) | SELECT_LBA | ata_device; ATA_COMMAND = CMD_WRITE_SECTORS; for (i=0; i= 5us */ ATA_CONTROL = CONTROL_nIEN; sleep(HZ/400); /* >2ms */ /* This little sucker can take up to 30 seconds */ retry_count = 8; do { ret = wait_for_rdy(); } while(!ret && retry_count--); /* Massage the return code so it is 0 on success and -1 on failure */ ret = ret?0:-1; sleeping = false; return ret; } int ata_soft_reset(void) { int ret; mutex_lock(&ata_mtx); ret = perform_soft_reset(); mutex_unlock(&ata_mtx); return ret; } #ifdef USE_POWEROFF static int ata_power_on(void) { int ret; int retry_count; ide_power_enable(true); sleep(HZ/2); ATA_CONTROL = CONTROL_nIEN; /* This little sucker can take up to 30 seconds */ retry_count = 8; do { ret = wait_for_rdy(); } while(!ret && retry_count--); /* Massage the return code so it is 0 on success and -1 on failure */ ret = ret?0:-1; sleeping = false; return ret; } #endif static int master_slave_detect(void) { /* master? */ ATA_SELECT = 0; if ( ATA_STATUS & STATUS_RDY ) { ata_device = 0; DEBUGF("Found master harddisk\n"); } else { /* slave? */ ATA_SELECT = SELECT_DEVICE1; if ( ATA_STATUS & STATUS_RDY ) { ata_device = SELECT_DEVICE1; DEBUGF("Found slave harddisk\n"); } else return -1; } return 0; } static int io_address_detect(void) { unsigned char tmp = ATA_STATUS & 0xf9; /* Mask the IDX and CORR bits */ unsigned char dummy; /* We compare the STATUS register with the ALT_STATUS register, which is located at the same address as CONTROL. If they are the same, we assume that we have the correct address. We can't read the ATA_STATUS directly, since the read data will stay on the data bus if the following read does not assert the Chip Select to the ATA controller. We read a register that we know exists to make sure that the data on the bus isn't identical to the STATUS register contents. */ ATA_SECTOR = 0; dummy = ATA_SECTOR; if(tmp == ((*ATA_CONTROL2) & 0xf9)) { DEBUGF("CONTROL is at 0x306\n"); ata_io_address = 0x300; /* For debug purposes only */ old_recorder = true; ata_control = ATA_CONTROL2; } else { DEBUGF("CONTROL is at 0x206\n"); ata_io_address = 0x200; /* For debug purposes only */ old_recorder = false; ata_control = ATA_CONTROL1; } /* Let's check again, to be sure */ if(tmp != ATA_CONTROL) { DEBUGF("ATA I/O address detection failed\n"); return -1; } return 0; } void ata_enable(bool on) { if(on) PADR &= ~0x80; /* enable ATA */ else PADR |= 0x80; /* disable ATA */ PAIOR |= 0x80; } static int identify(void) { int i; if(!wait_for_rdy()) { DEBUGF("identify() - not RDY\n"); return -1; } ATA_SELECT = ata_device; ATA_COMMAND = CMD_IDENTIFY; if (!wait_for_start_of_transfer()) { DEBUGF("identify() - CMD failed\n"); return -2; } for (i=0; i