Commit b025beef authored by Oleg Dzhimiev's avatar Oleg Dzhimiev
Browse files

updated

parent 9159c877
Loading
Loading
Loading
Loading
+165 −101
Original line number Diff line number Diff line
@@ -83,8 +83,6 @@ struct pl35x_nand_command_format {
/**
 * struct pl35x_nand_info - Defines the NAND flash driver instance
 * @chip:		NAND chip information structure
 * @mtd:		MTD information structure
 * @parts:		Pointer to the mtd_partition structure
 * @nand_base:		Virtual address of the NAND flash device
 * @end_cmd_pending:	End command is pending
 * @end_cmd:		End command
@@ -93,8 +91,6 @@ struct pl35x_nand_command_format {
 */
struct pl35x_nand_info {
	struct nand_chip chip;
	struct mtd_info mtd;
	struct mtd_partition *parts;
	void __iomem *nand_base;
	unsigned long end_cmd_pending;
	unsigned long end_cmd;
@@ -133,43 +129,110 @@ static const struct pl35x_nand_command_format pl35x_nand_commands[] = {
	 */
};

/* Define default oob placement schemes for large and small page devices */
static struct nand_ecclayout nand_oob_16 = {
	.eccbytes = 3,
	.eccpos = {0, 1, 2},
	.oobfree = {
		{.offset = 8,
		 . length = 8} }
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 * 16) + 52;
	oobregion->length = chip->ecc.bytes;

	return 0;
}

static struct nand_ecclayout nand_oob_64 = {
	.eccbytes = 12,
	.eccpos = {
		   52, 53, 54, 55, 56, 57,
		   58, 59, 60, 61, 62, 63},
	.oobfree = {
		{.offset = 2,
		 .length = 50} }
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 >= chip->ecc.steps)
		return -ERANGE;

	oobregion->offset = (section * 16) + 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);

static struct nand_ecclayout ondie_nand_oob_64 = {
	.eccbytes = 32,
	if (section >= chip->ecc.steps)
		return -ERANGE;

	.eccpos = {
		8, 9, 10, 11, 12, 13, 14, 15,
		24, 25, 26, 27, 28, 29, 30, 31,
		40, 41, 42, 43, 44, 45, 46, 47,
		56, 57, 58, 59, 60, 61, 62, 63
	},
	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;

