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 */










Reply via email to