📄 ata.c
字号:
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2002 by Linus Nielsen Feltzing
*
* 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 <unistd.h>
#include "ata.h"
#include "fat_file.h"
#include "system.h"
#include "debug.h"
#include "altera_avalon_cf_regs.h"
#include "altera_avalon_cf.h"
#include "sys/alt_alarm.h"
#ifndef CF_IDE_BASE
#error This IDE software requires a CompactFlash Interface component named "cf"
#endif
#ifndef CF_CTL_BASE
#error This IDE software requires a CompactFlash Interface component named "cf"
#endif
#define ide_base (int*)CF_IDE_BASE
#define ctl_base (int*)CF_CTL_BASE
/* wrap-safe macros for tick comparison */
#define TIME_AFTER(a,b) ((long)(b) - (long)(a) < 0)
#define TIME_BEFORE(a,b) TIME_AFTER(b,a)
#define ATA_STATUS IORD_ALTERA_AVALON_CF_IDE_STATUS( ide_base )
#define ATA_ALT_STATUS IORD_ALTERA_AVALON_CF_IDE_ALTERNATE_STATUS( ide_base )
#define ATA_DATA IORD_ALTERA_AVALON_CF_IDE_DATA( ide_base )
#define ATA_ERROR IORD_ALTERA_AVALON_CF_IDE_ERROR( ide_base )
#define SECTOR_SIZE 512
#define WRITE_PATTERN1 0xa5
#define WRITE_PATTERN2 0x5a
#define WRITE_PATTERN3 0xaa
#define WRITE_PATTERN4 0x55
#define STATUS_BSY 0x80
#define STATUS_RDY 0x40
#define STATUS_DF 0x20
#define STATUS_DRQ 0x08
#define STATUS_ERR 0x01
#define ERROR_ABRT 0x0400
#define SELECT_DEVICE1 0x10
#define SELECT_LBA 0x40
#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_SET_FEATURES 0xEF
#define CMD_SECURITY_FREEZE_LOCK 0xF5
static bool initialized = false;
char ata_device; /* device 0 (master) or 1 (slave) */
long last_disk_activity = -1;
static int multisectors; /* number of supported multisectors */
static unsigned short identify_info[SECTOR_SIZE];
static int check_registers(void)
{
int i;
if ( ATA_STATUS & STATUS_BSY )
return -1;
for (i = 0; i<64; i++) {
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT(ide_base, WRITE_PATTERN1);
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_NUMBER(ide_base, WRITE_PATTERN2);
IOWR_ALTERA_AVALON_CF_IDE_CYLINDER_LOW(ide_base, WRITE_PATTERN3);
IOWR_ALTERA_AVALON_CF_IDE_CYLINDER_HIGH(ide_base, WRITE_PATTERN4);
if ( (IORD_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base ) == WRITE_PATTERN1) &&
(IORD_ALTERA_AVALON_CF_IDE_SECTOR_NUMBER( ide_base ) == WRITE_PATTERN2) &&
(IORD_ALTERA_AVALON_CF_IDE_CYLINDER_LOW( ide_base ) == WRITE_PATTERN3) &&
(IORD_ALTERA_AVALON_CF_IDE_CYLINDER_HIGH( ide_base ) == WRITE_PATTERN4) )
return 0;
}
return -2;
}
static int master_slave_detect(void)
{
/* master? */
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD(ide_base, 0);
if ( ATA_STATUS & (STATUS_RDY|STATUS_BSY) ) {
ata_device = 0;
DEBUGF("Found master harddisk\n");
}
else {
/* slave? */
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD(ide_base, 0);
if ( ATA_STATUS & (STATUS_RDY|STATUS_BSY) ) {
ata_device = SELECT_DEVICE1;
DEBUGF("Found slave harddisk\n");
}
else
return -1;
}
return 0;
}
void yield( void )
{
return;
}
static int wait_for_bsy(void)
{
long timeout = alt_nticks() + (alt_ticks_per_second() * 30);
while (TIME_BEFORE(alt_nticks(), timeout) && (ATA_STATUS & STATUS_BSY)) {
last_disk_activity = alt_nticks();
yield();
}
if (TIME_BEFORE(alt_nticks(), timeout))
return 1;
else
return 0; /* timeout */
}
static int wait_for_rdy(void)
{
long timeout;
if (!wait_for_bsy())
return 0;
timeout = alt_nticks() + (alt_ticks_per_second() * 10);
while (TIME_BEFORE(alt_nticks(), timeout) &&
!(ATA_ALT_STATUS & STATUS_RDY)) {
last_disk_activity = alt_nticks();
yield();
}
if (TIME_BEFORE(alt_nticks(), timeout))
return STATUS_RDY;
else
return 0; /* timeout */
}
static int init_and_check(bool reset)
{
int rc;
if ( reset )
{
if (alt_avalon_ide_cf_init( ide_base ))
return -1;
}
rc = master_slave_detect();
if (rc)
return -10 + rc;
rc = check_registers();
if (rc)
return -30 + rc;
return 0;
}
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 identify(void)
{
int i;
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, ata_device );
if(!wait_for_rdy()) {
DEBUGF("identify() - not RDY\n");
return -1;
}
IOWR_ALTERA_AVALON_CF_IDE_COMMAND( ide_base, CMD_IDENTIFY);
if (!wait_for_start_of_transfer())
{
DEBUGF("identify() - CMD failed\n");
return -2;
}
for (i=0; i<SECTOR_SIZE/2; i++) {
/* the IDENTIFY words are already swapped, so we need to treat
this info differently that normal sector data */
#ifdef SWAP_WORDS
identify_info[i] = ATA_DATA;
#else
identify_info[i] = SWAB16(ATA_DATA);
#endif
}
return 0;
}
static int freeze_lock(void)
{
/* does the disk support Security Mode feature set? */
if (identify_info[82] & 2)
{
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, ata_device );
if (!wait_for_rdy())
return -1;
IOWR_ALTERA_AVALON_CF_IDE_COMMAND( ide_base, CMD_SECURITY_FREEZE_LOCK );
if (!wait_for_rdy())
return -2;
}
return 0;
}
static int set_features(void)
{
struct {
unsigned char id_word;
unsigned char id_bit;
unsigned char subcommand;
unsigned char parameter;
} features[] = {
{ 83, 3, 0x05, 0x80 }, /* power management: lowest power without standby */
{ 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */
{ 82, 6, 0xaa, 0 }, /* enable read look-ahead */
{ 83, 14, 0x03, 0 }, /* force PIO mode */
{ 0, 0, 0, 0 } /* <end of list> */
};
int i;
int pio_mode = 2;
/* Find out the highest supported PIO mode */
if(identify_info[64] & 2)
pio_mode = 4;
else
if(identify_info[64] & 1)
pio_mode = 3;
/* Update the table */
features[3].parameter = 8 + pio_mode;
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, ata_device );
// SET_REG(ATA_SELECT, ata_device);
if (!wait_for_rdy()) {
DEBUGF("set_features() - not RDY\n");
return -1;
}
for (i=0; features[i].id_word; i++) {
if (identify_info[features[i].id_word] & (1 << features[i].id_bit)) {
IOWR_ALTERA_AVALON_CF_IDE_FEATURES( ide_base, features[i].subcommand );
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base, features[i].parameter );
IOWR_ALTERA_AVALON_CF_IDE_COMMAND( ide_base, CMD_SET_FEATURES );
if (!wait_for_rdy()) {
DEBUGF("set_features() - CMD failed\n");
return -10 - i;
}
if(ATA_ALT_STATUS & STATUS_ERR) {
if(ATA_ERROR & ERROR_ABRT) {
return -20 - i;
}
}
}
}
return 0;
}
static int set_multiple_mode(int sectors)
{
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, ata_device );
if(!wait_for_rdy()) {
DEBUGF("set_multiple_mode() - not RDY\n");
return -1;
}
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base, sectors );
IOWR_ALTERA_AVALON_CF_IDE_COMMAND( ide_base, CMD_SET_MULTIPLE_MODE );
if (!wait_for_rdy())
{
DEBUGF("set_multiple_mode() - CMD failed\n");
return -2;
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -