If this option is set, the driver enables cache flush.
- TPM Support:
- CONFIG_GENERIC_LPC_TPM
+ CONFIG_TPM
+ Support TPM devices.
+
+ CONFIG_TPM_TIS_I2C
+ Support for i2c bus TPM devices. Only one device
+ per system is supported at this time.
+
+ CONFIG_TPM_TIS_I2C_BUS_NUMBER
+ Define the the i2c bus number for the TPM device
+
+ CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS
+ Define the TPM's address on the i2c bus
+
+ CONFIG_TPM_TIS_I2C_BURST_LIMITATION
+ Define the burst count bytes upper limit
+
+ CONFIG_TPM_TIS_LPC
Support for generic parallel port TPM devices. Only one device
per system is supported at this time.
#ifdef CONFIG_NAND_DAVINCI
static int
davinci_std_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
struct nand_chip *this = mtd->priv;
int i, eccsize = chip->ecc.size;
return 0;
}
-static void davinci_std_write_page_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf)
+static int davinci_std_write_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required)
{
unsigned char davinci_ecc_buf[NAND_MAX_OOBSIZE];
struct nand_chip *this = mtd->priv;
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->write_buf(mtd, oob, i);
+ return 0;
}
static int davinci_std_write_oob_syndrome(struct mtd_info *mtd,
}
static int davinci_std_read_oob_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, int page, int sndcmd)
+ struct nand_chip *chip, int page)
{
struct nand_chip *this = mtd->priv;
uint8_t *buf = chip->oob_poi;
chip->read_buf(mtd, bufpoi, mtd->oobsize);
- return 1;
+ return 0;
}
static void nand_dm365evm_select_chip(struct mtd_info *mtd, int chip)
#ifdef CONFIG_ARM
timer_init, /* initialize timer */
#endif
-#ifdef CONFIG_BOARD_POSTCLK_INIT
- board_postclk_init,
-#endif
-#ifdef CONFIG_FSL_ESDHC
- get_clocks,
-#endif
#ifdef CONFIG_SYS_ALLOC_DPRAM
#if !defined(CONFIG_CPM2)
dpram_init,
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
board_postclk_init,
+#endif
+#ifdef CONFIG_FSL_ESDHC
+ get_clocks,
#endif
env_init, /* initialize environment */
#if defined(CONFIG_8xx_CPUCLK_DEFAULT)
static void fixup_silent_linux(void);
#endif
-static image_header_t *image_get_kernel(ulong img_addr, int verify);
-#if defined(CONFIG_FIT)
-static int fit_check_kernel(const void *fit, int os_noffset, int verify);
-#endif
-
static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[], bootm_headers_t *images,
ulong *os_data, ulong *os_len);
#if defined(CONFIG_OF_LIBFDT)
/* find flattened device tree */
- ret = boot_get_fdt(flag, argc, argv, &images,
+ ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,
&images.ft_addr, &images.ft_len);
if (ret) {
puts("Could not find a valid device tree\n");
ulong image_len = os.image_len;
__maybe_unused uint unc_len = CONFIG_SYS_BOOTM_LEN;
int no_overlap = 0;
+ void *load_buf, *image_buf;
#if defined(CONFIG_LZMA) || defined(CONFIG_LZO)
int ret;
#endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */
const char *type_name = genimg_get_type_name(os.type);
+ load_buf = map_sysmem(load, image_len);
+ image_buf = map_sysmem(image_start, image_len);
switch (comp) {
case IH_COMP_NONE:
if (load == blob_start || load == image_start) {
no_overlap = 1;
} else {
printf(" Loading %s ... ", type_name);
- memmove_wd((void *)load, (void *)image_start,
- image_len, CHUNKSZ);
+ memmove_wd(load_buf, image_buf, image_len, CHUNKSZ);
}
*load_end = load + image_len;
puts("OK\n");
#ifdef CONFIG_GZIP
case IH_COMP_GZIP:
printf(" Uncompressing %s ... ", type_name);
- if (gunzip((void *)load, unc_len,
- (uchar *)image_start, &image_len) != 0) {
+ if (gunzip(load_buf, unc_len, image_buf, &image_len) != 0) {
puts("GUNZIP: uncompress, out-of-mem or overwrite "
"error - must RESET board to recover\n");
if (boot_progress)
* use slower decompression algorithm which requires
* at most 2300 KB of memory.
*/
- int i = BZ2_bzBuffToBuffDecompress((char *)load,
- &unc_len, (char *)image_start, image_len,
- CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
+ int i = BZ2_bzBuffToBuffDecompress(load_buf, &unc_len,
+ image_buf, image_len,
+ CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
if (i != BZ_OK) {
printf("BUNZIP2: uncompress or overwrite error %d "
"- must RESET board to recover\n", i);
SizeT lzma_len = unc_len;
printf(" Uncompressing %s ... ", type_name);
- ret = lzmaBuffToBuffDecompress(
- (unsigned char *)load, &lzma_len,
- (unsigned char *)image_start, image_len);
+ ret = lzmaBuffToBuffDecompress(load_buf, &lzma_len,
+ image_buf, image_len);
unc_len = lzma_len;
if (ret != SZ_OK) {
printf("LZMA: uncompress or overwrite error %d "
case IH_COMP_LZO:
printf(" Uncompressing %s ... ", type_name);
- ret = lzop_decompress((const unsigned char *)image_start,
- image_len, (unsigned char *)load,
- &unc_len);
+ ret = lzop_decompress(image_buf, image_len, load_buf,
+ &unc_len);
if (ret != LZO_E_OK) {
printf("LZO: uncompress or overwrite error %d "
"- must RESET board to recover\n", ret);
return hdr;
}
-/**
- * fit_check_kernel - verify FIT format kernel subimage
- * @fit_hdr: pointer to the FIT image header
- * os_noffset: kernel subimage node offset within FIT image
- * @verify: data CRC verification flag
- *
- * fit_check_kernel() verifies integrity of the kernel subimage and from
- * specified FIT image.
- *
- * returns:
- * 1, on success
- * 0, on failure
- */
-#if defined(CONFIG_FIT)
-static int fit_check_kernel(const void *fit, int os_noffset, int verify)
-{
- fit_image_print(fit, os_noffset, " ");
-
- if (verify) {
- puts(" Verifying Hash Integrity ... ");
- if (!fit_image_verify(fit, os_noffset)) {
- puts("Bad Data Hash\n");
- bootstage_error(BOOTSTAGE_ID_FIT_CHECK_HASH);
- return 0;
- }
- puts("OK\n");
- }
- bootstage_mark(BOOTSTAGE_ID_FIT_CHECK_ARCH);
-
- if (!fit_image_check_target_arch(fit, os_noffset)) {
- puts("Unsupported Architecture\n");
- bootstage_error(BOOTSTAGE_ID_FIT_CHECK_ARCH);
- return 0;
- }
-
- bootstage_mark(BOOTSTAGE_ID_FIT_CHECK_KERNEL);
- if (!fit_image_check_type(fit, os_noffset, IH_TYPE_KERNEL) &&
- !fit_image_check_type(fit, os_noffset, IH_TYPE_KERNEL_NOLOAD)) {
- puts("Not a kernel image\n");
- bootstage_error(BOOTSTAGE_ID_FIT_CHECK_KERNEL);
- return 0;
- }
-
- bootstage_mark(BOOTSTAGE_ID_FIT_CHECKED);
- return 1;
-}
-#endif /* CONFIG_FIT */
-
/**
* boot_get_kernel - find kernel image
* @os_data: pointer to a ulong variable, will hold os data start address
ulong img_addr;
const void *buf;
#if defined(CONFIG_FIT)
- const void *fit_hdr;
const char *fit_uname_config = NULL;
const char *fit_uname_kernel = NULL;
- const void *data;
- size_t len;
- int cfg_noffset;
int os_noffset;
#endif
break;
#if defined(CONFIG_FIT)
case IMAGE_FORMAT_FIT:
- fit_hdr = buf;
- printf("## Booting kernel from FIT Image at %08lx ...\n",
- img_addr);
-
- if (!fit_check_format(fit_hdr)) {
- puts("Bad FIT kernel image format!\n");
- bootstage_error(BOOTSTAGE_ID_FIT_FORMAT);
+ os_noffset = fit_image_load(images, FIT_KERNEL_PROP,
+ img_addr,
+ &fit_uname_kernel, fit_uname_config,
+ IH_ARCH_DEFAULT, IH_TYPE_KERNEL,
+ BOOTSTAGE_ID_FIT_KERNEL_START,
+ FIT_LOAD_IGNORED, os_data, os_len);
+ if (os_noffset < 0)
return NULL;
- }
- bootstage_mark(BOOTSTAGE_ID_FIT_FORMAT);
-
- if (!fit_uname_kernel) {
- /*
- * no kernel image node unit name, try to get config
- * node first. If config unit node name is NULL
- * fit_conf_get_node() will try to find default config
- * node
- */
- bootstage_mark(BOOTSTAGE_ID_FIT_NO_UNIT_NAME);
-#ifdef CONFIG_FIT_BEST_MATCH
- if (fit_uname_config)
- cfg_noffset =
- fit_conf_get_node(fit_hdr,
- fit_uname_config);
- else
- cfg_noffset =
- fit_conf_find_compat(fit_hdr,
- gd->fdt_blob);
-#else
- cfg_noffset = fit_conf_get_node(fit_hdr,
- fit_uname_config);
-#endif
- if (cfg_noffset < 0) {
- bootstage_error(BOOTSTAGE_ID_FIT_NO_UNIT_NAME);
- return NULL;
- }
- /* save configuration uname provided in the first
- * bootm argument
- */
- images->fit_uname_cfg = fdt_get_name(fit_hdr,
- cfg_noffset,
- NULL);
- printf(" Using '%s' configuration\n",
- images->fit_uname_cfg);
- bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG);
-
- os_noffset = fit_conf_get_kernel_node(fit_hdr,
- cfg_noffset);
- fit_uname_kernel = fit_get_name(fit_hdr, os_noffset,
- NULL);
- } else {
- /* get kernel component image node offset */
- bootstage_mark(BOOTSTAGE_ID_FIT_UNIT_NAME);
- os_noffset = fit_image_get_node(fit_hdr,
- fit_uname_kernel);
- }
- if (os_noffset < 0) {
- bootstage_error(BOOTSTAGE_ID_FIT_CONFIG);
- return NULL;
- }
-
- printf(" Trying '%s' kernel subimage\n", fit_uname_kernel);
-
- bootstage_mark(BOOTSTAGE_ID_FIT_CHECK_SUBIMAGE);
- if (!fit_check_kernel(fit_hdr, os_noffset, images->verify))
- return NULL;
-
- /* get kernel image data address and length */
- if (fit_image_get_data(fit_hdr, os_noffset, &data, &len)) {
- puts("Could not find kernel subimage data!\n");
- bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO_ERR);
- return NULL;
- }
- bootstage_mark(BOOTSTAGE_ID_FIT_KERNEL_INFO);
- *os_len = len;
- *os_data = (ulong)data;
- images->fit_hdr_os = (void *)fit_hdr;
+ images->fit_hdr_os = map_sysmem(img_addr, 0);
images->fit_uname_os = fit_uname_kernel;
images->fit_noffset_os = os_noffset;
break;
#if defined(CONFIG_OF_LIBFDT)
/* find flattened device tree */
- ret = boot_get_fdt(flag, argc, argv, images,
+ ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, images,
&images->ft_addr, &images->ft_len);
if (ret) {
puts("Could not find a valid device tree\n");
#include <common.h>
#include <command.h>
+#include <net.h>
#if !defined(CONFIG_UPDATE_TFTP)
#error "CONFIG_UPDATE_TFTP required"
#endif
-extern int update_tftp(ulong addr);
-
static int do_fitupd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
ulong addr = 0UL;
ops.oobbuf = oobbuf;
ops.len = nand->writesize;
ops.ooblen = nand->oobsize;
- ops.mode = MTD_OOB_RAW;
- i = nand->read_oob(nand, addr, &ops);
+ ops.mode = MTD_OPS_RAW;
+ i = mtd_read_oob(nand, addr, &ops);
if (i < 0) {
printf("Error (%d) reading page %08lx\n", i, off);
free(datbuf);
.oobbuf = ((u8 *)addr) + nand->writesize,
.len = nand->writesize,
.ooblen = nand->oobsize,
- .mode = MTD_OOB_RAW
+ .mode = MTD_OPS_RAW
};
if (read)
- ret = nand->read_oob(nand, off, &ops);
+ ret = mtd_read_oob(nand, off, &ops);
else
- ret = nand->write_oob(nand, off, &ops);
+ ret = mtd_write_oob(nand, off, &ops);
if (ret) {
printf("%s: error at offset %llx, ret %d\n",
return ret;
}
+/* Adjust a chip/partition size down for bad blocks so we don't
+ * read/write/erase past the end of a chip/partition by accident.
+ */
+static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev)
+{
+ /* We grab the nand info object here fresh because this is usually
+ * called after arg_off_size() which can change the value of dev.
+ */
+ nand_info_t *nand = &nand_info[dev];
+ loff_t maxoffset = offset + *size;
+ int badblocks = 0;
+
+ /* count badblocks in NAND from offset to offset + size */
+ for (; offset < maxoffset; offset += nand->erasesize) {
+ if (nand_block_isbad(nand, offset))
+ badblocks++;
+ }
+ /* adjust size if any bad blocks found */
+ if (badblocks) {
+ *size -= badblocks * nand->erasesize;
+ printf("size adjusted to 0x%llx (%d bad blocks)\n",
+ (unsigned long long)*size, badblocks);
+ }
+}
+
static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int i, ret = 0;
int scrub = !strncmp(cmd, "scrub", 5);
int spread = 0;
int args = 2;
+ int adjust_size = 0;
const char *scrub_warn =
"Warning: "
"scrub option will erase all factory set bad blocks!\n"
spread = 1;
} else if (!strcmp(&cmd[5], ".part")) {
args = 1;
+ adjust_size = 1;
} else if (!strcmp(&cmd[5], ".chip")) {
args = 0;
+ adjust_size = 1;
} else {
goto usage;
}
&maxsize) != 0)
return 1;
+ /* size is unspecified */
+ if (adjust_size && !scrub)
+ adjust_size_for_badblocks(&size, off, dev);
+
nand = &nand_info[dev];
memset(&opts, 0, sizeof(opts));
&off, &size, &maxsize) != 0)
return 1;
+ /* size is unspecified */
+ if (argc < 5)
+ adjust_size_for_badblocks(&size, off, dev);
rwsize = size;
}
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = rwsize,
- .mode = MTD_OOB_RAW
+ .mode = MTD_OPS_RAW
};
if (read)
- ret = nand->read_oob(nand, off, &ops);
+ ret = mtd_read_oob(nand, off, &ops);
else
- ret = nand->write_oob(nand, off, &ops);
+ ret = mtd_write_oob(nand, off, &ops);
} else if (raw) {
ret = raw_access(nand, addr, off, pagecount, read);
} else {
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
- if (nand->block_markbad(nand, addr)) {
+ if (mtd_block_markbad(nand, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %d\n",
addr, ret);
ops.len = blocksize;
while (blocks) {
- ret = mtd->block_isbad(mtd, ofs);
+ ret = mtd_block_isbad(mtd, ofs);
if (ret) {
printk("Bad blocks %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
ops.datbuf = buf;
ops.retlen = 0;
- ret = mtd->read_oob(mtd, ofs, &ops);
+ ret = mtd_read_oob(mtd, ofs, &ops);
if (ret) {
printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
ofs += blocksize;
struct mtd_oob_ops ops = {
.len = mtd->writesize,
.ooblen = mtd->oobsize,
- .mode = MTD_OOB_AUTO,
+ .mode = MTD_OPS_AUTO_OOB,
};
int page, ret = 0;
for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) {
buf += mtd->writesize;
ops.oobbuf = (u_char *)buf;
buf += mtd->oobsize;
- ret = mtd->write_oob(mtd, to, &ops);
+ ret = mtd_write_oob(mtd, to, &ops);
if (ret)
break;
to += mtd->writesize;
ofs = to;
while (blocks) {
- ret = mtd->block_isbad(mtd, ofs);
+ ret = mtd_block_isbad(mtd, ofs);
if (ret) {
printk("Bad blocks %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
}
if (!withoob)
- ret = mtd->write(mtd, ofs, blocksize, &_retlen, buf);
+ ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf);
else
ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen);
if (ret) {
int blocksize = 1 << this->erase_shift;
for (ofs = start; ofs < (start + size); ofs += blocksize) {
- ret = mtd->block_isbad(mtd, ofs);
+ ret = mtd_block_isbad(mtd, ofs);
if (ret && !force) {
printf("Skip erase bad block %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
instr.len = blocksize;
instr.priv = force;
instr.mtd = mtd;
- ret = mtd->erase(mtd, &instr);
+ ret = mtd_erase(mtd, &instr);
if (ret) {
printf("erase failed block %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
while (blocks < end_block) {
printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
- ret = mtd->block_isbad(mtd, ofs);
+ ret = mtd_block_isbad(mtd, ofs);
if (ret) {
printf("Skip erase bad block %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
instr.addr = ofs;
instr.len = blocksize;
- ret = mtd->erase(mtd, &instr);
+ ret = mtd_erase(mtd, &instr);
if (ret) {
printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
goto next;
}
- ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
+ ret = mtd_write(mtd, ofs, blocksize, &retlen, buf);
if (ret) {
printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
goto next;
}
- ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
+ ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf);
if (ret) {
printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
goto next;
ops.len = mtd->writesize;
ops.ooblen = mtd->oobsize;
ops.retlen = 0;
- i = mtd->read_oob(mtd, addr, &ops);
+ i = mtd_read_oob(mtd, addr, &ops);
if (i < 0) {
printf("Error (%d) reading page %08lx\n", i, off);
free(datbuf);
/* Currently only one OneNAND device is supported */
printf("\nDevice %d bad blocks:\n", 0);
for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
- if (mtd->block_isbad(mtd, ofs))
+ if (mtd_block_isbad(mtd, ofs))
printf(" %08x\n", (u32)ofs);
}
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
- if (mtd->block_markbad(mtd, addr)) {
+ if (mtd_block_markbad(mtd, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %d\n",
addr, ret);
/* Check OneNAND exist */
if (mtd->writesize)
/* Ignore read fail */
- mtd->read(mtd, env_addr, ONENAND_MAX_ENV_SIZE,
+ mtd_read(mtd, env_addr, ONENAND_MAX_ENV_SIZE,
&retlen, (u_char *)buf);
else
mtd->writesize = MAX_ONENAND_PAGESIZE;
#endif
instr.addr = env_addr;
instr.mtd = mtd;
- if (mtd->erase(mtd, &instr)) {
+ if (mtd_erase(mtd, &instr)) {
printf("OneNAND: erase failed at 0x%08llx\n", env_addr);
return 1;
}
- if (mtd->write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen,
+ if (mtd_write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen,
(u_char *)&env_new)) {
printf("OneNAND: write failed at 0x%llx\n", instr.addr);
return 2;
return 1;
}
-#if defined(CONFIG_FIT)
-/**
- * fit_check_fdt - verify FIT format FDT subimage
- * @fit_hdr: pointer to the FIT header
- * fdt_noffset: FDT subimage node offset within FIT image
- * @verify: data CRC verification flag
- *
- * fit_check_fdt() verifies integrity of the FDT subimage and from
- * specified FIT image.
- *
- * returns:
- * 1, on success
- * 0, on failure
- */
-static int fit_check_fdt(const void *fit, int fdt_noffset, int verify)
-{
- fit_image_print(fit, fdt_noffset, " ");
-
- if (verify) {
- puts(" Verifying Hash Integrity ... ");
- if (!fit_image_verify(fit, fdt_noffset)) {
- fdt_error("Bad Data Hash");
- return 0;
- }
- puts("OK\n");
- }
-
- if (!fit_image_check_type(fit, fdt_noffset, IH_TYPE_FLATDT)) {
- fdt_error("Not a FDT image");
- return 0;
- }
-
- if (!fit_image_check_comp(fit, fdt_noffset, IH_COMP_NONE)) {
- fdt_error("FDT image is compressed");
- return 0;
- }
-
- return 1;
-}
-#endif
-
/**
* boot_get_fdt - main fdt handling routine
* @argc: command argument count
* @argv: command argument list
+ * @arch: architecture (IH_ARCH_...)
* @images: pointer to the bootm images structure
* @of_flat_tree: pointer to a char* variable, will hold fdt start address
* @of_size: pointer to a ulong variable, will hold fdt length
* 1, if fdt image is found but corrupted
* of_flat_tree and of_size are set to 0 if no fdt exists
*/
-int boot_get_fdt(int flag, int argc, char * const argv[],
+int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
bootm_headers_t *images, char **of_flat_tree, ulong *of_size)
{
const image_header_t *fdt_hdr;
ulong fdt_addr;
char *fdt_blob = NULL;
ulong image_start, image_data, image_end;
- ulong load_start, load_end;
+ ulong load, load_end;
void *buf;
#if defined(CONFIG_FIT)
- void *fit_hdr;
const char *fit_uname_config = NULL;
const char *fit_uname_fdt = NULL;
ulong default_addr;
- int cfg_noffset;
int fdt_noffset;
- const void *data;
- size_t size;
#endif
*of_flat_tree = NULL;
* command argument
*/
fdt_addr = map_to_sysmem(images->fit_hdr_os);
- fit_uname_config = images->fit_uname_cfg;
- debug("* fdt: using config '%s' from image at 0x%08lx\n",
- fit_uname_config, fdt_addr);
-
- /*
- * Check whether configuration has FDT blob defined,
- * if not quit silently.
- */
- fit_hdr = images->fit_hdr_os;
- cfg_noffset = fit_conf_get_node(fit_hdr,
- fit_uname_config);
- if (cfg_noffset < 0) {
- debug("* fdt: no such config\n");
+ fdt_noffset = fit_get_node_from_config(images,
+ FIT_FDT_PROP,
+ fdt_addr);
+ if (fdt_noffset == -ENOLINK)
return 0;
- }
-
- fdt_noffset = fit_conf_get_fdt_node(fit_hdr,
- cfg_noffset);
- if (fdt_noffset < 0) {
- debug("* fdt: no fdt in config\n");
- return 0;
- }
+ else if (fdt_noffset < 0)
+ return 1;
}
#endif
-
debug("## Checking for 'FDT'/'FDT Image' at %08lx\n",
fdt_addr);
image_data = (ulong)image_get_data(fdt_hdr);
image_end = image_get_image_end(fdt_hdr);
- load_start = image_get_load(fdt_hdr);
- load_end = load_start + image_get_data_size(fdt_hdr);
+ load = image_get_load(fdt_hdr);
+ load_end = load + image_get_data_size(fdt_hdr);
- if (load_start == image_start ||
- load_start == image_data) {
+ if (load == image_start ||
+ load == image_data) {
fdt_blob = (char *)image_data;
break;
}
- if ((load_start < image_end) &&
- (load_end > image_start)) {
+ if ((load < image_end) && (load_end > image_start)) {
fdt_error("fdt overwritten");
goto error;
}
debug(" Loading FDT from 0x%08lx to 0x%08lx\n",
- image_data, load_start);
+ image_data, load);
- memmove((void *)load_start,
+ memmove((void *)load,
(void *)image_data,
image_get_data_size(fdt_hdr));
- fdt_blob = (char *)load_start;
+ fdt_addr = load;
break;
case IMAGE_FORMAT_FIT:
/*
#if defined(CONFIG_FIT)
/* check FDT blob vs FIT blob */
if (fit_check_format(buf)) {
- /*
- * FIT image
- */
- fit_hdr = buf;
- printf("## Flattened Device Tree from FIT Image at %08lx\n",
- fdt_addr);
-
- if (!fit_uname_fdt) {
- /*
- * no FDT blob image node unit name,
- * try to get config node first. If
- * config unit node name is NULL
- * fit_conf_get_node() will try to
- * find default config node
- */
- cfg_noffset = fit_conf_get_node(fit_hdr,
- fit_uname_config);
-
- if (cfg_noffset < 0) {
- fdt_error("Could not find configuration node\n");
- goto error;
- }
-
- fit_uname_config = fdt_get_name(fit_hdr,
- cfg_noffset, NULL);
- printf(" Using '%s' configuration\n",
- fit_uname_config);
-
- fdt_noffset = fit_conf_get_fdt_node(
- fit_hdr,
- cfg_noffset);
- fit_uname_fdt = fit_get_name(fit_hdr,
- fdt_noffset, NULL);
- } else {
- /*
- * get FDT component image node
- * offset
- */
- fdt_noffset = fit_image_get_node(
- fit_hdr,
- fit_uname_fdt);
- }
- if (fdt_noffset < 0) {
- fdt_error("Could not find subimage node\n");
- goto error;
- }
-
- printf(" Trying '%s' FDT blob subimage\n",
- fit_uname_fdt);
-
- if (!fit_check_fdt(fit_hdr, fdt_noffset,
- images->verify))
- goto error;
-
- /* get ramdisk image data address and length */
- if (fit_image_get_data(fit_hdr, fdt_noffset,
- &data, &size)) {
- fdt_error("Could not find FDT subimage data");
- goto error;
- }
+ ulong load, len;
- /*
- * verify that image data is a proper FDT
- * blob
- */
- if (fdt_check_header((char *)data) != 0) {
- fdt_error("Subimage data is not a FTD");
- goto error;
- }
-
- /*
- * move image data to the load address,
- * make sure we don't overwrite initial image
- */
- image_start = (ulong)fit_hdr;
- image_end = fit_get_end(fit_hdr);
-
- if (fit_image_get_load(fit_hdr, fdt_noffset,
- &load_start) == 0) {
- load_end = load_start + size;
+ fdt_noffset = fit_image_load(images,
+ FIT_FDT_PROP,
+ fdt_addr, &fit_uname_fdt,
+ fit_uname_config,
+ arch, IH_TYPE_FLATDT,
+ BOOTSTAGE_ID_FIT_FDT_START,
+ FIT_LOAD_OPTIONAL, &load, &len);
- if ((load_start < image_end) &&
- (load_end > image_start)) {
- fdt_error("FDT overwritten");
- goto error;
- }
-
- printf(" Loading FDT from 0x%08lx to 0x%08lx\n",
- (ulong)data, load_start);
-
- memmove((void *)load_start,
- (void *)data, size);
-
- fdt_blob = (char *)load_start;
- } else {
- fdt_blob = (char *)data;
- }
-
- images->fit_hdr_fdt = fit_hdr;
+ images->fit_hdr_fdt = map_sysmem(fdt_addr, 0);
images->fit_uname_fdt = fit_uname_fdt;
images->fit_noffset_fdt = fdt_noffset;
+ fdt_addr = load;
break;
} else
#endif
/*
* FDT blob
*/
- fdt_blob = buf;
debug("* fdt: raw FDT blob\n");
printf("## Flattened Device Tree blob at %08lx\n",
(long)fdt_addr);
goto error;
}
- printf(" Booting using the fdt blob at 0x%p\n", fdt_blob);
-
+ printf(" Booting using the fdt blob at %#08lx\n", fdt_addr);
+ fdt_blob = map_sysmem(fdt_addr, 0);
} else if (images->legacy_hdr_valid &&
image_check_type(&images->legacy_hdr_os_copy,
IH_TYPE_MULTI)) {
#include <time.h>
#else
#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
#endif /* !USE_HOSTCC*/
#include <bootstage.h>
#ifndef USE_HOSTCC
printf("%s Data Start: ", p);
- if (ret)
+ if (ret) {
printf("unavailable\n");
- else
- printf("0x%08lx\n", (ulong)data);
+ } else {
+ void *vdata = (void *)data;
+
+ printf("0x%08lx\n", (ulong)map_to_sysmem(vdata));
+ }
#endif
printf("%s Data Size: ", p);
return fit_image_get_node(fit, uname);
}
-/**
- * fit_conf_get_kernel_node - get kernel image node offset that corresponds to
- * a given configuration
- * @fit: pointer to the FIT format image header
- * @noffset: configuration node offset
- *
- * fit_conf_get_kernel_node() retrives kernel image node unit name from
- * configuration FIT_KERNEL_PROP property and translates it to the node
- * offset.
- *
- * returns:
- * image node offset when found (>=0)
- * negative number on failure (FDT_ERR_* code)
- */
-int fit_conf_get_kernel_node(const void *fit, int noffset)
-{
- return fit_conf_get_prop_node(fit, noffset, FIT_KERNEL_PROP);
-}
-
-/**
- * fit_conf_get_ramdisk_node - get ramdisk image node offset that corresponds to
- * a given configuration
- * @fit: pointer to the FIT format image header
- * @noffset: configuration node offset
- *
- * fit_conf_get_ramdisk_node() retrives ramdisk image node unit name from
- * configuration FIT_KERNEL_PROP property and translates it to the node
- * offset.
- *
- * returns:
- * image node offset when found (>=0)
- * negative number on failure (FDT_ERR_* code)
- */
-int fit_conf_get_ramdisk_node(const void *fit, int noffset)
-{
- return fit_conf_get_prop_node(fit, noffset, FIT_RAMDISK_PROP);
-}
-
-/**
- * fit_conf_get_fdt_node - get fdt image node offset that corresponds to
- * a given configuration
- * @fit: pointer to the FIT format image header
- * @noffset: configuration node offset
- *
- * fit_conf_get_fdt_node() retrives fdt image node unit name from
- * configuration FIT_KERNEL_PROP property and translates it to the node
- * offset.
- *
- * returns:
- * image node offset when found (>=0)
- * negative number on failure (FDT_ERR_* code)
- */
-int fit_conf_get_fdt_node(const void *fit, int noffset)
-{
- return fit_conf_get_prop_node(fit, noffset, FIT_FDT_PROP);
-}
-
/**
* fit_conf_print - prints out the FIT configuration details
* @fit: pointer to the FIT format image header
printf("%s FDT: %s\n", p, uname);
}
-/**
- * fit_check_ramdisk - verify FIT format ramdisk subimage
- * @fit_hdr: pointer to the FIT ramdisk header
- * @rd_noffset: ramdisk subimage node offset within FIT image
- * @arch: requested ramdisk image architecture type
- * @verify: data CRC verification flag
- *
- * fit_check_ramdisk() verifies integrity of the ramdisk subimage and from
- * specified FIT image.
- *
- * returns:
- * 1, on success
- * 0, on failure
- */
-int fit_check_ramdisk(const void *fit, int rd_noffset, uint8_t arch,
- int verify)
+int fit_image_select(const void *fit, int rd_noffset, int verify)
{
fit_image_print(fit, rd_noffset, " ");
puts(" Verifying Hash Integrity ... ");
if (!fit_image_verify(fit, rd_noffset)) {
puts("Bad Data Hash\n");
- bootstage_error(BOOTSTAGE_ID_FIT_RD_HASH);
- return 0;
+ return -EACCES;
}
puts("OK\n");
}
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_CHECK_ALL);
- if (!fit_image_check_os(fit, rd_noffset, IH_OS_LINUX) ||
- !fit_image_check_arch(fit, rd_noffset, arch) ||
- !fit_image_check_type(fit, rd_noffset, IH_TYPE_RAMDISK)) {
- printf("No Linux %s Ramdisk Image\n",
- genimg_get_arch_name(arch));
- bootstage_error(BOOTSTAGE_ID_FIT_RD_CHECK_ALL);
- return 0;
+ return 0;
+}
+
+int fit_get_node_from_config(bootm_headers_t *images, const char *prop_name,
+ ulong addr)
+{
+ int cfg_noffset;
+ void *fit_hdr;
+ int noffset;
+
+ debug("* %s: using config '%s' from image at 0x%08lx\n",
+ prop_name, images->fit_uname_cfg, addr);
+
+ /* Check whether configuration has this property defined */
+ fit_hdr = map_sysmem(addr, 0);
+ cfg_noffset = fit_conf_get_node(fit_hdr, images->fit_uname_cfg);
+ if (cfg_noffset < 0) {
+ debug("* %s: no such config\n", prop_name);
+ return -ENOENT;
}
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_CHECK_ALL_OK);
- return 1;
+ noffset = fit_conf_get_prop_node(fit_hdr, cfg_noffset, prop_name);
+ if (noffset < 0) {
+ debug("* %s: no '%s' in config\n", prop_name, prop_name);
+ return -ENOLINK;
+ }
+
+ return noffset;
+}
+
+int fit_image_load(bootm_headers_t *images, const char *prop_name, ulong addr,
+ const char **fit_unamep, const char *fit_uname_config,
+ int arch, int image_type, int bootstage_id,
+ enum fit_load_op load_op, ulong *datap, ulong *lenp)
+{
+ int cfg_noffset, noffset;
+ const char *fit_uname;
+ const void *fit;
+ const void *buf;
+ size_t size;
+ int type_ok, os_ok;
+ ulong load, data, len;
+ int ret;
+
+ fit = map_sysmem(addr, 0);
+ fit_uname = fit_unamep ? *fit_unamep : NULL;
+ printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr);
+
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT);
+ if (!fit_check_format(fit)) {
+ printf("Bad FIT %s image format!\n", prop_name);
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT);
+ return -ENOEXEC;
+ }
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK);
+ if (fit_uname) {
+ /* get ramdisk component image node offset */
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_UNIT_NAME);
+ noffset = fit_image_get_node(fit, fit_uname);
+ } else {
+ /*
+ * no image node unit name, try to get config
+ * node first. If config unit node name is NULL
+ * fit_conf_get_node() will try to find default config node
+ */
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_NO_UNIT_NAME);
+ if (IMAGE_ENABLE_BEST_MATCH && !fit_uname_config) {
+ cfg_noffset = fit_conf_find_compat(fit, gd_fdt_blob());
+ } else {
+ cfg_noffset = fit_conf_get_node(fit,
+ fit_uname_config);
+ }
+ if (cfg_noffset < 0) {
+ puts("Could not find configuration node\n");
+ bootstage_error(bootstage_id +
+ BOOTSTAGE_SUB_NO_UNIT_NAME);
+ return -ENOENT;
+ }
+ fit_uname_config = fdt_get_name(fit, cfg_noffset, NULL);
+ printf(" Using '%s' configuration\n", fit_uname_config);
+ if (image_type == IH_TYPE_KERNEL) {
+ /* Remember (and possibly verify) this config */
+ images->fit_uname_cfg = fit_uname_config;
+ if (IMAGE_ENABLE_VERIFY && images->verify) {
+ puts(" Verifying Hash Integrity ... ");
+ if (!fit_config_verify(fit, cfg_noffset)) {
+ puts("Bad Data Hash\n");
+ bootstage_error(bootstage_id +
+ BOOTSTAGE_SUB_HASH);
+ return -EACCES;
+ }
+ puts("OK\n");
+ }
+ bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG);
+ }
+
+ noffset = fit_conf_get_prop_node(fit, cfg_noffset,
+ prop_name);
+ fit_uname = fit_get_name(fit, noffset, NULL);
+ }
+ if (noffset < 0) {
+ puts("Could not find subimage node\n");
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_SUBNODE);
+ return -ENOENT;
+ }
+
+ printf(" Trying '%s' %s subimage\n", fit_uname, prop_name);
+
+ ret = fit_image_select(fit, noffset, images->verify);
+ if (ret) {
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
+ return ret;
+ }
+
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH);
+ if (!fit_image_check_target_arch(fit, noffset)) {
+ puts("Unsupported Architecture\n");
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ARCH);
+ return -ENOEXEC;
+ }
+
+ if (image_type == IH_TYPE_FLATDT &&
+ !fit_image_check_comp(fit, noffset, IH_COMP_NONE)) {
+ puts("FDT image is compressed");
+ return -EPROTONOSUPPORT;
+ }
+
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL);
+ type_ok = fit_image_check_type(fit, noffset, image_type) ||
+ (image_type == IH_TYPE_KERNEL &&
+ fit_image_check_type(fit, noffset,
+ IH_TYPE_KERNEL_NOLOAD));
+ os_ok = image_type == IH_TYPE_FLATDT ||
+ fit_image_check_os(fit, noffset, IH_OS_LINUX);
+ if (!type_ok || !os_ok) {
+ printf("No Linux %s %s Image\n", genimg_get_arch_name(arch),
+ genimg_get_type_name(image_type));
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL);
+ return -EIO;
+ }
+
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL_OK);
+
+ /* get image data address and length */
+ if (fit_image_get_data(fit, noffset, &buf, &size)) {
+ printf("Could not find %s subimage data!\n", prop_name);
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_GET_DATA);
+ return -ENOMEDIUM;
+ }
+ len = (ulong)size;
+
+ /* verify that image data is a proper FDT blob */
+ if (image_type == IH_TYPE_FLATDT && fdt_check_header((char *)buf)) {
+ puts("Subimage data is not a FDT");
+ return -ENOEXEC;
+ }
+
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_GET_DATA_OK);
+
+ /*
+ * Work-around for eldk-4.2 which gives this warning if we try to
+ * case in the unmap_sysmem() call:
+ * warning: initialization discards qualifiers from pointer target type
+ */
+ {
+ void *vbuf = (void *)buf;
+
+ data = map_to_sysmem(vbuf);
+ }
+
+ if (load_op == FIT_LOAD_IGNORED) {
+ /* Don't load */
+ } else if (fit_image_get_load(fit, noffset, &load)) {
+ if (load_op == FIT_LOAD_REQUIRED) {
+ printf("Can't get %s subimage load address!\n",
+ prop_name);
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_LOAD);
+ return -EBADF;
+ }
+ } else {
+ ulong image_start, image_end;
+ ulong load_end;
+ void *dst;
+
+ /*
+ * move image data to the load address,
+ * make sure we don't overwrite initial image
+ */
+ image_start = addr;
+ image_end = addr + fit_get_size(fit);
+
+ load_end = load + len;
+ if (image_type != IH_TYPE_KERNEL &&
+ load < image_end && load_end > image_start) {
+ printf("Error: %s overwritten\n", prop_name);
+ return -EXDEV;
+ }
+
+ printf(" Loading %s from 0x%08lx to 0x%08lx\n",
+ prop_name, data, load);
+
+ dst = map_sysmem(load, len);
+ memmove(dst, buf, len);
+ data = load;
+ }
+ bootstage_mark(bootstage_id + BOOTSTAGE_SUB_LOAD);
+
+ *datap = data;
+ *lenp = len;
+ if (fit_unamep)
+ *fit_unamep = (char *)fit_uname;
+
+ return noffset;
}
#include <u-boot/md5.h>
#include <sha1.h>
+#include <asm/errno.h>
#include <asm/io.h>
#ifdef CONFIG_CMD_BDI
char *end;
#endif
#if defined(CONFIG_FIT)
- void *fit_hdr;
const char *fit_uname_config = NULL;
const char *fit_uname_ramdisk = NULL;
ulong default_addr;
int rd_noffset;
- int cfg_noffset;
- const void *data;
- size_t size;
#endif
*rd_start = 0;
#if defined(CONFIG_FIT)
} else {
/* use FIT configuration provided in first bootm
- * command argument
+ * command argument. If the property is not defined,
+ * quit silently.
*/
rd_addr = map_to_sysmem(images->fit_hdr_os);
- fit_uname_config = images->fit_uname_cfg;
- debug("* ramdisk: using config '%s' from image "
- "at 0x%08lx\n",
- fit_uname_config, rd_addr);
-
- /*
- * Check whether configuration has ramdisk defined,
- * if not, don't try to use it, quit silently.
- */
- fit_hdr = images->fit_hdr_os;
- cfg_noffset = fit_conf_get_node(fit_hdr,
- fit_uname_config);
- if (cfg_noffset < 0) {
- debug("* ramdisk: no such config\n");
- return 1;
- }
-
- rd_noffset = fit_conf_get_ramdisk_node(fit_hdr,
- cfg_noffset);
- if (rd_noffset < 0) {
- debug("* ramdisk: no ramdisk in config\n");
+ rd_noffset = fit_get_node_from_config(images,
+ FIT_RAMDISK_PROP, rd_addr);
+ if (rd_noffset == -ENOLINK)
return 0;
- }
+ else if (rd_noffset < 0)
+ return 1;
}
#endif
break;
#if defined(CONFIG_FIT)
case IMAGE_FORMAT_FIT:
- fit_hdr = buf;
- printf("## Loading init Ramdisk from FIT "
- "Image at %08lx ...\n", rd_addr);
-
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_FORMAT);
- if (!fit_check_format(fit_hdr)) {
- puts("Bad FIT ramdisk image format!\n");
- bootstage_error(
- BOOTSTAGE_ID_FIT_RD_FORMAT);
+ rd_noffset = fit_image_load(images, FIT_RAMDISK_PROP,
+ rd_addr, &fit_uname_ramdisk,
+ fit_uname_config, arch,
+ IH_TYPE_RAMDISK,
+ BOOTSTAGE_ID_FIT_RD_START,
+ FIT_LOAD_REQUIRED, &rd_data, &rd_len);
+ if (rd_noffset < 0)
return 1;
- }
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_FORMAT_OK);
-
- if (!fit_uname_ramdisk) {
- /*
- * no ramdisk image node unit name, try to get config
- * node first. If config unit node name is NULL
- * fit_conf_get_node() will try to find default config node
- */
- bootstage_mark(
- BOOTSTAGE_ID_FIT_RD_NO_UNIT_NAME);
- cfg_noffset = fit_conf_get_node(fit_hdr,
- fit_uname_config);
- if (cfg_noffset < 0) {
- puts("Could not find configuration "
- "node\n");
- bootstage_error(
- BOOTSTAGE_ID_FIT_RD_NO_UNIT_NAME);
- return 1;
- }
- fit_uname_config = fdt_get_name(fit_hdr,
- cfg_noffset, NULL);
- printf(" Using '%s' configuration\n",
- fit_uname_config);
-
- rd_noffset = fit_conf_get_ramdisk_node(fit_hdr,
- cfg_noffset);
- fit_uname_ramdisk = fit_get_name(fit_hdr,
- rd_noffset, NULL);
- } else {
- /* get ramdisk component image node offset */
- bootstage_mark(
- BOOTSTAGE_ID_FIT_RD_UNIT_NAME);
- rd_noffset = fit_image_get_node(fit_hdr,
- fit_uname_ramdisk);
- }
- if (rd_noffset < 0) {
- puts("Could not find subimage node\n");
- bootstage_error(BOOTSTAGE_ID_FIT_RD_SUBNODE);
- return 1;
- }
-
- printf(" Trying '%s' ramdisk subimage\n",
- fit_uname_ramdisk);
-
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_CHECK);
- if (!fit_check_ramdisk(fit_hdr, rd_noffset, arch,
- images->verify))
- return 1;
-
- /* get ramdisk image data address and length */
- if (fit_image_get_data(fit_hdr, rd_noffset, &data,
- &size)) {
- puts("Could not find ramdisk subimage data!\n");
- bootstage_error(BOOTSTAGE_ID_FIT_RD_GET_DATA);
- return 1;
- }
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_GET_DATA_OK);
-
- rd_data = (ulong)data;
- rd_len = size;
-
- if (fit_image_get_load(fit_hdr, rd_noffset, &rd_load)) {
- puts("Can't get ramdisk subimage load "
- "address!\n");
- bootstage_error(BOOTSTAGE_ID_FIT_RD_LOAD);
- return 1;
- }
- bootstage_mark(BOOTSTAGE_ID_FIT_RD_LOAD);
- images->fit_hdr_rd = fit_hdr;
+ images->fit_hdr_rd = map_sysmem(rd_addr, 0);
images->fit_uname_rd = fit_uname_ramdisk;
images->fit_noffset_rd = rd_noffset;
break;
/* #define DEBUG */
#include <common.h>
-#include <watchdog.h>
#include <command.h>
#include <fdtdec.h>
-#include <malloc.h>
-#include <version.h>
-#ifdef CONFIG_MODEM_SUPPORT
-#include <malloc.h> /* for free() prototype */
-#endif
-
-#ifdef CONFIG_SYS_HUSH_PARSER
#include <hush.h>
-#endif
-
-#ifdef CONFIG_OF_CONTROL
-#include <fdtdec.h>
-#endif
-
+#include <malloc.h>
+#include <menu.h>
#include <post.h>
+#include <version.h>
+#include <watchdog.h>
#include <linux/ctype.h>
-#include <menu.h>
DECLARE_GLOBAL_DATA_PTR;
void inline __show_boot_progress (int val) {}
void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress")));
-#if defined(CONFIG_UPDATE_TFTP)
-int update_tftp (ulong addr);
-#endif /* CONFIG_UPDATE_TFTP */
-
#define MAX_DELAY_STOP_STR 32
-#undef DEBUG_PARSER
+#define DEBUG_PARSER 0 /* set to 1 to debug */
+
+#define debug_parser(fmt, args...) \
+ debug_cond(DEBUG_PARSER, fmt, ##args)
+
+#ifndef DEBUG_BOOTKEYS
+#define DEBUG_BOOTKEYS 0
+#endif
+#define debug_bootkeys(fmt, args...) \
+ debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
*/
#if defined(CONFIG_BOOTDELAY)
# if defined(CONFIG_AUTOBOOT_KEYED)
-#ifndef CONFIG_MENU
-static inline
-#endif
-int abortboot(int bootdelay)
+static int abortboot_keyed(int bootdelay)
{
int abort = 0;
uint64_t etime = endtick(bootdelay);
presskey_max = presskey_max > delaykey[i].len ?
presskey_max : delaykey[i].len;
-# if DEBUG_BOOTKEYS
- printf("%s key:<%s>\n",
- delaykey[i].retry ? "delay" : "stop",
- delaykey[i].str ? delaykey[i].str : "NULL");
-# endif
+ debug_bootkeys("%s key:<%s>\n",
+ delaykey[i].retry ? "delay" : "stop",
+ delaykey[i].str ? delaykey[i].str : "NULL");
}
/* In order to keep up with incoming data, check timeout only
memcmp (presskey + presskey_len - delaykey[i].len,
delaykey[i].str,
delaykey[i].len) == 0) {
-# if DEBUG_BOOTKEYS
- printf("got %skey\n",
- delaykey[i].retry ? "delay" : "stop");
-# endif
+ debug_bootkeys("got %skey\n",
+ delaykey[i].retry ? "delay" :
+ "stop");
# ifdef CONFIG_BOOT_RETRY_TIME
/* don't retry auto boot */
}
} while (!abort && get_ticks() <= etime);
-# if DEBUG_BOOTKEYS
if (!abort)
- puts("key timeout\n");
-# endif
+ debug_bootkeys("key timeout\n");
#ifdef CONFIG_SILENT_CONSOLE
if (abort)
static int menukey = 0;
#endif
-#ifndef CONFIG_MENU
-static inline
-#endif
-int abortboot(int bootdelay)
+static int abortboot_normal(int bootdelay)
{
int abort = 0;
unsigned long ts;
return abort;
}
# endif /* CONFIG_AUTOBOOT_KEYED */
+
+static int abortboot(int bootdelay)
+{
+#ifdef CONFIG_AUTOBOOT_KEYED
+ return abortboot_keyed(bootdelay);
+#else
+ return abortboot_normal(bootdelay);
+#endif
+}
#endif /* CONFIG_BOOTDELAY */
/*
}
#endif /* CONFIG_OF_CONTROL */
-
-/****************************************************************************/
-
-void main_loop (void)
+#ifdef CONFIG_BOOTDELAY
+static void process_boot_delay(void)
{
-#ifndef CONFIG_SYS_HUSH_PARSER
- static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
- int len;
- int rc = 1;
- int flag;
-#endif
-#if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL)
+#ifdef CONFIG_OF_CONTROL
char *env;
#endif
-#if defined(CONFIG_BOOTDELAY)
char *s;
int bootdelay;
-#endif
-#ifdef CONFIG_PREBOOT
- char *p;
-#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = 0;
unsigned long bootlimit = 0;
- char *bcs;
- char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */
- bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
-
#ifdef CONFIG_BOOTCOUNT_LIMIT
bootcount = bootcount_load();
bootcount++;
bootcount_store (bootcount);
- sprintf (bcs_set, "%lu", bootcount);
- setenv ("bootcount", bcs_set);
- bcs = getenv ("bootlimit");
- bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
+ setenv_ulong("bootcount", bootcount);
+ bootlimit = getenv_ulong("bootlimit", 10, 0);
#endif /* CONFIG_BOOTCOUNT_LIMIT */
-#ifdef CONFIG_MODEM_SUPPORT
- debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
- if (do_mdm_init) {
- char *str = strdup(getenv("mdm_cmd"));
- setenv ("preboot", str); /* set or delete definition */
- if (str != NULL)
- free (str);
- mdm_init(); /* wait for modem connection */
- }
-#endif /* CONFIG_MODEM_SUPPORT */
-
-#ifdef CONFIG_VERSION_VARIABLE
- {
- setenv ("ver", version_string); /* set version variable */
- }
-#endif /* CONFIG_VERSION_VARIABLE */
-
-#ifdef CONFIG_SYS_HUSH_PARSER
- u_boot_hush_start ();
-#endif
-
-#if defined(CONFIG_HUSH_INIT_VAR)
- hush_init_var ();
-#endif
-
-#ifdef CONFIG_PREBOOT
- if ((p = getenv ("preboot")) != NULL) {
-# ifdef CONFIG_AUTOBOOT_KEYED
- int prev = disable_ctrlc(1); /* disable Control C checking */
-# endif
-
- run_command_list(p, -1, 0);
-
-# ifdef CONFIG_AUTOBOOT_KEYED
- disable_ctrlc(prev); /* restore Control C checking */
-# endif
- }
-#endif /* CONFIG_PREBOOT */
-
-#if defined(CONFIG_UPDATE_TFTP)
- update_tftp (0UL);
-#endif /* CONFIG_UPDATE_TFTP */
-
-#if defined(CONFIG_BOOTDELAY)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
+#ifdef CONFIG_OF_CONTROL
+ bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
+ bootdelay);
+#endif
+
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
#if defined(CONFIG_MENU_SHOW)
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
if (bootdelay != -1 && s && !abortboot(bootdelay)) {
-# ifdef CONFIG_AUTOBOOT_KEYED
+#ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
-# endif
+#endif
run_command_list(s, -1, 0);
-# ifdef CONFIG_AUTOBOOT_KEYED
+#ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
-# endif
+#endif
}
-# ifdef CONFIG_MENUKEY
+#ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s)
run_command_list(s, -1, 0);
}
#endif /* CONFIG_MENUKEY */
+}
#endif /* CONFIG_BOOTDELAY */
+void main_loop(void)
+{
+#ifndef CONFIG_SYS_HUSH_PARSER
+ static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
+ int len;
+ int rc = 1;
+ int flag;
+#endif
+#ifdef CONFIG_PREBOOT
+ char *p;
+#endif
+
+ bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
+
+#ifdef CONFIG_MODEM_SUPPORT
+ debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
+ if (do_mdm_init) {
+ char *str = strdup(getenv("mdm_cmd"));
+ setenv("preboot", str); /* set or delete definition */
+ if (str != NULL)
+ free(str);
+ mdm_init(); /* wait for modem connection */
+ }
+#endif /* CONFIG_MODEM_SUPPORT */
+
+#ifdef CONFIG_VERSION_VARIABLE
+ {
+ setenv("ver", version_string); /* set version variable */
+ }
+#endif /* CONFIG_VERSION_VARIABLE */
+
+#ifdef CONFIG_SYS_HUSH_PARSER
+ u_boot_hush_start();
+#endif
+
+#if defined(CONFIG_HUSH_INIT_VAR)
+ hush_init_var();
+#endif
+
+#ifdef CONFIG_PREBOOT
+ p = getenv("preboot");
+ if (p != NULL) {
+# ifdef CONFIG_AUTOBOOT_KEYED
+ int prev = disable_ctrlc(1); /* disable Control C checking */
+# endif
+
+ run_command_list(p, -1, 0);
+
+# ifdef CONFIG_AUTOBOOT_KEYED
+ disable_ctrlc(prev); /* restore Control C checking */
+# endif
+ }
+#endif /* CONFIG_PREBOOT */
+
+#if defined(CONFIG_UPDATE_TFTP)
+ update_tftp(0UL);
+#endif /* CONFIG_UPDATE_TFTP */
+
+#ifdef CONFIG_BOOTDELAY
+ process_boot_delay();
+#endif
/*
* Main Loop for Monitor Command Processing
*/
* Special character handling
*/
switch (c) {
- case '\r': /* Enter */
+ case '\r': /* Enter */
case '\n':
*p = '\0';
puts ("\r\n");
- return (p - p_buf);
+ return p - p_buf;
- case '\0': /* nul */
+ case '\0': /* nul */
continue;
- case 0x03: /* ^C - break */
+ case 0x03: /* ^C - break */
p_buf[0] = '\0'; /* discard input */
- return (-1);
+ return -1;
- case 0x15: /* ^U - erase line */
+ case 0x15: /* ^U - erase line */
while (col > plen) {
puts (erase_seq);
--col;
n = 0;
continue;
- case 0x17: /* ^W - erase word */
+ case 0x17: /* ^W - erase word */
p=delete_char(p_buf, p, &col, &n, plen);
while ((n > 0) && (*p != ' ')) {
p=delete_char(p_buf, p, &col, &n, plen);
}
continue;
- case 0x08: /* ^H - backspace */
- case 0x7F: /* DEL - backspace */
+ case 0x08: /* ^H - backspace */
+ case 0x7F: /* DEL - backspace */
p=delete_char(p_buf, p, &col, &n, plen);
continue;
* Must be a normal character then
*/
if (n < CONFIG_SYS_CBSIZE-2) {
- if (c == '\t') { /* expand TABs */
+ if (c == '\t') { /* expand TABs */
#ifdef CONFIG_AUTO_COMPLETE
/* if auto completion triggered just continue */
*p = '\0';
char buf[2];
/*
- * Echo input using puts() to force am
+ * Echo input using puts() to force an
* LCD flush if we are using an LCD
*/
++col;
{
int nargs = 0;
-#ifdef DEBUG_PARSER
- printf ("parse_line: \"%s\"\n", line);
-#endif
+ debug_parser("parse_line: \"%s\"\n", line);
while (nargs < CONFIG_SYS_MAXARGS) {
/* skip any white space */
if (*line == '\0') { /* end of line, no more args */
argv[nargs] = NULL;
-#ifdef DEBUG_PARSER
- printf ("parse_line: nargs=%d\n", nargs);
-#endif
- return (nargs);
+ debug_parser("parse_line: nargs=%d\n", nargs);
+ return nargs;
}
argv[nargs++] = line; /* begin of argument string */
if (*line == '\0') { /* end of line, no more args */
argv[nargs] = NULL;
-#ifdef DEBUG_PARSER
- printf ("parse_line: nargs=%d\n", nargs);
-#endif
- return (nargs);
+ debug_parser("parse_line: nargs=%d\n", nargs);
+ return nargs;
}
*line++ = '\0'; /* terminate current arg */
printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);
-#ifdef DEBUG_PARSER
- printf ("parse_line: nargs=%d\n", nargs);
-#endif
+ debug_parser("parse_line: nargs=%d\n", nargs);
return (nargs);
}
/* 1 = waiting for '(' or '{' */
/* 2 = waiting for ')' or '}' */
/* 3 = waiting for ''' */
-#ifdef DEBUG_PARSER
char *output_start = output;
- printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input),
- input);
-#endif
+ debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input),
+ input);
prev = '\0'; /* previous character */
else
*(output - 1) = 0;
-#ifdef DEBUG_PARSER
- printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
- strlen (output_start), output_start);
-#endif
+ debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n",
+ strlen(output_start), output_start);
}
/****************************************************************************
int repeatable = 1;
int rc = 0;
-#ifdef DEBUG_PARSER
- printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
- puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
- puts ("\"\n");
-#endif
-
+ debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd);
+ if (DEBUG_PARSER) {
+ /* use puts - string may be loooong */
+ puts(cmd ? cmd : "NULL");
+ puts("\"\n");
+ }
clear_ctrlc(); /* forget any previous Control C */
if (!cmd || !*cmd) {
* repeatable commands
*/
-#ifdef DEBUG_PARSER
- printf ("[PROCESS_SEPARATORS] %s\n", cmd);
-#endif
+ debug_parser("[PROCESS_SEPARATORS] %s\n", cmd);
while (*str) {
/*
}
else
str = sep; /* no more commands for next pass */
-#ifdef DEBUG_PARSER
- printf ("token: \"%s\"\n", token);
-#endif
+ debug_parser("token: \"%s\"\n", token);
/* find macros in this token and replace them */
process_macros (token, finaltoken);
u32 offset = (u32)le32_to_cpu(gpt_h->first_usable_lba);
ulong start;
int i, k;
- size_t name_len;
+ size_t efiname_len, dosname_len;
#ifdef CONFIG_PARTITION_UUIDS
char *str_uuid;
#endif
sizeof(gpt_entry_attributes));
/* partition name */
- name_len = sizeof(gpt_e[i].partition_name)
+ efiname_len = sizeof(gpt_e[i].partition_name)
/ sizeof(efi_char16_t);
- for (k = 0; k < name_len; k++)
+ dosname_len = sizeof(partitions[i].name);
+
+ memset(gpt_e[i].partition_name, 0,
+ sizeof(gpt_e[i].partition_name));
+
+ for (k = 0; k < min(dosname_len, efiname_len); k++)
gpt_e[i].partition_name[k] =
(efi_char16_t)(partitions[i].name[k]);
return map;
}
-int key_matrix_decode_fdt(struct key_matrix *config, const void *blob,
- int node)
+int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, int node)
{
const struct fdt_property *prop;
- const char prefix[] = "linux,";
- int plen = sizeof(prefix) - 1;
- int offset;
-
- /* Check each property name for ones that we understand */
- for (offset = fdt_first_property_offset(blob, node);
- offset > 0;
- offset = fdt_next_property_offset(blob, offset)) {
- const char *name;
- int len;
-
- prop = fdt_get_property_by_offset(blob, offset, NULL);
- name = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
- len = strlen(name);
-
- /* Name needs to match "1,<type>keymap" */
- debug("%s: property '%s'\n", __func__, name);
- if (strncmp(name, prefix, plen) ||
- len < plen + 6 ||
- strcmp(name + len - 6, "keymap"))
- continue;
+ int proplen;
+ uchar *plain_keycode;
- len -= plen + 6;
- if (len == 0) {
- config->plain_keycode = create_keymap(config,
- (u32 *)prop->data, fdt32_to_cpu(prop->len),
- KEY_FN, &config->fn_pos);
- } else if (0 == strncmp(name + plen, "fn-", len)) {
- config->fn_keycode = create_keymap(config,
- (u32 *)prop->data, fdt32_to_cpu(prop->len),
- -1, NULL);
- } else {
- debug("%s: unrecognised property '%s'\n", __func__,
- name);
- }
- }
- debug("%s: Decoded key maps %p, %p from fdt\n", __func__,
- config->plain_keycode, config->fn_keycode);
+ prop = fdt_get_property(blob, node, "linux,keymap", &proplen);
+ /* Basic keymap is required */
+ if (!prop)
+ return -1;
+ plain_keycode = create_keymap(config, (u32 *)prop->data,
+ proplen, KEY_FN, &config->fn_pos);
+ config->plain_keycode = plain_keycode;
+ /* Conversion error -> fail */
+ if (!config->plain_keycode)
+ return -1;
+
+ prop = fdt_get_property(blob, node, "linux,fn-keymap", &proplen);
+ /* fn keymap is optional */
+ if (!prop)
+ goto done;
+
+ config->fn_keycode = create_keymap(config, (u32 *)prop->data,
+ proplen, -1, NULL);
+ /* Conversion error -> fail */
if (!config->plain_keycode) {
- debug("%s: cannot find keycode-plain map\n", __func__);
+ free(plain_keycode);
return -1;
}
+done:
+ debug("%s: Decoded key maps %p, %p from fdt\n", __func__,
+ config->plain_keycode, config->fn_keycode);
return 0;
}
LIB := $(obj)libmtd.o
-COBJS-$(CONFIG_MTD_DEVICE) += mtdcore.o
+ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)))
+COBJS-y += mtdcore.o
+endif
COBJS-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
COBJS-$(CONFIG_MTD_CONCAT) += mtdconcat.o
COBJS-$(CONFIG_HAS_DATAFLASH) += at45.o
mtd->size = fi->size;
mtd->writesize = 1;
- mtd->erase = cfi_mtd_erase;
- mtd->read = cfi_mtd_read;
- mtd->write = cfi_mtd_write;
- mtd->sync = cfi_mtd_sync;
- mtd->lock = cfi_mtd_lock;
- mtd->unlock = cfi_mtd_unlock;
+ mtd->_erase = cfi_mtd_erase;
+ mtd->_read = cfi_mtd_read;
+ mtd->_write = cfi_mtd_write;
+ mtd->_sync = cfi_mtd_sync;
+ mtd->_lock = cfi_mtd_lock;
+ mtd->_unlock = cfi_mtd_unlock;
mtd->priv = fi;
if (add_mtd_device(mtd))
/* Entire transaction goes into this subdev */
size = len;
- err = subdev->read(subdev, from, size, &retsize, buf);
+ err = mtd_read(subdev, from, size, &retsize, buf);
/* Save information about bitflips! */
if (unlikely(err)) {
- if (err == -EBADMSG) {
+ if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
- } else if (err == -EUCLEAN) {
+ } else if (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++;
/* Do not overwrite -EBADMSG !! */
if (!ret)
int err = -EINVAL;
int i;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
*retlen = 0;
for (i = 0; i < concat->num_subdev; i++) {
else
size = len;
- if (!(subdev->flags & MTD_WRITEABLE))
- err = -EROFS;
- else
- err = subdev->write(subdev, to, size, &retsize, buf);
-
+ err = mtd_write(subdev, to, size, &retsize, buf);
if (err)
break;
if (from + devops.len > subdev->size)
devops.len = subdev->size - from;
- err = subdev->read_oob(subdev, from, &devops);
+ err = mtd_read_oob(subdev, from, &devops);
ops->retlen += devops.retlen;
ops->oobretlen += devops.oobretlen;
/* Save information about bitflips! */
if (unlikely(err)) {
- if (err == -EBADMSG) {
+ if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
- } else if (err == -EUCLEAN) {
+ } else if (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++;
/* Do not overwrite -EBADMSG !! */
if (!ret)
if (to + devops.len > subdev->size)
devops.len = subdev->size - to;
- err = subdev->write_oob(subdev, to, &devops);
+ err = mtd_write_oob(subdev, to, &devops);
ops->retlen += devops.retlen;
if (err)
return err;
* FIXME: Allow INTERRUPTIBLE. Which means
* not having the wait_queue head on the stack.
*/
- err = mtd->erase(mtd, erase);
+ err = mtd_erase(mtd, erase);
if (!err) {
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&waitq, &wait);
uint64_t length, offset = 0;
struct erase_info *erase;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
- if (instr->addr > concat->mtd.size)
- return -EINVAL;
-
- if (instr->len + instr->addr > concat->mtd.size)
- return -EINVAL;
-
/*
* Check for proper erase block alignment of the to-be-erased area.
* It is easier to do this based on the super device's erase
return -EINVAL;
}
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
/* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
else
erase->len = length;
- if (!(subdev->flags & MTD_WRITEABLE)) {
- err = -EROFS;
- break;
- }
length -= erase->len;
if ((err = concat_dev_erase(subdev, erase))) {
/* sanity check: should never happen since
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
- if ((len + ofs) > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
else
size = len;
- err = subdev->lock(subdev, ofs, size);
+ err = mtd_lock(subdev, ofs, size);
if (err)
break;
struct mtd_concat *concat = CONCAT(mtd);
int i, err = 0;
- if ((len + ofs) > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
else
size = len;
- err = subdev->unlock(subdev, ofs, size);
+ err = mtd_unlock(subdev, ofs, size);
if (err)
break;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
- subdev->sync(subdev);
+ mtd_sync(subdev);
}
}
struct mtd_concat *concat = CONCAT(mtd);
int i, res = 0;
- if (!concat->subdev[0]->block_isbad)
+ if (!mtd_can_have_bb(concat->subdev[0]))
return res;
- if (ofs > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
continue;
}
- res = subdev->block_isbad(subdev, ofs);
+ res = mtd_block_isbad(subdev, ofs);
break;
}
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
- if (!concat->subdev[0]->block_markbad)
+ if (!mtd_can_have_bb(concat->subdev[0]))
return 0;
- if (ofs > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
continue;
}
- err = subdev->block_markbad(subdev, ofs);
+ err = mtd_block_markbad(subdev, ofs);
if (!err)
mtd->ecc_stats.badblocks++;
break;
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.oobavail = subdev[0]->oobavail;
- if (subdev[0]->read_oob)
- concat->mtd.read_oob = concat_read_oob;
- if (subdev[0]->write_oob)
- concat->mtd.write_oob = concat_write_oob;
- if (subdev[0]->block_isbad)
- concat->mtd.block_isbad = concat_block_isbad;
- if (subdev[0]->block_markbad)
- concat->mtd.block_markbad = concat_block_markbad;
+ if (subdev[0]->_read_oob)
+ concat->mtd._read_oob = concat_read_oob;
+ if (subdev[0]->_write_oob)
+ concat->mtd._write_oob = concat_write_oob;
+ if (subdev[0]->_block_isbad)
+ concat->mtd._block_isbad = concat_block_isbad;
+ if (subdev[0]->_block_markbad)
+ concat->mtd._block_markbad = concat_block_markbad;
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
if (concat->mtd.writesize != subdev[i]->writesize ||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
concat->mtd.oobsize != subdev[i]->oobsize ||
- !concat->mtd.read_oob != !subdev[i]->read_oob ||
- !concat->mtd.write_oob != !subdev[i]->write_oob) {
+ !concat->mtd._read_oob != !subdev[i]->_read_oob ||
+ !concat->mtd._write_oob != !subdev[i]->_write_oob) {
kfree(concat);
printk("Incompatible OOB or ECC data on \"%s\"\n",
subdev[i]->name);
concat->num_subdev = num_devs;
concat->mtd.name = name;
- concat->mtd.erase = concat_erase;
- concat->mtd.read = concat_read;
- concat->mtd.write = concat_write;
- concat->mtd.sync = concat_sync;
- concat->mtd.lock = concat_lock;
- concat->mtd.unlock = concat_unlock;
+ concat->mtd._erase = concat_erase;
+ concat->mtd._read = concat_read;
+ concat->mtd._write = concat_write;
+ concat->mtd._sync = concat_sync;
+ concat->mtd._lock = concat_lock;
+ concat->mtd._unlock = concat_unlock;
/*
* Combine the erase block size info of the subdevices:
mtd->index = i;
mtd->usecount = 0;
+ /* default value if not set by driver */
+ if (mtd->bitflip_threshold == 0)
+ mtd->bitflip_threshold = mtd->ecc_strength;
+
+
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
}
}
#endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */
+
+ /*
+ * Erase is an asynchronous operation. Device drivers are supposed
+ * to call instr->callback() whenever the operation completes, even
+ * if it completes with a failure.
+ * Callers are supposed to pass a callback function and wait for it
+ * to be called before writing to the block.
+ */
+int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
+ return -EINVAL;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+ if (!instr->len) {
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+ return 0;
+ }
+ return mtd->_erase(mtd, instr);
+}
+
+int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ u_char *buf)
+{
+ if (from < 0 || from > mtd->size || len > mtd->size - from)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_read(mtd, from, len, retlen, buf);
+}
+
+int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u_char *buf)
+{
+ *retlen = 0;
+ if (to < 0 || to > mtd->size || len > mtd->size - to)
+ return -EINVAL;
+ if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (!len)
+ return 0;
+ return mtd->_write(mtd, to, len, retlen, buf);
+}
+
+/*
+ * In blackbox flight recorder like scenarios we want to make successful writes
+ * in interrupt context. panic_write() is only intended to be called when its
+ * known the kernel is about to panic and we need the write to succeed. Since
+ * the kernel is not going to be running for much longer, this function can
+ * break locks and delay to ensure the write succeeds (but not sleep).
+ */
+int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_panic_write)
+ return -EOPNOTSUPP;
+ if (to < 0 || to > mtd->size || len > mtd->size - to)
+ return -EINVAL;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (!len)
+ return 0;
+ return mtd->_panic_write(mtd, to, len, retlen, buf);
+}
+
+int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
+{
+ ops->retlen = ops->oobretlen = 0;
+ if (!mtd->_read_oob)
+ return -EOPNOTSUPP;
+ return mtd->_read_oob(mtd, from, ops);
+}
+
+/*
+ * Method to access the protection register area, present in some flash
+ * devices. The user data is one time programmable but the factory data is read
+ * only.
+ */
+int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+ size_t len)
+{
+ if (!mtd->_get_fact_prot_info)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_get_fact_prot_info(mtd, buf, len);
+}
+
+int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_read_fact_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf);
+}
+
+int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+ size_t len)
+{
+ if (!mtd->_get_user_prot_info)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_get_user_prot_info(mtd, buf, len);
+}
+
+int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_read_user_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf);
+}
+
+int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_write_user_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+}
+
+int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
+{
+ if (!mtd->_lock_user_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_lock_user_prot_reg(mtd, from, len);
+}
+
+/* Chip-supported device locking */
+int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ if (!mtd->_lock)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_lock(mtd, ofs, len);
+}
+
+int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ if (!mtd->_unlock)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_unlock(mtd, ofs, len);
+}
+
+int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ if (!mtd->_block_isbad)
+ return 0;
+ if (ofs < 0 || ofs > mtd->size)
+ return -EINVAL;
+ return mtd->_block_isbad(mtd, ofs);
+}
+
+int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ if (!mtd->_block_markbad)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size)
+ return -EINVAL;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ return mtd->_block_markbad(mtd, ofs);
+}
+
int res;
stats = part->master->ecc_stats;
-
- if (from >= mtd->size)
- len = 0;
- else if (from + len > mtd->size)
- len = mtd->size - from;
- res = part->master->read(part->master, from + part->offset,
- len, retlen, buf);
+ res = mtd_read(part->master, from + part->offset, len, retlen, buf);
if (unlikely(res)) {
- if (res == -EUCLEAN)
+ if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
- if (res == -EBADMSG)
+ if (mtd_is_eccerr(res))
mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
}
return res;
return -EINVAL;
if (ops->datbuf && from + ops->len > mtd->size)
return -EINVAL;
- res = part->master->read_oob(part->master, from + part->offset, ops);
+ res = mtd_read_oob(part->master, from + part->offset, ops);
if (unlikely(res)) {
- if (res == -EUCLEAN)
+ if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
- if (res == -EBADMSG)
+ if (mtd_is_eccerr(res))
mtd->ecc_stats.failed++;
}
return res;
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
- return part->master->read_user_prot_reg(part->master, from,
- len, retlen, buf);
+ return mtd_read_user_prot_reg(part->master, from, len, retlen, buf);
}
static int part_get_user_prot_info(struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
struct mtd_part *part = PART(mtd);
- return part->master->get_user_prot_info(part->master, buf, len);
+ return mtd_get_user_prot_info(part->master, buf, len);
}
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
- return part->master->read_fact_prot_reg(part->master, from,
- len, retlen, buf);
+ return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf);
}
static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
size_t len)
{
struct mtd_part *part = PART(mtd);
- return part->master->get_fact_prot_info(part->master, buf, len);
+ return mtd_get_fact_prot_info(part->master, buf, len);
}
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (to >= mtd->size)
- len = 0;
- else if (to + len > mtd->size)
- len = mtd->size - to;
- return part->master->write(part->master, to + part->offset,
- len, retlen, buf);
-}
-
-static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
-{
- struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (to >= mtd->size)
- len = 0;
- else if (to + len > mtd->size)
- len = mtd->size - to;
- return part->master->panic_write(part->master, to + part->offset,
- len, retlen, buf);
+ return mtd_write(part->master, to + part->offset, len, retlen, buf);
}
static int part_write_oob(struct mtd_info *mtd, loff_t to,
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
if (to >= mtd->size)
return -EINVAL;
if (ops->datbuf && to + ops->len > mtd->size)
return -EINVAL;
- return part->master->write_oob(part->master, to + part->offset, ops);
+ return mtd_write_oob(part->master, to + part->offset, ops);
}
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
- return part->master->write_user_prot_reg(part->master, from,
- len, retlen, buf);
+ return mtd_write_user_prot_reg(part->master, from, len, retlen, buf);
}
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
struct mtd_part *part = PART(mtd);
- return part->master->lock_user_prot_reg(part->master, from, len);
+ return mtd_lock_user_prot_reg(part->master, from, len);
}
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mtd_part *part = PART(mtd);
int ret;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (instr->addr >= mtd->size)
- return -EINVAL;
+
instr->addr += part->offset;
- ret = part->master->erase(part->master, instr);
+ ret = mtd_erase(part->master, instr);
if (ret) {
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= part->offset;
void mtd_erase_callback(struct erase_info *instr)
{
- if (instr->mtd->erase == part_erase) {
+ if (instr->mtd->_erase == part_erase) {
struct mtd_part *part = PART(instr->mtd);
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = PART(mtd);
- if ((len + ofs) > mtd->size)
- return -EINVAL;
- return part->master->lock(part->master, ofs + part->offset, len);
+ return mtd_lock(part->master, ofs + part->offset, len);
}
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = PART(mtd);
- if ((len + ofs) > mtd->size)
- return -EINVAL;
- return part->master->unlock(part->master, ofs + part->offset, len);
+ return mtd_unlock(part->master, ofs + part->offset, len);
}
static void part_sync(struct mtd_info *mtd)
{
struct mtd_part *part = PART(mtd);
- part->master->sync(part->master);
+ mtd_sync(part->master);
}
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = PART(mtd);
- if (ofs >= mtd->size)
- return -EINVAL;
ofs += part->offset;
- return part->master->block_isbad(part->master, ofs);
+ return mtd_block_isbad(part->master, ofs);
}
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct mtd_part *part = PART(mtd);
int res;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (ofs >= mtd->size)
- return -EINVAL;
ofs += part->offset;
- res = part->master->block_markbad(part->master, ofs);
+ res = mtd_block_markbad(part->master, ofs);
if (!res)
mtd->ecc_stats.badblocks++;
return res;
slave->mtd.name = part->name;
slave->mtd.owner = master->owner;
- slave->mtd.read = part_read;
- slave->mtd.write = part_write;
-
- if (master->panic_write)
- slave->mtd.panic_write = part_panic_write;
-
- if (master->read_oob)
- slave->mtd.read_oob = part_read_oob;
- if (master->write_oob)
- slave->mtd.write_oob = part_write_oob;
- if (master->read_user_prot_reg)
- slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
- if (master->read_fact_prot_reg)
- slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
- if (master->write_user_prot_reg)
- slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
- if (master->lock_user_prot_reg)
- slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
- if (master->get_user_prot_info)
- slave->mtd.get_user_prot_info = part_get_user_prot_info;
- if (master->get_fact_prot_info)
- slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
- if (master->sync)
- slave->mtd.sync = part_sync;
- if (master->lock)
- slave->mtd.lock = part_lock;
- if (master->unlock)
- slave->mtd.unlock = part_unlock;
- if (master->block_isbad)
- slave->mtd.block_isbad = part_block_isbad;
- if (master->block_markbad)
- slave->mtd.block_markbad = part_block_markbad;
- slave->mtd.erase = part_erase;
+ slave->mtd._read = part_read;
+ slave->mtd._write = part_write;
+
+ if (master->_read_oob)
+ slave->mtd._read_oob = part_read_oob;
+ if (master->_write_oob)
+ slave->mtd._write_oob = part_write_oob;
+ if (master->_read_user_prot_reg)
+ slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
+ if (master->_read_fact_prot_reg)
+ slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
+ if (master->_write_user_prot_reg)
+ slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
+ if (master->_lock_user_prot_reg)
+ slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
+ if (master->_get_user_prot_info)
+ slave->mtd._get_user_prot_info = part_get_user_prot_info;
+ if (master->_get_fact_prot_info)
+ slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
+ if (master->_sync)
+ slave->mtd._sync = part_sync;
+ if (master->_lock)
+ slave->mtd._lock = part_lock;
+ if (master->_unlock)
+ slave->mtd._unlock = part_unlock;
+ if (master->_block_isbad)
+ slave->mtd._block_isbad = part_block_isbad;
+ if (master->_block_markbad)
+ slave->mtd._block_markbad = part_block_markbad;
+ slave->mtd._erase = part_erase;
slave->master = master;
slave->offset = part->offset;
slave->index = partno;
}
slave->mtd.ecclayout = master->ecclayout;
- if (master->block_isbad) {
+ if (master->_block_isbad) {
uint64_t offs = 0;
while (offs < slave->mtd.size) {
- if (master->block_isbad(master,
- offs + slave->offset))
+ if (mtd_block_isbad(master, offs + slave->offset))
slave->mtd.ecc_stats.badblocks++;
offs += slave->mtd.erasesize;
}
}
static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int page)
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
struct atmel_nand_host *host = chip->priv;
int eccsize = chip->ecc.size;
return 0;
}
-static void atmel_nand_pmecc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf)
+static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required)
{
struct atmel_nand_host *host = chip->priv;
uint32_t *eccpos = chip->ecc.layout->eccpos;
if (!timeout) {
printk(KERN_ERR "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n");
- return;
+ goto out;
}
for (i = 0; i < host->pmecc_sector_number; i++) {
}
}
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+out:
+ return 0;
}
static void atmel_pmecc_core_init(struct mtd_info *mtd)
nand->ecc.read_page = atmel_nand_pmecc_read_page;
nand->ecc.write_page = atmel_nand_pmecc_write_page;
+ nand->ecc.strength = cap;
atmel_pmecc_core_init(mtd);
* mtd: mtd info structure
* chip: nand chip info structure
* buf: buffer to store read data
+ * oob_required: caller expects OOB data read to chip->oob_poi
*/
-static int atmel_nand_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int page)
+static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
if (!NAND_IS_512()) {
chip->ecc.bytes = 3;
chip->ecc.size = 256;
+ chip->ecc.strength = 1;
} else {
chip->ecc.bytes = 6;
chip->ecc.size = 512;
+ chip->ecc.strength = 2;
}
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.calculate = bfin_nfc_calculate_ecc;
{
nand->chip_delay = 0;
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
- nand->options |= NAND_USE_FLASH_BBT;
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
#endif
#ifdef CONFIG_SYS_NAND_HW_ECC
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
nand->ecc.calculate = nand_davinci_calculate_ecc;
nand->ecc.correct = nand_davinci_correct_data;
nand->ecc.hwctl = nand_davinci_enable_hwecc;
nand->ecc.mode = NAND_ECC_HW_OOB_FIRST;
nand->ecc.size = 512;
nand->ecc.bytes = 10;
+ nand->ecc.strength = 4;
nand->ecc.calculate = nand_davinci_4bit_calculate_ecc;
nand->ecc.correct = nand_davinci_4bit_correct_data;
nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc;
/*
* The HW decoder in the DoC ASIC's provides us a error syndrome,
- * which we must convert to a standard syndrom usable by the generic
+ * which we must convert to a standard syndrome usable by the generic
* Reed-Solomon library code.
*
* Fabrice Bellard figured this out in the old docecc code. I added
ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
parity = ecc[1];
- /* Initialize the syndrom buffer */
+ /* Initialize the syndrome buffer */
for (i = 0; i < NROOTS; i++)
s[i] = ds[0];
/*
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
else
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- if (no_ecc_failures && (ret == -EBADMSG)) {
+ if (no_ecc_failures && mtd_is_eccerr(ret)) {
printk(KERN_ERR "suppressing ECC failure\n");
ret = 0;
}
size_t retlen;
for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
- ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf);
+ ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
if (retlen != mtd->writesize)
continue;
if (ret) {
/* Only one mediaheader was found. We want buf to contain a
mediaheader on return, so we'll have to re-read the one we found. */
offs = doc->mh0_page << this->page_shift;
- ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf);
+ ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf);
if (retlen != mtd->writesize) {
/* Insanity. Give up. */
printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = 512;
nand->ecc.bytes = 6;
- nand->options = NAND_USE_FLASH_BBT;
+ nand->ecc.strength = 2;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
doc->physadr = physadr;
doc->virtadr = virtadr;
return fsl_elbc_read_byte(mtd);
}
-static int fsl_elbc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- uint8_t *buf, int page)
+static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
fsl_elbc_read_buf(mtd, buf, mtd->writesize);
fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
/* ECC will be calculated automatically, and errors will be detected in
* waitfunc.
*/
-static void fsl_elbc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf)
+static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
static struct fsl_elbc_ctrl *elbc_ctrl;
nand->bbt_md = &bbt_mirror_descr;
/* set up nand options */
- nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
- NAND_USE_FLASH_BBT | NAND_NO_SUBPAGE_WRITE;
+ nand->options = NAND_NO_SUBPAGE_WRITE;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
nand->controller = &elbc_ctrl->controller;
nand->priv = priv;
nand->ecc.read_page = fsl_elbc_read_page;
nand->ecc.write_page = fsl_elbc_write_page;
-#ifdef CONFIG_FSL_ELBC_FMR
- priv->fmr = CONFIG_FSL_ELBC_FMR;
-#else
priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
- /*
- * Hardware expects small page has ECCM0, large page has ECCM1
- * when booting from NAND. Board config can override if not
- * booting from NAND.
- */
- if (or & OR_FCM_PGS)
- priv->fmr |= FMR_ECCM;
-#endif
-
/* If CS Base Register selects full hardware ECC then use it */
if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
nand->ecc.steps = 1;
+ nand->ecc.strength = 1;
} else {
/* otherwise fall back to default software ECC */
nand->ecc.mode = NAND_ECC_SOFT;
}
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
/* Large-page-specific setup */
- if (or & OR_FCM_PGS) {
+ if (mtd->writesize == 2048) {
+ setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
+ OR_FCM_PGS);
+ in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+
priv->page_size = 1;
nand->badblock_pattern = &largepage_memorybased;
+ /*
+ * Hardware expects small page has ECCM0, large page has
+ * ECCM1 when booting from NAND, and we follow that even
+ * when not booting from NAND.
+ */
+ priv->fmr |= FMR_ECCM;
+
/* adjust ecc setup if needed */
if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
nand->ecc.steps = 4;
&fsl_elbc_oob_lp_eccm1 :
&fsl_elbc_oob_lp_eccm0;
}
+ } else if (mtd->writesize == 512) {
+ clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
+ OR_FCM_PGS);
+ in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+ } else {
+ return -ENODEV;
}
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret)
- return ret;
-
ret = nand_scan_tail(mtd);
if (ret)
return ret;
#include <common.h>
#include <malloc.h>
+#include <nand.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
/* mtd information per set */
struct fsl_ifc_mtd {
- struct mtd_info mtd;
struct nand_chip chip;
struct fsl_ifc_ctrl *ctrl;
return nand_fsr;
}
-static int fsl_ifc_read_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- uint8_t *buf, int page)
+static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
struct fsl_ifc_mtd *priv = chip->priv;
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
/* ECC will be calculated automatically, and errors will be detected in
* waitfunc.
*/
-static void fsl_ifc_write_page(struct mtd_info *mtd,
- struct nand_chip *chip,
- const uint8_t *buf)
+static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
fsl_ifc_write_buf(mtd, buf, mtd->writesize);
fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
static void fsl_ifc_ctrl_init(void)
out_be32(&ifc_ctrl->regs->csor_cs[cs].csor_ext, csor_ext);
}
-int board_nand_init(struct nand_chip *nand)
+static int fsl_ifc_chip_init(int devnum, u8 *addr)
{
+ struct mtd_info *mtd = &nand_info[devnum];
+ struct nand_chip *nand;
struct fsl_ifc_mtd *priv;
struct nand_ecclayout *layout;
uint32_t cspr = 0, csor = 0, ver = 0;
+ int ret;
if (!ifc_ctrl) {
fsl_ifc_ctrl_init();
return -ENOMEM;
priv->ctrl = ifc_ctrl;
- priv->vbase = nand->IO_ADDR_R;
+ priv->vbase = addr;
/* Find which chip select it is connected to.
*/
for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) {
- phys_addr_t base_addr = virt_to_phys(nand->IO_ADDR_R);
+ phys_addr_t phys_addr = virt_to_phys(addr);
cspr = in_be32(&ifc_ctrl->regs->cspr_cs[priv->bank].cspr);
csor = in_be32(&ifc_ctrl->regs->csor_cs[priv->bank].csor);
if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND &&
- (cspr & CSPR_BA) == CSPR_PHYS_ADDR(base_addr)) {
+ (cspr & CSPR_BA) == CSPR_PHYS_ADDR(phys_addr)) {
ifc_ctrl->cs_nand = priv->bank << IFC_NAND_CSEL_SHIFT;
break;
}
return -ENODEV;
}
+ nand = &priv->chip;
+ mtd->priv = nand;
+
ifc_ctrl->chips[priv->bank] = priv;
/* fill in nand_chip structure */
nand->bbt_md = &bbt_mirror_descr;
/* set up nand options */
- nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
- NAND_USE_FLASH_BBT | NAND_NO_SUBPAGE_WRITE;
+ nand->options = NAND_NO_SUBPAGE_WRITE;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
if (cspr & CSPR_PORT_SIZE_16) {
nand->read_byte = fsl_ifc_read_byte16;
bbt_mirror_descr.offs = 0;
}
+ nand->ecc.strength = 4;
priv->bufnum_mask = 15;
break;
case CSOR_NAND_PGS_2K:
layout = &oob_2048_ecc4;
+ nand->ecc.strength = 4;
priv->bufnum_mask = 3;
break;
if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
CSOR_NAND_ECC_MODE_4) {
layout = &oob_4096_ecc4;
+ nand->ecc.strength = 4;
} else {
layout = &oob_4096_ecc8;
+ nand->ecc.strength = 8;
nand->ecc.bytes = 16;
}
if (ver == FSL_IFC_V1_1_0)
fsl_ifc_sram_init();
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(devnum);
+ if (ret)
+ return ret;
return 0;
}
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+
+static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] =
+ CONFIG_SYS_NAND_BASE_LIST;
+
+void board_nand_init(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ fsl_ifc_chip_init(i, (u8 *)base_address[i]);
+}
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
* @page: page number to read
*
* This routine is needed for fsmc verison 8 as reading from NAND chip has to be
* max of 8 bits)
*/
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
struct fsmc_eccplace *fsmc_eccpl;
int i, j, s, stat, eccsize = chip->ecc.size;
switch (fsmc_version) {
case FSMC_VER8:
nand->ecc.bytes = 13;
+ nand->ecc.strength = 8;
nand->ecc.correct = fsmc_bch8_correct_data;
nand->ecc.read_page = fsmc_read_page_hwecc;
if (mtd->writesize == 512)
break;
default:
nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
nand->ecc.layout = &fsmc_ecc1_layout;
nand->ecc.correct = nand_correct_data;
break;
nand->ecc.mode = NAND_ECC_HW_OOB_FIRST;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
+ nand->ecc.strength = 4;
nand->ecc.layout = &qi_lb60_ecclayout_2gb;
nand->chip_delay = 50;
nand->options = NAND_USE_FLASH_BBT;
chip->write_buf = mpc5121_nfc_write_buf;
chip->verify_buf = mpc5121_nfc_verify_buf;
chip->select_chip = mpc5121_nfc_select_chip;
- chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT;
+ chip->bbt_options = NAND_BBT_USE_FLASH;
chip->ecc.mode = NAND_ECC_SOFT;
/* Reset NAND Flash controller */
#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
- int page, int sndcmd)
+ int page)
{
struct mxc_nand_host *host = chip->priv;
uint8_t *buf = chip->oob_poi;
static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf,
+ int oob_required,
int page)
{
struct mxc_nand_host *host = chip->priv;
static int mxc_nand_read_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf,
+ int oob_required,
int page)
{
struct mxc_nand_host *host = chip->priv;
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
-static void mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd,
+static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
- const uint8_t *buf)
+ const uint8_t *buf,
+ int oob_required)
{
struct mxc_nand_host *host = chip->priv;
int eccsize = chip->ecc.size;
size = mtd->oobsize - (oob - chip->oob_poi);
if (size)
chip->write_buf(mtd, oob, size);
+ return 0;
}
-static void mxc_nand_write_page_syndrome(struct mtd_info *mtd,
+static int mxc_nand_write_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
- const uint8_t *buf)
+ const uint8_t *buf,
+ int oob_required)
{
struct mxc_nand_host *host = chip->priv;
int i, n, eccsize = chip->ecc.size;
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->write_buf(mtd, oob, i);
+ return 0;
}
static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
#endif
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
- this->options |= NAND_USE_FLASH_BBT;
+ this->bbt_options |= NAND_BBT_USE_FLASH;
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
#endif
this->ecc.mode = NAND_ECC_HW;
}
+ if (this->ecc.mode == NAND_ECC_HW) {
+ if (is_mxc_nfc_1())
+ this->ecc.strength = 1;
+ else
+ this->ecc.strength = 4;
+ }
+
host->pagesize_2k = 0;
this->ecc.size = 512;
* Read a page from NAND.
*/
static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required,
+ int page)
{
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
/*
* Write a page to NAND.
*/
-static void mxs_nand_ecc_write_page(struct mtd_info *mtd,
- struct nand_chip *nand, const uint8_t *buf)
+static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf,
+ int oob_required)
{
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
rtn:
mxs_nand_return_dma_descs(nand_info);
+ return 0;
}
/*
struct mxs_nand_info *nand_info = chip->priv;
int ret;
- if (ops->mode == MTD_OOB_RAW)
+ if (ops->mode == MTD_OPS_RAW)
nand_info->raw_oob_mode = 1;
else
nand_info->raw_oob_mode = 0;
struct mxs_nand_info *nand_info = chip->priv;
int ret;
- if (ops->mode == MTD_OOB_RAW)
+ if (ops->mode == MTD_OPS_RAW)
nand_info->raw_oob_mode = 1;
else
nand_info->raw_oob_mode = 0;
* what to do.
*/
static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
- int page, int cmd)
+ int page)
{
struct mxs_nand_info *nand_info = nand->priv;
writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set);
/* Hook some operations at the MTD level. */
- if (mtd->read_oob != mxs_nand_hook_read_oob) {
- nand_info->hooked_read_oob = mtd->read_oob;
- mtd->read_oob = mxs_nand_hook_read_oob;
+ if (mtd->_read_oob != mxs_nand_hook_read_oob) {
+ nand_info->hooked_read_oob = mtd->_read_oob;
+ mtd->_read_oob = mxs_nand_hook_read_oob;
}
- if (mtd->write_oob != mxs_nand_hook_write_oob) {
- nand_info->hooked_write_oob = mtd->write_oob;
- mtd->write_oob = mxs_nand_hook_write_oob;
+ if (mtd->_write_oob != mxs_nand_hook_write_oob) {
+ nand_info->hooked_write_oob = mtd->_write_oob;
+ mtd->_write_oob = mxs_nand_hook_write_oob;
}
- if (mtd->block_markbad != mxs_nand_hook_block_markbad) {
- nand_info->hooked_block_markbad = mtd->block_markbad;
- mtd->block_markbad = mxs_nand_hook_block_markbad;
+ if (mtd->_block_markbad != mxs_nand_hook_block_markbad) {
+ nand_info->hooked_block_markbad = mtd->_block_markbad;
+ mtd->_block_markbad = mxs_nand_hook_block_markbad;
}
/* We use the reference implementation for bad block management. */
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.bytes = 9;
nand->ecc.size = 512;
+ nand->ecc.strength = 8;
return 0;
* TODO:
* Enable cached programming for 2k page size chips
* Check, if mtd->ecctype should be set to MTD_ECC_HW
- * if we have HW ecc support.
+ * if we have HW ECC support.
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
* BBT table is not serialized, has to be fixed
ret = -EINVAL;
}
- /* Do not allow past end of device */
- if (ofs + len > mtd->size) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n",
- __func__);
- ret = -EINVAL;
- }
-
return ret;
}
/**
* nand_release_device - [GENERIC] release chip
- * @mtd: MTD device structure
+ * @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.
*/
static void nand_release_device(struct mtd_info *mtd)
{
/**
* nand_read_byte - [DEFAULT] read one byte from the chip
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Default read function for 8bit buswith
+ * Default read function for 8bit buswidth.
*/
uint8_t nand_read_byte(struct mtd_info *mtd)
{
/**
* nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
- * @mtd: MTD device structure
+ * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 16bit buswidth with endianness conversion.
*
- * Default read function for 16bit buswith with
- * endianess conversion
*/
static uint8_t nand_read_byte16(struct mtd_info *mtd)
{
/**
* nand_read_word - [DEFAULT] read one word from the chip
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Default read function for 16bit buswith without
- * endianess conversion
+ * Default read function for 16bit buswidth without endianness conversion.
*/
static u16 nand_read_word(struct mtd_info *mtd)
{
/**
* nand_select_chip - [DEFAULT] control CE line
- * @mtd: MTD device structure
- * @chipnr: chipnumber to select, -1 for deselect
+ * @mtd: MTD device structure
+ * @chipnr: chipnumber to select, -1 for deselect
*
* Default select function for 1 chip devices.
*/
/**
* nand_write_buf - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
*
- * Default write function for 8bit buswith
+ * Default write function for 8bit buswidth.
*/
void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
/**
* nand_read_buf - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
*
- * Default read function for 8bit buswith
+ * Default read function for 8bit buswidth.
*/
void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
/**
* 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
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
*
- * Default verify function for 8bit buswith
+ * Default verify function for 8bit buswidth.
*/
static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
/**
* nand_write_buf16 - [DEFAULT] write buffer to chip
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
*
- * Default write function for 16bit buswith
+ * Default write function for 16bit buswidth.
*/
void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
/**
* nand_read_buf16 - [DEFAULT] read chip data into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
*
- * Default read function for 16bit buswith
+ * Default read function for 16bit buswidth.
*/
void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
/**
* 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
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
*
- * Default verify function for 16bit buswith
+ * Default verify function for 16bit buswidth.
*/
static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
/**
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
- * @mtd: MTD device structure
- * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
*
* 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;
+ int page, chipnr, res = 0, i = 0;
struct nand_chip *chip = mtd->priv;
u16 bad;
- if (chip->options & NAND_BBT_SCANLASTPAGE)
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
ofs += mtd->erasesize - mtd->writesize;
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
chip->select_chip(mtd, chipnr);
}
- if (chip->options & NAND_BUSWIDTH_16) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
- page);
- bad = cpu_to_le16(chip->read_word(mtd));
- if (chip->badblockpos & 0x1)
- bad >>= 8;
- else
- bad &= 0xFF;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
- bad = chip->read_byte(mtd);
- }
+ do {
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB,
+ chip->badblockpos & 0xFE, page);
+ bad = cpu_to_le16(chip->read_word(mtd));
+ if (chip->badblockpos & 0x1)
+ bad >>= 8;
+ else
+ bad &= 0xFF;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
+ page);
+ bad = chip->read_byte(mtd);
+ }
- if (likely(chip->badblockbits == 8))
- res = bad != 0xFF;
- else
- res = hweight8(bad) < chip->badblockbits;
+ if (likely(chip->badblockbits == 8))
+ res = bad != 0xFF;
+ else
+ res = hweight8(bad) < chip->badblockbits;
+ ofs += mtd->writesize;
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ i++;
+ } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
if (getchip)
nand_release_device(mtd);
/**
* nand_default_block_markbad - [DEFAULT] mark a block bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
*
- * This is the default implementation, which can be overridden by
- * a hardware specific driver.
+ * This is the default implementation, which can be overridden by a hardware
+ * specific driver. We try operations in the following order, according to our
+ * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
+ * (1) erase the affected block, to allow OOB marker to be written cleanly
+ * (2) update in-memory BBT
+ * (3) write bad block marker to OOB area of affected block
+ * (4) update flash-based BBT
+ * Note that we retain the first error encountered in (3) or (4), finish the
+ * procedures, and dump the error in the end.
*/
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
uint8_t buf[2] = { 0, 0 };
- int block, ret, i = 0;
+ int block, res, ret = 0, i = 0;
+ int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
- if (chip->options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
+ if (write_oob) {
+ struct erase_info einfo;
+
+ /* Attempt erase before marking OOB */
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.mtd = mtd;
+ einfo.addr = ofs;
+ einfo.len = 1 << chip->phys_erase_shift;
+ nand_erase_nand(mtd, &einfo, 0);
+ }
/* Get block number */
block = (int)(ofs >> chip->bbt_erase_shift);
+ /* Mark block bad in memory-based BBT */
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
- /* Do we have a flash based bad block table ? */
- if (chip->options & NAND_USE_FLASH_BBT)
- ret = nand_update_bbt(mtd, ofs);
- else {
+ /* Write bad block marker to OOB */
+ if (write_oob) {
+ struct mtd_oob_ops ops;
+ loff_t wr_ofs = ofs;
+
nand_get_device(chip, mtd, FL_WRITING);
- /* Write to first two pages and to byte 1 and 6 if necessary.
- * If we write to more than one location, the first error
- * encountered quits the procedure. We write two bytes per
- * location, so we dont have to mess with 16 bit access.
- */
- do {
- chip->ops.len = chip->ops.ooblen = 2;
- chip->ops.datbuf = NULL;
- chip->ops.oobbuf = buf;
- chip->ops.ooboffs = chip->badblockpos & ~0x01;
+ ops.datbuf = NULL;
+ ops.oobbuf = buf;
+ ops.ooboffs = chip->badblockpos;
+ if (chip->options & NAND_BUSWIDTH_16) {
+ ops.ooboffs &= ~0x01;
+ ops.len = ops.ooblen = 2;
+ } else {
+ ops.len = ops.ooblen = 1;
+ }
+ ops.mode = MTD_OPS_PLACE_OOB;
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+ /* Write to first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+ wr_ofs += mtd->erasesize - mtd->writesize;
+ do {
+ res = nand_do_write_oob(mtd, wr_ofs, &ops);
+ if (!ret)
+ ret = res;
- if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) {
- chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS
- & ~0x01;
- ret = nand_do_write_oob(mtd, ofs, &chip->ops);
- }
i++;
- ofs += mtd->writesize;
- } while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) &&
- i < 2);
+ wr_ofs += mtd->writesize;
+ } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
nand_release_device(mtd);
}
+
+ /* Update flash-based bad block table */
+ if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+ res = nand_update_bbt(mtd, ofs);
+ if (!ret)
+ ret = res;
+ }
+
if (!ret)
mtd->ecc_stats.badblocks++;
/**
* nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- * Check, if the device is write protected
+ * @mtd: MTD device structure
*
- * The function expects, that the device is already selected
+ * Check, if the device is write protected. The function expects, that the
+ * device is already selected.
*/
static int nand_check_wp(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
- /* broken xD cards report WP despite being writable */
+ /* Broken xD cards report WP despite being writable */
if (chip->options & NAND_BROKEN_XD)
return 0;
/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
- * @mtd: MTD device structure
- * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
- * @allowbbt: 1, if its allowed to access the bbt area
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
+ * @allowbbt: 1, if its allowed to access the bbt area
*
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
return nand_isbad_bbt(mtd, ofs, allowbbt);
}
-/*
- * Wait for the ready pin, after a command
- * The timeout is catched later.
- */
+/* Wait for the ready pin, after a command. The timeout is caught later. */
void nand_wait_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
time_start = get_timer(0);
- /* wait until command is processed or timeout occures */
+ /* Wait until command is processed or timeout occurs */
while (get_timer(time_start) < timeo) {
if (chip->dev_ready)
if (chip->dev_ready(mtd))
/**
* nand_command - [DEFAULT] Send command to NAND device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
*
- * Send command to NAND device. This function is used for small page
- * devices (256/512 Bytes per page)
+ * Send command to NAND device. This function is used for small page devices
+ * (256/512 Bytes per page).
*/
static void nand_command(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT;
- /*
- * Write out the command to the device.
- */
+ /* Write out the command to the device */
if (command == NAND_CMD_SEQIN) {
int readcmd;
}
chip->cmd_ctrl(mtd, command, ctrl);
- /*
- * Address cycle, when necessary
- */
+ /* Address cycle, when necessary */
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
/* Serially input address */
if (column != -1) {
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
- * program and erase have their own busy handlers
- * status and sequential in needs no delay
+ * Program and erase have their own busy handlers status and sequential
+ * in needs no delay
*/
switch (command) {
return;
}
}
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
ndelay(100);
nand_wait_ready(mtd);
/**
* nand_command_lp - [DEFAULT] Send command to NAND large page device
- * @mtd: MTD device structure
- * @command: the command to be sent
- * @column: the column address for this command, -1 if none
- * @page_addr: the page address for this command, -1 if none
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
*
* Send command to NAND device. This is the version for the new large page
- * devices We dont have the separate regions as we have in the small page
- * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
+ * devices. We don't have the separate regions as we have in the small page
+ * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
*/
static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/*
- * program and erase have their own busy handlers
- * status, sequential in, and deplete1 need no delay
+ * Program and erase have their own busy handlers status, sequential
+ * in, and deplete1 need no delay.
*/
switch (command) {
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:
+ /* Read error status commands require only a short delay */
udelay(chip->chip_delay);
return;
default:
/*
* If we don't have access to the busy pin, we apply the given
- * command delay
+ * command delay.
*/
if (!chip->dev_ready) {
udelay(chip->chip_delay);
}
}
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
ndelay(100);
nand_wait_ready(mtd);
/**
* nand_get_device - [GENERIC] Get chip for selected access
- * @chip: the nand chip descriptor
- * @mtd: MTD device structure
- * @new_state: the state which is requested
+ * @chip: the nand chip descriptor
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
*
* Get the device and lock it for exclusive access
*/
}
/**
- * nand_wait - [DEFAULT] wait until the command is done
- * @mtd: MTD device structure
- * @chip: NAND chip structure
+ * nand_wait - [DEFAULT] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
*
- * Wait for command done. This applies to erase and program only
- * Erase can take up to 400ms and program up to 20ms according to
- * general NAND and SmartMedia specs
+ * Wait for command done. This applies to erase and program only. Erase can
+ * take up to 400ms and program up to 20ms according to general NAND and
+ * SmartMedia specs.
*/
static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
}
/**
- * nand_read_page_raw - [Intern] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_raw - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*
- * Not for syndrome calculating ecc controllers, which use a special oob layout
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
chip->read_buf(mtd, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ if (oob_required)
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
/**
- * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*
* We need a special oob layout and handling even when OOB isn't used.
*/
static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip,
- uint8_t *buf, int page)
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
}
/**
- * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*/
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
- chip->ecc.read_page_raw(mtd, chip, buf, page);
+ chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
}
/**
- * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @data_offs: offset of requested data within the page
- * @readlen: data length
- * @bufpoi: buffer to store read data
+ * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
int index = 0;
- /* Column address wihin the page aligned to ECC size (256bytes). */
+ /* Column address within the page aligned to ECC size (256bytes) */
start_step = data_offs / chip->ecc.size;
end_step = (data_offs + readlen - 1) / chip->ecc.size;
num_steps = end_step - start_step + 1;
- /* Data size aligned to ECC ecc.size*/
+ /* Data size aligned to ECC ecc.size */
datafrag_len = num_steps * chip->ecc.size;
eccfrag_len = num_steps * chip->ecc.bytes;
p = bufpoi + data_col_addr;
chip->read_buf(mtd, p, datafrag_len);
- /* Calculate ECC */
+ /* Calculate ECC */
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
- /* The performance is faster if to position offsets
- according to ecc.pos. Let make sure here that
- there are no gaps in ecc positions */
+ /*
+ * The performance is faster if we position offsets according to
+ * ecc.pos. Let's make sure that there are no gaps in ECC positions.
+ */
for (i = 0; i < eccfrag_len - 1; i++) {
if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
eccpos[i + start_step * chip->ecc.bytes + 1]) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
} else {
- /* send the command to read the particular ecc bytes */
- /* take care about buswidth alignment in read_buf */
+ /*
+ * Send the command to read the particular ECC bytes take care
+ * about buswidth alignment in read_buf.
+ */
index = start_step * chip->ecc.bytes;
aligned_pos = eccpos[index] & ~(busw - 1);
}
/**
- * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*
- * Not for syndrome calculating ecc controllers which need a special oob layout
+ * Not for syndrome calculating ECC controllers which need a special oob layout.
*/
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
}
/**
- * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*
- * Hardware ECC for large page chips, require OOB to be read first.
- * For this ECC mode, the write_page method is re-used from ECC_HW.
- * These methods read/write ECC from the OOB area, unlike the
- * ECC_HW_SYNDROME support with multiple ECC steps, follows the
- * "infix ECC" scheme and reads/writes ECC from the data area, by
- * overwriting the NAND manufacturer bad block markings.
+ * Hardware ECC for large page chips, require OOB to be read first. For this
+ * ECC mode, the write_page method is re-used from ECC_HW. These methods
+ * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
+ * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
+ * the data area, by overwriting the NAND manufacturer bad block markings.
*/
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int page)
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
}
/**
- * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @page: page number to read
+ * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
*
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
*/
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
}
/**
- * nand_transfer_oob - [Internal] Transfer oob to client buffer
- * @chip: nand chip structure
- * @oob: oob destination address
- * @ops: oob ops structure
- * @len: size of oob to transfer
+ * nand_transfer_oob - [INTERN] Transfer oob to client buffer
+ * @chip: nand chip structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
*/
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
struct mtd_oob_ops *ops, size_t len)
{
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
return oob + len;
- case MTD_OOB_AUTO: {
+ case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0;
for (; free->length && len; free++, len -= bytes) {
- /* Read request not from offset 0 ? */
+ /* Read request not from offset 0? */
if (unlikely(roffs)) {
if (roffs >= free->length) {
roffs -= free->length;
}
/**
- * nand_do_read_ops - [Internal] Read data with ECC
- *
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob ops structure
+ * nand_do_read_ops - [INTERN] Read data with ECC
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob ops structure
*
* Internal function. Called with chip held.
*/
static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- int chipnr, page, realpage, col, bytes, aligned;
+ int chipnr, page, realpage, col, bytes, aligned, oob_required;
struct nand_chip *chip = mtd->priv;
struct mtd_ecc_stats stats;
- int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
- int sndcmd = 1;
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
- uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
+ uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
uint8_t *bufpoi, *oob, *buf;
buf = ops->datbuf;
oob = ops->oobbuf;
+ oob_required = oob ? 1 : 0;
while (1) {
WATCHDOG_RESET();
bytes = min(mtd->writesize - col, readlen);
aligned = (bytes == mtd->writesize);
- /* Is the current page in the buffer ? */
+ /* Is the current page in the buffer? */
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
- if (likely(sndcmd)) {
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
- sndcmd = 0;
- }
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
/* Now read the page into the buffer */
- if (unlikely(ops->mode == MTD_OOB_RAW))
- ret = chip->ecc.read_page_raw(mtd, chip,
- bufpoi, page);
+ if (unlikely(ops->mode == MTD_OPS_RAW))
+ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
+ oob_required,
+ page);
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
ret = chip->ecc.read_subpage(mtd, chip,
col, bytes, bufpoi);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
- page);
- if (ret < 0)
+ oob_required, page);
+ if (ret < 0) {
+ if (!aligned)
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
break;
+ }
/* Transfer not aligned data */
if (!aligned) {
if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
- !(mtd->ecc_stats.failed - stats.failed))
+ !(mtd->ecc_stats.failed - stats.failed) &&
+ (ops->mode != MTD_OPS_RAW))
chip->pagebuf = realpage;
+ else
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
memcpy(buf, chip->buffers->databuf + col, bytes);
}
buf += bytes;
if (unlikely(oob)) {
-
int toread = min(oobreadlen, max_oobsize);
if (toread) {
oobreadlen -= toread;
}
}
-
- if (!(chip->options & NAND_NO_READRDY)) {
- /*
- * 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 (!chip->dev_ready)
- udelay(chip->chip_delay);
- else
- nand_wait_ready(mtd);
- }
} else {
memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes;
if (!readlen)
break;
- /* For subsequent reads align to page boundary. */
+ /* For subsequent reads align to page boundary */
col = 0;
/* Increment page address */
realpage++;
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
-
- /* Check, if the chip supports auto page increment
- * or if we have hit a block boundary.
- */
- if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
- sndcmd = 1;
}
ops->retlen = ops->len - (size_t) readlen;
/**
* nand_read - [MTD Interface] MTD compatibility 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
+ * @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
*
- * Get hold of the chip and call nand_do_read
+ * Get hold of the chip and call nand_do_read.
*/
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
- /* Do not allow reads past end of device */
- if ((from + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
nand_get_device(chip, mtd, FL_READING);
-
- chip->ops.len = len;
- chip->ops.datbuf = buf;
- chip->ops.oobbuf = NULL;
-
- ret = nand_do_read_ops(mtd, from, &chip->ops);
-
- *retlen = chip->ops.retlen;
-
+ ops.len = len;
+ ops.datbuf = buf;
+ ops.oobbuf = NULL;
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ret = nand_do_read_ops(mtd, from, &ops);
+ *retlen = ops.retlen;
nand_release_device(mtd);
-
return ret;
}
/**
- * nand_read_oob_std - [REPLACABLE] the most common OOB data read function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- * @sndcmd: flag whether to issue read command or not
+ * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
*/
static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
- int page, int sndcmd)
+ int page)
{
- if (sndcmd) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- sndcmd = 0;
- }
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- return sndcmd;
+ return 0;
}
/**
- * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
+ * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
* with syndromes
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to read
- * @sndcmd: flag whether to issue read command or not
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
*/
static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
- int page, int sndcmd)
+ int page)
{
uint8_t *buf = chip->oob_poi;
int length = mtd->oobsize;
if (length > 0)
chip->read_buf(mtd, bufpoi, length);
- return 1;
+ return 0;
}
/**
- * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
+ * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
*/
static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page)
}
/**
- * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
- * with syndrome - only for large page flash !
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @page: page number to write
+ * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
+ * with syndrome - only for large page flash
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
*/
static int nand_write_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, int page)
}
/**
- * nand_do_read_oob - [Intern] NAND read out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operations description structure
+ * nand_do_read_oob - [INTERN] NAND read out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
*
- * NAND read out-of-band data from the spare area
+ * NAND read out-of-band data from the spare area.
*/
static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- int page, realpage, chipnr, sndcmd = 1;
+ int page, realpage, chipnr;
struct nand_chip *chip = mtd->priv;
- int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+ struct mtd_ecc_stats stats;
int readlen = ops->ooblen;
int len;
uint8_t *buf = ops->oobbuf;
+ int ret = 0;
MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
__func__, (unsigned long long)from, readlen);
- if (ops->mode == MTD_OOB_AUTO)
+ stats = mtd->ecc_stats;
+
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
while (1) {
WATCHDOG_RESET();
- sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
+ if (ops->mode == MTD_OPS_RAW)
+ ret = chip->ecc.read_oob_raw(mtd, chip, page);
+ else
+ ret = chip->ecc.read_oob(mtd, chip, page);
+
+ if (ret < 0)
+ break;
len = min(len, readlen);
buf = nand_transfer_oob(chip, buf, ops, len);
- if (!(chip->options & NAND_NO_READRDY)) {
- /*
- * 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 (!chip->dev_ready)
- udelay(chip->chip_delay);
- else
- nand_wait_ready(mtd);
- }
-
readlen -= len;
if (!readlen)
break;
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
-
- /* Check, if the chip supports auto page increment
- * or if we have hit a block boundary.
- */
- if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
- sndcmd = 1;
}
- ops->oobretlen = ops->ooblen;
- return 0;
+ ops->oobretlen = ops->ooblen - readlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
/**
* nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
- * @mtd: MTD device structure
- * @from: offset to read from
- * @ops: oob operation description structure
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
*
- * NAND read data and/or out-of-band data
+ * NAND read data and/or out-of-band data.
*/
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
nand_get_device(chip, mtd, FL_READING);
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
break;
default:
/**
- * nand_write_page_raw - [Intern] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_raw - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
*
- * Not for syndrome calculating ecc controllers, which use a special oob layout
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf)
+static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
chip->write_buf(mtd, buf, mtd->writesize);
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ if (oob_required)
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
/**
- * nand_write_page_raw_syndrome - [Intern] raw page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_raw_syndrome - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
*
* We need a special oob layout and handling even when ECC isn't checked.
*/
-static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
+static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
- const uint8_t *buf)
+ const uint8_t *buf, int oob_required)
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
size = mtd->oobsize - (oob - chip->oob_poi);
if (size)
chip->write_buf(mtd, oob, size);
+
+ return 0;
}
/**
- * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
*/
-static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf)
+static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
- /* Software ecc calculation */
+ /* Software ECC calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- chip->ecc.write_page_raw(mtd, chip, buf);
+ return chip->ecc.write_page_raw(mtd, chip, buf, 1);
}
/**
- * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
*/
-static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf)
+static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
/**
- * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- * @buf: data buffer
+ * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
*
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
*/
-static void nand_write_page_syndrome(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf)
+static int nand_write_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->write_buf(mtd, oob, i);
+
+ return 0;
}
/**
* nand_write_page - [REPLACEABLE] write one page
- * @mtd: MTD device structure
- * @chip: NAND chip descriptor
- * @buf: the data to write
- * @page: page number to write
- * @cached: cached programming
- * @raw: use _raw version of write_page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ * @cached: cached programming
+ * @raw: use _raw version of write_page
*/
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int page, int cached, int raw)
+ const uint8_t *buf, int oob_required, int page,
+ int cached, int raw)
{
int status;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
- chip->ecc.write_page_raw(mtd, chip, buf);
+ status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
else
- chip->ecc.write_page(mtd, chip, buf);
+ status = chip->ecc.write_page(mtd, chip, buf, oob_required);
+
+ if (status < 0)
+ return status;
/*
- * Cached progamming disabled for now, Not sure if its worth the
- * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+ * Cached progamming disabled for now. Not sure if it's worth the
+ * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s).
*/
cached = 0;
status = chip->waitfunc(mtd, chip);
/*
* See if operation failed and additional status checks are
- * available
+ * available.
*/
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status,
if (chip->verify_buf(mtd, buf, mtd->writesize))
return -EIO;
+
+ /* Make sure the next page prog is preceded by a status read */
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
#endif
return 0;
}
/**
- * nand_fill_oob - [Internal] Transfer client buffer to oob
- * @chip: nand chip structure
- * @oob: oob data buffer
- * @len: oob data write length
- * @ops: oob ops structure
+ * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @mtd: MTD device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
*/
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
- struct mtd_oob_ops *ops)
+static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
{
+ struct nand_chip *chip = mtd->priv;
+
+ /*
+ * Initialise to all 0xFF, to avoid the possibility of left over OOB
+ * data from a previous OOB read.
+ */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
return oob + len;
- case MTD_OOB_AUTO: {
+ case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
for (; free->length && len; free++, len -= bytes) {
- /* Write request not from offset 0 ? */
+ /* Write request not from offset 0? */
if (unlikely(woffs)) {
if (woffs >= free->length) {
woffs -= free->length;
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
/**
- * nand_do_write_ops - [Internal] NAND write with ECC
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operations description structure
+ * nand_do_write_ops - [INTERN] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
*
- * NAND write with ECC
+ * NAND write with ECC.
*/
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
uint32_t writelen = ops->len;
uint32_t oobwritelen = ops->ooblen;
- uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?
+ uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
mtd->oobavail : mtd->oobsize;
uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf;
int ret, subpage;
+ int oob_required = oob ? 1 : 0;
ops->retlen = 0;
if (!writelen)
(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;
- /* If we're not given explicit OOB data, let it be 0xFF */
- if (likely(!oob))
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
/* Don't allow multipage oob writes with offset */
if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
return -EINVAL;
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
- /* Partial page write ? */
- if (unlikely(column || writelen < (mtd->writesize - 1))) {
+ /* Partial page write? */
+ if (unlikely(column || writelen < mtd->writesize)) {
cached = 0;
bytes = min_t(int, bytes - column, (int) writelen);
chip->pagebuf = -1;
if (unlikely(oob)) {
size_t len = min(oobwritelen, oobmaxlen);
- oob = nand_fill_oob(chip, oob, len, ops);
+ oob = nand_fill_oob(mtd, oob, len, ops);
oobwritelen -= len;
+ } else {
+ /* We still need to erase leftover OOB data */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
}
- ret = chip->write_page(mtd, chip, wbuf, page, cached,
- (ops->mode == MTD_OOB_RAW));
+ ret = chip->write_page(mtd, chip, wbuf, oob_required, page,
+ cached, (ops->mode == MTD_OPS_RAW));
if (ret)
break;
/**
* nand_write - [MTD Interface] NAND write with ECC
- * @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
- * @buf: the data to write
+ * @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
+ * @buf: the data to write
*
- * NAND write with ECC
+ * NAND write with ECC.
*/
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops;
int ret;
- /* Do not allow writes past end of device */
- if ((to + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
nand_get_device(chip, mtd, FL_WRITING);
-
- chip->ops.len = len;
- chip->ops.datbuf = (uint8_t *)buf;
- chip->ops.oobbuf = NULL;
-
- ret = nand_do_write_ops(mtd, to, &chip->ops);
-
- *retlen = chip->ops.retlen;
-
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.oobbuf = NULL;
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ret = nand_do_write_ops(mtd, to, &ops);
+ *retlen = ops.retlen;
nand_release_device(mtd);
-
return ret;
}
/**
* nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
*
- * NAND write out-of-band
+ * NAND write out-of-band.
*/
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
__func__, (unsigned int)to, (int)ops->ooblen);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
if (page == chip->pagebuf)
chip->pagebuf = -1;
- memset(chip->oob_poi, 0xff, mtd->oobsize);
- nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
- status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
- memset(chip->oob_poi, 0xff, mtd->oobsize);
+ nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
+
+ if (ops->mode == MTD_OPS_RAW)
+ status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
+ else
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
if (status)
return status;
/**
* nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
*/
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
nand_get_device(chip, mtd, FL_WRITING);
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
- case MTD_OOB_RAW:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
break;
default:
}
/**
- * single_erease_cmd - [GENERIC] NAND standard block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
+ * single_erase_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
*
- * Standard erase command for NAND chips
+ * Standard erase command for NAND chips.
*/
static void single_erase_cmd(struct mtd_info *mtd, int page)
{
}
/**
- * multi_erease_cmd - [GENERIC] AND specific block erase command function
- * @mtd: MTD device structure
- * @page: the page address of the block which will be erased
+ * multi_erase_cmd - [GENERIC] AND specific block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
*
- * AND multi block erase command function
- * Erase 4 consecutive blocks
+ * AND multi block erase command function. Erase 4 consecutive blocks.
*/
static void multi_erase_cmd(struct mtd_info *mtd, int page)
{
/**
* nand_erase - [MTD Interface] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
+ * @mtd: MTD device structure
+ * @instr: erase instruction
*
- * Erase one ore more blocks
+ * Erase one ore more blocks.
*/
static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
#define BBT_PAGE_MASK 0xffffff3f
/**
- * nand_erase_nand - [Internal] erase block(s)
- * @mtd: MTD device structure
- * @instr: erase instruction
- * @allowbbt: allow erasing the bbt area
+ * nand_erase_nand - [INTERN] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
*
- * Erase one ore more blocks
+ * Erase one ore more blocks.
*/
int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt)
if (check_offs_len(mtd, instr->addr, instr->len))
return -EINVAL;
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
/* Grab the lock and see if the device is available */
nand_get_device(chip, mtd, FL_ERASING);
* If BBT requires refresh, set the BBT page mask to see if the BBT
* should be rewritten. Otherwise the mask is set to 0xffffffff which
* can not be matched. This is also done when the bbt is actually
- * erased to avoid recusrsive updates
+ * erased to avoid recursive updates.
*/
if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
while (len) {
WATCHDOG_RESET();
- /*
- * heck if we have a bad block, we do not erase bad blocks !
- */
+ /* Check if we have a bad block, we do not erase bad blocks! */
if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) <<
chip->page_shift, 0, allowbbt)) {
- printk(KERN_WARNING "%s: attempt to erase a bad block "
- "at page 0x%08x\n", __func__, page);
+ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+ __func__, page);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/*
* Invalidate the page cache, if we erase the block which
- * contains the current cached page
+ * contains the current cached page.
*/
if (page <= chip->pagebuf && chip->pagebuf <
(page + pages_per_block))
/*
* If BBT requires refresh, set the BBT rewrite flag to the
- * page being erased
+ * page being erased.
*/
if (bbt_masked_page != 0xffffffff &&
(page & BBT_PAGE_MASK) == bbt_masked_page)
/*
* If BBT requires refresh and BBT-PERCHIP, set the BBT
- * page mask to see if this BBT should be rewritten
+ * page mask to see if this BBT should be rewritten.
*/
if (bbt_masked_page != 0xffffffff &&
(chip->bbt_td->options & NAND_BBT_PERCHIP))
/*
* If BBT requires refresh and erase was successful, rewrite any
- * selected bad block tables
+ * selected bad block tables.
*/
if (bbt_masked_page == 0xffffffff || ret)
return ret;
for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
if (!rewrite_bbt[chipnr])
continue;
- /* update the BBT for chip */
+ /* Update the BBT for chip */
MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt "
"(%d:0x%0llx 0x%0x)\n", __func__, chipnr,
rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]);
/**
* nand_sync - [MTD Interface] sync
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * Sync is actually a wait for chip ready function
+ * Sync is actually a wait for chip ready function.
*/
static void nand_sync(struct mtd_info *mtd)
{
/**
* nand_block_isbad - [MTD Interface] Check if block at offset is bad
- * @mtd: MTD device structure
- * @offs: offset relative to mtd start
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
*/
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
- /* Check for invalid offset */
- if (offs > mtd->size)
- return -EINVAL;
-
return nand_block_checkbad(mtd, offs, 1, 0);
}
/**
* nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
- * @mtd: MTD device structure
- * @ofs: offset relative to mtd start
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
*/
static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
ret = nand_block_isbad(mtd, ofs);
if (ret) {
- /* If it was bad already, return success and do nothing. */
+ /* If it was bad already, return success and do nothing */
if (ret > 0)
return 0;
return ret;
return chip->block_markbad(mtd, ofs);
}
-/*
- * Set default functions
+ /**
+ * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
*/
+static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, uint8_t *subfeature_param)
+{
+ int status;
+
+ if (!chip->onfi_version)
+ return -EINVAL;
+
+ chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
+ chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+ return 0;
+}
+
+/**
+ * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ */
+static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, uint8_t *subfeature_param)
+{
+ if (!chip->onfi_version)
+ return -EINVAL;
+
+ /* clear the sub feature parameters */
+ memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
+
+ chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
+ chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+ return 0;
+}
+
+/* Set default functions */
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
/* check for proper chip_delay setup, set 20us if not */
}
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
-/*
- * sanitize ONFI strings so we can safely print them
- */
+/* Sanitize ONFI strings so we can safely print them */
static void sanitize_string(char *s, size_t len)
{
ssize_t i;
- /* null terminate */
+ /* Null terminate */
s[len - 1] = 0;
- /* remove non printable chars */
+ /* Remove non printable chars */
for (i = 0; i < len - 1; i++) {
if (s[i] < ' ' || s[i] > 127)
s[i] = '?';
}
- /* remove trailing spaces */
+ /* Remove trailing spaces */
strim(s);
}
}
/*
- * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
*/
static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
int *busw)
int i;
int val;
- /* try ONFI for unknow chip or LP */
+ /* Try ONFI for unknown chip or LP */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
- MTDDEBUG(MTD_DEBUG_LEVEL0, "ONFI flash detected\n");
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
- MTDDEBUG(MTD_DEBUG_LEVEL0,
- "ONFI param page %d valid\n", i);
+ pr_info("ONFI param page %d valid\n", i);
break;
}
}
if (i == 3)
return 0;
- /* check version */
+ /* Check version */
val = le16_to_cpu(p->revision);
if (val & (1 << 5))
chip->onfi_version = 23;
chip->onfi_version = 0;
if (!chip->onfi_version) {
- printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
- __func__, val);
+ pr_info("%s: unsupported ONFI version: %d\n", __func__, val);
return 0;
}
if (le16_to_cpu(p->features) & 1)
*busw = NAND_BUSWIDTH_16;
- chip->options |= NAND_NO_READRDY | NAND_NO_AUTOINCR;
-
+ pr_info("ONFI flash detected\n");
return 1;
}
#else
#endif
/*
- * Get the flash and manufacturer id and lookup if the type is supported
+ * nand_id_has_period - Check if an ID string has a given wraparound period
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+ * @period: the period of repitition
+ *
+ * Check if an ID string is repeated within a given sequence of bytes at
+ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
+ * period of 2). This is a helper function for nand_id_len(). Returns non-zero
+ * if the repetition has a period of @period; otherwise, returns zero.
+ */
+static int nand_id_has_period(u8 *id_data, int arrlen, int period)
+{
+ int i, j;
+ for (i = 0; i < period; i++)
+ for (j = i + period; j < arrlen; j += period)
+ if (id_data[i] != id_data[j])
+ return 0;
+ return 1;
+}
+
+/*
+ * nand_id_len - Get the length of an ID string returned by CMD_READID
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+
+ * Returns the length of the ID string, according to known wraparound/trailing
+ * zero patterns. If no pattern exists, returns the length of the array.
+ */
+static int nand_id_len(u8 *id_data, int arrlen)
+{
+ int last_nonzero, period;
+
+ /* Find last non-zero byte */
+ for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
+ if (id_data[last_nonzero])
+ break;
+
+ /* All zeros */
+ if (last_nonzero < 0)
+ return 0;
+
+ /* Calculate wraparound period */
+ for (period = 1; period < arrlen; period++)
+ if (nand_id_has_period(id_data, arrlen, period))
+ break;
+
+ /* There's a repeated pattern */
+ if (period < arrlen)
+ return period;
+
+ /* There are trailing zeros */
+ if (last_nonzero < arrlen - 1)
+ return last_nonzero + 1;
+
+ /* No pattern detected */
+ return arrlen;
+}
+
+/*
+ * Many new NAND share similar device ID codes, which represent the size of the
+ * chip. The rest of the parameters must be decoded according to generic or
+ * manufacturer-specific "extended ID" decoding patterns.
+ */
+static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 id_data[8], int *busw)
+{
+ int extid, id_len;
+ /* The 3rd id byte holds MLC / multichip data */
+ chip->cellinfo = id_data[2];
+ /* The 4th id byte is the important one */
+ extid = id_data[3];
+
+ id_len = nand_id_len(id_data, 8);
+
+ /*
+ * Field definitions are in the following datasheets:
+ * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
+ * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
+ * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22)
+ *
+ * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung
+ * ID to decide what to do.
+ */
+ if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
+ (chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
+ id_data[5] != 0x00) {
+ /* Calc pagesize */
+ mtd->writesize = 2048 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
+ case 1:
+ mtd->oobsize = 128;
+ break;
+ case 2:
+ mtd->oobsize = 218;
+ break;
+ case 3:
+ mtd->oobsize = 400;
+ break;
+ case 4:
+ mtd->oobsize = 436;
+ break;
+ case 5:
+ mtd->oobsize = 512;
+ break;
+ case 6:
+ default: /* Other cases are "reserved" (unknown) */
+ mtd->oobsize = 640;
+ break;
+ }
+ extid >>= 2;
+ /* Calc blocksize */
+ mtd->erasesize = (128 * 1024) <<
+ (((extid >> 1) & 0x04) | (extid & 0x03));
+ *busw = 0;
+ } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
+ (chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
+ unsigned int tmp;
+
+ /* Calc pagesize */
+ mtd->writesize = 2048 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
+ case 0:
+ mtd->oobsize = 128;
+ break;
+ case 1:
+ mtd->oobsize = 224;
+ break;
+ case 2:
+ mtd->oobsize = 448;
+ break;
+ case 3:
+ mtd->oobsize = 64;
+ break;
+ case 4:
+ mtd->oobsize = 32;
+ break;
+ case 5:
+ mtd->oobsize = 16;
+ break;
+ default:
+ mtd->oobsize = 640;
+ break;
+ }
+ extid >>= 2;
+ /* Calc blocksize */
+ tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
+ if (tmp < 0x03)
+ mtd->erasesize = (128 * 1024) << tmp;
+ else if (tmp == 0x03)
+ mtd->erasesize = 768 * 1024;
+ else
+ mtd->erasesize = (64 * 1024) << tmp;
+ *busw = 0;
+ } else {
+ /* Calc pagesize */
+ mtd->writesize = 1024 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ mtd->oobsize = (8 << (extid & 0x01)) *
+ (mtd->writesize >> 9);
+ 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;
+ }
+}
+
+ /*
+ * Old devices have chip data hardcoded in the device ID table. nand_decode_id
+ * decodes a matching ID table entry and assigns the MTD size parameters for
+ * the chip.
+ */
+static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
+ const struct nand_flash_dev *type, u8 id_data[8],
+ int *busw)
+{
+ int maf_id = id_data[0];
+
+ mtd->erasesize = type->erasesize;
+ mtd->writesize = type->pagesize;
+ mtd->oobsize = mtd->writesize / 32;
+ *busw = type->options & NAND_BUSWIDTH_16;
+
+ /*
+ * Check for Spansion/AMD ID + repeating 5th, 6th byte since
+ * some Spansion chips have erasesize that conflicts with size
+ * listed in nand_ids table.
+ * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
+ */
+ if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
+ && id_data[6] == 0x00 && id_data[7] == 0x00
+ && mtd->writesize == 512) {
+ mtd->erasesize = 128 * 1024;
+ mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
+ }
+}
+
+ /*
+ * Set the bad block marker/indicator (BBM/BBI) patterns according to some
+ * heuristic patterns using various detected parameters (e.g., manufacturer,
+ * page size, cell-type information).
+ */
+static void nand_decode_bbm_options(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 id_data[8])
+{
+ int maf_id = id_data[0];
+
+ /* Set the bad block position */
+ if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
+ chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
+ else
+ chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
+
+ /*
+ * Bad block marker is stored in the last page of each block on Samsung
+ * and Hynix MLC devices; stored in first two pages of each block on
+ * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
+ * AMD/Spansion, and Macronix. All others scan only the first page.
+ */
+ if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
+ (maf_id == NAND_MFR_SAMSUNG ||
+ maf_id == NAND_MFR_HYNIX))
+ chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
+ else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
+ (maf_id == NAND_MFR_SAMSUNG ||
+ maf_id == NAND_MFR_HYNIX ||
+ maf_id == NAND_MFR_TOSHIBA ||
+ maf_id == NAND_MFR_AMD ||
+ maf_id == NAND_MFR_MACRONIX)) ||
+ (mtd->writesize == 2048 &&
+ maf_id == NAND_MFR_MICRON))
+ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+}
+
+/*
+ * Get the flash and manufacturer id and lookup if the type is supported.
*/
static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
const char *name;
int i, maf_idx;
u8 id_data[8];
- int ret;
/* Select the device */
chip->select_chip(mtd, 0);
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
- * after power-up
+ * after power-up.
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
- /* Try again to make sure, as some systems the bus-hold or other
+ /*
+ * Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
- for (i = 0; i < 2; i++)
+ /* Read entire ID string */
+ for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
- printk(KERN_INFO "%s: second ID read did not match "
- "%02x,%02x against %02x,%02x\n", __func__,
- *maf_id, *dev_id, id_data[0], id_data[1]);
+ pr_info("%s: second ID read did not match "
+ "%02x,%02x against %02x,%02x\n", __func__,
+ *maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}
chip->onfi_version = 0;
if (!type->name || !type->pagesize) {
/* Check is chip is ONFI compliant */
- ret = nand_flash_detect_onfi(mtd, chip, &busw);
- if (ret)
+ if (nand_flash_detect_onfi(mtd, chip, &busw))
goto ident_done;
}
- chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
-
- /* Read entire ID string */
-
- for (i = 0; i < 8; i++)
- id_data[i] = chip->read_byte(mtd);
-
if (!type->name)
return ERR_PTR(-ENODEV);
chip->chipsize = (uint64_t)type->chipsize << 20;
if (!type->pagesize && chip->init_size) {
- /* set the pagesize, oobsize, erasesize by the driver*/
+ /* Set the pagesize, oobsize, erasesize by the driver */
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
- int extid;
- /* The 3rd id byte holds MLC / multichip data */
- chip->cellinfo = id_data[2];
- /* The 4th id byte is the important one */
- extid = id_data[3];
-
- /*
- * Field definitions are in the following datasheets:
- * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
- * New style (6 byte ID): Samsung K9GBG08U0M (p.40)
- *
- * Check for wraparound + Samsung ID + nonzero 6th byte
- * to decide what to do.
- */
- if (id_data[0] == id_data[6] && id_data[1] == id_data[7] &&
- id_data[0] == NAND_MFR_SAMSUNG &&
- (chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
- id_data[5] != 0x00) {
- /* Calc pagesize */
- mtd->writesize = 2048 << (extid & 0x03);
- extid >>= 2;
- /* Calc oobsize */
- switch (extid & 0x03) {
- case 1:
- mtd->oobsize = 128;
- break;
- case 2:
- mtd->oobsize = 218;
- break;
- case 3:
- mtd->oobsize = 400;
- break;
- default:
- mtd->oobsize = 436;
- break;
- }
- extid >>= 2;
- /* Calc blocksize */
- mtd->erasesize = (128 * 1024) <<
- (((extid >> 1) & 0x04) | (extid & 0x03));
- busw = 0;
- } else {
- /* Calc pagesize */
- mtd->writesize = 1024 << (extid & 0x03);
- extid >>= 2;
- /* Calc oobsize */
- mtd->oobsize = (8 << (extid & 0x01)) *
- (mtd->writesize >> 9);
- 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;
- }
+ /* Decode parameters from extended ID */
+ nand_decode_ext_id(mtd, chip, id_data, &busw);
} else {
- /*
- * Old devices have chip data hardcoded in the device id table
- */
- mtd->erasesize = type->erasesize;
- mtd->writesize = type->pagesize;
- mtd->oobsize = mtd->writesize / 32;
- busw = type->options & NAND_BUSWIDTH_16;
-
- /*
- * Check for Spansion/AMD ID + repeating 5th, 6th byte since
- * some Spansion chips have erasesize that conflicts with size
- * listed in nand_ids table
- * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
- */
- if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
- id_data[5] == 0x00 && id_data[6] == 0x00 &&
- id_data[7] == 0x00 && mtd->writesize == 512) {
- mtd->erasesize = 128 * 1024;
- mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
- }
+ nand_decode_id(mtd, chip, type, id_data, &busw);
}
/* Get chip options, preserve non chip based options */
chip->options |= type->options;
- /* Check if chip is a not a samsung device. Do not clear the
- * options for chips which are not having an extended id.
+ /*
+ * Check if chip is not a Samsung device. Do not clear the
+ * options for chips which do not have an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
- /*
- * Set chip as a default. Board drivers can override it, if necessary
- */
- chip->options |= NAND_NO_AUTOINCR;
-
/* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
/*
* Check, if buswidth is correct. Hardware drivers should set
- * chip correct !
+ * chip correct!
*/
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
- printk(KERN_INFO "NAND device: Manufacturer ID:"
- " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
- *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
- printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
- (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
- busw ? 16 : 8);
+ pr_info("NAND device: Manufacturer ID:"
+ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
+ *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+ pr_warn("NAND bus width %d instead %d bit\n",
+ (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
+ busw ? 16 : 8);
return ERR_PTR(-EINVAL);
}
+ nand_decode_bbm_options(mtd, chip, id_data);
+
/* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1;
- /* Convert chipsize to number of pages per chip -1. */
+ /* Convert chipsize to number of pages per chip -1 */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
chip->bbt_erase_shift = chip->phys_erase_shift =
chip->badblockbits = 8;
- /* Set the bad block position */
- if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))
- chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
- else
- chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
-
- /*
- * Bad block marker is stored in the last page of each block
- * on Samsung and Hynix MLC devices; stored in first two pages
- * of each block on Micron devices with 2KiB pages and on
- * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan
- * only the first page.
- */
- if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
- (*maf_id == NAND_MFR_SAMSUNG ||
- *maf_id == NAND_MFR_HYNIX))
- chip->options |= NAND_BBT_SCANLASTPAGE;
- else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
- (*maf_id == NAND_MFR_SAMSUNG ||
- *maf_id == NAND_MFR_HYNIX ||
- *maf_id == NAND_MFR_TOSHIBA ||
- *maf_id == NAND_MFR_AMD)) ||
- (mtd->writesize == 2048 &&
- *maf_id == NAND_MFR_MICRON))
- chip->options |= NAND_BBT_SCAN2NDPAGE;
-
- /*
- * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6
- */
- if (!(busw & NAND_BUSWIDTH_16) &&
- *maf_id == NAND_MFR_STMICRO &&
- mtd->writesize == 2048) {
- chip->options |= NAND_BBT_SCANBYTE1AND6;
- chip->badblockpos = 0;
- }
-
/* Check for AND chips with 4 page planes */
if (chip->options & NAND_4PAGE_ARRAY)
chip->erase_cmd = multi_erase_cmd;
else
chip->erase_cmd = single_erase_cmd;
- /* Do not replace user supplied command function ! */
+ /* Do not replace user supplied command function! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
- /* TODO onfi flash name */
name = type->name;
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
if (chip->onfi_version)
name = chip->onfi_params.model;
#endif
- MTDDEBUG(MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
- " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
- nand_manuf_ids[maf_idx].name, name);
+ pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s),"
+ " page size: %d, OOB size: %d\n",
+ *maf_id, *dev_id, nand_manuf_ids[maf_idx].name,
+ name,
+ mtd->writesize, mtd->oobsize);
return type;
}
/**
* nand_scan_ident - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: Number of chips to scan for
- * @table: Alternative NAND ID table
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
+ * @table: alternative NAND ID table
*
- * This is the first phase of the normal nand_scan() function. It
- * reads the flash ID and sets up MTD fields accordingly.
+ * This is the first phase of the normal nand_scan() function. It reads the
+ * flash ID and sets up MTD fields accordingly.
*
* The mtd->owner field must be set to the module of the caller.
*/
if (IS_ERR(type)) {
#ifndef CONFIG_SYS_NAND_QUIET_TEST
- printk(KERN_WARNING "No NAND device found!!!\n");
+ pr_warn("No NAND device found\n");
#endif
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
#ifdef DEBUG
if (i > 1)
- printk(KERN_INFO "%d NAND chips detected\n", i);
+ pr_info("%d NAND chips detected\n", i);
#endif
/* Store the number of chips and calc total size for mtd */
/**
* nand_scan_tail - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
+ * @mtd: MTD device structure
*
- * This is the second phase of the normal nand_scan() function. It
- * fills out all the uninitialized function pointers with the defaults
- * and scans for a bad block table if appropriate.
+ * This is the second phase of the normal nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults and scans for a
+ * bad block table if appropriate.
*/
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
struct nand_chip *chip = mtd->priv;
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH));
+
if (!(chip->options & NAND_OWN_BUFFERS))
chip->buffers = memalign(ARCH_DMA_MINALIGN,
sizeof(*chip->buffers));
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
- * If no default placement scheme is given, select an appropriate one
+ * If no default placement scheme is given, select an appropriate one.
*/
if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
switch (mtd->oobsize) {
chip->ecc.layout = &nand_oob_128;
break;
default:
- printk(KERN_WARNING "No oob scheme defined for "
- "oobsize %d\n", mtd->oobsize);
+ pr_warn("No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
}
}
if (!chip->write_page)
chip->write_page = nand_write_page;
+ /* set for ONFI nand */
+ if (!chip->onfi_set_features)
+ chip->onfi_set_features = nand_onfi_set_features;
+ if (!chip->onfi_get_features)
+ chip->onfi_get_features = nand_onfi_get_features;
+
/*
- * check ECC mode, default to software if 3byte/512byte hardware ECC is
+ * Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
/* Similar to NAND_ECC_HW, but a separate read_page handle */
if (!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) {
- printk(KERN_WARNING "No ECC functions supplied; "
- "Hardware ECC not possible\n");
+ pr_warn("No ECC functions supplied; "
+ "hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
case NAND_ECC_HW:
- /* Use standard hwecc read page function ? */
+ /* Use standard hwecc read page function? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc;
if (!chip->ecc.write_page)
chip->ecc.read_page == nand_read_page_hwecc ||
!chip->ecc.write_page ||
chip->ecc.write_page == nand_write_page_hwecc)) {
- printk(KERN_WARNING "No ECC functions supplied; "
- "Hardware ECC not possible\n");
+ pr_warn("No ECC functions supplied; "
+ "hardware ECC not possible\n");
BUG();
}
- /* Use standard syndrome read/write page function ? */
+ /* Use standard syndrome read/write page function? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_syndrome;
if (!chip->ecc.write_page)
if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_syndrome;
- if (mtd->writesize >= chip->ecc.size)
+ if (mtd->writesize >= chip->ecc.size) {
+ if (!chip->ecc.strength) {
+ pr_warn("Driver must set ecc.strength when using hardware ECC\n");
+ BUG();
+ }
break;
- printk(KERN_WARNING "%d byte HW ECC not possible on "
- "%d byte page size, fallback to SW ECC\n",
- chip->ecc.size, mtd->writesize);
+ }
+ pr_warn("%d byte HW ECC not possible on "
+ "%d byte page size, fallback to SW ECC\n",
+ chip->ecc.size, mtd->writesize);
chip->ecc.mode = NAND_ECC_SOFT;
case NAND_ECC_SOFT:
if (!chip->ecc.size)
chip->ecc.size = 256;
chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
break;
case NAND_ECC_SOFT_BCH:
if (!mtd_nand_has_bch()) {
- printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n");
+ pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
return -EINVAL;
}
chip->ecc.calculate = nand_bch_calculate_ecc;
/*
* Board driver should supply ecc.size and ecc.bytes values to
* select how many bits are correctable; see nand_bch_init()
- * for details.
- * Otherwise, default to 4 bits for large page devices
+ * for details. Otherwise, default to 4 bits for large page
+ * devices.
*/
if (!chip->ecc.size && (mtd->oobsize >= 64)) {
chip->ecc.size = 512;
chip->ecc.bytes,
&chip->ecc.layout);
if (!chip->ecc.priv)
- printk(KERN_WARNING "BCH ECC initialization failed!\n");
-
+ pr_warn("BCH ECC initialization failed!\n");
+ chip->ecc.strength =
+ chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
break;
case NAND_ECC_NONE:
- printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
- "This is not recommended !!\n");
+ pr_warn("NAND_ECC_NONE selected by board driver. "
+ "This is not recommended !!\n");
chip->ecc.read_page = nand_read_page_raw;
chip->ecc.write_page = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
break;
default:
- printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
- chip->ecc.mode);
+ pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);
BUG();
}
+ /* For many systems, the standard OOB write also works for raw */
+ if (!chip->ecc.read_oob_raw)
+ chip->ecc.read_oob_raw = chip->ecc.read_oob;
+ if (!chip->ecc.write_oob_raw)
+ chip->ecc.write_oob_raw = chip->ecc.write_oob;
+
/*
* The number of bytes available for a client to place data into
- * the out of band area
+ * the out of band area.
*/
chip->ecc.layout->oobavail = 0;
for (i = 0; chip->ecc.layout->oobfree[i].length
/*
* Set the number of read / write steps for one page depending on ECC
- * mode
+ * mode.
*/
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
- printk(KERN_WARNING "Invalid ecc parameters\n");
+ pr_warn("Invalid ECC parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
- /*
- * Allow subpage writes up to ecc.steps. Not possible for MLC
- * FLASH.
- */
+ /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch (chip->ecc.steps) {
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
MTD_CAP_NANDFLASH;
- mtd->erase = nand_erase;
- mtd->point = NULL;
- mtd->unpoint = NULL;
- mtd->read = nand_read;
- mtd->write = nand_write;
- mtd->read_oob = nand_read_oob;
- mtd->write_oob = nand_write_oob;
- mtd->sync = nand_sync;
- mtd->lock = NULL;
- mtd->unlock = NULL;
- mtd->block_isbad = nand_block_isbad;
- mtd->block_markbad = nand_block_markbad;
-
- /* propagate ecc.layout to mtd_info */
+ mtd->_erase = nand_erase;
+ mtd->_point = NULL;
+ mtd->_unpoint = NULL;
+ mtd->_read = nand_read;
+ mtd->_write = nand_write;
+ mtd->_read_oob = nand_read_oob;
+ mtd->_write_oob = nand_write_oob;
+ mtd->_sync = nand_sync;
+ mtd->_lock = NULL;
+ mtd->_unlock = NULL;
+ mtd->_block_isbad = nand_block_isbad;
+ mtd->_block_markbad = nand_block_markbad;
+
+ /* propagate ecc info to mtd_info */
mtd->ecclayout = chip->ecc.layout;
+ mtd->ecc_strength = chip->ecc.strength;
+ /*
+ * Initialize bitflip_threshold to its default prior scan_bbt() call.
+ * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
+ * properly set.
+ */
+ if (!mtd->bitflip_threshold)
+ mtd->bitflip_threshold = mtd->ecc_strength;
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
/**
* nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: Number of chips to scan for
- *
- * This fills out all the uninitialized function pointers
- * with the defaults.
- * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values.
- * The mtd->owner field must be set to the module of the caller
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
*
+ * This fills out all the uninitialized function pointers with the defaults.
+ * The flash ID is read and the mtd/chip structures are filled with the
+ * appropriate values. The mtd->owner field must be set to the module of the
+ * caller.
*/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
/**
* nand_release - [NAND Interface] Free resources held by the NAND device
- * @mtd: MTD device structure
-*/
+ * @mtd: MTD device structure
+ */
void nand_release(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
* Overview:
* Bad block table support for the NAND driver
*
- * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
*
* 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
*
* When nand_scan_bbt is called, then it tries to find the bad block table
* depending on the options in the BBT descriptor(s). If no flash based BBT
- * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
+ * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory
* marked good / bad blocks. This information is used to create a memory BBT.
* Once a new bad block is discovered then the "factory" information is updated
* on the device.
* BBT on flash. If a BBT is found then the contents are read and the memory
* based BBT is created. If a mirrored BBT is selected then the mirror is
* searched too and the versions are compared. If the mirror has a greater
- * version number than the mirror BBT is used to build the memory based BBT.
+ * version number, then the mirror BBT is used to build the memory based BBT.
* If the tables are not versioned, then we "or" the bad block information.
* If one of the BBTs is out of date or does not exist it is (re)created.
* If no BBT exists at all then the device is scanned for factory marked
* The table is marked in the OOB area with an ident pattern and a version
* number which indicates which of both tables is more up to date. If the NAND
* controller needs the complete OOB area for the ECC information then the
- * option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern
- * and the version byte into the data area and the OOB area will remain
- * untouched.
+ * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of
+ * course): it moves the ident pattern and the version byte into the data area
+ * and the OOB area will remain untouched.
*
* The table uses 2 bits per block
* 11b: block is good
#include <malloc.h>
#include <linux/compat.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/bbm.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/bitops.h>
+#include <linux/string.h>
#include <asm/errno.h>
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
{
- int ret;
-
- ret = memcmp(buf, td->pattern, td->len);
- if (!ret)
- return ret;
- return -1;
+ if (memcmp(buf, td->pattern, td->len))
+ return -1;
+ return 0;
}
/**
* check_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf: the buffer to search
- * @len: the length of buffer to search
- * @paglen: the pagelength
- * @td: search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers.
- * If the SCAN_EMPTY option is set then check, if all bytes except the
- * pattern area contain 0xff
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
*
-*/
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if
+ * all bytes except the pattern area contain 0xff.
+ */
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
- int i, end = 0;
+ int end = 0;
uint8_t *p = buf;
if (td->options & NAND_BBT_NO_OOB)
return check_pattern_no_oob(buf, td);
end = paglen + td->offs;
- if (td->options & NAND_BBT_SCANEMPTY) {
- for (i = 0; i < end; i++) {
- if (p[i] != 0xff)
- return -1;
- }
- }
+ if (td->options & NAND_BBT_SCANEMPTY)
+ if (memchr_inv(p, 0xff, end))
+ return -1;
p += end;
/* Compare the pattern */
- for (i = 0; i < td->len; i++) {
- if (p[i] != td->pattern[i])
- return -1;
- }
-
- /* Check both positions 1 and 6 for pattern? */
- if (td->options & NAND_BBT_SCANBYTE1AND6) {
- if (td->options & NAND_BBT_SCANEMPTY) {
- p += td->len;
- end += NAND_SMALL_BADBLOCK_POS - td->offs;
- /* Check region between positions 1 and 6 */
- for (i = 0; i < NAND_SMALL_BADBLOCK_POS - td->offs - td->len;
- i++) {
- if (*p++ != 0xff)
- return -1;
- }
- }
- else {
- p += NAND_SMALL_BADBLOCK_POS - td->offs;
- }
- /* Compare the pattern */
- for (i = 0; i < td->len; i++) {
- if (p[i] != td->pattern[i])
- return -1;
- }
- }
+ if (memcmp(p, td->pattern, td->len))
+ return -1;
if (td->options & NAND_BBT_SCANEMPTY) {
p += td->len;
end += td->len;
- for (i = end; i < len; i++) {
- if (*p++ != 0xff)
- return -1;
- }
+ if (memchr_inv(p, 0xff, len - end))
+ return -1;
}
return 0;
}
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf: the buffer to search
- * @td: search pattern descriptor
+ * @buf: the buffer to search
+ * @td: search pattern descriptor
*
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers. Same as check_pattern, but
- * no optional empty check
- *
-*/
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. Same as check_pattern, but no optional empty
+ * check.
+ */
static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
{
- int i;
- uint8_t *p = buf;
-
/* Compare the pattern */
- for (i = 0; i < td->len; i++) {
- if (p[td->offs + i] != td->pattern[i])
- return -1;
- }
- /* Need to check location 1 AND 6? */
- if (td->options & NAND_BBT_SCANBYTE1AND6) {
- for (i = 0; i < td->len; i++) {
- if (p[NAND_SMALL_BADBLOCK_POS + i] != td->pattern[i])
- return -1;
- }
- }
+ if (memcmp(buf + td->offs, td->pattern, td->len))
+ return -1;
return 0;
}
/**
* add_marker_len - compute the length of the marker in data area
- * @td: BBT descriptor used for computation
+ * @td: BBT descriptor used for computation
*
- * The length will be 0 if the markeris located in OOB area.
+ * The length will be 0 if the marker is located in OOB area.
*/
static u32 add_marker_len(struct nand_bbt_descr *td)
{
/**
* read_bbt - [GENERIC] Read the bad block table starting from page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @page: the starting page
- * @num: the number of bbt descriptors to read
- * @td: the bbt describtion table
- * @offs: offset in the memory table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @td: the bbt describtion table
+ * @offs: offset in the memory table
*
* Read the bad block table starting from page.
- *
*/
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
struct nand_bbt_descr *td, int offs)
{
- int res, i, j, act = 0;
+ int res, ret = 0, i, j, act = 0;
struct nand_chip *this = mtd->priv;
size_t retlen, len, totlen;
loff_t from;
int bits = td->options & NAND_BBT_NRBITS_MSK;
- uint8_t msk = (uint8_t) ((1 << bits) - 1);
+ uint8_t msk = (uint8_t)((1 << bits) - 1);
u32 marker_len;
int reserved_block_code = td->reserved_block_code;
totlen = (num * bits) >> 3;
marker_len = add_marker_len(td);
- from = ((loff_t) page) << this->page_shift;
+ from = ((loff_t)page) << this->page_shift;
while (totlen) {
- len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+ len = min(totlen, (size_t)(1 << this->bbt_erase_shift));
if (marker_len) {
/*
* In case the BBT marker is not in the OOB area it
from += marker_len;
marker_len = 0;
}
- res = mtd->read(mtd, from, len, &retlen, buf);
+ res = mtd_read(mtd, from, len, &retlen, buf);
if (res < 0) {
- if (retlen != len) {
- printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
+ if (mtd_is_eccerr(res)) {
+ pr_info("nand_bbt: ECC error in BBT at "
+ "0x%012llx\n", from & ~mtd->writesize);
+ return res;
+ } else if (mtd_is_bitflip(res)) {
+ pr_info("nand_bbt: corrected error in BBT at "
+ "0x%012llx\n", from & ~mtd->writesize);
+ ret = res;
+ } else {
+ pr_info("nand_bbt: error reading BBT\n");
return res;
}
- printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
}
/* Analyse data */
if (tmp == msk)
continue;
if (reserved_block_code && (tmp == reserved_block_code)) {
- printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n",
- (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
+ (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
mtd->ecc_stats.bbtblocks++;
continue;
}
- MTDDEBUG(MTD_DEBUG_LEVEL0, "nand_read_bbt: " \
- "Bad block at 0x%012llx\n",
+ pr_info("nand_read_bbt: Bad block at 0x%012llx\n",
(loff_t)((offs << 2) + (act >> 1))
<< this->bbt_erase_shift);
- /* Factory marked bad or worn out ? */
+ /* Factory marked bad or worn out? */
if (tmp == 0)
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
else
totlen -= len;
from += len;
}
- return 0;
+ return ret;
}
/**
* read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @chip: read the table for a specific chip, -1 read all chips.
- * Applies only if NAND_BBT_PERCHIP option is set
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @chip: read the table for a specific chip, -1 read all chips; applies only if
+ * NAND_BBT_PERCHIP option is set
*
- * Read the bad block table for all chips starting at a given page
- * We assume that the bbt bits are in consecutive order.
-*/
+ * Read the bad block table for all chips starting at a given page. We assume
+ * that the bbt bits are in consecutive order.
+ */
static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
{
struct nand_chip *this = mtd->priv;
return 0;
}
-/*
- * BBT marker is in the first page, no OOB.
- */
-static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+/* BBT marker is in the first page, no OOB */
+static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
struct nand_bbt_descr *td)
{
size_t retlen;
if (td->options & NAND_BBT_VERSION)
len++;
- return mtd->read(mtd, offs, len, &retlen, buf);
+ return mtd_read(mtd, offs, len, &retlen, buf);
}
-/*
- * Scan read raw data from flash
+/**
+ * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @offs: offset at which to scan
+ * @len: length of data region to read
+ *
+ * Scan read data from data+OOB. May traverse multiple pages, interleaving
+ * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
+ * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
*/
-static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len)
{
struct mtd_oob_ops ops;
- int res;
+ int res, ret = 0;
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
-
while (len > 0) {
- if (len <= mtd->writesize) {
- ops.oobbuf = buf + len;
- ops.datbuf = buf;
- ops.len = len;
- return mtd->read_oob(mtd, offs, &ops);
- } else {
- ops.oobbuf = buf + mtd->writesize;
- ops.datbuf = buf;
- ops.len = mtd->writesize;
- res = mtd->read_oob(mtd, offs, &ops);
+ ops.datbuf = buf;
+ ops.len = min(len, (size_t)mtd->writesize);
+ ops.oobbuf = buf + ops.len;
- if (res)
+ res = mtd_read_oob(mtd, offs, &ops);
+ if (res) {
+ if (!mtd_is_bitflip_or_eccerr(res))
return res;
+ else if (mtd_is_eccerr(res) || !ret)
+ ret = res;
}
buf += mtd->oobsize + mtd->writesize;
len -= mtd->writesize;
+ offs += mtd->writesize;
}
- return 0;
+ return ret;
}
-static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len, struct nand_bbt_descr *td)
{
if (td->options & NAND_BBT_NO_OOB)
- return scan_read_raw_data(mtd, buf, offs, td);
+ return scan_read_data(mtd, buf, offs, td);
else
- return scan_read_raw_oob(mtd, buf, offs, len);
+ return scan_read_oob(mtd, buf, offs, len);
}
-/*
- * Scan write data with oob to flash
- */
+/* Scan write data with oob to flash */
static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
uint8_t *buf, uint8_t *oob)
{
struct mtd_oob_ops ops;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.datbuf = buf;
ops.oobbuf = oob;
ops.len = len;
- return mtd->write_oob(mtd, offs, &ops);
+ return mtd_write_oob(mtd, offs, &ops);
}
static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
/**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
- *
- * Read the bad block table(s) for all chips starting at a given page
- * We assume that the bbt bits are in consecutive order.
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
*
-*/
-static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
- struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+ * Read the bad block table(s) for all chips starting at a given page. We
+ * assume that the bbt bits are in consecutive order.
+ */
+static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
struct nand_chip *this = mtd->priv;
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
- scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+ scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
mtd->writesize, td);
td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
- printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
- td->pages[0], td->version[0]);
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ td->pages[0], td->version[0]);
}
/* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) {
- scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
- mtd->writesize, td);
+ scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+ mtd->writesize, md);
md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
- printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
- md->pages[0], md->version[0]);
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ md->pages[0], md->version[0]);
}
- return 1;
}
-/*
- * Scan a given block full
- */
+/* Scan a given block full */
static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, size_t readlen,
- int scanlen, int len)
+ int scanlen, int numpages)
{
int ret, j;
- ret = scan_read_raw_oob(mtd, buf, offs, readlen);
- if (ret)
+ ret = scan_read_oob(mtd, buf, offs, readlen);
+ /* Ignore ECC errors when checking for BBM */
+ if (ret && !mtd_is_bitflip_or_eccerr(ret))
return ret;
- for (j = 0; j < len; j++, buf += scanlen) {
+ for (j = 0; j < numpages; j++, buf += scanlen) {
if (check_pattern(buf, scanlen, mtd->writesize, bd))
return 1;
}
return 0;
}
-/*
- * Scan a given block partially
- */
+/* Scan a given block partially */
static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
- loff_t offs, uint8_t *buf, int len)
+ loff_t offs, uint8_t *buf, int numpages)
{
struct mtd_oob_ops ops;
int j, ret;
ops.oobbuf = buf;
ops.ooboffs = 0;
ops.datbuf = NULL;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
- for (j = 0; j < len; j++) {
+ for (j = 0; j < numpages; j++) {
/*
- * Read the full oob until read_oob is fixed to
- * handle single byte reads for 16 bit
- * buswidth
+ * Read the full oob until read_oob is fixed to handle single
+ * byte reads for 16 bit buswidth.
*/
- ret = mtd->read_oob(mtd, offs, &ops);
- if (ret)
+ ret = mtd_read_oob(mtd, offs, &ops);
+ /* Ignore ECC errors when checking for BBM */
+ if (ret && !mtd_is_bitflip_or_eccerr(ret))
return ret;
if (check_short_pattern(buf, bd))
/**
* create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @bd: descriptor for the good/bad block search pattern
- * @chip: create the table for a specific chip, -1 read all chips.
- * Applies only if NAND_BBT_PERCHIP option is set
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips; applies only
+ * if NAND_BBT_PERCHIP option is set
*
- * Create a bad block table by scanning the device
- * for the given good/bad block identify pattern
+ * Create a bad block table by scanning the device for the given good/bad block
+ * identify pattern.
*/
static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *bd, int chip)
{
struct nand_chip *this = mtd->priv;
- int i, numblocks, len, scanlen;
+ int i, numblocks, numpages, scanlen;
int startblock;
loff_t from;
size_t readlen;
- MTDDEBUG(MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n");
+ pr_info("Scanning device for bad blocks\n");
if (bd->options & NAND_BBT_SCANALLPAGES)
- len = 1 << (this->bbt_erase_shift - this->page_shift);
+ numpages = 1 << (this->bbt_erase_shift - this->page_shift);
else if (bd->options & NAND_BBT_SCAN2NDPAGE)
- len = 2;
+ numpages = 2;
else
- len = 1;
+ numpages = 1;
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
/* We need only read few bytes from the OOB area */
} else {
/* Full page content should be read */
scanlen = mtd->writesize + mtd->oobsize;
- readlen = len * mtd->writesize;
+ readlen = numpages * mtd->writesize;
}
if (chip == -1) {
- /* Note that numblocks is 2 * (real numblocks) here, see i+=2
- * below as it makes shifting and masking less painful */
+ /*
+ * Note that numblocks is 2 * (real numblocks) here, see i+=2
+ * below as it makes shifting and masking less painful
+ */
numblocks = mtd->size >> (this->bbt_erase_shift - 1);
startblock = 0;
from = 0;
} else {
if (chip >= this->numchips) {
- printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+ pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
chip + 1, this->numchips);
return -EINVAL;
}
from = (loff_t)startblock << (this->bbt_erase_shift - 1);
}
- if (this->options & NAND_BBT_SCANLASTPAGE)
- from += mtd->erasesize - (mtd->writesize * len);
+ if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
+ from += mtd->erasesize - (mtd->writesize * numpages);
for (i = startblock; i < numblocks;) {
int ret;
if (bd->options & NAND_BBT_SCANALLPAGES)
ret = scan_block_full(mtd, bd, from, buf, readlen,
- scanlen, len);
+ scanlen, numpages);
else
- ret = scan_block_fast(mtd, bd, from, buf, len);
+ ret = scan_block_fast(mtd, bd, from, buf, numpages);
if (ret < 0)
return ret;
if (ret) {
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
- MTDDEBUG(MTD_DEBUG_LEVEL0,
- "Bad eraseblock %d at 0x%012llx\n",
+ pr_warn("Bad eraseblock %d at 0x%012llx\n",
i >> 1, (unsigned long long)from);
mtd->ecc_stats.badblocks++;
}
/**
* search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
*
- * Read the bad block table by searching for a given ident pattern.
- * Search is preformed either from the beginning up or from the end of
- * the device downwards. The search starts always at the start of a
- * block.
- * If the option NAND_BBT_PERCHIP is given, each chip is searched
- * for a bbt, which contains the bad block information of this chip.
- * This is necessary to provide support for certain DOC devices.
+ * Read the bad block table by searching for a given ident pattern. Search is
+ * preformed either from the beginning up or from the end of the device
+ * downwards. The search starts always at the start of a block. If the option
+ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
+ * the bad block information of this chip. This is necessary to provide support
+ * for certain DOC devices.
*
- * The bbt ident pattern resides in the oob area of the first page
- * in a block.
+ * The bbt ident pattern resides in the oob area of the first page in a block.
*/
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
int bbtblocks;
int blocktopage = this->bbt_erase_shift - this->page_shift;
- /* Search direction top -> down ? */
+ /* Search direction top -> down? */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = (mtd->size >> this->bbt_erase_shift) - 1;
dir = -1;
dir = 1;
}
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
bbtblocks = this->chipsize >> this->bbt_erase_shift;
loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */
- scan_read_raw(mtd, buf, offs, mtd->writesize, td);
+ scan_read(mtd, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) {
/* Check, if we found a bbt for each requested chip */
for (i = 0; i < chips; i++) {
if (td->pages[i] == -1)
- printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
+ pr_warn("Bad block table not found for chip %d\n", i);
else
- MTDDEBUG(MTD_DEBUG_LEVEL0, "Bad block table found " \
- "at page %d, version 0x%02X\n", td->pages[i],
+ pr_info("Bad block table found at page %d, version 0x%02X\n", td->pages[i],
td->version[i]);
}
return 0;
/**
* search_read_bbts - [GENERIC] scan the device for bad block table(s)
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
*
- * Search and read the bad block table(s)
-*/
-static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+ * Search and read the bad block table(s).
+ */
+static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td,
+ struct nand_bbt_descr *md)
{
/* Search the primary table */
search_bbt(mtd, buf, td);
/* Search the mirror table */
if (md)
search_bbt(mtd, buf, md);
-
- /* Force result check */
- return 1;
}
/**
* write_bbt - [GENERIC] (Re)write the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
*
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @td: descriptor for the bad block table
- * @md: descriptor for the bad block table mirror
- * @chipsel: selector for a specific chip, -1 for all
- *
- * (Re)write the bad block table
- *
-*/
+ * (Re)write the bad block table.
+ */
static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
ops.ooblen = mtd->oobsize;
ops.ooboffs = 0;
ops.datbuf = NULL;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
if (!rcode)
rcode = 0xff;
- /* Write bad block table per chip rather than per device ? */
+ /* Write bad block table per chip rather than per device? */
if (td->options & NAND_BBT_PERCHIP) {
numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
- /* Full device write or specific chip ? */
+ /* Full device write or specific chip? */
if (chipsel == -1) {
nrchips = this->numchips;
} else {
/* Loop through the chips */
for (; chip < nrchips; chip++) {
-
- /* There was already a version of the table, reuse the page
+ /*
+ * There was already a version of the table, reuse the page
* This applies for absolute placement too, as we have the
* page nr. in td->pages.
*/
goto write;
}
- /* Automatic placement of the bad block table */
- /* Search direction top -> down ? */
+ /*
+ * Automatic placement of the bad block table. Search direction
+ * top -> down?
+ */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = numblocks * (chip + 1) - 1;
dir = -1;
if (!md || md->pages[chip] != page)
goto write;
}
- printk(KERN_ERR "No space left to write bad block table\n");
+ pr_err("No space left to write bad block table\n");
return -ENOSPC;
write:
bbtoffs = chip * (numblocks >> 2);
- to = ((loff_t) page) << this->page_shift;
+ to = ((loff_t)page) << this->page_shift;
- /* Must we save the block contents ? */
+ /* Must we save the block contents? */
if (td->options & NAND_BBT_SAVECONTENT) {
/* Make it block aligned */
- to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+ to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
len = 1 << this->bbt_erase_shift;
- res = mtd->read(mtd, to, len, &retlen, buf);
+ res = mtd_read(mtd, to, len, &retlen, buf);
if (res < 0) {
if (retlen != len) {
- printk(KERN_INFO "nand_bbt: Error "
- "reading block for writing "
- "the bad block table\n");
+ pr_info("nand_bbt: error reading block "
+ "for writing the bad block table\n");
return res;
}
- printk(KERN_WARNING "nand_bbt: ECC error "
- "while reading block for writing "
- "bad block table\n");
+ pr_warn("nand_bbt: ECC error while reading "
+ "block for writing bad block table\n");
}
/* Read oob data */
ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
ops.oobbuf = &buf[len];
- res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
+ res = mtd_read_oob(mtd, to + mtd->writesize, &ops);
if (res < 0 || ops.oobretlen != ops.ooblen)
goto outerr;
pageoffs = page - (int)(to >> this->page_shift);
offs = pageoffs << this->page_shift;
/* Preset the bbt area with 0xff */
- memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
+ memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
ooboffs = len + (pageoffs * mtd->oobsize);
} else if (td->options & NAND_BBT_NO_OOB) {
ooboffs = 0;
offs = td->len;
- /* the version byte */
+ /* The version byte */
if (td->options & NAND_BBT_VERSION)
offs++;
/* Calc length */
- len = (size_t) (numblocks >> sft);
+ len = (size_t)(numblocks >> sft);
len += offs;
- /* Make it page aligned ! */
+ /* Make it page aligned! */
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len);
memcpy(buf, td->pattern, td->len);
} else {
/* Calc length */
- len = (size_t) (numblocks >> sft);
- /* Make it page aligned ! */
+ len = (size_t)(numblocks >> sft);
+ /* Make it page aligned! */
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len +
if (td->options & NAND_BBT_VERSION)
buf[ooboffs + td->veroffs] = td->version[chip];
- /* walk through the memory table */
+ /* Walk through the memory table */
for (i = 0; i < numblocks;) {
uint8_t dat;
dat = this->bbt[bbtoffs + (i >> 2)];
for (j = 0; j < 4; j++, i++) {
int sftcnt = (i << (3 - sft)) & sftmsk;
- /* Do not store the reserved bbt blocks ! */
+ /* Do not store the reserved bbt blocks! */
buf[offs + (i >> sft)] &=
~(msk[dat & 0x03] << sftcnt);
dat >>= 2;
if (res < 0)
goto outerr;
- printk(KERN_DEBUG "Bad block table written to 0x%012llx, version "
- "0x%02X\n", (unsigned long long)to, td->version[chip]);
+ pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
+ (unsigned long long)to, td->version[chip]);
/* Mark it as used */
td->pages[chip] = page;
return 0;
outerr:
- printk(KERN_WARNING
- "nand_bbt: Error while writing bad block table %d\n", res);
+ pr_warn("nand_bbt: error while writing bad block table %d\n", res);
return res;
}
/**
* nand_memory_bbt - [GENERIC] create a memory based bad block table
- * @mtd: MTD device structure
- * @bd: descriptor for the good/bad block search pattern
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
*
- * The function creates a memory based bbt by scanning the device
- * for manufacturer / software marked good / bad blocks
-*/
+ * The function creates a memory based bbt by scanning the device for
+ * manufacturer / software marked good / bad blocks.
+ */
static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
/**
* check_create - [GENERIC] create and write bbt(s) if necessary
- * @mtd: MTD device structure
- * @buf: temporary buffer
- * @bd: descriptor for the good/bad block search pattern
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
*
- * The function checks the results of the previous call to read_bbt
- * and creates / updates the bbt(s) if necessary
- * Creation is necessary if no bbt was found for the chip/device
- * Update is necessary if one of the tables is missing or the
- * version nr. of one table is less than the other
-*/
+ * The function checks the results of the previous call to read_bbt and creates
+ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
+ * for the chip/device. Update is necessary if one of the tables is missing or
+ * the version nr. of one table is less than the other.
+ */
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
{
- int i, chips, writeops, chipsel, res;
+ int i, chips, writeops, create, chipsel, res, res2;
struct nand_chip *this = mtd->priv;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
struct nand_bbt_descr *rd, *rd2;
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP)
chips = this->numchips;
else
for (i = 0; i < chips; i++) {
writeops = 0;
+ create = 0;
rd = NULL;
rd2 = NULL;
- /* Per chip or per device ? */
+ res = res2 = 0;
+ /* Per chip or per device? */
chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
- /* Mirrored table available ? */
+ /* Mirrored table available? */
if (md) {
if (td->pages[i] == -1 && md->pages[i] == -1) {
+ create = 1;
writeops = 0x03;
- goto create;
- }
-
- if (td->pages[i] == -1) {
+ } else if (td->pages[i] == -1) {
rd = md;
- td->version[i] = md->version[i];
- writeops = 1;
- goto writecheck;
- }
-
- if (md->pages[i] == -1) {
+ writeops = 0x01;
+ } else if (md->pages[i] == -1) {
rd = td;
- md->version[i] = td->version[i];
- writeops = 2;
- goto writecheck;
- }
-
- if (td->version[i] == md->version[i]) {
+ writeops = 0x02;
+ } else if (td->version[i] == md->version[i]) {
rd = td;
if (!(td->options & NAND_BBT_VERSION))
rd2 = md;
- goto writecheck;
- }
-
- if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+ } else if (((int8_t)(td->version[i] - md->version[i])) > 0) {
rd = td;
- md->version[i] = td->version[i];
- writeops = 2;
+ writeops = 0x02;
} else {
rd = md;
- td->version[i] = md->version[i];
- writeops = 1;
+ writeops = 0x01;
}
-
- goto writecheck;
-
} else {
if (td->pages[i] == -1) {
+ create = 1;
writeops = 0x01;
- goto create;
+ } else {
+ rd = td;
}
- rd = td;
- goto writecheck;
}
- create:
- /* Create the bad block table by scanning the device ? */
- if (!(td->options & NAND_BBT_CREATE))
- continue;
- /* Create the table in memory by scanning the chip(s) */
- if (!(this->options & NAND_CREATE_EMPTY_BBT))
- create_bbt(mtd, buf, bd, chipsel);
-
- td->version[i] = 1;
- if (md)
- md->version[i] = 1;
- writecheck:
- /* read back first ? */
- if (rd)
- read_abs_bbt(mtd, buf, rd, chipsel);
- /* If they weren't versioned, read both. */
- if (rd2)
- read_abs_bbt(mtd, buf, rd2, chipsel);
-
- /* Write the bad block table to the device ? */
+ if (create) {
+ /* Create the bad block table by scanning the device? */
+ if (!(td->options & NAND_BBT_CREATE))
+ continue;
+
+ /* Create the table in memory by scanning the chip(s) */
+ if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
+ create_bbt(mtd, buf, bd, chipsel);
+
+ td->version[i] = 1;
+ if (md)
+ md->version[i] = 1;
+ }
+
+ /* Read back first? */
+ if (rd) {
+ res = read_abs_bbt(mtd, buf, rd, chipsel);
+ if (mtd_is_eccerr(res)) {
+ /* Mark table as invalid */
+ rd->pages[i] = -1;
+ rd->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+ /* If they weren't versioned, read both */
+ if (rd2) {
+ res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+ if (mtd_is_eccerr(res2)) {
+ /* Mark table as invalid */
+ rd2->pages[i] = -1;
+ rd2->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+
+ /* Scrub the flash table(s)? */
+ if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
+ writeops = 0x03;
+
+ /* Update version numbers before writing */
+ if (md) {
+ td->version[i] = max(td->version[i], md->version[i]);
+ md->version[i] = td->version[i];
+ }
+
+ /* Write the bad block table to the device? */
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, td, md, chipsel);
if (res < 0)
return res;
}
- /* Write the mirror bad block table to the device ? */
+ /* Write the mirror bad block table to the device? */
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, md, td, chipsel);
if (res < 0)
/**
* mark_bbt_regions - [GENERIC] mark the bad block table regions
- * @mtd: MTD device structure
- * @td: bad block table descriptor
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
*
- * The bad block table regions are marked as "bad" to prevent
- * accidental erasures / writes. The regions are identified by
- * the mark 0x02.
-*/
+ * The bad block table regions are marked as "bad" to prevent accidental
+ * erasures / writes. The regions are identified by the mark 0x02.
+ */
static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
{
struct nand_chip *this = mtd->priv;
int i, j, chips, block, nrblocks, update;
uint8_t oldval, newval;
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
update = 1;
block += 2;
}
- /* If we want reserved blocks to be recorded to flash, and some
- new ones have been marked, then we need to update the stored
- bbts. This should only happen once. */
+ /*
+ * If we want reserved blocks to be recorded to flash, and some
+ * new ones have been marked, then we need to update the stored
+ * bbts. This should only happen once.
+ */
if (update && td->reserved_block_code)
nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1));
}
/**
* verify_bbt_descr - verify the bad block description
- * @mtd: MTD device structure
- * @bd: the table to verify
+ * @mtd: MTD device structure
+ * @bd: the table to verify
*
* This functions performs a few sanity checks on the bad block description
* table.
pattern_len = bd->len;
bits = bd->options & NAND_BBT_NRBITS_MSK;
- BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
- !(this->options & NAND_USE_FLASH_BBT));
+ BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
+ !(this->bbt_options & NAND_BBT_USE_FLASH));
BUG_ON(!bits);
if (bd->options & NAND_BBT_VERSION)
pattern_len++;
if (bd->options & NAND_BBT_NO_OOB) {
- BUG_ON(!(this->options & NAND_USE_FLASH_BBT));
- BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB));
+ BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
+ BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
BUG_ON(bd->offs);
if (bd->options & NAND_BBT_VERSION)
BUG_ON(bd->veroffs != bd->len);
/**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
- * @mtd: MTD device structure
- * @bd: descriptor for the good/bad block search pattern
- *
- * The function checks, if a bad block table(s) is/are already
- * available. If not it scans the device for manufacturer
- * marked good / bad blocks and writes the bad block table(s) to
- * the selected place.
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
*
- * The bad block table memory is allocated here. It must be freed
- * by calling the nand_free_bbt function.
+ * The function checks, if a bad block table(s) is/are already available. If
+ * not it scans the device for manufacturer marked good / bad blocks and writes
+ * the bad block table(s) to the selected place.
*
-*/
+ * The bad block table memory is allocated here. It must be freed by calling
+ * the nand_free_bbt function.
+ */
int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
struct nand_bbt_descr *md = this->bbt_md;
len = mtd->size >> (this->bbt_erase_shift + 2);
- /* Allocate memory (2bit per block) and clear the memory bad block table */
+ /*
+ * Allocate memory (2bit per block) and clear the memory bad block
+ * table.
+ */
this->bbt = kzalloc(len, GFP_KERNEL);
- if (!this->bbt) {
- printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
+ if (!this->bbt)
return -ENOMEM;
- }
- /* If no primary table decriptor is given, scan the device
- * to build a memory based bad block table
+ /*
+ * If no primary table decriptor is given, scan the device to build a
+ * memory based bad block table.
*/
if (!td) {
if ((res = nand_memory_bbt(mtd, bd))) {
- printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
+ pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
kfree(this->bbt);
this->bbt = NULL;
}
len += (len >> this->page_shift) * mtd->oobsize;
buf = vmalloc(len);
if (!buf) {
- printk(KERN_ERR "nand_bbt: Out of memory\n");
kfree(this->bbt);
this->bbt = NULL;
return -ENOMEM;
}
- /* Is the bbt at a given page ? */
+ /* Is the bbt at a given page? */
if (td->options & NAND_BBT_ABSPAGE) {
- res = read_abs_bbts(mtd, buf, td, md);
+ read_abs_bbts(mtd, buf, td, md);
} else {
/* Search the bad block table using a pattern in oob */
- res = search_read_bbts(mtd, buf, td, md);
+ search_read_bbts(mtd, buf, td, md);
}
- if (res)
- res = check_create(mtd, buf, bd);
+ res = check_create(mtd, buf, bd);
/* Prevent the bbt regions from erasing / writing */
mark_bbt_region(mtd, td);
/**
* nand_update_bbt - [NAND Interface] update bad block table(s)
- * @mtd: MTD device structure
- * @offs: the offset of the newly marked block
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
*
- * The function updates the bad block table(s)
-*/
+ * The function updates the bad block table(s).
+ */
int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
{
struct nand_chip *this = mtd->priv;
- int len, res = 0, writeops = 0;
+ int len, res = 0;
int chip, chipsel;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize;
buf = kmalloc(len, GFP_KERNEL);
- if (!buf) {
- printk(KERN_ERR "nand_update_bbt: Out of memory\n");
+ if (!buf)
return -ENOMEM;
- }
- writeops = md != NULL ? 0x03 : 0x01;
-
- /* Do we have a bbt per chip ? */
+ /* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chip = (int)(offs >> this->chip_shift);
chipsel = chip;
if (md)
md->version[chip]++;
- /* Write the bad block table to the device ? */
- if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+ /* Write the bad block table to the device? */
+ if (td->options & NAND_BBT_WRITE) {
res = write_bbt(mtd, buf, td, md, chipsel);
if (res < 0)
goto out;
}
- /* Write the mirror bad block table to the device ? */
- if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+ /* Write the mirror bad block table to the device? */
+ if (md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt(mtd, buf, md, td, chipsel);
}
return res;
}
-/* Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks. */
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
.pattern = scan_agand_pattern
};
-/* Generic flash bbt decriptors
-*/
+/* Generic flash bbt descriptors */
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
.offs = 8,
.len = 4,
.veroffs = 12,
- .maxblocks = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = bbt_pattern
};
.offs = 8,
.len = 4,
.veroffs = 12,
- .maxblocks = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = mirror_pattern
};
-static struct nand_bbt_descr bbt_main_no_bbt_descr = {
+static struct nand_bbt_descr bbt_main_no_oob_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB,
.len = 4,
.veroffs = 4,
- .maxblocks = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = bbt_pattern
};
-static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
+static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB,
.len = 4,
.veroffs = 4,
- .maxblocks = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
.pattern = mirror_pattern
};
-#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
- NAND_BBT_SCANBYTE1AND6)
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
/**
- * nand_create_default_bbt_descr - [Internal] Creates a BBT descriptor structure
- * @this: NAND chip to create descriptor for
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: NAND chip to create descriptor for
*
* This function allocates and initializes a nand_bbt_descr for BBM detection
- * based on the properties of "this". The new descriptor is stored in
+ * based on the properties of @this. The new descriptor is stored in
* this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
* passed to this function.
- *
*/
-static int nand_create_default_bbt_descr(struct nand_chip *this)
+static int nand_create_badblock_pattern(struct nand_chip *this)
{
struct nand_bbt_descr *bd;
if (this->badblock_pattern) {
- printk(KERN_WARNING "BBT descr already allocated; not replacing.\n");
+ pr_warn("Bad block pattern already allocated; not replacing\n");
return -EINVAL;
}
bd = kzalloc(sizeof(*bd), GFP_KERNEL);
- if (!bd) {
- printk(KERN_ERR "nand_create_default_bbt_descr: Out of memory\n");
+ if (!bd)
return -ENOMEM;
- }
- bd->options = this->options & BBT_SCAN_OPTIONS;
+ bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
bd->offs = this->badblockpos;
bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
bd->pattern = scan_ff_pattern;
/**
* nand_default_bbt - [NAND Interface] Select a default bad block table for the device
- * @mtd: MTD device structure
- *
- * This function selects the default bad block table
- * support for the device and calls the nand_scan_bbt function
+ * @mtd: MTD device structure
*
-*/
+ * This function selects the default bad block table support for the device and
+ * calls the nand_scan_bbt function.
+ */
int nand_default_bbt(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
- /* Default for AG-AND. We must use a flash based
- * bad block table as the devices have factory marked
- * _good_ blocks. Erasing those blocks leads to loss
- * of the good / bad information, so we _must_ store
- * this information in a good / bad table during
- * startup
+ /*
+ * Default for AG-AND. We must use a flash based bad block table as the
+ * devices have factory marked _good_ blocks. Erasing those blocks
+ * leads to loss of the good / bad information, so we _must_ store this
+ * information in a good / bad table during startup.
*/
if (this->options & NAND_IS_AND) {
/* Use the default pattern descriptors */
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
- this->options |= NAND_USE_FLASH_BBT;
+ this->bbt_options |= NAND_BBT_USE_FLASH;
return nand_scan_bbt(mtd, &agand_flashbased);
}
- /* Is a flash based bad block table requested ? */
- if (this->options & NAND_USE_FLASH_BBT) {
+ /* Is a flash based bad block table requested? */
+ if (this->bbt_options & NAND_BBT_USE_FLASH) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
- if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
- this->bbt_td = &bbt_main_no_bbt_descr;
- this->bbt_md = &bbt_mirror_no_bbt_descr;
+ if (this->bbt_options & NAND_BBT_NO_OOB) {
+ this->bbt_td = &bbt_main_no_oob_descr;
+ this->bbt_md = &bbt_mirror_no_oob_descr;
} else {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
if (!this->badblock_pattern)
- nand_create_default_bbt_descr(this);
+ nand_create_badblock_pattern(this);
return nand_scan_bbt(mtd, this->badblock_pattern);
}
/**
* nand_isbad_bbt - [NAND Interface] Check if a block is bad
- * @mtd: MTD device structure
- * @offs: offset in the device
- * @allowbbt: allow access to bad block table region
- *
-*/
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ */
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
{
struct nand_chip *this = mtd->priv;
* These are the new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes
*/
-#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
+#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
- /*512 Megabit */
+ /* 512 Megabit */
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
+ {"NAND 64MiB 3,3V 8-bit", 0xF0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
* writes possible, but not implemented now
*/
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
- NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |
- BBT_AUTO_REFRESH
- },
+ NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
{NULL,}
};
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
- {NAND_MFR_AMD, "AMD"},
+ {NAND_MFR_AMD, "AMD/Spansion"},
+ {NAND_MFR_MACRONIX, "Macronix"},
+ {NAND_MFR_EON, "Eon"},
{0x0, "Unknown"}
};
+
WATCHDOG_RESET();
if (!opts->scrub && bbtest) {
- int ret = meminfo->block_isbad(meminfo, erase.addr);
+ int ret = mtd_block_isbad(meminfo, erase.addr);
if (ret > 0) {
if (!opts->quiet)
printf("\rSkipping bad block at "
erased_length++;
- result = meminfo->erase(meminfo, &erase);
+ result = mtd_erase(meminfo, &erase);
if (result != 0) {
printf("\n%s: MTD Erase failure: %d\n",
mtd_device, result);
/* format for JFFS2 ? */
if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) {
- chip->ops.ooblen = 8;
- chip->ops.datbuf = NULL;
- chip->ops.oobbuf = (uint8_t *)&cleanmarker;
- chip->ops.ooboffs = 0;
- chip->ops.mode = MTD_OOB_AUTO;
+ struct mtd_oob_ops ops;
+ ops.ooblen = 8;
+ ops.datbuf = NULL;
+ ops.oobbuf = (uint8_t *)&cleanmarker;
+ ops.ooboffs = 0;
+ ops.mode = MTD_OPS_AUTO_OOB;
- result = meminfo->write_oob(meminfo,
+ result = mtd_write_oob(meminfo,
erase.addr,
- &chip->ops);
+ &ops);
if (result != 0) {
printf("\n%s: MTD writeoob failure: %d\n",
mtd_device, result);
static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
const size_t *len)
{
- size_t i, l = *len;
+ size_t l = *len;
+ ssize_t i;
for (i = l - 1; i >= 0; i--)
if (buf[i] != 0xFF)
ops.len = pagesize;
ops.ooblen = nand->oobsize;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
ops.datbuf = p_buffer;
ops.oobbuf = ops.datbuf + pagesize;
- rval = nand->write_oob(nand, offset, &ops);
+ rval = mtd_write_oob(nand, offset, &ops);
if (rval != 0)
break;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = 256;
nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
nand->select_chip = ndfc_select_chip;
#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.bytes = 3;
chip->ecc.size = 512;
+ chip->ecc.strength = 1;
chip->ecc.layout = &nomadik_ecc_layout;
chip->ecc.calculate = nomadik_ecc_calculate;
chip->ecc.hwctl = nomadik_ecc_hwctl;
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
* @page: page number to read
*
*/
static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page)
+ uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
nand->ecc.hwctl = NULL;
nand->ecc.correct = NULL;
nand->ecc.calculate = NULL;
+ nand->ecc.strength = eccstrength;
/* Setup the ecc configurations again */
if (hardware) {
nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
nand->cmd_ctrl = omap_nand_hwcontrol;
- nand->options = NAND_NO_PADDING | NAND_CACHEPRG | NAND_NO_AUTOINCR;
+ nand->options = NAND_NO_PADDING | NAND_CACHEPRG;
/* If we are 16 bit dev, our gpmc config tells us that */
if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000)
nand->options |= NAND_BUSWIDTH_16;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
+ nand->ecc.strength = 1;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
* -EIO when command timeout
*/
static int nand_read_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int page)
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
return nand_rw_page(mtd, chip, buf, page, 1, 0);
}
* @param chip nand chip info structure
* @param buf data buffer
*/
-static void nand_write_page_hwecc(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf)
+static int nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
int page;
struct nand_drv *info;
(readl(&info->reg->addr_reg2) << 16);
nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
+ return 0;
}
* -EIO when command timeout
*/
static int nand_read_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf, int page)
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
return nand_rw_page(mtd, chip, buf, page, 0, 0);
}
* @param chip nand chip info structure
* @param buf data buffer
*/
-static void nand_write_page_raw(struct mtd_info *mtd,
- struct nand_chip *chip, const uint8_t *buf)
+static int nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
int page;
struct nand_drv *info;
(readl(&info->reg->addr_reg2) << 16);
nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1);
+ return 0;
}
/**
* @param mtd mtd info structure
* @param chip nand chip info structure
* @param page page number to read
- * @param sndcmd flag whether to issue read command or not
- * @return 1 - issue read command next time
- * 0 - not to issue
*/
static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page, int sndcmd)
+ int page)
{
- if (sndcmd) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- sndcmd = 0;
- }
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
nand_rw_oob(mtd, chip, page, 0, 0);
- return sndcmd;
+ return 0;
}
/**
nand->ecc.write_page_raw = nand_write_page_raw;
nand->ecc.read_oob = nand_read_oob;
nand->ecc.write_oob = nand_write_oob;
+ nand->ecc.strength = 1;
nand->select_chip = nand_select_chip;
nand->dev_ready = nand_dev_ready;
nand->priv = &nand_ctrl;
#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8)
#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF
-#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR)
+#define LP_OPTIONS 0
struct nand_ctlr {
u32 command; /* offset 00h */
}
/**
- * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
* @param mtd MTD device structure
* @param buf destination address
* @param column oob offset to read from
return status;
/* check if we failed due to uncorrectable error */
- if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR)
+ if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
return status;
/* check if address lies in MLC region */
MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
thisooblen = oobsize - oobcolumn;
thisooblen = min_t(int, thisooblen, ooblen - oobread);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
else
this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
if (unlikely(ret))
ret = onenand_recover_lsb(mtd, from, ret);
onenand_update_bufferram(mtd, from, !ret);
- if (ret == -EBADMSG)
+ if (mtd_is_eccerr(ret))
ret = 0;
}
/* Now wait for load */
ret = this->wait(mtd, FL_READING);
onenand_update_bufferram(mtd, from, !ret);
- if (ret == -EBADMSG)
+ if (mtd_is_eccerr(ret))
ret = 0;
}
}
struct mtd_ecc_stats stats;
int read = 0, thislen, column, oobsize;
size_t len = ops->ooblen;
- mtd_oob_mode_t mode = ops->mode;
+ unsigned int mode = ops->mode;
u_char *buf = ops->oobbuf;
int ret = 0, readcmd;
/* Initialize return length value */
ops->oobretlen = 0;
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
break;
}
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
onenand_transfer_auto_oob(mtd, buf, column, thislen);
else
this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
int ret;
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
break;
- case MTD_OOB_RAW:
+ case MTD_OPS_RAW:
/* Not implemented yet */
default:
return -EINVAL;
#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0)
/**
- * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
* @param mtd MTD device structure
* @param oob_buf oob buffer
* @param buf source address
ops->retlen = 0;
ops->oobretlen = 0;
- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
- return -EINVAL;
- }
-
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
return -EINVAL;
}
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
/* We send data to spare ram with oobsize
* * to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
- if (ops->mode == MTD_OOB_AUTO)
+ if (ops->mode == MTD_OPS_AUTO_OOB)
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob, thisooblen);
}
/**
- * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
+ * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param len number of bytes to write
u_char *oobbuf;
size_t len = ops->ooblen;
const u_char *buf = ops->oobbuf;
- mtd_oob_mode_t mode = ops->mode;
+ unsigned int mode = ops->mode;
to += ops->ooboffs;
/* Initialize retlen, in case of early exit */
ops->oobretlen = 0;
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
- if (mode == MTD_OOB_AUTO)
+ if (mode == MTD_OPS_AUTO_OOB)
onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
else
memcpy(oobbuf + column, buf, thislen);
int ret;
switch (ops->mode) {
- case MTD_OOB_PLACE:
- case MTD_OOB_AUTO:
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
break;
- case MTD_OOB_RAW:
+ case MTD_OPS_RAW:
/* Not implemented yet */
default:
return -EINVAL;
MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
(unsigned int) addr, len);
- /* Do not allow erase past end of device */
- if (unlikely((len + addr) > mtd->size)) {
- MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
- "Erase past end of device\n");
- return -EINVAL;
- }
-
if (FLEXONENAND(this)) {
/* Find the eraseregion of this address */
i = flexonenand_region(mtd, addr);
return -EINVAL;
}
- instr->fail_addr = 0xffffffff;
-
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_ERASING);
struct bbm_info *bbm = this->bbm;
u_char buf[2] = {0, 0};
struct mtd_oob_ops ops = {
- .mode = MTD_OOB_PLACE,
+ .mode = MTD_OPS_PLACE_OOB,
.ooblen = 2,
.oobbuf = buf,
.ooboffs = 0,
*/
int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
- struct onenand_chip *this = mtd->priv;
int ret;
ret = onenand_block_isbad(mtd, ofs);
return ret;
}
- ret = this->block_markbad(mtd, ofs);
+ ret = mtd_block_markbad(mtd, ofs);
return ret;
}
int i, ret;
int block;
struct mtd_oob_ops ops = {
- .mode = MTD_OOB_PLACE,
+ .mode = MTD_OPS_PLACE_OOB,
.ooboffs = 0,
.ooblen = mtd->oobsize,
.datbuf = NULL,
mtd->size = this->chipsize;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->erase = onenand_erase;
- mtd->read = onenand_read;
- mtd->write = onenand_write;
- mtd->read_oob = onenand_read_oob;
- mtd->write_oob = onenand_write_oob;
- mtd->sync = onenand_sync;
- mtd->block_isbad = onenand_block_isbad;
- mtd->block_markbad = onenand_block_markbad;
+ mtd->_erase = onenand_erase;
+ mtd->_read = onenand_read;
+ mtd->_write = onenand_write;
+ mtd->_read_oob = onenand_read_oob;
+ mtd->_write_oob = onenand_write_oob;
+ mtd->_sync = onenand_sync;
+ mtd->_block_isbad = onenand_block_isbad;
+ mtd->_block_markbad = onenand_block_markbad;
return 0;
}
startblock = 0;
from = 0;
- ops.mode = MTD_OOB_PLACE;
+ ops.mode = MTD_OPS_PLACE_OOB;
ops.ooblen = readlen;
ops.oobbuf = buf;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
len = this->chipsize >> (this->erase_shift + 2);
/* Allocate memory (2bit per block) */
bbm->bbt = malloc(len);
- if (!bbm->bbt) {
- printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
+ if (!bbm->bbt)
return -ENOMEM;
- }
/* Clear the memory bad block table */
memset(bbm->bbt, 0x00, len);
ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd);
ubi->flash_size = ubi->mtd->size;
- if (ubi->mtd->block_isbad && ubi->mtd->block_markbad)
+ if (mtd_can_have_bb(ubi->mtd))
ubi->bad_allowed = 1;
ubi->min_io_size = ubi->mtd->writesize;
if (err == UBI_IO_BITFLIPS) {
scrub = 1;
err = 0;
- } else if (err == -EBADMSG) {
+ } else if (mtd_is_eccerr(err)) {
if (vol->vol_type == UBI_DYNAMIC_VOLUME)
goto out_unlock;
scrub = 1;
addr = (loff_t)pnum * ubi->peb_size + offset;
retry:
- err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
+ err = mtd_read(ubi->mtd, addr, len, &read, buf);
if (err) {
if (err == -EUCLEAN) {
/*
}
addr = (loff_t)pnum * ubi->peb_size + offset;
- err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf);
+ err = mtd_write(ubi->mtd, addr, len, &written, buf);
if (err) {
ubi_err("error %d while writing %d bytes to PEB %d:%d, written"
" %zd bytes", err, len, pnum, offset, written);
ei.callback = erase_callback;
ei.priv = (unsigned long)&wq;
- err = ubi->mtd->erase(ubi->mtd, &ei);
+ err = mtd_erase(ubi->mtd, &ei);
if (err) {
if (retries++ < UBI_IO_RETRIES) {
dbg_io("error %d while erasing PEB %d, retry",
if (ubi->bad_allowed) {
int ret;
- ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size);
+ ret = mtd_block_isbad(mtd, (loff_t)pnum * ubi->peb_size);
if (ret < 0)
ubi_err("error %d while checking if PEB %d is bad",
ret, pnum);
if (!ubi->bad_allowed)
return 0;
- err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size);
+ err = mtd_block_markbad(mtd, (loff_t)pnum * ubi->peb_size);
if (err)
ubi_err("cannot mark PEB %d bad, error %d", pnum, err);
return err;
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
mutex_lock(&ubi->dbg_buf_mutex);
- err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf);
+ err = mtd_read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf);
if (err && err != -EUCLEAN) {
ubi_err("error %d while reading %d bytes from PEB %d:%d, "
"read %zd bytes", err, len, pnum, offset, read);
return 0;
err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check);
- if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) {
+ if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) {
ubi_warn("mark volume %d as corrupted", vol_id);
vol->corrupted = 1;
}
err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1);
if (err) {
- if (err == -EBADMSG)
+ if (mtd_is_eccerr(err))
err = 1;
break;
}
err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
ubi->vtbl_size);
- if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
+ if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err))
/*
* Scrub the PEB later. Note, -EBADMSG indicates an
* uncorrectable ECC error, but we have our own CRC and
$(shell mkdir -p $(obj)slb9635_i2c)
-COBJS-$(CONFIG_GENERIC_LPC_TPM) = generic_lpc_tpm.o
-COBJS-$(CONFIG_INFINEON_TPM_I2C) += tis_i2c.o slb9635_i2c/tpm.o
-COBJS-$(CONFIG_INFINEON_TPM_I2C) += slb9635_i2c/tpm_tis_i2c.o
+# TODO: Merge tpm_tis_lpc.c with tpm.c
+COBJS-$(CONFIG_TPM_TIS_I2C) += tpm.o
+COBJS-$(CONFIG_TPM_TIS_I2C) += tpm_tis_i2c.o
+COBJS-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
+++ /dev/null
-/*
- * Copyright (c) 2011 The Chromium OS Authors.
- *
- * 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
- */
-
-/*
- * The code in this file is based on the article "Writing a TPM Device Driver"
- * published on http://ptgmedia.pearsoncmg.com.
- *
- * One principal difference is that in the simplest config the other than 0
- * TPM localities do not get mapped by some devices (for instance, by Infineon
- * slb9635), so this driver provides access to locality 0 only.
- */
-
-#include <common.h>
-#include <asm/io.h>
-#include <tpm.h>
-
-#define PREFIX "lpc_tpm: "
-
-struct tpm_locality {
- u32 access;
- u8 padding0[4];
- u32 int_enable;
- u8 vector;
- u8 padding1[3];
- u32 int_status;
- u32 int_capability;
- u32 tpm_status;
- u8 padding2[8];
- u8 data;
- u8 padding3[3803];
- u32 did_vid;
- u8 rid;
- u8 padding4[251];
-};
-
-/*
- * This pointer refers to the TPM chip, 5 of its localities are mapped as an
- * array.
- */
-#define TPM_TOTAL_LOCALITIES 5
-static struct tpm_locality *lpc_tpm_dev =
- (struct tpm_locality *)CONFIG_TPM_TIS_BASE_ADDRESS;
-
-/* Some registers' bit field definitions */
-#define TIS_STS_VALID (1 << 7) /* 0x80 */
-#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
-#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
-#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
-#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
-#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
-
-#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
-#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
-#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
-#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
-#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
-#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
-#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
-
-#define TIS_STS_BURST_COUNT_MASK (0xffff)
-#define TIS_STS_BURST_COUNT_SHIFT (8)
-
-/*
- * Error value returned if a tpm register does not enter the expected state
- * after continuous polling. No actual TPM register reading ever returns -1,
- * so this value is a safe error indication to be mixed with possible status
- * register values.
- */
-#define TPM_TIMEOUT_ERR (-1)
-
-/* Error value returned on various TPM driver errors. */
-#define TPM_DRIVER_ERR (1)
-
- /* 1 second is plenty for anything TPM does. */
-#define MAX_DELAY_US (1000 * 1000)
-
-/* Retrieve burst count value out of the status register contents. */
-static u16 burst_count(u32 status)
-{
- return (status >> TIS_STS_BURST_COUNT_SHIFT) & TIS_STS_BURST_COUNT_MASK;
-}
-
-/*
- * Structures defined below allow creating descriptions of TPM vendor/device
- * ID information for run time discovery. The only device the system knows
- * about at this time is Infineon slb9635.
- */
-struct device_name {
- u16 dev_id;
- const char * const dev_name;
-};
-
-struct vendor_name {
- u16 vendor_id;
- const char *vendor_name;
- const struct device_name *dev_names;
-};
-
-static const struct device_name infineon_devices[] = {
- {0xb, "SLB9635 TT 1.2"},
- {0}
-};
-
-static const struct vendor_name vendor_names[] = {
- {0x15d1, "Infineon", infineon_devices},
-};
-
-/*
- * Cached vendor/device ID pair to indicate that the device has been already
- * discovered.
- */
-static u32 vendor_dev_id;
-
-/* TPM access wrappers to support tracing */
-static u8 tpm_read_byte(const u8 *ptr)
-{
- u8 ret = readb(ptr);
- debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
- (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret);
- return ret;
-}
-
-static u32 tpm_read_word(const u32 *ptr)
-{
- u32 ret = readl(ptr);
- debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
- (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret);
- return ret;
-}
-
-static void tpm_write_byte(u8 value, u8 *ptr)
-{
- debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
- (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value);
- writeb(value, ptr);
-}
-
-static void tpm_write_word(u32 value, u32 *ptr)
-{
- debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
- (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value);
- writel(value, ptr);
-}
-
-/*
- * tis_wait_reg()
- *
- * Wait for at least a second for a register to change its state to match the
- * expected state. Normally the transition happens within microseconds.
- *
- * @reg - pointer to the TPM register
- * @mask - bitmask for the bitfield(s) to watch
- * @expected - value the field(s) are supposed to be set to
- *
- * Returns the register contents in case the expected value was found in the
- * appropriate register bits, or TPM_TIMEOUT_ERR on timeout.
- */
-static u32 tis_wait_reg(u32 *reg, u8 mask, u8 expected)
-{
- u32 time_us = MAX_DELAY_US;
-
- while (time_us > 0) {
- u32 value = tpm_read_word(reg);
- if ((value & mask) == expected)
- return value;
- udelay(1); /* 1 us */
- time_us--;
- }
- return TPM_TIMEOUT_ERR;
-}
-
-/*
- * Probe the TPM device and try determining its manufacturer/device name.
- *
- * Returns 0 on success (the device is found or was found during an earlier
- * invocation) or TPM_DRIVER_ERR if the device is not found.
- */
-int tis_init(void)
-{
- u32 didvid = tpm_read_word(&lpc_tpm_dev[0].did_vid);
- int i;
- const char *device_name = "unknown";
- const char *vendor_name = device_name;
- u16 vid, did;
-
- if (vendor_dev_id)
- return 0; /* Already probed. */
-
- if (!didvid || (didvid == 0xffffffff)) {
- printf("%s: No TPM device found\n", __func__);
- return TPM_DRIVER_ERR;
- }
-
- vendor_dev_id = didvid;
-
- vid = didvid & 0xffff;
- did = (didvid >> 16) & 0xffff;
- for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
- int j = 0;
- u16 known_did;
-
- if (vid == vendor_names[i].vendor_id)
- vendor_name = vendor_names[i].vendor_name;
-
- while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
- if (known_did == did) {
- device_name =
- vendor_names[i].dev_names[j].dev_name;
- break;
- }
- j++;
- }
- break;
- }
-
- printf("Found TPM %s by %s\n", device_name, vendor_name);
- return 0;
-}
-
-/*
- * tis_senddata()
- *
- * send the passed in data to the TPM device.
- *
- * @data - address of the data to send, byte by byte
- * @len - length of the data to send
- *
- * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
- * not accept the entire command).
- */
-static u32 tis_senddata(const u8 * const data, u32 len)
-{
- u32 offset = 0;
- u16 burst = 0;
- u32 max_cycles = 0;
- u8 locality = 0;
- u32 value;
-
- value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
- TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
- if (value == TPM_TIMEOUT_ERR) {
- printf("%s:%d - failed to get 'command_ready' status\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
- burst = burst_count(value);
-
- while (1) {
- unsigned count;
-
- /* Wait till the device is ready to accept more data. */
- while (!burst) {
- if (max_cycles++ == MAX_DELAY_US) {
- printf("%s:%d failed to feed %d bytes of %d\n",
- __FILE__, __LINE__, len - offset, len);
- return TPM_DRIVER_ERR;
- }
- udelay(1);
- burst = burst_count(tpm_read_word(&lpc_tpm_dev
- [locality].tpm_status));
- }
-
- max_cycles = 0;
-
- /*
- * Calculate number of bytes the TPM is ready to accept in one
- * shot.
- *
- * We want to send the last byte outside of the loop (hence
- * the -1 below) to make sure that the 'expected' status bit
- * changes to zero exactly after the last byte is fed into the
- * FIFO.
- */
- count = min(burst, len - offset - 1);
- while (count--)
- tpm_write_byte(data[offset++],
- &lpc_tpm_dev[locality].data);
-
- value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
- TIS_STS_VALID, TIS_STS_VALID);
-
- if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) {
- printf("%s:%d TPM command feed overflow\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- burst = burst_count(value);
- if ((offset == (len - 1)) && burst) {
- /*
- * We need to be able to send the last byte to the
- * device, so burst size must be nonzero before we
- * break out.
- */
- break;
- }
- }
-
- /* Send the last byte. */
- tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data);
- /*
- * Verify that TPM does not expect any more data as part of this
- * command.
- */
- value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
- TIS_STS_VALID, TIS_STS_VALID);
- if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) {
- printf("%s:%d unexpected TPM status 0x%x\n",
- __FILE__, __LINE__, value);
- return TPM_DRIVER_ERR;
- }
-
- /* OK, sitting pretty, let's start the command execution. */
- tpm_write_word(TIS_STS_TPM_GO, &lpc_tpm_dev[locality].tpm_status);
- return 0;
-}
-
-/*
- * tis_readresponse()
- *
- * read the TPM device response after a command was issued.
- *
- * @buffer - address where to read the response, byte by byte.
- * @len - pointer to the size of buffer
- *
- * On success stores the number of received bytes to len and returns 0. On
- * errors (misformatted TPM data or synchronization problems) returns
- * TPM_DRIVER_ERR.
- */
-static u32 tis_readresponse(u8 *buffer, u32 *len)
-{
- u16 burst;
- u32 value;
- u32 offset = 0;
- u8 locality = 0;
- const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
- u32 expected_count = *len;
- int max_cycles = 0;
-
- /* Wait for the TPM to process the command. */
- value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
- has_data, has_data);
- if (value == TPM_TIMEOUT_ERR) {
- printf("%s:%d failed processing command\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- do {
- while ((burst = burst_count(value)) == 0) {
- if (max_cycles++ == MAX_DELAY_US) {
- printf("%s:%d TPM stuck on read\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
- udelay(1);
- value = tpm_read_word(&lpc_tpm_dev
- [locality].tpm_status);
- }
-
- max_cycles = 0;
-
- while (burst-- && (offset < expected_count)) {
- buffer[offset++] = tpm_read_byte(&lpc_tpm_dev
- [locality].data);
-
- if (offset == 6) {
- /*
- * We got the first six bytes of the reply,
- * let's figure out how many bytes to expect
- * total - it is stored as a 4 byte number in
- * network order, starting with offset 2 into
- * the body of the reply.
- */
- u32 real_length;
- memcpy(&real_length,
- buffer + 2,
- sizeof(real_length));
- expected_count = be32_to_cpu(real_length);
-
- if ((expected_count < offset) ||
- (expected_count > *len)) {
- printf("%s:%d bad response size %d\n",
- __FILE__, __LINE__,
- expected_count);
- return TPM_DRIVER_ERR;
- }
- }
- }
-
- /* Wait for the next portion. */
- value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
- TIS_STS_VALID, TIS_STS_VALID);
- if (value == TPM_TIMEOUT_ERR) {
- printf("%s:%d failed to read response\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- if (offset == expected_count)
- break; /* We got all we needed. */
-
- } while ((value & has_data) == has_data);
-
- /*
- * Make sure we indeed read all there was. The TIS_STS_VALID bit is
- * known to be set.
- */
- if (value & TIS_STS_DATA_AVAILABLE) {
- printf("%s:%d wrong receive status %x\n",
- __FILE__, __LINE__, value);
- return TPM_DRIVER_ERR;
- }
-
- /* Tell the TPM that we are done. */
- tpm_write_word(TIS_STS_COMMAND_READY, &lpc_tpm_dev
- [locality].tpm_status);
- *len = offset;
- return 0;
-}
-
-int tis_open(void)
-{
- u8 locality = 0; /* we use locality zero for everything. */
-
- if (tis_close())
- return TPM_DRIVER_ERR;
-
- /* now request access to locality. */
- tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access);
-
- /* did we get a lock? */
- if (tis_wait_reg(&lpc_tpm_dev[locality].access,
- TIS_ACCESS_ACTIVE_LOCALITY,
- TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) {
- printf("%s:%d - failed to lock locality %d\n",
- __FILE__, __LINE__, locality);
- return TPM_DRIVER_ERR;
- }
-
- tpm_write_word(TIS_STS_COMMAND_READY,
- &lpc_tpm_dev[locality].tpm_status);
- return 0;
-}
-
-int tis_close(void)
-{
- u8 locality = 0;
-
- if (tpm_read_word(&lpc_tpm_dev[locality].access) &
- TIS_ACCESS_ACTIVE_LOCALITY) {
- tpm_write_word(TIS_ACCESS_ACTIVE_LOCALITY,
- &lpc_tpm_dev[locality].access);
-
- if (tis_wait_reg(&lpc_tpm_dev[locality].access,
- TIS_ACCESS_ACTIVE_LOCALITY, 0) ==
- TPM_TIMEOUT_ERR) {
- printf("%s:%d - failed to release locality %d\n",
- __FILE__, __LINE__, locality);
- return TPM_DRIVER_ERR;
- }
- }
- return 0;
-}
-
-int tis_sendrecv(const u8 *sendbuf, size_t send_size,
- u8 *recvbuf, size_t *recv_len)
-{
- if (tis_senddata(sendbuf, send_size)) {
- printf("%s:%d failed sending data to TPM\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- return tis_readresponse(recvbuf, (u32 *)recv_len);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@infineon.com>
- *
- * Version: 2.1.1
- *
- * 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
- */
-
-#ifndef _COMPATIBILITY_H_
-#define _COMPATIBILITY_H_
-
-/* all includes from U-Boot */
-#include <linux/types.h>
-#include <linux/unaligned/be_byteshift.h>
-#include <asm-generic/errno.h>
-#include <compiler.h>
-#include <common.h>
-
-/* extended error numbers from linux (see errno.h) */
-#define ECANCELED 125 /* Operation Canceled */
-
-#define msleep(t) udelay((t)*1000)
-
-/* Timer frequency. Corresponds to msec timer resolution*/
-#define HZ 1000
-
-#define dev_dbg(dev, format, arg...) debug(format, ##arg)
-#define dev_err(dev, format, arg...) printf(format, ##arg)
-#define dev_info(dev, format, arg...) debug(format, ##arg)
-#define dbg_printf debug
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@infineon.com>
- *
- * Description:
- * Device driver for TCG/TCPA TPM (trusted platform module).
- * Specifications at www.trustedcomputinggroup.org
- *
- * It is based on the Linux kernel driver tpm.c from Leendert van
- * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
- *
- * Version: 2.1.1
- *
- * 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, version 2 of the
- * License.
- *
- * 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 <malloc.h>
-#include "tpm.h"
-
-/* global structure for tpm chip data */
-struct tpm_chip g_chip;
-
-enum tpm_duration {
- TPM_SHORT = 0,
- TPM_MEDIUM = 1,
- TPM_LONG = 2,
- TPM_UNDEFINED,
-};
-
-#define TPM_MAX_ORDINAL 243
-#define TPM_MAX_PROTECTED_ORDINAL 12
-#define TPM_PROTECTED_ORDINAL_MASK 0xFF
-
-/*
- * Array with one entry per ordinal defining the maximum amount
- * of time the chip could take to return the result. The ordinal
- * designation of short, medium or long is defined in a table in
- * TCG Specification TPM Main Part 2 TPM Structures Section 17. The
- * values of the SHORT, MEDIUM, and LONG durations are retrieved
- * from the chip during initialization with a call to tpm_get_timeouts.
- */
-static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {
- TPM_UNDEFINED, /* 0 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 5 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 10 */
- TPM_SHORT,
-};
-
-static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
- TPM_UNDEFINED, /* 0 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 5 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 10 */
- TPM_SHORT,
- TPM_MEDIUM,
- TPM_LONG,
- TPM_LONG,
- TPM_MEDIUM, /* 15 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_MEDIUM,
- TPM_LONG,
- TPM_SHORT, /* 20 */
- TPM_SHORT,
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_SHORT, /* 25 */
- TPM_SHORT,
- TPM_MEDIUM,
- TPM_SHORT,
- TPM_SHORT,
- TPM_MEDIUM, /* 30 */
- TPM_LONG,
- TPM_MEDIUM,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT, /* 35 */
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_MEDIUM, /* 40 */
- TPM_LONG,
- TPM_MEDIUM,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT, /* 45 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_LONG,
- TPM_MEDIUM, /* 50 */
- TPM_MEDIUM,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 55 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_MEDIUM, /* 60 */
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_SHORT,
- TPM_SHORT,
- TPM_MEDIUM, /* 65 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 70 */
- TPM_SHORT,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 75 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_LONG, /* 80 */
- TPM_UNDEFINED,
- TPM_MEDIUM,
- TPM_LONG,
- TPM_SHORT,
- TPM_UNDEFINED, /* 85 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 90 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_UNDEFINED, /* 95 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_MEDIUM, /* 100 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 105 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 110 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT, /* 115 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_LONG, /* 120 */
- TPM_LONG,
- TPM_MEDIUM,
- TPM_UNDEFINED,
- TPM_SHORT,
- TPM_SHORT, /* 125 */
- TPM_SHORT,
- TPM_LONG,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT, /* 130 */
- TPM_MEDIUM,
- TPM_UNDEFINED,
- TPM_SHORT,
- TPM_MEDIUM,
- TPM_UNDEFINED, /* 135 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 140 */
- TPM_SHORT,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 145 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 150 */
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_SHORT,
- TPM_SHORT,
- TPM_UNDEFINED, /* 155 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 160 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 165 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_LONG, /* 170 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 175 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_MEDIUM, /* 180 */
- TPM_SHORT,
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_MEDIUM, /* 185 */
- TPM_SHORT,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 190 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 195 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 200 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT,
- TPM_SHORT, /* 205 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_MEDIUM, /* 210 */
- TPM_UNDEFINED,
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_MEDIUM,
- TPM_UNDEFINED, /* 215 */
- TPM_MEDIUM,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT,
- TPM_SHORT, /* 220 */
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_SHORT,
- TPM_UNDEFINED, /* 225 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 230 */
- TPM_LONG,
- TPM_MEDIUM,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED, /* 235 */
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_UNDEFINED,
- TPM_SHORT, /* 240 */
- TPM_UNDEFINED,
- TPM_MEDIUM,
-};
-
-/*
- * Returns max number of milliseconds to wait
- */
-unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
-{
- int duration_idx = TPM_UNDEFINED;
- int duration = 0;
-
- if (ordinal < TPM_MAX_ORDINAL)
- duration_idx = tpm_ordinal_duration[ordinal];
- else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
- TPM_MAX_PROTECTED_ORDINAL)
- duration_idx =
- tpm_protected_ordinal_duration[ordinal &
- TPM_PROTECTED_ORDINAL_MASK];
-
- if (duration_idx != TPM_UNDEFINED)
- duration = chip->vendor.duration[duration_idx];
- if (duration <= 0)
- return 2 * 60 * HZ; /*two minutes timeout*/
- else
- return duration;
-}
-
-#define TPM_CMD_COUNT_BYTE 2
-#define TPM_CMD_ORDINAL_BYTE 6
-
-ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
-{
- ssize_t rc;
- u32 count, ordinal;
- unsigned long start, stop;
-
- struct tpm_chip *chip = &g_chip;
-
- /* switch endianess: big->little */
- count = get_unaligned_be32(buf + TPM_CMD_COUNT_BYTE);
- ordinal = get_unaligned_be32(buf + TPM_CMD_ORDINAL_BYTE);
-
- if (count == 0) {
- dev_err(chip->dev, "no data\n");
- return -ENODATA;
- }
- if (count > bufsiz) {
- dev_err(chip->dev,
- "invalid count value %x %zx\n", count, bufsiz);
- return -E2BIG;
- }
-
- rc = chip->vendor.send(chip, (u8 *)buf, count);
- if (rc < 0) {
- dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc);
- goto out;
- }
-
- if (chip->vendor.irq)
- goto out_recv;
-
- start = get_timer(0);
- stop = tpm_calc_ordinal_duration(chip, ordinal);
- do {
- dbg_printf("waiting for status...\n");
- u8 status = chip->vendor.status(chip);
- if ((status & chip->vendor.req_complete_mask) ==
- chip->vendor.req_complete_val) {
- dbg_printf("...got it;\n");
- goto out_recv;
- }
-
- if ((status == chip->vendor.req_canceled)) {
- dev_err(chip->dev, "Operation Canceled\n");
- rc = -ECANCELED;
- goto out;
- }
- msleep(TPM_TIMEOUT);
- } while (get_timer(start) < stop);
-
- chip->vendor.cancel(chip);
- dev_err(chip->dev, "Operation Timed out\n");
- rc = -ETIME;
- goto out;
-
-out_recv:
-
- dbg_printf("out_recv: reading response...\n");
- rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE);
- if (rc < 0)
- dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc);
-out:
- return rc;
-}
-
-#define TPM_ERROR_SIZE 10
-
-enum tpm_capabilities {
- TPM_CAP_PROP = cpu_to_be32(5),
-};
-
-enum tpm_sub_capabilities {
- TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
- TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
-};
-
-struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry)
-{
- struct tpm_chip *chip;
-
- /* Driver specific per-device data */
- chip = &g_chip;
- memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific));
- chip->is_open = 1;
-
- return chip;
-}
-
-int tpm_open(uint32_t dev_addr)
-{
- int rc;
- if (g_chip.is_open)
- return -EBUSY;
- rc = tpm_vendor_init(dev_addr);
- if (rc < 0)
- g_chip.is_open = 0;
- return rc;
-}
-
-void tpm_close(void)
-{
- if (g_chip.is_open) {
- tpm_vendor_cleanup(&g_chip);
- g_chip.is_open = 0;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@infineon.com>
- *
- * Version: 2.1.1
- *
- * Description:
- * Device driver for TCG/TCPA TPM (trusted platform module).
- * Specifications at www.trustedcomputinggroup.org
- *
- * It is based on the Linux kernel driver tpm.c from Leendert van
- * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
- *
- *
- * 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, version 2 of the
- * License.
- *
- * 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
- */
-
-#ifndef _TPM_H_
-#define _TPM_H_
-
-#include <linux/compiler.h>
-
-#include "compatibility.h"
-
-enum tpm_timeout {
- TPM_TIMEOUT = 5, /* msecs */
-};
-
-/* Size of external transmit buffer (used in tpm_transmit)*/
-#define TPM_BUFSIZE 4096
-
-/* Index of fields in TPM command buffer */
-#define TPM_CMD_SIZE_BYTE 2
-#define TPM_CMD_ORDINAL_BYTE 6
-
-/* Index of Count field in TPM response buffer */
-#define TPM_RSP_SIZE_BYTE 2
-#define TPM_RSP_RC_BYTE 6
-
-struct tpm_chip;
-
-struct tpm_vendor_specific {
- const u8 req_complete_mask;
- const u8 req_complete_val;
- const u8 req_canceled;
- int irq;
- int (*recv) (struct tpm_chip *, u8 *, size_t);
- int (*send) (struct tpm_chip *, u8 *, size_t);
- void (*cancel) (struct tpm_chip *);
- u8(*status) (struct tpm_chip *);
- int locality;
- unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
- unsigned long duration[3]; /* msec */
-};
-
-struct tpm_chip {
- int is_open;
- struct tpm_vendor_specific vendor;
-};
-
-struct tpm_input_header {
- __be16 tag;
- __be32 length;
- __be32 ordinal;
-} __packed;
-
-struct tpm_output_header {
- __be16 tag;
- __be32 length;
- __be32 return_code;
-} __packed;
-
-struct timeout_t {
- __be32 a;
- __be32 b;
- __be32 c;
- __be32 d;
-} __packed;
-
-struct duration_t {
- __be32 tpm_short;
- __be32 tpm_medium;
- __be32 tpm_long;
-} __packed;
-
-union cap_t {
- struct timeout_t timeout;
- struct duration_t duration;
-};
-
-struct tpm_getcap_params_in {
- __be32 cap;
- __be32 subcap_size;
- __be32 subcap;
-} __packed;
-
-struct tpm_getcap_params_out {
- __be32 cap_size;
- union cap_t cap;
-} __packed;
-
-union tpm_cmd_header {
- struct tpm_input_header in;
- struct tpm_output_header out;
-};
-
-union tpm_cmd_params {
- struct tpm_getcap_params_out getcap_out;
- struct tpm_getcap_params_in getcap_in;
-};
-
-struct tpm_cmd_t {
- union tpm_cmd_header header;
- union tpm_cmd_params params;
-} __packed;
-
-
-/* ---------- Interface for TPM vendor ------------ */
-
-extern struct tpm_chip *tpm_register_hardware(
- const struct tpm_vendor_specific *);
-
-extern int tpm_vendor_init(uint32_t dev_addr);
-
-extern void tpm_vendor_cleanup(struct tpm_chip *chip);
-
-/* ---------- Interface for TDDL ------------------- */
-
-/*
- * if dev_addr != 0 - redefines TPM device address
- * Returns < 0 on error, 0 on success.
- */
-extern int tpm_open(uint32_t dev_addr);
-
-extern void tpm_close(void);
-
-/*
- * Transmit bufsiz bytes out of buf to TPM and get results back in buf, too.
- * Returns < 0 on error, 0 on success.
- */
-extern ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@infineon.com>
- *
- * Description:
- * Device driver for TCG/TCPA TPM (trusted platform module).
- * Specifications at www.trustedcomputinggroup.org
- *
- * This device driver implements the TPM interface as defined in
- * the TCG TPM Interface Spec version 1.2, revision 1.0 and the
- * Infineon I2C Protocol Stack Specification v0.20.
- *
- * It is based on the Linux kernel driver tpm.c from Leendert van
- * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
- *
- * Version: 2.1.1
- *
- * 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, version 2 of the
- * License.
- *
- * 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 <i2c.h>
-#include <linux/types.h>
-
-#include "compatibility.h"
-#include "tpm.h"
-
-/* max. buffer size supported by our tpm */
-#ifdef TPM_BUFSIZE
-#undef TPM_BUFSIZE
-#endif
-#define TPM_BUFSIZE 1260
-/* Address of the TPM on the I2C bus */
-#define TPM_I2C_ADDR 0x20
-/* max. number of iterations after i2c NAK */
-#define MAX_COUNT 3
-
-#define SLEEP_DURATION 60 /*in usec*/
-
-/* max. number of iterations after i2c NAK for 'long' commands
- * we need this especially for sending TPM_READY, since the cleanup after the
- * transtion to the ready state may take some time, but it is unpredictable
- * how long it will take.
- */
-#define MAX_COUNT_LONG 50
-
-#define SLEEP_DURATION_LONG 210 /* in usec */
-
-/* expected value for DIDVID register */
-#define TPM_TIS_I2C_DID_VID 0x000b15d1L
-
-/* Structure to store I2C TPM specific stuff */
-struct tpm_inf_dev {
- uint addr;
- u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
-};
-
-static struct tpm_inf_dev tpm_dev = {
- .addr = TPM_I2C_ADDR
-};
-
-/*
- * iic_tpm_read() - read from TPM register
- * @addr: register address to read from
- * @buffer: provided by caller
- * @len: number of bytes to read
- *
- * Read len bytes from TPM register and put them into
- * buffer (little-endian format, i.e. first byte is put into buffer[0]).
- *
- * NOTE: TPM is big-endian for multi-byte values. Multi-byte
- * values have to be swapped.
- *
- * Return -EIO on error, 0 on success.
- */
-int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
-{
- int rc;
- int count;
- uint myaddr = addr;
- /* we have to use uint here, uchar hangs the board */
-
- for (count = 0; count < MAX_COUNT; count++) {
- rc = i2c_write(tpm_dev.addr, 0, 0, (uchar *)&myaddr, 1);
- if (rc == 0)
- break; /*success, break to skip sleep*/
-
- udelay(SLEEP_DURATION);
- }
-
- if (rc)
- return -rc;
-
- /* After the TPM has successfully received the register address it needs
- * some time, thus we're sleeping here again, before retrieving the data
- */
- for (count = 0; count < MAX_COUNT; count++) {
- udelay(SLEEP_DURATION);
- rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len);
- if (rc == 0)
- break; /*success, break to skip sleep*/
- }
-
- if (rc)
- return -rc;
-
- return 0;
-}
-
-static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
- unsigned int sleep_time,
- u8 max_count)
-{
- int rc = 0;
- int count;
-
- /* prepare send buffer */
- tpm_dev.buf[0] = addr;
- memcpy(&(tpm_dev.buf[1]), buffer, len);
-
- for (count = 0; count < max_count; count++) {
- rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1);
- if (rc == 0)
- break; /*success, break to skip sleep*/
-
- udelay(sleep_time);
- }
-
- if (rc)
- return -rc;
-
- return 0;
-}
-
-/*
- * iic_tpm_write() - write to TPM register
- * @addr: register address to write to
- * @buffer: containing data to be written
- * @len: number of bytes to write
- *
- * Write len bytes from provided buffer to TPM register (little
- * endian format, i.e. buffer[0] is written as first byte).
- *
- * NOTE: TPM is big-endian for multi-byte values. Multi-byte
- * values have to be swapped.
- *
- * NOTE: use this function instead of the iic_tpm_write_generic function.
- *
- * Return -EIO on error, 0 on success
- */
-static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
-{
- return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION,
- MAX_COUNT);
-}
-
-/*
- * This function is needed especially for the cleanup situation after
- * sending TPM_READY
- * */
-static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
-{
- return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG,
- MAX_COUNT_LONG);
-}
-
-#define TPM_HEADER_SIZE 10
-
-enum tis_access {
- TPM_ACCESS_VALID = 0x80,
- TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
- TPM_ACCESS_REQUEST_PENDING = 0x04,
- TPM_ACCESS_REQUEST_USE = 0x02,
-};
-
-enum tis_status {
- TPM_STS_VALID = 0x80,
- TPM_STS_COMMAND_READY = 0x40,
- TPM_STS_GO = 0x20,
- TPM_STS_DATA_AVAIL = 0x10,
- TPM_STS_DATA_EXPECT = 0x08,
-};
-
-enum tis_defaults {
- TIS_SHORT_TIMEOUT = 750, /* ms */
- TIS_LONG_TIMEOUT = 2000, /* 2 sec */
-};
-
-#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
-#define TPM_STS(l) (0x0001 | ((l) << 4))
-#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
-#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
-
-static int check_locality(struct tpm_chip *chip, int loc)
-{
- u8 buf;
- int rc;
-
- rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
- if (rc < 0)
- return rc;
-
- if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
- (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
- chip->vendor.locality = loc;
- return loc;
- }
-
- return -1;
-}
-
-static void release_locality(struct tpm_chip *chip, int loc, int force)
-{
- u8 buf;
- if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
- return;
-
- if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
- (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
- buf = TPM_ACCESS_ACTIVE_LOCALITY;
- iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
- }
-}
-
-static int request_locality(struct tpm_chip *chip, int loc)
-{
- unsigned long start, stop;
- u8 buf = TPM_ACCESS_REQUEST_USE;
-
- if (check_locality(chip, loc) >= 0)
- return loc; /* we already have the locality */
-
- iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
-
- /* wait for burstcount */
- start = get_timer(0);
- stop = chip->vendor.timeout_a;
- do {
- if (check_locality(chip, loc) >= 0)
- return loc;
- msleep(TPM_TIMEOUT);
- } while (get_timer(start) < stop);
-
- return -1;
-}
-
-static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
-{
- /* NOTE: since i2c read may fail, return 0 in this case --> time-out */
- u8 buf;
- if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
- return 0;
- else
- return buf;
-}
-
-static void tpm_tis_i2c_ready(struct tpm_chip *chip)
-{
- /* this causes the current command to be aborted */
- u8 buf = TPM_STS_COMMAND_READY;
- iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
-}
-
-static ssize_t get_burstcount(struct tpm_chip *chip)
-{
- unsigned long start, stop;
- ssize_t burstcnt;
- u8 buf[3];
-
- /* wait for burstcount */
- /* which timeout value, spec has 2 answers (c & d) */
- start = get_timer(0);
- stop = chip->vendor.timeout_d;
- do {
- /* Note: STS is little endian */
- if (iic_tpm_read(TPM_STS(chip->vendor.locality) + 1, buf, 3)
- < 0)
- burstcnt = 0;
- else
- burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
-
- if (burstcnt)
- return burstcnt;
- msleep(TPM_TIMEOUT);
- } while (get_timer(start) < stop);
-
- return -EBUSY;
-}
-
-static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
- int *status)
-{
- unsigned long start, stop;
-
- /* check current status */
- *status = tpm_tis_i2c_status(chip);
- if ((*status & mask) == mask)
- return 0;
-
- start = get_timer(0);
- stop = timeout;
- do {
- msleep(TPM_TIMEOUT);
- *status = tpm_tis_i2c_status(chip);
- if ((*status & mask) == mask)
- return 0;
-
- } while (get_timer(start) < stop);
-
- return -ETIME;
-}
-
-static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- size_t size = 0;
- ssize_t burstcnt;
- int rc;
-
- while (size < count) {
- burstcnt = get_burstcount(chip);
-
- /* burstcount < 0 = tpm is busy */
- if (burstcnt < 0)
- return burstcnt;
-
- /* limit received data to max. left */
- if (burstcnt > (count - size))
- burstcnt = count - size;
-
- rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
- &(buf[size]),
- burstcnt);
- if (rc == 0)
- size += burstcnt;
- }
-
- return size;
-}
-
-static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- int size = 0;
- int expected, status;
-
- if (count < TPM_HEADER_SIZE) {
- size = -EIO;
- goto out;
- }
-
- /* read first 10 bytes, including tag, paramsize, and result */
- size = recv_data(chip, buf, TPM_HEADER_SIZE);
- if (size < TPM_HEADER_SIZE) {
- dev_err(chip->dev, "Unable to read header\n");
- goto out;
- }
-
- expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
- if ((size_t)expected > count) {
- size = -EIO;
- goto out;
- }
-
- size += recv_data(chip, &buf[TPM_HEADER_SIZE],
- expected - TPM_HEADER_SIZE);
- if (size < expected) {
- dev_err(chip->dev, "Unable to read remainder of result\n");
- size = -ETIME;
- goto out;
- }
-
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
- if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->dev, "Error left over data\n");
- size = -EIO;
- goto out;
- }
-
-out:
- tpm_tis_i2c_ready(chip);
- /* The TPM needs some time to clean up here,
- * so we sleep rather than keeping the bus busy
- */
- udelay(2000);
- release_locality(chip, chip->vendor.locality, 0);
-
- return size;
-}
-
-static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
-{
- int rc, status;
- ssize_t burstcnt;
- size_t count = 0;
- u8 sts = TPM_STS_GO;
-
- if (len > TPM_BUFSIZE)
- return -E2BIG; /* command is too long for our tpm, sorry */
-
- if (request_locality(chip, 0) < 0)
- return -EBUSY;
-
- status = tpm_tis_i2c_status(chip);
- if ((status & TPM_STS_COMMAND_READY) == 0) {
- tpm_tis_i2c_ready(chip);
- if (wait_for_stat
- (chip, TPM_STS_COMMAND_READY,
- chip->vendor.timeout_b, &status) < 0) {
- rc = -ETIME;
- goto out_err;
- }
- }
-
- while (count < len - 1) {
- burstcnt = get_burstcount(chip);
-
- /* burstcount < 0 = tpm is busy */
- if (burstcnt < 0)
- return burstcnt;
-
- if (burstcnt > (len-1-count))
- burstcnt = len-1-count;
-
-#ifdef CONFIG_TPM_I2C_BURST_LIMITATION
- if (burstcnt > CONFIG_TPM_I2C_BURST_LIMITATION)
- burstcnt = CONFIG_TPM_I2C_BURST_LIMITATION;
-#endif /* CONFIG_TPM_I2C_BURST_LIMITATION */
-
- rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
- &(buf[count]), burstcnt);
- if (rc == 0)
- count += burstcnt;
-
- wait_for_stat(chip, TPM_STS_VALID,
- chip->vendor.timeout_c, &status);
-
- if ((status & TPM_STS_DATA_EXPECT) == 0) {
- rc = -EIO;
- goto out_err;
- }
- }
-
- /* write last byte */
- iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
- if ((status & TPM_STS_DATA_EXPECT) != 0) {
- rc = -EIO;
- goto out_err;
- }
-
- /* go and do it */
- iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
-
- return len;
-out_err:
- tpm_tis_i2c_ready(chip);
- /* The TPM needs some time to clean up here,
- * so we sleep rather than keeping the bus busy
- */
- udelay(2000);
- release_locality(chip, chip->vendor.locality, 0);
-
- return rc;
-}
-
-static struct tpm_vendor_specific tpm_tis_i2c = {
- .status = tpm_tis_i2c_status,
- .recv = tpm_tis_i2c_recv,
- .send = tpm_tis_i2c_send,
- .cancel = tpm_tis_i2c_ready,
- .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- .req_canceled = TPM_STS_COMMAND_READY,
-};
-
-/* initialisation of i2c tpm */
-
-
-int tpm_vendor_init(uint32_t dev_addr)
-{
- u32 vendor;
- uint old_addr;
- int rc = 0;
- struct tpm_chip *chip;
-
- old_addr = tpm_dev.addr;
- if (dev_addr != 0)
- tpm_dev.addr = dev_addr;
-
- chip = tpm_register_hardware(&tpm_tis_i2c);
- if (chip < 0) {
- rc = -ENODEV;
- goto out_err;
- }
-
- /* Disable interrupts (not supported) */
- chip->vendor.irq = 0;
-
- /* Default timeouts */
- chip->vendor.timeout_a = TIS_SHORT_TIMEOUT;
- chip->vendor.timeout_b = TIS_LONG_TIMEOUT;
- chip->vendor.timeout_c = TIS_SHORT_TIMEOUT;
- chip->vendor.timeout_d = TIS_SHORT_TIMEOUT;
-
- if (request_locality(chip, 0) != 0) {
- rc = -ENODEV;
- goto out_err;
- }
-
- /* read four bytes from DID_VID register */
- if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
- rc = -EIO;
- goto out_release;
- }
-
- /* create DID_VID register value, after swapping to little-endian */
- vendor = be32_to_cpu(vendor);
-
- if (vendor != TPM_TIS_I2C_DID_VID) {
- rc = -ENODEV;
- goto out_release;
- }
-
- dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
-
- /*
- * A timeout query to TPM can be placed here.
- * Standard timeout values are used so far
- */
-
- return 0;
-
-out_release:
- release_locality(chip, 0, 1);
-
-out_err:
- tpm_dev.addr = old_addr;
- return rc;
-}
-
-void tpm_vendor_cleanup(struct tpm_chip *chip)
-{
- release_locality(chip, chip->vendor.locality, 1);
-}
int i2c_bus;
node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM);
+ if (node < 0) {
+ node = fdtdec_next_compatible(blob, 0,
+ COMPAT_INFINEON_SLB9645_TPM);
+ }
if (node < 0) {
debug("%s: Node not found\n", __func__);
return -1;
--- /dev/null
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external@infineon.com>
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ * Version: 2.1.1
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <compiler.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <tpm.h>
+#include <asm-generic/errno.h>
+#include <linux/types.h>
+#include <linux/unaligned/be_byteshift.h>
+
+#include "tpm_private.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* TPM configuration */
+struct tpm {
+ int i2c_bus;
+ int slave_addr;
+ char inited;
+ int old_bus;
+} tpm;
+
+/* Global structure for tpm chip data */
+static struct tpm_chip g_chip;
+
+enum tpm_duration {
+ TPM_SHORT = 0,
+ TPM_MEDIUM = 1,
+ TPM_LONG = 2,
+ TPM_UNDEFINED,
+};
+
+/* Extended error numbers from linux (see errno.h) */
+#define ECANCELED 125 /* Operation Canceled */
+
+/* Timer frequency. Corresponds to msec timer resolution*/
+#define HZ 1000
+
+#define TPM_MAX_ORDINAL 243
+#define TPM_MAX_PROTECTED_ORDINAL 12
+#define TPM_PROTECTED_ORDINAL_MASK 0xFF
+
+#define TPM_CMD_COUNT_BYTE 2
+#define TPM_CMD_ORDINAL_BYTE 6
+
+/*
+ * Array with one entry per ordinal defining the maximum amount
+ * of time the chip could take to return the result. The ordinal
+ * designation of short, medium or long is defined in a table in
+ * TCG Specification TPM Main Part 2 TPM Structures Section 17. The
+ * values of the SHORT, MEDIUM, and LONG durations are retrieved
+ * from the chip during initialization with a call to tpm_get_timeouts.
+ */
+static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {
+ TPM_UNDEFINED, /* 0 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 5 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 10 */
+ TPM_SHORT,
+};
+
+static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
+ TPM_UNDEFINED, /* 0 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 5 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 10 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_LONG,
+ TPM_LONG,
+ TPM_MEDIUM, /* 15 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_LONG,
+ TPM_SHORT, /* 20 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_SHORT, /* 25 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM, /* 30 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 35 */
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 40 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 45 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_LONG,
+ TPM_MEDIUM, /* 50 */
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 55 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 60 */
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM, /* 65 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 70 */
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 75 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_LONG, /* 80 */
+ TPM_UNDEFINED,
+ TPM_MEDIUM,
+ TPM_LONG,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 85 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 90 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 95 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 100 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 105 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 110 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 115 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_LONG, /* 120 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_SHORT, /* 125 */
+ TPM_SHORT,
+ TPM_LONG,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 130 */
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_UNDEFINED, /* 135 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 140 */
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 145 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 150 */
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 155 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 160 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 165 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_LONG, /* 170 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 175 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 180 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM, /* 185 */
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 190 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 195 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 200 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_SHORT, /* 205 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM, /* 210 */
+ TPM_UNDEFINED,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_UNDEFINED, /* 215 */
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_SHORT, /* 220 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 225 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 230 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 235 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 240 */
+ TPM_UNDEFINED,
+ TPM_MEDIUM,
+};
+
+/* Returns max number of milliseconds to wait */
+static unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
+ u32 ordinal)
+{
+ int duration_idx = TPM_UNDEFINED;
+ int duration = 0;
+
+ if (ordinal < TPM_MAX_ORDINAL) {
+ duration_idx = tpm_ordinal_duration[ordinal];
+ } else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
+ TPM_MAX_PROTECTED_ORDINAL) {
+ duration_idx = tpm_protected_ordinal_duration[
+ ordinal & TPM_PROTECTED_ORDINAL_MASK];
+ }
+
+ if (duration_idx != TPM_UNDEFINED)
+ duration = chip->vendor.duration[duration_idx];
+
+ if (duration <= 0)
+ return 2 * 60 * HZ; /* Two minutes timeout */
+ else
+ return duration;
+}
+
+static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
+{
+ ssize_t rc;
+ u32 count, ordinal;
+ unsigned long start, stop;
+
+ struct tpm_chip *chip = &g_chip;
+
+ /* switch endianess: big->little */
+ count = get_unaligned_be32(buf + TPM_CMD_COUNT_BYTE);
+ ordinal = get_unaligned_be32(buf + TPM_CMD_ORDINAL_BYTE);
+
+ if (count == 0) {
+ error("no data\n");
+ return -ENODATA;
+ }
+ if (count > bufsiz) {
+ error("invalid count value %x %zx\n", count, bufsiz);
+ return -E2BIG;
+ }
+
+ rc = chip->vendor.send(chip, (u8 *)buf, count);
+ if (rc < 0) {
+ error("tpm_transmit: tpm_send: error %zd\n", rc);
+ goto out;
+ }
+
+ if (chip->vendor.irq)
+ goto out_recv;
+
+ start = get_timer(0);
+ stop = tpm_calc_ordinal_duration(chip, ordinal);
+ do {
+ debug("waiting for status...\n");
+ u8 status = chip->vendor.status(chip);
+ if ((status & chip->vendor.req_complete_mask) ==
+ chip->vendor.req_complete_val) {
+ debug("...got it;\n");
+ goto out_recv;
+ }
+
+ if ((status == chip->vendor.req_canceled)) {
+ error("Operation Canceled\n");
+ rc = -ECANCELED;
+ goto out;
+ }
+ udelay(TPM_TIMEOUT * 1000);
+ } while (get_timer(start) < stop);
+
+ chip->vendor.cancel(chip);
+ error("Operation Timed out\n");
+ rc = -ETIME;
+ goto out;
+
+out_recv:
+ debug("out_recv: reading response...\n");
+ rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE);
+ if (rc < 0)
+ error("tpm_transmit: tpm_recv: error %zd\n", rc);
+
+out:
+ return rc;
+}
+
+static int tpm_open(uint32_t dev_addr)
+{
+ int rc;
+ if (g_chip.is_open)
+ return -EBUSY;
+ rc = tpm_vendor_init(dev_addr);
+ if (rc < 0)
+ g_chip.is_open = 0;
+ return rc;
+}
+
+static void tpm_close(void)
+{
+ if (g_chip.is_open) {
+ tpm_vendor_cleanup(&g_chip);
+ g_chip.is_open = 0;
+ }
+}
+
+static int tpm_select(void)
+{
+ int ret;
+
+ tpm.old_bus = i2c_get_bus_num();
+ if (tpm.old_bus != tpm.i2c_bus) {
+ ret = i2c_set_bus_num(tpm.i2c_bus);
+ if (ret) {
+ debug("%s: Fail to set i2c bus %d\n", __func__,
+ tpm.i2c_bus);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int tpm_deselect(void)
+{
+ int ret;
+
+ if (tpm.old_bus != i2c_get_bus_num()) {
+ ret = i2c_set_bus_num(tpm.old_bus);
+ if (ret) {
+ debug("%s: Fail to restore i2c bus %d\n",
+ __func__, tpm.old_bus);
+ return -1;
+ }
+ }
+ tpm.old_bus = -1;
+ return 0;
+}
+
+/**
+ * Decode TPM configuration.
+ *
+ * @param dev Returns a configuration of TPM device
+ * @return 0 if ok, -1 on error
+ */
+static int tpm_decode_config(struct tpm *dev)
+{
+#ifdef CONFIG_OF_CONTROL
+ const void *blob = gd->fdt_blob;
+ int node, parent;
+ int i2c_bus;
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM);
+ if (node < 0) {
+ node = fdtdec_next_compatible(blob, 0,
+ COMPAT_INFINEON_SLB9645_TPM);
+ }
+ if (node < 0) {
+ debug("%s: Node not found\n", __func__);
+ return -1;
+ }
+ parent = fdt_parent_offset(blob, node);
+ if (parent < 0) {
+ debug("%s: Cannot find node parent\n", __func__);
+ return -1;
+ }
+ i2c_bus = i2c_get_bus_num_fdt(parent);
+ if (i2c_bus < 0)
+ return -1;
+ dev->i2c_bus = i2c_bus;
+ dev->slave_addr = fdtdec_get_addr(blob, node, "reg");
+#else
+ dev->i2c_bus = CONFIG_TPM_TIS_I2C_BUS_NUMBER;
+ dev->slave_addr = CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS;
+#endif
+ return 0;
+}
+
+struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry)
+{
+ struct tpm_chip *chip;
+
+ /* Driver specific per-device data */
+ chip = &g_chip;
+ memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific));
+ chip->is_open = 1;
+
+ return chip;
+}
+
+int tis_init(void)
+{
+ if (tpm.inited)
+ return 0;
+
+ if (tpm_decode_config(&tpm))
+ return -1;
+
+ if (tpm_select())
+ return -1;
+
+ /*
+ * Probe TPM twice; the first probing might fail because TPM is asleep,
+ * and the probing can wake up TPM.
+ */
+ if (i2c_probe(tpm.slave_addr) && i2c_probe(tpm.slave_addr)) {
+ debug("%s: fail to probe i2c addr 0x%x\n", __func__,
+ tpm.slave_addr);
+ return -1;
+ }
+
+ tpm_deselect();
+
+ tpm.inited = 1;
+
+ return 0;
+}
+
+int tis_open(void)
+{
+ int rc;
+
+ if (!tpm.inited)
+ return -1;
+
+ if (tpm_select())
+ return -1;
+
+ rc = tpm_open(tpm.slave_addr);
+
+ tpm_deselect();
+
+ return rc;
+}
+
+int tis_close(void)
+{
+ if (!tpm.inited)
+ return -1;
+
+ if (tpm_select())
+ return -1;
+
+ tpm_close();
+
+ tpm_deselect();
+
+ return 0;
+}
+
+int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
+ uint8_t *recvbuf, size_t *rbuf_len)
+{
+ int len;
+ uint8_t buf[4096];
+
+ if (!tpm.inited)
+ return -1;
+
+ if (sizeof(buf) < sbuf_size)
+ return -1;
+
+ memcpy(buf, sendbuf, sbuf_size);
+
+ if (tpm_select())
+ return -1;
+
+ len = tpm_transmit(buf, sbuf_size);
+
+ tpm_deselect();
+
+ if (len < 10) {
+ *rbuf_len = 0;
+ return -1;
+ }
+
+ memcpy(recvbuf, buf, len);
+ *rbuf_len = len;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external@infineon.com>
+ *
+ * Version: 2.1.1
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ * 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
+ */
+
+#ifndef _TPM_PRIVATE_H_
+#define _TPM_PRIVATE_H_
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+enum tpm_timeout {
+ TPM_TIMEOUT = 5, /* msecs */
+};
+
+/* Size of external transmit buffer (used in tpm_transmit)*/
+#define TPM_BUFSIZE 4096
+
+/* Index of Count field in TPM response buffer */
+#define TPM_RSP_SIZE_BYTE 2
+#define TPM_RSP_RC_BYTE 6
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+ const u8 req_complete_mask;
+ const u8 req_complete_val;
+ const u8 req_canceled;
+ int irq;
+ int (*recv) (struct tpm_chip *, u8 *, size_t);
+ int (*send) (struct tpm_chip *, u8 *, size_t);
+ void (*cancel) (struct tpm_chip *);
+ u8(*status) (struct tpm_chip *);
+ int locality;
+ unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
+ unsigned long duration[3]; /* msec */
+};
+
+struct tpm_chip {
+ int is_open;
+ struct tpm_vendor_specific vendor;
+};
+
+struct tpm_input_header {
+ __be16 tag;
+ __be32 length;
+ __be32 ordinal;
+} __packed;
+
+struct tpm_output_header {
+ __be16 tag;
+ __be32 length;
+ __be32 return_code;
+} __packed;
+
+struct timeout_t {
+ __be32 a;
+ __be32 b;
+ __be32 c;
+ __be32 d;
+} __packed;
+
+struct duration_t {
+ __be32 tpm_short;
+ __be32 tpm_medium;
+ __be32 tpm_long;
+} __packed;
+
+union cap_t {
+ struct timeout_t timeout;
+ struct duration_t duration;
+};
+
+struct tpm_getcap_params_in {
+ __be32 cap;
+ __be32 subcap_size;
+ __be32 subcap;
+} __packed;
+
+struct tpm_getcap_params_out {
+ __be32 cap_size;
+ union cap_t cap;
+} __packed;
+
+union tpm_cmd_header {
+ struct tpm_input_header in;
+ struct tpm_output_header out;
+};
+
+union tpm_cmd_params {
+ struct tpm_getcap_params_out getcap_out;
+ struct tpm_getcap_params_in getcap_in;
+};
+
+struct tpm_cmd_t {
+ union tpm_cmd_header header;
+ union tpm_cmd_params params;
+} __packed;
+
+struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *);
+
+int tpm_vendor_init(uint32_t dev_addr);
+
+void tpm_vendor_cleanup(struct tpm_chip *chip);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external@infineon.com>
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0 and the
+ * Infineon I2C Protocol Stack Specification v0.20.
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ * Version: 2.1.1
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ * 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 <fdtdec.h>
+#include <compiler.h>
+#include <i2c.h>
+#include <tpm.h>
+#include <asm-generic/errno.h>
+#include <linux/types.h>
+#include <linux/unaligned/be_byteshift.h>
+
+#include "tpm_private.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Address of the TPM on the I2C bus */
+#define TPM_I2C_ADDR 0x20
+
+/* Max buffer size supported by our tpm */
+#define TPM_DEV_BUFSIZE 1260
+
+/* Max number of iterations after i2c NAK */
+#define MAX_COUNT 3
+
+/*
+ * Max number of iterations after i2c NAK for 'long' commands
+ *
+ * We need this especially for sending TPM_READY, since the cleanup after the
+ * transtion to the ready state may take some time, but it is unpredictable
+ * how long it will take.
+ */
+#define MAX_COUNT_LONG 50
+
+#define SLEEP_DURATION 60 /* in usec */
+#define SLEEP_DURATION_LONG 210 /* in usec */
+
+#define TPM_HEADER_SIZE 10
+
+/*
+ * Expected value for DIDVID register
+ *
+ * The only device the system knows about at this moment is Infineon slb9635.
+ */
+#define TPM_TIS_I2C_DID_VID 0x000b15d1L
+
+enum tis_access {
+ TPM_ACCESS_VALID = 0x80,
+ TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+ TPM_ACCESS_REQUEST_PENDING = 0x04,
+ TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+ TPM_STS_VALID = 0x80,
+ TPM_STS_COMMAND_READY = 0x40,
+ TPM_STS_GO = 0x20,
+ TPM_STS_DATA_AVAIL = 0x10,
+ TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_defaults {
+ TIS_SHORT_TIMEOUT = 750, /* ms */
+ TIS_LONG_TIMEOUT = 2000, /* ms */
+};
+
+/* expected value for DIDVID register */
+#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
+#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
+
+enum i2c_chip_type {
+ SLB9635,
+ SLB9645,
+ UNKNOWN,
+};
+
+static const char * const chip_name[] = {
+ [SLB9635] = "slb9635tt",
+ [SLB9645] = "slb9645tt",
+ [UNKNOWN] = "unknown/fallback to slb9635",
+};
+
+#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
+#define TPM_STS(l) (0x0001 | ((l) << 4))
+#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
+#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
+
+/* Structure to store I2C TPM specific stuff */
+struct tpm_dev {
+ uint addr;
+ u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */
+ enum i2c_chip_type chip_type;
+};
+
+static struct tpm_dev tpm_dev = {
+ .addr = TPM_I2C_ADDR
+};
+
+static struct tpm_dev tpm_dev;
+
+/*
+ * iic_tpm_read() - read from TPM register
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * Read len bytes from TPM register and put them into
+ * buffer (little-endian format, i.e. first byte is put into buffer[0]).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * Return -EIO on error, 0 on success.
+ */
+static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
+{
+ int rc;
+ int count;
+ uint32_t addrbuf = addr;
+
+ if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) {
+ /* slb9635 protocol should work in both cases */
+ for (count = 0; count < MAX_COUNT; count++) {
+ rc = i2c_write(tpm_dev.addr, 0, 0,
+ (uchar *)&addrbuf, 1);
+ if (rc == 0)
+ break; /* Success, break to skip sleep */
+ udelay(SLEEP_DURATION);
+ }
+ if (rc)
+ return -rc;
+
+ /* After the TPM has successfully received the register address
+ * it needs some time, thus we're sleeping here again, before
+ * retrieving the data
+ */
+ for (count = 0; count < MAX_COUNT; count++) {
+ udelay(SLEEP_DURATION);
+ rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len);
+ if (rc == 0)
+ break; /* success, break to skip sleep */
+ }
+ } else {
+ /*
+ * Use a combined read for newer chips.
+ * Unfortunately the smbus functions are not suitable due to
+ * the 32 byte limit of the smbus.
+ * Retries should usually not be needed, but are kept just to
+ * be safe on the safe side.
+ */
+ for (count = 0; count < MAX_COUNT; count++) {
+ rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len);
+ if (rc == 0)
+ break; /* break here to skip sleep */
+ udelay(SLEEP_DURATION);
+ }
+ }
+
+ /* Take care of 'guard time' */
+ udelay(SLEEP_DURATION);
+ if (rc)
+ return -rc;
+
+ return 0;
+}
+
+static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
+ unsigned int sleep_time, u8 max_count)
+{
+ int rc = 0;
+ int count;
+
+ /* Prepare send buffer */
+ tpm_dev.buf[0] = addr;
+ memcpy(&(tpm_dev.buf[1]), buffer, len);
+
+ for (count = 0; count < max_count; count++) {
+ rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1);
+ if (rc == 0)
+ break; /* Success, break to skip sleep */
+ udelay(sleep_time);
+ }
+
+ /* take care of 'guard time' */
+ udelay(SLEEP_DURATION);
+ if (rc)
+ return -rc;
+
+ return 0;
+}
+
+/*
+ * iic_tpm_write() - write to TPM register
+ * @addr: register address to write to
+ * @buffer: containing data to be written
+ * @len: number of bytes to write
+ *
+ * Write len bytes from provided buffer to TPM register (little
+ * endian format, i.e. buffer[0] is written as first byte).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: use this function instead of the iic_tpm_write_generic function.
+ *
+ * Return -EIO on error, 0 on success
+ */
+static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
+{
+ return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION,
+ MAX_COUNT);
+}
+
+/*
+ * This function is needed especially for the cleanup situation after
+ * sending TPM_READY
+ */
+static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
+{
+ return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG,
+ MAX_COUNT_LONG);
+}
+
+static int check_locality(struct tpm_chip *chip, int loc)
+{
+ const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
+ u8 buf;
+ int rc;
+
+ rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
+ if (rc < 0)
+ return rc;
+
+ if ((buf & mask) == mask) {
+ chip->vendor.locality = loc;
+ return loc;
+ }
+
+ return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int loc, int force)
+{
+ const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
+ u8 buf;
+
+ if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
+ return;
+
+ if (force || (buf & mask) == mask) {
+ buf = TPM_ACCESS_ACTIVE_LOCALITY;
+ iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+ }
+}
+
+static int request_locality(struct tpm_chip *chip, int loc)
+{
+ unsigned long start, stop;
+ u8 buf = TPM_ACCESS_REQUEST_USE;
+
+ if (check_locality(chip, loc) >= 0)
+ return loc; /* We already have the locality */
+
+ iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+
+ /* Wait for burstcount */
+ start = get_timer(0);
+ stop = chip->vendor.timeout_a;
+ do {
+ if (check_locality(chip, loc) >= 0)
+ return loc;
+ udelay(TPM_TIMEOUT * 1000);
+ } while (get_timer(start) < stop);
+
+ return -1;
+}
+
+static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
+{
+ /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
+ u8 buf;
+
+ if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+ return 0;
+ else
+ return buf;
+}
+
+static void tpm_tis_i2c_ready(struct tpm_chip *chip)
+{
+ /* This causes the current command to be aborted */
+ u8 buf = TPM_STS_COMMAND_READY;
+
+ iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+}
+
+static ssize_t get_burstcount(struct tpm_chip *chip)
+{
+ unsigned long start, stop;
+ ssize_t burstcnt;
+ u8 addr, buf[3];
+
+ /* Wait for burstcount */
+ /* XXX: Which timeout value? Spec has 2 answers (c & d) */
+ start = get_timer(0);
+ stop = chip->vendor.timeout_d;
+ do {
+ /* Note: STS is little endian */
+ addr = TPM_STS(chip->vendor.locality) + 1;
+ if (iic_tpm_read(addr, buf, 3) < 0)
+ burstcnt = 0;
+ else
+ burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+ if (burstcnt)
+ return burstcnt;
+ udelay(TPM_TIMEOUT * 1000);
+ } while (get_timer(start) < stop);
+
+ return -EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
+ int *status)
+{
+ unsigned long start, stop;
+
+ /* Check current status */
+ *status = tpm_tis_i2c_status(chip);
+ if ((*status & mask) == mask)
+ return 0;
+
+ start = get_timer(0);
+ stop = timeout;
+ do {
+ udelay(TPM_TIMEOUT * 1000);
+ *status = tpm_tis_i2c_status(chip);
+ if ((*status & mask) == mask)
+ return 0;
+ } while (get_timer(start) < stop);
+
+ return -ETIME;
+}
+
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ size_t size = 0;
+ ssize_t burstcnt;
+ int rc;
+
+ while (size < count) {
+ burstcnt = get_burstcount(chip);
+
+ /* burstcount < 0 -> tpm is busy */
+ if (burstcnt < 0)
+ return burstcnt;
+
+ /* Limit received data to max left */
+ if (burstcnt > (count - size))
+ burstcnt = count - size;
+
+ rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+ &(buf[size]), burstcnt);
+ if (rc == 0)
+ size += burstcnt;
+ }
+
+ return size;
+}
+
+static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ int size = 0;
+ int expected, status;
+
+ if (count < TPM_HEADER_SIZE) {
+ size = -EIO;
+ goto out;
+ }
+
+ /* Read first 10 bytes, including tag, paramsize, and result */
+ size = recv_data(chip, buf, TPM_HEADER_SIZE);
+ if (size < TPM_HEADER_SIZE) {
+ error("Unable to read header\n");
+ goto out;
+ }
+
+ expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
+ if ((size_t)expected > count) {
+ size = -EIO;
+ goto out;
+ }
+
+ size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_SIZE);
+ if (size < expected) {
+ error("Unable to read remainder of result\n");
+ size = -ETIME;
+ goto out;
+ }
+
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+ if (status & TPM_STS_DATA_AVAIL) { /* Retry? */
+ error("Error left over data\n");
+ size = -EIO;
+ goto out;
+ }
+
+out:
+ tpm_tis_i2c_ready(chip);
+ /*
+ * The TPM needs some time to clean up here,
+ * so we sleep rather than keeping the bus busy
+ */
+ udelay(2000);
+ release_locality(chip, chip->vendor.locality, 0);
+
+ return size;
+}
+
+static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ int rc, status;
+ ssize_t burstcnt;
+ size_t count = 0;
+ int retry = 0;
+ u8 sts = TPM_STS_GO;
+
+ if (len > TPM_DEV_BUFSIZE)
+ return -E2BIG; /* Command is too long for our tpm, sorry */
+
+ if (request_locality(chip, 0) < 0)
+ return -EBUSY;
+
+ status = tpm_tis_i2c_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_tis_i2c_ready(chip);
+ if (wait_for_stat(chip, TPM_STS_COMMAND_READY,
+ chip->vendor.timeout_b, &status) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+
+ burstcnt = get_burstcount(chip);
+
+ /* burstcount < 0 -> tpm is busy */
+ if (burstcnt < 0)
+ return burstcnt;
+
+ while (count < len - 1) {
+ if (burstcnt > len - 1 - count)
+ burstcnt = len - 1 - count;
+
+#ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION
+ if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION)
+ burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION;
+#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */
+
+ rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+ &(buf[count]), burstcnt);
+ if (rc == 0)
+ count += burstcnt;
+ else {
+ retry++;
+ wait_for_stat(chip, TPM_STS_VALID,
+ chip->vendor.timeout_c, &status);
+
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+ }
+
+ /* Write last byte */
+ iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
+ wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ /* Go and do it */
+ iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+
+ return len;
+
+out_err:
+ tpm_tis_i2c_ready(chip);
+ /*
+ * The TPM needs some time to clean up here,
+ * so we sleep rather than keeping the bus busy
+ */
+ udelay(2000);
+ release_locality(chip, chip->vendor.locality, 0);
+
+ return rc;
+}
+
+static struct tpm_vendor_specific tpm_tis_i2c = {
+ .status = tpm_tis_i2c_status,
+ .recv = tpm_tis_i2c_recv,
+ .send = tpm_tis_i2c_send,
+ .cancel = tpm_tis_i2c_ready,
+ .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_canceled = TPM_STS_COMMAND_READY,
+};
+
+
+static enum i2c_chip_type tpm_vendor_chip_type(void)
+{
+#ifdef CONFIG_OF_CONTROL
+ const void *blob = gd->fdt_blob;
+
+ if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0)
+ return SLB9645;
+
+ if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM) >= 0)
+ return SLB9635;
+#endif
+ return UNKNOWN;
+}
+
+/* Initialisation of i2c tpm */
+int tpm_vendor_init(uint32_t dev_addr)
+{
+ u32 vendor;
+ u32 expected_did_vid;
+ uint old_addr;
+ int rc = 0;
+ struct tpm_chip *chip;
+
+ old_addr = tpm_dev.addr;
+ if (dev_addr != 0)
+ tpm_dev.addr = dev_addr;
+
+ tpm_dev.chip_type = tpm_vendor_chip_type();
+
+ chip = tpm_register_hardware(&tpm_tis_i2c);
+ if (chip < 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* Disable interrupts (not supported) */
+ chip->vendor.irq = 0;
+
+ /* Default timeouts */
+ chip->vendor.timeout_a = TIS_SHORT_TIMEOUT;
+ chip->vendor.timeout_b = TIS_LONG_TIMEOUT;
+ chip->vendor.timeout_c = TIS_SHORT_TIMEOUT;
+ chip->vendor.timeout_d = TIS_SHORT_TIMEOUT;
+
+ if (request_locality(chip, 0) < 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* Read four bytes from DID_VID register */
+ if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
+ rc = -EIO;
+ goto out_release;
+ }
+
+ if (tpm_dev.chip_type == SLB9635) {
+ vendor = be32_to_cpu(vendor);
+ expected_did_vid = TPM_TIS_I2C_DID_VID_9635;
+ } else {
+ /* device id and byte order has changed for newer i2c tpms */
+ expected_did_vid = TPM_TIS_I2C_DID_VID_9645;
+ }
+
+ if (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) {
+ error("Vendor id did not match! ID was %08x\n", vendor);
+ rc = -ENODEV;
+ goto out_release;
+ }
+
+ debug("1.2 TPM (chip type %s device-id 0x%X)\n",
+ chip_name[tpm_dev.chip_type], vendor >> 16);
+
+ /*
+ * A timeout query to TPM can be placed here.
+ * Standard timeout values are used so far
+ */
+
+ return 0;
+
+out_release:
+ release_locality(chip, 0, 1);
+
+out_err:
+ tpm_dev.addr = old_addr;
+ return rc;
+}
+
+void tpm_vendor_cleanup(struct tpm_chip *chip)
+{
+ release_locality(chip, chip->vendor.locality, 1);
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * 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
+ */
+
+/*
+ * The code in this file is based on the article "Writing a TPM Device Driver"
+ * published on http://ptgmedia.pearsoncmg.com.
+ *
+ * One principal difference is that in the simplest config the other than 0
+ * TPM localities do not get mapped by some devices (for instance, by Infineon
+ * slb9635), so this driver provides access to locality 0 only.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <tpm.h>
+
+#define PREFIX "lpc_tpm: "
+
+struct tpm_locality {
+ u32 access;
+ u8 padding0[4];
+ u32 int_enable;
+ u8 vector;
+ u8 padding1[3];
+ u32 int_status;
+ u32 int_capability;
+ u32 tpm_status;
+ u8 padding2[8];
+ u8 data;
+ u8 padding3[3803];
+ u32 did_vid;
+ u8 rid;
+ u8 padding4[251];
+};
+
+/*
+ * This pointer refers to the TPM chip, 5 of its localities are mapped as an
+ * array.
+ */
+#define TPM_TOTAL_LOCALITIES 5
+static struct tpm_locality *lpc_tpm_dev =
+ (struct tpm_locality *)CONFIG_TPM_TIS_BASE_ADDRESS;
+
+/* Some registers' bit field definitions */
+#define TIS_STS_VALID (1 << 7) /* 0x80 */
+#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
+#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
+#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
+#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
+#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
+
+#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
+#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
+#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
+#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
+#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
+#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
+#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
+
+#define TIS_STS_BURST_COUNT_MASK (0xffff)
+#define TIS_STS_BURST_COUNT_SHIFT (8)
+
+/*
+ * Error value returned if a tpm register does not enter the expected state
+ * after continuous polling. No actual TPM register reading ever returns -1,
+ * so this value is a safe error indication to be mixed with possible status
+ * register values.
+ */
+#define TPM_TIMEOUT_ERR (-1)
+
+/* Error value returned on various TPM driver errors. */
+#define TPM_DRIVER_ERR (1)
+
+ /* 1 second is plenty for anything TPM does. */
+#define MAX_DELAY_US (1000 * 1000)
+
+/* Retrieve burst count value out of the status register contents. */
+static u16 burst_count(u32 status)
+{
+ return (status >> TIS_STS_BURST_COUNT_SHIFT) & TIS_STS_BURST_COUNT_MASK;
+}
+
+/*
+ * Structures defined below allow creating descriptions of TPM vendor/device
+ * ID information for run time discovery. The only device the system knows
+ * about at this time is Infineon slb9635.
+ */
+struct device_name {
+ u16 dev_id;
+ const char * const dev_name;
+};
+
+struct vendor_name {
+ u16 vendor_id;
+ const char *vendor_name;
+ const struct device_name *dev_names;
+};
+
+static const struct device_name infineon_devices[] = {
+ {0xb, "SLB9635 TT 1.2"},
+ {0}
+};
+
+static const struct vendor_name vendor_names[] = {
+ {0x15d1, "Infineon", infineon_devices},
+};
+
+/*
+ * Cached vendor/device ID pair to indicate that the device has been already
+ * discovered.
+ */
+static u32 vendor_dev_id;
+
+/* TPM access wrappers to support tracing */
+static u8 tpm_read_byte(const u8 *ptr)
+{
+ u8 ret = readb(ptr);
+ debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
+ (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret);
+ return ret;
+}
+
+static u32 tpm_read_word(const u32 *ptr)
+{
+ u32 ret = readl(ptr);
+ debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
+ (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret);
+ return ret;
+}
+
+static void tpm_write_byte(u8 value, u8 *ptr)
+{
+ debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
+ (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value);
+ writeb(value, ptr);
+}
+
+static void tpm_write_word(u32 value, u32 *ptr)
+{
+ debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
+ (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value);
+ writel(value, ptr);
+}
+
+/*
+ * tis_wait_reg()
+ *
+ * Wait for at least a second for a register to change its state to match the
+ * expected state. Normally the transition happens within microseconds.
+ *
+ * @reg - pointer to the TPM register
+ * @mask - bitmask for the bitfield(s) to watch
+ * @expected - value the field(s) are supposed to be set to
+ *
+ * Returns the register contents in case the expected value was found in the
+ * appropriate register bits, or TPM_TIMEOUT_ERR on timeout.
+ */
+static u32 tis_wait_reg(u32 *reg, u8 mask, u8 expected)
+{
+ u32 time_us = MAX_DELAY_US;
+
+ while (time_us > 0) {
+ u32 value = tpm_read_word(reg);
+ if ((value & mask) == expected)
+ return value;
+ udelay(1); /* 1 us */
+ time_us--;
+ }
+ return TPM_TIMEOUT_ERR;
+}
+
+/*
+ * Probe the TPM device and try determining its manufacturer/device name.
+ *
+ * Returns 0 on success (the device is found or was found during an earlier
+ * invocation) or TPM_DRIVER_ERR if the device is not found.
+ */
+int tis_init(void)
+{
+ u32 didvid = tpm_read_word(&lpc_tpm_dev[0].did_vid);
+ int i;
+ const char *device_name = "unknown";
+ const char *vendor_name = device_name;
+ u16 vid, did;
+
+ if (vendor_dev_id)
+ return 0; /* Already probed. */
+
+ if (!didvid || (didvid == 0xffffffff)) {
+ printf("%s: No TPM device found\n", __func__);
+ return TPM_DRIVER_ERR;
+ }
+
+ vendor_dev_id = didvid;
+
+ vid = didvid & 0xffff;
+ did = (didvid >> 16) & 0xffff;
+ for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
+ int j = 0;
+ u16 known_did;
+
+ if (vid == vendor_names[i].vendor_id)
+ vendor_name = vendor_names[i].vendor_name;
+
+ while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
+ if (known_did == did) {
+ device_name =
+ vendor_names[i].dev_names[j].dev_name;
+ break;
+ }
+ j++;
+ }
+ break;
+ }
+
+ printf("Found TPM %s by %s\n", device_name, vendor_name);
+ return 0;
+}
+
+/*
+ * tis_senddata()
+ *
+ * send the passed in data to the TPM device.
+ *
+ * @data - address of the data to send, byte by byte
+ * @len - length of the data to send
+ *
+ * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
+ * not accept the entire command).
+ */
+static u32 tis_senddata(const u8 * const data, u32 len)
+{
+ u32 offset = 0;
+ u16 burst = 0;
+ u32 max_cycles = 0;
+ u8 locality = 0;
+ u32 value;
+
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
+ if (value == TPM_TIMEOUT_ERR) {
+ printf("%s:%d - failed to get 'command_ready' status\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+ burst = burst_count(value);
+
+ while (1) {
+ unsigned count;
+
+ /* Wait till the device is ready to accept more data. */
+ while (!burst) {
+ if (max_cycles++ == MAX_DELAY_US) {
+ printf("%s:%d failed to feed %d bytes of %d\n",
+ __FILE__, __LINE__, len - offset, len);
+ return TPM_DRIVER_ERR;
+ }
+ udelay(1);
+ burst = burst_count(tpm_read_word(&lpc_tpm_dev
+ [locality].tpm_status));
+ }
+
+ max_cycles = 0;
+
+ /*
+ * Calculate number of bytes the TPM is ready to accept in one
+ * shot.
+ *
+ * We want to send the last byte outside of the loop (hence
+ * the -1 below) to make sure that the 'expected' status bit
+ * changes to zero exactly after the last byte is fed into the
+ * FIFO.
+ */
+ count = min(burst, len - offset - 1);
+ while (count--)
+ tpm_write_byte(data[offset++],
+ &lpc_tpm_dev[locality].data);
+
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_VALID, TIS_STS_VALID);
+
+ if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) {
+ printf("%s:%d TPM command feed overflow\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ burst = burst_count(value);
+ if ((offset == (len - 1)) && burst) {
+ /*
+ * We need to be able to send the last byte to the
+ * device, so burst size must be nonzero before we
+ * break out.
+ */
+ break;
+ }
+ }
+
+ /* Send the last byte. */
+ tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data);
+ /*
+ * Verify that TPM does not expect any more data as part of this
+ * command.
+ */
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_VALID, TIS_STS_VALID);
+ if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) {
+ printf("%s:%d unexpected TPM status 0x%x\n",
+ __FILE__, __LINE__, value);
+ return TPM_DRIVER_ERR;
+ }
+
+ /* OK, sitting pretty, let's start the command execution. */
+ tpm_write_word(TIS_STS_TPM_GO, &lpc_tpm_dev[locality].tpm_status);
+ return 0;
+}
+
+/*
+ * tis_readresponse()
+ *
+ * read the TPM device response after a command was issued.
+ *
+ * @buffer - address where to read the response, byte by byte.
+ * @len - pointer to the size of buffer
+ *
+ * On success stores the number of received bytes to len and returns 0. On
+ * errors (misformatted TPM data or synchronization problems) returns
+ * TPM_DRIVER_ERR.
+ */
+static u32 tis_readresponse(u8 *buffer, u32 *len)
+{
+ u16 burst;
+ u32 value;
+ u32 offset = 0;
+ u8 locality = 0;
+ const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
+ u32 expected_count = *len;
+ int max_cycles = 0;
+
+ /* Wait for the TPM to process the command. */
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ has_data, has_data);
+ if (value == TPM_TIMEOUT_ERR) {
+ printf("%s:%d failed processing command\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ do {
+ while ((burst = burst_count(value)) == 0) {
+ if (max_cycles++ == MAX_DELAY_US) {
+ printf("%s:%d TPM stuck on read\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+ udelay(1);
+ value = tpm_read_word(&lpc_tpm_dev
+ [locality].tpm_status);
+ }
+
+ max_cycles = 0;
+
+ while (burst-- && (offset < expected_count)) {
+ buffer[offset++] = tpm_read_byte(&lpc_tpm_dev
+ [locality].data);
+
+ if (offset == 6) {
+ /*
+ * We got the first six bytes of the reply,
+ * let's figure out how many bytes to expect
+ * total - it is stored as a 4 byte number in
+ * network order, starting with offset 2 into
+ * the body of the reply.
+ */
+ u32 real_length;
+ memcpy(&real_length,
+ buffer + 2,
+ sizeof(real_length));
+ expected_count = be32_to_cpu(real_length);
+
+ if ((expected_count < offset) ||
+ (expected_count > *len)) {
+ printf("%s:%d bad response size %d\n",
+ __FILE__, __LINE__,
+ expected_count);
+ return TPM_DRIVER_ERR;
+ }
+ }
+ }
+
+ /* Wait for the next portion. */
+ value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
+ TIS_STS_VALID, TIS_STS_VALID);
+ if (value == TPM_TIMEOUT_ERR) {
+ printf("%s:%d failed to read response\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ if (offset == expected_count)
+ break; /* We got all we needed. */
+
+ } while ((value & has_data) == has_data);
+
+ /*
+ * Make sure we indeed read all there was. The TIS_STS_VALID bit is
+ * known to be set.
+ */
+ if (value & TIS_STS_DATA_AVAILABLE) {
+ printf("%s:%d wrong receive status %x\n",
+ __FILE__, __LINE__, value);
+ return TPM_DRIVER_ERR;
+ }
+
+ /* Tell the TPM that we are done. */
+ tpm_write_word(TIS_STS_COMMAND_READY, &lpc_tpm_dev
+ [locality].tpm_status);
+ *len = offset;
+ return 0;
+}
+
+int tis_open(void)
+{
+ u8 locality = 0; /* we use locality zero for everything. */
+
+ if (tis_close())
+ return TPM_DRIVER_ERR;
+
+ /* now request access to locality. */
+ tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access);
+
+ /* did we get a lock? */
+ if (tis_wait_reg(&lpc_tpm_dev[locality].access,
+ TIS_ACCESS_ACTIVE_LOCALITY,
+ TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) {
+ printf("%s:%d - failed to lock locality %d\n",
+ __FILE__, __LINE__, locality);
+ return TPM_DRIVER_ERR;
+ }
+
+ tpm_write_word(TIS_STS_COMMAND_READY,
+ &lpc_tpm_dev[locality].tpm_status);
+ return 0;
+}
+
+int tis_close(void)
+{
+ u8 locality = 0;
+
+ if (tpm_read_word(&lpc_tpm_dev[locality].access) &
+ TIS_ACCESS_ACTIVE_LOCALITY) {
+ tpm_write_word(TIS_ACCESS_ACTIVE_LOCALITY,
+ &lpc_tpm_dev[locality].access);
+
+ if (tis_wait_reg(&lpc_tpm_dev[locality].access,
+ TIS_ACCESS_ACTIVE_LOCALITY, 0) ==
+ TPM_TIMEOUT_ERR) {
+ printf("%s:%d - failed to release locality %d\n",
+ __FILE__, __LINE__, locality);
+ return TPM_DRIVER_ERR;
+ }
+ }
+ return 0;
+}
+
+int tis_sendrecv(const u8 *sendbuf, size_t send_size,
+ u8 *recvbuf, size_t *recv_len)
+{
+ if (tis_senddata(sendbuf, send_size)) {
+ printf("%s:%d failed sending data to TPM\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ return tis_readresponse(recvbuf, (u32 *)recv_len);
+}
u8 spareAsBytes[8]; /* OOB */
if (data && !spare)
- retval = mtd->write(mtd, addr, dev->data_bytes_per_chunk,
+ retval = mtd_write(mtd, addr, dev->data_bytes_per_chunk,
&dummy, data);
else if (spare) {
if (dev->param.use_nand_ecc) {
translate_spare2oob(spare, spareAsBytes);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = 8; /* temp hack */
} else {
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_RAW;
ops.ooblen = YAFFS_BYTES_PER_SPARE;
}
ops.len = data ? dev->data_bytes_per_chunk : ops.ooblen;
ops.datbuf = (u8 *)data;
ops.ooboffs = 0;
ops.oobbuf = spareAsBytes;
- retval = mtd->write_oob(mtd, addr, &ops);
+ retval = mtd_write_oob(mtd, addr, &ops);
}
if (retval == 0)
u8 spareAsBytes[8]; /* OOB */
if (data && !spare)
- retval = mtd->read(mtd, addr, dev->data_bytes_per_chunk,
+ retval = mtd_read(mtd, addr, dev->data_bytes_per_chunk,
&dummy, data);
else if (spare) {
if (dev->param.use_nand_ecc) {
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = 8; /* temp hack */
} else {
- ops.mode = MTD_OOB_RAW;
+ ops.mode = MTD_OPS_RAW;
ops.ooblen = YAFFS_BYTES_PER_SPARE;
}
ops.len = data ? dev->data_bytes_per_chunk : ops.ooblen;
ops.datbuf = data;
ops.ooboffs = 0;
ops.oobbuf = spareAsBytes;
- retval = mtd->read_oob(mtd, addr, &ops);
+ retval = mtd_read_oob(mtd, addr, &ops);
if (dev->param.use_nand_ecc)
translate_oob2spare(spare, spareAsBytes);
}
/* Todo finish off the ei if required */
- retval = mtd->erase(mtd, &ei);
+ retval = mtd_erase(mtd, &ei);
if (retval == 0)
return YAFFS_OK;
yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
}
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
ops.len = dev->param.total_bytes_per_chunk;
ops.ooboffs = 0;
ops.datbuf = (u8 *) data;
ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
- retval = mtd->write_oob(mtd, addr, &ops);
+ retval = mtd_write_oob(mtd, addr, &ops);
if (retval == 0)
return YAFFS_OK;
}
if (dev->param.inband_tags || (data && !tags))
- retval = mtd->read(mtd, addr, dev->param.total_bytes_per_chunk,
+ retval = mtd_read(mtd, addr, dev->param.total_bytes_per_chunk,
&dummy, data);
else if (tags) {
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = packed_tags_size;
ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
ops.ooboffs = 0;
ops.datbuf = data;
ops.oobbuf = local_spare;
- retval = mtd->read_oob(mtd, addr, &ops);
+ retval = mtd_read_oob(mtd, addr, &ops);
}
if (dev->param.inband_tags) {
"nandmtd2_MarkNANDBlockBad %d", blockNo);
retval =
- mtd->block_markbad(mtd,
+ mtd_block_markbad(mtd,
blockNo * dev->param.chunks_per_block *
dev->data_bytes_per_chunk);
yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_QueryNANDBlock %d", blockNo);
retval =
- mtd->block_isbad(mtd,
+ mtd_block_isbad(mtd,
blockNo * dev->param.chunks_per_block *
dev->data_bytes_per_chunk);
BOOTSTAGEF_ALLOC = 1 << 1, /* Allocate an id */
};
+/* bootstate sub-IDs used for kernel and ramdisk ranges */
+enum {
+ BOOTSTAGE_SUB_FORMAT,
+ BOOTSTAGE_SUB_FORMAT_OK,
+ BOOTSTAGE_SUB_NO_UNIT_NAME,
+ BOOTSTAGE_SUB_UNIT_NAME,
+ BOOTSTAGE_SUB_SUBNODE,
+
+ BOOTSTAGE_SUB_CHECK,
+ BOOTSTAGE_SUB_HASH = 5,
+ BOOTSTAGE_SUB_CHECK_ARCH = 5,
+ BOOTSTAGE_SUB_CHECK_ALL,
+ BOOTSTAGE_SUB_GET_DATA,
+ BOOTSTAGE_SUB_CHECK_ALL_OK = 7,
+ BOOTSTAGE_SUB_GET_DATA_OK,
+ BOOTSTAGE_SUB_LOAD,
+};
+
/*
* A list of boot stages that we know about. Each of these indicates the
* state that we are at, and the action that we are about to perform. For
BOOTSTAGE_ID_NET_DONE_ERR,
BOOTSTAGE_ID_NET_DONE,
+ BOOTSTAGE_ID_FIT_FDT_START = 90,
/*
* Boot stages related to loading a FIT image. Some of these are a
* bit wonky.
*/
- BOOTSTAGE_ID_FIT_FORMAT = 100,
- BOOTSTAGE_ID_FIT_NO_UNIT_NAME,
- BOOTSTAGE_ID_FIT_UNIT_NAME,
- BOOTSTAGE_ID_FIT_CONFIG,
- BOOTSTAGE_ID_FIT_CHECK_SUBIMAGE,
- BOOTSTAGE_ID_FIT_CHECK_HASH = 104,
-
- BOOTSTAGE_ID_FIT_CHECK_ARCH,
- BOOTSTAGE_ID_FIT_CHECK_KERNEL,
- BOOTSTAGE_ID_FIT_CHECKED,
-
- BOOTSTAGE_ID_FIT_KERNEL_INFO_ERR = 107,
- BOOTSTAGE_ID_FIT_KERNEL_INFO,
+ BOOTSTAGE_ID_FIT_KERNEL_START = 100,
+
+ BOOTSTAGE_ID_FIT_CONFIG = 110,
BOOTSTAGE_ID_FIT_TYPE,
+ BOOTSTAGE_ID_FIT_KERNEL_INFO,
BOOTSTAGE_ID_FIT_COMPRESSION,
BOOTSTAGE_ID_FIT_OS,
BOOTSTAGE_ID_FIT_LOADADDR,
BOOTSTAGE_ID_OVERWRITTEN,
- BOOTSTAGE_ID_FIT_RD_FORMAT = 120,
- BOOTSTAGE_ID_FIT_RD_FORMAT_OK,
- BOOTSTAGE_ID_FIT_RD_NO_UNIT_NAME,
- BOOTSTAGE_ID_FIT_RD_UNIT_NAME,
- BOOTSTAGE_ID_FIT_RD_SUBNODE,
-
- BOOTSTAGE_ID_FIT_RD_CHECK,
- BOOTSTAGE_ID_FIT_RD_HASH = 125,
- BOOTSTAGE_ID_FIT_RD_CHECK_ALL,
- BOOTSTAGE_ID_FIT_RD_GET_DATA,
- BOOTSTAGE_ID_FIT_RD_CHECK_ALL_OK = 127,
- BOOTSTAGE_ID_FIT_RD_GET_DATA_OK,
- BOOTSTAGE_ID_FIT_RD_LOAD,
+ /* Next 10 IDs used by BOOTSTAGE_SUB_... */
+ BOOTSTAGE_ID_FIT_RD_START = 120, /* Ramdisk stages */
BOOTSTAGE_ID_IDE_FIT_READ = 140,
BOOTSTAGE_ID_IDE_FIT_READ_OK,
int parse_line (char *, char *[]);
void init_cmd_timeout(void);
void reset_cmd_timeout(void);
-#ifdef CONFIG_MENU
-int abortboot(int bootdelay);
-#endif
extern char console_buffer[];
/* arch/$(ARCH)/lib/board.c */
#define CONFIG_SYS_HUSH_PARSER
/* Video */
-#define CONFIG_FSL_DIU_FB
#ifdef CONFIG_FSL_DIU_FB
#define CONFIG_SYS_DIU_ADDR (CONFIG_SYS_CCSRBAR + 0x10000)
#endif
#ifndef CONFIG_FSL_DIU_FB
-#define CONFIG_ATI
#endif
#ifdef CONFIG_ATI
#endif
/* Generic TPM interfaced through LPC bus */
-#define CONFIG_GENERIC_LPC_TPM
+#define CONFIG_TPM
+#define CONFIG_TPM_TIS_LPC
#define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000
/*-----------------------------------------------------------------------
/* TPM */
#define CONFIG_TPM
#define CONFIG_CMD_TPM
-#define CONFIG_INFINEON_TPM_I2C
-#define CONFIG_INFINEON_TPM_I2C_BUS 3
-#define CONFIG_INFINEON_TPM_I2C_ADDR 0x20
+#define CONFIG_TPM_TIS_I2C
+#define CONFIG_TPM_TIS_I2C_BUS_NUMBER 3
+#define CONFIG_TPM_TIS_I2C_SLAVE_ADDR 0x20
/* MMC SPL */
#define CONFIG_SPL
#define CONFIG_BOOTCOMMAND "run flashboot"
#define CONFIG_ROOTPATH "/ronetix/rootfs"
-#define CONFIG_AUTOBOOT_PROMPT "autoboot in %d seconds\n"
+#define CONFIG_AUTOBOOT_PROMPT "autoboot in %d seconds\n", bootdelay
#define CONFIG_CON_ROT "fbcon=rotate:3 "
#define CONFIG_BOOTARGS "root=/dev/mtdblock4 rootfstype=jffs2 "\
COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */
COMPAT_MAXIM_98095_CODEC, /* MAX98095 Codec */
COMPAT_INFINEON_SLB9635_TPM, /* Infineon SLB9635 TPM */
+ COMPAT_INFINEON_SLB9645_TPM, /* Infineon SLB9645 TPM */
COMPAT_COUNT,
};
#endif
void genimg_print_time(time_t timestamp);
+/* What to do with a image load address ('load = <> 'in the FIT) */
+enum fit_load_op {
+ FIT_LOAD_IGNORED, /* Ignore load address */
+ FIT_LOAD_OPTIONAL, /* Can be provided, but optional */
+ FIT_LOAD_REQUIRED, /* Must be provided */
+};
+
#ifndef USE_HOSTCC
/* Image format types, returned by _get_format() routine */
#define IMAGE_FORMAT_INVALID 0x00
int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images,
uint8_t arch, ulong *rd_start, ulong *rd_end);
-int boot_get_fdt(int flag, int argc, char * const argv[],
- bootm_headers_t *images, char **of_flat_tree, ulong *of_size);
+/**
+ * fit_image_load() - load an image from a FIT
+ *
+ * This deals with all aspects of loading an image from a FIT, including
+ * selecting the right image based on configuration, verifying it, printing
+ * out progress messages, checking the type/arch/os and optionally copying it
+ * to the right load address.
+ *
+ * @param images Boot images structure
+ * @param prop_name Property name to look up (FIT_..._PROP)
+ * @param addr Address of FIT in memory
+ * @param fit_unamep On entry this is the requested image name
+ * (e.g. "kernel@1") or NULL to use the default. On exit
+ * points to the selected image name
+ * @param fit_uname_config Requested configuration name, or NULL for the
+ * default
+ * @param arch Expected architecture (IH_ARCH_...)
+ * @param image_type Required image type (IH_TYPE_...). If this is
+ * IH_TYPE_KERNEL then we allow IH_TYPE_KERNEL_NOLOAD
+ * also.
+ * @param bootstage_id ID of starting bootstage to use for progress updates.
+ * This will be added to the BOOTSTAGE_SUB values when
+ * calling bootstage_mark()
+ * @param load_op Decribes what to do with the load address
+ * @param datap Returns address of loaded image
+ * @param lenp Returns length of loaded image
+ */
+int fit_image_load(bootm_headers_t *images, const char *prop_name, ulong addr,
+ const char **fit_unamep, const char *fit_uname_config,
+ int arch, int image_type, int bootstage_id,
+ enum fit_load_op load_op, ulong *datap, ulong *lenp);
+
+/**
+ * fit_get_node_from_config() - Look up an image a FIT by type
+ *
+ * This looks in the selected conf@ node (images->fit_uname_cfg) for a
+ * particular image type (e.g. "kernel") and then finds the image that is
+ * referred to.
+ *
+ * For example, for something like:
+ *
+ * images {
+ * kernel@1 {
+ * ...
+ * };
+ * };
+ * configurations {
+ * conf@1 {
+ * kernel = "kernel@1";
+ * };
+ * };
+ *
+ * the function will return the node offset of the kernel@1 node, assuming
+ * that conf@1 is the chosen configuration.
+ *
+ * @param images Boot images structure
+ * @param prop_name Property name to look up (FIT_..._PROP)
+ * @param addr Address of FIT in memory
+ */
+int fit_get_node_from_config(bootm_headers_t *images, const char *prop_name,
+ ulong addr);
+
+int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
+ bootm_headers_t *images,
+ char **of_flat_tree, ulong *of_size);
void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob);
int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size);
int fit_add_verification_data(void *fit);
int fit_image_verify(const void *fit, int noffset);
+int fit_config_verify(const void *fit, int conf_noffset);
int fit_all_image_verify(const void *fit);
int fit_image_check_os(const void *fit, int noffset, uint8_t os);
int fit_image_check_arch(const void *fit, int noffset, uint8_t arch);
int fit_conf_find_compat(const void *fit, const void *fdt);
int fit_conf_get_node(const void *fit, const char *conf_uname);
-int fit_conf_get_kernel_node(const void *fit, int noffset);
-int fit_conf_get_ramdisk_node(const void *fit, int noffset);
-int fit_conf_get_fdt_node(const void *fit, int noffset);
/**
* fit_conf_get_prop_node() - Get node refered to by a configuration
int calculate_hash(const void *data, int data_len, const char *algo,
uint8_t *value, int *value_len);
-#ifndef USE_HOSTCC
+/*
+ * At present we only support verification on the device
+ */
+#if defined(CONFIG_FIT_SIGNATURE)
+# ifdef USE_HOSTCC
+# define IMAGE_ENABLE_VERIFY 0
+#else
+# define IMAGE_ENABLE_VERIFY 1
+# endif
+#else
+# define IMAGE_ENABLE_VERIFY 0
+#endif
+
+#ifdef USE_HOSTCC
+# define gd_fdt_blob() NULL
+#else
+# define gd_fdt_blob() (gd->fdt_blob)
+#endif
+
+#ifdef CONFIG_FIT_BEST_MATCH
+#define IMAGE_ENABLE_BEST_MATCH 1
+#else
+#define IMAGE_ENABLE_BEST_MATCH 0
+#endif
+
static inline int fit_image_check_target_arch(const void *fdt, int node)
{
return fit_image_check_arch(fdt, node, IH_ARCH_DEFAULT);
}
-#endif /* USE_HOSTCC */
#ifdef CONFIG_FIT_VERBOSE
#define fit_unsupported(msg) printf("! %s:%d " \
#define NAND_BBT_LASTBLOCK 0x00000010
/* The bbt is at the given page, else we must scan for the bbt */
#define NAND_BBT_ABSPAGE 0x00000020
-/* The bbt is at the given page, else we must scan for the bbt */
-#define NAND_BBT_SEARCH 0x00000040
/* bbt is stored per chip on multichip devices */
#define NAND_BBT_PERCHIP 0x00000080
/* bbt has a version counter at offset veroffs */
#define NAND_BBT_VERSION 0x00000100
/* Create a bbt if none exists */
#define NAND_BBT_CREATE 0x00000200
+/*
+ * Create an empty BBT with no vendor information. Vendor's information may be
+ * unavailable, for example, if the NAND controller has a different data and OOB
+ * layout or if this information is already purged. Must be used in conjunction
+ * with NAND_BBT_CREATE.
+ */
+#define NAND_BBT_CREATE_EMPTY 0x00000400
/* Search good / bad pattern through all pages of a block */
-#define NAND_BBT_SCANALLPAGES 0x00000400
+#define NAND_BBT_SCANALLPAGES 0x00000800
/* Scan block empty during good / bad block scan */
-#define NAND_BBT_SCANEMPTY 0x00000800
+#define NAND_BBT_SCANEMPTY 0x00001000
/* Write bbt if neccecary */
-#define NAND_BBT_WRITE 0x00001000
+#define NAND_BBT_WRITE 0x00002000
/* Read and write back block contents when writing bbt */
-#define NAND_BBT_SAVECONTENT 0x00002000
+#define NAND_BBT_SAVECONTENT 0x00004000
/* Search good / bad pattern on the first and the second page */
-#define NAND_BBT_SCAN2NDPAGE 0x00004000
+#define NAND_BBT_SCAN2NDPAGE 0x00008000
/* Search good / bad pattern on the last page of the eraseblock */
-#define NAND_BBT_SCANLASTPAGE 0x00008000
-/* Chip stores bad block marker on BOTH 1st and 6th bytes of OOB */
-#define NAND_BBT_SCANBYTE1AND6 0x00100000
-/* The nand_bbt_descr was created dynamicaly and must be freed */
-#define NAND_BBT_DYNAMICSTRUCT 0x00200000
-/* The bad block table does not OOB for marker */
-#define NAND_BBT_NO_OOB 0x00400000
+#define NAND_BBT_SCANLASTPAGE 0x00010000
+/*
+ * Use a flash based bad block table. By default, OOB identifier is saved in
+ * OOB area. This option is passed to the default bad block table function.
+ */
+#define NAND_BBT_USE_FLASH 0x00020000
+/*
+ * Do not store flash based bad block table marker in the OOB area; store it
+ * in-band.
+ */
+#define NAND_BBT_NO_OOB 0x00040000
+/*
+ * Do not write new bad block markers to OOB; useful, e.g., when ECC covers
+ * entire spare area. Must be used with NAND_BBT_USE_FLASH.
+ */
+#define NAND_BBT_NO_OOB_BBM 0x00080000
+
+/*
+ * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
+ * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
+ * in nand_chip.bbt_options.
+ */
+#define NAND_BBT_DYNAMICSTRUCT 0x80000000
/* The maximum number of blocks to scan for a bbt */
#define NAND_BBT_SCAN_MAXBLOCKS 4
+++ /dev/null
-/*
- * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $
- *
- * Portions of MTD ABI definition which are shared by kernel and user space
- */
-
-#ifndef __MTD_ABI_H__
-#define __MTD_ABI_H__
-
-#if 1
-#include <linux/compat.h>
-#endif
-
-#include <linux/compiler.h>
-
-struct erase_info_user {
- uint32_t start;
- uint32_t length;
-};
-
-struct mtd_oob_buf {
- uint32_t start;
- uint32_t length;
- unsigned char __user *ptr;
-};
-
-#define MTD_ABSENT 0
-#define MTD_RAM 1
-#define MTD_ROM 2
-#define MTD_NORFLASH 3
-#define MTD_NANDFLASH 4
-#define MTD_DATAFLASH 6
-#define MTD_UBIVOLUME 7
-
-#define MTD_WRITEABLE 0x400 /* Device is writeable */
-#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
-#define MTD_NO_ERASE 0x1000 /* No erase necessary */
-#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */
-
-/* Some common devices / combinations of capabilities */
-#define MTD_CAP_ROM 0
-#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
-#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
-#define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
-
-/* ECC byte placement */
-#define MTD_NANDECC_OFF 0 /* Switch off ECC (Not recommended) */
-#define MTD_NANDECC_PLACE 1 /* Use the given placement in the structure (YAFFS1 legacy mode) */
-#define MTD_NANDECC_AUTOPLACE 2 /* Use the default placement scheme */
-#define MTD_NANDECC_PLACEONLY 3 /* Use the given placement in the structure (Do not store ecc result on read) */
-#define MTD_NANDECC_AUTOPL_USR 4 /* Use the given autoplacement scheme rather than using the default */
-
-/* OTP mode selection */
-#define MTD_OTP_OFF 0
-#define MTD_OTP_FACTORY 1
-#define MTD_OTP_USER 2
-
-struct mtd_info_user {
- uint8_t type;
- uint32_t flags;
- uint32_t size; /* Total size of the MTD */
- uint32_t erasesize;
- uint32_t writesize;
- uint32_t oobsize; /* Amount of OOB data per block (e.g. 16) */
- /* The below two fields are obsolete and broken, do not use them
- * (TODO: remove at some point) */
- uint32_t ecctype;
- uint32_t eccsize;
-};
-
-struct region_info_user {
- uint32_t offset; /* At which this region starts,
- * from the beginning of the MTD */
- uint32_t erasesize; /* For this region */
- uint32_t numblocks; /* Number of blocks in this region */
- uint32_t regionindex;
-};
-
-struct otp_info {
- uint32_t start;
- uint32_t length;
- uint32_t locked;
-};
-
-#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
-#define MEMERASE _IOW('M', 2, struct erase_info_user)
-#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
-#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
-#define MEMLOCK _IOW('M', 5, struct erase_info_user)
-#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
-#define MEMGETREGIONCOUNT _IOR('M', 7, int)
-#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
-#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
-#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
-#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
-#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
-#define OTPSELECT _IOR('M', 13, int)
-#define OTPGETREGIONCOUNT _IOW('M', 14, int)
-#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
-#define OTPLOCK _IOR('M', 16, struct otp_info)
-#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
-#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
-#define MTDFILEMODE _IO('M', 19)
-
-/*
- * Obsolete legacy interface. Keep it in order not to break userspace
- * interfaces
- */
-struct nand_oobinfo {
- uint32_t useecc;
- uint32_t eccbytes;
- uint32_t oobfree[8][2];
- uint32_t eccpos[48];
-};
-
-struct nand_oobfree {
- uint32_t offset;
- uint32_t length;
-};
-
-#define MTD_MAX_OOBFREE_ENTRIES 8
-/*
- * ECC layout control structure. Exported to userspace for
- * diagnosis and to allow creation of raw images
- */
-struct nand_ecclayout {
- uint32_t eccbytes;
- uint32_t eccpos[128];
- uint32_t oobavail;
- struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
-};
-
-/**
- * struct mtd_ecc_stats - error correction stats
- *
- * @corrected: number of corrected bits
- * @failed: number of uncorrectable errors
- * @badblocks: number of bad blocks in this partition
- * @bbtblocks: number of blocks reserved for bad block tables
- */
-struct mtd_ecc_stats {
- uint32_t corrected;
- uint32_t failed;
- uint32_t badblocks;
- uint32_t bbtblocks;
-};
-
-/*
- * Read/write file modes for access to MTD
- */
-enum mtd_file_modes {
- MTD_MODE_NORMAL = MTD_OTP_OFF,
- MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
- MTD_MODE_OTP_USER = MTD_OTP_USER,
- MTD_MODE_RAW,
-};
-
-#endif /* __MTD_ABI_H__ */
#include <linux/types.h>
#include <div64.h>
-#include <linux/mtd/mtd-abi.h>
+#include <mtd/mtd-abi.h>
+#include <asm/errno.h>
#define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
unsigned long *lockmap; /* If keeping bitmap of locks */
};
-/*
- * oob operation modes
- *
- * MTD_OOB_PLACE: oob data are placed at the given offset
- * MTD_OOB_AUTO: oob data are automatically placed at the free areas
- * which are defined by the ecclayout
- * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data
- * is inserted into the data. Thats a raw image of the
- * flash contents.
- */
-typedef enum {
- MTD_OOB_PLACE,
- MTD_OOB_AUTO,
- MTD_OOB_RAW,
-} mtd_oob_mode_t;
-
/**
* struct mtd_oob_ops - oob operation operands
* @mode: operation mode
* @ooblen: number of oob bytes to write/read
* @oobretlen: number of oob bytes written/read
* @ooboffs: offset of oob data in the oob area (only relevant when
- * mode = MTD_OOB_PLACE)
+ * mode = MTD_OPS_PLACE_OOB or MTD_OPS_RAW)
* @datbuf: data buffer - if NULL only oob data are read/written
* @oobbuf: oob data buffer
*
* OOB area.
*/
struct mtd_oob_ops {
- mtd_oob_mode_t mode;
+ unsigned int mode;
size_t len;
size_t retlen;
size_t ooblen;
u_int32_t oobsize; /* Amount of OOB data per block (e.g. 16) */
u_int32_t oobavail; /* Available OOB bytes per block */
+ /*
+ * read ops return -EUCLEAN if max number of bitflips corrected on any
+ * one region comprising an ecc step equals or exceeds this value.
+ * Settable by driver, else defaults to ecc_strength. User can override
+ * in sysfs. N.B. The meaning of the -EUCLEAN return code has changed;
+ * see Documentation/ABI/testing/sysfs-class-mtd for more detail.
+ */
+ unsigned int bitflip_threshold;
+
/* Kernel-only stuff starts here. */
const char *name;
int index;
- /* ecc layout structure pointer - read only ! */
+ /* ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
+ /* max number of correctible bit errors per ecc step */
+ unsigned int ecc_strength;
+
/* Data for variable erase regions. If numeraseregions is zero,
* it means that the whole device has erasesize as given above.
*/
struct mtd_erase_region_info *eraseregions;
/*
- * Erase is an asynchronous operation. Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
+ * Do not call via these pointers, use corresponding mtd_*()
+ * wrappers instead.
*/
- int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
-
- /* This stuff for eXecute-In-Place */
- /* phys is optional and may be set to NULL */
- int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
+ int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
+ int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, phys_addr_t *phys);
-
- /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
- void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
-
-
- int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+ void (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
+ int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+ int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
/* In blackbox flight recorder like scenarios we want to make successful
writes in interrupt context. panic_write() is only intended to be
longer, this function can break locks and delay to ensure the write
succeeds (but not sleep). */
- int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+ int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
- int (*read_oob) (struct mtd_info *mtd, loff_t from,
+ int (*_read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
- int (*write_oob) (struct mtd_info *mtd, loff_t to,
+ int (*_write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
-
+ int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
+ size_t len);
+ int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf);
+ int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
+ size_t len);
+ int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf);
+ int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, u_char *buf);
+ int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
+ size_t len);
+ void (*_sync) (struct mtd_info *mtd);
+ int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
+ int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
+ int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
+ int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
/*
- * Methods to access the protection register area, present in some
- * flash devices. The user data is one time programmable but the
- * factory data is read only.
+ * If the driver is something smart, like UBI, it may need to maintain
+ * its own reference counting. The below functions are only for driver.
*/
- int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
+ int (*_get_device) (struct mtd_info *mtd);
+ void (*_put_device) (struct mtd_info *mtd);
/* XXX U-BOOT XXX */
#if 0
*/
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
#endif
-
- /* Sync */
- void (*sync) (struct mtd_info *mtd);
-
- /* Chip-supported device locking */
- int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
- int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
-
- /* Bad block management functions */
- int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
- int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
-
/* XXX U-BOOT XXX */
#if 0
struct notifier_block reboot_notifier; /* default mode before reboot */
struct module *owner;
int usecount;
-
- /* If the driver is something smart, like UBI, it may need to maintain
- * its own reference counting. The below functions are only for driver.
- * The driver may register its callbacks. These callbacks are not
- * supposed to be called by MTD users */
- int (*get_device) (struct mtd_info *mtd);
- void (*put_device) (struct mtd_info *mtd);
};
+int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
+int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ u_char *buf);
+int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u_char *buf);
+int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u_char *buf);
+
+int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
+
+static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ ops->retlen = ops->oobretlen = 0;
+ if (!mtd->_write_oob)
+ return -EOPNOTSUPP;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ return mtd->_write_oob(mtd, to, ops);
+}
+
+int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+ size_t len);
+int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+ size_t len);
+int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, u_char *buf);
+int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len);
+
+/* XXX U-BOOT XXX */
+#if 0
+int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen);
+#endif
+
+static inline void mtd_sync(struct mtd_info *mtd)
+{
+ if (mtd->_sync)
+ mtd->_sync(mtd);
+}
+
+int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
+int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
+
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
{
do_div(sz, mtd->erasesize);
return do_div(sz, mtd->erasesize);
}
+static inline int mtd_has_oob(const struct mtd_info *mtd)
+{
+ return mtd->_read_oob && mtd->_write_oob;
+}
+
+static inline int mtd_can_have_bb(const struct mtd_info *mtd)
+{
+ return !!mtd->_block_isbad;
+}
+
/* Kernel-side ioctl definitions */
extern int add_mtd_device(struct mtd_info *mtd);
extern void register_mtd_user (struct mtd_notifier *new);
extern int unregister_mtd_user (struct mtd_notifier *old);
-
-int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
- unsigned long count, loff_t to, size_t *retlen);
-
-int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
- unsigned long count, loff_t from, size_t *retlen);
#endif
#ifdef CONFIG_MTD_PARTITIONS
#define MTD_DEBUG_LEVEL3 (3) /* Noisy */
#ifdef CONFIG_MTD_DEBUG
+#define pr_debug(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
#define MTDDEBUG(n, args...) \
do { \
if (n <= CONFIG_MTD_DEBUG_VERBOSE) \
printk(KERN_INFO args); \
} while(0)
#else /* CONFIG_MTD_DEBUG */
+#define pr_debug(args...)
#define MTDDEBUG(n, args...) \
do { \
if (0) \
printk(KERN_INFO args); \
} while(0)
#endif /* CONFIG_MTD_DEBUG */
+#define pr_info(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
+#define pr_warn(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
+#define pr_err(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args)
+
+static inline int mtd_is_bitflip(int err) {
+ return err == -EUCLEAN;
+}
+
+static inline int mtd_is_eccerr(int err) {
+ return err == -EBADMSG;
+}
+
+static inline int mtd_is_bitflip_or_eccerr(int err) {
+ return mtd_is_bitflip(err) || mtd_is_eccerr(err);
+}
#endif /* __MTD_MTD_H__ */
* is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly.
*/
-#define NAND_MAX_OOBSIZE 576
+#define NAND_MAX_OOBSIZE 640
#define NAND_MAX_PAGESIZE 8192
/*
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_PARAM 0xec
+#define NAND_CMD_GET_FEATURES 0xee
+#define NAND_CMD_SET_FEATURES 0xef
#define NAND_CMD_RESET 0xff
#define NAND_CMD_LOCK 0x2a
#define NAND_ECC_READ 0
/* Reset Hardware ECC for write */
#define NAND_ECC_WRITE 1
-/* Enable Hardware ECC before syndrom is read back from flash */
+/* Enable Hardware ECC before syndrome is read back from flash */
#define NAND_ECC_READSYN 2
/* Bit mask for flags passed to do_nand_read_ecc */
* Option constants for bizarre disfunctionality and real
* features.
*/
-/* Chip can not auto increment pages */
-#define NAND_NO_AUTOINCR 0x00000001
-/* Buswitdh is 16 bit */
+/* Buswidth is 16 bit */
#define NAND_BUSWIDTH_16 0x00000002
/* Device supports partial programming without padding */
#define NAND_NO_PADDING 0x00000004
* This happens with the Renesas AG-AND chips, possibly others.
*/
#define BBT_AUTO_REFRESH 0x00000080
-/*
- * Chip does not require ready check on read. true
- * for all large page devices, as they do not support
- * autoincrement.
- */
-#define NAND_NO_READRDY 0x00000100
/* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE 0x00000200
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
/* Macros to identify the above */
-#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ))
/* Non chip related options */
-/*
- * Use a flash based bad block table. OOB identifier is saved in OOB area.
- * This option is passed to the default bad block table function.
- */
-#define NAND_USE_FLASH_BBT 0x00010000
/* This option skips the bbt scan during initialization. */
-#define NAND_SKIP_BBTSCAN 0x00020000
+#define NAND_SKIP_BBTSCAN 0x00010000
/*
* This option is defined if the board driver allocates its own buffers
* (e.g. because it needs them DMA-coherent).
*/
-#define NAND_OWN_BUFFERS 0x00040000
+#define NAND_OWN_BUFFERS 0x00020000
/* Chip may not exist, so silence any errors in scan */
-#define NAND_SCAN_SILENT_NODEV 0x00080000
-/*
- * If passed additionally to NAND_USE_FLASH_BBT then BBT code will not touch
- * the OOB area.
- */
-#define NAND_USE_FLASH_BBT_NO_OOB 0x00800000
-/* Create an empty BBT with no vendor information if the BBT is available */
-#define NAND_CREATE_EMPTY_BBT 0x01000000
+#define NAND_SCAN_SILENT_NODEV 0x00040000
/* Options set by nand scan */
/* bbt has already been read */
/* Keep gcc happy */
struct nand_chip;
+/* ONFI timing mode, used in both asynchronous and synchronous mode */
+#define ONFI_TIMING_MODE_0 (1 << 0)
+#define ONFI_TIMING_MODE_1 (1 << 1)
+#define ONFI_TIMING_MODE_2 (1 << 2)
+#define ONFI_TIMING_MODE_3 (1 << 3)
+#define ONFI_TIMING_MODE_4 (1 << 4)
+#define ONFI_TIMING_MODE_5 (1 << 5)
+#define ONFI_TIMING_MODE_UNKNOWN (1 << 6)
+
+/* ONFI feature address */
+#define ONFI_FEATURE_ADDR_TIMING_MODE 0x1
+
+/* ONFI subfeature parameters length */
+#define ONFI_SUBFEATURE_PARAM_LEN 4
+
struct nand_onfi_params {
/* rev info and features block */
/* 'O' 'N' 'F' 'I' */
};
/**
- * struct nand_ecc_ctrl - Control structure for ecc
- * @mode: ecc mode
- * @steps: number of ecc steps per page
- * @size: data bytes per ecc step
- * @bytes: ecc bytes per step
- * @total: total number of ecc bytes per page
- * @prepad: padding information for syndrome based ecc generators
- * @postpad: padding information for syndrome based ecc generators
+ * struct nand_ecc_ctrl - Control structure for ECC
+ * @mode: ECC mode
+ * @steps: number of ECC steps per page
+ * @size: data bytes per ECC step
+ * @bytes: ECC bytes per step
+ * @strength: max number of correctible bits per ECC step
+ * @total: total number of ECC bytes per page
+ * @prepad: padding information for syndrome based ECC generators
+ * @postpad: padding information for syndrome based ECC generators
* @layout: ECC layout control struct pointer
- * @priv: pointer to private ecc control data
- * @hwctl: function to control hardware ecc generator. Must only
+ * @priv: pointer to private ECC control data
+ * @hwctl: function to control hardware ECC generator. Must only
* be provided if an hardware ECC is available
- * @calculate: function for ecc calculation or readback from ecc hardware
- * @correct: function for ecc correction, matching to ecc generator (sw/hw)
+ * @calculate: function for ECC calculation or readback from ECC hardware
+ * @correct: function for ECC correction, matching to ECC generator (sw/hw)
* @read_page_raw: function to read a raw page without ECC
* @write_page_raw: function to write a raw page without ECC
- * @read_page: function to read a page according to the ecc generator
- * requirements.
- * @read_subpage: function to read parts of the page covered by ECC.
- * @write_page: function to write a page according to the ecc generator
+ * @read_page: function to read a page according to the ECC generator
+ * requirements; returns maximum number of bitflips corrected in
+ * any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
+ * @read_subpage: function to read parts of the page covered by ECC;
+ * returns same as read_page()
+ * @write_page: function to write a page according to the ECC generator
* requirements.
+ * @write_oob_raw: function to write chip OOB data without ECC
+ * @read_oob_raw: function to read chip OOB data without ECC
* @read_oob: function to read chip OOB data
* @write_oob: function to write chip OOB data
*/
int size;
int bytes;
int total;
+ int strength;
int prepad;
int postpad;
struct nand_ecclayout *layout;
int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
uint8_t *calc_ecc);
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page);
- void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf);
+ uint8_t *buf, int oob_required, int page);
+ int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required);
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int page);
+ uint8_t *buf, int oob_required, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf);
- void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf);
- int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page,
- int sndcmd);
+ int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required);
+ int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+ int page);
+ int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+ int page);
+ int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);
int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
int page);
};
/**
* struct nand_buffers - buffer structure for read/write
- * @ecccalc: buffer for calculated ecc
- * @ecccode: buffer for ecc read from flash
+ * @ecccalc: buffer for calculated ECC
+ * @ecccode: buffer for ECC read from flash
* @databuf: buffer for data - dynamically sized
*
* Do not change the order of buffers. databuf and oobrbuf must be in
* mtd->oobsize, mtd->writesize and so on.
* @id_data contains the 8 bytes values of NAND_CMD_READID.
* Return with the bus width.
- * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing
+ * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accessing
* device ready/busy line. If set to NULL no access to
* ready/busy is available and the ready/busy information
* is read from the chip status register.
* commands to the chip.
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
* ready.
- * @ecc: [BOARDSPECIFIC] ecc control ctructure
+ * @ecc: [BOARDSPECIFIC] ECC control structure
* @buffers: buffer structure for read/write
* @hwcontrol: platform-specific hardware control structure
- * @ops: oob operation operands
* @erase_cmd: [INTERN] erase command write function, selectable due
* to AND support.
* @scan_bbt: [REPLACEABLE] function to scan bad block table
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring
* data from array to read regs (tR).
* @state: [INTERN] the current state of the NAND device
- * @oob_poi: poison value buffer
+ * @oob_poi: "poison value buffer," used for laying out OOB data
+ * before writing
* @page_shift: [INTERN] number of address bits in a page (column
* address bits).
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
* @options: [BOARDSPECIFIC] various chip options. They can partly
* be set to inform nand_scan about special functionality.
* See the defines for further explanation.
+ * @bbt_options: [INTERN] bad block specific options. All options used
+ * here must come from bbm.h. By default, these options
+ * will be copied to the appropriate nand_bbt_descr's.
* @badblockpos: [INTERN] position of the bad block marker in the oob
* area.
- * @badblockbits: [INTERN] number of bits to left-shift the bad block
- * number
+ * @badblockbits: [INTERN] minimum number of set bits in a good block's
+ * bad block marker position; i.e., BBM == 11110111b is
+ * not bad when badblockbits == 7
* @cellinfo: [INTERN] MLC/multichip data from chip ident
* @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays
* non 0 if ONFI supported.
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
* supported, 0 otherwise.
- * @ecclayout: [REPLACEABLE] the default ecc placement scheme
+ * @onfi_set_features [REPLACEABLE] set the features for ONFI nand
+ * @onfi_get_features [REPLACEABLE] get the features for ONFI nand
+ * @ecclayout: [REPLACEABLE] the default ECC placement scheme
* @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
* lookup.
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
* bad block scan.
* @controller: [REPLACEABLE] a pointer to a hardware controller
- * structure which is shared among multiple independend
+ * structure which is shared among multiple independent
* devices.
- * @priv: [OPTIONAL] pointer to private chip date
+ * @priv: [OPTIONAL] pointer to private chip data
* @errstat: [OPTIONAL] hardware specific function to perform
* additional error status checks (determine if errors are
* correctable).
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int page, int cached, int raw);
+ const uint8_t *buf, int oob_required, int page,
+ int cached, int raw);
+ int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
+ int feature_addr, uint8_t *subfeature_para);
+ int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
+ int feature_addr, uint8_t *subfeature_para);
int chip_delay;
unsigned int options;
+ unsigned int bbt_options;
int page_shift;
int phys_erase_shift;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
- struct mtd_oob_ops ops;
-
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c
#define NAND_MFR_AMD 0x01
+#define NAND_MFR_MACRONIX 0xc2
+#define NAND_MFR_EON 0x92
/**
* struct nand_flash_dev - NAND Flash Device ID Structure
* @partitions: mtd partition list
* @chip_delay: R/B delay value in us
* @options: Option flags, e.g. 16bit buswidth
- * @ecclayout: ecc layout info structure
+ * @bbt_options: BBT option flags, e.g. NAND_BBT_USE_FLASH
+ * @ecclayout: ECC layout info structure
* @part_probe_types: NULL-terminated array of probe types
- * @priv: hardware controller specific settings
*/
struct platform_nand_chip {
int nr_chips;
struct nand_ecclayout *ecclayout;
int chip_delay;
unsigned int options;
+ unsigned int bbt_options;
const char **part_probe_types;
- void *priv;
};
/* Keep gcc happy */
int (*dev_ready)(struct mtd_info *mtd);
void (*select_chip)(struct mtd_info *mtd, int chip);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
+ unsigned char (*read_byte)(struct mtd_info *mtd);
void *priv;
};
void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len);
uint8_t nand_read_byte(struct mtd_info *mtd);
+/* return the supported asynchronous timing mode. */
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+static inline int onfi_get_async_timing_mode(struct nand_chip *chip)
+{
+ if (!chip->onfi_version)
+ return ONFI_TIMING_MODE_UNKNOWN;
+ return le16_to_cpu(chip->onfi_params.async_timing_mode);
+}
+
+/* return the supported synchronous timing mode. */
+static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)
+{
+ if (!chip->onfi_version)
+ return ONFI_TIMING_MODE_UNKNOWN;
+ return le16_to_cpu(chip->onfi_params.src_sync_timing_mode);
+}
+#endif
+
#endif /* __LINUX_MTD_NAND_H */
#ifndef __HAVE_ARCH_MEMCHR
extern void * memchr(const void *,int,__kernel_size_t);
#endif
+#ifndef __HAVE_ARCH_MEMCHR_INV
+void *memchr_inv(const void *, int, size_t);
+#endif
#ifdef __cplusplus
}
--- /dev/null
+/*
+ * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $
+ *
+ * Portions of MTD ABI definition which are shared by kernel and user space
+ */
+
+#ifndef __MTD_ABI_H__
+#define __MTD_ABI_H__
+
+#if 1
+#include <linux/compat.h>
+#endif
+
+#include <linux/compiler.h>
+
+struct erase_info_user {
+ uint32_t start;
+ uint32_t length;
+};
+
+struct mtd_oob_buf {
+ uint32_t start;
+ uint32_t length;
+ unsigned char __user *ptr;
+};
+
+/*
+ * MTD operation modes
+ *
+ * @MTD_OPS_PLACE_OOB: OOB data are placed at the given offset (default)
+ * @MTD_OPS_AUTO_OOB: OOB data are automatically placed at the free areas
+ * which are defined by the internal ecclayout
+ * @MTD_OPS_RAW: data are transferred as-is, with no error correction;
+ * this mode implies %MTD_OPS_PLACE_OOB
+ *
+ * These modes can be passed to ioctl(MEMWRITE) and are also used internally.
+ * See notes on "MTD file modes" for discussion on %MTD_OPS_RAW vs.
+ * %MTD_FILE_MODE_RAW.
+ */
+enum {
+ MTD_OPS_PLACE_OOB = 0,
+ MTD_OPS_AUTO_OOB = 1,
+ MTD_OPS_RAW = 2,
+};
+
+#define MTD_ABSENT 0
+#define MTD_RAM 1
+#define MTD_ROM 2
+#define MTD_NORFLASH 3
+#define MTD_NANDFLASH 4
+#define MTD_DATAFLASH 6
+#define MTD_UBIVOLUME 7
+
+#define MTD_WRITEABLE 0x400 /* Device is writeable */
+#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
+#define MTD_NO_ERASE 0x1000 /* No erase necessary */
+#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */
+
+/* Some common devices / combinations of capabilities */
+#define MTD_CAP_ROM 0
+#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
+#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
+#define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
+
+/* ECC byte placement */
+#define MTD_NANDECC_OFF 0 /* Switch off ECC (Not recommended) */
+#define MTD_NANDECC_PLACE 1 /* Use the given placement in the structure (YAFFS1 legacy mode) */
+#define MTD_NANDECC_AUTOPLACE 2 /* Use the default placement scheme */
+#define MTD_NANDECC_PLACEONLY 3 /* Use the given placement in the structure (Do not store ecc result on read) */
+#define MTD_NANDECC_AUTOPL_USR 4 /* Use the given autoplacement scheme rather than using the default */
+
+/* OTP mode selection */
+#define MTD_OTP_OFF 0
+#define MTD_OTP_FACTORY 1
+#define MTD_OTP_USER 2
+
+struct mtd_info_user {
+ uint8_t type;
+ uint32_t flags;
+ uint32_t size; /* Total size of the MTD */
+ uint32_t erasesize;
+ uint32_t writesize;
+ uint32_t oobsize; /* Amount of OOB data per block (e.g. 16) */
+ /* The below two fields are obsolete and broken, do not use them
+ * (TODO: remove at some point) */
+ uint32_t ecctype;
+ uint32_t eccsize;
+};
+
+struct region_info_user {
+ uint32_t offset; /* At which this region starts,
+ * from the beginning of the MTD */
+ uint32_t erasesize; /* For this region */
+ uint32_t numblocks; /* Number of blocks in this region */
+ uint32_t regionindex;
+};
+
+struct otp_info {
+ uint32_t start;
+ uint32_t length;
+ uint32_t locked;
+};
+
+/* Get basic MTD characteristics info (better to use sysfs) */
+#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
+/* Erase segment of MTD */
+#define MEMERASE _IOW('M', 2, struct erase_info_user)
+/* Write out-of-band data from MTD */
+#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
+/* Read out-of-band data from MTD */
+#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
+/* Lock a chip (for MTD that supports it) */
+#define MEMLOCK _IOW('M', 5, struct erase_info_user)
+/* Unlock a chip (for MTD that supports it) */
+#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
+/* Get the number of different erase regions */
+#define MEMGETREGIONCOUNT _IOR('M', 7, int)
+/* Get information about the erase region for a specific index */
+#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
+/* Get info about OOB modes (e.g., RAW, PLACE, AUTO) - legacy interface */
+#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
+#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
+/* Check if an eraseblock is bad */
+#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
+/* Mark an eraseblock as bad */
+#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
+/* Set OTP (One-Time Programmable) mode (factory vs. user) */
+#define OTPSELECT _IOR('M', 13, int)
+/* Get number of OTP (One-Time Programmable) regions */
+#define OTPGETREGIONCOUNT _IOW('M', 14, int)
+/* Get all OTP (One-Time Programmable) info about MTD */
+#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
+/* Lock a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */
+#define OTPLOCK _IOR('M', 16, struct otp_info)
+/* Get ECC layout (deprecated) */
+#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
+/* Get statistics about corrected/uncorrected errors */
+#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
+/* Set MTD mode on a per-file-descriptor basis (see "MTD file modes") */
+#define MTDFILEMODE _IO('M', 19)
+
+/*
+ * Obsolete legacy interface. Keep it in order not to break userspace
+ * interfaces
+ */
+struct nand_oobinfo {
+ uint32_t useecc;
+ uint32_t eccbytes;
+ uint32_t oobfree[8][2];
+ uint32_t eccpos[48];
+};
+
+struct nand_oobfree {
+ uint32_t offset;
+ uint32_t length;
+};
+
+#define MTD_MAX_OOBFREE_ENTRIES 8
+/*
+ * ECC layout control structure. Exported to userspace for
+ * diagnosis and to allow creation of raw images
+ */
+struct nand_ecclayout {
+ uint32_t eccbytes;
+ uint32_t eccpos[128];
+ uint32_t oobavail;
+ struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
+};
+
+/**
+ * struct mtd_ecc_stats - error correction stats
+ *
+ * @corrected: number of corrected bits
+ * @failed: number of uncorrectable errors
+ * @badblocks: number of bad blocks in this partition
+ * @bbtblocks: number of blocks reserved for bad block tables
+ */
+struct mtd_ecc_stats {
+ uint32_t corrected;
+ uint32_t failed;
+ uint32_t badblocks;
+ uint32_t bbtblocks;
+};
+
+/*
+ * MTD file modes - for read/write access to MTD
+ *
+ * @MTD_FILE_MODE_NORMAL: OTP disabled, ECC enabled
+ * @MTD_FILE_MODE_OTP_FACTORY: OTP enabled in factory mode
+ * @MTD_FILE_MODE_OTP_USER: OTP enabled in user mode
+ * @MTD_FILE_MODE_RAW: OTP disabled, ECC disabled
+ *
+ * These modes can be set via ioctl(MTDFILEMODE). The mode mode will be retained
+ * separately for each open file descriptor.
+ *
+ * Note: %MTD_FILE_MODE_RAW provides the same functionality as %MTD_OPS_RAW -
+ * raw access to the flash, without error correction or autoplacement schemes.
+ * Wherever possible, the MTD_OPS_* mode will override the MTD_FILE_MODE_* mode
+ * (e.g., when using ioctl(MEMWRITE)), but in some cases, the MTD_FILE_MODE is
+ * used out of necessity (e.g., `write()', ioctl(MEMWRITEOOB64)).
+ */
+enum mtd_file_modes {
+ MTD_MODE_NORMAL = MTD_OTP_OFF,
+ MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
+ MTD_MODE_OTP_USER = MTD_OTP_USER,
+ MTD_MODE_RAW,
+};
+
+#endif /* __MTD_ABI_H__ */
* at the same time, so do it here. When all drivers are
* converted, this will go away.
*/
-#if defined(CONFIG_NAND_FSL_ELBC) || defined(CONFIG_NAND_ATMEL)
+#if defined(CONFIG_NAND_FSL_ELBC) || defined(CONFIG_NAND_ATMEL)\
+ || defined(CONFIG_NAND_FSL_IFC)
#define CONFIG_SYS_NAND_SELF_INIT
#endif
static inline int nand_read(nand_info_t *info, loff_t ofs, size_t *len, u_char *buf)
{
- return info->read(info, ofs, *len, (size_t *)len, buf);
+ return mtd_read(info, ofs, *len, (size_t *)len, buf);
}
static inline int nand_write(nand_info_t *info, loff_t ofs, size_t *len, u_char *buf)
{
- return info->write(info, ofs, *len, (size_t *)len, buf);
+ return mtd_write(info, ofs, *len, (size_t *)len, buf);
}
static inline int nand_block_isbad(nand_info_t *info, loff_t ofs)
{
- return info->block_isbad(info, ofs);
+ return mtd_block_isbad(info, ofs);
}
static inline int nand_erase(nand_info_t *info, loff_t off, size_t size)
instr.len = size;
instr.callback = 0;
- return info->erase(info, &instr);
+ return mtd_erase(info, &instr);
}
/* get a random source port */
extern unsigned int random_port(void);
+/* Update U-Boot over TFTP */
+extern int update_tftp(ulong addr);
+
/**********************************************************************/
#endif /* __NET_H__ */
COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"),
COMPAT(INFINEON_SLB9635_TPM, "infineon,slb9635-tpm"),
+ COMPAT(INFINEON_SLB9645_TPM, "infineon,slb9645-tpm"),
};
const char *fdtdec_get_compatible(enum fdt_compat_id id)
}
#endif
+#ifndef __HAVE_ARCH_MEMCHR_INV
+static void *check_bytes8(const u8 *start, u8 value, unsigned int bytes)
+{
+ while (bytes) {
+ if (*start != value)
+ return (void *)start;
+ start++;
+ bytes--;
+ }
+ return NULL;
+}
+/**
+ * memchr_inv - Find an unmatching character in an area of memory.
+ * @start: The memory area
+ * @c: Find a character other than c
+ * @bytes: The size of the area.
+ *
+ * returns the address of the first character other than @c, or %NULL
+ * if the whole buffer contains just @c.
+ */
+void *memchr_inv(const void *start, int c, size_t bytes)
+{
+ u8 value = c;
+ u64 value64;
+ unsigned int words, prefix;
+
+ if (bytes <= 16)
+ return check_bytes8(start, value, bytes);
+
+ value64 = value;
+ value64 |= value64 << 8;
+ value64 |= value64 << 16;
+ value64 |= value64 << 32;
+
+ prefix = (unsigned long)start % 8;
+ if (prefix) {
+ u8 *r;
+
+ prefix = 8 - prefix;
+ r = check_bytes8(start, value, prefix);
+ if (r)
+ return r;
+ start += prefix;
+ bytes -= prefix;
+ }
+
+ words = bytes / 8;
+
+ while (words) {
+ if (*(u64 *)start != value64)
+ return check_bytes8(start, value, 8);
+ start += 8;
+ words--;
+ }
+
+ return check_bytes8(start, value, bytes % 8);
+}
+#endif
+
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright (c) 2013, Google Inc.
+#
+# Sanity check of the FIT handling in U-Boot
+#
+# 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
+#
+# To run this:
+#
+# make O=sandbox sandbox_config
+# make O=sandbox
+# ./test/image/test-fit.py -u sandbox/u-boot
+
+import doctest
+from optparse import OptionParser
+import os
+import shutil
+import struct
+import sys
+import tempfile
+
+# The 'command' library in patman is convenient for running commands
+base_path = os.path.dirname(sys.argv[0])
+patman = os.path.join(base_path, '../../tools/patman')
+sys.path.append(patman)
+
+import command
+
+# Define a base ITS which we can adjust using % and a dictionary
+base_its = '''
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel@1 {
+ data = /incbin/("%(kernel)s");
+ type = "kernel";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x40000>;
+ entry = <0x8>;
+ };
+ fdt@1 {
+ description = "snow";
+ data = /incbin/("u-boot.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ %(fdt_load)s
+ compression = "none";
+ signature@1 {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ ramdisk@1 {
+ description = "snow";
+ data = /incbin/("%(ramdisk)s");
+ type = "ramdisk";
+ arch = "sandbox";
+ os = "linux";
+ %(ramdisk_load)s
+ compression = "none";
+ };
+ };
+ configurations {
+ default = "conf@1";
+ conf@1 {
+ kernel = "kernel@1";
+ fdt = "fdt@1";
+ %(ramdisk_config)s
+ };
+ };
+};
+'''
+
+# Define a base FDT - currently we don't use anything in this
+base_fdt = '''
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+};
+'''
+
+# This is the U-Boot script that is run for each test. First load the fit,
+# then do the 'bootm' command, then save out memory from the places where
+# we expect 'bootm' to write things. Then quit.
+base_script = '''
+sb load host 0 %(fit_addr)x %(fit)s
+fdt addr %(fit_addr)x
+bootm start %(fit_addr)x
+bootm loados
+sb save host 0 %(kernel_out)s %(kernel_addr)x %(kernel_size)x
+sb save host 0 %(fdt_out)s %(fdt_addr)x %(fdt_size)x
+sb save host 0 %(ramdisk_out)s %(ramdisk_addr)x %(ramdisk_size)x
+reset
+'''
+
+def make_fname(leaf):
+ """Make a temporary filename
+
+ Args:
+ leaf: Leaf name of file to create (within temporary directory)
+ Return:
+ Temporary filename
+ """
+ global base_dir
+
+ return os.path.join(base_dir, leaf)
+
+def filesize(fname):
+ """Get the size of a file
+
+ Args:
+ fname: Filename to check
+ Return:
+ Size of file in bytes
+ """
+ return os.stat(fname).st_size
+
+def read_file(fname):
+ """Read the contents of a file
+
+ Args:
+ fname: Filename to read
+ Returns:
+ Contents of file as a string
+ """
+ with open(fname, 'r') as fd:
+ return fd.read()
+
+def make_dtb():
+ """Make a sample .dts file and compile it to a .dtb
+
+ Returns:
+ Filename of .dtb file created
+ """
+ src = make_fname('u-boot.dts')
+ dtb = make_fname('u-boot.dtb')
+ with open(src, 'w') as fd:
+ print >>fd, base_fdt
+ command.Output('dtc', src, '-O', 'dtb', '-o', dtb)
+ return dtb
+
+def make_its(params):
+ """Make a sample .its file with parameters embedded
+
+ Args:
+ params: Dictionary containing parameters to embed in the %() strings
+ Returns:
+ Filename of .its file created
+ """
+ its = make_fname('test.its')
+ with open(its, 'w') as fd:
+ print >>fd, base_its % params
+ return its
+
+def make_fit(mkimage, params):
+ """Make a sample .fit file ready for loading
+
+ This creates a .its script with the selected parameters and uses mkimage to
+ turn this into a .fit image.
+
+ Args:
+ mkimage: Filename of 'mkimage' utility
+ params: Dictionary containing parameters to embed in the %() strings
+ Return:
+ Filename of .fit file created
+ """
+ fit = make_fname('test.fit')
+ its = make_its(params)
+ command.Output(mkimage, '-f', its, fit)
+ with open(make_fname('u-boot.dts'), 'w') as fd:
+ print >>fd, base_fdt
+ return fit
+
+def make_kernel():
+ """Make a sample kernel with test data
+
+ Returns:
+ Filename of kernel created
+ """
+ fname = make_fname('test-kernel.bin')
+ data = ''
+ for i in range(100):
+ data += 'this kernel %d is unlikely to boot\n' % i
+ with open(fname, 'w') as fd:
+ print >>fd, data
+ return fname
+
+def make_ramdisk():
+ """Make a sample ramdisk with test data
+
+ Returns:
+ Filename of ramdisk created
+ """
+ fname = make_fname('test-ramdisk.bin')
+ data = ''
+ for i in range(100):
+ data += 'ramdisk %d was seldom used in the middle ages\n' % i
+ with open(fname, 'w') as fd:
+ print >>fd, data
+ return fname
+
+def find_matching(text, match):
+ """Find a match in a line of text, and return the unmatched line portion
+
+ This is used to extract a part of a line from some text. The match string
+ is used to locate the line - we use the first line that contains that
+ match text.
+
+ Once we find a match, we discard the match string itself from the line,
+ and return what remains.
+
+ TODO: If this function becomes more generally useful, we could change it
+ to use regex and return groups.
+
+ Args:
+ text: Text to check (each line separated by \n)
+ match: String to search for
+ Return:
+ String containing unmatched portion of line
+ Exceptions:
+ ValueError: If match is not found
+
+ >>> find_matching('first line:10\\nsecond_line:20', 'first line:')
+ '10'
+ >>> find_matching('first line:10\\nsecond_line:20', 'second linex')
+ Traceback (most recent call last):
+ ...
+ ValueError: Test aborted
+ >>> find_matching('first line:10\\nsecond_line:20', 'second_line:')
+ '20'
+ """
+ for line in text.splitlines():
+ pos = line.find(match)
+ if pos != -1:
+ return line[:pos] + line[pos + len(match):]
+
+ print "Expected '%s' but not found in output:"
+ print text
+ raise ValueError('Test aborted')
+
+def set_test(name):
+ """Set the name of the current test and print a message
+
+ Args:
+ name: Name of test
+ """
+ global test_name
+
+ test_name = name
+ print name
+
+def fail(msg):
+ """Raise an error with a helpful failure message
+
+ Args:
+ msg: Message to display
+ """
+ raise ValueError("Test '%s' failed: %s" % (test_name, msg))
+
+def run_fit_test(mkimage, u_boot):
+ """Basic sanity check of FIT loading in U-Boot
+
+ TODO: Almost everything:
+ - hash algorithms - invalid hash/contents should be detected
+ - signature algorithms - invalid sig/contents should be detected
+ - compression
+ - checking that errors are detected like:
+ - image overwriting
+ - missing images
+ - invalid configurations
+ - incorrect os/arch/type fields
+ - empty data
+ - images too large/small
+ - invalid FDT (e.g. putting a random binary in instead)
+ - default configuration selection
+ - bootm command line parameters should have desired effect
+ - run code coverage to make sure we are testing all the code
+ """
+ global test_name
+
+ # Set up invariant files
+ control_dtb = make_dtb()
+ kernel = make_kernel()
+ ramdisk = make_ramdisk()
+ kernel_out = make_fname('kernel-out.bin')
+ fdt_out = make_fname('fdt-out.dtb')
+ ramdisk_out = make_fname('ramdisk-out.bin')
+
+ # Set up basic parameters with default values
+ params = {
+ 'fit_addr' : 0x1000,
+
+ 'kernel' : kernel,
+ 'kernel_out' : kernel_out,
+ 'kernel_addr' : 0x40000,
+ 'kernel_size' : filesize(kernel),
+
+ 'fdt_out' : fdt_out,
+ 'fdt_addr' : 0x80000,
+ 'fdt_size' : filesize(control_dtb),
+ 'fdt_load' : '',
+
+ 'ramdisk' : ramdisk,
+ 'ramdisk_out' : ramdisk_out,
+ 'ramdisk_addr' : 0xc0000,
+ 'ramdisk_size' : filesize(ramdisk),
+ 'ramdisk_load' : '',
+ 'ramdisk_config' : '',
+ }
+
+ # Make a basic FIT and a script to load it
+ fit = make_fit(mkimage, params)
+ params['fit'] = fit
+ cmd = base_script % params
+
+ # First check that we can load a kernel
+ # We could perhaps reduce duplication with some loss of readability
+ set_test('Kernel load')
+ stdout = command.Output(u_boot, '-d', control_dtb, '-c', cmd)
+ if read_file(kernel) != read_file(kernel_out):
+ fail('Kernel not loaded')
+ if read_file(control_dtb) == read_file(fdt_out):
+ fail('FDT loaded but should be ignored')
+ if read_file(ramdisk) == read_file(ramdisk_out):
+ fail('Ramdisk loaded but should not be')
+
+ # Find out the offset in the FIT where U-Boot has found the FDT
+ line = find_matching(stdout, 'Booting using the fdt blob at ')
+ fit_offset = int(line, 16) - params['fit_addr']
+ fdt_magic = struct.pack('>L', 0xd00dfeed)
+ data = read_file(fit)
+
+ # Now find where it actually is in the FIT (skip the first word)
+ real_fit_offset = data.find(fdt_magic, 4)
+ if fit_offset != real_fit_offset:
+ fail('U-Boot loaded FDT from offset %#x, FDT is actually at %#x' %
+ (fit_offset, real_fit_offset))
+
+ # Now a kernel and an FDT
+ set_test('Kernel + FDT load')
+ params['fdt_load'] = 'load = <%#x>;' % params['fdt_addr']
+ fit = make_fit(mkimage, params)
+ stdout = command.Output(u_boot, '-d', control_dtb, '-c', cmd)
+ if read_file(kernel) != read_file(kernel_out):
+ fail('Kernel not loaded')
+ if read_file(control_dtb) != read_file(fdt_out):
+ fail('FDT not loaded')
+ if read_file(ramdisk) == read_file(ramdisk_out):
+ fail('Ramdisk loaded but should not be')
+
+ # Try a ramdisk
+ set_test('Kernel + FDT + Ramdisk load')
+ params['ramdisk_config'] = 'ramdisk = "ramdisk@1";'
+ params['ramdisk_load'] = 'load = <%#x>;' % params['ramdisk_addr']
+ fit = make_fit(mkimage, params)
+ stdout = command.Output(u_boot, '-d', control_dtb, '-c', cmd)
+ if read_file(ramdisk) != read_file(ramdisk_out):
+ fail('Ramdisk not loaded')
+
+def run_tests():
+ """Parse options, run the FIT tests and print the result"""
+ global base_path, base_dir
+
+ # Work in a temporary directory
+ base_dir = tempfile.mkdtemp()
+ parser = OptionParser()
+ parser.add_option('-u', '--u-boot',
+ default=os.path.join(base_path, 'u-boot'),
+ help='Select U-Boot sandbox binary')
+ parser.add_option('-k', '--keep', action='store_true',
+ help="Don't delete temporary directory even when tests pass")
+ parser.add_option('-t', '--selftest', action='store_true',
+ help='Run internal self tests')
+ (options, args) = parser.parse_args()
+
+ # Find the path to U-Boot, and assume mkimage is in its tools/mkimage dir
+ base_path = os.path.dirname(options.u_boot)
+ mkimage = os.path.join(base_path, 'tools/mkimage')
+
+ # There are a few doctests - handle these here
+ if options.selftest:
+ doctest.testmod()
+ return
+
+ title = 'FIT Tests'
+ print title, '\n', '=' * len(title)
+
+ run_fit_test(mkimage, options.u_boot)
+
+ print '\nTests passed'
+ print 'Caveat: this is only a sanity check - test coverage is poor'
+
+ # Remove the tempoerary directory unless we are asked to keep it
+ if options.keep:
+ print "Output files are in '%s'" % base_dir
+ else:
+ shutil.rmtree(base_dir)
+
+run_tests()
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+static inline void *map_sysmem(ulong paddr, unsigned long len)
+{
+ return (void *)(uintptr_t)paddr;
+}
+
+static inline ulong map_to_sysmem(void *ptr)
+{
+ return (ulong)(uintptr_t)ptr;
+}
+
#define MKIMAGE_TMPFILE_SUFFIX ".tmp"
#define MKIMAGE_MAX_TMPFILE_LEN 256
#define MKIMAGE_DEFAULT_DTC_OPTIONS "-I dts -O dtb -p 500"
#define MKIMAGE_MAX_DTC_CMDLINE_LEN 512
#define MKIMAGE_DTC "dtc" /* assume dtc is in $PATH */
+#define IH_ARCH_DEFAULT IH_ARCH_INVALID
+
/*
* This structure defines all such variables those are initialized by
* mkimage main core and need to be referred by image type specific