/* This function is very useful. It is called only when the bug occur. */ staticvoid gpmi_dump_info(struct gpmi_nand_data *this)
{ struct resources *r = &this->resources; struct bch_geometry *geo = &this->bch_geometry;
u32 reg; int i;
dev_err(this->dev, "Show GPMI registers :\n"); for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
reg = readl(r->gpmi_regs + i * 0x10);
dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
}
/* start to print out the BCH info */
dev_err(this->dev, "Show BCH registers :\n"); for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) {
reg = readl(r->bch_regs + i * 0x10);
dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
}
dev_err(this->dev, "BCH Geometry :\n" "GF length : %u\n" "ECC Strength : %u\n" "Page Size in Bytes : %u\n" "Metadata Size in Bytes : %u\n" "ECC0 Chunk Size in Bytes: %u\n" "ECCn Chunk Size in Bytes: %u\n" "ECC Chunk Count : %u\n" "Payload Size in Bytes : %u\n" "Auxiliary Size in Bytes: %u\n" "Auxiliary Status Offset: %u\n" "Block Mark Byte Offset : %u\n" "Block Mark Bit Offset : %u\n",
geo->gf_len,
geo->ecc_strength,
geo->page_size,
geo->metadata_size,
geo->ecc0_chunk_size,
geo->eccn_chunk_size,
geo->ecc_chunk_count,
geo->payload_size,
geo->auxiliary_size,
geo->auxiliary_status_offset,
geo->block_mark_byte_offset,
geo->block_mark_bit_offset);
}
/* sanity check for the minimum ecc nand required */ if (!(requirements->strength > 0 &&
requirements->step_size > 0)) return -EINVAL;
geo->ecc_strength = requirements->strength;
/* check if platform can support this nand */ if (!gpmi_check_ecc(this)) {
dev_err(this->dev, "unsupported NAND chip, minimum ecc required %d\n",
geo->ecc_strength); return -EINVAL;
}
/* calculate the maximum ecc platform can support*/
geo->metadata_size = 10;
geo->gf_len = 14;
geo->ecc0_chunk_size = 1024;
geo->eccn_chunk_size = 1024;
geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
max_ecc = min(get_ecc_strength(this), this->devdata->bch_max_ecc_strength);
/* if none of them works, keep using the minimum ecc */ /* nand required but changing ecc page layout */
geo->ecc_strength = requirements->strength; /* add extra ecc for meta data */
geo->ecc0_chunk_size = 0;
geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1;
geo->ecc_for_meta = 1; /* check if oob can afford this extra ecc chunk */ if (mtd->oobsize * 8 < geo->metadata_size * 8 +
geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) {
dev_err(this->dev, "unsupported NAND chip with new layout\n"); return -EINVAL;
}
/* We use the same ECC strength for all chunks. */
geo->ecc_strength = get_ecc_strength(this); if (!gpmi_check_ecc(this)) {
dev_err(this->dev, "ecc strength: %d cannot be supported by the controller (%d)\n" "try to use minimum ecc strength that NAND chip required\n",
geo->ecc_strength, this->devdata->bch_max_ecc_strength); return -EINVAL;
}
/* use legacy bch geometry settings by default*/ if ((!use_minimun_ecc && mtd->oobsize < 1024) ||
!(requirements->strength > 0 && requirements->step_size > 0)) {
dev_dbg(this->dev, "use legacy bch geometry\n");
err = legacy_set_geometry(this); if (!err) return0;
}
/* for large oob nand */ if (mtd->oobsize > 1024) {
dev_dbg(this->dev, "use large oob bch geometry\n");
err = set_geometry_for_large_oob(this); if (!err) return0;
}
/* otherwise use the minimum ecc nand chip required */
dev_dbg(this->dev, "use minimum ecc bch geometry\n");
err = set_geometry_by_ecc_info(this, requirements->strength,
requirements->step_size); if (err)
dev_err(this->dev, "none of the bch geometry setting works\n");
return err;
}
/* Configures the geometry for BCH. */ staticint bch_set_geometry(struct gpmi_nand_data *this)
{ struct resources *r = &this->resources; int ret;
ret = common_nfc_set_geometry(this); if (ret) return ret;
ret = pm_runtime_resume_and_get(this->dev); if (ret < 0) { return ret;
}
/* *Duetoerratum#2847oftheMX23,theBCHcannotbesoftresetonthis *chip,otherwiseitwilllockup.SoweskipresettingBCHontheMX23. *andMX28.
*/
ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); if (ret) goto err_out;
/* Set *all* chip selects to use layout 0. */
writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
ret = 0;
err_out:
pm_runtime_mark_last_busy(this->dev);
pm_runtime_put_autosuspend(this->dev);
/* Clock dividers do NOT guarantee a clean clock signal on its output *duringthechangeofthedividefactoroni.MX6Q/UL/SX.Oni.MX7/8, *allclockdividersprovidetheseguarantee.
*/ if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this))
clk_disable_unprepare(r->clock[0]);
ret = clk_set_rate(r->clock[0], hw->clk_rate); if (ret) {
dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret); return ret;
}
if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) {
ret = clk_prepare_enable(r->clock[0]); if (ret) return ret;
}
/* Wait 64 clock cycles before using the GPMI after enabling the DLL */
dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64; if (!dll_wait_time_us)
dll_wait_time_us = 1;
/* Wait for the DLL to settle. */
udelay(dll_wait_time_us);
return0;
}
staticint gpmi_setup_interface(struct nand_chip *chip, int chipnr, conststruct nand_interface_config *conf)
{ struct gpmi_nand_data *this = nand_get_controller_data(chip); conststruct nand_sdr_timings *sdr; int ret;
staticstruct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
{ /* We use the DMA channel 0 to access all the nand chips. */ returnthis->dma_chans[0];
}
/* This will be called after the DMA operation is finished. */ staticvoid dma_irq_callback(void *param)
{ struct gpmi_nand_data *this = param; struct completion *dma_c = &this->dma_done;
staticint gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len)
{ /* *raw_lenisthelengthtoread/writeincludingbchdatawhich *wearepassedinexec_op.Calculatethedatalengthfromit.
*/ if (this->bch) return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size); else return raw_len;
}
/* Can we use the upper's buffer directly for DMA? */ staticbool prepare_data_dma(struct gpmi_nand_data *this, constvoid *buf, int raw_len, struct scatterlist *sgl, enum dma_data_direction dr)
{ int ret; int len = gpmi_raw_len_to_len(this, raw_len);
/* first try to map the upper buffer directly */ if (virt_addr_valid(buf) && !object_is_on_stack(buf)) {
sg_init_one(sgl, buf, len);
ret = dma_map_sg(this->dev, sgl, 1, dr); if (ret == 0) goto map_fail;
returntrue;
}
map_fail: /* We have to use our own DMA buffer. */
sg_init_one(sgl, this->data_buffer_dma, len);
/* *Ifcontrolarriveshere,we'reswapping.Makesomeconvenience *variables.
*/
bit = nfc_geo->block_mark_bit_offset;
p = payload + nfc_geo->block_mark_byte_offset;
a = auxiliary;
staticint gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, int last, int meta)
{ struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; struct mtd_info *mtd = nand_to_mtd(chip); int i; unsignedchar *status; unsignedint max_bitflips = 0;
/* Loop over status bytes, accumulating ECC status. */
status = this->auxiliary_virt + ALIGN(meta, 4);
for (i = first; i < last; i++, status++) { if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) continue;
if (*status == STATUS_UNCORRECTABLE) { int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *eccbuf = this->raw_buffer; int offset, bitoffset; int eccbytes; int flips;
/* Fake a virtual small page for the subpage read */ staticint gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs,
uint32_t len, uint8_t *buf, int page)
{ struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *geo = &this->bch_geometry; int size = chip->ecc.size; /* ECC chunk size */ int meta, n, page_size; unsignedint max_bitflips; unsignedint ecc_strength; int first, last, marker_pos; int ecc_parity_size; int col = 0; int ret;
/* The size of ECC parity */
ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
/* Align it with the chunk size */
first = offs / size;
last = (offs + len - 1) / size;
meta = geo->metadata_size; if (first) { if (geo->ecc_for_meta)
col = meta + ecc_parity_size
+ (size + ecc_parity_size) * first; else
col = meta + (size + ecc_parity_size) * first;
meta = 0;
buf = buf + first * size;
}
ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
n = last - first + 1;
if (geo->ecc_for_meta && meta)
page_size = meta + ecc_parity_size
+ (size + ecc_parity_size) * n; else
page_size = meta + (size + ecc_parity_size) * n;
/* clear the OOB buffer */
memset(chip->oob_poi, ~0, mtd->oobsize);
/* Read out the conventional OOB. */
ret = nand_read_page_op(chip, page, mtd->writesize, chip->oob_poi,
mtd->oobsize);
if (ret)
return ret;
/*
* Now, we want to make sure the block mark is correct. In the
* non-transcribing case (!GPMI_IS_MX23()), we already have it.
* Otherwise, we need to explicitly read it.
*/
if (GPMI_IS_MX23(this)) {
/* Read the block mark into the first byte of the OOB buffer. */
ret = nand_read_page_op(chip, page, 0, chip->oob_poi, 1);
if (ret)
return ret;
}
return 0;
}
static int gpmi_ecc_write_oob(struct nand_chip *chip, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct mtd_oob_region of = { };
/* Do we have available oob area? */
mtd_ooblayout_free(mtd, 0, &of);
if (!of.length)
return -EPERM;
/*
* This function reads a NAND page without involving the ECC engine (no HW
* ECC correction).
* The tricky part in the GPMI/BCH controller is that it stores ECC bits
* inline (interleaved with payload DATA), and do not align data chunk on
* byte boundaries.
* We thus need to take care moving the payload data and ECC bits stored in the
* page into the provided buffers, which is why we're using nand_extract_bits().
*
* See set_geometry_by_ecc_info inline comments to have a full description
* of the layout used by the GPMI controller.
*/
static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->eccn_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
size_t src_bit_off;
size_t oob_bit_off;
size_t oob_byte_off;
uint8_t *oob = chip->oob_poi;
int step;
int ret;
ret = nand_read_page_op(chip, page, 0, tmp_buf,
mtd->writesize + mtd->oobsize);
if (ret)
return ret;
/*
* If required, swap the bad block marker and the data stored in the
* metadata section, so that we don't wrongly consider a block as bad.
*
* See the layout description for a detailed explanation on why this
* is needed.
*/
if (this->swap_block_mark)
swap(tmp_buf[0], tmp_buf[mtd->writesize]);
/*
* Copy the metadata section into the oob buffer (this section is
* guaranteed to be aligned on a byte boundary).
*/
if (oob_required)
memcpy(oob, tmp_buf, nfc_geo->metadata_size);
/*
* This function writes a NAND page without involving the ECC engine (no HW
* ECC generation).
* The tricky part in the GPMI/BCH controller is that it stores ECC bits
* inline (interleaved with payload DATA), and do not align data chunk on
* byte boundaries.
* We thus need to take care moving the OOB area at the right place in the
* final page, which is why we're using nand_extract_bits().
*
* See set_geometry_by_ecc_info inline comments to have a full description
* of the layout used by the GPMI controller.
*/
static int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->eccn_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
uint8_t *oob = chip->oob_poi;
size_t dst_bit_off;
size_t oob_bit_off;
size_t oob_byte_off;
int step;
/*
* Initialize all bits to 1 in case we don't have a buffer for the
* payload or oob data in order to leave unspecified bits of data
* to their initial state.
*/
if (!buf || !oob_required)
memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize);
/*
* First copy the metadata section (stored in oob buffer) at the
* beginning of the page, as imposed by the GPMI layout.
*/
memcpy(tmp_buf, oob, nfc_geo->metadata_size);
oob_bit_off = nfc_geo->metadata_size * 8;
dst_bit_off = oob_bit_off;
/* Interleave payload data and ECC bits */
for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
if (buf)
nand_extract_bits(tmp_buf, dst_bit_off, buf,
step * eccsize * 8, eccsize * 8);
dst_bit_off += eccsize * 8;
/* Align last ECC block to align a byte boundary */
if (step == nfc_geo->ecc_chunk_count - 1 &&
(oob_bit_off + eccbits) % 8)
eccbits += 8 - ((oob_bit_off + eccbits) % 8);
if (oob_required)
nand_extract_bits(tmp_buf, dst_bit_off, oob,
oob_bit_off, eccbits);
/*
* If required, swap the bad block marker and the first byte of the
* metadata section, so that we don't modify the bad block marker.
*
* See the layout description for a detailed explanation on why this
* is needed.
*/
if (this->swap_block_mark)
swap(tmp_buf[0], tmp_buf[mtd->writesize]);
/*
* Set the boot block stride size.
*
* In principle, we should be reading this from the OTP bits, since
* that's where the ROM is going to get it. In fact, we don't have any
* way to read the OTP bits, so we go with the default and hope for the
* best.
*/
geometry->stride_size_in_pages = 64;
/*
* Set the search area stride exponent.
*
* In principle, we should be reading this from the OTP bits, since
* that's where the ROM is going to get it. In fact, we don't have any
* way to read the OTP bits, so we go with the default and hope for the
* best.
*/
geometry->search_area_stride_exponent = 2;
return 0;
}
static const char *fingerprint = "STMP";
static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
{
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
struct device *dev = this->dev;
struct nand_chip *chip = &this->nand;
unsigned int search_area_size_in_strides;
unsigned int stride;
unsigned int page;
u8 *buffer = nand_get_data_buf(chip);
int found_an_ncb_fingerprint = false;
int ret;
/* Compute the number of strides in a search area. */
search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
nand_select_target(chip, 0);
/*
* Loop through the first search area, looking for the NCB fingerprint.
*/
dev_dbg(dev, "Scanning for an NCB fingerprint...\n");
for (stride = 0; stride < search_area_size_in_strides; stride++) {
/* Compute the page addresses. */
page = stride * rom_geo->stride_size_in_pages;
dev_dbg(dev, "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.
*/
ret = nand_read_page_op(chip, page, 12, buffer,
strlen(fingerprint));
if (ret)
continue;
/* Look for the fingerprint. */
if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
found_an_ncb_fingerprint = true;
break;
}
}
nand_deselect_target(chip);
if (found_an_ncb_fingerprint)
dev_dbg(dev, "\tFound a fingerprint\n");
else
dev_dbg(dev, "\tNo fingerprint found\n");
return found_an_ncb_fingerprint;
}
/* Writes a transcription stamp. */
static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
{
struct device *dev = this->dev;
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
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;
u8 *buffer = nand_get_data_buf(chip);
int status;
/* Loop over blocks in the first search area, erasing them. */
dev_dbg(dev, "Erasing the search area...\n");
for (block = 0; block < search_area_size_in_blocks; block++) {
/* Erase this block. */
dev_dbg(dev, "\tErasing block 0x%x\n", block);
status = nand_erase_op(chip, block);
if (status)
dev_err(dev, "[%s] Erase failed.\n", __func__);
}
/* Write the NCB fingerprint into the page buffer. */
memset(buffer, ~0, mtd->writesize);
memcpy(buffer + 12, fingerprint, strlen(fingerprint));
/* Loop through the first search area, writing NCB fingerprints. */
dev_dbg(dev, "Writing NCB fingerprints...\n");
for (stride = 0; stride < search_area_size_in_strides; stride++) {
/* Compute the page addresses. */
page = stride * rom_geo->stride_size_in_pages;
/* Write the first page of the current stride. */
dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
status = chip->ecc.write_page_raw(chip, buffer, 0, page);
if (status)
dev_err(dev, "[%s] Write failed.\n", __func__);
}
nand_deselect_target(chip);
return 0;
}
static int mx23_boot_init(struct gpmi_nand_data *this)
{
struct device *dev = this->dev;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int block_count;
unsigned int block;
int chipnr;
int page;
loff_t byte;
uint8_t block_mark;
int ret = 0;
/*
* If control arrives here, we can't use block mark swapping, which
* means we're forced to use transcription. First, scan for the
* transcription stamp. If we find it, then we don't have to do
* anything -- the block marks are already transcribed.
*/
if (mx23_check_transcription_stamp(this))
return 0;
/*
* If control arrives here, we couldn't find a transcription stamp, so
* so we presume the block marks are in the conventional location.
*/
dev_dbg(dev, "Transcribing bad block marks...\n");
/* Compute the number of blocks in the entire medium. */
block_count = nanddev_eraseblocks_per_target(&chip->base);
/*
* Loop over all the blocks in the medium, transcribing block marks as
* we go.
*/
for (block = 0; block < block_count; block++) {
/*
* Compute the chip, page and byte addresses for this block's
* conventional mark.
*/
chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
page = block << (chip->phys_erase_shift - chip->page_shift);
byte = block << chip->phys_erase_shift;
/* Send the command to read the conventional block mark. */
nand_select_target(chip, chipnr);
ret = nand_read_page_op(chip, page, mtd->writesize, &block_mark, 1);
nand_deselect_target(chip);
if (ret)
continue;
/*
* Check if the block is marked bad. If so, we need to mark it
* again, but this time the result will be a mark in the
* location where we transcribe block marks.
*/
if (block_mark != 0xff) {
dev_dbg(dev, "Transcribing mark in block %u\n", block);
ret = chip->legacy.block_markbad(chip, byte);
if (ret)
dev_err(dev,
"Failed to mark block bad with ret %d\n",
ret);
}
}
/* Write the stamp that indicates we've transcribed the block marks. */
mx23_write_transcription_stamp(this);
return 0;
}
static int nand_boot_init(struct gpmi_nand_data *this)
{
nand_boot_set_geometry(this);
/* This is ROM arch-specific initilization before the BBT scanning. */
if (GPMI_IS_MX23(this))
return mx23_boot_init(this);
return 0;
}
static int gpmi_set_geometry(struct gpmi_nand_data *this)
{
int ret;
/* Free the temporary DMA memory for reading ID. */
gpmi_free_dma_buffer(this);
/* Set up the NFC geometry which is used by BCH. */
ret = bch_set_geometry(this);
if (ret) {
dev_err(this->dev, "Error setting BCH geometry : %d\n", ret);
return ret;
}
/* Alloc the new DMA buffers according to the pagesize and oobsize */
return gpmi_alloc_dma_buffer(this);
}
/*
* We only enable the subpage read when:
* (1) the chip is imx6, and
* (2) the size of the ECC parity is byte aligned.
*/
if (GPMI_IS_MX6(this) &&
((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
ecc->read_subpage = gpmi_ecc_read_subpage;
chip->options |= NAND_SUBPAGE_READ;
}
return 0;
}
static int gpmi_nand_attach_chip(struct nand_chip *chip)
{
struct gpmi_nand_data *this = nand_get_controller_data(chip);
int ret;
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
chip->bbt_options |= NAND_BBT_NO_OOB;
this->ntransfers = 0;
for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
this->transfers[i].direction = DMA_NONE;
ret = pm_runtime_resume_and_get(this->dev);
if (ret < 0)
return ret;
/*
* This driver currently supports only one NAND chip. Plus, dies share
* the same configuration. So once timings have been applied on the
* controller side, they will not change anymore. When the time will
* come, the check on must_apply_timings will have to be dropped.
*/
if (this->hw.must_apply_timings) {
this->hw.must_apply_timings = false;
ret = gpmi_nfc_apply_timings(this);
if (ret)
goto out_pm;
}
for (i = 0; i < op->ninstrs; i++) {
instr = &op->instrs[i];
nand_op_trace(" ", instr);
switch (instr->type) {
case NAND_OP_WAITRDY_INSTR:
desc = gpmi_chain_wait_ready(this);
break;
case NAND_OP_CMD_INSTR:
cmd = instr->ctx.cmd.opcode;
/*
* When this command has an address cycle chain it
* together with the address cycle
*/
if (i + 1 != op->ninstrs &&
op->instrs[i + 1].type == NAND_OP_ADDR_INSTR)
continue;
desc = gpmi_chain_command(this, cmd, NULL, 0);
break;
case NAND_OP_ADDR_INSTR:
desc = gpmi_chain_command(this, cmd, instr->ctx.addr.addrs,
instr->ctx.addr.naddrs);
break;
case NAND_OP_DATA_OUT_INSTR:
buf_write = instr->ctx.data.buf.out;
buf_len = instr->ctx.data.len;
nbufs++;
to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
if (!to) {
dev_err(this->dev, "DMA timeout, last DMA\n");
gpmi_dump_info(this);
ret = -ETIMEDOUT;
goto unmap;
}
if (this->bch && buf_read) {
to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000));
if (!to) {
dev_err(this->dev, "BCH timeout, last DMA\n");
gpmi_dump_info(this);
ret = -ETIMEDOUT;
goto unmap;
}
}
static int gpmi_nand_init(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
/* init the MTD data structures */
mtd->name = "gpmi-nand";
mtd->dev.parent = this->dev;
/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
nand_set_controller_data(chip, this);
nand_set_flash_node(chip, this->pdev->dev.of_node);
chip->legacy.block_markbad = gpmi_block_markbad;
chip->badblock_pattern = &gpmi_bbt_descr;
chip->options |= NAND_NO_SUBPAGE_WRITE;
/* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
this->swap_block_mark = !GPMI_IS_MX23(this);
/*
* Allocate a temporary DMA buffer for reading ID in the
* nand_scan_ident().
*/
this->bch_geometry.payload_size = 1024;
this->bch_geometry.auxiliary_size = 128;
ret = gpmi_alloc_dma_buffer(this);
if (ret)
return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.