mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	Boot ROM on Meson needs some pages to be read/written in a special mode: 384 byte ECC mode (so called "short" by Amlogic) and with scrambling enabled. Such pages are located on the chip in the following way (for example): [ p0 ][ p1 ][ p2 ][ p3 ][ p4 ][ p5 ][ p6 ][ p7 ] ... [ pN ] ^ ^ ^ ^ pX is page number "X". "^" means "special" page used by boot ROM - e.g. every 2nd page in the range of [0, 7]. Step (2 here) and last page in range is read from the device tree. Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com> Reviewed-by: Michael Trimarchi <michael@amarulasolutions.com> Link: https://lore.kernel.org/r/20240826131710.29746-4-avkrasnov@salutedevices.com Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
		
			
				
	
	
		
			1278 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1278 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
 | 
						|
/*
 | 
						|
 * Amlogic Meson Nand Flash Controller Driver
 | 
						|
 *
 | 
						|
 * Copyright (c) 2018 Amlogic, inc.
 | 
						|
 * Author: Liang Yang <liang.yang@amlogic.com>
 | 
						|
 *
 | 
						|
 * Copyright (c) 2023 SaluteDevices, Inc.
 | 
						|
 * Author: Arseniy Krasnov <avkrasnov@salutedevices.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <nand.h>
 | 
						|
#include <asm/io.h>
 | 
						|
#include <dm.h>
 | 
						|
#include <dm/device_compat.h>
 | 
						|
#include <dm/ofnode.h>
 | 
						|
#include <dm/uclass.h>
 | 
						|
#include <linux/bug.h>
 | 
						|
#include <linux/clk-provider.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/dma-mapping.h>
 | 
						|
#include <linux/iopoll.h>
 | 
						|
#include <linux/mtd/mtd.h>
 | 
						|
#include <linux/mtd/rawnand.h>
 | 
						|
#include <linux/sizes.h>
 | 
						|
 | 
						|
#define NFC_CMD_IDLE			(0xc << 14)
 | 
						|
#define NFC_CMD_CLE			(0x5 << 14)
 | 
						|
#define NFC_CMD_ALE			(0x6 << 14)
 | 
						|
#define NFC_CMD_DWR			(0x4 << 14)
 | 
						|
#define NFC_CMD_DRD			(0x8 << 14)
 | 
						|
#define NFC_CMD_ADL			((0 << 16) | (3 << 20))
 | 
						|
#define NFC_CMD_ADH			((1 << 16) | (3 << 20))
 | 
						|
#define NFC_CMD_AIL			((2 << 16) | (3 << 20))
 | 
						|
#define NFC_CMD_AIH			((3 << 16) | (3 << 20))
 | 
						|
#define NFC_CMD_SEED			((8 << 16) | (3 << 20))
 | 
						|
#define NFC_CMD_M2N			((0 << 17) | (2 << 20))
 | 
						|
#define NFC_CMD_N2M			((1 << 17) | (2 << 20))
 | 
						|
#define NFC_CMD_RB			BIT(20)
 | 
						|
#define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
 | 
						|
#define NFC_CMD_SCRAMBLER_DISABLE	0
 | 
						|
#define NFC_CMD_SHORTMODE_ENABLE	1
 | 
						|
#define NFC_CMD_SHORTMODE_DISABLE	0
 | 
						|
#define NFC_CMD_RB_INT			BIT(14)
 | 
						|
#define NFC_CMD_RB_INT_NO_PIN		((0xb << 10) | BIT(18) | BIT(16))
 | 
						|
 | 
						|
#define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
 | 
						|
 | 
						|
#define NFC_REG_CMD		0x00
 | 
						|
#define NFC_REG_CFG		0x04
 | 
						|
#define NFC_REG_DADR		0x08
 | 
						|
#define NFC_REG_IADR		0x0c
 | 
						|
#define NFC_REG_BUF		0x10
 | 
						|
#define NFC_REG_INFO		0x14
 | 
						|
#define NFC_REG_DC		0x18
 | 
						|
#define NFC_REG_ADR		0x1c
 | 
						|
#define NFC_REG_DL		0x20
 | 
						|
#define NFC_REG_DH		0x24
 | 
						|
#define NFC_REG_CADR		0x28
 | 
						|
#define NFC_REG_SADR		0x2c
 | 
						|
#define NFC_REG_PINS		0x30
 | 
						|
#define NFC_REG_VER		0x38
 | 
						|
 | 
						|
#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)	\
 | 
						|
	(								\
 | 
						|
		(cmd_dir)			|			\
 | 
						|
		(ran)				|			\
 | 
						|
		((bch) << 14)			|			\
 | 
						|
		((short_mode) << 13)		|			\
 | 
						|
		(((page_size) & 0x7f) << 6)	|			\
 | 
						|
		((pages) & 0x3f)					\
 | 
						|
	)
 | 
						|
 | 
						|
#define GENCMDDADDRL(adl, addr)		((adl) | ((addr) & 0xffff))
 | 
						|
#define GENCMDDADDRH(adh, addr)		((adh) | (((addr) >> 16) & 0xffff))
 | 
						|
#define GENCMDIADDRL(ail, addr)		((ail) | ((addr) & 0xffff))
 | 
						|
#define GENCMDIADDRH(aih, addr)		((aih) | (((addr) >> 16) & 0xffff))
 | 
						|
 | 
						|
#define DMA_DIR(dir)		((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
 | 
						|
 | 
						|
#define NFC_SHORT_MODE_ECC_SZ	384
 | 
						|
 | 
						|
#define ECC_CHECK_RETURN_FF	-1
 | 
						|
 | 
						|
#define NAND_CE0		(0xe << 10)
 | 
						|
#define NAND_CE1		(0xd << 10)
 | 
						|
 | 
						|
#define DMA_BUSY_TIMEOUT_US	1000000
 | 
						|
#define CMD_DRAIN_TIMEOUT_US	1000
 | 
						|
#define ECC_POLL_TIMEOUT_US	15
 | 
						|
 | 
						|
#define MAX_CE_NUM		2
 | 
						|
 | 
						|
/* eMMC clock register, misc control */
 | 
						|
#define CLK_SELECT_NAND		BIT(31)
 | 
						|
#define CLK_ALWAYS_ON_NAND	BIT(24)
 | 
						|
#define CLK_ENABLE_VALUE	0x245
 | 
						|
 | 
						|
#define DIRREAD			1
 | 
						|
#define DIRWRITE		0
 | 
						|
 | 
						|
