aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2006-05-11 17:35:28 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2006-05-11 17:35:28 -0400
commit179fdc3f8dec5757ddbebd95a1b493d65fa08671 (patch)
tree422f229859e847b5987ec7f0d757eecbdac2f6df
parentb04ecae3d62edea2af49fd40ab12435b5ad8a492 (diff)
[MTD] Basic NAND driver for AMD/NatSemi CS5535/CS5536 Geode companion chip
This lacks hardware ECC support and a few optimisations we're going to want fairly soon, but it works well enough to mount and use JFFS2. Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r--drivers/mtd/nand/Kconfig4
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/cs553x_nand.c293
3 files changed, 298 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index b735ab61c1e7..4a1db31aef6a 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -189,6 +189,10 @@ config MTD_NAND_SHARPSL
189 tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" 189 tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
190 depends on MTD_NAND && ARCH_PXA 190 depends on MTD_NAND && ARCH_PXA
191 191
192config MTD_NAND_CS553X
193 tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
194 depends on MTD_NAND && X86_PC && PCI
195
192config MTD_NAND_NANDSIM 196config MTD_NAND_NANDSIM
193 tristate "Support for NAND Flash Simulator" 197 tristate "Support for NAND Flash Simulator"
194 depends on MTD_NAND && MTD_PARTITIONS 198 depends on MTD_NAND && MTD_PARTITIONS
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 073705bc8c2d..0741d739cb87 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -19,5 +19,6 @@ obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
19obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o 19obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
20obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o 20obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
21obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o 21obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
22obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
22 23
23nand-objs = nand_base.o nand_bbt.o 24nand-objs = nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
new file mode 100644
index 000000000000..91207a45ad55
--- /dev/null
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -0,0 +1,293 @@
1/*
2 * drivers/mtd/nand/cs553x_nand.c
3 *
4 * (C) 2005, 2006 Red Hat Inc.
5 *
6 * Author: David Woodhouse <dwmw2@infradead.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * Overview:
13 * This is a device driver for the NAND flash controller found on
14 * the AMD CS5535/CS5536 companion chipsets for the Geode processor.
15 *
16 */
17
18#include <linux/slab.h>
19#include <linux/init.h>
20#include <linux/module.h>
21#include <linux/delay.h>
22#include <linux/pci.h>
23#include <linux/mtd/mtd.h>
24#include <linux/mtd/nand.h>
25#include <linux/mtd/partitions.h>
26
27#include <asm/msr.h>
28#include <asm/io.h>
29
30#define NR_CS553X_CONTROLLERS 4
31
32/* NAND Timing MSRs */
33#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */
34#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */
35#define MSR_NANDF_RSVD 0x5140001d /* Reserved */
36
37/* NAND BAR MSRs */
38#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */
39#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */
40#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */
41#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */
42 /* Each made up of... */
43#define FLSH_LBAR_EN (1ULL<<32)
44#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */
45#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */
46 /* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */
47 /* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */
48
49/* Pin function selection MSR (IDE vs. flash on the IDE pins) */
50#define MSR_DIVIL_BALL_OPTS 0x51400015
51#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */
52
53/* Registers within the NAND flash controller BAR -- memory mapped */
54#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */
55#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */
56#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */
57#define MM_NAND_STS 0x810
58#define MM_NAND_ECC_LSB 0x811
59#define MM_NAND_ECC_MSB 0x812
60#define MM_NAND_ECC_COL 0x813
61#define MM_NAND_LAC 0x814
62#define MM_NAND_ECC_CTL 0x815
63
64/* Registers within the NAND flash controller BAR -- I/O mapped */
65#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */
66#define IO_NAND_CTL 0x04
67#define IO_NAND_IO 0x05
68#define IO_NAND_STS 0x06
69#define IO_NAND_ECC_CTL 0x08
70#define IO_NAND_ECC_LSB 0x09
71#define IO_NAND_ECC_MSB 0x0a
72#define IO_NAND_ECC_COL 0x0b
73#define IO_NAND_LAC 0x0c
74
75#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */
76#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */
77#define CS_NAND_CTL_ALE (1<<2)
78#define CS_NAND_CTL_CLE (1<<1)
79#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */
80
81#define CS_NAND_STS_FLASH_RDY (1<<3)
82#define CS_NAND_CTLR_BUSY (1<<2)
83#define CS_NAND_CMD_COMP (1<<1)
84#define CS_NAND_DIST_ST (1<<0)
85
86#define CS_NAND_ECC_PARITY (1<<2)
87#define CS_NAND_ECC_CLRECC (1<<1)
88#define CS_NAND_ECC_ENECC (1<<0)
89
90static unsigned char cs553x_read_byte(struct mtd_info *mtd)
91{
92 struct nand_chip *this = mtd->priv;
93 unsigned char foo = readb(this->IO_ADDR_R);
94 return foo;
95}
96
97static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
98{
99 struct nand_chip *this = mtd->priv;
100 int i = 100000;
101
102 while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
103 udelay(1);
104 i--;
105 }
106 writeb(byte, this->IO_ADDR_W+0x801);
107}
108
109static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd)
110{
111 struct nand_chip *this = mtd->priv;
112 void __iomem *mmio_base = this->IO_ADDR_R;
113
114 uint8_t old = readb(mmio_base + MM_NAND_CTL);
115
116 switch(cmd) {
117 case NAND_CTL_SETCLE:
118 old |= CS_NAND_CTL_CLE;
119 break;
120
121 case NAND_CTL_CLRCLE:
122 old &= ~CS_NAND_CTL_CLE;
123 break;
124
125 case NAND_CTL_SETALE:
126 old |= CS_NAND_CTL_ALE;
127 break;
128
129 case NAND_CTL_CLRALE:
130 old &= ~CS_NAND_CTL_ALE;
131 break;
132
133 case NAND_CTL_SETNCE:
134 old &= ~CS_NAND_CTL_CE;
135 break;
136
137 case NAND_CTL_CLRNCE:
138 old |= CS_NAND_CTL_CE;
139 break;
140 }
141 writeb(old, mmio_base + MM_NAND_CTL);
142}
143
144
145static int cs553x_device_ready(struct mtd_info *mtd)
146{
147 struct nand_chip *this = mtd->priv;
148 void __iomem *mmio_base = this->IO_ADDR_R;
149 unsigned char foo = readb(mmio_base + MM_NAND_STS);
150
151 return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
152}
153
154static struct mtd_info *cs553x_mtd[4];
155
156static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
157{
158 int err = 0;
159 struct nand_chip *this;
160 struct mtd_info *new_mtd;
161
162 printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
163
164 if (!mmio) {
165 printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n");
166 return -ENXIO;
167 }
168
169 /* Allocate memory for MTD device structure and private data */
170 new_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
171 if (!new_mtd) {
172 printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n");
173 err = -ENOMEM;
174 goto out;
175 }
176
177 /* Get pointer to private data */
178 this = (struct nand_chip *) (&new_mtd[1]);
179
180 /* Initialize structures */
181 memset((char *) new_mtd, 0, sizeof(struct mtd_info));
182 memset((char *) this, 0, sizeof(struct nand_chip));
183
184 /* Link the private data with the MTD structure */
185 new_mtd->priv = this;
186
187 /* map physical address */
188 this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
189 if (!this->IO_ADDR_R) {
190 printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr);
191 err = -EIO;
192 goto out_mtd;
193 }
194
195 this->hwcontrol = cs553x_hwcontrol;
196 this->dev_ready = cs553x_device_ready;
197 this->read_byte = cs553x_read_byte;
198 this->write_byte = cs553x_write_byte;
199
200 /* 20 us command delay time */
201 this->chip_delay = 20;
202 this->eccmode = NAND_ECC_SOFT;
203
204 /* Enable the following for a flash based bad block table */
205 // this->options = NAND_USE_FLASH_BBT;
206
207 /* Scan to find existance of the device */
208 if (nand_scan (new_mtd, 1)) {
209 err = -ENXIO;
210 goto out_ior;
211 }
212
213 cs553x_mtd[cs] = new_mtd;
214 goto out;
215
216out_ior:
217 iounmap((void *)this->IO_ADDR_R);
218out_mtd:
219 kfree (new_mtd);
220out:
221 return err;
222}
223
224int __init cs553x_init(void)
225{
226 int err = -ENXIO;
227 int i;
228 uint64_t val;
229
230 /* Check whether we actually have a CS5535 or CS5536 */
231 if (!pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, NULL) &&
232 !pci_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA, NULL))
233 return -ENXIO;
234
235 rdmsrl(MSR_DIVIL_BALL_OPTS, val);
236 if (val & 1) {
237 printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
238 return -ENXIO;
239 }
240
241 for (i=0; i<NR_CS553X_CONTROLLERS; i++) {
242 rdmsrl(MSR_DIVIL_LBAR_FLSH0+i, val);
243
244 if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND))
245 err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
246 }
247
248 /* Register all devices together here. This means we can easily hack it to
249 do mtdconcat etc. if we want to. */
250 for (i=0; i<NR_CS553X_CONTROLLERS; i++) {
251 if (cs553x_mtd[i]) {
252 add_mtd_device(cs553x_mtd[i]);
253
254 /* If any devices registered, return success. Else the last error. */
255 err = 0;
256 }
257 }
258
259 return err;
260}
261module_init(cs553x_init);
262
263static void __exit cs553x_cleanup (void)
264{
265 int i;
266
267 for (i=0; i<NR_CS553X_CONTROLLERS; i++) {
268 struct mtd_info *mtd = cs553x_mtd[i];
269 struct nand_chip *this;
270 void __iomem *mmio_base;
271
272 if (!mtd)
273 break;
274
275 this = cs553x_mtd[i]->priv;
276 mmio_base = this->IO_ADDR_R;
277
278 /* Release resources, unregister device */
279 nand_release (cs553x_mtd[i]);
280 cs553x_mtd[i] = NULL;
281
282 /* unmap physical adress */
283 iounmap(mmio_base);
284
285 /* Free the MTD device structure */
286 kfree (mtd);
287 }
288}
289module_exit(cs553x_cleanup);
290
291MODULE_LICENSE("GPL");
292MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
293MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip");