blob: 3012645c2c5147e1e13c8f5e6480e811dc719fb3 [file] [log] [blame]
/*
* Copyright (C) 2014 Imagination Technologies Ltd.
*
* 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; version 2 of the License.
*
* Notes:
* 1. Erase and program operations need to call write_enable() first,
* to clear the enable bit. This bit is cleared automatically after
* the erase or program operation.
*
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nand.h>
#include <linux/of_platform.h>
#include <linux/of_mtd.h>
#include <linux/slab.h>
/* Registers common to all devices */
#define SPI_NAND_LOCK_REG 0xa0
#define SPI_NAND_PROT_UNLOCK_ALL 0x0
#define SPI_NAND_FEATURE_REG 0xb0
#define SPI_NAND_ECC_EN BIT(4)
#define SPI_NAND_STATUS_REG 0xc0
#define SPI_NAND_STATUS_REG_ECC_MASK 0x3
#define SPI_NAND_STATUS_REG_ECC_SHIFT 4
#define SPI_NAND_STATUS_REG_PROG_FAIL BIT(3)
#define SPI_NAND_STATUS_REG_ERASE_FAIL BIT(2)
#define SPI_NAND_STATUS_REG_WREN BIT(1)
#define SPI_NAND_STATUS_REG_BUSY BIT(0)
#define SPI_NAND_CMD_BUF_LEN 8
/* Rewind and fill the buffer with 0xff */
static void spi_nand_clear_buffer(struct spi_nand *snand)
{
snand->buf_start = 0;
memset(snand->data_buf, 0xff, snand->buf_size);
}
static int spi_nand_enable_ecc(struct spi_nand *snand)
{
int ret;
ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
if (ret)
return ret;
snand->buf[0] |= SPI_NAND_ECC_EN;
ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
if (ret)
return ret;
snand->ecc = true;
return 0;
}
static int spi_nand_disable_ecc(struct spi_nand *snand)
{
int ret;
ret = snand->read_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
if (ret)
return ret;
snand->buf[0] &= ~SPI_NAND_ECC_EN;
ret = snand->write_reg(snand, SPI_NAND_FEATURE_REG, snand->buf);
if (ret)
return ret;
snand->ecc = false;
return 0;
}
/*
* Wait until the status register busy bit is cleared.
* Returns a negatie errno on error or time out, and a non-negative status
* value if the device is ready.
*/
static int spi_nand_wait_till_ready(struct spi_nand *snand)
{
unsigned long deadline = jiffies + msecs_to_jiffies(100);
bool timeout = false;
int ret;
/*
* Perhaps we should set a different timeout for each
* operation (reset, read, write, erase).
*/
while (!timeout) {
if (time_after_eq(jiffies, deadline))
timeout = true;
ret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);
if (ret < 0) {
dev_err(snand->dev, "error reading status register\n");
return ret;
} else if (!(snand->buf[0] & SPI_NAND_STATUS_REG_BUSY)) {
return snand->buf[0];
}
cond_resched();
}
dev_err(snand->dev, "operation timed out\n");
return -ETIMEDOUT;
}
static int spi_nand_reset(struct spi_nand *snand)
{
int ret;
ret = snand->reset(snand);
if (ret < 0) {
dev_err(snand->dev, "reset command failed\n");
return ret;
}
/*
* The NAND core won't wait after a device reset, so we need
* to do that here.
*/
ret = spi_nand_wait_till_ready(snand);
if (ret < 0)
return ret;
return 0;
}
static int spi_nand_status(struct spi_nand *snand)
{
int ret, status;
ret = snand->read_reg(snand, SPI_NAND_STATUS_REG, snand->buf);
if (ret < 0) {
dev_err(snand->dev, "error reading status register\n");
return ret;
}
status = snand->buf[0];
/* Convert this into standard NAND_STATUS values */
if (status & SPI_NAND_STATUS_REG_BUSY)
snand->buf[0] = 0;
else
snand->buf[0] = NAND_STATUS_READY;
if (status & SPI_NAND_STATUS_REG_PROG_FAIL ||
status & SPI_NAND_STATUS_REG_ERASE_FAIL)
snand->buf[0] |= NAND_STATUS_FAIL;
/*
* Since we unlock the entire device at initialization, unconditionally
* set the WP bit to indicate it's not protected.
*/
snand->buf[0] |= NAND_STATUS_WP;
return 0;
}
static int spi_nand_erase(struct spi_nand *snand, int page_addr)
{
int ret;
ret = snand->write_enable(snand);
if (ret < 0) {
dev_err(snand->dev, "write enable command failed\n");
return ret;
}
ret = snand->block_erase(snand, page_addr);
if (ret < 0) {
dev_err(snand->dev, "block erase command failed\n");
return ret;
}
return 0;
}
static int spi_nand_write(struct spi_nand *snand)
{
int ret;
/* Store the page to cache */
ret = snand->store_cache(snand, 0, snand->buf_size, snand->data_buf);
if (ret < 0) {
dev_err(snand->dev, "error %d storing page 0x%x to cache\n",
ret, snand->page_addr);
return ret;
}
ret = snand->write_enable(snand);
if (ret < 0) {
dev_err(snand->dev, "write enable command failed\n");
return ret;
}
/* Get page from the device cache into our internal buffer */
ret = snand->write_page(snand, snand->page_addr);
if (ret < 0) {
dev_err(snand->dev, "error %d reading page 0x%x from cache\n",
ret, snand->page_addr);
return ret;
}
return 0;
}
static int spi_nand_read_id(struct spi_nand *snand)
{
int ret;
ret = snand->read_id(snand, snand->data_buf);
if (ret < 0) {
dev_err(snand->dev, "error %d reading ID\n", ret);
return ret;
}
return 0;
}
static int spi_nand_read_page(struct spi_nand *snand, unsigned int page_addr,
unsigned int page_offset, size_t length)
{
unsigned int corrected = 0, ecc_error = 0;
int ret;
/* Load a page into the cache register */
ret = snand->load_page(snand, page_addr);
if (ret < 0) {
dev_err(snand->dev, "error %d loading page 0x%x to cache\n",
ret, page_addr);
return ret;
}
ret = spi_nand_wait_till_ready(snand);
if (ret < 0)
return ret;
if (snand->ecc) {
snand->get_ecc_status(ret, &corrected, &ecc_error);
snand->bitflips = corrected;
/*
* If there's an ECC error, print a message and notify MTD
* about it. Then complete the read, to load actual data on
* the buffer (instead of the status result).
*/
if (ecc_error) {
dev_err(snand->dev,
"internal ECC error reading page 0x%x\n",
page_addr);
snand->mtd.ecc_stats.failed++;
} else {
snand->mtd.ecc_stats.corrected += corrected;
}
}
/* Get page from the device cache into our internal buffer */
ret = snand->read_cache(snand, page_offset, length, snand->data_buf);
if (ret < 0) {
dev_err(snand->dev, "error %d reading page 0x%x from cache\n",
ret, page_addr);
return ret;
}
return 0;
}
static u8 spi_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
struct spi_nand *snand = chip->priv;
char val = 0xff;
if (snand->buf_start < snand->buf_size)
val = snand->data_buf[snand->buf_start++];
return val;
}
static void spi_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *chip = mtd->priv;
struct spi_nand *snand = chip->priv;
size_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);
memcpy(snand->data_buf + snand->buf_start, buf, n);
snand->buf_start += n;
}
static void spi_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *chip = mtd->priv;
struct spi_nand *snand = chip->priv;
size_t n = min_t(size_t, len, snand->buf_size - snand->buf_start);
memcpy(buf, snand->data_buf + snand->buf_start, n);
snand->buf_start += n;
}
static int spi_nand_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
chip->write_buf(mtd, buf, mtd->writesize);
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
static int spi_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required,
int page)
{
struct spi_nand *snand = chip->priv;
chip->read_buf(mtd, buf, mtd->writesize);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
return snand->bitflips;
}
static int spi_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
{
struct spi_nand *snand = chip->priv;
int ret;
ret = spi_nand_wait_till_ready(snand);
if (ret < 0) {
return NAND_STATUS_FAIL;
} else if (ret & SPI_NAND_STATUS_REG_PROG_FAIL) {
dev_err(snand->dev, "page program failed\n");
return NAND_STATUS_FAIL;
} else if (ret & SPI_NAND_STATUS_REG_ERASE_FAIL) {
dev_err(snand->dev, "block erase failed\n");
return NAND_STATUS_FAIL;
}
return NAND_STATUS_READY;
}
static void spi_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
struct nand_chip *chip = mtd->priv;
struct spi_nand *snand = chip->priv;
/*
* In case there's any unsupported command, let's make sure
* we don't keep garbage around in the buffer.
*/
if (command != NAND_CMD_PAGEPROG) {
spi_nand_clear_buffer(snand);
snand->page_addr = 0;
}
switch (command) {
case NAND_CMD_READ0:
spi_nand_read_page(snand, page_addr, 0, mtd->writesize);
break;
case NAND_CMD_READOOB:
spi_nand_disable_ecc(snand);
spi_nand_read_page(snand, page_addr, mtd->writesize,
mtd->oobsize);
spi_nand_enable_ecc(snand);
break;
case NAND_CMD_READID:
spi_nand_read_id(snand);
break;
case NAND_CMD_ERASE1:
spi_nand_erase(snand, page_addr);
break;
case NAND_CMD_ERASE2:
/* There's nothing to do here, as the erase is one-step */
break;
case NAND_CMD_SEQIN:
snand->buf_start = column;
snand->page_addr = page_addr;
break;
case NAND_CMD_PAGEPROG:
spi_nand_write(snand);
break;
case NAND_CMD_STATUS:
spi_nand_status(snand);
break;
case NAND_CMD_RESET:
spi_nand_reset(snand);
break;
default:
dev_err(&mtd->dev, "unknown command 0x%x\n", command);
}
}
static void spi_nand_select_chip(struct mtd_info *mtd, int chip)
{
/* We need this to override the default */
}
int spi_nand_check(struct spi_nand *snand)
{
if (!snand->dev)
return -ENODEV;
if (!snand->read_cache)
return -ENODEV;
if (!snand->load_page)
return -ENODEV;
if (!snand->store_cache)
return -ENODEV;
if (!snand->write_page)
return -ENODEV;
if (!snand->write_reg)
return -ENODEV;
if (!snand->read_reg)
return -ENODEV;
if (!snand->block_erase)
return -ENODEV;
if (!snand->reset)
return -ENODEV;
if (!snand->write_enable)
return -ENODEV;
if (!snand->write_disable)
return -ENODEV;
if (!snand->get_ecc_status)
return -ENODEV;
return 0;
}
int spi_nand_register(struct spi_nand *snand, struct nand_flash_dev *flash_ids)
{
struct nand_chip *chip = &snand->nand_chip;
struct mtd_part_parser_data ppdata = {};
struct mtd_info *mtd = &snand->mtd;
struct device_node *np = snand->dev->of_node;
int ret;
/* Let's check all the hooks are in-place so we don't panic later */
ret = spi_nand_check(snand);
if (ret)
return ret;
chip->priv = snand;
chip->read_buf = spi_nand_read_buf;
chip->write_buf = spi_nand_write_buf;
chip->read_byte = spi_nand_read_byte;
chip->cmdfunc = spi_nand_cmdfunc;
chip->waitfunc = spi_nand_waitfunc;
chip->select_chip = spi_nand_select_chip;
chip->options |= NAND_NO_SUBPAGE_WRITE;
chip->bits_per_cell = 1;
chip->ecc.read_page = spi_nand_read_page_hwecc;
chip->ecc.write_page = spi_nand_write_page_hwecc;
chip->ecc.mode = NAND_ECC_HW;
if (of_get_nand_on_flash_bbt(np))
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
mtd->name = snand->name;
mtd->owner = THIS_MODULE;
mtd->priv = chip;
/* Allocate buffer to be used to read/write the internal registers */
snand->buf = kmalloc(SPI_NAND_CMD_BUF_LEN, GFP_KERNEL);
if (!snand->buf)
return -ENOMEM;
/* Preallocate buffer for flash identification (NAND_CMD_READID) */
snand->buf_size = SPI_NAND_CMD_BUF_LEN;
snand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);
ret = nand_scan_ident(mtd, 1, flash_ids);
if (ret)
return ret;
/*
* SPI NAND has on-die ECC, which means we can correct as much as
* we are required to. This must be done after identification of
* the device.
*/
chip->ecc.strength = chip->ecc_strength_ds;
chip->ecc.size = chip->ecc_step_ds;
/*
* Unlock all the device before calling nand_scan_tail. This is needed
* in case the in-flash bad block table needs to be created.
* We could override __nand_unlock(), but since it's not currently used
* by the NAND core we call this explicitly.
*/
snand->buf[0] = SPI_NAND_PROT_UNLOCK_ALL;
ret = snand->write_reg(snand, SPI_NAND_LOCK_REG, snand->buf);
if (ret)
return ret;
/* Free the buffer and allocate a good one, to fit a page plus OOB */
kfree(snand->data_buf);
snand->buf_size = mtd->writesize + mtd->oobsize;
snand->data_buf = kmalloc(snand->buf_size, GFP_KERNEL);
if (!snand->data_buf)
return -ENOMEM;
ret = nand_scan_tail(mtd);
if (ret)
return ret;
ppdata.of_node = np;
return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
}
EXPORT_SYMBOL_GPL(spi_nand_register);
void spi_nand_unregister(struct spi_nand *snand)
{
kfree(snand->buf);
kfree(snand->data_buf);
nand_release(&snand->mtd);
}
EXPORT_SYMBOL_GPL(spi_nand_unregister);
MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@imgtec.com>");
MODULE_DESCRIPTION("Framework for SPI NAND");
MODULE_LICENSE("GPL v2");