	.oobfree = {
		{ .offset = 4, .length = 4 },
		{ .offset = 20, .length = 4 },
		{ .offset = 36, .length = 4 },
		{ .offset = 52, .length = 4 }

	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' };
@@ -211,7 +274,6 @@ static int pl35x_nand_calculate_hwecc(struct mtd_info *mtd,
	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())
@@ -415,7 +477,8 @@ static int pl35x_nand_read_page_raw(struct mtd_info *mtd,
 */
static int pl35x_nand_write_page_raw(struct mtd_info *mtd,
				    struct nand_chip *chip,
				    const uint8_t *buf, int oob_required)
				    const uint8_t *buf, int oob_required,
				    int page)
{
	unsigned long data_phase_addr;
	uint8_t *p;
@@ -450,15 +513,15 @@ static int pl35x_nand_write_page_raw(struct mtd_info *mtd,
 */
static int pl35x_nand_write_page_hwecc(struct mtd_info *mtd,
				    struct nand_chip *chip, const uint8_t *buf,
				    int oob_required)
				    int oob_required, int page)
{
	int i, eccsize = chip->ecc.size;
	int eccsize = chip->ecc.size;
	int eccsteps = chip->ecc.steps;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	const uint8_t *p = buf;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	unsigned long data_phase_addr;
	uint8_t *oob_ptr;
	u32 ret;

	for ( ; (eccsteps - 1); eccsteps--) {
		chip->write_buf(mtd, p, eccsize);
@@ -473,13 +536,14 @@ static int pl35x_nand_write_page_hwecc(struct mtd_info *mtd,
	chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
	chip->write_buf(mtd, p, PL35X_NAND_LAST_TRANSFER_LENGTH);

	/* Wait for ECC to be calculated and read the error values */
	p = buf;
	chip->ecc.calculate(mtd, p, &ecc_calc[0]);

	for (i = 0; i < chip->ecc.total; i++)
		chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]);

	/* 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 &= ~PL35X_NAND_ECC_LAST;
@@ -511,23 +575,23 @@ static int pl35x_nand_write_page_hwecc(struct mtd_info *mtd,
 */
static int pl35x_nand_write_page_swecc(struct mtd_info *mtd,
				    struct nand_chip *chip, const uint8_t *buf,
				    int oob_required)
				    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;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	u32 ret;

	/* Software ecc calculation */
	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
		chip->ecc.calculate(mtd, p, &ecc_calc[i]);

	for (i = 0; i < chip->ecc.total; i++)
		chip->oob_poi[eccpos[i]] = ecc_calc[i];
		chip->ecc.calculate(mtd, p, &ecc_calc[0]);

	chip->ecc.write_page_raw(mtd, chip, buf, 1);
	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;
}
@@ -555,9 +619,9 @@ static int pl35x_nand_read_page_hwecc(struct mtd_info *mtd,
	uint8_t *p = buf;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	uint8_t *ecc_code = chip->buffers->ecccode;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	unsigned long data_phase_addr;
	uint8_t *oob_ptr;
	u32 ret;

	for ( ; (eccsteps - 1); eccsteps--) {
		chip->read_buf(mtd, p, eccsize);
@@ -594,8 +658,10 @@ static int pl35x_nand_read_page_hwecc(struct mtd_info *mtd,
	oob_ptr += (mtd->oobsize - PL35X_NAND_LAST_TRANSFER_LENGTH);
	chip->read_buf(mtd, oob_ptr, PL35X_NAND_LAST_TRANSFER_LENGTH);

	for (i = 0; i < chip->ecc.total; i++)
		ecc_code[i] = ~(chip->oob_poi[eccpos[i]]);
	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;
@@ -631,15 +697,15 @@ static int pl35x_nand_read_page_swecc(struct mtd_info *mtd,
	uint8_t *p = buf;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	uint8_t *ecc_code = chip->buffers->ecccode;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	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]);

	for (i = 0; i < chip->ecc.total; i++)
		ecc_code[i] = chip->oob_poi[eccpos[i]];
	ret = mtd_ooblayout_get_eccbytes(mtd, ecc_calc, chip->oob_poi,
						0, chip->ecc.total);

	eccsteps = chip->ecc.steps;
	p = buf;
@@ -684,10 +750,10 @@ static void pl35x_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
	if (command == NAND_CMD_UNLOCK1) pr_debug("NAND UNLOCK1\n");
	if (command == NAND_CMD_SET_FEATURES) pr_debug("NAND NAND_CMD_SET_FEATURES\n");

	struct nand_chip *chip = mtd->priv;
	struct nand_chip *chip = mtd_to_nand(mtd);
	const struct pl35x_nand_command_format *curr_cmd = NULL;
	struct pl35x_nand_info *xnand =
		container_of(mtd, struct pl35x_nand_info, mtd);
		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;
@@ -834,7 +900,7 @@ static void pl35x_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
static void pl35x_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
	int i;
	struct nand_chip *chip = mtd->priv;
	struct nand_chip *chip =  mtd_to_nand(mtd);
	unsigned long *ptr = (unsigned long *)buf;

	len >>= 2;
@@ -852,18 +918,17 @@ static void pl35x_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
				int len)
{
	int i;
	struct nand_chip *chip = mtd->priv;
	struct nand_chip *chip = mtd_to_nand(mtd);
	unsigned long *ptr = (unsigned long *)buf;

	pr_debug("pl35x_nand_write_buf: datasize=%d len=%d len>>2=%d\n",sizeof(ptr[0]),len,len>>2);

	len >>= 2;

	for (i = 0; i < len; i++)
		writel(ptr[i], chip->IO_ADDR_W);
		//writeb(byte, chip->IO_ADDR_W);
}


/**
 * pl35x_nand_device_ready - Check device ready/busy line
 * @mtd:	Pointer to the mtd_info structure
@@ -879,6 +944,7 @@ static int pl35x_nand_device_ready(struct mtd_info *mtd)
	return 0;
}

// Elphel: whole function
/**
 * pl35x_nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
 * @mtd: MTD device structure
@@ -916,6 +982,7 @@ static int pl35x_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *
	return 0;
}

// Elphel: whole function
/**
 * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
 * @mtd: MTD device structure
@@ -952,7 +1019,7 @@ static int pl35x_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *
 */
static int pl35x_nand_detect_ondie_ecc(struct mtd_info *mtd)
{
	struct nand_chip *nand_chip = mtd->priv;
	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 };

@@ -1009,16 +1076,17 @@ static int pl35x_nand_detect_ondie_ecc(struct mtd_info *mtd)
 * This function initializes the ecc block and functional pointers as per the
 * ecc mode
 */
static void pl35x_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state)
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->priv;
	struct nand_chip *nand_chip = mtd_to_nand(mtd);

	nand_chip->ecc.mode = NAND_ECC_HW;
	nand_chip->ecc.read_oob = pl35x_nand_read_oob;
	nand_chip->ecc.read_page_raw = pl35x_nand_read_page_raw;
	nand_chip->ecc.strength = 1;
	nand_chip->ecc.write_oob = pl35x_nand_write_oob;
	nand_chip->ecc.write_page_raw = pl35x_nand_write_page_raw;
	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 */
@@ -1028,11 +1096,11 @@ static void pl35x_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state)
		 * The software ECC routines won't work with the
		 * SMC controller
		 */
		nand_chip->ecc.bytes = 0;
		nand_chip->ecc.layout = &ondie_nand_oob_64;
		nand_chip->ecc.read_page = pl35x_nand_read_page_raw;
		nand_chip->ecc.write_page = pl35x_nand_write_page_raw;
		nand_chip->ecc.size = mtd->writesize;
		ecc->bytes = 0;
		mtd_set_ooblayout(mtd, &fsmc_ecc_ooblayout_ondie64_ops);
		ecc->read_page = pl35x_nand_read_page_raw;
		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
@@ -1041,13 +1109,13 @@ static void pl35x_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state)
		nand_chip->bbt_md = &bbt_mirror_descr;
	} else {
		/* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
		nand_chip->ecc.bytes = 3;
		nand_chip->ecc.calculate = pl35x_nand_calculate_hwecc;
		nand_chip->ecc.correct = pl35x_nand_correct_data;
		nand_chip->ecc.hwctl = NULL;
		nand_chip->ecc.read_page = pl35x_nand_read_page_hwecc;
		nand_chip->ecc.size = PL35X_NAND_ECC_SIZE;
		nand_chip->ecc.write_page = pl35x_nand_write_page_hwecc;
		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) {
@@ -1061,18 +1129,18 @@ static void pl35x_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state)
			 * The software ECC routines won't work with the
			 * SMC controller
			 */
			nand_chip->ecc.calculate = nand_calculate_ecc;
			nand_chip->ecc.correct = nand_correct_data;
			nand_chip->ecc.read_page = pl35x_nand_read_page_swecc;
			nand_chip->ecc.write_page = pl35x_nand_write_page_swecc;
			nand_chip->ecc.size = 256;
			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)
			nand_chip->ecc.layout = &nand_oob_16;
			mtd_set_ooblayout(mtd, &fsmc_ecc_ooblayout16_ops);
		else if (mtd->oobsize == 64)
			nand_chip->ecc.layout = &nand_oob_64;
			mtd_set_ooblayout(mtd, &fsmc_ecc_ooblayout64_ops);
	}
}

