📄 ata_if.c
字号:
/*
Copyright (C) 2001 Jesper Hansen <jesperh@telia.com>.
Rewritten by: Nikolai Vorontsov <nickviz@mail.be>
This file is part of the yampp system.
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 2
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, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <io.h>
#include <interrupt.h>
#include <progmem.h>
#include "ata_if.h"
#include "delay.h"
#include "mem.h"
#include "uart.h"
// To speed up disk read
#define ASM_OPT
static u08 SetAddress(u08 addr);
static u08 ReadBYTE(u08 addr);
static void WriteBYTE(u08 addr, u08 dat);
static u08 ExecuteCommand(u08 NumSectors, u08 *Buffer);
static void CheckBusy(void);
static void CheckDataReady(void);
//----------------------------------------------------------------------------
// Init hard drive
//----------------------------------------------------------------------------
void ATA_Init(void)
{
WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_CHS0); // Set Drive/Head register,
// ie. select drive 0
CheckBusy(); // Wait for !BUSY
}
//----------------------------------------------------------------------------
// Reset hard drive(s)
//----------------------------------------------------------------------------
void ATA_SW_Reset(void)
{
WriteBYTE(CTRL_A + CTRL_DEV_CTRL, CT_SRST | CT_nIEN); delay10us();
WriteBYTE(CTRL_A + CTRL_DEV_CTRL, CT_nIEN); delay10us();
CheckBusy();
}
//----------------------------------------------------------------------------
// Read one or more sectors in CSH mode from drive 0
// Returns 0 if no error detected
//----------------------------------------------------------------------------
u08 ATA_ReadCHS0(u08 Head, u16 Track, u08 Sector, u08 NumSectors, u08 *Buffer)
{
cli();
// Prepare parameters...
CheckBusy();
WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_CHS0 | Head);
CheckDataReady();
WriteBYTE(CMD_A + CMD_CYL_HIGH, Track >> 8);
WriteBYTE(CMD_A + CMD_CYL_LOW, Track);
WriteBYTE(CMD_A + CMD_SECT_NUM, Sector);
WriteBYTE(CMD_A + CMD_SECT_CNT, NumSectors);
// Issue read sector command...
WriteBYTE(CMD_A + CMD_COMMAND, ATA_READ_SECTS);
delay10us();
#ifndef DEBUG_ATA
return ExecuteCommand(NumSectors, Buffer);
#else
{
u08 res = ExecuteCommand(NumSectors, Buffer);
PRINT("CHS RD\n");
DiskStat();
return res;
}
#endif
}
//----------------------------------------------------------------------------
// Read one or more sectors in LBA mode from drive 0
// Returns 0 if no error detected
//----------------------------------------------------------------------------
u08 ATA_ReadLBA0(u32 LBA, u08 NumSectors, u08 *Buffer)
{
union u32convert LBAC;
LBAC.value = LBA;
cli();
// Prepare parameters...
CheckBusy();
WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_LBA0 | LBAC.bytes.byte4); // <= 28 bits !!!
CheckDataReady();
WriteBYTE(CMD_A + CMD_CYL_HIGH, LBAC.bytes.byte3);
WriteBYTE(CMD_A + CMD_CYL_LOW, LBAC.bytes.byte2);
WriteBYTE(CMD_A + CMD_SECT_NUM, LBAC.bytes.byte1);
WriteBYTE(CMD_A + CMD_SECT_CNT, NumSectors);
// Issue read sector command...
WriteBYTE(CMD_A + CMD_COMMAND, ATA_READ_SECTS);
delay10us();
#ifndef DEBUG_ATA
return ExecuteCommand(NumSectors, Buffer);
#else
{
u08 res = ExecuteCommand(NumSectors, Buffer);
PRINT("LBA ");
UART_Printfu32(LBA);
PRINT(" ");
UART_Printfu16(NumSectors);
PRINT(" ");
DiskStat();
return res;
}
#endif
}
#define MINIBUFFERSIZE 32
u08 minibuffer[MINIBUFFERSIZE];
//----------------------------------------------------------------------------
// Execute read command for one or more sectors. ATA command is already issued.
// Returns 0 if no error detected
//----------------------------------------------------------------------------
u08 ExecuteCommand(u08 NumSectors, u08 *Buffer)
{
register u08 i;
#ifdef ASM_OPT
register u08 j;
asm volatile ("mov r26,r22");
asm volatile ("mov r27,r23");
#undef ENABLE_CRC
#endif
#ifdef ENABLE_CRC
u16 intCRC = 0, extCRC, buf_size = NumSectors * BPS;
u08* crc_buf = Buffer;
#endif // ENABLE_CRC
do // suppose that NumSectors at least 1
{ // for each sector requested
WDR; // enable full timeout for watchdog
// loop reading ALT STATUS until BUSY is cleared
while ((ReadBYTE(CTRL_A + CTRL_ALT_STAT) & SR_BUSY) == SR_BUSY);
// then read STATUS register to reset interrupt
u08 err = ReadBYTE(CMD_A + CMD_STATUS);
if (err & SR_ERR) // fail on error
{
PRINT("CMD ERR\n");
DiskStat();
return err;
}
if (!(err & SR_DRQ)) // failure if DRQ not set
{
PRINT("NO DATA\n");
DiskStat();
return err;
}
#ifdef ASM_OPT
i = 0;
do // cycle sector by minibuffers
{
outp(0x00, DDRA); // port A as input
outp(0x00, DDRC); // port C as input
outp(0xff, PORTA); // activate Pullups
outp(0xff, PORTC); // activate Pullups
SetAddress(CMD_A + CMD_DATA); // setup addressing and chip selects
cbi(MCUCR, SRE); // disable ExtRAM and store data into
// the temporary buffer
asm volatile ("ldi r30,lo8(minibuffer)");
asm volatile ("ldi r31,hi8(minibuffer)");
j = 0;
do // cycle minibuffer by words
{
cbi(PORTB, 1); // set DIOR lo
j += 2; // nop replacement, allow pin change
inp(PINA); // read lo byte
asm volatile ("st Z+,r24"); // store to minibuf
inp(PINC); // read hi byte
asm volatile ("st Z+,r24"); // store to minibuf
sbi(PORTB, 1); // set DIOR hi
} while (j < MINIBUFFERSIZE);
sbi(MCUCR, SRE); // enable ExtRAM and flush temporary
// buffer
asm volatile ("ldi r30,lo8(minibuffer)");
asm volatile ("ldi r31,hi8(minibuffer)");
j = 0;
do
{
asm volatile ("ld r24,Z+");
asm volatile ("st X+,r24");
} while (++j < MINIBUFFERSIZE);
} while (++i < BPS / MINIBUFFERSIZE);
///#endif // ASM_OPT
#else // ASM_OPT
///#ifdef ASM_OPT
u08 *miniPtr;
for (i = 0; i < BPS / MINIBUFFERSIZE; i++)
{
outp(0x00, DDRA); // port A as input
outp(0x00, DDRC); // port C as input
outp(0xff, PORTA); // activate Pullups
outp(0xff, PORTC); // activate Pullups
SetAddress(CMD_A + CMD_DATA); // setup addressing and chip selects
cbi(MCUCR, SRE); // Disable ExtRAM and store data into
// the temporary buffer
for (miniPtr = minibuffer; miniPtr < &minibuffer[MINIBUFFERSIZE];)
{
cbi(PORTB, 1); // set DIOR lo
asm volatile ("nop"); // allow pin change
#ifdef ENABLE_CRC
err = inp(PINA);
intCRC += err;
*miniPtr++ = err;
err = inp(PINC);
intCRC += err;
*miniPtr++ = err;
#else // ENABLE_CRC
*miniPtr++ = inp(PINA); // read lo byte
*miniPtr++ = inp(PINC); // read hi byte
#endif // ENABLE_CRC
sbi(PORTB, 1); // set DIOR hi
}
sbi(MCUCR, SRE); // enable ExtRAM and flush temporary buffer
for (miniPtr = minibuffer; miniPtr < &minibuffer[MINIBUFFERSIZE];)
*Buffer++ = *miniPtr++;
} // for (i < BPS / MINIBUF
#endif // ASM_OPT
} while (--NumSectors != 0);
sei(); // enable interrupt again
#ifdef ENABLE_CRC
extCRC = CalculateCRC(crc_buf, buf_size);
if (extCRC != intCRC)
{
UART_Puts("Err chk ");
UART_Printfu16(intCRC);
UART_Printfu16(extCRC);
UART_Puts(" ata, buf ");
UART_Printfu16((u16)crc_buf);
UART_Printfu16(buf_size);
EOL();
}
if (crc_buf >= (u08*)BUFFERSTART)
crcs[((u16)crc_buf - BUFFERSTART) / 0x1000] = extCRC;
#endif // ENABLE_CRC
return 0;
}
//----------------------------------------------------------------------------
// Select address and CS signals
//
// addressing bits
// 35 DA0 A0 0x01 Address Line 0
// 33 DA1 A1 0x02 Address Line 1
// 36 DA2 A2 0x04 Address Line 2
//
// chip selects
// 37 CS0 A3 0x08 Command Block Select
// 38 CS1 A4 0x10 Control Block Select
//
//
//----------------------------------------------------------------------------
u08 SetAddress(u08 addr)
{
// register u16 i;
//
// if (cs == CTRL)
// i = 0xE000 + adr + 0x08; // select A4 low -> CS1 -> CTRL
// else
// i = 0xE000 + adr + 0x10; // select A3 low -> CS0 -> CMD
//
// return *(u08 *)i;
return *(u08 *)(0xE000 + addr);
// dummy return to avoid optimization problems
}
//----------------------------------------------------------------------------
// Read data BYTE from Drive
//----------------------------------------------------------------------------
u08 ReadBYTE(u08 addr)
{
register u08 tmp;
SetAddress(addr);
cbi(MCUCR, SRE); // disable ExtRAM
outp(0x00, DDRA); // port A as input
outp(0x00, DDRC); // port C as input
outp(0xff, PORTA); // activate Pullups
outp(0xff, PORTC); // activate Pullups
cbi(PORTB, 1); // set DIOR lo
asm volatile ("nop"); // allow pin change
tmp = inp(PINA); // read byte
sbi(PORTB, 1); // set DIOR hi
sbi(MCUCR, SRE); // enable ExtRAM
return tmp;
}
//----------------------------------------------------------------------------
// Write data BYTE to Drive
//----------------------------------------------------------------------------
void WriteBYTE(u08 addr, u08 dat)
{
SetAddress(addr);
outp(0xff, DDRA); // port A as output
outp(0xff, DDRC); // port C as output
cbi(MCUCR, SRE); // disable ExtRAM
asm volatile ("nop"); // allow pin change
cbi(PORTB, 0); // set DIOW lo
asm volatile ("nop"); // allow pin change
outp(dat, PORTA); // write byte
sbi(PORTB, 0); // set DIOW hi
sbi(MCUCR, SRE); // enable ExtRAM
}
void CheckBusy(void)
{
WDR;
while ((ReadBYTE(CMD_A + CMD_STATUS) & SR_BUSY) == SR_BUSY);
}
void CheckDataReady(void)
{
WDR;
while ((ReadBYTE(CMD_A + CMD_STATUS) & (SR_BUSY | SR_DRDY)) != SR_DRDY);
}
#ifdef DEBUG_ATA
void DiskStat(void)
{
register u08 b;
sei();
b = ReadBYTE(CMD_A + CMD_ERROR);
PRINT("Error : "); UART_Printfu08(b); EOL();
if (b)
{
b = ReadBYTE(CMD_A + CMD_SECT_CNT);
PRINT("SecCnt: "); UART_Printfu08(b); EOL();
b = ReadBYTE(CMD_A + CMD_SECT_NUM);
PRINT("SecNbr: "); UART_Printfu08(b); EOL();
b = ReadBYTE(CMD_A + CMD_CYL_LOW);
PRINT("CylLow: "); UART_Printfu08(b); EOL();
b = ReadBYTE(CMD_A + CMD_CYL_HIGH);
PRINT("CylHi : "); UART_Printfu08(b); EOL();
b = ReadBYTE(CMD_A + CMD_DEV_HEAD);
PRINT("Dev/Hd: "); UART_Printfu08(b); EOL();
b = ReadBYTE(CMD_A + CMD_STATUS);
PRINT("Status: "); UART_Printfu08(b); EOL();
}
}
u08 IdentifyDrive0(u08 *Buffer)
{
// Prepare parameters...
CheckBusy();
WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_CHS0);
CheckDataReady();
// Issue Identify command...
WriteBYTE(CMD_A + CMD_COMMAND, ATA_IDENTIFY);
delay10us();
return ExecuteCommand(1, Buffer);
}
#endif // DEBUG_ATA
#ifdef ENABLE_CRC
u16 crcs[10];
u16 CalculateCRC(u08* buf, u16 size)
{
u08* end = &buf[size];
u16 crc = 0;
while (buf != end) crc += *buf++;
return crc;
}
#endif // ENABLE_CRC
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -