Commit 1ac03d4a authored by Oleg Dzhimiev's avatar Oleg Dzhimiev

updated nand driver from linux-xlnx 4.14 to 4.19

parent 88aecd2b
......@@ -296,24 +296,24 @@
reg = <0xf8001000 0x1000>;
} ;
ps7_smcc_0: ps7-smcc@e000e000 {
#address-cells = <1>;
smcc: memory-controller@e000e000 {
#address-cells = <2>;
#size-cells = <1>;
clock-names = "memclk", "aclk";
clock-names = "memclk", "apb_pclk";
clocks = <&clkc 11>, <&clkc 44>;
compatible = "arm,pl353-smc-r2p1";
compatible = "arm,pl353-smc-r2p1", "arm,primecell";
interrupt-parent = <&ps7_scugic_0>;
interrupts = < 0 18 4 >;
ranges ;
reg = < 0xe000e000 0x1000 >;
arm,addr25 = <0x0>;
arm,nor-chip-sel0 = <0x0>;
arm,nor-chip-sel1 = <0x0>;
arm,sram-chip-sel0 = <0x0>;
arm,sram-chip-sel1 = <0x0>;
ps7_nand_0: ps7-nand@e1000000 {
ranges = <0x0 0x0 0xe1000000 0x1000000 //Nand CS Region
0x1 0x0 0xe2000000 0x2000000 //SRAM/NOR CS Region
0x2 0x0 0xe4000000 0x2000000>; //SRAM/NOR CS Region
reg = <0xe000e000 0x1000>;
nand0: flash@e1000000 {
compatible = "arm,pl353-nand-r2p1";
reg = < 0xe1000000 0x1000000 >;
reg = <0 0 0x1000000>;
nand-ecc-mode = "on-die";
nand-ecc-step-size = <2048>;
/*arm,nand-clk-freq-hz = <0x5f5e100>;*/
arm,nand-width = <0x8>;
arm,nand-cycle-t0 = <0x4>;
......
......@@ -119,21 +119,12 @@
};
};
ps7_smcc_0: ps7-smcc@e000e000 {
ps7_nand_0: ps7-nand@e1000000 {
compatible = "arm,pl353-nand-r2p1";
reg = < 0xe1000000 0x1000000 >;
/*arm,nand-clk-freq-hz = <0x5f5e100>;*/
arm,nand-width = <0x8>;
arm,nand-cycle-t0 = <0x4>;
arm,nand-cycle-t1 = <0x4>;
arm,nand-cycle-t2 = <0x1>;
arm,nand-cycle-t3 = <0x2>;
arm,nand-cycle-t4 = <0x2>;
arm,nand-cycle-t5 = <0x2>;
arm,nand-cycle-t6 = <0x4>;
#address-cells = <0x1>;
#size-cells = <0x1>;
smcc: memory-controller@e000e000 {
nand0: flash@e1000000 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot-spl";
reg = <0x0 0x100000>;/*1MB for backup spl image(s)*/
......@@ -154,6 +145,7 @@
label = "rootfs";
reg = <0x1600000 0x10000000>;/*256MB*/
};
};
} ;
} ;
......
/*
* ARM PL35X NAND Flash Controller Driver
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.
*
* This driver is based on plat_nand.c and mxc_nand.c drivers
*
* 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 published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/memory/pl35x-smc.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define PL35X_NAND_DRIVER_NAME "pl35x-nand"
/* NAND flash driver defines */
#define PL35X_NAND_CMD_PHASE 1 /* End command valid in command phase */
#define PL35X_NAND_DATA_PHASE 2 /* End command valid in data phase */
#define PL35X_NAND_ECC_SIZE 512 /* Size of data for ECC operation */
/* Flash memory controller operating parameters */
#define PL35X_NAND_ECC_CONFIG (BIT(4) | /* ECC read at end of page */ \
(0 << 5)) /* No Jumping */
/* AXI Address definitions */
#define START_CMD_SHIFT 3
#define END_CMD_SHIFT 11
#define END_CMD_VALID_SHIFT 20
#define ADDR_CYCLES_SHIFT 21
#define CLEAR_CS_SHIFT 21
#define ECC_LAST_SHIFT 10
#define COMMAND_PHASE (0 << 19)
#define DATA_PHASE BIT(19)
#define PL35X_NAND_ECC_LAST BIT(ECC_LAST_SHIFT) /* Set ECC_Last */
#define PL35X_NAND_CLEAR_CS BIT(CLEAR_CS_SHIFT) /* Clear chip select */
#define ONDIE_ECC_FEATURE_ADDR 0x90
#define PL35X_NAND_ECC_BUSY_TIMEOUT (1 * HZ)
#define PL35X_NAND_DEV_BUSY_TIMEOUT (1 * HZ)
#define PL35X_NAND_LAST_TRANSFER_LENGTH 4
/* Inline function for the NAND controller register write */
static inline void pl35x_nand_write32(void __iomem *addr, u32 val)
{
writel_relaxed((val), (addr));
}
/**
* struct pl35x_nand_command_format - Defines NAND flash command format
* @start_cmd: First cycle command (Start command)
* @end_cmd: Second cycle command (Last command)
* @addr_cycles: Number of address cycles required to send the address
* @end_cmd_valid: The second cycle command is valid for cmd or data phase
*/
struct pl35x_nand_command_format {
int start_cmd;
int end_cmd;
u8 addr_cycles;
u8 end_cmd_valid;
};
/**
* struct pl35x_nand_info - Defines the NAND flash driver instance
* @chip: NAND chip information structure
* @nand_base: Virtual address of the NAND flash device
* @end_cmd_pending: End command is pending
* @end_cmd: End command
* @row_addr_cycles: Row address cycles
* @col_addr_cycles: Column address cycles
*/
struct pl35x_nand_info {
struct nand_chip chip;
void __iomem *nand_base;
unsigned long end_cmd_pending;
unsigned long end_cmd;
u8 row_addr_cycles;
u8 col_addr_cycles;
};
/*
* The NAND flash operations command format
*/
// Elphel: added UNLOCK1, UNLOCK2 and LOCK
static const struct pl35x_nand_command_format pl35x_nand_commands[] = {
{NAND_CMD_READ0, NAND_CMD_READSTART, 5, PL35X_NAND_CMD_PHASE},
{NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, PL35X_NAND_CMD_PHASE},
{NAND_CMD_READID, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_STATUS, NAND_CMD_NONE, 0, NAND_CMD_NONE},
{NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, PL35X_NAND_DATA_PHASE},
{NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE},
{NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, PL35X_NAND_CMD_PHASE},
{NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE},
{NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_UNLOCK1, NAND_CMD_NONE, 3, NAND_CMD_NONE},
{NAND_CMD_UNLOCK2, NAND_CMD_NONE, 3, NAND_CMD_NONE},
{NAND_CMD_LOCK, NAND_CMD_NONE, 0, NAND_CMD_NONE},
{NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
/* Add all the flash commands supported by the flash device and Linux */
/*
* The cache program command is not supported by driver because driver
* cant differentiate between page program and cached page program from
* start command, these commands can be differentiated through end
* command, which doesn't fit in to the driver design. The cache program
* command is not supported by NAND subsystem also, look at 1612 line
* number (in nand_write_page function) of nand_base.c file.
* {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, PL35X_NAND_YES},
*/
};
static int pl35x_ecc_ooblayout16_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * 16) + 0;
oobregion->length = chip->ecc.bytes;
return 0;
}
static int pl35x_ecc_ooblayout16_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * 16) + 8;
oobregion->length = 8;
return 0;
}
static const struct mtd_ooblayout_ops fsmc_ecc_ooblayout16_ops = {
.ecc = pl35x_ecc_ooblayout16_ecc,
.free = pl35x_ecc_ooblayout16_free,
};
static int pl35x_ecc_ooblayout64_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * chip->ecc.bytes) + 52;
oobregion->length = chip->ecc.bytes;
return 0;
}
static int pl35x_ecc_ooblayout64_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * chip->ecc.bytes) + 2;
oobregion->length = 50;
return 0;
}
static const struct mtd_ooblayout_ops fsmc_ecc_ooblayout64_ops = {
.ecc = pl35x_ecc_ooblayout64_ecc,
.free = pl35x_ecc_ooblayout64_free,
};
static int pl35x_ecc_ooblayout_ondie64_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * 16) + 8;
oobregion->length = chip->ecc.bytes;
return 0;
}
static int pl35x_ecc_ooblayout_ondie64_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->length = 4;
if (!section)
oobregion->offset = 4;
else
oobregion->offset = (section * 16) + 4;
return 0;
}
static const struct mtd_ooblayout_ops fsmc_ecc_ooblayout_ondie64_ops = {
.ecc = pl35x_ecc_ooblayout_ondie64_ecc,
.free = pl35x_ecc_ooblayout_ondie64_free,
};
/* Generic flash bbt decriptors */
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
static struct nand_bbt_descr bbt_main_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 4,
.len = 4,
.veroffs = 20,
.maxblocks = 4,
.pattern = bbt_pattern
};
static struct nand_bbt_descr bbt_mirror_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 4,
.len = 4,
.veroffs = 20,
.maxblocks = 4,
.pattern = mirror_pattern
};
/**
* pl35x_nand_calculate_hwecc - Calculate Hardware ECC
* @mtd: Pointer to the mtd_info structure
* @data: Pointer to the page data
* @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored
*
* This function retrieves the Hardware ECC data from the controller and returns
* ECC data back to the MTD subsystem.
*
* Return: 0 on success or error value on failure
*/
static int pl35x_nand_calculate_hwecc(struct mtd_info *mtd,
const u8 *data, u8 *ecc_code)
{
u32 ecc_value, ecc_status;
u8 ecc_reg, ecc_byte;
unsigned long timeout = jiffies + PL35X_NAND_ECC_BUSY_TIMEOUT;
/* Wait till the ECC operation is complete or timeout */
do {
if (pl35x_smc_ecc_is_busy())
cpu_relax();
else
break;
} while (!time_after_eq(jiffies, timeout));
if (time_after_eq(jiffies, timeout)) {
pr_err("%s timed out\n", __func__);
return -ETIMEDOUT;
}
for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) {
/* Read ECC value for each block */
ecc_value = pl35x_smc_get_ecc_val(ecc_reg);
ecc_status = (ecc_value >> 24) & 0xFF;
/* ECC value valid */
if (ecc_status & 0x40) {
for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) {
/* Copy ECC bytes to MTD buffer */
*ecc_code = ~ecc_value & 0xFF;
ecc_value = ecc_value >> 8;
ecc_code++;
}
} else {
pr_warn("%s status failed\n", __func__);
return -1;
}
}
return 0;
}
/**
* onehot - onehot function
* @value: Value to check for onehot
*
* This function checks whether a value is onehot or not.
* onehot is if and only if onebit is set.
*
* Return: 1 if it is onehot else 0
*/
static int onehot(unsigned short value)
{
return (value & (value - 1)) == 0;
}
/**
* pl35x_nand_correct_data - ECC correction function
* @mtd: Pointer to the mtd_info structure
* @buf: Pointer to the page data
* @read_ecc: Pointer to the ECC value read from spare data area
* @calc_ecc: Pointer to the calculated ECC value
*
* This function corrects the ECC single bit errors & detects 2-bit errors.
*
* Return: 0 if no ECC errors found
* 1 if single bit error found and corrected.
* -1 if multiple ECC errors found.
*/
static int pl35x_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc,
unsigned char *calc_ecc)
{
unsigned char bit_addr;
unsigned int byte_addr;
unsigned short ecc_odd, ecc_even, read_ecc_lower, read_ecc_upper;
unsigned short calc_ecc_lower, calc_ecc_upper;
read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff;
read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff;
calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff;
calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff;
ecc_odd = read_ecc_lower ^ calc_ecc_lower;
ecc_even = read_ecc_upper ^ calc_ecc_upper;
if ((ecc_odd == 0) && (ecc_even == 0))
return 0; /* no error */
if (ecc_odd == (~ecc_even & 0xfff)) {
/* bits [11:3] of error code is byte offset */
byte_addr = (ecc_odd >> 3) & 0x1ff;
/* bits [2:0] of error code is bit offset */
bit_addr = ecc_odd & 0x7;
/* Toggling error bit */
buf[byte_addr] ^= (1 << bit_addr);
return 1;
}
if (onehot(ecc_odd | ecc_even) == 1)
return 1; /* one error in parity */
return -1; /* Uncorrectable error */
}
/**
* pl35x_nand_read_oob - [REPLACABLE] the most common OOB data read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
unsigned long data_phase_addr;
uint8_t *p;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* pl35x_nand_write_oob - [REPLACABLE] the most common OOB data write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to write
*
* Return: Zero on success and EIO on failure
*/
static int pl35x_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
int status = 0;
const uint8_t *buf = chip->oob_poi;
unsigned long data_phase_addr;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
chip->write_buf(mtd, buf,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
buf += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
data_phase_addr += nand_offset;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, buf, PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Send command to program the OOB data */
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
return (status & NAND_STATUS_FAIL) ? -EIO : 0;
}
/**
* pl35x_nand_read_page_ondie - [Intern] read page data with ondie ecc
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_page_ondie(struct mtd_info *mtd,
struct nand_chip *chip,
u8 *buf, int oob_required, int page)
{
unsigned long data_phase_addr;
u8 *p;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
int status;
chip->read_buf(mtd, buf, mtd->writesize);
p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Check ECC info */
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
status = (int)chip->read_byte(mtd);
if (!(status & NAND_STATUS_READY)) {
pr_err("%s: page %d status=%#x - transfer not finished!\n",
__func__, page, status);
ndelay(100);
if (!(status & NAND_STATUS_READY))
return -EIO;
}
/* UnCorrectable Error bitflips. 5+ on our chip */
#define NAND_UCE_BITS 5
if (status & NAND_STATUS_FAIL) {
pr_warn("%s: page %d status=%#x - ECC Uncorrectable Error\n",
__func__, page, status);
/*
* Note: don't return -EIO which means no data and is fatal
* during a filesystem mount.
* Instead return NAND_UCE_BITS bitflips and increment
* the uncorrectable error count. This allows the layer above
* (such as UBI) to know data is present but corrupt and to
* be safely manage if its only a EC or VI page.
*/
mtd->ecc_stats.failed++;
return NAND_UCE_BITS;
}
#define NAND_STATUS_ECC_COR BIT(3) /* Correctable error */
if (status & NAND_STATUS_ECC_COR) {
pr_info("%s: page %d status=%#x - ECC Correctable error\n",
__func__, page, status);
mtd->ecc_stats.corrected++;
return 1;
}
return 0;
}
/**
* pl35x_nand_read_page_raw - [Intern] read raw page data without ecc
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
unsigned long data_phase_addr;
uint8_t *p;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
chip->read_buf(mtd, buf, mtd->writesize);
p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* pl35x_nand_write_page_raw - [Intern] raw page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to write
*
* Return: Always return zero
*/
static int pl35x_nand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required,
int page)
{
unsigned long data_phase_addr;
uint8_t *p;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
chip->write_buf(mtd, buf, mtd->writesize);
p = chip->oob_poi;
chip->write_buf(mtd, p,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
data_phase_addr += nand_offset;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* nand_write_page_hwecc - Hardware ECC based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to write
*
* This functions writes data and hardware generated ECC values in to the page.
*
* Return: Always return zero
*/
static int pl35x_nand_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
unsigned long data_phase_addr;
uint8_t *oob_ptr;
u32 ret;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
for ( ; (eccsteps - 1); eccsteps--) {
chip->write_buf(mtd, p, eccsize);
p += eccsize;
}
chip->write_buf(mtd, p, (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Set ECC Last bit to 1 */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_ECC_LAST;
data_phase_addr += nand_offset;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
p = buf;
chip->ecc.calculate(mtd, p, &ecc_calc[0]);
/* Wait for ECC to be calculated and read the error values */
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
0, chip->ecc.total);
if (ret)
return ret;
/* Clear ECC last bit */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr -= nand_offset;
data_phase_addr &= ~PL35X_NAND_ECC_LAST;
data_phase_addr += nand_offset;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
/* Write the spare area with ECC bytes */
oob_ptr = chip->oob_poi;
chip->write_buf(mtd, oob_ptr,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
data_phase_addr += nand_offset;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
oob_ptr += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
chip->write_buf(mtd, oob_ptr, PL35X_NAND_LAST_TRANSFER_LENGTH);
return 0;
}
/**
* pl35x_nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to write
*
* Return: Always return zero
*/
static int pl35x_nand_write_page_swecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
u32 ret;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[0]);
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
0, chip->ecc.total);
if (ret)
return ret;
chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
return 0;
}
/**
* pl35x_nand_read_page_hwecc - Hardware ECC based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* This functions reads data and checks the data integrity by comparing hardware
* generated ECC values and read ECC values from spare area.
*
* Return: 0 always and updates ECC operation status in to MTD structure
*/
static int pl35x_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, stat, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
unsigned long data_phase_addr;
uint8_t *oob_ptr;
u32 ret;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
unsigned long nand_offset = (unsigned long __force)xnand->nand_base;
for ( ; (eccsteps - 1); eccsteps--) {
chip->read_buf(mtd, p, eccsize);
p += eccsize;
}
chip->read_buf(mtd, p, (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
p += (eccsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Set ECC Last bit to 1 */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_ECC_LAST;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);
/* Read the calculated ECC value */
p = buf;
chip->ecc.calculate(mtd, p, &ecc_calc[0]);
/* Clear ECC last bit */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr -= nand_offset;
data_phase_addr &= ~PL35X_NAND_ECC_LAST;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
/* Read the stored ECC value */
oob_ptr = chip->oob_poi;
chip->read_buf(mtd, oob_ptr,
(mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH));
/* de-assert chip select */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
oob_ptr += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
chip->read_buf(mtd, oob_ptr, PL35X_NAND_LAST_TRANSFER_LENGTH);
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
eccsteps = chip->ecc.steps;
p = buf;
/* Check ECC error for all blocks and correct if it is correctable */
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
/**
* pl35x_nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl35x_nand_read_page_swecc(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
u32 ret;
chip->ecc.read_page_raw(mtd, chip, buf, page, 1);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_calc, chip->oob_poi,
0, chip->ecc.total);
if (ret)
return ret;
eccsteps = chip->ecc.steps;
p = buf;
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
/**
* pl35x_nand_select_chip - Select the flash device
* @mtd: Pointer to the mtd info structure
* @chipnr: chipnumber to select, -1 for deselect
*
* This function is empty as the NAND controller handles chip select line
* internally based on the chip address passed in command and data phase.
*/
static void pl35x_nand_select_chip(struct mtd_info *mtd, int chipnr)
{
unsigned long data_phase_addr;
struct pl35x_nand_info *xnand;
unsigned long nand_offset;
struct nand_chip *chip;
/*
* Chip selection will happen in command/data operation
*/
if (chipnr >= 0)
return;
/* de-assert chip select */
chip = mtd_to_nand(mtd);
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
xnand = container_of(chip, struct pl35x_nand_info, chip);
nand_offset = (unsigned long __force)xnand->nand_base;
data_phase_addr -= nand_offset;
data_phase_addr |= PL35X_NAND_CLEAR_CS;
data_phase_addr += nand_offset;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_byte(mtd);
}
/**
* pl35x_nand_cmd_function - Send command to NAND device
* @mtd: Pointer to the mtd_info structure
* @command: The command to be sent to the flash device
* @column: The column address for this command, -1 if none
* @page_addr: The page address for this command, -1 if none
*/
static void pl35x_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
struct nand_chip *chip = mtd_to_nand(mtd);
const struct pl35x_nand_command_format *curr_cmd = NULL;
struct pl35x_nand_info *xnand =
container_of(chip, struct pl35x_nand_info, chip);
void __iomem *cmd_addr;
unsigned long cmd_data = 0, end_cmd_valid = 0;
unsigned long cmd_phase_addr, data_phase_addr, end_cmd, i;
unsigned long timeout = jiffies + PL35X_NAND_DEV_BUSY_TIMEOUT;
u32 addrcycles;
if (xnand->end_cmd_pending) {
/*
* Check for end command if this command request is same as the
* pending command then return
*/
if (xnand->end_cmd == command) {
xnand->end_cmd = 0;
xnand->end_cmd_pending = 0;
return;
}
}
/* Emulate NAND_CMD_READOOB for large page device */
if ((mtd->writesize > PL35X_NAND_ECC_SIZE) &&
(command == NAND_CMD_READOOB)) {
column += mtd->writesize;
command = NAND_CMD_READ0;
}
/* Get the command format */
for (i = 0; (pl35x_nand_commands[i].start_cmd != NAND_CMD_NONE ||
pl35x_nand_commands[i].end_cmd != NAND_CMD_NONE); i++)
if (command == pl35x_nand_commands[i].start_cmd)
curr_cmd = &pl35x_nand_commands[i];
if (curr_cmd == NULL)
return;
/* Clear interrupt */
pl35x_smc_clr_nand_int();
/* Get the command phase address */
if (curr_cmd->end_cmd_valid == PL35X_NAND_CMD_PHASE)
end_cmd_valid = 1;
if (curr_cmd->end_cmd == NAND_CMD_NONE)
end_cmd = 0x0;
else
end_cmd = curr_cmd->end_cmd;
if (command == NAND_CMD_READ0 || command == NAND_CMD_SEQIN)
addrcycles = xnand->row_addr_cycles + xnand->col_addr_cycles;
else if (command == NAND_CMD_ERASE1)
addrcycles = xnand->row_addr_cycles;
else
addrcycles = curr_cmd->addr_cycles;
cmd_phase_addr = (unsigned long __force)xnand->nand_base + (
(addrcycles << ADDR_CYCLES_SHIFT) |
(end_cmd_valid << END_CMD_VALID_SHIFT) |
(COMMAND_PHASE) |
(end_cmd << END_CMD_SHIFT) |
(curr_cmd->start_cmd << START_CMD_SHIFT));
cmd_addr = (void __iomem * __force)cmd_phase_addr;
/* Get the data phase address */
end_cmd_valid = 0;
data_phase_addr = (unsigned long __force)xnand->nand_base + (
(0x0 << CLEAR_CS_SHIFT) |
(end_cmd_valid << END_CMD_VALID_SHIFT) |
(DATA_PHASE) |
(end_cmd << END_CMD_SHIFT) |
(0x0 << ECC_LAST_SHIFT));
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->IO_ADDR_W = chip->IO_ADDR_R;
/* Command phase AXI write */
/* Read & Write */
if (column != -1 && page_addr != -1) {
/* Adjust columns for 16 bit bus width */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
cmd_data = column;
if (mtd->writesize > PL35X_NAND_ECC_SIZE) {
cmd_data |= page_addr << 16;
/* Another address cycle for devices > 128MiB */
if (chip->chipsize > (128 << 20)) {
pl35x_nand_write32(cmd_addr, cmd_data);
cmd_data = (page_addr >> 16);
}
} else {
cmd_data |= page_addr << 8;
}
} else if (page_addr != -1) {
/* Erase */
cmd_data = page_addr;
} else if (column != -1) {
/*
* Change read/write column, read id etc
* Adjust columns for 16 bit bus width
*/
if ((chip->options & NAND_BUSWIDTH_16) &&
((command == NAND_CMD_READ0) ||
(command == NAND_CMD_SEQIN) ||
(command == NAND_CMD_RNDOUT) ||
(command == NAND_CMD_RNDIN)))
column >>= 1;
cmd_data = column;
}
pl35x_nand_write32(cmd_addr, cmd_data);
if (curr_cmd->end_cmd_valid) {
xnand->end_cmd = curr_cmd->end_cmd;
xnand->end_cmd_pending = 1;
}
ndelay(100);
if ((command == NAND_CMD_READ0) ||
(command == NAND_CMD_RESET) ||
(command == NAND_CMD_PARAM) ||
(command == NAND_CMD_GET_FEATURES)) {
/* Wait till the device is ready or timeout */
do {
if (chip->dev_ready(mtd))
break;
else
cpu_relax();
} while (!time_after_eq(jiffies, timeout));
if (time_after_eq(jiffies, timeout))
pr_err("%s timed out\n", __func__);
return;
}
}
/**
* pl35x_nand_read_buf - read chip data into buffer
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to read
*/
static void pl35x_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd_to_nand(mtd);
unsigned long *ptr = (unsigned long *)buf;
len >>= 2;
for (i = 0; i < len; i++)
ptr[i] = readl(chip->IO_ADDR_R);
}
/**
* pl35x_nand_write_buf - write buffer to chip
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to write
*/
static void pl35x_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
int i;
struct nand_chip *chip = mtd_to_nand(mtd);
unsigned long *ptr = (unsigned long *)buf;
len >>= 2;
for (i = 0; i < len; i++)
writel(ptr[i], chip->IO_ADDR_W);
}
/**
* pl35x_nand_device_ready - Check device ready/busy line
* @mtd: Pointer to the mtd_info structure
*
* Return: 0 on busy or 1 on ready state
*/
static int pl35x_nand_device_ready(struct mtd_info *mtd)
{
if (pl35x_smc_get_nand_int_status_raw()) {
pl35x_smc_clr_nand_int();
return 1;
}
return 0;
}
// Elphel: whole function
/**
* pl35x_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 pl35x_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
int status;
int i;
uint8_t ondie_ecc_feature;
if (!chip->onfi_version ||
!(le16_to_cpu(chip->onfi_params.opt_cmd)
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
if (addr==ONDIE_ECC_FEATURE_ADDR){
//keep ondie ecc on;
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
ondie_ecc_feature = readb(chip->IO_ADDR_R);
subfeature_param[0] |= (ondie_ecc_feature&0x08);
}
chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
writeb(subfeature_param[i], chip->IO_ADDR_W);
//chip->write_byte(mtd, subfeature_param[i]);
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
}
// Elphel: whole function
/**
* 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 pl35x_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
int i;
if (!chip->onfi_version ||
!(le16_to_cpu(chip->onfi_params.opt_cmd)
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
/* clear the sub feature parameters */
memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
*subfeature_param++ = chip->read_byte(mtd);
return 0;
}
/**
* pl35x_nand_detect_ondie_ecc - Get the flash ondie ecc state
* @mtd: Pointer to the mtd_info structure
*
* This function enables the ondie ecc for the Micron ondie ecc capable devices
*
* Return: 1 on detect, 0 if fail to detect
*/
static int pl35x_nand_detect_ondie_ecc(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
u8 maf_id, dev_id, i, get_feature;
u8 set_feature[4] = { 0x08, 0x00, 0x00, 0x00 };
/* Check if On-Die ECC flash */
nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
maf_id = readb(nand_chip->IO_ADDR_R);
dev_id = readb(nand_chip->IO_ADDR_R);
if ((maf_id == NAND_MFR_MICRON) &&
((dev_id == 0xf1) || (dev_id == 0xa1) ||
(dev_id == 0xb1) || (dev_id == 0xaa) ||
(dev_id == 0xba) || (dev_id == 0xda) ||
(dev_id == 0xca) || (dev_id == 0xac) ||
(dev_id == 0xbc) || (dev_id == 0xdc) ||
(dev_id == 0xcc) || (dev_id == 0xa3) ||
(dev_id == 0xb3) ||
(dev_id == 0xd3) || (dev_id == 0xc3))) {
nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
get_feature = readb(nand_chip->IO_ADDR_R);
if (get_feature & 0x08) {
return 1;
} else {
nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
for (i = 0; i < 4; i++)
writeb(set_feature[i], nand_chip->IO_ADDR_W);
ndelay(1000);
nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
get_feature = readb(nand_chip->IO_ADDR_R);
if (get_feature & 0x08)
return 1;
}
}
return 0;
}
/**
* pl35x_nand_ecc_init - Initialize the ecc information as per the ecc mode
* @mtd: Pointer to the mtd_info structure
* @ecc: Pointer to ECC control structure
* @ondie_ecc_state: ondie ecc status
*
* This function initializes the ecc block and functional pointers as per the
* ecc mode
*/
static void pl35x_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
int ondie_ecc_state)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
ecc->mode = NAND_ECC_HW;
ecc->read_oob = pl35x_nand_read_oob;
ecc->read_page_raw = pl35x_nand_read_page_raw;
ecc->strength = 1;
ecc->write_oob = pl35x_nand_write_oob;
ecc->write_page_raw = pl35x_nand_write_page_raw;
if (ondie_ecc_state) {
/* bypass the controller ECC block */
pl35x_smc_set_ecc_mode(PL35X_SMC_ECCMODE_BYPASS);
/*
* The software ECC routines won't work with the
* SMC controller
*/
ecc->bytes = 0;
mtd_set_ooblayout(mtd, &fsmc_ecc_ooblayout_ondie64_ops);
ecc->read_page = pl35x_nand_read_page_ondie;
ecc->write_page = pl35x_nand_write_page_raw;
ecc->size = mtd->writesize;
/*
* On-Die ECC spare bytes offset 8 is used for ECC codes
* Use the BBT pattern descriptors
*/
nand_chip->bbt_td = &bbt_main_descr;
nand_chip->bbt_md = &bbt_mirror_descr;
} else {
/* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
ecc->bytes = 3;
ecc->calculate = pl35x_nand_calculate_hwecc;
ecc->correct = pl35x_nand_correct_data;
ecc->hwctl = NULL;
ecc->read_page = pl35x_nand_read_page_hwecc;
ecc->size = PL35X_NAND_ECC_SIZE;
ecc->write_page = pl35x_nand_write_page_hwecc;
pl35x_smc_set_ecc_pg_size(mtd->writesize);
switch (mtd->writesize) {
case 512:
case 1024:
case 2048:
pl35x_smc_set_ecc_mode(PL35X_SMC_ECCMODE_APB);
break;
default:
/*
* The software ECC routines won't work with the
* SMC controller
*/
ecc->calculate = nand_calculate_ecc;
ecc->correct = nand_correct_data;
ecc->read_page = pl35x_nand_read_page_swecc;
ecc->write_page = pl35x_nand_write_page_swecc;
ecc->size = 256;
break;
}
if (mtd->oobsize == 16)
mtd_set_ooblayout(mtd, &fsmc_ecc_ooblayout16_ops);
else if (mtd->oobsize == 64)
mtd_set_ooblayout(mtd, &fsmc_ecc_ooblayout64_ops);
}
}
/**
* pl35x_nand_probe - Probe method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function initializes the driver data structures and the hardware.
*
* Return: 0 on success or error value on failure
*/
static int pl35x_nand_probe(struct platform_device *pdev)
{
struct pl35x_nand_info *xnand;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct resource *res;
int ondie_ecc_state;
xnand = devm_kzalloc(&pdev->dev, sizeof(*xnand), GFP_KERNEL);
if (!xnand)
return -ENOMEM;
/* Map physical address of NAND flash */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xnand->nand_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(xnand->nand_base))
return PTR_ERR(xnand->nand_base);
nand_chip = &xnand->chip;
mtd = nand_to_mtd(nand_chip);
nand_set_controller_data(nand_chip, xnand);
mtd->priv = nand_chip;
mtd->owner = THIS_MODULE;
mtd->name = PL35X_NAND_DRIVER_NAME;
nand_set_flash_node(nand_chip, pdev->dev.of_node);
/* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = xnand->nand_base;
nand_chip->IO_ADDR_W = xnand->nand_base;
/* Set the driver entry points for MTD */
nand_chip->cmdfunc = pl35x_nand_cmd_function;
nand_chip->dev_ready = pl35x_nand_device_ready;
nand_chip->select_chip = pl35x_nand_select_chip;
nand_chip->onfi_set_features = pl35x_nand_onfi_set_features;
nand_chip->onfi_get_features = pl35x_nand_onfi_get_features;
//nand_chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
//nand_chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
/* If we don't set this delay driver sets 20us by default */
nand_chip->chip_delay = 30;
/* Buffer read/write routines */
nand_chip->read_buf = pl35x_nand_read_buf;
nand_chip->write_buf = pl35x_nand_write_buf;
/* Set the device option and flash width */
nand_chip->options = NAND_BUSWIDTH_AUTO;
nand_chip->bbt_options = NAND_BBT_USE_FLASH;
platform_set_drvdata(pdev, xnand);
ondie_ecc_state = pl35x_nand_detect_ondie_ecc(mtd);
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
return -ENXIO;
}
xnand->row_addr_cycles = nand_chip->onfi_params.addr_cycles & 0xF;
xnand->col_addr_cycles =
(nand_chip->onfi_params.addr_cycles >> 4) & 0xF;
pl35x_nand_ecc_init(mtd, &nand_chip->ecc, ondie_ecc_state);
if (nand_chip->options & NAND_BUSWIDTH_16)
pl35x_smc_set_buswidth(PL35X_SMC_MEM_WIDTH_16);
/* second phase scan */
if (nand_scan_tail(mtd)) {
dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
// elphel393: don't want to fail exit on the very first boot when flash is locked and the bad block table is not written yet
//return -ENXIO;
}
//elphel393 modification for Micron NAND chips
//TODO: add Micron chip ID checking
mtd->_unlock = nand_unlock;
mtd->_lock = nand_lock;
mtd_device_register(mtd, NULL, 0);
return 0;
}
/**
* pl35x_nand_remove - Remove method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function is called if the driver module is being unloaded. It frees all
* resources allocated to the device.
*
* Return: 0 on success or error value on failure
*/
static int pl35x_nand_remove(struct platform_device *pdev)
{
struct pl35x_nand_info *xnand = platform_get_drvdata(pdev);
struct mtd_info *mtd = nand_to_mtd(&xnand->chip);
/* Release resources, unregister device */
nand_release(mtd);
return 0;
}
/* Match table for device tree binding */
static const struct of_device_id pl35x_nand_of_match[] = {
{ .compatible = "arm,pl353-nand-r2p1" },
{},
};
MODULE_DEVICE_TABLE(of, pl35x_nand_of_match);
/*
* pl35x_nand_driver - This structure defines the NAND subsystem platform driver
*/
static struct platform_driver pl35x_nand_driver = {
.probe = pl35x_nand_probe,
.remove = pl35x_nand_remove,
.driver = {
.name = PL35X_NAND_DRIVER_NAME,
.of_match_table = pl35x_nand_of_match,
},
};
module_platform_driver(pl35x_nand_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_ALIAS("platform:" PL35X_NAND_DRIVER_NAME);
MODULE_DESCRIPTION("ARM PL35X NAND Flash Driver");
MODULE_LICENSE("GPL");
......@@ -11,12 +11,12 @@ config MTD_NAND_ECC_SMC
menuconfig MTD_NAND
tristate "NAND Device Support"
tristate "Raw/Parallel NAND Device Support"
depends on MTD
select MTD_NAND_ECC
help
This enables support for accessing all type of NAND flash
devices. For further information see
This enables support for accessing all type of raw/parallel
NAND flash devices. For further information see
<http://www.linux-mtd.infradead.org/doc/nand.html>.
if MTD_NAND
......@@ -46,7 +46,7 @@ config MTD_NAND_DENALI
config MTD_NAND_DENALI_PCI
tristate "Support Denali NAND controller on Intel Moorestown"
select MTD_NAND_DENALI
depends on HAS_DMA && PCI
depends on PCI
help
Enable the driver for NAND flash on Intel Moorestown, using the
Denali NAND controller core.
......@@ -77,7 +77,8 @@ config MTD_NAND_AMS_DELTA
config MTD_NAND_OMAP2
tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone"
depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE)
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
depends on HAS_IOMEM
help
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
and Keystone platforms.
......@@ -116,38 +117,6 @@ config MTD_NAND_AU1550
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_BF5XX
tristate "Blackfin on-chip NAND Flash Controller driver"
depends on BF54x || BF52x
help
This enables the Blackfin on-chip NAND flash controller
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
This driver can also be built as a module. If so, the module
will be called bf5xx-nand.
config MTD_NAND_BF5XX_HWECC
bool "BF5XX NAND Hardware ECC"
default y
depends on MTD_NAND_BF5XX
help
Enable the use of the BF5XX's internal ECC generator when
using NAND.
config MTD_NAND_BF5XX_BOOTROM_ECC
bool "Use Blackfin BootROM ECC Layout"
default n
depends on MTD_NAND_BF5XX_HWECC
help
If you wish to modify NAND pages and allow the Blackfin on-chip
BootROM to boot from them, say Y here. This is only necessary
if you are booting U-Boot out of NAND and you wish to update
U-Boot from Linux' userspace. Otherwise, you should say N here.
If unsure, say N.
config MTD_NAND_S3C2410
tristate "NAND Flash support for Samsung S3C SoCs"
depends on ARCH_S3C24XX || ARCH_S3C64XX
......@@ -184,7 +153,7 @@ config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_TANGO
tristate "NAND Flash support for Tango chips"
depends on ARCH_TANGO || COMPILE_TEST
depends on HAS_DMA
depends on HAS_IOMEM
help
Enables the NAND Flash controller on Tango chips.
......@@ -213,7 +182,7 @@ config MTD_NAND_DISKONCHIP_PROBE_ADDRESS
hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED
depends on MTD_NAND_DISKONCHIP
default "0"
---help---
help
By default, the probe for DiskOnChip devices will look for a
DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000.
This option allows you to specify a single address at which to probe
......@@ -280,7 +249,8 @@ config MTD_NAND_DOCG4
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on ARCH_PXA
depends on ARCH_PXA || COMPILE_TEST
depends on HAS_IOMEM
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
......@@ -307,22 +277,30 @@ config MTD_NAND_CS553X
config MTD_NAND_ATMEL
tristate "Support for NAND Flash / SmartMedia on AT91"
depends on ARCH_AT91
depends on ARCH_AT91 || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_ALLOCATOR
select MFD_ATMEL_SMC
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 processors.
config MTD_NAND_PXA3xx
tristate "NAND support on PXA3xx and Armada 370/XP"
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU
config MTD_NAND_MARVELL
tristate "NAND controller support on Marvell boards"
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
COMPILE_TEST
depends on HAS_IOMEM
help
This enables the driver for the NAND flash device found on
PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
This enables the NAND flash controller driver for Marvell boards,
including:
- PXA3xx processors (NFCv1)
- 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
- 64-bit Aramda platforms (7k, 8k) (NFCv2)
config MTD_NAND_SLC_LPC32XX
tristate "NXP LPC32xx SLC Controller"
depends on ARCH_LPC32XX
depends on ARCH_LPC32XX || COMPILE_TEST
depends on HAS_IOMEM
help
Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell
chips) NAND controller. This is the default for the PHYTEC 3250
......@@ -333,7 +311,8 @@ config MTD_NAND_SLC_LPC32XX
config MTD_NAND_MLC_LPC32XX
tristate "NXP LPC32xx MLC Controller"
depends on ARCH_LPC32XX
depends on ARCH_LPC32XX || COMPILE_TEST
depends on HAS_IOMEM
help
Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND
controller. This is the default for the WORK92105 controller
......@@ -368,18 +347,17 @@ config MTD_NAND_NANDSIM
config MTD_NAND_GPMI_NAND
tristate "GPMI NAND Flash Controller driver"
depends on MTD_NAND && MXS_DMA
depends on MXS_DMA
help
Enables NAND Flash support for IMX23, IMX28 or IMX6.
The GPMI controller is very powerful, with the help of BCH
module, it can do the hardware ECC. The GPMI supports several
NAND flashs at the same time. The GPMI may conflicts with other
block, such as SD card. So pay attention to it when you enable
the GPMI.
NAND flashs at the same time.
config MTD_NAND_BRCMNAND
tristate "Broadcom STB NAND controller"
depends on ARM || ARM64 || MIPS
depends on ARM || ARM64 || MIPS || COMPILE_TEST
depends on HAS_IOMEM
help
Enables the Broadcom NAND controller driver. The controller was
originally designed for Set-Top Box but is used on various BCM7xxx,
......@@ -388,6 +366,7 @@ config MTD_NAND_BRCMNAND
config MTD_NAND_BCM47XXNFLASH
tristate "Support for NAND flash on BCM4706 BCMA bus"
depends on BCMA_NFLASH
depends on BCMA
help
BCMA bus can have various flash memories attached, they are
registered by bcma as platform devices. This enables driver for
......@@ -429,7 +408,8 @@ config MTD_NAND_FSL_ELBC
config MTD_NAND_FSL_IFC
tristate "NAND support for Freescale IFC controller"
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST
depends on HAS_IOMEM
select FSL_IFC
select MEMORY
help
......@@ -467,7 +447,8 @@ config MTD_NAND_VF610_NFC
config MTD_NAND_MXC
tristate "MXC NAND support"
depends on ARCH_MXC
depends on ARCH_MXC || COMPILE_TEST
depends on HAS_IOMEM
help
This enables the driver for the NAND flash controller on the
MXC processors.
......@@ -476,21 +457,22 @@ config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
depends on SUPERH || COMPILE_TEST
depends on HAS_IOMEM
depends on HAS_DMA
help
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL.
config MTD_NAND_DAVINCI
tristate "Support NAND on DaVinci/Keystone SoC"
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF)
depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) || COMPILE_TEST
depends on HAS_IOMEM
help
Enable the driver for NAND flash chips on Texas Instruments
DaVinci/Keystone processors.
config MTD_NAND_TXX9NDFMC
tristate "NAND Flash support for TXx9 SoC"
depends on SOC_TX4938 || SOC_TX4939
depends on SOC_TX4938 || SOC_TX4939 || COMPILE_TEST
depends on HAS_IOMEM
help
This enables the NAND flash controller on the TXx9 SoCs.
......@@ -502,36 +484,31 @@ config MTD_NAND_SOCRATES
config MTD_NAND_NUC900
tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
depends on ARCH_W90X900
depends on ARCH_W90X900 || COMPILE_TEST
depends on HAS_IOMEM
help
This enables the driver for the NAND Flash on evaluation board based
on w90p910 / NUC9xx.
config MTD_NAND_PL35X
tristate "ARM Pl35X NAND flash driver"
depends on MTD_NAND && ARM
depends on PL35X_SMC
help
This enables access to the NAND flash device on PL351/PL353
SMC controller.
config MTD_NAND_JZ4740
tristate "Support for JZ4740 SoC NAND controller"
depends on MACH_JZ4740
depends on MACH_JZ4740 || COMPILE_TEST
depends on HAS_IOMEM
help
Enables support for NAND Flash on JZ4740 SoC based boards.
config MTD_NAND_JZ4780
tristate "Support for NAND on JZ4780 SoC"
depends on MACH_JZ4780 && JZ4780_NEMC
depends on JZ4780_NEMC
help
Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
based boards, using the BCH controller for hardware error correction.
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
depends on OF
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
depends on OF && HAS_IOMEM
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 || \
COMPILE_TEST
help
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
......@@ -545,28 +522,22 @@ config MTD_NAND_XWAY
config MTD_NAND_SUNXI
tristate "Support for NAND on Allwinner SoCs"
depends on ARCH_SUNXI
help
Enables support for NAND Flash chips on Allwinner SoCs.
config MTD_NAND_ARASAN
tristate "Support for Arasan Nand Flash controller"
depends on ARCH_SUNXI || COMPILE_TEST
depends on HAS_IOMEM
depends on HAS_DMA
help
Enables the driver for the Arasan Nand Flash controller on
Zynq Ultrascale+ MPSoC.
Enables support for NAND Flash chips on Allwinner SoCs.
config MTD_NAND_HISI504
tristate "Support for NAND controller on Hisilicon SoC Hip04"
depends on ARCH_HISI || COMPILE_TEST
depends on HAS_DMA
depends on HAS_IOMEM
help
Enables support for NAND controller on Hisilicon SoC Hip04.
config MTD_NAND_QCOM
tristate "Support for NAND on QCOM SoCs"
depends on ARCH_QCOM
depends on ARCH_QCOM || COMPILE_TEST
depends on HAS_IOMEM
help
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
controller. This controller is found on IPQ806x SoC.
......@@ -574,12 +545,37 @@ config MTD_NAND_QCOM
config MTD_NAND_MTK
tristate "Support for NAND controller on MTK SoCs"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_DMA
depends on HAS_IOMEM
help
Enables support for NAND controller on MTK SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
# Elphel (comes from elphel393.cfg)
config MTD_NAND_TEGRA
tristate "Support for NAND controller on NVIDIA Tegra"
depends on ARCH_TEGRA || COMPILE_TEST
depends on HAS_IOMEM
help
Enables support for NAND flash controller on NVIDIA Tegra SoC.
The driver has been developed and tested on a Tegra 2 SoC. DMA
support, raw read/write page as well as HW ECC read/write page
is supported. Extra OOB bytes when using HW ECC are currently
not supported.
config MTD_NAND_ARASAN
tristate "Support for Arasan Nand Flash controller"
depends on HAS_IOMEM && HAS_DMA
help
Enables the driver for the Arasan Nand Flash controller on
Zynq Ultrascale+ MPSoC.
config MTD_NAND_PL353
tristate "ARM Pl353 NAND flash driver"
depends on MTD_NAND && ARM
depends on PL353_SMC
help
Enables support for PrimeCell Static Memory Controller PL353.
# Elphel (enabled in elphel393.cfg)
config MTD_NAND_OTP
tristate "NAND OTP area r/w support for Micron devices"
default n
......
# SPDX-License-Identifier: GPL-2.0
#
# linux/drivers/nand/Makefile
#
obj-$(CONFIG_MTD_NAND) += nand.o
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
......@@ -14,7 +11,6 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
......@@ -31,7 +27,7 @@ omap2_nand-objs := omap2.o
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o
obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
......@@ -56,17 +52,18 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_PL35X) += pl35x_nand.o
obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_ARASAN) += arasan_nand.o
obj-$(CONFIG_MTD_NAND_PL353) += pl353_nand.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o nandchip-micron.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
nand-objs += nand_amd.o
nand-objs += nand_hynix.o
nand-objs += nand_macronix.o
nand-objs += nand_micron.o
nand-objs += nand_micron.o nand_micron_mt29f.o
nand-objs += nand_samsung.o
nand-objs += nand_toshiba.o
......@@ -2,12 +2,11 @@
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
int nand_check_wp(struct mtd_info *mtd);
int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len);
int nand_get_device(struct mtd_info *mtd, int new_state);
void nand_release_device(struct mtd_info *mtd);
int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
int nand_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);
int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
void nandchip_micron_init(struct mtd_info *mtd, int dev_id);
void nand_micron_mt29f_init(struct mtd_info *mtd, int dev_id);
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright (C) 2017 Free Electrons
* Copyright (C) 2017 NextThing Co
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*
* 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.
*/
#include <linux/mtd/rawnand.h>
#include <linux/slab.h>
#include "nand.h"
/*
* Special Micron status bit 3 indicates that the block has been
* corrected by on-die ECC and should be rewritten.
*/
#define NAND_ECC_STATUS_WRITE_RECOMMENDED BIT(3)
/*
* On chips with 8-bit ECC and additional bit can be used to distinguish
* cases where a errors were corrected without needing a rewrite
*
* Bit 4 Bit 3 Bit 0 Description
* ----- ----- ----- -----------
* 0 0 0 No Errors
* 0 0 1 Multiple uncorrected errors
* 0 1 0 4 - 6 errors corrected, recommend rewrite
* 0 1 1 Reserved
* 1 0 0 1 - 3 errors corrected
* 1 0 1 Reserved
* 1 1 0 7 - 8 errors corrected, recommend rewrite
*/
#define NAND_ECC_STATUS_MASK (BIT(4) | BIT(3) | BIT(0))
#define NAND_ECC_STATUS_UNCORRECTABLE BIT(0)
#define NAND_ECC_STATUS_4_6_CORRECTED BIT(3)
#define NAND_ECC_STATUS_1_3_CORRECTED BIT(4)
#define NAND_ECC_STATUS_7_8_CORRECTED (BIT(4) | BIT(3))
struct nand_onfi_vendor_micron {
u8 two_plane_read;
u8 read_cache;
u8 read_unique_id;
u8 dq_imped;
u8 dq_imped_num_settings;
u8 dq_imped_feat_addr;
u8 rb_pulldown_strength;
u8 rb_pulldown_strength_feat_addr;
u8 rb_pulldown_strength_num_settings;
u8 otp_mode;
u8 otp_page_start;
u8 otp_data_prot_addr;
u8 otp_num_pages;
u8 otp_feat_addr;
u8 read_retry_options;
u8 reserved[72];
u8 param_revision;
} __packed;
struct micron_on_die_ecc {
bool forced;
bool enabled;
void *rawbuf;
};
struct micron_nand {
struct micron_on_die_ecc ecc;
};
// Elphel begin
/*
Elphel - reintroducing mtd->_lock/_unlock as suggested in d4cb37e71662dac72049bf7c55a9038bd7d2bcb5
of the mainline kernel, which originally dropped these functions.
"
mtd: nand: Remove support for block locking/unlocking
Commit 7d70f33 ("mtd: nand: add lock/unlock routines") introduced
support for the Micron LOCK/UNLOCK commands but no one ever used the
nand_lock/unlock() functions.
Remove support for these vendor-specific operations from the core. If
one ever wants to add them back they should be put in nand_micron.c and
mtd->_lock/_unlock should be directly assigned from there instead of
exporting the functions.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
"
*/
#define NAND_CMD_LOCK 0x2a
#define NAND_CMD_UNLOCK1 0x23
#define NAND_CMD_UNLOCK2 0x24
/**
* nand_unlock_op - Do UNLOCK operation
* @chip: The NAND chip
* @ofs: offset to start unlock from
* @len: length to unlock
* @invert: when = 0, unlock the range of blocks within the lower and
* upper boundary address
* when = 1, unlock the range of blocks outside the boundaries
* of the lower and upper boundary address
*
* This function sends an UNLOCK1 and UNLOCK2 commands and checks status
* before returning.
*
* Elphel:
* * Note 1: see Micron MT29F8G08ADBDAH4
* * Note 2: datasheet: timings for LOCK/UNLOCK are not provided
* * Note 3: datasheet: mandatory status checking is not implied
*
* Returns 0 on success, a negative error code otherwise.
*/
static int nand_unlock_op(struct nand_chip *chip, loff_t ofs, uint64_t len, int invert)
{
struct mtd_info *mtd = nand_to_mtd(chip);
/* Address of the lower boundary */
unsigned int page_lo = ofs >> chip->page_shift;
/* Address of the upper boundary */
unsigned int page_hi = (ofs + len) >> chip->page_shift;
int ret;
u8 status;
if (chip->exec_op) {
u8 addrs_lo[3] = { page_lo, page_lo >> 8, page_lo >> 16 };
u8 addrs_hi[3] = { page_hi|invert, page_hi >> 8, page_hi >> 16 };
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_UNLOCK1, 0),
NAND_OP_ADDR(3, addrs_lo, 0),
NAND_OP_WAIT_RDY(0, 0),
NAND_OP_CMD(NAND_CMD_UNLOCK2, 0),
NAND_OP_ADDR(3, addrs_hi, 0),
NAND_OP_WAIT_RDY(0, 0),
};
struct nand_operation op = NAND_OPERATION(instrs);
ret = nand_exec_op(chip, &op);
if (ret)
return ret;
// not necessary
ret = nand_status_op(chip, &status);
if (ret)
return ret;
} else {
chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page_lo & chip->pagemask);
chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, (page_hi | invert) & chip->pagemask);
ret = chip->waitfunc(mtd, chip);
if (ret < 0)
return ret;
status = ret;
}
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
}
/**
* nand_unlock - unlocks specified locked blocks
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
*
* Returns unlock status.
*/
static int micron_nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
int ret = 0;
int chipnr;
struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
if (check_offs_len(mtd, ofs, len))
return -EINVAL;
/* Align to last block address if size addresses end of the device */
if (ofs + len == mtd->size)
len -= mtd->erasesize;
nand_get_device(mtd, FL_UNLOCKING);
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
/*
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* we must reset the chip
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
nand_reset(chip, chipnr);
chip->select_chip(mtd, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
pr_debug("%s: device is write protected!\n",
__func__);
ret = -EIO;
goto out;
}
ret = nand_unlock_op(chip, ofs, len, 0x0);
out:
chip->select_chip(mtd, -1);
nand_release_device(mtd);
return ret;
}
/**
* micron_nand_lock - locks all blocks present in the device
* @mtd: mtd info
* @ofs: offset to start unlock from
* @len: length to unlock
*
* This feature is not supported in many NAND parts. 'Micron' NAND parts do
* have this feature, but it allows only to lock all blocks, not for specified
* range for block. Implementing 'lock' feature by making use of 'unlock', for
* now.
*
* Returns lock status.
*/
static int micron_nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
int ret = 0;
int chipnr, status, page;
struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
if (check_offs_len(mtd, ofs, len))
return -EINVAL;
nand_get_device(mtd, FL_LOCKING);
/* Shift to get chip number */
chipnr = ofs >> chip->chip_shift;
/*
* Reset the chip.
* If we want to check the WP through READ STATUS and check the bit 7
* we must reset the chip
* some operation can also clear the bit 7 of status register
* eg. erase/program a locked block
*/
nand_reset(chip, chipnr);
chip->select_chip(mtd, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
pr_debug("%s: device is write protected!\n",
__func__);
ret = -EIO;
goto out;
}
if (chip->exec_op) {
u8 op_status;
struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_LOCK, 0),
};
struct nand_operation op = NAND_OPERATION(instrs);
ret = nand_exec_op(chip, &op);
if (ret)
return ret;
ret = nand_status_op(chip, &op_status);
if (ret)
return ret;
}else{
/* Submit address of first page to lock */
page = ofs >> chip->page_shift;
chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask);
/* Call wait ready function */
status = chip->waitfunc(mtd, chip);
/* See if device thinks it succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: error status = 0x%08x\n",
__func__, status);
ret = -EIO;
goto out;
}
}
// now unlock the outer range?
ret = nand_unlock_op(chip, ofs, len, 0x1);
out:
chip->select_chip(mtd, -1);
nand_release_device(mtd);
return ret;
}
// Elphel end
static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
{
struct nand_chip *chip = mtd_to_nand(mtd);
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature);
}
/*
* Configure chip properties from Micron vendor-specific ONFI table
*/
static int micron_nand_onfi_init(struct nand_chip *chip)
{
struct nand_parameters *p = &chip->parameters;
if (p->onfi) {
struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor;
chip->read_retries = micron->read_retry_options;
chip->setup_read_retry = micron_nand_setup_read_retry;
}
if (p->supports_set_get_features) {
set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list);
set_bit(ONFI_FEATURE_ON_DIE_ECC, p->set_feature_list);
set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list);
set_bit(ONFI_FEATURE_ON_DIE_ECC, p->get_feature_list);
}
return 0;
}
static int micron_nand_on_die_4_ooblayout_ecc(struct mtd_info *mtd,
int section,
struct mtd_oob_region *oobregion)
{
if (section >= 4)
return -ERANGE;
oobregion->offset = (section * 16) + 8;
oobregion->length = 8;
return 0;
}
static int micron_nand_on_die_4_ooblayout_free(struct mtd_info *mtd,
int section,
struct mtd_oob_region *oobregion)
{
if (section >= 4)
return -ERANGE;
oobregion->offset = (section * 16) + 2;
oobregion->length = 6;
return 0;
}
static const struct mtd_ooblayout_ops micron_nand_on_die_4_ooblayout_ops = {
.ecc = micron_nand_on_die_4_ooblayout_ecc,
.free = micron_nand_on_die_4_ooblayout_free,
};
static int micron_nand_on_die_8_ooblayout_ecc(struct mtd_info *mtd,
int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = mtd->oobsize - chip->ecc.total;
oobregion->length = chip->ecc.total;
return 0;
}
static int micron_nand_on_die_8_ooblayout_free(struct mtd_info *mtd,
int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = 2;
oobregion->length = mtd->oobsize - chip->ecc.total - 2;
return 0;
}
static const struct mtd_ooblayout_ops micron_nand_on_die_8_ooblayout_ops = {
.ecc = micron_nand_on_die_8_ooblayout_ecc,
.free = micron_nand_on_die_8_ooblayout_free,
};
static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
{
struct micron_nand *micron = nand_get_manufacturer_data(chip);
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
int ret;
if (micron->ecc.forced)
return 0;
if (micron->ecc.enabled == enable)
return 0;
if (enable)
feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
ret = nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature);
if (!ret)
micron->ecc.enabled = enable;
return ret;
}
static int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status,
void *buf, int page,
int oob_required)
{
struct micron_nand *micron = nand_get_manufacturer_data(chip);
struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int step, max_bitflips = 0;
int ret;
if (!(status & NAND_ECC_STATUS_WRITE_RECOMMENDED)) {
if (status & NAND_STATUS_FAIL)
mtd->ecc_stats.failed++;
return 0;
}
/*
* The internal ECC doesn't tell us the number of bitflips that have
* been corrected, but tells us if it recommends to rewrite the block.
* If it's the case, we need to read the page in raw mode and compare
* its content to the corrected version to extract the actual number of
* bitflips.
* But before we do that, we must make sure we have all OOB bytes read
* in non-raw mode, even if the user did not request those bytes.
*/
if (!oob_required) {
ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
false);
if (ret)
return ret;
}
micron_nand_on_die_ecc_setup(chip, false);
ret = nand_read_page_op(chip, page, 0, micron->ecc.rawbuf,
mtd->writesize + mtd->oobsize);
if (ret)
return ret;
for (step = 0; step < chip->ecc.steps; step++) {
unsigned int offs, i, nbitflips = 0;
u8 *rawbuf, *corrbuf;
offs = step * chip->ecc.size;
rawbuf = micron->ecc.rawbuf + offs;
corrbuf = buf + offs;
for (i = 0; i < chip->ecc.size; i++)
nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]);
offs = (step * 16) + 4;
rawbuf = micron->ecc.rawbuf + mtd->writesize + offs;
corrbuf = chip->oob_poi + offs;
for (i = 0; i < chip->ecc.bytes + 4; i++)
nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]);
if (WARN_ON(nbitflips > chip->ecc.strength))
return -EINVAL;
max_bitflips = max(nbitflips, max_bitflips);
mtd->ecc_stats.corrected += nbitflips;
}
return max_bitflips;
}
static int micron_nand_on_die_ecc_status_8(struct nand_chip *chip, u8 status)
{
struct mtd_info *mtd = nand_to_mtd(chip);
/*
* With 8/512 we have more information but still don't know precisely
* how many bit-flips were seen.
*/
switch (status & NAND_ECC_STATUS_MASK) {
case NAND_ECC_STATUS_UNCORRECTABLE:
mtd->ecc_stats.failed++;
return 0;
case NAND_ECC_STATUS_1_3_CORRECTED:
mtd->ecc_stats.corrected += 3;
return 3;
case NAND_ECC_STATUS_4_6_CORRECTED:
mtd->ecc_stats.corrected += 6;
/* rewrite recommended */
return 6;
case NAND_ECC_STATUS_7_8_CORRECTED:
mtd->ecc_stats.corrected += 8;
/* rewrite recommended */
return 8;
default:
return 0;
}
}
static int
micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required,
int page)
{
u8 status;
int ret, max_bitflips = 0;
ret = micron_nand_on_die_ecc_setup(chip, true);
if (ret)
return ret;
ret = nand_read_page_op(chip, page, 0, NULL, 0);
if (ret)
goto out;
ret = nand_status_op(chip, &status);
if (ret)
goto out;
ret = nand_exit_status_op(chip);
if (ret)
goto out;
ret = nand_read_data_op(chip, buf, mtd->writesize, false);
if (!ret && oob_required)
ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize,
false);
if (chip->ecc.strength == 4)
max_bitflips = micron_nand_on_die_ecc_status_4(chip, status,
buf, page,
oob_required);
else
max_bitflips = micron_nand_on_die_ecc_status_8(chip, status);
out:
micron_nand_on_die_ecc_setup(chip, false);
return ret ? ret : max_bitflips;
}
static int
micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required,
int page)
{
int ret;
ret = micron_nand_on_die_ecc_setup(chip, true);
if (ret)
return ret;
ret = nand_write_page_raw(mtd, chip, buf, oob_required, page);
micron_nand_on_die_ecc_setup(chip, false);
return ret;
}
enum {
/* The NAND flash doesn't support on-die ECC */
MICRON_ON_DIE_UNSUPPORTED,
/*
* The NAND flash supports on-die ECC and it can be
* enabled/disabled by a set features command.
*/
MICRON_ON_DIE_SUPPORTED,
/*
* The NAND flash supports on-die ECC, and it cannot be
* disabled.
*/
MICRON_ON_DIE_MANDATORY,
};
#define MICRON_ID_INTERNAL_ECC_MASK GENMASK(1, 0)
#define MICRON_ID_ECC_ENABLED BIT(7)
/*
* Try to detect if the NAND support on-die ECC. To do this, we enable
* the feature, and read back if it has been enabled as expected. We
* also check if it can be disabled, because some Micron NANDs do not
* allow disabling the on-die ECC and we don't support such NANDs for
* now.
*
* This function also has the side effect of disabling on-die ECC if
* it had been left enabled by the firmware/bootloader.
*/
static int micron_supports_on_die_ecc(struct nand_chip *chip)
{
u8 id[5];
int ret;
if (!chip->parameters.onfi)
return MICRON_ON_DIE_UNSUPPORTED;
if (chip->bits_per_cell != 1)
return MICRON_ON_DIE_UNSUPPORTED;
/*
* We only support on-die ECC of 4/512 or 8/512
*/
if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8)
return MICRON_ON_DIE_UNSUPPORTED;
/* 0x2 means on-die ECC is available. */
if (chip->id.len != 5 ||
(chip->id.data[4] & MICRON_ID_INTERNAL_ECC_MASK) != 0x2)
return MICRON_ON_DIE_UNSUPPORTED;
ret = micron_nand_on_die_ecc_setup(chip, true);
if (ret)
return MICRON_ON_DIE_UNSUPPORTED;
ret = nand_readid_op(chip, 0, id, sizeof(id));
if (ret)
return MICRON_ON_DIE_UNSUPPORTED;
if (!(id[4] & MICRON_ID_ECC_ENABLED))
return MICRON_ON_DIE_UNSUPPORTED;
ret = micron_nand_on_die_ecc_setup(chip, false);
if (ret)
return MICRON_ON_DIE_UNSUPPORTED;
ret = nand_readid_op(chip, 0, id, sizeof(id));
if (ret)
return MICRON_ON_DIE_UNSUPPORTED;
if (id[4] & MICRON_ID_ECC_ENABLED)
return MICRON_ON_DIE_MANDATORY;
/*
* We only support on-die ECC of 4/512 or 8/512
*/
if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8)
return MICRON_ON_DIE_UNSUPPORTED;
return MICRON_ON_DIE_SUPPORTED;
}
static int micron_nand_init(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct micron_nand *micron;
int ondie;
int ret;
micron = kzalloc(sizeof(*micron), GFP_KERNEL);
if (!micron)
return -ENOMEM;
nand_set_manufacturer_data(chip, micron);
ret = micron_nand_onfi_init(chip);
if (ret)
goto err_free_manuf_data;
if (mtd->writesize == 2048)
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
ondie = micron_supports_on_die_ecc(chip);
if (ondie == MICRON_ON_DIE_MANDATORY &&
chip->ecc.mode != NAND_ECC_ON_DIE) {
pr_err("On-die ECC forcefully enabled, not supported\n");
ret = -EINVAL;
goto err_free_manuf_data;
}
if (chip->ecc.mode == NAND_ECC_ON_DIE) {
if (ondie == MICRON_ON_DIE_UNSUPPORTED) {
pr_err("On-die ECC selected but not supported\n");
ret = -EINVAL;
goto err_free_manuf_data;
}
if (ondie == MICRON_ON_DIE_MANDATORY) {
micron->ecc.forced = true;
micron->ecc.enabled = true;
}
/*
* In case of 4bit on-die ECC, we need a buffer to store a
* page dumped in raw mode so that we can compare its content
* to the same page after ECC correction happened and extract
* the real number of bitflips from this comparison.
* That's not needed for 8-bit ECC, because the status expose
* a better approximation of the number of bitflips in a page.
*/
if (chip->ecc_strength_ds == 4) {
micron->ecc.rawbuf = kmalloc(mtd->writesize +
mtd->oobsize,
GFP_KERNEL);
if (!micron->ecc.rawbuf) {
ret = -ENOMEM;
goto err_free_manuf_data;
}
}
if (chip->ecc_strength_ds == 4)
mtd_set_ooblayout(mtd,
&micron_nand_on_die_4_ooblayout_ops);
else
mtd_set_ooblayout(mtd,
&micron_nand_on_die_8_ooblayout_ops);
chip->ecc.bytes = chip->ecc_strength_ds * 2;
// this is already read from the device tree
if (!chip->ecc.size){
chip->ecc.size = 512;
}
chip->ecc.strength = chip->ecc_strength_ds;
chip->ecc.algo = NAND_ECC_BCH;
chip->ecc.read_page = micron_nand_read_page_on_die_ecc;
chip->ecc.write_page = micron_nand_write_page_on_die_ecc;
if (ondie == MICRON_ON_DIE_MANDATORY) {
chip->ecc.read_page_raw = nand_read_page_raw_notsupp;
chip->ecc.write_page_raw = nand_write_page_raw_notsupp;
} else {
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
}
}
// Elphel: functions to work with OTP area: *user_prot*
// chip->id.data[1] is dev_id (0xa3 in Elphel 10393)
nand_micron_mt29f_init(mtd, chip->id.data[1]);
// Elphel: modification for Micron NAND chips
//TODO: add Micron chip ID checking
mtd->_unlock = micron_nand_unlock;
mtd->_lock = micron_nand_lock;
return 0;
err_free_manuf_data:
kfree(micron->ecc.rawbuf);
kfree(micron);
return ret;
}
static void micron_nand_cleanup(struct nand_chip *chip)
{
struct micron_nand *micron = nand_get_manufacturer_data(chip);
kfree(micron->ecc.rawbuf);
kfree(micron);
}
static void micron_fixup_onfi_param_page(struct nand_chip *chip,
struct nand_onfi_params *p)
{
/*
* MT29F1G08ABAFAWP-ITE:F and possibly others report 00 00 for the
* revision number field of the ONFI parameter page. Assume ONFI
* version 1.0 if the revision number is 00 00.
*/
if (le16_to_cpu(p->revision) == 0)
p->revision = cpu_to_le16(ONFI_VERSION_1_0);
}
const struct nand_manufacturer_ops micron_nand_manuf_ops = {
.init = micron_nand_init,
.cleanup = micron_nand_cleanup,
.fixup_onfi_param_page = micron_fixup_onfi_param_page,
};
......@@ -48,7 +48,6 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
struct nand_chip *chip = mtd->priv;
struct mtd_oob_ops ops;
int ret;
u8 get_feature;
/* Valid pages in otp are 02h-1Fh. */
if (from > MICRON_NUM_OTP_PAGES << chip->page_shift)
......@@ -63,19 +62,18 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
nand_get_device(mtd, FL_READING);
chip->select_chip(mtd, 0);
ret = chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,MICRON_SETFEATURE_ARRAYOP_OTP);
ndelay(1000);
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, MICRON_SETFEATURE_ARRAYOP, -1);
get_feature = readb(chip->IO_ADDR_R);
pr_debug("Feature on: 0x%02x\n",get_feature);
ret = nand_set_features(chip, MICRON_SETFEATURE_ARRAYOP, MICRON_SETFEATURE_ARRAYOP_OTP);
if (ret)
goto out;
ops.len = len;
ops.datbuf = buf;
ops.oobbuf = NULL;
ops.mode = 0;
// old, triggers chip->ecc.read_page() which enables/disables ondie ECC at each call
//ops.mode = 0;
// new, triggers chip->ecc.read_page_raw()
ops.mode = MTD_OPS_RAW;
/*
* XXX: some things in nand_do_read_ops might be wrong for OTP. e.g.
* chip->pagemask, chip->pagebuf handling, caching
......@@ -86,11 +84,7 @@ static int mt29f_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
/* nand_do_read_ops deselects the chip so reselect here */
chip->select_chip(mtd, 0);
chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,MICRON_SETFEATURE_ARRAYOP_NORMAL);
ndelay(1000);
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, MICRON_SETFEATURE_ARRAYOP, -1);
get_feature = readb(chip->IO_ADDR_R);
pr_debug("Feature off: 0x%02x\n",get_feature);
ret = nand_set_features(chip, MICRON_SETFEATURE_ARRAYOP, MICRON_SETFEATURE_ARRAYOP_NORMAL);
out:
nand_release_device(mtd);
......@@ -118,17 +112,19 @@ static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
nand_get_device(mtd, FL_WRITING);
chip->select_chip(mtd, 0);
ret = chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_OTP);
ret = nand_set_features(chip, MICRON_SETFEATURE_ARRAYOP, MICRON_SETFEATURE_ARRAYOP_OTP);
if (ret)
goto out;
ops.len = len;
ops.datbuf = buf;
ops.oobbuf = NULL;
ops.mode = 0;
// old
//ops.mode = 0;
// need raw mode
ops.mode = MTD_OPS_RAW;
/*
* some things in nand_do_write_ops might be wrong for OTP. e.g.
* chip->pagemask, chip->pagebuf handling
......@@ -138,9 +134,7 @@ static int mt29f_write_user_prot_reg(struct mtd_info *mtd, loff_t to,
/* nand_do_write_ops deselects the chip so reselect here */
chip->select_chip(mtd, 0);
chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_NORMAL);
ret = nand_set_features(chip, MICRON_SETFEATURE_ARRAYOP, MICRON_SETFEATURE_ARRAYOP_NORMAL);
out:
nand_release_device(mtd);
......@@ -153,6 +147,7 @@ static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
struct nand_chip *chip = mtd->priv;
int ret;
int i;
uint8_t zerobuf = 0;
/* assert from and len are aligned */
if (NOTALIGNED(from) || NOTALIGNED(len)) {
......@@ -175,29 +170,36 @@ static int mt29f_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
nand_get_device(mtd, FL_WRITING);
chip->select_chip(mtd, 0);
ret = chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
ret = chip->set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_OTPPROTECT);
if (ret)
goto out;
// old
/*
for (i = 0; i < len << chip->page_shift; ++i) {
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0,
(from << chip->page_shift) + i);
chip->write_byte(mtd, 0);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
}
*/
// new
// TODO: Test this? Not critical. It's doubtful locking OTP will ever be needed.
for (i = 0; i < len << chip->page_shift; ++i) {
nand_prog_page_op(chip,(from << chip->page_shift)+i, 0, &zerobuf, 1);
}
chip->onfi_set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
chip->set_features(mtd, chip, MICRON_SETFEATURE_ARRAYOP,
MICRON_SETFEATURE_ARRAYOP_NORMAL);
out:
nand_release_device(mtd);
return ret;
}
void nandchip_micron_init(struct mtd_info *mtd, int dev_id)
void nand_micron_mt29f_init(struct mtd_info *mtd, int dev_id)
{
/*
* OTP is available on (at least) Micron's MT29F2G{08,16}AB[AB]EA,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment