18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Handles the M-Systems DiskOnChip G3 chip
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Robert Jarzmik
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/string.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
188c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
198c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
208c2ecf20Sopenharmony_ci#include <linux/bitrev.h>
218c2ecf20Sopenharmony_ci#include <linux/bch.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
248c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
278c2ecf20Sopenharmony_ci#include "docg3.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * This driver handles the DiskOnChip G3 flash memory.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * As no specification is available from M-Systems/Sandisk, this drivers lacks
338c2ecf20Sopenharmony_ci * several functions available on the chip, as :
348c2ecf20Sopenharmony_ci *  - IPL write
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and
378c2ecf20Sopenharmony_ci * the driver assumes a 16bits data bus.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * DocG3 relies on 2 ECC algorithms, which are handled in hardware :
408c2ecf20Sopenharmony_ci *  - a 1 byte Hamming code stored in the OOB for each page
418c2ecf20Sopenharmony_ci *  - a 7 bytes BCH code stored in the OOB for each page
428c2ecf20Sopenharmony_ci * The BCH ECC is :
438c2ecf20Sopenharmony_ci *  - BCH is in GF(2^14)
448c2ecf20Sopenharmony_ci *  - BCH is over data of 520 bytes (512 page + 7 page_info bytes
458c2ecf20Sopenharmony_ci *                                   + 1 hamming byte)
468c2ecf20Sopenharmony_ci *  - BCH can correct up to 4 bits (t = 4)
478c2ecf20Sopenharmony_ci *  - BCH syndroms are calculated in hardware, and checked in hardware as well
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic unsigned int reliable_mode;
528c2ecf20Sopenharmony_cimodule_param(reliable_mode, uint, 0);
538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, "
548c2ecf20Sopenharmony_ci		 "2=reliable) : MLC normal operations are in normal mode");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int docg3_ooblayout_ecc(struct mtd_info *mtd, int section,
578c2ecf20Sopenharmony_ci			       struct mtd_oob_region *oobregion)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	if (section)
608c2ecf20Sopenharmony_ci		return -ERANGE;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* byte 7 is Hamming ECC, byte 8-14 are BCH ECC */
638c2ecf20Sopenharmony_ci	oobregion->offset = 7;
648c2ecf20Sopenharmony_ci	oobregion->length = 8;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int docg3_ooblayout_free(struct mtd_info *mtd, int section,
708c2ecf20Sopenharmony_ci				struct mtd_oob_region *oobregion)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	if (section > 1)
738c2ecf20Sopenharmony_ci		return -ERANGE;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* free bytes: byte 0 until byte 6, byte 15 */
768c2ecf20Sopenharmony_ci	if (!section) {
778c2ecf20Sopenharmony_ci		oobregion->offset = 0;
788c2ecf20Sopenharmony_ci		oobregion->length = 7;
798c2ecf20Sopenharmony_ci	} else {
808c2ecf20Sopenharmony_ci		oobregion->offset = 15;
818c2ecf20Sopenharmony_ci		oobregion->length = 1;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_docg3_ops = {
888c2ecf20Sopenharmony_ci	.ecc = docg3_ooblayout_ecc,
898c2ecf20Sopenharmony_ci	.free = docg3_ooblayout_free,
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic inline u8 doc_readb(struct docg3 *docg3, u16 reg)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	u8 val = readb(docg3->cascade->base + reg);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	trace_docg3_io(0, 8, reg, (int)val);
978c2ecf20Sopenharmony_ci	return val;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic inline u16 doc_readw(struct docg3 *docg3, u16 reg)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	u16 val = readw(docg3->cascade->base + reg);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	trace_docg3_io(0, 16, reg, (int)val);
1058c2ecf20Sopenharmony_ci	return val;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	writeb(val, docg3->cascade->base + reg);
1118c2ecf20Sopenharmony_ci	trace_docg3_io(1, 8, reg, val);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	writew(val, docg3->cascade->base + reg);
1178c2ecf20Sopenharmony_ci	trace_docg3_io(1, 16, reg, val);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic inline void doc_flash_command(struct docg3 *docg3, u8 cmd)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	doc_writeb(docg3, cmd, DOC_FLASHCOMMAND);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline void doc_flash_sequence(struct docg3 *docg3, u8 seq)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	doc_writeb(docg3, seq, DOC_FLASHSEQUENCE);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline void doc_flash_address(struct docg3 *docg3, u8 addr)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	doc_writeb(docg3, addr, DOC_FLASHADDRESS);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic char const * const part_probes[] = { "cmdlinepart", "saftlpart", NULL };
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int doc_register_readb(struct docg3 *docg3, int reg)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u8 val;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	doc_writew(docg3, reg, DOC_READADDRESS);
1428c2ecf20Sopenharmony_ci	val = doc_readb(docg3, reg);
1438c2ecf20Sopenharmony_ci	doc_vdbg("Read register %04x : %02x\n", reg, val);
1448c2ecf20Sopenharmony_ci	return val;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int doc_register_readw(struct docg3 *docg3, int reg)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	u16 val;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	doc_writew(docg3, reg, DOC_READADDRESS);
1528c2ecf20Sopenharmony_ci	val = doc_readw(docg3, reg);
1538c2ecf20Sopenharmony_ci	doc_vdbg("Read register %04x : %04x\n", reg, val);
1548c2ecf20Sopenharmony_ci	return val;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/**
1588c2ecf20Sopenharmony_ci * doc_delay - delay docg3 operations
1598c2ecf20Sopenharmony_ci * @docg3: the device
1608c2ecf20Sopenharmony_ci * @nbNOPs: the number of NOPs to issue
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * As no specification is available, the right timings between chip commands are
1638c2ecf20Sopenharmony_ci * unknown. The only available piece of information are the observed nops on a
1648c2ecf20Sopenharmony_ci * working docg3 chip.
1658c2ecf20Sopenharmony_ci * Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler
1668c2ecf20Sopenharmony_ci * friendlier msleep() functions or blocking mdelay().
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_cistatic void doc_delay(struct docg3 *docg3, int nbNOPs)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int i;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	doc_vdbg("NOP x %d\n", nbNOPs);
1738c2ecf20Sopenharmony_ci	for (i = 0; i < nbNOPs; i++)
1748c2ecf20Sopenharmony_ci		doc_writeb(docg3, 0, DOC_NOP);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int is_prot_seq_error(struct docg3 *docg3)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int ctrl;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
1828c2ecf20Sopenharmony_ci	return ctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int doc_is_ready(struct docg3 *docg3)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int ctrl;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
1908c2ecf20Sopenharmony_ci	return ctrl & DOC_CTRL_FLASHREADY;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int doc_wait_ready(struct docg3 *docg3)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int maxWaitCycles = 100;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	do {
1988c2ecf20Sopenharmony_ci		doc_delay(docg3, 4);
1998c2ecf20Sopenharmony_ci		cpu_relax();
2008c2ecf20Sopenharmony_ci	} while (!doc_is_ready(docg3) && maxWaitCycles--);
2018c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
2028c2ecf20Sopenharmony_ci	if (maxWaitCycles > 0)
2038c2ecf20Sopenharmony_ci		return 0;
2048c2ecf20Sopenharmony_ci	else
2058c2ecf20Sopenharmony_ci		return -EIO;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int doc_reset_seq(struct docg3 *docg3)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int ret;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	doc_writeb(docg3, 0x10, DOC_FLASHCONTROL);
2138c2ecf20Sopenharmony_ci	doc_flash_sequence(docg3, DOC_SEQ_RESET);
2148c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_RESET);
2158c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
2168c2ecf20Sopenharmony_ci	ret = doc_wait_ready(docg3);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	doc_dbg("doc_reset_seq() -> isReady=%s\n", ret ? "false" : "true");
2198c2ecf20Sopenharmony_ci	return ret;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/**
2238c2ecf20Sopenharmony_ci * doc_read_data_area - Read data from data area
2248c2ecf20Sopenharmony_ci * @docg3: the device
2258c2ecf20Sopenharmony_ci * @buf: the buffer to fill in (might be NULL is dummy reads)
2268c2ecf20Sopenharmony_ci * @len: the length to read
2278c2ecf20Sopenharmony_ci * @first: first time read, DOC_READADDRESS should be set
2288c2ecf20Sopenharmony_ci *
2298c2ecf20Sopenharmony_ci * Reads bytes from flash data. Handles the single byte / even bytes reads.
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_cistatic void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
2328c2ecf20Sopenharmony_ci			       int first)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	int i, cdr, len4;
2358c2ecf20Sopenharmony_ci	u16 data16, *dst16;
2368c2ecf20Sopenharmony_ci	u8 data8, *dst8;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len);
2398c2ecf20Sopenharmony_ci	cdr = len & 0x1;
2408c2ecf20Sopenharmony_ci	len4 = len - cdr;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (first)
2438c2ecf20Sopenharmony_ci		doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS);
2448c2ecf20Sopenharmony_ci	dst16 = buf;
2458c2ecf20Sopenharmony_ci	for (i = 0; i < len4; i += 2) {
2468c2ecf20Sopenharmony_ci		data16 = doc_readw(docg3, DOC_IOSPACE_DATA);
2478c2ecf20Sopenharmony_ci		if (dst16) {
2488c2ecf20Sopenharmony_ci			*dst16 = data16;
2498c2ecf20Sopenharmony_ci			dst16++;
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (cdr) {
2548c2ecf20Sopenharmony_ci		doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE,
2558c2ecf20Sopenharmony_ci			   DOC_READADDRESS);
2568c2ecf20Sopenharmony_ci		doc_delay(docg3, 1);
2578c2ecf20Sopenharmony_ci		dst8 = (u8 *)dst16;
2588c2ecf20Sopenharmony_ci		for (i = 0; i < cdr; i++) {
2598c2ecf20Sopenharmony_ci			data8 = doc_readb(docg3, DOC_IOSPACE_DATA);
2608c2ecf20Sopenharmony_ci			if (dst8) {
2618c2ecf20Sopenharmony_ci				*dst8 = data8;
2628c2ecf20Sopenharmony_ci				dst8++;
2638c2ecf20Sopenharmony_ci			}
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/**
2698c2ecf20Sopenharmony_ci * doc_write_data_area - Write data into data area
2708c2ecf20Sopenharmony_ci * @docg3: the device
2718c2ecf20Sopenharmony_ci * @buf: the buffer to get input bytes from
2728c2ecf20Sopenharmony_ci * @len: the length to write
2738c2ecf20Sopenharmony_ci *
2748c2ecf20Sopenharmony_ci * Writes bytes into flash data. Handles the single byte / even bytes writes.
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic void doc_write_data_area(struct docg3 *docg3, const void *buf, int len)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int i, cdr, len4;
2798c2ecf20Sopenharmony_ci	u16 *src16;
2808c2ecf20Sopenharmony_ci	u8 *src8;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len);
2838c2ecf20Sopenharmony_ci	cdr = len & 0x3;
2848c2ecf20Sopenharmony_ci	len4 = len - cdr;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS);
2878c2ecf20Sopenharmony_ci	src16 = (u16 *)buf;
2888c2ecf20Sopenharmony_ci	for (i = 0; i < len4; i += 2) {
2898c2ecf20Sopenharmony_ci		doc_writew(docg3, *src16, DOC_IOSPACE_DATA);
2908c2ecf20Sopenharmony_ci		src16++;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	src8 = (u8 *)src16;
2948c2ecf20Sopenharmony_ci	for (i = 0; i < cdr; i++) {
2958c2ecf20Sopenharmony_ci		doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE,
2968c2ecf20Sopenharmony_ci			   DOC_READADDRESS);
2978c2ecf20Sopenharmony_ci		doc_writeb(docg3, *src8, DOC_IOSPACE_DATA);
2988c2ecf20Sopenharmony_ci		src8++;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/**
3038c2ecf20Sopenharmony_ci * doc_set_data_mode - Sets the flash to normal or reliable data mode
3048c2ecf20Sopenharmony_ci * @docg3: the device
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * The reliable data mode is a bit slower than the fast mode, but less errors
3078c2ecf20Sopenharmony_ci * occur.  Entering the reliable mode cannot be done without entering the fast
3088c2ecf20Sopenharmony_ci * mode first.
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks
3118c2ecf20Sopenharmony_ci * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading
3128c2ecf20Sopenharmony_ci * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same
3138c2ecf20Sopenharmony_ci * result, which is a logical and between bytes from page 0 and page 1 (which is
3148c2ecf20Sopenharmony_ci * consistent with the fact that writing to a page is _clearing_ bits of that
3158c2ecf20Sopenharmony_ci * page).
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_cistatic void doc_set_reliable_mode(struct docg3 *docg3)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	static char *strmode[] = { "normal", "fast", "reliable", "invalid" };
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]);
3228c2ecf20Sopenharmony_ci	switch (docg3->reliable) {
3238c2ecf20Sopenharmony_ci	case 0:
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci	case 1:
3268c2ecf20Sopenharmony_ci		doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE);
3278c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_FAST_MODE);
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	case 2:
3308c2ecf20Sopenharmony_ci		doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE);
3318c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_FAST_MODE);
3328c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE);
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	default:
3358c2ecf20Sopenharmony_ci		doc_err("doc_set_reliable_mode(): invalid mode\n");
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/**
3428c2ecf20Sopenharmony_ci * doc_set_asic_mode - Set the ASIC mode
3438c2ecf20Sopenharmony_ci * @docg3: the device
3448c2ecf20Sopenharmony_ci * @mode: the mode
3458c2ecf20Sopenharmony_ci *
3468c2ecf20Sopenharmony_ci * The ASIC can work in 3 modes :
3478c2ecf20Sopenharmony_ci *  - RESET: all registers are zeroed
3488c2ecf20Sopenharmony_ci *  - NORMAL: receives and handles commands
3498c2ecf20Sopenharmony_ci *  - POWERDOWN: minimal poweruse, flash parts shut off
3508c2ecf20Sopenharmony_ci */
3518c2ecf20Sopenharmony_cistatic void doc_set_asic_mode(struct docg3 *docg3, u8 mode)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	int i;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	for (i = 0; i < 12; i++)
3568c2ecf20Sopenharmony_ci		doc_readb(docg3, DOC_IOSPACE_IPL);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	mode |= DOC_ASICMODE_MDWREN;
3598c2ecf20Sopenharmony_ci	doc_dbg("doc_set_asic_mode(%02x)\n", mode);
3608c2ecf20Sopenharmony_ci	doc_writeb(docg3, mode, DOC_ASICMODE);
3618c2ecf20Sopenharmony_ci	doc_writeb(docg3, ~mode, DOC_ASICMODECONFIRM);
3628c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci/**
3668c2ecf20Sopenharmony_ci * doc_set_device_id - Sets the devices id for cascaded G3 chips
3678c2ecf20Sopenharmony_ci * @docg3: the device
3688c2ecf20Sopenharmony_ci * @id: the chip to select (amongst 0, 1, 2, 3)
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * There can be 4 cascaded G3 chips. This function selects the one which will
3718c2ecf20Sopenharmony_ci * should be the active one.
3728c2ecf20Sopenharmony_ci */
3738c2ecf20Sopenharmony_cistatic void doc_set_device_id(struct docg3 *docg3, int id)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	u8 ctrl;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	doc_dbg("doc_set_device_id(%d)\n", id);
3788c2ecf20Sopenharmony_ci	doc_writeb(docg3, id, DOC_DEVICESELECT);
3798c2ecf20Sopenharmony_ci	ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ctrl &= ~DOC_CTRL_VIOLATION;
3828c2ecf20Sopenharmony_ci	ctrl |= DOC_CTRL_CE;
3838c2ecf20Sopenharmony_ci	doc_writeb(docg3, ctrl, DOC_FLASHCONTROL);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/**
3878c2ecf20Sopenharmony_ci * doc_set_extra_page_mode - Change flash page layout
3888c2ecf20Sopenharmony_ci * @docg3: the device
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci * Normally, the flash page is split into the data (512 bytes) and the out of
3918c2ecf20Sopenharmony_ci * band data (16 bytes). For each, 4 more bytes can be accessed, where the wear
3928c2ecf20Sopenharmony_ci * leveling counters are stored.  To access this last area of 4 bytes, a special
3938c2ecf20Sopenharmony_ci * mode must be input to the flash ASIC.
3948c2ecf20Sopenharmony_ci *
3958c2ecf20Sopenharmony_ci * Returns 0 if no error occurred, -EIO else.
3968c2ecf20Sopenharmony_ci */
3978c2ecf20Sopenharmony_cistatic int doc_set_extra_page_mode(struct docg3 *docg3)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	int fctrl;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	doc_dbg("doc_set_extra_page_mode()\n");
4028c2ecf20Sopenharmony_ci	doc_flash_sequence(docg3, DOC_SEQ_PAGE_SIZE_532);
4038c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PAGE_SIZE_532);
4048c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
4078c2ecf20Sopenharmony_ci	if (fctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR))
4088c2ecf20Sopenharmony_ci		return -EIO;
4098c2ecf20Sopenharmony_ci	else
4108c2ecf20Sopenharmony_ci		return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/**
4148c2ecf20Sopenharmony_ci * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane
4158c2ecf20Sopenharmony_ci * @docg3: the device
4168c2ecf20Sopenharmony_ci * @sector: the sector
4178c2ecf20Sopenharmony_ci */
4188c2ecf20Sopenharmony_cistatic void doc_setup_addr_sector(struct docg3 *docg3, int sector)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
4218c2ecf20Sopenharmony_ci	doc_flash_address(docg3, sector & 0xff);
4228c2ecf20Sopenharmony_ci	doc_flash_address(docg3, (sector >> 8) & 0xff);
4238c2ecf20Sopenharmony_ci	doc_flash_address(docg3, (sector >> 16) & 0xff);
4248c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci/**
4288c2ecf20Sopenharmony_ci * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane
4298c2ecf20Sopenharmony_ci * @docg3: the device
4308c2ecf20Sopenharmony_ci * @sector: the sector
4318c2ecf20Sopenharmony_ci * @ofs: the offset in the page, between 0 and (512 + 16 + 512)
4328c2ecf20Sopenharmony_ci */
4338c2ecf20Sopenharmony_cistatic void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	ofs = ofs >> 2;
4368c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
4378c2ecf20Sopenharmony_ci	doc_flash_address(docg3, ofs & 0xff);
4388c2ecf20Sopenharmony_ci	doc_flash_address(docg3, sector & 0xff);
4398c2ecf20Sopenharmony_ci	doc_flash_address(docg3, (sector >> 8) & 0xff);
4408c2ecf20Sopenharmony_ci	doc_flash_address(docg3, (sector >> 16) & 0xff);
4418c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci/**
4458c2ecf20Sopenharmony_ci * doc_seek - Set both flash planes to the specified block, page for reading
4468c2ecf20Sopenharmony_ci * @docg3: the device
4478c2ecf20Sopenharmony_ci * @block0: the first plane block index
4488c2ecf20Sopenharmony_ci * @block1: the second plane block index
4498c2ecf20Sopenharmony_ci * @page: the page index within the block
4508c2ecf20Sopenharmony_ci * @wear: if true, read will occur on the 4 extra bytes of the wear area
4518c2ecf20Sopenharmony_ci * @ofs: offset in page to read
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * Programs the flash even and odd planes to the specific block and page.
4548c2ecf20Sopenharmony_ci * Alternatively, programs the flash to the wear area of the specified page.
4558c2ecf20Sopenharmony_ci */
4568c2ecf20Sopenharmony_cistatic int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page,
4578c2ecf20Sopenharmony_ci			 int wear, int ofs)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	int sector, ret = 0;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	doc_dbg("doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d)\n",
4628c2ecf20Sopenharmony_ci		block0, block1, page, ofs, wear);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (!wear && (ofs < 2 * DOC_LAYOUT_PAGE_SIZE)) {
4658c2ecf20Sopenharmony_ci		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1);
4668c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_READ_PLANE1);
4678c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
4688c2ecf20Sopenharmony_ci	} else {
4698c2ecf20Sopenharmony_ci		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2);
4708c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_READ_PLANE2);
4718c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	doc_set_reliable_mode(docg3);
4758c2ecf20Sopenharmony_ci	if (wear)
4768c2ecf20Sopenharmony_ci		ret = doc_set_extra_page_mode(docg3);
4778c2ecf20Sopenharmony_ci	if (ret)
4788c2ecf20Sopenharmony_ci		goto out;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	doc_flash_sequence(docg3, DOC_SEQ_READ);
4818c2ecf20Sopenharmony_ci	sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
4828c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
4838c2ecf20Sopenharmony_ci	doc_setup_addr_sector(docg3, sector);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
4868c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
4878c2ecf20Sopenharmony_ci	doc_setup_addr_sector(docg3, sector);
4888c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ciout:
4918c2ecf20Sopenharmony_ci	return ret;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci/**
4958c2ecf20Sopenharmony_ci * doc_write_seek - Set both flash planes to the specified block, page for writing
4968c2ecf20Sopenharmony_ci * @docg3: the device
4978c2ecf20Sopenharmony_ci * @block0: the first plane block index
4988c2ecf20Sopenharmony_ci * @block1: the second plane block index
4998c2ecf20Sopenharmony_ci * @page: the page index within the block
5008c2ecf20Sopenharmony_ci * @ofs: offset in page to write
5018c2ecf20Sopenharmony_ci *
5028c2ecf20Sopenharmony_ci * Programs the flash even and odd planes to the specific block and page.
5038c2ecf20Sopenharmony_ci * Alternatively, programs the flash to the wear area of the specified page.
5048c2ecf20Sopenharmony_ci */
5058c2ecf20Sopenharmony_cistatic int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page,
5068c2ecf20Sopenharmony_ci			 int ofs)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	int ret = 0, sector;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n",
5118c2ecf20Sopenharmony_ci		block0, block1, page, ofs);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	doc_set_reliable_mode(docg3);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) {
5168c2ecf20Sopenharmony_ci		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1);
5178c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_READ_PLANE1);
5188c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
5198c2ecf20Sopenharmony_ci	} else {
5208c2ecf20Sopenharmony_ci		doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2);
5218c2ecf20Sopenharmony_ci		doc_flash_command(docg3, DOC_CMD_READ_PLANE2);
5228c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP);
5268c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
5298c2ecf20Sopenharmony_ci	doc_setup_writeaddr_sector(docg3, sector, ofs);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3);
5328c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
5338c2ecf20Sopenharmony_ci	ret = doc_wait_ready(docg3);
5348c2ecf20Sopenharmony_ci	if (ret)
5358c2ecf20Sopenharmony_ci		goto out;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1);
5388c2ecf20Sopenharmony_ci	sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK);
5398c2ecf20Sopenharmony_ci	doc_setup_writeaddr_sector(docg3, sector, ofs);
5408c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ciout:
5438c2ecf20Sopenharmony_ci	return ret;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/**
5488c2ecf20Sopenharmony_ci * doc_read_page_ecc_init - Initialize hardware ECC engine
5498c2ecf20Sopenharmony_ci * @docg3: the device
5508c2ecf20Sopenharmony_ci * @len: the number of bytes covered by the ECC (BCH covered)
5518c2ecf20Sopenharmony_ci *
5528c2ecf20Sopenharmony_ci * The function does initialize the hardware ECC engine to compute the Hamming
5538c2ecf20Sopenharmony_ci * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes).
5548c2ecf20Sopenharmony_ci *
5558c2ecf20Sopenharmony_ci * Return 0 if succeeded, -EIO on error
5568c2ecf20Sopenharmony_ci */
5578c2ecf20Sopenharmony_cistatic int doc_read_page_ecc_init(struct docg3 *docg3, int len)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	doc_writew(docg3, DOC_ECCCONF0_READ_MODE
5608c2ecf20Sopenharmony_ci		   | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE
5618c2ecf20Sopenharmony_ci		   | (len & DOC_ECCCONF0_DATA_BYTES_MASK),
5628c2ecf20Sopenharmony_ci		   DOC_ECCCONF0);
5638c2ecf20Sopenharmony_ci	doc_delay(docg3, 4);
5648c2ecf20Sopenharmony_ci	doc_register_readb(docg3, DOC_FLASHCONTROL);
5658c2ecf20Sopenharmony_ci	return doc_wait_ready(docg3);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci/**
5698c2ecf20Sopenharmony_ci * doc_write_page_ecc_init - Initialize hardware BCH ECC engine
5708c2ecf20Sopenharmony_ci * @docg3: the device
5718c2ecf20Sopenharmony_ci * @len: the number of bytes covered by the ECC (BCH covered)
5728c2ecf20Sopenharmony_ci *
5738c2ecf20Sopenharmony_ci * The function does initialize the hardware ECC engine to compute the Hamming
5748c2ecf20Sopenharmony_ci * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes).
5758c2ecf20Sopenharmony_ci *
5768c2ecf20Sopenharmony_ci * Return 0 if succeeded, -EIO on error
5778c2ecf20Sopenharmony_ci */
5788c2ecf20Sopenharmony_cistatic int doc_write_page_ecc_init(struct docg3 *docg3, int len)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	doc_writew(docg3, DOC_ECCCONF0_WRITE_MODE
5818c2ecf20Sopenharmony_ci		   | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE
5828c2ecf20Sopenharmony_ci		   | (len & DOC_ECCCONF0_DATA_BYTES_MASK),
5838c2ecf20Sopenharmony_ci		   DOC_ECCCONF0);
5848c2ecf20Sopenharmony_ci	doc_delay(docg3, 4);
5858c2ecf20Sopenharmony_ci	doc_register_readb(docg3, DOC_FLASHCONTROL);
5868c2ecf20Sopenharmony_ci	return doc_wait_ready(docg3);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci/**
5908c2ecf20Sopenharmony_ci * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator
5918c2ecf20Sopenharmony_ci * @docg3: the device
5928c2ecf20Sopenharmony_ci *
5938c2ecf20Sopenharmony_ci * Disables the hardware ECC generator and checker, for unchecked reads (as when
5948c2ecf20Sopenharmony_ci * reading OOB only or write status byte).
5958c2ecf20Sopenharmony_ci */
5968c2ecf20Sopenharmony_cistatic void doc_ecc_disable(struct docg3 *docg3)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0);
5998c2ecf20Sopenharmony_ci	doc_delay(docg3, 4);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci/**
6038c2ecf20Sopenharmony_ci * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine
6048c2ecf20Sopenharmony_ci * @docg3: the device
6058c2ecf20Sopenharmony_ci * @nb_bytes: the number of bytes covered by the ECC (Hamming covered)
6068c2ecf20Sopenharmony_ci *
6078c2ecf20Sopenharmony_ci * This function programs the ECC hardware to compute the hamming code on the
6088c2ecf20Sopenharmony_ci * last provided N bytes to the hardware generator.
6098c2ecf20Sopenharmony_ci */
6108c2ecf20Sopenharmony_cistatic void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	u8 ecc_conf1;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1);
6158c2ecf20Sopenharmony_ci	ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK;
6168c2ecf20Sopenharmony_ci	ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK);
6178c2ecf20Sopenharmony_ci	doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci/**
6218c2ecf20Sopenharmony_ci * doc_ecc_bch_fix_data - Fix if need be read data from flash
6228c2ecf20Sopenharmony_ci * @docg3: the device
6238c2ecf20Sopenharmony_ci * @buf: the buffer of read data (512 + 7 + 1 bytes)
6248c2ecf20Sopenharmony_ci * @hwecc: the hardware calculated ECC.
6258c2ecf20Sopenharmony_ci *         It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB
6268c2ecf20Sopenharmony_ci *         area data, and calc_ecc the ECC calculated by the hardware generator.
6278c2ecf20Sopenharmony_ci *
6288c2ecf20Sopenharmony_ci * Checks if the received data matches the ECC, and if an error is detected,
6298c2ecf20Sopenharmony_ci * tries to fix the bit flips (at most 4) in the buffer buf.  As the docg3
6308c2ecf20Sopenharmony_ci * understands the (data, ecc, syndroms) in an inverted order in comparison to
6318c2ecf20Sopenharmony_ci * the BCH library, the function reverses the order of bits (ie. bit7 and bit0,
6328c2ecf20Sopenharmony_ci * bit6 and bit 1, ...) for all ECC data.
6338c2ecf20Sopenharmony_ci *
6348c2ecf20Sopenharmony_ci * The hardware ecc unit produces oob_ecc ^ calc_ecc.  The kernel's bch
6358c2ecf20Sopenharmony_ci * algorithm is used to decode this.  However the hw operates on page
6368c2ecf20Sopenharmony_ci * data in a bit order that is the reverse of that of the bch alg,
6378c2ecf20Sopenharmony_ci * requiring that the bits be reversed on the result.  Thanks to Ivan
6388c2ecf20Sopenharmony_ci * Djelic for his analysis.
6398c2ecf20Sopenharmony_ci *
6408c2ecf20Sopenharmony_ci * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit
6418c2ecf20Sopenharmony_ci * errors were detected and cannot be fixed.
6428c2ecf20Sopenharmony_ci */
6438c2ecf20Sopenharmony_cistatic int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	u8 ecc[DOC_ECC_BCH_SIZE];
6468c2ecf20Sopenharmony_ci	int errorpos[DOC_ECC_BCH_T], i, numerrs;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
6498c2ecf20Sopenharmony_ci		ecc[i] = bitrev8(hwecc[i]);
6508c2ecf20Sopenharmony_ci	numerrs = bch_decode(docg3->cascade->bch, NULL,
6518c2ecf20Sopenharmony_ci			     DOC_ECC_BCH_COVERED_BYTES,
6528c2ecf20Sopenharmony_ci			     NULL, ecc, NULL, errorpos);
6538c2ecf20Sopenharmony_ci	BUG_ON(numerrs == -EINVAL);
6548c2ecf20Sopenharmony_ci	if (numerrs < 0)
6558c2ecf20Sopenharmony_ci		goto out;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	for (i = 0; i < numerrs; i++)
6588c2ecf20Sopenharmony_ci		errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7));
6598c2ecf20Sopenharmony_ci	for (i = 0; i < numerrs; i++)
6608c2ecf20Sopenharmony_ci		if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8)
6618c2ecf20Sopenharmony_ci			/* error is located in data, correct it */
6628c2ecf20Sopenharmony_ci			change_bit(errorpos[i], buf);
6638c2ecf20Sopenharmony_ciout:
6648c2ecf20Sopenharmony_ci	doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs);
6658c2ecf20Sopenharmony_ci	return numerrs;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/**
6708c2ecf20Sopenharmony_ci * doc_read_page_prepare - Prepares reading data from a flash page
6718c2ecf20Sopenharmony_ci * @docg3: the device
6728c2ecf20Sopenharmony_ci * @block0: the first plane block index on flash memory
6738c2ecf20Sopenharmony_ci * @block1: the second plane block index on flash memory
6748c2ecf20Sopenharmony_ci * @page: the page index in the block
6758c2ecf20Sopenharmony_ci * @offset: the offset in the page (must be a multiple of 4)
6768c2ecf20Sopenharmony_ci *
6778c2ecf20Sopenharmony_ci * Prepares the page to be read in the flash memory :
6788c2ecf20Sopenharmony_ci *   - tell ASIC to map the flash pages
6798c2ecf20Sopenharmony_ci *   - tell ASIC to be in read mode
6808c2ecf20Sopenharmony_ci *
6818c2ecf20Sopenharmony_ci * After a call to this method, a call to doc_read_page_finish is mandatory,
6828c2ecf20Sopenharmony_ci * to end the read cycle of the flash.
6838c2ecf20Sopenharmony_ci *
6848c2ecf20Sopenharmony_ci * Read data from a flash page. The length to be read must be between 0 and
6858c2ecf20Sopenharmony_ci * (page_size + oob_size + wear_size), ie. 532, and a multiple of 4 (because
6868c2ecf20Sopenharmony_ci * the extra bytes reading is not implemented).
6878c2ecf20Sopenharmony_ci *
6888c2ecf20Sopenharmony_ci * As pages are grouped by 2 (in 2 planes), reading from a page must be done
6898c2ecf20Sopenharmony_ci * in two steps:
6908c2ecf20Sopenharmony_ci *  - one read of 512 bytes at offset 0
6918c2ecf20Sopenharmony_ci *  - one read of 512 bytes at offset 512 + 16
6928c2ecf20Sopenharmony_ci *
6938c2ecf20Sopenharmony_ci * Returns 0 if successful, -EIO if a read error occurred.
6948c2ecf20Sopenharmony_ci */
6958c2ecf20Sopenharmony_cistatic int doc_read_page_prepare(struct docg3 *docg3, int block0, int block1,
6968c2ecf20Sopenharmony_ci				 int page, int offset)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	int wear_area = 0, ret = 0;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	doc_dbg("doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d)\n",
7018c2ecf20Sopenharmony_ci		block0, block1, page, offset);
7028c2ecf20Sopenharmony_ci	if (offset >= DOC_LAYOUT_WEAR_OFFSET)
7038c2ecf20Sopenharmony_ci		wear_area = 1;
7048c2ecf20Sopenharmony_ci	if (!wear_area && offset > (DOC_LAYOUT_PAGE_OOB_SIZE * 2))
7058c2ecf20Sopenharmony_ci		return -EINVAL;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
7088c2ecf20Sopenharmony_ci	ret = doc_reset_seq(docg3);
7098c2ecf20Sopenharmony_ci	if (ret)
7108c2ecf20Sopenharmony_ci		goto err;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* Program the flash address block and page */
7138c2ecf20Sopenharmony_ci	ret = doc_read_seek(docg3, block0, block1, page, wear_area, offset);
7148c2ecf20Sopenharmony_ci	if (ret)
7158c2ecf20Sopenharmony_ci		goto err;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_READ_ALL_PLANES);
7188c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
7198c2ecf20Sopenharmony_ci	doc_wait_ready(docg3);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_SET_ADDR_READ);
7228c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
7238c2ecf20Sopenharmony_ci	if (offset >= DOC_LAYOUT_PAGE_SIZE * 2)
7248c2ecf20Sopenharmony_ci		offset -= 2 * DOC_LAYOUT_PAGE_SIZE;
7258c2ecf20Sopenharmony_ci	doc_flash_address(docg3, offset >> 2);
7268c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
7278c2ecf20Sopenharmony_ci	doc_wait_ready(docg3);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_READ_FLASH);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return 0;
7328c2ecf20Sopenharmony_cierr:
7338c2ecf20Sopenharmony_ci	doc_writeb(docg3, 0, DOC_DATAEND);
7348c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
7358c2ecf20Sopenharmony_ci	return -EIO;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci/**
7398c2ecf20Sopenharmony_ci * doc_read_page_getbytes - Reads bytes from a prepared page
7408c2ecf20Sopenharmony_ci * @docg3: the device
7418c2ecf20Sopenharmony_ci * @len: the number of bytes to be read (must be a multiple of 4)
7428c2ecf20Sopenharmony_ci * @buf: the buffer to be filled in (or NULL is forget bytes)
7438c2ecf20Sopenharmony_ci * @first: 1 if first time read, DOC_READADDRESS should be set
7448c2ecf20Sopenharmony_ci * @last_odd: 1 if last read ended up on an odd byte
7458c2ecf20Sopenharmony_ci *
7468c2ecf20Sopenharmony_ci * Reads bytes from a prepared page. There is a trickery here : if the last read
7478c2ecf20Sopenharmony_ci * ended up on an odd offset in the 1024 bytes double page, ie. between the 2
7488c2ecf20Sopenharmony_ci * planes, the first byte must be read apart. If a word (16bit) read was used,
7498c2ecf20Sopenharmony_ci * the read would return the byte of plane 2 as low *and* high endian, which
7508c2ecf20Sopenharmony_ci * will mess the read.
7518c2ecf20Sopenharmony_ci *
7528c2ecf20Sopenharmony_ci */
7538c2ecf20Sopenharmony_cistatic int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf,
7548c2ecf20Sopenharmony_ci				  int first, int last_odd)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	if (last_odd && len > 0) {
7578c2ecf20Sopenharmony_ci		doc_read_data_area(docg3, buf, 1, first);
7588c2ecf20Sopenharmony_ci		doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0);
7598c2ecf20Sopenharmony_ci	} else {
7608c2ecf20Sopenharmony_ci		doc_read_data_area(docg3, buf, len, first);
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
7638c2ecf20Sopenharmony_ci	return len;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci/**
7678c2ecf20Sopenharmony_ci * doc_write_page_putbytes - Writes bytes into a prepared page
7688c2ecf20Sopenharmony_ci * @docg3: the device
7698c2ecf20Sopenharmony_ci * @len: the number of bytes to be written
7708c2ecf20Sopenharmony_ci * @buf: the buffer of input bytes
7718c2ecf20Sopenharmony_ci *
7728c2ecf20Sopenharmony_ci */
7738c2ecf20Sopenharmony_cistatic void doc_write_page_putbytes(struct docg3 *docg3, int len,
7748c2ecf20Sopenharmony_ci				    const u_char *buf)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	doc_write_data_area(docg3, buf, len);
7778c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci/**
7818c2ecf20Sopenharmony_ci * doc_get_bch_hw_ecc - Get hardware calculated BCH ECC
7828c2ecf20Sopenharmony_ci * @docg3: the device
7838c2ecf20Sopenharmony_ci * @hwecc:  the array of 7 integers where the hardware ecc will be stored
7848c2ecf20Sopenharmony_ci */
7858c2ecf20Sopenharmony_cistatic void doc_get_bch_hw_ecc(struct docg3 *docg3, u8 *hwecc)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	int i;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
7908c2ecf20Sopenharmony_ci		hwecc[i] = doc_register_readb(docg3, DOC_BCH_HW_ECC(i));
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci/**
7948c2ecf20Sopenharmony_ci * doc_page_finish - Ends reading/writing of a flash page
7958c2ecf20Sopenharmony_ci * @docg3: the device
7968c2ecf20Sopenharmony_ci */
7978c2ecf20Sopenharmony_cistatic void doc_page_finish(struct docg3 *docg3)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	doc_writeb(docg3, 0, DOC_DATAEND);
8008c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci/**
8048c2ecf20Sopenharmony_ci * doc_read_page_finish - Ends reading of a flash page
8058c2ecf20Sopenharmony_ci * @docg3: the device
8068c2ecf20Sopenharmony_ci *
8078c2ecf20Sopenharmony_ci * As a side effect, resets the chip selector to 0. This ensures that after each
8088c2ecf20Sopenharmony_ci * read operation, the floor 0 is selected. Therefore, if the systems halts, the
8098c2ecf20Sopenharmony_ci * reboot will boot on floor 0, where the IPL is.
8108c2ecf20Sopenharmony_ci */
8118c2ecf20Sopenharmony_cistatic void doc_read_page_finish(struct docg3 *docg3)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	doc_page_finish(docg3);
8148c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, 0);
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/**
8188c2ecf20Sopenharmony_ci * calc_block_sector - Calculate blocks, pages and ofs.
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci * @from: offset in flash
8218c2ecf20Sopenharmony_ci * @block0: first plane block index calculated
8228c2ecf20Sopenharmony_ci * @block1: second plane block index calculated
8238c2ecf20Sopenharmony_ci * @page: page calculated
8248c2ecf20Sopenharmony_ci * @ofs: offset in page
8258c2ecf20Sopenharmony_ci * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in
8268c2ecf20Sopenharmony_ci * reliable mode.
8278c2ecf20Sopenharmony_ci *
8288c2ecf20Sopenharmony_ci * The calculation is based on the reliable/normal mode. In normal mode, the 64
8298c2ecf20Sopenharmony_ci * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are
8308c2ecf20Sopenharmony_ci * clones, only 32 pages per block are available.
8318c2ecf20Sopenharmony_ci */
8328c2ecf20Sopenharmony_cistatic void calc_block_sector(loff_t from, int *block0, int *block1, int *page,
8338c2ecf20Sopenharmony_ci			      int *ofs, int reliable)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	uint sector, pages_biblock;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES;
8388c2ecf20Sopenharmony_ci	if (reliable == 1 || reliable == 2)
8398c2ecf20Sopenharmony_ci		pages_biblock /= 2;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	sector = from / DOC_LAYOUT_PAGE_SIZE;
8428c2ecf20Sopenharmony_ci	*block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES;
8438c2ecf20Sopenharmony_ci	*block1 = *block0 + 1;
8448c2ecf20Sopenharmony_ci	*page = sector % pages_biblock;
8458c2ecf20Sopenharmony_ci	*page /= DOC_LAYOUT_NBPLANES;
8468c2ecf20Sopenharmony_ci	if (reliable == 1 || reliable == 2)
8478c2ecf20Sopenharmony_ci		*page *= 2;
8488c2ecf20Sopenharmony_ci	if (sector % 2)
8498c2ecf20Sopenharmony_ci		*ofs = DOC_LAYOUT_PAGE_OOB_SIZE;
8508c2ecf20Sopenharmony_ci	else
8518c2ecf20Sopenharmony_ci		*ofs = 0;
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci/**
8558c2ecf20Sopenharmony_ci * doc_read_oob - Read out of band bytes from flash
8568c2ecf20Sopenharmony_ci * @mtd: the device
8578c2ecf20Sopenharmony_ci * @from: the offset from first block and first page, in bytes, aligned on page
8588c2ecf20Sopenharmony_ci *        size
8598c2ecf20Sopenharmony_ci * @ops: the mtd oob structure
8608c2ecf20Sopenharmony_ci *
8618c2ecf20Sopenharmony_ci * Reads flash memory OOB area of pages.
8628c2ecf20Sopenharmony_ci *
8638c2ecf20Sopenharmony_ci * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred
8648c2ecf20Sopenharmony_ci */
8658c2ecf20Sopenharmony_cistatic int doc_read_oob(struct mtd_info *mtd, loff_t from,
8668c2ecf20Sopenharmony_ci			struct mtd_oob_ops *ops)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	struct docg3 *docg3 = mtd->priv;
8698c2ecf20Sopenharmony_ci	int block0, block1, page, ret, skip, ofs = 0;
8708c2ecf20Sopenharmony_ci	u8 *oobbuf = ops->oobbuf;
8718c2ecf20Sopenharmony_ci	u8 *buf = ops->datbuf;
8728c2ecf20Sopenharmony_ci	size_t len, ooblen, nbdata, nboob;
8738c2ecf20Sopenharmony_ci	u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
8748c2ecf20Sopenharmony_ci	int max_bitflips = 0;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if (buf)
8778c2ecf20Sopenharmony_ci		len = ops->len;
8788c2ecf20Sopenharmony_ci	else
8798c2ecf20Sopenharmony_ci		len = 0;
8808c2ecf20Sopenharmony_ci	if (oobbuf)
8818c2ecf20Sopenharmony_ci		ooblen = ops->ooblen;
8828c2ecf20Sopenharmony_ci	else
8838c2ecf20Sopenharmony_ci		ooblen = 0;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB)
8868c2ecf20Sopenharmony_ci		oobbuf += ops->ooboffs;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
8898c2ecf20Sopenharmony_ci		from, ops->mode, buf, len, oobbuf, ooblen);
8908c2ecf20Sopenharmony_ci	if (ooblen % DOC_LAYOUT_OOB_SIZE)
8918c2ecf20Sopenharmony_ci		return -EINVAL;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	ops->oobretlen = 0;
8948c2ecf20Sopenharmony_ci	ops->retlen = 0;
8958c2ecf20Sopenharmony_ci	ret = 0;
8968c2ecf20Sopenharmony_ci	skip = from % DOC_LAYOUT_PAGE_SIZE;
8978c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
8988c2ecf20Sopenharmony_ci	while (ret >= 0 && (len > 0 || ooblen > 0)) {
8998c2ecf20Sopenharmony_ci		calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
9008c2ecf20Sopenharmony_ci			docg3->reliable);
9018c2ecf20Sopenharmony_ci		nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
9028c2ecf20Sopenharmony_ci		nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE);
9038c2ecf20Sopenharmony_ci		ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
9048c2ecf20Sopenharmony_ci		if (ret < 0)
9058c2ecf20Sopenharmony_ci			goto out;
9068c2ecf20Sopenharmony_ci		ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
9078c2ecf20Sopenharmony_ci		if (ret < 0)
9088c2ecf20Sopenharmony_ci			goto err_in_read;
9098c2ecf20Sopenharmony_ci		ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0);
9108c2ecf20Sopenharmony_ci		if (ret < skip)
9118c2ecf20Sopenharmony_ci			goto err_in_read;
9128c2ecf20Sopenharmony_ci		ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2);
9138c2ecf20Sopenharmony_ci		if (ret < nbdata)
9148c2ecf20Sopenharmony_ci			goto err_in_read;
9158c2ecf20Sopenharmony_ci		doc_read_page_getbytes(docg3,
9168c2ecf20Sopenharmony_ci				       DOC_LAYOUT_PAGE_SIZE - nbdata - skip,
9178c2ecf20Sopenharmony_ci				       NULL, 0, (skip + nbdata) % 2);
9188c2ecf20Sopenharmony_ci		ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0);
9198c2ecf20Sopenharmony_ci		if (ret < nboob)
9208c2ecf20Sopenharmony_ci			goto err_in_read;
9218c2ecf20Sopenharmony_ci		doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob,
9228c2ecf20Sopenharmony_ci				       NULL, 0, nboob % 2);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		doc_get_bch_hw_ecc(docg3, hwecc);
9258c2ecf20Sopenharmony_ci		eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci		if (nboob >= DOC_LAYOUT_OOB_SIZE) {
9288c2ecf20Sopenharmony_ci			doc_dbg("OOB - INFO: %*phC\n", 7, oobbuf);
9298c2ecf20Sopenharmony_ci			doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]);
9308c2ecf20Sopenharmony_ci			doc_dbg("OOB - BCH_ECC: %*phC\n", 7, oobbuf + 8);
9318c2ecf20Sopenharmony_ci			doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]);
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci		doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1);
9348c2ecf20Sopenharmony_ci		doc_dbg("ECC HW_ECC: %*phC\n", 7, hwecc);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		ret = -EIO;
9378c2ecf20Sopenharmony_ci		if (is_prot_seq_error(docg3))
9388c2ecf20Sopenharmony_ci			goto err_in_read;
9398c2ecf20Sopenharmony_ci		ret = 0;
9408c2ecf20Sopenharmony_ci		if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) &&
9418c2ecf20Sopenharmony_ci		    (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) &&
9428c2ecf20Sopenharmony_ci		    (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) &&
9438c2ecf20Sopenharmony_ci		    (ops->mode != MTD_OPS_RAW) &&
9448c2ecf20Sopenharmony_ci		    (nbdata == DOC_LAYOUT_PAGE_SIZE)) {
9458c2ecf20Sopenharmony_ci			ret = doc_ecc_bch_fix_data(docg3, buf, hwecc);
9468c2ecf20Sopenharmony_ci			if (ret < 0) {
9478c2ecf20Sopenharmony_ci				mtd->ecc_stats.failed++;
9488c2ecf20Sopenharmony_ci				ret = -EBADMSG;
9498c2ecf20Sopenharmony_ci			}
9508c2ecf20Sopenharmony_ci			if (ret > 0) {
9518c2ecf20Sopenharmony_ci				mtd->ecc_stats.corrected += ret;
9528c2ecf20Sopenharmony_ci				max_bitflips = max(max_bitflips, ret);
9538c2ecf20Sopenharmony_ci				ret = max_bitflips;
9548c2ecf20Sopenharmony_ci			}
9558c2ecf20Sopenharmony_ci		}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci		doc_read_page_finish(docg3);
9588c2ecf20Sopenharmony_ci		ops->retlen += nbdata;
9598c2ecf20Sopenharmony_ci		ops->oobretlen += nboob;
9608c2ecf20Sopenharmony_ci		buf += nbdata;
9618c2ecf20Sopenharmony_ci		oobbuf += nboob;
9628c2ecf20Sopenharmony_ci		len -= nbdata;
9638c2ecf20Sopenharmony_ci		ooblen -= nboob;
9648c2ecf20Sopenharmony_ci		from += DOC_LAYOUT_PAGE_SIZE;
9658c2ecf20Sopenharmony_ci		skip = 0;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ciout:
9698c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
9708c2ecf20Sopenharmony_ci	return ret;
9718c2ecf20Sopenharmony_cierr_in_read:
9728c2ecf20Sopenharmony_ci	doc_read_page_finish(docg3);
9738c2ecf20Sopenharmony_ci	goto out;
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic int doc_reload_bbt(struct docg3 *docg3)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	int block = DOC_LAYOUT_BLOCK_BBT;
9798c2ecf20Sopenharmony_ci	int ret = 0, nbpages, page;
9808c2ecf20Sopenharmony_ci	u_char *buf = docg3->bbt;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE);
9838c2ecf20Sopenharmony_ci	for (page = 0; !ret && (page < nbpages); page++) {
9848c2ecf20Sopenharmony_ci		ret = doc_read_page_prepare(docg3, block, block + 1,
9858c2ecf20Sopenharmony_ci					    page + DOC_LAYOUT_PAGE_BBT, 0);
9868c2ecf20Sopenharmony_ci		if (!ret)
9878c2ecf20Sopenharmony_ci			ret = doc_read_page_ecc_init(docg3,
9888c2ecf20Sopenharmony_ci						     DOC_LAYOUT_PAGE_SIZE);
9898c2ecf20Sopenharmony_ci		if (!ret)
9908c2ecf20Sopenharmony_ci			doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE,
9918c2ecf20Sopenharmony_ci					       buf, 1, 0);
9928c2ecf20Sopenharmony_ci		buf += DOC_LAYOUT_PAGE_SIZE;
9938c2ecf20Sopenharmony_ci	}
9948c2ecf20Sopenharmony_ci	doc_read_page_finish(docg3);
9958c2ecf20Sopenharmony_ci	return ret;
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci/**
9998c2ecf20Sopenharmony_ci * doc_block_isbad - Checks whether a block is good or not
10008c2ecf20Sopenharmony_ci * @mtd: the device
10018c2ecf20Sopenharmony_ci * @from: the offset to find the correct block
10028c2ecf20Sopenharmony_ci *
10038c2ecf20Sopenharmony_ci * Returns 1 if block is bad, 0 if block is good
10048c2ecf20Sopenharmony_ci */
10058c2ecf20Sopenharmony_cistatic int doc_block_isbad(struct mtd_info *mtd, loff_t from)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct docg3 *docg3 = mtd->priv;
10088c2ecf20Sopenharmony_ci	int block0, block1, page, ofs, is_good;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	calc_block_sector(from, &block0, &block1, &page, &ofs,
10118c2ecf20Sopenharmony_ci		docg3->reliable);
10128c2ecf20Sopenharmony_ci	doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n",
10138c2ecf20Sopenharmony_ci		from, block0, block1, page, ofs);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (block0 < DOC_LAYOUT_BLOCK_FIRST_DATA)
10168c2ecf20Sopenharmony_ci		return 0;
10178c2ecf20Sopenharmony_ci	if (block1 > docg3->max_block)
10188c2ecf20Sopenharmony_ci		return -EINVAL;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	is_good = docg3->bbt[block0 >> 3] & (1 << (block0 & 0x7));
10218c2ecf20Sopenharmony_ci	return !is_good;
10228c2ecf20Sopenharmony_ci}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci#if 0
10258c2ecf20Sopenharmony_ci/**
10268c2ecf20Sopenharmony_ci * doc_get_erase_count - Get block erase count
10278c2ecf20Sopenharmony_ci * @docg3: the device
10288c2ecf20Sopenharmony_ci * @from: the offset in which the block is.
10298c2ecf20Sopenharmony_ci *
10308c2ecf20Sopenharmony_ci * Get the number of times a block was erased. The number is the maximum of
10318c2ecf20Sopenharmony_ci * erase times between first and second plane (which should be equal normally).
10328c2ecf20Sopenharmony_ci *
10338c2ecf20Sopenharmony_ci * Returns The number of erases, or -EINVAL or -EIO on error.
10348c2ecf20Sopenharmony_ci */
10358c2ecf20Sopenharmony_cistatic int doc_get_erase_count(struct docg3 *docg3, loff_t from)
10368c2ecf20Sopenharmony_ci{
10378c2ecf20Sopenharmony_ci	u8 buf[DOC_LAYOUT_WEAR_SIZE];
10388c2ecf20Sopenharmony_ci	int ret, plane1_erase_count, plane2_erase_count;
10398c2ecf20Sopenharmony_ci	int block0, block1, page, ofs;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf);
10428c2ecf20Sopenharmony_ci	if (from % DOC_LAYOUT_PAGE_SIZE)
10438c2ecf20Sopenharmony_ci		return -EINVAL;
10448c2ecf20Sopenharmony_ci	calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable);
10458c2ecf20Sopenharmony_ci	if (block1 > docg3->max_block)
10468c2ecf20Sopenharmony_ci		return -EINVAL;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	ret = doc_reset_seq(docg3);
10498c2ecf20Sopenharmony_ci	if (!ret)
10508c2ecf20Sopenharmony_ci		ret = doc_read_page_prepare(docg3, block0, block1, page,
10518c2ecf20Sopenharmony_ci					    ofs + DOC_LAYOUT_WEAR_OFFSET, 0);
10528c2ecf20Sopenharmony_ci	if (!ret)
10538c2ecf20Sopenharmony_ci		ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE,
10548c2ecf20Sopenharmony_ci					     buf, 1, 0);
10558c2ecf20Sopenharmony_ci	doc_read_page_finish(docg3);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK))
10588c2ecf20Sopenharmony_ci		return -EIO;
10598c2ecf20Sopenharmony_ci	plane1_erase_count = (u8)(~buf[1]) | ((u8)(~buf[4]) << 8)
10608c2ecf20Sopenharmony_ci		| ((u8)(~buf[5]) << 16);
10618c2ecf20Sopenharmony_ci	plane2_erase_count = (u8)(~buf[3]) | ((u8)(~buf[6]) << 8)
10628c2ecf20Sopenharmony_ci		| ((u8)(~buf[7]) << 16);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	return max(plane1_erase_count, plane2_erase_count);
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci#endif
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci/**
10698c2ecf20Sopenharmony_ci * doc_get_op_status - get erase/write operation status
10708c2ecf20Sopenharmony_ci * @docg3: the device
10718c2ecf20Sopenharmony_ci *
10728c2ecf20Sopenharmony_ci * Queries the status from the chip, and returns it
10738c2ecf20Sopenharmony_ci *
10748c2ecf20Sopenharmony_ci * Returns the status (bits DOC_PLANES_STATUS_*)
10758c2ecf20Sopenharmony_ci */
10768c2ecf20Sopenharmony_cistatic int doc_get_op_status(struct docg3 *docg3)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	u8 status;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS);
10818c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PLANES_STATUS);
10828c2ecf20Sopenharmony_ci	doc_delay(docg3, 5);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	doc_ecc_disable(docg3);
10858c2ecf20Sopenharmony_ci	doc_read_data_area(docg3, &status, 1, 1);
10868c2ecf20Sopenharmony_ci	return status;
10878c2ecf20Sopenharmony_ci}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci/**
10908c2ecf20Sopenharmony_ci * doc_write_erase_wait_status - wait for write or erase completion
10918c2ecf20Sopenharmony_ci * @docg3: the device
10928c2ecf20Sopenharmony_ci *
10938c2ecf20Sopenharmony_ci * Wait for the chip to be ready again after erase or write operation, and check
10948c2ecf20Sopenharmony_ci * erase/write status.
10958c2ecf20Sopenharmony_ci *
10968c2ecf20Sopenharmony_ci * Returns 0 if erase successful, -EIO if erase/write issue, -ETIMEOUT if
10978c2ecf20Sopenharmony_ci * timeout
10988c2ecf20Sopenharmony_ci */
10998c2ecf20Sopenharmony_cistatic int doc_write_erase_wait_status(struct docg3 *docg3)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	int i, status, ret = 0;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	for (i = 0; !doc_is_ready(docg3) && i < 5; i++)
11048c2ecf20Sopenharmony_ci		msleep(20);
11058c2ecf20Sopenharmony_ci	if (!doc_is_ready(docg3)) {
11068c2ecf20Sopenharmony_ci		doc_dbg("Timeout reached and the chip is still not ready\n");
11078c2ecf20Sopenharmony_ci		ret = -EAGAIN;
11088c2ecf20Sopenharmony_ci		goto out;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	status = doc_get_op_status(docg3);
11128c2ecf20Sopenharmony_ci	if (status & DOC_PLANES_STATUS_FAIL) {
11138c2ecf20Sopenharmony_ci		doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n",
11148c2ecf20Sopenharmony_ci			status);
11158c2ecf20Sopenharmony_ci		ret = -EIO;
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ciout:
11198c2ecf20Sopenharmony_ci	doc_page_finish(docg3);
11208c2ecf20Sopenharmony_ci	return ret;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci/**
11248c2ecf20Sopenharmony_ci * doc_erase_block - Erase a couple of blocks
11258c2ecf20Sopenharmony_ci * @docg3: the device
11268c2ecf20Sopenharmony_ci * @block0: the first block to erase (leftmost plane)
11278c2ecf20Sopenharmony_ci * @block1: the second block to erase (rightmost plane)
11288c2ecf20Sopenharmony_ci *
11298c2ecf20Sopenharmony_ci * Erase both blocks, and return operation status
11308c2ecf20Sopenharmony_ci *
11318c2ecf20Sopenharmony_ci * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not
11328c2ecf20Sopenharmony_ci * ready for too long
11338c2ecf20Sopenharmony_ci */
11348c2ecf20Sopenharmony_cistatic int doc_erase_block(struct docg3 *docg3, int block0, int block1)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	int ret, sector;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1);
11398c2ecf20Sopenharmony_ci	ret = doc_reset_seq(docg3);
11408c2ecf20Sopenharmony_ci	if (ret)
11418c2ecf20Sopenharmony_ci		return -EIO;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	doc_set_reliable_mode(docg3);
11448c2ecf20Sopenharmony_ci	doc_flash_sequence(docg3, DOC_SEQ_ERASE);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	sector = block0 << DOC_ADDR_BLOCK_SHIFT;
11478c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
11488c2ecf20Sopenharmony_ci	doc_setup_addr_sector(docg3, sector);
11498c2ecf20Sopenharmony_ci	sector = block1 << DOC_ADDR_BLOCK_SHIFT;
11508c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR);
11518c2ecf20Sopenharmony_ci	doc_setup_addr_sector(docg3, sector);
11528c2ecf20Sopenharmony_ci	doc_delay(docg3, 1);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_ERASECYCLE2);
11558c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	if (is_prot_seq_error(docg3)) {
11588c2ecf20Sopenharmony_ci		doc_err("Erase blocks %d,%d error\n", block0, block1);
11598c2ecf20Sopenharmony_ci		return -EIO;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	return doc_write_erase_wait_status(docg3);
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci/**
11668c2ecf20Sopenharmony_ci * doc_erase - Erase a portion of the chip
11678c2ecf20Sopenharmony_ci * @mtd: the device
11688c2ecf20Sopenharmony_ci * @info: the erase info
11698c2ecf20Sopenharmony_ci *
11708c2ecf20Sopenharmony_ci * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is
11718c2ecf20Sopenharmony_ci * split into 2 pages of 512 bytes on 2 contiguous blocks.
11728c2ecf20Sopenharmony_ci *
11738c2ecf20Sopenharmony_ci * Returns 0 if erase successful, -EINVAL if addressing error, -EIO if erase
11748c2ecf20Sopenharmony_ci * issue
11758c2ecf20Sopenharmony_ci */
11768c2ecf20Sopenharmony_cistatic int doc_erase(struct mtd_info *mtd, struct erase_info *info)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	struct docg3 *docg3 = mtd->priv;
11798c2ecf20Sopenharmony_ci	uint64_t len;
11808c2ecf20Sopenharmony_ci	int block0, block1, page, ret = 0, ofs = 0;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	calc_block_sector(info->addr + info->len, &block0, &block1, &page,
11858c2ecf20Sopenharmony_ci			  &ofs, docg3->reliable);
11868c2ecf20Sopenharmony_ci	if (info->addr + info->len > mtd->size || page || ofs)
11878c2ecf20Sopenharmony_ci		return -EINVAL;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
11908c2ecf20Sopenharmony_ci			  docg3->reliable);
11918c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
11928c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
11938c2ecf20Sopenharmony_ci	doc_set_reliable_mode(docg3);
11948c2ecf20Sopenharmony_ci	for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
11958c2ecf20Sopenharmony_ci		ret = doc_erase_block(docg3, block0, block1);
11968c2ecf20Sopenharmony_ci		block0 += 2;
11978c2ecf20Sopenharmony_ci		block1 += 2;
11988c2ecf20Sopenharmony_ci	}
11998c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	return ret;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci/**
12058c2ecf20Sopenharmony_ci * doc_write_page - Write a single page to the chip
12068c2ecf20Sopenharmony_ci * @docg3: the device
12078c2ecf20Sopenharmony_ci * @to: the offset from first block and first page, in bytes, aligned on page
12088c2ecf20Sopenharmony_ci *      size
12098c2ecf20Sopenharmony_ci * @buf: buffer to get bytes from
12108c2ecf20Sopenharmony_ci * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be
12118c2ecf20Sopenharmony_ci *       written)
12128c2ecf20Sopenharmony_ci * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or
12138c2ecf20Sopenharmony_ci *           BCH computations. If 1, only bytes 0-7 and byte 15 are taken,
12148c2ecf20Sopenharmony_ci *           remaining ones are filled with hardware Hamming and BCH
12158c2ecf20Sopenharmony_ci *           computations. Its value is not meaningfull is oob == NULL.
12168c2ecf20Sopenharmony_ci *
12178c2ecf20Sopenharmony_ci * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the
12188c2ecf20Sopenharmony_ci * OOB data. The OOB ECC is automatically computed by the hardware Hamming and
12198c2ecf20Sopenharmony_ci * BCH generator if autoecc is not null.
12208c2ecf20Sopenharmony_ci *
12218c2ecf20Sopenharmony_ci * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout
12228c2ecf20Sopenharmony_ci */
12238c2ecf20Sopenharmony_cistatic int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf,
12248c2ecf20Sopenharmony_ci			  const u_char *oob, int autoecc)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	int block0, block1, page, ret, ofs = 0;
12278c2ecf20Sopenharmony_ci	u8 hwecc[DOC_ECC_BCH_SIZE], hamming;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	doc_dbg("doc_write_page(to=%lld)\n", to);
12308c2ecf20Sopenharmony_ci	calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
12338c2ecf20Sopenharmony_ci	ret = doc_reset_seq(docg3);
12348c2ecf20Sopenharmony_ci	if (ret)
12358c2ecf20Sopenharmony_ci		goto err;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* Program the flash address block and page */
12388c2ecf20Sopenharmony_ci	ret = doc_write_seek(docg3, block0, block1, page, ofs);
12398c2ecf20Sopenharmony_ci	if (ret)
12408c2ecf20Sopenharmony_ci		goto err;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
12438c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
12448c2ecf20Sopenharmony_ci	doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	if (oob && autoecc) {
12478c2ecf20Sopenharmony_ci		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob);
12488c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
12498c2ecf20Sopenharmony_ci		oob += DOC_LAYOUT_OOB_UNUSED_OFS;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci		hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY);
12528c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
12538c2ecf20Sopenharmony_ci		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ,
12548c2ecf20Sopenharmony_ci					&hamming);
12558c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci		doc_get_bch_hw_ecc(docg3, hwecc);
12588c2ecf20Sopenharmony_ci		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, hwecc);
12598c2ecf20Sopenharmony_ci		doc_delay(docg3, 2);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob);
12628c2ecf20Sopenharmony_ci	}
12638c2ecf20Sopenharmony_ci	if (oob && !autoecc)
12648c2ecf20Sopenharmony_ci		doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
12678c2ecf20Sopenharmony_ci	doc_page_finish(docg3);
12688c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
12698c2ecf20Sopenharmony_ci	doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2);
12708c2ecf20Sopenharmony_ci	doc_delay(docg3, 2);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	/*
12738c2ecf20Sopenharmony_ci	 * The wait status will perform another doc_page_finish() call, but that
12748c2ecf20Sopenharmony_ci	 * seems to please the docg3, so leave it.
12758c2ecf20Sopenharmony_ci	 */
12768c2ecf20Sopenharmony_ci	ret = doc_write_erase_wait_status(docg3);
12778c2ecf20Sopenharmony_ci	return ret;
12788c2ecf20Sopenharmony_cierr:
12798c2ecf20Sopenharmony_ci	doc_read_page_finish(docg3);
12808c2ecf20Sopenharmony_ci	return ret;
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci/**
12848c2ecf20Sopenharmony_ci * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops
12858c2ecf20Sopenharmony_ci * @ops: the oob operations
12868c2ecf20Sopenharmony_ci *
12878c2ecf20Sopenharmony_ci * Returns 0 or 1 if success, -EINVAL if invalid oob mode
12888c2ecf20Sopenharmony_ci */
12898c2ecf20Sopenharmony_cistatic int doc_guess_autoecc(struct mtd_oob_ops *ops)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	int autoecc;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	switch (ops->mode) {
12948c2ecf20Sopenharmony_ci	case MTD_OPS_PLACE_OOB:
12958c2ecf20Sopenharmony_ci	case MTD_OPS_AUTO_OOB:
12968c2ecf20Sopenharmony_ci		autoecc = 1;
12978c2ecf20Sopenharmony_ci		break;
12988c2ecf20Sopenharmony_ci	case MTD_OPS_RAW:
12998c2ecf20Sopenharmony_ci		autoecc = 0;
13008c2ecf20Sopenharmony_ci		break;
13018c2ecf20Sopenharmony_ci	default:
13028c2ecf20Sopenharmony_ci		autoecc = -EINVAL;
13038c2ecf20Sopenharmony_ci	}
13048c2ecf20Sopenharmony_ci	return autoecc;
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci/**
13088c2ecf20Sopenharmony_ci * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes
13098c2ecf20Sopenharmony_ci * @dst: the target 16 bytes OOB buffer
13108c2ecf20Sopenharmony_ci * @oobsrc: the source 8 bytes non-ECC OOB buffer
13118c2ecf20Sopenharmony_ci *
13128c2ecf20Sopenharmony_ci */
13138c2ecf20Sopenharmony_cistatic void doc_fill_autooob(u8 *dst, u8 *oobsrc)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ);
13168c2ecf20Sopenharmony_ci	dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ];
13178c2ecf20Sopenharmony_ci}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci/**
13208c2ecf20Sopenharmony_ci * doc_backup_oob - Backup OOB into docg3 structure
13218c2ecf20Sopenharmony_ci * @docg3: the device
13228c2ecf20Sopenharmony_ci * @to: the page offset in the chip
13238c2ecf20Sopenharmony_ci * @ops: the OOB size and buffer
13248c2ecf20Sopenharmony_ci *
13258c2ecf20Sopenharmony_ci * As the docg3 should write a page with its OOB in one pass, and some userland
13268c2ecf20Sopenharmony_ci * applications do write_oob() to setup the OOB and then write(), store the OOB
13278c2ecf20Sopenharmony_ci * into a temporary storage. This is very dangerous, as 2 concurrent
13288c2ecf20Sopenharmony_ci * applications could store an OOB, and then write their pages (which will
13298c2ecf20Sopenharmony_ci * result into one having its OOB corrupted).
13308c2ecf20Sopenharmony_ci *
13318c2ecf20Sopenharmony_ci * The only reliable way would be for userland to call doc_write_oob() with both
13328c2ecf20Sopenharmony_ci * the page data _and_ the OOB area.
13338c2ecf20Sopenharmony_ci *
13348c2ecf20Sopenharmony_ci * Returns 0 if success, -EINVAL if ops content invalid
13358c2ecf20Sopenharmony_ci */
13368c2ecf20Sopenharmony_cistatic int doc_backup_oob(struct docg3 *docg3, loff_t to,
13378c2ecf20Sopenharmony_ci			  struct mtd_oob_ops *ops)
13388c2ecf20Sopenharmony_ci{
13398c2ecf20Sopenharmony_ci	int ooblen = ops->ooblen, autoecc;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	if (ooblen != DOC_LAYOUT_OOB_SIZE)
13428c2ecf20Sopenharmony_ci		return -EINVAL;
13438c2ecf20Sopenharmony_ci	autoecc = doc_guess_autoecc(ops);
13448c2ecf20Sopenharmony_ci	if (autoecc < 0)
13458c2ecf20Sopenharmony_ci		return autoecc;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	docg3->oob_write_ofs = to;
13488c2ecf20Sopenharmony_ci	docg3->oob_autoecc = autoecc;
13498c2ecf20Sopenharmony_ci	if (ops->mode == MTD_OPS_AUTO_OOB) {
13508c2ecf20Sopenharmony_ci		doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf);
13518c2ecf20Sopenharmony_ci		ops->oobretlen = 8;
13528c2ecf20Sopenharmony_ci	} else {
13538c2ecf20Sopenharmony_ci		memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE);
13548c2ecf20Sopenharmony_ci		ops->oobretlen = DOC_LAYOUT_OOB_SIZE;
13558c2ecf20Sopenharmony_ci	}
13568c2ecf20Sopenharmony_ci	return 0;
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci/**
13608c2ecf20Sopenharmony_ci * doc_write_oob - Write out of band bytes to flash
13618c2ecf20Sopenharmony_ci * @mtd: the device
13628c2ecf20Sopenharmony_ci * @ofs: the offset from first block and first page, in bytes, aligned on page
13638c2ecf20Sopenharmony_ci *       size
13648c2ecf20Sopenharmony_ci * @ops: the mtd oob structure
13658c2ecf20Sopenharmony_ci *
13668c2ecf20Sopenharmony_ci * Either write OOB data into a temporary buffer, for the subsequent write
13678c2ecf20Sopenharmony_ci * page. The provided OOB should be 16 bytes long. If a data buffer is provided
13688c2ecf20Sopenharmony_ci * as well, issue the page write.
13698c2ecf20Sopenharmony_ci * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will
13708c2ecf20Sopenharmony_ci * still be filled in if asked for).
13718c2ecf20Sopenharmony_ci *
13728c2ecf20Sopenharmony_ci * Returns 0 is successful, EINVAL if length is not 14 bytes
13738c2ecf20Sopenharmony_ci */
13748c2ecf20Sopenharmony_cistatic int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
13758c2ecf20Sopenharmony_ci			 struct mtd_oob_ops *ops)
13768c2ecf20Sopenharmony_ci{
13778c2ecf20Sopenharmony_ci	struct docg3 *docg3 = mtd->priv;
13788c2ecf20Sopenharmony_ci	int ret, autoecc, oobdelta;
13798c2ecf20Sopenharmony_ci	u8 *oobbuf = ops->oobbuf;
13808c2ecf20Sopenharmony_ci	u8 *buf = ops->datbuf;
13818c2ecf20Sopenharmony_ci	size_t len, ooblen;
13828c2ecf20Sopenharmony_ci	u8 oob[DOC_LAYOUT_OOB_SIZE];
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	if (buf)
13858c2ecf20Sopenharmony_ci		len = ops->len;
13868c2ecf20Sopenharmony_ci	else
13878c2ecf20Sopenharmony_ci		len = 0;
13888c2ecf20Sopenharmony_ci	if (oobbuf)
13898c2ecf20Sopenharmony_ci		ooblen = ops->ooblen;
13908c2ecf20Sopenharmony_ci	else
13918c2ecf20Sopenharmony_ci		ooblen = 0;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB)
13948c2ecf20Sopenharmony_ci		oobbuf += ops->ooboffs;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
13978c2ecf20Sopenharmony_ci		ofs, ops->mode, buf, len, oobbuf, ooblen);
13988c2ecf20Sopenharmony_ci	switch (ops->mode) {
13998c2ecf20Sopenharmony_ci	case MTD_OPS_PLACE_OOB:
14008c2ecf20Sopenharmony_ci	case MTD_OPS_RAW:
14018c2ecf20Sopenharmony_ci		oobdelta = mtd->oobsize;
14028c2ecf20Sopenharmony_ci		break;
14038c2ecf20Sopenharmony_ci	case MTD_OPS_AUTO_OOB:
14048c2ecf20Sopenharmony_ci		oobdelta = mtd->oobavail;
14058c2ecf20Sopenharmony_ci		break;
14068c2ecf20Sopenharmony_ci	default:
14078c2ecf20Sopenharmony_ci		return -EINVAL;
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci	if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) ||
14108c2ecf20Sopenharmony_ci	    (ofs % DOC_LAYOUT_PAGE_SIZE))
14118c2ecf20Sopenharmony_ci		return -EINVAL;
14128c2ecf20Sopenharmony_ci	if (len && ooblen &&
14138c2ecf20Sopenharmony_ci	    (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta))
14148c2ecf20Sopenharmony_ci		return -EINVAL;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	ops->oobretlen = 0;
14178c2ecf20Sopenharmony_ci	ops->retlen = 0;
14188c2ecf20Sopenharmony_ci	ret = 0;
14198c2ecf20Sopenharmony_ci	if (len == 0 && ooblen == 0)
14208c2ecf20Sopenharmony_ci		return -EINVAL;
14218c2ecf20Sopenharmony_ci	if (len == 0 && ooblen > 0)
14228c2ecf20Sopenharmony_ci		return doc_backup_oob(docg3, ofs, ops);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	autoecc = doc_guess_autoecc(ops);
14258c2ecf20Sopenharmony_ci	if (autoecc < 0)
14268c2ecf20Sopenharmony_ci		return autoecc;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
14298c2ecf20Sopenharmony_ci	while (!ret && len > 0) {
14308c2ecf20Sopenharmony_ci		memset(oob, 0, sizeof(oob));
14318c2ecf20Sopenharmony_ci		if (ofs == docg3->oob_write_ofs)
14328c2ecf20Sopenharmony_ci			memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE);
14338c2ecf20Sopenharmony_ci		else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB)
14348c2ecf20Sopenharmony_ci			doc_fill_autooob(oob, oobbuf);
14358c2ecf20Sopenharmony_ci		else if (ooblen > 0)
14368c2ecf20Sopenharmony_ci			memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE);
14378c2ecf20Sopenharmony_ci		ret = doc_write_page(docg3, ofs, buf, oob, autoecc);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci		ofs += DOC_LAYOUT_PAGE_SIZE;
14408c2ecf20Sopenharmony_ci		len -= DOC_LAYOUT_PAGE_SIZE;
14418c2ecf20Sopenharmony_ci		buf += DOC_LAYOUT_PAGE_SIZE;
14428c2ecf20Sopenharmony_ci		if (ooblen) {
14438c2ecf20Sopenharmony_ci			oobbuf += oobdelta;
14448c2ecf20Sopenharmony_ci			ooblen -= oobdelta;
14458c2ecf20Sopenharmony_ci			ops->oobretlen += oobdelta;
14468c2ecf20Sopenharmony_ci		}
14478c2ecf20Sopenharmony_ci		ops->retlen += DOC_LAYOUT_PAGE_SIZE;
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, 0);
14518c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
14528c2ecf20Sopenharmony_ci	return ret;
14538c2ecf20Sopenharmony_ci}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_cistatic struct docg3 *sysfs_dev2docg3(struct device *dev,
14568c2ecf20Sopenharmony_ci				     struct device_attribute *attr)
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	int floor;
14598c2ecf20Sopenharmony_ci	struct mtd_info **docg3_floors = dev_get_drvdata(dev);
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	floor = attr->attr.name[1] - '0';
14628c2ecf20Sopenharmony_ci	if (floor < 0 || floor >= DOC_MAX_NBFLOORS)
14638c2ecf20Sopenharmony_ci		return NULL;
14648c2ecf20Sopenharmony_ci	else
14658c2ecf20Sopenharmony_ci		return docg3_floors[floor]->priv;
14668c2ecf20Sopenharmony_ci}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_cistatic ssize_t dps0_is_key_locked(struct device *dev,
14698c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
14708c2ecf20Sopenharmony_ci{
14718c2ecf20Sopenharmony_ci	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
14728c2ecf20Sopenharmony_ci	int dps0;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
14758c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
14768c2ecf20Sopenharmony_ci	dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
14778c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, 0);
14788c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK));
14818c2ecf20Sopenharmony_ci}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_cistatic ssize_t dps1_is_key_locked(struct device *dev,
14848c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
14858c2ecf20Sopenharmony_ci{
14868c2ecf20Sopenharmony_ci	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
14878c2ecf20Sopenharmony_ci	int dps1;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
14908c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
14918c2ecf20Sopenharmony_ci	dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
14928c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, 0);
14938c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK));
14968c2ecf20Sopenharmony_ci}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_cistatic ssize_t dps0_insert_key(struct device *dev,
14998c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
15008c2ecf20Sopenharmony_ci			       const char *buf, size_t count)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
15038c2ecf20Sopenharmony_ci	int i;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
15068c2ecf20Sopenharmony_ci		return -EINVAL;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
15098c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
15108c2ecf20Sopenharmony_ci	for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
15118c2ecf20Sopenharmony_ci		doc_writeb(docg3, buf[i], DOC_DPS0_KEY);
15128c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, 0);
15138c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
15148c2ecf20Sopenharmony_ci	return count;
15158c2ecf20Sopenharmony_ci}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_cistatic ssize_t dps1_insert_key(struct device *dev,
15188c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
15198c2ecf20Sopenharmony_ci			       const char *buf, size_t count)
15208c2ecf20Sopenharmony_ci{
15218c2ecf20Sopenharmony_ci	struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
15228c2ecf20Sopenharmony_ci	int i;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
15258c2ecf20Sopenharmony_ci		return -EINVAL;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
15288c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
15298c2ecf20Sopenharmony_ci	for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
15308c2ecf20Sopenharmony_ci		doc_writeb(docg3, buf[i], DOC_DPS1_KEY);
15318c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, 0);
15328c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
15338c2ecf20Sopenharmony_ci	return count;
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci#define FLOOR_SYSFS(id) { \
15378c2ecf20Sopenharmony_ci	__ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \
15388c2ecf20Sopenharmony_ci	__ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \
15398c2ecf20Sopenharmony_ci	__ATTR(f##id##_dps0_protection_key, S_IWUSR|S_IWGRP, NULL, dps0_insert_key), \
15408c2ecf20Sopenharmony_ci	__ATTR(f##id##_dps1_protection_key, S_IWUSR|S_IWGRP, NULL, dps1_insert_key), \
15418c2ecf20Sopenharmony_ci}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_cistatic struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
15448c2ecf20Sopenharmony_ci	FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3)
15458c2ecf20Sopenharmony_ci};
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_cistatic int doc_register_sysfs(struct platform_device *pdev,
15488c2ecf20Sopenharmony_ci			      struct docg3_cascade *cascade)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
15518c2ecf20Sopenharmony_ci	int floor;
15528c2ecf20Sopenharmony_ci	int ret;
15538c2ecf20Sopenharmony_ci	int i;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	for (floor = 0;
15568c2ecf20Sopenharmony_ci	     floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
15578c2ecf20Sopenharmony_ci	     floor++) {
15588c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++) {
15598c2ecf20Sopenharmony_ci			ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
15608c2ecf20Sopenharmony_ci			if (ret)
15618c2ecf20Sopenharmony_ci				goto remove_files;
15628c2ecf20Sopenharmony_ci		}
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	return 0;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ciremove_files:
15688c2ecf20Sopenharmony_ci	do {
15698c2ecf20Sopenharmony_ci		while (--i >= 0)
15708c2ecf20Sopenharmony_ci			device_remove_file(dev, &doc_sys_attrs[floor][i]);
15718c2ecf20Sopenharmony_ci		i = 4;
15728c2ecf20Sopenharmony_ci	} while (--floor >= 0);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	return ret;
15758c2ecf20Sopenharmony_ci}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_cistatic void doc_unregister_sysfs(struct platform_device *pdev,
15788c2ecf20Sopenharmony_ci				 struct docg3_cascade *cascade)
15798c2ecf20Sopenharmony_ci{
15808c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
15818c2ecf20Sopenharmony_ci	int floor, i;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
15848c2ecf20Sopenharmony_ci	     floor++)
15858c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++)
15868c2ecf20Sopenharmony_ci			device_remove_file(dev, &doc_sys_attrs[floor][i]);
15878c2ecf20Sopenharmony_ci}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci/*
15908c2ecf20Sopenharmony_ci * Debug sysfs entries
15918c2ecf20Sopenharmony_ci */
15928c2ecf20Sopenharmony_cistatic int flashcontrol_show(struct seq_file *s, void *p)
15938c2ecf20Sopenharmony_ci{
15948c2ecf20Sopenharmony_ci	struct docg3 *docg3 = (struct docg3 *)s->private;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	u8 fctrl;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
15998c2ecf20Sopenharmony_ci	fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
16008c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	seq_printf(s, "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
16038c2ecf20Sopenharmony_ci		   fctrl,
16048c2ecf20Sopenharmony_ci		   fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-",
16058c2ecf20Sopenharmony_ci		   fctrl & DOC_CTRL_CE ? "active" : "inactive",
16068c2ecf20Sopenharmony_ci		   fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-",
16078c2ecf20Sopenharmony_ci		   fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-",
16088c2ecf20Sopenharmony_ci		   fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready");
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	return 0;
16118c2ecf20Sopenharmony_ci}
16128c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(flashcontrol);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_cistatic int asic_mode_show(struct seq_file *s, void *p)
16158c2ecf20Sopenharmony_ci{
16168c2ecf20Sopenharmony_ci	struct docg3 *docg3 = (struct docg3 *)s->private;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	int pctrl, mode;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
16218c2ecf20Sopenharmony_ci	pctrl = doc_register_readb(docg3, DOC_ASICMODE);
16228c2ecf20Sopenharmony_ci	mode = pctrl & 0x03;
16238c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	seq_printf(s,
16268c2ecf20Sopenharmony_ci		   "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
16278c2ecf20Sopenharmony_ci		   pctrl,
16288c2ecf20Sopenharmony_ci		   pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0,
16298c2ecf20Sopenharmony_ci		   pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0,
16308c2ecf20Sopenharmony_ci		   pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0,
16318c2ecf20Sopenharmony_ci		   pctrl & DOC_ASICMODE_MDWREN ? 1 : 0,
16328c2ecf20Sopenharmony_ci		   pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0,
16338c2ecf20Sopenharmony_ci		   mode >> 1, mode & 0x1);
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	switch (mode) {
16368c2ecf20Sopenharmony_ci	case DOC_ASICMODE_RESET:
16378c2ecf20Sopenharmony_ci		seq_puts(s, "reset");
16388c2ecf20Sopenharmony_ci		break;
16398c2ecf20Sopenharmony_ci	case DOC_ASICMODE_NORMAL:
16408c2ecf20Sopenharmony_ci		seq_puts(s, "normal");
16418c2ecf20Sopenharmony_ci		break;
16428c2ecf20Sopenharmony_ci	case DOC_ASICMODE_POWERDOWN:
16438c2ecf20Sopenharmony_ci		seq_puts(s, "powerdown");
16448c2ecf20Sopenharmony_ci		break;
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci	seq_puts(s, ")\n");
16478c2ecf20Sopenharmony_ci	return 0;
16488c2ecf20Sopenharmony_ci}
16498c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(asic_mode);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_cistatic int device_id_show(struct seq_file *s, void *p)
16528c2ecf20Sopenharmony_ci{
16538c2ecf20Sopenharmony_ci	struct docg3 *docg3 = (struct docg3 *)s->private;
16548c2ecf20Sopenharmony_ci	int id;
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
16578c2ecf20Sopenharmony_ci	id = doc_register_readb(docg3, DOC_DEVICESELECT);
16588c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	seq_printf(s, "DeviceId = %d\n", id);
16618c2ecf20Sopenharmony_ci	return 0;
16628c2ecf20Sopenharmony_ci}
16638c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(device_id);
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_cistatic int protection_show(struct seq_file *s, void *p)
16668c2ecf20Sopenharmony_ci{
16678c2ecf20Sopenharmony_ci	struct docg3 *docg3 = (struct docg3 *)s->private;
16688c2ecf20Sopenharmony_ci	int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	mutex_lock(&docg3->cascade->lock);
16718c2ecf20Sopenharmony_ci	protect = doc_register_readb(docg3, DOC_PROTECTION);
16728c2ecf20Sopenharmony_ci	dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
16738c2ecf20Sopenharmony_ci	dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW);
16748c2ecf20Sopenharmony_ci	dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH);
16758c2ecf20Sopenharmony_ci	dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
16768c2ecf20Sopenharmony_ci	dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW);
16778c2ecf20Sopenharmony_ci	dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
16788c2ecf20Sopenharmony_ci	mutex_unlock(&docg3->cascade->lock);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	seq_printf(s, "Protection = 0x%02x (", protect);
16818c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK)
16828c2ecf20Sopenharmony_ci		seq_puts(s, "FOUNDRY_OTP_LOCK,");
16838c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK)
16848c2ecf20Sopenharmony_ci		seq_puts(s, "CUSTOMER_OTP_LOCK,");
16858c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_LOCK_INPUT)
16868c2ecf20Sopenharmony_ci		seq_puts(s, "LOCK_INPUT,");
16878c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_STICKY_LOCK)
16888c2ecf20Sopenharmony_ci		seq_puts(s, "STICKY_LOCK,");
16898c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_PROTECTION_ENABLED)
16908c2ecf20Sopenharmony_ci		seq_puts(s, "PROTECTION ON,");
16918c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK)
16928c2ecf20Sopenharmony_ci		seq_puts(s, "IPL_DOWNLOAD_LOCK,");
16938c2ecf20Sopenharmony_ci	if (protect & DOC_PROTECT_PROTECTION_ERROR)
16948c2ecf20Sopenharmony_ci		seq_puts(s, "PROTECT_ERR,");
16958c2ecf20Sopenharmony_ci	else
16968c2ecf20Sopenharmony_ci		seq_puts(s, "NO_PROTECT_ERR");
16978c2ecf20Sopenharmony_ci	seq_puts(s, ")\n");
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	seq_printf(s, "DPS0 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
17008c2ecf20Sopenharmony_ci		   dps0, dps0_low, dps0_high,
17018c2ecf20Sopenharmony_ci		   !!(dps0 & DOC_DPS_OTP_PROTECTED),
17028c2ecf20Sopenharmony_ci		   !!(dps0 & DOC_DPS_READ_PROTECTED),
17038c2ecf20Sopenharmony_ci		   !!(dps0 & DOC_DPS_WRITE_PROTECTED),
17048c2ecf20Sopenharmony_ci		   !!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
17058c2ecf20Sopenharmony_ci		   !!(dps0 & DOC_DPS_KEY_OK));
17068c2ecf20Sopenharmony_ci	seq_printf(s, "DPS1 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
17078c2ecf20Sopenharmony_ci		   dps1, dps1_low, dps1_high,
17088c2ecf20Sopenharmony_ci		   !!(dps1 & DOC_DPS_OTP_PROTECTED),
17098c2ecf20Sopenharmony_ci		   !!(dps1 & DOC_DPS_READ_PROTECTED),
17108c2ecf20Sopenharmony_ci		   !!(dps1 & DOC_DPS_WRITE_PROTECTED),
17118c2ecf20Sopenharmony_ci		   !!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
17128c2ecf20Sopenharmony_ci		   !!(dps1 & DOC_DPS_KEY_OK));
17138c2ecf20Sopenharmony_ci	return 0;
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(protection);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_cistatic void __init doc_dbg_register(struct mtd_info *floor)
17188c2ecf20Sopenharmony_ci{
17198c2ecf20Sopenharmony_ci	struct dentry *root = floor->dbg.dfs_dir;
17208c2ecf20Sopenharmony_ci	struct docg3 *docg3 = floor->priv;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(root)) {
17238c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_DEBUG_FS) &&
17248c2ecf20Sopenharmony_ci		    !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
17258c2ecf20Sopenharmony_ci			dev_warn(floor->dev.parent,
17268c2ecf20Sopenharmony_ci				 "CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n");
17278c2ecf20Sopenharmony_ci		return;
17288c2ecf20Sopenharmony_ci	}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	debugfs_create_file("docg3_flashcontrol", S_IRUSR, root, docg3,
17318c2ecf20Sopenharmony_ci			    &flashcontrol_fops);
17328c2ecf20Sopenharmony_ci	debugfs_create_file("docg3_asic_mode", S_IRUSR, root, docg3,
17338c2ecf20Sopenharmony_ci			    &asic_mode_fops);
17348c2ecf20Sopenharmony_ci	debugfs_create_file("docg3_device_id", S_IRUSR, root, docg3,
17358c2ecf20Sopenharmony_ci			    &device_id_fops);
17368c2ecf20Sopenharmony_ci	debugfs_create_file("docg3_protection", S_IRUSR, root, docg3,
17378c2ecf20Sopenharmony_ci			    &protection_fops);
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci/**
17418c2ecf20Sopenharmony_ci * doc_set_driver_info - Fill the mtd_info structure and docg3 structure
17428c2ecf20Sopenharmony_ci * @chip_id: The chip ID of the supported chip
17438c2ecf20Sopenharmony_ci * @mtd: The structure to fill
17448c2ecf20Sopenharmony_ci */
17458c2ecf20Sopenharmony_cistatic int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
17468c2ecf20Sopenharmony_ci{
17478c2ecf20Sopenharmony_ci	struct docg3 *docg3 = mtd->priv;
17488c2ecf20Sopenharmony_ci	int cfg;
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	cfg = doc_register_readb(docg3, DOC_CONFIGURATION);
17518c2ecf20Sopenharmony_ci	docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0);
17528c2ecf20Sopenharmony_ci	docg3->reliable = reliable_mode;
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	switch (chip_id) {
17558c2ecf20Sopenharmony_ci	case DOC_CHIPID_G3:
17568c2ecf20Sopenharmony_ci		mtd->name = devm_kasprintf(docg3->dev, GFP_KERNEL, "docg3.%d",
17578c2ecf20Sopenharmony_ci					   docg3->device_id);
17588c2ecf20Sopenharmony_ci		if (!mtd->name)
17598c2ecf20Sopenharmony_ci			return -ENOMEM;
17608c2ecf20Sopenharmony_ci		docg3->max_block = 2047;
17618c2ecf20Sopenharmony_ci		break;
17628c2ecf20Sopenharmony_ci	}
17638c2ecf20Sopenharmony_ci	mtd->type = MTD_NANDFLASH;
17648c2ecf20Sopenharmony_ci	mtd->flags = MTD_CAP_NANDFLASH;
17658c2ecf20Sopenharmony_ci	mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE;
17668c2ecf20Sopenharmony_ci	if (docg3->reliable == 2)
17678c2ecf20Sopenharmony_ci		mtd->size /= 2;
17688c2ecf20Sopenharmony_ci	mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
17698c2ecf20Sopenharmony_ci	if (docg3->reliable == 2)
17708c2ecf20Sopenharmony_ci		mtd->erasesize /= 2;
17718c2ecf20Sopenharmony_ci	mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
17728c2ecf20Sopenharmony_ci	mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
17738c2ecf20Sopenharmony_ci	mtd->_erase = doc_erase;
17748c2ecf20Sopenharmony_ci	mtd->_read_oob = doc_read_oob;
17758c2ecf20Sopenharmony_ci	mtd->_write_oob = doc_write_oob;
17768c2ecf20Sopenharmony_ci	mtd->_block_isbad = doc_block_isbad;
17778c2ecf20Sopenharmony_ci	mtd_set_ooblayout(mtd, &nand_ooblayout_docg3_ops);
17788c2ecf20Sopenharmony_ci	mtd->oobavail = 8;
17798c2ecf20Sopenharmony_ci	mtd->ecc_strength = DOC_ECC_BCH_T;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	return 0;
17828c2ecf20Sopenharmony_ci}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci/**
17858c2ecf20Sopenharmony_ci * doc_probe_device - Check if a device is available
17868c2ecf20Sopenharmony_ci * @base: the io space where the device is probed
17878c2ecf20Sopenharmony_ci * @floor: the floor of the probed device
17888c2ecf20Sopenharmony_ci * @dev: the device
17898c2ecf20Sopenharmony_ci * @cascade: the cascade of chips this devices will belong to
17908c2ecf20Sopenharmony_ci *
17918c2ecf20Sopenharmony_ci * Checks whether a device at the specified IO range, and floor is available.
17928c2ecf20Sopenharmony_ci *
17938c2ecf20Sopenharmony_ci * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM
17948c2ecf20Sopenharmony_ci * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is
17958c2ecf20Sopenharmony_ci * launched.
17968c2ecf20Sopenharmony_ci */
17978c2ecf20Sopenharmony_cistatic struct mtd_info * __init
17988c2ecf20Sopenharmony_cidoc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	int ret, bbt_nbpages;
18018c2ecf20Sopenharmony_ci	u16 chip_id, chip_id_inv;
18028c2ecf20Sopenharmony_ci	struct docg3 *docg3;
18038c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	ret = -ENOMEM;
18068c2ecf20Sopenharmony_ci	docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL);
18078c2ecf20Sopenharmony_ci	if (!docg3)
18088c2ecf20Sopenharmony_ci		goto nomem1;
18098c2ecf20Sopenharmony_ci	mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
18108c2ecf20Sopenharmony_ci	if (!mtd)
18118c2ecf20Sopenharmony_ci		goto nomem2;
18128c2ecf20Sopenharmony_ci	mtd->priv = docg3;
18138c2ecf20Sopenharmony_ci	mtd->dev.parent = dev;
18148c2ecf20Sopenharmony_ci	bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1,
18158c2ecf20Sopenharmony_ci				   8 * DOC_LAYOUT_PAGE_SIZE);
18168c2ecf20Sopenharmony_ci	docg3->bbt = kcalloc(DOC_LAYOUT_PAGE_SIZE, bbt_nbpages, GFP_KERNEL);
18178c2ecf20Sopenharmony_ci	if (!docg3->bbt)
18188c2ecf20Sopenharmony_ci		goto nomem3;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	docg3->dev = dev;
18218c2ecf20Sopenharmony_ci	docg3->device_id = floor;
18228c2ecf20Sopenharmony_ci	docg3->cascade = cascade;
18238c2ecf20Sopenharmony_ci	doc_set_device_id(docg3, docg3->device_id);
18248c2ecf20Sopenharmony_ci	if (!floor)
18258c2ecf20Sopenharmony_ci		doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
18268c2ecf20Sopenharmony_ci	doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL);
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	chip_id = doc_register_readw(docg3, DOC_CHIPID);
18298c2ecf20Sopenharmony_ci	chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV);
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	ret = 0;
18328c2ecf20Sopenharmony_ci	if (chip_id != (u16)(~chip_id_inv)) {
18338c2ecf20Sopenharmony_ci		goto nomem4;
18348c2ecf20Sopenharmony_ci	}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	switch (chip_id) {
18378c2ecf20Sopenharmony_ci	case DOC_CHIPID_G3:
18388c2ecf20Sopenharmony_ci		doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n",
18398c2ecf20Sopenharmony_ci			 docg3->cascade->base, floor);
18408c2ecf20Sopenharmony_ci		break;
18418c2ecf20Sopenharmony_ci	default:
18428c2ecf20Sopenharmony_ci		doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
18438c2ecf20Sopenharmony_ci		goto nomem4;
18448c2ecf20Sopenharmony_ci	}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	ret = doc_set_driver_info(chip_id, mtd);
18478c2ecf20Sopenharmony_ci	if (ret)
18488c2ecf20Sopenharmony_ci		goto nomem4;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
18518c2ecf20Sopenharmony_ci	doc_reload_bbt(docg3);
18528c2ecf20Sopenharmony_ci	return mtd;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_cinomem4:
18558c2ecf20Sopenharmony_ci	kfree(docg3->bbt);
18568c2ecf20Sopenharmony_cinomem3:
18578c2ecf20Sopenharmony_ci	kfree(mtd);
18588c2ecf20Sopenharmony_cinomem2:
18598c2ecf20Sopenharmony_ci	kfree(docg3);
18608c2ecf20Sopenharmony_cinomem1:
18618c2ecf20Sopenharmony_ci	return ret ? ERR_PTR(ret) : NULL;
18628c2ecf20Sopenharmony_ci}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci/**
18658c2ecf20Sopenharmony_ci * doc_release_device - Release a docg3 floor
18668c2ecf20Sopenharmony_ci * @mtd: the device
18678c2ecf20Sopenharmony_ci */
18688c2ecf20Sopenharmony_cistatic void doc_release_device(struct mtd_info *mtd)
18698c2ecf20Sopenharmony_ci{
18708c2ecf20Sopenharmony_ci	struct docg3 *docg3 = mtd->priv;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	mtd_device_unregister(mtd);
18738c2ecf20Sopenharmony_ci	kfree(docg3->bbt);
18748c2ecf20Sopenharmony_ci	kfree(docg3);
18758c2ecf20Sopenharmony_ci	kfree(mtd);
18768c2ecf20Sopenharmony_ci}
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci/**
18798c2ecf20Sopenharmony_ci * docg3_resume - Awakens docg3 floor
18808c2ecf20Sopenharmony_ci * @pdev: platfrom device
18818c2ecf20Sopenharmony_ci *
18828c2ecf20Sopenharmony_ci * Returns 0 (always successful)
18838c2ecf20Sopenharmony_ci */
18848c2ecf20Sopenharmony_cistatic int docg3_resume(struct platform_device *pdev)
18858c2ecf20Sopenharmony_ci{
18868c2ecf20Sopenharmony_ci	int i;
18878c2ecf20Sopenharmony_ci	struct docg3_cascade *cascade;
18888c2ecf20Sopenharmony_ci	struct mtd_info **docg3_floors, *mtd;
18898c2ecf20Sopenharmony_ci	struct docg3 *docg3;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	cascade = platform_get_drvdata(pdev);
18928c2ecf20Sopenharmony_ci	docg3_floors = cascade->floors;
18938c2ecf20Sopenharmony_ci	mtd = docg3_floors[0];
18948c2ecf20Sopenharmony_ci	docg3 = mtd->priv;
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci	doc_dbg("docg3_resume()\n");
18978c2ecf20Sopenharmony_ci	for (i = 0; i < 12; i++)
18988c2ecf20Sopenharmony_ci		doc_readb(docg3, DOC_IOSPACE_IPL);
18998c2ecf20Sopenharmony_ci	return 0;
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci/**
19038c2ecf20Sopenharmony_ci * docg3_suspend - Put in low power mode the docg3 floor
19048c2ecf20Sopenharmony_ci * @pdev: platform device
19058c2ecf20Sopenharmony_ci * @state: power state
19068c2ecf20Sopenharmony_ci *
19078c2ecf20Sopenharmony_ci * Shuts off most of docg3 circuitery to lower power consumption.
19088c2ecf20Sopenharmony_ci *
19098c2ecf20Sopenharmony_ci * Returns 0 if suspend succeeded, -EIO if chip refused suspend
19108c2ecf20Sopenharmony_ci */
19118c2ecf20Sopenharmony_cistatic int docg3_suspend(struct platform_device *pdev, pm_message_t state)
19128c2ecf20Sopenharmony_ci{
19138c2ecf20Sopenharmony_ci	int floor, i;
19148c2ecf20Sopenharmony_ci	struct docg3_cascade *cascade;
19158c2ecf20Sopenharmony_ci	struct mtd_info **docg3_floors, *mtd;
19168c2ecf20Sopenharmony_ci	struct docg3 *docg3;
19178c2ecf20Sopenharmony_ci	u8 ctrl, pwr_down;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	cascade = platform_get_drvdata(pdev);
19208c2ecf20Sopenharmony_ci	docg3_floors = cascade->floors;
19218c2ecf20Sopenharmony_ci	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
19228c2ecf20Sopenharmony_ci		mtd = docg3_floors[floor];
19238c2ecf20Sopenharmony_ci		if (!mtd)
19248c2ecf20Sopenharmony_ci			continue;
19258c2ecf20Sopenharmony_ci		docg3 = mtd->priv;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci		doc_writeb(docg3, floor, DOC_DEVICESELECT);
19288c2ecf20Sopenharmony_ci		ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
19298c2ecf20Sopenharmony_ci		ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE;
19308c2ecf20Sopenharmony_ci		doc_writeb(docg3, ctrl, DOC_FLASHCONTROL);
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci		for (i = 0; i < 10; i++) {
19338c2ecf20Sopenharmony_ci			usleep_range(3000, 4000);
19348c2ecf20Sopenharmony_ci			pwr_down = doc_register_readb(docg3, DOC_POWERMODE);
19358c2ecf20Sopenharmony_ci			if (pwr_down & DOC_POWERDOWN_READY)
19368c2ecf20Sopenharmony_ci				break;
19378c2ecf20Sopenharmony_ci		}
19388c2ecf20Sopenharmony_ci		if (pwr_down & DOC_POWERDOWN_READY) {
19398c2ecf20Sopenharmony_ci			doc_dbg("docg3_suspend(): floor %d powerdown ok\n",
19408c2ecf20Sopenharmony_ci				floor);
19418c2ecf20Sopenharmony_ci		} else {
19428c2ecf20Sopenharmony_ci			doc_err("docg3_suspend(): floor %d powerdown failed\n",
19438c2ecf20Sopenharmony_ci				floor);
19448c2ecf20Sopenharmony_ci			return -EIO;
19458c2ecf20Sopenharmony_ci		}
19468c2ecf20Sopenharmony_ci	}
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci	mtd = docg3_floors[0];
19498c2ecf20Sopenharmony_ci	docg3 = mtd->priv;
19508c2ecf20Sopenharmony_ci	doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN);
19518c2ecf20Sopenharmony_ci	return 0;
19528c2ecf20Sopenharmony_ci}
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci/**
19558c2ecf20Sopenharmony_ci * doc_probe - Probe the IO space for a DiskOnChip G3 chip
19568c2ecf20Sopenharmony_ci * @pdev: platform device
19578c2ecf20Sopenharmony_ci *
19588c2ecf20Sopenharmony_ci * Probes for a G3 chip at the specified IO space in the platform data
19598c2ecf20Sopenharmony_ci * ressources. The floor 0 must be available.
19608c2ecf20Sopenharmony_ci *
19618c2ecf20Sopenharmony_ci * Returns 0 on success, -ENOMEM, -ENXIO on error
19628c2ecf20Sopenharmony_ci */
19638c2ecf20Sopenharmony_cistatic int __init docg3_probe(struct platform_device *pdev)
19648c2ecf20Sopenharmony_ci{
19658c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
19668c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
19678c2ecf20Sopenharmony_ci	struct resource *ress;
19688c2ecf20Sopenharmony_ci	void __iomem *base;
19698c2ecf20Sopenharmony_ci	int ret, floor;
19708c2ecf20Sopenharmony_ci	struct docg3_cascade *cascade;
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	ret = -ENXIO;
19738c2ecf20Sopenharmony_ci	ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
19748c2ecf20Sopenharmony_ci	if (!ress) {
19758c2ecf20Sopenharmony_ci		dev_err(dev, "No I/O memory resource defined\n");
19768c2ecf20Sopenharmony_ci		return ret;
19778c2ecf20Sopenharmony_ci	}
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	ret = -ENOMEM;
19808c2ecf20Sopenharmony_ci	base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE);
19818c2ecf20Sopenharmony_ci	if (!base) {
19828c2ecf20Sopenharmony_ci		dev_err(dev, "devm_ioremap dev failed\n");
19838c2ecf20Sopenharmony_ci		return ret;
19848c2ecf20Sopenharmony_ci	}
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	cascade = devm_kcalloc(dev, DOC_MAX_NBFLOORS, sizeof(*cascade),
19878c2ecf20Sopenharmony_ci			       GFP_KERNEL);
19888c2ecf20Sopenharmony_ci	if (!cascade)
19898c2ecf20Sopenharmony_ci		return ret;
19908c2ecf20Sopenharmony_ci	cascade->base = base;
19918c2ecf20Sopenharmony_ci	mutex_init(&cascade->lock);
19928c2ecf20Sopenharmony_ci	cascade->bch = bch_init(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
19938c2ecf20Sopenharmony_ci				DOC_ECC_BCH_PRIMPOLY, false);
19948c2ecf20Sopenharmony_ci	if (!cascade->bch)
19958c2ecf20Sopenharmony_ci		return ret;
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
19988c2ecf20Sopenharmony_ci		mtd = doc_probe_device(cascade, floor, dev);
19998c2ecf20Sopenharmony_ci		if (IS_ERR(mtd)) {
20008c2ecf20Sopenharmony_ci			ret = PTR_ERR(mtd);
20018c2ecf20Sopenharmony_ci			goto err_probe;
20028c2ecf20Sopenharmony_ci		}
20038c2ecf20Sopenharmony_ci		if (!mtd) {
20048c2ecf20Sopenharmony_ci			if (floor == 0)
20058c2ecf20Sopenharmony_ci				goto notfound;
20068c2ecf20Sopenharmony_ci			else
20078c2ecf20Sopenharmony_ci				continue;
20088c2ecf20Sopenharmony_ci		}
20098c2ecf20Sopenharmony_ci		cascade->floors[floor] = mtd;
20108c2ecf20Sopenharmony_ci		ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL,
20118c2ecf20Sopenharmony_ci						0);
20128c2ecf20Sopenharmony_ci		if (ret)
20138c2ecf20Sopenharmony_ci			goto err_probe;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci		doc_dbg_register(cascade->floors[floor]);
20168c2ecf20Sopenharmony_ci	}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	ret = doc_register_sysfs(pdev, cascade);
20198c2ecf20Sopenharmony_ci	if (ret)
20208c2ecf20Sopenharmony_ci		goto err_probe;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, cascade);
20238c2ecf20Sopenharmony_ci	return 0;
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_cinotfound:
20268c2ecf20Sopenharmony_ci	ret = -ENODEV;
20278c2ecf20Sopenharmony_ci	dev_info(dev, "No supported DiskOnChip found\n");
20288c2ecf20Sopenharmony_cierr_probe:
20298c2ecf20Sopenharmony_ci	bch_free(cascade->bch);
20308c2ecf20Sopenharmony_ci	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
20318c2ecf20Sopenharmony_ci		if (cascade->floors[floor])
20328c2ecf20Sopenharmony_ci			doc_release_device(cascade->floors[floor]);
20338c2ecf20Sopenharmony_ci	return ret;
20348c2ecf20Sopenharmony_ci}
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci/**
20378c2ecf20Sopenharmony_ci * docg3_release - Release the driver
20388c2ecf20Sopenharmony_ci * @pdev: the platform device
20398c2ecf20Sopenharmony_ci *
20408c2ecf20Sopenharmony_ci * Returns 0
20418c2ecf20Sopenharmony_ci */
20428c2ecf20Sopenharmony_cistatic int docg3_release(struct platform_device *pdev)
20438c2ecf20Sopenharmony_ci{
20448c2ecf20Sopenharmony_ci	struct docg3_cascade *cascade = platform_get_drvdata(pdev);
20458c2ecf20Sopenharmony_ci	struct docg3 *docg3 = cascade->floors[0]->priv;
20468c2ecf20Sopenharmony_ci	int floor;
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	doc_unregister_sysfs(pdev, cascade);
20498c2ecf20Sopenharmony_ci	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
20508c2ecf20Sopenharmony_ci		if (cascade->floors[floor])
20518c2ecf20Sopenharmony_ci			doc_release_device(cascade->floors[floor]);
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci	bch_free(docg3->cascade->bch);
20548c2ecf20Sopenharmony_ci	return 0;
20558c2ecf20Sopenharmony_ci}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
20588c2ecf20Sopenharmony_cistatic const struct of_device_id docg3_dt_ids[] = {
20598c2ecf20Sopenharmony_ci	{ .compatible = "m-systems,diskonchip-g3" },
20608c2ecf20Sopenharmony_ci	{}
20618c2ecf20Sopenharmony_ci};
20628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, docg3_dt_ids);
20638c2ecf20Sopenharmony_ci#endif
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_cistatic struct platform_driver g3_driver = {
20668c2ecf20Sopenharmony_ci	.driver		= {
20678c2ecf20Sopenharmony_ci		.name	= "docg3",
20688c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(docg3_dt_ids),
20698c2ecf20Sopenharmony_ci	},
20708c2ecf20Sopenharmony_ci	.suspend	= docg3_suspend,
20718c2ecf20Sopenharmony_ci	.resume		= docg3_resume,
20728c2ecf20Sopenharmony_ci	.remove		= docg3_release,
20738c2ecf20Sopenharmony_ci};
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_cimodule_platform_driver_probe(g3_driver, docg3_probe);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
20788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
20798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD driver for DiskOnChip G3");
2080