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 <weijie.gao@mediatek.com>
This commit is contained in:
committed by
Daniel Schwierzeck
parent
ad80d48979
commit
3ab8beaadc
@@ -526,12 +526,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
|
||||
@@ -556,7 +569,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.
|
||||
|
||||
@@ -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_spl.o
|
||||
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
|
||||
|
||||
1205
drivers/mtd/nand/raw/mt7621_nand.c
Normal file
1205
drivers/mtd/nand/raw/mt7621_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
29
drivers/mtd/nand/raw/mt7621_nand.h
Normal file
29
drivers/mtd/nand/raw/mt7621_nand.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2022 MediaTek Inc. All rights reserved.
|
||||
*
|
||||
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
237
drivers/mtd/nand/raw/mt7621_nand_spl.c
Normal file
237
drivers/mtd/nand/raw/mt7621_nand_spl.c
Normal file
@@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2022 MediaTek Inc. All rights reserved.
|
||||
*
|
||||
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user