mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 00:21:06 +00:00
4c12eeb8b5
Change all files in common/ to use CMD_RET_USAGE instead of calling cmd_usage() directly. I'm not completely sure about this patch since the code since impact is small (100 byte or so on ARM) and it might need splitting into smaller patches. But for now here it is. Signed-off-by: Simon Glass <sjg@chromium.org>
2068 lines
50 KiB
C
2068 lines
50 KiB
C
/*
|
|
* (C) Copyright 2000-2011
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* IDE support
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <config.h>
|
|
#include <watchdog.h>
|
|
#include <command.h>
|
|
#include <image.h>
|
|
#include <asm/byteorder.h>
|
|
#include <asm/io.h>
|
|
|
|
#if defined(CONFIG_IDE_8xx_DIRECT) || defined(CONFIG_IDE_PCMCIA)
|
|
# include <pcmcia.h>
|
|
#endif
|
|
|
|
#ifdef CONFIG_8xx
|
|
# include <mpc8xx.h>
|
|
#endif
|
|
|
|
#ifdef CONFIG_MPC5xxx
|
|
#include <mpc5xxx.h>
|
|
#endif
|
|
|
|
#include <ide.h>
|
|
#include <ata.h>
|
|
|
|
#ifdef CONFIG_STATUS_LED
|
|
# include <status_led.h>
|
|
#endif
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
#endif
|
|
|
|
#ifdef __PPC__
|
|
# define EIEIO __asm__ volatile ("eieio")
|
|
# define SYNC __asm__ volatile ("sync")
|
|
#else
|
|
# define EIEIO /* nothing */
|
|
# define SYNC /* nothing */
|
|
#endif
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
/* Timings for IDE Interface
|
|
*
|
|
* SETUP / LENGTH / HOLD - cycles valid for 50 MHz clk
|
|
* 70 165 30 PIO-Mode 0, [ns]
|
|
* 4 9 2 [Cycles]
|
|
* 50 125 20 PIO-Mode 1, [ns]
|
|
* 3 7 2 [Cycles]
|
|
* 30 100 15 PIO-Mode 2, [ns]
|
|
* 2 6 1 [Cycles]
|
|
* 30 80 10 PIO-Mode 3, [ns]
|
|
* 2 5 1 [Cycles]
|
|
* 25 70 10 PIO-Mode 4, [ns]
|
|
* 2 4 1 [Cycles]
|
|
*/
|
|
|
|
const static pio_config_t pio_config_ns [IDE_MAX_PIO_MODE+1] =
|
|
{
|
|
/* Setup Length Hold */
|
|
{ 70, 165, 30 }, /* PIO-Mode 0, [ns] */
|
|
{ 50, 125, 20 }, /* PIO-Mode 1, [ns] */
|
|
{ 30, 101, 15 }, /* PIO-Mode 2, [ns] */
|
|
{ 30, 80, 10 }, /* PIO-Mode 3, [ns] */
|
|
{ 25, 70, 10 }, /* PIO-Mode 4, [ns] */
|
|
};
|
|
|
|
static pio_config_t pio_config_clk [IDE_MAX_PIO_MODE+1];
|
|
|
|
#ifndef CONFIG_SYS_PIO_MODE
|
|
#define CONFIG_SYS_PIO_MODE 0 /* use a relaxed default */
|
|
#endif
|
|
static int pio_mode = CONFIG_SYS_PIO_MODE;
|
|
|
|
/* Make clock cycles and always round up */
|
|
|
|
#define PCMCIA_MK_CLKS( t, T ) (( (t) * (T) + 999U ) / 1000U )
|
|
|
|
#endif /* CONFIG_IDE_8xx_DIRECT */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Current I/O Device */
|
|
static int curr_device = -1;
|
|
|
|
/* Current offset for IDE0 / IDE1 bus access */
|
|
ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS] = {
|
|
#if defined(CONFIG_SYS_ATA_IDE0_OFFSET)
|
|
CONFIG_SYS_ATA_IDE0_OFFSET,
|
|
#endif
|
|
#if defined(CONFIG_SYS_ATA_IDE1_OFFSET) && (CONFIG_SYS_IDE_MAXBUS > 1)
|
|
CONFIG_SYS_ATA_IDE1_OFFSET,
|
|
#endif
|
|
};
|
|
|
|
static int ide_bus_ok[CONFIG_SYS_IDE_MAXBUS];
|
|
|
|
block_dev_desc_t ide_dev_desc[CONFIG_SYS_IDE_MAXDEVICE];
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#ifdef CONFIG_IDE_LED
|
|
# if !defined(CONFIG_BMS2003) && \
|
|
!defined(CONFIG_CPC45) && \
|
|
!defined(CONFIG_KUP4K) && \
|
|
!defined(CONFIG_KUP4X)
|
|
static void ide_led (uchar led, uchar status);
|
|
#else
|
|
extern void ide_led (uchar led, uchar status);
|
|
#endif
|
|
#else
|
|
#define ide_led(a,b) /* dummy */
|
|
#endif
|
|
|
|
#ifdef CONFIG_IDE_RESET
|
|
static void ide_reset (void);
|
|
#else
|
|
#define ide_reset() /* dummy */
|
|
#endif
|
|
|
|
static void ide_ident (block_dev_desc_t *dev_desc);
|
|
static uchar ide_wait (int dev, ulong t);
|
|
|
|
#define IDE_TIME_OUT 2000 /* 2 sec timeout */
|
|
|
|
#define ATAPI_TIME_OUT 7000 /* 7 sec timeout (5 sec seems to work...) */
|
|
|
|
#define IDE_SPIN_UP_TIME_OUT 5000 /* 5 sec spin-up timeout */
|
|
|
|
static void input_data(int dev, ulong *sect_buf, int words);
|
|
static void output_data(int dev, const ulong *sect_buf, int words);
|
|
static void ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len);
|
|
|
|
#ifndef CONFIG_SYS_ATA_PORT_ADDR
|
|
#define CONFIG_SYS_ATA_PORT_ADDR(port) (port)
|
|
#endif
|
|
|
|
#ifdef CONFIG_ATAPI
|
|
static void atapi_inquiry(block_dev_desc_t *dev_desc);
|
|
ulong atapi_read (int device, lbaint_t blknr, ulong blkcnt, void *buffer);
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
static void set_pcmcia_timing (int pmode);
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
int do_ide(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
int rcode = 0;
|
|
|
|
switch (argc) {
|
|
case 0:
|
|
case 1:
|
|
return CMD_RET_USAGE;
|
|
case 2:
|
|
if (strncmp(argv[1], "res", 3) == 0) {
|
|
puts("\nReset IDE"
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
" on PCMCIA " PCMCIA_SLOT_MSG
|
|
#endif
|
|
": ");
|
|
|
|
ide_init();
|
|
return 0;
|
|
} else if (strncmp(argv[1], "inf", 3) == 0) {
|
|
int i;
|
|
|
|
putc('\n');
|
|
|
|
for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i) {
|
|
if (ide_dev_desc[i].type == DEV_TYPE_UNKNOWN)
|
|
continue; /* list only known devices */
|
|
printf("IDE device %d: ", i);
|
|
dev_print(&ide_dev_desc[i]);
|
|
}
|
|
return 0;
|
|
|
|
} else if (strncmp(argv[1], "dev", 3) == 0) {
|
|
if ((curr_device < 0)
|
|
|| (curr_device >= CONFIG_SYS_IDE_MAXDEVICE)) {
|
|
puts("\nno IDE devices available\n");
|
|
return 1;
|
|
}
|
|
printf("\nIDE device %d: ", curr_device);
|
|
dev_print(&ide_dev_desc[curr_device]);
|
|
return 0;
|
|
} else if (strncmp(argv[1], "part", 4) == 0) {
|
|
int dev, ok;
|
|
|
|
for (ok = 0, dev = 0;
|
|
dev < CONFIG_SYS_IDE_MAXDEVICE;
|
|
++dev) {
|
|
if (ide_dev_desc[dev].part_type !=
|
|
PART_TYPE_UNKNOWN) {
|
|
++ok;
|
|
if (dev)
|
|
putc('\n');
|
|
print_part(&ide_dev_desc[dev]);
|
|
}
|
|
}
|
|
if (!ok) {
|
|
puts("\nno IDE devices available\n");
|
|
rcode++;
|
|
}
|
|
return rcode;
|
|
}
|
|
return CMD_RET_USAGE;
|
|
case 3:
|
|
if (strncmp(argv[1], "dev", 3) == 0) {
|
|
int dev = (int) simple_strtoul(argv[2], NULL, 10);
|
|
|
|
printf("\nIDE device %d: ", dev);
|
|
if (dev >= CONFIG_SYS_IDE_MAXDEVICE) {
|
|
puts("unknown device\n");
|
|
return 1;
|
|
}
|
|
dev_print(&ide_dev_desc[dev]);
|
|
/*ide_print (dev); */
|
|
|
|
if (ide_dev_desc[dev].type == DEV_TYPE_UNKNOWN)
|
|
return 1;
|
|
|
|
curr_device = dev;
|
|
|
|
puts("... is now current device\n");
|
|
|
|
return 0;
|
|
} else if (strncmp(argv[1], "part", 4) == 0) {
|
|
int dev = (int) simple_strtoul(argv[2], NULL, 10);
|
|
|
|
if (ide_dev_desc[dev].part_type != PART_TYPE_UNKNOWN) {
|
|
print_part(&ide_dev_desc[dev]);
|
|
} else {
|
|
printf("\nIDE device %d not available\n",
|
|
dev);
|
|
rcode = 1;
|
|
}
|
|
return rcode;
|
|
}
|
|
|
|
return CMD_RET_USAGE;
|
|
default:
|
|
/* at least 4 args */
|
|
|
|
if (strcmp(argv[1], "read") == 0) {
|
|
ulong addr = simple_strtoul(argv[2], NULL, 16);
|
|
ulong cnt = simple_strtoul(argv[4], NULL, 16);
|
|
ulong n;
|
|
|
|
#ifdef CONFIG_SYS_64BIT_LBA
|
|
lbaint_t blk = simple_strtoull(argv[3], NULL, 16);
|
|
|
|
printf("\nIDE read: device %d block # %lld, count %ld ... ",
|
|
curr_device, blk, cnt);
|
|
#else
|
|
lbaint_t blk = simple_strtoul(argv[3], NULL, 16);
|
|
|
|
printf("\nIDE read: device %d block # %ld, count %ld ... ",
|
|
curr_device, blk, cnt);
|
|
#endif
|
|
|
|
n = ide_dev_desc[curr_device].block_read(curr_device,
|
|
blk, cnt,
|
|
(ulong *)addr);
|
|
/* flush cache after read */
|
|
flush_cache(addr,
|
|
cnt * ide_dev_desc[curr_device].blksz);
|
|
|
|
printf("%ld blocks read: %s\n",
|
|
n, (n == cnt) ? "OK" : "ERROR");
|
|
if (n == cnt)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
} else if (strcmp(argv[1], "write") == 0) {
|
|
ulong addr = simple_strtoul(argv[2], NULL, 16);
|
|
ulong cnt = simple_strtoul(argv[4], NULL, 16);
|
|
ulong n;
|
|
|
|
#ifdef CONFIG_SYS_64BIT_LBA
|
|
lbaint_t blk = simple_strtoull(argv[3], NULL, 16);
|
|
|
|
printf("\nIDE write: device %d block # %lld, count %ld ... ",
|
|
curr_device, blk, cnt);
|
|
#else
|
|
lbaint_t blk = simple_strtoul(argv[3], NULL, 16);
|
|
|
|
printf("\nIDE write: device %d block # %ld, count %ld ... ",
|
|
curr_device, blk, cnt);
|
|
#endif
|
|
n = ide_write(curr_device, blk, cnt, (ulong *) addr);
|
|
|
|
printf("%ld blocks written: %s\n",
|
|
n, (n == cnt) ? "OK" : "ERROR");
|
|
if (n == cnt)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
} else {
|
|
return CMD_RET_USAGE;
|
|
}
|
|
|
|
return rcode;
|
|
}
|
|
}
|
|
|
|
int do_diskboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
char *boot_device = NULL;
|
|
char *ep;
|
|
int dev, part = 0;
|
|
ulong addr, cnt;
|
|
disk_partition_t info;
|
|
image_header_t *hdr;
|
|
|
|
#if defined(CONFIG_FIT)
|
|
const void *fit_hdr = NULL;
|
|
#endif
|
|
|
|
show_boot_progress(41);
|
|
switch (argc) {
|
|
case 1:
|
|
addr = CONFIG_SYS_LOAD_ADDR;
|
|
boot_device = getenv("bootdevice");
|
|
break;
|
|
case 2:
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
boot_device = getenv("bootdevice");
|
|
break;
|
|
case 3:
|
|
addr = simple_strtoul(argv[1], NULL, 16);
|
|
boot_device = argv[2];
|
|
break;
|
|
default:
|
|
show_boot_progress(-42);
|
|
return CMD_RET_USAGE;
|
|
}
|
|
show_boot_progress(42);
|
|
|
|
if (!boot_device) {
|
|
puts("\n** No boot device **\n");
|
|
show_boot_progress(-43);
|
|
return 1;
|
|
}
|
|
show_boot_progress(43);
|
|
|
|
dev = simple_strtoul(boot_device, &ep, 16);
|
|
|
|
if (ide_dev_desc[dev].type == DEV_TYPE_UNKNOWN) {
|
|
printf("\n** Device %d not available\n", dev);
|
|
show_boot_progress(-44);
|
|
return 1;
|
|
}
|
|
show_boot_progress(44);
|
|
|
|
if (*ep) {
|
|
if (*ep != ':') {
|
|
puts("\n** Invalid boot device, use `dev[:part]' **\n");
|
|
show_boot_progress(-45);
|
|
return 1;
|
|
}
|
|
part = simple_strtoul(++ep, NULL, 16);
|
|
}
|
|
show_boot_progress(45);
|
|
if (get_partition_info(&ide_dev_desc[dev], part, &info)) {
|
|
show_boot_progress(-46);
|
|
return 1;
|
|
}
|
|
show_boot_progress(46);
|
|
if ((strncmp((char *)info.type, BOOT_PART_TYPE, sizeof(info.type)) != 0)
|
|
&&
|
|
(strncmp((char *)info.type, BOOT_PART_COMP, sizeof(info.type)) != 0)
|
|
) {
|
|
printf("\n** Invalid partition type \"%.32s\"" " (expect \""
|
|
BOOT_PART_TYPE "\")\n",
|
|
info.type);
|
|
show_boot_progress(-47);
|
|
return 1;
|
|
}
|
|
show_boot_progress(47);
|
|
|
|
printf("\nLoading from IDE device %d, partition %d: "
|
|
"Name: %.32s Type: %.32s\n", dev, part, info.name, info.type);
|
|
|
|
debug("First Block: %ld, # of blocks: %ld, Block Size: %ld\n",
|
|
info.start, info.size, info.blksz);
|
|
|
|
if (ide_dev_desc[dev].
|
|
block_read(dev, info.start, 1, (ulong *) addr) != 1) {
|
|
printf("** Read error on %d:%d\n", dev, part);
|
|
show_boot_progress(-48);
|
|
return 1;
|
|
}
|
|
show_boot_progress(48);
|
|
|
|
switch (genimg_get_format((void *) addr)) {
|
|
case IMAGE_FORMAT_LEGACY:
|
|
hdr = (image_header_t *) addr;
|
|
|
|
show_boot_progress(49);
|
|
|
|
if (!image_check_hcrc(hdr)) {
|
|
puts("\n** Bad Header Checksum **\n");
|
|
show_boot_progress(-50);
|
|
return 1;
|
|
}
|
|
show_boot_progress(50);
|
|
|
|
image_print_contents(hdr);
|
|
|
|
cnt = image_get_image_size(hdr);
|
|
break;
|
|
#if defined(CONFIG_FIT)
|
|
case IMAGE_FORMAT_FIT:
|
|
fit_hdr = (const void *) addr;
|
|
puts("Fit image detected...\n");
|
|
|
|
cnt = fit_get_size(fit_hdr);
|
|
break;
|
|
#endif
|
|
default:
|
|
show_boot_progress(-49);
|
|
puts("** Unknown image type\n");
|
|
return 1;
|
|
}
|
|
|
|
cnt += info.blksz - 1;
|
|
cnt /= info.blksz;
|
|
cnt -= 1;
|
|
|
|
if (ide_dev_desc[dev].block_read(dev, info.start + 1, cnt,
|
|
(ulong *)(addr + info.blksz)) != cnt) {
|
|
printf("** Read error on %d:%d\n", dev, part);
|
|
show_boot_progress(-51);
|
|
return 1;
|
|
}
|
|
show_boot_progress(51);
|
|
|
|
#if defined(CONFIG_FIT)
|
|
/* This cannot be done earlier, we need complete FIT image in RAM first */
|
|
if (genimg_get_format((void *) addr) == IMAGE_FORMAT_FIT) {
|
|
if (!fit_check_format(fit_hdr)) {
|
|
show_boot_progress(-140);
|
|
puts("** Bad FIT image format\n");
|
|
return 1;
|
|
}
|
|
show_boot_progress(141);
|
|
fit_print_contents(fit_hdr);
|
|
}
|
|
#endif
|
|
|
|
/* Loading ok, update default load address */
|
|
|
|
load_addr = addr;
|
|
|
|
return bootm_maybe_autostart(cmdtp, argv[0]);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
inline void __ide_outb(int dev, int port, unsigned char val)
|
|
{
|
|
debug("ide_outb (dev= %d, port= 0x%x, val= 0x%02x) : @ 0x%08lx\n",
|
|
dev, port, val,
|
|
(ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port)));
|
|
|
|
#if defined(CONFIG_IDE_AHB)
|
|
if (port) {
|
|
/* write command */
|
|
ide_write_register(dev, port, val);
|
|
} else {
|
|
/* write data */
|
|
outb(val, (ATA_CURR_BASE(dev)));
|
|
}
|
|
#else
|
|
outb(val, (ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port)));
|
|
#endif
|
|
}
|
|
|
|
void ide_outb(int dev, int port, unsigned char val)
|
|
__attribute__ ((weak, alias("__ide_outb")));
|
|
|
|
inline unsigned char __ide_inb(int dev, int port)
|
|
{
|
|
uchar val;
|
|
|
|
#if defined(CONFIG_IDE_AHB)
|
|
val = ide_read_register(dev, port);
|
|
#else
|
|
val = inb((ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port)));
|
|
#endif
|
|
|
|
debug("ide_inb (dev= %d, port= 0x%x) : @ 0x%08lx -> 0x%02x\n",
|
|
dev, port,
|
|
(ATA_CURR_BASE(dev) + CONFIG_SYS_ATA_PORT_ADDR(port)), val);
|
|
return val;
|
|
}
|
|
|
|
unsigned char ide_inb(int dev, int port)
|
|
__attribute__ ((weak, alias("__ide_inb")));
|
|
|
|
#ifdef CONFIG_TUNE_PIO
|
|
inline int __ide_set_piomode(int pio_mode)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
inline int ide_set_piomode(int pio_mode)
|
|
__attribute__ ((weak, alias("__ide_set_piomode")));
|
|
#endif
|
|
|
|
void ide_init(void)
|
|
{
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
|
|
volatile pcmconf8xx_t *pcmp = &(immr->im_pcmcia);
|
|
#endif
|
|
unsigned char c;
|
|
int i, bus;
|
|
|
|
#if defined(CONFIG_SC3)
|
|
unsigned int ata_reset_time = ATA_RESET_TIME;
|
|
#endif
|
|
#ifdef CONFIG_IDE_8xx_PCCARD
|
|
extern int pcmcia_on(void);
|
|
extern int ide_devices_found; /* Initialized in check_ide_device() */
|
|
#endif /* CONFIG_IDE_8xx_PCCARD */
|
|
|
|
#ifdef CONFIG_IDE_PREINIT
|
|
extern int ide_preinit(void);
|
|
|
|
WATCHDOG_RESET();
|
|
|
|
if (ide_preinit()) {
|
|
puts("ide_preinit failed\n");
|
|
return;
|
|
}
|
|
#endif /* CONFIG_IDE_PREINIT */
|
|
|
|
#ifdef CONFIG_IDE_8xx_PCCARD
|
|
extern int pcmcia_on(void);
|
|
extern int ide_devices_found; /* Initialized in check_ide_device() */
|
|
|
|
WATCHDOG_RESET();
|
|
|
|
ide_devices_found = 0;
|
|
/* initialize the PCMCIA IDE adapter card */
|
|
pcmcia_on();
|
|
if (!ide_devices_found)
|
|
return;
|
|
udelay(1000000); /* 1 s */
|
|
#endif /* CONFIG_IDE_8xx_PCCARD */
|
|
|
|
WATCHDOG_RESET();
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
/* Initialize PIO timing tables */
|
|
for (i = 0; i <= IDE_MAX_PIO_MODE; ++i) {
|
|
pio_config_clk[i].t_setup =
|
|
PCMCIA_MK_CLKS(pio_config_ns[i].t_setup, gd->bus_clk);
|
|
pio_config_clk[i].t_length =
|
|
PCMCIA_MK_CLKS(pio_config_ns[i].t_length,
|
|
gd->bus_clk);
|
|
pio_config_clk[i].t_hold =
|
|
PCMCIA_MK_CLKS(pio_config_ns[i].t_hold, gd->bus_clk);
|
|
debug("PIO Mode %d: setup=%2d ns/%d clk" " len=%3d ns/%d clk"
|
|
" hold=%2d ns/%d clk\n", i, pio_config_ns[i].t_setup,
|
|
pio_config_clk[i].t_setup, pio_config_ns[i].t_length,
|
|
pio_config_clk[i].t_length, pio_config_ns[i].t_hold,
|
|
pio_config_clk[i].t_hold);
|
|
}
|
|
#endif /* CONFIG_IDE_8xx_DIRECT */
|
|
|
|
/*
|
|
* Reset the IDE just to be sure.
|
|
* Light LED's to show
|
|
*/
|
|
ide_led((LED_IDE1 | LED_IDE2), 1); /* LED's on */
|
|
|
|
/* ATAPI Drives seems to need a proper IDE Reset */
|
|
ide_reset();
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
/* PCMCIA / IDE initialization for common mem space */
|
|
pcmp->pcmc_pgcrb = 0;
|
|
|
|
/* start in PIO mode 0 - most relaxed timings */
|
|
pio_mode = 0;
|
|
set_pcmcia_timing(pio_mode);
|
|
#endif /* CONFIG_IDE_8xx_DIRECT */
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
#endif
|
|
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 */
|
|
|
|
c = ide_inb(dev, ATA_STATUS);
|
|
i++;
|
|
#if defined(CONFIG_SC3)
|
|
if (i > (ata_reset_time * 100)) {
|
|
#else
|
|
if (i > (ATA_RESET_TIME * 100)) {
|
|
#endif
|
|
puts("** Timeout **\n");
|
|
/* LED's off */
|
|
ide_led((LED_IDE1 | LED_IDE2), 0);
|
|
return;
|
|
}
|
|
if ((i >= 100) && ((i % 100) == 0))
|
|
putc('.');
|
|
|
|
} while (c & ATA_STAT_BUSY);
|
|
|
|
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;
|
|
}
|
|
WATCHDOG_RESET();
|
|
}
|
|
|
|
putc('\n');
|
|
|
|
ide_led((LED_IDE1 | LED_IDE2), 0); /* LED's off */
|
|
|
|
curr_device = -1;
|
|
for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i) {
|
|
#ifdef CONFIG_IDE_LED
|
|
int led = (IDE_BUS(i) == 0) ? LED_IDE1 : LED_IDE2;
|
|
#endif
|
|
ide_dev_desc[i].type = DEV_TYPE_UNKNOWN;
|
|
ide_dev_desc[i].if_type = IF_TYPE_IDE;
|
|
ide_dev_desc[i].dev = i;
|
|
ide_dev_desc[i].part_type = PART_TYPE_UNKNOWN;
|
|
ide_dev_desc[i].blksz = 0;
|
|
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 */
|
|
init_part(&ide_dev_desc[i]);
|
|
if (curr_device < 0)
|
|
curr_device = i;
|
|
}
|
|
}
|
|
WATCHDOG_RESET();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#ifdef CONFIG_PARTITIONS
|
|
block_dev_desc_t *ide_get_dev(int dev)
|
|
{
|
|
return (dev < CONFIG_SYS_IDE_MAXDEVICE) ? &ide_dev_desc[dev] : NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_IDE_8xx_DIRECT
|
|
|
|
static void set_pcmcia_timing(int pmode)
|
|
{
|
|
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
|
|
volatile pcmconf8xx_t *pcmp = &(immr->im_pcmcia);
|
|
ulong timings;
|
|
|
|
debug("Set timing for PIO Mode %d\n", pmode);
|
|
|
|
timings = PCMCIA_SHT(pio_config_clk[pmode].t_hold)
|
|
| PCMCIA_SST(pio_config_clk[pmode].t_setup)
|
|
| PCMCIA_SL(pio_config_clk[pmode].t_length);
|
|
|
|
/*
|
|
* IDE 0
|
|
*/
|
|
pcmp->pcmc_pbr0 = CONFIG_SYS_PCMCIA_PBR0;
|
|
pcmp->pcmc_por0 = CONFIG_SYS_PCMCIA_POR0
|
|
#if (CONFIG_SYS_PCMCIA_POR0 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR0: %08x POR0: %08x\n", pcmp->pcmc_pbr0, pcmp->pcmc_por0);
|
|
|
|
pcmp->pcmc_pbr1 = CONFIG_SYS_PCMCIA_PBR1;
|
|
pcmp->pcmc_por1 = CONFIG_SYS_PCMCIA_POR1
|
|
#if (CONFIG_SYS_PCMCIA_POR1 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR1: %08x POR1: %08x\n", pcmp->pcmc_pbr1, pcmp->pcmc_por1);
|
|
|
|
pcmp->pcmc_pbr2 = CONFIG_SYS_PCMCIA_PBR2;
|
|
pcmp->pcmc_por2 = CONFIG_SYS_PCMCIA_POR2
|
|
#if (CONFIG_SYS_PCMCIA_POR2 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR2: %08x POR2: %08x\n", pcmp->pcmc_pbr2, pcmp->pcmc_por2);
|
|
|
|
pcmp->pcmc_pbr3 = CONFIG_SYS_PCMCIA_PBR3;
|
|
pcmp->pcmc_por3 = CONFIG_SYS_PCMCIA_POR3
|
|
#if (CONFIG_SYS_PCMCIA_POR3 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR3: %08x POR3: %08x\n", pcmp->pcmc_pbr3, pcmp->pcmc_por3);
|
|
|
|
/*
|
|
* IDE 1
|
|
*/
|
|
pcmp->pcmc_pbr4 = CONFIG_SYS_PCMCIA_PBR4;
|
|
pcmp->pcmc_por4 = CONFIG_SYS_PCMCIA_POR4
|
|
#if (CONFIG_SYS_PCMCIA_POR4 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR4: %08x POR4: %08x\n", pcmp->pcmc_pbr4, pcmp->pcmc_por4);
|
|
|
|
pcmp->pcmc_pbr5 = CONFIG_SYS_PCMCIA_PBR5;
|
|
pcmp->pcmc_por5 = CONFIG_SYS_PCMCIA_POR5
|
|
#if (CONFIG_SYS_PCMCIA_POR5 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR5: %08x POR5: %08x\n", pcmp->pcmc_pbr5, pcmp->pcmc_por5);
|
|
|
|
pcmp->pcmc_pbr6 = CONFIG_SYS_PCMCIA_PBR6;
|
|
pcmp->pcmc_por6 = CONFIG_SYS_PCMCIA_POR6
|
|
#if (CONFIG_SYS_PCMCIA_POR6 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR6: %08x POR6: %08x\n", pcmp->pcmc_pbr6, pcmp->pcmc_por6);
|
|
|
|
pcmp->pcmc_pbr7 = CONFIG_SYS_PCMCIA_PBR7;
|
|
pcmp->pcmc_por7 = CONFIG_SYS_PCMCIA_POR7
|
|
#if (CONFIG_SYS_PCMCIA_POR7 != 0)
|
|
| timings
|
|
#endif
|
|
;
|
|
debug("PBR7: %08x POR7: %08x\n", pcmp->pcmc_pbr7, pcmp->pcmc_por7);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_IDE_8xx_DIRECT */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* We only need to swap data if we are running on a big endian cpu. */
|
|
/* But Au1x00 cpu:s already swaps data in big endian mode! */
|
|
#if defined(__LITTLE_ENDIAN) || \
|
|
(defined(CONFIG_SOC_AU1X00) && !defined(CONFIG_GTH2))
|
|
#define input_swap_data(x,y,z) input_data(x,y,z)
|
|
#else
|
|
static void input_swap_data(int dev, ulong *sect_buf, int words)
|
|
{
|
|
#if defined(CONFIG_CPC45)
|
|
uchar i;
|
|
volatile uchar *pbuf_even =
|
|
(uchar *) (ATA_CURR_BASE(dev) + ATA_DATA_EVEN);
|
|
volatile uchar *pbuf_odd =
|
|
(uchar *) (ATA_CURR_BASE(dev) + ATA_DATA_ODD);
|
|
ushort *dbuf = (ushort *) sect_buf;
|
|
|
|
while (words--) {
|
|
for (i = 0; i < 2; i++) {
|
|
*(((uchar *) (dbuf)) + 1) = *pbuf_even;
|
|
*(uchar *) dbuf = *pbuf_odd;
|
|
dbuf += 1;
|
|
}
|
|
}
|
|
#else
|
|
volatile ushort *pbuf =
|
|
(ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG);
|
|
ushort *dbuf = (ushort *) sect_buf;
|
|
|
|
debug("in input swap data base for read is %lx\n",
|
|
(unsigned long) pbuf);
|
|
|
|
while (words--) {
|
|
#ifdef __MIPS__
|
|
*dbuf++ = swab16p((u16 *) pbuf);
|
|
*dbuf++ = swab16p((u16 *) pbuf);
|
|
#elif defined(CONFIG_PCS440EP)
|
|
*dbuf++ = *pbuf;
|
|
*dbuf++ = *pbuf;
|
|
#else
|
|
*dbuf++ = ld_le16(pbuf);
|
|
*dbuf++ = ld_le16(pbuf);
|
|
#endif /* !MIPS */
|
|
}
|
|
#endif
|
|
}
|
|
#endif /* __LITTLE_ENDIAN || CONFIG_AU1X00 */
|
|
|
|
|
|
#if defined(CONFIG_IDE_SWAP_IO)
|
|
static void output_data(int dev, const ulong *sect_buf, int words)
|
|
{
|
|
#if defined(CONFIG_CPC45)
|
|
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);
|
|
dbuf = (uchar *) sect_buf;
|
|
while (words--) {
|
|
EIEIO;
|
|
*pbuf_even = *dbuf++;
|
|
EIEIO;
|
|
*pbuf_odd = *dbuf++;
|
|
EIEIO;
|
|
*pbuf_even = *dbuf++;
|
|
EIEIO;
|
|
*pbuf_odd = *dbuf++;
|
|
}
|
|
#else
|
|
ushort *dbuf;
|
|
volatile ushort *pbuf;
|
|
|
|
pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG);
|
|
dbuf = (ushort *) sect_buf;
|
|
while (words--) {
|
|
#if defined(CONFIG_PCS440EP)
|
|
/* not tested, because CF was write protected */
|
|
EIEIO;
|
|
*pbuf = ld_le16(dbuf++);
|
|
EIEIO;
|
|
*pbuf = ld_le16(dbuf++);
|
|
#else
|
|
EIEIO;
|
|
*pbuf = *dbuf++;
|
|
EIEIO;
|
|
*pbuf = *dbuf++;
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#else /* ! CONFIG_IDE_SWAP_IO */
|
|
static void 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)
|
|
static void input_data(int dev, ulong *sect_buf, int words)
|
|
{
|
|
#if defined(CONFIG_CPC45)
|
|
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);
|
|
dbuf = (uchar *) sect_buf;
|
|
while (words--) {
|
|
*dbuf++ = *pbuf_even;
|
|
EIEIO;
|
|
SYNC;
|
|
*dbuf++ = *pbuf_odd;
|
|
EIEIO;
|
|
SYNC;
|
|
*dbuf++ = *pbuf_even;
|
|
EIEIO;
|
|
SYNC;
|
|
*dbuf++ = *pbuf_odd;
|
|
EIEIO;
|
|
SYNC;
|
|
}
|
|
#else
|
|
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--) {
|
|
#if defined(CONFIG_PCS440EP)
|
|
EIEIO;
|
|
*dbuf++ = ld_le16(pbuf);
|
|
EIEIO;
|
|
*dbuf++ = ld_le16(pbuf);
|
|
#else
|
|
EIEIO;
|
|
*dbuf++ = *pbuf;
|
|
EIEIO;
|
|
*dbuf++ = *pbuf;
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#else /* ! CONFIG_IDE_SWAP_IO */
|
|
static void 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 */
|
|
|
|
/* -------------------------------------------------------------------------
|
|
*/
|
|
static void ide_ident(block_dev_desc_t *dev_desc)
|
|
{
|
|
unsigned char c;
|
|
hd_driveid_t iop;
|
|
|
|
#ifdef CONFIG_ATAPI
|
|
int retries = 0;
|
|
#endif
|
|
|
|
#ifdef CONFIG_TUNE_PIO
|
|
int pio_mode;
|
|
#endif
|
|
|
|
#if 0
|
|
int mode, cycle_time;
|
|
#endif
|
|
int device;
|
|
|
|
device = dev_desc->dev;
|
|
printf(" Device %d: ", device);
|
|
|
|
ide_led(DEVICE_LED(device), 1); /* LED on */
|
|
/* Select device
|
|
*/
|
|
ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
|
|
dev_desc->if_type = IF_TYPE_IDE;
|
|
#ifdef CONFIG_ATAPI
|
|
|
|
retries = 0;
|
|
|
|
/* Warning: This will be tricky to read */
|
|
while (retries <= 1) {
|
|
/* 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)) {
|
|
/* ATAPI Signature found */
|
|
dev_desc->if_type = IF_TYPE_ATAPI;
|
|
/*
|
|
* Start Ident Command
|
|
*/
|
|
ide_outb(device, ATA_COMMAND, ATAPI_CMD_IDENT);
|
|
/*
|
|
* Wait for completion - ATAPI devices need more time
|
|
* to become ready
|
|
*/
|
|
c = ide_wait(device, ATAPI_TIME_OUT);
|
|
} else
|
|
#endif
|
|
{
|
|
/*
|
|
* Start Ident Command
|
|
*/
|
|
ide_outb(device, ATA_COMMAND, ATA_CMD_IDENT);
|
|
|
|
/*
|
|
* 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_ATAPI
|
|
{
|
|
/*
|
|
* Need to soft reset the device
|
|
* in case it's an ATAPI...
|
|
*/
|
|
debug("Retrying...\n");
|
|
ide_outb(device, ATA_DEV_HD,
|
|
ATA_LBA | ATA_DEVICE(device));
|
|
udelay(100000);
|
|
ide_outb(device, ATA_COMMAND, 0x08);
|
|
udelay(500000); /* 500 ms */
|
|
}
|
|
/*
|
|
* Select device
|
|
*/
|
|
ide_outb(device, ATA_DEV_HD,
|
|
ATA_LBA | ATA_DEVICE(device));
|
|
retries++;
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
#ifdef CONFIG_ATAPI
|
|
else
|
|
break;
|
|
} /* see above - ugly to read */
|
|
|
|
if (retries == 2) /* Not found */
|
|
return;
|
|
#endif
|
|
|
|
input_swap_data(device, (ulong *)&iop, ATA_SECTORWORDS);
|
|
|
|
ident_cpy((unsigned char *) dev_desc->revision, iop.fw_rev,
|
|
sizeof(dev_desc->revision));
|
|
ident_cpy((unsigned char *) dev_desc->vendor, iop.model,
|
|
sizeof(dev_desc->vendor));
|
|
ident_cpy((unsigned char *) dev_desc->product, iop.serial_no,
|
|
sizeof(dev_desc->product));
|
|
#ifdef __LITTLE_ENDIAN
|
|
/*
|
|
* firmware revision, model, and serial number have Big Endian Byte
|
|
* order in Word. Convert all three to little endian.
|
|
*
|
|
* See CF+ and CompactFlash Specification Revision 2.0:
|
|
* 6.2.1.6: Identify Drive, Table 39 for more details
|
|
*/
|
|
|
|
strswab(dev_desc->revision);
|
|
strswab(dev_desc->vendor);
|
|
strswab(dev_desc->product);
|
|
#endif /* __LITTLE_ENDIAN */
|
|
|
|
if ((iop.config & 0x0080) == 0x0080)
|
|
dev_desc->removable = 1;
|
|
else
|
|
dev_desc->removable = 0;
|
|
|
|
#ifdef CONFIG_TUNE_PIO
|
|
/* Mode 0 - 2 only, are directly determined by word 51. */
|
|
pio_mode = iop.tPIO;
|
|
if (pio_mode > 2) {
|
|
printf("WARNING: Invalid PIO (word 51 = %d).\n", pio_mode);
|
|
/* Force it to dead slow, and hope for the best... */
|
|
pio_mode = 0;
|
|
}
|
|
|
|
/* Any CompactFlash Storage Card that supports PIO mode 3 or above
|
|
* shall set bit 1 of word 53 to one and support the fields contained
|
|
* in words 64 through 70.
|
|
*/
|
|
if (iop.field_valid & 0x02) {
|
|
/*
|
|
* Mode 3 and above are possible. Check in order from slow
|
|
* to fast, so we wind up with the highest mode allowed.
|
|
*/
|
|
if (iop.eide_pio_modes & 0x01)
|
|
pio_mode = 3;
|
|
if (iop.eide_pio_modes & 0x02)
|
|
pio_mode = 4;
|
|
if (ata_id_is_cfa((u16 *)&iop)) {
|
|
if ((iop.cf_advanced_caps & 0x07) == 0x01)
|
|
pio_mode = 5;
|
|
if ((iop.cf_advanced_caps & 0x07) == 0x02)
|
|
pio_mode = 6;
|
|
}
|
|
}
|
|
|
|
/* System-specific, depends on bus speeds, etc. */
|
|
ide_set_piomode(pio_mode);
|
|
#endif /* CONFIG_TUNE_PIO */
|
|
|
|
#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;
|
|
debug("Override tPIO -> 2\n");
|
|
}
|
|
if (iop.field_valid & 2) { /* drive implements ATA2? */
|
|
debug("Drive implements ATA2\n");
|
|
if (iop.capability & 8) { /* drive supports use_iordy? */
|
|
cycle_time = iop.eide_pio_iordy;
|
|
} else {
|
|
cycle_time = iop.eide_pio;
|
|
}
|
|
debug("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
|
|
/* 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 */
|
|
|
|
#ifdef CONFIG_LBA48
|
|
if (iop.command_set_2 & 0x0400) { /* LBA 48 support */
|
|
dev_desc->lba48 = 1;
|
|
dev_desc->lba = (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->lba48 = 0;
|
|
}
|
|
#endif /* CONFIG_LBA48 */
|
|
/* 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));
|
|
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 | ATA_DEVICE(device));
|
|
ide_outb(device, ATA_COMMAND, 0xe3);
|
|
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, void *buffer)
|
|
{
|
|
ulong n = 0;
|
|
unsigned char c;
|
|
unsigned char pwrsave = 0; /* power save */
|
|
|
|
#ifdef CONFIG_LBA48
|
|
unsigned char lba48 = 0;
|
|
|
|
if (blknr & 0x0000fffff0000000ULL) {
|
|
/* more than 28 bits used, use 48bit mode */
|
|
lba48 = 1;
|
|
}
|
|
#endif
|
|
debug("ide_read dev %d start %lX, blocks %lX buffer at %lX\n",
|
|
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));
|
|
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);
|
|
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);
|
|
debug("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;
|
|
}
|
|
#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);
|
|
|
|
#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);
|
|
|
|
} else
|
|
#endif
|
|
{
|
|
ide_outb(device, ATA_DEV_HD, ATA_LBA |
|
|
ATA_DEVICE(device) | ((blknr >> 24) & 0xF));
|
|
ide_outb(device, ATA_COMMAND, ATA_CMD_READ);
|
|
}
|
|
|
|
udelay(50);
|
|
|
|
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);
|
|
}
|
|
|
|
if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) !=
|
|
ATA_STAT_DRQ) {
|
|
#if defined(CONFIG_SYS_64BIT_LBA)
|
|
printf("Error (no IRQ) dev %d blk %lld: status 0x%02x\n",
|
|
device, blknr, c);
|
|
#else
|
|
printf("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
|
|
device, (ulong) blknr, c);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
input_data(device, buffer, ATA_SECTORWORDS);
|
|
(void) ide_inb(device, ATA_STATUS); /* clear IRQ */
|
|
|
|
++n;
|
|
++blknr;
|
|
buffer += ATA_BLOCKSIZE;
|
|
}
|
|
IDE_READ_E:
|
|
ide_led(DEVICE_LED(device), 0); /* LED off */
|
|
return (n);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
|
ulong ide_write(int device, lbaint_t blknr, ulong blkcnt, const void *buffer)
|
|
{
|
|
ulong n = 0;
|
|
unsigned char c;
|
|
|
|
#ifdef CONFIG_LBA48
|
|
unsigned char lba48 = 0;
|
|
|
|
if (blknr & 0x0000fffff0000000ULL) {
|
|
/* more than 28 bits used, use 48bit mode */
|
|
lba48 = 1;
|
|
}
|
|
#endif
|
|
|
|
ide_led(DEVICE_LED(device), 1); /* LED on */
|
|
|
|
/* Select device
|
|
*/
|
|
ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
|
|
|
|
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;
|
|
}
|
|
#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);
|
|
|
|
#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);
|
|
|
|
} else
|
|
#endif
|
|
{
|
|
ide_outb(device, ATA_DEV_HD, ATA_LBA |
|
|
ATA_DEVICE(device) | ((blknr >> 24) & 0xF));
|
|
ide_outb(device, ATA_COMMAND, ATA_CMD_WRITE);
|
|
}
|
|
|
|
udelay(50);
|
|
|
|
/* 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) {
|
|
#if defined(CONFIG_SYS_64BIT_LBA)
|
|
printf("Error (no IRQ) dev %d blk %lld: status 0x%02x\n",
|
|
device, blknr, c);
|
|
#else
|
|
printf("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
|
|
device, (ulong) blknr, c);
|
|
#endif
|
|
goto WR_OUT;
|
|
}
|
|
|
|
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 */
|
|
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'
|
|
*/
|
|
static void ident_cpy(unsigned char *dst, unsigned char *src,
|
|
unsigned int len)
|
|
{
|
|
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;
|
|
}
|
|
OUT:
|
|
*last = '\0';
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* 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) {
|
|
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(CONFIG_SYS_PB_12V_ENABLE) || defined(CONFIG_SYS_PB_IDE_MOTOR)
|
|
volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
|
|
#endif
|
|
int i;
|
|
|
|
curr_device = -1;
|
|
for (i = 0; i < CONFIG_SYS_IDE_MAXBUS; ++i)
|
|
ide_bus_ok[i] = 0;
|
|
for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; ++i)
|
|
ide_dev_desc[i].type = DEV_TYPE_UNKNOWN;
|
|
|
|
ide_set_reset(1); /* assert reset */
|
|
|
|
/* the reset signal shall be asserted for et least 25 us */
|
|
udelay(25);
|
|
|
|
WATCHDOG_RESET();
|
|
|
|
#ifdef CONFIG_SYS_PB_12V_ENABLE
|
|
/* 12V Enable output OFF */
|
|
immr->im_cpm.cp_pbdat &= ~(CONFIG_SYS_PB_12V_ENABLE);
|
|
|
|
immr->im_cpm.cp_pbpar &= ~(CONFIG_SYS_PB_12V_ENABLE);
|
|
immr->im_cpm.cp_pbodr &= ~(CONFIG_SYS_PB_12V_ENABLE);
|
|
immr->im_cpm.cp_pbdir |= CONFIG_SYS_PB_12V_ENABLE;
|
|
|
|
/* wait 500 ms for the voltage to stabilize */
|
|
for (i = 0; i < 500; ++i)
|
|
udelay(1000);
|
|
|
|
/* 12V Enable output ON */
|
|
immr->im_cpm.cp_pbdat |= CONFIG_SYS_PB_12V_ENABLE;
|
|
#endif /* CONFIG_SYS_PB_12V_ENABLE */
|
|
|
|
#ifdef CONFIG_SYS_PB_IDE_MOTOR
|
|
/* configure IDE Motor voltage monitor pin as input */
|
|
immr->im_cpm.cp_pbpar &= ~(CONFIG_SYS_PB_IDE_MOTOR);
|
|
immr->im_cpm.cp_pbodr &= ~(CONFIG_SYS_PB_IDE_MOTOR);
|
|
immr->im_cpm.cp_pbdir &= ~(CONFIG_SYS_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 & CONFIG_SYS_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 /* CONFIG_SYS_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_CPC45) && \
|
|
!defined(CONFIG_KUP4K) && \
|
|
!defined(CONFIG_KUP4X)
|
|
|
|
static uchar led_buffer; /* 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 */
|
|
|
|
#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
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#ifdef CONFIG_ATAPI
|
|
/****************************************************************************
|
|
* ATAPI Support
|
|
*/
|
|
|
|
#if defined(CONFIG_IDE_SWAP_IO)
|
|
/* 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)
|
|
{
|
|
#if defined(CONFIG_CPC45)
|
|
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--) {
|
|
EIEIO;
|
|
*pbuf_even = *dbuf++;
|
|
EIEIO;
|
|
*pbuf_odd = *dbuf++;
|
|
}
|
|
#else
|
|
ushort *dbuf;
|
|
volatile ushort *pbuf;
|
|
|
|
pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG);
|
|
dbuf = (ushort *) sect_buf;
|
|
|
|
debug("in output data shorts base for read is %lx\n",
|
|
(unsigned long) pbuf);
|
|
|
|
while (shorts--) {
|
|
EIEIO;
|
|
*pbuf = *dbuf++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void input_data_shorts(int dev, ushort *sect_buf, int shorts)
|
|
{
|
|
#if defined(CONFIG_CPC45)
|
|
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--) {
|
|
EIEIO;
|
|
*dbuf++ = *pbuf_even;
|
|
EIEIO;
|
|
*dbuf++ = *pbuf_odd;
|
|
}
|
|
#else
|
|
ushort *dbuf;
|
|
volatile ushort *pbuf;
|
|
|
|
pbuf = (ushort *) (ATA_CURR_BASE(dev) + ATA_DATA_REG);
|
|
dbuf = (ushort *) sect_buf;
|
|
|
|
debug("in input data shorts base for read is %lx\n",
|
|
(unsigned long) pbuf);
|
|
|
|
while (shorts--) {
|
|
EIEIO;
|
|
*dbuf++ = *pbuf;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#else /* ! CONFIG_IDE_SWAP_IO */
|
|
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);
|
|
}
|
|
|
|
#endif /* CONFIG_IDE_SWAP_IO */
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/* prevents to read the status before valid */
|
|
c = ide_inb(dev, ATA_DEV_CTL);
|
|
|
|
while (((c = ide_inb(dev, ATA_STATUS)) & mask) != res) {
|
|
/* 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;
|
|
ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
|
|
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));
|
|
ide_outb(device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
|
|
|
|
ide_outb(device, ATA_COMMAND, ATAPI_CMD_PACKET);
|
|
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("ATAPI_ISSUE: Error (no IRQ) before sending ccb dev %d status 0x%02x\n",
|
|
device, c);
|
|
err = 0xFF;
|
|
goto AI_OUT;
|
|
}
|
|
|
|
/* write command block */
|
|
output_data_shorts(device, (unsigned short *) ccb, ccblen / 2);
|
|
|
|
/* 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;
|
|
debug("atapi_issue 1 returned sense key %X status %02X\n",
|
|
err, c);
|
|
} else {
|
|
printf("ATAPI_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);
|
|
n <<= 8;
|
|
n += ide_inb(device, ATA_CYL_LOW);
|
|
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) {
|
|
debug("WARNING, transfer bytes %d not equal with requested %d\n",
|
|
n, buflen);
|
|
}
|
|
if (n != 0) { /* data transfer */
|
|
debug("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) {
|
|
debug("Write to device\n");
|
|
output_data_shorts(device, (unsigned short *) buffer,
|
|
n);
|
|
} else {
|
|
debug("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);
|
|
debug("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;
|
|
|
|
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 */
|
|
|
|
debug("(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 */
|
|
|
|
res = atapi_issue(device, sense_ccb, 12, sense_data, 18);
|
|
key = (sense_data[2] & 0xF);
|
|
asc = (sense_data[12]);
|
|
ascq = (sense_data[13]);
|
|
|
|
debug("ATAPI_CMD_REQ_SENSE returned %x\n", res);
|
|
debug(" 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) {
|
|
debug("Media not present\n");
|
|
goto error;
|
|
}
|
|
|
|
printf("ERROR: Unknown Sense key %02X ASC %02X ASCQ %02X\n", key, asc,
|
|
ascq);
|
|
error:
|
|
debug("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);
|
|
|
|
debug("ATAPI_CMD_INQUIRY returned %x\n", c);
|
|
if (c != 0)
|
|
return;
|
|
|
|
/* copy device ident strings */
|
|
ident_cpy((unsigned char *) dev_desc->vendor, &iobuf[8], 8);
|
|
ident_cpy((unsigned char *) dev_desc->product, &iobuf[16], 16);
|
|
ident_cpy((unsigned char *) 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);
|
|
|
|
debug("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);
|
|
|
|
debug("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);
|
|
debug("ATAPI_CMD_READ_CAP returned %x\n", c);
|
|
if (c != 0)
|
|
return;
|
|
|
|
debug("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]);
|
|
#ifdef CONFIG_LBA48
|
|
/* ATAPI devices cannot use 48bit addressing (ATA/ATAPI v7) */
|
|
dev_desc->lba48 = 0;
|
|
#endif
|
|
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)
|
|
|
|
ulong atapi_read(int device, lbaint_t blknr, ulong blkcnt, void *buffer)
|
|
{
|
|
ulong n = 0;
|
|
unsigned char ccb[12]; /* Command descriptor block */
|
|
ulong cnt;
|
|
|
|
debug("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
|
|
cnt = blkcnt;
|
|
|
|
ccb[0] = ATAPI_CMD_READ_12;
|
|
ccb[1] = 0; /* reserved */
|
|
ccb[2] = (unsigned char) (blknr >> 24) & 0xFF; /* MSB Block */
|
|
ccb[3] = (unsigned char) (blknr >> 16) & 0xFF; /* */
|
|
ccb[4] = (unsigned char) (blknr >> 8) & 0xFF;
|
|
ccb[5] = (unsigned char) blknr & 0xFF; /* LSB Block */
|
|
ccb[6] = (unsigned char) (cnt >> 24) & 0xFF; /* MSB Block cnt */
|
|
ccb[7] = (unsigned char) (cnt >> 16) & 0xFF;
|
|
ccb[8] = (unsigned char) (cnt >> 8) & 0xFF;
|
|
ccb[9] = (unsigned char) cnt & 0xFF; /* LSB Block */
|
|
ccb[10] = 0; /* reserved */
|
|
ccb[11] = 0; /* reserved */
|
|
|
|
if (atapi_issue_autoreq(device, ccb, 12,
|
|
(unsigned char *) buffer,
|
|
cnt * ATAPI_READ_BLOCK_SIZE)
|
|
== 0xFF) {
|
|
return (n);
|
|
}
|
|
n += cnt;
|
|
blkcnt -= cnt;
|
|
blknr += cnt;
|
|
buffer += (cnt * ATAPI_READ_BLOCK_SIZE);
|
|
} while (blkcnt > 0);
|
|
return (n);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#endif /* CONFIG_ATAPI */
|
|
|
|
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");
|