ollie <[EMAIL PROTECTED]> writes:
> P.S. Actually, we are doing almost the same as what you described.
There are some similiarities and the basic principles are the same,
but the techniques are noticeably different.
Your maximum wait appears to be 26 seconds instead of 30.
0xFFFF * 400us.
Also I'm not at all a fan of inb_p/outb_p. I get the feeling
sometimes there is deep magic involved, in the pause length selected.
Or even if the pause is what is really desired. At least so far I
haven't heard a clear explination of what the gain of outputing a
value to another port is.
I've attached my code so you can compare.
Just skimming it I am certain your ide code won't detect cd's or
any other packet devices. Because after a reset they are required
to clear bits 6,5,4,3,2 and 0 after reset in the status register.
Polling the sector count register until it takes a stable value
(implying that BSY has finally cleared) is sane, but unless your
objective is to support slave devices without a master it is much
more sane to simply poll BSY in the status register.
The IDE spec says while BSY is set all other values are undefined.
Running around and poking into other registers when you haven't read
BSY to make certain it is clear gives me the creeps.
Beyond that it looks like you have put together some good
infrastructure.
Eric
#include <stdint.h>
#include <printk.h>
#include <arch/io.h>
#include <delay.h>
#include <ide.h>
#include "ide.h"
unsigned ide_probe(unsigned base, unsigned basedrive)
{
/* This routine implements an IDE power on and drive
* detection sequence.
*
* The steps.
* 1. Wait for BSY bit to clear. It is unknown what command
* set the BSY bit so the maximum wait for all commands of
* 30 seconds is used.
* 2. Disable interrupts and start a software reset. Then
* wait up to 30 seconds for the BSY bit to clear.
* 3. Read the device signatures.
* 4. Verify devices with correct signatures have writeable
* registers.
*
* There is a pathlogical case here where the BSY bit floats
* hi. In which case there is no fully correct approach short
* of waiting 30 seconds to recognize there is no drive
* present.
*
* It may be possible to detect this case by testing the
* status register for 0xFF. As I have not yet encountered
* this pathlogical case in practice I have no need to cheat
* it.
*
* So far I have only noticed a slowdown in step 1, from
* drives doing spinup despite the fact they are allowed to
* be slow in step 2 as well.
*
* There are several places in the code below where there are
* hard coded 50ms sleeps. I use this number when switching
* drives because it is reasonable to have a delay here,
* and the linux ide code indicates 50ms is polite interval to
* sleep. In the timing loops 50ms is used so a fixed number
* of loop interations can be used to implement a timeout.
* The timing loops actually timeout at 31 seconds to be on
* the safe side.
*
*/
struct ide_device {
uint8_t sig[5];
uint8_t error;
unsigned type;
} drive[2];
unsigned char status;
unsigned counter;
int device;
int i;
printk_info("Probing ide%d\n", basedrive/2);
/* Wait until the bsy bit is clear before doing anything */
mdelay(2);
status = inb(IDE_REG_STATUS(base));
counter = 0;
while((status & IDE_STATUS_BSY) && (++counter < 620)) {
mdelay(50);
status = inb(IDE_REG_STATUS(base));
}
if (status & IDE_STATUS_BSY) {
printk_info("ide%d appears dead\n");
return 0;
}
/* Do a software reset of the IDE channel and wait for it to finish */
outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, IDE_REG_CONTROL(base));
udelay(50);
outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_CONTROL(base));
mdelay(2);
counter = 0;
status = inb(IDE_REG_STATUS(base));
while((status & IDE_STATUS_BSY) && (++counter < 620)) {
mdelay(50);
status = inb(IDE_REG_STATUS(base));
}
if (status & IDE_STATUS_BSY) {
printk_info("ide%d did not complete a software reset\n");
return 0;
}
/* Read the signatures before I spoil them */
for(device = 0; device < 2; device++) {
outb(device << 4, IDE_REG_DEVICE(base));
mdelay(50);
drive[device].error = inb(IDE_REG_ERROR(base));
drive[device].sig[0] = inb(IDE_REG_SECTOR_COUNT(base));
drive[device].sig[1] = inb(IDE_REG_LBA_LOW(base));
drive[device].sig[2] = inb(IDE_REG_LBA_MID(base));
drive[device].sig[3] = inb(IDE_REG_LBA_HIGH(base));
drive[device].sig[4] = inb(IDE_REG_DEVICE(base));
}
for(device = 0; device < 2; device++) {
unsigned type;
printk_info("hd%c: ", 'a' + basedrive + device);
outb(device << 4, IDE_REG_DEVICE(base));
mdelay(50);
printk_spew("<%02x %02x %02x %02x %02x : %02x %02x> ",
drive[device].sig[0],
drive[device].sig[1],
drive[device].sig[2],
drive[device].sig[3],
drive[device].sig[4],
drive[device].error, status);
type = DRIVE_ABSENT;
if (
(drive[device].sig[0] == 0x01) &&
(drive[device].sig[1] == 0x01) &&
(drive[device].sig[2] == 0x00) &&
(drive[device].sig[3] == 0x00)) {
type = DRIVE_ATA;
}
else if (
(drive[device].sig[0] == 0x01) &&
(drive[device].sig[1] == 0x01) &&
(drive[device].sig[2] == 0x14) &&
(drive[device].sig[3] == 0xeb)) {
type = DRIVE_ATAPI;
}
/* verify we can write to the drive registers */
if (type != DRIVE_ABSENT) {
for(i = 0; i < 4; i++) {
outb(0xaa + i, base + 2 + i);
}
for(i = 0; i < 4; i++) {
if (inb(base +2 +i) != 0xaa +i) {
type = DRIVE_ABSENT;
printk_debug("<failed test1> ");
break;
}
}
}
if (type != DRIVE_ABSENT) {
for(i = 0; i < 4; i++) {
outb(0x55 + i, base + 2 + i);
}
for(i = 0; i < 4; i++) {
if (inb(base +2 +i) != 0x55 +i) {
type = DRIVE_ABSENT;
printk_debug("<failed test2> ");
break;
}
}
}
drive[device].type = type;
if (type == DRIVE_ATA) {
printk_info("ATA Drive\n");
}
else if (type == DRIVE_ATAPI) {
printk_info("ATAPI Device\n");
}
else {
printk_info("not present\n");
}
}
return (drive[1].type << DRIVE_SHIFT) | drive[0].type;
}
/*
* UBL, The Universal Talkware Boot Loader
* Copyright (C) 2000 Universal Talkware Inc.
*
* 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
*
* Note: Parts of this code grew out of the Openbios effort that was
* abandoned. Without the info that we were able to learn from
* OpenBios this program would have never worked. We are forever
* grateful for those who came before us.
*
* This code can be retrieved in a machine/human readable form at:
*
* http://www.talkware.net/GPL/UBL
*
* Anyone making changes to this code please send them to us so we
* can include them in the standard release.
*
* $Id: ide.h,v 1.1 2002/02/05 03:06:50 eric-cvs Exp $
*
*/
#if !defined(IDE_H_INCLUDE)
#define IDE_H_INCLUDE
#include <types.h>
typedef struct {
unsigned short controller_port;
unsigned short num_heads;
unsigned short num_cylinders;
unsigned short num_sectors_per_track;
unsigned long num_sectors; /* total */
unsigned char address_mode; /* am i lba (0x40) or chs (0x00) */
unsigned char drive_exists;
} harddisk_info_t;
#define NUM_HD (2)
extern harddisk_info_t harddisk_info[NUM_HD];
extern int ide_init(void);
extern int ide_read_sector(int driveno, void * buf, unsigned int sector,
int byte_offset, int n_bytes);
extern int ide_read_data(unsigned base, void * buf, size_t size);
extern int ide_write_data(unsigned base, void * buf, size_t size);
extern int ide_shutdown(void);
#define IDE_SECTOR_SIZE 0x200
#define IDE_BASE1 (0x1F0u) /* primary controller */
#define IDE_BASE2 (0x170u) /* secondary */
#define IDE_BASE3 (0x0F0u) /* third */
#define IDE_BASE4 (0x070u) /* fourth */
#define IDE_REG_EXTENDED_OFFSET (0x200u)
#define IDE_REG_DATA(base) ((base) + 0u) /* word register */
#define IDE_REG_ERROR(base) ((base) + 1u)
#define IDE_REG_PRECOMP(base) ((base) + 1u)
#define IDE_REG_FEATURE(base) ((base) + 1u)
#define IDE_REG_SECTOR_COUNT(base) ((base) + 2u)
#define IDE_REG_SECTOR_NUMBER(base) ((base) + 3u)
#define IDE_REG_LBA_LOW(base) ((base) + 3u)
#define IDE_REG_CYLINDER_LSB(base) ((base) + 4u)
#define IDE_REG_LBA_MID(base) ((base) + 4u)
#define IDE_REG_CYLINDER_MSB(base) ((base) + 5u)
#define IDE_REG_LBA_HIGH(base) ((base) + 5u)
#define IDE_REG_DRIVEHEAD(base) ((base) + 6u)
#define IDE_REG_DEVICE(base) ((base) + 6u)
#define IDE_REG_STATUS(base) ((base) + 7u)
#define IDE_REG_COMMAND(base) ((base) + 7u)
#define IDE_REG_ALTSTATUS(base) ((base) + IDE_REG_EXTENDED_OFFSET + 6u)
#define IDE_REG_CONTROL(base) ((base) + IDE_REG_EXTENDED_OFFSET + 6u)
#define IDE_REG_ADDRESS(base) ((base) + IDE_REG_EXTENDED_OFFSET + 7u)
typedef struct {
unsigned char precomp;
unsigned char sector_count;
unsigned char sector_number;
unsigned short cylinder;
unsigned char drivehead;
# define IDE_DH_DEFAULT (0xA0)
# define IDE_DH_HEAD(x) ((x) & 0x0F)
# define IDE_DH_MASTER (0x00)
# define IDE_DH_SLAVE (0x10)
# define IDE_DH_DRIVE(x) ((((x) & 1) != 0)?IDE_DH_SLAVE:IDE_DH_MASTER)
# define IDE_DH_LBA (0x40)
# define IDE_DH_CHS (0x00)
} ide_cmd_param_t;
#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT }
typedef enum {
IDE_CMD_NOOP = 0,
IDE_CMD_RECALIBRATE = 0x10,
IDE_CMD_READ_MULTI_RETRY = 0x20,
IDE_CMD_READ_MULTI = IDE_CMD_READ_MULTI_RETRY,
IDE_CMD_READ_MULTI_NORETRY = 0x21,
IDE_CMD_DRIVE_DIAG = 0x90,
IDE_CMD_SET_PARAMS = 0x91,
IDE_CMD_STANDBY_IMMEDIATE = 0x94, /* 2 byte command- also send
IDE_CMD_STANDBY_IMMEDIATE2 */
IDE_CMD_SET_MULTIMODE = 0xC6,
IDE_CMD_STANDBY_IMMEDIATE2 = 0xE0,
IDE_CMD_GET_INFO = 0xEC
} ide_command_t;
#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */
#define IDE_ERR_BBK 0x80 /* ATA bad block */
#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */
#define IDE_ERR_MC 0x20 /* ATA media change */
#define IDE_ERR_IDNF 0x10 /* ATA id not found */
#define IDE_ERR_MCR 0x08 /* ATA media change request */
#define IDE_ERR_ABRT 0x04 /* ATA command aborted */
#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */
#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */
#define IDE_STATUS_BSY 0x80 /* busy */
#define IDE_STATUS_RDY 0x40 /* ready */
#define IDE_STATUS_DF 0x20 /* device fault */
#define IDE_STATUS_WFT 0x20 /* write fault (old name) */
#define IDE_STATUS_SKC 0x10 /* seek complete */
#define IDE_STATUS_DRQ 0x08 /* data request */
#define IDE_STATUS_CORR 0x04 /* corrected */
#define IDE_STATUS_IDX 0x02 /* index */
#define IDE_STATUS_ERR 0x01 /* error (ATA) */
#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */
#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */
#define IDE_CTRL_SRST 0x04 /* soft reset */
#define IDE_CTRL_NIEN 0x02 /* disable interrupts */
#if 0
/* Most mandtory and optional ATA commands (from ATA-3), */
#define IDE_CMD_CFA_ERASE_SECTORS 0xC0
#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87
#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
#define IDE_CMD_CHECK_POWER_MODE1 0xE5
#define IDE_CMD_CHECK_POWER_MODE2 0x98
#define IDE_CMD_DEVICE_RESET 0x08
#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
#define IDE_CMD_FLUSH_CACHE 0xE7
#define IDE_CMD_FORMAT_TRACK 0x50
#define IDE_CMD_IDENTIFY_DEVICE 0xEC
#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1
#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1
#define IDE_CMD_IDLE1 0xE3
#define IDE_CMD_IDLE2 0x97
#define IDE_CMD_IDLE_IMMEDIATE1 0xE1
#define IDE_CMD_IDLE_IMMEDIATE2 0x95
#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
#define IDE_CMD_NOP 0x00
#define IDE_CMD_PACKET 0xA0
#define IDE_CMD_READ_BUFFER 0xE4
#define IDE_CMD_READ_DMA 0xC8
#define IDE_CMD_READ_DMA_QUEUED 0xC7
#define IDE_CMD_READ_MULTIPLE 0xC4
#define IDE_CMD_READ_SECTORS 0x20
#define IDE_CMD_READ_VERIFY_SECTORS 0x40
#define IDE_CMD_RECALIBRATE 0x10
#define IDE_CMD_SEEK 0x70
#define IDE_CMD_SET_FEATURES 0xEF
#define IDE_CMD_SET_MULTIPLE_MODE 0xC6
#define IDE_CMD_SLEEP1 0xE6
#define IDE_CMD_SLEEP2 0x99
#define IDE_CMD_STANDBY1 0xE2
#define IDE_CMD_STANDBY2 0x96
#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0
#define IDE_CMD_STANDBY_IMMEDIATE2 0x94
#define IDE_CMD_WRITE_BUFFER 0xE8
#define IDE_CMD_WRITE_DMA 0xCA
#define IDE_CMD_WRITE_DMA_QUEUED 0xCC
#define IDE_CMD_WRITE_MULTIPLE 0xC5
#define IDE_CMD_WRITE_SECTORS 0x30
#define IDE_CMD_WRITE_VERIFY 0x3C
#endif
#endif /* IDE_H_INCLUDE */