/*************************************************************************** * __________ __ ___. * 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" #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_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_STANDBY_IMMEDIATE 0xE0 #define CMD_STANDBY 0xE2 #define CMD_SLEEP 0xE6 #define CMD_SECURITY_FREEZE_LOCK 0xF5 #define Q_SLEEP 0 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_timer = 0; 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 int wait_for_bsy(void) __attribute__ ((section (".icode"))); static int wait_for_bsy(void) { int timeout = current_tick + HZ*4; 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) { if (!wait_for_bsy()) return 0; return ATA_ALT_STATUS & STATUS_RDY; } 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, unsigned char count, void* buf) __attribute__ ((section (".icode"))); int ata_read_sectors(unsigned long start, unsigned char count, void* buf) { int i; int ret = 0; if ( sleeping ) { if (ata_soft_reset()) { return -1; } } mutex_lock(&ata_mtx); sleep_timer = sleep_timeout; if (!wait_for_rdy()) { mutex_unlock(&ata_mtx); return -1; } led(true); ATA_NSECTOR = 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_SECTORS; for (i=0; i> 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; mutex_unlock(&ata_mtx); return ret; } 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; } int ata_init(void) { mutex_init(&ata_mtx); led(false); ata_enable(true); if ( !initialized ) { if (master_slave_detect()) return -1; if (io_address_detect()) return -2; if (check_registers()) return -3; if (freeze_lock()) return -4; queue_init(&ata_queue); create_thread(ata_thread, ata_stack, sizeof(ata_stack), ata_thread_name); tick_add_task(ata_tick); initialized = true; } ATA_SELECT = SELECT_LBA; ATA_CONTROL = CONTROL_nIEN; return 0; }