blob: 3e8b08bb308a3977b222ff51972a44bc02f8ba6f [file] [log] [blame]
/*
* Atheros AR71XX/AR724X/AR913X NAND controller driver
*
* Copyright (c) 2012 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/mach-ath79/ar71xx_regs.h>
/* module related macros */
#define DRV_NAME "ath79-nand"
#define DRV_VERSION "1.0"
#define DRV_AUTHOR "Qualcomm-Atheros Inc."
#define DRV_DESC "AR71xx/AR9xxx Qualcomm-Atheros NAND FLash Controller driver"
/* misc macros */
#define AR934X_NAND_STATUS_RETRY 1000
#define DDR_WB_FLUSH_USB_ADDRESS 0x180000a4
/* static data for ath79_nand driver configuration */
static const char *ath79_nand_part_probes[] __initdata =
{ "cmdlinepart", "RedBoot", NULL };
static struct mtd_partition ath79_nand_partitions[] = {
{
.name = "nand0",
.offset = 0,
.size = MTDPART_SIZ_FULL,
},
};
static struct nand_ecclayout ath79_nand_oob_64 = {
.eccbytes = 28,
.eccpos = {
20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47},
.oobfree = {
{.offset = 4,
.length = 16},
{.offset = 48,
.length = 16}
}
};
typedef struct {
uint8_t vid,
did,
b3,
addrcyc,
small,
spare; // for small block;
uint16_t pgsz; // for small block
uint32_t blk; // for small block
} ath_nand_vend_data_t;
ath_nand_vend_data_t ath_nand_arr[] = {
{ 0x20, 0xda, 0x10, 5, }, // NU2g3B2D
{ 0x20, 0xf1, 0x00, 4, }, // NU1g3B2C
{ 0x20, 0xdc, 0x10, 5, }, // NU4g3B2D
{ 0x20, 0xd3, 0x10, 5, }, // NU8g3F2A
{ 0x20, 0xd3, 0x14, 5, }, // NU8g3C2B
{ 0xad, 0xf1, 0x00, 4, }, // HY1g2b
{ 0xad, 0xda, 0x10, 5, }, // HY2g2b
{ 0xec, 0xf1, 0x00, 4, }, // Samsung 3,3V 8-bit [128MB]
{ 0x98, 0xd1, 0x90, 4, }, // Toshiba
{ 0xad, 0x76, 0xad, 5, 1, 16, 512, 16 << 10 }, // Hynix 64MB NAND Flash
{ 0xad, 0x36, 0xad, 5, 1, 16, 512, 16 << 10 }, // Hynix 64MB NAND Flash
{ 0x20, 0x76, 0x20, 5, 1, 16, 512, 16 << 10 }, // ST Micro 64MB NAND Flash
};
#define NUM_ARRAY_ENTRIES(a) (sizeof((a)) / sizeof((a)[0]))
#define NUM_ATH_NAND NUM_ARRAY_ENTRIES(ath_nand_arr)
/* ath nand controller DMA descriptor */
typedef struct {
uint32_t addr;
uint32_t len;
} ath79_nand_dma_descr;
/* ath nand priv info */
typedef struct {
struct nand_chip nand;
uint32_t nf_ctrl;
uint32_t command;
uint32_t curr_data;
uint32_t data_index;
uint8_t *page_buf;
void __iomem *io_base;
void __iomem *ddr_io_addr;
ath79_nand_dma_descr *dma_descr_virtaddr;
dma_addr_t dma_descr_phyaddr;
struct device *dev;
} ath79_nand_priv;
/****************************************/
/***** NAND chip internal functions *****/
/****************************************/
static int ath79_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, int rw,
int page);
static inline int ath79_oobbuf_is_dirty(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
uint8_t *p = chip->oob_poi;
int i;
for (i = 0; i < mtd->oobsize; i++) {
if (p[i] != 0xff)
return 1;
}
return 0;
}
static int ath79_page_no_ecc(struct mtd_info *mtd, int page)
{
struct nand_chip *chip = mtd->priv;
uint8_t *p = chip->oob_poi;
int i;
ath79_rw_oob(mtd, chip, 1 /*read */ , page);
for (i = 0; i < mtd->ecclayout->eccbytes; i++) {
if (p[mtd->ecclayout->eccpos[i]] != 0xff)
return 0;
}
return 1;
}
static int ath79_nand_hw_init(ath79_nand_priv * ath79_priv)
{
unsigned rddata;
void __iomem *tmp_io_addr;
// Put into reset
tmp_io_addr =
ioremap(AR71XX_RESET_BASE + AR934X_RESET_REG_RESET_MODULE, 4);
rddata = ioread32(tmp_io_addr);
iowrite32(rddata | AR934X_RESET_NANDF, tmp_io_addr);
udelay(250);
iowrite32(rddata & (~AR934X_RESET_NANDF), tmp_io_addr);
udelay(100);
iounmap(tmp_io_addr);
iowrite32(AR934X_NAND_CMD_END_INT,
ath79_priv->io_base + AR934X_NAND_REG_INT_MASK);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
// TIMINGS_ASYN Reg Settings
iowrite32(AR934X_NAND_TIMING_ASYN_SETTING,
ath79_priv->io_base + AR934X_NAND_REG_TIMINGS_ASYN);
iowrite32(AR934X_NAND_TIME_SEQ_SETTING,
ath79_priv->io_base + AR934X_NAND_REG_TIME_SEQ);
// NAND Mem Control Reg
iowrite32(0xff00, ath79_priv->io_base + AR934X_NAND_REG_MEM_CTRL);
return 0;
}
static int ath79_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, int rw,
int page)
{
ath79_nand_priv *ath79_priv = mtd->priv;
int i, dmastatus, status = 0;
int dir, dma_ctrl;
dma_addr_t pa;
dir = rw ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
pa = dma_map_single(NULL, chip->oob_poi, mtd->oobsize, dir);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
iowrite32((page << 16) | mtd->writesize,
ath79_priv->io_base + AR934X_NAND_REG_ADDR0_0);
iowrite32((page >> 16) & 0x0ff,
ath79_priv->io_base + AR934X_NAND_REG_ADDR0_1);
iowrite32(pa, ath79_priv->io_base + AR934X_NAND_REG_DMA_ADDR);
iowrite32(mtd->oobsize,
ath79_priv->io_base + AR934X_NAND_REG_DMA_COUNT);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_ECC_OFFSET);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_ECC_CTRL);
iowrite32(ath79_priv->nf_ctrl | AR934X_NAND_CTRL_CUSTOM_SIZE_EN,
ath79_priv->io_base + AR934X_NAND_REG_CTRL);
iowrite32(mtd->oobsize, ath79_priv->io_base + AR934X_NAND_REG_PG_SIZE);
dma_ctrl =
AR934X_NAND_DMA_CTRL_DMA_START | AR934X_NAND_DMA_CTRL_DMA_BURST_3;
if (rw) { // read OOB
iowrite32(dma_ctrl | AR934X_NAND_DMA_CTRL_DMA_DIR_READ,
ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
iowrite32(0x30006a,
ath79_priv->io_base + AR934X_NAND_REG_COMMAND);
ath79_priv->data_index = 0;
} else { // write OOB
iowrite32(0xff00, ath79_priv->io_base + AR934X_NAND_REG_MEM_CTRL); // Remove write protect
iowrite32(dma_ctrl | AR934X_NAND_DMA_CTRL_DMA_DIR_WRITE,
ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
iowrite32(0x10804c,
ath79_priv->io_base + AR934X_NAND_REG_COMMAND);
}
while ((ioread32(ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS) &
AR934X_NAND_CMD_END_INT) == 0) ;
status = ioread32(ath79_priv->io_base + AR934X_NAND_REG_STATUS);
for (i = 0; i < AR934X_NAND_STATUS_RETRY && status != 0xff; i++) {
udelay(2);
status = ioread32(ath79_priv->io_base + AR934X_NAND_REG_STATUS);
}
if (status != 0xff) {
dev_err(ath79_priv->dev,
"%s: NAND status error. Status = 0x%x\n", __func__,
status);
status = -EIO;
} else {
status = 0;
}
if (rw) {
nand_wait_ready(mtd); // wait for ready pin for read OOB
} else {
status = chip->waitfunc(mtd, chip); // check device status for write OOB
if (status & NAND_STATUS_FAIL) {
dev_err(ath79_priv->dev,
"%s: Write Failed. status = 0x%x\n", __func__,
status);
status = -EIO;
} else {
status = 0;
}
}
dmastatus = ioread32(ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
for (i = 0; i < AR934X_NAND_STATUS_RETRY && !(dmastatus & 1); i++) {
udelay(5);
dmastatus =
ioread32(ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
}
if (!(dmastatus & 1)) {
dev_err(ath79_priv->dev, "%s: unfinished DMA. status = 0x%x\n",
__func__, dmastatus);
ath79_nand_hw_init(ath79_priv);
/* is this for killing unfinished DMA ??? */
iowrite32(1, ath79_priv->ddr_io_addr);
while (ioread32(ath79_priv->ddr_io_addr) & 1) ;
udelay(2);
}
dma_unmap_single(NULL, pa, mtd->oobsize, dir);
iowrite32(1, ath79_priv->io_base + AR934X_NAND_REG_FIFO_INIT);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_FIFO_INIT);
return status;
}
static int ath79_rw_page(struct mtd_info *mtd, struct nand_chip *chip, int rw,
uint8_t * buf, int len, int page)
{
ath79_nand_priv *ath79_priv = mtd->priv;
int status = 0;
int i, dir, dmastatus, ecc_enable, ecc_status;
int dma_ctrl, copy = 0;
dma_addr_t pa1, pa2 = 0;
dir = rw ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
iowrite32(page << 16, ath79_priv->io_base + AR934X_NAND_REG_ADDR0_0);
iowrite32((page >> 16) & 0x0ff,
ath79_priv->io_base + AR934X_NAND_REG_ADDR0_1);
iowrite32(len, ath79_priv->io_base + AR934X_NAND_REG_DMA_COUNT);
dma_ctrl =
AR934X_NAND_DMA_CTRL_DMA_START | AR934X_NAND_DMA_CTRL_DMA_BURST_3;
if (is_vmalloc_addr(buf)) {
if (!rw)
memcpy(ath79_priv->page_buf, buf, mtd->writesize);
pa1 =
dma_map_single(NULL, ath79_priv->page_buf, mtd->writesize,
dir);
copy = 1;
} else {
pa1 = dma_map_single(NULL, buf, mtd->writesize, dir);
}
if (!rw)
dev_dbg(ath79_priv->dev,
"%s: read %d, page 0x%x, size=0x%x, virt buffer %d\n",
__func__, rw, page, len, copy);
if ((len & mtd->writesize_mask) == 0) {
/* non-scatter-gather DMA mode */
iowrite32(pa1, ath79_priv->io_base + AR934X_NAND_REG_DMA_ADDR);
iowrite32(mtd->writesize + chip->ecc.layout->eccpos[0],
ath79_priv->io_base + AR934X_NAND_REG_ECC_OFFSET);
iowrite32(AR934X_NAND_ECC_CORR_BITS(4),
ath79_priv->io_base + AR934X_NAND_REG_ECC_CTRL);
iowrite32(ath79_priv->nf_ctrl | AR934X_NAND_CTRL_ECC_EN,
ath79_priv->io_base + AR934X_NAND_REG_CTRL);
ecc_enable = 1;
} else {
/* scatter-gather DMA mode */
ath79_priv->dma_descr_virtaddr[0].addr = pa1;
ath79_priv->dma_descr_virtaddr[0].len = mtd->writesize << 16;
pa2 =
dma_map_single(NULL, chip->oob_poi, len - mtd->writesize,
dir);
ath79_priv->dma_descr_virtaddr[1].addr = pa2;
ath79_priv->dma_descr_virtaddr[1].len =
((len - mtd->writesize) << 16) | 0x01;
iowrite32(ath79_priv->dma_descr_phyaddr,
ath79_priv->io_base + AR934X_NAND_REG_DMA_ADDR);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_ECC_OFFSET);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_ECC_CTRL);
iowrite32(ath79_priv->nf_ctrl | AR934X_NAND_CTRL_CUSTOM_SIZE_EN,
ath79_priv->io_base + AR934X_NAND_REG_CTRL);
iowrite32(len, ath79_priv->io_base + AR934X_NAND_REG_PG_SIZE);
dma_ctrl |= AR934X_NAND_DMA_CTRL_DMA_MODE_SG;
ecc_enable = 0;
}
if (rw) { // read page
iowrite32(dma_ctrl | AR934X_NAND_DMA_CTRL_DMA_DIR_READ,
ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
iowrite32(0x30006a,
ath79_priv->io_base + AR934X_NAND_REG_COMMAND);
ath79_priv->data_index = 0;
} else { // write page
iowrite32(0xff00, ath79_priv->io_base + AR934X_NAND_REG_MEM_CTRL); // Remove write protect
iowrite32(dma_ctrl | AR934X_NAND_DMA_CTRL_DMA_DIR_WRITE,
ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
iowrite32(0x10804c,
ath79_priv->io_base + AR934X_NAND_REG_COMMAND);
}
while ((ioread32(ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS) &
AR934X_NAND_CMD_END_INT) == 0) ;
status = ioread32(ath79_priv->io_base + AR934X_NAND_REG_STATUS);
for (i = 0; i < AR934X_NAND_STATUS_RETRY && status != 0xff; i++) {
udelay(2);
status = ioread32(ath79_priv->io_base + AR934X_NAND_REG_STATUS);
}
if (status != 0xff) {
dev_err(ath79_priv->dev,
"%s: NAND status error. Status = 0x%x\n", __func__,
status);
status = -EIO;
} else {
status = 0;
}
if (rw) {
nand_wait_ready(mtd); // wait for ready pin for read transaction
} else {
status = chip->waitfunc(mtd, chip); // check device status for write transaction
if (status & NAND_STATUS_FAIL) {
dev_err(ath79_priv->dev,
"%s: Write Failed. status = 0x%x\n", __func__,
status);
status = -EIO;
} else {
status = 0;
}
}
dmastatus = ioread32(ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
for (i = 0; i < AR934X_NAND_STATUS_RETRY && !(dmastatus & 1); i++) {
udelay(5);
dmastatus =
ioread32(ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL);
}
if (!(dmastatus & 1)) {
dev_err(ath79_priv->dev, "%s: unfinished DMA. status = 0x%x\n",
__func__, dmastatus);
ath79_nand_hw_init(ath79_priv);
/* is this for killing unfinished DMA ??? */
iowrite32(1, ath79_priv->ddr_io_addr);
while (ioread32(ath79_priv->ddr_io_addr) & 1) ;
udelay(2);
}
if (rw && ecc_enable) {
ecc_status =
ioread32(ath79_priv->io_base + AR934X_NAND_REG_ECC_CTRL);
if (ecc_status & AR934X_NAND_ECC_ERROR) {
if (!ath79_page_no_ecc(mtd, page)) {
dev_err(ath79_priv->dev,
"%s: Read [%d @ 0x%x] uncorrectable errors. status = 0x%x\n",
__func__, len, page, ecc_status);
mtd->ecc_stats.failed++;
}
} else if (ecc_status & AR934X_NAND_ECC_CTRL_ERR_CORR) {
mtd->ecc_stats.corrected++;
}
}
if (ecc_enable)
dma_unmap_single(NULL, pa1, len, dir);
else {
dma_unmap_single(NULL, pa1, mtd->writesize, dir);
dma_unmap_single(NULL, pa2, len - mtd->writesize, dir);
}
if (rw && copy) // read page
memcpy(buf, ath79_priv->page_buf, mtd->writesize);
iowrite32(1, ath79_priv->io_base + AR934X_NAND_REG_FIFO_INIT);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_FIFO_INIT);
return status;
}
/****************************************/
/***** NAND chip private APIs ***********/
/****************************************/
static void ath79_cmdfunc(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
ath79_nand_priv *ath79_priv = mtd->priv;
int i, status;
ath79_priv->command = command;
switch (command) {
case NAND_CMD_STATUS:
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
iowrite32(0,
ath79_priv->io_base +
AR934X_NAND_REG_GENERIC_SEQ_CTRL);
iowrite32(0x07024, ath79_priv->io_base + AR934X_NAND_REG_COMMAND); // READ STATUS
while ((ioread32
(ath79_priv->io_base +
AR934X_NAND_REG_INT_STATUS) & AR934X_NAND_CMD_END_INT)
== 0) ;
break;
case NAND_CMD_READID:
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_ADDR0_0);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_ADDR0_1);
iowrite32(0x4, ath79_priv->io_base + AR934X_NAND_REG_PG_SIZE);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_DMA_CTRL); // disable DMA
iowrite32(ath79_priv->nf_ctrl | AR934X_NAND_CTRL_CUSTOM_SIZE_EN,
ath79_priv->io_base + AR934X_NAND_REG_CTRL);
iowrite32(0x9021, ath79_priv->io_base + AR934X_NAND_REG_COMMAND); // READ ID
while ((ioread32
(ath79_priv->io_base +
AR934X_NAND_REG_INT_STATUS) & AR934X_NAND_CMD_END_INT)
== 0) ;
udelay(10);
break;
case NAND_CMD_RESET:
dev_dbg(ath79_priv->dev, "NAND %s: Reset\n", __func__);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
iowrite32(0xff00, ath79_priv->io_base + AR934X_NAND_REG_COMMAND); // RESET
while ((ioread32
(ath79_priv->io_base +
AR934X_NAND_REG_INT_STATUS) & AR934X_NAND_CMD_END_INT)
== 0) ;
udelay(1000);
status = ioread32(ath79_priv->io_base + AR934X_NAND_REG_STATUS);
for (i = 0; i < AR934X_NAND_STATUS_RETRY && status != 0xff; i++) {
udelay(2);
status =
ioread32(ath79_priv->io_base +
AR934X_NAND_REG_STATUS);
}
break;
default:
break;
}
ath79_priv->data_index = 0;
return;
}
static void ath79_null(struct mtd_info *mtd)
{
return;
}
static int ath79_device_ready(struct mtd_info *mtd)
{
ath79_nand_priv *ath79_priv = mtd->priv;
int rddata;
rddata = ioread32(ath79_priv->io_base + AR934X_NAND_REG_STATUS);
return ((rddata == 0xff) ? 1 : 0);
}
static void ath79_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
{
ath79_nand_priv *ath79_priv = mtd->priv;
volatile int rddata, i;
volatile uint8_t *p = (volatile uint8_t *)&rddata;
for (i = 0; i < len; i++) {
if (((ath79_priv->data_index + i) % 4) == 0) {
rddata =
le32_to_cpu(ioread32
(ath79_priv->io_base +
AR934X_NAND_REG_FIFO_DATA));
if (ath79_priv->command == NAND_CMD_READID)
dev_dbg(ath79_priv->dev, "NAND %s: ReadID %x\n",
__func__, rddata);
ath79_priv->curr_data = rddata;
} else {
rddata = ath79_priv->curr_data;
}
buf[i] = p[(ath79_priv->data_index + i) % 4];
ath79_priv->data_index++;
}
}
static uint8_t ath79_read_byte(struct mtd_info *mtd)
{
ath79_nand_priv *ath79_priv = mtd->priv;
uint8_t rdbyte;
if (ath79_priv->command == NAND_CMD_STATUS) {
rdbyte =
ioread32(ath79_priv->io_base + AR934X_NAND_REG_RD_STATUS);
dev_dbg(ath79_priv->dev, "NAND: Read Status 0x%x\n", rdbyte);
} else {
ath79_read_buf(mtd, &rdbyte, 1);
}
return rdbyte;
}
static int ath79_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
struct nand_chip *chip = mtd->priv;
uint8_t *p = chip->oob_poi;
ath79_rw_oob(mtd, chip, 1 /*read */ , ofs >> chip->page_shift);
if (*p == 0xff)
return 0;
else
return 1;
}
static int ath79_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page, int sndcmd)
{
ath79_rw_oob(mtd, chip, 1 /*read */ , page);
return sndcmd;
}
static int ath79_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
return ath79_rw_oob(mtd, chip, 0 /*write */ , page);
}
static int ath79_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t * buf, int page)
{
return ath79_rw_page(mtd, chip, 1 /*read */ , buf,
mtd->writesize + mtd->oobsize, page);
}
static int ath79_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t * buf, int page)
{
return ath79_rw_page(mtd, chip, 1 /*read */ , buf, mtd->writesize,
page);
}
static int ath79_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t * buf, int page, int cached, int raw)
{
int status;
if (unlikely(raw))
ath79_rw_page(mtd, chip, 0 /*write */ , (uint8_t *) buf,
mtd->writesize + mtd->oobsize, page);
else {
if (ath79_oobbuf_is_dirty(mtd))
ath79_rw_oob(mtd, chip, 0 /*write */ , page);
ath79_rw_page(mtd, chip, 0 /*write */ , (uint8_t *) buf,
mtd->writesize, page);
}
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
memset(ath79_priv->page_buf, 0, mtd->writesize);
ath79_rw_page(mtd, chip, 1 /*read */ , ath79_priv->page_buf,
mtd->writesize, page)
if (memcmp(page, ath79_priv->page_buf, mtd->writesize))
return -EIO;
#endif
return 0;
}
static void ath79_erase_cmd(struct mtd_info *mtd, int page)
{
ath79_nand_priv *ath79_priv = mtd->priv;
dev_dbg(ath79_priv->dev, "%s: page 0x%x\n", __func__, page);
iowrite32(0, ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS);
iowrite32(page << 16, ath79_priv->io_base + AR934X_NAND_REG_ADDR0_0);
iowrite32((page >> 16) & 0x0ff,
ath79_priv->io_base + AR934X_NAND_REG_ADDR0_1);
iowrite32(0xff00, ath79_priv->io_base + AR934X_NAND_REG_MEM_CTRL);
iowrite32(ath79_priv->nf_ctrl,
ath79_priv->io_base + AR934X_NAND_REG_CTRL);
iowrite32(0xd0600e, ath79_priv->io_base + AR934X_NAND_REG_COMMAND); // block erase
while ((ioread32(ath79_priv->io_base + AR934X_NAND_REG_INT_STATUS) &
AR934X_NAND_CMD_END_INT) == 0) ;
return;
}
static ath_nand_vend_data_t *
ath79_get_entry( int *nand_id, ath_nand_vend_data_t *tbl, int count)
{
int i;
for (i = 0; i < count; i++, tbl ++) {
if ((nand_id[0] == tbl->vid) &&
(nand_id[1] == tbl->did)) {
return tbl;
}
}
return NULL;
}
/****************************/
/****** ath79_nand_remove *****/
/****************************/
static int __devexit ath79_nand_remove(struct platform_device *pdev)
{
ath79_nand_priv *ath79_priv;
struct mtd_info *mtd;
mtd = (struct mtd_info *)platform_get_drvdata(pdev);
if (!mtd)
return 0;
ath79_priv = mtd->priv;
nand_release(mtd);
mtd_device_unregister(mtd);
dma_free_coherent(NULL, AR934X_NAND_DMA_DESCR_SIZE * 2,
(void *)(ath79_priv->dma_descr_virtaddr),
ath79_priv->dma_descr_phyaddr);
iounmap(ath79_priv->io_base);
iounmap(ath79_priv->ddr_io_addr);
kfree(ath79_priv->page_buf);
kfree(mtd);
platform_set_drvdata(pdev, NULL);
return 0;
}
/***************************/
/****** ath79_nand_probe *****/
/***************************/
static int __devinit ath79_nand_probe(struct platform_device *pdev)
{
struct resource *res;
struct mtd_info *mtd = NULL;
ath79_nand_priv *ath79_priv = NULL;
ath_nand_vend_data_t *entry = NULL;
int nandid[2];
int err = 0;
dev_info(&pdev->dev,
DRV_DESC ", Version " DRV_VERSION
" (c) 2010 Atheros Communications, Ltd.\n");
mtd = kzalloc(sizeof(*mtd) + sizeof(ath79_nand_priv), GFP_KERNEL);
if (!mtd) {
dev_err(&pdev->dev, "no memory for flash driver\n");
err = -ENOMEM;
goto out_err_kzalloc;
}
ath79_priv = (ath79_nand_priv *) (&mtd[1]);
mtd->priv = ath79_priv;
mtd->name = DRV_NAME;
mtd->owner = THIS_MODULE;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "resource missing\n");
err = -EINVAL;
goto out_err_ioremap1;
}
ath79_priv->dev = &(pdev->dev);
ath79_priv->io_base = ioremap(res->start, resource_size(res));
if (ath79_priv->io_base == NULL) {
dev_err(&pdev->dev, "ioremap error\n");
err = -EIO;
goto out_err_ioremap1;
}
ath79_priv->ddr_io_addr = ioremap(DDR_WB_FLUSH_USB_ADDRESS, 4);
if (ath79_priv->ddr_io_addr == NULL) {
dev_err(&pdev->dev, "ioremap error\n");
err = -EIO;
goto out_err_ioremap2;
}
ath79_priv->dma_descr_virtaddr =
(void *)dma_alloc_coherent(NULL, sizeof(ath79_nand_dma_descr) * 2,
&(ath79_priv->dma_descr_phyaddr),
GFP_KERNEL);
if (ath79_priv->dma_descr_virtaddr == NULL) {
dev_err(&pdev->dev, "dma coherent memory alloc error\n");
err = -ENOMEM;
goto out_err_dma_alloc_coherent;
}
/* ath79_nand chip private functions */
ath79_priv->nand.cmdfunc = ath79_cmdfunc;
ath79_priv->nand.select_chip = (void *)ath79_null;
ath79_priv->nand.dev_ready = ath79_device_ready;
ath79_priv->nand.read_byte = ath79_read_byte;
ath79_priv->nand.read_buf = ath79_read_buf;
ath79_priv->nand.block_bad = ath79_block_bad;
/* enable flash based bad block table */
ath79_priv->nand.bbt_options =
NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB | NAND_BBT_SCAN2NDPAGE;
/* initialise the NAND controller hardware */
ath79_nand_hw_init(ath79_priv);
err = nand_scan_ident(mtd, 1, NULL);
if (err) {
dev_err(&pdev->dev, "nand scan identify error\n");
err = -ENXIO;
goto out_err_hw_init;
}
ath79_priv->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
if (!ath79_priv->page_buf) {
dev_err(&pdev->dev, "page buffer alloc error\n");
err = -ENOMEM;
goto out_err_hw_init;
}
/* Send the command for reading device ID */
ath79_priv->nand.cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
nandid[0] = ath79_priv->nand.read_byte(mtd);
nandid[1] = ath79_priv->nand.read_byte(mtd);
entry = ath79_get_entry(nandid, ath_nand_arr, NUM_ATH_NAND);
if(entry){
ath79_priv->nf_ctrl = AR934X_NAND_CTRL_ADDR_CYCLE0(entry->addrcyc);
}else
ath79_priv->nf_ctrl = AR934X_NAND_CTRL_ADDR_CYCLE0(4);
/* set nand controller page size */
switch (mtd->writesize) {
case 256:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_256;
break;
case 512:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_512;
break;
case 1024:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_1024;
break;
case 2048:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_2048;
break;
case 4096:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_4096;
break;
case 8192:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_8192;
break;
default:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_PAGE_SIZE_2048;
break;
}
/* set nand controller block size */
switch (mtd->erasesize / mtd->writesize) {
case 32:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_BLOCK_SIZE_32;
break;
case 64:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_BLOCK_SIZE_64;
break;
case 128:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_BLOCK_SIZE_128;
break;
case 256:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_BLOCK_SIZE_256;
break;
default:
ath79_priv->nf_ctrl |= AR934X_NAND_CTRL_BLOCK_SIZE_64;
break;
}
/* ath79_nand chip private ECC functions */
ath79_priv->nand.ecc.layout = &ath79_nand_oob_64;
ath79_priv->nand.ecc.mode = NAND_ECC_HW;
ath79_priv->nand.ecc.size = mtd->writesize;
ath79_priv->nand.ecc.bytes = 28;
ath79_priv->nand.ecc.hwctl = (void *)ath79_null;
ath79_priv->nand.ecc.calculate = (void *)ath79_null;
ath79_priv->nand.ecc.correct = (void *)ath79_null;
ath79_priv->nand.write_page = ath79_write_page;
ath79_priv->nand.ecc.write_oob = ath79_write_oob;
ath79_priv->nand.ecc.read_page = ath79_read_page;
ath79_priv->nand.ecc.read_page_raw = ath79_read_page_raw;
ath79_priv->nand.ecc.read_oob = ath79_read_oob;
ath79_priv->nand.erase_cmd = ath79_erase_cmd;
err = nand_scan_tail(mtd);
if (err) {
dev_err(&pdev->dev, "nand scan tail error %d\n", err);
err = -ENXIO;
goto out_err_nand_scan;
}
/* create NAND partition */
mtd_device_parse_register(mtd, ath79_nand_part_probes, 0,
ath79_nand_partitions,
ARRAY_SIZE(ath79_nand_partitions));
dev_info(&pdev->dev,
"====== NAND Parameters ======\n total size = 0x%llx, page = 0x%x block = 0x%x oob = 0x%x\n",
mtd->size, mtd->writesize, mtd->erasesize, mtd->oobsize);
/* save platform driver private data */
platform_set_drvdata(pdev, mtd);
return 0;
out_err_nand_scan:
kfree(ath79_priv->page_buf);
out_err_hw_init:
dma_free_coherent(NULL, AR934X_NAND_DMA_DESCR_SIZE * 2,
(void *)ath79_priv->dma_descr_virtaddr,
ath79_priv->dma_descr_phyaddr);
out_err_dma_alloc_coherent:
iounmap(ath79_priv->ddr_io_addr);
out_err_ioremap2:
iounmap(ath79_priv->io_base);
out_err_ioremap1:
kfree(mtd);
out_err_kzalloc:
return err;
}
static struct platform_driver ath79_nand_driver = {
.probe = ath79_nand_probe,
.remove = __devexit_p(ath79_nand_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
module_platform_driver(ath79_nand_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_ALIAS("platform:" DRV_NAME);