#define ECC_PARITY_BCH8_512B	14
 | 
						|
#define ECC_COMPLETE            BIT(31)
 | 
						|
#define ECC_ERR_CNT(x)		(((x) >> 24) & GENMASK(5, 0))
 | 
						|
#define ECC_ZERO_CNT(x)		(((x) >> 16) & GENMASK(5, 0))
 | 
						|
#define ECC_UNCORRECTABLE	0x3f
 | 
						|
 | 
						|
#define PER_INFO_BYTE		8
 | 
						|
 | 
						|
#define NFC_SEND_CMD(host, cmd) \
 | 
						|
	(writel((cmd), (host)->reg_base + NFC_REG_CMD))
 | 
						|
 | 
						|
#define NFC_GET_CMD(host) \
 | 
						|
	(readl((host)->reg_base + NFC_REG_CMD))
 | 
						|
 | 
						|
#define NFC_CMDFIFO_SIZE(host)	((NFC_GET_CMD((host)) >> 22) & GENMASK(4, 0))
 | 
						|
 | 
						|
#define NFC_CMD_MAKE_IDLE(ce, delay)	((ce) | NFC_CMD_IDLE | ((delay) & 0x3ff))
 | 
						|
#define NFC_CMD_MAKE_DRD(ce, size)	((ce) | NFC_CMD_DRD | (size))
 | 
						|
#define NFC_CMD_MAKE_DWR(ce, data)	((ce) | NFC_CMD_DWR | ((data) & 0xff))
 | 
						|
#define NFC_CMD_MAKE_CLE(ce, cmd_val)	((ce) | NFC_CMD_CLE | ((cmd_val) & 0xff))
 | 
						|
#define NFC_CMD_MAKE_ALE(ce, addr)	((ce) | NFC_CMD_ALE | ((addr) & 0xff))
 | 
						|
 | 
						|
#define NAND_TWB_TIME_CYCLE	10
 | 
						|
 | 
						|
#define NFC_DEV_READY_TICK_MAX	5000
 | 
						|
 | 
						|
/* Both values are recommended by vendor, as the most
 | 
						|
 * tested with almost all SLC NAND flash. Second value
 | 
						|
 * could be calculated dynamically from timing parameters,
 | 
						|
 * but we need both values for initial start of the NAND
 | 
						|
 * controller (e.g. before NAND subsystem processes timings),
 | 
						|
 * so use hardcoded constants.
 | 
						|
 */
 | 
						|
#define NFC_DEFAULT_BUS_CYCLE	6
 | 
						|
#define NFC_DEFAULT_BUS_TIMING	7
 | 
						|
 | 
						|
#define NFC_SEED_OFFSET		0xc2
 | 
						|
#define NFC_SEED_MASK		0x7fff
 | 
						|
 | 
						|
#define DMA_ADDR_ALIGN		8
 | 
						|
 | 
						|
struct meson_nfc_nand_chip {
 | 
						|
	struct list_head node;
 | 
						|
	struct nand_chip nand;
 | 
						|
	u32 boot_pages;
 | 
						|
	u32 boot_page_step;
 | 
						|
 | 
						|
	u32 bch_mode;
 | 
						|
	u8 *data_buf;
 | 
						|
	__le64 *info_buf;
 | 
						|
	u32 nsels;
 | 
						|
	u8 sels[];
 | 
						|
};
 | 
						|
 | 
						|
struct meson_nfc_param {
 | 
						|
	u32 chip_select;
 | 
						|
	u32 rb_select;
 | 
						|
};
 | 
						|
 | 
						|
struct meson_nfc {
 | 
						|
	void __iomem *reg_base;
 | 
						|
	void __iomem *reg_clk;
 | 
						|
	struct list_head chips;
 | 
						|
	struct meson_nfc_param param;
 | 
						|
	struct udevice *dev;
 | 
						|
	dma_addr_t daddr;
 | 
						|
	dma_addr_t iaddr;
 | 
						|
	u32 data_bytes;
 | 
						|
	u32 info_bytes;
 | 
						|
	u64 assigned_cs;
 | 
						|
};
 | 
						|
 | 
						|
struct meson_nand_ecc {
 | 
						|
	u32 bch;
 | 
						|
	u32 strength;
 | 
						|
	u32 size;
 | 
						|
};
 | 
						|
 | 
						|
enum {
 | 
						|
	NFC_ECC_BCH8_512 = 1,
 | 
						|
	NFC_ECC_BCH8_1K,
 | 
						|
	NFC_ECC_BCH24_1K,
 | 
						|
	NFC_ECC_BCH30_1K,
 | 
						|
	NFC_ECC_BCH40_1K,
 | 
						|
	NFC_ECC_BCH50_1K,
 | 
						|
	NFC_ECC_BCH60_1K,
 | 
						|
};
 | 
						|
 | 
						|
#define MESON_ECC_DATA(b, s, sz) { .bch = (b), .strength = (s), .size = (sz) }
 | 
						|
 | 
						|
static struct meson_nand_ecc meson_ecc[] = {
 | 
						|
	MESON_ECC_DATA(NFC_ECC_BCH8_512, 8,  512),
 | 
						|
	MESON_ECC_DATA(NFC_ECC_BCH8_1K,  8,  1024),
 | 
						|
};
 | 
						|
 | 
						|
static int meson_nand_calc_ecc_bytes(int step_size, int strength)
 | 
						|
{
 | 
						|
	int ecc_bytes;
 | 
						|
 | 
						|
	if (step_size == 512 && strength == 8)
 | 
						|
		return ECC_PARITY_BCH8_512B;
 | 
						|
 | 
						|
	ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
 | 
						|
	ecc_bytes = ALIGN(ecc_bytes, 2);
 | 
						|
 | 
						|
	return ecc_bytes;
 | 
						|
}
 | 
						|
 | 
						|
