?? s3c2410.c
字號:
/* linux/drivers/mtd/nand/s3c2410.c * * Copyright (c) 2004 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * * Samsung S3C2410 NAND driver * * Changelog: * 21-Sep-2004 BJD Initial version * 23-Sep-2004 BJD Mulitple device support * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode * 12-Oct-2004 BJD Fixed errors in use of platform data * * $Id: s3c2410.c,v 1.5 2004/10/12 10:10:15 bjd Exp $ * * 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; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include <config/mtd/nand/s3c2410/hwecc.h>#include <config/mtd/nand/s3c2410/debug.h>#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG#define DEBUG#endif#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/device.h>#include <linux/delay.h>#include <linux/err.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 <asm/mach-types.h>#include <asm/hardware/clock.h>#include <asm/arch/regs-nand.h>#include <asm/arch/nand.h>#define PFX "s3c2410-nand: "#ifdef CONFIG_MTD_NAND_S3C2410_HWECCstatic int hardware_ecc = 1;#elsestatic int hardware_ecc = 0;#endif/* new oob placement block for use with hardware ecc generation */static struct nand_oobinfo nand_hw_eccoob = { .useecc = MTD_NANDECC_AUTOPLACE, .eccbytes = 3, .eccpos = {0, 1, 2 }, .oobfree = { {8, 8} }};/* controller and mtd information */struct s3c2410_nand_info;struct s3c2410_nand_mtd { struct mtd_info mtd; struct nand_chip chip; struct s3c2410_nand_set *set; struct s3c2410_nand_info *info; int scan_res;};/* overview of the s3c2410 nand state */struct s3c2410_nand_info { /* mtd info */ struct nand_hw_control controller; struct s3c2410_nand_mtd *mtds; struct s3c2410_platform_nand *platform; /* device info */ struct device *device; struct resource *area; struct clk *clk; void *regs; int mtd_count;};/* conversion functions */static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd){ return container_of(mtd, struct s3c2410_nand_mtd, mtd);}static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd){ return s3c2410_nand_mtd_toours(mtd)->info;}static struct s3c2410_nand_info *to_nand_info(struct device *dev){ return (struct s3c2410_nand_info *)dev_get_drvdata(dev);}static struct s3c2410_platform_nand *to_nand_plat(struct device *dev){ return (struct s3c2410_platform_nand *)dev->platform_data;}/* timing calculations */#define NS_IN_KHZ 10000000static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max){ int result; result = (wanted * NS_IN_KHZ) / clk; result++; pr_debug("result %d from %ld, %d\n", result, clk, wanted); if (result > max) { printk("%d ns is too big for current clock rate %ld\n", wanted, clk); return -1; } if (result < 1) result = 1; return result;}#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ)/* controller setup */static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct device *dev){ struct s3c2410_platform_nand *plat = to_nand_plat(dev); unsigned int tacls, twrph0, twrph1; unsigned long clkrate = clk_get_rate(info->clk); unsigned long cfg; /* calculate the timing information for the controller */ if (plat != NULL) { tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); twrph1 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); } else { /* default timings */ tacls = 8; twrph0 = 8; twrph1 = 8; } if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { printk(KERN_ERR PFX "cannot get timings suitable for board\n"); return -EINVAL; } printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n", to_ns(tacls, clkrate), to_ns(twrph0, clkrate), to_ns(twrph1, clkrate)); cfg = S3C2410_NFCONF_EN; cfg |= S3C2410_NFCONF_TACLS(tacls-1); cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); writel(cfg, info->regs + S3C2410_NFCONF); return 0;}/* select chip */static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip){ struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct nand_chip *this = mtd->priv; unsigned long cur; nmtd = (struct s3c2410_nand_mtd *)this->priv; info = nmtd->info; cur = readl(info->regs + S3C2410_NFCONF); if (chip == -1) { cur |= S3C2410_NFCONF_nFCE; } else { if (chip > nmtd->set->nr_chips) { printk(KERN_ERR PFX "chip %d out of range\n", chip); return; } if (info->platform != NULL) { if (info->platform->select_chip != NULL) (info->platform->select_chip)(nmtd->set, chip); } cur &= ~S3C2410_NFCONF_nFCE; } writel(cur, info->regs + S3C2410_NFCONF);}/* command and control functions */static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd){ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); unsigned long cur; switch (cmd) { case NAND_CTL_SETNCE: cur = readl(info->regs + S3C2410_NFCONF); cur &= ~S3C2410_NFCONF_nFCE; writel(cur, info->regs + S3C2410_NFCONF); break; case NAND_CTL_CLRNCE: cur = readl(info->regs + S3C2410_NFCONF); cur |= S3C2410_NFCONF_nFCE; writel(cur, info->regs + S3C2410_NFCONF); break; /* we don't need to implement these */ case NAND_CTL_SETCLE: case NAND_CTL_CLRCLE: case NAND_CTL_SETALE: case NAND_CTL_CLRALE: pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd); break; }}/* s3c2410_nand_command * * This function implements sending commands and the relevant address * information to the chip, via the hardware controller. Since the * S3C2410 generates the correct ALE/CLE signaling automatically, we * do not need to use hwcontrol.*/static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr){ register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); register struct nand_chip *this = mtd->priv; /* * Write out the command to the device. */ if (command == NAND_CMD_SEQIN) { int readcmd; if (column >= mtd->oobblock) { /* OOB area */ column -= mtd->oobblock; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ readcmd = NAND_CMD_READ0; } else { column -= 256; readcmd = NAND_CMD_READ1; } writeb(readcmd, info->regs + S3C2410_NFCMD); } writeb(command, info->regs + S3C2410_NFCMD); /* Set ALE and clear CLE to start address cycle */ if (column != -1 || page_addr != -1) { /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ if (this->options & NAND_BUSWIDTH_16) column >>= 1; writeb(column, info->regs + S3C2410_NFADDR); } if (page_addr != -1) { writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR); writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR); /* One more address cycle for higher density devices */ if (this->chipsize & 0x0c000000) writeb((unsigned char) ((page_addr >> 16) & 0x0f), info->regs + S3C2410_NFADDR); } /* Latch in address */ } /* * program and erase have their own busy handlers * status and sequential in needs no delay */ switch (command) { case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: return; case NAND_CMD_RESET: if (this->dev_ready) break; udelay(this->chip_delay); writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD); while ( !(this->read_byte(mtd) & 0x40)); return; /* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given * command delay */ if (!this->dev_ready) { udelay (this->chip_delay); return; } }
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -