Skip to content
Snippets Groups Projects
cmd_ide.c 50.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	int max_bus_scan;
    	int retries = 0;
    	char *s;
    	int do_retry = 0;
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #if 0
    	int mode, cycle_time;
    #endif
    	int device;
    	device=dev_desc->dev;
    	printf ("  Device %d: ", device);
    
    
    #ifdef CONFIG_AMIGAONEG3SE
    	s = getenv("ide_maxbus");
    	if (s) {
    		max_bus_scan = simple_strtol(s, NULL, 10);
    	} else {
    		max_bus_scan = CFG_IDE_MAXBUS;
    	}
    	if (device >= max_bus_scan*2) {
    		dev_desc->type=DEV_TYPE_UNKNOWN;
    		return;
    	}
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	ide_led (DEVICE_LED(device), 1);	/* LED on	*/
    	/* Select device
    	 */
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	dev_desc->if_type=IF_TYPE_IDE;
    #ifdef CONFIG_ATAPI
    
    
    #ifdef CONFIG_AMIGAONEG3SE
        do_retry = 0;
        retries = 0;
    
        /* Warning: This will be tricky to read */
    
        while (retries <= 1) {
    
    #endif	/* CONFIG_AMIGAONEG3SE */
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* check signature */
    
    	if ((ide_inb(device,ATA_SECT_CNT) == 0x01) &&
    		 (ide_inb(device,ATA_SECT_NUM) == 0x01) &&
    		 (ide_inb(device,ATA_CYL_LOW)  == 0x14) &&
    		 (ide_inb(device,ATA_CYL_HIGH) == 0xEB)) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* ATAPI Signature found */
    		dev_desc->if_type=IF_TYPE_ATAPI;
    		/* Start Ident Command
    		 */
    
    		ide_outb (device, ATA_COMMAND, ATAPI_CMD_IDENT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/*
    		 * Wait for completion - ATAPI devices need more time
    		 * to become ready
    		 */
    		c = ide_wait (device, ATAPI_TIME_OUT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    	{
    		/* Start Ident Command
    		 */
    
    		ide_outb (device, ATA_COMMAND, ATA_CMD_IDENT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		/* Wait for completion
    		 */
    		c = ide_wait (device, IDE_TIME_OUT);
    	}
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    
    	if (((c & ATA_STAT_DRQ) == 0) ||
    	    ((c & (ATA_STAT_FAULT|ATA_STAT_ERR)) != 0) ) {
    
    #ifdef CONFIG_AMIGAONEG3SE
    		if (retries == 0) {
    			do_retry = 1;
    		} else {
    			return;
    		}
    #else
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return;
    
    #endif	/* CONFIG_AMIGAONEG3SE */
    	}
    
    #ifdef CONFIG_AMIGAONEG3SE
    	s = getenv("ide_doreset");
    	if (s && strcmp(s, "on") == 0 && 1 == do_retry) {
    		/* Need to soft reset the device in case it's an ATAPI...  */
    		PRINTF("Retrying...\n");
    		ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    		udelay(100000);
    		ide_outb (device, ATA_COMMAND, 0x08);
    		udelay (100000);	/* 100 ms */
    		retries++;
    	} else {
    		retries = 100;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
        }	/* see above - ugly to read */
    #endif	/* CONFIG_AMIGAONEG3SE */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	input_swap_data (device, iobuf, ATA_SECTORWORDS);
    
    	ident_cpy (dev_desc->revision, iop->fw_rev, sizeof(dev_desc->revision));
    	ident_cpy (dev_desc->vendor, iop->model, sizeof(dev_desc->vendor));
    	ident_cpy (dev_desc->product, iop->serial_no, sizeof(dev_desc->product));
    
    #ifdef __LITTLE_ENDIAN
    	/*
    	 * firmware revision and model number have Big Endian Byte
    	 * order in Word. Convert both to little endian.
    	 *
    	 * See CF+ and CompactFlash Specification Revision 2.0:
    	 * 6.2.1.6: Identfy Drive, Table 39 for more details
    	 */
    
    	strswab (dev_desc->revision);
    	strswab (dev_desc->vendor);
    #endif /* __LITTLE_ENDIAN */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	if ((iop->config & 0x0080)==0x0080)
    		dev_desc->removable = 1;
    	else
    		dev_desc->removable = 0;
    
    #if 0
    	/*
    	 * Drive PIO mode autoselection
    	 */
    	mode = iop->tPIO;
    
    	printf ("tPIO = 0x%02x = %d\n",mode, mode);
    	if (mode > 2) {		/* 2 is maximum allowed tPIO value */
    		mode = 2;
    		PRINTF ("Override tPIO -> 2\n");
    	}
    	if (iop->field_valid & 2) {	/* drive implements ATA2? */
    		PRINTF ("Drive implements ATA2\n");
    		if (iop->capability & 8) {	/* drive supports use_iordy? */
    			cycle_time = iop->eide_pio_iordy;
    		} else {
    			cycle_time = iop->eide_pio;
    		}
    		PRINTF ("cycle time = %d\n", cycle_time);
    		mode = 4;
    		if (cycle_time > 120) mode = 3;	/* 120 ns for PIO mode 4 */
    		if (cycle_time > 180) mode = 2;	/* 180 ns for PIO mode 3 */
    		if (cycle_time > 240) mode = 1;	/* 240 ns for PIO mode 4 */
    		if (cycle_time > 383) mode = 0;	/* 383 ns for PIO mode 4 */
    	}
    	printf ("PIO mode to use: PIO %d\n", mode);
    #endif /* 0 */
    
    #ifdef CONFIG_ATAPI
    	if (dev_desc->if_type==IF_TYPE_ATAPI) {
    		atapi_inquiry(dev_desc);
    		return;
    	}
    #endif /* CONFIG_ATAPI */
    
    
    #ifdef __BIG_ENDIAN
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* swap shorts */
    	dev_desc->lba = (iop->lba_capacity << 16) | (iop->lba_capacity >> 16);
    
    #else	/* ! __BIG_ENDIAN */
    	/*
    	 * do not swap shorts on little endian
    	 *
    	 * See CF+ and CompactFlash Specification Revision 2.0:
    	 * 6.2.1.6: Identfy Drive, Table 39, Word Address 57-58 for details.
    	 */
    	dev_desc->lba = iop->lba_capacity;
    #endif	/* __BIG_ENDIAN */
    
    	if (iop->command_set_2 & 0x0400) { /* LBA 48 support */
    		dev_desc->lba48support = 1;
    		dev_desc->lba48 = (unsigned long long)iop->lba48_capacity[0] |
    						  ((unsigned long long)iop->lba48_capacity[1] << 16) |
    						  ((unsigned long long)iop->lba48_capacity[2] << 32) |
    						  ((unsigned long long)iop->lba48_capacity[3] << 48);
    	} else {
    		dev_desc->lba48support = 0;
    		dev_desc->lba48 = 0;
    	}
    #endif /* CONFIG_LBA48 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* assuming HD */
    	dev_desc->type=DEV_TYPE_HARDDISK;
    	dev_desc->blksz=ATA_BLOCKSIZE;
    	dev_desc->lun=0; /* just to fill something in... */
    
    #if 0 	/* only used to test the powersaving mode,
    	 * if enabled, the drive goes after 5 sec
    	 * in standby mode */
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	c = ide_wait (device, IDE_TIME_OUT);
    
    	ide_outb (device, ATA_SECT_CNT, 1);
    	ide_outb (device, ATA_LBA_LOW,  0);
    	ide_outb (device, ATA_LBA_MID,  0);
    	ide_outb (device, ATA_LBA_HIGH, 0);
    	ide_outb (device, ATA_DEV_HD,   ATA_LBA		|
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				    ATA_DEVICE(device));
    
    	ide_outb (device, ATA_COMMAND,  0xe3);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	udelay (50);
    	c = ide_wait (device, IDE_TIME_OUT);	/* can't take over 500 ms */
    #endif
    }
    
    
    /* ------------------------------------------------------------------------- */
    
    
    ulong ide_read (int device, lbaint_t blknr, ulong blkcnt, ulong *buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	ulong n = 0;
    	unsigned char c;
    	unsigned char pwrsave=0; /* power save */
    
    	unsigned char lba48 = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if (blknr & 0x0000fffff0000000) {
    		/* more than 28 bits used, use 48bit mode */
    		lba48 = 1;
    	}
    #endif
    	PRINTF ("ide_read dev %d start %qX, blocks %lX buffer at %lX\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		device, blknr, blkcnt, (ulong)buffer);
    
    	ide_led (DEVICE_LED(device), 1);	/* LED on	*/
    
    	/* Select device
    	 */
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	c = ide_wait (device, IDE_TIME_OUT);
    
    	if (c & ATA_STAT_BUSY) {
    		printf ("IDE read: device %d not ready\n", device);
    		goto IDE_READ_E;
    	}
    
    	/* first check if the drive is in Powersaving mode, if yes,
    	 * increase the timeout value */
    
    	ide_outb (device, ATA_COMMAND,  ATA_CMD_CHK_PWR);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	udelay (50);
    
    	c = ide_wait (device, IDE_TIME_OUT);	/* can't take over 500 ms */
    
    	if (c & ATA_STAT_BUSY) {
    		printf ("IDE read: device %d not ready\n", device);
    		goto IDE_READ_E;
    	}
    	if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) {
    		printf ("No Powersaving mode %X\n", c);
    	} else {
    
    		c = ide_inb(device,ATA_SECT_CNT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		PRINTF("Powersaving %02X\n",c);
    		if(c==0)
    			pwrsave=1;
    	}
    
    
    	while (blkcnt-- > 0) {
    
    		c = ide_wait (device, IDE_TIME_OUT);
    
    		if (c & ATA_STAT_BUSY) {
    			printf ("IDE read: device %d not ready\n", device);
    			break;
    		}
    
    		if (lba48) {
    			/* write high bits */
    			ide_outb (device, ATA_SECT_CNT, 0);
    			ide_outb (device, ATA_LBA_LOW,	(blknr >> 24) & 0xFF);
    			ide_outb (device, ATA_LBA_MID,	(blknr >> 32) & 0xFF);
    			ide_outb (device, ATA_LBA_HIGH, (blknr >> 40) & 0xFF);
    		}
    #endif
    
    		ide_outb (device, ATA_SECT_CNT, 1);
    		ide_outb (device, ATA_LBA_LOW,  (blknr >>  0) & 0xFF);
    		ide_outb (device, ATA_LBA_MID,  (blknr >>  8) & 0xFF);
    		ide_outb (device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF);
    
    		if (lba48) {
    			ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device) );
    			ide_outb (device, ATA_COMMAND, ATA_CMD_READ_EXT);
    
    		} else
    #endif
    		{
    			ide_outb (device, ATA_DEV_HD,   ATA_LBA		|
    						    ATA_DEVICE(device)	|
    						    ((blknr >> 24) & 0xF) );
    			ide_outb (device, ATA_COMMAND,  ATA_CMD_READ);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		udelay (50);
    
    		if(pwrsave) {
    			c = ide_wait (device, IDE_SPIN_UP_TIME_OUT);	/* may take up to 4 sec */
    			pwrsave=0;
    		} else {
    			c = ide_wait (device, IDE_TIME_OUT);	/* can't take over 500 ms */
    		}
    
    		if ((c&(ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR)) != ATA_STAT_DRQ) {
    
    #if defined(CFG_64BIT_LBA) && defined(CFG_64BIT_VSPRINTF)
    
    			printf ("Error (no IRQ) dev %d blk %qd: status 0x%02x\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				device, blknr, c);
    
    #else
    			printf ("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
    				device, (ulong)blknr, c);
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			break;
    		}
    
    		input_data (device, buffer, ATA_SECTORWORDS);
    
    		(void) ide_inb (device, ATA_STATUS);	/* clear IRQ */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		++n;
    		++blknr;
    		buffer += ATA_SECTORWORDS;
    	}
    IDE_READ_E:
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    	return (n);
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    
    ulong ide_write (int device, lbaint_t blknr, ulong blkcnt, ulong *buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	ulong n = 0;
    	unsigned char c;
    
    	unsigned char lba48 = 0;
    
    	if (blknr & 0x0000fffff0000000) {
    		/* more than 28 bits used, use 48bit mode */
    		lba48 = 1;
    	}
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	ide_led (DEVICE_LED(device), 1);	/* LED on	*/
    
    	/* Select device
    	 */
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	while (blkcnt-- > 0) {
    
    		c = ide_wait (device, IDE_TIME_OUT);
    
    		if (c & ATA_STAT_BUSY) {
    			printf ("IDE read: device %d not ready\n", device);
    			goto WR_OUT;
    		}
    
    		if (lba48) {
    			/* write high bits */
    			ide_outb (device, ATA_SECT_CNT, 0);
    			ide_outb (device, ATA_LBA_LOW,	(blknr >> 24) & 0xFF);
    			ide_outb (device, ATA_LBA_MID,	(blknr >> 32) & 0xFF);
    			ide_outb (device, ATA_LBA_HIGH, (blknr >> 40) & 0xFF);
    		}
    #endif
    
    		ide_outb (device, ATA_SECT_CNT, 1);
    		ide_outb (device, ATA_LBA_LOW,  (blknr >>  0) & 0xFF);
    		ide_outb (device, ATA_LBA_MID,  (blknr >>  8) & 0xFF);
    		ide_outb (device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF);
    
    		if (lba48) {
    			ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device) );
    			ide_outb (device, ATA_COMMAND,	ATA_CMD_WRITE_EXT);
    
    		} else
    #endif
    		{
    			ide_outb (device, ATA_DEV_HD,   ATA_LBA		|
    						    ATA_DEVICE(device)	|
    						    ((blknr >> 24) & 0xF) );
    			ide_outb (device, ATA_COMMAND,  ATA_CMD_WRITE);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		udelay (50);
    
    		c = ide_wait (device, IDE_TIME_OUT);	/* can't take over 500 ms */
    
    		if ((c&(ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR)) != ATA_STAT_DRQ) {
    
    #if defined(CFG_64BIT_LBA) && defined(CFG_64BIT_VSPRINTF)
    
    			printf ("Error (no IRQ) dev %d blk %qd: status 0x%02x\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				device, blknr, c);
    
    #else
    			printf ("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
    				device, (ulong)blknr, c);
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			goto WR_OUT;
    		}
    
    		output_data (device, buffer, ATA_SECTORWORDS);
    
    		c = ide_inb (device, ATA_STATUS);	/* clear IRQ */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		++n;
    		++blknr;
    		buffer += ATA_SECTORWORDS;
    	}
    WR_OUT:
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    	return (n);
    }
    
    /* ------------------------------------------------------------------------- */
    
    /*
     * copy src to dest, skipping leading and trailing blanks and null
     * terminate the string
    
     * "len" is the size of available memory including the terminating '\0'
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    static void ident_cpy (unsigned char *dst, unsigned char *src, unsigned int len)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	unsigned char *end, *last;
    
    	last = dst;
    
    	end  = src + len - 1;
    
    
    	/* reserve space for '\0' */
    	if (len < 2)
    		goto OUT;
    
    	/* skip leading white space */
    	while ((*src) && (src<end) && (*src==' '))
    		++src;
    
    	/* copy string, omitting trailing white space */
    	while ((*src) && (src<end)) {
    		*dst++ = *src;
    		if (*src++ != ' ')
    			last = dst;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    OUT:
    	*last = '\0';
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    /* ------------------------------------------------------------------------- */
    
    /*
     * Wait until Busy bit is off, or timeout (in ms)
     * Return last status
     */
    static uchar ide_wait (int dev, ulong t)
    {
    	ulong delay = 10 * t;		/* poll every 100 us */
    	uchar c;
    
    
    	while ((c = ide_inb(dev, ATA_STATUS)) & ATA_STAT_BUSY) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		udelay (100);
    		if (delay-- == 0) {
    			break;
    		}
    	}
    	return (c);
    }
    
    /* ------------------------------------------------------------------------- */
    
    #ifdef CONFIG_IDE_RESET
    extern void ide_set_reset(int idereset);
    
    static void ide_reset (void)
    {
    #if defined(CFG_PB_12V_ENABLE) || defined(CFG_PB_IDE_MOTOR)
    	volatile immap_t *immr = (immap_t *)CFG_IMMR;
    #endif
    	int i;
    
    	curr_device = -1;
    	for (i=0; i<CFG_IDE_MAXBUS; ++i)
    		ide_bus_ok[i] = 0;
    	for (i=0; i<CFG_IDE_MAXDEVICE; ++i)
    		ide_dev_desc[i].type = DEV_TYPE_UNKNOWN;
    
    	ide_set_reset (1); /* assert reset */
    
    	WATCHDOG_RESET();
    
    #ifdef CFG_PB_12V_ENABLE
    	immr->im_cpm.cp_pbdat &= ~(CFG_PB_12V_ENABLE);	/* 12V Enable output OFF */
    	immr->im_cpm.cp_pbpar &= ~(CFG_PB_12V_ENABLE);
    	immr->im_cpm.cp_pbodr &= ~(CFG_PB_12V_ENABLE);
    	immr->im_cpm.cp_pbdir |=   CFG_PB_12V_ENABLE;
    
    	/* wait 500 ms for the voltage to stabilize
    	 */
    	for (i=0; i<500; ++i) {
    		udelay (1000);
    	}
    
    	immr->im_cpm.cp_pbdat |=   CFG_PB_12V_ENABLE;	/* 12V Enable output ON */
    #endif	/* CFG_PB_12V_ENABLE */
    
    #ifdef CFG_PB_IDE_MOTOR
    	/* configure IDE Motor voltage monitor pin as input */
    	immr->im_cpm.cp_pbpar &= ~(CFG_PB_IDE_MOTOR);
    	immr->im_cpm.cp_pbodr &= ~(CFG_PB_IDE_MOTOR);
    	immr->im_cpm.cp_pbdir &= ~(CFG_PB_IDE_MOTOR);
    
    	/* wait up to 1 s for the motor voltage to stabilize
    	 */
    	for (i=0; i<1000; ++i) {
    		if ((immr->im_cpm.cp_pbdat & CFG_PB_IDE_MOTOR) != 0) {
    			break;
    		}
    		udelay (1000);
    	}
    
    	if (i == 1000) {	/* Timeout */
    		printf ("\nWarning: 5V for IDE Motor missing\n");
    # ifdef CONFIG_STATUS_LED
    #  ifdef STATUS_LED_YELLOW
    		status_led_set  (STATUS_LED_YELLOW, STATUS_LED_ON );
    #  endif
    #  ifdef STATUS_LED_GREEN
    		status_led_set  (STATUS_LED_GREEN,  STATUS_LED_OFF);
    #  endif
    # endif	/* CONFIG_STATUS_LED */
    	}
    #endif	/* CFG_PB_IDE_MOTOR */
    
    	WATCHDOG_RESET();
    
    	/* de-assert RESET signal */
    	ide_set_reset(0);
    
    	/* wait 250 ms */
    	for (i=0; i<250; ++i) {
    		udelay (1000);
    	}
    }
    
    #endif	/* CONFIG_IDE_RESET */
    
    /* ------------------------------------------------------------------------- */
    
    
    #if defined(CONFIG_IDE_LED) && !defined(CONFIG_AMIGAONEG3SE) && !defined(CONFIG_KUP4K) && !defined(CONFIG_HMI10)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    static	uchar	led_buffer = 0;		/* Buffer for current LED status	*/
    
    static void ide_led (uchar led, uchar status)
    {
    	uchar *led_port = LED_PORT;
    
    	if (status)	{		/* switch LED on	*/
    		led_buffer |=  led;
    	} else {			/* switch LED off	*/
    		led_buffer &= ~led;
    	}
    
    	*led_port = led_buffer;
    }
    
    #endif	/* CONFIG_IDE_LED */
    
    /* ------------------------------------------------------------------------- */
    
    #ifdef CONFIG_ATAPI
    /****************************************************************************
     * ATAPI Support
     */
    
    
    #undef	ATAPI_DEBUG
    
    #ifdef	ATAPI_DEBUG
    #define	AT_PRINTF(fmt,args...)	printf (fmt ,##args)
    #else
    #define AT_PRINTF(fmt,args...)
    #endif
    
    
    #ifdef __PPC__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /* since ATAPI may use commands with not 4 bytes alligned length
     * we have our own transfer functions, 2 bytes alligned */
    static void
    output_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    
    #ifndef CONFIG_HMI10
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	ushort	*dbuf;
    	volatile ushort	*pbuf;
    
    	pbuf = (ushort *)(ATA_CURR_BASE(dev)+ATA_DATA_REG);
    	dbuf = (ushort *)sect_buf;
    	while (shorts--) {
    		__asm__ volatile ("eieio");
    		*pbuf = *dbuf++;
    	}
    
    #else	/* CONFIG_HMI10 */
    
    	uchar	*dbuf;
    	volatile uchar	*pbuf_even;
    	volatile uchar	*pbuf_odd;
    
    	pbuf_even = (uchar *)(ATA_CURR_BASE(dev)+ATA_DATA_EVEN);
    	pbuf_odd  = (uchar *)(ATA_CURR_BASE(dev)+ATA_DATA_ODD);
    	while (shorts--) {
    		__asm__ volatile ("eieio");
    		*pbuf_even = *dbuf++;
    		__asm__ volatile ("eieio");
    		*pbuf_odd = *dbuf++;
    	}
    
    #endif	/* CONFIG_HMI10 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    static void
    input_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    
    #ifndef CONFIG_HMI10
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	ushort	*dbuf;
    	volatile ushort	*pbuf;
    
    	pbuf = (ushort *)(ATA_CURR_BASE(dev)+ATA_DATA_REG);
    	dbuf = (ushort *)sect_buf;
    	while (shorts--) {
    		__asm__ volatile ("eieio");
    		*dbuf++ = *pbuf;
    	}
    
    #else	/* CONFIG_HMI10 */
    
    	uchar	*dbuf;
    	volatile uchar	*pbuf_even;
    	volatile uchar	*pbuf_odd;
    
    	pbuf_even = (uchar *)(ATA_CURR_BASE(dev)+ATA_DATA_EVEN);
    	pbuf_odd  = (uchar *)(ATA_CURR_BASE(dev)+ATA_DATA_ODD);
    	while (shorts--) {
    		__asm__ volatile ("eieio");
    		*dbuf++ = *pbuf_even;
    		__asm__ volatile ("eieio");
    		*dbuf++ = *pbuf_odd;
    	}
    
    #endif	/* CONFIG_HMI10 */
    
    #else	/* ! __PPC__ */
    static void
    output_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    
    	outsw(ATA_CURR_BASE(dev)+ATA_DATA_REG, sect_buf, shorts);
    
    }
    
    
    static void
    input_data_shorts(int dev, ushort *sect_buf, int shorts)
    {
    
    	insw(ATA_CURR_BASE(dev)+ATA_DATA_REG, sect_buf, shorts);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*
     * Wait until (Status & mask) == res, or timeout (in ms)
     * Return last status
     * This is used since some ATAPI CD ROMs clears their Busy Bit first
     * and then they set their DRQ Bit
     */
    static uchar atapi_wait_mask (int dev, ulong t,uchar mask, uchar res)
    {
    	ulong delay = 10 * t;		/* poll every 100 us */
    	uchar c;
    
    
    	c = ide_inb(dev,ATA_DEV_CTL); /* prevents to read the status before valid */
    	while (((c = ide_inb(dev, ATA_STATUS)) & mask) != res) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* break if error occurs (doesn't make sense to wait more) */
    		if((c & ATA_STAT_ERR)==ATA_STAT_ERR)
    			break;
    		udelay (100);
    		if (delay-- == 0) {
    			break;
    		}
    	}
    	return (c);
    }
    
    /*
     * issue an atapi command
     */
    unsigned char atapi_issue(int device,unsigned char* ccb,int ccblen, unsigned char * buffer,int buflen)
    {
    	unsigned char c,err,mask,res;
    	int n;
    	ide_led (DEVICE_LED(device), 1);	/* LED on	*/
    
    	/* Select device
    	 */
    	mask = ATA_STAT_BUSY|ATA_STAT_DRQ;
    	res = 0;
    
    #ifdef	CONFIG_AMIGAONEG3SE
    # warning THF: Removed LBA mode ???
    #endif
    
    	ide_outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    	if ((c & mask) != res) {
    		printf ("ATAPI_ISSUE: device %d not ready status %X\n", device,c);
    		err=0xFF;
    		goto AI_OUT;
    	}
    	/* write taskfile */
    
    	ide_outb (device, ATA_ERROR_REG, 0); /* no DMA, no overlaped */
    
    	ide_outb (device, ATA_SECT_CNT, 0);
    	ide_outb (device, ATA_SECT_NUM, 0);
    
    	ide_outb (device, ATA_CYL_LOW,  (unsigned char)(buflen & 0xFF));
    
    	ide_outb (device, ATA_CYL_HIGH, (unsigned char)((buflen>>8) & 0xFF));
    #ifdef	CONFIG_AMIGAONEG3SE
    # warning THF: Removed LBA mode ???
    #endif
    
    	ide_outb (device, ATA_DEV_HD,   ATA_LBA | ATA_DEVICE(device));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	ide_outb (device, ATA_COMMAND,  ATAPI_CMD_PACKET);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	udelay (50);
    
    	mask = ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR;
    	res = ATA_STAT_DRQ;
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    
    	if ((c & mask) != res) { /* DRQ must be 1, BSY 0 */
    		printf ("ATTAPI_ISSUE: Error (no IRQ) before sending ccb dev %d status 0x%02x\n",device,c);
    		err=0xFF;
    		goto AI_OUT;
    	}
    
    	output_data_shorts (device, (unsigned short *)ccb,ccblen/2); /* write command block */
     	/* ATAPI Command written wait for completition */
    	udelay (5000); /* device must set bsy */
    
    	mask = ATA_STAT_DRQ|ATA_STAT_BUSY|ATA_STAT_ERR;
    	/* if no data wait for DRQ = 0 BSY = 0
    	 * if data wait for DRQ = 1 BSY = 0 */
    	res=0;
    	if(buflen)
    		res = ATA_STAT_DRQ;
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    	if ((c & mask) != res ) {
    		if (c & ATA_STAT_ERR) {
    
    			err=(ide_inb(device,ATA_ERROR_REG))>>4;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			AT_PRINTF("atapi_issue 1 returned sense key %X status %02X\n",err,c);
    		} else {
    			printf ("ATTAPI_ISSUE: (no DRQ) after sending ccb (%x)  status 0x%02x\n", ccb[0],c);
    			err=0xFF;
    		}
    		goto AI_OUT;
    	}
    
    	n=ide_inb(device, ATA_CYL_HIGH);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	n<<=8;
    
    	n+=ide_inb(device, ATA_CYL_LOW);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if(n>buflen) {
    		printf("ERROR, transfer bytes %d requested only %d\n",n,buflen);
    		err=0xff;
    		goto AI_OUT;
    	}
    	if((n==0)&&(buflen<0)) {
    		printf("ERROR, transfer bytes %d requested %d\n",n,buflen);
    		err=0xff;
    		goto AI_OUT;
    	}
    	if(n!=buflen) {
    		AT_PRINTF("WARNING, transfer bytes %d not equal with requested %d\n",n,buflen);
    	}
    	if(n!=0) { /* data transfer */
    		AT_PRINTF("ATAPI_ISSUE: %d Bytes to transfer\n",n);
    		 /* we transfer shorts */
    		n>>=1;
    		/* ok now decide if it is an in or output */
    
    		if ((ide_inb(device, ATA_SECT_CNT)&0x02)==0) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			AT_PRINTF("Write to device\n");
    			output_data_shorts(device,(unsigned short *)buffer,n);
    		} else {
    			AT_PRINTF("Read from device @ %p shorts %d\n",buffer,n);
    			input_data_shorts(device,(unsigned short *)buffer,n);
    		}
    	}
    	udelay(5000); /* seems that some CD ROMs need this... */
    	mask = ATA_STAT_BUSY|ATA_STAT_ERR;
    	res=0;
    	c = atapi_wait_mask(device,ATAPI_TIME_OUT,mask,res);
    	if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) {
    
    		err=(ide_inb(device,ATA_ERROR_REG) >> 4);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		AT_PRINTF("atapi_issue 2 returned sense key %X status %X\n",err,c);
    	} else {
    		err = 0;
    	}
    AI_OUT:
    	ide_led (DEVICE_LED(device), 0);	/* LED off	*/
    	return (err);
    }
    
    /*
     * sending the command to atapi_issue. If an status other than good
     * returns, an request_sense will be issued
     */
    
    #define ATAPI_DRIVE_NOT_READY 	100
    #define ATAPI_UNIT_ATTN		10
    
    unsigned char atapi_issue_autoreq (int device,
    				   unsigned char* ccb,
    				   int ccblen,
    				   unsigned char *buffer,
    				   int buflen)
    {
    	unsigned char sense_data[18],sense_ccb[12];
    	unsigned char res,key,asc,ascq;
    	int notready,unitattn;
    
    
    #ifdef CONFIG_AMIGAONEG3SE
    	char *s;
    	unsigned int timeout, retrycnt;
    
    	s = getenv("ide_cd_timeout");
    	timeout = s ? (simple_strtol(s, NULL, 10)*1000000)/5 : 0;
    
    	retrycnt = 0;
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	unitattn=ATAPI_UNIT_ATTN;
    	notready=ATAPI_DRIVE_NOT_READY;
    
    retry:
    	res= atapi_issue(device,ccb,ccblen,buffer,buflen);
    	if (res==0)
    		return (0); /* Ok */
    
    	if (res==0xFF)
    		return (0xFF); /* error */
    
    	AT_PRINTF("(auto_req)atapi_issue returned sense key %X\n",res);
    
    	memset(sense_ccb,0,sizeof(sense_ccb));
    	memset(sense_data,0,sizeof(sense_data));
    	sense_ccb[0]=ATAPI_CMD_REQ_SENSE;
    
    	sense_ccb[4]=18; /* allocation Length */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	res=atapi_issue(device,sense_ccb,12,sense_data,18);
    	key=(sense_data[2]&0xF);
    	asc=(sense_data[12]);
    	ascq=(sense_data[13]);
    
    	AT_PRINTF("ATAPI_CMD_REQ_SENSE returned %x\n",res);
    	AT_PRINTF(" Sense page: %02X key %02X ASC %02X ASCQ %02X\n",
    		sense_data[0],
    		key,
    		asc,
    		ascq);
    
    	if((key==0))
    		return 0; /* ok device ready */
    
    	if((key==6)|| (asc==0x29) || (asc==0x28)) { /* Unit Attention */
    		if(unitattn-->0) {
    			udelay(200*1000);
    			goto retry;
    		}
    		printf("Unit Attention, tried %d\n",ATAPI_UNIT_ATTN);
    		goto error;
    	}
    	if((asc==0x4) && (ascq==0x1)) { /* not ready, but will be ready soon */
    		if (notready-->0) {
    			udelay(200*1000);
    			goto retry;
    		}
    		printf("Drive not ready, tried %d times\n",ATAPI_DRIVE_NOT_READY);
    		goto error;
    	}
    	if(asc==0x3a) {
    		AT_PRINTF("Media not present\n");
    		goto error;
    	}
    
    
    #ifdef CONFIG_AMIGAONEG3SE
    	if ((sense_data[2]&0xF)==0x0B) {
    		AT_PRINTF("ABORTED COMMAND...retry\n");
    		if (retrycnt++ < 4)
    			goto retry;
    		return (0xFF);
    	}
    
    	if ((sense_data[2]&0xf) == 0x02 &&
    	    sense_data[12] == 0x04	&&
    	    sense_data[13] == 0x01	) {
    		AT_PRINTF("Waiting for unit to become active\n");
    		udelay(timeout);
    		if (retrycnt++ < 4)
    			goto retry;
    		return 0xFF;
    	}
    #endif	/* CONFIG_AMIGAONEG3SE */
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	printf ("ERROR: Unknown Sense key %02X ASC %02X ASCQ %02X\n",key,asc,ascq);
    error:
    	AT_PRINTF ("ERROR Sense key %02X ASC %02X ASCQ %02X\n",key,asc,ascq);
    	return (0xFF);
    }
    
    
    static void	atapi_inquiry(block_dev_desc_t * dev_desc)
    {
    	unsigned char ccb[12]; /* Command descriptor block */
    	unsigned char iobuf[64]; /* temp buf */
    	unsigned char c;
    	int device;
    
    	device=dev_desc->dev;
    	dev_desc->type=DEV_TYPE_UNKNOWN; /* not yet valid */
    	dev_desc->block_read=atapi_read;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    
    	ccb[0]=ATAPI_CMD_INQUIRY;
    	ccb[4]=40; /* allocation Legnth */
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,40);
    
    	AT_PRINTF("ATAPI_CMD_INQUIRY returned %x\n",c);
    	if (c!=0)
    		return;
    
    	/* copy device ident strings */
    	ident_cpy(dev_desc->vendor,&iobuf[8],8);
    	ident_cpy(dev_desc->product,&iobuf[16],16);
    	ident_cpy(dev_desc->revision,&iobuf[32],5);
    
    	dev_desc->lun=0;
    	dev_desc->lba=0;
    	dev_desc->blksz=0;
    	dev_desc->type=iobuf[0] & 0x1f;
    
    	if ((iobuf[1]&0x80)==0x80)
    		dev_desc->removable = 1;
    	else
    		dev_desc->removable = 0;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    	ccb[0]=ATAPI_CMD_START_STOP;
    	ccb[4]=0x03; /* start */
    
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,0);
    
    	AT_PRINTF("ATAPI_CMD_START_STOP returned %x\n",c);
    	if (c!=0)
    		return;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,0);
    
    	AT_PRINTF("ATAPI_CMD_UNIT_TEST_READY returned %x\n",c);
    	if (c!=0)
    		return;
    
    	memset(ccb,0,sizeof(ccb));
    	memset(iobuf,0,sizeof(iobuf));
    	ccb[0]=ATAPI_CMD_READ_CAP;
    	c=atapi_issue_autoreq(device,ccb,12,(unsigned char *)iobuf,8);
    	AT_PRINTF("ATAPI_CMD_READ_CAP returned %x\n",c);
    	if (c!=0)
    		return;
    
    	AT_PRINTF("Read Cap: LBA %02X%02X%02X%02X blksize %02X%02X%02X%02X\n",
    		iobuf[0],iobuf[1],iobuf[2],iobuf[3],
    		iobuf[4],iobuf[5],iobuf[6],iobuf[7]);
    
    	dev_desc->lba  =((unsigned long)iobuf[0]<<24) +
    			((unsigned long)iobuf[1]<<16) +
    			((unsigned long)iobuf[2]<< 8) +
    			((unsigned long)iobuf[3]);
    	dev_desc->blksz=((unsigned long)iobuf[4]<<24) +
    			((unsigned long)iobuf[5]<<16) +
    			((unsigned long)iobuf[6]<< 8) +
    			((unsigned long)iobuf[7]);
    
    	dev_desc->lba48 = 0; /* ATAPI devices cannot use 48bit addressing (ATA/ATAPI v7) */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return;
    }
    
    
    /*
     * atapi_read:
     * we transfer only one block per command, since the multiple DRQ per
     * command is not yet implemented
     */
    #define ATAPI_READ_MAX_BYTES	2048	/* we read max 2kbytes */
    #define ATAPI_READ_BLOCK_SIZE	2048	/* assuming CD part */
    #define ATAPI_READ_MAX_BLOCK ATAPI_READ_MAX_BYTES/ATAPI_READ_BLOCK_SIZE	/* max blocks */
    
    
    ulong atapi_read (int device, lbaint_t blknr, ulong blkcnt, ulong *buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	ulong n = 0;
    	unsigned char ccb[12]; /* Command descriptor block */
    	ulong cnt;
    
    	AT_PRINTF("atapi_read dev %d start %lX, blocks %lX buffer at %lX\n",
    		device, blknr, blkcnt, (ulong)buffer);
    
    	do {
    		if (blkcnt>ATAPI_READ_MAX_BLOCK) {
    			cnt=ATAPI_READ_MAX_BLOCK;
    		} else {