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
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,
24 #include <linux/mtd/nand.h>
25 #include <linux/mtd/mtd.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
;
40 debug("nand_read: 0x%08x 0x%08x\n", offset
, count
);
42 ret
= info
->read(info
, offset
, count
, &retlen
, buf
);
45 printf("err %d\n", ret
);
51 #define NOTALIGNED(x) (x & (info->writesize - 1)) != 0
53 static int all_ff(const void *buf
, int len
)
56 const uint8_t *p
= buf
;
58 for (i
= 0; i
< len
; i
++)
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
;
70 size_t count
= _count
;
72 if (NOTALIGNED(offset
)) {
73 printf("offset 0x%08x not page aligned\n", offset
);
77 debug("write: 0x%08x 0x%08x\n", offset
, 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
);
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
);
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
;
115 debug("MEMGETBADBLOCK: 0x%08x\n", (off_t
)buf
);
116 return info
->block_isbad(info
, (off_t
)buf
);
118 debug("MEMSETBADBLOCK: 0x%08x\n", (off_t
)buf
);
119 return info
->block_markbad(info
, (off_t
)buf
);
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 */
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
;
141 memset(&erase
, 0, sizeof(erase
));
144 erase
.len
= info
->erasesize
;
147 debug("erase %d %d\n", erase
.addr
, erase
.len
);
149 ret
= info
->block_isbad(info
, erase
.addr
);
151 printf("Skipping bad block at 0x%08x\n", erase
.addr
);
153 ret
= info
->erase(info
, &erase
);
158 erase
.addr
+= info
->erasesize
;
159 count
-= count
> info
->erasesize
? info
->erasesize
: count
;
165 static char* mtd_get_size(struct device_d
*, struct param_d
*param
)
171 static struct file_operations nand_ops
= {
175 .lseek
= dev_lseek_default
,
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
;
186 if (count
< info
->oobsize
)
189 ops
.mode
= MTD_OOB_RAW
;
191 ops
.ooblen
= info
->oobsize
;
194 ops
.len
= info
->oobsize
;
196 offset
/= info
->oobsize
;
197 ret
= info
->read_oob(info
, offset
<< chip
->page_shift
, &ops
);
201 return info
->oobsize
;
204 static struct file_operations nand_ops_oob
= {
205 .read
= nand_read_oob
,
207 .lseek
= dev_lseek_default
,
210 int add_mtd_device(struct mtd_info
*mtd
)
212 struct nand_chip
*chip
= mtd
->priv
;
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
);
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
);