aboutsummaryrefslogtreecommitdiff
path: root/common/cmd_ide.c
diff options
context:
space:
mode:
authorwdenk2002-11-03 00:24:07 +0000
committerwdenk2002-11-03 00:24:07 +0000
commitc609719b8d1b2dca590e0ed499016d041203e403 (patch)
tree7ea1755d80903ff972f312a249eb856061d40e15 /common/cmd_ide.c
parent5b1d713721c3ea02549940133f09236783dda1f9 (diff)
Initial revision
Diffstat (limited to 'common/cmd_ide.c')
-rw-r--r--common/cmd_ide.c1563
1 files changed, 1563 insertions, 0 deletions
diff --git a/common/cmd_ide.c b/common/cmd_ide.c
new file mode 100644
index 00000000000..9cbfe1bb04a
--- /dev/null
+++ b/common/cmd_ide.c
@@ -0,0 +1,1563 @@
+/*
+ * (C) Copyright 2000-2002
+ * 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>
+#if defined(CONFIG_IDE_8xx_DIRECT) || defined(CONFIG_IDE_PCMCIA)
+# include <pcmcia.h>
+#endif
+#ifdef CONFIG_8xx
+# include <mpc8xx.h>
+#endif
+#include <ide.h>
+#include <ata.h>
+#include <cmd_ide.h>
+#include <cmd_disk.h>
+#ifdef CONFIG_STATUS_LED
+# include <status_led.h>
+#endif
+
+#ifdef CONFIG_SHOW_BOOT_PROGRESS
+# include <status_led.h>
+# define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg)
+#else
+# define SHOW_BOOT_PROGRESS(arg)
+#endif
+
+
+#undef IDE_DEBUG
+
+#ifdef IDE_DEBUG
+#define PRINTF(fmt,args...) printf (fmt ,##args)
+#else
+#define PRINTF(fmt,args...)
+#endif
+
+#if (CONFIG_COMMANDS & CFG_CMD_IDE)
+
+/* 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 CFG_PIO_MODE
+#define CFG_PIO_MODE 0 /* use a relaxed default */
+#endif
+static int pio_mode = CFG_PIO_MODE;
+
+/* Make clock cycles and always round up */
+
+#define PCMCIA_MK_CLKS( t, T ) (( (t) * (T) + 999U ) / 1000U )
+
+/* ------------------------------------------------------------------------- */
+
+/* Current I/O Device */
+static int curr_device = -1;
+
+/* Current offset for IDE0 / IDE1 bus access */
+ulong ide_bus_offset[CFG_IDE_MAXBUS] = {
+#if defined(CFG_ATA_IDE0_OFFSET)
+ CFG_ATA_IDE0_OFFSET,
+#endif
+#if defined(CFG_ATA_IDE1_OFFSET) && (CFG_IDE_MAXBUS > 1)
+ CFG_ATA_IDE1_OFFSET,
+#endif
+};
+
+#define ATA_CURR_BASE(dev) (CFG_ATA_BASE_ADDR+ide_bus_offset[IDE_BUS(dev)])
+
+static int ide_bus_ok[CFG_IDE_MAXBUS];
+
+static block_dev_desc_t ide_dev_desc[CFG_IDE_MAXDEVICE];
+/* ------------------------------------------------------------------------- */
+
+#ifdef CONFIG_IDE_LED
+static void ide_led (uchar led, uchar status);
+#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 __inline__ outb(int dev, int port, unsigned char val);
+static unsigned char __inline__ inb(int dev, int port);
+static void input_swap_data(int dev, ulong *sect_buf, int words);
+static void input_data(int dev, ulong *sect_buf, int words);
+static void output_data(int dev, ulong *sect_buf, int words);
+static void ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len);
+
+
+#ifdef CONFIG_ATAPI
+static void atapi_inquiry(block_dev_desc_t *dev_desc);
+ulong atapi_read (int device, ulong blknr, ulong blkcnt, ulong *buffer);
+#endif
+
+
+#ifdef CONFIG_IDE_8xx_DIRECT
+static void set_pcmcia_timing (int pmode);
+#else
+#define set_pcmcia_timing(a) /* dummy */
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+int do_ide (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int rcode = 0;
+
+ switch (argc) {
+ case 0:
+ case 1:
+ printf ("Usage:\n%s\n", cmdtp->usage);
+ return 1;
+ 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<CFG_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 >= CFG_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<CFG_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;
+ }
+ printf ("Usage:\n%s\n", cmdtp->usage);
+ return 1;
+ 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 >= CFG_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;
+#if 0
+ } else if (strncmp(argv[1],"pio",4) == 0) {
+ int mode = (int)simple_strtoul(argv[2], NULL, 10);
+
+ if ((mode >= 0) && (mode <= IDE_MAX_PIO_MODE)) {
+ puts ("\nSetting ");
+ pio_mode = mode;
+ ide_init ();
+ } else {
+ printf ("\nInvalid PIO mode %d (0 ... %d only)\n",
+ mode, IDE_MAX_PIO_MODE);
+ }
+ return;
+#endif
+ }
+
+ printf ("Usage:\n%s\n", cmdtp->usage);
+ return 1;
+ default:
+ /* at least 4 args */
+
+ if (strcmp(argv[1],"read") == 0) {
+ ulong addr = simple_strtoul(argv[2], NULL, 16);
+ ulong blk = simple_strtoul(argv[3], NULL, 16);
+ ulong cnt = simple_strtoul(argv[4], NULL, 16);
+ ulong n;
+
+ printf ("\nIDE read: device %d block # %ld, count %ld ... ",
+ curr_device, blk, cnt);
+
+ 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 blk = simple_strtoul(argv[3], NULL, 16);
+ ulong cnt = simple_strtoul(argv[4], NULL, 16);
+ ulong n;
+
+ printf ("\nIDE write: device %d block # %ld, count %ld ... ",
+ curr_device, blk, cnt);
+
+ 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 {
+ printf ("Usage:\n%s\n", cmdtp->usage);
+ rcode = 1;
+ }
+
+ return rcode;
+ }
+}
+
+int do_diskboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ char *boot_device = NULL;
+ char *ep;
+ int dev, part = 0;
+ ulong cnt;
+ ulong addr;
+ disk_partition_t info;
+ image_header_t *hdr;
+ int rcode = 0;
+
+ switch (argc) {
+ case 1:
+ addr = CFG_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:
+ printf ("Usage:\n%s\n", cmdtp->usage);
+ SHOW_BOOT_PROGRESS (-1);
+ return 1;
+ }
+
+ if (!boot_device) {
+ puts ("\n** No boot device **\n");
+ SHOW_BOOT_PROGRESS (-1);
+ return 1;
+ }
+
+ 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 (-1);
+ return 1;
+ }
+
+ if (*ep) {
+ if (*ep != ':') {
+ puts ("\n** Invalid boot device, use `dev[:part]' **\n");
+ SHOW_BOOT_PROGRESS (-1);
+ return 1;
+ }
+ part = simple_strtoul(++ep, NULL, 16);
+ }
+ if (get_partition_info (&ide_dev_desc[dev], part, &info)) {
+ SHOW_BOOT_PROGRESS (-1);
+ return 1;
+ }
+ if (strncmp(info.type, BOOT_PART_TYPE, sizeof(info.type)) != 0) {
+ printf ("\n** Invalid partition type \"%.32s\""
+ " (expect \"" BOOT_PART_TYPE "\")\n",
+ info.type);
+ SHOW_BOOT_PROGRESS (-1);
+ return 1;
+ }
+
+ printf ("\nLoading from IDE device %d, partition %d: "
+ "Name: %.32s Type: %.32s\n",
+ dev, part, info.name, info.type);
+
+ PRINTF ("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 (-1);
+ return 1;
+ }
+
+ hdr = (image_header_t *)addr;
+
+ if (ntohl(hdr->ih_magic) == IH_MAGIC) {
+
+ print_image_hdr (hdr);
+
+ cnt = (ntohl(hdr->ih_size) + sizeof(image_header_t));
+ cnt += info.blksz - 1;
+ cnt /= info.blksz;
+ cnt -= 1;
+ } else {
+ printf("\n** Bad Magic Number **\n");
+ SHOW_BOOT_PROGRESS (-1);
+ return 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 (-1);
+ return 1;
+ }
+
+
+ /* Loading ok, update default load address */
+
+ load_addr = addr;
+
+ /* Check if we should attempt an auto-start */
+ if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) {
+ char *local_args[2];
+ extern int do_bootm (cmd_tbl_t *, int, int, char *[]);
+
+ local_args[0] = argv[0];
+ local_args[1] = NULL;
+
+ printf ("Automatic boot of image at addr 0x%08lX ...\n", addr);
+
+ do_bootm (cmdtp, 0, 1, local_args);
+ rcode = 1;
+ }
+ return rcode;
+}
+
+/* ------------------------------------------------------------------------- */
+
+void ide_init (void)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_IDE_8xx_DIRECT
+ volatile immap_t *immr = (immap_t *)CFG_IMMR;
+ volatile pcmconf8xx_t *pcmp = &(immr->im_pcmcia);
+#endif
+ unsigned char c;
+ int i, bus;
+
+#ifdef CONFIG_IDE_8xx_PCCARD
+ extern int pcmcia_on (void);
+
+ WATCHDOG_RESET();
+
+ /* initialize the PCMCIA IDE adapter card */
+ if (pcmcia_on())
+ return;
+ udelay (1000000); /* 1 s */
+#endif /* CONFIG_IDE_8xx_PCCARD */
+
+ WATCHDOG_RESET();
+
+ /* 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);
+ PRINTF ("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);
+ }
+
+ /* Reset the IDE just to be sure.
+ * Light LED's to show
+ */
+ ide_led ((LED_IDE1 | LED_IDE2), 1); /* LED's on */
+ ide_reset (); /* ATAPI Drives seems to need a proper IDE Reset */
+
+#ifdef CONFIG_IDE_8xx_DIRECT
+ /* PCMCIA / IDE initialization for common mem space */
+ pcmp->pcmc_pgcrb = 0;
+#endif
+
+ /* start in PIO mode 0 - most relaxed timings */
+ pio_mode = 0;
+ set_pcmcia_timing (pio_mode);
+
+ /*
+ * Wait for IDE to get ready.
+ * According to spec, this can take up to 31 seconds!
+ */
+ for (bus=0; bus<CFG_IDE_MAXBUS; ++bus) {
+ int dev = bus * (CFG_IDE_MAXDEVICE / CFG_IDE_MAXBUS);
+
+ printf ("Bus %d: ", bus);
+
+ ide_bus_ok[bus] = 0;
+
+ /* Select device
+ */
+ udelay (100000); /* 100 ms */
+ outb (dev, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(dev));
+ udelay (100000); /* 100 ms */
+
+ i = 0;
+ do {
+ udelay (10000); /* 10 ms */
+
+ c = inb (dev, ATA_STATUS);
+ i++;
+ if (i > (ATA_RESET_TIME * 100)) {
+ puts ("** Timeout **\n");
+ ide_led ((LED_IDE1 | LED_IDE2), 0); /* LED's off */
+ return;
+ }
+ if ((i >= 100) && ((i%100)==0)) {
+ putc ('.');
+ }
+ } while (c & ATA_STAT_BUSY);
+
+ if (c & (ATA_STAT_BUSY | ATA_STAT_FAULT)) {
+ puts ("not available ");
+ PRINTF ("Status = 0x%02X ", c);
+#ifndef CONFIG_ATAPI /* ATAPI Devices do not set DRDY */
+ } else if ((c & ATA_STAT_READY) == 0) {
+ puts ("not available ");
+ PRINTF ("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<CFG_IDE_MAXDEVICE; ++i) {
+#ifdef CONFIG_IDE_LED
+ int led = (IDE_BUS(i) == 0) ? LED_IDE1 : LED_IDE2;
+#endif
+ 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;
+ 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]);
+/* ide_print (i); */
+ if ((ide_dev_desc[i].lba > 0) && (ide_dev_desc[i].blksz > 0)) {
+ init_part (&ide_dev_desc[i]); /* initialize partition type */
+ if (curr_device < 0)
+ curr_device = i;
+ }
+ }
+ WATCHDOG_RESET();
+}
+
+/* ------------------------------------------------------------------------- */
+
+block_dev_desc_t * ide_get_dev(int dev)
+{
+ return ((block_dev_desc_t *)&ide_dev_desc[dev]);
+}
+
+
+#ifdef CONFIG_IDE_8xx_DIRECT
+
+static void
+set_pcmcia_timing (int pmode)
+{
+ volatile immap_t *immr = (immap_t *)CFG_IMMR;
+ volatile pcmconf8xx_t *pcmp = &(immr->im_pcmcia);
+ ulong timings;
+
+ PRINTF ("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 = CFG_PCMCIA_PBR0;
+ pcmp->pcmc_por0 = CFG_PCMCIA_POR0
+#if (CFG_PCMCIA_POR0 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR0: %08x POR0: %08x\n", pcmp->pcmc_pbr0, pcmp->pcmc_por0);
+
+ pcmp->pcmc_pbr1 = CFG_PCMCIA_PBR1;
+ pcmp->pcmc_por1 = CFG_PCMCIA_POR1
+#if (CFG_PCMCIA_POR1 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR1: %08x POR1: %08x\n", pcmp->pcmc_pbr1, pcmp->pcmc_por1);
+
+ pcmp->pcmc_pbr2 = CFG_PCMCIA_PBR2;
+ pcmp->pcmc_por2 = CFG_PCMCIA_POR2
+#if (CFG_PCMCIA_POR2 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR2: %08x POR2: %08x\n", pcmp->pcmc_pbr2, pcmp->pcmc_por2);
+
+ pcmp->pcmc_pbr3 = CFG_PCMCIA_PBR3;
+ pcmp->pcmc_por3 = CFG_PCMCIA_POR3
+#if (CFG_PCMCIA_POR3 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR3: %08x POR3: %08x\n", pcmp->pcmc_pbr3, pcmp->pcmc_por3);
+
+ /* IDE 1
+ */
+ pcmp->pcmc_pbr4 = CFG_PCMCIA_PBR4;
+ pcmp->pcmc_por4 = CFG_PCMCIA_POR4
+#if (CFG_PCMCIA_POR4 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR4: %08x POR4: %08x\n", pcmp->pcmc_pbr4, pcmp->pcmc_por4);
+
+ pcmp->pcmc_pbr5 = CFG_PCMCIA_PBR5;
+ pcmp->pcmc_por5 = CFG_PCMCIA_POR5
+#if (CFG_PCMCIA_POR5 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR5: %08x POR5: %08x\n", pcmp->pcmc_pbr5, pcmp->pcmc_por5);
+
+ pcmp->pcmc_pbr6 = CFG_PCMCIA_PBR6;
+ pcmp->pcmc_por6 = CFG_PCMCIA_POR6
+#if (CFG_PCMCIA_POR6 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR6: %08x POR6: %08x\n", pcmp->pcmc_pbr6, pcmp->pcmc_por6);
+
+ pcmp->pcmc_pbr7 = CFG_PCMCIA_PBR7;
+ pcmp->pcmc_por7 = CFG_PCMCIA_POR7
+#if (CFG_PCMCIA_POR7 != 0)
+ | timings
+#endif
+ ;
+ PRINTF ("PBR7: %08x POR7: %08x\n", pcmp->pcmc_pbr7, pcmp->pcmc_por7);
+
+}
+
+#endif /* CONFIG_IDE_8xx_DIRECT */
+
+/* ------------------------------------------------------------------------- */
+
+static void __inline__
+outb(int dev, int port, unsigned char val)
+{
+ /* Ensure I/O operations complete */
+ __asm__ volatile("eieio");
+ *((uchar *)(ATA_CURR_BASE(dev)+port)) = val;
+#if 0
+ printf ("OUTB: 0x%08lx <== 0x%02x\n", ATA_CURR_BASE(dev)+port, val);
+#endif
+}
+
+static unsigned char __inline__
+inb(int dev, int port)
+{
+ uchar val;
+ /* Ensure I/O operations complete */
+ __asm__ volatile("eieio");
+ val = *((uchar *)(ATA_CURR_BASE(dev)+port));
+#if 0
+ printf ("INB: 0x%08lx ==> 0x%02x\n", ATA_CURR_BASE(dev)+port, val);
+#endif
+ return (val);
+}
+
+__inline__ unsigned ld_le16(const volatile unsigned short *addr)
+{
+ unsigned val;
+
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r"(val) : "r"(addr), "m"(*addr));
+ return val;
+}
+
+static void
+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;
+
+ while (words--) {
+ *dbuf++ = ld_le16(pbuf);
+ *dbuf++ = ld_le16(pbuf);
+ }
+}
+
+static void
+output_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;
+ while (words--) {
+ __asm__ volatile ("eieio");
+ *pbuf = *dbuf++;
+ __asm__ volatile ("eieio");
+ *pbuf = *dbuf++;
+ }
+}
+
+static void
+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;
+ while (words--) {
+ __asm__ volatile ("eieio");
+ *dbuf++ = *pbuf;
+ __asm__ volatile ("eieio");
+ *dbuf++ = *pbuf;
+ }
+}
+
+/* -------------------------------------------------------------------------
+ */
+static void ide_ident (block_dev_desc_t *dev_desc)
+{
+ ulong iobuf[ATA_SECTORWORDS];
+ unsigned char c;
+ hd_driveid_t *iop = (hd_driveid_t *)iobuf;
+
+#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
+ */
+ outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
+ dev_desc->if_type=IF_TYPE_IDE;
+#ifdef CONFIG_ATAPI
+ /* check signature */
+ if ((inb(device,ATA_SECT_CNT)==0x01) &&
+ (inb(device,ATA_SECT_NUM)==0x01) &&
+ (inb(device,ATA_CYL_LOW)==0x14) &&
+ (inb(device,ATA_CYL_HIGH)==0xEB)) {
+ /* ATAPI Signature found */
+ dev_desc->if_type=IF_TYPE_ATAPI;
+ /* Start Ident Command
+ */
+ 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
+ */
+ 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) ) {
+ dev_desc->type=DEV_TYPE_UNKNOWN;
+ return;
+ }
+
+ 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));
+
+ 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 */
+
+ /* swap shorts */
+ dev_desc->lba = (iop->lba_capacity << 16) | (iop->lba_capacity >> 16);
+ /* 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 */
+ outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
+ c = ide_wait (device, IDE_TIME_OUT);
+ outb (device, ATA_SECT_CNT, 1);
+ outb (device, ATA_LBA_LOW, 0);
+ outb (device, ATA_LBA_MID, 0);
+ outb (device, ATA_LBA_HIGH, 0);
+ outb (device, ATA_DEV_HD, ATA_LBA |
+ ATA_DEVICE(device));
+ 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, ulong blknr, ulong blkcnt, ulong *buffer)
+{
+ ulong n = 0;
+ unsigned char c;
+ unsigned char pwrsave=0; /* power save */
+
+ PRINTF ("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
+ */
+ 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 */
+ 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 = inb(device,ATA_SECT_CNT);
+ 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;
+ }
+
+ outb (device, ATA_SECT_CNT, 1);
+ outb (device, ATA_LBA_LOW, (blknr >> 0) & 0xFF);
+ outb (device, ATA_LBA_MID, (blknr >> 8) & 0xFF);
+ outb (device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF);
+ outb (device, ATA_DEV_HD, ATA_LBA |
+ ATA_DEVICE(device) |
+ ((blknr >> 24) & 0xF) );
+ outb (device, ATA_COMMAND, ATA_CMD_READ);
+
+ 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) {
+ printf ("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
+ device, blknr, c);
+ break;
+ }
+
+ input_data (device, buffer, ATA_SECTORWORDS);
+ (void) inb (device, ATA_STATUS); /* clear IRQ */
+
+ ++n;
+ ++blknr;
+ buffer += ATA_SECTORWORDS;
+ }
+IDE_READ_E:
+ ide_led (DEVICE_LED(device), 0); /* LED off */
+ return (n);
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+ulong ide_write (int device, ulong blknr, ulong blkcnt, ulong *buffer)
+{
+ ulong n = 0;
+ unsigned char c;
+
+ ide_led (DEVICE_LED(device), 1); /* LED on */
+
+ /* Select device
+ */
+ 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;
+ }
+
+ outb (device, ATA_SECT_CNT, 1);
+ outb (device, ATA_LBA_LOW, (blknr >> 0) & 0xFF);
+ outb (device, ATA_LBA_MID, (blknr >> 8) & 0xFF);
+ outb (device, ATA_LBA_HIGH, (blknr >> 16) & 0xFF);
+ outb (device, ATA_DEV_HD, ATA_LBA |
+ ATA_DEVICE(device) |
+ ((blknr >> 24) & 0xF) );
+ outb (device, ATA_COMMAND, ATA_CMD_WRITE);
+
+ 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) {
+ printf ("Error (no IRQ) dev %d blk %ld: status 0x%02x\n",
+ device, blknr, c);
+ goto WR_OUT;
+ }
+
+ output_data (device, buffer, ATA_SECTORWORDS);
+ c = inb (device, ATA_STATUS); /* clear IRQ */
+ ++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
+ */
+static void ident_cpy (unsigned char *dest, unsigned char *src, unsigned int len)
+{
+ int start,end;
+
+ start=0;
+ while (start<len) {
+ if (src[start]!=' ')
+ break;
+ start++;
+ }
+ end=len-1;
+ while (end>start) {
+ if (src[end]!=' ')
+ break;
+ end--;
+ }
+ for ( ; start<=end; start++) {
+ *dest++=src[start];
+ }
+ *dest='\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 = 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(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 */
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef CONFIG_IDE_LED
+
+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
+
+/* 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)
+{
+ 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++;
+ }
+}
+
+static void
+input_data_shorts(int dev, ushort *sect_buf, int shorts)
+{
+ 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;
+ }
+}
+
+/*
+ * 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 = inb(dev,ATA_DEV_CTL); /* prevents to read the status before valid */
+ while (((c = 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;
+ 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 */
+ outb (device, ATA_ERROR_REG, 0); /* no DMA, no overlaped */
+ outb (device, ATA_CYL_LOW, (unsigned char)(buflen & 0xFF));
+ outb (device, ATA_CYL_HIGH, (unsigned char)((buflen<<8) & 0xFF));
+ outb (device, ATA_DEV_HD, ATA_LBA | ATA_DEVICE(device));
+
+ 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 ("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=(inb(device,ATA_ERROR_REG))>>4;
+ 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=inb(device, ATA_CYL_HIGH);
+ n<<=8;
+ n+=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) {
+ 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 ((inb(device, ATA_SECT_CNT)&0x02)==0) {
+ 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=(inb(device,ATA_ERROR_REG) >> 4);
+ 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;
+
+ 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 Legnth */
+
+ 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;
+ }
+ 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]);
+ 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, ulong blknr, ulong blkcnt, ulong *buffer)
+{
+ 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 {
+ 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 count */
+ 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/4); /* ulong blocksize in ulong */
+ } while (blkcnt > 0);
+ return (n);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#endif /* CONFIG_ATAPI */
+
+#endif /* CONFIG_COMMANDS & CFG_CMD_IDE */