static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
 | 
						|
{
 | 
						|
	return container_of(nand, struct meson_nfc_nand_chip, nand);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_nand_select_chip(struct mtd_info *mtd, int chip)
 | 
						|
{
 | 
						|
	struct nand_chip *nand = mtd_to_nand(mtd);
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
 | 
						|
	nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0;
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
 | 
						|
{
 | 
						|
	writel(NFC_CMD_MAKE_IDLE(nfc->param.chip_select, time),
 | 
						|
	       nfc->reg_base + NFC_REG_CMD);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_cmd_seed(const struct meson_nfc *nfc, u32 seed)
 | 
						|
{
 | 
						|
	writel(NFC_CMD_SEED | (NFC_SEED_OFFSET + (seed & NFC_SEED_MASK)),
 | 
						|
	       nfc->reg_base + NFC_REG_CMD);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_is_boot_page(struct nand_chip *nand, int page)
 | 
						|
{
 | 
						|
	const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
 | 
						|
	return (nand->options & NAND_IS_BOOT_MEDIUM) &&
 | 
						|
	       !(page % meson_chip->boot_page_step) &&
 | 
						|
	       (page < meson_chip->boot_pages);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_cmd_access(struct nand_chip *nand, bool raw, bool dir, int page)
 | 
						|
{
 | 
						|
	const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	struct mtd_info *mtd = nand_to_mtd(nand);
 | 
						|
	const struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
 | 
						|
	int len = mtd->writesize, pagesize, pages;
 | 
						|
	unsigned int scrambler;
 | 
						|
	u32 cmd;
 | 
						|
 | 
						|
	if (nand->options & NAND_NEED_SCRAMBLING)
 | 
						|
		scrambler = NFC_CMD_SCRAMBLER_ENABLE;
 | 
						|
	else
 | 
						|
		scrambler = NFC_CMD_SCRAMBLER_DISABLE;
 | 
						|
 | 
						|
	if (raw) {
 | 
						|
		len = mtd->writesize + mtd->oobsize;
 | 
						|
		cmd = len | scrambler | DMA_DIR(dir);
 | 
						|
	} else if (meson_nfc_is_boot_page(nand, page)) {
 | 
						|
		pagesize = NFC_SHORT_MODE_ECC_SZ >> 3;
 | 
						|
		pages = mtd->writesize / 512;
 | 
						|
 | 
						|
		scrambler = NFC_CMD_SCRAMBLER_ENABLE;
 | 
						|
		cmd = CMDRWGEN(DMA_DIR(dir), scrambler, NFC_ECC_BCH8_1K,
 | 
						|
			       NFC_CMD_SHORTMODE_ENABLE, pagesize, pages);
 | 
						|
	} else {
 | 
						|
		pagesize = nand->ecc.size >> 3;
 | 
						|
		pages = len / nand->ecc.size;
 | 
						|
 | 
						|
		cmd = CMDRWGEN(DMA_DIR(dir), scrambler, meson_chip->bch_mode,
 | 
						|
			       NFC_CMD_SHORTMODE_DISABLE, pagesize, pages);
 | 
						|
	}
 | 
						|
 | 
						|
	if (scrambler == NFC_CMD_SCRAMBLER_ENABLE)
 | 
						|
		meson_nfc_cmd_seed(nfc, page);
 | 
						|
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Insert two commands to make sure all valid commands are finished.
 | 
						|
	 *
 | 
						|
	 * The Nand flash controller is designed as two stages pipleline -
 | 
						|
	 *  a) fetch and b) execute.
 | 
						|
	 * There might be cases when the driver see command queue is empty,
 | 
						|
	 * but the Nand flash controller still has two commands buffered,
 | 
						|
	 * one is fetched into NFC request queue (ready to run), and another
 | 
						|
	 * is actively executing. So pushing 2 "IDLE" commands guarantees that
 | 
						|
	 * the pipeline is emptied.
 | 
						|
	 */
 | 
						|
	meson_nfc_cmd_idle(nfc, 0);
 | 
						|
	meson_nfc_cmd_idle(nfc, 0);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_wait_cmd_finish(const struct meson_nfc *nfc,
 | 
						|
				     unsigned int timeout_us)
 | 
						|
{
 | 
						|
	u32 cmd_size = 0;
 | 
						|
 | 
						|
	/* wait cmd fifo is empty */
 | 
						|
	return readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
 | 
						|
					  !NFC_CMD_GET_SIZE(cmd_size),
 | 
						|
					  timeout_us);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
 | 
						|
{
 | 
						|
	meson_nfc_drain_cmd(nfc);
 | 
						|
 | 
						|
	return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT_US);
 | 
						|
}
 | 
						|
 | 
						|
static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
 | 
						|
{
 | 
						|
	const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	int len;
 | 
						|
 | 
						|
	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
 | 
						|
 | 
						|
	return meson_chip->data_buf + len;
 | 
						|
}
 | 
						|
 | 
						|
static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 | 
						|
{
 | 
						|
	const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	int len, temp;
 | 
						|
 | 
						|
	temp = nand->ecc.size + nand->ecc.bytes;
 | 
						|
	len = (temp + 2) * i;
 | 
						|
 | 
						|
	return meson_chip->data_buf + len;
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_get_data_oob(struct nand_chip *nand,
 | 
						|
				   u8 *buf, u8 *oobbuf)
 | 
						|
{
 | 
						|
	u8 *dsrc, *osrc;
 | 
						|
	int i, oob_len;
 | 
						|
 | 
						|
	oob_len = nand->ecc.bytes + 2;
 | 
						|
	for (i = 0; i < nand->ecc.steps; i++) {
 | 
						|
		if (buf) {
 | 
						|
			dsrc = meson_nfc_data_ptr(nand, i);
 | 
						|
			memcpy(buf, dsrc, nand->ecc.size);
 | 
						|
			buf += nand->ecc.size;
 | 
						|
		}
 | 
						|
 | 
						|
		if (oobbuf) {
 | 
						|
			osrc = meson_nfc_oob_ptr(nand, i);
 | 
						|
			memcpy(oobbuf, osrc, oob_len);
 | 
						|
			oobbuf += oob_len;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_set_data_oob(struct nand_chip *nand,
 | 
						|
				   const u8 *buf, u8 *oobbuf)
 | 
						|
{
 | 
						|
	int i, oob_len;
 | 
						|
 | 
						|
	oob_len = nand->ecc.bytes + 2;
 | 
						|
	for (i = 0; i < nand->ecc.steps; i++) {
 | 
						|
		u8 *osrc;
 | 
						|
 | 
						|
		if (buf) {
 | 
						|
			u8 *dsrc;
 | 
						|
 | 
						|
			dsrc = meson_nfc_data_ptr(nand, i);
 | 
						|
			memcpy(dsrc, buf, nand->ecc.size);
 | 
						|
			buf += nand->ecc.size;
 | 
						|
		}
 | 
						|
 | 
						|
		osrc = meson_nfc_oob_ptr(nand, i);
 | 
						|
		memcpy(osrc, oobbuf, oob_len);
 | 
						|
		oobbuf += oob_len;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_set_user_byte(struct nand_chip *nand, const u8 *oob_buf)
 | 
						|
{
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	int i, count;
 | 
						|
 | 
						|
	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) {
 | 
						|
		__le64 *info = &meson_chip->info_buf[i];
 | 
						|
 | 
						|
		*info |= oob_buf[count];
 | 
						|
		*info |= oob_buf[count + 1] << 8;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
 | 
						|
{
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	int i, count;
 | 
						|
 | 
						|
	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) {
 | 
						|
		const __le64 *info = &meson_chip->info_buf[i];
 | 
						|
 | 
						|
		oob_buf[count] = *info;
 | 
						|
		oob_buf[count + 1] = *info >> 8;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
 | 
						|
				 u64 *correct_bitmap)
 | 
						|
{
 | 
						|
	struct mtd_info *mtd = nand_to_mtd(nand);
 | 
						|
	int ret = 0, i;
 | 
						|
 | 
						|
	for (i = 0; i < nand->ecc.steps; i++) {
 | 
						|
		struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
		const __le64 *info = &meson_chip->info_buf[i];
 | 
						|
 | 
						|
		if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
 | 
						|
			mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
 | 
						|
			*bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info));
 | 
						|
			*correct_bitmap |= BIT_ULL(i);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if ((nand->options & NAND_NEED_SCRAMBLING) &&
 | 
						|
		    ECC_ZERO_CNT(*info) < nand->ecc.strength) {
 | 
						|
			mtd->ecc_stats.corrected += ECC_ZERO_CNT(*info);
 | 
						|
			*bitflips = max_t(u32, *bitflips,
 | 
						|
					  ECC_ZERO_CNT(*info));
 | 
						|
			ret = ECC_CHECK_RETURN_FF;
 | 
						|
		} else {
 | 
						|
			ret = -EBADMSG;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf,
 | 
						|
				      int datalen, void *infobuf, int infolen,
 | 
						|
				      enum dma_data_direction dir)
 | 
						|
{
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
	int ret;
 | 
						|
	u32 cmd;
 | 
						|
 | 
						|
	nfc->daddr = dma_map_single(databuf, datalen, DMA_BIDIRECTIONAL);
 | 
						|
	ret = dma_mapping_error(nfc->dev, nfc->daddr);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	if (infobuf) {
 | 
						|
		nfc->iaddr = dma_map_single(infobuf, infolen,
 | 
						|
					    DMA_BIDIRECTIONAL);
 | 
						|
		ret = dma_mapping_error(nfc->dev, nfc->iaddr);
 | 
						|
		if (ret) {
 | 
						|
			dma_unmap_single(nfc->daddr, datalen, dir);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		nfc->info_bytes = infolen;
 | 
						|
		cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
 | 
						|
		writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
		cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
 | 
						|
		writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
 | 
						|
					 int datalen, int infolen,
 | 
						|
					 enum dma_data_direction dir)
 | 
						|
{
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
 | 
						|
	dma_unmap_single(nfc->daddr, datalen, dir);
 | 
						|
 | 
						|
	if (infolen) {
 | 
						|
		dma_unmap_single(nfc->iaddr, infolen, dir);
 | 
						|
		nfc->info_bytes = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int size)
 | 
						|
{
 | 
						|
	struct nand_chip *nand = mtd_to_nand(mtd);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	u8 *dma_buf;
 | 
						|
	int ret;
 | 
						|
	u32 cmd;
 | 
						|
 | 
						|
	if ((uintptr_t)buf % DMA_ADDR_ALIGN) {
 | 
						|
		unsigned long tmp_addr;
 | 
						|
 | 
						|
		dma_buf = dma_alloc_coherent(size, &tmp_addr);
 | 
						|
		if (!dma_buf)
 | 
						|
			return;
 | 
						|
	} else {
 | 
						|
		dma_buf = buf;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = meson_nfc_dma_buffer_setup(nand, dma_buf, size, meson_chip->info_buf,
 | 
						|
					 PER_INFO_BYTE, DMA_FROM_DEVICE);
 | 
						|
	if (ret) {
 | 
						|
		pr_err("Failed to setup DMA buffer %p/%p\n", dma_buf,
 | 
						|
		       meson_chip->info_buf);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	cmd = NFC_CMD_N2M | size;
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	meson_nfc_drain_cmd(nfc);
 | 
						|
	meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US);
 | 
						|
	meson_nfc_dma_buffer_release(nand, size, PER_INFO_BYTE, DMA_FROM_DEVICE);
 | 
						|
 | 
						|
	if (buf != dma_buf) {
 | 
						|
		memcpy(buf, dma_buf, size);
 | 
						|
		dma_free_coherent(dma_buf);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int size)
 | 
						|
{
 | 
						|
	struct nand_chip *nand = mtd_to_nand(mtd);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
	u8 *dma_buf;
 | 
						|
	int ret;
 | 
						|
	u32 cmd;
 | 
						|
 | 
						|
	if ((uintptr_t)buf % DMA_ADDR_ALIGN) {
 | 
						|
		unsigned long tmp_addr;
 | 
						|
 | 
						|
		dma_buf = dma_alloc_coherent(size, &tmp_addr);
 | 
						|
		if (!dma_buf)
 | 
						|
			return;
 | 
						|
 | 
						|
		memcpy(dma_buf, buf, size);
 | 
						|
	} else {
 | 
						|
		dma_buf = (u8 *)buf;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = meson_nfc_dma_buffer_setup(nand, (void *)dma_buf, size, NULL,
 | 
						|
					 0, DMA_TO_DEVICE);
 | 
						|
	if (ret) {
 | 
						|
		pr_err("Failed to setup DMA buffer %p\n", dma_buf);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	cmd = NFC_CMD_M2N | size;
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	meson_nfc_drain_cmd(nfc);
 | 
						|
	meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US);
 | 
						|
	meson_nfc_dma_buffer_release(nand, size, 0, DMA_TO_DEVICE);
 | 
						|
 | 
						|
	if (buf != dma_buf)
 | 
						|
		dma_free_coherent(dma_buf);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_write_page_sub(struct nand_chip *nand,
 | 
						|
				    int page, bool raw)
 | 
						|
{
 | 
						|
	const struct mtd_info *mtd = nand_to_mtd(nand);
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
	int data_len, info_len;
 | 
						|
	int ret;
 | 
						|
	u32 cmd;
 | 
						|
 | 
						|
	data_len =  mtd->writesize + mtd->oobsize;
 | 
						|
	info_len = nand->ecc.steps * PER_INFO_BYTE;
 | 
						|
 | 
						|
	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
 | 
						|
					 data_len, meson_chip->info_buf,
 | 
						|
					 info_len, DMA_TO_DEVICE);
 | 
						|
	if (ret) {
 | 
						|
		pr_err("Failed to setup DMA buffer %p/%p\n",
 | 
						|
		       meson_chip->data_buf, meson_chip->info_buf);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	meson_nfc_cmd_access(nand, raw, DIRWRITE, page);
 | 
						|
 | 
						|
	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
				    const u8 *buf, int oob_required, int page)
 | 
						|
{
 | 
						|
	meson_nfc_set_data_oob(chip, buf, oob_required ? chip->oob_poi : NULL);
 | 
						|
 | 
						|
	return meson_nfc_write_page_sub(chip, page, true);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
				      const u8 *buf, int oob_required, int page)
 | 
						|
{
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip);
 | 
						|
 | 
						|
	if (buf)
 | 
						|
		memcpy(meson_chip->data_buf, buf, mtd->writesize);
 | 
						|
 | 
						|
	memset(meson_chip->info_buf, 0, chip->ecc.steps * PER_INFO_BYTE);
 | 
						|
 | 
						|
	if (oob_required)
 | 
						|
		meson_nfc_set_user_byte(chip, chip->oob_poi);
 | 
						|
 | 
						|
	return meson_nfc_write_page_sub(chip, page, false);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
 | 
						|
					    struct nand_chip *nand, bool raw)
 | 
						|
{
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	__le64 *info;
 | 
						|
	u32 neccpages;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	neccpages = raw ? 1 : nand->ecc.steps;
 | 
						|
	info = &meson_chip->info_buf[neccpages - 1];
 | 
						|
	do {
 | 
						|
		udelay(ECC_POLL_TIMEOUT_US);
 | 
						|
		/* info is updated by nfc dma engine*/
 | 
						|
		rmb();
 | 
						|
		invalidate_dcache_range(nfc->iaddr, nfc->iaddr + nfc->info_bytes);
 | 
						|
		ret = *info & ECC_COMPLETE;
 | 
						|
	} while (!ret);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_read_page_sub(struct nand_chip *nand,
 | 
						|
				   int page, bool raw)
 | 
						|
{
 | 
						|
	struct mtd_info *mtd = nand_to_mtd(nand);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	u32 data_len, info_len;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	data_len = mtd->writesize + mtd->oobsize;
 | 
						|
	info_len = nand->ecc.steps * PER_INFO_BYTE;
 | 
						|
 | 
						|
	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, data_len,
 | 
						|
					 meson_chip->info_buf, info_len,
 | 
						|
					 DMA_FROM_DEVICE);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	meson_nfc_cmd_access(nand, raw, DIRREAD, page);
 | 
						|
 | 
						|
	meson_nfc_wait_dma_finish(nfc);
 | 
						|
	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
 | 
						|
 | 
						|
	meson_nfc_dma_buffer_release(nand, data_len, info_len,
 | 
						|
				     DMA_FROM_DEVICE);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
				   u8 *buf, int oob_required, int page)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = meson_nfc_read_page_sub(chip, page, true);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	meson_nfc_get_data_oob(chip, buf, oob_required ? chip->oob_poi : NULL);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
				     u8 *buf, int oob_required, int page)
 | 
						|
{
 | 
						|
	const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip);
 | 
						|
	u64 correct_bitmap = 0;
 | 
						|
	u32 bitflips = 0;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = meson_nfc_read_page_sub(chip, page, false);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	if (oob_required)
 | 
						|
		meson_nfc_get_user_byte(chip, chip->oob_poi);
 | 
						|
 | 
						|
	ret = meson_nfc_ecc_correct(chip, &bitflips, &correct_bitmap);
 | 
						|
 | 
						|
	if (ret == ECC_CHECK_RETURN_FF) {
 | 
						|
		if (buf)
 | 
						|
			memset(buf, 0xff, mtd->writesize);
 | 
						|
 | 
						|
		if (oob_required)
 | 
						|
			memset(chip->oob_poi, 0xff, mtd->oobsize);
 | 
						|
	} else if (ret < 0) {
 | 
						|
		struct nand_ecc_ctrl *ecc;
 | 
						|
		int i;
 | 
						|
 | 
						|
		if ((chip->options & NAND_NEED_SCRAMBLING) || !buf) {
 | 
						|
			mtd->ecc_stats.failed++;
 | 
						|
			return bitflips;
 | 
						|
		}
 | 
						|
 | 
						|
		chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
 | 
						|
 | 
						|
		ret = meson_nfc_read_page_raw(mtd, chip, buf, 1, page);
 | 
						|
		if (ret)
 | 
						|
			return ret;
 | 
						|
 | 
						|
		ecc = &chip->ecc;
 | 
						|
 | 
						|
		for (i = 0; i < chip->ecc.steps ; i++) {
 | 
						|
			u8 *data = buf + i * ecc->size;
 | 
						|
			u8 *oob = chip->oob_poi + i * (ecc->bytes + 2);
 | 
						|
 | 
						|
			if (correct_bitmap & BIT_ULL(i))
 | 
						|
				continue;
 | 
						|
 | 
						|
			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
 | 
						|
							  oob, ecc->bytes + 2,
 | 
						|
							  NULL, 0,
 | 
						|
							  ecc->strength);
 | 
						|
			if (ret < 0) {
 | 
						|
				mtd->ecc_stats.failed++;
 | 
						|
			} else {
 | 
						|
				mtd->ecc_stats.corrected += ret;
 | 
						|
				bitflips =  max_t(u32, bitflips, ret);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else if (buf && buf != meson_chip->data_buf) {
 | 
						|
		memcpy(buf, meson_chip->data_buf, mtd->writesize);
 | 
						|
	}
 | 
						|
 | 
						|
	return bitflips;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
				  int page)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = nand_read_page_op(chip, page, 0, NULL, 0);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return meson_nfc_read_page_raw(mtd, chip, NULL, 1, page);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
			      int page)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = nand_read_page_op(chip, page, 0, NULL, 0);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return meson_nfc_read_page_hwecc(mtd, chip, NULL, 1, page);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
				   int page)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	ret = meson_nfc_write_page_raw(mtd, chip, NULL, 1, page);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return nand_prog_page_end_op(chip);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
 | 
						|
			       int page)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	ret = meson_nfc_write_page_hwecc(mtd, chip, NULL, 1, page);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return nand_prog_page_end_op(chip);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
 | 
						|
					int column, int page_addr)
 | 
						|
{
 | 
						|
	struct nand_chip *chip = mtd_to_nand(mtd);
 | 
						|
 | 
						|
	chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
 | 
						|
 | 
						|
	if (column != -1 || page_addr != -1) {
 | 
						|
		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
 | 
						|
 | 
						|
		/* Serially input address */
 | 
						|
		if (column != -1) {
 | 
						|
			/* Adjust columns for 16 bit buswidth */
 | 
						|
			if (chip->options & NAND_BUSWIDTH_16 &&
 | 
						|
			    !nand_opcode_8bits(command))
 | 
						|
				column >>= 1;
 | 
						|
 | 
						|
			chip->cmd_ctrl(mtd, column, ctrl);
 | 
						|
			ctrl &= ~NAND_CTRL_CHANGE;
 | 
						|
			/* Only output a single addr cycle for 8bits
 | 
						|
			 * opcodes.
 | 
						|
			 */
 | 
						|
			if (!nand_opcode_8bits(command))
 | 
						|
				chip->cmd_ctrl(mtd, column >> 8, ctrl);
 | 
						|
		}
 | 
						|
 | 
						|
		if (page_addr != -1) {
 | 
						|
			chip->cmd_ctrl(mtd, page_addr, ctrl);
 | 
						|
			chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE |
 | 
						|
							    NAND_ALE);
 | 
						|
			/* One more address cycle for devices > 128MiB */
 | 
						|
			if (chip->chipsize > SZ_128M)
 | 
						|
				chip->cmd_ctrl(mtd, page_addr >> 16,
 | 
						|
					       NAND_NCE | NAND_ALE);
 | 
						|
		}
 | 
						|
 | 
						|
		switch (command) {
 | 
						|
		case NAND_CMD_READ0:
 | 
						|
			chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
 | 
						|
				       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
 | 
						|
			fallthrough;
 | 
						|
		case NAND_CMD_PARAM:
 | 
						|
			nand_wait_ready(mtd);
 | 
						|
			nand_exit_status_op(chip);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 | 
						|
{
 | 
						|
	struct nand_chip *nand = mtd_to_nand(mtd);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
 | 
						|
	if (cmd == NAND_CMD_NONE)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (ctrl & NAND_CLE)
 | 
						|
		cmd = NFC_CMD_MAKE_CLE(nfc->param.chip_select, cmd);
 | 
						|
	else
 | 
						|
		cmd = NFC_CMD_MAKE_ALE(nfc->param.chip_select, cmd);
 | 
						|
 | 
						|
	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_wait_cmd_fifo(struct meson_nfc *nfc)
 | 
						|
{
 | 
						|
	while ((NFC_GET_CMD(nfc) >> 22) & GENMASK(4, 0))
 | 
						|
		;
 | 
						|
}
 | 
						|
 | 
						|
static u8 meson_nfc_nand_read_byte(struct mtd_info *mtd)
 | 
						|
{
 | 
						|
	struct nand_chip *nand = mtd_to_nand(mtd);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
 | 
						|
	writel(NFC_CMD_MAKE_DRD(nfc->param.chip_select, 0), nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
 | 
						|
	meson_nfc_cmd_idle(nfc, 0);
 | 
						|
	meson_nfc_cmd_idle(nfc, 0);
 | 
						|
 | 
						|
	meson_nfc_wait_cmd_fifo(nfc);
 | 
						|
 | 
						|
	return readl(nfc->reg_base + NFC_REG_BUF);
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_nand_write_byte(struct mtd_info *mtd, u8 val)
 | 
						|
{
 | 
						|
	struct nand_chip *nand = mtd_to_nand(mtd);
 | 
						|
	struct meson_nfc *nfc = nand_get_controller_data(nand);
 | 
						|
 | 
						|
	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
 | 
						|
 | 
						|
	writel(NFC_CMD_MAKE_DWR(nfc->param.chip_select, val), nfc->reg_base + NFC_REG_CMD);
 | 
						|
 | 
						|
	meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
 | 
						|
	meson_nfc_cmd_idle(nfc, 0);
 | 
						|
	meson_nfc_cmd_idle(nfc, 0);
 | 
						|
 | 
						|
	meson_nfc_wait_cmd_fifo(nfc);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_dev_ready(struct mtd_info *mtd)
 | 
						|
{
 | 
						|
	struct nand_chip *chip = mtd_to_nand(mtd);
 | 
						|
	unsigned int time_out_cnt = 0;
 | 
						|
 | 
						|
	chip->select_chip(mtd, 0);
 | 
						|
 | 
						|
	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
 | 
						|
 | 
						|
	do {
 | 
						|
		int status;
 | 
						|
 | 
						|
		status = (int)chip->read_byte(mtd);
 | 
						|
		if (status & NAND_STATUS_READY)
 | 
						|
			break;
 | 
						|
	} while (time_out_cnt++ < NFC_DEV_READY_TICK_MAX);
 | 
						|
 | 
						|
	return time_out_cnt != NFC_DEV_READY_TICK_MAX;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_chip_buffer_init(struct nand_chip *nand)
 | 
						|
{
 | 
						|
	const struct mtd_info *mtd = nand_to_mtd(nand);
 | 
						|
	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
	u32 page_bytes, info_bytes, nsectors;
 | 
						|
	unsigned long tmp_addr;
 | 
						|
 | 
						|
	nsectors = mtd->writesize / nand->ecc.size;
 | 
						|
 | 
						|
	page_bytes =  mtd->writesize + mtd->oobsize;
 | 
						|
	info_bytes = nsectors * PER_INFO_BYTE;
 | 
						|
 | 
						|
	meson_chip->data_buf = dma_alloc_coherent(page_bytes, &tmp_addr);
 | 
						|
	if (!meson_chip->data_buf)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	meson_chip->info_buf = dma_alloc_coherent(info_bytes, &tmp_addr);
 | 
						|
	if (!meson_chip->info_buf) {
 | 
						|
		dma_free_coherent(meson_chip->data_buf);
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const int axg_stepinfo_strengths[] = { 8 };
 | 
						|
static const struct nand_ecc_step_info axg_stepinfo_1024 = {
 | 
						|
	.stepsize = 1024,
 | 
						|
	.strengths = axg_stepinfo_strengths,
 | 
						|
	.nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
 | 
						|
};
 | 
						|
 | 
						|
static const struct nand_ecc_step_info axg_stepinfo_512 = {
 | 
						|
	.stepsize = 512,
 | 
						|
	.strengths = axg_stepinfo_strengths,
 | 
						|
	.nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
 | 
						|
};
 | 
						|
 | 
						|
static const struct nand_ecc_step_info axg_stepinfo[] = { axg_stepinfo_1024, axg_stepinfo_512 };
 | 
						|
 | 
						|
static const struct nand_ecc_caps meson_axg_ecc_caps = {
 | 
						|
	.stepinfos = axg_stepinfo,
 | 
						|
	.nstepinfos = ARRAY_SIZE(axg_stepinfo),
 | 
						|
	.calc_ecc_bytes = meson_nand_calc_ecc_bytes,
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * OOB layout:
 | 
						|
 *
 | 
						|
 * For ECC with 512 bytes step size:
 | 
						|
 * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB
 | 
						|
 * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC
 | 
						|
 * 0x20:
 | 
						|
 * 0x30:
 | 
						|
 *
 | 
						|
 * For ECC with 1024 bytes step size:
 | 
						|
 * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB
 | 
						|
 * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC
 | 
						|
 * 0x20: AA AA DD DD DD DD DD DD DD DD DD DD DD DD DD DD
 | 
						|
 * 0x30: AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE
 | 
						|
 *
 | 
						|
 * AA - user bytes.
 | 
						|
 * BB, CC, DD, EE - ECC code bytes for each step.
 | 
						|
 */
 | 
						|
static struct nand_ecclayout nand_oob;
 | 
						|
 | 
						|
static void meson_nfc_init_nand_oob(struct nand_chip *nand)
 | 
						|
{
 | 
						|
	int section_size = 2 + nand->ecc.bytes;
 | 
						|
	int i;
 | 
						|
	int k;
 | 
						|
 | 
						|
	nand_oob.eccbytes = nand->ecc.steps * nand->ecc.bytes;
 | 
						|
	k = 0;
 | 
						|
 | 
						|
	for (i = 0; i < nand->ecc.steps; i++) {
 | 
						|
		int j;
 | 
						|
 | 
						|
		for (j = 0; j < nand->ecc.bytes; j++)
 | 
						|
			nand_oob.eccpos[k++] = (i * section_size) + 2 + j;
 | 
						|
 | 
						|
		nand_oob.oobfree[i].offset = (i * section_size);
 | 
						|
		nand_oob.oobfree[i].length = 2;
 | 
						|
	}
 | 
						|
 | 
						|
	nand_oob.oobavail = 2 * nand->ecc.steps;
 | 
						|
	nand->ecc.layout = &nand_oob;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_init_ecc(struct nand_chip *nand, ofnode node)
 | 
						|
{
 | 
						|
	const struct mtd_info *mtd = nand_to_mtd(nand);
 | 
						|
	int ret;
 | 
						|
	int i;
 | 
						|
 | 
						|
	ret = nand_check_ecc_caps(nand, &meson_axg_ecc_caps, mtd->oobsize - 2);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) {
 | 
						|
		if (meson_ecc[i].strength == nand->ecc.strength &&
 | 
						|
		    meson_ecc[i].size == nand->ecc.size) {
 | 
						|
			struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 | 
						|
 | 
						|
			nand->ecc.steps = mtd->writesize / nand->ecc.size;
 | 
						|
			meson_chip->bch_mode = meson_ecc[i].bch;
 | 
						|
 | 
						|
			meson_nfc_init_nand_oob(nand);
 | 
						|
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return -EINVAL;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_nand_chip_init(struct udevice *dev, struct meson_nfc *nfc,
 | 
						|
				    ofnode node)
 | 
						|
{
 | 
						|
	struct meson_nfc_nand_chip *meson_chip;
 | 
						|
	struct nand_chip *nand;
 | 
						|
	struct mtd_info *mtd;
 | 
						|
	u32 cs[MAX_CE_NUM];
 | 
						|
	u32 nsels;
 | 
						|
	int ret;
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (!ofnode_get_property(node, "reg", &nsels)) {
 | 
						|
		dev_err(dev, "\"reg\" property is not found\n");
 | 
						|
		return -ENODEV;
 | 
						|
	}
 | 
						|
 | 
						|
	nsels /= sizeof(u32);
 | 
						|
	if (nsels >= MAX_CE_NUM) {
 | 
						|
		dev_err(dev, "invalid size of CS array, max is %d\n",
 | 
						|
			MAX_CE_NUM);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = ofnode_read_u32_array(node, "reg", cs, nsels);
 | 
						|
	if (ret < 0) {
 | 
						|
		dev_err(dev, "failed to read \"reg\" property\n");
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < nsels; i++) {
 | 
						|
		if (test_and_set_bit(cs[i], &nfc->assigned_cs)) {
 | 
						|
			dev_err(dev, "CS %d already assigned\n", cs[i]);
 | 
						|
			return -EINVAL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	meson_chip = malloc(sizeof(*meson_chip) + nsels * sizeof(meson_chip->sels[0]));
 | 
						|
	if (!meson_chip) {
 | 
						|
		dev_err(dev, "failed to allocate memory for chip\n");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	meson_chip->nsels = nsels;
 | 
						|
	nand = &meson_chip->nand;
 | 
						|
 | 
						|
	nand->flash_node = node;
 | 
						|
	nand_set_controller_data(nand, nfc);
 | 
						|
	/* Set the driver entry points for MTD */
 | 
						|
	nand->cmdfunc = meson_nfc_nand_cmd_function;
 | 
						|
	nand->cmd_ctrl = meson_nfc_cmd_ctrl;
 | 
						|
	nand->select_chip = meson_nfc_nand_select_chip;
 | 
						|
	nand->read_byte = meson_nfc_nand_read_byte;
 | 
						|
	nand->write_byte = meson_nfc_nand_write_byte;
 | 
						|
	nand->dev_ready = meson_nfc_dev_ready;
 | 
						|
 | 
						|
	/* Buffer read/write routines */
 | 
						|
	nand->read_buf = meson_nfc_read_buf;
 | 
						|
	nand->write_buf = meson_nfc_write_buf;
 | 
						|
	nand->options |= NAND_NO_SUBPAGE_WRITE;
 | 
						|
 | 
						|
	nand->ecc.mode = NAND_ECC_HW;
 | 
						|
	nand->ecc.hwctl = NULL;
 | 
						|
	nand->ecc.read_page = meson_nfc_read_page_hwecc;
 | 
						|
	nand->ecc.write_page = meson_nfc_write_page_hwecc;
 | 
						|
	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
 | 
						|
	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
 | 
						|
 | 
						|
	nand->ecc.read_oob = meson_nfc_read_oob;
 | 
						|
	nand->ecc.write_oob = meson_nfc_write_oob;
 | 
						|
	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
 | 
						|
	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
 | 
						|
 | 
						|
	nand->ecc.algo = NAND_ECC_BCH;
 | 
						|
 | 
						|
	mtd = nand_to_mtd(nand);
 | 
						|
 | 
						|
	ret = nand_scan_ident(mtd, 1, NULL);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "'nand_scan_ident()' failed: %d\n", ret);
 | 
						|
		goto err_chip_free;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = meson_nfc_init_ecc(nand, node);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "failed to init ECC settings: %d\n", ret);
 | 
						|
		goto err_chip_free;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = meson_chip_buffer_init(nand);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "failed to init DMA buffers: %d\n", ret);
 | 
						|
		goto err_chip_free;
 | 
						|
	}
 | 
						|
 | 
						|
	/* 'nand_scan_tail()' needs ECC parameters to be already
 | 
						|
	 * set and correct.
 | 
						|
	 */
 | 
						|
	ret = nand_scan_tail(mtd);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "'nand_scan_tail()' failed: %d\n", ret);
 | 
						|
		goto err_chip_buf_free;
 | 
						|
	}
 | 
						|
 | 
						|
	if (nand->options & NAND_IS_BOOT_MEDIUM) {
 | 
						|
		ret = ofnode_read_u32(node, "amlogic,boot-pages",
 | 
						|
				      &meson_chip->boot_pages);
 | 
						|
		if (ret) {
 | 
						|
			dev_err(dev, "could not retrieve 'amlogic,boot-pages' property: %d",
 | 
						|
				ret);
 | 
						|
			goto err_chip_buf_free;
 | 
						|
		}
 | 
						|
 | 
						|
		ret = ofnode_read_u32(node, "amlogic,boot-page-step",
 | 
						|
				      &meson_chip->boot_page_step);
 | 
						|
		if (ret) {
 | 
						|
			dev_err(dev, "could not retrieve 'amlogic,boot-page-step' property: %d",
 | 
						|
				ret);
 | 
						|
			goto err_chip_buf_free;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ret = nand_register(0, mtd);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(dev, "'nand_register()' failed: %d\n", ret);
 | 
						|
		goto err_chip_buf_free;
 | 
						|
	}
 | 
						|
 | 
						|
	list_add_tail(&meson_chip->node, &nfc->chips);
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
err_chip_buf_free:
 | 
						|
	dma_free_coherent(meson_chip->info_buf);
 | 
						|
	dma_free_coherent(meson_chip->data_buf);
 | 
						|
 | 
						|
err_chip_free:
 | 
						|
	free(meson_chip);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int meson_nfc_nand_chips_init(struct udevice *dev,
 | 
						|
				     struct meson_nfc *nfc)
 | 
						|
{
 | 
						|
	ofnode parent = dev_ofnode(dev);
 | 
						|
	ofnode node;
 | 
						|
 | 
						|
	ofnode_for_each_subnode(node, parent) {
 | 
						|
		int ret = meson_nfc_nand_chip_init(dev, nfc, node);
 | 
						|
 | 
						|
		if (ret)
 | 
						|
			return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void meson_nfc_clk_init(struct meson_nfc *nfc)
 | 
						|
{
 | 
						|
	u32 bus_cycle = NFC_DEFAULT_BUS_CYCLE;
 | 
						|
	u32 bus_timing = NFC_DEFAULT_BUS_TIMING;
 | 
						|
	u32 bus_cfg_val;
 | 
						|
 | 
						|
	writel(CLK_ALWAYS_ON_NAND | CLK_SELECT_NAND | CLK_ENABLE_VALUE, nfc->reg_clk);
 | 
						|
	writel(0, nfc->reg_base + NFC_REG_CFG);
 | 
						|
 | 
						|
	bus_cfg_val = (((bus_cycle - 1) & 31) | ((bus_timing & 31) << 5));
 | 
						|
	writel(bus_cfg_val, nfc->reg_base + NFC_REG_CFG);
 | 
						|
	writel(BIT(31), nfc->reg_base + NFC_REG_CMD);
 | 
						|
}
 | 
						|
 | 
						|
static int meson_probe(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct meson_nfc *nfc = dev_get_priv(dev);
 | 
						|
	void *addr;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	addr = dev_read_addr_ptr(dev);
 | 
						|
	if (!addr) {
 | 
						|
		dev_err(dev, "base register address not found\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	nfc->reg_base = addr;
 | 
						|
 | 
						|
	addr = dev_read_addr_index_ptr(dev, 1);
 | 
						|
	if (!addr) {
 | 
						|
		dev_err(dev, "clk register address not found\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	nfc->reg_clk = addr;
 | 
						|
	nfc->dev = dev;
 | 
						|
 | 
						|
	meson_nfc_clk_init(nfc);
 | 
						|
 | 
						|
	ret = meson_nfc_nand_chips_init(dev, nfc);
 | 
						|
	if (ret) {
 | 
						|
		dev_err(nfc->dev, "failed to init chips\n");
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct udevice_id meson_nand_dt_ids[] = {
 | 
						|
	{.compatible = "amlogic,meson-axg-nfc",},
 | 
						|
	{ /* sentinel */ }
 | 
						|
};
 | 
						|
 | 
						|
U_BOOT_DRIVER(meson_nand) = {
 | 
						|
	.name = "meson_nand",
 | 
						|
	.id = UCLASS_MTD,
 | 
						|
	.of_match = meson_nand_dt_ids,
 | 
						|
	.probe = meson_probe,
 | 
						|
	.priv_auto = sizeof(struct meson_nfc),
 | 
						|
};
 | 
						|
 | 
						|
void board_nand_init(void)
 | 
						|
{
 | 
						|
	struct udevice *dev;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = uclass_get_device_by_driver(UCLASS_MTD,
 | 
						|
					  DM_DRIVER_GET(meson_nand), &dev);
 | 
						|
 | 
						|
	if (ret && ret != -ENODEV)
 | 
						|
		pr_err("Failed to initialize: %d\n", ret);
 | 
						|
}
 |