diff options
author | wdenk <wdenk> | 2002-11-03 00:24:07 +0000 |
---|---|---|
committer | wdenk <wdenk> | 2002-11-03 00:24:07 +0000 |
commit | c609719b8d1b2dca590e0ed499016d041203e403 (patch) | |
tree | 7ea1755d80903ff972f312a249eb856061d40e15 /common | |
parent | 5b1d713721c3ea02549940133f09236783dda1f9 (diff) | |
download | u-boot-imx-c609719b8d1b2dca590e0ed499016d041203e403.zip u-boot-imx-c609719b8d1b2dca590e0ed499016d041203e403.tar.gz u-boot-imx-c609719b8d1b2dca590e0ed499016d041203e403.tar.bz2 |
Initial revision
Diffstat (limited to 'common')
-rw-r--r-- | common/cmd_doc.c | 1563 | ||||
-rw-r--r-- | common/cmd_ide.c | 1563 | ||||
-rw-r--r-- | common/cmd_immap.c | 578 | ||||
-rw-r--r-- | common/cmd_jffs2.c | 179 | ||||
-rw-r--r-- | common/cmd_pci.c | 477 | ||||
-rw-r--r-- | common/cmd_pcmcia.c | 2243 | ||||
-rw-r--r-- | common/env_flash.c | 377 | ||||
-rw-r--r-- | common/env_nvram.c | 138 | ||||
-rw-r--r-- | common/environment.c | 198 | ||||
-rw-r--r-- | common/main.c | 817 | ||||
-rw-r--r-- | common/miiphyutil.c | 172 | ||||
-rw-r--r-- | common/soft_i2c.c | 416 |
12 files changed, 8721 insertions, 0 deletions
diff --git a/common/cmd_doc.c b/common/cmd_doc.c new file mode 100644 index 0000000..37d0fbd --- /dev/null +++ b/common/cmd_doc.c @@ -0,0 +1,1563 @@ +/* + * Driver for Disk-On-Chip 2000 and Millennium + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> + * + * $Id: doc2000.c,v 1.46 2001/10/02 15:05:13 dwmw2 Exp $ + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <malloc.h> +#include <asm/io.h> + +#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 + +#if (CONFIG_COMMANDS & CFG_CMD_DOC) + +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ids.h> +#include <linux/mtd/doc2000.h> +#include <linux/mtd/nftl.h> + +#ifdef CFG_DOC_SUPPORT_2000 +#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k) +#else +#define DoC_is_2000(doc) (0) +#endif + +#ifdef CFG_DOC_SUPPORT_MILLENNIUM +#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) +#else +#define DoC_is_Millennium(doc) (0) +#endif + +/* CFG_DOC_PASSIVE_PROBE: + In order to ensure that the BIOS checksum is correct at boot time, and + hence that the onboard BIOS extension gets executed, the DiskOnChip + goes into reset mode when it is read sequentially: all registers + return 0xff until the chip is woken up again by writing to the + DOCControl register. + + Unfortunately, this means that the probe for the DiskOnChip is unsafe, + because one of the first things it does is write to where it thinks + the DOCControl register should be - which may well be shared memory + for another device. I've had machines which lock up when this is + attempted. Hence the possibility to do a passive probe, which will fail + to detect a chip in reset mode, but is at least guaranteed not to lock + the machine. + + If you have this problem, uncomment the following line: +#define CFG_DOC_PASSIVE_PROBE +*/ + +#undef DOC_DEBUG +#undef ECC_DEBUG +#undef PSYCHO_DEBUG +#undef NFTL_DEBUG + +static struct DiskOnChip doc_dev_desc[CFG_MAX_DOC_DEVICE]; + +/* Current DOC Device */ +static int curr_device = -1; + +/* ------------------------------------------------------------------------- */ + +int do_doc (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 (strcmp(argv[1],"info") == 0) { + int i; + + putc ('\n'); + + for (i=0; i<CFG_MAX_DOC_DEVICE; ++i) { + if(doc_dev_desc[i].ChipID == DOC_ChipID_UNKNOWN) + continue; /* list only known devices */ + printf ("Device %d: ", i); + doc_print(&doc_dev_desc[i]); + } + return 0; + + } else if (strcmp(argv[1],"device") == 0) { + if ((curr_device < 0) || (curr_device >= CFG_MAX_DOC_DEVICE)) { + puts ("\nno devices available\n"); + return 1; + } + printf ("\nDevice %d: ", curr_device); + doc_print(&doc_dev_desc[curr_device]); + return 0; + } + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + case 3: + if (strcmp(argv[1],"device") == 0) { + int dev = (int)simple_strtoul(argv[2], NULL, 10); + + printf ("\nDevice %d: ", dev); + if (dev >= CFG_MAX_DOC_DEVICE) { + puts ("unknown device\n"); + return 1; + } + doc_print(&doc_dev_desc[dev]); + /*doc_print (dev);*/ + + if (doc_dev_desc[dev].ChipID == DOC_ChipID_UNKNOWN) { + return 1; + } + + curr_device = dev; + + puts ("... is now current device\n"); + + return 0; + } + + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + default: + /* at least 4 args */ + + if (strcmp(argv[1],"read") == 0 || strcmp(argv[1],"write") == 0) { + ulong addr = simple_strtoul(argv[2], NULL, 16); + ulong off = simple_strtoul(argv[3], NULL, 16); + ulong size = simple_strtoul(argv[4], NULL, 16); + int cmd = (strcmp(argv[1],"read") == 0); + int ret, total; + + printf ("\nDOC %s: device %d offset %ld, size %ld ... ", + cmd ? "read" : "write", curr_device, off, size); + + ret = doc_rw(doc_dev_desc + curr_device, cmd, off, size, + &total, (u_char*)addr); + + printf ("%d bytes %s: %s\n", total, cmd ? "read" : "write", + ret ? "ERROR" : "OK"); + + return ret; + } else if (strcmp(argv[1],"erase") == 0) { + ulong off = simple_strtoul(argv[2], NULL, 16); + ulong size = simple_strtoul(argv[3], NULL, 16); + int ret; + + printf ("\nDOC erase: device %d offset %ld, size %ld ... ", + curr_device, off, size); + + ret = doc_erase (doc_dev_desc + curr_device, off, size); + + printf("%s\n", ret ? "ERROR" : "OK"); + + return ret; + } else { + printf ("Usage:\n%s\n", cmdtp->usage); + rcode = 1; + } + + return rcode; + } +} + +int do_docboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char *boot_device = NULL; + char *ep; + int dev; + ulong cnt; + ulong addr; + ulong offset = 0; + 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; + case 4: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + offset = simple_strtoul(argv[3], NULL, 16); + 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 ((dev >= CFG_MAX_DOC_DEVICE) || + (doc_dev_desc[dev].ChipID == DOC_ChipID_UNKNOWN)) { + printf ("\n** Device %d not available\n", dev); + SHOW_BOOT_PROGRESS (-1); + return 1; + } + + printf ("\nLoading from device %d: %s at 0x%lX (offset 0x%lX)\n", + dev, doc_dev_desc[dev].name, doc_dev_desc[dev].physadr, + offset); + + if (doc_rw (doc_dev_desc + dev, 1, offset, + SECTORSIZE, NULL, (u_char *)addr)) { + printf ("** Read error on %d\n", dev); + SHOW_BOOT_PROGRESS (-1); + return 1; + } + + hdr = (image_header_t *)addr; + + if (hdr->ih_magic == IH_MAGIC) { + + print_image_hdr (hdr); + + cnt = (hdr->ih_size + sizeof(image_header_t)); + cnt -= SECTORSIZE; + } else { + puts ("\n** Bad Magic Number **\n"); + SHOW_BOOT_PROGRESS (-1); + return 1; + } + + if (doc_rw (doc_dev_desc + dev, 1, offset + SECTORSIZE, cnt, + NULL, (u_char *)(addr+SECTORSIZE))) { + printf ("** Read error on %d\n", dev); + 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; +} + +int doc_rw (struct DiskOnChip* this, int cmd, + loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + int noecc, ret = 0, n, total = 0; + char eccbuf[6]; + + while(len) { + /* The ECC will not be calculated correctly if + less than 512 is written or read */ + noecc = (from != (from | 0x1ff) + 1) || (len < 0x200); + + if (cmd) + ret = doc_read_ecc(this, from, len, + &n, (u_char*)buf, + noecc ? NULL : eccbuf); + else + ret = doc_write_ecc(this, from, len, + &n, (u_char*)buf, + noecc ? NULL : eccbuf); + + if (ret) + break; + + from += n; + buf += n; + total += n; + len -= n; + } + + if (retlen) + *retlen = total; + + return ret; +} + +void doc_print(struct DiskOnChip *this) { + printf("%s at 0x%lX,\n" + "\t %d chip%s %s, size %d MB, \n" + "\t total size %ld MB, sector size %ld kB\n", + this->name, this->physadr, this->numchips, + this->numchips>1 ? "s" : "", this->chips_name, + 1 << (this->chipshift - 20), + this->totlen >> 20, this->erasesize >> 10); + + if (this->nftl_found) { + struct NFTLrecord *nftl = &this->nftl; + unsigned long bin_size, flash_size; + + bin_size = nftl->nb_boot_blocks * this->erasesize; + flash_size = (nftl->nb_blocks - nftl->nb_boot_blocks) * this->erasesize; + + printf("\t NFTL boot record:\n" + "\t Binary partition: size %ld%s\n" + "\t Flash disk partition: size %ld%s, offset 0x%lx\n", + bin_size > (1 << 20) ? bin_size >> 20 : bin_size >> 10, + bin_size > (1 << 20) ? "MB" : "kB", + flash_size > (1 << 20) ? flash_size >> 20 : flash_size >> 10, + flash_size > (1 << 20) ? "MB" : "kB", bin_size); + } else { + puts ("\t No NFTL boot record found.\n"); + } +} + +/* ------------------------------------------------------------------------- */ + +/* This function is needed to avoid calls of the __ashrdi3 function. */ +static int shr(int val, int shift) { + return val >> shift; +} + +/* Perform the required delay cycles by reading from the appropriate register */ +static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles) +{ + volatile char dummy; + int i; + + for (i = 0; i < cycles; i++) { + if (DoC_is_Millennium(doc)) + dummy = ReadDOC(doc->virtadr, NOP); + else + dummy = ReadDOC(doc->virtadr, DOCStatus); + } + +} + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(struct DiskOnChip *doc) +{ + unsigned long docptr = doc->virtadr; + unsigned long start = get_timer(0); + +#ifdef PSYCHO_DEBUG + puts ("_DoC_WaitReady called for out-of-line wait\n"); +#endif + + /* Out-of-line routine to wait for chip response */ + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { +#ifdef CFG_DOC_SHORT_TIMEOUT + /* it seems that after a certain time the DoC deasserts + * the CDSN_CTRL_FR_B although it is not ready... + * using a short timout solve this (timer increments every ms) */ + if (get_timer(start) > 10) { + return DOC_ETIMEOUT; + } +#else + if (get_timer(start) > 10 * 1000) { + puts ("_DoC_WaitReady timed out.\n"); + return DOC_ETIMEOUT; + } +#endif + udelay(1); + } + + return 0; +} + +static int DoC_WaitReady(struct DiskOnChip *doc) +{ + unsigned long docptr = doc->virtadr; + /* This is inline, to optimise the common case, where it's ready instantly */ + int ret = 0; + + /* 4 read form NOP register should be issued in prior to the read from CDSNControl + see Software Requirement 11.4 item 2. */ + DoC_Delay(doc, 4); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + + /* issue 2 read from NOP register after reading from CDSNControl register + see Software Requirement 11.4 item 2. */ + DoC_Delay(doc, 2); + + return ret; +} + +/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to + bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ + +static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, + unsigned char xtraflags) +{ + unsigned long docptr = doc->virtadr; + + if (DoC_is_2000(doc)) + xtraflags |= CDSN_CTRL_FLASH_IO; + + /* Assert the CLE (Command Latch Enable) line to the flash chip */ + WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + if (DoC_is_Millennium(doc)) + WriteDOC(command, docptr, CDSNSlowIO); + + /* Send the command */ + WriteDOC_(command, docptr, doc->ioreg); + + /* Lower the CLE line */ + WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + /* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */ + return DoC_WaitReady(doc); +} + +/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to + bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is + required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ + +static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) +{ + unsigned long docptr; + int i; + + docptr = doc->virtadr; + + if (DoC_is_2000(doc)) + xtraflags1 |= CDSN_CTRL_FLASH_IO; + + /* Assert the ALE (Address Latch Enable) line to the flash chip */ + WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl); + + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + /* Send the address */ + /* Devices with 256-byte page are addressed as: + Column (bits 0-7), Page (bits 8-15, 16-23, 24-31) + * there is no device on the market with page256 + and more than 24 bits. + Devices with 512-byte page are addressed as: + Column (bits 0-7), Page (bits 9-16, 17-24, 25-31) + * 25-31 is sent only if the chip support it. + * bit 8 changes the read command to be sent + (NAND_CMD_READ0 or NAND_CMD_READ1). + */ + + if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) { + if (DoC_is_Millennium(doc)) + WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); + WriteDOC_(ofs & 0xff, docptr, doc->ioreg); + } + + if (doc->page256) { + ofs = ofs >> 8; + } else { + ofs = ofs >> 9; + } + + if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) { + for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) { + if (DoC_is_Millennium(doc)) + WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); + WriteDOC_(ofs & 0xff, docptr, doc->ioreg); + } + } + + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ + + /* FIXME: The SlowIO's for millennium could be replaced by + a single WritePipeTerm here. mf. */ + + /* Lower the ALE line */ + WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, + CDSNControl); + + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + /* Wait for the chip to respond - Software requirement 11.4.1 */ + return DoC_WaitReady(doc); +} + +/* Read a buffer from DoC, taking care of Millennium odditys */ +static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len) +{ + volatile int dummy; + int modulus = 0xffff; + unsigned long docptr; + int i; + + docptr = doc->virtadr; + + if (len <= 0) + return; + + if (DoC_is_Millennium(doc)) { + /* Read the data via the internal pipeline through CDSN IO register, + see Pipelined Read Operations 11.3 */ + dummy = ReadDOC(docptr, ReadPipeInit); + + /* Millennium should use the LastDataRead register - Pipeline Reads */ + len--; + + /* This is needed for correctly ECC calculation */ + modulus = 0xff; + } + + for (i = 0; i < len; i++) + buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus)); + + if (DoC_is_Millennium(doc)) { + buf[i] = ReadDOC(docptr, LastDataRead); + } +} + +/* Write a buffer to DoC, taking care of Millennium odditys */ +static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len) +{ + unsigned long docptr; + int i; + + docptr = doc->virtadr; + + if (len <= 0) + return; + + for (i = 0; i < len; i++) + WriteDOC_(buf[i], docptr, doc->ioreg + i); + + if (DoC_is_Millennium(doc)) { + WriteDOC(0x00, docptr, WritePipeTerm); + } +} + + +/* DoC_SelectChip: Select a given flash chip within the current floor */ + +static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip) +{ + unsigned long docptr = doc->virtadr; + + /* Software requirement 11.4.4 before writing DeviceSelect */ + /* Deassert the CE line to eliminate glitches on the FCE# outputs */ + WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + /* Select the individual flash chip requested */ + WriteDOC(chip, docptr, CDSNDeviceSelect); + DoC_Delay(doc, 4); + + /* Reassert the CE line */ + WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr, + CDSNControl); + DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */ + + /* Wait for it to be ready */ + return DoC_WaitReady(doc); +} + +/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ + +static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) +{ + unsigned long docptr = doc->virtadr; + + /* Select the floor (bank) of chips required */ + WriteDOC(floor, docptr, FloorSelect); + + /* Wait for the chip to be ready */ + return DoC_WaitReady(doc); +} + +/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ + +static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) +{ + int mfr, id, i; + volatile char dummy; + + /* Page in the required floor/chip */ + DoC_SelectFloor(doc, floor); + DoC_SelectChip(doc, chip); + + /* Reset the chip */ + if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) { +#ifdef DOC_DEBUG + printf("DoC_Command (reset) for %d,%d returned true\n", + floor, chip); +#endif + return 0; + } + + + /* Read the NAND chip ID: 1. Send ReadID command */ + if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) { +#ifdef DOC_DEBUG + printf("DoC_Command (ReadID) for %d,%d returned true\n", + floor, chip); +#endif + return 0; + } + + /* Read the NAND chip ID: 2. Send address byte zero */ + DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0); + + /* Read the manufacturer and device id codes from the device */ + + /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + mfr = ReadDOC_(doc->virtadr, doc->ioreg); + + /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + id = ReadDOC_(doc->virtadr, doc->ioreg); + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) + return 0; + + /* Check it's the same as the first chip we identified. + * M-Systems say that any given DiskOnChip device should only + * contain _one_ type of flash part, although that's not a + * hardware restriction. */ + if (doc->mfr) { + if (doc->mfr == mfr && doc->id == id) + return 1; /* This is another the same the first */ + else + printf("Flash chip at floor %d, chip %d is different:\n", + floor, chip); + } + + /* Print and store the manufacturer and ID codes. */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (mfr == nand_flash_ids[i].manufacture_id && + id == nand_flash_ids[i].model_id) { +#ifdef DOC_DEBUG + printf("Flash chip found: Manufacturer ID: %2.2X, " + "Chip ID: %2.2X (%s)\n", mfr, id, + nand_flash_ids[i].name); +#endif + if (!doc->mfr) { + doc->mfr = mfr; + doc->id = id; + doc->chipshift = + nand_flash_ids[i].chipshift; + doc->page256 = nand_flash_ids[i].page256; + doc->pageadrlen = + nand_flash_ids[i].pageadrlen; + doc->erasesize = + nand_flash_ids[i].erasesize; + doc->chips_name = + nand_flash_ids[i].name; + return 1; + } + return 0; + } + } + + +#ifdef DOC_DEBUG + /* We haven't fully identified the chip. Print as much as we know. */ + printf("Unknown flash chip found: %2.2X %2.2X\n", + id, mfr); +#endif + + return 0; +} + +/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ + +static void DoC_ScanChips(struct DiskOnChip *this) +{ + int floor, chip; + int numchips[MAX_FLOORS]; + int maxchips = MAX_CHIPS; + int ret = 1; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + + if (DoC_is_Millennium(this)) + maxchips = MAX_CHIPS_MIL; + + /* For each floor, find the number of valid chips it contains */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + ret = 1; + numchips[floor] = 0; + for (chip = 0; chip < maxchips && ret != 0; chip++) { + + ret = DoC_IdentChip(this, floor, chip); + if (ret) { + numchips[floor]++; + this->numchips++; + } + } + } + + /* If there are none at all that we recognise, bail */ + if (!this->numchips) { + puts ("No flash chips recognised.\n"); + return; + } + + /* Allocate an array to hold the information for each chip */ + this->chips = malloc(sizeof(struct Nand) * this->numchips); + if (!this->chips) { + puts ("No memory for allocating chip info structures\n"); + return; + } + + ret = 0; + + /* Fill out the chip array with {floor, chipno} for each + * detected chip in the device. */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + for (chip = 0; chip < numchips[floor]; chip++) { + this->chips[ret].floor = floor; + this->chips[ret].chip = chip; + this->chips[ret].curadr = 0; + this->chips[ret].curmode = 0x50; + ret++; + } + } + + /* Calculate and print the total size of the device */ + this->totlen = this->numchips * (1 << this->chipshift); + +#ifdef DOC_DEBUG + printf("%d flash chips found. Total DiskOnChip size: %ld MB\n", + this->numchips, this->totlen >> 20); +#endif +} + +/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the + * various device information of the NFTL partition and Bad Unit Table. Update + * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] + * is used for management of Erase Unit in other routines in nftl.c and nftlmount.c + */ +static int find_boot_record(struct NFTLrecord *nftl) +{ + struct nftl_uci1 h1; + struct nftl_oob oob; + unsigned int block, boot_record_count = 0; + int retlen; + u8 buf[SECTORSIZE]; + struct NFTLMediaHeader *mh = &nftl->MediaHdr; + unsigned int i; + + nftl->MediaUnit = BLOCK_NIL; + nftl->SpareMediaUnit = BLOCK_NIL; + + /* search for a valid boot record */ + for (block = 0; block < nftl->nb_blocks; block++) { + int ret; + + /* Check for ANAND header first. Then can whinge if it's found but later + checks fail */ + if ((ret = doc_read_ecc(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf, NULL))) { + static int warncount = 5; + + if (warncount) { + printf("Block read at 0x%x failed\n", block * nftl->EraseSize); + if (!--warncount) + puts ("Further failures for this block will not be printed\n"); + } + continue; + } + + if (retlen < 6 || memcmp(buf, "ANAND", 6)) { + /* ANAND\0 not found. Continue */ +#ifdef PSYCHO_DEBUG + printf("ANAND header not found at 0x%x\n", block * nftl->EraseSize); +#endif + continue; + } + +#ifdef NFTL_DEBUG + printf("ANAND header found at 0x%x\n", block * nftl->EraseSize); +#endif + + /* To be safer with BIOS, also use erase mark as discriminant */ + if ((ret = doc_read_oob(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&h1) < 0)) { +#ifdef NFTL_DEBUG + printf("ANAND header found at 0x%x, but OOB data read failed\n", + block * nftl->EraseSize); +#endif + continue; + } + + /* OK, we like it. */ + + if (boot_record_count) { + /* We've already processed one. So we just check if + this one is the same as the first one we found */ + if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) { +#ifdef NFTL_DEBUG + printf("NFTL Media Headers at 0x%x and 0x%x disagree.\n", + nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); +#endif + /* if (debug) Print both side by side */ + return -1; + } + if (boot_record_count == 1) + nftl->SpareMediaUnit = block; + + boot_record_count++; + continue; + } + + /* This is the first we've seen. Copy the media header structure into place */ + memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); + + /* Do some sanity checks on it */ + if (mh->UnitSizeFactor != 0xff) { + puts ("Sorry, we don't support UnitSizeFactor " + "of != 1 yet.\n"); + return -1; + } + + nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); + if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { + printf ("NFTL Media Header sanity check failed:\n" + "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n", + nftl->nb_boot_blocks, nftl->nb_blocks); + return -1; + } + + nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize; + if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) { + printf ("NFTL Media Header sanity check failed:\n" + "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n", + nftl->numvunits, + nftl->nb_blocks, + nftl->nb_boot_blocks); + return -1; + } + + nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); + + /* If we're not using the last sectors in the device for some reason, + reduce nb_blocks accordingly so we forget they're there */ + nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN); + + /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ + for (i = 0; i < nftl->nb_blocks; i++) { + if ((i & (SECTORSIZE - 1)) == 0) { + /* read one sector for every SECTORSIZE of blocks */ + if ((ret = doc_read_ecc(nftl->mtd, block * nftl->EraseSize + + i + SECTORSIZE, SECTORSIZE, + &retlen, buf, (char *)&oob)) < 0) { + puts ("Read of bad sector table failed\n"); + return -1; + } + } + /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ + if (buf[i & (SECTORSIZE - 1)] != 0xff) + nftl->ReplUnitTable[i] = BLOCK_RESERVED; + } + + nftl->MediaUnit = block; + boot_record_count++; + + } /* foreach (block) */ + + return boot_record_count?0:-1; +} + +/* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The + * addresses passed back in mtd are valid as long as the use count of + * this module is non-zero, i.e. between inter_module_get and + * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. + */ +static void DoC2k_init(struct DiskOnChip* this) +{ + struct NFTLrecord *nftl; + + switch (this->ChipID) { + case DOC_ChipID_Doc2k: + this->name = "DiskOnChip 2000"; + this->ioreg = DoC_2k_CDSN_IO; + break; + case DOC_ChipID_DocMil: + this->name = "DiskOnChip Millennium"; + this->ioreg = DoC_Mil_CDSN_IO; + break; + } + +#ifdef DOC_DEBUG + printf("%s found at address 0x%lX\n", this->name, + this->physadr); +#endif + + this->totlen = 0; + this->numchips = 0; + + this->curfloor = -1; + this->curchip = -1; + + /* Ident all the chips present. */ + DoC_ScanChips(this); + + nftl = &this->nftl; + + /* Get physical parameters */ + nftl->EraseSize = this->erasesize; + nftl->nb_blocks = this->totlen / this->erasesize; + nftl->mtd = this; + + if (find_boot_record(nftl) != 0) + this->nftl_found = 0; + else + this->nftl_found = 1; + + printf("%s @ 0x%lX, %ld MB\n", this->name, this->physadr, this->totlen >> 20); +} + +int doc_read_ecc(struct DiskOnChip* this, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * eccbuf) +{ + unsigned long docptr; + struct Nand *mychip; + unsigned char syndrome[6]; + volatile char dummy; + int i, len256 = 0, ret=0; + + docptr = this->virtadr; + + /* Don't allow read past end of device */ + if (from >= this->totlen) { + puts ("Out of flash\n"); + return DOC_EINVAL; + } + + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ((from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; + + /* The ECC will not be calculated correctly if less than 512 is read */ + if (len != 0x200 && eccbuf) + printf("ECC needs a full sector read (adr: %lx size %lx)\n", + (long) from, (long) len); + +#ifdef PHYCH_DEBUG + printf("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); +#endif + + /* Find the chip which is to be used and select it */ + mychip = &this->chips[shr(from, this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + DoC_Command(this, + (!this->page256 + && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, + CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP, + CDSN_CTRL_ECC_IO); + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + } + + /* treat crossing 256-byte sector for 2M x 8bits devices */ + if (this->page256 && from + len > (from | 0xff) + 1) { + len256 = (from | 0xff) + 1 - from; + DoC_ReadBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, from + len256, + CDSN_CTRL_WP, CDSN_CTRL_ECC_IO); + } + + DoC_ReadBuf(this, &buf[len256], len - len256); + + /* Let the caller know we completed it */ + *retlen = len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ + /* Note: this will work even with 2M x 8bit devices as */ + /* they have 8 bytes of OOB per 256 page. mf. */ + DoC_ReadBuf(this, eccbuf, 6); + + /* Flush the pipeline */ + if (DoC_is_Millennium(this)) { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + i = ReadDOC(docptr, ECCConf); + } else { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + i = ReadDOC(docptr, 2k_ECCStatus); + } + + /* Check the ECC Status */ + if (i & 0x80) { + int nb_errors; + /* There was an ECC error */ +#ifdef ECC_DEBUG + printf("DiskOnChip ECC Error: Read at %lx\n", (long)from); +#endif + /* Read the ECC syndrom through the DiskOnChip ECC logic. + These syndrome will be all ZERO when there is no error */ + for (i = 0; i < 6; i++) { + syndrome[i] = + ReadDOC(docptr, ECCSyndrome0 + i); + } + nb_errors = doc_decode_ecc(buf, syndrome); + +#ifdef ECC_DEBUG + printf("Errors corrected: %x\n", nb_errors); +#endif + if (nb_errors < 0) { + /* We return error, but have actually done the read. Not that + this can be told to user-space, via sys_read(), but at least + MTD-aware stuff can know about it by checking *retlen */ + printf("ECC Errors at %lx\n", (long)from); + ret = DOC_EECC; + } + } + +#ifdef PSYCHO_DEBUG + printf("ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5]); +#endif + + /* disable the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr , ECCConf); + } + + /* according to 11.4.1, we need to wait for the busy line + * drop if we read to the end of the page. */ + if(0 == ((from + *retlen) & 0x1ff)) + { + DoC_WaitReady(this); + } + + return ret; +} + +int doc_write_ecc(struct DiskOnChip* this, loff_t to, size_t len, + size_t * retlen, const u_char * buf, + u_char * eccbuf) +{ + int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ + unsigned long docptr; + volatile char dummy; + int len256 = 0; + struct Nand *mychip; + + docptr = this->virtadr; + + /* Don't allow write past end of device */ + if (to >= this->totlen) { + puts ("Out of flash\n"); + return DOC_EINVAL; + } + + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ((to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + + /* The ECC will not be calculated correctly if less than 512 is written */ + if (len != 0x200 && eccbuf) + printf("ECC needs a full sector write (adr: %lx size %lx)\n", + (long) to, (long) len); + + /* printf("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ + + /* Find the chip which is to be used and select it */ + mychip = &this->chips[shr(to, this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + /* Set device to main plane of flash */ + DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(this, + (!this->page256 + && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, + CDSN_CTRL_WP); + + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO); + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + } + + /* treat crossing 256-byte sector for 2M x 8bits devices */ + if (this->page256 && to + len > (to | 0xff) + 1) { + len256 = (to | 0xff) + 1 - to; + DoC_WriteBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + puts ("Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + return DOC_EIO; + } + + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0, + CDSN_CTRL_ECC_IO); + } + + DoC_WriteBuf(this, &buf[len256], len - len256); + + if (eccbuf) { + WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, + CDSNControl); + + if (DoC_is_Millennium(this)) { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } else { + WriteDOC_(0, docptr, this->ioreg); + WriteDOC_(0, docptr, this->ioreg); + WriteDOC_(0, docptr, this->ioreg); + } + + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di = 0; di < 6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); + } + + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + +#ifdef PSYCHO_DEBUG + printf + ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], + eccbuf[4], eccbuf[5]); +#endif + } + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + puts ("Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + return DOC_EIO; + } + + /* Let the caller know we completed it */ + *retlen = len; + + if (eccbuf) { + unsigned char x[8]; + size_t dummy; + int ret; + + /* Write the ECC data to flash */ + for (di=0; di<6; di++) + x[di] = eccbuf[di]; + + x[6]=0x55; + x[7]=0x55; + + ret = doc_write_oob(this, to, 8, &dummy, x); + return ret; + } + return 0; +} + +int doc_read_oob(struct DiskOnChip* this, loff_t ofs, size_t len, + size_t * retlen, u_char * buf) +{ + int len256 = 0, ret; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + mychip = &this->chips[shr(ofs, this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + /* update address for 2M x 8bit devices. OOB starts on the second */ + /* page to maintain compatibility with doc_read_ecc. */ + if (this->page256) { + if (!(ofs & 0x8)) + ofs += 0x100; + else + ofs -= 0x8; + } + + DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0); + + /* treat crossing 8-byte OOB data for 2M x 8bit devices */ + /* Note: datasheet says it should automaticaly wrap to the */ + /* next OOB block, but it didn't work here. mf. */ + if (this->page256 && ofs + len > (ofs | 0x7) + 1) { + len256 = (ofs | 0x7) + 1 - ofs; + DoC_ReadBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), + CDSN_CTRL_WP, 0); + } + + DoC_ReadBuf(this, &buf[len256], len - len256); + + *retlen = len; + /* Reading the full OOB data drops us off of the end of the page, + * causing the flash device to go into busy mode, so we need + * to wait until ready 11.4.1 and Toshiba TC58256FT docs */ + + ret = DoC_WaitReady(this); + + return ret; + +} + +int doc_write_oob(struct DiskOnChip* this, loff_t ofs, size_t len, + size_t * retlen, const u_char * buf) +{ + int len256 = 0; + unsigned long docptr = this->virtadr; + struct Nand *mychip = &this->chips[shr(ofs, this->chipshift)]; + volatile int dummy; + +#ifdef PSYCHO_DEBUG + printf("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n", + (long)ofs, len, buf[0], buf[1], buf[2], buf[3], + buf[8], buf[9], buf[14],buf[15]); +#endif + + /* Find the chip which is to be used and select it */ + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + /* disable the ECC engine */ + WriteDOC (DOC_ECC_RESET, docptr, ECCConf); + WriteDOC (DOC_ECC_DIS, docptr, ECCConf); + + /* Reset the chip, see Software Requirement 11.4 item 1. */ + DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); + + /* issue the Read2 command to set the pointer to the Spare Data Area. */ + DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP); + + /* update address for 2M x 8bit devices. OOB starts on the second */ + /* page to maintain compatibility with doc_read_ecc. */ + if (this->page256) { + if (!(ofs & 0x8)) + ofs += 0x100; + else + ofs -= 0x8; + } + + /* issue the Serial Data In command to initial the Page Program process */ + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0); + + /* treat crossing 8-byte OOB data for 2M x 8bit devices */ + /* Note: datasheet says it should automaticaly wrap to the */ + /* next OOB block, but it didn't work here. mf. */ + if (this->page256 && ofs + len > (ofs | 0x7) + 1) { + len256 = (ofs | 0x7) + 1 - ofs; + DoC_WriteBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + puts ("Error programming oob data\n"); + /* There was an error */ + *retlen = 0; + return DOC_EIO; + } + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0); + } + + DoC_WriteBuf(this, &buf[len256], len - len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + puts ("Error programming oob data\n"); + /* There was an error */ + *retlen = 0; + return DOC_EIO; + } + + *retlen = len; + return 0; + +} + +int doc_erase(struct DiskOnChip* this, loff_t ofs, size_t len) +{ + volatile int dummy; + unsigned long docptr; + struct Nand *mychip; + + if (ofs & (this->erasesize-1) || len & (this->erasesize-1)) { + puts ("Offset and size must be sector aligned\n"); + return DOC_EINVAL; + } + + docptr = this->virtadr; + + /* FIXME: Do this in the background. Use timers or schedule_task() */ + while(len) { + mychip = &this->chips[shr(ofs, this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + DoC_Command(this, NAND_CMD_ERASE1, 0); + DoC_Address(this, ADDR_PAGE, ofs, 0, 0); + DoC_Command(this, NAND_CMD_ERASE2, 0); + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + printf("Error erasing at 0x%lx\n", (long)ofs); + /* There was an error */ + goto callback; + } + ofs += this->erasesize; + len -= this->erasesize; + } + + callback: + return 0; +} + +static inline int doccheck(unsigned long potential, unsigned long physadr) +{ + unsigned long window=potential; + unsigned char tmp, ChipID; +#ifndef DOC_PASSIVE_PROBE + unsigned char tmp2; +#endif + + /* Routine copied from the Linux DOC driver */ + +#ifdef CFG_DOCPROBE_55AA + /* Check for 0x55 0xAA signature at beginning of window, + this is no longer true once we remove the IPL (for Millennium */ + if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa) + return 0; +#endif /* CFG_DOCPROBE_55AA */ + +#ifndef DOC_PASSIVE_PROBE + /* It's not possible to cleanly detect the DiskOnChip - the + * bootup procedure will put the device into reset mode, and + * it's not possible to talk to it without actually writing + * to the DOCControl register. So we store the current contents + * of the DOCControl register's location, in case we later decide + * that it's not a DiskOnChip, and want to put it back how we + * found it. + */ + tmp2 = ReadDOC(window, DOCControl); + + /* Reset the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + window, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + window, DOCControl); + + /* Enable the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + window, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + window, DOCControl); +#endif /* !DOC_PASSIVE_PROBE */ + + ChipID = ReadDOC(window, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; + if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp) + return ChipID; + break; + + case DOC_ChipID_DocMil: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; + if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) + return ChipID; + break; + + default: +#ifndef CFG_DOCPROBE_55AA +/* + * if the ID isn't the DoC2000 or DoCMillenium ID, so we can assume + * the DOC is missing + */ +# if 0 + printf("Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", + ChipID, physadr); +# endif +#endif +#ifndef DOC_PASSIVE_PROBE + /* Put back the contents of the DOCControl register, in case it's not + * actually a DiskOnChip. + */ + WriteDOC(tmp2, window, DOCControl); +#endif + return 0; + } + + puts ("DiskOnChip failed TOGGLE test, dropping.\n"); + +#ifndef DOC_PASSIVE_PROBE + /* Put back the contents of the DOCControl register: it's not a DiskOnChip */ + WriteDOC(tmp2, window, DOCControl); +#endif + return 0; +} + +void doc_probe(unsigned long physadr) +{ + struct DiskOnChip *this = NULL; + int i=0, ChipID; + + if ((ChipID = doccheck(physadr, physadr))) { + + for (i=0; i<CFG_MAX_DOC_DEVICE; i++) { + if (doc_dev_desc[i].ChipID == DOC_ChipID_UNKNOWN) { + this = doc_dev_desc + i; + break; + } + } + + if (!this) { + puts ("Cannot allocate memory for data structures.\n"); + return; + } + + if (curr_device == -1) + curr_device = i; + + memset((char *)this, 0, sizeof(struct DiskOnChip)); + + this->virtadr = physadr; + this->physadr = physadr; + this->ChipID = ChipID; + + DoC2k_init(this); + } else { + puts ("No DiskOnChip found\n"); + } +} + +#endif /* (CONFIG_COMMANDS & CFG_CMD_DOC) */ diff --git a/common/cmd_ide.c b/common/cmd_ide.c new file mode 100644 index 0000000..9cbfe1b --- /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 */ diff --git a/common/cmd_immap.c b/common/cmd_immap.c new file mode 100644 index 0000000..443335b --- /dev/null +++ b/common/cmd_immap.c @@ -0,0 +1,578 @@ +/* + * (C) Copyright 2000 + * 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 + */ + +/* + * MPC8xx/MPC8260 Internal Memory Map Functions + */ + +#include <common.h> +#include <command.h> +#include <cmd_immap.h> + +#if (CONFIG_COMMANDS & CFG_CMD_IMMAP) && \ + (defined(CONFIG_8xx) || defined(CONFIG_8260)) + +#if defined(CONFIG_8xx) +#include <asm/8xx_immap.h> +#include <commproc.h> +#elif defined(CONFIG_8260) +#include <asm/immap_8260.h> +#include <asm/cpm_8260.h> +#include <asm/iopin_8260.h> +#endif + +static void +unimplemented ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + printf ("Sorry, but the '%s' command has not been implemented\n", + cmdtp->name); +} + +int +do_siuinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + +#if defined(CONFIG_8xx) + volatile sysconf8xx_t *sc = &immap->im_siu_conf; +#elif defined(CONFIG_8260) + volatile sysconf8260_t *sc = &immap->im_siu_conf; +#endif + + printf ("SIUMCR= %08x SYPCR = %08x\n", sc->sc_siumcr, sc->sc_sypcr); +#if defined(CONFIG_8xx) + printf ("SWT = %08x\n", sc->sc_swt); + printf ("SIPEND= %08x SIMASK= %08x\n", sc->sc_sipend, sc->sc_simask); + printf ("SIEL = %08x SIVEC = %08x\n", sc->sc_siel, sc->sc_sivec); + printf ("TESR = %08x SDCR = %08x\n", sc->sc_tesr, sc->sc_sdcr); +#elif defined(CONFIG_8260) + printf ("BCR = %08x\n", sc->sc_bcr); + printf ("P_ACR = %02x P_ALRH= %08x P_ALRL= %08x\n", + sc->sc_ppc_acr, sc->sc_ppc_alrh, sc->sc_ppc_alrl); + printf ("L_ACR = %02x L_ALRH= %08x L_ALRL= %08x\n", + sc->sc_lcl_acr, sc->sc_lcl_alrh, sc->sc_lcl_alrl); + printf ("PTESR1= %08x PTESR2= %08x\n", sc->sc_tescr1, sc->sc_tescr2); + printf ("LTESR1= %08x LTESR2= %08x\n", sc->sc_ltescr1, sc->sc_ltescr2); + printf ("PDTEA = %08x PDTEM = %02x\n", sc->sc_pdtea, sc->sc_pdtem); + printf ("LDTEA = %08x LDTEM = %02x\n", sc->sc_ldtea, sc->sc_ldtem); +#endif + return 0; +} + +int +do_memcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + +#if defined(CONFIG_8xx) + volatile memctl8xx_t *memctl = &immap->im_memctl; + int nbanks = 8; +#elif defined(CONFIG_8260) + volatile memctl8260_t *memctl = &immap->im_memctl; + int nbanks = 12; +#endif + volatile uint *p = &memctl->memc_br0; + int i; + + for (i = 0; i < nbanks; i++, p += 2) { + if (i < 10) { + printf ("BR%d = %08x OR%d = %08x\n", + i, p[0], i, p[1]); + } else { + printf ("BR%d = %08x OR%d = %08x\n", + i, p[0], i, p[1]); + } + } + + printf ("MAR = %08x", memctl->memc_mar); +#if defined(CONFIG_8xx) + printf (" MCR = %08x\n", memctl->memc_mcr); +#elif defined(CONFIG_8260) + printf ("\n"); +#endif + printf ("MAMR = %08x MBMR = %08x", + memctl->memc_mamr, memctl->memc_mbmr); +#if defined(CONFIG_8xx) + printf ("\nMSTAT = %04x\n", memctl->memc_mstat); +#elif defined(CONFIG_8260) + printf (" MCMR = %08x\n", memctl->memc_mcmr); +#endif + printf ("MPTPR = %04x MDR = %08x\n", + memctl->memc_mptpr, memctl->memc_mdr); +#if defined(CONFIG_8260) + printf ("PSDMR = %08x LSDMR = %08x\n", + memctl->memc_psdmr, memctl->memc_lsdmr); + printf ("PURT = %02x PSRT = %02x\n", + memctl->memc_purt, memctl->memc_psrt); + printf ("LURT = %02x LSRT = %02x\n", + memctl->memc_lurt, memctl->memc_lsrt); + printf ("IMMR = %08x\n", memctl->memc_immr); +#endif + return 0; +} + +int +do_sitinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +#ifdef CONFIG_8260 +int +do_icinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} +#endif + +int +do_carinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +static int counter; + +static void +header(void) +{ + char *data = "\ + -------------------------------- --------------------------------\ + 00000000001111111111222222222233 00000000001111111111222222222233\ + 01234567890123456789012345678901 01234567890123456789012345678901\ + -------------------------------- --------------------------------\ + "; + int i; + + if (counter % 2) + putc('\n'); + counter = 0; + + for (i = 0; i < 4; i++, data += 79) + printf("%.79s\n", data); +} + +static void binary (char *label, uint value, int nbits) +{ + uint mask = 1 << (nbits - 1); + int i, second = (counter++ % 2); + + if (second) + putc (' '); + puts (label); + for (i = 32 + 1; i != nbits; i--) + putc (' '); + + while (mask != 0) { + if (value & mask) + putc ('1'); + else + putc ('0'); + mask >>= 1; + } + + if (second) + putc ('\n'); +} + +#if defined(CONFIG_8xx) +#define PA_NBITS 16 +#define PA_NB_ODR 8 +#define PB_NBITS 18 +#define PB_NB_ODR 16 +#define PC_NBITS 12 +#define PD_NBITS 13 +#elif defined(CONFIG_8260) +#define PA_NBITS 32 +#define PA_NB_ODR 32 +#define PB_NBITS 28 +#define PB_NB_ODR 28 +#define PC_NBITS 32 +#define PD_NBITS 28 +#endif + +int +do_iopinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + +#if defined(CONFIG_8xx) + volatile iop8xx_t *iop = &immap->im_ioport; + volatile ushort *l, *r; +#elif defined(CONFIG_8260) + volatile iop8260_t *iop = &immap->im_ioport; + volatile uint *l, *r; +#endif + volatile uint *R; + + counter = 0; + header (); + + /* + * Ports A & B + */ + +#if defined(CONFIG_8xx) + l = &iop->iop_padir; + R = &immap->im_cpm.cp_pbdir; +#elif defined(CONFIG_8260) + l = &iop->iop_pdira; + R = &iop->iop_pdirb; +#endif + binary ("PA_DIR", *l++, PA_NBITS); + binary ("PB_DIR", *R++, PB_NBITS); + binary ("PA_PAR", *l++, PA_NBITS); + binary ("PB_PAR", *R++, PB_NBITS); +#if defined(CONFIG_8260) + binary ("PA_SOR", *l++, PA_NBITS); + binary ("PB_SOR", *R++, PB_NBITS); +#endif + binary ("PA_ODR", *l++, PA_NB_ODR); + binary ("PB_ODR", *R++, PB_NB_ODR); + binary ("PA_DAT", *l++, PA_NBITS); + binary ("PB_DAT", *R++, PB_NBITS); + + header (); + + /* + * Ports C & D + */ + +#if defined(CONFIG_8xx) + l = &iop->iop_pcdir; + r = &iop->iop_pddir; +#elif defined(CONFIG_8260) + l = &iop->iop_pdirc; + r = &iop->iop_pdird; +#endif + binary ("PC_DIR", *l++, PC_NBITS); + binary ("PD_DIR", *r++, PD_NBITS); + binary ("PC_PAR", *l++, PC_NBITS); + binary ("PD_PAR", *r++, PD_NBITS); +#if defined(CONFIG_8xx) + binary ("PC_SO ", *l++, PC_NBITS); + binary (" ", 0, 0); + r++; +#elif defined(CONFIG_8260) + binary ("PC_SOR", *l++, PC_NBITS); + binary ("PD_SOR", *r++, PD_NBITS); + binary ("PC_ODR", *l++, PC_NBITS); + binary ("PD_ODR", *r++, PD_NBITS); +#endif + binary ("PC_DAT", *l++, PC_NBITS); + binary ("PD_DAT", *r++, PD_NBITS); +#if defined(CONFIG_8xx) + binary ("PC_INT", *l++, PC_NBITS); +#endif + + header (); + return 0; +} + +/* + * set the io pins + * this needs a clean up for smaller tighter code + * use *uint and set the address based on cmd + port + */ +int +do_iopset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +#if defined(CONFIG_8260) + uint rcode = 0; + static uint port = 0; + static uint pin = 0; + static uint value = 0; + static enum { DIR, PAR, SOR, ODR, DAT } cmd = DAT; + iopin_t iopin; + + if (argc != 5) { + printf ("iopset PORT PIN CMD VALUE\n"); + return 1; + } + port = argv[1][0] - 'A'; + if (port > 3) + port -= 0x20; + if (port > 3) + rcode = 1; + pin = simple_strtol (argv[2], NULL, 10); + if (pin > 31) + rcode = 1; + + + switch (argv[3][0]) { + case 'd': + if (argv[3][1] == 'a') + cmd = DAT; + else if (argv[3][1] == 'i') + cmd = DIR; + else + rcode = 1; + break; + case 'p': + cmd = PAR; + break; + case 'o': + cmd = ODR; + break; + case 's': + cmd = SOR; + break; + default: + printf ("iopset: unknown command %s\n", argv[3]); + rcode = 1; + } + if (argv[4][0] == '1') + value = 1; + else if (argv[4][0] == '0') + value = 0; + else + rcode = 1; + if (rcode == 0) { + iopin.port = port; + iopin.pin = pin; + switch (cmd) { + case DIR: + if (value) + iopin_set_out (&iopin); + else + iopin_set_in (&iopin); + break; + case PAR: + if (value) + iopin_set_ded (&iopin); + else + iopin_set_gen (&iopin); + break; + case SOR: + if (value) + iopin_set_opt2 (&iopin); + else + iopin_set_opt1 (&iopin); + break; + case ODR: + if (value) + iopin_set_odr (&iopin); + else + iopin_set_act (&iopin); + break; + case DAT: + if (value) + iopin_set_high (&iopin); + else + iopin_set_low (&iopin); + break; + } + + } + return rcode; +#else + unimplemented (cmdtp, flag, argc, argv); + return 0; +#endif +} + +int +do_dmainfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_fccinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +static void prbrg (int n, uint val) +{ + uint extc = (val >> 14) & 3; + uint cd = (val & CPM_BRG_CD_MASK) >> 1; + uint div16 = (val & CPM_BRG_DIV16) != 0; + +#if defined(CONFIG_8xx) + DECLARE_GLOBAL_DATA_PTR; + ulong clock = gd->cpu_clk; +#elif defined(CONFIG_8260) + DECLARE_GLOBAL_DATA_PTR; + ulong clock = gd->brg_clk; +#endif + + printf ("BRG%d:", n); + + if (val & CPM_BRG_RST) + puts (" RESET"); + else + puts (" "); + + if (val & CPM_BRG_EN) + puts (" ENABLED"); + else + puts (" DISABLED"); + + printf (" EXTC=%d", extc); + + if (val & CPM_BRG_ATB) + puts (" ATB"); + else + puts (" "); + + printf (" DIVIDER=%4d", cd); + if (extc == 0 && cd != 0) { + uint baudrate; + + if (div16) + baudrate = (clock / 16) / (cd + 1); + else + baudrate = clock / (cd + 1); + + printf ("=%6d bps", baudrate); + } else { + puts (" "); + } + + if (val & CPM_BRG_DIV16) + puts (" DIV16"); + else + puts (" "); + + putc ('\n'); +} + +int +do_brginfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + +#if defined(CONFIG_8xx) + volatile cpm8xx_t *cp = &immap->im_cpm; + volatile uint *p = &cp->cp_brgc1; +#elif defined(CONFIG_8260) + volatile uint *p = &immap->im_brgc1; +#endif + int i = 1; + + while (i <= 4) + prbrg (i++, *p++); + +#if defined(CONFIG_8260) + p = &immap->im_brgc5; + while (i <= 8) + prbrg (i++, *p++); +#endif + return 0; +} + +int +do_i2cinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + volatile immap_t *immap = (immap_t *) CFG_IMMR; + +#if defined(CONFIG_8xx) + volatile i2c8xx_t *i2c = &immap->im_i2c; + volatile cpm8xx_t *cp = &immap->im_cpm; + volatile iic_t *iip = (iic_t *) & cp->cp_dparam[PROFF_IIC]; +#elif defined(CONFIG_8260) + volatile i2c8260_t *i2c = &immap->im_i2c; + volatile iic_t *iip; + uint dpaddr; + + dpaddr = *((unsigned short *) (&immap->im_dprambase[PROFF_I2C_BASE])); + if (dpaddr == 0) + iip = NULL; + else + iip = (iic_t *) & immap->im_dprambase[dpaddr]; +#endif + + printf ("I2MOD = %02x I2ADD = %02x\n", i2c->i2c_i2mod, i2c->i2c_i2add); + printf ("I2BRG = %02x I2COM = %02x\n", i2c->i2c_i2brg, i2c->i2c_i2com); + printf ("I2CER = %02x I2CMR = %02x\n", i2c->i2c_i2cer, i2c->i2c_i2cmr); + + if (iip == NULL) + printf ("i2c parameter ram not allocated\n"); + else { + printf ("RBASE = %08x TBASE = %08x\n", + iip->iic_rbase, iip->iic_tbase); + printf ("RFCR = %02x TFCR = %02x\n", + iip->iic_rfcr, iip->iic_tfcr); + printf ("MRBLR = %04x\n", iip->iic_mrblr); + printf ("RSTATE= %08x RDP = %08x\n", + iip->iic_rstate, iip->iic_rdp); + printf ("RBPTR = %04x RBC = %04x\n", + iip->iic_rbptr, iip->iic_rbc); + printf ("RXTMP = %08x\n", iip->iic_rxtmp); + printf ("TSTATE= %08x TDP = %08x\n", + iip->iic_tstate, iip->iic_tdp); + printf ("TBPTR = %04x TBC = %04x\n", + iip->iic_tbptr, iip->iic_tbc); + printf ("TXTMP = %08x\n", iip->iic_txtmp); + } + return 0; +} + +int +do_sccinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_smcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_spiinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_muxinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_siinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} + +int +do_mccinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + unimplemented (cmdtp, flag, argc, argv); + return 0; +} +#endif /* CFG_CMD_IMMAP && (CONFIG_8xx || CONFIG_8260) */ diff --git a/common/cmd_jffs2.c b/common/cmd_jffs2.c new file mode 100644 index 0000000..fc1d9c4 --- /dev/null +++ b/common/cmd_jffs2.c @@ -0,0 +1,179 @@ +/* + * (C) Copyright 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 + */ + +/* + * Boot support + */ +#include <common.h> +#include <command.h> +#include <cmd_boot.h> +#include <cmd_autoscript.h> +#include <s_record.h> +#include <net.h> + +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +#include <jffs2/jffs2.h> +static int part_num=0; + +#ifndef CFG_JFFS_CUSTOM_PART + +static struct part_info part; + +struct part_info* +jffs2_part_info(int part_num) +{ + extern flash_info_t flash_info[]; /* info for FLASH chips */ + int i; + + if(part_num==0){ + + if(part.usr_priv==(void*)1) + return ∂ + + memset(&part, 0, sizeof(part)); + +#if defined(CFG_JFFS2_FIRST_SECTOR) + part.offset = (unsigned char *) flash_info[CFG_JFFS2_FIRST_BANK].start[CFG_JFFS2_FIRST_SECTOR]; +#else + part.offset = (unsigned char *) flash_info[CFG_JFFS2_FIRST_BANK].start[0]; +#endif + + /* Figure out flash partition size */ + for (i = CFG_JFFS2_FIRST_BANK; i < CFG_JFFS2_NUM_BANKS+CFG_JFFS2_FIRST_BANK; i++) + part.size += flash_info[i].size; + +#if defined(CFG_JFFS2_FIRST_SECTOR) && (CFG_JFFS2_FIRST_SECTOR > 0) + part.size -= + flash_info[CFG_JFFS2_FIRST_BANK].start[CFG_JFFS2_FIRST_SECTOR] - + flash_info[CFG_JFFS2_FIRST_BANK].start[0]; +#endif + + /* unused in current jffs2 loader */ + part.erasesize = 0; + + /* Mark the struct as ready */ + part.usr_priv=(void*)1; + + return ∂ + } + return 0; +} +#endif /* ifndef CFG_JFFS_CUSTOM_PART */ +int +do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char *filename = "pImage"; + ulong offset = CFG_LOAD_ADDR; + int size; + struct part_info *part; + + if (argc == 2) { + filename = argv[1]; + } + if (argc == 3) { + offset = simple_strtoul(argv[1], NULL, 16); + filename = argv[2]; + } + + if (0 != (part=jffs2_part_info(part_num))){ + + printf("### JFFS2 loading '%s' to 0x%lx\n", filename, offset); + size = jffs2_1pass_load((char *)offset, part, filename); + + if (size > 0) { + char buf[10]; + printf("### JFFS2 load complete: %d bytes loaded to 0x%lx\n", + size, offset); + sprintf(buf, "%x", size); + setenv("filesize", buf); + } else { + printf("### JFFS2 LOAD ERROR<%x> for %s!\n", size, filename); + } + + return !(size > 0); + } + printf("Active partition not valid\n"); + return 0; +} + +int +do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char *filename = "/"; + int ret; + struct part_info *part; + + if (argc == 2) + filename = argv[1]; + + if (0 != (part=jffs2_part_info(part_num))){ + + ret = jffs2_1pass_ls(jffs2_part_info(part_num), filename); + + return (ret == 1); + } + printf("Active partition not valid\n"); + return 0; +} + +int +do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int ret; + struct part_info *part; + + if (0 != (part=jffs2_part_info(part_num))){ + + ret = jffs2_1pass_info(jffs2_part_info(part_num)); + + return (ret == 1); + } + printf("Active partition not valid\n"); + return 0; +} + +int +do_jffs2_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int tmp_part; + + if (argc >= 2) { + tmp_part = simple_strtoul(argv[1], NULL, 16); + }else{ + printf("Need partition number in argument list\n"); + return 0; + + } + + if (jffs2_part_info(tmp_part)){ + printf("Partiton changed to %d\n",tmp_part); + part_num=tmp_part; + return 0; + } + + printf("Partition %d is not valid partiton\n",tmp_part); + return 0; + +} +#endif /* CFG_CMD_JFFS2 */ diff --git a/common/cmd_pci.c b/common/cmd_pci.c new file mode 100644 index 0000000..22e4b9a --- /dev/null +++ b/common/cmd_pci.c @@ -0,0 +1,477 @@ +/* + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Wolfgang Grandegger, DENX Software Engineering, wg@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 + */ + +/* + * PCI routines + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <command.h> +#include <cmd_boot.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <cmd_pci.h> +#include <pci.h> + +#if (CONFIG_COMMANDS & CFG_CMD_PCI) + +extern int cmd_get_data_size(char* arg, int default_size); + +unsigned char ShortPCIListing = 1; + +/* + * Follows routines for the output of infos about devices on PCI bus. + */ + +void pci_header_show(pci_dev_t dev); +void pci_header_show_brief(pci_dev_t dev); + +/* + * Subroutine: pciinfo + * + * Description: Show information about devices on PCI bus. + * Depending on the define CFG_SHORT_PCI_LISTING + * the output will be more or less exhaustive. + * + * Inputs: bus_no the number of the bus to be scanned. + * + * Return: None + * + */ +void pciinfo(int BusNum, int ShortPCIListing) +{ + int Device; + int Function; + unsigned char HeaderType; + unsigned short VendorID; + pci_dev_t dev; + + printf("Scanning PCI devices on bus %d\n", BusNum); + + if (ShortPCIListing) { + printf("BusDevFun VendorId DeviceId Device Class Sub-Class\n"); + printf("_____________________________________________________________\n"); + } + + for (Device = 0; Device < PCI_MAX_PCI_DEVICES; Device++) { + HeaderType = 0; + VendorID = 0; + for (Function = 0; Function < PCI_MAX_PCI_FUNCTIONS; Function++) { + /* + * If this is not a multi-function device, we skip the rest. + */ + if (Function && !(HeaderType & 0x80)) + break; + + dev = PCI_BDF(BusNum, Device, Function); + + pci_read_config_word(dev, PCI_VENDOR_ID, &VendorID); + if ((VendorID == 0xFFFF) || (VendorID == 0x0000)) + continue; + + pci_read_config_byte(dev, PCI_HEADER_TYPE, &HeaderType); + + if (ShortPCIListing) + { + printf("%02x.%02x.%02x ", BusNum, Device, Function); + pci_header_show_brief(dev); + } + else + { + printf("\nFound PCI device %02x.%02x.%02x:\n", + BusNum, Device, Function); + pci_header_show(dev); + } + } + } +} + +char* pci_classes_str(u8 class) +{ + static char *pci_classes[] = { + "Build before PCI Rev2.0", + "Mass storage controller", + "Network controller ", + "Display controller ", + "Multimedia device ", + "Memory controller ", + "Bridge device ", + "Simple comm. controller", + "Base system peripheral ", + "Input device ", + "Docking station ", + "Processor ", + "Serial bus controller ", + "Reserved entry ", + "Does not fit any class " + }; + + if (class < (sizeof pci_classes / sizeof *pci_classes)) + return pci_classes[(int) class]; + + return "??? "; +} + +/* + * Subroutine: pci_header_show_brief + * + * Description: Reads and prints the header of the + * specified PCI device in short form. + * + * Inputs: dev Bus+Device+Function number + * + * Return: None + * + */ +void pci_header_show_brief(pci_dev_t dev) +{ + u16 vendor, device; + u8 class, subclass; + + pci_read_config_word(dev, PCI_VENDOR_ID, &vendor); + pci_read_config_word(dev, PCI_DEVICE_ID, &device); + pci_read_config_byte(dev, PCI_CLASS_CODE, &class); + pci_read_config_byte(dev, PCI_CLASS_SUB_CODE, &subclass); + + printf("0x%.4x 0x%.4x %s 0x%.2x\n", + vendor, device, + pci_classes_str(class), subclass); +} + +/* + * Subroutine: PCI_Header_Show + * + * Description: Reads the header of the specified PCI device. + * + * Inputs: BusDevFunc Bus+Device+Function number + * + * Return: None + * + */ +void pci_header_show(pci_dev_t dev) +{ + u8 _byte, header_type; + u16 _word; + u32 _dword; + +#define PRINT(msg, type, reg) \ + pci_read_config_##type(dev, reg, &_##type); \ + printf(msg, _##type) + +#define PRINT2(msg, type, reg, func) \ + pci_read_config_##type(dev, reg, &_##type); \ + printf(msg, _##type, func(_##type)) + + pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); + + PRINT (" vendor ID = 0x%.4x\n", word, PCI_VENDOR_ID); + PRINT (" device ID = 0x%.4x\n", word, PCI_DEVICE_ID); + PRINT (" command register = 0x%.4x\n", word, PCI_COMMAND); + PRINT (" status register = 0x%.4x\n", word, PCI_STATUS); + PRINT (" revision ID = 0x%.2x\n", byte, PCI_REVISION_ID); + PRINT2(" class code = 0x%.2x (%s)\n", byte, PCI_CLASS_CODE, + pci_classes_str); + PRINT (" sub class code = 0x%.2x\n", byte, PCI_CLASS_SUB_CODE); + PRINT (" programming interface = 0x%.2x\n", byte, PCI_CLASS_PROG); + PRINT (" cache line = 0x%.2x\n", byte, PCI_CACHE_LINE_SIZE); + PRINT (" latency time = 0x%.2x\n", byte, PCI_LATENCY_TIMER); + PRINT (" header type = 0x%.2x\n", byte, PCI_HEADER_TYPE); + PRINT (" BIST = 0x%.2x\n", byte, PCI_BIST); + PRINT (" base address 0 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_0); + PRINT (" base address 1 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_1); + + if (header_type & 0x01) { /* PCI-to-PCI bridge */ + PRINT (" primary bus number = 0x%.2x\n", byte, PCI_PRIMARY_BUS); + PRINT (" secondary bus number = 0x%.2x\n", byte, PCI_SECONDARY_BUS); + PRINT (" subordinate bus number = 0x%.2x\n", byte, PCI_SUBORDINATE_BUS); + PRINT (" secondary latency timer = 0x%.2x\n", byte, PCI_SEC_LATENCY_TIMER); + PRINT (" IO base = 0x%.2x\n", byte, PCI_IO_BASE); + PRINT (" IO limit = 0x%.2x\n", byte, PCI_IO_LIMIT); + PRINT (" secondary status = 0x%.4x\n", word, PCI_SEC_STATUS); + PRINT (" memory base = 0x%.4x\n", word, PCI_MEMORY_BASE); + PRINT (" memory limit = 0x%.4x\n", word, PCI_MEMORY_LIMIT); + PRINT (" prefetch memory base = 0x%.4x\n", word, PCI_PREF_MEMORY_BASE); + PRINT (" prefetch memory limit = 0x%.4x\n", word, PCI_PREF_MEMORY_LIMIT); + PRINT (" prefetch memory base upper = 0x%.8x\n", dword, PCI_PREF_BASE_UPPER32); + PRINT (" prefetch memory limit upper = 0x%.8x\n", dword, PCI_PREF_LIMIT_UPPER32); + PRINT (" IO base upper 16 bits = 0x%.4x\n", word, PCI_IO_BASE_UPPER16); + PRINT (" IO limit upper 16 bits = 0x%.4x\n", word, PCI_IO_LIMIT_UPPER16); + PRINT (" expansion ROM base address = 0x%.8x\n", dword, PCI_ROM_ADDRESS1); + PRINT (" interrupt line = 0x%.2x\n", byte, PCI_INTERRUPT_LINE); + PRINT (" interrupt pin = 0x%.2x\n", byte, PCI_INTERRUPT_PIN); + PRINT (" bridge control = 0x%.4x\n", word, PCI_BRIDGE_CONTROL); + } else { /* PCI device */ + PRINT(" base address 2 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_2); + PRINT(" base address 3 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_3); + PRINT(" base address 4 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_4); + PRINT(" base address 5 = 0x%.8x\n", dword, PCI_BASE_ADDRESS_5); + PRINT(" cardBus CIS pointer = 0x%.8x\n", dword, PCI_CARDBUS_CIS); + PRINT(" sub system vendor ID = 0x%.4x\n", word, PCI_SUBSYSTEM_VENDOR_ID); + PRINT(" sub system ID = 0x%.4x\n", word, PCI_SUBSYSTEM_ID); + PRINT(" expansion ROM base address = 0x%.8x\n", dword, PCI_ROM_ADDRESS); + PRINT(" interrupt line = 0x%.2x\n", byte, PCI_INTERRUPT_LINE); + PRINT(" interrupt pin = 0x%.2x\n", byte, PCI_INTERRUPT_PIN); + PRINT(" min Grant = 0x%.2x\n", byte, PCI_MIN_GNT); + PRINT(" max Latency = 0x%.2x\n", byte, PCI_MAX_LAT); + } + +#undef PRINT +#undef PRINT2 +} + +/* Convert the "bus.device.function" identifier into a number. + */ +static pci_dev_t get_pci_dev(char* name) +{ + char cnum[12]; + int len, i, iold, n; + int bdfs[3] = {0,0,0}; + + len = strlen(name); + if (len > 8) + return -1; + for (i = 0, iold = 0, n = 0; i < len; i++) { + if (name[i] == '.') { + memcpy(cnum, &name[iold], i - iold); + cnum[i - iold] = '\0'; + bdfs[n++] = simple_strtoul(cnum, NULL, 16); + iold = i + 1; + } + } + strcpy(cnum, &name[iold]); + if (n == 0) + n = 1; + bdfs[n] = simple_strtoul(cnum, NULL, 16); + return PCI_BDF(bdfs[0], bdfs[1], bdfs[2]); +} + +static int pci_cfg_display(pci_dev_t bdf, ulong addr, ulong size, ulong length) +{ +#define DISP_LINE_LEN 16 + ulong i, nbytes, linebytes; + int rc = 0; + + if (length == 0) + length = 0x40 / size; /* Standard PCI configuration space */ + + /* Print the lines. + * once, and all accesses are with the specified bus width. + */ + nbytes = length * size; + do { + uint val4; + ushort val2; + u_char val1; + + printf("%08lx:", addr); + linebytes = (nbytes>DISP_LINE_LEN)?DISP_LINE_LEN:nbytes; + for (i=0; i<linebytes; i+= size) { + if (size == 4) { + pci_read_config_dword(bdf, addr, &val4); + printf(" %08x", val4); + } else if (size == 2) { + pci_read_config_word(bdf, addr, &val2); + printf(" %04x", val2); + } else { + pci_read_config_byte(bdf, addr, &val1); + printf(" %02x", val1); + } + addr += size; + } + printf("\n"); + nbytes -= linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); + + return (rc); +} + +static int pci_cfg_write (pci_dev_t bdf, ulong addr, ulong size, ulong value) +{ + if (size == 4) { + pci_write_config_dword(bdf, addr, value); + } + else if (size == 2) { + ushort val = value & 0xffff; + pci_write_config_word(bdf, addr, val); + } + else { + u_char val = value & 0xff; + pci_write_config_byte(bdf, addr, val); + } + return 0; +} + +static int +pci_cfg_modify (pci_dev_t bdf, ulong addr, ulong size, ulong value, int incrflag) +{ + ulong i; + int nbytes; + extern char console_buffer[]; + uint val4; + ushort val2; + u_char val1; + + /* Print the address, followed by value. Then accept input for + * the next value. A non-converted value exits. + */ + do { + printf("%08lx:", addr); + if (size == 4) { + pci_read_config_dword(bdf, addr, &val4); + printf(" %08x", val4); + } + else if (size == 2) { + pci_read_config_word(bdf, addr, &val2); + printf(" %04x", val2); + } + else { + pci_read_config_byte(bdf, addr, &val1); + printf(" %02x", val1); + } + + nbytes = readline (" ? "); + if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) { + /* <CR> pressed as only input, don't modify current + * location and move to next. "-" pressed will go back. + */ + if (incrflag) + addr += nbytes ? -size : size; + nbytes = 1; +#ifdef CONFIG_BOOT_RETRY_TIME + reset_cmd_timeout(); /* good enough to not time out */ +#endif + } +#ifdef CONFIG_BOOT_RETRY_TIME + else if (nbytes == -2) { + break; /* timed out, exit the command */ + } +#endif + else { + char *endp; + i = simple_strtoul(console_buffer, &endp, 16); + nbytes = endp - console_buffer; + if (nbytes) { +#ifdef CONFIG_BOOT_RETRY_TIME + /* good enough to not time out + */ + reset_cmd_timeout(); +#endif + pci_cfg_write (bdf, addr, size, i); + if (incrflag) + addr += size; + } + } + } while (nbytes); + + return 0; +} + +/* PCI Configuration Space access commands + * + * Syntax: + * pci display[.b, .w, .l] bus.device.function} [addr] [len] + * pci next[.b, .w, .l] bus.device.function [addr] + * pci modify[.b, .w, .l] bus.device.function [addr] + * pci write[.b, .w, .l] bus.device.function addr value + */ +int do_pci (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + ulong addr = 0, value = 0, size = 0; + pci_dev_t bdf = 0; + char cmd = 's'; + + if (argc > 1) + cmd = argv[1][0]; + + switch (cmd) { + case 'd': /* display */ + case 'n': /* next */ + case 'm': /* modify */ + case 'w': /* write */ + /* Check for a size specification. */ + size = cmd_get_data_size(argv[1], 4); + if (argc > 3) + addr = simple_strtoul(argv[3], NULL, 16); + if (argc > 4) + value = simple_strtoul(argv[4], NULL, 16); + case 'h': /* header */ + if (argc < 3) + goto usage; + if ((bdf = get_pci_dev(argv[2])) == -1) + return 1; + break; + default: /* scan bus */ + value = 1; /* short listing */ + bdf = 0; /* bus number */ + if (argc > 1) { + if (argv[argc-1][0] == 'l') { + value = 0; + argc--; + } + if (argc > 1) + bdf = simple_strtoul(argv[1], NULL, 16); + } + pciinfo(bdf, value); + return 0; + } + + switch (argv[1][0]) { + case 'h': /* header */ + pci_header_show(bdf); + return 0; + case 'd': /* display */ + return pci_cfg_display(bdf, addr, size, value); + case 'n': /* next */ + if (argc < 4) + goto usage; + return pci_cfg_modify(bdf, addr, size, value, 0); + case 'm': /* modify */ + if (argc < 4) + goto usage; + return pci_cfg_modify(bdf, addr, size, value, 1); + case 'w': /* write */ + if (argc < 5) + goto usage; + return pci_cfg_write(bdf, addr, size, value); + } + + return 1; + usage: + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; +} + +#endif /* (CONFIG_COMMANDS & CFG_CMD_PCI) */ + +#endif /* CONFIG_PCI */ diff --git a/common/cmd_pcmcia.c b/common/cmd_pcmcia.c new file mode 100644 index 0000000..ccf21a9 --- /dev/null +++ b/common/cmd_pcmcia.c @@ -0,0 +1,2243 @@ +/* + * (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 + * + ******************************************************************** + * + * Lots of code copied from: + * + * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series. + * (C) 1999-2000 Magnus Damm <damm@bitsmart.com> + * + * "The ExCA standard specifies that socket controllers should provide + * two IO and five memory windows per socket, which can be independently + * configured and positioned in the host address space and mapped to + * arbitrary segments of card address space. " - David A Hinds. 1999 + * + * This controller does _not_ meet the ExCA standard. + * + * m8xx pcmcia controller brief info: + * + 8 windows (attrib, mem, i/o) + * + up to two slots (SLOT_A and SLOT_B) + * + inputpins, outputpins, event and mask registers. + * - no offset register. sigh. + * + * Because of the lacking offset register we must map the whole card. + * We assign each memory window PCMCIA_MEM_WIN_SIZE address space. + * Make sure there is (PCMCIA_MEM_WIN_SIZE * PCMCIA_MEM_WIN_NO + * * PCMCIA_SOCKETS_NO) bytes at PCMCIA_MEM_WIN_BASE. + * The i/o windows are dynamically allocated at PCMCIA_IO_WIN_BASE. + * They are maximum 64KByte each... + */ + +/* #define DEBUG 1 */ + +/* + * PCMCIA support + */ +#include <common.h> +#include <command.h> +#include <config.h> +#include <pcmcia.h> +#include <cmd_pcmcia.h> +#if defined(CONFIG_IDE_8xx_PCCARD) && defined(CONFIG_8xx) +#include <mpc8xx.h> +#endif +#if defined(CONFIG_LWMON) +#include <i2c.h> +#endif + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) || \ + ((CONFIG_COMMANDS & CFG_CMD_IDE) && defined(CONFIG_IDE_8xx_PCCARD)) + +int pcmcia_on (void); + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int pcmcia_off (void); +static int hardware_disable(int slot); +#endif +static int hardware_enable (int slot); +static int voltage_set(int slot, int vcc, int vpp); +#ifdef CONFIG_IDE_8xx_PCCARD +static void print_funcid (int func); +static void print_fixed (volatile uchar *p); +static int identify (volatile uchar *p); +static int check_ide_device (void); +#endif /* CONFIG_IDE_8xx_PCCARD */ + +static u_int m8xx_get_graycode(u_int size); +#if 0 +static u_int m8xx_get_speed(u_int ns, u_int is_io); +#endif + +/* ------------------------------------------------------------------------- */ + +/* look up table for pgcrx registers */ + +static u_int *pcmcia_pgcrx[2] = { + &((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pgcra, + &((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pgcrb, +}; + +#define PCMCIA_PGCRX(slot) (*pcmcia_pgcrx[slot]) + +const char *indent = "\t "; + +/* ------------------------------------------------------------------------- */ + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) + +int do_pinit (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int rcode = 0; + + if (argc != 2) { + printf ("Usage: pinit {on | off}\n"); + return 1; + } + if (strcmp(argv[1],"on") == 0) { + rcode = pcmcia_on (); + } else if (strcmp(argv[1],"off") == 0) { + rcode = pcmcia_off (); + } else { + printf ("Usage: pinit {on | off}\n"); + return 1; + } + + return rcode; +} +#endif /* CFG_CMD_PCMCIA */ + +/* ------------------------------------------------------------------------- */ + +#if defined(CONFIG_LWMON) +# define CFG_PCMCIA_TIMING (PCMCIA_SHT(9) | PCMCIA_SST(3) | PCMCIA_SL(12)) +#else +# define CFG_PCMCIA_TIMING (PCMCIA_SHT(2) | PCMCIA_SST(4) | PCMCIA_SL(9)) +#endif + +int pcmcia_on (void) +{ + int i; + u_long reg, base; + pcmcia_win_t *win; + + debug ("Enable PCMCIA " PCMCIA_SLOT_MSG "\n"); + + /* intialize the fixed memory windows */ + win = (pcmcia_win_t *)(&((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pbr0); + base = CFG_PCMCIA_MEM_ADDR; + + if((reg = m8xx_get_graycode(CFG_PCMCIA_MEM_SIZE)) == -1) { + printf ("Cannot set window size to 0x%08x\n", + CFG_PCMCIA_MEM_SIZE); + return (1); + } + + for (i=0; i<PCMCIA_MEM_WIN_NO; ++i) { + win->br = base; + + switch (i) { +#ifdef CONFIG_IDE_8xx_PCCARD + case 0: { /* map attribute memory */ + win->or = ( PCMCIA_BSIZE_64M + | PCMCIA_PPS_8 + | PCMCIA_PRS_ATTR + | PCMCIA_SLOT_x + | PCMCIA_PV + | CFG_PCMCIA_TIMING ); + break; + } + + case 1: { /* map I/O window for data reg */ + win->or = ( PCMCIA_BSIZE_1K + | PCMCIA_PPS_16 + | PCMCIA_PRS_IO + | PCMCIA_SLOT_x + | PCMCIA_PV + | CFG_PCMCIA_TIMING ); + break; + } + + case 2: { /* map I/O window for command/ctrl reg block */ + win->or = ( PCMCIA_BSIZE_1K + | PCMCIA_PPS_8 + | PCMCIA_PRS_IO + | PCMCIA_SLOT_x + | PCMCIA_PV + | CFG_PCMCIA_TIMING ); + break; + } +#endif /* CONFIG_IDE_8xx_PCCARD */ + default: /* set to not valid */ + win->or = 0; + break; + } + + debug ("MemWin %d: PBR 0x%08lX POR %08lX\n", + i, win->br, win->or); + base += CFG_PCMCIA_MEM_SIZE; + ++win; + } + + /* turn off voltage */ + if (voltage_set(_slot_, 0, 0)) + return (1); + + /* Enable external hardware */ + if (hardware_enable(_slot_)) + return (1); + +#ifdef CONFIG_IDE_8xx_PCCARD + if (check_ide_device()) + return (1); +#endif + return (0); +} + +/* ------------------------------------------------------------------------- */ + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) + +static int pcmcia_off (void) +{ + int i; + pcmcia_win_t *win; + + printf ("Disable PCMCIA " PCMCIA_SLOT_MSG "\n"); + + /* clear interrupt state, and disable interrupts */ + ((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pscr = PCMCIA_MASK(_slot_); + ((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* turn off interrupt and disable CxOE */ + PCMCIA_PGCRX(_slot_) = __MY_PCMCIA_GCRX_CXOE; + + /* turn off memory windows */ + win = (pcmcia_win_t *)(&((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pbr0); + + for (i=0; i<PCMCIA_MEM_WIN_NO; ++i) { + /* disable memory window */ + win->or = 0; + ++win; + } + + /* turn off voltage */ + voltage_set(_slot_, 0, 0); + + /* disable external hardware */ + printf ("Shutdown and Poweroff " PCMCIA_SLOT_MSG "\n"); + hardware_disable(_slot_); + return 0; +} + +#endif /* CFG_CMD_PCMCIA */ + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_IDE_8xx_PCCARD + +#define MAX_TUPEL_SZ 512 +#define MAX_FEATURES 4 + +static int check_ide_device (void) +{ + volatile uchar *ident = NULL; + volatile uchar *feature_p[MAX_FEATURES]; + volatile uchar *p, *start; + int n_features = 0; + uchar func_id = ~0; + uchar code, len; + ushort config_base = 0; + int found = 0; + int i; + + debug ("PCMCIA MEM: %08X\n", CFG_PCMCIA_MEM_ADDR); + + start = p = (volatile uchar *) CFG_PCMCIA_MEM_ADDR; + + while ((p - start) < MAX_TUPEL_SZ) { + + code = *p; p += 2; + + if (code == 0xFF) { /* End of chain */ + break; + } + + len = *p; p += 2; +#if defined(DEBUG) && (DEBUG > 1) + { volatile uchar *q = p; + printf ("\nTuple code %02x length %d\n\tData:", + code, len); + + for (i = 0; i < len; ++i) { + printf (" %02x", *q); + q+= 2; + } + } +#endif /* DEBUG */ + switch (code) { + case CISTPL_VERS_1: + ident = p + 4; + break; + case CISTPL_FUNCID: + /* Fix for broken SanDisk which may have 0x80 bit set */ + func_id = *p & 0x7F; + break; + case CISTPL_FUNCE: + if (n_features < MAX_FEATURES) + feature_p[n_features++] = p; + break; + case CISTPL_CONFIG: + config_base = (*(p+6) << 8) + (*(p+4)); + debug ("\n## Config_base = %04x ###\n", config_base); + default: + break; + } + p += 2 * len; + } + + found = identify (ident); + + if (func_id != ((uchar)~0)) { + print_funcid (func_id); + + if (func_id == CISTPL_FUNCID_FIXED) + found = 1; + else + return (1); /* no disk drive */ + } + + for (i=0; i<n_features; ++i) { + print_fixed (feature_p[i]); + } + + if (!found) { + printf ("unknown card type\n"); + return (1); + } + + /* set I/O area in config reg -> only valid for ARGOSY D5!!! */ + *((uchar *)(CFG_PCMCIA_MEM_ADDR + config_base)) = 1; + + return (0); +} +#endif /* CONFIG_IDE_8xx_PCCARD */ + +/* ------------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------------- */ +/* board specific stuff: */ +/* voltage_set(), hardware_enable() and hardware_disable() */ +/* ---------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------- */ +/* RPX Boards from Embedded Planet */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_RPXCLASSIC) || defined(CONFIG_RPXLITE) + +/* The RPX boards seems to have it's bus monitor timeout set to 6*8 clocks. + * SYPCR is write once only, therefore must the slowest memory be faster + * than the bus monitor or we will get a machine check due to the bus timeout. + */ + +#define PCMCIA_BOARD_MSG "RPX CLASSIC or RPX LITE" + +#undef PCMCIA_BMT_LIMIT +#define PCMCIA_BMT_LIMIT (6*8) + +static int voltage_set(int slot, int vcc, int vpp) +{ + u_long reg = 0; + + switch(vcc) { + case 0: break; + case 33: reg |= BCSR1_PCVCTL4; break; + case 50: reg |= BCSR1_PCVCTL5; break; + default: return 1; + } + + switch(vpp) { + case 0: break; + case 33: + case 50: + if(vcc == vpp) + reg |= BCSR1_PCVCTL6; + else + return 1; + break; + case 120: + reg |= BCSR1_PCVCTL7; + default: return 1; + } + + if(vcc == 120) + return 1; + + /* first, turn off all power */ + + *((uint *)RPX_CSR_ADDR) &= ~(BCSR1_PCVCTL4 | BCSR1_PCVCTL5 + | BCSR1_PCVCTL6 | BCSR1_PCVCTL7); + + /* enable new powersettings */ + + *((uint *)RPX_CSR_ADDR) |= reg; + + return 0; +} + +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V +static int hardware_enable (int slot) +{ + return 0; /* No hardware to enable */ +} +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + return 0; /* No hardware to disable */ +} +#endif /* CFG_CMD_PCMCIA */ +#endif /* CONFIG_RPXCLASSIC */ + +/* ---------------------------------------------------------------------------- */ +/* (F)ADS Boards from Motorola */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_ADS) || defined(CONFIG_FADS) + +#ifdef CONFIG_ADS +#define PCMCIA_BOARD_MSG "ADS" +#define PCMCIA_GLITCHY_CD /* My ADS board needs this */ +#else +#define PCMCIA_BOARD_MSG "FADS" +#endif + +static int voltage_set(int slot, int vcc, int vpp) +{ + u_long reg = 0; + + switch(vpp) { + case 0: reg = 0; break; + case 50: reg = 1; break; + case 120: reg = 2; break; + default: return 1; + } + + switch(vcc) { + case 0: reg = 0; break; +#ifdef CONFIG_ADS + case 50: reg = BCSR1_PCCVCCON; break; +#endif +#ifdef CONFIG_FADS + case 33: reg = BCSR1_PCCVCC0 | BCSR1_PCCVCC1; break; + case 50: reg = BCSR1_PCCVCC1; break; +#endif + default: return 1; + } + + /* first, turn off all power */ + +#ifdef CONFIG_ADS + *((uint *)BCSR1) |= BCSR1_PCCVCCON; +#endif +#ifdef CONFIG_FADS + *((uint *)BCSR1) &= ~(BCSR1_PCCVCC0 | BCSR1_PCCVCC1); +#endif + *((uint *)BCSR1) &= ~BCSR1_PCCVPP_MASK; + + /* enable new powersettings */ + +#ifdef CONFIG_ADS + *((uint *)BCSR1) &= ~reg; +#endif +#ifdef CONFIG_FADS + *((uint *)BCSR1) |= reg; +#endif + + *((uint *)BCSR1) |= reg << 20; + + return 0; +} + +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V + +static int hardware_enable(int slot) +{ + *((uint *)BCSR1) &= ~BCSR1_PCCEN; + return 0; +} + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + *((uint *)BCSR1) &= ~BCSR1_PCCEN; + return 0; +} +#endif /* CFG_CMD_PCMCIA */ + +#endif /* (F)ADS */ + +/* ---------------------------------------------------------------------------- */ +/* TQM8xxL Boards by TQ Components */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_TQM8xxL) + +#define PCMCIA_BOARD_MSG "TQM8xxL" + + +static int hardware_enable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, mask; + + debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + udelay(10000); + + immap = (immap_t *)CFG_IMMR; + sysp = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* + * Configure SIUMCR to enable PCMCIA port B + * (VFLS[0:1] are not used for debugging, we connect FRZ# instead) + */ + sysp->sc_siumcr &= ~SIUMCR_DBGC11; /* set DBGC to 00 */ + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK(_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX(_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Configure Port C pins for + * 5 Volts Enable and 3 Volts enable + */ + immap->im_ioport.iop_pcpar &= ~(0x0002 | 0x0004); + immap->im_ioport.iop_pcso &= ~(0x0002 | 0x0004); + /* remove all power */ + + immap->im_ioport.iop_pcdat &= ~(0x0002 | 0x0004); + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", + __LINE__,__FUNCTION__, + &(pcmp->pcmc_pipr),pcmp->pcmc_pipr); + if (pcmp->pcmc_pipr & 0x00001800) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On. + */ + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + reg = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + reg, + (reg&PCMCIA_VS1(slot))?"n":"ff", + (reg&PCMCIA_VS2(slot))?"n":"ff"); + if ((reg & mask) == mask) { + immap->im_ioport.iop_pcdat |= 0x0004; + puts (" 5.0V card found: "); + } else { + immap->im_ioport.iop_pcdat |= 0x0002; + puts (" 3.3V card found: "); + } + immap->im_ioport.iop_pcdir |= (0x0002 | 0x0004); +#if 0 + /* VCC switch error flag, PCMCIA slot INPACK_ pin */ + cp->cp_pbdir &= ~(0x0020 | 0x0010); + cp->cp_pbpar &= ~(0x0020 | 0x0010); + udelay(500000); +#endif + udelay(1000); + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return (0); +} + + + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + volatile immap_t *immap; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + + /* remove all power */ + immap->im_ioport.iop_pcdat &= ~(0x0002 | 0x0004); + + /* Configure PCMCIA General Control Register */ + PCMCIA_PGCRX(_slot_) = 0; + + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(10000); + + return (0); +} +#endif /* CFG_CMD_PCMCIA */ + + + +static int voltage_set(int slot, int vcc, int vpp) +{ + volatile immap_t *immap; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("voltage_set: " + PCMCIA_BOARD_MSG + " Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Configure Port C pins for + * 5 Volts Enable and 3 Volts enable, + * Turn off all power + */ + debug ("PCMCIA power OFF\n"); + immap->im_ioport.iop_pcpar &= ~(0x0002 | 0x0004); + immap->im_ioport.iop_pcso &= ~(0x0002 | 0x0004); + immap->im_ioport.iop_pcdat &= ~(0x0002 | 0x0004); + + reg = 0; + switch(vcc) { + case 0: break; + case 33: reg |= 0x0002; break; + case 50: reg |= 0x0004; break; + default: goto done; + } + + /* Checking supported voltages */ + + debug ("PIPR: 0x%x --> %s\n", + pcmp->pcmc_pipr, + (pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V"); + + immap->im_ioport.iop_pcdat |= reg; + immap->im_ioport.iop_pcdir |= (0x0002 | 0x0004); + if (reg) { + debug ("PCMCIA powered at %sV\n", + (reg&0x0004) ? "5.0" : "3.3"); + } else { + debug ("PCMCIA powered down\n"); + } + +done: + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", + slot+'A'); + return (0); +} + +#endif /* TQM8xxL */ + + +/* ---------------------------------------------------------------------------- */ +/* LWMON Board */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_LWMON) + +#define PCMCIA_BOARD_MSG "LWMON" + +/* #define's for MAX1604 Power Switch */ +#define MAX1604_OP_SUS 0x80 +#define MAX1604_VCCBON 0x40 +#define MAX1604_VCC_35 0x20 +#define MAX1604_VCCBHIZ 0x10 +#define MAX1604_VPPBON 0x08 +#define MAX1604_VPPBPBPGM 0x04 +#define MAX1604_VPPBHIZ 0x02 +/* reserved 0x01 */ + +static int hardware_enable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, mask; + uchar val; + + + debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + /* Switch on PCMCIA port in PIC register 0x60 */ + reg = pic_read (0x60); + debug ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg); + reg &= ~0x10; + /* reg |= 0x08; Vpp not needed */ + pic_write (0x60, reg); +#ifdef DEBUG + reg = pic_read (0x60); + printf ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg); +#endif + udelay(10000); + + immap = (immap_t *)CFG_IMMR; + sysp = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* + * Configure SIUMCR to enable PCMCIA port B + * (VFLS[0:1] are not used for debugging, we connect FRZ# instead) + */ + sysp->sc_siumcr &= ~SIUMCR_DBGC11; /* set DBGC to 00 */ + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK(_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX(_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", + __LINE__,__FUNCTION__, + &(pcmp->pcmc_pipr),pcmp->pcmc_pipr); + if (pcmp->pcmc_pipr & 0x00001800) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On. + */ + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + reg = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + reg, + (reg&PCMCIA_VS1(slot))?"n":"ff", + (reg&PCMCIA_VS2(slot))?"n":"ff"); + if ((reg & mask) == mask) { + val = 0; /* VCCB3/5 = 0 ==> use Vx = 5.0 V */ + puts (" 5.0V card found: "); + } else { + val = MAX1604_VCC_35; /* VCCB3/5 = 1 ==> use Vy = 3.3 V */ + puts (" 3.3V card found: "); + } + + /* switch VCC on */ + val |= MAX1604_OP_SUS | MAX1604_VCCBON; + i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); + i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1); + + udelay(500000); + + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return (0); +} + + + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + volatile immap_t *immap; + volatile pcmconf8xx_t *pcmp; + u_long reg; + uchar val; + + debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + + /* remove all power, put output in high impedance state */ + val = MAX1604_VCCBHIZ | MAX1604_VPPBHIZ; + i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); + i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1); + + /* Configure PCMCIA General Control Register */ + PCMCIA_PGCRX(_slot_) = 0; + + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + /* Switch off PCMCIA port in PIC register 0x60 */ + reg = pic_read (0x60); + debug ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg); + reg |= 0x10; + reg &= ~0x08; + pic_write (0x60, reg); +#ifdef DEBUG + reg = pic_read (0x60); + printf ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg); +#endif + udelay(10000); + + return (0); +} +#endif /* CFG_CMD_PCMCIA */ + + + +static int voltage_set(int slot, int vcc, int vpp) +{ + volatile immap_t *immap; + volatile pcmconf8xx_t *pcmp; + u_long reg; + uchar val; + + debug ("voltage_set: " + PCMCIA_BOARD_MSG + " Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Turn off all power (switch to high impedance) + */ + debug ("PCMCIA power OFF\n"); + val = MAX1604_VCCBHIZ | MAX1604_VPPBHIZ; + i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); + i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1); + + val = 0; + switch(vcc) { + case 0: break; + case 33: val = MAX1604_VCC_35; break; + case 50: break; + default: goto done; + } + + /* Checking supported voltages */ + + debug ("PIPR: 0x%x --> %s\n", + pcmp->pcmc_pipr, + (pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V"); + + i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1); + if (val) { + debug ("PCMCIA powered at %sV\n", + (val & MAX1604_VCC_35) ? "3.3" : "5.0"); + } else { + debug ("PCMCIA powered down\n"); + } + +done: + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", + slot+'A'); + return (0); +} + +#endif /* LWMON */ + +/* ---------------------------------------------------------------------------- */ +/* GTH board by Corelatus AB */ +/* ---------------------------------------------------------------------------- */ +#if defined(CONFIG_GTH) + +#define PCMCIA_BOARD_MSG "GTH COMPACT FLASH" + +static int voltage_set(int slot, int vcc, int vpp) +{ /* Do nothing */ + return 0; +} + +static int hardware_enable (int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, mask; + + debug ("hardware_enable: GTH Slot %c\n", 'A'+slot); + + immap = (immap_t *)CFG_IMMR; + sysp = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK(_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX(_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", + __LINE__,__FUNCTION__, + &(pcmp->pcmc_pipr),pcmp->pcmc_pipr); + if (pcmp->pcmc_pipr & 0x98000000) { + printf (" No Card found\n"); + return (1); + } + + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + reg = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + reg, + (reg&PCMCIA_VS1(slot))?"n":"ff", + (reg&PCMCIA_VS2(slot))?"n":"ff"); + + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return 0; +} +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + return 0; /* No hardware to disable */ +} +#endif /* CFG_CMD_PCMCIA */ +#endif /* CONFIG_GTH */ + +/* ---------------------------------------------------------------------------- */ +/* ICU862 Boards by Cambridge Broadband Ltd. */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_ICU862) + +#define PCMCIA_BOARD_MSG "ICU862" + +static void cfg_port_B (void); + +static int hardware_enable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, pipr, mask; + int i; + + debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + udelay(10000); + + immap = (immap_t *)CFG_IMMR; + sysp = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* Configure Port B for TPS2205 PC-Card Power-Interface Switch */ + cfg_port_B (); + + /* + * Configure SIUMCR to enable PCMCIA port B + * (VFLS[0:1] are not used for debugging, we connect FRZ# instead) + */ + sysp->sc_siumcr &= ~SIUMCR_DBGC11; /* set DBGC to 00 */ + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK(_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX(_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", + __LINE__,__FUNCTION__, + &(pcmp->pcmc_pipr),pcmp->pcmc_pipr); + if (pcmp->pcmc_pipr & 0x00001800) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On: Set VAVCC to 3.3V or 5V, set VAVPP to Hi-Z + */ + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + pipr = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + pipr, + (reg&PCMCIA_VS1(slot))?"n":"ff", + (reg&PCMCIA_VS2(slot))?"n":"ff"); + + reg = cp->cp_pbdat; + if ((pipr & mask) == mask) { + reg |= (TPS2205_VPP_PGM | TPS2205_VPP_VCC | /* VAVPP => Hi-Z */ + TPS2205_VCC3); /* 3V off */ + reg &= ~(TPS2205_VCC5); /* 5V on */ + puts (" 5.0V card found: "); + } else { + reg |= (TPS2205_VPP_PGM | TPS2205_VPP_VCC | /* VAVPP => Hi-Z */ + TPS2205_VCC5); /* 5V off */ + reg &= ~(TPS2205_VCC3); /* 3V on */ + puts (" 3.3V card found: "); + } + + debug ("\nPB DAT: %08x -> 3.3V %s 5.0V %s VPP_PGM %s VPP_VCC %s\n", + reg, + (reg & TPS2205_VCC3) ? "off" : "on", + (reg & TPS2205_VCC5) ? "off" : "on", + (reg & TPS2205_VPP_PGM) ? "off" : "on", + (reg & TPS2205_VPP_VCC) ? "off" : "on" ); + + cp->cp_pbdat = reg; + + /* Wait 500 ms; use this to check for over-current */ + for (i=0; i<5000; ++i) { + if ((cp->cp_pbdat & TPS2205_OC) == 0) { + printf (" *** Overcurrent - Safety shutdown ***\n"); + cp->cp_pbdat &= ~(TPS2205_SHDN); + return (1); + } + udelay (100); + } + + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return (0); +} + + + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + immap = (immap_t *)CFG_IMMR; + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + + /* Shut down */ + cp->cp_pbdat &= ~(TPS2205_SHDN); + + /* Configure PCMCIA General Control Register */ + PCMCIA_PGCRX(_slot_) = 0; + + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(10000); + + return (0); +} +#endif /* CFG_CMD_PCMCIA */ + + + +static int voltage_set(int slot, int vcc, int vpp) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("voltage_set: " + PCMCIA_BOARD_MSG + " Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10); + + immap = (immap_t *)CFG_IMMR; + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Configure Port C pins for + * 5 Volts Enable and 3 Volts enable, + * Turn all power pins to Hi-Z + */ + debug ("PCMCIA power OFF\n"); + cfg_port_B (); /* Enables switch, but all in Hi-Z */ + + reg = cp->cp_pbdat; + + switch(vcc) { + case 0: break; /* Switch off */ + case 33: reg &= ~TPS2205_VCC3; break; /* Switch on 3.3V */ + case 50: reg &= ~TPS2205_VCC5; break; /* Switch on 5.0V */ + default: goto done; + } + + /* Checking supported voltages */ + + debug ("PIPR: 0x%x --> %s\n", + pcmp->pcmc_pipr, + (pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V"); + + cp->cp_pbdat = reg; + +#ifdef DEBUG + { + char *s; + + if ((reg & TPS2205_VCC3) == 0) { + s = "at 3.3V"; + } else if ((reg & TPS2205_VCC5) == 0) { + s = "at 5.0V"; + } else { + s = "down"; + } + printf ("PCMCIA powered %s\n", s); + } +#endif + +done: + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", + slot+'A'); + return (0); +} + +static void cfg_port_B (void) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + uint reg; + + immap = (immap_t *)CFG_IMMR; + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* + * Configure Port B for TPS2205 PC-Card Power-Interface Switch + * + * Switch off all voltages, assert shutdown + */ + reg = cp->cp_pbdat; + reg |= (TPS2205_VPP_PGM | TPS2205_VPP_VCC | /* VAVPP => Hi-Z */ + TPS2205_VCC3 | TPS2205_VCC5 | /* VAVCC => Hi-Z */ + TPS2205_SHDN); /* enable switch */ + cp->cp_pbdat = reg; + + cp->cp_pbpar &= ~(TPS2205_INPUTS | TPS2205_OUTPUTS); + + reg = cp->cp_pbdir & ~(TPS2205_INPUTS); + cp->cp_pbdir = reg | TPS2205_OUTPUTS; + + debug ("Set Port B: PAR: %08x DIR: %08x DAT: %08x\n", + cp->cp_pbpar, cp->cp_pbdir, cp->cp_pbdat); +} + +#endif /* ICU862 */ + + +/* ---------------------------------------------------------------------------- */ +/* C2MON Boards by TTTech Computertechnik AG */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_C2MON) + +#define PCMCIA_BOARD_MSG "C2MON" + +static void cfg_ports (void); + +static int hardware_enable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, pipr, mask; + ushort sreg; + int i; + + debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + udelay(10000); + + immap = (immap_t *)CFG_IMMR; + sysp = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* Configure Ports for TPS2211A PC-Card Power-Interface Switch */ + cfg_ports (); + + /* + * Configure SIUMCR to enable PCMCIA port B + * (VFLS[0:1] are not used for debugging, we connect FRZ# instead) + */ + sysp->sc_siumcr &= ~SIUMCR_DBGC11; /* set DBGC to 00 */ + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK(_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX(_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", + __LINE__,__FUNCTION__, + &(pcmp->pcmc_pipr),pcmp->pcmc_pipr); + if (pcmp->pcmc_pipr & 0x00001800) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On: Set VAVCC to 3.3V or 5V, set VAVPP to Hi-Z + */ + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + pipr = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + pipr, + (reg&PCMCIA_VS1(slot))?"n":"ff", + (reg&PCMCIA_VS2(slot))?"n":"ff"); + + sreg = immap->im_ioport.iop_pcdat; + if ((pipr & mask) == mask) { + sreg |= (TPS2211_VPPD0 | TPS2211_VPPD1 | /* VAVPP => Hi-Z */ + TPS2211_VCCD1); /* 5V on */ + sreg &= ~(TPS2211_VCCD0); /* 3V off */ + puts (" 5.0V card found: "); + } else { + sreg |= (TPS2211_VPPD0 | TPS2211_VPPD1 | /* VAVPP => Hi-Z */ + TPS2211_VCCD0); /* 3V on */ + sreg &= ~(TPS2211_VCCD1); /* 5V off */ + puts (" 3.3V card found: "); + } + + debug ("\nPC DAT: %04x -> 3.3V %s 5.0V %s\n", + sreg, + ( (sreg & TPS2211_VCCD0) && !(sreg & TPS2211_VCCD1)) ? "on" : "off", + (!(sreg & TPS2211_VCCD0) && (sreg & TPS2211_VCCD1)) ? "on" : "off" + ); + + immap->im_ioport.iop_pcdat = sreg; + + /* Wait 500 ms; use this to check for over-current */ + for (i=0; i<5000; ++i) { + if ((cp->cp_pbdat & TPS2211_OC) == 0) { + printf (" *** Overcurrent - Safety shutdown ***\n"); + immap->im_ioport.iop_pcdat &= ~(TPS2211_VCCD0|TPS2211_VCCD1); + return (1); + } + udelay (100); + } + + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return (0); +} + + + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + + /* Configure PCMCIA General Control Register */ + PCMCIA_PGCRX(_slot_) = 0; + + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + /* ALl voltages off / Hi-Z */ + immap->im_ioport.iop_pcdat |= (TPS2211_VPPD0 | TPS2211_VPPD1 | + TPS2211_VCCD0 | TPS2211_VCCD1 ); + + udelay(10000); + + return (0); +} +#endif /* CFG_CMD_PCMCIA */ + + + +static int voltage_set(int slot, int vcc, int vpp) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + u_long reg; + ushort sreg; + + debug ("voltage_set: " + PCMCIA_BOARD_MSG + " Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10); + + immap = (immap_t *)CFG_IMMR; + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Configure Port C pins for + * 5 Volts Enable and 3 Volts enable, + * Turn all power pins to Hi-Z + */ + debug ("PCMCIA power OFF\n"); + cfg_ports (); /* Enables switch, but all in Hi-Z */ + + sreg = immap->im_ioport.iop_pcdat; + sreg |= TPS2211_VPPD0 | TPS2211_VPPD1; /* VAVPP always Hi-Z */ + + switch(vcc) { + case 0: break; /* Switch off */ + case 33: sreg |= TPS2211_VCCD0; /* Switch on 3.3V */ + sreg &= ~TPS2211_VCCD1; + break; + case 50: sreg &= ~TPS2211_VCCD0; /* Switch on 5.0V */ + sreg |= TPS2211_VCCD1; + break; + default: goto done; + } + + /* Checking supported voltages */ + + debug ("PIPR: 0x%x --> %s\n", + pcmp->pcmc_pipr, + (pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V"); + + immap->im_ioport.iop_pcdat = sreg; + +#ifdef DEBUG + { + char *s; + + if ((sreg & TPS2211_VCCD0) && !(sreg & TPS2211_VCCD1)) { + s = "at 3.3V"; + } else if (!(sreg & TPS2211_VCCD0) && (sreg & TPS2211_VCCD1)) { + s = "at 5.0V"; + } else { + s = "down"; + } + printf ("PCMCIA powered %s\n", s); + } +#endif + +done: + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", + slot+'A'); + return (0); +} + +static void cfg_ports (void) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + ushort sreg; + + immap = (immap_t *)CFG_IMMR; + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* + * Configure Port C for TPS2211 PC-Card Power-Interface Switch + * + * Switch off all voltages, assert shutdown + */ + sreg = immap->im_ioport.iop_pcdat; + sreg |= (TPS2211_VPPD0 | TPS2211_VPPD1); /* VAVPP => Hi-Z */ + sreg &= ~(TPS2211_VCCD0 | TPS2211_VCCD1); /* 3V and 5V off */ + immap->im_ioport.iop_pcdat = sreg; + + immap->im_ioport.iop_pcpar &= ~(TPS2211_OUTPUTS); + immap->im_ioport.iop_pcdir |= TPS2211_OUTPUTS; + + debug ("Set Port C: PAR: %04x DIR: %04x DAT: %04x\n", + immap->im_ioport.iop_pcpar, + immap->im_ioport.iop_pcdir, + immap->im_ioport.iop_pcdat); + + /* + * Configure Port B for TPS2211 PC-Card Power-Interface Switch + * + * Over-Current Input only + */ + cp->cp_pbpar &= ~(TPS2211_INPUTS); + cp->cp_pbdir &= ~(TPS2211_INPUTS); + + debug ("Set Port B: PAR: %08x DIR: %08x DAT: %08x\n", + cp->cp_pbpar, cp->cp_pbdir, cp->cp_pbdat); +} + +#endif /* C2MON */ + +/* ---------------------------------------------------------------------------- + MBX board from Morotola + ---------------------------------------------------------------------------- */ + +#if defined( CONFIG_MBX ) +#include <../board/mbx8xx/csr.h> + +/* A lot of this has been taken from the RPX code in this file it works from me. + I have added the voltage selection for the MBX board. */ + +/* MBX voltage bit in control register #2 */ +#define CR2_VPP12 ((uchar)0x10) +#define CR2_VPPVDD ((uchar)0x20) +#define CR2_VDD5 ((uchar)0x40) +#define CR2_VDD3 ((uchar)0x80) + +#define PCMCIA_BOARD_MSG "MBX860" + +static int voltage_set (int slot, int vcc, int vpp) +{ + uchar reg = 0; + + debug ("voltage_set: PCMCIA_BOARD_MSG Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A' + slot, vcc / 10, vcc % 10, vpp / 10, vcc % 10); + + switch (vcc) { + case 0: + break; + case 33: + reg |= CR2_VDD3; + break; + case 50: + reg |= CR2_VDD5; + break; + default: + return 1; + } + + switch (vpp) { + case 0: + break; + case 33: + case 50: + if (vcc == vpp) { + reg |= CR2_VPPVDD; + } else { + return 1; + } + break; + case 120: + reg |= CR2_VPP12; + break; + default: + return 1; + } + + /* first, turn off all power */ + MBX_CSR2 &= ~(CR2_VDDSEL | CR2_VPPSEL); + + /* enable new powersettings */ + MBX_CSR2 |= reg; + debug ("MBX_CSR2 read = 0x%02x\n", MBX_CSR2); + + return (0); +} + +static int hardware_enable (int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, mask; + + debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", + 'A' + slot); + + udelay (10000); + + immap = (immap_t *) CFG_IMMR; + sysp = (sysconf8xx_t *) (&(((immap_t *) CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *) (&(((immap_t *) CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *) (&(((immap_t *) CFG_IMMR)->im_cpm)); + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK (_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK (_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX (_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX (_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX (_slot_) = reg; + udelay (500); + + /* remove all power */ + voltage_set (slot, 0, 0); + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay (10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", __LINE__, __FUNCTION__, + &(pcmp->pcmc_pipr), pcmp->pcmc_pipr); + + if (pcmp->pcmc_pipr & 0x00001800) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On. + */ + mask = PCMCIA_VS1 (_slot_) | PCMCIA_VS2 (_slot_); + reg = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", reg, + (reg & PCMCIA_VS1 (slot)) ? "n" : "ff", + (reg & PCMCIA_VS2 (slot)) ? "n" : "ff"); + + if ((reg & mask) == mask) { + voltage_set (_slot_, 50, 0); + printf (" 5.0V card found: "); + } else { + voltage_set (_slot_, 33, 0); + printf (" 3.3V card found: "); + } + + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX (_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX (_slot_) = reg; + + udelay (250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return (0); +} + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable (int slot) +{ + return 0; /* No hardware to disable */ +} +#endif /* CFG_CMD_PCMCIA */ +#endif /* CONFIG_MBX */ +/* ---------------------------------------------------------------------------- */ +/* R360MPI Board */ +/* ---------------------------------------------------------------------------- */ + +#if defined(CONFIG_R360MPI) + +#define PCMCIA_BOARD_MSG "R360MPI" + + +static int hardware_enable(int slot) +{ + volatile immap_t *immap; + volatile cpm8xx_t *cp; + volatile pcmconf8xx_t *pcmp; + volatile sysconf8xx_t *sysp; + uint reg, mask; + + debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + udelay(10000); + + immap = (immap_t *)CFG_IMMR; + sysp = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf)); + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + cp = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm)); + + /* + * Configure SIUMCR to enable PCMCIA port B + * (VFLS[0:1] are not used for debugging, we connect FRZ# instead) + */ + sysp->sc_siumcr &= ~SIUMCR_DBGC11; /* set DBGC to 00 */ + + /* clear interrupt state, and disable interrupts */ + pcmp->pcmc_pscr = PCMCIA_MASK(_slot_); + pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_); + + /* disable interrupts & DMA */ + PCMCIA_PGCRX(_slot_) = 0; + + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Configure Ports A, B & C pins for + * 5 Volts Enable and 3 Volts enable + */ + immap->im_ioport.iop_pcpar &= ~(0x0400); + immap->im_ioport.iop_pcso &= ~(0x0400);/* + immap->im_ioport.iop_pcdir |= 0x0400;*/ + + immap->im_ioport.iop_papar &= ~(0x0200);/* + immap->im_ioport.iop_padir |= 0x0200;*/ +#if 0 + immap->im_ioport.iop_pbpar &= ~(0xC000); + immap->im_ioport.iop_pbdir &= ~(0xC000); +#endif + /* remove all power */ + + immap->im_ioport.iop_pcdat |= 0x0400; + immap->im_ioport.iop_padat |= 0x0200; + + /* + * Make sure there is a card in the slot, then configure the interface. + */ + udelay(10000); + debug ("[%d] %s: PIPR(%p)=0x%x\n", + __LINE__,__FUNCTION__, + &(pcmp->pcmc_pipr),pcmp->pcmc_pipr); + if (pcmp->pcmc_pipr & 0x00001800) { + printf (" No Card found\n"); + return (1); + } + + /* + * Power On. + */ + mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot); + reg = pcmp->pcmc_pipr; + debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n", + reg, + (reg&PCMCIA_VS1(slot))?"n":"ff", + (reg&PCMCIA_VS2(slot))?"n":"ff"); + if ((reg & mask) == mask) { + immap->im_ioport.iop_pcdat &= ~(0x4000); + puts (" 5.0V card found: "); + } else { + immap->im_ioport.iop_padat &= ~(0x0002); + puts (" 3.3V card found: "); + } + immap->im_ioport.iop_pcdir |= 0x0400; + immap->im_ioport.iop_padir |= 0x0200; +#if 0 + /* VCC switch error flag, PCMCIA slot INPACK_ pin */ + cp->cp_pbdir &= ~(0x0020 | 0x0010); + cp->cp_pbpar &= ~(0x0020 | 0x0010); + udelay(500000); +#endif + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(250000); /* some cards need >150 ms to come up :-( */ + + debug ("# hardware_enable done\n"); + + return (0); +} + + + +#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) +static int hardware_disable(int slot) +{ + volatile immap_t *immap; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + + /* remove all power */ + immap->im_ioport.iop_pcdat |= 0x0400; + immap->im_ioport.iop_padat |= 0x0200; + + /* Configure PCMCIA General Control Register */ + PCMCIA_PGCRX(_slot_) = 0; + + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + + udelay(10000); + + return (0); +} +#endif /* CFG_CMD_PCMCIA */ + + + +static int voltage_set(int slot, int vcc, int vpp) +{ + volatile immap_t *immap; + volatile pcmconf8xx_t *pcmp; + u_long reg; + + debug ("voltage_set: " + PCMCIA_BOARD_MSG + " Slot %c, Vcc=%d.%d, Vpp=%d.%d\n", + 'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10); + + immap = (immap_t *)CFG_IMMR; + pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia)); + /* + * Disable PCMCIA buffers (isolate the interface) + * and assert RESET signal + */ + debug ("Disable PCMCIA buffers and assert RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg |= __MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg |= __MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + /* + * Configure Ports A & C pins for + * 5 Volts Enable and 3 Volts enable, + * Turn off all power + */ + debug ("PCMCIA power OFF\n"); + immap->im_ioport.iop_pcpar &= ~(0x0400); + immap->im_ioport.iop_pcso &= ~(0x0400);/* + immap->im_ioport.iop_pcdir |= 0x0400;*/ + + immap->im_ioport.iop_papar &= ~(0x0200);/* + immap->im_ioport.iop_padir |= 0x0200;*/ + + immap->im_ioport.iop_pcdat |= 0x0400; + immap->im_ioport.iop_padat |= 0x0200; + + reg = 0; + switch(vcc) { + case 0: break; + case 33: reg |= 0x0200; break; + case 50: reg |= 0x0400; break; + default: goto done; + } + + /* Checking supported voltages */ + + debug ("PIPR: 0x%x --> %s\n", + pcmp->pcmc_pipr, + (pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V"); + + if (reg & 0x0200) + immap->im_ioport.iop_pcdat &= !reg; + if (reg & 0x0400) + immap->im_ioport.iop_padat &= !reg; + immap->im_ioport.iop_pcdir |= 0x0200; + immap->im_ioport.iop_padir |= 0x0400; + if (reg) { + debug ("PCMCIA powered at %sV\n", + (reg&0x0400) ? "5.0" : "3.3"); + } else { + debug ("PCMCIA powered down\n"); + } + +done: + debug ("Enable PCMCIA buffers and stop RESET\n"); + reg = PCMCIA_PGCRX(_slot_); + reg &= ~__MY_PCMCIA_GCRX_CXRESET; /* active high */ + reg &= ~__MY_PCMCIA_GCRX_CXOE; /* active low */ + PCMCIA_PGCRX(_slot_) = reg; + udelay(500); + + debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n", + slot+'A'); + return (0); +} + +#endif /* R360MPI */ + + +/* ---------------------------------------------------------------------------- */ +/* End of Board Specific Stuff */ +/* ---------------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------------- */ +/* MPC8xx Specific Stuff - should go to MPC8xx directory */ +/* ---------------------------------------------------------------------------- */ + +/* + * Search this table to see if the windowsize is + * supported... + */ + +#define M8XX_SIZES_NO 32 + +static const u_int m8xx_size_to_gray[M8XX_SIZES_NO] = +{ 0x00000001, 0x00000002, 0x00000008, 0x00000004, + 0x00000080, 0x00000040, 0x00000010, 0x00000020, + 0x00008000, 0x00004000, 0x00001000, 0x00002000, + 0x00000100, 0x00000200, 0x00000800, 0x00000400, + + 0x0fffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x01000000, 0x02000000, 0xffffffff, 0x04000000, + 0x00010000, 0x00020000, 0x00080000, 0x00040000, + 0x00800000, 0x00400000, 0x00100000, 0x00200000 }; + + +/* ---------------------------------------------------------------------------- */ + +static u_int m8xx_get_graycode(u_int size) +{ + u_int k; + + for (k = 0; k < M8XX_SIZES_NO; k++) { + if(m8xx_size_to_gray[k] == size) + break; + } + + if((k == M8XX_SIZES_NO) || (m8xx_size_to_gray[k] == -1)) + k = -1; + + return k; +} + +/* ------------------------------------------------------------------------- */ + +#if 0 +static u_int m8xx_get_speed(u_int ns, u_int is_io) +{ + u_int reg, clocks, psst, psl, psht; + + if(!ns) { + + /* + * We get called with IO maps setup to 0ns + * if not specified by the user. + * They should be 255ns. + */ + + if(is_io) + ns = 255; + else + ns = 100; /* fast memory if 0 */ + } + + /* + * In PSST, PSL, PSHT fields we tell the controller + * timing parameters in CLKOUT clock cycles. + * CLKOUT is the same as GCLK2_50. + */ + +/* how we want to adjust the timing - in percent */ + +#define ADJ 180 /* 80 % longer accesstime - to be sure */ + + clocks = ((M8XX_BUSFREQ / 1000) * ns) / 1000; + clocks = (clocks * ADJ) / (100*1000); + + if(clocks >= PCMCIA_BMT_LIMIT) { + DEBUG(0, "Max access time limit reached\n"); + clocks = PCMCIA_BMT_LIMIT-1; + } + + psst = clocks / 7; /* setup time */ + psht = clocks / 7; /* hold time */ + psl = (clocks * 5) / 7; /* strobe length */ + + psst += clocks - (psst + psht + psl); + + reg = psst << 12; + reg |= psl << 7; + reg |= psht << 16; + + return reg; +} +#endif + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_IDE_8xx_PCCARD +static void print_funcid (int func) +{ + puts (indent); + switch (func) { + case CISTPL_FUNCID_MULTI: + puts (" Multi-Function"); + break; + case CISTPL_FUNCID_MEMORY: + puts (" Memory"); + break; + case CISTPL_FUNCID_SERIAL: + puts (" Serial Port"); + break; + case CISTPL_FUNCID_PARALLEL: + puts (" Parallel Port"); + break; + case CISTPL_FUNCID_FIXED: + puts (" Fixed Disk"); + break; + case CISTPL_FUNCID_VIDEO: + puts (" Video Adapter"); + break; + case CISTPL_FUNCID_NETWORK: + puts (" Network Adapter"); + break; + case CISTPL_FUNCID_AIMS: + puts (" AIMS Card"); + break; + case CISTPL_FUNCID_SCSI: + puts (" SCSI Adapter"); + break; + default: + puts (" Unknown"); + break; + } + puts (" Card\n"); +} +#endif /* CONFIG_IDE_8xx_PCCARD */ + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_IDE_8xx_PCCARD +static void print_fixed (volatile uchar *p) +{ + if (p == NULL) + return; + + puts(indent); + + switch (*p) { + case CISTPL_FUNCE_IDE_IFACE: + { uchar iface = *(p+2); + + puts ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown"); + puts (" interface "); + break; + } + case CISTPL_FUNCE_IDE_MASTER: + case CISTPL_FUNCE_IDE_SLAVE: + { uchar f1 = *(p+2); + uchar f2 = *(p+4); + + puts ((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]"); + + if (f1 & CISTPL_IDE_UNIQUE) + puts (" [unique]"); + + puts ((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]"); + + if (f2 & CISTPL_IDE_HAS_SLEEP) + puts (" [sleep]"); + + if (f2 & CISTPL_IDE_HAS_STANDBY) + puts (" [standby]"); + + if (f2 & CISTPL_IDE_HAS_IDLE) + puts (" [idle]"); + + if (f2 & CISTPL_IDE_LOW_POWER) + puts (" [low power]"); + + if (f2 & CISTPL_IDE_REG_INHIBIT) + puts (" [reg inhibit]"); + + if (f2 & CISTPL_IDE_HAS_INDEX) + puts (" [index]"); + + if (f2 & CISTPL_IDE_IOIS16) + puts (" [IOis16]"); + + break; + } + } + putc ('\n'); +} +#endif /* CONFIG_IDE_8xx_PCCARD */ + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_IDE_8xx_PCCARD + +#define MAX_IDENT_CHARS 64 +#define MAX_IDENT_FIELDS 4 + +static uchar *known_cards[] = { + "ARGOSY PnPIDE D5", + NULL +}; + +static int identify (volatile uchar *p) +{ + uchar id_str[MAX_IDENT_CHARS]; + uchar data; + uchar *t; + uchar **card; + int i, done; + + if (p == NULL) + return (0); /* Don't know */ + + t = id_str; + done =0; + + for (i=0; i<=4 && !done; ++i, p+=2) { + while ((data = *p) != '\0') { + if (data == 0xFF) { + done = 1; + break; + } + *t++ = data; + if (t == &id_str[MAX_IDENT_CHARS-1]) { + done = 1; + break; + } + p += 2; + } + if (!done) + *t++ = ' '; + } + *t = '\0'; + while (--t > id_str) { + if (*t == ' ') + *t = '\0'; + else + break; + } + puts (id_str); + putc ('\n'); + + for (card=known_cards; *card; ++card) { + debug ("## Compare against \"%s\"\n", *card); + if (strcmp(*card, id_str) == 0) { /* found! */ + debug ("## CARD FOUND ##\n"); + return (1); + } + } + + return (0); /* don't know */ +} +#endif /* CONFIG_IDE_8xx_PCCARD */ + +/* ------------------------------------------------------------------------- */ + +#endif /* CFG_CMD_PCMCIA || (CFG_CMD_IDE && CONFIG_IDE_8xx_PCCARD) */ diff --git a/common/env_flash.c b/common/env_flash.c new file mode 100644 index 0000000..1593d2c --- /dev/null +++ b/common/env_flash.c @@ -0,0 +1,377 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.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 + */ + +/* #define DEBUG */ + +#include <common.h> + +#if defined(CFG_ENV_IS_IN_FLASH) /* Environment is in Flash */ + +#include <command.h> +#include <environment.h> +#include <cmd_nvedit.h> +#include <linux/stddef.h> + +#if ((CONFIG_COMMANDS&(CFG_CMD_ENV|CFG_CMD_FLASH)) == (CFG_CMD_ENV|CFG_CMD_FLASH)) +#define CMD_SAVEENV +#elif defined(CFG_ENV_ADDR_REDUND) +#error Cannot use CFG_ENV_ADDR_REDUND without CFG_CMD_ENV & CFG_CMD_FLASH +#endif + +#if defined(CFG_ENV_SECT_SIZE) && (CFG_ENV_SECT_SIZE > CFG_ENV_SIZE) && \ + defined(CFG_ENV_ADDR_REDUND) +#error CFG_ENV_ADDR_REDUND should not be used when CFG_ENV_SECT_SIZE > CFG_ENV_SIZE +#endif + +#if defined(CFG_ENV_SIZE_REDUND) && (CFG_ENV_SIZE_REDUND < CFG_ENV_SIZE) +#error CFG_ENV_SIZE_REDUND should not be less then CFG_ENV_SIZE +#endif + +#ifdef CONFIG_INFERNO +# ifdef CFG_ENV_ADDR_REDUND +#error CFG_ENV_ADDR_REDUND is not implemented for CONFIG_INFERNO +# endif +#endif + +char * env_name_spec = "Flash"; + +#ifdef ENV_IS_EMBEDDED + +extern uchar environment[]; +env_t *env_ptr = (env_t *)(&environment[0]); + +#ifdef CMD_SAVEENV +/* static env_t *flash_addr = (env_t *)(&environment[0]);-broken on ARM-wd-*/ +static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; +#endif + +#else /* ! ENV_IS_EMBEDDED */ + +env_t *env_ptr = (env_t *)CFG_ENV_ADDR; +#ifdef CMD_SAVEENV +static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; +#endif + +#endif /* ENV_IS_EMBEDDED */ + +#ifdef CFG_ENV_ADDR_REDUND +static env_t *flash_addr_new = (env_t *)CFG_ENV_ADDR_REDUND; + +static ulong end_addr = CFG_ENV_ADDR + CFG_ENV_SIZE - 1; +static ulong end_addr_new = CFG_ENV_ADDR_REDUND + CFG_ENV_SIZE_REDUND - 1; + +static uchar active_flag = 1; +static uchar obsolete_flag = 0; +#endif + +extern uchar default_environment[]; +extern int default_environment_size; + + +uchar env_get_char_spec (int index) +{ + DECLARE_GLOBAL_DATA_PTR; + + return ( *((uchar *)(gd->env_addr + index)) ); +} + +#ifdef CFG_ENV_ADDR_REDUND + +int env_init(void) +{ + DECLARE_GLOBAL_DATA_PTR; + + int crc1_ok = + (crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc); + int crc2_ok = + (crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc); + + uchar flag1 = flash_addr->flags; + uchar flag2 = flash_addr_new->flags; + + ulong addr_default = (ulong)&default_environment[0]; + ulong addr1 = (ulong)&(flash_addr->data); + ulong addr2 = (ulong)&(flash_addr_new->data); + + if (crc1_ok && ! crc2_ok) + { + gd->env_addr = addr1; + gd->env_valid = 1; + } + else if (! crc1_ok && crc2_ok) + { + gd->env_addr = addr2; + gd->env_valid = 1; + } + else if (! crc1_ok && ! crc2_ok) + { + gd->env_addr = addr_default; + gd->env_valid = 0; + } + else if (flag1 == active_flag && flag2 == obsolete_flag) + { + gd->env_addr = addr1; + gd->env_valid = 1; + } + else if (flag1 == obsolete_flag && flag2 == active_flag) + { + gd->env_addr = addr2; + gd->env_valid = 1; + } + else if (flag1 == flag2) + { + gd->env_addr = addr1; + gd->env_valid = 2; + } + else if (flag1 == 0xFF) + { + gd->env_addr = addr1; + gd->env_valid = 2; + } + else if (flag2 == 0xFF) + { + gd->env_addr = addr2; + gd->env_valid = 2; + } + + return (0); +} + +#ifdef CMD_SAVEENV +int saveenv(void) +{ + int rc = 1; + + debug ("Protect off %08lX ... %08lX\n", + (ulong)flash_addr, end_addr); + + if (flash_sect_protect (0, (ulong)flash_addr, end_addr)) { + goto Done; + } + + debug ("Protect off %08lX ... %08lX\n", + (ulong)flash_addr_new, end_addr_new); + + if (flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new)) { + goto Done; + } + + puts ("Erasing Flash..."); + debug (" %08lX ... %08lX ...", + (ulong)flash_addr_new, end_addr_new); + + if (flash_sect_erase ((ulong)flash_addr_new, end_addr_new)) { + goto Done; + } + + puts ("Writing to Flash... "); + debug (" %08lX ... %08lX ...", + (ulong)&(flash_addr_new->data), + sizeof(env_ptr->data)+(ulong)&(flash_addr_new->data)); + if (flash_write(env_ptr->data, + (ulong)&(flash_addr_new->data), + sizeof(env_ptr->data)) || + + flash_write((char *)&(env_ptr->crc), + (ulong)&(flash_addr_new->crc), + sizeof(env_ptr->crc)) || + + flash_write((char *)&obsolete_flag, + (ulong)&(flash_addr->flags), + sizeof(flash_addr->flags)) || + + flash_write((char *)&active_flag, + (ulong)&(flash_addr_new->flags), + sizeof(flash_addr_new->flags))) + { + flash_perror (rc); + goto Done; + } + puts ("done\n"); + + { + env_t * etmp = flash_addr; + ulong ltmp = end_addr; + + flash_addr = flash_addr_new; + flash_addr_new = etmp; + + end_addr = end_addr_new; + end_addr_new = ltmp; + } + + rc = 0; +Done: + + /* try to re-protect */ + (void) flash_sect_protect (1, (ulong)flash_addr, end_addr); + (void) flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new); + + return rc; +} +#endif /* CMD_SAVEENV */ + +#else /* ! CFG_ENV_ADDR_REDUND */ + +int env_init(void) +{ + DECLARE_GLOBAL_DATA_PTR; + + if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { + gd->env_addr = (ulong)&(env_ptr->data); + gd->env_valid = 1; + } else { + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 0; + } + + return (0); +} + +#ifdef CMD_SAVEENV + +int saveenv(void) +{ + int len, rc; + ulong end_addr; + ulong flash_sect_addr; +#if defined(CFG_ENV_SECT_SIZE) && (CFG_ENV_SECT_SIZE > CFG_ENV_SIZE) + ulong flash_offset; + uchar env_buffer[CFG_ENV_SECT_SIZE]; +#else + uchar *env_buffer = (char *)env_ptr; +#endif /* CFG_ENV_SECT_SIZE */ + int rcode = 0; + +#if defined(CFG_ENV_SECT_SIZE) && (CFG_ENV_SECT_SIZE > CFG_ENV_SIZE) + + flash_offset = ((ulong)flash_addr) & (CFG_ENV_SECT_SIZE-1); + flash_sect_addr = ((ulong)flash_addr) & ~(CFG_ENV_SECT_SIZE-1); + + debug ( "copy old content: " + "sect_addr: %08lX env_addr: %08lX offset: %08lX\n", + flash_sect_addr, (ulong)flash_addr, flash_offset); + + /* copy old contents to temporary buffer */ + memcpy (env_buffer, (void *)flash_sect_addr, CFG_ENV_SECT_SIZE); + + /* copy current environment to temporary buffer */ + memcpy ((uchar *)((unsigned long)env_buffer + flash_offset), + env_ptr, + CFG_ENV_SIZE); + + len = CFG_ENV_SECT_SIZE; +#else + flash_sect_addr = (ulong)flash_addr; + len = CFG_ENV_SIZE; +#endif /* CFG_ENV_SECT_SIZE */ + +#ifndef CONFIG_INFERNO + end_addr = flash_sect_addr + len - 1; +#else + /* this is the last sector, and the size is hardcoded here */ + /* otherwise we will get stack problems on loading 128 KB environment */ + end_addr = flash_sect_addr + 0x20000 - 1; +#endif + + debug ("Protect off %08lX ... %08lX\n", + (ulong)flash_sect_addr, end_addr); + + if (flash_sect_protect (0, flash_sect_addr, end_addr)) + return 1; + + puts ("Erasing Flash..."); + if (flash_sect_erase (flash_sect_addr, end_addr)) + return 1; + + puts ("Writing to Flash... "); + rc = flash_write(env_buffer, flash_sect_addr, len); + if (rc != 0) { + flash_perror (rc); + rcode = 1; + } else { + puts ("done\n"); + } + + /* try to re-protect */ + (void) flash_sect_protect (1, flash_sect_addr, end_addr); + return rcode; +} + +#endif /* CMD_SAVEENV */ + +#endif /* CFG_ENV_ADDR_REDUND */ + +void env_relocate_spec (void) +{ +#if !defined(ENV_IS_EMBEDDED) || defined(CFG_ENV_ADDR_REDUND) +#ifdef CFG_ENV_ADDR_REDUND + DECLARE_GLOBAL_DATA_PTR; + + if (gd->env_addr != (ulong)&(flash_addr->data)) + { + env_t * etmp = flash_addr; + ulong ltmp = end_addr; + + flash_addr = flash_addr_new; + flash_addr_new = etmp; + + end_addr = end_addr_new; + end_addr_new = ltmp; + } + + if (flash_addr_new->flags != obsolete_flag && + crc32(0, flash_addr_new->data, ENV_SIZE) == + flash_addr_new->crc) + { + gd->env_valid = 2; + flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new); + flash_write((char *)&obsolete_flag, + (ulong)&(flash_addr_new->flags), + sizeof(flash_addr_new->flags)); + flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new); + } + + if (flash_addr->flags != active_flag && + (flash_addr->flags & active_flag) == active_flag) + { + gd->env_valid = 2; + flash_sect_protect (0, (ulong)flash_addr, end_addr); + flash_write((char *)&active_flag, + (ulong)&(flash_addr->flags), + sizeof(flash_addr->flags)); + flash_sect_protect (1, (ulong)flash_addr, end_addr); + } + + if (gd->env_valid == 2) + puts ("*** Warning - some problems detected " + "reading environment; recovered successfully\n\n"); +#endif /* CFG_ENV_ADDR_REDUND */ + memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE); +#endif /* ! ENV_IS_EMBEDDED || CFG_ENV_ADDR_REDUND */ +} + +#endif /* CFG_ENV_IS_IN_FLASH) */ diff --git a/common/env_nvram.c b/common/env_nvram.c new file mode 100644 index 0000000..fdfa4fc --- /dev/null +++ b/common/env_nvram.c @@ -0,0 +1,138 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.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 + */ + +/* + * 09-18-2001 Andreas Heppel, Sysgo RTS GmbH <aheppel@sysgo.de> + * + * It might not be possible in all cases to use 'memcpy()' to copy + * the environment to NVRAM, as the NVRAM might not be mapped into + * the memory space. (I.e. this is the case for the BAB750). In those + * cases it might be possible to access the NVRAM using a different + * method. For example, the RTC on the BAB750 is accessible in IO + * space using its address and data registers. To enable usage of + * NVRAM in those cases I invented the functions 'nvram_read()' and + * 'nvram_write()', which will be activated upon the configuration + * #define CFG_NVRAM_ACCESS_ROUTINE. Note, that those functions are + * strongly dependent on the used HW, and must be redefined for each + * board that wants to use them. + */ + +#include <common.h> + +#ifdef CFG_ENV_IS_IN_NVRAM /* Environment is in NVRAM */ + +#include <command.h> +#include <environment.h> +#include <cmd_nvedit.h> +#include <linux/stddef.h> +#include <malloc.h> + +#ifdef CFG_NVRAM_ACCESS_ROUTINE +extern void *nvram_read(void *dest, const long src, size_t count); +extern void nvram_write(long dest, const void *src, size_t count); +env_t *env_ptr = NULL; +#else +env_t *env_ptr = (env_t *)CFG_ENV_ADDR; +#endif + +char * env_name_spec = "NVRAM"; + +extern uchar default_environment[]; +extern int default_environment_size; + +extern uchar (*env_get_char)(int); +extern uchar env_get_char_memory (int index); + + +uchar env_get_char_spec (int index) +{ +#ifdef CFG_NVRAM_ACCESS_ROUTINE + uchar c; + + nvram_read(&c, CFG_ENV_ADDR+index, 1); + + return c; +#else + DECLARE_GLOBAL_DATA_PTR; + + return *((uchar *)(gd->env_addr + index)); +#endif +} + +void env_relocate_spec (void) +{ +#if defined(CFG_NVRAM_ACCESS_ROUTINE) + nvram_read(env_ptr, CFG_ENV_ADDR, CFG_ENV_SIZE); +#else + memcpy (env_ptr, (void*)CFG_ENV_ADDR, CFG_ENV_SIZE); +#endif +} + +int saveenv (void) +{ + int rcode = 0; + +#ifdef CFG_NVRAM_ACCESS_ROUTINE + nvram_write(CFG_ENV_ADDR, env_ptr, CFG_ENV_SIZE); +#else + if (memcpy ((char *)CFG_ENV_ADDR, env_ptr, CFG_ENV_SIZE) == NULL) + rcode = 1 ; +#endif + return rcode; +} + + +/************************************************************************ + * Initialize Environment use + * + * We are still running from ROM, so data use is limited + */ +int env_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + +#if defined(CFG_NVRAM_ACCESS_ROUTINE) + ulong crc; + uchar data[ENV_SIZE]; + nvram_read (&crc, CFG_ENV_ADDR, sizeof(ulong)); + nvram_read (data, CFG_ENV_ADDR+sizeof(ulong), ENV_SIZE); + + if (crc32(0, data, ENV_SIZE) == crc) { + gd->env_addr = (ulong)CFG_ENV_ADDR + sizeof(long); +#else + if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { + gd->env_addr = (ulong)&(env_ptr->data); +#endif + gd->env_valid = 1; + } else { + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 0; + } + + return (0); +} + +#endif /* CFG_ENV_IS_IN_NVRAM */ diff --git a/common/environment.c b/common/environment.c new file mode 100644 index 0000000..74bfd62 --- /dev/null +++ b/common/environment.c @@ -0,0 +1,198 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * 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 + */ + +#include <config.h> +#include <environment.h> + +/* + * Handle HOSTS that have prepended + * crap on symbol names, not TARGETS. + */ +#if defined(__APPLE__) +/* Leading underscore on symbols */ +# define SYM_CHAR "_" +#else /* No leading character on symbols */ +# define SYM_CHAR +#endif + +/* + * Generate embedded environment table + * inside U-Boot image, if needed. + */ +#if defined(ENV_IS_EMBEDDED) +/* + * Only put the environment in it's own section when we are building + * U-Boot proper. The host based program "tools/envcrc" does not need + * a seperate section. Note that ENV_CRC is only defined when building + * U-Boot itself. + */ +#if (defined(CONFIG_FADS) || \ + defined(CONFIG_HYMOD) || \ + defined(CONFIG_ICU862) || \ + defined(CONFIG_R360MPI) || \ + defined(CONFIG_TQM8xxL) || \ + defined(CONFIG_RRVISION) || \ + defined(CONFIG_TRAB) ) && \ + defined(ENV_CRC) /* Environment embedded in U-Boot .ppcenv section */ +/* XXX - This only works with GNU C */ +# define __PPCENV__ __attribute__ ((section(".ppcenv"))) +# define __PPCTEXT__ __attribute__ ((section(".text"))) + +#elif defined(USE_HOSTCC) /* Native for 'tools/envcrc' */ +# define __PPCENV__ /*XXX DO_NOT_DEL_THIS_COMMENT*/ +# define __PPCTEXT__ /*XXX DO_NOT_DEL_THIS_COMMENT*/ + +#else /* Environment is embedded in U-Boot's .text section */ +/* XXX - This only works with GNU C */ +# define __PPCENV__ __attribute__ ((section(".text"))) +# define __PPCTEXT__ __attribute__ ((section(".text"))) +#endif + +/* + * Macros to generate global absolutes. + */ +#define GEN_SYMNAME(str) SYM_CHAR #str +#define GEN_VALUE(str) #str +#define GEN_ABS(name, value) \ + asm (".globl " GEN_SYMNAME(name)); \ + asm (GEN_SYMNAME(name) " = " GEN_VALUE(value)) + +/* + * Macros to transform values + * into environment strings. + */ +#define XMK_STR(x) #x +#define MK_STR(x) XMK_STR(x) + +/* + * Check to see if we are building with a + * computed CRC. Otherwise define it as ~0. + */ +#if !defined(ENV_CRC) +# define ENV_CRC ~0 +#endif + +env_t environment __PPCENV__ = { + ENV_CRC, /* CRC Sum */ +#ifdef CFG_REDUNDAND_ENVIRONMENT + 1, /* Flags: valid */ +#endif + { +#if defined(CONFIG_BOOTARGS) + "bootargs=" CONFIG_BOOTARGS "\0" +#endif +#if defined(CONFIG_BOOTCOMMAND) + "bootcmd=" CONFIG_BOOTCOMMAND "\0" +#endif +#if defined(CONFIG_RAMBOOTCOMMAND) + "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" +#endif +#if defined(CONFIG_NFSBOOTCOMMAND) + "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" +#endif +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0" +#endif +#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) + "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0" +#endif +#ifdef CONFIG_LOADS_ECHO + "loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0" +#endif +#ifdef CONFIG_ETHADDR + "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0" +#endif +#ifdef CONFIG_ETH1ADDR + "eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0" +#endif +#ifdef CONFIG_ETH2ADDR + "eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0" +#endif +#ifdef CONFIG_ETHPRIME + "ethprime=" CONFIG_ETHPRIME "\0" +#endif +#ifdef CONFIG_IPADDR + "ipaddr=" MK_STR(CONFIG_IPADDR) "\0" +#endif +#ifdef CONFIG_SERVERIP + "serverip=" MK_STR(CONFIG_SERVERIP) "\0" +#endif +#ifdef CFG_AUTOLOAD + "autoload=" CFG_AUTOLOAD "\0" +#endif +#ifdef CONFIG_ROOTPATH + "rootpath=" MK_STR(CONFIG_ROOTPATH) "\0" +#endif +#ifdef CONFIG_GATEWAYIP + "gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0" +#endif +#ifdef CONFIG_NETMASK + "netmask=" MK_STR(CONFIG_NETMASK) "\0" +#endif +#ifdef CONFIG_HOSTNAME + "hostname=" MK_STR(CONFIG_HOSTNAME) "\0" +#endif +#ifdef CONFIG_BOOTFILE + "bootfile=" MK_STR(CONFIG_BOOTFILE) "\0" +#endif +#ifdef CONFIG_LOADADDR + "loadaddr=" MK_STR(CONFIG_LOADADDR) "\0" +#endif +#ifdef CONFIG_PREBOOT + "preboot=" CONFIG_PREBOOT "\0" +#endif +#ifdef CONFIG_CLOCKS_IN_MHZ + "clocks_in_mhz=" "1" "\0" +#endif +#ifdef CONFIG_EXTRA_ENV_SETTINGS + CONFIG_EXTRA_ENV_SETTINGS +#endif + "\0" /* Term. env_t.data with 2 NULLs */ + } +}; +#ifdef CFG_ENV_ADDR_REDUND +env_t redundand_environment __PPCENV__ = { + 0, /* CRC Sum: invalid */ + 0, /* Flags: invalid */ + { + "\0" + } +}; +#endif /* CFG_ENV_ADDR_REDUND */ + +/* + * These will end up in the .text section + * if the environment strings are embedded + * in the image. When this is used for + * tools/envcrc, they are placed in the + * .data/.sdata section. + * + */ +unsigned long env_size __PPCTEXT__ = sizeof(env_t); + +/* + * Add in absolutes. + */ +GEN_ABS(env_offset, CFG_ENV_OFFSET); + +#endif /* ENV_IS_EMBEDDED */ diff --git a/common/main.c b/common/main.c new file mode 100644 index 0000000..014804b --- /dev/null +++ b/common/main.c @@ -0,0 +1,817 @@ +/* + * (C) Copyright 2000 + * 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 + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <cmd_nvedit.h> +#include <cmd_bootm.h> +#include <malloc.h> +#if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY) +#include <cmd_boot.h> /* for do_reset() prototype */ +#endif + +#ifdef CFG_HUSH_PARSER +#include <hush.h> +#endif + +#define MAX_DELAY_STOP_STR 32 + +static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); +static int parse_line (char *, char *[]); +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +static int abortboot(int); +#endif + +#undef DEBUG_PARSER + +char console_buffer[CFG_CBSIZE]; /* console I/O buffer */ + +static char erase_seq[] = "\b \b"; /* erase sequence */ +static char tab_seq[] = " "; /* used to expand TABs */ + +#ifdef CONFIG_BOOT_RETRY_TIME +static uint64_t endtime = 0; /* must be set, default is instant timeout */ +static int retry_time = -1; /* -1 so can call readline before main_loop */ +#endif + +#define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +#ifdef CONFIG_MODEM_SUPPORT +int do_mdm_init = 0; +extern void mdm_init(void); /* defined in board.c */ +#endif + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop or autoboot delay string. + * returns: 0 - no key string, allow autoboot + * 1 - got key string, abort + */ +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +# if defined(CONFIG_AUTOBOOT_KEYED) +static __inline__ int abortboot(int bootdelay) +{ + int abort = 0; + uint64_t etime = endtick(bootdelay); + struct + { + char* str; + u_int len; + int retry; + } + delaykey [] = + { + { str: getenv ("bootdelaykey"), retry: 1 }, + { str: getenv ("bootdelaykey2"), retry: 1 }, + { str: getenv ("bootstopkey"), retry: 0 }, + { str: getenv ("bootstopkey2"), retry: 0 }, + }; + + char presskey [MAX_DELAY_STOP_STR]; + u_int presskey_len = 0; + u_int presskey_max = 0; + u_int i; + +# ifdef CONFIG_AUTOBOOT_PROMPT + printf (CONFIG_AUTOBOOT_PROMPT, bootdelay); +# endif + +# ifdef CONFIG_AUTOBOOT_DELAY_STR + if (delaykey[0].str == NULL) + delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; +# endif +# ifdef CONFIG_AUTOBOOT_DELAY_STR2 + if (delaykey[1].str == NULL) + delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR + if (delaykey[2].str == NULL) + delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; +# endif +# ifdef CONFIG_AUTOBOOT_STOP_STR2 + if (delaykey[3].str == NULL) + delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; +# endif + + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { + delaykey[i].len = delaykey[i].str == NULL ? + 0 : strlen (delaykey[i].str); + delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? + MAX_DELAY_STOP_STR : delaykey[i].len; + + presskey_max = presskey_max > delaykey[i].len ? + presskey_max : delaykey[i].len; + +# if DEBUG_BOOTKEYS + printf("%s key:<%s>\n", + delaykey[i].retry ? "delay" : "stop", + delaykey[i].str ? delaykey[i].str : "NULL"); +# endif + } + + /* In order to keep up with incoming data, check timeout only + * when catch up. + */ + while (!abort && get_ticks() <= etime) { + for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { + if (delaykey[i].len > 0 && + presskey_len >= delaykey[i].len && + memcmp (presskey + presskey_len - delaykey[i].len, + delaykey[i].str, + delaykey[i].len) == 0) { +# if DEBUG_BOOTKEYS + printf("got %skey\n", + delaykey[i].retry ? "delay" : "stop"); +# endif + +# ifdef CONFIG_BOOT_RETRY_TIME + /* don't retry auto boot */ + if (! delaykey[i].retry) + retry_time = -1; +# endif + abort = 1; + } + } + + if (tstc()) { + if (presskey_len < presskey_max) { + presskey [presskey_len ++] = getc(); + } + else { + for (i = 0; i < presskey_max - 1; i ++) + presskey [i] = presskey [i + 1]; + + presskey [i] = getc(); + } + } + } +# if DEBUG_BOOTKEYS + if (!abort) + printf("key timeout\n"); +# endif + + return abort; +} + +# else /* !defined(CONFIG_AUTOBOOT_KEYED) */ + +static __inline__ int abortboot(int bootdelay) +{ + int abort = 0; + + printf("Hit any key to stop autoboot: %2d ", bootdelay); + +#if defined CONFIG_ZERO_BOOTDELAY_CHECK + /* + * Check if key already pressed + * Don't check if bootdelay < 0 + */ + if (bootdelay >= 0) { + if (tstc()) { /* we got a key press */ + (void) getc(); /* consume input */ + printf ("\b\b\b 0\n"); + return 1; /* don't auto boot */ + } + } +#endif + + while (bootdelay > 0) { + int i; + + --bootdelay; + /* delay 100 * 10ms */ + for (i=0; !abort && i<100; ++i) { + if (tstc()) { /* we got a key press */ + abort = 1; /* don't auto boot */ + bootdelay = 0; /* no more delay */ + (void) getc(); /* consume input */ + break; + } + udelay (10000); + } + + printf ("\b\b\b%2d ", bootdelay); + } + + putc ('\n'); + + return abort; +} +# endif /* CONFIG_AUTOBOOT_KEYED */ +#endif /* CONFIG_BOOTDELAY >= 0 */ + +/****************************************************************************/ + +void main_loop (void) +{ +#ifndef CFG_HUSH_PARSER + static char lastcommand[CFG_CBSIZE] = { 0, }; + int len; + int rc = 1; + int flag; +#endif + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + char *s; + int bootdelay; +#endif +#ifdef CONFIG_PREBOOT + char *p; +#endif + +#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) + ulong bmp = 0; /* default bitmap */ + extern int trab_vfd (ulong bitmap); + +#ifdef CONFIG_MODEM_SUPPORT + if (do_mdm_init) + bmp = 1; /* alternate bitmap */ +#endif + trab_vfd (bmp); +#endif /* CONFIG_VFD && VFD_TEST_LOGO */ + +#ifdef CONFIG_MODEM_SUPPORT + debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); + if (do_mdm_init) { + uchar *str = strdup(getenv("mdm_cmd")); + setenv ("preboot", str); /* set or delete definition */ + if (str != NULL) + free (str); + mdm_init(); /* wait for modem connection */ + } +#endif /* CONFIG_MODEM_SUPPORT */ + +#ifdef CFG_HUSH_PARSER + u_boot_hush_start (); +#endif + +#ifdef CONFIG_PREBOOT + if ((p = getenv ("preboot")) != NULL) { +# ifdef CONFIG_AUTOBOOT_KEYED + int prev = disable_ctrlc(1); /* disable Control C checking */ +# endif + +# ifndef CFG_HUSH_PARSER + run_command (p, 0); +# else + parse_string_outer(p, FLAG_PARSE_SEMICOLON | + FLAG_EXIT_FROM_LOOP); +# endif + +# ifdef CONFIG_AUTOBOOT_KEYED + disable_ctrlc(prev); /* restore Control C checking */ +# endif + } +#endif /* CONFIG_PREBOOT */ + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + s = getenv ("bootdelay"); + bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; + +#if 0 + printf ("### main_loop entered:\n\n"); +#endif + +# ifdef CONFIG_BOOT_RETRY_TIME + s = getenv ("bootretry"); + if (s != NULL) + retry_time = (int)simple_strtoul(s, NULL, 10); + else + retry_time = CONFIG_BOOT_RETRY_TIME; + if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) + retry_time = CONFIG_BOOT_RETRY_MIN; +# endif /* CONFIG_BOOT_RETRY_TIME */ + + s = getenv ("bootcmd"); + if (bootdelay >= 0 && s && !abortboot (bootdelay)) { +# ifdef CONFIG_AUTOBOOT_KEYED + int prev = disable_ctrlc(1); /* disable Control C checking */ +# endif + +# ifndef CFG_HUSH_PARSER + run_command (s, 0); +# else + parse_string_outer(s, FLAG_PARSE_SEMICOLON | + FLAG_EXIT_FROM_LOOP); +# endif + +# ifdef CONFIG_AUTOBOOT_KEYED + disable_ctrlc(prev); /* restore Control C checking */ +# endif + } +#endif /* CONFIG_BOOTDELAY */ + + /* + * Main Loop for Monitor Command Processing + */ +#ifdef CFG_HUSH_PARSER + parse_file_outer(); + /* This point is never reached */ + for (;;); +#else + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + if (rc >= 0) { + /* Saw enough of a valid command to + * restart the timeout. + */ + reset_cmd_timeout(); + } +#endif + len = readline (CFG_PROMPT); + + flag = 0; /* assume no special flags for now */ + if (len > 0) + strcpy (lastcommand, console_buffer); + else if (len == 0) + flag |= CMD_FLAG_REPEAT; +#ifdef CONFIG_BOOT_RETRY_TIME + else if (len == -2) { + /* -2 means timed out, retry autoboot + */ + printf("\nTimed out waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY + /* Reinit board to run initialization code again */ + do_reset (NULL, 0, 0, NULL); +# else + return; /* retry autoboot */ +# endif + } +#endif + + if (len == -1) + printf ("<INTERRUPT>\n"); + else + rc = run_command (lastcommand, flag); + + if (rc <= 0) { + /* invalid command or not repeatable, forget it */ + lastcommand[0] = 0; + } + } +#endif /*CFG_HUSH_PARSER*/ +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +#ifdef CONFIG_BOOT_RETRY_TIME +void reset_cmd_timeout(void) +{ + endtime = endtick(retry_time); +} +#endif + +/****************************************************************************/ + +/* + * Prompt for input and read a line. + * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, + * time out when time goes past endtime (timebase time in ticks). + * Return: number of read characters + * -1 if break + * -2 if timed out + */ +int readline (const char *const prompt) +{ + char *p = console_buffer; + int n = 0; /* buffer index */ + int plen = 0; /* prompt length */ + int col; /* output column cnt */ + char c; + + /* print prompt */ + if (prompt) { + plen = strlen (prompt); + puts (prompt); + } + col = plen; + + for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME + while (!tstc()) { /* while no incoming data */ + if (retry_time >= 0 && get_ticks() > endtime) + return (-2); /* timed out */ + } +#endif + WATCHDOG_RESET(); /* Trigger watchdog, if needed */ + +#ifdef CONFIG_SHOW_ACTIVITY + while (!tstc()) { + extern void show_activity(int arg); + show_activity(0); + } +#endif + c = getc(); + + /* + * Special character handling + */ + switch (c) { + case '\r': /* Enter */ + case '\n': + *p = '\0'; + puts ("\r\n"); + return (p - console_buffer); + + case 0x03: /* ^C - break */ + console_buffer[0] = '\0'; /* discard input */ + return (-1); + + case 0x15: /* ^U - erase line */ + while (col > plen) { + puts (erase_seq); + --col; + } + p = console_buffer; + n = 0; + continue; + + case 0x17: /* ^W - erase word */ + p=delete_char(console_buffer, p, &col, &n, plen); + while ((n > 0) && (*p != ' ')) { + p=delete_char(console_buffer, p, &col, &n, plen); + } + continue; + + case 0x08: /* ^H - backspace */ + case 0x7F: /* DEL - backspace */ + p=delete_char(console_buffer, p, &col, &n, plen); + continue; + + default: + /* + * Must be a normal character then + */ + if (n < CFG_CBSIZE-2) { + if (c == '\t') { /* expand TABs */ + puts (tab_seq+(col&07)); + col += 8 - (col&07); + } else { + ++col; /* echo input */ + putc (c); + } + *p++ = c; + ++n; + } else { /* Buffer full */ + putc ('\a'); + } + } + } +} + +/****************************************************************************/ + +static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ + char *s; + + if (*np == 0) { + return (p); + } + + if (*(--p) == '\t') { /* will retype the whole line */ + while (*colp > plen) { + puts (erase_seq); + (*colp)--; + } + for (s=buffer; s<p; ++s) { + if (*s == '\t') { + puts (tab_seq+((*colp) & 07)); + *colp += 8 - ((*colp) & 07); + } else { + ++(*colp); + putc (*s); + } + } + } else { + puts (erase_seq); + (*colp)--; + } + (*np)--; + return (p); +} + +/****************************************************************************/ + +int parse_line (char *line, char *argv[]) +{ + int nargs = 0; + +#ifdef DEBUG_PARSER + printf ("parse_line: \"%s\"\n", line); +#endif + while (nargs < CFG_MAXARGS) { + + /* skip any white space */ + while ((*line == ' ') || (*line == '\t')) { + ++line; + } + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; +#ifdef DEBUG_PARSER + printf ("parse_line: nargs=%d\n", nargs); +#endif + return (nargs); + } + + argv[nargs++] = line; /* begin of argument string */ + + /* find end of string */ + while (*line && (*line != ' ') && (*line != '\t')) { + ++line; + } + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; +#ifdef DEBUG_PARSER + printf ("parse_line: nargs=%d\n", nargs); +#endif + return (nargs); + } + + *line++ = '\0'; /* terminate current arg */ + } + + printf ("** Too many args (max. %d) **\n", CFG_MAXARGS); + +#ifdef DEBUG_PARSER + printf ("parse_line: nargs=%d\n", nargs); +#endif + return (nargs); +} + +/****************************************************************************/ + +static void process_macros (const char *input, char *output) +{ + char c, prev; + const char *varname_start = NULL; + int inputcnt = strlen (input); + int outputcnt = CFG_CBSIZE; + int state = 0; /* 0 = waiting for '$' */ + /* 1 = waiting for '(' */ + /* 2 = waiting for ')' */ + +#ifdef DEBUG_PARSER + char *output_start = output; + + printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input), input); +#endif + + prev = '\0'; /* previous character */ + + while (inputcnt && outputcnt) { + c = *input++; + inputcnt--; + + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (inputcnt-- == 0) + break; + prev = c; + c = *input++; + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '$') && (prev != '\\')) { + state++; + } else { + *(output++) = c; + outputcnt--; + } + break; + case 1: /* Waiting for ( */ + if (c == '(') { + state++; + varname_start = input; + } else { + state = 0; + *(output++) = '$'; + outputcnt--; + + if (outputcnt) { + *(output++) = c; + outputcnt--; + } + } + break; + case 2: /* Waiting for ) */ + if (c == ')') { + int i; + char envname[CFG_CBSIZE], *envval; + int envcnt = input-varname_start-1; /* Varname # of chars */ + + /* Get the varname */ + for (i = 0; i < envcnt; i++) { + envname[i] = varname_start[i]; + } + envname[i] = 0; + + /* Get its value */ + envval = getenv (envname); + + /* Copy into the line if it exists */ + if (envval != NULL) + while ((*envval) && outputcnt) { + *(output++) = *(envval++); + outputcnt--; + } + /* Look for another '$' */ + state = 0; + } + break; + } + + prev = c; + } + + if (outputcnt) + *output = 0; + +#ifdef DEBUG_PARSER + printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", + strlen(output_start), output_start); +#endif +} + +/**************************************************************************** + * returns: + * 1 - command executed, repeatable + * 0 - command executed but not repeatable, interrupted commands are + * always considered not repeatable + * -1 - not executed (unrecognized, bootd recursion or too many args) + * (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is + * considered unrecognized) + * + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from getenv(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ + +int run_command (const char *cmd, int flag) +{ + cmd_tbl_t *cmdtp; + char cmdbuf[CFG_CBSIZE]; /* working copy of cmd */ + char *token; /* start of token in cmdbuf */ + char *sep; /* end of token (separator) in cmdbuf */ + char finaltoken[CFG_CBSIZE]; + char *str = cmdbuf; + char *argv[CFG_MAXARGS + 1]; /* NULL terminated */ + int argc; + int repeatable = 1; + +#ifdef DEBUG_PARSER + printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); + puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */ + puts ("\"\n"); +#endif + + clear_ctrlc(); /* forget any previous Control C */ + + if (!cmd || !*cmd) { + return -1; /* empty command */ + } + + if (strlen(cmd) >= CFG_CBSIZE) { + puts ("## Command too long!\n"); + return -1; + } + + strcpy (cmdbuf, cmd); + + /* Process separators and check for invalid + * repeatable commands + */ + +#ifdef DEBUG_PARSER + printf ("[PROCESS_SEPARATORS] %s\n", cmd); +#endif + while (*str) { + + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (sep = str; *sep; sep++) { + if ((*sep == ';') && /* separator */ + ( sep != str) && /* past string start */ + (*(sep-1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } + else + str = sep; /* no more commands for next pass */ +#ifdef DEBUG_PARSER + printf ("token: \"%s\"\n", token); +#endif + + /* find macros in this token and replace them */ + process_macros (token, finaltoken); + + /* Extract arguments */ + argc = parse_line (finaltoken, argv); + + /* Look up command in command table */ + if ((cmdtp = find_cmd(argv[0])) == NULL) { + printf ("Unknown command '%s' - try 'help'\n", argv[0]); + return -1; /* give up after bad command */ + } + + /* found - check max args */ + if (argc > cmdtp->maxargs) { + printf ("Usage:\n%s\n", cmdtp->usage); + return -1; + } + +#if (CONFIG_COMMANDS & CFG_CMD_BOOTD) + /* avoid "bootd" recursion */ + if (cmdtp->cmd == do_bootd) { +#ifdef DEBUG_PARSER + printf ("[%s]\n", finaltoken); +#endif + if (flag & CMD_FLAG_BOOTD) { + printf ("'bootd' recursion detected\n"); + return -1; + } + else + flag |= CMD_FLAG_BOOTD; + } +#endif /* CFG_CMD_BOOTD */ + + /* OK - call function to do the command */ + if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { + return (-1); + } + + repeatable &= cmdtp->repeatable; + + /* Did the user stop this? */ + if (had_ctrlc ()) + return 0; /* if stopped then not repeatable */ + } + + return repeatable; +} + +/****************************************************************************/ + +#if (CONFIG_COMMANDS & CFG_CMD_RUN) +int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + int i; + int rcode = 1; + + if (argc < 2) { + printf ("Usage:\n%s\n", cmdtp->usage); + return 1; + } + + for (i=1; i<argc; ++i) { +#ifndef CFG_HUSH_PARSER + if (run_command (getenv (argv[i]), flag) != -1) ++rcode; +#else + if (parse_string_outer(getenv (argv[i]), + FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) == 0) ++rcode; +#endif + } + return ((rcode == i) ? 0 : 1); +} +#endif diff --git a/common/miiphyutil.c b/common/miiphyutil.c new file mode 100644 index 0000000..6b2425f --- /dev/null +++ b/common/miiphyutil.c @@ -0,0 +1,172 @@ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * 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 + */ + +/* + * This provides a bit-banged interface to the ethernet MII management + * channel. + */ + +#include <common.h> +#include <miiphy.h> + +#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) + +/***************************************************************************** + * + * Read the OUI, manufacture's model number, and revision number. + * + * OUI: 22 bits (unsigned int) + * Model: 6 bits (unsigned char) + * Revision: 4 bits (unsigned char) + * + * Returns: + * 0 on success + */ +int miiphy_info (unsigned char addr, + unsigned int *oui, + unsigned char *model, unsigned char *rev) +{ + unsigned int reg = 0; + + /* + * Trick: we are reading two 16 registers into a 32 bit variable + * so we do a 16 read into the high order bits of the variable (big + * endian, you know), shift it down 16 bits, and the read the rest. + */ + if (miiphy_read (addr, PHY_PHYIDR2, (unsigned short *) ®) != 0) { +#ifdef DEBUG + printf ("PHY ID register 2 read failed\n"); +#endif + return (-1); + } + reg >>= 16; + +#ifdef DEBUG + printf ("PHY_PHYIDR2 @ 0x%x = 0x%04x\n", addr, reg); +#endif + if (reg == 0xFFFF) { + /* No physical device present at this address */ + return (-1); + } + + if (miiphy_read (addr, PHY_PHYIDR1, (unsigned short *) ®) != 0) { +#ifdef DEBUG + printf ("PHY ID register 1 read failed\n"); +#endif + return (-1); + } +#ifdef DEBUG + printf ("PHY_PHYIDR[1,2] @ 0x%x = 0x%08x\n", addr, reg); +#endif + *oui = ( reg >> 10); + *model = (unsigned char) ((reg >> 4) & 0x0000003F); + *rev = (unsigned char) ( reg & 0x0000000F); + return (0); +} + + +/***************************************************************************** + * + * Reset the PHY. + * Returns: + * 0 on success + */ +int miiphy_reset (unsigned char addr) +{ + unsigned short reg; + int loop_cnt; + + if (miiphy_write (addr, PHY_BMCR, 0x8000) != 0) { +#ifdef DEBUG + printf ("PHY reset failed\n"); +#endif + return (-1); + } + + /* + * Poll the control register for the reset bit to go to 0 (it is + * auto-clearing). This should happen within 0.5 seconds per the + * IEEE spec. + */ + loop_cnt = 0; + reg = 0x8000; + while (((reg & 0x8000) != 0) && (loop_cnt++ < 1000000)) { + if (miiphy_read (addr, PHY_BMCR, ®) != 0) { +# ifdef DEBUG + printf ("PHY status read failed\n"); +# endif + return (-1); + } + } + if ((reg & 0x8000) == 0) { + return (0); + } else { + printf ("PHY reset timed out\n"); + return (-1); + } + return (0); +} + + +/***************************************************************************** + * + * Determine the ethernet speed (10/100). + */ +int miiphy_speed (unsigned char addr) +{ + unsigned short reg; + + if (miiphy_read (addr, PHY_ANLPAR, ®)) { + printf ("PHY speed1 read failed, assuming 10bT\n"); + return (_10BASET); + } + + if ((reg & PHY_ANLPAR_100) != 0) { + return (_100BASET); + } else { + return (_10BASET); + } +} + + +/***************************************************************************** + * + * Determine full/half duplex. + */ +int miiphy_duplex (unsigned char addr) +{ + unsigned short reg; + + if (miiphy_read (addr, PHY_ANLPAR, ®)) { + printf ("PHY duplex read failed, assuming half duplex\n"); + return (HALF); + } + + if ((reg & (PHY_ANLPAR_10FD | PHY_ANLPAR_TXFD)) != 0) { + return (FULL); + } else { + return (HALF); + } +} + +#endif /* CONFIG_MII || (CONFIG_COMMANDS & CFG_CMD_MII) */ diff --git a/common/soft_i2c.c b/common/soft_i2c.c new file mode 100644 index 0000000..7777480 --- /dev/null +++ b/common/soft_i2c.c @@ -0,0 +1,416 @@ +/* + * (C) Copyright 2001, 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 + * + * This has been changed substantially by Gerald Van Baren, Custom IDEAS, + * vanbaren@cideas.com. It was heavily influenced by LiMon, written by + * Neil Russell. + */ + +#include <common.h> +#ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ +#include <ioports.h> +#endif +#include <i2c.h> + +#if defined(CONFIG_SOFT_I2C) + +/* #define DEBUG_I2C */ + + +/*----------------------------------------------------------------------- + * Definitions + */ + +#define RETRIES 0 + + +#define I2C_ACK 0 /* PD_SDA level to ack a byte */ +#define I2C_NOACK 1 /* PD_SDA level to noack a byte */ + + +#ifdef DEBUG_I2C +#define PRINTD(fmt,args...) do { \ + DECLARE_GLOBAL_DATA_PTR; \ + if (gd->have_console) \ + printf (fmt ,##args); \ + } while (0) +#else +#define PRINTD(fmt,args...) +#endif + +/*----------------------------------------------------------------------- + * Local functions + */ +static void send_reset (void); +static void send_start (void); +static void send_stop (void); +static void send_ack (int); +static int write_byte (uchar byte); +static uchar read_byte (int); + + +/*----------------------------------------------------------------------- + * Send a reset sequence consisting of 9 clocks with the data signal high + * to clock any confused device back into an idle state. Also send a + * <stop> at the end of the sequence for belts & suspenders. + */ +static void send_reset(void) +{ +#ifdef CONFIG_MPC8260 + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT); +#endif +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + int j; + + I2C_ACTIVE; + I2C_SDA(1); + for(j = 0; j < 9; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + } + send_stop(); + I2C_TRISTATE; +} + +/*----------------------------------------------------------------------- + * START: High -> Low on SDA while SCL is High + */ +static void send_start(void) +{ +#ifdef CONFIG_MPC8260 + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT); +#endif +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + + I2C_DELAY; + I2C_SDA(1); + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_SDA(0); + I2C_DELAY; +} + +/*----------------------------------------------------------------------- + * STOP: Low -> High on SDA while SCL is High + */ +static void send_stop(void) +{ +#ifdef CONFIG_MPC8260 + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT); +#endif +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(0); + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_SDA(1); + I2C_DELAY; + I2C_TRISTATE; +} + + +/*----------------------------------------------------------------------- + * ack should be I2C_ACK or I2C_NOACK + */ +static void send_ack(int ack) +{ +#ifdef CONFIG_MPC8260 + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT); +#endif +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + + I2C_ACTIVE; + I2C_SCL(0); + I2C_DELAY; + + I2C_SDA(ack); + + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + I2C_SCL(0); + I2C_DELAY; +} + + +/*----------------------------------------------------------------------- + * Send 8 bits and look for an acknowledgement. + */ +static int write_byte(uchar data) +{ +#ifdef CONFIG_MPC8260 + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT); +#endif +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + int j; + int nack; + + I2C_ACTIVE; + for(j = 0; j < 8; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(data & 0x80); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + + data <<= 1; + } + + /* + * Look for an <ACK>(negative logic) and return it. + */ + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(1); + I2C_TRISTATE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + nack = I2C_READ; + I2C_SCL(0); + I2C_DELAY; + I2C_ACTIVE; + + return(nack); /* not a nack is an ack */ +} + + +/*----------------------------------------------------------------------- + * if ack == I2C_ACK, ACK the byte so can continue reading, else + * send I2C_NOACK to end the read. + */ +static uchar read_byte(int ack) +{ +#ifdef CONFIG_MPC8260 + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT); +#endif +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + int data; + int j; + + /* + * Read 8 bits, MSB first. + */ + I2C_TRISTATE; + data = 0; + for(j = 0; j < 8; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + data <<= 1; + data |= I2C_READ; + I2C_DELAY; + } + send_ack(ack); + + return(data); +} + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void i2c_init (int speed, int slaveaddr) +{ +#ifdef CONFIG_8xx + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + +#ifdef I2C_INIT + I2C_INIT; +#endif + /* + * WARNING: Do NOT save speed in a static variable: if the + * I2C routines are called before RAM is initialized (to read + * the DIMM SPD, for instance), RAM won't be usable and your + * system will crash. + */ + send_reset (); +} + +/*----------------------------------------------------------------------- + * Probe to see if a chip is present. Also good for checking for the + * completion of EEPROM writes since the chip stops responding until + * the write completes (typically 10mSec). + */ +int i2c_probe(uchar addr) +{ + int rc; + + send_start(); + rc = write_byte ((addr << 1) | 1); + send_stop(); + + return (rc ? 1 : 0); +} + +/*----------------------------------------------------------------------- + * Read bytes + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int shift; + PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); + + PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n", + chip, addr); +#endif + + /* + * Do the addressing portion of a write cycle to set the + * chip's address pointer. If the address length is zero, + * don't do the normal write cycle to set the address pointer, + * there is no address pointer in this chip. + */ + send_start(); + if(alen > 0) { + if(write_byte(chip << 1)) { /* write cycle */ + send_stop(); + PRINTD("i2c_read, no chip responded %02X\n", chip); + return(1); + } + shift = (alen-1) * 8; + while(alen-- > 0) { + if(write_byte(addr >> shift)) { + PRINTD("i2c_read, address not <ACK>ed\n"); + return(1); + } + shift -= 8; + } + send_stop(); /* reportedly some chips need a full stop */ + send_start(); + } + /* + * Send the chip address again, this time for a read cycle. + * Then read the data. On the last byte, we do a NACK instead + * of an ACK(len == 0) to terminate the read. + */ + write_byte((chip << 1) | 1); /* read cycle */ + while(len-- > 0) { + *buffer++ = read_byte(len == 0); + } + send_stop(); + return(0); +} + +/*----------------------------------------------------------------------- + * Write bytes + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int shift, failures = 0; + + PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + + send_start(); + if(write_byte(chip << 1)) { /* write cycle */ + send_stop(); + PRINTD("i2c_write, no chip responded %02X\n", chip); + return(1); + } + shift = (alen-1) * 8; + while(alen-- > 0) { + if(write_byte(addr >> shift)) { + PRINTD("i2c_write, address not <ACK>ed\n"); + return(1); + } + shift -= 8; + } + + while(len-- > 0) { + if(write_byte(*buffer++)) { + failures++; + } + } + send_stop(); + return(failures); +} + +/*----------------------------------------------------------------------- + * Read a register + */ +uchar i2c_reg_read(uchar i2c_addr, uchar reg) +{ + char buf; + + i2c_read(i2c_addr, reg, 1, &buf, 1); + + return(buf); +} + +/*----------------------------------------------------------------------- + * Write a register + */ +void i2c_reg_write(uchar i2c_addr, uchar reg, uchar val) +{ + i2c_write(i2c_addr, reg, 1, &val, 1); +} + + +#endif /* CONFIG_SOFT_I2C */ |