move drivers/nand to drivers/mtd/nand
[barebox-mini2440.git] / drivers / mtd / nand / nand.c
blob4927231c171e3de652196f896406086c7d4b1abe
1 /*
2 * (C) Copyright 2005
3 * 2N Telekomunikace, a.s. <www.2n.cz>
4 * Ladislav Michl <michl@2n.cz>
6 * See file CREDITS for list of people who contributed to this
7 * project.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * version 2 as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307 USA
23 #include <common.h>
24 #include <linux/mtd/nand.h>
25 #include <linux/mtd/mtd.h>
26 #include <init.h>
27 #include <xfuncs.h>
28 #include <driver.h>
29 #include <malloc.h>
30 #include <ioctl.h>
31 #include <nand.h>
32 #include <errno.h>
34 static ssize_t nand_read(struct cdev *cdev, void* buf, size_t count, ulong offset, ulong flags)
36 struct mtd_info *info = cdev->priv;
37 size_t retlen;
38 int ret;
40 debug("nand_read: 0x%08x 0x%08x\n", offset, count);
42 ret = info->read(info, offset, count, &retlen, buf);
44 if(ret) {
45 printf("err %d\n", ret);
46 return ret;
48 return retlen;
51 #define NOTALIGNED(x) (x & (info->writesize - 1)) != 0
53 static int all_ff(const void *buf, int len)
55 int i;
56 const uint8_t *p = buf;
58 for (i = 0; i < len; i++)
59 if (p[i] != 0xFF)
60 return 0;
61 return 1;
64 static ssize_t nand_write(struct cdev* cdev, const void *buf, size_t _count, ulong offset, ulong flags)
66 struct mtd_info *info = cdev->priv;
67 size_t retlen, now;
68 int ret = 0;
69 void *wrbuf = NULL;
70 size_t count = _count;
72 if (NOTALIGNED(offset)) {
73 printf("offset 0x%08x not page aligned\n", offset);
74 return -EINVAL;
77 debug("write: 0x%08x 0x%08x\n", offset, count);
79 while (count) {
80 now = count > info->writesize ? info->writesize : count;
82 if (NOTALIGNED(now)) {
83 debug("not aligned: %d %d\n", info->writesize, (offset % info->writesize));
84 wrbuf = xmalloc(info->writesize);
85 memset(wrbuf, 0xff, info->writesize);
86 memcpy(wrbuf + (offset % info->writesize), buf, now);
87 if (!all_ff(wrbuf, info->writesize))
88 ret = info->write(info, offset & ~(info->writesize - 1),
89 info->writesize, &retlen, wrbuf);
90 free(wrbuf);
91 } else {
92 if (!all_ff(buf, info->writesize))
93 ret = info->write(info, offset, now, &retlen, buf);
94 debug("offset: 0x%08x now: 0x%08x retlen: 0x%08x\n", offset, now, retlen);
96 if (ret)
97 goto out;
99 offset += now;
100 count -= now;
101 buf += now;
104 out:
105 return ret ? ret : _count;
108 static int nand_ioctl(struct cdev *cdev, int request, void *buf)
110 struct mtd_info *info = cdev->priv;
111 struct mtd_info_user *user = buf;
113 switch (request) {
114 case MEMGETBADBLOCK:
115 debug("MEMGETBADBLOCK: 0x%08x\n", (off_t)buf);
116 return info->block_isbad(info, (off_t)buf);
117 case MEMSETBADBLOCK:
118 debug("MEMSETBADBLOCK: 0x%08x\n", (off_t)buf);
119 return info->block_markbad(info, (off_t)buf);
120 case MEMGETINFO:
121 user->type = info->type;
122 user->flags = info->flags;
123 user->size = info->size;
124 user->erasesize = info->erasesize;
125 user->oobsize = info->oobsize;
126 /* The below fields are obsolete */
127 user->ecctype = -1;
128 user->eccsize = 0;
129 return 0;
132 return 0;
135 static ssize_t nand_erase(struct cdev *cdev, size_t count, unsigned long offset)
137 struct mtd_info *info = cdev->priv;
138 struct erase_info erase;
139 int ret;
141 memset(&erase, 0, sizeof(erase));
142 erase.mtd = info;
143 erase.addr = offset;
144 erase.len = info->erasesize;
146 while (count > 0) {
147 debug("erase %d %d\n", erase.addr, erase.len);
149 ret = info->block_isbad(info, erase.addr);
150 if (ret > 0) {
151 printf("Skipping bad block at 0x%08x\n", erase.addr);
152 } else {
153 ret = info->erase(info, &erase);
154 if (ret)
155 return ret;
158 erase.addr += info->erasesize;
159 count -= count > info->erasesize ? info->erasesize : count;
162 return 0;
164 #if 0
165 static char* mtd_get_size(struct device_d *, struct param_d *param)
167 static char
169 #endif
171 static struct file_operations nand_ops = {
172 .read = nand_read,
173 .write = nand_write,
174 .ioctl = nand_ioctl,
175 .lseek = dev_lseek_default,
176 .erase = nand_erase,
179 static ssize_t nand_read_oob(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags)
181 struct mtd_info *info = cdev->priv;
182 struct nand_chip *chip = info->priv;
183 struct mtd_oob_ops ops;
184 int ret;
186 if (count < info->oobsize)
187 return -EINVAL;
189 ops.mode = MTD_OOB_RAW;
190 ops.ooboffs = 0;
191 ops.ooblen = info->oobsize;
192 ops.oobbuf = buf;
193 ops.datbuf = NULL;
194 ops.len = info->oobsize;
196 offset /= info->oobsize;
197 ret = info->read_oob(info, offset << chip->page_shift, &ops);
198 if (ret)
199 return ret;
201 return info->oobsize;
204 static struct file_operations nand_ops_oob = {
205 .read = nand_read_oob,
206 .ioctl = nand_ioctl,
207 .lseek = dev_lseek_default,
210 int add_mtd_device(struct mtd_info *mtd)
212 struct nand_chip *chip = mtd->priv;
213 char str[16];
215 strcpy(mtd->class_dev.name, "nand");
216 register_device(&mtd->class_dev);
218 mtd->cdev.ops = &nand_ops;
219 mtd->cdev.size = mtd->size;
220 mtd->cdev.name = asprintf("nand%d", mtd->class_dev.id);
221 mtd->cdev.priv = mtd;
222 mtd->cdev.dev = &mtd->class_dev;
224 sprintf(str, "%u", mtd->size);
225 dev_add_param_fixed(&mtd->class_dev, "size", str);
227 devfs_create(&mtd->cdev);
229 mtd->cdev_oob.ops = &nand_ops_oob;
230 mtd->cdev_oob.size = (mtd->size >> chip->page_shift) * mtd->oobsize;
231 mtd->cdev_oob.name = asprintf("nand_oob%d", mtd->class_dev.id);
232 mtd->cdev_oob.priv = mtd;
233 mtd->cdev_oob.dev = &mtd->class_dev;
234 devfs_create(&mtd->cdev_oob);
236 return 0;
239 int del_mtd_device (struct mtd_info *mtd)
241 unregister_device(&mtd->class_dev);
242 free(mtd->cdev_oob.name);
243 free(mtd->param_size.value);
244 free(mtd->cdev.name);
245 return 0;