/* * Freescale GPMI NFC NAND Flash Driver * * Copyright (C) 2010 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "gpmi-nfc.h" /* * Useful variables for Boot ROM Helper version 0. */ static const char *fingerprint = "STMP"; /** * set_geometry() - Sets geometry for the Boot ROM Helper. * * @this: Per-device data. */ static int set_geometry(struct gpmi_nfc_data *this) { struct gpmi_nfc_platform_data *pdata = this->pdata; struct physical_geometry *physical = &this->physical_geometry; struct boot_rom_geometry *geometry = &this->rom_geometry; int error; /* Version-independent geometry. */ error = gpmi_nfc_rom_helper_set_geometry(this); if (error) return error; /* * Check if the platform data indicates we are to protect the boot area. */ if (!pdata->boot_area_size_in_bytes) { geometry->boot_area_count = 0; geometry->boot_area_size_in_bytes = 0; return 0; } /* * If control arrives here, we are supposed to set up partitions to * protect the boot areas. In this version of the ROM, the number of * boot areas and their size depends on the number of chips. */ if (physical->chip_count == 1) { geometry->boot_area_count = 1; geometry->boot_area_size_in_bytes = pdata->boot_area_size_in_bytes * 2; } else { geometry->boot_area_count = 2; geometry->boot_area_size_in_bytes = pdata->boot_area_size_in_bytes; } /* Return success. */ return 0; } /** * check_transcription_stamp() - Checks for a transcription stamp. * * Returns 0 if a stamp is not found. * * @this: Per-device data. */ static int check_transcription_stamp(struct gpmi_nfc_data *this) { struct physical_geometry *physical = &this->physical_geometry; struct boot_rom_geometry *rom_geo = &this->rom_geometry; struct mil *mil = &this->mil; struct mtd_info *mtd = &mil->mtd; struct nand_chip *nand = &mil->nand; unsigned int search_area_size_in_strides; unsigned int stride; unsigned int page; loff_t byte; uint8_t *buffer = nand->buffers->databuf; int saved_chip_number; int found_an_ncb_fingerprint = false; /* Compute the number of strides in a search area. */ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; /* Select chip 0. */ saved_chip_number = mil->current_chip; nand->select_chip(mtd, 0); /* * Loop through the first search area, looking for the NCB fingerprint. */ pr_info("Scanning for an NCB fingerprint...\n"); for (stride = 0; stride < search_area_size_in_strides; stride++) { /* Compute the page and byte addresses. */ page = stride * rom_geo->stride_size_in_pages; byte = page * physical->page_data_size_in_bytes; pr_info(" Looking for a fingerprint in page 0x%x\n", page); /* * Read the NCB fingerprint. The fingerprint is four bytes long * and starts in the 12th byte of the page. */ nand->cmdfunc(mtd, NAND_CMD_READ0, 12, page); nand->read_buf(mtd, buffer, strlen(fingerprint)); /* Look for the fingerprint. */ if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { found_an_ncb_fingerprint = true; break; } } /* Deselect chip 0. */ nand->select_chip(mtd, saved_chip_number); /* Return. */ if (found_an_ncb_fingerprint) pr_info(" Found a fingerprint\n"); else pr_info(" No fingerprint found\n"); return found_an_ncb_fingerprint; } /** * write_transcription_stamp() - Writes a transcription stamp. * * @this: Per-device data. */ static int write_transcription_stamp(struct gpmi_nfc_data *this) { struct device *dev = this->dev; struct physical_geometry *physical = &this->physical_geometry; struct boot_rom_geometry *rom_geo = &this->rom_geometry; struct mil *mil = &this->mil; struct mtd_info *mtd = &mil->mtd; struct nand_chip *nand = &mil->nand; unsigned int block_size_in_pages; unsigned int search_area_size_in_strides; unsigned int search_area_size_in_pages; unsigned int search_area_size_in_blocks; unsigned int block; unsigned int stride; unsigned int page; loff_t byte; uint8_t *buffer = nand->buffers->databuf; int saved_chip_number; int status; /* Compute the search area geometry. */ block_size_in_pages = physical->block_size_in_bytes >> (ffs(physical->page_data_size_in_bytes) - 1); search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; search_area_size_in_pages = search_area_size_in_strides * rom_geo->stride_size_in_pages; search_area_size_in_blocks = (search_area_size_in_pages + (block_size_in_pages - 1)) / /*-------------------------------------------------------*/ block_size_in_pages; #if defined(DETAILED_INFO) pr_info("--------------------\n"); pr_info("Search Area Geometry\n"); pr_info("--------------------\n"); pr_info("Search Area Size in Blocks : %u", search_area_size_in_blocks); pr_info("Search Area Size in Strides: %u", search_area_size_in_strides); pr_info("Search Area Size in Pages : %u", search_area_size_in_pages); #endif /* Select chip 0. */ saved_chip_number = mil->current_chip; nand->select_chip(mtd, 0); /* Loop over blocks in the first search area, erasing them. */ pr_info("Erasing the search area...\n"); for (block = 0; block < search_area_size_in_blocks; block++) { /* Compute the page address. */ page = block * block_size_in_pages; /* Erase this block. */ pr_info(" Erasing block 0x%x\n", block); nand->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); nand->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* Wait for the erase to finish. */ status = nand->waitfunc(mtd, nand); if (status & NAND_STATUS_FAIL) dev_err(dev, "[%s] Erase failed.\n", __func__); } /* Write the NCB fingerprint into the page buffer. */ memset(buffer, ~0, mtd->writesize); memset(nand->oob_poi, ~0, mtd->oobsize); memcpy(buffer + 12, fingerprint, strlen(fingerprint)); /* Loop through the first search area, writing NCB fingerprints. */ pr_info("Writing NCB fingerprints...\n"); for (stride = 0; stride < search_area_size_in_strides; stride++) { /* Compute the page and byte addresses. */ page = stride * rom_geo->stride_size_in_pages; byte = page * physical->page_data_size_in_bytes; /* Write the first page of the current stride. */ pr_info(" Writing an NCB fingerprint in page 0x%x\n", page); nand->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); nand->ecc.write_page_raw(mtd, nand, buffer); nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); /* Wait for the write to finish. */ status = nand->waitfunc(mtd, nand); if (status & NAND_STATUS_FAIL) dev_err(dev, "[%s] Write failed.\n", __func__); } /* Deselect chip 0. */ nand->select_chip(mtd, saved_chip_number); /* Return success. */ return 0; } /* This structure represents the Boot ROM Helper for this version. */ struct boot_rom_helper gpmi_nfc_boot_rom_helper_v0 = { .version = 0, .description = "Single/dual-chip boot area, " "no block mark swapping", .swap_block_mark = false, .set_geometry = set_geometry, .check_transcription_stamp = check_transcription_stamp, .write_transcription_stamp = write_transcription_stamp, };