@@ -1090,7 +1158,6 @@ static int pl35x_nand_probe(struct platform_device *pdev)
	struct mtd_info *mtd;
	struct nand_chip *nand_chip;
	struct resource *res;
	struct mtd_part_parser_data ppdata;
	int ondie_ecc_state;

	xnand = devm_kzalloc(&pdev->dev, sizeof(*xnand), GFP_KERNEL);
@@ -1103,14 +1170,14 @@ static int pl35x_nand_probe(struct platform_device *pdev)
	if (IS_ERR(xnand->nand_base))
		return PTR_ERR(xnand->nand_base);

	/* Link the private data with the MTD structure */
	mtd = &xnand->mtd;
	nand_chip = &xnand->chip;
	mtd = nand_to_mtd(nand_chip);

	nand_chip->priv = xnand;
	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;
@@ -1149,7 +1216,7 @@ static int pl35x_nand_probe(struct platform_device *pdev)
	xnand->col_addr_cycles =
				(nand_chip->onfi_params.addr_cycles >> 4) & 0xF;

	pl35x_nand_ecc_init(mtd, ondie_ecc_state);
	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);

@@ -1165,9 +1232,7 @@ static int pl35x_nand_probe(struct platform_device *pdev)
	mtd->_unlock = nand_unlock;
	mtd->_lock = nand_lock;

	ppdata.of_node = pdev->dev.of_node;

	mtd_device_parse_register(&xnand->mtd, NULL, &ppdata, NULL, 0);
	mtd_device_register(mtd, NULL, 0);

	return 0;
}
@@ -1184,11 +1249,10 @@ static int pl35x_nand_probe(struct platform_device *pdev)
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(&xnand->mtd);
	/* kfree(NULL) is safe */
	kfree(xnand->parts);
	nand_release(mtd);

	return 0;
}