| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556 |
- From 8d94833f13ccd7e1dfea605cfdf9a8eb53505515 Mon Sep 17 00:00:00 2001
- From: Weijie Gao <[email protected]>
- Date: Fri, 20 May 2022 11:23:47 +0800
- Subject: [PATCH 20/25] nand: raw: add support for MediaTek MT7621 SoC
- This patch adds NAND flash controller driver for MediaTek MT7621 SoC.
- The NAND flash controller of MT7621 supports only SLC NAND flashes.
- It supports 4~12 bits correction with maximum 4KB page size.
- Signed-off-by: Weijie Gao <[email protected]>
- ---
- drivers/mtd/nand/raw/Kconfig | 17 +-
- drivers/mtd/nand/raw/Makefile | 2 +
- drivers/mtd/nand/raw/mt7621_nand.c | 1205 ++++++++++++++++++++++++
- drivers/mtd/nand/raw/mt7621_nand.h | 29 +
- drivers/mtd/nand/raw/mt7621_nand_spl.c | 237 +++++
- 5 files changed, 1488 insertions(+), 2 deletions(-)
- create mode 100644 drivers/mtd/nand/raw/mt7621_nand.c
- create mode 100644 drivers/mtd/nand/raw/mt7621_nand.h
- create mode 100644 drivers/mtd/nand/raw/mt7621_nand_spl.c
- --- a/drivers/mtd/nand/raw/Kconfig
- +++ b/drivers/mtd/nand/raw/Kconfig
- @@ -521,12 +521,25 @@ config TEGRA_NAND
- help
- Enables support for NAND Flash chips on Tegra SoCs platforms.
-
- +config NAND_MT7621
- + bool "Support for MediaTek MT7621 NAND flash controller"
- + depends on SOC_MT7621
- + select SYS_NAND_SELF_INIT
- + select SPL_SYS_NAND_SELF_INIT
- + imply CMD_NAND
- + help
- + This enables NAND driver for the NAND flash controller on MediaTek
- + MT7621 platform.
- + The controller supports 4~12 bits correction per 512 bytes with a
- + maximum 4KB page size.
- +
- comment "Generic NAND options"
-
- config SYS_NAND_BLOCK_SIZE
- hex "NAND chip eraseblock size"
- depends on ARCH_SUNXI || SPL_NAND_SUPPORT || TPL_NAND_SUPPORT
- - depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_FSL_IFC
- + depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && \
- + !NAND_FSL_IFC && !NAND_MT7621
- help
- Number of data bytes in one eraseblock for the NAND chip on the
- board. This is the multiple of NAND_PAGE_SIZE and the number of
- @@ -551,7 +564,7 @@ config SYS_NAND_PAGE_SIZE
- depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \
- SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \
- (NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER
- - depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC
- + depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621
- help
- Number of data bytes in one page for the NAND chip on the
- board, not including the OOB area.
- --- a/drivers/mtd/nand/raw/Makefile
- +++ b/drivers/mtd/nand/raw/Makefile
- @@ -72,6 +72,7 @@ obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
- obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.o
- obj-$(CONFIG_CORTINA_NAND) += cortina_nand.o
- obj-$(CONFIG_ROCKCHIP_NAND) += rockchip_nfc.o
- +obj-$(CONFIG_NAND_MT7621) += mt7621_nand.o
-
- else # minimal SPL drivers
-
- @@ -80,5 +81,6 @@ obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_sp
- obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
- obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
- obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
- +obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
-
- endif # drivers
- --- /dev/null
- +++ b/drivers/mtd/nand/raw/mt7621_nand.c
- @@ -0,0 +1,1205 @@
- +// SPDX-License-Identifier: GPL-2.0
- +/*
- + * Copyright (C) 2022 MediaTek Inc. All rights reserved.
- + *
- + * Author: Weijie Gao <[email protected]>
- + */
- +
- +#include <log.h>
- +#include <nand.h>
- +#include <malloc.h>
- +#include <asm/addrspace.h>
- +#include <linux/io.h>
- +#include <linux/iopoll.h>
- +#include <linux/sizes.h>
- +#include <linux/bitops.h>
- +#include <linux/bitfield.h>
- +#include "mt7621_nand.h"
- +
- +/* NFI core registers */
- +#define NFI_CNFG 0x000
- +#define CNFG_OP_MODE GENMASK(14, 12)
- +#define CNFG_OP_CUSTOM 6
- +#define CNFG_AUTO_FMT_EN BIT(9)
- +#define CNFG_HW_ECC_EN BIT(8)
- +#define CNFG_BYTE_RW BIT(6)
- +#define CNFG_READ_MODE BIT(1)
- +
- +#define NFI_PAGEFMT 0x004
- +#define PAGEFMT_FDM_ECC GENMASK(15, 12)
- +#define PAGEFMT_FDM GENMASK(11, 8)
- +#define PAGEFMT_SPARE GENMASK(5, 4)
- +#define PAGEFMT_PAGE GENMASK(1, 0)
- +
- +#define NFI_CON 0x008
- +#define CON_NFI_SEC GENMASK(15, 12)
- +#define CON_NFI_BWR BIT(9)
- +#define CON_NFI_BRD BIT(8)
- +#define CON_NFI_RST BIT(1)
- +#define CON_FIFO_FLUSH BIT(0)
- +
- +#define NFI_ACCCON 0x00c
- +#define ACCCON_POECS GENMASK(31, 28)
- +#define ACCCON_POECS_DEF 3
- +#define ACCCON_PRECS GENMASK(27, 22)
- +#define ACCCON_PRECS_DEF 3
- +#define ACCCON_C2R GENMASK(21, 16)
- +#define ACCCON_C2R_DEF 7
- +#define ACCCON_W2R GENMASK(15, 12)
- +#define ACCCON_W2R_DEF 7
- +#define ACCCON_WH GENMASK(11, 8)
- +#define ACCCON_WH_DEF 15
- +#define ACCCON_WST GENMASK(7, 4)
- +#define ACCCON_WST_DEF 15
- +#define ACCCON_WST_MIN 3
- +#define ACCCON_RLT GENMASK(3, 0)
- +#define ACCCON_RLT_DEF 15
- +#define ACCCON_RLT_MIN 3
- +
- +#define NFI_CMD 0x020
- +
- +#define NFI_ADDRNOB 0x030
- +#define ADDR_ROW_NOB GENMASK(6, 4)
- +#define ADDR_COL_NOB GENMASK(2, 0)
- +
- +#define NFI_COLADDR 0x034
- +#define NFI_ROWADDR 0x038
- +
- +#define NFI_STRDATA 0x040
- +#define STR_DATA BIT(0)
- +
- +#define NFI_CNRNB 0x044
- +#define CB2R_TIME GENMASK(7, 4)
- +#define STR_CNRNB BIT(0)
- +
- +#define NFI_DATAW 0x050
- +#define NFI_DATAR 0x054
- +
- +#define NFI_PIO_DIRDY 0x058
- +#define PIO_DIRDY BIT(0)
- +
- +#define NFI_STA 0x060
- +#define STA_NFI_FSM GENMASK(19, 16)
- +#define STA_FSM_CUSTOM_DATA 14
- +#define STA_BUSY BIT(8)
- +#define STA_ADDR BIT(1)
- +#define STA_CMD BIT(0)
- +
- +#define NFI_ADDRCNTR 0x070
- +#define SEC_CNTR GENMASK(15, 12)
- +#define SEC_ADDR GENMASK(9, 0)
- +
- +#define NFI_CSEL 0x090
- +#define CSEL GENMASK(1, 0)
- +
- +#define NFI_FDM0L 0x0a0
- +#define NFI_FDML(n) (0x0a0 + ((n) << 3))
- +
- +#define NFI_FDM0M 0x0a4
- +#define NFI_FDMM(n) (0x0a4 + ((n) << 3))
- +
- +#define NFI_MASTER_STA 0x210
- +#define MAS_ADDR GENMASK(11, 9)
- +#define MAS_RD GENMASK(8, 6)
- +#define MAS_WR GENMASK(5, 3)
- +#define MAS_RDDLY GENMASK(2, 0)
- +
- +/* ECC engine registers */
- +#define ECC_ENCCON 0x000
- +#define ENC_EN BIT(0)
- +
- +#define ECC_ENCCNFG 0x004
- +#define ENC_CNFG_MSG GENMASK(28, 16)
- +#define ENC_MODE GENMASK(5, 4)
- +#define ENC_MODE_NFI 1
- +#define ENC_TNUM GENMASK(2, 0)
- +
- +#define ECC_ENCIDLE 0x00c
- +#define ENC_IDLE BIT(0)
- +
- +#define ECC_DECCON 0x100
- +#define DEC_EN BIT(0)
- +
- +#define ECC_DECCNFG 0x104
- +#define DEC_EMPTY_EN BIT(31)
- +#define DEC_CS GENMASK(28, 16)
- +#define DEC_CON GENMASK(13, 12)
- +#define DEC_CON_EL 2
- +#define DEC_MODE GENMASK(5, 4)
- +#define DEC_MODE_NFI 1
- +#define DEC_TNUM GENMASK(2, 0)
- +
- +#define ECC_DECIDLE 0x10c
- +#define DEC_IDLE BIT(1)
- +
- +#define ECC_DECENUM 0x114
- +#define ERRNUM_S 2
- +#define ERRNUM_M GENMASK(3, 0)
- +
- +#define ECC_DECDONE 0x118
- +#define DEC_DONE7 BIT(7)
- +#define DEC_DONE6 BIT(6)
- +#define DEC_DONE5 BIT(5)
- +#define DEC_DONE4 BIT(4)
- +#define DEC_DONE3 BIT(3)
- +#define DEC_DONE2 BIT(2)
- +#define DEC_DONE1 BIT(1)
- +#define DEC_DONE0 BIT(0)
- +
- +#define ECC_DECEL(n) (0x11c + (n) * 4)
- +#define DEC_EL_ODD_S 16
- +#define DEC_EL_M 0x1fff
- +#define DEC_EL_BYTE_POS_S 3
- +#define DEC_EL_BIT_POS_M GENMASK(2, 0)
- +
- +#define ECC_FDMADDR 0x13c
- +
- +/* ENCIDLE and DECIDLE */
- +#define ECC_IDLE BIT(0)
- +
- +#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
- + (FIELD_PREP(ACCCON_POECS, tpoecs) | \
- + FIELD_PREP(ACCCON_PRECS, tprecs) | \
- + FIELD_PREP(ACCCON_C2R, tc2r) | \
- + FIELD_PREP(ACCCON_W2R, tw2r) | \
- + FIELD_PREP(ACCCON_WH, twh) | \
- + FIELD_PREP(ACCCON_WST, twst) | \
- + FIELD_PREP(ACCCON_RLT, trlt))
- +
- +#define MASTER_STA_MASK (MAS_ADDR | MAS_RD | MAS_WR | \
- + MAS_RDDLY)
- +#define NFI_RESET_TIMEOUT 1000000
- +#define NFI_CORE_TIMEOUT 500000
- +#define ECC_ENGINE_TIMEOUT 500000
- +
- +#define ECC_SECTOR_SIZE 512
- +#define ECC_PARITY_BITS 13
- +
- +#define NFI_FDM_SIZE 8
- +
- +/* Register base */
- +#define NFI_BASE 0x1e003000
- +#define NFI_ECC_BASE 0x1e003800
- +
- +static struct mt7621_nfc nfc_dev;
- +
- +static const u16 mt7621_nfi_page_size[] = { SZ_512, SZ_2K, SZ_4K };
- +static const u8 mt7621_nfi_spare_size[] = { 16, 26, 27, 28 };
- +static const u8 mt7621_ecc_strength[] = { 4, 6, 8, 10, 12 };
- +
- +static inline u32 nfi_read32(struct mt7621_nfc *nfc, u32 reg)
- +{
- + return readl(nfc->nfi_regs + reg);
- +}
- +
- +static inline void nfi_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
- +{
- + writel(val, nfc->nfi_regs + reg);
- +}
- +
- +static inline u16 nfi_read16(struct mt7621_nfc *nfc, u32 reg)
- +{
- + return readw(nfc->nfi_regs + reg);
- +}
- +
- +static inline void nfi_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
- +{
- + writew(val, nfc->nfi_regs + reg);
- +}
- +
- +static inline void ecc_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
- +{
- + writew(val, nfc->ecc_regs + reg);
- +}
- +
- +static inline u32 ecc_read32(struct mt7621_nfc *nfc, u32 reg)
- +{
- + return readl(nfc->ecc_regs + reg);
- +}
- +
- +static inline void ecc_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
- +{
- + return writel(val, nfc->ecc_regs + reg);
- +}
- +
- +static inline u8 *oob_fdm_ptr(struct nand_chip *nand, int sect)
- +{
- + return nand->oob_poi + sect * NFI_FDM_SIZE;
- +}
- +
- +static inline u8 *oob_ecc_ptr(struct mt7621_nfc *nfc, int sect)
- +{
- + struct nand_chip *nand = &nfc->nand;
- +
- + return nand->oob_poi + nand->ecc.steps * NFI_FDM_SIZE +
- + sect * (nfc->spare_per_sector - NFI_FDM_SIZE);
- +}
- +
- +static inline u8 *page_data_ptr(struct nand_chip *nand, const u8 *buf,
- + int sect)
- +{
- + return (u8 *)buf + sect * nand->ecc.size;
- +}
- +
- +static int mt7621_ecc_wait_idle(struct mt7621_nfc *nfc, u32 reg)
- +{
- + u32 val;
- + int ret;
- +
- + ret = readw_poll_timeout(nfc->ecc_regs + reg, val, val & ECC_IDLE,
- + ECC_ENGINE_TIMEOUT);
- + if (ret) {
- + pr_warn("ECC engine timed out entering idle mode\n");
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +
- +static int mt7621_ecc_decoder_wait_done(struct mt7621_nfc *nfc, u32 sect)
- +{
- + u32 val;
- + int ret;
- +
- + ret = readw_poll_timeout(nfc->ecc_regs + ECC_DECDONE, val,
- + val & (1 << sect), ECC_ENGINE_TIMEOUT);
- + if (ret) {
- + pr_warn("ECC decoder for sector %d timed out\n", sect);
- + return -ETIMEDOUT;
- + }
- +
- + return 0;
- +}
- +
- +static void mt7621_ecc_encoder_op(struct mt7621_nfc *nfc, bool enable)
- +{
- + mt7621_ecc_wait_idle(nfc, ECC_ENCIDLE);
- + ecc_write16(nfc, ECC_ENCCON, enable ? ENC_EN : 0);
- +}
- +
- +static void mt7621_ecc_decoder_op(struct mt7621_nfc *nfc, bool enable)
- +{
- + mt7621_ecc_wait_idle(nfc, ECC_DECIDLE);
- + ecc_write16(nfc, ECC_DECCON, enable ? DEC_EN : 0);
- +}
- +
- +static int mt7621_ecc_correct_check(struct mt7621_nfc *nfc, u8 *sector_buf,
- + u8 *fdm_buf, u32 sect)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + u32 decnum, num_error_bits, fdm_end_bits;
- + u32 error_locations, error_bit_loc;
- + u32 error_byte_pos, error_bit_pos;
- + int bitflips = 0;
- + u32 i;
- +
- + decnum = ecc_read32(nfc, ECC_DECENUM);
- + num_error_bits = (decnum >> (sect << ERRNUM_S)) & ERRNUM_M;
- + fdm_end_bits = (nand->ecc.size + NFI_FDM_SIZE) << 3;
- +
- + if (!num_error_bits)
- + return 0;
- +
- + if (num_error_bits == ERRNUM_M)
- + return -1;
- +
- + for (i = 0; i < num_error_bits; i++) {
- + error_locations = ecc_read32(nfc, ECC_DECEL(i / 2));
- + error_bit_loc = (error_locations >> ((i % 2) * DEC_EL_ODD_S)) &
- + DEC_EL_M;
- + error_byte_pos = error_bit_loc >> DEC_EL_BYTE_POS_S;
- + error_bit_pos = error_bit_loc & DEC_EL_BIT_POS_M;
- +
- + if (error_bit_loc < (nand->ecc.size << 3)) {
- + if (sector_buf) {
- + sector_buf[error_byte_pos] ^=
- + (1 << error_bit_pos);
- + }
- + } else if (error_bit_loc < fdm_end_bits) {
- + if (fdm_buf) {
- + fdm_buf[error_byte_pos - nand->ecc.size] ^=
- + (1 << error_bit_pos);
- + }
- + }
- +
- + bitflips++;
- + }
- +
- + return bitflips;
- +}
- +
- +static int mt7621_nfc_wait_write_completion(struct mt7621_nfc *nfc,
- + struct nand_chip *nand)
- +{
- + u16 val;
- + int ret;
- +
- + ret = readw_poll_timeout(nfc->nfi_regs + NFI_ADDRCNTR, val,
- + FIELD_GET(SEC_CNTR, val) >= nand->ecc.steps,
- + NFI_CORE_TIMEOUT);
- +
- + if (ret) {
- + pr_warn("NFI core write operation timed out\n");
- + return -ETIMEDOUT;
- + }
- +
- + return ret;
- +}
- +
- +static void mt7621_nfc_hw_reset(struct mt7621_nfc *nfc)
- +{
- + u32 val;
- + int ret;
- +
- + /* reset all registers and force the NFI master to terminate */
- + nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
- +
- + /* wait for the master to finish the last transaction */
- + ret = readw_poll_timeout(nfc->nfi_regs + NFI_MASTER_STA, val,
- + !(val & MASTER_STA_MASK), NFI_RESET_TIMEOUT);
- + if (ret) {
- + pr_warn("Failed to reset NFI master in %dms\n",
- + NFI_RESET_TIMEOUT);
- + }
- +
- + /* ensure any status register affected by the NFI master is reset */
- + nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
- + nfi_write16(nfc, NFI_STRDATA, 0);
- +}
- +
- +static inline void mt7621_nfc_hw_init(struct mt7621_nfc *nfc)
- +{
- + u32 acccon;
- +
- + /*
- + * CNRNB: nand ready/busy register
- + * -------------------------------
- + * 7:4: timeout register for polling the NAND busy/ready signal
- + * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles.
- + */
- + nfi_write16(nfc, NFI_CNRNB, CB2R_TIME | STR_CNRNB);
- +
- + mt7621_nfc_hw_reset(nfc);
- +
- + /* Apply default access timing */
- + acccon = ACCTIMING(ACCCON_POECS_DEF, ACCCON_PRECS_DEF, ACCCON_C2R_DEF,
- + ACCCON_W2R_DEF, ACCCON_WH_DEF, ACCCON_WST_DEF,
- + ACCCON_RLT_DEF);
- +
- + nfi_write32(nfc, NFI_ACCCON, acccon);
- +}
- +
- +static int mt7621_nfc_send_command(struct mt7621_nfc *nfc, u8 command)
- +{
- + u32 val;
- + int ret;
- +
- + nfi_write32(nfc, NFI_CMD, command);
- +
- + ret = readl_poll_timeout(nfc->nfi_regs + NFI_STA, val, !(val & STA_CMD),
- + NFI_CORE_TIMEOUT);
- + if (ret) {
- + pr_warn("NFI core timed out entering command mode\n");
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +
- +static int mt7621_nfc_send_address_byte(struct mt7621_nfc *nfc, int addr)
- +{
- + u32 val;
- + int ret;
- +
- + nfi_write32(nfc, NFI_COLADDR, addr);
- + nfi_write32(nfc, NFI_ROWADDR, 0);
- + nfi_write16(nfc, NFI_ADDRNOB, 1);
- +
- + ret = readl_poll_timeout(nfc->nfi_regs + NFI_STA, val,
- + !(val & STA_ADDR), NFI_CORE_TIMEOUT);
- + if (ret) {
- + pr_warn("NFI core timed out entering address mode\n");
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +
- +static void mt7621_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
- + unsigned int ctrl)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + if (ctrl & NAND_ALE) {
- + mt7621_nfc_send_address_byte(nfc, dat & 0xff);
- + } else if (ctrl & NAND_CLE) {
- + mt7621_nfc_hw_reset(nfc);
- + nfi_write16(nfc, NFI_CNFG,
- + FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM));
- + mt7621_nfc_send_command(nfc, dat);
- + }
- +}
- +
- +static int mt7621_nfc_dev_ready(struct mtd_info *mtd)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + if (nfi_read32(nfc, NFI_STA) & STA_BUSY)
- + return 0;
- +
- + return 1;
- +}
- +
- +static void mt7621_nfc_select_chip(struct mtd_info *mtd, int chipnr)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + nfi_write16(nfc, NFI_CSEL, 0);
- +}
- +
- +static void mt7621_nfc_wait_pio_ready(struct mt7621_nfc *nfc)
- +{
- + int ret;
- + u16 val;
- +
- + ret = readw_poll_timeout(nfc->nfi_regs + NFI_PIO_DIRDY, val,
- + val & PIO_DIRDY, NFI_CORE_TIMEOUT);
- + if (ret < 0)
- + pr_err("NFI core PIO mode not ready\n");
- +}
- +
- +static u32 mt7621_nfc_pio_read(struct mt7621_nfc *nfc, bool br)
- +{
- + u32 reg, fsm;
- +
- + /* after each byte read, the NFI_STA reg is reset by the hardware */
- + reg = nfi_read32(nfc, NFI_STA);
- + fsm = FIELD_GET(STA_NFI_FSM, reg);
- +
- + if (fsm != STA_FSM_CUSTOM_DATA) {
- + reg = nfi_read16(nfc, NFI_CNFG);
- + reg |= CNFG_READ_MODE | CNFG_BYTE_RW;
- + if (!br)
- + reg &= ~CNFG_BYTE_RW;
- + nfi_write16(nfc, NFI_CNFG, reg);
- +
- + /*
- + * set to max sector to allow the HW to continue reading over
- + * unaligned accesses
- + */
- + nfi_write16(nfc, NFI_CON, CON_NFI_SEC | CON_NFI_BRD);
- +
- + /* trigger to fetch data */
- + nfi_write16(nfc, NFI_STRDATA, STR_DATA);
- + }
- +
- + mt7621_nfc_wait_pio_ready(nfc);
- +
- + return nfi_read32(nfc, NFI_DATAR);
- +}
- +
- +static void mt7621_nfc_read_data(struct mt7621_nfc *nfc, u8 *buf, u32 len)
- +{
- + while (((uintptr_t)buf & 3) && len) {
- + *buf = mt7621_nfc_pio_read(nfc, true);
- + buf++;
- + len--;
- + }
- +
- + while (len >= 4) {
- + *(u32 *)buf = mt7621_nfc_pio_read(nfc, false);
- + buf += 4;
- + len -= 4;
- + }
- +
- + while (len) {
- + *buf = mt7621_nfc_pio_read(nfc, true);
- + buf++;
- + len--;
- + }
- +}
- +
- +static void mt7621_nfc_read_data_discard(struct mt7621_nfc *nfc, u32 len)
- +{
- + while (len >= 4) {
- + mt7621_nfc_pio_read(nfc, false);
- + len -= 4;
- + }
- +
- + while (len) {
- + mt7621_nfc_pio_read(nfc, true);
- + len--;
- + }
- +}
- +
- +static void mt7621_nfc_pio_write(struct mt7621_nfc *nfc, u32 val, bool bw)
- +{
- + u32 reg, fsm;
- +
- + reg = nfi_read32(nfc, NFI_STA);
- + fsm = FIELD_GET(STA_NFI_FSM, reg);
- +
- + if (fsm != STA_FSM_CUSTOM_DATA) {
- + reg = nfi_read16(nfc, NFI_CNFG);
- + reg &= ~(CNFG_READ_MODE | CNFG_BYTE_RW);
- + if (bw)
- + reg |= CNFG_BYTE_RW;
- + nfi_write16(nfc, NFI_CNFG, reg);
- +
- + nfi_write16(nfc, NFI_CON, CON_NFI_SEC | CON_NFI_BWR);
- + nfi_write16(nfc, NFI_STRDATA, STR_DATA);
- + }
- +
- + mt7621_nfc_wait_pio_ready(nfc);
- + nfi_write32(nfc, NFI_DATAW, val);
- +}
- +
- +static void mt7621_nfc_write_data(struct mt7621_nfc *nfc, const u8 *buf,
- + u32 len)
- +{
- + while (((uintptr_t)buf & 3) && len) {
- + mt7621_nfc_pio_write(nfc, *buf, true);
- + buf++;
- + len--;
- + }
- +
- + while (len >= 4) {
- + mt7621_nfc_pio_write(nfc, *(const u32 *)buf, false);
- + buf += 4;
- + len -= 4;
- + }
- +
- + while (len) {
- + mt7621_nfc_pio_write(nfc, *buf, true);
- + buf++;
- + len--;
- + }
- +}
- +
- +static void mt7621_nfc_write_data_empty(struct mt7621_nfc *nfc, u32 len)
- +{
- + while (len >= 4) {
- + mt7621_nfc_pio_write(nfc, 0xffffffff, false);
- + len -= 4;
- + }
- +
- + while (len) {
- + mt7621_nfc_pio_write(nfc, 0xff, true);
- + len--;
- + }
- +}
- +
- +static void mt7621_nfc_write_byte(struct mtd_info *mtd, u8 byte)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + mt7621_nfc_pio_write(nfc, byte, true);
- +}
- +
- +static void mt7621_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + return mt7621_nfc_write_data(nfc, buf, len);
- +}
- +
- +static u8 mt7621_nfc_read_byte(struct mtd_info *mtd)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + return mt7621_nfc_pio_read(nfc, true);
- +}
- +
- +static void mt7621_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
- +
- + mt7621_nfc_read_data(nfc, buf, len);
- +}
- +
- +static int mt7621_nfc_calc_ecc_strength(struct mt7621_nfc *nfc,
- + u32 avail_ecc_bytes)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + struct mtd_info *mtd = nand_to_mtd(nand);
- + u32 strength;
- + int i;
- +
- + strength = avail_ecc_bytes * 8 / ECC_PARITY_BITS;
- +
- + /* Find the closest supported ecc strength */
- + for (i = ARRAY_SIZE(mt7621_ecc_strength) - 1; i >= 0; i--) {
- + if (mt7621_ecc_strength[i] <= strength)
- + break;
- + }
- +
- + if (unlikely(i < 0)) {
- + pr_err("OOB size (%u) is not supported\n", mtd->oobsize);
- + return -EINVAL;
- + }
- +
- + nand->ecc.strength = mt7621_ecc_strength[i];
- + nand->ecc.bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
- +
- + pr_debug("ECC strength adjusted to %u bits\n", nand->ecc.strength);
- +
- + return i;
- +}
- +
- +static int mt7621_nfc_set_spare_per_sector(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + struct mtd_info *mtd = nand_to_mtd(nand);
- + u32 size;
- + int i;
- +
- + size = nand->ecc.bytes + NFI_FDM_SIZE;
- +
- + /* Find the closest supported spare size */
- + for (i = 0; i < ARRAY_SIZE(mt7621_nfi_spare_size); i++) {
- + if (mt7621_nfi_spare_size[i] >= size)
- + break;
- + }
- +
- + if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_spare_size))) {
- + pr_err("OOB size (%u) is not supported\n", mtd->oobsize);
- + return -EINVAL;
- + }
- +
- + nfc->spare_per_sector = mt7621_nfi_spare_size[i];
- +
- + return i;
- +}
- +
- +static int mt7621_nfc_ecc_init(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + struct mtd_info *mtd = nand_to_mtd(nand);
- + u32 avail_ecc_bytes, encode_block_size, decode_block_size;
- + u32 ecc_enccfg, ecc_deccfg;
- + int ecc_cap;
- +
- + nand->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
- +
- + nand->ecc.size = ECC_SECTOR_SIZE;
- + nand->ecc.steps = mtd->writesize / nand->ecc.size;
- +
- + avail_ecc_bytes = mtd->oobsize / nand->ecc.steps - NFI_FDM_SIZE;
- +
- + ecc_cap = mt7621_nfc_calc_ecc_strength(nfc, avail_ecc_bytes);
- + if (ecc_cap < 0)
- + return ecc_cap;
- +
- + /* Sector + FDM */
- + encode_block_size = (nand->ecc.size + NFI_FDM_SIZE) * 8;
- + ecc_enccfg = ecc_cap | FIELD_PREP(ENC_MODE, ENC_MODE_NFI) |
- + FIELD_PREP(ENC_CNFG_MSG, encode_block_size);
- +
- + /* Sector + FDM + ECC parity bits */
- + decode_block_size = ((nand->ecc.size + NFI_FDM_SIZE) * 8) +
- + nand->ecc.strength * ECC_PARITY_BITS;
- + ecc_deccfg = ecc_cap | FIELD_PREP(DEC_MODE, DEC_MODE_NFI) |
- + FIELD_PREP(DEC_CS, decode_block_size) |
- + FIELD_PREP(DEC_CON, DEC_CON_EL) | DEC_EMPTY_EN;
- +
- + mt7621_ecc_encoder_op(nfc, false);
- + ecc_write32(nfc, ECC_ENCCNFG, ecc_enccfg);
- +
- + mt7621_ecc_decoder_op(nfc, false);
- + ecc_write32(nfc, ECC_DECCNFG, ecc_deccfg);
- +
- + return 0;
- +}
- +
- +static int mt7621_nfc_set_page_format(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + struct mtd_info *mtd = nand_to_mtd(nand);
- + int i, spare_size;
- + u32 pagefmt;
- +
- + spare_size = mt7621_nfc_set_spare_per_sector(nfc);
- + if (spare_size < 0)
- + return spare_size;
- +
- + for (i = 0; i < ARRAY_SIZE(mt7621_nfi_page_size); i++) {
- + if (mt7621_nfi_page_size[i] == mtd->writesize)
- + break;
- + }
- +
- + if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_page_size))) {
- + pr_err("Page size (%u) is not supported\n", mtd->writesize);
- + return -EINVAL;
- + }
- +
- + pagefmt = FIELD_PREP(PAGEFMT_PAGE, i) |
- + FIELD_PREP(PAGEFMT_SPARE, spare_size) |
- + FIELD_PREP(PAGEFMT_FDM, NFI_FDM_SIZE) |
- + FIELD_PREP(PAGEFMT_FDM_ECC, NFI_FDM_SIZE);
- +
- + nfi_write16(nfc, NFI_PAGEFMT, pagefmt);
- +
- + return 0;
- +}
- +
- +static int mt7621_nfc_attach_chip(struct nand_chip *nand)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(nand);
- + int ret;
- +
- + if (nand->options & NAND_BUSWIDTH_16) {
- + pr_err("16-bit buswidth is not supported");
- + return -EINVAL;
- + }
- +
- + ret = mt7621_nfc_ecc_init(nfc);
- + if (ret)
- + return ret;
- +
- + return mt7621_nfc_set_page_format(nfc);
- +}
- +
- +static void mt7621_nfc_write_fdm(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + u32 vall, valm;
- + u8 *oobptr;
- + int i, j;
- +
- + for (i = 0; i < nand->ecc.steps; i++) {
- + vall = 0;
- + valm = 0;
- + oobptr = oob_fdm_ptr(nand, i);
- +
- + for (j = 0; j < 4; j++)
- + vall |= (u32)oobptr[j] << (j * 8);
- +
- + for (j = 0; j < 4; j++)
- + valm |= (u32)oobptr[j + 4] << (j * 8);
- +
- + nfi_write32(nfc, NFI_FDML(i), vall);
- + nfi_write32(nfc, NFI_FDMM(i), valm);
- + }
- +}
- +
- +static void mt7621_nfc_read_sector_fdm(struct mt7621_nfc *nfc, u32 sect)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + u32 vall, valm;
- + u8 *oobptr;
- + int i;
- +
- + vall = nfi_read32(nfc, NFI_FDML(sect));
- + valm = nfi_read32(nfc, NFI_FDMM(sect));
- + oobptr = oob_fdm_ptr(nand, sect);
- +
- + for (i = 0; i < 4; i++)
- + oobptr[i] = (vall >> (i * 8)) & 0xff;
- +
- + for (i = 0; i < 4; i++)
- + oobptr[i + 4] = (valm >> (i * 8)) & 0xff;
- +}
- +
- +static int mt7621_nfc_read_page_hwecc(struct mtd_info *mtd,
- + struct nand_chip *nand, uint8_t *buf,
- + int oob_required, int page)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(nand);
- + int bitflips = 0, ret = 0;
- + int rc, i;
- +
- + nand_read_page_op(nand, page, 0, NULL, 0);
- +
- + nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
- + CNFG_READ_MODE | CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
- +
- + mt7621_ecc_decoder_op(nfc, true);
- +
- + nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
- + CON_NFI_BRD);
- +
- + for (i = 0; i < nand->ecc.steps; i++) {
- + if (buf)
- + mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
- + nand->ecc.size);
- + else
- + mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
- +
- + rc = mt7621_ecc_decoder_wait_done(nfc, i);
- +
- + mt7621_nfc_read_sector_fdm(nfc, i);
- +
- + if (rc < 0) {
- + ret = -EIO;
- + continue;
- + }
- +
- + rc = mt7621_ecc_correct_check(nfc,
- + buf ? page_data_ptr(nand, buf, i) : NULL,
- + oob_fdm_ptr(nand, i), i);
- +
- + if (rc < 0) {
- + pr_warn("Uncorrectable ECC error at page %d step %d\n",
- + page, i);
- + bitflips = nand->ecc.strength + 1;
- + mtd->ecc_stats.failed++;
- + } else {
- + if (rc > bitflips)
- + bitflips = rc;
- + mtd->ecc_stats.corrected += rc;
- + }
- + }
- +
- + mt7621_ecc_decoder_op(nfc, false);
- +
- + nfi_write16(nfc, NFI_CON, 0);
- +
- + if (ret < 0)
- + return ret;
- +
- + return bitflips;
- +}
- +
- +static int mt7621_nfc_read_page_raw(struct mtd_info *mtd,
- + struct nand_chip *nand, uint8_t *buf,
- + int oob_required, int page)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(nand);
- + int i;
- +
- + nand_read_page_op(nand, page, 0, NULL, 0);
- +
- + nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
- + CNFG_READ_MODE);
- +
- + nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
- + CON_NFI_BRD);
- +
- + for (i = 0; i < nand->ecc.steps; i++) {
- + /* Read data */
- + if (buf)
- + mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
- + nand->ecc.size);
- + else
- + mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
- +
- + /* Read FDM */
- + mt7621_nfc_read_data(nfc, oob_fdm_ptr(nand, i), NFI_FDM_SIZE);
- +
- + /* Read ECC parity data */
- + mt7621_nfc_read_data(nfc, oob_ecc_ptr(nfc, i),
- + nfc->spare_per_sector - NFI_FDM_SIZE);
- + }
- +
- + nfi_write16(nfc, NFI_CON, 0);
- +
- + return 0;
- +}
- +
- +static int mt7621_nfc_read_oob_hwecc(struct mtd_info *mtd,
- + struct nand_chip *nand, int page)
- +{
- + return mt7621_nfc_read_page_hwecc(mtd, nand, NULL, 1, page);
- +}
- +
- +static int mt7621_nfc_read_oob_raw(struct mtd_info *mtd,
- + struct nand_chip *nand, int page)
- +{
- + return mt7621_nfc_read_page_raw(mtd, nand, NULL, 1, page);
- +}
- +
- +static int mt7621_nfc_check_empty_page(struct nand_chip *nand, const u8 *buf)
- +{
- + struct mtd_info *mtd = nand_to_mtd(nand);
- + u8 *oobptr;
- + u32 i, j;
- +
- + if (buf) {
- + for (i = 0; i < mtd->writesize; i++)
- + if (buf[i] != 0xff)
- + return 0;
- + }
- +
- + for (i = 0; i < nand->ecc.steps; i++) {
- + oobptr = oob_fdm_ptr(nand, i);
- + for (j = 0; j < NFI_FDM_SIZE; j++)
- + if (oobptr[j] != 0xff)
- + return 0;
- + }
- +
- + return 1;
- +}
- +
- +static int mt7621_nfc_write_page_hwecc(struct mtd_info *mtd,
- + struct nand_chip *nand,
- + const u8 *buf, int oob_required,
- + int page)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(nand);
- +
- + if (mt7621_nfc_check_empty_page(nand, buf)) {
- + /*
- + * MT7621 ECC engine always generates parity code for input
- + * pages, even for empty pages. Doing so will write back ECC
- + * parity code to the oob region, which means such pages will
- + * no longer be empty pages.
- + *
- + * To avoid this, stop write operation if current page is an
- + * empty page.
- + */
- + return 0;
- + }
- +
- + nand_prog_page_begin_op(nand, page, 0, NULL, 0);
- +
- + nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
- + CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
- +
- + mt7621_ecc_encoder_op(nfc, true);
- +
- + mt7621_nfc_write_fdm(nfc);
- +
- + nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
- + CON_NFI_BWR);
- +
- + if (buf)
- + mt7621_nfc_write_data(nfc, buf, mtd->writesize);
- + else
- + mt7621_nfc_write_data_empty(nfc, mtd->writesize);
- +
- + mt7621_nfc_wait_write_completion(nfc, nand);
- +
- + mt7621_ecc_encoder_op(nfc, false);
- +
- + nfi_write16(nfc, NFI_CON, 0);
- +
- + return nand_prog_page_end_op(nand);
- +}
- +
- +static int mt7621_nfc_write_page_raw(struct mtd_info *mtd,
- + struct nand_chip *nand,
- + const u8 *buf, int oob_required,
- + int page)
- +{
- + struct mt7621_nfc *nfc = nand_get_controller_data(nand);
- + int i;
- +
- + nand_prog_page_begin_op(nand, page, 0, NULL, 0);
- +
- + nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM));
- +
- + nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
- + CON_NFI_BWR);
- +
- + for (i = 0; i < nand->ecc.steps; i++) {
- + /* Write data */
- + if (buf)
- + mt7621_nfc_write_data(nfc, page_data_ptr(nand, buf, i),
- + nand->ecc.size);
- + else
- + mt7621_nfc_write_data_empty(nfc, nand->ecc.size);
- +
- + /* Write FDM */
- + mt7621_nfc_write_data(nfc, oob_fdm_ptr(nand, i),
- + NFI_FDM_SIZE);
- +
- + /* Write dummy ECC parity data */
- + mt7621_nfc_write_data_empty(nfc, nfc->spare_per_sector -
- + NFI_FDM_SIZE);
- + }
- +
- + mt7621_nfc_wait_write_completion(nfc, nand);
- +
- + nfi_write16(nfc, NFI_CON, 0);
- +
- + return nand_prog_page_end_op(nand);
- +}
- +
- +static int mt7621_nfc_write_oob_hwecc(struct mtd_info *mtd,
- + struct nand_chip *nand, int page)
- +{
- + return mt7621_nfc_write_page_hwecc(mtd, nand, NULL, 1, page);
- +}
- +
- +static int mt7621_nfc_write_oob_raw(struct mtd_info *mtd,
- + struct nand_chip *nand, int page)
- +{
- + return mt7621_nfc_write_page_raw(mtd, nand, NULL, 1, page);
- +}
- +
- +static int mt7621_nfc_ooblayout_free(struct mtd_info *mtd, int section,
- + struct mtd_oob_region *oob_region)
- +{
- + struct nand_chip *nand = mtd_to_nand(mtd);
- +
- + if (section >= nand->ecc.steps)
- + return -ERANGE;
- +
- + oob_region->length = NFI_FDM_SIZE - 1;
- + oob_region->offset = section * NFI_FDM_SIZE + 1;
- +
- + return 0;
- +}
- +
- +static int mt7621_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
- + struct mtd_oob_region *oob_region)
- +{
- + struct nand_chip *nand = mtd_to_nand(mtd);
- +
- + if (section)
- + return -ERANGE;
- +
- + oob_region->offset = NFI_FDM_SIZE * nand->ecc.steps;
- + oob_region->length = mtd->oobsize - oob_region->offset;
- +
- + return 0;
- +}
- +
- +static const struct mtd_ooblayout_ops mt7621_nfc_ooblayout_ops = {
- + .rfree = mt7621_nfc_ooblayout_free,
- + .ecc = mt7621_nfc_ooblayout_ecc,
- +};
- +
- +/*
- + * This function will override the default one which is not supposed to be
- + * used for ECC syndrome based pages.
- + */
- +static int mt7621_nfc_block_bad(struct mtd_info *mtd, loff_t ofs)
- +{
- + struct nand_chip *nand = mtd_to_nand(mtd);
- + struct mtd_oob_ops ops;
- + int ret, i = 0;
- + u16 bad;
- +
- + memset(&ops, 0, sizeof(ops));
- + ops.oobbuf = (uint8_t *)&bad;
- + ops.ooboffs = nand->badblockpos;
- + if (nand->options & NAND_BUSWIDTH_16) {
- + ops.ooboffs &= ~0x01;
- + ops.ooblen = 2;
- + } else {
- + ops.ooblen = 1;
- + }
- + ops.mode = MTD_OPS_RAW;
- +
- + /* Read from first/last page(s) if necessary */
- + if (nand->bbt_options & NAND_BBT_SCANLASTPAGE)
- + ofs += mtd->erasesize - mtd->writesize;
- +
- + do {
- + ret = mtd_read_oob(mtd, ofs, &ops);
- + if (ret)
- + return ret;
- +
- + if (likely(nand->badblockbits == 8))
- + ret = bad != 0xFF;
- + else
- + ret = hweight8(bad) < nand->badblockbits;
- +
- + i++;
- + ofs += mtd->writesize;
- + } while (!ret && (nand->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
- +
- + return ret;
- +}
- +
- +static void mt7621_nfc_init_chip(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + struct mtd_info *mtd;
- + int ret;
- +
- + nand_set_controller_data(nand, nfc);
- +
- + nand->options |= NAND_NO_SUBPAGE_WRITE;
- +
- + nand->ecc.mode = NAND_ECC_HW_SYNDROME;
- + nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
- + nand->ecc.read_page_raw = mt7621_nfc_read_page_raw;
- + nand->ecc.write_page = mt7621_nfc_write_page_hwecc;
- + nand->ecc.write_page_raw = mt7621_nfc_write_page_raw;
- + nand->ecc.read_oob = mt7621_nfc_read_oob_hwecc;
- + nand->ecc.read_oob_raw = mt7621_nfc_read_oob_raw;
- + nand->ecc.write_oob = mt7621_nfc_write_oob_hwecc;
- + nand->ecc.write_oob_raw = mt7621_nfc_write_oob_raw;
- +
- + nand->dev_ready = mt7621_nfc_dev_ready;
- + nand->select_chip = mt7621_nfc_select_chip;
- + nand->write_byte = mt7621_nfc_write_byte;
- + nand->write_buf = mt7621_nfc_write_buf;
- + nand->read_byte = mt7621_nfc_read_byte;
- + nand->read_buf = mt7621_nfc_read_buf;
- + nand->cmd_ctrl = mt7621_nfc_cmd_ctrl;
- + nand->block_bad = mt7621_nfc_block_bad;
- +
- + mtd = nand_to_mtd(nand);
- + mtd_set_ooblayout(mtd, &mt7621_nfc_ooblayout_ops);
- +
- + /* Reset NFI master */
- + mt7621_nfc_hw_init(nfc);
- +
- + ret = nand_scan_ident(mtd, 1, NULL);
- + if (ret)
- + return;
- +
- + mt7621_nfc_attach_chip(nand);
- +
- + ret = nand_scan_tail(mtd);
- + if (ret)
- + return;
- +
- + nand_register(0, mtd);
- +}
- +
- +static void mt7621_nfc_set_regs(struct mt7621_nfc *nfc)
- +{
- + nfc->nfi_regs = (void __iomem *)CKSEG1ADDR(NFI_BASE);
- + nfc->ecc_regs = (void __iomem *)CKSEG1ADDR(NFI_ECC_BASE);
- +}
- +
- +void mt7621_nfc_spl_init(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- +
- + mt7621_nfc_set_regs(nfc);
- +
- + nand_set_controller_data(nand, nfc);
- +
- + nand->options |= NAND_NO_SUBPAGE_WRITE;
- +
- + nand->ecc.mode = NAND_ECC_HW_SYNDROME;
- + nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
- +
- + nand->dev_ready = mt7621_nfc_dev_ready;
- + nand->select_chip = mt7621_nfc_select_chip;
- + nand->read_byte = mt7621_nfc_read_byte;
- + nand->read_buf = mt7621_nfc_read_buf;
- + nand->cmd_ctrl = mt7621_nfc_cmd_ctrl;
- +
- + /* Reset NFI master */
- + mt7621_nfc_hw_init(nfc);
- +}
- +
- +int mt7621_nfc_spl_post_init(struct mt7621_nfc *nfc)
- +{
- + struct nand_chip *nand = &nfc->nand;
- + int nand_maf_id, nand_dev_id;
- + struct nand_flash_dev *type;
- +
- + type = nand_get_flash_type(&nand->mtd, nand, &nand_maf_id,
- + &nand_dev_id, NULL);
- +
- + if (IS_ERR(type))
- + return PTR_ERR(type);
- +
- + nand->numchips = 1;
- + nand->mtd.size = nand->chipsize;
- +
- + return mt7621_nfc_attach_chip(nand);
- +}
- +
- +void board_nand_init(void)
- +{
- + mt7621_nfc_set_regs(&nfc_dev);
- + mt7621_nfc_init_chip(&nfc_dev);
- +}
- --- /dev/null
- +++ b/drivers/mtd/nand/raw/mt7621_nand.h
- @@ -0,0 +1,29 @@
- +/* SPDX-License-Identifier: GPL-2.0 */
- +/*
- + * Copyright (C) 2022 MediaTek Inc. All rights reserved.
- + *
- + * Author: Weijie Gao <[email protected]>
- + */
- +
- +#ifndef _MT7621_NAND_H_
- +#define _MT7621_NAND_H_
- +
- +#include <linux/types.h>
- +#include <linux/mtd/mtd.h>
- +#include <linux/compiler.h>
- +#include <linux/mtd/rawnand.h>
- +
- +struct mt7621_nfc {
- + struct nand_chip nand;
- +
- + void __iomem *nfi_regs;
- + void __iomem *ecc_regs;
- +
- + u32 spare_per_sector;
- +};
- +
- +/* for SPL */
- +void mt7621_nfc_spl_init(struct mt7621_nfc *nfc);
- +int mt7621_nfc_spl_post_init(struct mt7621_nfc *nfc);
- +
- +#endif /* _MT7621_NAND_H_ */
- --- /dev/null
- +++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
- @@ -0,0 +1,237 @@
- +// SPDX-License-Identifier: GPL-2.0
- +/*
- + * Copyright (C) 2022 MediaTek Inc. All rights reserved.
- + *
- + * Author: Weijie Gao <[email protected]>
- + */
- +
- +#include <image.h>
- +#include <malloc.h>
- +#include <linux/sizes.h>
- +#include <linux/delay.h>
- +#include <linux/mtd/rawnand.h>
- +#include "mt7621_nand.h"
- +
- +static struct mt7621_nfc nfc_dev;
- +static u8 *buffer;
- +static int nand_valid;
- +
- +static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
- + int column, int page_addr)
- +{
- + register struct nand_chip *chip = mtd_to_nand(mtd);
- +
- + /* Command latch cycle */
- + 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) {
- + chip->cmd_ctrl(mtd, column, ctrl);
- + ctrl &= ~NAND_CTRL_CHANGE;
- + if (command != NAND_CMD_READID)
- + 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);
- + if (chip->options & NAND_ROW_ADDR_3)
- + chip->cmd_ctrl(mtd, page_addr >> 16,
- + NAND_NCE | NAND_ALE);
- + }
- + }
- + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- +
- + /*
- + * Program and erase have their own busy handlers status, sequential
- + * in and status need no delay.
- + */
- + switch (command) {
- + case NAND_CMD_STATUS:
- + case NAND_CMD_READID:
- + case NAND_CMD_SET_FEATURES:
- + return;
- +
- + case NAND_CMD_READ0:
- + chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
- + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
- + chip->cmd_ctrl(mtd, NAND_CMD_NONE,
- + NAND_NCE | NAND_CTRL_CHANGE);
- + }
- +
- + /*
- + * Apply this short delay always to ensure that we do wait tWB in
- + * any case on any machine.
- + */
- + ndelay(100);
- +
- + nand_wait_ready(mtd);
- +}
- +
- +static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
- + unsigned int page)
- +{
- + struct nand_chip *chip = mtd_to_nand(mtd);
- + int ret;
- +
- + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
- +
- + ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
- + if (ret < 0 || ret > chip->ecc.strength)
- + return -1;
- +
- + return 0;
- +}
- +
- +static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
- + unsigned int page)
- +{
- + struct nand_chip *chip = mtd_to_nand(mtd);
- + int ret;
- +
- + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
- +
- + ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
- + if (ret < 0)
- + return -1;
- +
- + if (len > mtd->oobsize)
- + len = mtd->oobsize;
- +
- + memcpy(buf, chip->oob_poi, len);
- +
- + return 0;
- +}
- +
- +static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
- +{
- + struct nand_chip *chip = mtd_to_nand(mtd);
- + u32 pages_per_block, i = 0;
- + int ret;
- + u8 bad;
- +
- + pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
- +
- + /* Read from first/last page(s) if necessary */
- + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
- + page += pages_per_block - 1;
- + if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
- + page--;
- + }
- +
- + do {
- + ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
- + if (ret)
- + return ret;
- +
- + ret = bad != 0xFF;
- +
- + i++;
- + page++;
- + } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
- +
- + return ret;
- +}
- +
- +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
- +{
- + struct mt7621_nfc *nfc = &nfc_dev;
- + struct nand_chip *chip = &nfc->nand;
- + struct mtd_info *mtd = &chip->mtd;
- + u32 addr, col, page, chksz;
- + bool check_bad = true;
- +
- + if (!nand_valid)
- + return -ENODEV;
- +
- + while (size) {
- + if (check_bad || !(offs & mtd->erasesize_mask)) {
- + addr = offs & (~mtd->erasesize_mask);
- + page = addr >> mtd->writesize_shift;
- + if (nfc_check_bad_block(mtd, page)) {
- + /* Skip bad block */
- + if (addr >= mtd->size - mtd->erasesize)
- + return -1;
- +
- + offs += mtd->erasesize;
- + continue;
- + }
- +
- + check_bad = false;
- + }
- +
- + col = offs & mtd->writesize_mask;
- + page = offs >> mtd->writesize_shift;
- + chksz = min(mtd->writesize - col, (uint32_t)size);
- +
- + if (unlikely(chksz < mtd->writesize)) {
- + /* Not reading a full page */
- + if (nfc_read_page_hwecc(mtd, buffer, page))
- + return -1;
- +
- + memcpy(dest, buffer + col, chksz);
- + } else {
- + if (nfc_read_page_hwecc(mtd, dest, page))
- + return -1;
- + }
- +
- + dest += chksz;
- + offs += chksz;
- + size -= chksz;
- + }
- +
- + return 0;
- +}
- +
- +int nand_default_bbt(struct mtd_info *mtd)
- +{
- + return 0;
- +}
- +
- +unsigned long nand_size(void)
- +{
- + if (!nand_valid)
- + return 0;
- +
- + /* Unlikely that NAND size > 2GBytes */
- + if (nfc_dev.nand.chipsize <= SZ_2G)
- + return nfc_dev.nand.chipsize;
- +
- + return SZ_2G;
- +}
- +
- +void nand_deselect(void)
- +{
- +}
- +
- +void nand_init(void)
- +{
- + struct mtd_info *mtd;
- + struct nand_chip *chip;
- +
- + if (nand_valid)
- + return;
- +
- + mt7621_nfc_spl_init(&nfc_dev);
- +
- + chip = &nfc_dev.nand;
- + mtd = &chip->mtd;
- + chip->cmdfunc = nand_command_lp;
- +
- + if (mt7621_nfc_spl_post_init(&nfc_dev))
- + return;
- +
- + mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
- + mtd->writesize_shift = ffs(mtd->writesize) - 1;
- + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
- + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
- +
- + buffer = malloc(mtd->writesize);
- + if (!buffer)
- + return;
- +
- + nand_valid = 1;
- +}
|