diff options
author | David Woodhouse <dwmw2@infradead.org> | 2006-05-11 17:35:28 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-05-11 17:35:28 -0400 |
commit | 179fdc3f8dec5757ddbebd95a1b493d65fa08671 (patch) | |
tree | 422f229859e847b5987ec7f0d757eecbdac2f6df /drivers/mtd/nand | |
parent | b04ecae3d62edea2af49fd40ab12435b5ad8a492 (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>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/cs553x_nand.c | 293 |
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 | ||
192 | config MTD_NAND_CS553X | ||
193 | tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" | ||
194 | depends on MTD_NAND && X86_PC && PCI | ||
195 | |||
192 | config MTD_NAND_NANDSIM | 196 | config 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 | |||
19 | obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o | 19 | obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o |
20 | obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o | 20 | obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o |
21 | obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o | 21 | obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o |
22 | obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o | ||
22 | 23 | ||
23 | nand-objs = nand_base.o nand_bbt.o | 24 | nand-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 | |||
90 | static 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 | |||
97 | static 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 | |||
109 | static 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 | |||
145 | static 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 | |||
154 | static struct mtd_info *cs553x_mtd[4]; | ||
155 | |||
156 | static 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 | |||
216 | out_ior: | ||
217 | iounmap((void *)this->IO_ADDR_R); | ||
218 | out_mtd: | ||
219 | kfree (new_mtd); | ||
220 | out: | ||
221 | return err; | ||
222 | } | ||
223 | |||
224 | int __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 | } | ||
261 | module_init(cs553x_init); | ||
262 | |||
263 | static 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 | } | ||
289 | module_exit(cs553x_cleanup); | ||
290 | |||
291 | MODULE_LICENSE("GPL"); | ||
292 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | ||
293 | MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip"); | ||