nand
This commit is contained in:
parent
207b4c8202
commit
ead200eafe
@ -98,4 +98,12 @@
|
|||||||
reg = <0x80200000 0x1000>;
|
reg = <0x80200000 0x1000>;
|
||||||
interrupts = <22>;
|
interrupts = <22>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nand0: nand@a4000000 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
compatible = "cavium,cnc1800l-nand";
|
||||||
|
reg = <0x80100000 0x48>, //SMC
|
||||||
|
<0xa4000000 0x2000000>; //NAND
|
||||||
|
};
|
||||||
};
|
};
|
@ -38,6 +38,13 @@ config MTD_NAND_AMS_DELTA
|
|||||||
help
|
help
|
||||||
Support for NAND flash on Amstrad E3 (Delta).
|
Support for NAND flash on Amstrad E3 (Delta).
|
||||||
|
|
||||||
|
config MTD_NAND_CELESTIAL_CNC1800L
|
||||||
|
tristate "Celestial CNC1800L NAND"
|
||||||
|
depends on MACH_CELESTIAL || COMPILE_TEST
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Support for NAND flash on Celestial CNC1800L.
|
||||||
|
|
||||||
config MTD_NAND_OMAP2
|
config MTD_NAND_OMAP2
|
||||||
tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller"
|
tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller"
|
||||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
||||||
|
@ -5,6 +5,7 @@ obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
|||||||
|
|
||||||
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
|
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_CELESTIAL_CNC1800L) += cnc1800l-nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
|
obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
|
||||||
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
|
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
|
||||||
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
|
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
|
||||||
|
505
drivers/mtd/nand/raw/cnc1800l-nand.c
Normal file
505
drivers/mtd/nand/raw/cnc1800l-nand.c
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
/*
|
||||||
|
* drivers/mtd/nand/cnc_nand.c
|
||||||
|
*
|
||||||
|
* Copyrigth (C) 2010 Celestial Semiconductor
|
||||||
|
* 2011 Cavium
|
||||||
|
* Author: xiaodong fan <xiaodong.fan@caviumnetworks.com>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Overview:
|
||||||
|
* This is a device driver for the NAND flash device found on the
|
||||||
|
* Celestial CNC18xx SOC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/mtd/rawnand.h>
|
||||||
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "cnc1800l_nand"
|
||||||
|
|
||||||
|
#define CE0_CFG_0_OFFSET 0
|
||||||
|
#define CE0_CFG_1_OFFSET 0x04
|
||||||
|
#define CE0_CFG_2_OFFSET 0x08
|
||||||
|
|
||||||
|
#define CE1_CFG_0_OFFSET 0x0C
|
||||||
|
#define CE1_CFG_1_OFFSET 0x10
|
||||||
|
#define CE1_CFG_2_OFFSET 0x14
|
||||||
|
|
||||||
|
|
||||||
|
#define CE2_CFG_0_OFFSET 0x18
|
||||||
|
#define CE2_CFG_1_OFFSET 0x1C
|
||||||
|
#define CE2_CFG_2_OFFSET 0x20
|
||||||
|
|
||||||
|
|
||||||
|
#define CE3_CFG_0_OFFSET 0x24
|
||||||
|
#define CE3_CFG_1_OFFSET 0x28
|
||||||
|
#define CE3_CFG_2_OFFSET 0x2c
|
||||||
|
|
||||||
|
#define STATUS_OFFSET 0x30
|
||||||
|
#define MODE_OFFSET 0x38
|
||||||
|
|
||||||
|
#define ECC_CODE_OFFSET 0x40
|
||||||
|
#define ECC_CNT_OFFSET 0x44
|
||||||
|
#define ECC_A_OFFSET 0x48
|
||||||
|
|
||||||
|
#define MASK_ALE 0x8
|
||||||
|
#define MASK_CLE 0x4
|
||||||
|
|
||||||
|
#define MASK_CE_FORCE_LOW 0x200
|
||||||
|
|
||||||
|
#define PADDR 0x80100000
|
||||||
|
|
||||||
|
static struct mtd_partition cnc_nand_partitions[] = {
|
||||||
|
{
|
||||||
|
.name = "cavm_miniloader",
|
||||||
|
.offset = 0,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_uboot1",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_512K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_uboot1_pad",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_512K,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_nvram_factory",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_nvram_factory_pad",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_nvram1b",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_nvram2",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_nvram2b",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_splash",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_2M,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_all_img1_info",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_all_img1_info_pad",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_128K,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_netHD_Image1",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_32M,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_netHD_Image2",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_32M,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_free1",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_16M + SZ_2M + SZ_1M + SZ_512K, /* 19.5M */
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_free1_pad",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_256K,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_netHD_img2_info",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_256K,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "cavm_blob",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_4M - SZ_1M,
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
}, {
|
||||||
|
.name = "cavm_uboot2",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_1M,
|
||||||
|
// #ifdef UBOOT_UPGRADE_ALLOWED
|
||||||
|
// .mask_flags = 0,
|
||||||
|
// #else
|
||||||
|
.mask_flags = MTD_WRITEABLE,/*Read only*/
|
||||||
|
// #endif
|
||||||
|
}, {
|
||||||
|
.name = "cavm_ffs",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_32M,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}, {
|
||||||
|
.name = "customer_area",
|
||||||
|
.offset = MTDPART_OFS_APPEND,
|
||||||
|
.size = SZ_4M, //MTDPART_SIZ_FULL,
|
||||||
|
.mask_flags = 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cnc_nand_info {
|
||||||
|
struct nand_chip *chip;
|
||||||
|
|
||||||
|
struct device *dev;
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
|
void __iomem *smcbase;
|
||||||
|
void __iomem *iobase;
|
||||||
|
|
||||||
|
uint32_t mask_ale;
|
||||||
|
uint32_t mask_cle;
|
||||||
|
|
||||||
|
struct nand_controller controller;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned int cnc_nand_readl(struct cnc_nand_info *info,
|
||||||
|
int offset)
|
||||||
|
{
|
||||||
|
return __raw_readl(info->smcbase + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cnc_nand_writel(struct cnc_nand_info *info,
|
||||||
|
int offset, unsigned long value)
|
||||||
|
{
|
||||||
|
__raw_writel(value, info->smcbase + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cnc_nand_1bitecc_hwctl(struct nand_chip *chip, int mode)
|
||||||
|
{
|
||||||
|
struct cnc_nand_info *info = chip->priv;
|
||||||
|
unsigned long ecc_addr;
|
||||||
|
|
||||||
|
|
||||||
|
if (mode == NAND_ECC_READ){
|
||||||
|
ecc_addr = (unsigned long)((u32)PADDR + ((u32)chip->legacy.IO_ADDR_R - (u32)info->iobase));
|
||||||
|
cnc_nand_writel(info, ECC_A_OFFSET, ecc_addr);
|
||||||
|
// CNC_DEBUG("HW Read ECC enable--0x%x\n",(u32)ecc_addr);
|
||||||
|
}
|
||||||
|
else if (mode == NAND_ECC_WRITE) {
|
||||||
|
ecc_addr = (unsigned long)((u32)PADDR + ((u32)chip->legacy.IO_ADDR_W - (u32)info->iobase));
|
||||||
|
cnc_nand_writel(info, ECC_A_OFFSET, ecc_addr);
|
||||||
|
// CNC_DEBUG("HW Write ECC enable--0x%x\n",(u32)ecc_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read hardware ECC value and pack into three bytes
|
||||||
|
*/
|
||||||
|
static int cnc_nand_1bitecc_calculate(struct nand_chip *chip,
|
||||||
|
const uint8_t *dat, uint8_t *ecc_code)
|
||||||
|
{
|
||||||
|
struct cnc_nand_info *info = chip->priv;
|
||||||
|
unsigned int ecc24 = cnc_nand_readl(info, ECC_CODE_OFFSET);
|
||||||
|
|
||||||
|
ecc_code[0] = (uint8_t)(ecc24);
|
||||||
|
ecc_code[1] = (uint8_t)(ecc24 >> 8);
|
||||||
|
ecc_code[2] = (uint8_t)(ecc24 >> 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cnc_nand_1bitecc_correct(struct nand_chip *chip, uint8_t *dat,
|
||||||
|
uint8_t *read_ecc, uint8_t *calc_ecc)
|
||||||
|
{
|
||||||
|
// struct nand_chip *chip = mtd->priv;
|
||||||
|
uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
|
||||||
|
(read_ecc[2] << 16);
|
||||||
|
uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
|
||||||
|
(calc_ecc[2] << 16);
|
||||||
|
uint32_t diff = eccCalc ^ eccNand;
|
||||||
|
unsigned int bit, byte;
|
||||||
|
|
||||||
|
|
||||||
|
// CNC_DEBUG("eccNand=0x%x, eccCalc=0x%x, diff=0x%x\n", eccNand, eccCalc, diff);
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
if ((((diff >> 1) ^ diff) & 0x555555) == 0x555555) {
|
||||||
|
/* Correctable error */
|
||||||
|
|
||||||
|
/* calculate the bit position of the error */
|
||||||
|
bit = ((diff >> 19) & 1) |
|
||||||
|
((diff >> 20) & 2) |
|
||||||
|
((diff >> 21) & 4);
|
||||||
|
/* calculate the byte position of the error */
|
||||||
|
|
||||||
|
byte = ((diff >> 9) & 0x100) |
|
||||||
|
(diff & 0x80) |
|
||||||
|
((diff << 1) & 0x40) |
|
||||||
|
((diff << 2) & 0x20) |
|
||||||
|
((diff << 3) & 0x10) |
|
||||||
|
((diff >> 12) & 0x08) |
|
||||||
|
((diff >> 11) & 0x04) |
|
||||||
|
((diff >> 10) & 0x02) |
|
||||||
|
((diff >> 9) & 0x01);
|
||||||
|
|
||||||
|
// CNC_DEBUG("byte=%d, bit=%d, dat[byte]=0x%2x\n",byte,bit,dat[byte]);
|
||||||
|
|
||||||
|
dat[byte] ^= (1 << bit);
|
||||||
|
|
||||||
|
|
||||||
|
// CNC_DEBUG("corrected dat[byte]=0x%x\n",dat[byte]);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if (!(diff & (diff - 1))) {
|
||||||
|
/* Single bit ECC error in the ECC itself,
|
||||||
|
* nothing to fix */
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
/* Uncorrectable error */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cnc_nand_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl)
|
||||||
|
{
|
||||||
|
|
||||||
|
struct cnc_nand_info *info = chip->priv;
|
||||||
|
struct nand_chip *nand = chip;
|
||||||
|
uint32_t addr = (uint32_t __force)info->iobase;
|
||||||
|
|
||||||
|
if (ctrl & NAND_CTRL_CHANGE) {
|
||||||
|
if (ctrl & NAND_NCE){
|
||||||
|
(* (unsigned int *) (info->smcbase)) |= MASK_CE_FORCE_LOW;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(* (unsigned int *) (info->smcbase)) &= ~(MASK_CE_FORCE_LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
//command latch enable, address latch enable
|
||||||
|
//these are controlled by address line
|
||||||
|
if (ctrl & NAND_CLE){
|
||||||
|
addr |= info->mask_cle;
|
||||||
|
}
|
||||||
|
else if (ctrl & NAND_ALE) {
|
||||||
|
addr |= info->mask_ale;
|
||||||
|
}
|
||||||
|
nand->legacy.IO_ADDR_W = (void __iomem __force *)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (cmd != NAND_CMD_NONE)
|
||||||
|
iowrite8(cmd, nand->legacy.IO_ADDR_W);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cnc_nand_device_ready(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
struct cnc_nand_info *info = chip->priv;
|
||||||
|
|
||||||
|
return cnc_nand_readl(info, STATUS_OFFSET) & BIT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cnc_nand_select_chip(struct nand_chip *chip, int cs)
|
||||||
|
{
|
||||||
|
struct cnc_nand_info *info = chip->priv;
|
||||||
|
|
||||||
|
//-1: ctrl change
|
||||||
|
if (cs == -1) {
|
||||||
|
chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->legacy.IO_ADDR_W = info->iobase;
|
||||||
|
chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cnc_nand_read_buf(struct nand_chip *chip, u8 *buf, int len)
|
||||||
|
{
|
||||||
|
__raw_readsb(chip->legacy.IO_ADDR_R, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cnc_nand_write_buf(struct nand_chip *chip, const u8 *buf, int len)
|
||||||
|
{
|
||||||
|
__raw_writesb(chip->legacy.IO_ADDR_W, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cnc_nand_attach_chip(struct nand_chip *chip)
|
||||||
|
{
|
||||||
|
chip->ecc.calculate = cnc_nand_1bitecc_calculate;
|
||||||
|
chip->ecc.correct = cnc_nand_1bitecc_correct;
|
||||||
|
chip->ecc.hwctl = cnc_nand_1bitecc_hwctl;
|
||||||
|
chip->ecc.strength = 1;
|
||||||
|
chip->ecc.bytes = 3;
|
||||||
|
chip->ecc.size = 512;
|
||||||
|
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct nand_controller_ops cnc_nand_controller_ops = {
|
||||||
|
.attach_chip = cnc_nand_attach_chip,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cnc1800l_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct nand_chip *this;
|
||||||
|
struct cnc_nand_info *pdata;
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
struct resource *res1;
|
||||||
|
struct resource *res2;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
this = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!this){
|
||||||
|
dev_err(&pdev->dev, "cnc-nand: allocation failed\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata = devm_kzalloc(&pdev->dev, sizeof(struct cnc_nand_info), GFP_KERNEL);
|
||||||
|
|
||||||
|
if(!pdata)
|
||||||
|
goto nopdata;
|
||||||
|
|
||||||
|
this->priv = pdata;
|
||||||
|
|
||||||
|
pdata->chip = this;
|
||||||
|
|
||||||
|
pdata->dev = &pdev->dev;
|
||||||
|
mtd = nand_to_mtd(this);
|
||||||
|
|
||||||
|
pdata->mask_ale = MASK_ALE;
|
||||||
|
pdata->mask_cle = MASK_CLE;
|
||||||
|
|
||||||
|
mtd->dev.parent = &pdev->dev;
|
||||||
|
mtd->name = DRIVER_NAME;
|
||||||
|
mtd->priv = &pdata->chip;
|
||||||
|
|
||||||
|
this->legacy.chip_delay = 0;
|
||||||
|
|
||||||
|
nand_set_controller_data(this, pdata);
|
||||||
|
nand_set_flash_node(this, pdev->dev.of_node);
|
||||||
|
|
||||||
|
this->options |= NAND_KEEP_TIMINGS | NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK | NAND_BBT_WRITE;
|
||||||
|
|
||||||
|
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
pdata->smcbase = devm_ioremap_resource(&pdev->dev, res1);
|
||||||
|
if(IS_ERR(pdata->smcbase)){
|
||||||
|
err = PTR_ERR(pdata->smcbase);
|
||||||
|
goto err_res1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
pdata->iobase = devm_ioremap_resource(&pdev->dev, res2);
|
||||||
|
if (IS_ERR(pdata->iobase)){
|
||||||
|
err = PTR_ERR(pdata->iobase);
|
||||||
|
goto err_res2;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->controller = &pdata->controller;
|
||||||
|
|
||||||
|
this->legacy.IO_ADDR_R = pdata->iobase;
|
||||||
|
this->legacy.IO_ADDR_W = pdata->iobase;
|
||||||
|
|
||||||
|
this->legacy.select_chip = cnc_nand_select_chip;
|
||||||
|
this->legacy.cmd_ctrl = cnc_nand_cmd_ctrl;
|
||||||
|
this->legacy.dev_ready = cnc_nand_device_ready;
|
||||||
|
|
||||||
|
|
||||||
|
this->legacy.read_buf = cnc_nand_read_buf;
|
||||||
|
this->legacy.write_buf = cnc_nand_write_buf;
|
||||||
|
|
||||||
|
this->controller->ops = &cnc_nand_controller_ops;
|
||||||
|
err = nand_scan(this, 1);
|
||||||
|
if (err){
|
||||||
|
dev_err(&pdev->dev, "cnc-nand: failed to scan.\n");
|
||||||
|
goto escan;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mtd_device_register(mtd, cnc_nand_partitions, ARRAY_SIZE(cnc_nand_partitions));
|
||||||
|
if (err){
|
||||||
|
dev_err(&pdev->dev, "cnc-nand: failed to register partitions.\n");
|
||||||
|
goto cleanup_nand;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, pdata);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
|
escan:
|
||||||
|
cleanup_nand:
|
||||||
|
nand_cleanup(this);
|
||||||
|
err_res2:
|
||||||
|
err_res1:
|
||||||
|
kfree(pdata);
|
||||||
|
nopdata:
|
||||||
|
kfree(this);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cnc1800l_nand__remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct cnc_nand_info *info = platform_get_drvdata(pdev);
|
||||||
|
struct nand_chip *chip = info->chip;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mtd_device_unregister(nand_to_mtd(chip));
|
||||||
|
WARN_ON(ret);
|
||||||
|
nand_cleanup(chip);
|
||||||
|
kfree(chip->priv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id cnc1800l_nand_dt_ids[] = {
|
||||||
|
{ .compatible = "cavium,cnc1800l-nand", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, cnc1800l_nand_dt_ids);
|
||||||
|
|
||||||
|
|
||||||
|
static struct platform_driver cnc1800l_nand_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.of_match_table = cnc1800l_nand_dt_ids,
|
||||||
|
},
|
||||||
|
.probe = cnc1800l_nand_probe,
|
||||||
|
.remove = cnc1800l_nand__remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(cnc1800l_nand_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("xiaodong fan <xiaodong.fan@caviumnetworks.com>");
|
||||||
|
MODULE_DESCRIPTION("NAND flash driver for Cavium celestial CNC18xx serials SOC");
|
||||||
|
MODULE_ALIAS("platform:cnc1800l-nand");
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user