[PATCH 2/3] mtd: hisilicon: add a new nand controller driver for hisilicon hip04 Soc
From: Ivan Khoronzhuk <hidden>
Date: 2014-06-30 09:47:17
Also in:
linux-devicetree, lkml
On 06/30/2014 11:03 AM, Zhou Wang wrote:
quoted hunk ↗ jump to hunk
Signed-off-by: Zhou Wang <redacted> --- drivers/mtd/nand/Kconfig | 5 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/hisi_nand.c | 847 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 853 insertions(+) create mode 100644 drivers/mtd/nand/hisi_nand.cdiff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 90ff447..253f8c8 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig@@ -510,4 +510,9 @@ config MTD_NAND_XWAY Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached to the External Bus Unit (EBU). +config MTD_NAND_HISI + tristate "Support for NAND controller on Hisilicon SoC" + help + Enables support for NAND controller on Hisilicon SoC. + endif # MTD_NANDdiff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 542b568..d0881cf 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ +obj-$(CONFIG_MTD_NAND_HISI) += hisi_nand.o nand-objs := nand_base.o nand_bbt.odiff --git a/drivers/mtd/nand/hisi_nand.c b/drivers/mtd/nand/hisi_nand.c new file mode 100644 index 0000000..fbcb065 --- /dev/null +++ b/drivers/mtd/nand/hisi_nand.c@@ -0,0 +1,847 @@ +/* + * Hisilicon NAND Flash controller driver + * + * Copyright ? 2012-2014 HiSilicon Technologies Co., Ltd. + * http://www.hisilicon.com + * + * Author: Zhou Wang <wangzhou.bry@gmail.com> + * The initial developer of the original code is Zhiyong Cai + * <caizhiyong@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/of.h> +#include <linux/of_mtd.h> +#include <linux/mtd/mtd.h> +#include <linux/sizes.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mtd/nand.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/mtd/partitions.h> + +#define HINFC504_MAX_CHIP (4) +#define HINFC504_W_LATCH (5) +#define HINFC504_R_LATCH (7) +#define HINFC504_RW_LATCH (3) + +#define HINFC504_NFC_TIMEOUT (2 * HZ) +#define HINFC504_NFC_DMA_TIMEOUT (5 * HZ) +#define HINFC504_CHIP_DELAY (25) + +#define HINFC504_REG_BASE_ADDRESS_LEN (0x100) +#define HINFC504_BUFFER_BASE_ADDRESS_LEN (2048 + 128) + +#define HINFC504_ADDR_CYCLE_MASK 0x4 + +#define HINFC504_CON 0x00 +#define HINFC504_CON_OP_MODE_NORMAL (1U << 0) +#define HINFC504_CON_PAGEISZE_SHIFT (1) +#define HINFC504_CON_PAGESIZE_MASK (0x07) +#define HINFC504_CON_BUS_WIDTH (1U << 4) +#define HINFC504_CON_READY_BUSY_SEL (1U << 8) +#define HINFC504_CON_ECCTYPE_SHIFT (9) +#define HINFC504_CON_ECCTYPE_MASK (0x07) + +#define HINFC504_PWIDTH 0x04 +#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ + ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) + +#define HINFC504_CMD 0x0C +#define HINFC504_ADDRL 0x10 +#define HINFC504_ADDRH 0x14 +#define HINFC504_DATA_NUM 0x18 + +#define HINFC504_OP 0x1C +#define HINFC504_OP_READ_DATA_EN (1U << 1) +#define HINFC504_OP_WAIT_READY_EN (1U << 2) +#define HINFC504_OP_CMD2_EN (1U << 3) +#define HINFC504_OP_WRITE_DATA_EN (1U << 4) +#define HINFC504_OP_ADDR_EN (1U << 5) +#define HINFC504_OP_CMD1_EN (1U << 6) +#define HINFC504_OP_NF_CS_SHIFT (7) +#define HINFC504_OP_NF_CS_MASK (3) +#define HINFC504_OP_ADDR_CYCLE_SHIFT (9) +#define HINFC504_OP_ADDR_CYCLE_MASK (7) + +#define HINFC504_STATUS 0x20 +#define HINFC504_READY (1U << 0) + +#define HINFC504_INTEN 0x24 +#define HINFC504_INTEN_DMA (1U << 9) +#define HINFC504_INTEN_UE (1U << 6) +#define HINFC504_INTEN_CE (1U << 5) + +#define HINFC504_INTS 0x28 +#define HINFC504_INTS_DMA (1U << 9) +#define HINFC504_INTS_UE (1U << 6) +#define HINFC504_INTS_CE (1U << 5) + +#define HINFC504_INTCLR 0x2C +#define HINFC504_INTCLR_DMA (1U << 9) +#define HINFC504_INTCLR_UE (1U << 6) +#define HINFC504_INTCLR_CE (1U << 5) + +#define HINFC504_ECC_STATUS 0x5C +#define HINFC504_ECC_1_BIT_SHIFT 16 +#define HINFC504_ECC_16_BIT_SHIFT 12 + +#define HINFC504_DMA_CTRL 0x60 +#define HINFC504_DMA_CTRL_DMA_START (1U << 0) +#define HINFC504_DMA_CTRL_WE (1U << 1) +#define HINFC504_DMA_CTRL_DATA_AREA_EN (1U << 2) +#define HINFC504_DMA_CTRL_OOB_AREA_EN (1U << 3) +#define HINFC504_DMA_CTRL_BURST4_EN (1U << 4) +#define HINFC504_DMA_CTRL_BURST8_EN (1U << 5) +#define HINFC504_DMA_CTRL_BURST16_EN (1U << 6) +#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT (7) +#define HINFC504_DMA_CTRL_ADDR_NUM_MASK (1) +#define HINFC504_DMA_CTRL_CS_SHIFT (8) +#define HINFC504_DMA_CTRL_CS_MASK (0x03) + +#define HINFC504_DMA_ADDR_DATA 0x64 +#define HINFC504_DMA_ADDR_OOB 0x68 + +#define HINFC504_DMA_LEN 0x6C +#define HINFC504_DMA_LEN_OOB_SHIFT (16) +#define HINFC504_DMA_LEN_OOB_MASK (0xFFF) + +#define HINFC504_DMA_PARA 0x70 +#define HINFC504_DMA_PARA_DATA_RW_EN (1U << 0) +#define HINFC504_DMA_PARA_OOB_RW_EN (1U << 1) +#define HINFC504_DMA_PARA_DATA_EDC_EN (1U << 2) +#define HINFC504_DMA_PARA_OOB_EDC_EN (1U << 3) +#define HINFC504_DMA_PARA_DATA_ECC_EN (1U << 4) +#define HINFC504_DMA_PARA_OOB_ECC_EN (1U << 5) + +#define HINFC_VERSION 0x74 +#define HINFC504_LOG_READ_ADDR 0x7C +#define HINFC504_LOG_READ_LEN 0x80 + +#define HINFC504_NANDINFO_LEN 0x10 + +#define hinfc_read(_host, _reg) readl(_host->iobase + (_reg)) +#define hinfc_write(_host, _value, _reg)\ + writel((_value), _host->iobase + (_reg)) + +struct hinfc_host { + struct nand_chip *chip; + struct mtd_info *mtd; + struct device *dev; + void __iomem *iobase; + struct completion cmd_complete; + unsigned int offset; + unsigned int command; + int chipselect; + unsigned int addr_cycle; + unsigned int addr_value[2]; + unsigned int cache_addr_value[2]; + char *buffer; + dma_addr_t dma_buffer; + dma_addr_t dma_oob; + int version; + unsigned int ecc_bits; + unsigned int irq_status; /* interrupt status */ + + int (*send_cmd_pageprog)(struct hinfc_host *host); + int (*send_cmd_status)(struct hinfc_host *host); + int (*send_cmd_readstart)(struct hinfc_host *host); + int (*send_cmd_erase)(struct hinfc_host *host); + int (*send_cmd_readid)(struct hinfc_host *host); + int (*send_cmd_reset)(struct hinfc_host *host, int chipselect); +}; + +void wait_controller_finished(struct hinfc_host *host) +{ + unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT; + int val; + + while (time_before(jiffies, timeout)) { + val = hinfc_read(host, HINFC504_STATUS); + if (host->command == NAND_CMD_ERASE2) { + /* nfc is ready */ + while (!(val & HINFC504_READY)) { + usleep_range(500, 1000); + val = hinfc_read(host, HINFC504_STATUS); + } + return; + } else { + if (val & HINFC504_READY) + return; + } + } + + /* wait cmd timeout */ + dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n"); +} + +static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) +{ + struct mtd_info *mtd = host->mtd; + struct nand_chip *chip = mtd->priv; + unsigned long val; + int ret; + + hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); + hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); + + if (chip->ecc.mode == NAND_ECC_NONE) { + hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) + << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); + + hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN + | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA); + } else
Add {} for else also
./scripts/checkpatch.pl.
--
Regards,
Ivan Khoronzhuk