Release 4.11 drivers/mtd/nand/nuc900_nand.c
/*
* Copyright © 2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
*
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#define REG_FMICSR 0x00
#define REG_SMCSR 0xa0
#define REG_SMISR 0xac
#define REG_SMCMD 0xb0
#define REG_SMADDR 0xb4
#define REG_SMDATA 0xb8
#define RESET_FMI 0x01
#define NAND_EN 0x08
#define READYBUSY (0x01 << 18)
#define SWRST 0x01
#define PSIZE (0x01 << 3)
#define DMARWEN (0x03 << 1)
#define BUSWID (0x01 << 4)
#define ECC4EN (0x01 << 5)
#define WP (0x01 << 24)
#define NANDCS (0x01 << 25)
#define ENDADDR (0x01 << 31)
#define read_data_reg(dev) \
__raw_readl((dev)->reg + REG_SMDATA)
#define write_data_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMDATA)
#define write_cmd_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMCMD)
#define write_addr_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMADDR)
struct nuc900_nand {
struct nand_chip chip;
void __iomem *reg;
struct clk *clk;
spinlock_t lock;
};
static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd)
{
return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Boris Brezillon | 28 | 100.00% | 2 | 100.00% |
Total | 28 | 100.00% | 2 | 100.00% |
static const struct mtd_partition partitions[] = {
{
.name = "NAND FS 0",
.offset = 0,
.size = 8 * 1024 * 1024
},
{
.name = "NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
};
static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd)
{
unsigned char ret;
struct nuc900_nand *nand = mtd_to_nuc900(mtd);
ret = (unsigned char)read_data_reg(nand);
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 35 | 87.50% | 1 | 33.33% |
Boris Brezillon | 3 | 7.50% | 1 | 33.33% |
David Woodhouse | 2 | 5.00% | 1 | 33.33% |
Total | 40 | 100.00% | 3 | 100.00% |
static void nuc900_nand_read_buf(struct mtd_info *mtd,
unsigned char *buf, int len)
{
int i;
struct nuc900_nand *nand = mtd_to_nuc900(mtd);
for (i = 0; i < len; i++)
buf[i] = (unsigned char)read_data_reg(nand);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 54 | 91.53% | 1 | 33.33% |
Boris Brezillon | 3 | 5.08% | 1 | 33.33% |
David Woodhouse | 2 | 3.39% | 1 | 33.33% |
Total | 59 | 100.00% | 3 | 100.00% |
static void nuc900_nand_write_buf(struct mtd_info *mtd,
const unsigned char *buf, int len)
{
int i;
struct nuc900_nand *nand = mtd_to_nuc900(mtd);
for (i = 0; i < len; i++)
write_data_reg(nand, buf[i]);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 51 | 91.07% | 1 | 33.33% |
Boris Brezillon | 3 | 5.36% | 1 | 33.33% |
David Woodhouse | 2 | 3.57% | 1 | 33.33% |
Total | 56 | 100.00% | 3 | 100.00% |
static int nuc900_check_rb(struct nuc900_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
val = __raw_readl(nand->reg + REG_SMISR);
val &= READYBUSY;
spin_unlock(&nand->lock);
return val;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 43 | 87.76% | 1 | 33.33% |
Arnd Bergmann | 4 | 8.16% | 1 | 33.33% |
David Woodhouse | 2 | 4.08% | 1 | 33.33% |
Total | 49 | 100.00% | 3 | 100.00% |
static int nuc900_nand_devready(struct mtd_info *mtd)
{
struct nuc900_nand *nand = mtd_to_nuc900(mtd);
int ready;
ready = (nuc900_check_rb(nand)) ? 1 : 0;
return ready;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 32 | 80.00% | 1 | 33.33% |
Boris Brezillon | 5 | 12.50% | 1 | 33.33% |
David Woodhouse | 3 | 7.50% | 1 | 33.33% |
Total | 40 | 100.00% | 3 | 100.00% |
static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
register struct nand_chip *chip = mtd_to_nand(mtd);
struct nuc900_nand *nand = mtd_to_nuc900(mtd);
if (command == NAND_CMD_READOOB) {
column += mtd->writesize;
command = NAND_CMD_READ0;
}
write_cmd_reg(nand, command & 0xff);
if (column != -1 || page_addr != -1) {
if (column != -1) {
if (chip->options & NAND_BUSWIDTH_16 &&
!nand_opcode_8bits(command))
column >>= 1;
write_addr_reg(nand, column);
write_addr_reg(nand, column >> 8 | ENDADDR);
}
if (page_addr != -1) {
write_addr_reg(nand, page_addr);
if (chip->chipsize > (128 << 20)) {
write_addr_reg(nand, page_addr >> 8);
write_addr_reg(nand, page_addr >> 16 | ENDADDR);
} else {
write_addr_reg(nand, page_addr >> 8 | ENDADDR);
}
}
}
switch (command) {
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (chip->dev_ready)
break;
udelay(chip->chip_delay);
write_cmd_reg(nand, NAND_CMD_STATUS);
write_cmd_reg(nand, command);
while (!nuc900_check_rb(nand))
;
return;
case NAND_CMD_RNDOUT:
write_cmd_reg(nand, NAND_CMD_RNDOUTSTART);
return;
case NAND_CMD_READ0:
write_cmd_reg(nand, NAND_CMD_READSTART);
default:
if (!chip->dev_ready) {
udelay(chip->chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay(100);
while (!chip->dev_ready(mtd))
;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 302 | 95.27% | 1 | 20.00% |
Boris Brezillon | 6 | 1.89% | 2 | 40.00% |
Brian Norris | 6 | 1.89% | 1 | 20.00% |
David Woodhouse | 3 | 0.95% | 1 | 20.00% |
Total | 317 | 100.00% | 5 | 100.00% |
static void nuc900_nand_enable(struct nuc900_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
__raw_writel(RESET_FMI, (nand->reg + REG_FMICSR));
val = __raw_readl(nand->reg + REG_FMICSR);
if (!(val & NAND_EN))
__raw_writel(val | NAND_EN, nand->reg + REG_FMICSR);
val = __raw_readl(nand->reg + REG_SMCSR);
val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS);
val |= WP;
__raw_writel(val, nand->reg + REG_SMCSR);
spin_unlock(&nand->lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 114 | 95.00% | 1 | 33.33% |
Dan Carpenter | 4 | 3.33% | 1 | 33.33% |
David Woodhouse | 2 | 1.67% | 1 | 33.33% |
Total | 120 | 100.00% | 3 | 100.00% |
static int nuc900_nand_probe(struct platform_device *pdev)
{
struct nuc900_nand *nuc900_nand;
struct nand_chip *chip;
struct mtd_info *mtd;
struct resource *res;
nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand),
GFP_KERNEL);
if (!nuc900_nand)
return -ENOMEM;
chip = &(nuc900_nand->chip);
mtd = nand_to_mtd(chip);
mtd->dev.parent = &pdev->dev;
spin_lock_init(&nuc900_nand->lock);
nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_nand->clk))
return -ENOENT;
clk_enable(nuc900_nand->clk);
chip->cmdfunc = nuc900_nand_command_lp;
chip->dev_ready = nuc900_nand_devready;
chip->read_byte = nuc900_nand_read_byte;
chip->write_buf = nuc900_nand_write_buf;
chip->read_buf = nuc900_nand_read_buf;
chip->chip_delay = 50;
chip->options = 0;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(nuc900_nand->reg))
return PTR_ERR(nuc900_nand->reg);
nuc900_nand_enable(nuc900_nand);
if (nand_scan(mtd, 1))
return -ENXIO;
mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions));
platform_set_drvdata(pdev, nuc900_nand);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 194 | 72.66% | 1 | 14.29% |
Jingoo Han | 27 | 10.11% | 1 | 14.29% |
David Woodhouse | 21 | 7.87% | 1 | 14.29% |
Boris Brezillon | 9 | 3.37% | 1 | 14.29% |
Rafał Miłecki | 8 | 3.00% | 1 | 14.29% |
Frans Klaver | 7 | 2.62% | 1 | 14.29% |
Jamie Iles | 1 | 0.37% | 1 | 14.29% |
Total | 267 | 100.00% | 7 | 100.00% |
static int nuc900_nand_remove(struct platform_device *pdev)
{
struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev);
nand_release(nand_to_mtd(&nuc900_nand->chip));
clk_disable(nuc900_nand->clk);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 27 | 64.29% | 1 | 25.00% |
Axel Lin | 7 | 16.67% | 1 | 25.00% |
David Woodhouse | 4 | 9.52% | 1 | 25.00% |
Boris Brezillon | 4 | 9.52% | 1 | 25.00% |
Total | 42 | 100.00% | 4 | 100.00% |
static struct platform_driver nuc900_nand_driver = {
.probe = nuc900_nand_probe,
.remove = nuc900_nand_remove,
.driver = {
.name = "nuc900-fmi",
},
};
module_platform_driver(nuc900_nand_driver);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nuc900-fmi");
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Wan ZongShun | 1105 | 86.40% | 2 | 13.33% |
Boris Brezillon | 61 | 4.77% | 3 | 20.00% |
David Woodhouse | 47 | 3.67% | 1 | 6.67% |
Jingoo Han | 27 | 2.11% | 1 | 6.67% |
Axel Lin | 9 | 0.70% | 2 | 13.33% |
Rafał Miłecki | 8 | 0.63% | 1 | 6.67% |
Frans Klaver | 7 | 0.55% | 1 | 6.67% |
Brian Norris | 6 | 0.47% | 1 | 6.67% |
Dan Carpenter | 4 | 0.31% | 1 | 6.67% |
Arnd Bergmann | 4 | 0.31% | 1 | 6.67% |
Jamie Iles | 1 | 0.08% | 1 | 6.67% |
Total | 1279 | 100.00% | 15 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.