diff options
Diffstat (limited to 'drivers')
27 files changed, 5228 insertions, 608 deletions
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 68ab55f8a5f..aaca81b01da 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -32,7 +32,7 @@ */ /* The DEBUG define must be before common to enable debugging */ -/* #define DEBUG */ +/* #define DEBUG */ #include <common.h> #include <asm/processor.h> @@ -162,6 +162,10 @@ static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST; flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* FLASH chips info */ #endif +#ifdef CFG_FLASH_SPL_ACCESS +void board_flash_set_access(ulong bank_base, int banknum, flash_info_t* flash_info); +#endif + /* * Check if chip width is defined. If not, start detecting with 8bit. */ @@ -358,9 +362,9 @@ static inline uchar flash_read_uchar (flash_info_t * info, uint offset) cp = flash_map (info, 0, offset); #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - retval = flash_read8(cp); + retval = info->read8(cp); #else - retval = flash_read8(cp + info->portwidth - 1); + retval = info->read8(cp + info->portwidth - 1); #endif flash_unmap (info, 0, offset, cp); return retval; @@ -374,7 +378,7 @@ static inline ushort flash_read_word (flash_info_t * info, uint offset) ushort *addr, retval; addr = flash_map (info, 0, offset); - retval = flash_read16 (addr); + retval = info->read16 (addr); flash_unmap (info, 0, offset, addr); return retval; } @@ -399,19 +403,19 @@ static ulong flash_read_long (flash_info_t * info, flash_sect_t sect, debug ("long addr is at %p info->portwidth = %d\n", addr, info->portwidth); for (x = 0; x < 4 * info->portwidth; x++) { - debug ("addr[%x] = 0x%x\n", x, flash_read8(addr + x)); + debug ("addr[%x] = 0x%x\n", x, info->read8(addr + x)); } #endif #if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - retval = ((flash_read8(addr) << 16) | - (flash_read8(addr + info->portwidth) << 24) | - (flash_read8(addr + 2 * info->portwidth)) | - (flash_read8(addr + 3 * info->portwidth) << 8)); + retval = ((info->read8(addr) << 16) | + (info->read8(addr + info->portwidth) << 24) | + (info->read8(addr + 2 * info->portwidth)) | + (info->read8(addr + 3 * info->portwidth) << 8)); #else - retval = ((flash_read8(addr + 2 * info->portwidth - 1) << 24) | - (flash_read8(addr + info->portwidth - 1) << 16) | - (flash_read8(addr + 4 * info->portwidth - 1) << 8) | - (flash_read8(addr + 3 * info->portwidth - 1))); + retval = ((info->read8(addr + 2 * info->portwidth - 1) << 24) | + (info->read8(addr + info->portwidth - 1) << 16) | + (info->read8(addr + 4 * info->portwidth - 1) << 8) | + (info->read8(addr + 3 * info->portwidth - 1))); #endif flash_unmap(info, sect, offset, addr); @@ -434,19 +438,19 @@ static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, case FLASH_CFI_8BIT: debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd, cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - flash_write8(cword.c, addr); + info->write8(cword.c, addr); break; case FLASH_CFI_16BIT: debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr, cmd, cword.w, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - flash_write16(cword.w, addr); + info->write16(cword.w, addr); break; case FLASH_CFI_32BIT: debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr, cmd, cword.l, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - flash_write32(cword.l, addr); + info->write32(cword.l, addr); break; case FLASH_CFI_64BIT: #ifdef DEBUG @@ -460,7 +464,7 @@ static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); } #endif - flash_write64(cword.ll, addr); + info->write64(cword.ll, addr); break; } @@ -491,16 +495,16 @@ static int flash_isequal (flash_info_t * info, flash_sect_t sect, debug ("is= cmd %x(%c) addr %p ", cmd, cmd, addr); switch (info->portwidth) { case FLASH_CFI_8BIT: - debug ("is= %x %x\n", flash_read8(addr), cword.c); - retval = (flash_read8(addr) == cword.c); + debug ("is= %x %x\n", info->read8(addr), cword.c); + retval = (info->read8(addr) == cword.c); break; case FLASH_CFI_16BIT: - debug ("is= %4.4x %4.4x\n", flash_read16(addr), cword.w); - retval = (flash_read16(addr) == cword.w); + debug ("is= %4.4x %4.4x\n", info->read16(addr), cword.w); + retval = (info->read16(addr) == cword.w); break; case FLASH_CFI_32BIT: - debug ("is= %8.8lx %8.8lx\n", flash_read32(addr), cword.l); - retval = (flash_read32(addr) == cword.l); + debug ("is= %8.8lx %8.8lx\n", info->read32(addr), cword.l); + retval = (info->read32(addr) == cword.l); break; case FLASH_CFI_64BIT: #ifdef DEBUG @@ -508,12 +512,12 @@ static int flash_isequal (flash_info_t * info, flash_sect_t sect, char str1[20]; char str2[20]; - print_longlong (str1, flash_read64(addr)); + print_longlong (str1, info->read64(addr)); print_longlong (str2, cword.ll); debug ("is= %s %s\n", str1, str2); } #endif - retval = (flash_read64(addr) == cword.ll); + retval = (info->read64(addr) == cword.ll); break; default: retval = 0; @@ -537,16 +541,16 @@ static int flash_isset (flash_info_t * info, flash_sect_t sect, flash_make_cmd (info, cmd, &cword); switch (info->portwidth) { case FLASH_CFI_8BIT: - retval = ((flash_read8(addr) & cword.c) == cword.c); + retval = ((info->read8(addr) & cword.c) == cword.c); break; case FLASH_CFI_16BIT: - retval = ((flash_read16(addr) & cword.w) == cword.w); + retval = ((info->read16(addr) & cword.w) == cword.w); break; case FLASH_CFI_32BIT: - retval = ((flash_read32(addr) & cword.l) == cword.l); + retval = ((info->read32(addr) & cword.l) == cword.l); break; case FLASH_CFI_64BIT: - retval = ((flash_read64(addr) & cword.ll) == cword.ll); + retval = ((info->read64(addr) & cword.ll) == cword.ll); break; default: retval = 0; @@ -570,20 +574,20 @@ static int flash_toggle (flash_info_t * info, flash_sect_t sect, flash_make_cmd (info, cmd, &cword); switch (info->portwidth) { case FLASH_CFI_8BIT: - retval = ((flash_read8(addr) & cword.c) != - (flash_read8(addr) & cword.c)); + retval = ((info->read8(addr) & cword.c) != + (info->read8(addr) & cword.c)); break; case FLASH_CFI_16BIT: - retval = ((flash_read16(addr) & cword.w) != - (flash_read16(addr) & cword.w)); + retval = ((info->read16(addr) & cword.w) != + (info->read16(addr) & cword.w)); break; case FLASH_CFI_32BIT: - retval = ((flash_read32(addr) & cword.l) != - (flash_read32(addr) & cword.l)); + retval = ((info->read32(addr) & cword.l) != + (info->read32(addr) & cword.l)); break; case FLASH_CFI_64BIT: - retval = ((flash_read64(addr) & cword.ll) != - (flash_read64(addr) & cword.ll)); + retval = ((info->read64(addr) & cword.ll) != + (info->read64(addr) & cword.ll)); break; default: retval = 0; @@ -768,16 +772,16 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest, /* Check if Flash is (sufficiently) erased */ switch (info->portwidth) { case FLASH_CFI_8BIT: - flag = ((flash_read8(dstaddr) & cword.c) == cword.c); + flag = ((info->read8(dstaddr) & cword.c) == cword.c); break; case FLASH_CFI_16BIT: - flag = ((flash_read16(dstaddr) & cword.w) == cword.w); + flag = ((info->read16(dstaddr) & cword.w) == cword.w); break; case FLASH_CFI_32BIT: - flag = ((flash_read32(dstaddr) & cword.l) == cword.l); + flag = ((info->read32(dstaddr) & cword.l) == cword.l); break; case FLASH_CFI_64BIT: - flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll); + flag = ((info->read64(dstaddr) & cword.ll) == cword.ll); break; default: flag = 0; @@ -809,16 +813,16 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest, switch (info->portwidth) { case FLASH_CFI_8BIT: - flash_write8(cword.c, dstaddr); + info->write8(cword.c, dstaddr); break; case FLASH_CFI_16BIT: - flash_write16(cword.w, dstaddr); + info->write16(cword.w, dstaddr); break; case FLASH_CFI_32BIT: - flash_write32(cword.l, dstaddr); + info->write32(cword.l, dstaddr); break; case FLASH_CFI_64BIT: - flash_write64(cword.ll, dstaddr); + info->write64(cword.ll, dstaddr); break; } @@ -870,23 +874,23 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, while ((cnt-- > 0) && (flag == 0)) { switch (info->portwidth) { case FLASH_CFI_8BIT: - flag = ((flash_read8(dst2) & flash_read8(src)) == - flash_read8(src)); + flag = ((info->read8(dst2) & info->read8(src)) == + info->read8(src)); src += 1, dst2 += 1; break; case FLASH_CFI_16BIT: - flag = ((flash_read16(dst2) & flash_read16(src)) == - flash_read16(src)); + flag = ((info->read16(dst2) & info->read16(src)) == + info->read16(src)); src += 2, dst2 += 2; break; case FLASH_CFI_32BIT: - flag = ((flash_read32(dst2) & flash_read32(src)) == - flash_read32(src)); + flag = ((info->read32(dst2) & info->read32(src)) == + info->read32(src)); src += 4, dst2 += 4; break; case FLASH_CFI_64BIT: - flag = ((flash_read64(dst2) & flash_read64(src)) == - flash_read64(src)); + flag = ((info->read64(dst2) & info->read64(src)) == + info->read64(src)); src += 8, dst2 += 8; break; } @@ -915,19 +919,19 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, while (cnt-- > 0) { switch (info->portwidth) { case FLASH_CFI_8BIT: - flash_write8(flash_read8(src), dst); + info->write8(info->read8(src), dst); src += 1, dst += 1; break; case FLASH_CFI_16BIT: - flash_write16(flash_read16(src), dst); + info->write16(info->read16(src), dst); src += 2, dst += 2; break; case FLASH_CFI_32BIT: - flash_write32(flash_read32(src), dst); + info->write32(info->read32(src), dst); src += 4, dst += 4; break; case FLASH_CFI_64BIT: - flash_write64(flash_read64(src), dst); + info->write64(info->read64(src), dst); src += 8, dst += 8; break; default: @@ -958,25 +962,25 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, switch (info->portwidth) { case FLASH_CFI_8BIT: while (cnt-- > 0) { - flash_write8(flash_read8(src), dst); + info->write8(info->read8(src), dst); src += 1, dst += 1; } break; case FLASH_CFI_16BIT: while (cnt-- > 0) { - flash_write16(flash_read16(src), dst); + info->write16(info->read16(src), dst); src += 2, dst += 2; } break; case FLASH_CFI_32BIT: while (cnt-- > 0) { - flash_write32(flash_read32(src), dst); + info->write32(info->read32(src), dst); src += 4, dst += 4; } break; case FLASH_CFI_64BIT: while (cnt-- > 0) { - flash_write64(flash_read64(src), dst); + info->write64(info->read64(src), dst); src += 8, dst += 8; } break; @@ -1240,14 +1244,14 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) cword.l = 0; p = map_physmem(wp, info->portwidth, MAP_NOCACHE); for (i = 0; i < aln; ++i) - flash_add_byte (info, &cword, flash_read8(p + i)); + flash_add_byte (info, &cword, info->read8(p + i)); for (; (i < info->portwidth) && (cnt > 0); i++) { flash_add_byte (info, &cword, *src++); cnt--; } for (; (cnt == 0) && (i < info->portwidth); ++i) - flash_add_byte (info, &cword, flash_read8(p + i)); + flash_add_byte (info, &cword, info->read8(p + i)); rc = flash_write_cfiword (info, wp, cword); unmap_physmem(p, info->portwidth); @@ -1315,7 +1319,7 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) --cnt; } for (; i < info->portwidth; ++i) - flash_add_byte (info, &cword, flash_read8(p + i)); + flash_add_byte (info, &cword, info->read8(p + i)); unmap_physmem(p, info->portwidth); return flash_write_cfiword (info, wp, cword); @@ -1888,6 +1892,29 @@ unsigned long flash_init (void) for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; + /* flash read and write routines board specific attention */ +#ifdef CFG_FLASH_SPL_ACCESS + board_flash_set_access(bank_base[i], i, &flash_info[i]); +#endif + + if(!flash_info[i].write8) + flash_info[i].write8 = flash_write8; + if(!flash_info[i].write16) + flash_info[i].write16 = flash_write16; + if(!flash_info[i].write32) + flash_info[i].write32 = flash_write32; + if(!flash_info[i].write64) + flash_info[i].write64 = flash_write64; + + if(!flash_info[i].read8) + flash_info[i].read8 = flash_read8; + if(!flash_info[i].read16) + flash_info[i].read16 = flash_read16; + if(!flash_info[i].read32) + flash_info[i].read32 = flash_read32; + if(!flash_info[i].read64) + flash_info[i].read64 = flash_read64; + if (!flash_detect_legacy (bank_base[i], i)) flash_get_size (bank_base[i], i); size += flash_info[i].size; @@ -1947,6 +1974,7 @@ unsigned long flash_init (void) } } #endif /* CFG_FLASH_PROTECTION */ + } /* Monitor protection ON by default */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2da1d4621c8..1a9cd65473c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -5,14 +5,14 @@ * This is the generic MTD driver for NAND flash devices. It should be * capable of working with almost all NAND chips currently available. * Basic support for AG-AND chips is provided. - * + * * Additional technical information is available on * http://www.linux-mtd.infradead.org/tech/nand.html - * + * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 2002 Thomas Gleixner (tglx@linutronix.de) * - * 02-08-2004 tglx: support for strange chips, which cannot auto increment + * 02-08-2004 tglx: support for strange chips, which cannot auto increment * pages on read / read_oob * * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes @@ -21,16 +21,38 @@ * Make reads over block boundaries work too * * 04-14-2004 tglx: first working version for 2k page size chips - * + * * 05-19-2004 tglx: Basic support for Renesas AG-AND chips * * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared * among multiple independend devices. Suggestions and initial patch * from Ben Dooks <ben-mtd@fluff.org> * - * Credits: - * David Woodhouse for adding multichip support + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. + * Basically, any block not rewritten may lose data when surrounding blocks + * are rewritten many times. JFFS2 ensures this doesn't happen for blocks + * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they + * do not lose data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby block (which + * could itself go bad), use a page address 'mask' to select several blocks + * in the same area, and rewrite the BBT when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas + * AG-AND chips. If there was a sudden loss of power during an erase operation, + * a "device recovery" operation must be performed when power is restored + * to ensure correct operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to + * perform extra error status checks on erase and write failures. This required + * adding a wrapper function for nand_read_ecc. + * + * 08-20-2005 vwool: suspend/resume added + * + * 11-01-2005: vwool: NAND page layouts introduces for HW ECC handling * + * Credits: + * David Woodhouse for adding multichip support + * * Aleph One Ltd. and Toby Churchill Ltd. for supporting the * rework for 2K page size chips * @@ -41,7 +63,7 @@ * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * - * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen Exp $ + * $Id: nand_base.c,v 1.145 2005/05/31 20:32:53 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -107,23 +129,15 @@ static struct nand_oobinfo nand_oob_64 = { .useecc = MTD_NANDECC_AUTOPLACE, .eccbytes = 24, .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, .oobfree = { {2, 38} } }; -/* This is used for padding purposes in nand_write_oob */ -static u_char ffchars[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; +/* This is used for padding purposes in nand_write_oob/nand_write_oob_hwecc */ +#define FFCHARS_SIZE 2048 +static u_char ffchars[FFCHARS_SIZE]; /* * NAND low-level MTD interface functions @@ -154,19 +168,19 @@ static void nand_sync (struct mtd_info *mtd); static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel, int mode); #ifdef CONFIG_MTD_NAND_VERIFY_WRITE -static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); #else #define nand_verify_pages(...) (0) #endif - + static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure - * - * Deselect, release chip lock and wake up anyone waiting on the device + * + * Deselect, release chip lock and wake up anyone waiting on the device */ /* XXX U-BOOT XXX */ #if 0 @@ -176,11 +190,20 @@ static void nand_release_device (struct mtd_info *mtd) /* De-select the NAND device */ this->select_chip(mtd, -1); - /* Do we have a hardware controller ? */ + if (this->controller) { + /* Release the controller and the chip */ spin_lock(&this->controller->lock); this->controller->active = NULL; + this->state = FL_READY; + wake_up(&this->controller->wq); spin_unlock(&this->controller->lock); + } else { + /* Release the chip */ + spin_lock(&this->chip_lock); + this->state = FL_READY; + wake_up(&this->wq); + spin_unlock(&this->chip_lock); } /* Release the chip */ spin_lock (&this->chip_lock); @@ -225,7 +248,7 @@ static void nand_write_byte(struct mtd_info *mtd, u_char byte) * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip * @mtd: MTD device structure * - * Default read function for 16bit buswith with + * Default read function for 16bit buswith with * endianess conversion */ static u_char nand_read_byte16(struct mtd_info *mtd) @@ -252,7 +275,7 @@ static void nand_write_byte16(struct mtd_info *mtd, u_char byte) * nand_read_word - [DEFAULT] read one word from the chip * @mtd: MTD device structure * - * Default read function for 16bit buswith without + * Default read function for 16bit buswith without * endianess conversion */ static u16 nand_read_word(struct mtd_info *mtd) @@ -266,7 +289,7 @@ static u16 nand_read_word(struct mtd_info *mtd) * @mtd: MTD device structure * @word: data word to write * - * Default write function for 16bit buswith without + * Default write function for 16bit buswith without * endianess conversion */ static void nand_write_word(struct mtd_info *mtd, u16 word) @@ -287,7 +310,7 @@ static void nand_select_chip(struct mtd_info *mtd, int chip) struct nand_chip *this = mtd->priv; switch(chip) { case -1: - this->hwcontrol(mtd, NAND_CTL_CLRNCE); + this->hwcontrol(mtd, NAND_CTL_CLRNCE); break; case 0: this->hwcontrol(mtd, NAND_CTL_SETNCE); @@ -316,7 +339,7 @@ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) } /** - * nand_read_buf - [DEFAULT] read chip data into buffer + * nand_read_buf - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read @@ -333,7 +356,7 @@ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) } /** - * nand_verify_buf - [DEFAULT] Verify chip data against buffer + * nand_verify_buf - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare @@ -366,14 +389,14 @@ static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) struct nand_chip *this = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; - + for (i=0; i<len; i++) writew(p[i], this->IO_ADDR_W); - + } /** - * nand_read_buf16 - [DEFAULT] read chip data into buffer + * nand_read_buf16 - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read @@ -392,7 +415,7 @@ static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) } /** - * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare @@ -419,17 +442,16 @@ static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) * @ofs: offset from device start * @getchip: 0, if the chip is already selected * - * Check, if the block is bad. + * Check, if the block is bad. */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { int page, chipnr, res = 0; struct nand_chip *this = mtd->priv; u16 bad; - - page = (int)(ofs >> this->page_shift) & this->pagemask; - + if (getchip) { + page = (int)(ofs >> this->page_shift); chipnr = (int)(ofs >> this->chip_shift); /* Grab the lock and see if the device is available */ @@ -437,25 +459,26 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) /* Select the NAND device */ this->select_chip(mtd, chipnr); - } + } else + page = (int) ofs; if (this->options & NAND_BUSWIDTH_16) { - this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page); + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); bad = cpu_to_le16(this->read_word(mtd)); if (this->badblockpos & 0x1) - bad >>= 1; + bad >>= 8; if ((bad & 0xFF) != 0xff) res = 1; } else { - this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page); + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); if (this->read_byte(mtd) != 0xff) res = 1; } - + if (getchip) { /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); - } + } return res; } @@ -474,33 +497,34 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) u_char buf[2] = {0, 0}; size_t retlen; int block; - + /* Get block number */ block = ((int) ofs) >> this->bbt_erase_shift; - this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + if (this->bbt) + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ if (this->options & NAND_USE_FLASH_BBT) return nand_update_bbt (mtd, ofs); - + /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (this->badblockpos & ~0x01); return nand_write_oob (mtd, ofs , 2, &retlen, buf); } -/** +/** * nand_check_wp - [GENERIC] check if the chip is write protected * @mtd: MTD device structure - * Check, if the device is write protected + * Check, if the device is write protected * - * The function expects, that the device is already selected + * The function expects, that the device is already selected */ static int nand_check_wp (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); - return (this->read_byte(mtd) & 0x80) ? 0 : 1; + return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } /** @@ -516,10 +540,10 @@ static int nand_check_wp (struct mtd_info *mtd) static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) { struct nand_chip *this = mtd->priv; - + if (!this->bbt) return this->block_bad(mtd, ofs, getchip); - + /* Return info from the table */ return nand_isbad_bbt (mtd, ofs, allowbbt); } @@ -584,13 +608,13 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in /* Latch in address */ this->hwcontrol(mtd, NAND_CTL_CLRALE); } - - /* - * program and erase have their own busy handlers + + /* + * program and erase have their own busy handlers * status and sequential in needs no delay */ switch (command) { - + case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: @@ -599,27 +623,26 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in return; case NAND_CMD_RESET: - if (this->dev_ready) + if (this->dev_ready) break; udelay(this->chip_delay); this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; - /* This applies to read commands */ + /* This applies to read commands */ default: - /* + /* * If we don't have access to the busy pin, we apply the given * command delay */ if (!this->dev_ready) { udelay (this->chip_delay); return; - } + } } - /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); @@ -648,12 +671,12 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, column += mtd->oobblock; command = NAND_CMD_READ0; } - - + + /* Begin command latch cycle */ this->hwcontrol(mtd, NAND_CTL_SETCLE); /* Write out the command to the device. */ - this->write_byte(mtd, command); + this->write_byte(mtd, (command & 0xff)); /* End command latch cycle */ this->hwcontrol(mtd, NAND_CTL_CLRCLE); @@ -667,7 +690,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, column >>= 1; this->write_byte(mtd, column & 0xff); this->write_byte(mtd, column >> 8); - } + } if (page_addr != -1) { this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); @@ -678,30 +701,41 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, /* Latch in address */ this->hwcontrol(mtd, NAND_CTL_CLRALE); } - - /* - * program and erase have their own busy handlers - * status and sequential in needs no delay - */ + + /* + * program and erase have their own busy handlers + * status, sequential in, and deplete1 need no delay + */ switch (command) { - + case NAND_CMD_CACHEDPROG: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: return; + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(this->chip_delay); + return; case NAND_CMD_RESET: - if (this->dev_ready) + if (this->dev_ready) break; udelay(this->chip_delay); this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; case NAND_CMD_READ0: @@ -712,23 +746,23 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, /* End command latch cycle */ this->hwcontrol(mtd, NAND_CTL_CLRCLE); /* Fall through into ready check */ - - /* This applies to read commands */ + + /* This applies to read commands */ default: - /* + /* * If we don't have access to the busy pin, we apply the given * command delay */ if (!this->dev_ready) { udelay (this->chip_delay); return; - } + } } /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); - /* wait until command is processed */ + while (!this->dev_ready(mtd)); } @@ -736,7 +770,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, * nand_get_device - [GENERIC] Get chip for selected access * @this: the nand chip descriptor * @mtd: MTD device structure - * @new_state: the state which is requested + * @new_state: the state which is requested * * Get the device and lock it for exclusive access */ @@ -744,37 +778,38 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, #if 0 static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) { - struct nand_chip *active = this; - + struct nand_chip *active; + spinlock_t *lock; + wait_queue_head_t *wq; DECLARE_WAITQUEUE (wait, current); - /* - * Grab the lock and see if the device is available - */ + lock = (this->controller) ? &this->controller->lock : &this->chip_lock; + wq = (this->controller) ? &this->controller->wq : &this->wq; retry: + active = this; + spin_lock(lock); + /* Hardware controller shared among independend devices */ if (this->controller) { - spin_lock (&this->controller->lock); if (this->controller->active) active = this->controller->active; else this->controller->active = this; - spin_unlock (&this->controller->lock); } - - if (active == this) { - spin_lock (&this->chip_lock); - if (this->state == FL_READY) { - this->state = new_state; - spin_unlock (&this->chip_lock); - return; - } + if (active == this && this->state == FL_READY) { + this->state = new_state; + spin_unlock(lock); + return 0; + } + if (new_state == FL_PM_SUSPENDED) { + spin_unlock(lock); + return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; } - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&active->wq, &wait); - spin_unlock (&active->chip_lock); - schedule (); - remove_wait_queue (&active->wq, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); goto retry; } #else @@ -788,7 +823,7 @@ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int n * @state: state to select the max. timeout value * * Wait for command done. This applies to erase and program only - * Erase can take up to 400ms and program up to 20ms according to + * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs * */ @@ -796,9 +831,10 @@ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int n #if 0 static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) { + unsigned long timeo = jiffies; int status; - + if (state == FL_ERASING) timeo += (HZ * 400) / 1000; else @@ -810,37 +846,42 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); - else + else this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); - while (time_before(jiffies, timeo)) { + while (time_before(jiffies, timeo)) { /* Check, if we were interrupted */ if (this->state != state) return 0; if (this->dev_ready) { if (this->dev_ready(mtd)) - break; + break; } else { if (this->read_byte(mtd) & NAND_STATUS_READY) break; } - yield (); + cond_resched(); } status = (int) this->read_byte(mtd); return status; - - return 0; } #else static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) { unsigned long timeo; - +#if 0 if (state == FL_ERASING) timeo = (CFG_HZ * 400) / 1000; else timeo = (CFG_HZ * 20) / 1000; +#endif + + if (state == FL_ERASING) + timeo = (CFG_HZ_CLOCK * 400) / 1000; + else + timeo = (CFG_HZ_CLOCK * 20) / 1000; + if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); @@ -887,19 +928,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) * * Cached programming is not supported yet. */ -static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) { - int i, status; - u_char ecc_code[32]; + int i, oobidx, status; + u_char ecc_code[40]; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; - uint *oob_config = oobsel->eccpos; + int *oob_config = oobsel->eccpos; int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; int eccbytes = 0; - + /* FIXME: Enable cached programming */ cached = 0; - + /* Send command to begin auto page programming */ this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); @@ -908,9 +949,44 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa /* No ecc, write all */ case NAND_ECC_NONE: printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); - this->write_buf(mtd, this->data_poi, mtd->oobblock); - break; + if (!this->layout) { + this->write_buf(mtd, this->data_poi, mtd->oobblock); + this->write_buf(mtd, oob_buf, mtd->oobsize); + break; + } + /* + * Since we have a page layout, we must observe the layout to + * position data and oob correctly even though we aren't + * calculating ECC. + */ + for (oobidx = 0; eccsteps; eccsteps--) { + int j = 0; + for (; this->layout[j].length; j++) { + int len = this->layout[j].length; + int oidx = oobidx; + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + this->write_buf(mtd, &this->data_poi[datidx], this->layout[j].length); + datidx += len; + break; + case ITEM_TYPE_ECC: + case ITEM_TYPE_OOB: + if (this->options & NAND_BUSWIDTH_16) { + if (oidx & 1) { + oidx--; + len++; + } + if (len & 1) + len--; + } + this->write_buf(mtd, &oob_buf[oidx], len); + oobidx += len; + break; + } + } + } + break; /* Software ecc 3/256, write all */ case NAND_ECC_SOFT: for (; eccsteps; eccsteps--) { @@ -920,49 +996,106 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa datidx += this->eccsize; } this->write_buf(mtd, this->data_poi, mtd->oobblock); + this->write_buf(mtd, oob_buf, mtd->oobsize); break; default: eccbytes = this->eccbytes; - for (; eccsteps; eccsteps--) { - /* enable hardware ecc logic for write */ - this->enable_hwecc(mtd, NAND_ECC_WRITE); - this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); - this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); - for (i = 0; i < eccbytes; i++, eccidx++) - oob_buf[oob_config[eccidx]] = ecc_code[i]; - /* If the hardware ecc provides syndromes then - * the ecc code must be written immediately after - * the data bytes (words) */ + + if (! this->layout) { + for (; eccsteps; eccsteps--) { + /* enable hardware ecc logic for write */ + this->enable_hwecc(mtd, NAND_ECC_WRITE); + this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < eccbytes; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + /* If the hardware ecc provides syndromes then + * the ecc code must be written immidiately after + * the data bytes (words) */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, ecc_code, eccbytes); + datidx += this->eccsize; + } + if (this->options & NAND_HWECC_SYNDROME) - this->write_buf(mtd, ecc_code, eccbytes); - datidx += this->eccsize; + this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - + oobsel->eccbytes); + else + this->write_buf(mtd, oob_buf, mtd->oobsize); + + + break; + } + + for (oobidx = 0; eccsteps; eccsteps--) { + int j = 0, last_datidx = datidx, last_oobidx; + for (; this->layout[j].length; j++) { + int len = this->layout[j].length; + int oidx = oobidx; + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + this->enable_hwecc(mtd, NAND_ECC_WRITE); + this->write_buf(mtd, &this->data_poi[datidx], this->layout[j].length); + datidx += len; + break; + case ITEM_TYPE_ECC: + this->enable_hwecc(mtd, NAND_ECC_WRITESYN); + this->calculate_ecc(mtd, &this->data_poi[last_datidx], &ecc_code[eccidx]); + for (last_oobidx = oobidx; oobidx < last_oobidx + len; oobidx++, eccidx++) + oob_buf[oobidx] = ecc_code[eccidx]; + if (this->options & NAND_BUSWIDTH_16) { + if (oidx & 1) { + oidx--; + len++; + } + if (len & 1) + len--; + } + this->write_buf(mtd, &oob_buf[oidx], len); + break; + case ITEM_TYPE_OOB: + this->enable_hwecc(mtd, NAND_ECC_WRITEOOB); + if (this->options & NAND_BUSWIDTH_16) { + if (oidx & 1) { + oidx--; + len++; + } + if (len & 1) + len--; + } + this->write_buf(mtd, &oob_buf[oidx], len); + oobidx += len; + break; + } + } + } break; } - - /* Write out OOB data */ - if (this->options & NAND_HWECC_SYNDROME) - this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); - else - this->write_buf(mtd, oob_buf, mtd->oobsize); - + /* Send command to actually program the data */ this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); if (!cached) { /* call wait ready function */ status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_WRITING, status, page); + } + /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); return -EIO; } } else { /* FIXME: Implement cached programming ! */ /* wait until cache is ready*/ - /* status = this->waitfunc (mtd, this, FL_CACHEDRPG); */ + // status = this->waitfunc (mtd, this, FL_CACHEDRPG); } - return 0; + return 0; } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE @@ -978,19 +1111,19 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa * @oobmode: 1 = full buffer verify, 0 = ecc only * * The NAND device assumes that it is always writing to a cleanly erased page. - * Hence, it performs its internal write verification only on bits that + * Hence, it performs its internal write verification only on bits that * transitioned from 1 to 0. The device does NOT verify the whole page on a - * byte by byte basis. It is possible that the page was not completely erased - * or the page is becoming unusable due to wear. The read with ECC would catch - * the error later when the ECC page check fails, but we would rather catch + * byte by byte basis. It is possible that the page was not completely erased + * or the page is becoming unusable due to wear. The read with ECC would catch + * the error later when the ECC page check fails, but we would rather catch * it early in the page write stage. Better to write no data than invalid data. */ -static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) { int i, j, datidx = 0, oobofs = 0, res = -EIO; int eccsteps = this->eccsteps; - int hweccbytes; + int hweccbytes; u_char oobdata[64]; hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; @@ -1030,7 +1163,7 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { int ecccnt = oobsel->eccbytes; - + for (i = 0; i < ecccnt; i++) { int idx = oobsel->eccpos[i]; if (oobdata[idx] != oob_buf[oobofs + idx] ) { @@ -1040,20 +1173,20 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int goto out; } } - } + } } oobofs += mtd->oobsize - hweccbytes * eccsteps; page++; numpages--; - /* Apply delay or wait for ready/busy pin + /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check, so no problems * arise if a chip which does auto increment * is marked as NOAUTOINCR by the board driver. * Do this also before returning, so the chip is * ready for the next command. */ - if (!this->dev_ready) + if (!this->dev_ready) udelay (this->chip_delay); else while (!this->dev_ready(mtd)); @@ -1061,40 +1194,41 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int /* All done, return happy */ if (!numpages) return 0; - - - /* Check, if the chip supports auto page increment */ + + + /* Check, if the chip supports auto page increment */ if (!NAND_CANAUTOINCR(this)) this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); } - /* + /* * Terminate the read command. We come here in case of an error * So we must issue a reset command. */ -out: +out: this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); return res; } #endif /** - * nand_read - [MTD Interface] MTD compability function for nand_read_ecc + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * - * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL -*/ + * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL + * and flags = 0xff + */ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { - return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); + return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); } /** - * nand_read_ecc - [MTD Interface] Read data with ECC + * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read @@ -1103,20 +1237,47 @@ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * re * @oob_buf: filesystem supplied oob data buffer * @oobsel: oob selection structure * - * NAND read with ECC + * This function simply calls nand_do_read_ecc with flags = 0xff */ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) { + /* use userspace supplied oobinfo, if zero */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); +} + + +/** + * nand_do_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer (can be NULL) + * @oobsel: oob selection structure + * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed + * and how many corrected error bits are acceptable: + * bits 0..7 - number of tolerable errors + * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * + * NAND read with ECC + */ +int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags) +{ + int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; - int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; + int read = 0, oob = 0, oobidx, ecc_status = 0, ecc_failed = 0, eccidx; struct nand_chip *this = mtd->priv; u_char *data_poi, *oob_data = oob_buf; u_char ecc_calc[32]; u_char ecc_code[32]; - int eccmode, eccsteps; - unsigned *oob_config; - int datidx; + int eccmode, eccsteps; + int *oob_config, datidx; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; int eccbytes; int compareecc = 1; @@ -1133,16 +1294,13 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd ,FL_READING); - - /* use userspace supplied oobinfo, if zero */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; + if (flags & NAND_GET_DEVICE) + nand_get_device (this, mtd, FL_READING); /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) oobsel = this->autooob; - + eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; oob_config = oobsel->eccpos; @@ -1160,28 +1318,28 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, end = mtd->oobblock; ecc = this->eccsize; eccbytes = this->eccbytes; - + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; - if (this->options & NAND_HWECC_SYNDROME) + if (this->options & NAND_HWECC_SYNDROME) oobreadlen -= oobsel->eccbytes; /* Loop until all data read */ while (read < len) { - + int aligned = (!col && (len - read) >= end); - /* + /* * If the read is not page aligned, we have to read into data buffer * due to ecc, else we read into return buffer direct */ if (aligned) data_poi = &buf[read]; - else + else data_poi = this->data_buf; - - /* Check, if we have this page in the buffer + + /* Check, if we have this page in the buffer * * FIXME: Make it work when we must provide oob data too, * check the usage of data_buf oob field @@ -1197,7 +1355,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, if (sndcmd) { this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; - } + } /* get oob area, if we have no oob buffer from fs-driver */ if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || @@ -1205,7 +1363,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, oob_data = &this->data_buf[end]; eccsteps = this->eccsteps; - + switch (eccmode) { case NAND_ECC_NONE: { /* No ECC, Read in a page */ /* XXX U-BOOT XXX */ @@ -1218,50 +1376,151 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, #else puts("Reading data from NAND FLASH without ECC is not recommended\n"); #endif - this->read_buf(mtd, data_poi, end); + if (!this->layout) { + this->read_buf(mtd, data_poi, end); + break; + } + + /* + * Since we have a page layout, we must observe the + * layout to position data and oob correctly even though + * we aren't calculating ECC. + */ + for (oobidx = 0, datidx = 0; eccsteps; eccsteps--) { + for (j = 0; this->layout[j].length; j++) { + int len = this->layout[j].length; + int oidx = oobidx; + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d bytes of data\n", __FUNCTION__, this->layout[j].length); + this->read_buf(mtd, &data_poi[datidx], len); + datidx += this->layout[j].length; + break; + case ITEM_TYPE_ECC: + case ITEM_TYPE_OOB: + DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d oob bytes\n", __FUNCTION__, this->layout[j].length); + if (this->options & NAND_BUSWIDTH_16) { + if (oidx & 1) { + oidx--; + len++; + } + if (len & 1) + len--; + } + this->read_buf(mtd, &oob_data[oidx], len); + oobidx += this->layout[j].length; + break; + } + } + } break; } - case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ this->read_buf(mtd, data_poi, end); - for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); - break; + this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + break; default: - for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { - this->enable_hwecc(mtd, NAND_ECC_READ); - this->read_buf(mtd, &data_poi[datidx], ecc); - - /* HW ecc with syndrome calculation must read the - * syndrome from flash immidiately after the data */ - if (!compareecc) { - /* Some hw ecc generators need to know when the - * syndrome is read from flash */ - this->enable_hwecc(mtd, NAND_ECC_READSYN); - this->read_buf(mtd, &oob_data[i], eccbytes); - /* We calc error correction directly, it checks the hw - * generator for an error, reads back the syndrome and - * does the error correction on the fly */ - if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { - DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " - "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); - ecc_failed++; + if (! this->layout) { + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { + this->enable_hwecc(mtd, NAND_ECC_READ); + this->read_buf(mtd, &data_poi[datidx], ecc); + + /* HW ecc with syndrome calculation must read the + * syndrome from flash immidiately after the data */ + if (!compareecc) { + /* Some hw ecc generators need to know when the + * syndrome is read from flash */ + this->enable_hwecc(mtd, NAND_ECC_READSYN); + this->read_buf(mtd, &oob_data[i], eccbytes); + /* We calc error correction directly, it checks the hw + * generator for an error, reads back the syndrome and + * does the error correction on the fly */ + ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + if ((ecc_status == -1) || (ecc_status > (flags & 0xff))) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); + ecc_failed++; + } + } else { + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + } + } + + this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + + break; + } + + for (oobidx = 0, datidx = 0, eccidx = 0; eccsteps; eccsteps--) { + int last_datidx = datidx, last_oobidx = oobidx; + for (j = 0; this->layout[j].length; j++) { + int len = this->layout[j].length; + int oidx = oobidx; + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d bytes of data\n", __FUNCTION__, this->layout[j].length); + this->enable_hwecc(mtd, NAND_ECC_READ); + this->read_buf(mtd, &data_poi[datidx], len); + datidx += this->layout[j].length; + break; + + case ITEM_TYPE_ECC: + DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d ecc bytes\n", __FUNCTION__, this->layout[j].length); + /* let the particular driver decide whether to read ECC */ + this->enable_hwecc(mtd, NAND_ECC_READSYN); + if (this->options & NAND_BUSWIDTH_16) { + if (oidx & 1) { + oidx--; + len++; + } + if (len & 1) + len--; + } + + this->read_buf(mtd, &oob_data[oidx], len); + if (!compareecc) { + /* We calc error correction directly, it checks the hw + * generator for an error, reads back the syndrome and + * does the error correction on the fly */ + ecc_status = this->correct_data(mtd, &data_poi[last_datidx], &oob_data[last_oobidx], &ecc_code[eccidx]); + if ((ecc_status == -1) || (ecc_status > (flags & 0xff))) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); + ecc_failed++; + } + } else + this->calculate_ecc(mtd, &data_poi[last_datidx], &ecc_calc[eccidx]); + oobidx += this->layout[j].length; + eccidx += this->layout[j].length; + break; + case ITEM_TYPE_OOB: + DEBUG (MTD_DEBUG_LEVEL3, "%s: reading %d free oob bytes\n", __FUNCTION__, this->layout[j].length); + this->enable_hwecc(mtd, NAND_ECC_READOOB); + if (this->options & NAND_BUSWIDTH_16) { + if (oidx & 1) { + oidx--; + len++; + } + if (len & 1) + len--; + } + + this->read_buf(mtd, &oob_data[oidx], len); + oobidx += this->layout[j].length; + break; } - } else { - this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); } } - break; + break; } - /* read oobdata */ - this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); - /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ if (!compareecc) - goto readoob; - + goto readoob; + /* Pick the ECC bytes out of the oob data */ for (j = 0; j < oobsel->eccbytes; j++) ecc_code[j] = oob_data[oob_config[j]]; @@ -1269,24 +1528,24 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, /* correct data, if neccecary */ for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); - + /* Get next chunk of ecc bytes */ j += eccbytes; - - /* Check, if we have a fs supplied oob-buffer, + + /* Check, if we have a fs supplied oob-buffer, * This is the legacy mode. Used by YAFFS1 * Should go away some day */ - if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { + if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { int *p = (int *)(&oob_data[mtd->oobsize]); p[i] = ecc_status; } - - if (ecc_status == -1) { + + if ((ecc_status == -1) || (ecc_status > (flags & 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } - } + } readoob: /* check, if we have a fs supplied oob-buffer */ @@ -1296,13 +1555,12 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, case MTD_NANDECC_AUTOPLACE: case MTD_NANDECC_AUTOPL_USR: /* Walk through the autoplace chunks */ - for (i = 0, j = 0; j < mtd->oobavail; i++) { + for (i = 0; oobsel->oobfree[i][1]; i++) { int from = oobsel->oobfree[i][0]; int num = oobsel->oobfree[i][1]; - memcpy(&oob_buf[oob+j], &oob_data[from], num); - j+= num; + memcpy(&oob_buf[oob], &oob_data[from], num); + oob += num; } - oob += mtd->oobavail; break; case MTD_NANDECC_PLACE: /* YAFFS1 legacy mode */ @@ -1313,25 +1571,25 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } readdata: /* Partial page read, transfer data into fs buffer */ - if (!aligned) { + if (!aligned) { for (j = col; j < end && read < len; j++) buf[read++] = data_poi[j]; - this->pagebuf = realpage; - } else + this->pagebuf = realpage; + } else read += mtd->oobblock; - /* Apply delay or wait for ready/busy pin + /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check, so no problems * arise if a chip which does auto increment * is marked as NOAUTOINCR by the board driver. */ - if (!this->dev_ready) + if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); - + while (!this->dev_ready(mtd)); + if (read == len) - break; + break; /* For subsequent reads align to page boundary. */ col = 0; @@ -1345,15 +1603,16 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); } - /* Check, if the chip supports auto page increment - * or if we have hit a block boundary. - */ + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) - sndcmd = 1; + sndcmd = 1; } /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); + if (flags & NAND_GET_DEVICE) + nand_release_device(mtd); /* * Return success, if no ECC failures, else -EBADMSG @@ -1385,7 +1644,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t /* Shift to get page */ page = (int)(from >> this->page_shift); chipnr = (int)(from >> this->chip_shift); - + /* Mask to get column */ col = from & (mtd->oobsize - 1); @@ -1407,7 +1666,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t /* Send the read command */ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); - /* + /* * Read the data, if we read more than one page * oob data, let the device transfer the data ! */ @@ -1417,16 +1676,16 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t thislen = min_t(int, thislen, len); this->read_buf(mtd, &buf[i], thislen); i += thislen; - - /* Apply delay or wait for ready/busy pin + + /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check, so no problems * arise if a chip which does auto increment * is marked as NOAUTOINCR by the board driver. */ - if (!this->dev_ready) + if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + while (!this->dev_ready(mtd)); /* Read more ? */ if (i < len) { @@ -1439,17 +1698,166 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); } - - /* Check, if the chip supports auto page increment - * or if we have hit a block boundary. - */ + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { /* For subsequent page reads set offset to 0 */ - this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); + this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); + } + } + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Return happy */ + *retlen = len; + return 0; +} + +/** + * nand_read_oob_hwecc - [MTD Interface] NAND read out-of-band (HW ECC) + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @oob_buf: the databuffer to put data + * + * NAND read out-of-band data from the spare area + * W/o assumptions that are valid only for software ECC + */ +static int nand_read_oob_hwecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * oob_buf) +{ + int i, col, page, chipnr, nleft; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", __FUNCTION__, + (unsigned int) from, (int) len); + + /* Shift to get page */ + page = (int)(from >> this->page_shift); + chipnr = (int)(from >> this->chip_shift); + + /* Mask to get column */ + col = from & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end of device\n", + __FUNCTION__); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* + * Read the data, if we read more than one page + * oob data, let the device transfer the data ! + */ + i = 0; + nleft = len; + while (i < len) { + int ooboff, pageoff, eccsteps; + + eccsteps = this->eccsteps; + for (ooboff = 0, pageoff = 0; eccsteps; eccsteps--) { + int j, first, last, thislen; + /* + * In the following we assume that each item (data, ECC, + * and OOB) in the layout has an even length such as + * would be required for a 16-bit-wide NAND. This + * assumption allows us to handle 16-bit-wide chips with + * no special cases versus 8-bit-wide chips. + */ + for (j = 0; this->layout[j].length; j++) { + thislen = this->layout[j].length; + /* are we done yet? */ + if (i == len) + goto finished; + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + pageoff += thislen; + continue; + case ITEM_TYPE_ECC: + case ITEM_TYPE_OOB: + /* + * Calculate the intersection of the oob + * data with this layout item. + */ + first = max(ooboff, col); + last = min(ooboff + thislen, + col + nleft); + if (first >= last) { + /* no intersection */ + break; + } + this->cmdfunc(mtd, NAND_CMD_READ0, + pageoff + + ((first - ooboff) & ~1), + page & this->pagemask); + /* handle an odd offset */ + if (first & 1) { + oob_buf[i++] = cpu_to_le16( + this->read_word(mtd)) + >> 8; + ++first; + } + if (last - first > 1) { + int n = ((last - first) & ~1); + /* read an even number of oob bytes */ + this->read_buf(mtd, oob_buf + i, n); + i += n; + first += n; + } + /* handle an odd length */ + if (last - first == 1) { + oob_buf[i++] = cpu_to_le16( + this->read_word(mtd)) + & 0xff; + ++first; + } + break; + } + pageoff += thislen; + ooboff += thislen; + } + } + + /* + * Apply delay or wait for ready/busy pin in case the chip is + * auto-incrementing to the next page. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + while (!this->dev_ready(mtd)); + + /* Read more ? */ + if (i < len) { + page++; + col = 0; + nleft = len - i; + + /* Check, if we cross a chip boundary */ + if (!(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); } } } +finished: /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); @@ -1458,6 +1866,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t return 0; } + /** * nand_read_raw - [GENERIC] Read raw data including oob into buffer * @mtd: MTD device structure @@ -1488,27 +1897,61 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, nand_get_device (this, mtd , FL_READING); this->select_chip (mtd, chip); - + /* Add requested oob length */ len += ooblen; - + while (len) { if (sndcmd) this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); - sndcmd = 0; + sndcmd = 0; + + if (!this->layout) + this->read_buf(mtd, &buf[cnt], pagesize); + else { + int oobidx, datidx, eccsteps, j; + uint8_t *datbuf, *oobbuf; - this->read_buf (mtd, &buf[cnt], pagesize); + /* + * Since we have a page layout, we must observe the + * layout to position data and oob correctly. + */ + datbuf = &buf[cnt]; + oobbuf = &datbuf[mtd->oobblock]; + eccsteps = this->eccsteps; + for (oobidx = 0, datidx = 0; eccsteps; eccsteps--) { + for (j = 0; this->layout[j].length; j++) { + int thislen = this->layout[j].length; + + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + this->read_buf(mtd, + &datbuf[datidx], + thislen); + datidx += thislen; + break; + case ITEM_TYPE_ECC: + case ITEM_TYPE_OOB: + this->read_buf(mtd, + &oobbuf[oobidx], + thislen); + oobidx += thislen; + break; + } + } + } + } len -= pagesize; cnt += pagesize; page++; - - if (!this->dev_ready) + + if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); - - /* Check, if the chip supports auto page increment */ + while (!this->dev_ready(mtd)); + + /* Check, if the chip supports auto page increment */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) sndcmd = 1; } @@ -1519,8 +1962,8 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, } -/** - * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer +/** + * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer * @mtd: MTD device structure * @fsbuf: buffer given by fs driver * @oobsel: out of band selection structre @@ -1549,20 +1992,20 @@ static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct int i, len, ofs; /* Zero copy fs supplied buffer */ - if (fsbuf && !autoplace) + if (fsbuf && !autoplace) return fsbuf; /* Check, if the buffer must be filled with ff again */ - if (this->oobdirty) { - memset (this->oob_buf, 0xff, + if (this->oobdirty) { + memset (this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); this->oobdirty = 0; - } - + } + /* If we have no autoplacement or no fs buffer use the internal one */ if (!autoplace || !fsbuf) return this->oob_buf; - + /* Walk through the pages and place the data */ this->oobdirty = 1; ofs = 0; @@ -1574,7 +2017,7 @@ static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct len += num; fsbuf += num; } - ofs += mtd->oobavail; + ofs += mtd->oobsize; } return this->oob_buf; } @@ -1596,7 +2039,7 @@ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * ret { return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); } - + /** * nand_write_ecc - [MTD Interface] NAND write with ECC * @mtd: MTD device structure @@ -1629,7 +2072,7 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, return -EINVAL; } - /* reject writes, which are not page aligned */ + /* reject writes, which are not page aligned */ if (NOTALIGNED (to) || NOTALIGNED(len)) { printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); return -EINVAL; @@ -1644,20 +2087,18 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, this->select_chip(mtd, chipnr); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - printk (KERN_NOTICE "nand_write_ecc: Device is write protected\n"); + if (nand_check_wp(mtd)) goto out; - } /* if oobsel is NULL, use chip defaults */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { oobsel = this->autooob; autoplace = 1; - } + } if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) autoplace = 1; @@ -1665,9 +2106,9 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, totalpages = len >> this->page_shift; page = (int) (to >> this->page_shift); /* Invalidate the page cache, if we write to the cached page */ - if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) + if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) this->pagebuf = -1; - + /* Set it relative to chip */ page &= this->pagemask; startpage = page; @@ -1689,14 +2130,14 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, if (ret) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); goto out; - } + } /* Next oob page */ oob += mtd->oobsize; /* Update written bytes count */ written += mtd->oobblock; - if (written == len) + if (written == len) goto cmp; - + /* Increment page address */ page++; @@ -1707,15 +2148,14 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, if (!(page & (ppblock - 1))){ int ofs; this->data_poi = bufstart; - ret = nand_verify_pages (mtd, this, startpage, + ret = nand_verify_pages (mtd, this, startpage, page - startpage, oobbuf, oobsel, chipnr, (eccbuf != NULL)); if (ret) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); goto out; - } + } *retlen = written; - bufstart = (u_char*) &buf[written]; ofs = autoplace ? mtd->oobavail : mtd->oobsize; if (eccbuf) @@ -1724,10 +2164,9 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, numpages = min (totalpages, ppblock); page &= this->pagemask; startpage = page; - oob = 0; - this->oobdirty = 1; - oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); + oob = 0; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; @@ -1743,7 +2182,7 @@ cmp: oobbuf, oobsel, chipnr, (eccbuf != NULL)); if (!ret) *retlen = written; - else + else DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); out: @@ -1803,7 +2242,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * /* Check, if it is write protected */ if (nand_check_wp(mtd)) goto out; - + /* Invalidate the page cache, if we write to the cached page */ if (page == this->pagebuf) this->pagebuf = -1; @@ -1829,7 +2268,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); ret = -EIO; goto out; @@ -1855,8 +2294,164 @@ out: return ret; } -/* XXX U-BOOT XXX */ -#if 0 +/** + * nand_write_oob_hwecc - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @oob_buf: the data to write + * + * NAND write out-of-band + * W/o assumptions that are valid only for software ECC + */ +static int nand_write_oob_hwecc (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * oob_buf) +{ + int column, page, status, ret = -EIO, chipnr, eccsteps; + int ooblen, oc; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", __FUNCTION__, (unsigned int) to, (int) len); + + /* Shift to get page */ + page = (int) (to >> this->page_shift); + chipnr = (int) (to >> this->chip_shift); + + /* Mask to get column */ + column = to & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow write past end of page */ + if ((column + len) > mtd->oobsize) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: Attempt to write past end of page\n", __FUNCTION__); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Reset the chip. Some chips (like the Toshiba TC5832DC found + in one of my DiskOnChip 2000 test units) will clear the whole + data page too if we don't do this. I have no clue why, but + I seem to have 'fixed' it in the doc2000 driver in + August 1999. dwmw2. */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == this->pagebuf) + this->pagebuf = -1; + + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, 0, page & this->pagemask); + + eccsteps = this->eccsteps; + + for (ooblen = 0, oc = 0; eccsteps; eccsteps--) { + int j, first, last, thislen; + /* + * In the following we assume that each item (data, ECC, + * and OOB) in the layout has an even length such as would be + * required for a 16-bit-wide NAND. This assumption allows us + * to handle 16-bit-wide chips with no special cases versus + * 8-bit-wide chips. + */ + for (j = 0; this->layout[j].length; j++) { + /* are we done yet? */ + if ((oc == len) && !NAND_MUST_PAD(this)) + goto finish; + thislen = this->layout[j].length; + switch (this->layout[j].type) { + case ITEM_TYPE_DATA: + this->write_buf(mtd, ffchars, thislen); + continue; + case ITEM_TYPE_ECC: + case ITEM_TYPE_OOB: + /* + * Calculate the intersection of the oob data + * with this layout item. + */ + first = max(ooblen, column); + last = min(ooblen + thislen, column + (int)len); + if (first >= last) { + /* no intersection */ + this->write_buf(mtd, ffchars, thislen); + break; + } + /* pre-pad */ + if (first > ooblen + 1) { + /* write an even number of FFs */ + this->write_buf(mtd, ffchars, + ((first - ooblen) & ~1)); + } + /* handle an odd offset */ + if (first & 1) { + this->write_word(mtd, + cpu_to_le16((oob_buf[oc++] << 8) + | 0xff)); + ++first; + } + if (last - first > 1) { + int n = ((last - first) & ~1); + /* write an even number of oob bytes */ + this->write_buf(mtd, oob_buf + oc, n); + oc += n; + first += n; + } + /* handle an odd length */ + if (last - first == 1) { + this->write_word(mtd, + cpu_to_le16(0xff00 + | oob_buf[oc++])); + ++first; + } + /* post-pad */ + if (((last + 1) & ~1) < ooblen + thislen) { + this->write_buf(mtd, ffchars, + ooblen + thislen + - ((last + 1) & ~1)); + } + break; + } + ooblen += thislen; + } + } + +finish: + /* Send command to program the OOB data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: Failed write, page 0x%08x\n", __FUNCTION__, page); + ret = -EIO; + goto out; + } + /* Return happy */ + *retlen = len; + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +#warning "Verify for OOB data in HW ECC case is NOT YET implemented" +#endif + ret = 0; + +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + /** * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc * @mtd: MTD device structure @@ -1867,10 +2462,11 @@ out: * * NAND write with kvec. This just calls the ecc function */ -static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, +#if 0 +static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t * retlen) { - return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); + return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); } /** @@ -1885,7 +2481,7 @@ static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned * * NAND write with iovec with ecc */ -static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) { int i, page, len, total_len, ret = -EIO, written = 0, chipnr; @@ -1911,7 +2507,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig return -EINVAL; } - /* reject writes, which are not page aligned */ + /* reject writes, which are not page aligned */ if (NOTALIGNED (to) || NOTALIGNED(total_len)) { printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); return -EINVAL; @@ -1930,21 +2526,21 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig goto out; /* if oobsel is NULL, use chip defaults */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; + if (oobsel == NULL) + oobsel = &mtd->oobinfo; /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { oobsel = this->autooob; autoplace = 1; - } + } if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) autoplace = 1; /* Setup start page */ page = (int) (to >> this->page_shift); /* Invalidate the page cache, if we write to the cached page */ - if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) + if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) this->pagebuf = -1; startpage = page & this->pagemask; @@ -1968,10 +2564,10 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig oob = 0; for (i = 1; i <= numpages; i++) { /* Write one page. If this is the last page to write - * then use the real pageprogram command, else select + * then use the real pageprogram command, else select * cached programming if supported by the chip. */ - ret = nand_write_page (mtd, this, page & this->pagemask, + ret = nand_write_page (mtd, this, page & this->pagemask, &oobbuf[oob], oobsel, i != numpages); if (ret) goto out; @@ -1987,12 +2583,12 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig count--; } } else { - /* We must use the internal buffer, read data out of each + /* We must use the internal buffer, read data out of each * tuple until we have a full page to write */ int cnt = 0; while (cnt < mtd->oobblock) { - if (vecs->iov_base != NULL && vecs->iov_len) + if (vecs->iov_base != NULL && vecs->iov_len) this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { @@ -2001,10 +2597,10 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig count--; } } - this->pagebuf = page; - this->data_poi = this->data_buf; + this->pagebuf = page; + this->data_poi = this->data_buf; bufstart = this->data_poi; - numpages = 1; + numpages = 1; oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); ret = nand_write_page (mtd, this, page & this->pagemask, oobbuf, oobsel, 0); @@ -2017,7 +2613,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); if (ret) goto out; - + written += mtd->oobblock * numpages; /* All done ? */ if (!count) @@ -2086,7 +2682,8 @@ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) { return nand_erase_nand (mtd, instr, 0); } - + +#define BBT_PAGE_MASK 0xffffff3f /** * nand_erase_intern - [NAND Interface] erase block(s) * @mtd: MTD device structure @@ -2099,6 +2696,10 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb { int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *this = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ + unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ + /* It is used to see if the current page is in the same */ + /* 256 block group and the same bank as the bbt. */ DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); @@ -2144,37 +2745,56 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb goto erase_exit; } + /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ + if (this->options & BBT_AUTO_REFRESH) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } else { + bbt_masked_page = 0xffffffff; /* should not match anything */ + } + /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { -#ifndef NAND_ALLOW_ERASE_ALL /* Check if we have a bad block, we do not erase bad blocks ! */ if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } -#endif - /* Invalidate the page cache, if we erase the block which contains + + /* Invalidate the page cache, if we erase the block which contains the current cached page */ if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) this->pagebuf = -1; this->erase_cmd (mtd, page & this->pagemask); - + status = this->waitfunc (mtd, this, FL_ERASING); + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_ERASING, status, page); + } + /* See if block erase succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << this->page_shift); goto erase_exit; } + /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ + if (this->options & BBT_AUTO_REFRESH) { + if (((page & BBT_PAGE_MASK) == bbt_masked_page) && + (page != this->bbt_td->pages[chipnr])) { + rewrite_bbt[chipnr] = (page << this->page_shift); + } + } + /* Increment page address and decrement length */ len -= (1 << this->phys_erase_shift); page += pages_per_block; @@ -2184,6 +2804,12 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); + /* if BBT requires refresh and BBT-PERCHIP, + * set the BBT page mask to see if this BBT should be rewritten */ + if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } + } } instr->state = MTD_ERASE_DONE; @@ -2198,6 +2824,18 @@ erase_exit: /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); + /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ + if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { + for (chipnr = 0; chipnr < this->numchips; chipnr++) { + if (rewrite_bbt[chipnr]) { + /* update the BBT for chip */ + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); + nand_update_bbt (mtd, rewrite_bbt[chipnr]); + } + } + } + /* Return more or less happy */ return ret; } @@ -2229,9 +2867,9 @@ static void nand_sync (struct mtd_info *mtd) static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) { /* Check for invalid offset */ - if (ofs > mtd->size) + if (ofs > mtd->size) return -EINVAL; - + return nand_block_checkbad (mtd, ofs, 1, 0); } @@ -2245,12 +2883,12 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) struct nand_chip *this = mtd->priv; int ret; - if ((ret = nand_block_isbad(mtd, ofs))) { - /* If it was bad already, return success and do nothing. */ + if ((ret = nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ if (ret > 0) return 0; - return ret; - } + return ret; + } return this->block_markbad(mtd, ofs); } @@ -2269,9 +2907,9 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) */ int nand_scan (struct mtd_info *mtd, int maxchips) { - int i, j, nand_maf_id, nand_dev_id, busw; + int i, nand_maf_id, nand_dev_id, busw, maf_id; struct nand_chip *this = mtd->priv; - + /* Get buswidth to select the correct functions*/ busw = this->options & NAND_BUSWIDTH_16; @@ -2310,9 +2948,13 @@ int nand_scan (struct mtd_info *mtd, int maxchips) if (!this->scan_bbt) this->scan_bbt = nand_default_bbt; + /* 'ff' the ffchars */ + memset(ffchars, 0xff, FFCHARS_SIZE); + /* Select the device */ this->select_chip(mtd, 0); + this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); @@ -2320,15 +2962,18 @@ int nand_scan (struct mtd_info *mtd, int maxchips) nand_maf_id = this->read_byte(mtd); nand_dev_id = this->read_byte(mtd); + printf("NAND Manufacturer id: %x\n", nand_maf_id); + printf("NAND Device id: %x\n", nand_dev_id); + /* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { - - if (nand_dev_id != nand_flash_ids[i].id) + + if (nand_dev_id != nand_flash_ids[i].id) continue; if (!mtd->name) mtd->name = nand_flash_ids[i].name; this->chipsize = nand_flash_ids[i].chipsize << 20; - + /* New devices have all the information in additional id bytes */ if (!nand_flash_ids[i].pagesize) { int extid; @@ -2340,14 +2985,14 @@ int nand_scan (struct mtd_info *mtd, int maxchips) mtd->oobblock = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512); + mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; - + } else { /* Old devices have this data hardcoded in the * device id table */ @@ -2357,27 +3002,33 @@ int nand_scan (struct mtd_info *mtd, int maxchips) busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; } + /* Try to identify manufacturer */ + for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { + if (nand_manuf_ids[maf_id].id == nand_maf_id) + break; + } + /* Check, if buswidth is correct. Hardware drivers should set * this correct ! */ if (busw != (this->options & NAND_BUSWIDTH_16)) { printk (KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, - nand_manuf_ids[i].name , mtd->name); - printk (KERN_WARNING - "NAND bus width %d instead %d bit\n", + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[maf_id].name , mtd->name); + printk (KERN_WARNING + "NAND bus width %d instead %d bit\n", (this->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); this->select_chip(mtd, -1); - return 1; + return 1; } - - /* Calculate the address shift from the page size */ + + /* Calculate the address shift from the page size */ this->page_shift = ffs(mtd->oobblock) - 1; this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; this->chip_shift = ffs(this->chipsize) - 1; /* Set the bad block position */ - this->badblockpos = mtd->oobblock > 512 ? + this->badblockpos = mtd->oobblock > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; /* Get chip options, preserve non chip based options */ @@ -2387,10 +3038,10 @@ int nand_scan (struct mtd_info *mtd, int maxchips) this->options |= NAND_NO_AUTOINCR; /* Check if this is a not a samsung device. Do not clear the options * for chips which are not having an extended id. - */ + */ if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) this->options &= ~NAND_SAMSUNG_LP_OPTIONS; - + /* Check for AND chips with 4 page planes */ if (this->options & NAND_4PAGE_ARRAY) this->erase_cmd = multi_erase_cmd; @@ -2400,19 +3051,15 @@ int nand_scan (struct mtd_info *mtd, int maxchips) /* Do not replace user supplied command function ! */ if (mtd->oobblock > 512 && this->cmdfunc == nand_command) this->cmdfunc = nand_command_lp; - - /* Try to identify manufacturer */ - for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { - if (nand_manuf_ids[j].id == nand_maf_id) - break; - } + + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[maf_id].name , nand_flash_ids[i].name); break; } if (!nand_flash_ids[i].name) { -#ifndef CFG_NAND_QUIET_TEST printk (KERN_WARNING "No NAND device found!!!\n"); -#endif this->select_chip(mtd, -1); return 1; } @@ -2430,7 +3077,7 @@ int nand_scan (struct mtd_info *mtd, int maxchips) } if (i > 1) printk(KERN_INFO "%d NAND chips detected\n", i); - + /* Allocate buffers, if neccecary */ if (!this->oob_buf) { size_t len; @@ -2442,7 +3089,7 @@ int nand_scan (struct mtd_info *mtd, int maxchips) } this->options |= NAND_OOBBUF_ALLOC; } - + if (!this->data_buf) { size_t len; len = mtd->oobblock + mtd->oobsize; @@ -2469,7 +3116,7 @@ int nand_scan (struct mtd_info *mtd, int maxchips) if (!this->autooob) { /* Select the appropriate default oob placement scheme for * placement agnostic filesystems */ - switch (mtd->oobsize) { + switch (mtd->oobsize) { case 8: this->autooob = &nand_oob_8; break; @@ -2482,22 +3129,22 @@ int nand_scan (struct mtd_info *mtd, int maxchips) default: printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", mtd->oobsize); -/* BUG(); */ + BUG(); } } - + /* The number of bytes available for the filesystem to place fs dependend * oob data */ mtd->oobavail = 0; - for (i=0; this->autooob->oobfree[i][1]; i++) + for (i = 0; this->autooob->oobfree[i][1]; i++) mtd->oobavail += this->autooob->oobfree[i][1]; - /* + /* * check ECC mode, default to software * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize - * fallback to software ECC + * fallback to software ECC */ - this->eccsize = 256; /* set default eccsize */ + this->eccsize = 256; /* set default eccsize */ this->eccbytes = 3; switch (this->eccmode) { @@ -2512,56 +3159,59 @@ int nand_scan (struct mtd_info *mtd, int maxchips) this->eccsize = 2048; break; - case NAND_ECC_HW3_512: - case NAND_ECC_HW6_512: - case NAND_ECC_HW8_512: + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + case NAND_ECC_HW10_512: if (mtd->oobblock == 256) { printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; - } else + } else this->eccsize = 512; /* set eccsize to 512 */ break; - + case NAND_ECC_HW3_256: break; - - case NAND_ECC_NONE: + + case NAND_ECC_NONE: printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); this->eccmode = NAND_ECC_NONE; break; - case NAND_ECC_SOFT: + case NAND_ECC_SOFT: this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; default: printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); -/* BUG(); */ - } + BUG(); + } - /* Check hardware ecc function availability and adjust number of ecc bytes per + /* Check hardware ecc function availability and adjust number of ecc bytes per * calculation step */ switch (this->eccmode) { case NAND_ECC_HW12_2048: - this->eccbytes += 4; - case NAND_ECC_HW8_512: this->eccbytes += 2; - case NAND_ECC_HW6_512: + case NAND_ECC_HW10_512: + this->eccbytes += 2; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: this->eccbytes += 3; - case NAND_ECC_HW3_512: + case NAND_ECC_HW3_512: case NAND_ECC_HW3_256: if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break; printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); -/* BUG(); */ + BUG(); } - + mtd->eccsize = this->eccsize; - + /* Set the number of read / write steps for one page to ensure ECC generation */ switch (this->eccmode) { case NAND_ECC_HW12_2048: @@ -2570,17 +3220,20 @@ int nand_scan (struct mtd_info *mtd, int maxchips) case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: + case NAND_ECC_HW10_512: this->eccsteps = mtd->oobblock / 512; break; case NAND_ECC_HW3_256: - case NAND_ECC_SOFT: + case NAND_ECC_SOFT: this->eccsteps = mtd->oobblock / 256; break; - - case NAND_ECC_NONE: + + case NAND_ECC_NONE: this->eccsteps = 1; break; } + + mtd->eccsize = this->eccsize; /* XXX U-BOOT XXX */ #if 0 @@ -2607,9 +3260,15 @@ int nand_scan (struct mtd_info *mtd, int maxchips) mtd->write = nand_write; mtd->read_ecc = nand_read_ecc; mtd->write_ecc = nand_write_ecc; - mtd->read_oob = nand_read_oob; - mtd->write_oob = nand_write_oob; -/* XXX U-BOOT XXX */ + + if ((this->eccmode != NAND_ECC_NONE && this->eccmode != NAND_ECC_SOFT) + && this->layout) { + mtd->read_oob = nand_read_oob_hwecc; + mtd->write_oob = nand_write_oob_hwecc; + } else { + mtd->read_oob = nand_read_oob; + mtd->write_oob = nand_write_oob; + } #if 0 mtd->readv = NULL; mtd->writev = nand_writev; @@ -2632,14 +3291,18 @@ int nand_scan (struct mtd_info *mtd, int maxchips) #if 0 mtd->owner = THIS_MODULE; #endif + /* Check, if we should skip the bad block table scan */ + if (this->options & NAND_SKIP_BBTSCAN) + return 0; + /* Build bad block table */ return this->scan_bbt (mtd); } /** - * nand_release - [NAND Interface] Free resources held by the NAND device + * nand_release - [NAND Interface] Free resources held by the NAND device * @mtd: MTD device structure - */ +*/ void nand_release (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 19a9bc2a5b6..4f683150bcf 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -564,7 +564,9 @@ write: return res; } + udelay(100000); res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); return res; @@ -812,7 +814,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; buf = kmalloc (len, GFP_KERNEL); - if (!buf) { + if(!buf) { printk (KERN_ERR "nand_bbt: Out of memory\n"); kfree (this->bbt); this->bbt = NULL; @@ -929,7 +931,7 @@ static struct nand_bbt_descr smallpage_flashbased = { }; static struct nand_bbt_descr largepage_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .options = 0, /* NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,*/ .offs = 0, .len = 2, .pattern = scan_ff_pattern @@ -952,9 +954,9 @@ static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; static struct nand_bbt_descr bbt_main_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, + .offs = 2, .len = 4, - .veroffs = 12, + .veroffs = 16, .maxblocks = 4, .pattern = bbt_pattern }; @@ -962,9 +964,9 @@ static struct nand_bbt_descr bbt_main_descr = { static struct nand_bbt_descr bbt_mirror_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, + .offs = 2, .len = 4, - .veroffs = 12, + .veroffs = 16, .maxblocks = 4, .pattern = mirror_pattern }; diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 4c532b0794e..a8871aea023 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -40,13 +40,6 @@ #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) #include<linux/mtd/mtd.h> - -/* - * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), - * only nand_correct_data() is needed - */ - -#ifndef CONFIG_NAND_SPL /* * Pre-calculated 256-way 1 byte column parity */ @@ -69,75 +62,90 @@ static const u_char nand_ecc_precalc_table[] = { 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 }; + +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2: line parity reg 2 + * @reg3: line parity reg 3 + * @ecc_code: ecc + * + * Creates non-inverted ECC code from line parity + */ +static void nand_trans_result(u_char reg2, u_char reg3, + u_char *ecc_code) +{ + u_char a, b, i, tmp1, tmp2; + + /* Initialize variables */ + a = b = 0x80; + tmp1 = tmp2 = 0; + + /* Calculate first ECC byte */ + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + a >>= 1; + } + + /* Calculate second ECC byte */ + b = 0x80; + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + a >>= 1; + } + + /* Store two of the ECC bytes */ + ecc_code[0] = tmp1; + ecc_code[1] = tmp2; +} + /** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block * @mtd: MTD block structure * @dat: raw data * @ecc_code: buffer for ECC */ -int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { - uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; - int i; + u_char idx, reg1, reg2, reg3; + int j; /* Initialize variables */ reg1 = reg2 = reg3 = 0; + ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; /* Build up column parity */ - for(i = 0; i < 256; i++) { + for(j = 0; j < 256; j++) { + /* Get CP0 - CP5 from table */ - idx = nand_ecc_precalc_table[*dat++]; + idx = nand_ecc_precalc_table[dat[j]]; reg1 ^= (idx & 0x3f); /* All bit XOR = 1 ? */ if (idx & 0x40) { - reg3 ^= (uint8_t) i; - reg2 ^= ~((uint8_t) i); + reg3 ^= (u_char) j; + reg2 ^= ~((u_char) j); } } /* Create non-inverted ECC code from line parity */ - tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ - tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ - tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ - tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ - tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ - tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ - tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ - tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ - - tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ - tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ - tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ - tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ - tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ - tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ - tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ - tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ + nand_trans_result(reg2, reg3, ecc_code); /* Calculate final ECC code */ -#ifdef CONFIG_MTD_NAND_ECC_SMC - ecc_code[0] = ~tmp2; - ecc_code[1] = ~tmp1; -#else - ecc_code[0] = ~tmp1; - ecc_code[1] = ~tmp2; -#endif + ecc_code[0] = ~ecc_code[0]; + ecc_code[1] = ~ecc_code[1]; ecc_code[2] = ((~reg1) << 2) | 0x03; - return 0; } -#endif /* CONFIG_NAND_SPL */ - -static inline int countbits(uint32_t byte) -{ - int res = 0; - - for (;byte; byte >>= 1) - res += byte & 0x01; - return res; -} /** * nand_correct_data - [NAND Interface] Detect and correct bit error(s) @@ -148,53 +156,89 @@ static inline int countbits(uint32_t byte) * * Detect and correct a 1 bit error for 256 byte block */ -int nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - uint8_t s0, s1, s2; - -#ifdef CONFIG_MTD_NAND_ECC_SMC - s0 = calc_ecc[0] ^ read_ecc[0]; - s1 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; -#else - s1 = calc_ecc[0] ^ read_ecc[0]; - s0 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; -#endif - if ((s0 | s1 | s2) == 0) - return 0; - - /* Check for a single bit error */ - if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && - ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && - ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + u_char a, b, c, d1, d2, d3, add, bit, i; - uint32_t byteoffs, bitnum; + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; - byteoffs = (s1 << 0) & 0x80; - byteoffs |= (s1 << 1) & 0x40; - byteoffs |= (s1 << 2) & 0x20; - byteoffs |= (s1 << 3) & 0x10; - - byteoffs |= (s0 >> 4) & 0x08; - byteoffs |= (s0 >> 3) & 0x04; - byteoffs |= (s0 >> 2) & 0x02; - byteoffs |= (s0 >> 1) & 0x01; - - bitnum = (s2 >> 5) & 0x04; - bitnum |= (s2 >> 4) & 0x02; - bitnum |= (s2 >> 3) & 0x01; - - dat[byteoffs] ^= (1 << bitnum); - - return 1; + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + else { + a = (d1 ^ (d1 >> 1)) & 0x55; + b = (d2 ^ (d2 >> 1)) & 0x55; + c = (d3 ^ (d3 >> 1)) & 0x54; + + /* Found and will correct single bit error in the data */ + if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { + c = 0x80; + add = 0; + a = 0x80; + for (i=0; i<4; i++) { + if (d1 & c) + add |= a; + c >>= 2; + a >>= 1; + } + c = 0x80; + for (i=0; i<4; i++) { + if (d2 & c) + add |= a; + c >>= 2; + a >>= 1; + } + bit = 0; + b = 0x04; + c = 0x80; + for (i=0; i<3; i++) { + if (d3 & c) + bit |= b; + c >>= 2; + b >>= 1; + } + b = 0x01; + a = dat[add]; + a ^= (b << bit); + dat[add] = a; + return 1; + } else { + i = 0; + while (d1) { + if (d1 & 0x01) + ++i; + d1 >>= 1; + } + while (d2) { + if (d2 & 0x01) + ++i; + d2 >>= 1; + } + while (d3) { + if (d3 & 0x01) + ++i; + d3 >>= 1; + } + if (i == 1) { + /* ECC Code Error Correction */ + read_ecc[0] = calc_ecc[0]; + read_ecc[1] = calc_ecc[1]; + read_ecc[2] = calc_ecc[2]; + return 2; + } + else { + /* Uncorrectable Error */ + return -1; + } + } } - if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) - return 1; - + /* Should never happen */ return -1; } -#endif +#endif /* CONFIG_COMMANDS & CFG_CMD_NAND */ diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 524b6b19a7e..e493002ccd0 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -61,15 +61,26 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, /* These are the new chips with large page size. The pagesize * and the erasesize is determined from the extended id bytes */ + /*512 Megabit */ + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, @@ -121,6 +132,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_NATIONAL, "National"}, {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix"}, {NAND_MFR_MICRON, "Micron"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile new file mode 100644 index 00000000000..5941235d8ac --- /dev/null +++ b/drivers/mtd/spi/Makefile @@ -0,0 +1,49 @@ +# +# (C) Copyright 2006 +# 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 $(TOPDIR)/config.mk + +LIB := $(obj)libspi_flash.a + +COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o +COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o +COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o +COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c new file mode 100644 index 00000000000..fb7a4a939b0 --- /dev/null +++ b/drivers/mtd/spi/atmel.c @@ -0,0 +1,362 @@ +/* + * Atmel SPI DataFlash support + * + * Copyright (C) 2008 Atmel Corporation + */ +#define DEBUG +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* AT45-specific commands */ +#define CMD_AT45_READ_STATUS 0xd7 +#define CMD_AT45_ERASE_PAGE 0x81 +#define CMD_AT45_LOAD_PROG_BUF1 0x82 +#define CMD_AT45_LOAD_BUF1 0x84 +#define CMD_AT45_LOAD_PROG_BUF2 0x85 +#define CMD_AT45_LOAD_BUF2 0x87 +#define CMD_AT45_PROG_BUF1 0x88 +#define CMD_AT45_PROG_BUF2 0x89 + +/* AT45 status register bits */ +#define AT45_STATUS_P2_PAGE_SIZE (1 << 0) +#define AT45_STATUS_READY (1 << 7) + +/* DataFlash family IDs, as obtained from the second idcode byte */ +#define DF_FAMILY_AT26F 0 +#define DF_FAMILY_AT45 1 +#define DF_FAMILY_AT26DF 2 /* AT25DF and AT26DF */ + +struct atmel_spi_flash_params { + u8 idcode1; + /* Log2 of page size in power-of-two mode */ + u8 l2_page_size; + u8 pages_per_block; + u8 blocks_per_sector; + u8 nr_sectors; + const char *name; +}; + +struct atmel_spi_flash { + const struct atmel_spi_flash_params *params; + struct spi_flash flash; +}; + +static inline struct atmel_spi_flash * +to_atmel_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct atmel_spi_flash, flash); +} + +static const struct atmel_spi_flash_params atmel_spi_flash_table[] = { + { + .idcode1 = 0x28, + .l2_page_size = 10, + .pages_per_block = 8, + .blocks_per_sector = 32, + .nr_sectors = 32, + .name = "AT45DB642D", + }, +}; + +static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 cmd = CMD_AT45_READ_STATUS; + u8 status; + + timebase = get_timer(0); + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) + return -1; + + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if (status & AT45_STATUS_READY) + break; + } while (get_timer(timebase) < timeout); + + /* Deactivate CS */ + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if (status & AT45_STATUS_READY) + return 0; + + /* Timed out */ + return -1; +} + +/* + * Assemble the address part of a command for AT45 devices in + * non-power-of-two page size mode. + */ +static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset) +{ + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + + /* + * The "extra" space per page is the power-of-two page size + * divided by 32. + */ + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + cmd[0] = page_addr >> (16 - page_shift); + cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[2] = byte_addr; +} + +static int dataflash_read_fast_p2(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = offset >> 16; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int dataflash_read_fast_at45(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + u8 cmd[5]; + + cmd[0] = CMD_READ_ARRAY_FAST; + at45_build_address(asf, cmd + 1, offset); + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int dataflash_write_at45(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + unsigned int page_shift; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + /* Use the same address bits for both commands */ + cmd[0] = CMD_AT45_LOAD_BUF1; + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8); + cmd[3] = byte_addr; + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Loading AT45 buffer failed\n"); + goto out; + } + + cmd[0] = CMD_AT45_PROG_BUF1; + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page programming failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page programming timed out\n"); + goto out; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: AT45: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len) +{ + struct atmel_spi_flash *asf = to_atmel_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + unsigned int page_shift; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * TODO: This function currently uses page erase only. We can + * probably speed things up by using block and/or sector erase + * when possible. + */ + + page_shift = asf->params->l2_page_size; + page_size = (1 << page_shift) + (1 << (page_shift - 5)); + page_shift++; + page_addr = offset / page_size; + + if (offset % page_size || len % page_size) { + debug("SF: Erase offset/length not multiple of page size\n"); + return -1; + } + + cmd[0] = CMD_AT45_ERASE_PAGE; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + for (actual = 0; actual < len; actual += page_size) { + cmd[1] = page_addr >> (16 - page_shift); + cmd[2] = page_addr << (page_shift - 8); + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: AT45 page erase failed\n"); + goto out; + } + + ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: AT45 page erase timed out\n"); + goto out; + } + + page_addr++; + } + + debug("SF: AT45: Successfully erased %u bytes @ 0x%x\n", + len, offset); + ret = 0; + +out: + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode) +{ + const struct atmel_spi_flash_params *params; + unsigned long page_size; + unsigned int family; + struct atmel_spi_flash *asf; + unsigned int i; + int ret; + u8 status; + + for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) { + params = &atmel_spi_flash_table[i]; + if (params->idcode1 == idcode[1]) + break; + } + + if (i == ARRAY_SIZE(atmel_spi_flash_table)) { + debug("SF: Unsupported DataFlash ID %02x\n", + idcode[1]); + return NULL; + } + + asf = malloc(sizeof(struct atmel_spi_flash)); + if (!asf) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + asf->params = params; + asf->flash.spi = spi; + asf->flash.name = params->name; + + /* Assuming power-of-two page size initially. */ + page_size = 1 << params->l2_page_size; + + family = idcode[1] >> 5; + + switch (family) { + case DF_FAMILY_AT45: + /* + * AT45 chips have configurable page size. The status + * register indicates which configuration is active. + */ + ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1); + if (ret) + goto err; + + debug("SF: AT45 status register: %02x\n", status); + + if (!(status & AT45_STATUS_P2_PAGE_SIZE)) { + asf->flash.read = dataflash_read_fast_at45; + asf->flash.write = dataflash_write_at45; + asf->flash.erase = dataflash_erase_at45; + page_size += 1 << (params->l2_page_size - 5); + } else { + asf->flash.read = dataflash_read_fast_p2; + } + + break; + + case DF_FAMILY_AT26F: + case DF_FAMILY_AT26DF: + asf->flash.read = dataflash_read_fast_p2; + break; + + default: + debug("SF: Unsupported DataFlash family %u\n", family); + goto err; + } + + asf->flash.size = page_size * params->pages_per_block + * params->blocks_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, page_size, asf->flash.size); + + return &asf->flash; + +err: + free(asf); + return NULL; +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c new file mode 100644 index 00000000000..6a3ec89f555 --- /dev/null +++ b/drivers/mtd/spi/spi_flash.c @@ -0,0 +1,172 @@ +/* + * SPI flash interface + * + * Copyright (C) 2008 Atmel Corporation + */ +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, 8, &cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + if (len) { + ret = spi_xfer(spi, len * 8, NULL, response, SPI_XFER_END); + if (ret) + debug("SF: Failed to read response (%zu bytes): %d\n", + len, ret); + } + + return ret; +} + +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send read command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, NULL, data, SPI_XFER_END); + if (ret) + debug("SF: Failed to read %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send read command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data, NULL, SPI_XFER_END); + if (ret) + debug("SF: Failed to read %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + + +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len) +{ + struct spi_slave *spi = flash->spi; + int ret; + + spi_claim_bus(spi); + ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); + spi_release_bus(spi); + + return ret; +} + +struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *spi; + struct spi_flash *flash; + int ret; + u8 idcode[3]; + + spi = spi_setup_slave(bus, cs, max_hz, spi_mode); + if (!spi) { + debug("SF: Failed to set up slave\n"); + return NULL; + } + + ret = spi_claim_bus(spi); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + goto err_claim_bus; + } + + /* Read the ID codes */ + ret = spi_flash_cmd(spi, CMD_READ_ID, &idcode, sizeof(idcode)); + if (ret) + goto err_read_id; + + debug("SF: Got idcode %02x %02x %02x\n", idcode[0], + idcode[1], idcode[2]); + + switch (idcode[0]) { +#ifdef CONFIG_SPI_FLASH_SPANSION + case 0x01: + flash = spi_flash_probe_spansion(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_ATMEL + case 0x1F: + flash = spi_flash_probe_atmel(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_WINBOND + case 0xef: + flash = spi_flash_probe_winbond(spi, idcode); + break; +#endif +#ifdef CONFIG_SPI_FLASH_STMICRO + case 0x00: + case 0xff: + flash = spi_flash_probe_stmicro(spi, idcode); + break; +#endif + default: + debug("SF: Unsupported manufacturer %02X\n", idcode[0]); + flash = NULL; + break; + } + + if (!flash) + goto err_manufacturer_probe; + + spi_release_bus(spi); + + return flash; + +err_manufacturer_probe: +err_read_id: + spi_release_bus(spi); +err_claim_bus: + spi_free_slave(spi); + return NULL; +} + +void spi_flash_free(struct spi_flash *flash) +{ + spi_free_slave(flash->spi); + free(flash); +} diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h new file mode 100644 index 00000000000..1eacb5c7487 --- /dev/null +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -0,0 +1,47 @@ +/* + * SPI flash internal definitions + * + * Copyright (C) 2008 Atmel Corporation + */ + +/* Common parameters */ +#define SPI_FLASH_PROG_TIMEOUT ((10 * CFG_HZ) / 1000) +#define SPI_FLASH_PAGE_ERASE_TIMEOUT ((50 * CFG_HZ) / 1000) +#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CFG_HZ) + +/* Common commands */ +#define CMD_READ_ID 0x9f + +#define CMD_READ_ARRAY_SLOW 0x03 +#define CMD_READ_ARRAY_FAST 0x0b +#define CMD_READ_ARRAY_LEGACY 0xe8 + +/* Send a single-byte command to the device and read the response */ +int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); + +/* + * Send a multi-byte command to the device and read the response. Used + * for flash array reads, etc. + */ +int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* + * Send a multi-byte command to the device followed by (optional) + * data. Used for programming the flash array, etc. + */ +int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len); + +/* + * Same as spi_flash_cmd_read() except it also claims/releases the SPI + * bus. Used as common part of the ->read() operation. + */ +int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, void *data, size_t data_len); + +/* Manufacturer-specific probe functions */ +struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode); diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c new file mode 100644 index 00000000000..b8b835a3ffe --- /dev/null +++ b/drivers/mtd/spi/stmicro.c @@ -0,0 +1,356 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.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 <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* M25Pxx-specific commands */ +#define CMD_M25PXX_WREN 0x06 /* Write Enable */ +#define CMD_M25PXX_WRDI 0x04 /* Write Disable */ +#define CMD_M25PXX_RDSR 0x05 /* Read Status Register */ +#define CMD_M25PXX_WRSR 0x01 /* Write Status Register */ +#define CMD_M25PXX_READ 0x03 /* Read Data Bytes */ +#define CMD_M25PXX_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_M25PXX_PP 0x02 /* Page Program */ +#define CMD_M25PXX_SE 0xd8 /* Sector Erase */ +#define CMD_M25PXX_BE 0xc7 /* Bulk Erase */ +#define CMD_M25PXX_DP 0xb9 /* Deep Power-down */ +#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */ + +#define STM_ID_M25P16 0x15 +#define STM_ID_M25P20 0x12 +#define STM_ID_M25P32 0x16 +#define STM_ID_M25P40 0x13 +#define STM_ID_M25P64 0x17 +#define STM_ID_M25P80 0x14 +#define STM_ID_M25P128 0x18 + +#define STMICRO_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct stmicro_spi_flash_params { + u8 idcode1; + u16 page_size; + u16 pages_per_sector; + u16 nr_sectors; + const char *name; +}; + +struct stmicro_spi_flash { + const struct stmicro_spi_flash_params *params; + struct spi_flash flash; +}; + +static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash + *flash) +{ + return container_of(flash, struct stmicro_spi_flash, flash); +} + +static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = { + { + .idcode1 = STM_ID_M25P16, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 32, + .name = "M25P16", + }, + { + .idcode1 = STM_ID_M25P20, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 4, + .name = "M25P20", + }, + { + .idcode1 = STM_ID_M25P32, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 64, + .name = "M25P32", + }, + { + .idcode1 = STM_ID_M25P40, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 8, + .name = "M25P40", + }, + { + .idcode1 = STM_ID_M25P64, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 128, + .name = "M25P64", + }, + { + .idcode1 = STM_ID_M25P80, + .page_size = 256, + .pages_per_sector = 256, + .nr_sectors = 16, + .name = "M25P80", + }, + { + .idcode1 = STM_ID_M25P128, + .page_size = 256, + .pages_per_sector = 1024, + .nr_sectors = 64, + .name = "M25P128", + }, +}; + +static int stmicro_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 cmd[4] = { CMD_M25PXX_RDSR, 0xff, 0xff, 0xff }; + + ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & STMICRO_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & STMICRO_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int stmicro_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + u8 cmd[5]; + + page_size = stm->params->page_size; + page_addr = offset / page_size; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = offset % page_size; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int stmicro_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_size = stm->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_M25PXX_PP; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = byte_addr; + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: STMicro Page Program failed\n"); + break; + } + + ret = stmicro_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: STMicro page programming timed out\n"); + break; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: STMicro: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash); + unsigned long sector_size; + size_t actual; + int ret; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + sector_size = stm->params->page_size * stm->params->pages_per_sector; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_M25PXX_SE; + cmd[2] = 0x00; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + cmd[1] = (offset / sector_size) + actual; + + ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: STMicro page erase failed\n"); + break; + } + + /* Up to 2 seconds */ + ret = stmicro_wait_ready(flash, 2 * CONFIG_SYS_HZ); + if (ret < 0) { + debug("SF: STMicro page erase timed out\n"); + break; + } + } + + debug("SF: STMicro: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode) +{ + const struct stmicro_spi_flash_params *params; + struct stmicro_spi_flash *stm; + unsigned int i; + int ret; + u8 id[3]; + + ret = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); + if (ret) + return NULL; + + for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) { + params = &stmicro_spi_flash_table[i]; + if (params->idcode1 == idcode[2]) { + break; + } + } + + if (i == ARRAY_SIZE(stmicro_spi_flash_table)) { + debug("SF: Unsupported STMicro ID %02x\n", id[1]); + return NULL; + } + + stm = malloc(sizeof(struct stmicro_spi_flash)); + if (!stm) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + stm->params = params; + stm->flash.spi = spi; + stm->flash.name = params->name; + + stm->flash.write = stmicro_write; + stm->flash.erase = stmicro_erase; + stm->flash.read = stmicro_read_fast; + stm->flash.size = params->page_size * params->pages_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, stm->flash.size); + + return &stm->flash; +} diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c new file mode 100644 index 00000000000..5fa4ce056f1 --- /dev/null +++ b/drivers/mtd/spi/winbond.c @@ -0,0 +1,334 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright 2008, Texas Instrumemnts Inc. + * Modified to suppport Winbond flash. + * + * Copyright 2008, Network Appliance Inc. + * Jason McMullan <mcmullan@netapp.com> + * + * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.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 <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* W25 specific commands. Derived from section 12.2 of + * http://www.winbond-usa.com/products/Nexflash/pdfs/datasheets/W25X16_32_64h.pdf + */ +#define CMD_W25_WREN 0x06 /* Write Enable */ +#define CMD_W25_WRDI 0x04 /* Write Disable */ +#define CMD_W25_RDSR 0x05 /* Read Status Register */ +#define CMD_W25_WRSR 0x01 /* Write Status Register */ +#define CMD_W25_READ 0x03 /* Read Data Bytes */ +#define CMD_W25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_W25_PP 0x02 /* Page Program */ +#define CMD_W25_SE 0x20 /* Sector Erase */ +#define CMD_W25_BE 0xd8 /* Bulk Erase */ +#define CMD_W25_DP 0xb9 /* Deep Power-down */ +#define CMD_W25_RES 0xab /* Release from DP, and Read Signature */ + +#define WINBOND_ID_W25X16 0x3015 +#define WINBOND_ID_W25X32 0x3016 +#define WINBOND_ID_W25X64 0x3017 + +#define WINBOND_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct winbond_spi_flash_params { + u16 idcode; + u16 page_size; + u16 pages_per_sector; + u16 nr_sectors; + const char *name; +}; + +struct winbond_spi_flash { + const struct winbond_spi_flash_params *params; + struct spi_flash flash; +}; + +static inline struct winbond_spi_flash *to_winbond_spi_flash(struct spi_flash + *flash) +{ + return container_of(flash, struct winbond_spi_flash, flash); +} + +/* W25 specific flash parameters. Derived from chapter 1 of + * http://www.winbond-usa.com/products/Nexflash/pdfs/datasheets/W25X16_32_64h.pdf + */ +static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { + { + .idcode = WINBOND_ID_W25X32, + .page_size = 256, + .pages_per_sector = 16, + .nr_sectors = 1024, + .name = "W25X32", + }, + { + .idcode = WINBOND_ID_W25X16, + .page_size = 256, + .pages_per_sector = 16, + .nr_sectors = 512, + .name = "W25X16", + }, + { + .idcode = WINBOND_ID_W25X64, + .page_size = 256, + .pages_per_sector = 16, + .nr_sectors = 2048, + .name = "W25X64", + }, + +}; + +static int winbond_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 status; + u8 cmd[4] = { CMD_W25_RDSR, 0xff, 0xff, 0xff }; + + ret = spi_xfer(spi, 32, &cmd[0], NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & WINBOND_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & WINBOND_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int winbond_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct winbond_spi_flash *win = to_winbond_spi_flash(flash); + u8 cmd[5]; + int i, j; + + cmd[0] = CMD_READ_ARRAY_FAST; + for (i = 0, j = 1; i < 24; i += 8) + cmd[j++] = ((offset >> (16 - i)) & 0xFF); + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int winbond_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct winbond_spi_flash *win = to_winbond_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret, i, j; + u8 cmd[4]; + + page_size = win->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_W25_PP; + for (i = 0, j = 1; i < 24; i += 8) + cmd[j++] = ((offset >> (16 - i)) & 0xFF); + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: Winbond Page Program failed\n"); + break; + } + + ret = winbond_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: Winbond page programming timed out\n"); + break; + } + + page_addr++; + offset += chunk_len; + byte_addr = 0; + } + + debug("SF: Winbond: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int winbond_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + struct winbond_spi_flash *win = to_winbond_spi_flash(flash); + unsigned long sector_size; + size_t actual; + int ret, i , j; + u8 cmd[4]; + + /* + * This function currently uses sector erase only. + * probably speed things up by using bulk erase + * when possible. + */ + + sector_size = win->params->page_size * win->params->pages_per_sector; + + if (offset % sector_size || len % sector_size) { + debug("SF: Erase offset/length not multiple of sector size\n"); + return -1; + } + + len /= sector_size; + cmd[0] = CMD_W25_SE; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + + for (i = 0, j = 1; i < 24; i += 8) + cmd[j++] = ((offset >> (16 - i)) & 0xFF); + + debug + ("SE: cmd = { 0x%02x 0x%02x%02x%02x }\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + ret = spi_flash_cmd(flash->spi, CMD_W25_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: Winbond page erase failed\n"); + break; + } + + /* Up to 2 seconds */ + ret = winbond_wait_ready(flash, 2 * CFG_HZ); + if (ret < 0) { + debug("SF: Winbond page erase timed out\n"); + break; + } + + offset += sector_size; + } + + debug("SF: Winbond: Successfully erased %u bytes @ 0x%x\n", + len * sector_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 * idcode) +{ + const struct winbond_spi_flash_params *params; + struct winbond_spi_flash *win; + unsigned int i; + int ret; + u8 id[3]; + u16 idmatch = ((idcode[1] << 8) | idcode[2]); + + ret = spi_flash_cmd(spi, CMD_READ_ID, id, sizeof(id)); + if (ret) + return NULL; + + for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) { + params = &winbond_spi_flash_table[i]; + if (params->idcode == idmatch) + break; + } + + if (i == ARRAY_SIZE(winbond_spi_flash_table)) { + debug("SF: Unsupported Winbond ID %02x\n", idmatch); + return NULL; + } + + win = malloc(sizeof(struct winbond_spi_flash)); + if (!win) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + win->params = params; + win->flash.spi = spi; + win->flash.name = params->name; + + win->flash.write = winbond_write; + win->flash.erase = winbond_erase; + win->flash.read = winbond_read_fast; + win->flash.size = params->page_size * params->pages_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, win->flash.size); + + return &win->flash; +} diff --git a/drivers/rtc/ds1306.c b/drivers/rtc/ds1306.c index 1c8ac7f2927..29854fc7c4c 100644 --- a/drivers/rtc/ds1306.c +++ b/drivers/rtc/ds1306.c @@ -62,13 +62,6 @@ #define RTC_USER_RAM_BASE 0x20 -/* - * External table of chip select functions (see the appropriate board - * support for the actual definition of the table). - */ -extern spi_chipsel_type spi_chipsel[]; -extern int spi_chipsel_cnt; - static unsigned int bin2bcd (unsigned int n); static unsigned char bcd2bin (unsigned char c); @@ -305,11 +298,29 @@ void rtc_reset (void) static unsigned char rtc_read (unsigned char reg); static void rtc_write (unsigned char reg, unsigned char val); +static struct spi_slave *slave; + /* read clock time from DS1306 and return it in *tmp */ int rtc_get (struct rtc_time *tmp) { unsigned char sec, min, hour, mday, wday, mon, year; + /* + * Assuming Vcc = 2.0V (lowest speed) + * + * REVISIT: If we add an rtc_init() function we can do this + * step just once. + */ + if (!slave) { + slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + sec = rtc_read (RTC_SECONDS); min = rtc_read (RTC_MINUTES); hour = rtc_read (RTC_HOURS); @@ -318,6 +329,8 @@ int rtc_get (struct rtc_time *tmp) mon = rtc_read (RTC_MONTH); year = rtc_read (RTC_YEAR); + spi_release_bus(slave); + debug ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " "hr: %02x min: %02x sec: %02x\n", year, mon, mday, wday, hour, min, sec); @@ -360,6 +373,17 @@ int rtc_get (struct rtc_time *tmp) /* set clock time from *tmp in DS1306 RTC */ void rtc_set (struct rtc_time *tmp) { + /* Assuming Vcc = 2.0V (lowest speed) */ + if (!slave) { + slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + debug ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); @@ -371,6 +395,8 @@ void rtc_set (struct rtc_time *tmp) rtc_write (RTC_DATE_OF_MONTH, bin2bcd (tmp->tm_mday)); rtc_write (RTC_MONTH, bin2bcd (tmp->tm_mon)); rtc_write (RTC_YEAR, bin2bcd (tmp->tm_year - 2000)); + + spi_release_bus(slave); } /* ------------------------------------------------------------------------- */ @@ -378,6 +404,17 @@ void rtc_set (struct rtc_time *tmp) /* reset the DS1306 */ void rtc_reset (void) { + /* Assuming Vcc = 2.0V (lowest speed) */ + if (!slave) { + slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + /* clear the control register */ rtc_write (RTC_CONTROL, 0x00); /* 1st step: reset WP */ rtc_write (RTC_CONTROL, 0x00); /* 2nd step: reset 1Hz, AIE1, AIE0 */ @@ -391,22 +428,18 @@ void rtc_reset (void) rtc_write (RTC_HOURS_ALARM1, 0x00); rtc_write (RTC_DAY_OF_WEEK_ALARM0, 0x00); rtc_write (RTC_DAY_OF_WEEK_ALARM1, 0x00); + + spi_release_bus(slave); } /* ------------------------------------------------------------------------- */ static unsigned char rtc_read (unsigned char reg) { - unsigned char dout[2]; /* SPI Output Data Bytes */ - unsigned char din[2]; /* SPI Input Data Bytes */ - - dout[0] = reg; + int ret; - if (spi_xfer (spi_chipsel[CFG_SPI_RTC_DEVID], 16, dout, din) != 0) { - return 0; - } else { - return din[1]; - } + ret = spi_w8r8(slave, reg); + return ret < 0 ? 0 : ret; } /* ------------------------------------------------------------------------- */ @@ -419,7 +452,7 @@ static void rtc_write (unsigned char reg, unsigned char val) dout[0] = 0x80 | reg; dout[1] = val; - spi_xfer (spi_chipsel[CFG_SPI_RTC_DEVID], 16, dout, din); + spi_xfer (slave, 16, dout, din, SPI_XFER_BEGIN | SPI_XFER_END); } #endif /* end of code exclusion (see #ifdef CONFIG_SXNI855T above) */ diff --git a/drivers/rtc/mc13783-rtc.c b/drivers/rtc/mc13783-rtc.c index 35b1b8b254d..b6e15014bb6 100644 --- a/drivers/rtc/mc13783-rtc.c +++ b/drivers/rtc/mc13783-rtc.c @@ -24,34 +24,50 @@ #include <rtc.h> #include <spi.h> +static struct spi_slave *slave; + int rtc_get(struct rtc_time *rtc) { u32 day1, day2, time; u32 reg; int err, tim, i = 0; - spi_select(1, 0, SPI_MODE_2 | SPI_CS_HIGH); + if (!slave) { + /* FIXME: Verify the max SCK rate */ + slave = spi_setup_slave(1, 0, 1000000, + SPI_MODE_2 | SPI_CS_HIGH); + if (!slave) + return -1; + } + + if (spi_claim_bus(slave)) + return -1; do { reg = 0x2c000000; - err = spi_xfer(0, 32, (uchar *)®, (uchar *)&day1); + err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&day1, + SPI_XFER_BEGIN | SPI_XFER_END); if (err) return err; reg = 0x28000000; - err = spi_xfer(0, 32, (uchar *)®, (uchar *)&time); + err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&time, + SPI_XFER_BEGIN | SPI_XFER_END); if (err) return err; reg = 0x2c000000; - err = spi_xfer(0, 32, (uchar *)®, (uchar *)&day2); + err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&day2, + SPI_XFER_BEGIN | SPI_XFER_END); if (err) return err; } while (day1 != day2 && i++ < 3); + spi_release_bus(slave); + tim = day1 * 86400 + time; to_tm(tim, rtc); @@ -65,16 +81,31 @@ void rtc_set(struct rtc_time *rtc) { u32 time, day, reg; + if (!slave) { + /* FIXME: Verify the max SCK rate */ + slave = spi_setup_slave(1, 0, 1000000, + SPI_MODE_2 | SPI_CS_HIGH); + if (!slave) + return; + } + time = mktime(rtc->tm_year, rtc->tm_mon, rtc->tm_mday, rtc->tm_hour, rtc->tm_min, rtc->tm_sec); day = time / 86400; time %= 86400; + if (spi_claim_bus(slave)) + return; + reg = 0x2c000000 | day | 0x80000000; - spi_xfer(0, 32, (uchar *)®, (uchar *)&day); + spi_xfer(slave, 32, (uchar *)®, (uchar *)&day, + SPI_XFER_BEGIN | SPI_XFER_END); reg = 0x28000000 | time | 0x80000000; - spi_xfer(0, 32, (uchar *)®, (uchar *)&time); + spi_xfer(slave, 32, (uchar *)®, (uchar *)&time, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(slave); } void rtc_reset(void) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bc8a1041210..a917ba5c1d6 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -26,7 +26,9 @@ include $(TOPDIR)/config.mk LIB := $(obj)libspi.a COBJS-y += mpc8xxx_spi.o +COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o +COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c new file mode 100644 index 00000000000..317c0b41b6f --- /dev/null +++ b/drivers/spi/atmel_spi.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * 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 <spi.h> +#include <malloc.h> + +#include <asm/io.h> + +#include <asm/arch/clk.h> +#include <asm/arch/memory-map.h> + +#include "atmel_spi.h" + +void spi_init() +{ + +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct atmel_spi_slave *as; + unsigned int scbr; + u32 csrx; + void *regs; + + if (cs > 3 || !spi_cs_is_valid(bus, cs)) + return NULL; + + switch (bus) { + case 0: + regs = (void *)SPI0_BASE; + break; +#ifdef SPI1_BASE + case 1: + regs = (void *)SPI1_BASE; + break; +#endif +#ifdef SPI2_BASE + case 2: + regs = (void *)SPI2_BASE; + break; +#endif +#ifdef SPI3_BASE + case 3: + regs = (void *)SPI3_BASE; + break; +#endif + default: + return NULL; + } + + + scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz; + if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) + /* Too low max SCK rate */ + return NULL; + if (scbr < 1) + scbr = 1; + + csrx = ATMEL_SPI_CSRx_SCBR(scbr); + csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); + if (!(mode & SPI_CPHA)) + csrx |= ATMEL_SPI_CSRx_NCPHA; + if (mode & SPI_CPOL) + csrx |= ATMEL_SPI_CSRx_CPOL; + + as = malloc(sizeof(struct atmel_spi_slave)); + if (!as) + return NULL; + + as->slave.bus = bus; + as->slave.cs = cs; + as->regs = regs; + as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS + | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); + spi_writel(as, CSR(cs), csrx); + + return &as->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + free(as); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + /* Enable the SPI hardware */ + spi_writel(as, CR, ATMEL_SPI_CR_SPIEN); + + /* + * Select the slave. This should set SCK to the correct + * initial state, etc. + */ + spi_writel(as, MR, as->mr); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + + /* Disable the SPI hardware */ + spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct atmel_spi_slave *as = to_atmel_spi(slave); + unsigned int len_tx; + unsigned int len_rx; + unsigned int len; + int ret; + u32 status; + const u8 *txp = dout; + u8 *rxp = din; + u8 value; + + ret = 0; + if (bitlen == 0) + /* Finish any previously submitted transfers */ + goto out; + + /* + * TODO: The controller can do non-multiple-of-8 bit + * transfers, but this driver currently doesn't support it. + * + * It's also not clear how such transfers are supposed to be + * represented as a stream of bytes...this is a limitation of + * the current SPI interface. + */ + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + /* + * The controller can do automatic CS control, but it is + * somewhat quirky, and it doesn't really buy us much anyway + * in the context of U-Boot. + */ + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + for (len_tx = 0, len_rx = 0; len_rx < len; ) { + status = spi_readl(as, SR); + + if (status & ATMEL_SPI_SR_OVRES) + return -1; + + if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) { + if (txp) + value = *txp++; + else + value = 0; + spi_writel(as, TDR, value); + len_tx++; + } + if (status & ATMEL_SPI_SR_RDRF) { + value = spi_readl(as, RDR); + if (rxp) + *rxp++ = value; + len_rx++; + } + } + +out: + if (flags & SPI_XFER_END) { + /* + * Wait until the transfer is completely done before + * we deactivate CS. + */ + do { + status = spi_readl(as, SR); + } while (!(status & ATMEL_SPI_SR_TXEMPTY)); + + spi_cs_deactivate(slave); + } + + return 0; +} diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h new file mode 100644 index 00000000000..2baf9c76ef2 --- /dev/null +++ b/drivers/spi/atmel_spi.h @@ -0,0 +1,95 @@ +/* + * Register definitions for the DaVinci SPI Controller + */ + +/* Register offsets */ +#define DAVINCI_SPI_CR 0x0000 +#define ATMEL_SPI_MR 0x0004 +#define ATMEL_SPI_RDR 0x0008 +#define ATMEL_SPI_TDR 0x000c +#define ATMEL_SPI_SR 0x0010 +#define ATMEL_SPI_IER 0x0014 +#define ATMEL_SPI_IDR 0x0018 +#define ATMEL_SPI_IMR 0x001c +#define ATMEL_SPI_CSR(x) (0x0030 + 4 * (x)) +#define ATMEL_SPI_VERSION 0x00fc + +/* Bits in CR */ +#define ATMEL_SPI_CR_SPIEN (1 << 0) +#define ATMEL_SPI_CR_SPIDIS (1 << 1) +#define ATMEL_SPI_CR_SWRST (1 << 7) +#define ATMEL_SPI_CR_LASTXFER (1 << 24) + +/* Bits in MR */ +#define ATMEL_SPI_MR_MSTR (1 << 0) +#define ATMEL_SPI_MR_PS (1 << 1) +#define ATMEL_SPI_MR_PCSDEC (1 << 2) +#define ATMEL_SPI_MR_FDIV (1 << 3) +#define ATMEL_SPI_MR_MODFDIS (1 << 4) +#define ATMEL_SPI_MR_LLB (1 << 7) +#define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16) +#define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24) + +/* Bits in RDR */ +#define ATMEL_SPI_RDR_RD(x) (x) +#define ATMEL_SPI_RDR_PCS(x) ((x) << 16) + +/* Bits in TDR */ +#define ATMEL_SPI_TDR_TD(x) (x) +#define ATMEL_SPI_TDR_PCS(x) ((x) << 16) +#define ATMEL_SPI_TDR_LASTXFER (1 << 24) + +/* Bits in SR/IER/IDR/IMR */ +#define ATMEL_SPI_SR_RDRF (1 << 0) +#define ATMEL_SPI_SR_TDRE (1 << 1) +#define ATMEL_SPI_SR_MODF (1 << 2) +#define ATMEL_SPI_SR_OVRES (1 << 3) +#define ATMEL_SPI_SR_ENDRX (1 << 4) +#define ATMEL_SPI_SR_ENDTX (1 << 5) +#define ATMEL_SPI_SR_RXBUFF (1 << 6) +#define ATMEL_SPI_SR_TXBUFE (1 << 7) +#define ATMEL_SPI_SR_NSSR (1 << 8) +#define ATMEL_SPI_SR_TXEMPTY (1 << 9) +#define ATMEL_SPI_SR_SPIENS (1 << 16) + +/* Bits in CSRx */ +#define ATMEL_SPI_CSRx_CPOL (1 << 0) +#define ATMEL_SPI_CSRx_NCPHA (1 << 1) +#define ATMEL_SPI_CSRx_CSAAT (1 << 3) +#define ATMEL_SPI_CSRx_BITS(x) ((x) << 4) +#define ATMEL_SPI_CSRx_SCBR(x) ((x) << 8) +#define ATMEL_SPI_CSRx_SCBR_MAX 0xff +#define ATMEL_SPI_CSRx_DLYBS(x) ((x) << 16) +#define ATMEL_SPI_CSRx_DLYBCT(x) ((x) << 24) + +/* Bits in VERSION */ +#define ATMEL_SPI_VERSION_REV(x) ((x) << 0) +#define ATMEL_SPI_VERSION_MFN(x) ((x) << 16) + +/* Constants for CSRx:BITS */ +#define ATMEL_SPI_BITS_8 0 +#define ATMEL_SPI_BITS_9 1 +#define ATMEL_SPI_BITS_10 2 +#define ATMEL_SPI_BITS_11 3 +#define ATMEL_SPI_BITS_12 4 +#define ATMEL_SPI_BITS_13 5 +#define ATMEL_SPI_BITS_14 6 +#define ATMEL_SPI_BITS_15 7 +#define ATMEL_SPI_BITS_16 8 + +struct atmel_spi_slave { + struct spi_slave slave; + void *regs; + u32 mr; +}; + +static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave) +{ + return container_of(slave, struct atmel_spi_slave, slave); +} + +/* Register access macros */ +#define spi_readl(as, reg) \ + readl(as->regs + ATMEL_SPI_##reg) +#define spi_writel(as, reg, value) \ + writel(value, as->regs + ATMEL_SPI_##reg) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c new file mode 100644 index 00000000000..ed895212bd0 --- /dev/null +++ b/drivers/spi/davinci_spi.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2008 Sekhar Nori, Texas Instruments, Inc <www.ti.com> + * + * Driver for SPI controller on DaVinci. Based on atmel_spi.c + * by Atmel Corporation + * + * Copyright (C) 2007 Atmel Corporation + * + * 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 <spi.h> +#include <malloc.h> + +#include <asm/io.h> + +#include <asm/arch/hardware.h> + +#include "davinci_spi.h" + +static unsigned int data1_reg_val; + + +void spi_init() +{ + /* do nothing */ + +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct davinci_spi_slave *ds; + void *regs; + unsigned int fmt0; + + ds = malloc(sizeof(struct davinci_spi_slave)); + if (!ds) + return NULL; + + ds->slave.bus = bus; + ds->slave.cs = cs; + ds->regs = CFG_SPI_BASE; + ds->freq = max_hz; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int scalar; + + /* Enable the SPI hardware */ + spi_writel(ds, GCR0, 0); + udelay(1000); + spi_writel(ds, GCR0, 1); + + /* Set master mode, powered up and not activated */ + spi_writel(ds, GCR1, 0x3); + + /* CS, CLK, SIMO and SOMI are functional pins */ + spi_writel(ds, PC0, (1 << 0) | (1 << 9) | (1 << 10) | (1 << 11)); + + /* setup format */ + scalar = ((CFG_SPI_CLK / ds->freq) - 1 ) & 0xFF; + + spi_writel(ds, FMT0, 8 | /* character length */ + (scalar << 8) | + (1 << 16) | /* clock signal delayed by half clk cycle */ + (0 << 17) | /* clock low in idle state - Mode 0 */ + (0 << 20)); /* MSB shifted out first */ + + /* hold cs active at end of transfer until explicitly de-asserted */ + data1_reg_val = (1 << 28) | (slave->cs << 16); + spi_writel(ds, DAT1, data1_reg_val); + + /* including a minor delay. No science here. Should be good even with + * no delay + */ + spi_writel(ds, DELAY, (50 << 24) | (50 << 16)); + + /* default chip select register */ + spi_writel(ds, DEF, 1); + + /* no interrupts */ + spi_writel(ds, INT0, 0); + spi_writel(ds, LVL, 0); + + /* enable SPI */ + spi_writel(ds, GCR1, spi_readl(ds, GCR1) | (1 << 24)); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + + /* Disable the SPI hardware */ + spi_writel(ds, GCR0, 0); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct davinci_spi_slave *ds = to_davinci_spi(slave); + unsigned int len_tx; + unsigned int len_rx; + unsigned int len; + int ret, i; + u32 status; + const u8 *txp = dout; + u8 *rxp = din; + u8 value, dummy = 0; + + ret = 0; + + if (bitlen == 0) + /* Finish any previously submitted transfers */ + goto out; + + /* + * It's not clear how non-8-bit-aligned transfers are supposed to be + * represented as a stream of bytes...this is a limitation of + * the current SPI interface - here we terminate on receiving such a + * transfer request. + */ + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + /* do an empty read to clear the current contents */ + spi_readl(ds, BUF); + + /* keep writing and reading 1 byte until done */ + for (i = 0; i < len; i++) { + + /* wait till TXFULL is asserted */ + while(spi_readl(ds, BUF) & (1 << 29)); + + /* write the data */ + data1_reg_val &= ~0xFFFF; + if(txp) { + data1_reg_val |= *txp & 0xFF; + txp++; + } + + /* write to DAT1 is required to keep the serial transfer going */ + /* we just terminate when we reach the end */ + if((i == (len -1)) && (flags & SPI_XFER_END)) { + spi_writel(ds, DAT1, data1_reg_val & ~(1 << 28)); /* clear CS hold */ + } else { + spi_writel(ds, DAT1, data1_reg_val); + } + + + /* read the data - wait for data availability */ + while(spi_readl(ds, BUF) & (1 << 31)); + + if(rxp) { + *rxp = spi_readl(ds, BUF) & 0xFF; + rxp++; + } else { + spi_readl(ds, BUF); /* simply drop the read character */ + } + + } + + return 0; + +out: + if (flags & SPI_XFER_END) { + spi_writel(ds, DAT1, data1_reg_val & ~(1 << 28)); + } + + return 0; +} + + +#ifdef CONFIG_CMD_EEPROM + +/* ------------------------------------------------------------------------ * + * SPI ROM Definitions * + * ------------------------------------------------------------------------ */ +#define SPIROM_SIZE 0x00008000 +#define SPIROM_BASE 0x00000000 +#define SPIROM_PAGESIZE 32 +#define SPIROM_PAGEMASK 0xffffffc0 + +/* ------------------------------------------------------------------------ * + * SPI ROM Commands * + * ------------------------------------------------------------------------ */ +#define SPIROM_CMD_WRSR 0x01 +#define SPIROM_CMD_WRITE 0x02 +#define SPIROM_CMD_READ 0x03 +#define SPIROM_CMD_WRDI 0x04 +#define SPIROM_CMD_RDSR 0x05 +#define SPIROM_CMD_WREN 0x06 + +static struct spi_slave *slave; + +void spi_init_f(void) +{ + slave = spi_setup_slave(0, 0, 1*1024*1024, 0); + spi_claim_bus(slave); +} + +static char spirombuf[3]; + +/* ------------------------------------------------------------------------ * + * spirom_status( ) * + * ------------------------------------------------------------------------ */ +static unsigned char spi_get_status( ) +{ + /* Issue read status command */ + spirombuf[0] = SPIROM_CMD_RDSR; + spirombuf[1] = 0; + + spi_xfer(slave, (2)*8, spirombuf, spirombuf, SPI_XFER_BEGIN | SPI_XFER_END); + + return spirombuf[1]; +} + +ssize_t spi_write(uchar *addr, int alen, uchar *buffer, int len) +{ + + spirombuf[0] = SPIROM_CMD_WREN; + spi_xfer(slave, 1*8, spirombuf, NULL, SPI_XFER_BEGIN | SPI_XFER_END); + + /* Create command block for program operation */ + spirombuf[0] = SPIROM_CMD_WRITE; + spirombuf[1] = addr[0]; + spirombuf[2] = addr[1]; + + spi_xfer(slave, 3 * 8, spirombuf, NULL, SPI_XFER_BEGIN); + spi_xfer(slave, len * 8, buffer, NULL, SPI_XFER_END); + + /* Wait while busy */ + while( (spi_get_status( ) & 0x01 ) ); + + return len; +} + +ssize_t spi_read(uchar *addr, int alen, uchar *buffer, int len) +{ + spirombuf[0] = 0x3; + spirombuf[1] = addr[0]; + spirombuf[2] = addr[1]; + + spi_xfer(slave, 3*8, spirombuf, NULL, SPI_XFER_BEGIN); + spi_xfer(slave, len*8, NULL, buffer, SPI_XFER_END); + + return len; +} + +#endif diff --git a/drivers/spi/davinci_spi.h b/drivers/spi/davinci_spi.h new file mode 100644 index 00000000000..0f2b0877baa --- /dev/null +++ b/drivers/spi/davinci_spi.h @@ -0,0 +1,46 @@ +/* + * Register definitions for the DaVinci SPI Controller + */ + +/* Register offsets */ +#define DAVINCI_SPI_GCR0 0x0000 +#define DAVINCI_SPI_GCR1 0x0004 +#define DAVINCI_SPI_INT0 0x0008 +#define DAVINCI_SPI_LVL 0x000c +#define DAVINCI_SPI_FLG 0x0010 +#define DAVINCI_SPI_PC0 0x0014 +#define DAVINCI_SPI_PC1 0x0018 +#define DAVINCI_SPI_PC2 0x001c +#define DAVINCI_SPI_PC3 0x0020 +#define DAVINCI_SPI_PC4 0x0024 +#define DAVINCI_SPI_PC5 0x0028 +#define DAVINCI_SPI_DAT0 0x0038 +#define DAVINCI_SPI_DAT1 0x003c +#define DAVINCI_SPI_BUF 0x0040 +#define DAVINCI_SPI_EMU 0x0044 +#define DAVINCI_SPI_DELAY 0x0048 +#define DAVINCI_SPI_DEF 0x004c +#define DAVINCI_SPI_FMT0 0x0050 +#define DAVINCI_SPI_FMT1 0x0054 +#define DAVINCI_SPI_FMT2 0x0058 +#define DAVINCI_SPI_FMT3 0x005c +#define DAVINCI_SPI_INTVEC0 0x0060 +#define DAVINCI_SPI_INTVEC1 0x0064 + +struct davinci_spi_slave { + struct spi_slave slave; + void *regs; + u32 mr; + unsigned int freq; +}; + +static inline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave) +{ + return container_of(slave, struct davinci_spi_slave, slave); +} + +#define spi_readl(as, reg) \ + readl(CFG_SPI_BASE + DAVINCI_SPI_##reg) +#define spi_writel(as, reg, value) \ + writel(value, CFG_SPI_BASE + DAVINCI_SPI_##reg) + diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index 2fe838c45d5..136fb50052f 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -24,6 +24,7 @@ #include <common.h> #if defined(CONFIG_MPC8XXX_SPI) && defined(CONFIG_HARD_SPI) +#include <malloc.h> #include <spi.h> #include <asm/mpc8xxx_spi.h> @@ -37,6 +38,34 @@ #define SPI_TIMEOUT 1000 +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = malloc(sizeof(struct spi_slave)); + if (!slave) + return NULL; + + slave->bus = bus; + slave->cs = cs; + + /* + * TODO: Some of the code in spi_init() should probably move + * here, or into spi_claim_bus() below. + */ + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + void spi_init(void) { volatile spi8xxx_t *spi = &((immap_t *) (CFG_IMMR))->spi; @@ -53,7 +82,18 @@ void spi_init(void) spi->com = 0; /* LST bit doesn't do anything, so disregard */ } -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) { volatile spi8xxx_t *spi = &((immap_t *) (CFG_IMMR))->spi; unsigned int tmpdout, tmpdin, event; @@ -61,11 +101,11 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) int tm, isRead = 0; unsigned char charSize = 32; - debug("spi_xfer: chipsel %08X dout %08X din %08X bitlen %d\n", - (int)chipsel, *(uint *) dout, *(uint *) din, bitlen); + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen); - if (chipsel != NULL) - (*chipsel) (1); /* select the target chip */ + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); spi->event = 0xffffffff; /* Clear all SPI events */ @@ -135,8 +175,8 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin); } - if (chipsel != NULL) - (*chipsel) (0); /* deselect the target chip */ + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); return 0; } diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index b2e3ab9b676..e0809593d10 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -19,6 +19,7 @@ */ #include <common.h> +#include <malloc.h> #include <spi.h> #include <asm/io.h> @@ -61,17 +62,18 @@ static unsigned long spi_bases[] = { 0x53f84000, }; -static unsigned long spi_base; - #endif -spi_chipsel_type spi_chipsel[] = { - (spi_chipsel_type)0, - (spi_chipsel_type)1, - (spi_chipsel_type)2, - (spi_chipsel_type)3, +struct mxc_spi_slave { + struct spi_slave slave; + unsigned long base; + u32 ctrl_reg; }; -int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]); + +static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct mxc_spi_slave, slave); +} static inline u32 reg_read(unsigned long addr) { @@ -83,30 +85,31 @@ static inline void reg_write(unsigned long addr, u32 val) *(volatile unsigned long*)addr = val; } -static u32 spi_xchg_single(u32 data, int bitlen) +static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen) { - - unsigned int cfg_reg = reg_read(spi_base + MXC_CSPICTRL); + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + unsigned int cfg_reg = reg_read(mxcs->base + MXC_CSPICTRL); if (MXC_CSPICTRL_BITCOUNT(bitlen - 1) != (cfg_reg & MXC_CSPICTRL_BITCOUNT(31))) { cfg_reg = (cfg_reg & ~MXC_CSPICTRL_BITCOUNT(31)) | MXC_CSPICTRL_BITCOUNT(bitlen - 1); - reg_write(spi_base + MXC_CSPICTRL, cfg_reg); + reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg); } - reg_write(spi_base + MXC_CSPITXDATA, data); + reg_write(mxcs->base + MXC_CSPITXDATA, data); cfg_reg |= MXC_CSPICTRL_XCH; - reg_write(spi_base + MXC_CSPICTRL, cfg_reg); + reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg); - while (reg_read(spi_base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH) + while (reg_read(mxcs->base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH) ; - return reg_read(spi_base + MXC_CSPIRXDATA); + return reg_read(mxcs->base + MXC_CSPIRXDATA); } -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) { int n_blks = (bitlen + 31) / 32; u32 *out_l, *in_l; @@ -117,13 +120,10 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) return 1; } - if (!spi_base) - spi_select(CONFIG_MXC_SPI_IFACE, (int)chipsel, SPI_MODE_2 | SPI_CS_HIGH); - for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout; i < n_blks; i++, in_l++, out_l++, bitlen -= 32) - *in_l = spi_xchg_single(*out_l, bitlen); + *in_l = spi_xchg_single(slave, *out_l, bitlen); return 0; } @@ -132,17 +132,17 @@ void spi_init(void) { } -int spi_select(unsigned int bus, unsigned int dev, unsigned long mode) +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) { unsigned int ctrl_reg; + struct mxc_spi_slave *mxcs; if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) || - dev > 3) - return 1; - - spi_base = spi_bases[bus]; + cs > 3) + return NULL; - ctrl_reg = MXC_CSPICTRL_CHIPSELECT(dev) | + ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) | MXC_CSPICTRL_BITCOUNT(31) | MXC_CSPICTRL_DATARATE(7) | /* FIXME: calculate data rate */ MXC_CSPICTRL_EN | @@ -155,12 +155,38 @@ int spi_select(unsigned int bus, unsigned int dev, unsigned long mode) if (mode & SPI_CS_HIGH) ctrl_reg |= MXC_CSPICTRL_SSPOL; - reg_write(spi_base + MXC_CSPIRESET, 1); + mxcs = malloc(sizeof(struct mxc_spi_slave)); + if (!mxcs) + return NULL; + + mxcs->slave.bus = bus; + mxcs->slave.cs = cs; + mxcs->base = spi_bases[bus]; + mxcs->ctrl_reg = ctrl_reg; + + return &mxcs->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + + reg_write(mxcs->base + MXC_CSPIRESET, 1); udelay(1); - reg_write(spi_base + MXC_CSPICTRL, ctrl_reg); - reg_write(spi_base + MXC_CSPIPERIOD, + reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg); + reg_write(mxcs->base + MXC_CSPIPERIOD, MXC_CSPIPERIOD_32KHZ); - reg_write(spi_base + MXC_CSPIINT, 0); + reg_write(mxcs->base + MXC_CSPIINT, 0); return 0; } + +void spi_release_bus(struct spi_slave *slave) +{ + /* TODO: Shut the controller down */ +} diff --git a/drivers/spi/spirom.c b/drivers/spi/spirom.c new file mode 100644 index 00000000000..87c93668ed3 --- /dev/null +++ b/drivers/spi/spirom.c @@ -0,0 +1,212 @@ +/* + * Copyright 2007 b7 Spectrum Digital Incorporated. + * All rights reserved. Property of Spectrum Digital Incorporated. + */ + +/* + * SPI ROM interface + * + */ + +#include "spirom.h" + +static Uint8 spirombuf[SPIROM_PAGESIZE + 5]; +static Uint8 statusbuf[8]; +static Uint32 spidat1; + +/* ------------------------------------------------------------------------ * + * * + * _wait( delay ) * + * Wait in a software loop for 'x' delay * + * * + * ------------------------------------------------------------------------ */ +void DAVINCIHD_wait( Uint32 delay ) +{ + volatile Uint32 i; + for ( i = 0 ; i < delay ; i++ ){ }; +} + + +/* ------------------------------------------------------------------------ * + * spirom_init( ) * + * ------------------------------------------------------------------------ */ +void spirom_init( ) +{ + /* Reset SPI */ + SPI_SPIGCR0 = 0; + _wait( 1000 ); + + /* Release SPI */ + SPI_SPIGCR0 = 1; + + /* SPI 4-Pin Mode setup */ + SPI_SPIGCR1 = 0 + | ( 0 << 24 ) + | ( 0 << 16 ) + | ( 1 << 1 ) + | ( 1 << 0 ); + + SPI_SPIPC0 = 0 + | ( 1 << 11 ) // DI + | ( 1 << 10 ) // DO + | ( 1 << 9 ) // CLK + | ( 1 << 1 ) // EN1 + | ( 1 << 0 ); // EN0 + + SPI_SPIFMT0 = 0 + | ( 0 << 20 ) // SHIFTDIR + | ( 0 << 17 ) // Polarity + | ( 1 << 16 ) // Phase + | ( 50 << 8 ) // Prescale + | ( 8 << 0 ); // Char Len + + spidat1 = 0 + | ( 1 << 28 ) // CSHOLD + | ( 0 << 24 ) // Format [0] + | ( 2 << 16 ) // CSNR [only CS0 enbled] + | ( 0 << 0 ); // + + SPI_SPIDAT1 = spidat1; + + SPI_SPIDELAY = 0 + | ( 8 << 24 ) // C2TDELAY + | ( 8 << 16 ); // T2CDELAY + + SPI_SPIDEF = 0 + | ( 1 << 1 ) // EN1 inactive high + | ( 1 << 0 ); // EN0 inactive high + + SPI_SPIINT = 0 + | ( 0 << 16 ) // + | ( 0 << 8 ) // + | ( 0 << 6 ) // + | ( 1 << 4 ); // + + SPI_SPILVL = 0 + | ( 0 << 8 ) // EN0 + | ( 0 << 6 ) // EN0 + | ( 0 << 4 ); // EN0 + + + /* Enable SPI */ + SPI_SPIGCR1 |= ( 1 << 24 ); +} + +/* ------------------------------------------------------------------------ * + * spirom_cycle( buf, len ) * + * * + * Execute a SPI spirom data transfer cycle. Each byte in buf is shifted * + * out and replaced with data coming back from the spirom. * + * ------------------------------------------------------------------------ */ +void spirom_cycle( Uint8 *buf, Uint16 len ) +{ + Uint16 i; + + /* Clear any old data */ + SPI_SPIBUF; + + /* SPIROM access cycle */ + for ( i = 0 ; i <= len ; i++ ) + { + /* Wait for transmit ready */ + while ( SPI_SPIBUF & 0x10000000 ); + + if ( i == len ) + SPI_SPIDAT1 = ( spidat1 & 0x0ffcffff ) | buf[i]; + else + SPI_SPIDAT1 = spidat1 | buf[i]; + + /* Wait for receive data ready */ + while ( SPI_SPIBUF & 0x80000000 ); + + /* Read 1 byte */ + buf[i] = SPI_SPIBUF; + } +} + +/* ------------------------------------------------------------------------ * + * spirom_status( ) * + * ------------------------------------------------------------------------ */ +Uint8 spirom_status( ) +{ + /* Issue read status command */ + statusbuf[0] = SPIROM_CMD_RDSR; + statusbuf[1] = 0; + + spirom_cycle( statusbuf, 2 ); + + return statusbuf[1]; +} + +/* ------------------------------------------------------------------------ * + * spirom_read( src, dst, length ) * + * ------------------------------------------------------------------------ */ +void spirom_read( Uint16 src, Uint32 dst, Uint32 length ) +{ + Int32 i; + Uint8 *psrc, *pdst; + + // Setup command + spirombuf[0] = SPIROM_CMD_READ; + spirombuf[1] = ( src >> 8 ); + spirombuf[2] = ( src >> 0 ); + + // Execute spirom read cycle + spirom_cycle( spirombuf, length + 3 ); + + // Copy returned data + pdst = ( Uint8 * )dst; + psrc = spirombuf + 3; + for ( i = 0 ; i < length ; i++ ) + *pdst++ = *psrc++; +} + +/* ------------------------------------------------------------------------ * + * spirom_write( src, dst, length ) * + * ------------------------------------------------------------------------ */ +void spirom_write( Uint32 src, Uint16 dst, Uint32 length ) +{ + Int32 i; + Int32 bytes_left; + Int32 bytes_to_program; + Uint8 *psrc; + + /* Establish source */ + psrc = ( Uint8 * )src; + bytes_left = length; + + while ( bytes_left > 0 ) + { + bytes_to_program = bytes_left; + + /* Most to program is SPIROM_CMD_BLOCKSIZE */ + if ( bytes_to_program > SPIROM_PAGESIZE ) + bytes_to_program = SPIROM_PAGESIZE; + + /* Make sure you don't run off the end of a block */ + if ( ( dst & SPIROM_PAGEMASK ) != ( ( dst + bytes_to_program ) & SPIROM_PAGEMASK ) ) + bytes_to_program -= ( dst + bytes_to_program ) - ( ( dst + bytes_to_program ) & SPIROM_PAGEMASK ); + + /* Issue WPEN */ + spirombuf[0] = SPIROM_CMD_WREN; + spirom_cycle( spirombuf, 0 ); + + /* Create command block for program operation */ + spirombuf[0] = SPIROM_CMD_WRITE; + spirombuf[1] = ( Uint8 )( dst >> 8 ); + spirombuf[2] = ( Uint8 )( dst ); + + for ( i = 0 ; i < bytes_to_program ; i++ ) + spirombuf[3+i] = *psrc++; + + /* Execute write command */ + spirom_cycle( spirombuf, bytes_to_program + 2 ); + + /* Wait while busy */ + while( ( spirom_status( ) & 0x01 ) ); + + /* Get ready for next iteration */ + bytes_left -= bytes_to_program; + dst += bytes_to_program; + } +} diff --git a/drivers/spi/spirom.h b/drivers/spi/spirom.h new file mode 100644 index 00000000000..2dfe3bc32f7 --- /dev/null +++ b/drivers/spi/spirom.h @@ -0,0 +1,77 @@ +/* + * Copyright 2007 by Spectrum Digital Incorporated. + * All rights reserved. Property of Spectrum Digital Incorporated. + */ + +/* + * SPI ROM header file + * + */ + +#ifndef SPIROM_ +#define SPIROM_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "davincihd.h" + +/* ------------------------------------------------------------------------ * + * SPI ROM Definitions * + * ------------------------------------------------------------------------ */ +#define SPIROM_SIZE 0x00008000 +#define SPIROM_BASE 0x00000000 +#define SPIROM_PAGESIZE 32 +#define SPIROM_PAGEMASK 0xffffffc0 + +/* ------------------------------------------------------------------------ * + * SPI ROM Commands * + * ------------------------------------------------------------------------ */ +#define SPIROM_CMD_WRSR 0x01 +#define SPIROM_CMD_WRITE 0x02 +#define SPIROM_CMD_READ 0x03 +#define SPIROM_CMD_WRDI 0x04 +#define SPIROM_CMD_RDSR 0x05 +#define SPIROM_CMD_WREN 0x06 + +/* ------------------------------------------------------------------------ * + * SPI Controller * + * ------------------------------------------------------------------------ */ +#define SPI_BASE 0x01c66800 +#define SPI_SPIGCR0 *( volatile Uint32* )( SPI_BASE + 0x0 ) +#define SPI_SPIGCR1 *( volatile Uint32* )( SPI_BASE + 0x4 ) +#define SPI_SPIINT *( volatile Uint32* )( SPI_BASE + 0x8 ) +#define SPI_SPILVL *( volatile Uint32* )( SPI_BASE + 0xc ) +#define SPI_SPIFLG *( volatile Uint32* )( SPI_BASE + 0x10 ) +#define SPI_SPIPC0 *( volatile Uint32* )( SPI_BASE + 0x14 ) +#define SPI_SPIPC2 *( volatile Uint32* )( SPI_BASE + 0x1c ) +#define SPI_SPIDAT1_TOP *( volatile Uint16* )( SPI_BASE + 0x3c ) +#define SPI_SPIDAT1 *( volatile Uint32* )( SPI_BASE + 0x3c ) +#define SPI_SPIDAT1_PTR16 *( volatile Uint16* )( SPI_BASE + 0x3e ) +#define SPI_SPIDAT1_PTR8 *( volatile Uint8* ) ( SPI_BASE + 0x3f ) +#define SPI_SPIBUF *( volatile Uint32* )( SPI_BASE + 0x40 ) +#define SPI_SPIBUF_PTR16 *( volatile Uint16* )( SPI_BASE + 0x42 ) +#define SPI_SPIBUF_PTR8 *( volatile Uint8* ) ( SPI_BASE + 0x43 ) +#define SPI_SPIEMU *( volatile Uint32* )( SPI_BASE + 0x44 ) +#define SPI_SPIDELAY *( volatile Uint32* )( SPI_BASE + 0x48 ) +#define SPI_SPIDEF *( volatile Uint32* )( SPI_BASE + 0x4c ) +#define SPI_SPIFMT0 *( volatile Uint32* )( SPI_BASE + 0x50 ) +#define SPI_SPIFMT1 *( volatile Uint32* )( SPI_BASE + 0x54 ) +#define SPI_SPIFMT2 *( volatile Uint32* )( SPI_BASE + 0x58 ) +#define SPI_SPIFMT3 *( volatile Uint32* )( SPI_BASE + 0x5c ) +#define SPI_INTVEC0 *( volatile Uint32* )( SPI_BASE + 0x60 ) +#define SPI_INTVEC1 *( volatile Uint32* )( SPI_BASE + 0x64 ) + +/* ------------------------------------------------------------------------ * + * Prototype * + * ------------------------------------------------------------------------ */ +void spirom_init( ); +void spirom_read( Uint16 src, Uint32 dst, Uint32 length ); +void spirom_write( Uint32 src, Uint16 dst, Uint32 length ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index f8ea167b12c..013a78dda9b 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -12,7 +12,7 @@ # # 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 +# 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 @@ -23,7 +23,7 @@ include $(TOPDIR)/config.mk -LIB := $(obj)libusb.a +LIB := $(obj)libusb.a COBJS-y += isp116x-hcd.o COBJS-y += sl811_usb.o @@ -32,14 +32,16 @@ COBJS-y += usbdcore.o COBJS-y += usbdcore_ep0.o COBJS-y += usbdcore_mpc8xx.o COBJS-y += usbdcore_omap1510.o +COBJS-y += da8xx_usb.o +COBJS-y += musbhdrc.o -COBJS := $(COBJS-y) -SRCS := $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) -all: $(LIB) +all: $(LIB) -$(LIB): $(obj).depend $(OBJS) +$(LIB): $(obj).depend $(OBJS) $(AR) $(ARFLAGS) $@ $(OBJS) ######################################################################### diff --git a/drivers/usb/da8xx_usb.c b/drivers/usb/da8xx_usb.c new file mode 100644 index 00000000000..5dd5f123936 --- /dev/null +++ b/drivers/usb/da8xx_usb.c @@ -0,0 +1,213 @@ +/* + * TI's DA8xx platform specific usb wrapper functions. + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * + */ + +#include <common.h> + +#ifdef CONFIG_USB_DA8XX + +#include "da8xx_usb.h" + +/* extern functions */ +extern void lpsc_on(unsigned int id); + +/* Timeout for DA8xx usb module */ +#define DA8XX_USB_TIMEOUT 0x3FFFF + + +/* This function writes to a 32-bit register of platform usb wrapper */ +inline void pusb_writel( u32 offset , u32 value ) +{ + *(volatile u32*)(DA8XX_USB0_BASE+offset) = value; +} + +/* This function reads a 32-bit register of platform usb wrapper */ +inline u32 pusb_readl(u32 offset) +{ + return(*(volatile u32*)(DA8XX_USB0_BASE+offset)); +} + +/* This function writes to a 16-bit register of platform musb core */ +inline void musb_writew(u32 offset, u16 value) +{ + *(volatile u16*)(MENTOR_USB0_BASE+offset) = value; +} + +/* This function writes to a 8-bit register of platform musb core */ +inline void musb_writeb(u32 offset, u8 value) +{ + *(volatile u8*)(MENTOR_USB0_BASE+offset) = value; +} + +/* This function reads a 16-bit register of platform usb wrapper */ +inline u16 musb_readw(u32 offset) +{ + return(*(volatile u16*)(MENTOR_USB0_BASE + offset)); +} + +/* This function reads a 8-bit register of platform usb wrapper */ +inline u8 musb_readb(u32 offset) +{ + return(*(volatile u8*)(MENTOR_USB0_BASE+offset)); +} + +/* + * This function enables VBUS by driving the GPIO Bank4 Pin 15 high. + */ +static void enable_vbus(void) +{ + u32 value; + + /* configure GPIO bank4 pin 15 in output direction */ + value = *(volatile u32 *)GPIO_BANK4_REG_DIR_ADDR; + value &= ~0x8000; + *(volatile u32 *)GPIO_BANK4_REG_DIR_ADDR = value; + + /* set GPIO bank4 pin 15 high to drive VBUS */ + value = *(volatile u32 *)GPIO_BANK4_REG_SET_ADDR; + value |= 0x8000; + *(volatile u32 *)GPIO_BANK4_REG_SET_ADDR = value; +} + +/* + * Enable the usb0 phy. This initialization procedure is explained in + * the DA8xx USB user guide document. + */ +static u8 phy_on( void ) +{ + u32 timeout; + u8 result = 1; + + /* write access keys */ + *(volatile u32 *)(KICK0) = 0x83e70b13; + *(volatile u32 *)(KICK1) = 0x95a4f1e0; + + +#ifdef CONFIG_USE_PINMUX + + /* set PINMUX[7:4] = 1 */ + *(volatile u32 *)(PINMUX9) |= ( 1 << 4 ); + *(volatile u32 *)(PINMUX9) &= 0xFFFFFF7F; + +#endif + + /* reset the usb controller */ + pusb_writel( DA8XX_USB_CTRL_REG , 0x1 ); + udelay( 5000 ); + + /* enable and then disable the usb phy reset */ + *(volatile u32*)CFGCHIP2 |= 0x00008000; + udelay( 5000 ); + *(volatile u32*)CFGCHIP2 &= 0xFFFF7FFF; + udelay( 5000 ); + + /* configure phy (refer to da8xx usb user guide use case section) */ + *(volatile u32*)CFGCHIP2 &= 0xFFFF9FFF; + *(volatile u32*)CFGCHIP2 &= 0xFFFFFBFF; + *(volatile u32*)CFGCHIP2 &= 0xFFFFFDFF; + *(volatile u32*)CFGCHIP2 |= 0x00000100; + *(volatile u32*)CFGCHIP2 |= 0x00000020; + *(volatile u32*)CFGCHIP2 |= 0x00000010; + *(volatile u32*)CFGCHIP2 |= 0x00000002; + *(volatile u32*)CFGCHIP2 &= 0xFFFFE7FF; + *(volatile u32*)CFGCHIP2 |= 0x00000800; + + *(volatile u32*)CFGCHIP2 &= 0xFFFF9FFF; + *(volatile u32*)CFGCHIP2 |= 0x00002000; + *(volatile u32*)CFGCHIP2 |= 0x00000040; + + /* wait until the usb phy pll locks */ + timeout = DA8XX_USB_TIMEOUT; + do { + timeout--; + if (timeout == 0) + break; + } + while(((*(volatile u32*)CFGCHIP2) & 0x00020000) == 0); + + /* disable register access by writing invalid keys */ + *(volatile u32 *)(0x01C14038) = 0x0; + *(volatile u32 *)(0x01C1403C) = 0x0; + + if (timeout == 0) + result = 0; + + return(result); +} + +/* + * Disable the usb phy + */ +static void phy_off(void) +{ + /* write access keys */ + *(volatile u32 *)(KICK0) = 0; + *(volatile u32 *)(KICK1) = 0; + + /* powerdown the on-chip PHY and its oscillator */ + *(volatile u32*)(CFGCHIP2) = 0x8600; +} + + +/* + * This function performs DA8xx platform specific initialization for usb0. + */ +int musb_platform_init(void) +{ + u32 revision; + + /* enable psc for usb2.0 */ + lpsc_on(33); + + /* enable usb vbus */ + enable_vbus(); + + /* start the on-chip usb phy and its pll */ + if (phy_on() == 0) + return(-1); + + /* reset the controller */ + pusb_writel(DA8XX_USB_CTRL_REG, 0x1); + udelay(5000); + + /* Returns zero if e.g. not clocked */ + revision = pusb_readl(DA8XX_USB_VERSION_REG); + if (revision == 0) + return(-1); + + /* Disable all interrupts */ + pusb_writel(DA8XX_USB_INT_MASK_SET_REG, 0x01FF1F1F); + return(0); +} + + +/* + * This function performs DA8xx platform specific deinitialization for usb0. + */ +void musb_platform_deinit(void) +{ + /* Turn of the phy */ + phy_off(); + + /* flush any interrupts */ + pusb_writel(DA8XX_USB_INT_MASK_CLR_REG, DA8XX_USB_USBINT_MASK|DA8XX_USB_TXINT_MASK|DA8XX_USB_RXINT_MASK); + pusb_writel(DA8XX_USB_EOI_REG, 0); +} + +#endif /* CONFIG_USB_DA8XX */ + diff --git a/drivers/usb/da8xx_usb.h b/drivers/usb/da8xx_usb.h new file mode 100644 index 00000000000..ffc0be39b18 --- /dev/null +++ b/drivers/usb/da8xx_usb.h @@ -0,0 +1,73 @@ +/* + * TI's DA8xx platform specific usb wrapper functions. + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * + */ + +#ifndef __DA8XX_MUSB_H__ +#define __DA8XX_MUSB_H__ + +#include "musbhdrc.h" + +/* Base address of da8xx usb0 wrapper */ +#define DA8XX_USB0_BASE 0x01E00000 + +/* Base address of da8xx musb core */ +#define MENTOR_USB0_BASE (DA8XX_USB0_BASE+0x400) + +/* For now include usb OTG module registers here */ +#define DA8XX_USB_VERSION_REG 0x00 +#define DA8XX_USB_CTRL_REG 0x04 +#define DA8XX_USB_STAT_REG 0x08 +#define DA8XX_MODE_TGCR_REG 0x10 /* Mode reg RNDIS */ +#define DA8XX_AUTOREQ_REG 0x14 +#define DA8XX_SRP_FIXTIME_REG 0x18 /* SRP Fixtime reg */ +#define DA8XX_TEARDOWN_REG 0x1C /* TearDown Register*/ +#define DA8XX_USB_INT_SOURCE_REG 0x20 +#define DA8XX_USB_INT_SET_REG 0x24 +#define DA8XX_USB_INT_SRC_CLR_REG 0x28 +#define DA8XX_USB_INT_MASK_REG 0x2c +#define DA8XX_USB_INT_MASK_SET_REG 0x30 +#define DA8XX_USB_INT_MASK_CLR_REG 0x34 +#define DA8XX_USB_INT_SRC_MASKED_REG 0x38 +#define DA8XX_USB_EOI_REG 0x3c +#define DA8XX_USB_EOI_INTVEC 0x40 +#define DA8XX_GRNDIS_EP1SIZE_REG 0x50 +#define DA8XX_GRNDIS_EP2SIZE_REG 0x54 +#define DA8XX_GRNDIS_EP3SIZE_REG 0x58 +#define DA8XX_GRNDIS_EP4SIZE_REG 0x5C + + +#define DA8XX_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */ +#define DA8XX_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */ + +#define DA8XX_USB_USBINT_SHIFT 16 +#define DA8XX_USB_TXINT_SHIFT 0 +#define DA8XX_USB_RXINT_SHIFT 8 + +#define DA8XX_INTR_DRVVBUS 0x0100 + +#define DA8XX_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */ +#define DA8XX_USB_TXINT_MASK \ + (DA8XX_USB_TX_ENDPTS_MASK << DA8XX_USB_TXINT_SHIFT) +#define DA8XX_USB_RXINT_MASK \ + (DA8XX_USB_RX_ENDPTS_MASK << DA8XX_USB_RXINT_SHIFT) + +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +#endif /* __DA8XX_MUSB_H__ */ + diff --git a/drivers/usb/musbhdrc.c b/drivers/usb/musbhdrc.c new file mode 100644 index 00000000000..ff907f9afe3 --- /dev/null +++ b/drivers/usb/musbhdrc.c @@ -0,0 +1,885 @@ +/* + * Mentor USB Core host controller driver. + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * + */ + +#include <common.h> + +/* Include usb hcd code if usb support is included in u-boot */ +#ifdef CONFIG_MUSB + +#include <usb.h> +#include "musbhdrc.h" + +/* extern functions */ +extern int musb_platform_init(void); +extern void musb_platform_deinit(void); +extern inline void musb_writew(u32 offset, u16 value); +extern inline void musb_writeb(u32 offset, u8 value); +extern inline u16 musb_readw(u32 offset); +extern inline u8 musb_readb(u32 offset); + +/* The controller driver polls for changes in the state. This defines a timeout + for cases where the states do not change so the appropriate error can be + returned. */ +#define MUSB_USB_TIMEOUT 0x3FFFFF + +/* This defines the endpoint number used for control transfers */ +#define MUSB_CONTROL_EP 0 + +/* This defines the endpoint number used for bulk transfer */ +#define MUSB_BULK_EP 1 + +/* Determine the operating speed of MUSB core */ +#define musb_ishighspeed() \ + ((musb_readb(MGC_O_HDRC_POWER) & MGC_M_POWER_HSMODE) >> 4) + +/* speed negotiated with the connected device */ +static u8 musb_speed; + +/* + * This function configures all the endpoint FIFOs. Endpoint 1 is used for bulk + * transfers and so the fifo size of EP1 Tx and Rx is set to 512 bytes. The + * other endpoints 2,3 & 4 are configured for default fifo size of 64 bytes but + * these endpoints are not used. + */ +void musb_configure_ep(void) +{ + /* Select Endpoint 1 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 1); + + /* Configure FIFO for endpoint 1 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x06); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x06); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0x08); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0x48); + + /* Select Endpoint 2 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 2); + + /* Configure FIFO for endpoint 2 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x03); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x03); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0x88); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0x90); + + /* Select Endpoint 3 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 3 ); + + /* Configure FIFO for endpoint 2 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x03); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x03); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0x98); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0xA0); + + /* Select Endpoint 3 for bulk transfer */ + musb_writeb(MGC_O_HDRC_INDEX, 4 ); + + /* Configure FIFO for endpoint 2 */ + musb_writeb(MGC_O_HDRC_TXFIFOSZ, 0x03); + musb_writeb(MGC_O_HDRC_RXFIFOSZ, 0x03); + musb_writew(MGC_O_HDRC_TXFIFOADD, 0xA8); + musb_writew(MGC_O_HDRC_RXFIFOADD, 0xB0); +} + +/* + * program the HDRC to start (enable interrupts, dma, etc.) + */ +void musb_start( void ) +{ + u8 devctl; + + /* disable all interrupts */ + musb_writew(MGC_O_HDRC_INTRTXE, 0x0000); + musb_writew(MGC_O_HDRC_INTRRXE, 0x0000); + musb_writeb(MGC_O_HDRC_INTRUSBE, 0x00); + musb_writeb(MGC_O_HDRC_TESTMODE, 0); + + /* put into basic highspeed mode and start session */ + musb_writeb(MGC_O_HDRC_POWER, (MGC_M_POWER_HSENAB)); + devctl = musb_readb(MGC_O_HDRC_DEVCTL); + devctl |= MGC_M_DEVCTL_SESSION; + musb_writeb(MGC_O_HDRC_DEVCTL, devctl); +} + +/* + * This function writes data to endpoint fifo + */ +static void write_fifo(u8 ep, u32 length, void *fifo_data) +{ + u32 address; + u8 *data = (u8*)fifo_data; + + /* select the endpoint index */ + musb_writeb(MGC_O_HDRC_INDEX, ep); + address = MUSB_FIFO_OFFSET(ep)+0x20; + + /* write the data to the fifo */ + while(length) { + musb_writeb(address, *data); + data++; + length--; + } +} + +/* + * This function reads data from endpoint fifo + */ +static void read_fifo(u8 ep, u32 length, void *fifo_data) +{ + u32 address; + u8 *data = (u8*)fifo_data; + + /* select the endpoint index */ + musb_writeb(MGC_O_HDRC_INDEX, ep); + address = MUSB_FIFO_OFFSET(ep)+0x20; + + /* read the data to the fifo */ + while(length) { + *data = musb_readb(address); + data++; + length--; + } +} + +/* + * This function performs all intializations required for setting up the + * bulk endpoint. + */ +static void setup_bulk_ep(u8 bulkep) +{ + u16 csr; + + /* select bulk endpoint */ + musb_writeb(MGC_O_HDRC_INDEX, bulkep); + + /* clear the data toggle bit of bluk endpoint */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + /* also, flush the Tx and Rx FIFO of endpoint 1 */ + if ((musb_readb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) & MGC_M_TXCSR_TXPKTRDY) == MGC_M_TXCSR_TXPKTRDY) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_FLUSHFIFO; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + } + + if ((musb_readb(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)) & MGC_M_RXCSR_RXPKTRDY) == MGC_M_RXCSR_RXPKTRDY) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_FLUSHFIFO; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + } +} + +/* + * This function checks if RxStall has occured on the endpoint. If a RxStall has + * occured, the RxStall is cleared and 1 is returned. If RxStall has not occured, + * 0 is returned. + */ +static u8 check_stall(u8 ep, u8 dir_out) +{ + u16 csr; + + /* For endpoint 0 */ + if (ep == 0) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + if ((csr & MGC_M_CSR0_H_RXSTALL ) == MGC_M_CSR0_H_RXSTALL) { + csr = csr & ~(MGC_M_CSR0_H_RXSTALL|MGC_M_CSR0_H_STATUSPKT|MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + return(1); + } + } else { /* For non-ep0 */ + if (dir_out == 1) { /* is it tx ep */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + if ((csr & MGC_M_TXCSR_H_RXSTALL) == MGC_M_TXCSR_H_RXSTALL) { + csr = csr & ~(MGC_M_CSR0_H_RXSTALL|MGC_M_CSR0_H_STATUSPKT|MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + return(1); + } + } else { /* is it rx ep */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + if ((csr & MGC_M_RXCSR_H_RXSTALL) == MGC_M_RXCSR_H_RXSTALL) { + csr = csr & ~(MGC_M_CSR0_H_RXSTALL|MGC_M_CSR0_H_STATUSPKT|MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + return(1); + } + } + } + + /* There is no RxStall at the endpoint */ + return(0); +} + + +/* + * waits until ep0 is ready. + */ +static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask) +{ + u32 timeout; + + timeout = MUSB_USB_TIMEOUT; + do { + /* is there a stall */ + if (check_stall(MUSB_CONTROL_EP, 0)) { + dev->status = USB_ST_STALLED; + return( -2 ); + } + + switch(bit_mask) { + case MGC_M_CSR0_TXPKTRDY: + /* check if TXPKTRDY bit is cleared */ + if ((musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)) & MGC_M_CSR0_TXPKTRDY) != MGC_M_CSR0_TXPKTRDY) + return(0); + break; + + case MGC_M_CSR0_RXPKTRDY: + /* check if RXPKTRDY bit is set */ + if ((musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)) & MGC_M_CSR0_RXPKTRDY) == MGC_M_CSR0_RXPKTRDY) + return(0); + break; + + case MGC_M_CSR0_H_REQPKT: + /* check if the request has been sent */ + if ((musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)) & MGC_M_CSR0_H_REQPKT) != MGC_M_CSR0_H_REQPKT) + return(0); + break; + } + timeout--; + } + while(timeout > 0); + + /* timed-out */ + dev->status = USB_ST_CRC_ERR; + return(-1); +} + +/* + * This function performs the setup phase of the control transfer + */ +static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup) +{ + int result = -1; + u16 csr; + + /* write the control request to ep0 fifo */ + write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void*)setup); + + /* enable transfer of setup packet */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_TXPKTRDY|MGC_M_CSR0_H_SETUPPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + /* wait until the setup packet is transmitted */ + result = wait_until_ep0_ready(dev, MGC_M_CSR0_TXPKTRDY); + dev->act_len = 0; + + /* control transfer setup phase completes */ + return(result); +} + +/* + * This function handles the control transfer in data phase + */ +static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 rxlen = 0; + u32 nextlen = 0; + u8 maxpktsize = ( 1 << dev->maxpacketsize ) * 8; + u8* rxbuff = (u8*)buffer; + u8 rxedlength; + int result; + + do { + /* Determine the next read length */ + nextlen = (( len-rxlen) > maxpktsize) ? maxpktsize:(len-rxlen); + + /* Set the ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | MGC_M_CSR0_H_REQPKT; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + result = wait_until_ep0_ready(dev, MGC_M_CSR0_RXPKTRDY); + if (result < 0) + return(result); + + /* Actual number of bytes received by usb */ + rxedlength = musb_readb(MGC_INDEXED_OFFSET(MGC_O_HDRC_COUNT0)); + + /* Read the data from the RxFIFO */ + read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]); + + /* Clear the RxPktRdy Bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr & (~MGC_M_CSR0_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + if (rxedlength != nextlen) { + dev->act_len += rxedlength; + break; + } + + rxlen = rxlen + nextlen; + dev->act_len = rxlen; + } + while(rxlen < len); + + /* done reading the data */ + return(0); +} + +/* + * This function handles the control transfer out data phase + */ +static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer) +{ + u16 csr; + u32 txlen = 0; + u32 nextlen = 0; + u8 maxpktsize = ( 1 << dev->maxpacketsize ) * 8; + u8* txbuff = (u8*)buffer; + int result; + + do { + /* Determine the next write length */ + nextlen = ((len-txlen) > maxpktsize) ? maxpktsize:(len-txlen); + + /* Load the data to send in FIFO */ + write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]); + + /* Set TXPKTRDY bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_H_DIS_PING|MGC_M_CSR0_TXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + result = wait_until_ep0_ready(dev, MGC_M_CSR0_TXPKTRDY); + if ( result < 0 ) + return(result); + + txlen = txlen + nextlen; + dev->act_len = txlen; + } + while(txlen < len); + + /* done writing the data */ + return(0); +} + + +/* + * This function handles the control transfer out status phase + */ +static int ctrlreq_out_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_H_DIS_PING|MGC_M_CSR0_TXPKTRDY|MGC_M_CSR0_H_STATUSPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + /* Wait until TXPKTRDY bit is cleared */ + result = wait_until_ep0_ready(dev, MGC_M_CSR0_TXPKTRDY); + return(result); +} + + +/* + * This function handles the control transfer in status phase + */ +static int ctrlreq_in_status_phase(struct usb_device *dev) +{ + u16 csr; + int result; + + /* Set the StatusPkt bit and ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr | (MGC_M_CSR0_H_DIS_PING|MGC_M_CSR0_H_REQPKT|MGC_M_CSR0_H_STATUSPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + result = wait_until_ep0_ready(dev, MGC_M_CSR0_H_REQPKT); + if (result < 0) + return(result); + + /* clear StatusPkt bit and RxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + csr = csr & (~((MGC_M_CSR0_RXPKTRDY|MGC_M_CSR0_H_STATUSPKT))); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), csr); + + return(0); +} + + +/* + * determines the speed of the device (High/Full/Slow) + */ +static u8 get_dev_speed(struct usb_device *dev) +{ + if (dev->high == 1) + return(MGC_TYPE_SPEED_HIGH); + else + if (dev->slow == 1) + return(MGC_TYPE_SPEED_LOW); + else + return(MGC_TYPE_SPEED_FULL); +} + +/* + * configure the hub address and the port address. + */ +static void config_hub_port(struct usb_device *dev, u8 ep) +{ + u8 chid; + u8 hub; + + /* Find out the nearest parent which is high speed */ + while(dev->parent->parent != NULL) { + if (get_dev_speed(dev->parent) != MGC_TYPE_SPEED_HIGH) + dev = dev->parent; + else + break; + } + + /* determine the port address at that hub */ + hub = dev->parent->devnum; + for (chid = 0; chid < USB_MAXCHILDREN; chid++) + if (dev->parent->children[chid] == dev) + break; + + /* configure the hub address and the port address */ + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_TXHUBADDR), hub ); + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_TXHUBPORT), (chid+1) ); + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_RXHUBADDR), hub ); + musb_writeb(MGC_BUSCTL_OFFSET(ep, MGC_O_HDRC_RXHUBPORT), (chid+1) ); +} + +/* + * do a control transfer + */ +int submit_control_msg( struct usb_device *dev , unsigned long pipe , void *buffer , + int len , struct devrequest *setup ) +{ + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + u16 csr; + u16 wIntrTxE; + u8 devspeed; + + /* select control endpoint */ + musb_writeb(MGC_O_HDRC_INDEX, MUSB_CONTROL_EP); + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0)); + + /* disable interrupt in case we flush */ + wIntrTxE = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_INTRTXE)); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_INTRTXE), (wIntrTxE & ~(1 << ep))); + + /* endpoint 0: just flush */ + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), (csr|MGC_M_CSR0_FLUSHFIFO)); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_CSR0), (csr|MGC_M_CSR0_FLUSHFIFO)); + + /* target addr and (for multipoint) hub addr/port */ + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_TXFUNCADDR), devnum); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_RXFUNCADDR), devnum); + + + /* configure the hub address and the port number as required */ + if (( musb_ishighspeed()) && (dev->parent != NULL)) { + devspeed = get_dev_speed(dev); + if (devspeed != MGC_TYPE_SPEED_HIGH) { + config_hub_port(dev, MUSB_CONTROL_EP); + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TYPE0), devspeed << 6); + } + } else { + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TYPE0), musb_speed << 6); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_TXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_TXHUBPORT), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_RXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_CONTROL_EP, MGC_O_HDRC_RXHUBPORT), 0); + } + + /* Control transfer setup phase */ + if (ctrlreq_setup_phase(dev, setup) < 0) + return(-1); + + if ((setup->request == 0x06) || /* GET_DESCRIPTOR */ + (setup->request == 0x08) || /* GET_CONFIGURATION */ + (setup->request == 0x0A) || /* GET_INTERFACE */ + (setup->request == 0x00)) { /* GET_STATUS */ + /* control transfer in-data-phase */ + if (ctrlreq_in_data_phase(dev, len, buffer) < 0) + return(-1); + + /* control transfer out-status-phase */ + if (ctrlreq_out_status_phase(dev) < 0) + return(-1); + } else { + if ((setup->request == 0x05) || /* SET_ADDRESS */ + (setup->request == 0x09) || /* SET_CONFIGURATION */ + (setup->request == 0x03) || /* SET_FEATURE */ + (setup->request == 0x03) || /* SET_FEATURE */ + (setup->request == 0x0B) || /* SET_INTERFACE */ + (setup->request == 0x01) || /* CLEAR_FEATURE */ + (setup->request == 0xFF)) { /* USB Mass Stroage Reset */ + + /* control transfer in status phase */ + if (ctrlreq_in_status_phase(dev) < 0) + return(-1); + } else { + if (setup->request == 0x07) { /* SET_DESCRIPTOR */ + /* control transfer out data phase */ + if (ctrlreq_out_data_phase(dev, len, buffer) < 0) + return(-1); + + /* control transfer in status phase */ + if ( ctrlreq_in_status_phase(dev) < 0 ) + return(-1); + } else { + /* unhandled control transfer */ + return(-1); + } + } + } + + /* end of control transfer */ + dev->status = 0; + dev->act_len = len; + return len; +} + +/* + * do a bulk transfer + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len) +{ + int dir_out = usb_pipeout(pipe); + int ep = usb_pipeendpoint(pipe); + int devnum = usb_pipedevice(pipe); + u8 type; + u16 csr; + u32 txLen = 0; + u32 nextLen = 0; + u8 devspeed; + u32 timeout; + + /* select bulk endpoint */ + musb_writeb(MGC_O_HDRC_INDEX, MUSB_BULK_EP); + + /* write the address of the device */ + if ( dir_out == 1 ) + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_TXFUNCADDR), devnum); + else + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_RXFUNCADDR), devnum); + + /* configure the hub address and the port number as required */ + if ((musb_ishighspeed()) && (dev->parent != NULL)) { + devspeed = get_dev_speed(dev); + if (devspeed != MGC_TYPE_SPEED_HIGH) { + /* MUSB is in high speed and the destination device is full speed device. + So configure the hub address and port address registers. */ + config_hub_port(dev, MUSB_BULK_EP); + } + } else { + if (dir_out == 1) { + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_TXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_TXHUBPORT), 0); + } else { + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_RXHUBADDR), 0); + musb_writeb(MGC_BUSCTL_OFFSET(MUSB_BULK_EP, MGC_O_HDRC_RXHUBPORT), 0); + } + devspeed = musb_speed; + } + + if (dir_out == 1) { /* bulk-out transfer */ + /* Write the old data toggle value */ + if (usb_gettoggle(dev, ep, dir_out) == 0) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = MGC_M_TXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + } else { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_H_WR_DATATOGGLE; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + csr = csr | (usb_gettoggle(dev, ep, dir_out)<<8); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + } + + /* Program the TxType register */ + type = (devspeed << MGC_S_TYPE_SPEED) | + (0x2 << MGC_S_TYPE_PROTO) | + (ep & 0xF); + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXTYPE), type); + + /* Write maximum packet size to the TxMaxp register */ + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXMAXP), dev->epmaxpacketout[ep]); + + while(txLen < len) { + nextLen = ((len-txLen) < dev->epmaxpacketout[ep]) ? (len-txLen):dev->epmaxpacketout[ep]; + + /* Write the data to the FIFO */ + write_fifo(1, nextLen, (void*)(((u8*)buffer)+txLen)); + + /* Set the TxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_TXPKTRDY; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + /* Wait until the TxPktRdy bit is cleared */ + timeout = MUSB_USB_TIMEOUT; + do { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + if ((csr & MGC_M_TXCSR_H_RXSTALL) == MGC_M_TXCSR_H_RXSTALL) { + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr & (~(MGC_M_TXCSR_H_RXSTALL))); + + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + + dev->status = USB_ST_STALLED; + dev->act_len = txLen; + return(0); + } + + if ((csr & MGC_M_TXCSR_H_ERROR) == MGC_M_TXCSR_H_ERROR) { + /* keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + return(0); + } + + /* maintain a timeout */ + if (timeout-- == 0) { + /* keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + + /* clear the TxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr & (~MGC_M_TXCSR_TXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + return(0); + } + } + while((csr & MGC_M_TXCSR_TXPKTRDY) == MGC_M_TXCSR_TXPKTRDY); + + txLen = txLen + nextLen; + #if 0 + if ((txLen == len) && (nextLen == dev->epmaxpacketout[ep])) { + /* Set the TxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)); + csr = csr | MGC_M_TXCSR_TXPKTRDY; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR), csr); + + /* Wait until the TxPktRdy bit is cleared */ + while(( musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR) & MGC_M_TXCSR_TXPKTRDY)) == MGC_M_TXCSR_TXPKTRDY); + } + #endif + } + + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_TXCSR)) >> 8) & 0x01); + } else { /* bulk-in transfer */ + /* Write the old data toggle value */ + if (usb_gettoggle(dev, ep, dir_out) == 0) { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = MGC_M_RXCSR_CLRDATATOG; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + } else { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_H_WR_DATATOGGLE; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + csr = csr | (usb_gettoggle(dev, ep, dir_out)<<9); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + } + + /* Program the RxType register */ + type = (devspeed << MGC_S_TYPE_SPEED) | + (0x2 << MGC_S_TYPE_PROTO) | + (ep & 0xF); + musb_writeb(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXTYPE), type); + + /* Write the maximum packet size to the RxMaxp register */ + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXMAXP), dev->epmaxpacketin[ep]); + + while(txLen < len) { + nextLen = ((len-txLen) < dev->epmaxpacketin[ep]) ? (len-txLen):dev->epmaxpacketin[ep]; + + /* Set the ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr | MGC_M_RXCSR_H_REQPKT; + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + /* Wait until the RxPktRdy bit is cleared */ + timeout = MUSB_USB_TIMEOUT; + do { + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + if ((csr & MGC_M_RXCSR_H_RXSTALL) == MGC_M_RXCSR_H_RXSTALL) { + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr & (~(MGC_M_RXCSR_H_RXSTALL))); + dev->status = USB_ST_STALLED; + dev->act_len = txLen; + return(0); + } + + if ((csr & MGC_M_RXCSR_H_ERROR) == MGC_M_RXCSR_H_ERROR) { + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + return(0); + } + + if (timeout-- == 0) { + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + dev->status = USB_ST_CRC_ERR; + dev->act_len = txLen; + + /* clear the ReqPkt bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr & (~MGC_M_RXCSR_H_REQPKT); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + return(0); + } + } + while((csr & MGC_M_RXCSR_RXPKTRDY) != MGC_M_RXCSR_RXPKTRDY); + + /* Read the data from the FIFO */ + read_fifo(1, nextLen, (void*)(((u8*)buffer) + txLen)); + + /* Clear the RxPktRdy bit */ + csr = musb_readw(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR)); + csr = csr & ~(MGC_M_RXCSR_RXPKTRDY); + musb_writew(MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR), csr); + + txLen = txLen + nextLen; + } + + /* Keep a copy of the data toggle bit */ + usb_settoggle(dev, ep, dir_out, (musb_readw( MGC_INDEXED_OFFSET(MGC_O_HDRC_RXCSR) ) >> 9) & 0x01); + } + + /* bulk transfer is complete */ + dev->status = 0; + dev->act_len = len; + return 0; +} + +/* + * This function initializes the usb controller module. + */ +int usb_lowlevel_init(void) +{ + u8 power; + u32 timeout; + + if (musb_platform_init() == -1) + return(-1); + + /* Configure all the endpoint FIFO's and start usb controller */ + musb_configure_ep(); + musb_start(); + + /* Wait until musb is enabled in host mode with a timeout. No hot + plug support. So there should be a usb device connected. */ + timeout = MUSB_USB_TIMEOUT*0x10; + do { + /* wait until the musb core moves into host mode */ + if (( musb_readb(MGC_O_HDRC_DEVCTL) & MGC_M_DEVCTL_HM) == MGC_M_DEVCTL_HM) + break; + + /* maintain a timeout */ + timeout--; + } + while( timeout > 0 ); + + /* if musb core is not in host mode, then return */ + if (timeout == 0) + return( -1 ); + + /* start usb bus reset */ + power = musb_readb(MGC_O_HDRC_POWER); + power = power | MGC_M_POWER_RESET; + musb_writeb(MGC_O_HDRC_POWER, power); + + /* After initiating a usb reset, wait for about 20ms to 30ms */ + udelay(30000); + + /* stop usb bus reset */ + power = musb_readb(MGC_O_HDRC_POWER); + power = power & (~MGC_M_POWER_RESET); + musb_writeb(MGC_O_HDRC_POWER, power); + + /* Determine if the connected device is a high/full/low speed device */ + if ((musb_readb(MGC_O_HDRC_POWER) & MGC_M_POWER_HSMODE) == MGC_M_POWER_HSMODE) { + /* High speed device is connected */ + musb_speed = MGC_TYPE_SPEED_HIGH; + } else { + if ((musb_readb(MGC_O_HDRC_DEVCTL) & MGC_M_DEVCTL_FSDEV) == MGC_M_DEVCTL_FSDEV) { + /* Full speed device is connected */ + musb_speed = MGC_TYPE_SPEED_FULL; + } else { + /* Low speed device is connected */ + musb_speed = MGC_TYPE_SPEED_LOW; + } + } + + /* setup the bulk endpoint */ + setup_bulk_ep(MUSB_BULK_EP); + + /* usb low level intialization is complete */ + return(0); +} + +/* + * This function stops the operation of the davinci usb module. + */ +int usb_lowlevel_stop(void) +{ + /* Reset the USB module */ + musb_platform_deinit(); + musb_writeb(MGC_O_HDRC_DEVCTL, 0); + + /* All done */ + return 0; +} + +/* + * This function supports usb interrupt transfers. Currently, usb interrupt transfers + * are not supported. + */ +int submit_int_msg( struct usb_device *dev, unsigned long pipe , + void *buffer , int len, int interval ) +{ + return(-1); +} + +#endif /* CONFIG_MUSB */ + + + diff --git a/drivers/usb/musbhdrc.h b/drivers/usb/musbhdrc.h new file mode 100644 index 00000000000..fba253e40b1 --- /dev/null +++ b/drivers/usb/musbhdrc.h @@ -0,0 +1,322 @@ +/****************************************************************** + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2006 by Texas Instruments + * + * This file is part of the Inventra Controller Driver for Linux. + * + * The Inventra Controller Driver for Linux is free software; you + * can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2 as published by the Free Software + * Foundation. + * + * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, + * Suite 330, Boston, MA 02111-1307 USA + * + * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION + * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE + * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS + * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER. + * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT + * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR + * GRAPHICS SUPPORT CUSTOMER. + ******************************************************************/ + +#ifndef __MUSB_HDRC_DEFS_H__ +#define __MUSB_HDRC_DEFS_H__ + +/* + * HDRC-specific definitions + */ + +#define MGC_MAX_USB_ENDS 4 + +#define MGC_END0_FIFOSIZE 64 /* this is non-configurable */ + +/* + * MUSBMHDRC Register map + */ + +/* Common USB registers */ + +#define MGC_O_HDRC_FADDR 0x00 /* 8-bit */ +#define MGC_O_HDRC_POWER 0x01 /* 8-bit */ + +#define MGC_O_HDRC_INTRTX 0x02 /* 16-bit */ +#define MGC_O_HDRC_INTRRX 0x04 +#define MGC_O_HDRC_INTRTXE 0x06 +#define MGC_O_HDRC_INTRRXE 0x08 +#define MGC_O_HDRC_INTRUSB 0x0A /* 8 bit */ +#define MGC_O_HDRC_INTRUSBE 0x0B /* 8 bit */ +#define MGC_O_HDRC_FRAME 0x0C +#define MGC_O_HDRC_INDEX 0x0E /* 8 bit */ +#define MGC_O_HDRC_TESTMODE 0x0F /* 8 bit */ + +/* Get offset for a given FIFO from musb->pRegs */ +#ifdef CONFIG_USB_TUSB6010 +#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20)) +#else +#define MUSB_FIFO_OFFSET(epnum) (epnum * 4) +#endif + +/* Additional Control Registers */ + +#define MGC_O_HDRC_DEVCTL 0x60 /* 8 bit */ + +/* These are always controlled through the INDEX register */ +#define MGC_O_HDRC_TXFIFOSZ 0x62 /* 8-bit (see masks) */ +#define MGC_O_HDRC_RXFIFOSZ 0x63 /* 8-bit (see masks) */ +#define MGC_O_HDRC_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */ +#define MGC_O_HDRC_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */ + +// vctrl/vstatus: optional vendor utmi+phy register at 0x68 +#define MGC_O_HDRC_HWVERS 0x6C /* 8 bit */ + +#define MGC_O_HDRC_EPINFO 0x78 /* 8 bit */ +#define MGC_O_HDRC_RAMINFO 0x79 /* 8 bit */ +#define MGC_O_HDRC_LINKINFO 0x7a /* 8 bit */ +#define MGC_O_HDRC_VPLEN 0x7b /* 8 bit */ +#define MGC_O_HDRC_HS_EOF1 0x7c /* 8 bit */ +#define MGC_O_HDRC_FS_EOF1 0x7d /* 8 bit */ +#define MGC_O_HDRC_LS_EOF1 0x7e /* 8 bit */ + +/* offsets to endpoint registers */ +#define MGC_O_HDRC_TXMAXP 0x00 +#define MGC_O_HDRC_TXCSR 0x02 +#define MGC_O_HDRC_CSR0 MGC_O_HDRC_TXCSR /* re-used for EP0 */ +#define MGC_O_HDRC_RXMAXP 0x04 +#define MGC_O_HDRC_RXCSR 0x06 +#define MGC_O_HDRC_RXCOUNT 0x08 +#define MGC_O_HDRC_COUNT0 MGC_O_HDRC_RXCOUNT /* re-used for EP0 */ +#define MGC_O_HDRC_TXTYPE 0x0A +#define MGC_O_HDRC_TYPE0 MGC_O_HDRC_TXTYPE /* re-used for EP0 */ +#define MGC_O_HDRC_TXINTERVAL 0x0B +#define MGC_O_HDRC_NAKLIMIT0 MGC_O_HDRC_TXINTERVAL /* re-used for EP0 */ +#define MGC_O_HDRC_RXTYPE 0x0C +#define MGC_O_HDRC_RXINTERVAL 0x0D +#define MGC_O_HDRC_FIFOSIZE 0x0F +#define MGC_O_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */ + +/* offsets to endpoint registers in indexed model (using INDEX register) */ +#define MGC_INDEXED_OFFSET(_bOffset) \ + (0x10 + (_bOffset)) + +/* offsets to endpoint registers in flat models */ +#define MGC_FLAT_OFFSET(_bEnd, _bOffset) \ + (0x100 + (0x10*(_bEnd)) + (_bOffset)) + +#ifdef CONFIG_USB_TUSB6010 +/* TUSB6010 EP0 configuration register is special */ +#define MGC_TUSB_OFFSET(_bEnd, _bOffset) \ + (0x10 + _bOffset) +#include "tusb6010.h" /* needed "only" for TUSB_EP0_CONF */ +#endif + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MGC_O_HDRC_TXFUNCADDR 0x00 +#define MGC_O_HDRC_TXHUBADDR 0x02 +#define MGC_O_HDRC_TXHUBPORT 0x03 + +#define MGC_O_HDRC_RXFUNCADDR 0x04 +#define MGC_O_HDRC_RXHUBADDR 0x06 +#define MGC_O_HDRC_RXHUBPORT 0x07 + +#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \ + (0x80 + (8*(_bEnd)) + (_bOffset)) + +/* + * MUSBHDRC Register bit masks + */ + +/* POWER */ + +#define MGC_M_POWER_ISOUPDATE 0x80 +#define MGC_M_POWER_SOFTCONN 0x40 +#define MGC_M_POWER_HSENAB 0x20 +#define MGC_M_POWER_HSMODE 0x10 +#define MGC_M_POWER_RESET 0x08 +#define MGC_M_POWER_RESUME 0x04 +#define MGC_M_POWER_SUSPENDM 0x02 +#define MGC_M_POWER_ENSUSPEND 0x01 + +/* INTRUSB */ +#define MGC_M_INTR_SUSPEND 0x01 +#define MGC_M_INTR_RESUME 0x02 +#define MGC_M_INTR_RESET 0x04 +#define MGC_M_INTR_BABBLE 0x04 +#define MGC_M_INTR_SOF 0x08 +#define MGC_M_INTR_CONNECT 0x10 +#define MGC_M_INTR_DISCONNECT 0x20 +#define MGC_M_INTR_SESSREQ 0x40 +#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */ + +/* DEVCTL */ +#define MGC_M_DEVCTL_BDEVICE 0x80 +#define MGC_M_DEVCTL_FSDEV 0x40 +#define MGC_M_DEVCTL_LSDEV 0x20 +#define MGC_M_DEVCTL_VBUS 0x18 +#define MGC_S_DEVCTL_VBUS 3 +#define MGC_M_DEVCTL_HM 0x04 +#define MGC_M_DEVCTL_HR 0x02 +#define MGC_M_DEVCTL_SESSION 0x01 + +/* TESTMODE */ + +#define MGC_M_TEST_FORCE_HOST 0x80 +#define MGC_M_TEST_FIFO_ACCESS 0x40 +#define MGC_M_TEST_FORCE_FS 0x20 +#define MGC_M_TEST_FORCE_HS 0x10 +#define MGC_M_TEST_PACKET 0x08 +#define MGC_M_TEST_K 0x04 +#define MGC_M_TEST_J 0x02 +#define MGC_M_TEST_SE0_NAK 0x01 + +/* allocate for double-packet buffering (effectively doubles assigned _SIZE) */ +#define MGC_M_FIFOSZ_DPB 0x10 +/* allocation size (8, 16, 32, ... 4096) */ +#define MGC_M_FIFOSZ_SIZE 0x0f + +/* CSR0 */ +#define MGC_M_CSR0_FLUSHFIFO 0x0100 +#define MGC_M_CSR0_TXPKTRDY 0x0002 +#define MGC_M_CSR0_RXPKTRDY 0x0001 + +/* CSR0 in Peripheral mode */ +#define MGC_M_CSR0_P_SVDSETUPEND 0x0080 +#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040 +#define MGC_M_CSR0_P_SENDSTALL 0x0020 +#define MGC_M_CSR0_P_SETUPEND 0x0010 +#define MGC_M_CSR0_P_DATAEND 0x0008 +#define MGC_M_CSR0_P_SENTSTALL 0x0004 + +/* CSR0 in Host mode */ +#define MGC_M_CSR0_H_DIS_PING 0x0800 +#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */ +#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */ +#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080 +#define MGC_M_CSR0_H_STATUSPKT 0x0040 +#define MGC_M_CSR0_H_REQPKT 0x0020 +#define MGC_M_CSR0_H_ERROR 0x0010 +#define MGC_M_CSR0_H_SETUPPKT 0x0008 +#define MGC_M_CSR0_H_RXSTALL 0x0004 + +/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MGC_M_CSR0_P_WZC_BITS \ + ( MGC_M_CSR0_P_SENTSTALL ) +#define MGC_M_CSR0_H_WZC_BITS \ + ( MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_RXSTALL \ + | MGC_M_CSR0_RXPKTRDY ) + + +/* TxType/RxType */ +#define MGC_M_TYPE_SPEED 0xc0 +#define MGC_S_TYPE_SPEED 6 +#define MGC_TYPE_SPEED_HIGH 1 +#define MGC_TYPE_SPEED_FULL 2 +#define MGC_TYPE_SPEED_LOW 3 +#define MGC_M_TYPE_PROTO 0x30 /* implicitly zero for ep0 */ +#define MGC_S_TYPE_PROTO 4 +#define MGC_M_TYPE_REMOTE_END 0xf /* implicitly zero for ep0 */ + +/* CONFIGDATA */ + +#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */ +#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */ +#define MGC_M_CONFIGDATA_BIGENDIAN 0x20 +#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ +#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ +#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */ +#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ +#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* data width 0/1 => 8/16bits */ + +/* TXCSR in Peripheral and Host mode */ + +#define MGC_M_TXCSR_AUTOSET 0x8000 +#define MGC_M_TXCSR_MODE 0x2000 +#define MGC_M_TXCSR_DMAENAB 0x1000 +#define MGC_M_TXCSR_FRCDATATOG 0x0800 +#define MGC_M_TXCSR_DMAMODE 0x0400 +#define MGC_M_TXCSR_CLRDATATOG 0x0040 +#define MGC_M_TXCSR_FLUSHFIFO 0x0008 +#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002 +#define MGC_M_TXCSR_TXPKTRDY 0x0001 + +/* TXCSR in Peripheral mode */ + +#define MGC_M_TXCSR_P_ISO 0x4000 +#define MGC_M_TXCSR_P_INCOMPTX 0x0080 +#define MGC_M_TXCSR_P_SENTSTALL 0x0020 +#define MGC_M_TXCSR_P_SENDSTALL 0x0010 +#define MGC_M_TXCSR_P_UNDERRUN 0x0004 + +/* TXCSR in Host mode */ + +#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200 +#define MGC_M_TXCSR_H_DATATOGGLE 0x0100 +#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080 +#define MGC_M_TXCSR_H_RXSTALL 0x0020 +#define MGC_M_TXCSR_H_ERROR 0x0004 + +/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MGC_M_TXCSR_P_WZC_BITS \ + ( MGC_M_TXCSR_P_INCOMPTX | MGC_M_TXCSR_P_SENTSTALL \ + | MGC_M_TXCSR_P_UNDERRUN | MGC_M_TXCSR_FIFONOTEMPTY ) +#define MGC_M_TXCSR_H_WZC_BITS \ + ( MGC_M_TXCSR_H_NAKTIMEOUT | MGC_M_TXCSR_H_RXSTALL \ + | MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_FIFONOTEMPTY ) + + +/* RXCSR in Peripheral and Host mode */ + +#define MGC_M_RXCSR_AUTOCLEAR 0x8000 +#define MGC_M_RXCSR_DMAENAB 0x2000 +#define MGC_M_RXCSR_DISNYET 0x1000 +#define MGC_M_RXCSR_PID_ERR 0x1000 +#define MGC_M_RXCSR_DMAMODE 0x0800 +#define MGC_M_RXCSR_INCOMPRX 0x0100 +#define MGC_M_RXCSR_CLRDATATOG 0x0080 +#define MGC_M_RXCSR_FLUSHFIFO 0x0010 +#define MGC_M_RXCSR_DATAERROR 0x0008 +#define MGC_M_RXCSR_FIFOFULL 0x0002 +#define MGC_M_RXCSR_RXPKTRDY 0x0001 + +/* RXCSR in Peripheral mode */ + +#define MGC_M_RXCSR_P_ISO 0x4000 +#define MGC_M_RXCSR_P_SENTSTALL 0x0040 +#define MGC_M_RXCSR_P_SENDSTALL 0x0020 +#define MGC_M_RXCSR_P_OVERRUN 0x0004 + +/* RXCSR in Host mode */ + +#define MGC_M_RXCSR_H_AUTOREQ 0x4000 +#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400 +#define MGC_M_RXCSR_H_DATATOGGLE 0x0200 +#define MGC_M_RXCSR_H_RXSTALL 0x0040 +#define MGC_M_RXCSR_H_REQPKT 0x0020 +#define MGC_M_RXCSR_H_ERROR 0x0004 + +/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */ +#define MGC_M_RXCSR_P_WZC_BITS \ + ( MGC_M_RXCSR_P_SENTSTALL | MGC_M_RXCSR_P_OVERRUN \ + | MGC_M_RXCSR_RXPKTRDY ) +#define MGC_M_RXCSR_H_WZC_BITS \ + ( MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_H_ERROR \ + | MGC_M_RXCSR_DATAERROR | MGC_M_RXCSR_RXPKTRDY ) + + +/* HUBADDR */ +#define MGC_M_HUBADDR_MULTI_TT 0x80 + + +#endif /* __MUSB_HDRC_DEFS_H__ */ + |