/* I am a newer to the MTD,This template is just write for study,I can not promise it can
*working better,but I think it can help some newer like me to understand the nand driver easy
*thank you for reading,if you have some suggestion,send me a mail:fansongsen@163.com or my QQ:735308843
*/
#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/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <plat/regs-nand.h>
#include <plat/nand.h>
#define REGS_BASE 0x0
#define REGS_SIZE 0x8
struct tt_nand{
//those are the two main structure when you use the mtd
struct mtd_info mtd;
struct nand_chip chip;
struct nand_hw_control controller; //this is for the nand internal use
//for this template,you only know the register base
void __iomem *regs_base; //this is our nand controler register base
}ttt_nand;
//this is the mtd_partition table of you nand chip,notice the size
static struct mtd_partition ttt_nand_part[] = {
[0] = {
.name = "part0",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "part1",
.offset = 0x00040000,
.size = 0x00020000,
},
[2] = {
.name = "part2",
.offset = 0x00060000,
.size = 0x00500000,
}
};
static void ttt_nand_select_chip(struct mtd_info *mtd, int chip) //for this template, this function is no use because we assume one chip
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
//now you can select the chip
}
static void ttt_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) //read data from the data register
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
//read the nand read buffer
}
static void ttt_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) //write data to the data register
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
//write the data to the buffer
}
static void ttt_nand_hwcontrol(struct mtd_info *mtd, int cmd,unsigned int ctrl) //send the nand command
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
writeb(cmd, regs_base + TTT_NFCMD);
else
writeb(cmd, regs_base + TTT_NFADDR);
}
static int ttt_nand_devready(struct mtd_info *mtd) //for poll
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
return readb(regs_base + TTT_NFSTAT) & TTT_NFSTAT_BUSY;
}
/*this mainly init the nand controler register based on the mapped ttt_nand.regs_base*/
/*mainly use the two kernel functions:writel(addr,val) and readl(addr)*/
void ttt_nand_hw_init(struct tt_nand ttt_nand)
{
//caculate the nand controler clock freq and enable it
//init the nand controler register
}
static int __init s3c2410_nand_init(void) //this mainly for the kernel used structure init,it was simple enough
{
ttt_nand.regs_base=ioremap(REGS_BASE,REGS_SIZE);
ttt_nand_hw_init(ttt_nand);
spin_lock_init(&ttt_nand.controller.lock);
init_waitqueue_head(&ttt_nand.controller.wq);
ttt_nand.chip.write_buf = ttt_nand_write_buf;
ttt_nand.chip.read_buf = ttt_nand_read_buf;
ttt_nand.chip.select_chip = ttt_nand_select_chip;
ttt_nand.chip.chip_delay = 50;
ttt_nand.chip.priv = &ttt_nand;
ttt_nand.chip.options = 0;
ttt_nand.chip.controller = &ttt_nand.controller;
ttt_nand.chip.IO_ADDR_W = TTT_NAND_DATAR;
ttt_nand.chip.cmd_ctrl = ttt_nand_hwcontrol;
ttt_nand.chip.dev_ready = ttt_nand_devready;
ttt_nand.chip.IO_ADDR_R = ttt_nand.chip.IO_ADDR_W;
ttt_nand.chip.ecc.mode = NAND_ECC_SOFT;
ttt_nand.mtd.priv = &ttt_nand.chip;
ttt_nand.mtd.owner = THIS_MODULE;
if(0==nand_scan_ident(&ttt_nand.mtd,1))
{
nand_scan_tail(&ttt_nand.mtd);
add_mtd_partitions(&ttt_nand.mtd, ttt_nand_part, ARRAY_SIZE(ttt_nand_part));
add_mtd_device(&ttt_nand.mtd);
}
return 0;
}
static void __exit s3c2410_nand_exit(void)
{
nand_release(&ttt_nand.mtd);
}
module_init(ttt_nand_init);
module_exit(ttt_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("fansongsen@163.com");
MODULE_DESCRIPTION("A Template of MTD NAND driver");
*working better,but I think it can help some newer like me to understand the nand driver easy
*thank you for reading,if you have some suggestion,send me a mail:fansongsen@163.com or my QQ:735308843
*/
#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/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <plat/regs-nand.h>
#include <plat/nand.h>
#define REGS_BASE 0x0
#define REGS_SIZE 0x8
struct tt_nand{
//those are the two main structure when you use the mtd
struct mtd_info mtd;
struct nand_chip chip;
struct nand_hw_control controller; //this is for the nand internal use
//for this template,you only know the register base
void __iomem *regs_base; //this is our nand controler register base
}ttt_nand;
//this is the mtd_partition table of you nand chip,notice the size
static struct mtd_partition ttt_nand_part[] = {
[0] = {
.name = "part0",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "part1",
.offset = 0x00040000,
.size = 0x00020000,
},
[2] = {
.name = "part2",
.offset = 0x00060000,
.size = 0x00500000,
}
};
static void ttt_nand_select_chip(struct mtd_info *mtd, int chip) //for this template, this function is no use because we assume one chip
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
//now you can select the chip
}
static void ttt_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) //read data from the data register
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
//read the nand read buffer
}
static void ttt_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) //write data to the data register
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
//write the data to the buffer
}
static void ttt_nand_hwcontrol(struct mtd_info *mtd, int cmd,unsigned int ctrl) //send the nand command
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
writeb(cmd, regs_base + TTT_NFCMD);
else
writeb(cmd, regs_base + TTT_NFADDR);
}
static int ttt_nand_devready(struct mtd_info *mtd) //for poll
{
void __iomem *regs_base=mtd->priv->priv->regs_base;
return readb(regs_base + TTT_NFSTAT) & TTT_NFSTAT_BUSY;
}
/*this mainly init the nand controler register based on the mapped ttt_nand.regs_base*/
/*mainly use the two kernel functions:writel(addr,val) and readl(addr)*/
void ttt_nand_hw_init(struct tt_nand ttt_nand)
{
//caculate the nand controler clock freq and enable it
//init the nand controler register
}
static int __init s3c2410_nand_init(void) //this mainly for the kernel used structure init,it was simple enough
{
ttt_nand.regs_base=ioremap(REGS_BASE,REGS_SIZE);
ttt_nand_hw_init(ttt_nand);
spin_lock_init(&ttt_nand.controller.lock);
init_waitqueue_head(&ttt_nand.controller.wq);
ttt_nand.chip.write_buf = ttt_nand_write_buf;
ttt_nand.chip.read_buf = ttt_nand_read_buf;
ttt_nand.chip.select_chip = ttt_nand_select_chip;
ttt_nand.chip.chip_delay = 50;
ttt_nand.chip.priv = &ttt_nand;
ttt_nand.chip.options = 0;
ttt_nand.chip.controller = &ttt_nand.controller;
ttt_nand.chip.IO_ADDR_W = TTT_NAND_DATAR;
ttt_nand.chip.cmd_ctrl = ttt_nand_hwcontrol;
ttt_nand.chip.dev_ready = ttt_nand_devready;
ttt_nand.chip.IO_ADDR_R = ttt_nand.chip.IO_ADDR_W;
ttt_nand.chip.ecc.mode = NAND_ECC_SOFT;
ttt_nand.mtd.priv = &ttt_nand.chip;
ttt_nand.mtd.owner = THIS_MODULE;
if(0==nand_scan_ident(&ttt_nand.mtd,1))
{
nand_scan_tail(&ttt_nand.mtd);
add_mtd_partitions(&ttt_nand.mtd, ttt_nand_part, ARRAY_SIZE(ttt_nand_part));
add_mtd_device(&ttt_nand.mtd);
}
return 0;
}
static void __exit s3c2410_nand_exit(void)
{
nand_release(&ttt_nand.mtd);
}
module_init(ttt_nand_init);
module_exit(ttt_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("fansongsen@163.com");
MODULE_DESCRIPTION("A Template of MTD NAND driver");