Skip to content
Snippets Groups Projects
ide.c 34.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    	/*
    	 * Wait for IDE to get ready.
    	 * According to spec, this can take up to 31 seconds!
    	 */
    	for (bus = 0; bus < CONFIG_SYS_IDE_MAXBUS; ++bus) {
    		int dev =
    			bus * (CONFIG_SYS_IDE_MAXDEVICE /
    			       CONFIG_SYS_IDE_MAXBUS);
    
    #ifdef CONFIG_IDE_8xx_PCCARD
    		/* Skip non-ide devices from probing */
    		if ((ide_devices_found & (1 << bus)) == 0) {
    			ide_led((LED_IDE1 | LED_IDE2), 0);	/* LED's off */
    			continue;
    
    		printf("Bus %d: ", bus);
    
    		ide_bus_ok[bus] = 0;
    
    		/* Select device
    		 */
    		udelay(100000);	/* 100 ms */
    		ide_outb(dev, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(dev));
    		udelay(100000);	/* 100 ms */
    		i = 0;
    		do {
    			udelay(10000);	/* 10 ms */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			c = ide_inb(dev, ATA_STATUS);
    			i++;
    			if (i > (ATA_RESET_TIME * 100)) {
    				puts("** Timeout **\n");
    				/* LED's off */
    				ide_led((LED_IDE1 | LED_IDE2), 0);
    				return;
    			}
    			if ((i >= 100) && ((i % 100) == 0))
    				putc('.');
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		} while (c & ATA_STAT_BUSY);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (c & (ATA_STAT_BUSY | ATA_STAT_FAULT)) {
    			puts("not available  ");
    			debug("Status = 0x%02X ", c);
    #ifndef CONFIG_ATAPI		/* ATAPI Devices do not set DRDY */
    		} else if ((c & ATA_STAT_READY) == 0) {
    			puts("not available  ");
    			debug("Status = 0x%02X ", c);
    #endif
    		} else {
    			puts("OK ");
    			ide_bus_ok[bus] = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    		WATCHDOG_RESET();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	ide_led((LED_IDE1 | LED_IDE2), 0);	/* LED's off    */
    
    	curr_device = -1;
    	for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i) {
    		int led = (IDE_BUS(i) == 0) ? LED_IDE1 : LED_IDE2;
    		ide_dev_desc[i].type = DEV_TYPE_UNKNOWN;
    		ide_dev_desc[i].if_type = IF_TYPE_IDE;
    		ide_dev_desc[i].devnum = i;
    		ide_dev_desc[i].part_type = PART_TYPE_UNKNOWN;
    		ide_dev_desc[i].blksz = 0;
    		ide_dev_desc[i].log2blksz =
    			LOG2_INVALID(typeof(ide_dev_desc[i].log2blksz));
    		ide_dev_desc[i].lba = 0;
    		ide_dev_desc[i].block_read = ide_read;
    		ide_dev_desc[i].block_write = ide_write;
    		if (!ide_bus_ok[IDE_BUS(i)])
    			continue;
    		ide_led(led, 1);	/* LED on       */
    		ide_ident(&ide_dev_desc[i]);
    		ide_led(led, 0);	/* LED off      */
    		dev_print(&ide_dev_desc[i]);
    
    		if ((ide_dev_desc[i].lba > 0) && (ide_dev_desc[i].blksz > 0)) {
    			/* initialize partition type */
    			part_init(&ide_dev_desc[i]);
    			if (curr_device < 0)
    				curr_device = i;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	WATCHDOG_RESET();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    #ifdef CONFIG_PARTITIONS
    struct blk_desc *ide_get_dev(int dev)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	return (dev < CONFIG_SYS_IDE_MAXDEVICE) ? &ide_dev_desc[dev] : NULL;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    /* ------------------------------------------------------------------------- */
    
    
    /* We only need to swap data if we are running on a big endian cpu. */
    #if defined(__LITTLE_ENDIAN)
    __weak void ide_input_swap_data(int dev, ulong *sect_buf, int words)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	ide_input_data(dev, sect_buf, words);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    #else
    __weak void ide_input_swap_data(int dev, ulong *sect_buf, int words)
    {
    	volatile ushort *pbuf =
    		(ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
    	ushort *dbuf = (ushort *)sect_buf;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	debug("in input swap data base for read is %lx\n",
    	      (unsigned long) pbuf);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	while (words--) {
    #ifdef __MIPS__
    		*dbuf++ = swab16p((u16 *)pbuf);
    		*dbuf++ = swab16p((u16 *)pbuf);
    #else
    		*dbuf++ = ld_le16(pbuf);
    		*dbuf++ = ld_le16(pbuf);
    #endif /* !MIPS */
    	}
    
    #endif /* __LITTLE_ENDIAN */
    
    Albert Aribaud's avatar
    Albert Aribaud committed
    #if defined(CONFIG_IDE_SWAP_IO)
    
    __weak void ide_output_data(int dev, const ulong *sect_buf, int words)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	ushort *dbuf;
    	volatile ushort *pbuf;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	pbuf = (ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
    	dbuf = (ushort *)sect_buf;
    	while (words--) {
    		EIEIO;
    		*pbuf = *dbuf++;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    #else  /* ! CONFIG_IDE_SWAP_IO */
    __weak void ide_output_data(int dev, const ulong *sect_buf, int words)
    {
    #if defined(CONFIG_IDE_AHB)
    	ide_write_data(dev, sect_buf, words);
    #else
    	outsw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, words << 1);
    #endif
    }
    #endif /* CONFIG_IDE_SWAP_IO */
    
    #if defined(CONFIG_IDE_SWAP_IO)
    __weak void ide_input_data(int dev, ulong *sect_buf, int words)
    
    	ushort *dbuf;
    	volatile ushort *pbuf;
    
    	pbuf = (ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
    	dbuf = (ushort *)sect_buf;
    
    	debug("in input data base for read is %lx\n", (unsigned long) pbuf);
    
    	while (words--) {
    		EIEIO;
    		*dbuf++ = *pbuf;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    #else  /* ! CONFIG_IDE_SWAP_IO */
    
    __weak void ide_input_data(int dev, ulong *sect_buf, int words)
    
    #if defined(CONFIG_IDE_AHB)
    	ide_read_data(dev, sect_buf, words);
    #else
    	insw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, words << 1);
    #endif
    
    #endif /* CONFIG_IDE_SWAP_IO */
    
    /* ------------------------------------------------------------------------- */
    
    ulong ide_read(struct blk_desc *block_dev, lbaint_t blknr, lbaint_t blkcnt,
    	       void *buffer)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	int device = block_dev->devnum;
    	ulong n = 0;
    	unsigned char c;
    	unsigned char pwrsave = 0;	/* power save */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #ifdef CONFIG_LBA48
    	unsigned char lba48 = 0;
    
    	if (blknr & 0x0000fffff0000000ULL) {
    		/* more than 28 bits used, use 48bit mode */
    		lba48 = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    #endif
    	debug("ide_read dev %d start " LBAF ", blocks " LBAF " buffer at %lX\n",
    	      device, blknr, blkcnt, (ulong) buffer);
    
    
    	ide_led(DEVICE_LED(device), 1);	/* LED on       */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Select device
    	 */
    
    	ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
    
    	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;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	/* first check if the drive is in Powersaving mode, if yes,
    	 * increase the timeout value */
    	ide_outb(device, ATA_COMMAND, ATA_CMD_CHK_PWR);
    	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;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	if ((c & ATA_STAT_ERR) == ATA_STAT_ERR) {
    		printf("No Powersaving mode %X\n", c);
    	} else {
    		c = ide_inb(device, ATA_SECT_CNT);
    		debug("Powersaving %02X\n", c);
    		if (c == 0)
    			pwrsave = 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	while (blkcnt-- > 0) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		c = ide_wait(device, IDE_TIME_OUT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (c & ATA_STAT_BUSY) {
    			printf("IDE read: device %d not ready\n", device);
    			break;
    		}
    #ifdef CONFIG_LBA48
    		if (lba48) {
    			/* write high bits */
    			ide_outb(device, ATA_SECT_CNT, 0);
    			ide_outb(device, ATA_LBA_LOW, (blknr >> 24) & 0xFF);
    #ifdef CONFIG_SYS_64BIT_LBA
    			ide_outb(device, ATA_LBA_MID, (blknr >> 32) & 0xFF);
    			ide_outb(device, ATA_LBA_HIGH, (blknr >> 40) & 0xFF);
    #else
    			ide_outb(device, ATA_LBA_MID, 0);
    			ide_outb(device, ATA_LBA_HIGH, 0);
    #endif
    		}
    #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);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #ifdef CONFIG_LBA48
    		if (lba48) {
    			ide_outb(device, ATA_DEV_HD,
    				 ATA_LBA | ATA_DEVICE(device));
    			ide_outb(device, ATA_COMMAND, ATA_CMD_READ_EXT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		} 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
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (pwrsave) {
    			/* may take up to 4 sec */
    			c = ide_wait(device, IDE_SPIN_UP_TIME_OUT);
    			pwrsave = 0;
    		} else {
    			/* can't take over 500 ms */
    			c = ide_wait(device, IDE_TIME_OUT);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) !=
    		    ATA_STAT_DRQ) {
    			printf("Error (no IRQ) dev %d blk " LBAF
    			       ": status %#02x\n", device, blknr, c);
    			break;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		ide_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_BLOCKSIZE;
    	}
    IDE_READ_E:
    	ide_led(DEVICE_LED(device), 0);	/* LED off      */
    	return n;
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    /* ------------------------------------------------------------------------- */
    
    ulong ide_write(struct blk_desc *block_dev, lbaint_t blknr, lbaint_t blkcnt,
    		const void *buffer)
    {
    	int device = block_dev->devnum;
    	ulong n = 0;
    	unsigned char c;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #ifdef CONFIG_LBA48
    	unsigned char lba48 = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if (blknr & 0x0000fffff0000000ULL) {
    		/* 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);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if (c & ATA_STAT_BUSY) {
    			printf("IDE read: device %d not ready\n", device);
    			goto WR_OUT;
    		}
    #ifdef CONFIG_LBA48
    		if (lba48) {
    			/* write high bits */
    			ide_outb(device, ATA_SECT_CNT, 0);
    			ide_outb(device, ATA_LBA_LOW, (blknr >> 24) & 0xFF);
    #ifdef CONFIG_SYS_64BIT_LBA
    			ide_outb(device, ATA_LBA_MID, (blknr >> 32) & 0xFF);
    			ide_outb(device, ATA_LBA_HIGH, (blknr >> 40) & 0xFF);
    #else
    			ide_outb(device, ATA_LBA_MID, 0);
    			ide_outb(device, ATA_LBA_HIGH, 0);
    #endif
    		}
    #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);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #ifdef CONFIG_LBA48
    		if (lba48) {
    			ide_outb(device, ATA_DEV_HD,
    				 ATA_LBA | ATA_DEVICE(device));
    			ide_outb(device, ATA_COMMAND, ATA_CMD_WRITE_EXT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		} 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
    
    
    		/* can't take over 500 ms */
    		c = ide_wait(device, IDE_TIME_OUT);
    
    		if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) !=
    		    ATA_STAT_DRQ) {
    			printf("Error (no IRQ) dev %d blk " LBAF
    			       ": status %#02x\n", device, blknr, c);
    			goto WR_OUT;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    
    		ide_output_data(device, buffer, ATA_SECTORWORDS);
    		c = ide_inb(device, ATA_STATUS);	/* clear IRQ */
    		++n;
    		++blknr;
    		buffer += ATA_BLOCKSIZE;
    	}
    WR_OUT:
    	ide_led(DEVICE_LED(device), 0);	/* LED off      */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    /* ------------------------------------------------------------------------- */
    
    
    #if defined(CONFIG_OF_IDE_FIXUP)
    int ide_device_present(int dev)
    {
    	if (dev >= CONFIG_SYS_IDE_MAXBUS)
    		return 0;
    	return ide_dev_desc[dev].type == DEV_TYPE_UNKNOWN ? 0 : 1;
    }
    #endif
    /* ------------------------------------------------------------------------- */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    U_BOOT_CMD(ide, 5, 1, do_ide,
    	   "IDE sub-system",
    	   "reset - reset IDE controller\n"
    	   "ide info  - show available IDE devices\n"
    	   "ide device [dev] - show or set current device\n"
    	   "ide part [dev] - print partition table of one or all IDE devices\n"
    	   "ide read  addr blk# cnt\n"
    	   "ide write addr blk# cnt - read/write `cnt'"
    	   " blocks starting at block `blk#'\n"
    	   "    to/from memory address `addr'");
    
    U_BOOT_CMD(diskboot, 3, 1, do_diskboot,
    	   "boot from IDE device", "loadAddr dev:part");