Index: distrib/sets/lists/base/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/base/mi,v
retrieving revision 1.921
diff -u -r1.921 mi
--- distrib/sets/lists/base/mi	8 Feb 2011 03:20:13 -0000	1.921
+++ distrib/sets/lists/base/mi	16 Feb 2011 13:46:37 -0000
@@ -1163,6 +1163,7 @@
 ./usr/sbin/iprop-log				base-krb5-bin		kerberos
 ./usr/sbin/faithd				base-router-bin		inet6
 ./usr/sbin/fixmount				base-nfsclient-bin
+./usr/sbin/flashctl				base-sysutil-bin
 ./usr/sbin/flush				base-obsolete		obsolete
 ./usr/sbin/fsinfo				base-sysutil-bin
 ./usr/sbin/fssconfig				base-sysutil-bin
Index: distrib/sets/lists/comp/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v
retrieving revision 1.1586
diff -u -r1.1586 mi
--- distrib/sets/lists/comp/mi	10 Feb 2011 20:56:02 -0000	1.1586
+++ distrib/sets/lists/comp/mi	16 Feb 2011 13:46:49 -0000
@@ -2054,6 +2054,7 @@
 ./usr/include/sys/file.h			comp-c-include
 ./usr/include/sys/filedesc.h			comp-c-include
 ./usr/include/sys/filio.h			comp-c-include
+./usr/include/sys/flashio.h			comp-c-include
 ./usr/include/sys/float_ieee.h			comp-obsolete		obsolete
 ./usr/include/sys/float_ieee754.h		comp-c-include
 ./usr/include/sys/fnv_hash.h			comp-obsolete		obsolete
@@ -9361,6 +9362,7 @@
 ./usr/share/man/cat9/firmware_malloc.0		comp-sys-catman		.cat
 ./usr/share/man/cat9/firmware_open.0		comp-sys-catman		.cat
 ./usr/share/man/cat9/firmware_read.0		comp-sys-catman		.cat
+./usr/share/man/cat9/flash.0			comp-sys-catman		.cat
 ./usr/share/man/cat9/fork1.0			comp-sys-catman		.cat
 ./usr/share/man/cat9/format_bytes.0		comp-sys-catman		.cat
 ./usr/share/man/cat9/fownsignal.0		comp-sys-catman		.cat
@@ -15293,6 +15295,7 @@
 ./usr/share/man/html9/firmware_malloc.html	comp-sys-htmlman	html
 ./usr/share/man/html9/firmware_open.html	comp-sys-htmlman	html
 ./usr/share/man/html9/firmware_read.html	comp-sys-htmlman	html
+./usr/share/man/html9/flash.html		comp-sys-htmlman	html
 ./usr/share/man/html9/fork1.html		comp-sys-htmlman	html
 ./usr/share/man/html9/format_bytes.html		comp-sys-htmlman	html
 ./usr/share/man/html9/fownsignal.html		comp-sys-htmlman	html
@@ -21376,6 +21379,7 @@
 ./usr/share/man/man9/firmware_malloc.9		comp-sys-man		.man
 ./usr/share/man/man9/firmware_open.9		comp-sys-man		.man
 ./usr/share/man/man9/firmware_read.9		comp-sys-man		.man
+./usr/share/man/man9/flash.9			comp-sys-man		.man
 ./usr/share/man/man9/fork1.9			comp-sys-man		.man
 ./usr/share/man/man9/format_bytes.9		comp-sys-man		.man
 ./usr/share/man/man9/fownsignal.9		comp-sys-man		.man
Index: distrib/sets/lists/man/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/man/mi,v
retrieving revision 1.1287
diff -u -r1.1287 mi
--- distrib/sets/lists/man/mi	10 Feb 2011 14:04:30 -0000	1.1287
+++ distrib/sets/lists/man/mi	16 Feb 2011 13:46:54 -0000
@@ -966,6 +966,7 @@
 ./usr/share/man/cat4/fd.0			man-sys-catman		.cat
 ./usr/share/man/cat4/fea.0			man-sys-catman		.cat
 ./usr/share/man/cat4/finsio.0			man-sys-catman		.cat
+./usr/share/man/cat4/flash.0			man-sys-catman		.cat
 ./usr/share/man/cat4/fms.0			man-sys-catman		.cat
 ./usr/share/man/cat4/fmv.0			man-sys-catman		.cat
 ./usr/share/man/cat4/fpa.0			man-sys-catman		.cat
@@ -3726,6 +3727,7 @@
 ./usr/share/man/html4/fd.html			man-sys-htmlman		html
 ./usr/share/man/html4/fea.html			man-sys-htmlman		html
 ./usr/share/man/html4/finsio.html		man-sys-htmlman		html
+./usr/share/man/html4/flash.html		man-sys-htmlman		html
 ./usr/share/man/html4/fms.html			man-sys-htmlman		html
 ./usr/share/man/html4/fmv.html			man-sys-htmlman		html
 ./usr/share/man/html4/fpa.html			man-sys-htmlman		html
@@ -6261,6 +6263,7 @@
 ./usr/share/man/man4/fd.4			man-sys-man		.man
 ./usr/share/man/man4/fea.4			man-sys-man		.man
 ./usr/share/man/man4/finsio.4			man-sys-man		.man
+./usr/share/man/man4/flash.4			man-sys-man		.man
 ./usr/share/man/man4/fms.4			man-sys-man		.man
 ./usr/share/man/man4/fmv.4			man-sys-man		.man
 ./usr/share/man/man4/fpa.4			man-sys-man		.man
Index: etc/MAKEDEV.tmpl
===================================================================
RCS file: /cvsroot/src/etc/MAKEDEV.tmpl,v
retrieving revision 1.137
diff -u -r1.137 MAKEDEV.tmpl
--- etc/MAKEDEV.tmpl	26 Jan 2011 01:18:47 -0000	1.137
+++ etc/MAKEDEV.tmpl	16 Feb 2011 13:46:57 -0000
@@ -1042,6 +1042,13 @@
 	%MKDISK% $name $unit $blk $chr
 	;;
 
+flash*)
+	unit=${i#flash}
+	flash=flash$unit
+	mkdev flash$unit b %flash_blk% $unit
+	mkdev rflash$unit c %flash_chr% $unit
+	;;
+
 altmem*)
 	name=altmem; unit=${i#altmem}; blk=%altmem_blk%; chr=%altmem_chr%
 	%MKDISK% $name $unit $blk $chr
Index: etc/etc.amd64/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.amd64/MAKEDEV.conf,v
retrieving revision 1.16
diff -u -r1.16 MAKEDEV.conf
--- etc/etc.amd64/MAKEDEV.conf	4 Nov 2008 14:25:10 -0000	1.16
+++ etc/etc.amd64/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -5,6 +5,7 @@
 	makedev std wscons wt0 fd0 fd1
 	makedev wd0 wd1 wd2 wd3 wd4 wd5 wd6 wd7
 	makedev sd0 sd1 sd2 sd3 sd4
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 tty2 tty3
 	makedev st0 st1 ch0 cd0 cd1 mcd0 vnd0
 	makedev bpf
Index: etc/etc.evbarm/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.evbarm/MAKEDEV.conf,v
retrieving revision 1.3
diff -u -r1.3 MAKEDEV.conf
--- etc/etc.evbarm/MAKEDEV.conf	13 Sep 2008 11:46:18 -0000	1.3
+++ etc/etc.evbarm/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -2,6 +2,7 @@
 
 all_md)
 	makedev wscons fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 st0 st1 ch0 cd0 cd1
 	makedev uk0 uk1 ss0
 	makedev lpa0 lpt0
@@ -17,6 +18,7 @@
 
 ramdisk|floppy)
 	makedev std fd0 fd1 wd0 wd1 wd2 wd3 md0 md1 sd0 sd1 sd2 sd3
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 opty
 	makedev st0 st1 cd0 cd1
 	;;
Index: etc/etc.evbmips/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.evbmips/MAKEDEV.conf,v
retrieving revision 1.4
diff -u -r1.4 MAKEDEV.conf
--- etc/etc.evbmips/MAKEDEV.conf	15 Jan 2007 23:35:11 -0000	1.4
+++ etc/etc.evbmips/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -2,6 +2,7 @@
 
 all_md)
 	makedev wscons sd0 sd1 sd2 sd3 sd4
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev st0 st1 ch0 cd0 cd1
 	makedev ss0 ss1 uk0 uk1
 	makedev ld0 ld1 ld2 ld3
@@ -21,5 +22,6 @@
 	makedev std
 	makedev sd0 sd1 sd2 sd3 opty st0 st1 ch0 cd0 cd1 ccd0 ccd1 md0
 	makedev wd0 wd1 fd0 fd1
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty00 tty01 ttyE0 ttyE1 wsmouse0 wskbd0 ttyEcfg
 	;;
Index: etc/etc.evbppc/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.evbppc/MAKEDEV.conf,v
retrieving revision 1.6
diff -u -r1.6 MAKEDEV.conf
--- etc/etc.evbppc/MAKEDEV.conf	13 Sep 2008 11:46:18 -0000	1.6
+++ etc/etc.evbppc/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -2,6 +2,7 @@
 
 all_md)
 	makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev ss0 ch0 uk0 uk1
 	makedev mlx0 ld0 ld1 ld2 ld3
 	makedev tty00 tty01
Index: etc/etc.hpcarm/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.hpcarm/MAKEDEV.conf,v
retrieving revision 1.11
diff -u -r1.11 MAKEDEV.conf
--- etc/etc.hpcarm/MAKEDEV.conf	1 Aug 2010 04:08:27 -0000	1.11
+++ etc/etc.hpcarm/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -2,6 +2,7 @@
 
 all_md)
 	makedev wscons std_hpcarm fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 st0 st1 ch0 cd0 cd1
 	makedev uk0 uk1 ss0
 	makedev ttyS0
@@ -26,6 +27,7 @@
 
 ramdisk|floppy)
 	makedev std std_hpcarm fd0 fd1 wd0 wd1 wd2 wd3 md0 md1 sd0 sd1 sd2 sd3
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 opty
 	makedev st0 st1 cd0 cd1
 	makedev ld0
Index: etc/etc.hpcmips/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.hpcmips/MAKEDEV.conf,v
retrieving revision 1.4
diff -u -r1.4 MAKEDEV.conf
--- etc/etc.hpcmips/MAKEDEV.conf	15 Jan 2007 23:35:12 -0000	1.4
+++ etc/etc.hpcmips/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -2,6 +2,7 @@
 
 all_md)
 	makedev wscons fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3 sd4
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 tty2
 	makedev st0 st1 ch0 cd0 cd1
 	makedev ss0 ch0 uk0 uk1
@@ -17,6 +18,7 @@
 
 floppy)
 	makedev std fd0 fd1 wd0 wd1 sd0 sd1 sd2 tty0 tty1 tty2
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev st0 st1 cd0 cd1 opty
 	;;
 
Index: etc/etc.i386/MAKEDEV.conf
===================================================================
RCS file: /cvsroot/src/etc/etc.i386/MAKEDEV.conf,v
retrieving revision 1.20
diff -u -r1.20 MAKEDEV.conf
--- etc/etc.i386/MAKEDEV.conf	4 Nov 2008 14:25:10 -0000	1.20
+++ etc/etc.i386/MAKEDEV.conf	16 Feb 2011 13:46:57 -0000
@@ -5,6 +5,7 @@
 	makedev std wscons wt0 fd0 fd1
 	makedev wd0 wd1 wd2 wd3 wd4 wd5 wd6 wd7
 	makedev sd0 sd1 sd2 sd3 sd4
+	makedev flash0 flash1 flash2 flash3 flash4 flash5 flash6 flash7
 	makedev tty0 tty1 tty2 tty3
 	makedev st0 st1 ch0 cd0 cd1 mcd0 vnd0
 	makedev bpf
Index: share/man/man4/Makefile
===================================================================
RCS file: /cvsroot/src/share/man/man4/Makefile,v
retrieving revision 1.549
diff -u -r1.549 Makefile
--- share/man/man4/Makefile	9 Feb 2011 15:31:30 -0000	1.549
+++ share/man/man4/Makefile	16 Feb 2011 13:47:58 -0000
@@ -24,7 +24,7 @@
 	dmphy.4 dpt.4 dpti.4 drm.4 drum.4 \
 	eap.4 ebus.4 edc.4 elmc.4 emuxki.4 en.4 envsys.4 ep.4 esh.4 esis.4 \
 	esa.4 esiop.4 esm.4 eso.4 et.4 etherip.4 etphy.4 exphy.4 \
-	fast_ipsec.4 fd.4 finsio.4 fpa.4 fms.4 fss.4 fujitsu.4 fxp.4 \
+	fast_ipsec.4 fd.4 finsio.4 flash.4 fpa.4 fms.4 fss.4 fujitsu.4 fxp.4 \
 	gcscaudio.4 gem.4 genfb.4 gentbi.4 geodeide.4 \
 	glxtphy.4 gpib.4 gpio.4 gpiolock.4 gpiosim.4 gre.4 gphyter.4 gsip.4 \
 	hdaudio.4 hifn.4 hme.4 hpqlb.4 hptide.4 \
Index: share/man/man4/flash.4
===================================================================
RCS file: share/man/man4/flash.4
diff -N share/man/man4/flash.4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ share/man/man4/flash.4	16 Feb 2011 13:47:58 -0000
@@ -0,0 +1,69 @@
+.\"	$NetBSD$
+.\"
+.\" Copyright (c) 2010 Department of Software Engineering,
+.\"		      University of Szeged, Hungary
+.\" Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by the Department of Software Engineering, University of Szeged, Hungary
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 21, 2010
+.Dt FLASH 4
+.Os
+.Sh NAME
+.Nm flash
+.Nd device-independent flash layer
+.Sh SYNOPSIS
+.In sys/flash.h
+.Sh DESCRIPTION
+The
+.Nm
+driver provides a device-independent interface for using flash memory.
+.Pp
+The following
+.Xr ioctl 2
+operations are supported on
+.Pa /dev/flash :
+.Bl -tag -width indent
+.It Dv FLASH_ERASE_BLOCK (struct flash_erase_params)
+This command erases one or more blocks.
+.It Dv FLASH_DUMP (struct flash_dump_params)
+This command dumps a block.
+.It Dv FLASH_GET_INFO (struct flash_info_params)
+This command aquires information aboout the flash device.
+.It Dv FLASH_BLOCK_ISBAD (struct flash_badblock_params)
+This command checks if a block is marked as bad.
+.It Dv FLASH_BLOCK_MARKBAD (struct flash_badblock_params)
+This command marks a block as bad.
+.El
+.Sh FILES
+.Bl -tag -width /dev/flash -compact
+.It Pa /dev/flash
+.El
+.Sh SEE ALSO
+.Xr flash 9
+.Xr nand 9
+.Sh AUTHORS
+.An Adam Hoka Aq ahoka@NetBSD.org
Index: share/man/man9/Makefile
===================================================================
RCS file: /cvsroot/src/share/man/man9/Makefile,v
retrieving revision 1.344
diff -u -r1.344 Makefile
--- share/man/man9/Makefile	27 Jan 2011 14:48:37 -0000	1.344
+++ share/man/man9/Makefile	16 Feb 2011 13:47:59 -0000
@@ -18,8 +18,8 @@
 	delay.9 disk.9 ddc.9 disklabel.9 dofileread.9 \
 	dopowerhooks.9 do_setresuid.9 doshutdownhooks.9 driver.9 \
 	edid.9 errno.9 ethersubr.9 evcnt.9 extattr.9 extent.9 \
-	fetch.9 file.9 fileassoc.9 filedesc.9 firmload.9 fork1.9 fsetown.9 \
-	fstrans.9 getiobuf.9 \
+	fetch.9 file.9 fileassoc.9 filedesc.9 firmload.9 flash.9 \
+	fork1.9 fsetown.9 fstrans.9 getiobuf.9 \
 	hash.9 hashinit.9 hardclock.9 humanize_number.9 hz.9 \
 	ieee80211.9 ieee80211_crypto.9 ieee80211_input.9 ieee80211_ioctl.9 \
 	ieee80211_node.9 ieee80211_output.9 ieee80211_proto.9 \
Index: share/man/man9/flash.9
===================================================================
RCS file: share/man/man9/flash.9
diff -N share/man/man9/flash.9
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ share/man/man9/flash.9	16 Feb 2011 13:47:59 -0000
@@ -0,0 +1,84 @@
+.\"	$NetBSD$
+.\"
+.\" Copyright (c) 2010 Department of Software Engineering,
+.\"		      University of Szeged, Hungary
+.\" Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by the Department of Software Engineering, University of Szeged, Hungary
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 21, 2010
+.Dt FLASH 9
+.Os
+.Sh NAME
+.Nm flash
+.Nd subsystem for flash-like memory devices
+.Sh SYNOPSIS
+.In dev/flash/flash.h
+.Ft device_t
+.Fn flash_attach_mi "const struct flash_interface *fl" "device_t dev"
+.Sh DESCRIPTION
+Flash-like devices can register themselves to the
+.Nm
+layer with the
+.Fa flash_hw_if
+structure.
+This structure has function pointers and and other fields.
+.Pp
+The attachment can be done by calling
+.Fa flash_attach_mi
+with this structure and the device's device_t as argument.
+Return value is the flash layer device.
+The
+.Fa flash_interface
+struct is shown below.
+.Bd -literal
+struct flash_interface {
+	int (*erase) (device_t, struct flash_erase_instruction *);
+	int (*read) (device_t, off_t, size_t, size_t *, uint8_t *);
+	int (*write) (device_t, off_t, size_t, size_t *, const uint8_t *);
+	int (*block_markbad)(device_t, uint64_t);
+	int (*block_isbad)(device_t, uint64_t);
+	int (*sync) (device_t);
+
+	int (*submit)(device_t, struct buf *);
+
+	/* storage for partition info */
+	struct flash_partition partition;
+
+	/* total size of mtd */
+	flash_addr_t size;
+	uint32_t page_size;
+	uint32_t erasesize;
+	uint32_t writesize;
+	uint32_t minor;
+	uint8_t	type;
+};
+.Ed
+.Sh SEE ALSO
+.Xr flash 4
+.Xr nand 9
+.Sh AUTHORS
+.An Adam Hoka Aq ahoka@NetBSD.org
Index: sys/arch/arm/omap/files.omap2
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/omap/files.omap2,v
retrieving revision 1.6
diff -u -r1.6 files.omap2
--- sys/arch/arm/omap/files.omap2	1 Sep 2010 06:23:59 -0000	1.6
+++ sys/arch/arm/omap/files.omap2	16 Feb 2011 13:48:04 -0000
@@ -79,6 +79,11 @@
 attach	gpmc at mainbus
 file	arch/arm/omap/omap2_gpmc.c		gpmc
 
+# NAND flash controller
+device	omapnand: nandbus
+attach	omapnand at gpmc
+file	arch/arm/omap/omap2_nand.c		omapnand
+
 # PRCM interface
 device	prcm
 attach	prcm at obio
Index: sys/arch/arm/omap/omap2_nand.c
===================================================================
RCS file: sys/arch/arm/omap/omap2_nand.c
diff -N sys/arch/arm/omap/omap2_nand.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/arch/arm/omap/omap2_nand.c	16 Feb 2011 13:48:04 -0000
@@ -0,0 +1,517 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Device driver for the NAND controller found in Texas Instruments OMAP2
+ * and later SOCs.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include "opt_omap.h"
+#include "opt_flash.h"
+
+/* TODO move to opt_* */
+#undef OMAP2_NAND_HARDWARE_ECC
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cdefs.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+
+#include <arch/arm/omap/omap2_gpmcvar.h>
+#include <arch/arm/omap/omap2_gpmcreg.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/onfi.h>
+
+/* GPMC_STATUS */
+#define WAIT0		__BIT(8)	/* active low */
+
+/* GPMC_ECC_CONTROL */
+#define ECCCLEAR	__BIT(8)
+#define ECCPOINTER	__BITS(3,0)
+
+/* GPMC_ECC_CONFIG */
+#define ECCALGORITHM	__BIT(16)
+#define ECCCS		__BITS(3,1)
+#define ECC16B		__BIT(7)
+#define ECCENABLE	__BIT(0)
+/* GPMC_ECC_SIZE_CONFIG */
+#define ECCSIZE1	__BITS(29,22)
+
+/* GPMC_CONFIG1_i */
+#define DEVICETYPE	__BITS(11,10)
+#define DEVICESIZE	__BITS(13,12)
+
+#define MASKEDINT(mask, integer) ((integer) << (ffs(mask) - 1) & mask)
+
+/* NAND status register */
+#define NAND_WP_BIT __BIT(4)
+
+static int	omap2_nand_match(struct device *, struct cfdata *, void *);
+static void	omap2_nand_attach(struct device *, struct device *, void *);
+static int	omap2_nand_detach(device_t, int);
+
+void omap2_nand_command(device_t self, uint8_t command);
+void omap2_nand_address(device_t self, uint8_t address);
+void omap2_nand_busy(device_t self);
+void omap2_nand_read_byte(device_t self, uint8_t *data);
+void omap2_nand_write_byte(device_t self, uint8_t data);
+void omap2_nand_read_word(device_t self, uint16_t *data);
+void omap2_nand_write_word(device_t self, uint16_t data);
+bool omap2_nand_isbusy(device_t self);
+void omap2_nand_read_buf_byte(device_t self, void *buf, size_t len);
+void omap2_nand_read_buf_word(device_t self, void *buf, size_t len);
+void omap2_nand_write_buf_byte(device_t self, const void *buf, size_t len);
+void omap2_nand_write_buf_word(device_t self, const void *buf, size_t len);
+
+int omap2_nand_ecc_init(device_t self);
+int omap2_nand_ecc_prepare(device_t self, int mode);
+int omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc);
+int omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc,
+    const uint8_t *calcecc);
+
+struct omap2_nand_softc {
+	device_t sc_dev;
+	device_t sc_nanddev;
+	struct gpmc_softc *sc_gpmcsc;
+
+	int sc_cs;
+	int sc_buswidth;	/* 0: 8bit, 1: 16bit */
+
+	struct nand_interface	sc_nand_if;
+
+	bus_space_handle_t	sc_ioh;
+	bus_space_tag_t		sc_iot;
+
+	bus_size_t		sc_cmd_reg;
+	bus_size_t		sc_addr_reg;
+	bus_size_t		sc_data_reg;
+};
+
+CFATTACH_DECL_NEW(omapnand, sizeof(struct omap2_nand_softc), omap2_nand_match,
+    omap2_nand_attach, omap2_nand_detach, NULL);
+
+void
+omap2_nand_command(device_t self, uint8_t command)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, command);
+};
+
+void
+omap2_nand_address(device_t self, uint8_t address)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_addr_reg, address);
+};
+
+bool
+omap2_nand_isbusy(device_t self)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	uint8_t status;
+	
+	DELAY(1);		/* just to be sure we are not early */
+	
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+	    sc->sc_cmd_reg, ONFI_READ_STATUS);
+	
+	DELAY(1);
+		
+	status = bus_space_read_1(sc->sc_iot,
+	    sc->sc_ioh, sc->sc_data_reg);
+
+	return !(status & ONFI_STATUS_RDY);
+};
+
+static int
+omap2_nand_match(struct device *parent, struct cfdata *match, void *aux)
+{
+	struct gpmc_attach_args *gpmc = aux;
+	bus_space_tag_t	iot;
+	bus_space_handle_t ioh;
+	bus_size_t cs_offset;
+	uint32_t result;
+	int ret = 0;
+	
+	iot = gpmc->gpmc_iot;
+
+	cs_offset = GPMC_CS_CONFIG_BASE(gpmc->gpmc_cs);
+
+	/* map i/o space */
+	if (bus_space_map(iot, cs_offset, GPMC_CS_SIZE, 0, &ioh) != 0) {
+		aprint_error("omap2_nand_match: can't map i/o space");
+		return 1;
+	}
+
+	/* read GPMC_CONFIG1_i */
+	result = bus_space_read_4(iot, ioh, GPMC_CONFIG1_i);
+
+	/* check if memory device is NAND type */
+	if ((result & DEVICETYPE) == MASKEDINT(DEVICETYPE, 0x02)) {
+		/* we got NAND, report positive match */
+		ret = 1;
+	}
+
+	bus_space_unmap(iot, ioh, GPMC_CS_SIZE);
+
+	return ret;
+}
+
+static void
+omap2_nand_attach(device_t parent, device_t self, void *aux)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	sc->sc_gpmcsc = device_private(parent);
+	struct gpmc_attach_args *gpmc = aux;
+	bus_size_t cs_offset;
+	uint32_t val;
+
+	aprint_normal("\n");
+
+	sc->sc_iot = gpmc->gpmc_iot;
+	sc->sc_dev = self;
+	sc->sc_cs = gpmc->gpmc_cs;
+
+//	cs_offset = GPMC_BASE + GPMC_CONFIG1_0 + sc->sc_cs * GPMC_CS_SIZE;
+	cs_offset = GPMC_CS_CONFIG_BASE(sc->sc_cs);
+
+	/* map i/o space */
+	if (bus_space_map(sc->sc_iot, cs_offset, GPMC_CS_SIZE, 0,
+		&sc->sc_ioh) != 0) {
+		aprint_error(": omap2_nand_attach: can't map i/o space");
+		return;
+	}
+
+        sc->sc_cmd_reg = GPMC_NAND_COMMAND_0 - GPMC_CONFIG1_0;
+	sc->sc_addr_reg = GPMC_NAND_ADDRESS_0 - GPMC_CONFIG1_0;
+	sc->sc_data_reg = GPMC_NAND_DATA_0 - GPMC_CONFIG1_0;
+
+	/* turn off write protection if enabled */
+	val = gpmc_register_read(sc->sc_gpmcsc, GPMC_CONFIG);
+	val |= NAND_WP_BIT;
+	gpmc_register_write(sc->sc_gpmcsc, GPMC_CONFIG, val);
+
+	/*
+	 * do the reset dance for NAND
+	 */
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+	    sc->sc_cmd_reg, ONFI_RESET);
+
+	omap2_nand_busy(self);
+
+	/* read GPMC_CONFIG1_i to get buswidth */
+	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPMC_CONFIG1_i);
+
+	if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x01)) {
+		/* 16bit */
+		sc->sc_buswidth = 1;
+	} else if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x00)) {
+		/* 8bit */
+		sc->sc_buswidth = 0;
+	} else {
+		panic("invalid buswidth reported by config1");
+	}
+
+	sc->sc_nand_if.select = &nand_default_select;
+	sc->sc_nand_if.command = &omap2_nand_command;
+	sc->sc_nand_if.address = &omap2_nand_address;
+	sc->sc_nand_if.read_buf_byte = &omap2_nand_read_buf_byte;
+	sc->sc_nand_if.read_buf_word = &omap2_nand_read_buf_word;
+	sc->sc_nand_if.read_byte = &omap2_nand_read_byte;
+	sc->sc_nand_if.read_word = &omap2_nand_read_word;
+	sc->sc_nand_if.write_buf_byte = &omap2_nand_write_buf_byte;
+	sc->sc_nand_if.write_buf_word = &omap2_nand_write_buf_word;
+	sc->sc_nand_if.write_byte = &omap2_nand_write_byte;
+	sc->sc_nand_if.write_word = &omap2_nand_write_word;
+	sc->sc_nand_if.busy = &omap2_nand_busy;
+
+#ifdef OMAP2_NAND_HARDWARE_ECC
+	omap2_nand_ecc_init(self);
+	sc->sc_nand_if.ecc_compute = &omap2_nand_ecc_compute;
+	sc->sc_nand_if.ecc_correct = &omap2_nand_ecc_correct;
+	sc->sc_nand_if.ecc_prepare = &omap2_nand_ecc_prepare;
+	sc->sc_nand_if.ecc.necc_code_size = 3;
+	sc->sc_nand_if.ecc.necc_block_size = 512;
+	sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_HW;
+#else
+	sc->sc_nand_if.ecc_compute = &nand_default_ecc_compute;
+	sc->sc_nand_if.ecc_correct = &nand_default_ecc_correct;
+	sc->sc_nand_if.ecc_prepare = NULL;
+	sc->sc_nand_if.ecc.necc_code_size = 3;
+	sc->sc_nand_if.ecc.necc_block_size = 256;
+	sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_SW;
+#endif	/* OMAP2_NAND_HARDWARE_ECC */
+
+	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL))
+		aprint_error_dev(sc->sc_dev,
+		    "couldn't establish power handler\n");
+
+	sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev);
+}
+
+static int
+omap2_nand_detach(device_t device, int flags)
+{
+	struct omap2_nand_softc *sc = device_private(device);
+	int ret = 0;
+
+	bus_space_unmap(sc->sc_iot, sc->sc_ioh, GPMC_CS_SIZE);
+
+	pmf_device_deregister(sc->sc_dev);
+
+	if (sc->sc_nanddev != NULL)
+		ret = config_detach(sc->sc_nanddev, flags);
+
+	return ret;
+}
+
+void
+omap2_nand_busy(device_t self)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	while (!(gpmc_register_read(sc->sc_gpmcsc, GPMC_STATUS) & WAIT0)) {
+		DELAY(1);
+	}
+}
+
+void
+omap2_nand_read_byte(device_t self, uint8_t *data)
+{	
+	struct omap2_nand_softc *sc = device_private(self);
+	
+	*data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg);
+}
+
+void
+omap2_nand_write_byte(device_t self, uint8_t data)
+{	
+	struct omap2_nand_softc *sc = device_private(self);
+	
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data);
+}
+
+void
+omap2_nand_read_word(device_t self, uint16_t *data)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	
+	*data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg);
+}
+
+void
+omap2_nand_write_word(device_t self, uint16_t data)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data);
+}
+
+void
+omap2_nand_read_buf_byte(device_t self, void *buf, size_t len)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 1);
+	
+	bus_space_read_multi_1(sc->sc_iot, sc->sc_ioh,
+	    sc->sc_data_reg, buf, len);
+}
+
+void
+omap2_nand_read_buf_word(device_t self, void *buf, size_t len)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 2);
+	KASSERT(!(len & 0x01));
+	
+	bus_space_read_multi_2(sc->sc_iot, sc->sc_ioh,
+	    sc->sc_data_reg, buf, len / 2);
+}
+
+void
+omap2_nand_write_buf_byte(device_t self, const void *buf, size_t len)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 1);
+	
+	bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh,
+	    sc->sc_data_reg, buf, len);
+}
+
+void
+omap2_nand_write_buf_word(device_t self, const void *buf, size_t len)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 2);
+	KASSERT(!(len & 0x01));
+	
+	bus_space_write_multi_2(sc->sc_iot, sc->sc_ioh,
+	    sc->sc_data_reg, buf, len / 2);
+}
+
+static uint32_t
+convert_ecc(const uint8_t *ecc)
+{
+	return ecc[0] | (ecc[1] << 16) | ((ecc[2] & 0xf0) << 20) |
+	    ((ecc[2] & 0x0f) << 8);
+}
+
+int
+omap2_nand_ecc_init(device_t self)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	uint32_t val;
+
+	val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL);
+	/* clear ecc, select ecc register 1 */
+	val &= ~ECCPOINTER;
+	val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1);
+	gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val);
+
+	/* XXX too many MAGIC */
+	/* set ecc size to 512, set all regs to eccsize1*/
+	val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_SIZE_CONFIG);
+	val &= ~ECCSIZE1;
+	val |= MASKEDINT(ECCSIZE1, 512) | 0x0f;
+	gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val);
+
+	return 0;
+}
+
+int
+omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	uint32_t val;
+
+	/* read ecc result register */
+	val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC1_RESULT);
+
+	ecc[0] = val & 0xff;
+	ecc[1] = (val >> 16) & 0xff;
+	ecc[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
+
+	/* disable ecc engine */
+	val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG);
+	val &= ~ECCENABLE;
+	gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val);
+
+	return 0;
+}
+
+int
+omap2_nand_ecc_prepare(device_t self, int mode)
+{
+	struct omap2_nand_softc *sc = device_private(self);
+	uint32_t val;
+
+	/* same for read/write */
+	switch (mode) {
+	case NAND_ECC_READ:
+	case NAND_ECC_WRITE:
+		val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL);
+		/* clear ecc, select ecc register 1 */
+		val &= ~ECCPOINTER;
+		val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1);
+		gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val);
+		
+		val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG);
+		val &= ~ECCCS;
+		val |= ECCENABLE | MASKEDINT(ECCCS, sc->sc_cs);
+		if (sc->sc_buswidth == 1)
+			val |= ECC16B;
+		else
+			val &= ~ECC16B;
+		gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val);
+		
+		break;
+	default:
+		aprint_error_dev(self, "invalid i/o mode for ecc prepare\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc,
+    const uint8_t *calcecc)
+{
+	uint32_t oecc, cecc, xor;
+	uint16_t parity, offset;
+	uint8_t bit;
+
+	oecc = convert_ecc(oldecc);
+	cecc = convert_ecc(calcecc);
+
+	/* get the difference */
+	xor = oecc ^ cecc;
+
+	/* the data was correct if all bits are zero */
+	if (xor == 0x00)
+		return NAND_ECC_OK;
+
+	switch (popcount32(xor)) {
+	case 12:
+		/* single byte error */
+		parity = xor >> 16;
+		bit = (parity & 0x07);
+		offset = (parity >> 3) & 0x01ff;
+		/* correct bit */
+		data[offset] ^= (0x01 << bit);
+		return NAND_ECC_CORRECTED;
+	case 1:
+		return NAND_ECC_INVALID;
+	default:
+		/* erased page! */
+		if ((oecc == 0x0fff0fff) && (cecc == 0x00000000))
+			return NAND_ECC_OK;
+		
+		return NAND_ECC_TWOBIT;
+	}
+}
Index: sys/arch/evbarm/conf/BEAGLEBOARD
===================================================================
RCS file: /cvsroot/src/sys/arch/evbarm/conf/BEAGLEBOARD,v
retrieving revision 1.14
diff -u -r1.14 BEAGLEBOARD
--- sys/arch/evbarm/conf/BEAGLEBOARD	4 Jan 2011 11:38:58 -0000	1.14
+++ sys/arch/evbarm/conf/BEAGLEBOARD	16 Feb 2011 13:48:06 -0000
@@ -183,6 +183,19 @@
 # General Purpose Memory Controller
 gpmc0		at mainbus? base 0x6e000000
 
+# NAND controller
+omapnand0	at gpmc? addr 0x30000000
+
+# NAND layer
+nand0		at nandbus?
+
+# Define flash partitions for board
+flash0		at nand0 offset 0x0 size 0x80000 readonly 1
+flash1		at nand0 offset 0x80000 size 0x80000 readonly 1
+flash2		at nand0 offset 0x260000 size 0x20000
+flash3		at nand0 offset 0x280000 size 0x400000
+flash4		at nand0 offset 0x680000 size 0x0
+
 # Interrupt Controller
 omapicu0	at obio0 addr 0x48200000 size 0x1000 intrbase 0
 omapgpio0	at obio1 addr 0x48310000 size 0x0400 intrbase 96  intr 29
Index: sys/arch/i386/conf/ALL
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/ALL,v
retrieving revision 1.286
diff -u -r1.286 ALL
--- sys/arch/i386/conf/ALL	11 Feb 2011 01:59:56 -0000	1.286
+++ sys/arch/i386/conf/ALL	16 Feb 2011 13:48:10 -0000
@@ -1595,6 +1595,12 @@
 # devices (watchdog timer, etc.)
 weasel* at pci?
 
+# Flash subsystem
+flash* at flashbus?
+
+# NAND subsystem
+nand* at nandbus?
+
 # Pull in optional local configuration
 #include 	"arch/i386/conf/ALL.local"
 
@@ -1714,6 +1720,8 @@
 # Hardware-assisted data mover interface
 pseudo-device	dmoverio
 
+pseudo-device	nandemulator
+
 options 	FILEASSOC		# fileassoc(9) - required for Veriexec
 
 # Veriexec
Index: sys/conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.998
diff -u -r1.998 files
--- sys/conf/files	9 Feb 2011 21:21:32 -0000	1.998
+++ sys/conf/files	16 Feb 2011 13:48:17 -0000
@@ -1703,3 +1703,14 @@
 # alternate memory device
 #
 include "dev/altmem/files.altmem"
+
+#
+# Flash subsystem
+#
+include "dev/flash/files.flash"
+
+#
+# NAND subsytem
+#
+include "dev/nand/files.nand"
+
Index: sys/dev/flash/files.flash
===================================================================
RCS file: sys/dev/flash/files.flash
diff -N sys/dev/flash/files.flash
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/flash/files.flash	16 Feb 2011 13:48:18 -0000
@@ -0,0 +1,9 @@
+# $NetBSD$
+
+define	flashbus	{ [offset = 0], [size = 0], [readonly = 0] }
+
+device	flash
+attach	flash at flashbus
+file	dev/flash/flash.c		flash
+
+defflag opt_flash.h			FLASH_STATIC_PARTITIONS
Index: sys/dev/flash/flash.c
===================================================================
RCS file: sys/dev/flash/flash.c
diff -N sys/dev/flash/flash.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/flash/flash.c	16 Feb 2011 13:48:18 -0000
@@ -0,0 +1,715 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Framework for storage devices based on Flash technology
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+
+#include <sys/atomic.h>
+#include <sys/buf.h>
+#include <sys/bufq.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/malloc.h>
+#include <sys/reboot.h>
+
+#include <sys/flashio.h>
+#include "flash.h"
+
+#define FLASH_DEBUG 1
+#ifdef FLASH_DEBUG
+#define DPRINTF(x)	if (flashdebug) printf x
+#define DPRINTFN(n,x)	if (flashdebug>(n)) printf x
+int	flashdebug = FLASH_DEBUG;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+unsigned int flash_usage_count = 0;
+
+extern struct cfdriver flash_cd;
+
+dev_type_open(flashopen);
+dev_type_close(flashclose);
+dev_type_read(flashread);
+dev_type_write(flashwrite);
+dev_type_ioctl(flashioctl);
+dev_type_strategy(flashstrategy);
+dev_type_dump(flashdump);
+dev_type_size(flashsize);
+
+int flash_print(void *aux, const char *pnp);
+
+bool flash_shutdown(device_t dev, int how);
+int flash_nsectors(struct buf *bp);
+int flash_sector(struct buf *bp);
+
+static inline off_t flash_get_part_offset(struct flash_softc *fl,
+    size_t poffset);
+
+int flash_match(device_t parent, cfdata_t match, void *aux);
+void flash_attach(device_t parent, device_t self, void *aux);
+int flash_detach(device_t device, int flags);
+
+CFATTACH_DECL_NEW(flash, sizeof(struct flash_softc),
+    flash_match, flash_attach, flash_detach, NULL);
+
+/**
+ * Block device's operation
+ */
+const struct bdevsw flash_bdevsw = {
+	.d_open = flashopen,
+	.d_close = flashclose,
+	.d_strategy = flashstrategy,
+	.d_ioctl = flashioctl,
+	.d_dump = flashdump,
+	.d_psize = flashsize,
+	.d_flag = D_DISK | D_MPSAFE
+};
+
+/**
+ * Character device's operations
+ */
+const struct cdevsw flash_cdevsw = {
+	.d_open = flashopen,
+	.d_close = flashclose,
+	.d_read = flashread,
+	.d_write = flashwrite,
+	.d_ioctl = flashioctl,
+	.d_stop = nostop,
+	.d_tty = notty,
+	.d_poll = nopoll,
+	.d_mmap = nommap,
+	.d_kqfilter = nokqfilter,
+	.d_flag = D_DISK | D_MPSAFE
+};
+
+/* ARGSUSED */
+int
+flash_match(device_t parent, cfdata_t match, void *aux)
+{
+	/* pseudo device, always attaches */
+	return 1;
+}
+
+/* ARGSUSED */
+void
+flash_attach(device_t parent, device_t self, void *aux)
+{
+	struct flash_softc *sc = device_private(self);
+	struct flash_attach_args *faa = aux;
+	char pbuf[2][sizeof("9999 KB")];
+
+	sc->sc_dev = self;
+	sc->sc_parent_dev = parent;
+	sc->flash_if = faa->flash_if;
+	sc->hw_softc = device_private(parent);
+
+	format_bytes(pbuf[0], sizeof(pbuf[0]), sc->flash_if->size);
+	format_bytes(pbuf[1], sizeof(pbuf[1]), sc->flash_if->erasesize);
+
+	aprint_naive("\n");
+	
+	switch (sc->flash_if->type) {
+	case FLASH_TYPE_NOR:
+		aprint_normal(": %s NOR flash\n", pbuf[0]);
+		break;
+
+	case FLASH_TYPE_NAND:
+		aprint_normal(": %s NAND flash\n", pbuf[0]);
+		break;
+		
+	default:
+		aprint_normal(": %s unknown flash\n", pbuf[0]);
+	}
+
+	aprint_normal_dev(sc->sc_dev,
+	    "size: 0x%jx, offset: 0x%jx",
+	    (uintmax_t )sc->flash_if->partition.part_size,
+	    (uintmax_t )sc->flash_if->partition.part_offset);
+
+	if (sc->flash_if->partition.part_flags & FLASH_PART_READONLY) {
+		sc->sc_readonly = true;
+		aprint_normal(", read only");
+	} else {
+		sc->sc_readonly = false;
+	}
+
+	aprint_normal("\n");
+
+	if (sc->flash_if->partition.part_size == 0) {
+		aprint_error_dev(self,
+		    "partition size must be larger than 0\n");
+		return;
+	}
+	
+	switch (sc->flash_if->type) {
+	case FLASH_TYPE_NOR:
+		aprint_normal_dev(sc->sc_dev,
+		    "erase size %s bytes, write size %d bytes\n",
+		    pbuf[1], sc->flash_if->writesize);
+		break;
+
+	case FLASH_TYPE_NAND:
+	default:
+		aprint_normal_dev(sc->sc_dev,
+		    "erase size %s, page size %d bytes, write size %d bytes\n",
+		    pbuf[1], sc->flash_if->page_size,
+		    sc->flash_if->writesize);
+		break;
+	}
+	
+	atomic_inc_uint(&flash_usage_count);
+
+	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, flash_shutdown))
+		aprint_error_dev(sc->sc_dev,
+		    "couldn't establish power handler\n");
+}
+
+int
+flash_detach(device_t device, int flags)
+{
+	struct flash_softc *sc = device_private(device);
+
+	pmf_device_deregister(sc->sc_dev);
+	atomic_dec_uint(&flash_usage_count);
+
+	/* freeing flash_if is our responsibility */
+	kmem_free(sc->flash_if, sizeof(*sc->flash_if));
+	
+	return 0;
+}
+
+int
+flash_print(void *aux, const char *pnp)
+{
+	struct flash_attach_args *arg;
+	const char *type;
+
+	if (pnp != NULL) {
+		arg = aux;
+		switch (arg->flash_if->type) {
+		case FLASH_TYPE_NOR:
+			type = "NOR";
+			break;
+		case FLASH_TYPE_NAND:
+			type = "NAND";
+			break;
+		default:
+			panic("flash_print: unknown type %d",
+			    arg->flash_if->type);
+		}
+		aprint_normal("%s flash at %s", type, pnp);
+	}
+	return UNCONF;
+}
+
+device_t
+flash_attach_mi(struct flash_interface *flash_if, device_t device)
+{
+	struct flash_attach_args arg;
+
+#ifdef DIAGNOSTIC
+	if (flash_if == NULL) {
+		aprint_error("flash_attach_mi: NULL\n");
+		return 0;
+	}
+#endif
+	arg.flash_if = flash_if;
+
+	return config_found_ia(device, "flashbus", &arg, flash_print);
+}
+
+/**
+ * flash_open - open the character device
+ * Checks if there is a driver registered to the minor number of the open
+ * request.
+ */
+int
+flashopen(dev_t dev, int flags, int fmt, struct lwp *process)
+{
+	int unit = minor(dev);
+	struct flash_softc *sc;
+
+	DPRINTFN(1, ("flash: opening device unit %d\n", unit));
+
+	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+		return ENXIO;
+
+	/* TODO return eperm if want to open for writing a read only dev */
+
+	/* reset buffer length */
+//	sc->sc_cache->fc_len = 0;
+	
+	return 0;
+}
+
+/**
+ * flash_close - close device
+ * We don't have to release any resources, so just return 0.
+ */
+int
+flashclose(dev_t dev, int flags, int fmt, struct lwp *process)
+{
+	int unit = minor(dev);
+	struct flash_softc *sc;
+	int err;
+
+	DPRINTFN(1, ("flash: closing flash device unit %d\n", unit));
+
+	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+		return ENXIO;
+
+	if (!sc->sc_readonly) {
+		err = flash_sync(sc->sc_dev);
+		if (err)
+			return err;
+	}
+	
+	return 0;
+}
+
+/**
+ * flash_read - read from character device
+ * This function uses the registered driver's read function to read the requested length to
+ * a buffer and then moves this buffer to userspace.
+ */
+int
+flashread(dev_t dev, struct uio *uio, int flag)
+{
+	return physio(flashstrategy, NULL, dev, B_READ, minphys, uio);
+}
+
+/**
+ * flash_write - write to character device
+ * This function moves the data into a buffer from userspace to kernel space,
+ * then uses the registered driver's write function to write out the data to
+ * the media.
+ */
+int
+flashwrite(dev_t dev, struct uio *uio, int flag)
+{
+	return physio(flashstrategy, NULL, dev, B_WRITE, minphys, uio);
+}
+
+void
+flashstrategy(struct buf *bp)
+{
+	struct flash_softc *sc;
+	const struct flash_interface *flash_if;
+	const struct flash_partition *part;
+	int unit, device_blks;
+
+	unit = minor(bp->b_dev);
+	sc = device_lookup_private(&flash_cd, unit);
+	if (sc == NULL) {
+		bp->b_error = ENXIO;
+		goto done;
+	}
+
+	flash_if = sc->flash_if;
+	part = &flash_if->partition;
+
+	/* divider */
+	KASSERT(flash_if->writesize != 0);
+	
+	aprint_debug_dev(sc->sc_dev, "flash_strategy()\n");
+
+	if (!(bp->b_flags & B_READ) && sc->sc_readonly) {
+		bp->b_error = EACCES;
+		goto done;
+	}
+
+	/* check if length is not negative */
+	if (bp->b_blkno < 0) {
+		bp->b_error = EINVAL;
+		goto done;
+	}
+
+	/* zero lenght i/o */
+	if (bp->b_bcount == 0) {
+		goto done;
+	}
+
+	device_blks = sc->flash_if->size / DEV_BSIZE;
+	KASSERT(part->part_offset % DEV_BSIZE == 0);
+	bp->b_rawblkno = bp->b_blkno + (part->part_offset / DEV_BSIZE);
+
+	if (bounds_check_with_mediasize(bp, DEV_BSIZE, device_blks) <= 0) {
+		goto done;
+	}
+
+	bp->b_resid = bp->b_bcount;
+	flash_if->submit(sc->sc_parent_dev, bp);
+	
+	return;
+done:
+	bp->b_resid = bp->b_bcount;
+	biodone(bp);
+}
+
+/*
+ * Handle the ioctl for the device
+ */
+int
+flashioctl(dev_t dev, u_long command, void *data, int flags,
+		 struct lwp *process)
+{
+	struct flash_erase_params *ep;
+	struct flash_info_params *ip;
+	struct flash_dump_params *dp;
+	struct flash_badblock_params *bbp;
+	struct flash_softc *sc;
+	int unit, err;
+	size_t retlen;
+	flash_addr_t offset;
+	
+	unit = minor(dev);
+	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+		return ENXIO;
+
+	err = 0;
+	switch (command) {
+	case FLASH_ERASE_BLOCK:
+		/**
+		 * Set up an erase instruction then call the registered
+		 * driver's erase operation.
+		 */
+		ep = data;
+
+		if (sc->sc_readonly)
+			return EACCES;
+
+//		ei = kmem_zalloc(sizeof(*ei), KM_SLEEP);
+		struct flash_erase_instruction ei;
+		
+		ei.ei_addr = ep->ep_addr;
+		ei.ei_len = ep->ep_len;
+//		ei.ei_if = sc->flash_if;
+		ei.ei_callback = NULL;
+		err = flash_erase(sc->sc_dev, &ei);
+		if (err)
+			return err;
+
+		break;
+	case FLASH_BLOCK_ISBAD:
+		/**
+		 * Set up an erase instruction then call the registered
+		 * driver's erase operation.
+		 */
+		bbp = data;
+
+		err = flash_block_isbad(sc->sc_dev, bbp->bbp_addr);
+		if (err == EIO) {
+			bbp->bbp_isbad = true;
+			err = 0;
+		} else if (err) {
+			return err;
+		} else {
+			bbp->bbp_isbad = false;
+		}
+
+		break;
+	case FLASH_BLOCK_MARKBAD:
+		bbp = data;
+
+		err = flash_block_markbad(sc->sc_dev, bbp->bbp_addr);
+
+		break;		
+	case FLASH_DUMP:
+		dp = data;
+		offset = dp->dp_block * sc->flash_if->erasesize;
+		DPRINTF(("Reading from block: %jd len: %jd\n",
+			(intmax_t )dp->dp_block, (intmax_t )dp->dp_len));
+		err = flash_read(sc->sc_parent_dev, offset, dp->dp_len,
+		    &retlen, dp->dp_buf);
+		if (err)
+			return err;
+		if (retlen != dp->dp_len) {
+			dp->dp_len = -1;
+			dp->dp_buf = NULL;
+		}
+
+		break;
+	case FLASH_GET_INFO:
+		ip = data;
+		
+		ip->ip_page_size = sc->flash_if->page_size;
+		ip->ip_erase_size = sc->flash_if->erasesize;
+		ip->ip_flash_type = sc->flash_if->type;
+		ip->ip_flash_size = sc->flash_if->size;
+		break;
+	default:
+		err = ENODEV;
+	}
+
+	return err;
+}
+int
+flashsize(dev_t dev)
+{
+    return -1;
+}
+
+int
+flashdump(dev_t dev, daddr_t blkno, void *va, size_t size)
+{
+    return EACCES;
+}
+
+bool
+flash_shutdown(device_t self, int how)
+{
+	struct flash_softc *sc = device_private(self);
+	
+	if ((how & RB_NOSYNC) == 0 && !sc->sc_readonly)
+		flash_sync(self);
+
+	return true;
+}
+
+const struct flash_interface *
+flash_get_interface(dev_t dev)
+{
+	struct flash_softc *sc;
+	int unit;
+
+	unit = minor(dev);
+	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+		return NULL;
+
+	return sc->flash_if;
+}
+
+const struct flash_softc *
+flash_get_softc(dev_t dev)
+{
+	struct flash_softc *sc;
+	int unit;
+
+	unit = minor(dev);
+	sc = device_lookup_private(&flash_cd, unit);
+
+	return sc;
+}
+
+device_t
+flash_get_device(dev_t dev)
+{
+	struct flash_softc *sc;
+	int unit;
+
+	unit = minor(dev);
+	sc = device_lookup_private(&flash_cd, unit);
+
+	return sc->sc_dev;
+}
+
+static inline off_t
+flash_get_part_offset(struct flash_softc *fl, size_t poffset)
+{
+	return fl->flash_if->partition.part_offset + poffset;
+}
+
+int
+flash_erase(device_t self, struct flash_erase_instruction *ei)
+{
+	struct flash_softc *sc = device_private(self);
+	KASSERT(ei != NULL);
+	struct flash_erase_instruction e = *ei;
+
+	if (sc->sc_readonly)
+		return EACCES;
+
+	/* adjust for flash partition */
+	e.ei_addr += sc->flash_if->partition.part_offset;
+
+	/* bounds check for flash partition */
+	if (e.ei_addr + e.ei_len > sc->flash_if->partition.part_size +
+	    sc->flash_if->partition.part_offset)
+		return EINVAL;
+	
+	return sc->flash_if->erase(device_parent(self), &e);
+}
+
+int
+flash_read(device_t self,
+    off_t offset, size_t len, size_t *retlen, uint8_t *buf)
+{
+	struct flash_softc *sc = device_private(self);
+
+	offset += sc->flash_if->partition.part_offset;
+
+	if (offset + len > sc->flash_if->partition.part_size +
+	    sc->flash_if->partition.part_offset)
+		return EINVAL;
+
+	return sc->flash_if->read(device_parent(self),
+	    offset, len, retlen, buf);
+}
+
+int
+flash_write(device_t self,
+    off_t offset, size_t len, size_t *retlen, const uint8_t *buf)
+{
+	struct flash_softc *sc = device_private(self);
+
+	if (sc->sc_readonly)
+		return EACCES;
+
+	offset += sc->flash_if->partition.part_offset;
+
+	if (offset + len > sc->flash_if->partition.part_size +
+	    sc->flash_if->partition.part_offset)
+		return EINVAL;
+
+	return sc->flash_if->write(device_parent(self),
+	    offset, len, retlen, buf);
+}
+
+int
+flash_block_markbad(device_t self, uint64_t offset)
+{
+	struct flash_softc *sc = device_private(self);
+
+	if (sc->sc_readonly)
+		return EACCES;
+	
+	offset += sc->flash_if->partition.part_offset;
+	
+	if (offset + sc->flash_if->erasesize >=
+	    sc->flash_if->partition.part_size +
+	    sc->flash_if->partition.part_offset)
+		return EINVAL;
+
+	return sc->flash_if->block_markbad(device_parent(self), offset);
+}
+
+int
+flash_block_isbad(device_t self, uint64_t offset)
+{
+	struct flash_softc *sc = device_private(self);
+	
+	offset += sc->flash_if->partition.part_offset;
+
+	if (offset + sc->flash_if->erasesize >
+	    sc->flash_if->partition.part_size +
+	    sc->flash_if->partition.part_offset)
+		return EINVAL;
+
+	return sc->flash_if->block_isbad(device_parent(self), offset);
+}
+
+int
+flash_sync(device_t self)
+{
+	struct flash_softc *sc = device_private(self);
+	
+	if (sc->sc_readonly)
+		return EACCES;
+
+	/* noop now TODO: implement */
+	return 0;
+}
+
+#if defined(_MODULE) && !defined(_FLASH_RUMP)
+CFDRIVER_DECL(flash, DV_DULL, NULL);
+#endif
+
+MODULE(MODULE_CLASS_MISC, flash, NULL);
+
+static int
+flash_modcmd(modcmd_t cmd, void *arg)
+{
+#ifdef _MODULE
+	/* XXX TODO: fix this */
+	int bmajor = -1, cmajor = -1, err;
+	
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+		err = config_cfdriver_attach(&flash_cd);
+		if (err)
+			return err;
+
+		err = config_cfattach_attach(flash_cd.cd_name, &flash_ca);
+		if (err) {
+			config_cfdriver_detach(&flash_cd);
+			aprint_error("%s: unable to register cfattach\n",
+				flash_cd.cd_name);
+
+			return err;
+		}
+		
+		err = devsw_attach(flash_cd.cd_name, &flash_bdevsw, &bmajor,
+		    &flash_cdevsw, &cmajor);
+		
+		return err;
+
+	case MODULE_CMD_FINI:
+		if (flash_usage_count > 0)
+			return EBUSY;
+
+		config_cfattach_detach(flash_cd.cd_name, &flash_ca);
+		config_cfdriver_detach(&flash_cd);
+		devsw_detach(&flash_bdevsw, &flash_cdevsw);
+		
+		return 0;
+
+	case MODULE_CMD_STAT:
+		return ENOTTY;
+
+	default:
+		return ENOTTY;
+	}
+
+	return 0;
+#else
+	return 0;
+#endif
+}
Index: sys/dev/flash/flash.h
===================================================================
RCS file: sys/dev/flash/flash.h
diff -N sys/dev/flash/flash.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/flash/flash.h	16 Feb 2011 13:48:18 -0000
@@ -0,0 +1,168 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FLASH_H_
+#define _FLASH_H_
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/buf.h>
+#include <sys/flashio.h>
+
+/**
+ *  flash_softc - private structure for flash layer driver
+ */
+
+struct flash_softc {
+	device_t sc_dev;
+	device_t sc_parent_dev;		/* Hardware (parent) device */
+	void *hw_softc;			/* Hardware device private softc */
+	struct flash_interface *flash_if;	/* Hardware interface */
+
+	bool sc_readonly;		/* read only flash device */
+};
+
+struct flash_attach_args {
+	struct flash_interface *flash_if;	/* Hardware interface */
+};
+
+device_t flash_attach_mi(struct flash_interface *, device_t);
+const struct flash_interface *flash_get_interface(dev_t);
+const struct flash_softc *flash_get_softc(dev_t);
+device_t flash_get_device(dev_t);
+
+/**
+ * struct erase_instruction - instructions to erase a flash eraseblock
+ * @fd: flash descriptor
+ * @addr: start address of the erase operation
+ * @len: the erase length
+ * @callback: callback operation, called when erase finished
+ * @priv: private data
+ * @state: the erase operation's result
+ */
+struct flash_erase_instruction {
+	flash_addr_t ei_addr;
+	flash_addr_t ei_len;
+	void (*ei_callback) (struct flash_erase_instruction *self);
+	u_long ei_priv;
+	u_char ei_state;
+};
+
+enum {
+	FLASH_PART_READONLY	= (1<<0),
+	FLASH_PART_FILESYSTEM	= (1<<2)
+};
+
+struct flash_partition {
+	flash_addr_t part_offset;
+	flash_addr_t part_size;
+	int part_flags;
+};
+
+/**
+ * struct flash_interface - interface for flash operations
+ * @type: type of flash device
+ * @size: size of flash
+ * @page_size: page size of flash
+ * @erasesize: erase size of flash
+ * @writesize: minimum write size of flash
+ * @minor: minor number of the character device attached to this driver
+ * @erase: erase operation of the flash
+ * @read: read operation of the flash
+ * @write: write operation of the flash
+ * @block_markbad: marks a block as bad on the flash
+ * @block_isbad: checks if a block is bad on flash
+ */
+struct flash_interface {
+	int (*erase) (device_t, struct flash_erase_instruction *);
+	int (*read) (device_t, off_t, size_t, size_t *, uint8_t *);
+	int (*write) (device_t, off_t, size_t, size_t *, const uint8_t *);
+	int (*block_markbad)(device_t, uint64_t);
+	int (*block_isbad)(device_t, uint64_t);
+	int (*sync) (device_t);
+
+	int (*submit)(device_t, struct buf *);
+
+	/* storage for partition info */
+	struct flash_partition partition;
+
+	/* total size of mtd */
+	flash_addr_t size;	 
+	uint32_t page_size;
+	uint32_t erasesize;
+	uint32_t writesize;
+	uint32_t minor;
+	uint8_t	type;
+};
+
+/**
+ * struct cache - for caching writes on block device
+ */
+struct flash_cache {
+	size_t fc_len;
+	flash_addr_t fc_block;
+	uint8_t *fc_data;
+};
+
+/* flash operations should be used through these */
+int flash_erase(device_t, struct flash_erase_instruction *);
+int flash_read(device_t, off_t, size_t, size_t *, uint8_t *);
+int flash_write(device_t, off_t, size_t, size_t *, const uint8_t *);
+int flash_block_markbad(device_t, uint64_t);
+int flash_block_isbad(device_t, uint64_t);
+int flash_sync(device_t);
+
+/*
+ * check_pattern - checks the buffer only contains the byte pattern
+ * @buf: the buffer to check
+ * @patt: the pattern to match
+ * @offset: the starting byte number, the matching starts from here
+ * @size: the buffer size
+ *
+ * This functions checks if the buffer only contains a specified byte pattern.
+ * Returns %0 if found something else, %1 otherwise.
+ */
+static inline int
+check_pattern(const void *buf, uint8_t patt, size_t offset, size_t size)
+{
+	size_t i;
+	for (i = offset; i < size; i++) {
+		if (((const uint8_t *) buf)[i] != patt)
+			return 0;
+	}
+	return 1;
+}
+
+#endif /* _FLASH_H_ */
Index: sys/dev/nand/files.nand
===================================================================
RCS file: sys/dev/nand/files.nand
diff -N sys/dev/nand/files.nand
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/files.nand	16 Feb 2011 13:48:22 -0000
@@ -0,0 +1,16 @@
+# $NetBSD$
+
+define	nandbus	{ }
+
+device	nand: flashbus
+attach	nand at nandbus
+file	dev/nand/nand.c		nand
+file	dev/nand/nand_io.c	nand
+file	dev/nand/hamming.c	nand
+file	dev/nand/nand_bbt.c	nand
+file	dev/nand/nand_crc.c	nand
+
+defpseudodev	nandemulator: nandbus
+file	dev/nand/nandemulator.c	nandemulator
+
+defflag opt_nand.h		NAND_BBT
Index: sys/dev/nand/hamming.c
===================================================================
RCS file: sys/dev/nand/hamming.c
diff -N sys/dev/nand/hamming.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/hamming.c	16 Feb 2011 13:48:22 -0000
@@ -0,0 +1,232 @@
+/*	$NetBSD$	*/
+
+/*
+ * Copyright (c) 2008, Atmel Corporation
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the disclaimer below.
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <lib/libkern/libkern.h>
+#include "hamming.h"
+
+/**
+ * Calculates the 22-bit hamming code for a 256-bytes block of data.
+ * \param data  Data buffer to calculate code for.
+ * \param code  Pointer to a buffer where the code should be stored.
+ */
+void
+hamming_compute_256(const uint8_t *data, uint8_t *code)
+{
+	unsigned int i;
+	uint8_t column_sum = 0;
+	uint8_t even_line_code = 0;
+	uint8_t odd_line_code = 0;
+	uint8_t even_column_code = 0;
+	uint8_t odd_column_code = 0;
+
+	/*-
+	 * Xor all bytes together to get the column sum;
+	 * At the same time, calculate the even and odd line codes
+	 */
+	for (i = 0; i < 256; i++) {
+		column_sum ^= data[i];
+
+		/*-
+		 * If the xor sum of the byte is 0, then this byte has no
+		 * incidence on the computed code; so check if the sum is 1.
+		 */
+		if ((popcount(data[i]) & 1) == 1) {
+			/*-
+			 * Parity groups are formed by forcing a particular
+			 * index bit to 0 (even) or 1 (odd).
+			 * Example on one byte:
+			 * 
+			 * bits (dec)  7   6   5   4   3   2   1   0    
+			 *      (bin) 111 110 101 100 011 010 001 000    
+			 *                            '---'---'---'----------.
+			 *                                                   |
+			 * groups P4' ooooooooooooooo eeeeeeeeeeeeeee P4     |
+			 *        P2' ooooooo eeeeeee ooooooo eeeeeee P2     |
+			 *        P1' ooo eee ooo eee ooo eee ooo eee P1     |
+			 *                                                   |
+			 * We can see that:                                  |
+			 *  - P4  -> bit 2 of index is 0 --------------------'
+			 *  - P4' -> bit 2 of index is 1.
+			 *  - P2  -> bit 1 of index if 0.
+			 *  - etc...
+			 * We deduce that a bit position has an impact on all
+			 * even Px if the log2(x)nth bit of its index is 0
+			 *     ex: log2(4) = 2,
+			 * bit2 of the index must be 0 (-> 0 1 2 3)
+			 * and on all odd Px' if the log2(x)nth bit
+			 * of its index is 1
+			 *     ex: log2(2) = 1,
+			 * bit1 of the index must be 1 (-> 0 1 4 5)
+			 * 
+			 * As such, we calculate all the possible Px and Px'
+			 * values at the same time in two variables,
+			 * even_line_code and odd_line_code, such as
+			 *     even_line_code bits: P128  P64  P32
+			 *                        P16  P8  P4  P2  P1
+			 *     odd_line_code  bits: P128' P64' P32' P16'
+			 *                        P8' P4' P2' P1'
+			 */
+			even_line_code ^= (255 - i);
+			odd_line_code ^= i;
+		}
+	}
+
+	/*- 
+	 * At this point, we have the line parities, and the column sum.
+	 * First, We must caculate the parity group values on the column sum.
+	 */
+	for (i = 0; i < 8; i++) {
+		if (column_sum & 1) {
+			even_column_code ^= (7 - i);
+			odd_column_code ^= i;
+		}
+		column_sum >>= 1;
+	}
+
+	/*-
+	 * Now, we must interleave the parity values,
+	 * to obtain the following layout:
+	 * Code[0] = Line1
+	 * Code[1] = Line2
+	 * Code[2] = Column
+	 * Line = Px' Px P(x-1)- P(x-1) ...
+	 * Column = P4' P4 P2' P2 P1' P1 PadBit PadBit 
+	 */
+	code[0] = 0;
+	code[1] = 0;
+	code[2] = 0;
+
+	for (i = 0; i < 4; i++) {
+		code[0] <<= 2;
+		code[1] <<= 2;
+		code[2] <<= 2;
+
+		/* Line 1 */
+		if ((odd_line_code & 0x80) != 0) {
+
+			code[0] |= 2;
+		}
+		if ((even_line_code & 0x80) != 0) {
+
+			code[0] |= 1;
+		}
+
+		/* Line 2 */
+		if ((odd_line_code & 0x08) != 0) {
+
+			code[1] |= 2;
+		}
+		if ((even_line_code & 0x08) != 0) {
+
+			code[1] |= 1;
+		}
+
+		/* Column */
+		if ((odd_column_code & 0x04) != 0) {
+
+			code[2] |= 2;
+		}
+		if ((even_column_code & 0x04) != 0) {
+
+			code[2] |= 1;
+		}
+
+		odd_line_code <<= 1;
+		even_line_code <<= 1;
+		odd_column_code <<= 1;
+		even_column_code <<= 1;
+	}
+
+	/* Invert codes (linux compatibility) */
+	code[0] = ~code[0];
+	code[1] = ~code[1];
+	code[2] = ~code[2];
+}
+
+/**
+ * Verifies and corrects a 256-bytes block of data using the given 22-bits
+ * hamming code.
+ * Returns 0 if there is no error, otherwise returns a HAMMING_ERROR code.
+ * param data  Data buffer to check.
+ * \param original_code  Hamming code to use for verifying the data.
+ */
+uint8_t
+hamming_correct_256(uint8_t *data, const uint8_t *original_code,
+    const uint8_t *computed_code)
+{
+	/* Calculate new code */
+	/* we allocate 4 bytes so we can use popcount32 in one step */
+	uint8_t correction_code[4];
+
+	/* this byte should remain zero all the time */
+	correction_code[3] = 0;
+
+	/* Xor both codes together */
+	correction_code[0] = computed_code[0] ^ original_code[0];
+	correction_code[1] = computed_code[1] ^ original_code[1];
+	correction_code[2] = computed_code[2] ^ original_code[2];
+
+	/* If all bytes are 0, there is no error */
+	if (*(uint32_t *)correction_code == 0) {
+		return 0;
+	}
+	/* If there is a single bit error, there are 11 bits set to 1 */
+	if (popcount32(*(uint32_t *)correction_code) == 11) {
+		/* Get byte and bit indexes */
+		uint8_t byte = correction_code[0] & 0x80;
+		byte |= (correction_code[0] << 1) & 0x40;
+		byte |= (correction_code[0] << 2) & 0x20;
+		byte |= (correction_code[0] << 3) & 0x10;
+
+		byte |= (correction_code[1] >> 4) & 0x08;
+		byte |= (correction_code[1] >> 3) & 0x04;
+		byte |= (correction_code[1] >> 2) & 0x02;
+		byte |= (correction_code[1] >> 1) & 0x01;
+
+		uint8_t bit = (correction_code[2] >> 5) & 0x04;
+		bit |= (correction_code[2] >> 4) & 0x02;
+		bit |= (correction_code[2] >> 3) & 0x01;
+
+		/* Correct bit */
+		data[byte] ^= (1 << bit);
+
+		return HAMMING_ERROR_SINGLEBIT;
+	}
+	/* Check if ECC has been corrupted */
+	if (popcount32(*(uint32_t *)correction_code) == 1) {
+		return HAMMING_ERROR_ECC;
+	} else {
+		/* Otherwise, this is a multi-bit error */
+		return HAMMING_ERROR_MULTIPLEBITS;
+	}
+}
+
Index: sys/dev/nand/hamming.h
===================================================================
RCS file: sys/dev/nand/hamming.h
diff -N sys/dev/nand/hamming.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/hamming.h	16 Feb 2011 13:48:22 -0000
@@ -0,0 +1,56 @@
+/*	$NetBSD$	*/
+
+/*
+ * Copyright (c) 2008, Atmel Corporation
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the disclaimer below.
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HAMMING_H
+#define HAMMING_H
+
+#include "nand.h"
+
+/*-
+ * These are the possible errors when trying to verify a block of data encoded
+ * using a Hamming code:
+ * 
+ *  - HAMMING_ERROR_SINGLEBIT
+ *  - HAMMING_ERROR_ECC
+ *  - HAMMING_ERROR_MULTIPLEBITS
+ */
+
+/* A single bit was incorrect but has been recovered. */
+#define HAMMING_ERROR_SINGLEBIT		NAND_ECC_CORRECTED
+
+/* The original code has been corrupted. */
+#define HAMMING_ERROR_ECC		NAND_ECC_INVALID
+
+/* Multiple bits are incorrect in the data and they cannot be corrected. */
+#define HAMMING_ERROR_MULTIPLEBITS	NAND_ECC_TWOBIT
+
+uint8_t hamming_correct_256(uint8_t *, const uint8_t *, const uint8_t *);
+void hamming_compute_256(const uint8_t *data, uint8_t *code);
+
+#endif /* HAMMING_H */
+
Index: sys/dev/nand/nand.c
===================================================================
RCS file: sys/dev/nand/nand.c
diff -N sys/dev/nand/nand.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand.c	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,1453 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Common driver for NAND chips implementing the ONFI 2.2 specification */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include "locators.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/sysctl.h>
+
+#include <dev/flash/flash.h>
+#include <dev/nand/nand.h>
+#include <dev/nand/onfi.h>
+#include <dev/nand/hamming.h>
+#include <dev/nand/nand_bbt.h>
+#include <dev/nand/nand_crc.h>
+
+#include "opt_nand.h"
+
+int nand_match(device_t parent, cfdata_t match, void *aux);
+void nand_attach(device_t parent, device_t self, void *aux);
+int nand_detach(device_t device, int flags);
+bool nand_shutdown(device_t self, int howto);
+
+int nand_print(void *aux, const char *pnp);
+
+static int nand_search(device_t parent, cfdata_t cf, const int *ldesc,
+    void *aux);
+static void nand_address_row(device_t self, size_t row);
+static void nand_address_column(device_t self, size_t row, size_t column);
+static void nand_readid(device_t self, struct nand_chip *chip);
+static void nand_read_parameter_page(device_t self, struct nand_chip *chip);
+static const char *nand_midtoname(int id);
+static int nand_scan_media(device_t self, struct nand_chip *chip);
+static bool nand_check_wp(device_t self);
+
+CFATTACH_DECL_NEW(nand, sizeof(struct nand_softc),
+    nand_match, nand_attach, nand_detach, NULL);
+
+#ifdef NAND_DEBUG
+int	nanddebug = NAND_DEBUG;
+#endif
+
+int nand_cachesync_timeout = 1;
+int nand_cachesync_nodenum;
+
+const struct nand_manufacturer nand_mfrs[] = {
+	{ NAND_MFR_AMD,		"AMD" },
+	{ NAND_MFR_FUJITSU,	"Fujitsu" },
+	{ NAND_MFR_RENESAS,	"Renesas" },
+	{ NAND_MFR_STMICRO,	"ST Micro" },
+	{ NAND_MFR_MICRON,	"Micron" },
+	{ NAND_MFR_NATIONAL,	"National" },
+	{ NAND_MFR_TOSHIBA,	"Toshiba" },
+	{ NAND_MFR_HYNIX,	"Hynix" },
+	{ NAND_MFR_SAMSUNG,	"Samsung" },
+	{ NAND_MFR_UNKNOWN,	"Unknown" }
+};
+
+/* ARGSUSED */
+int
+nand_match(device_t parent, cfdata_t match, void *aux)
+{
+	/* pseudo device, always attaches */
+	return 1;
+}
+
+void
+nand_attach(device_t parent, device_t self, void *aux)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_attach_args *naa = aux;
+	struct nand_chip *chip = &sc->sc_chip;
+//	struct flash_interface *flash_if;
+//	int i;
+
+	sc->sc_dev = self;
+	sc->nand_dev = parent;
+	sc->nand_if = naa->naa_nand_if;
+
+//	sc->nand_softc = device_private(parent);
+
+	aprint_naive("\n");
+//	aprint_normal(": NAND flash memory\n");
+
+	if (nand_check_wp(self)) {
+		aprint_error("NAND chip is write protected!\n");
+		return;
+	}
+	if (nand_scan_media(self, chip))
+		return;
+
+	/* allocate cache */
+	chip->nc_oob_cache = kmem_alloc(chip->nc_spare_size, KM_SLEEP);
+	chip->nc_page_cache = kmem_alloc(chip->nc_page_size, KM_SLEEP);
+
+	mutex_init(&sc->sc_device_lock, MUTEX_DEFAULT, IPL_NONE);
+
+	if (nand_sync_thread_start(self)) {
+		goto error;
+	}
+
+	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, nand_shutdown))
+		aprint_error_dev(sc->sc_dev,
+		    "couldn't establish power handler\n");
+
+#ifdef NAND_BBT
+	nand_bbt_init(self);
+	nand_bbt_scan(self);
+#endif
+
+	/*
+	 * Attach all our devices
+	 */
+	config_search_ia(nand_search, self, NULL, NULL);
+
+	return;
+error:
+	kmem_free(chip->nc_oob_cache, chip->nc_spare_size);
+	kmem_free(chip->nc_page_cache, chip->nc_page_size);
+	mutex_destroy(&sc->sc_device_lock);
+}
+
+static int
+nand_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
+{
+	struct nand_softc *sc = device_private(parent);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct flash_interface *flash_if;
+	struct flash_attach_args faa;
+
+	flash_if = kmem_alloc(sizeof(*flash_if), KM_SLEEP);
+
+	flash_if->type = FLASH_TYPE_NAND;
+
+	flash_if->read = nand_flash_read;
+	flash_if->write = nand_flash_write;
+	flash_if->erase = nand_flash_erase;
+	flash_if->block_isbad = nand_flash_isbad;
+	flash_if->block_markbad = nand_flash_markbad;
+
+	flash_if->submit = nand_io_submit;
+
+	flash_if->erasesize = chip->nc_block_size;
+	flash_if->page_size = chip->nc_page_size;
+	flash_if->writesize = chip->nc_page_size;
+
+	flash_if->partition.part_offset = cf->cf_loc[FLASHBUSCF_OFFSET];
+
+	if (cf->cf_loc[FLASHBUSCF_SIZE] == 0) {
+		flash_if->size = chip->nc_size -
+		    flash_if->partition.part_offset;
+		flash_if->partition.part_size = flash_if->size;
+	} else {
+		flash_if->size = cf->cf_loc[FLASHBUSCF_SIZE];
+		flash_if->partition.part_size = cf->cf_loc[FLASHBUSCF_SIZE];
+	}
+
+	if (cf->cf_loc[FLASHBUSCF_READONLY])
+		flash_if->partition.part_flags = FLASH_PART_READONLY;
+	else
+		flash_if->partition.part_flags = 0;
+
+	faa.flash_if = flash_if;
+
+	if (config_match(parent, cf, &faa)) {
+		config_attach(parent, cf, &faa, nand_print);
+		return 0;
+	} else {
+		kmem_free(flash_if, sizeof(*flash_if));
+	}
+
+	return 1;
+}
+
+int
+nand_detach(device_t self, int flags)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	int ret = 0;
+
+#ifdef NAND_BBT
+	nand_bbt_detach(self);
+#endif
+	nand_sync_thread_stop(self);
+
+	/* free oob cache */
+	kmem_free(chip->nc_oob_cache, chip->nc_spare_size);
+	kmem_free(chip->nc_page_cache, chip->nc_page_size);
+	kmem_free(chip->nc_ecc_cache, chip->nc_ecc->necc_size);
+
+	mutex_destroy(&sc->sc_device_lock);
+
+	pmf_device_deregister(sc->sc_dev);
+
+	return ret;
+}
+
+int
+nand_print(void *aux, const char *pnp)
+{
+	if (pnp != NULL)
+		aprint_normal("nand at %s\n", pnp);
+
+	return UNCONF;
+}
+
+device_t
+nand_attach_mi(struct nand_interface *nand_if, device_t parent)
+{
+	struct nand_attach_args arg;
+
+	KASSERT(nand_if != NULL);
+
+	arg.naa_nand_if = nand_if;
+	return config_found_ia(parent, "nandbus", &arg, nand_print);
+}
+
+static const char *
+nand_midtoname(int id)
+{
+	int i;
+
+	for (i = 0; nand_mfrs[i].id != 0; i++) {
+		if (nand_mfrs[i].id == id)
+			return nand_mfrs[i].name;
+	}
+
+	KASSERT(nand_mfrs[i].id == 0);
+
+	return nand_mfrs[i].name;
+}
+
+#if 0
+/* handle quirks here */
+static void
+nand_quirks(device_t self, struct nand_chip *chip)
+{
+	/* this is an example only! */
+	switch (chip->nc_manf_id) {
+	case NAND_MFR_SAMSUNG:
+		if (chip->nc_dev_id == 0x00) {
+			/* do something only samsung chips need */
+			/* or */
+			/* chip->nc_quirks |= NC_QUIRK_NO_READ_START */
+		}
+	}
+
+	return;
+}
+#endif
+
+/**
+ * scan media to determine the chip's properties
+ * this function resets the device
+ */
+static int
+nand_scan_media(device_t self, struct nand_chip *chip)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_ecc *ecc;
+	uint8_t onfi_signature[4];
+
+	nand_select(self, true);
+	nand_command(self, ONFI_RESET);
+	nand_select(self, false);
+
+	nand_select(self, true);
+	nand_command(self, ONFI_READ_ID);
+	nand_address(self, 0x20);
+	nand_read_byte(self, &onfi_signature[0]);
+	nand_read_byte(self, &onfi_signature[1]);
+	nand_read_byte(self, &onfi_signature[2]);
+	nand_read_byte(self, &onfi_signature[3]);
+	nand_select(self, false);
+
+	if (onfi_signature[0] != 'O' || onfi_signature[1] != 'N' ||
+	    onfi_signature[2] != 'F' || onfi_signature[3] != 'I') {
+		aprint_error_dev(self,
+		    "device does not support the ONFI specification\n");
+
+		return 1;
+	}
+
+	nand_readid(self, chip);
+
+	aprint_normal(": NAND Flash\n");
+
+	aprint_debug_dev(self,
+	    "manufacturer id: 0x%.2x (%s), device id: 0x%.2x\n",
+	    chip->nc_manf_id,
+	    nand_midtoname(chip->nc_manf_id),
+	    chip->nc_dev_id);
+
+	nand_read_parameter_page(self, chip);
+
+	ecc = chip->nc_ecc = &sc->nand_if->ecc;
+
+	/*
+	 * calculate the place of ecc data in oob
+	 * we try to be compatible with Linux here
+	 */
+	switch (chip->nc_spare_size) {
+	case 8:
+		ecc->necc_offset = 0;
+		break;
+	case 16:
+		ecc->necc_offset = 0;
+		break;
+	case 64:
+		ecc->necc_offset = 40;
+		break;
+	case 128:
+		ecc->necc_offset = 80;
+		break;
+	default:
+		panic("OOB size is unexpected");
+	}
+
+	ecc->necc_steps = chip->nc_page_size / ecc->necc_block_size;
+	ecc->necc_size = ecc->necc_steps * ecc->necc_code_size;
+
+	/* check if we fit in oob */
+	if (ecc->necc_offset + ecc->necc_size > chip->nc_spare_size) {
+		panic("NAND ECC bits dont fit in OOB");
+	}
+
+	/* TODO: mark free oob area available for file systems */
+
+	chip->nc_ecc_cache = kmem_zalloc(ecc->necc_size, KM_SLEEP);
+
+	/*
+	 * calculate badblock marker offset in oob
+	 * we try to be compatible with linux here
+	 */
+	if (chip->nc_page_size > 512)
+		chip->nc_badmarker_offs = 0;
+	else
+		chip->nc_badmarker_offs = 5;
+
+	/* Calculate page shift and mask */
+	chip->nc_page_shift = ffs(chip->nc_page_size) - 1;
+	chip->nc_page_mask = ~(chip->nc_page_size - 1);
+	/* same for block */
+	chip->nc_block_shift = ffs(chip->nc_block_size) - 1;
+	chip->nc_block_mask = ~(chip->nc_block_size - 1);
+
+	/* look for quirks here if needed in future */
+	/* nand_quirks(self, chip); */
+
+	return 0;
+}
+
+static void
+nand_readid(device_t self, struct nand_chip *chip)
+{
+	nand_select(self, true);
+	nand_command(self, ONFI_READ_ID);
+	nand_address(self, 0x00);
+	nand_read_byte(self, &chip->nc_manf_id);
+	nand_read_byte(self, &chip->nc_dev_id);
+	nand_select(self, false);
+}
+
+/* read the parameter page. TODO: check CRC! */
+static void
+nand_read_parameter_page(device_t self, struct nand_chip *chip)
+{
+	struct onfi_parameter_page params;
+	uint8_t *bufp;
+	uint8_t	vendor[13], model[21];
+	uint16_t crc;
+	int i;
+
+	KASSERT(sizeof(params) == 256);
+
+	nand_select(self, true);
+	nand_command(self, ONFI_READ_PARAMETER_PAGE);
+	nand_address(self, 0x00);
+
+	nand_busy(self);
+
+	bufp = (uint8_t *)&params;
+	for (i = 0; i < 256; i++) {
+		nand_read_byte(self, &bufp[i]);
+	}
+	nand_select(self, false);
+	
+	/* validate the parameter page with the crc */
+	crc = nand_crc16(bufp, 254);
+
+	if (crc != params.param_integrity_crc) {
+		aprint_error_dev(self, "parameter page crc check failed\n");
+		printf("original 0x%hx, calculated: 0x%hx\n",
+			params.param_integrity_crc, crc);
+	} else {
+		/* XXX remove this */
+		aprint_normal_dev(self, "param page ecc OK\n");
+	}
+
+	/* strip manufacturer and model string */
+	strlcpy(vendor, params.param_manufacturer, sizeof(vendor));
+	for (i = 11; i > 0 && vendor[i] == ' '; i--)
+		vendor[i] = 0;
+	strlcpy(model, params.param_model, sizeof(model));
+	for (i = 19; i > 0 && model[i] == ' '; i--)
+		model[i] = 0;
+
+	aprint_normal_dev(self, "vendor: %s, model: %s\n", vendor, model);
+
+	aprint_normal_dev(self,
+	   "page size: %u bytes, spare size: %u bytes, block size: %u bytes\n",
+	    params.param_pagesize, params.param_sparesize,
+	    params.param_blocksize * params.param_pagesize);
+
+	aprint_normal_dev(self,
+	    "LUN size: %u blocks, LUNs: %u, total storage size: %u MB\n",
+	    params.param_lunsize, params.param_numluns,
+	    params.param_blocksize * params.param_pagesize *
+	    params.param_lunsize * params.param_numluns / 1024 / 1024);
+
+	/* XXX TODO multiple LUNs */
+	if (__predict_false(params.param_numluns != 1))
+		panic("more than one LUNs are not supported yet!\n");
+
+	chip->nc_size = params.param_pagesize * params.param_blocksize *
+	    params.param_lunsize * params.param_numluns;
+
+	chip->nc_page_size = params.param_pagesize;
+	chip->nc_block_pages = params.param_blocksize;
+	chip->nc_block_size = params.param_blocksize * params.param_pagesize;
+	chip->nc_spare_size = params.param_sparesize;
+
+	/* the lower 4 bits contain the row address cycles */
+	chip->nc_addr_cycles_row = params.param_addr_cycles & 0x07;
+	/* the upper 4 bits contain the column address cycles */
+	chip->nc_addr_cycles_column = (params.param_addr_cycles & ~0x07) >> 4;
+
+#ifdef NAND_VERBOSE
+	aprint_normal_dev(self, "column cycles: %d, row cycles: %d\n",
+	    chip->nc_addr_cycles_column, chip->nc_addr_cycles_row);
+#endif
+
+	if (params.param_features & ONFI_FEATURE_16BIT)
+		chip->nc_flags |= NC_BUSWIDTH_16;
+
+	if (params.param_features & ONFI_FEATURE_EXTENDED_PARAM)
+		chip->nc_flags |= NC_EXTENDED_PARAM;
+}
+
+/* ARGSUSED */
+bool
+nand_shutdown(device_t self, int howto)
+{
+	return true;
+}
+
+static void
+nand_address_column(device_t self, size_t row, size_t column)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	uint8_t i;
+
+	DPRINTF(("addressing row: 0x%jx column: %zu\n",
+		(uintmax_t )row, column));
+
+	/* XXX TODO */
+	row >>= chip->nc_page_shift;
+//	DPRINTF(("row address is: 0x%jx\n", (uintmax_t )row));
+
+	/* Write the column (subpage) address */
+	if (chip->nc_flags & NC_BUSWIDTH_16)
+		column >>= 1;
+	for (i = 0; i < chip->nc_addr_cycles_column; i++, column >>= 8)
+		nand_address(self, column & 0xff);
+
+	/* Write the row (page) address */
+	for (i = 0; i < chip->nc_addr_cycles_row; i++, row >>= 8)
+		nand_address(self, row & 0xff);
+}
+
+static void
+nand_address_row(device_t self, size_t row)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	off_t i;
+
+//	DPRINTF(("addressing row: %zu\n", row));
+
+	/* XXX TODO */
+	row >>= chip->nc_page_shift;
+
+	/* Write the row (page) address */
+	for (i = 0; i < chip->nc_addr_cycles_row; i++, row >>= 8)
+		nand_address(self, row & 0xff);
+}
+
+static inline uint8_t
+nand_get_status(device_t self)
+{
+	uint8_t status;
+
+	nand_command(self, ONFI_READ_STATUS);
+	nand_busy(self);
+	nand_read_byte(self, &status);
+
+	return status;
+}
+
+static bool
+nand_check_wp(device_t self)
+{
+	if (nand_get_status(self) & 0x80)
+		return false;
+	else
+		return true;
+}
+
+static void
+nand_prepare_read(device_t self, flash_addr_t row, flash_addr_t column)
+{
+	nand_command(self, ONFI_READ);
+	nand_address_column(self, row, column);
+	nand_command(self, ONFI_READ_START);
+
+	nand_busy(self);
+}
+
+/* read a page with ecc correction */
+int
+nand_read_page(device_t self, size_t offset, uint8_t *data)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	size_t b, bs, e, cs;
+	uint8_t *ecc;
+	int result;
+
+	DPRINTF(("nand read page\n"));
+
+	nand_prepare_read(self, offset, 0);
+
+	bs = chip->nc_ecc->necc_block_size;
+	cs = chip->nc_ecc->necc_code_size;
+
+	/* decide if we access by 8 or 16 bits */
+	if (chip->nc_flags & NC_BUSWIDTH_16) {
+		for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+			nand_ecc_prepare(self, NAND_ECC_READ);
+			nand_read_buf_word(self, data + b, bs);
+			nand_ecc_compute(self, data + b,
+			    chip->nc_ecc_cache + e);
+		}
+	} else {
+		for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+			nand_ecc_prepare(self, NAND_ECC_READ);
+			nand_read_buf_byte(self, data + b, bs);
+			nand_ecc_compute(self, data + b,
+			    chip->nc_ecc_cache + e);
+		}
+	}
+
+//	nand_dump_data("page", data, chip->nc_page_size);
+
+	nand_read_oob(self, offset, chip->nc_oob_cache);
+	ecc = chip->nc_oob_cache + chip->nc_ecc->necc_offset;
+
+	/* useful for debugging new ecc drivers */
+#if 0
+	printf("dumping ecc %d\n--------------\n", chip->nc_ecc->necc_steps);
+	for (e = 0; e < chip->nc_ecc->necc_steps; e++) {
+		printf("0x");
+		for (b = 0; b < cs; b++) {
+			printf("%.2hhx", ecc[e+b]);
+		}
+		printf(" 0x");
+		for (b = 0; b < cs; b++) {
+			printf("%.2hhx", chip->nc_ecc_cache[e+b]);
+		}
+		printf("\n");
+	}
+	printf("--------------\n");
+#endif
+
+	for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+		result = nand_ecc_correct(self, data + b, ecc + e,
+		    chip->nc_ecc_cache + e);
+
+		switch (result) {
+		case NAND_ECC_OK:
+			break;
+		case NAND_ECC_CORRECTED:
+			aprint_error_dev(self,
+			    "data corrected with ECC at page offset 0x%jx "
+			    "block %zu\n", (uintmax_t)offset, b);
+			break;
+		case NAND_ECC_TWOBIT:
+			aprint_error_dev(self,
+			    "uncorrectable ECC error at page offset 0x%jx "
+			    "block %zu\n", (uintmax_t)offset, b);
+			return EIO;
+			break;
+		case NAND_ECC_INVALID:
+			aprint_error_dev(self,
+			    "invalid ECC in oob at page offset 0x%jx "
+			    "block %zu\n", (uintmax_t)offset, b);
+			return EIO;
+			break;
+		default:
+			panic("invalid ECC correction errno");
+		}
+	}
+
+	return 0;
+}
+
+static int
+nand_program_page(device_t self, size_t page, const uint8_t *data)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	size_t bs, cs, e, b;
+	uint8_t status;
+	uint8_t *ecc;
+
+	nand_command(self, ONFI_PAGE_PROGRAM);
+	nand_address_column(self, page, 0);
+
+	nand_busy(self);
+
+	bs = chip->nc_ecc->necc_block_size;
+	cs = chip->nc_ecc->necc_code_size;
+	ecc = chip->nc_oob_cache + chip->nc_ecc->necc_offset;
+
+	/* XXX code duplication */
+	/* decide if we access by 8 or 16 bits */
+	if (chip->nc_flags & NC_BUSWIDTH_16) {
+		for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+			nand_ecc_prepare(self, NAND_ECC_WRITE);
+			nand_write_buf_word(self, data + b, bs);
+			nand_ecc_compute(self, data + b, ecc + e);
+		}
+		/* write oob with ecc correction code */
+		nand_write_buf_word(self, chip->nc_oob_cache,
+		    chip->nc_spare_size);
+	} else {
+		for (b = 0, e = 0; b < chip->nc_page_size; b += bs, e += cs) {
+			nand_ecc_prepare(self, NAND_ECC_WRITE);
+			nand_write_buf_byte(self, data + b, bs);
+			nand_ecc_compute(self, data + b, ecc + e);
+		}
+		/* write oob with ecc correction code */
+		nand_write_buf_byte(self, chip->nc_oob_cache,
+		    chip->nc_spare_size);
+	}
+
+	nand_command(self, ONFI_PAGE_PROGRAM_START);
+
+	nand_busy(self);
+
+#if 0
+	printf("dumping ecc %d\n--------------\n", chip->nc_ecc->necc_steps);
+	for (e = 0; e < chip->nc_ecc->necc_steps; e++) {
+		printf("0x");
+		for (b = 0; b < cs; b++) {
+			printf("%.2hhx", ecc[e+b]);
+		}
+		printf("\n");
+	}
+	printf("--------------\n");
+#endif
+
+	status = nand_get_status(self);
+	KASSERT(status & ONFI_STATUS_RDY);
+	if (status & ONFI_STATUS_FAIL) {
+		aprint_error_dev(self, "page program failed!\n");
+		return EIO;
+	}
+
+	return 0;
+}
+
+int
+nand_read_oob(device_t self, size_t page, void *oob)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+
+	nand_prepare_read(self, page, chip->nc_page_size);
+
+	if (chip->nc_flags & NC_BUSWIDTH_16)
+		nand_read_buf_word(self, oob, chip->nc_spare_size);
+	else
+		nand_read_buf_byte(self, oob, chip->nc_spare_size);
+
+//	nand_dump_data("oob", oob, chip->nc_spare_size);
+
+	return 0;
+}
+
+static int
+nand_write_oob(device_t self, size_t offset, const void *oob)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	uint8_t status;
+
+	nand_command(self, ONFI_PAGE_PROGRAM);
+	nand_address_column(self, offset, chip->nc_page_size);
+	nand_command(self, ONFI_PAGE_PROGRAM_START);
+
+	nand_busy(self);
+
+	if (chip->nc_flags & NC_BUSWIDTH_16)
+		nand_write_buf_word(self, oob, chip->nc_spare_size);
+	else
+		nand_write_buf_byte(self, oob, chip->nc_spare_size);
+
+	status = nand_get_status(self);
+	KASSERT(status & ONFI_STATUS_RDY);
+	if (status & ONFI_STATUS_FAIL)
+		return EIO;
+	else
+		return 0;
+}
+
+void
+nand_markbad(device_t self, size_t offset)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t blockoffset, marker;
+#ifdef NAND_BBT
+	flash_addr_t block;
+
+	block = offset / chip->nc_block_size;
+
+	nand_bbt_block_markbad(self, block);
+#endif
+	blockoffset = offset & chip->nc_block_mask;
+	marker = chip->nc_badmarker_offs & ~0x01;
+
+	/* check if it is already marked bad */
+	if (nand_isbad(self, blockoffset))
+		return;
+
+	nand_read_oob(self, blockoffset, chip->nc_oob_cache);
+
+	chip->nc_oob_cache[chip->nc_badmarker_offs] = 0x00;
+	chip->nc_oob_cache[chip->nc_badmarker_offs + 1] = 0x00;
+
+	nand_write_oob(self, blockoffset, chip->nc_oob_cache);
+}
+
+bool
+nand_isfactorybad(device_t self, flash_addr_t offset)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t block, first_page, last_page, page;
+	int i;
+
+	/* Check for factory bad blocks first
+	 * Factory bad blocks are marked in the first or last
+	 * page of the blocks, see: ONFI 2.2, 3.2.2.
+	 */
+	block = offset / chip->nc_block_size;
+	first_page = block * chip->nc_block_size;
+	last_page = (block + 1) * chip->nc_block_size
+	    - chip->nc_page_size;
+
+	for (i = 0, page = first_page; i < 2; i++, page = last_page) {
+		/* address OOB */
+		nand_prepare_read(self, page, chip->nc_page_size);
+
+		if (chip->nc_flags & NC_BUSWIDTH_16) {
+			uint16_t word;
+			nand_read_word(self, &word);
+			if (word == 0x0000)
+				return true;
+		} else {
+			uint8_t byte;
+			nand_read_byte(self, &byte);
+			if (byte == 0x00)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+bool
+nand_iswornoutbad(device_t self, flash_addr_t offset)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t block;
+
+	/* we inspect the first page of the block */
+	block = offset & chip->nc_block_mask;
+
+	/* Linux/u-boot compatible badblock handling */
+	if (chip->nc_flags & NC_BUSWIDTH_16) {
+		uint16_t word, mark;
+
+		nand_prepare_read(self, block,
+		    chip->nc_page_size + (chip->nc_badmarker_offs & 0xfe));
+
+		nand_read_word(self, &word);
+		mark = htole16(word);
+		if (chip->nc_badmarker_offs & 0x01)
+			mark >>= 8;
+		if ((mark & 0xff) != 0xff)
+			return true;
+	} else {
+		uint8_t byte;
+
+		nand_prepare_read(self, block,
+		    chip->nc_page_size + chip->nc_badmarker_offs);
+
+		nand_read_byte(self, &byte);
+		if (byte != 0xff)
+			return true;
+	}
+
+	return false;
+}
+
+bool
+nand_isbad(device_t self, flash_addr_t offset)
+{
+#ifdef NAND_BBT
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t block;
+
+	block = offset / chip->nc_block_size;
+
+	return nand_bbt_block_isbad(self, block);
+#else
+	/* ONFI host requirement */
+	if (nand_isfactorybad(self, offset))
+		return true;
+
+	/* Look for Linux/U-Boot compatible bad marker */
+	if (nand_iswornoutbad(self, offset))
+		return true;
+
+	return false;
+#endif
+}
+
+int
+nand_erase_block(device_t self, size_t offset)
+{
+//	struct nand_softc *sc = device_private(self);
+//	struct nand_chip *chip = &sc->sc_chip;
+	uint8_t status;
+
+	/* xxx calculate first page of block for address? */
+
+	nand_command(self, ONFI_BLOCK_ERASE);
+	nand_address_row(self, offset);
+	nand_command(self, ONFI_BLOCK_ERASE_START);
+
+	nand_busy(self);
+
+	status = nand_get_status(self);
+	KASSERT(status & ONFI_STATUS_RDY);
+	if (status & ONFI_STATUS_FAIL) {
+		aprint_error_dev(self, "block erase failed!\n");
+		nand_markbad(self, offset);
+		return EIO;
+	} else {
+		return 0;
+	}
+}
+
+/* default functions for driver development */
+
+/* default ECC using hamming code of 256 byte chunks */
+int
+nand_default_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
+{
+	hamming_compute_256(data, code);
+
+	return 0;
+}
+
+int
+nand_default_ecc_correct(device_t self, uint8_t *data, const uint8_t *origcode,
+	const uint8_t *compcode)
+{
+	return hamming_correct_256(data, origcode, compcode);
+}
+
+void
+nand_default_select(device_t self, bool enable)
+{
+	/* do nothing */
+	return;
+}
+
+/* implementation of the block device API */
+
+/*
+ * handle (page) unaligned write to nand
+ */
+static int
+nand_flash_write_unaligned(device_t self, off_t offset, size_t len,
+    size_t *retlen, const uint8_t *buf)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t first, last, firstoff;
+	const uint8_t *bufp;
+	flash_addr_t addr;
+	size_t left, count;
+	int error, i;
+
+	/* to debug chfs */
+//	printf("unaligned write to nand\n");
+
+	first = offset & chip->nc_page_mask;
+	firstoff = offset & ~chip->nc_page_mask;
+	/* XXX check if this should be len - 1 */
+	last = (offset + len) & chip->nc_page_mask;
+	count = last - first + 1;
+
+	addr = first;
+	*retlen = 0;
+
+	if (count == 1) {
+		if (nand_isbad(self, addr)) {
+			aprint_error_dev(self,
+			    "nand_flash_write_unaligned: "
+			    "bad block encountered\n");
+			return EIO;
+		}
+
+		error = nand_read_page(self, addr, chip->nc_page_cache);
+		if (error)
+			return error;
+
+		memcpy(chip->nc_page_cache + firstoff, buf, len);
+
+		error = nand_program_page(self, addr, chip->nc_page_cache);
+		if (error)
+			return error;
+
+		*retlen = len;
+		return 0;
+	}
+
+	bufp = buf;
+	left = len;
+
+	for (i = 0; i < count && left != 0; i++) {
+		if (nand_isbad(self, addr)) {
+			aprint_error_dev(self,
+			    "nand_flash_write_unaligned: "
+			    "bad block encountered\n");
+			return EIO;
+		}
+
+		if (i == 0) {
+			error = nand_read_page(self,
+			    addr, chip->nc_page_cache);
+			if (error)
+				return error;
+
+			memcpy(chip->nc_page_cache + firstoff,
+			    bufp, chip->nc_page_size - firstoff);
+			
+			printf("program page: %s: %d\n", __FILE__, __LINE__);
+			error = nand_program_page(self,
+			    addr, chip->nc_page_cache);
+			if (error)
+				return error;
+
+			bufp += chip->nc_page_size - firstoff;
+			left -= chip->nc_page_size - firstoff;
+			*retlen += chip->nc_page_size - firstoff;
+
+		} else if (i == count - 1) {
+			error = nand_read_page(self,
+			    addr, chip->nc_page_cache);
+			if (error)
+				return error;
+
+			memcpy(chip->nc_page_cache, bufp, left);
+
+			error = nand_program_page(self,
+			    addr, chip->nc_page_cache);
+			if (error)
+				return error;
+
+			*retlen += left;
+			KASSERT(left < chip->nc_page_size);
+
+		} else {
+			/* XXX debug */
+			if (left > chip->nc_page_size) {
+				printf("left: %zu, i: %d, count: %zu\n",
+				    (size_t )left, i, count);
+			}
+			KASSERT(left > chip->nc_page_size);
+
+			error = nand_program_page(self, addr, bufp);
+			if (error)
+				return error;
+
+			bufp += chip->nc_page_size;
+			left -= chip->nc_page_size;
+			*retlen += chip->nc_page_size;
+		}
+
+		addr += chip->nc_page_size;
+	}
+
+	KASSERT(*retlen == len);
+
+	return 0;
+}
+
+int
+nand_flash_write(device_t self, off_t offset, size_t len, size_t *retlen,
+    const uint8_t *buf)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	const uint8_t *bufp;
+	size_t pages, page;
+	daddr_t addr;
+	int error = 0;
+
+	if ((offset + len) > chip->nc_size) {
+		DPRINTF(("nand_flash_write: write (off: 0x%jx, len: %ju),"
+			" is over device size (0x%jx)\n",
+			(uintmax_t)offset, (uintmax_t)len,
+			(uintmax_t)chip->nc_size));
+		return EINVAL;
+	}
+
+	if (len % chip->nc_page_size != 0 ||
+	    offset % chip->nc_page_size != 0) {
+		return nand_flash_write_unaligned(self,
+		    offset, len, retlen, buf);
+	}
+
+	pages = len / chip->nc_page_size;
+	KASSERT(pages != 0);
+	*retlen = 0;
+
+	addr = offset;
+	bufp = buf;
+
+	mutex_enter(&sc->sc_device_lock);
+	for (page = 0; page < pages; page++) {
+		/* do we need this check here? */
+		if (nand_isbad(self, addr)) {
+			aprint_error_dev(self,
+			    "nand_flash_write: bad block encountered\n");
+
+			error = EIO;
+			goto out;
+		}
+
+		error = nand_program_page(self, addr, bufp);
+		if (error)
+			goto out;
+
+		addr += chip->nc_page_size;
+		bufp += chip->nc_page_size;
+		*retlen += chip->nc_page_size;
+	}
+out:
+	mutex_exit(&sc->sc_device_lock);
+	DPRINTF(("page programming: retlen: %zu, len: %zu\n", *retlen, len));
+
+	return error;
+}
+
+/*
+ * handle (page) unaligned read from nand
+ */
+static int
+nand_flash_read_unaligned(device_t self, size_t offset,
+    size_t len, size_t *retlen, uint8_t *buf)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	daddr_t first, last, count, firstoff;
+	uint8_t *bufp;
+	daddr_t addr;
+	size_t left;
+	int error = 0, i;
+
+	/* to debug chfs */
+//	printf("unaligned read from nand\n");
+
+	first = offset & chip->nc_page_mask;
+	firstoff = offset & ~chip->nc_page_mask;
+	last = (offset + len) & chip->nc_page_mask;
+//	lastoff = chip->nc_page_size - (offset + len) & ~chip->nc_page_mask;
+	count = (last - first) / chip->nc_page_size + 1;
+
+	addr = first;
+	bufp = buf;
+	left = len;
+	*retlen = 0;
+
+	mutex_enter(&sc->sc_device_lock);
+	if (count == 1) {
+		error = nand_read_page(self, addr, chip->nc_page_cache);
+		if (error)
+			goto out;
+
+		memcpy(bufp, chip->nc_page_cache + firstoff, len);
+
+		*retlen = len;
+		goto out;
+	}
+
+	for (i = 0; i < count && left != 0; i++) {
+		error = nand_read_page(self, addr, chip->nc_page_cache);
+		if (error)
+			goto out;
+
+		if (i == 0) {
+			memcpy(bufp, chip->nc_page_cache + firstoff,
+			    chip->nc_page_size - firstoff);
+
+			bufp += chip->nc_page_size - firstoff;
+			left -= chip->nc_page_size - firstoff;
+			*retlen += chip->nc_page_size - firstoff;
+
+		} else if (i == count - 1) {
+			memcpy(bufp, chip->nc_page_cache, left);
+			*retlen += left;
+			KASSERT(left < chip->nc_page_size);
+
+		} else {
+			memcpy(bufp, chip->nc_page_cache, chip->nc_page_size);
+
+			bufp += chip->nc_page_size;
+			left -= chip->nc_page_size;
+			*retlen += chip->nc_page_size;
+		}
+
+		addr += chip->nc_page_size;
+	}
+
+	KASSERT(*retlen == len);
+
+out:
+	mutex_exit(&sc->sc_device_lock);
+
+	return error;
+}
+
+int
+nand_flash_read(device_t self, off_t offset, size_t len, size_t *retlen,
+    uint8_t *buf)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	uint8_t *bufp;
+	size_t addr;
+	size_t i, pages;
+	int error = 0;
+
+	*retlen = 0;
+
+	DPRINTF(("nand_flash_read: off: 0x%jx, len: %zu\n",
+		(uintmax_t)offset, len));
+
+	if (__predict_false((offset + len) > chip->nc_size)) {
+		DPRINTF(("nand_flash_read: read (off: 0x%jx, len: %zu),"
+			" is over device size (%ju)\n", (uintmax_t)offset,
+			len, (uintmax_t)chip->nc_size));
+		return EINVAL;
+	}
+
+	/* Handle unaligned access, shouldnt be needed when using the
+	 * block device, as strategy handles it, so only low level
+	 * accesses will use this path
+	 */
+//	if (len < chip->nc_page_size)
+//		panic("TODO page size is larger than read size");
+
+	if (len % chip->nc_page_size != 0 ||
+	    offset % chip->nc_page_size != 0) {
+		return nand_flash_read_unaligned(self,
+		    offset, len, retlen, buf);
+	}
+
+	bufp = buf;
+	addr = offset;
+	pages = len / chip->nc_page_size;
+
+	mutex_enter(&sc->sc_device_lock);
+	for (i = 0; i < pages; i++) {
+		/* do we need this check here? */
+		if (nand_isbad(self, addr)) {
+			aprint_error_dev(self, "bad block encountered\n");
+			error = EIO;
+			goto out;
+		}
+		error = nand_read_page(self, addr, bufp);
+		if (error)
+			goto out;
+
+		bufp += chip->nc_page_size;
+		addr += chip->nc_page_size;
+		*retlen += chip->nc_page_size;
+	}
+
+out:
+	mutex_exit(&sc->sc_device_lock);
+
+	return error;
+}
+
+int
+nand_flash_isbad(device_t self, uint64_t ofs)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	bool result;
+//	uint64_t block_num;
+
+	if (ofs > chip->nc_size) {
+		DPRINTF(("nand_flash_isbad: offset 0x%jx is larger than"
+			" device size (0x%jx)\n", (uintmax_t)ofs,
+			(uintmax_t)chip->nc_size));
+		return EINVAL;
+	}
+
+	if (ofs % chip->nc_block_size != 0) {
+		panic("offset (0x%jx) is not the multiple of block size (%ju)",
+		    (uintmax_t)ofs, (uintmax_t)chip->nc_block_size);
+	}
+//	block_num = ofs / fl->flash_if.erasesize;
+
+	mutex_enter(&sc->sc_device_lock);
+	result = nand_isbad(self, ofs);
+	mutex_exit(&sc->sc_device_lock);
+
+	if (result)
+		return 1;
+	else
+		return 0;
+}
+
+int
+nand_flash_markbad(device_t self, uint64_t ofs)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+
+//	uint64_t block_num;
+
+	if (ofs > chip->nc_size) {
+		DPRINTF(("nand_flash_markbad: offset 0x%jx is larger than"
+			" device size (0x%jx)\n", ofs,
+			(uintmax_t)chip->nc_size));
+		return EINVAL;
+	}
+
+	if (ofs % chip->nc_block_size != 0) {
+		panic("offset (%ju) is not the multiple of block size (%ju)",
+		    (uintmax_t)ofs, (uintmax_t)chip->nc_block_size);
+	}
+
+//	block_num = ofs / fl->flash_if->erasesize;
+
+	mutex_enter(&sc->sc_device_lock);
+	nand_markbad(self, ofs);
+	mutex_exit(&sc->sc_device_lock);
+
+	return 0;
+}
+
+int
+nand_flash_erase(device_t self,
+    struct flash_erase_instruction *ei)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t addr;
+	int error;
+//	off_t block_num;
+
+//	if (FLASH_CLOSED == sc->sc_flash.status)
+//		return FLASH_CLOSED;
+
+	if (ei->ei_addr < 0 || ei->ei_len < chip->nc_block_size)
+		return EINVAL;
+
+	if (ei->ei_addr + ei->ei_len > chip->nc_size) {
+		DPRINTF(("nand_flash_erase: erase address is over the end"
+			" of the device\n"));
+		return EINVAL;
+	}
+
+	if (ei->ei_addr % chip->nc_block_size != 0) {
+		aprint_error_dev(self,
+		    "nand_flash_erase: ei_addr (%ju) is not"
+		    "the multiple of block size (%ju)",
+		    (uintmax_t)ei->ei_addr,
+		    (uintmax_t)chip->nc_block_size);
+		return EINVAL;
+	}
+
+	if (ei->ei_len % chip->nc_block_size != 0) {
+		aprint_error_dev(self,
+		    "nand_flash_erase: ei_len (%ju) is not"
+		    "the multiple of block size (%ju)",
+		    (uintmax_t)ei->ei_addr,
+		    (uintmax_t)chip->nc_block_size);
+		return EINVAL;
+	}
+
+	mutex_enter(&sc->sc_device_lock);
+	addr = ei->ei_addr;
+	while (addr < ei->ei_addr + ei->ei_len) {
+		if (nand_isbad(self, addr)) {
+			mutex_exit(&sc->sc_device_lock);
+			aprint_error_dev(self, "bad block encountered\n");
+			ei->ei_state = FLASH_ERASE_FAILED;
+			return EIO;
+		}
+
+		error = nand_erase_block(self, addr);
+		if (error) {
+			mutex_exit(&sc->sc_device_lock);
+			ei->ei_state = FLASH_ERASE_FAILED;
+			return error;
+		}
+
+		addr += chip->nc_block_size;
+	}
+	mutex_exit(&sc->sc_device_lock);
+
+	ei->ei_state = FLASH_ERASE_DONE;
+	if (ei->ei_callback != NULL)
+		ei->ei_callback(ei);
+
+	return 0;
+}
+
+static int
+sysctl_nand_verify(SYSCTLFN_ARGS)
+{
+	int error, t;
+	struct sysctlnode node;
+
+	node = *rnode;
+	t = *(int *)rnode->sysctl_data;
+	node.sysctl_data = &t;
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error || newp == NULL)
+		return error;
+
+	if (node.sysctl_num == nand_cachesync_nodenum) {
+		if (t <= 0 || t > 60)
+			return EINVAL;
+	} else {
+		return EINVAL;
+	}
+
+	*(int *)rnode->sysctl_data = t;
+
+	return 0;
+}
+
+SYSCTL_SETUP(sysctl_nand, "sysctl nand subtree setup")
+{
+	int rc, nand_root_num;
+	const struct sysctlnode *node;
+
+	if ((rc = sysctl_createv(clog, 0, NULL, NULL,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
+	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) {
+		goto error;
+	}
+
+	if ((rc = sysctl_createv(clog, 0, NULL, &node,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "nand",
+	    SYSCTL_DESCR("NAND driver controls"),
+	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
+		goto error;
+	}
+
+	nand_root_num = node->sysctl_num;
+
+	if ((rc = sysctl_createv(clog, 0, NULL, &node,
+	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+	    CTLTYPE_INT, "cache_sync_timeout",
+	    SYSCTL_DESCR("NAND write cache sync timeout in seconds"),
+	    sysctl_nand_verify, 0, &nand_cachesync_timeout,
+	    0, CTL_HW, nand_root_num, CTL_CREATE,
+	    CTL_EOL)) != 0) {
+		goto error;
+	}
+
+	nand_cachesync_nodenum = node->sysctl_num;
+
+	return;
+
+error:
+	aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
+}
Index: sys/dev/nand/nand.h
===================================================================
RCS file: sys/dev/nand/nand.h
diff -N sys/dev/nand/nand.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand.h	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,465 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NAND_H_
+#define _NAND_H_
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#include <sys/bufq.h>
+#include <sys/buf.h>
+#include <sys/time.h>
+
+#include <dev/flash/flash.h>
+
+/* flash interface implementation */
+int nand_flash_isbad(device_t, uint64_t);
+int nand_flash_markbad(device_t, uint64_t);
+int nand_flash_write(device_t, off_t, size_t, size_t *, const u_char *);
+int nand_flash_read(device_t, off_t, size_t, size_t *, uint8_t *);
+int nand_flash_erase(device_t, struct flash_erase_instruction *);
+
+/* nand specific functions */
+int nand_erase_block(device_t, size_t);
+
+int nand_io_submit(device_t, struct buf *);
+void nand_sync_thread(void *);
+int nand_sync_thread_start(device_t);
+void nand_sync_thread_stop(device_t);
+
+bool nand_isfactorybad(device_t, flash_addr_t);
+bool nand_iswornoutbad(device_t, flash_addr_t);
+bool nand_isbad(device_t, flash_addr_t);
+void nand_markbad(device_t, size_t);
+
+int nand_read_page(device_t, size_t, uint8_t *);
+int nand_read_oob(device_t self, size_t page, void *oob);
+
+/*
+ * default functions for driver development
+ */
+void nand_default_select(device_t, bool);
+int nand_default_ecc_compute(device_t, const uint8_t *, uint8_t *);
+int nand_default_ecc_correct(device_t, uint8_t *, const uint8_t *,
+    const uint8_t *);
+
+static inline void nand_busy(device_t);
+static inline void nand_select(device_t, bool);
+static inline void nand_command(device_t, uint8_t);
+static inline void nand_address(device_t, uint32_t);
+static inline void nand_read_buf_byte(device_t, void *, size_t);
+static inline void nand_read_buf_word(device_t, void *, size_t);
+static inline void nand_read_byte(device_t, uint8_t *);
+static inline void nand_write_buf_byte(device_t, const void *, size_t);
+static inline void nand_write_buf_word(device_t, const void *, size_t);
+//static inline bool nand_block_isbad(device_t, off_t);
+//static inline void nand_block_markbad(device_t, off_t);
+//static inline bool nand_isbusy(device_t);
+
+//#define NAND_DEBUG 1
+#ifdef NAND_DEBUG
+#define DPRINTF(x)	if (nanddebug) printf x
+#define DPRINTFN(n,x)	if (nanddebug>(n)) printf x
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define NAND_VERBOSE
+
+/* same as in linux for compatibility */
+enum {
+	NAND_BAD_MARKER_OFFSET		= 0,
+	NAND_BAD_MARKER_OFFSET_SMALL	= 5
+};
+
+/* feature flags use in nc_flags */
+enum {
+	NC_BUSWIDTH_16		= (1<<0),
+	NC_SOURCE_SYNC		= (1<<2),
+	NC_INTERLEAVED_PE	= (1<<1),
+	NC_INTERLEAVED_R	= (1<<3),
+	NC_EXTENDED_PARAM	= (1<<4)
+};
+
+/* various quirks used in nc_quirks */
+enum {
+	NC_QUIRK_NO_READ_START = (1<<0)
+};
+
+enum {
+	NAND_ECC_READ,
+	NAND_ECC_WRITE
+};
+
+enum {
+	NAND_ECC_OK,
+	NAND_ECC_CORRECTED,
+	NAND_ECC_INVALID,
+	NAND_ECC_TWOBIT
+};
+
+enum {
+	NAND_ECC_TYPE_HW,
+	NAND_ECC_TYPE_SW
+};
+
+struct nand_bbt {
+	uint8_t *nbbt_bitmap;
+	size_t nbbt_size;
+};
+
+struct nand_ecc {
+	size_t necc_offset;		/* offset of ecc data in oob */
+	size_t necc_size;		/* size of ecc data in oob */
+	size_t necc_block_size;		/* block size used in ecc calc */
+	size_t necc_code_size;		/* reduntant bytes per block */
+	int necc_steps;			/* pagesize / code size */
+	int necc_type;			/* type of the ecc engine */
+};
+
+/**
+ * nand_chip: structure containing the required information
+ *	      about the NAND chip.
+ */
+struct nand_chip {
+	uint8_t	*nc_oob_cache;		/* buffer for oob cache */
+	uint8_t *nc_page_cache;		/* buffer for page cache */
+	uint8_t *nc_ecc_cache;
+	size_t nc_size;			/* storage size in bytes */
+	size_t nc_page_size;		/* page size in bytes */
+	size_t nc_block_pages;		/* block size in pages */
+	size_t nc_block_size;		/* block size in bytes */
+	size_t nc_spare_size;		/* spare (oob) size in bytes */
+	uint32_t nc_flags;		/* bitfield flags */
+	uint32_t nc_quirks;		/* bitfield quirks */
+	unsigned int nc_page_shift;	/* page shift for page alignment */
+	unsigned int nc_page_mask;	/* page mask for page alignment */
+	unsigned int nc_block_shift;	/* write shift */
+	unsigned int nc_block_mask;	/* write mask */
+	uint8_t nc_manf_id;		/* manufacturer id */
+	uint8_t nc_dev_id;		/* device id  */
+	uint8_t nc_addr_cycles_row;	/* row cycles for addressing */
+	uint8_t nc_addr_cycles_column;	/* column cycles for addressing */
+	uint8_t nc_badmarker_offs;	/* offset for marking bad blocks */
+	
+	struct nand_ecc *nc_ecc;
+};
+
+struct nand_write_cache {
+	struct bintime nwc_creation;
+	struct bintime nwc_last_write;
+	struct bufq_state *nwc_bufq;
+	uint8_t *nwc_data;
+	daddr_t nwc_block;
+	kmutex_t nwc_lock;
+	bool nwc_write_pending;
+};
+
+/* driver softc for nand */
+struct nand_softc {
+	device_t sc_dev;
+	device_t nand_dev;
+	struct nand_interface *nand_if;
+	void *nand_softc;
+	struct nand_chip sc_chip;
+	struct nand_bbt sc_bbt;
+	size_t sc_part_offset;
+	size_t sc_part_size;
+	kmutex_t sc_device_lock; /* serialize access to chip */
+
+	/* for the i/o thread */
+	struct lwp *sc_sync_thread;
+	struct nand_write_cache sc_cache;
+	kmutex_t sc_io_lock;
+	kmutex_t sc_waitq_lock;
+	kcondvar_t sc_io_cv;
+	bool sc_io_running;
+};
+
+/* structure holding the nand api */
+struct nand_interface
+{
+	void (*select) (device_t, bool);
+	void (*command) (device_t, uint8_t);
+	void (*address) (device_t, uint8_t);
+	void (*read_buf_byte) (device_t, void *, size_t);
+	void (*read_buf_word) (device_t, void *, size_t);
+	void (*read_byte) (device_t, uint8_t *);
+	void (*read_word) (device_t, uint16_t *);
+	void (*write_buf_byte) (device_t, const void *, size_t);
+	void (*write_buf_word) (device_t, const void *, size_t);
+	void (*write_byte) (device_t, uint8_t);
+	void (*write_word) (device_t, uint16_t);
+	void (*busy) (device_t);
+
+	/* functions specific to ecc computation */
+	int (*ecc_prepare)(device_t, int);
+	int (*ecc_compute)(device_t, const uint8_t *, uint8_t *);
+	int (*ecc_correct)(device_t, uint8_t *, const uint8_t *,
+	    const uint8_t *);
+
+	struct nand_ecc ecc;
+
+	/* flash partition information */
+	const struct flash_partition *part_info;
+	int part_num;
+};
+
+/* attach args */
+struct nand_attach_args {
+	struct nand_interface *naa_nand_if;
+};
+
+device_t nand_attach_mi(struct nand_interface *nand_if, device_t dev);
+
+static inline void
+nand_busy(device_t device)
+{
+	struct nand_softc *sc = device_private(device);
+	
+	KASSERT(sc->nand_if->select != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->select(sc->nand_dev, true);
+	
+	if (sc->nand_if->busy != NULL) {
+		sc->nand_if->busy(sc->nand_dev);
+	}
+
+	sc->nand_if->select(sc->nand_dev, false);
+}
+
+static inline void
+nand_select(device_t self, bool enable)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->select != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->select(sc->nand_dev, enable);
+}
+
+static inline void
+nand_address(device_t self, uint32_t address)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->address != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->address(sc->nand_dev, address);
+}
+
+static inline void
+nand_command(device_t self, uint8_t command)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->command != NULL);
+	KASSERT(sc->nand_dev != NULL);
+
+	sc->nand_if->command(sc->nand_dev, command);
+}
+
+static inline void
+nand_read_byte(device_t self, uint8_t *data)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->read_byte != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->read_byte(sc->nand_dev, data);
+}
+
+static inline void
+nand_write_byte(device_t self, uint8_t data)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->write_byte != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->write_byte(sc->nand_dev, data);
+}
+
+static inline void
+nand_read_word(device_t self, uint16_t *data)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->read_word != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->read_word(sc->nand_dev, data);
+}
+
+static inline void
+nand_write_word(device_t self, uint16_t data)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->write_word != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->write_word(sc->nand_dev, data);
+}
+
+static inline void
+nand_read_buf_byte(device_t self, void *buf, size_t size)
+{
+	struct nand_softc *sc = device_private(self);
+
+	KASSERT(sc->nand_if->read_buf_byte != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->read_buf_byte(sc->nand_dev, buf, size);	
+}
+
+static inline void
+nand_read_buf_word(device_t self, void *buf, size_t size)
+{
+	struct nand_softc *sc = device_private(self);
+
+	KASSERT(sc->nand_if->read_buf_word != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->read_buf_word(sc->nand_dev, buf, size);	
+}
+
+static inline void
+nand_write_buf_byte(device_t self, const void *buf, size_t size)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->write_buf_byte != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->write_buf_byte(sc->nand_dev, buf, size);	
+}
+
+static inline void
+nand_write_buf_word(device_t self, const void *buf, size_t size)
+{
+	struct nand_softc *sc = device_private(self);
+
+	KASSERT(sc->nand_if->write_buf_word != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	sc->nand_if->write_buf_word(sc->nand_dev, buf, size);	
+}
+
+static inline int
+nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldcode,
+    const uint8_t *newcode)
+{
+	struct nand_softc *sc = device_private(self);
+
+	KASSERT(sc->nand_if->ecc_correct != NULL);
+	KASSERT(sc->nand_dev != NULL);
+
+	return sc->nand_if->ecc_correct(sc->nand_dev, data, oldcode, newcode);
+}
+
+static inline void
+nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
+{
+	struct nand_softc *sc = device_private(self);
+
+	KASSERT(sc->nand_if->ecc_compute != NULL);
+	KASSERT(sc->nand_dev != NULL);
+
+	sc->nand_if->ecc_compute(sc->nand_dev, data, code);	
+}
+
+static inline void
+nand_ecc_prepare(device_t self, int mode)
+{
+	struct nand_softc *sc = device_private(self);
+
+	KASSERT(sc->nand_dev != NULL);
+	
+	if (sc->nand_if->ecc_prepare != NULL)
+		sc->nand_if->ecc_prepare(sc->nand_dev, mode);
+}
+
+#if 0
+static inline bool
+nand_block_isbad(device_t self, off_t block)
+{
+	struct nand_softc *sc = device_private(self);
+	
+	KASSERT(sc->nand_if->block_isbad != NULL);
+	KASSERT(sc->nand_dev != NULL);
+	
+	return sc->nand_if->block_isbad(sc->nand_dev, block);
+}
+#endif
+
+/* Manufacturer IDs defined by JEDEC */
+enum {
+	NAND_MFR_UNKNOWN	= 0x00,
+	NAND_MFR_AMD		= 0x01,
+	NAND_MFR_FUJITSU	= 0x04,
+	NAND_MFR_RENESAS	= 0x07,
+	NAND_MFR_STMICRO	= 0x20,
+	NAND_MFR_MICRON		= 0x2c,
+	NAND_MFR_NATIONAL	= 0x8f,
+	NAND_MFR_TOSHIBA	= 0x98,
+	NAND_MFR_HYNIX		= 0xad,
+	NAND_MFR_SAMSUNG	= 0xec
+};
+
+struct nand_manufacturer {
+	int id;
+	const char *name;
+};
+
+extern const struct nand_manufacturer nand_mfrs[];
+
+static inline void
+nand_dump_data(const char *name, void *data, size_t len)
+{
+	printf("dumping %s\n--------------\n", name);
+	uint8_t *dump = data;
+	for (int i = 0; i < len; i++) {
+		printf("0x%.2hhx ", *dump);
+		dump++;
+	}
+	printf("\n--------------\n");
+}
+
+#endif	/* _NAND_H_ */
Index: sys/dev/nand/nand_bbt.c
===================================================================
RCS file: sys/dev/nand/nand_bbt.c
diff -N sys/dev/nand/nand_bbt.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand_bbt.c	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,250 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Support for Bad Block Tables (BBTs) */
+
+#include <sys/param.h>
+#include <sys/kmem.h>
+
+#include "nand.h"
+#include "nand_bbt.h"
+
+void
+nand_bbt_init(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct nand_bbt *bbt = &sc->sc_bbt;
+
+	bbt->nbbt_size = chip->nc_size / chip->nc_block_size / 4;
+	bbt->nbbt_bitmap = kmem_alloc(bbt->nbbt_size, KM_SLEEP);
+
+	memset(bbt->nbbt_bitmap, 0xff, bbt->nbbt_size);
+}
+
+void
+nand_bbt_detach(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_bbt *bbt = &sc->sc_bbt;
+
+	kmem_free(bbt->nbbt_bitmap, bbt->nbbt_size);
+}
+
+void
+nand_bbt_scan(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+//	struct nand_bbt *bbt = sc->sc_bbt;
+	flash_addr_t i, blocks, addr;
+
+	blocks = chip->nc_size / chip->nc_block_size;
+
+	aprint_normal_dev(self, "scanning for bad blocks\n");
+
+	addr = 0;
+	for (i = 0; i < blocks; i++) {
+		if (nand_isfactorybad(self, addr)) {
+			nand_bbt_block_markfactorybad(self, i);
+		} else if (nand_iswornoutbad(self, addr)) {
+			nand_bbt_block_markbad(self, i);
+		}
+
+		addr += chip->nc_block_size;
+	}
+}
+
+bool
+nand_bbt_update(device_t self)
+{
+	return true;
+}
+
+static bool
+nand_bbt_page_has_bbt(device_t self, flash_addr_t addr) {
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	uint8_t *oob = chip->nc_oob_cache;
+	
+	nand_read_oob(self, addr, oob);
+	
+	if (oob[NAND_BBT_OFFSET] == 'B' &&
+	    oob[NAND_BBT_OFFSET + 1] == 'b' &&
+	    oob[NAND_BBT_OFFSET + 2] == 't') {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+static bool
+nand_bbt_get_bbt_from_page(device_t self, flash_addr_t addr)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct nand_bbt *bbt = &sc->sc_bbt;
+	uint8_t *bbtp, *buf = chip->nc_page_cache;
+	size_t left, bbt_pages, i;
+
+	bbt_pages = bbt->nbbt_size / chip->nc_page_size;
+	if (bbt->nbbt_size % chip->nc_page_size)
+		bbt_pages++;
+	
+	if (nand_isbad(self, addr)) {
+		return false;
+	}
+		
+	if (nand_bbt_page_has_bbt(self, addr)) {
+		bbtp = bbt->nbbt_bitmap;
+		left = bbt->nbbt_size;
+		
+		for (i = 0; i < bbt_pages; i++) {
+			nand_read_page(self, addr, buf);
+				
+			if (i == bbt_pages - 1) {
+				KASSERT(left <= chip->nc_page_size);
+				memcpy(bbtp, buf, left);
+			} else {
+				memcpy(bbtp, buf, chip->nc_page_size);
+			}
+				
+			bbtp += chip->nc_page_size;
+			left -= chip->nc_page_size;
+			addr += chip->nc_page_size;
+		}
+		
+		return true;
+	} else {
+		return false;
+	}
+}
+
+bool
+nand_bbt_load(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t blockaddr;
+	int n;
+
+	blockaddr = chip->nc_size - chip->nc_block_size;
+	/* XXX currently we check the last 4 blocks */
+	for (n = 0; n < 4; n++) {
+		if (nand_bbt_get_bbt_from_page(self, blockaddr)) {
+			break;
+		} else {
+			blockaddr -= chip->nc_block_size;
+		}
+	}
+
+	return true;
+}
+
+void
+nand_bbt_block_markbad(device_t self, flash_addr_t block)
+{
+	if (nand_bbt_block_isbad(self, block)) {
+		aprint_error_dev(self,
+		    "trying to mark block bad already marked in bbt\n");
+	}
+	/* XXX check if this is the correct marker */
+	nand_bbt_block_mark(self, block, NAND_BBT_MARKER_WORNOUT_BAD);
+}
+
+void
+nand_bbt_block_markfactorybad(device_t self, flash_addr_t block)
+{
+	if (nand_bbt_block_isbad(self, block)) {
+		aprint_error_dev(self,
+		    "trying to mark block factory bad already"
+		    " marked in bbt\n");
+	}
+	nand_bbt_block_mark(self, block, NAND_BBT_MARKER_FACTORY_BAD);
+}
+
+void
+nand_bbt_block_mark(device_t self, flash_addr_t block, uint8_t marker)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct nand_bbt *bbt = &sc->sc_bbt;
+//	flash_addr_t block;
+	uint8_t clean;
+
+//	block = offset / chip->nc_block_size;
+	KASSERT(block < chip->nc_size / chip->nc_block_size);
+
+	clean = (~0x03 << ((block % 4) * 2));
+	marker = (marker << ((block % 4) * 2));
+
+	/* set byte containing the 2 bit marker for this block */
+	bbt->nbbt_bitmap[block / 4] &= clean;
+	bbt->nbbt_bitmap[block / 4] |= marker;
+}
+
+bool
+nand_bbt_block_isbad(device_t self, flash_addr_t block)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct nand_bbt *bbt = &sc->sc_bbt;
+//	flash_addr_t block;
+	uint8_t byte, marker;
+	bool result;
+
+//	block = offset / chip->nc_block_size;
+	printf("bbt isbad for block %jd\n", (intmax_t )block);
+	KASSERT(block < chip->nc_size / chip->nc_block_size);
+
+	/* get byte containing the 2 bit marker for this block */
+	byte = bbt->nbbt_bitmap[block / 4];
+
+	/* extract the 2 bit marker from the byte */
+	marker = (byte >> ((block % 4) * 2)) & 0x03;
+
+	switch (marker) {
+	case NAND_BBT_MARKER_FACTORY_BAD:
+	case NAND_BBT_MARKER_WORNOUT_BAD:
+	case NAND_BBT_MARKER_RESERVED:
+		result = true;
+		break;
+	case NAND_BBT_MARKER_GOOD:
+		result = false;
+		break;
+	default:
+		panic("error in marker extraction");
+	}
+
+	return result;
+}
Index: sys/dev/nand/nand_bbt.h
===================================================================
RCS file: sys/dev/nand/nand_bbt.h
diff -N sys/dev/nand/nand_bbt.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand_bbt.h	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,58 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NAND_BBT_H_
+#define _NAND_BBT_H_
+
+enum {
+	NAND_BBT_MARKER_FACTORY_BAD = 0x00,	/* 00 */
+	NAND_BBT_MARKER_WORNOUT_BAD = 0x01,	/* 01 */
+	NAND_BBT_MARKER_RESERVED = 0x02,	/* 10 */
+	NAND_BBT_MARKER_GOOD = 0x03		/* 11 */
+};
+
+enum {
+	NAND_BBT_OFFSET = 0x0e
+};
+
+void nand_bbt_init(device_t);
+void nand_bbt_detach(device_t);
+void nand_bbt_scan(device_t);
+bool nand_bbt_update(device_t);
+bool nand_bbt_load(device_t);
+void nand_bbt_block_mark(device_t, flash_addr_t, uint8_t);
+void nand_bbt_block_markbad(device_t, flash_addr_t);
+void nand_bbt_block_markfactorybad(device_t, flash_addr_t);
+bool nand_bbt_block_isbad(device_t, flash_addr_t);
+
+#endif
Index: sys/dev/nand/nand_crc.c
===================================================================
RCS file: sys/dev/nand/nand_crc.c
diff -N sys/dev/nand/nand_crc.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand_crc.c	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,63 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Implements CRC-16 as required by the ONFI 2.3 specification */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include "nand_crc.h"
+
+uint16_t
+nand_crc16(uint8_t *data, size_t len)
+{
+	const uint16_t init = 0x4f4e;
+	const uint16_t polynom = 0x8005;
+	const uint16_t highbit = 0x0001 << 15;
+	uint16_t crc = init;
+	size_t i;
+	int j;
+
+	for (i = 0; i < len; i++) {
+		crc ^= data[i] << 8;
+		
+		for (j = 0; j < 8; j++) {
+			if ((crc & highbit) != 0x00)
+				crc = (crc << 1) ^ polynom;
+			else
+				crc <<= 1;
+		}
+	}
+
+	return crc;
+}
Index: sys/dev/nand/nand_crc.h
===================================================================
RCS file: sys/dev/nand/nand_crc.h
diff -N sys/dev/nand/nand_crc.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand_crc.h	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,42 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NAND_CRC_H_
+#define _NAND_CRC_H_
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+uint16_t nand_crc16(uint8_t *, size_t);
+
+#endif
Index: sys/dev/nand/nand_io.c
===================================================================
RCS file: sys/dev/nand/nand_io.c
diff -N sys/dev/nand/nand_io.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nand_io.c	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,373 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Inspired by the similar code in the NetBSD SPI driver, but I
+ * decided to do a rewrite from scratch to be suitable for NAND.
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/bufq.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/kthread.h>
+#include <sys/mutex.h>
+
+#include <dev/flash/flash.h>
+#include "nand.h"
+
+extern int nanddebug;
+extern int nand_cachesync_timeout;
+
+void nand_io_read(device_t, struct buf *);
+void nand_io_write(device_t, struct buf *);
+void nand_io_done(device_t, int error, struct buf *);
+int nand_io_cache_write(device_t, daddr_t, struct buf *);
+void nand_io_cache_sync(device_t);
+
+static int
+nand_timestamp_diff(struct bintime *bt, struct bintime *b2)
+{
+	struct bintime b1 = *bt;
+	struct timeval tv;
+
+	bintime_sub(&b1, b2);
+	bintime2timeval(&b1, &tv);
+
+	return tvtohz(&tv);
+}
+
+static daddr_t
+nand_io_getblock(device_t self, struct buf *bp)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	flash_addr_t block, last;
+
+	/* get block number of first byte */
+	block = bp->b_rawblkno * DEV_BSIZE / chip->nc_block_size;
+
+	/* block of the last bite */
+	last = (bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1)
+	    / chip->nc_block_size;
+
+	/* spans trough multiple blocks, needs special handling */
+	if (last != block) {
+		printf("0x%jx -> 0x%jx\n",
+		    bp->b_rawblkno * DEV_BSIZE,
+		    bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1);
+		panic("TODO: multiple block write. last: %jd, current: %jd",
+		    (intmax_t )last, (intmax_t )block);
+	}
+
+	return block;
+}
+
+int
+nand_sync_thread_start(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct nand_write_cache *wc = &sc->sc_cache;
+	int error;
+
+	DPRINTF(("starting nand io thread\n"));
+
+	sc->sc_cache.nwc_data = kmem_alloc(chip->nc_block_size, KM_SLEEP);
+
+	mutex_init(&sc->sc_io_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&wc->nwc_lock, MUTEX_DEFAULT, IPL_NONE);
+	cv_init(&sc->sc_io_cv, "nandcv");
+	
+	error = bufq_alloc(&wc->nwc_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
+	if (error)
+		goto err_bufq;
+	
+	sc->sc_io_running = true;
+	wc->nwc_write_pending = false;
+
+	/* arrange to allocate the kthread */
+	error = kthread_create(PRI_NONE, KTHREAD_JOINABLE, NULL,
+	    nand_sync_thread, self, &sc->sc_sync_thread, "nandio");
+
+	if (!error)
+		return 0;
+	
+	bufq_free(wc->nwc_bufq);
+err_bufq:
+	cv_destroy(&sc->sc_io_cv);
+	
+	mutex_destroy(&sc->sc_io_lock);
+	mutex_destroy(&wc->nwc_lock);
+	
+	kmem_free(sc->sc_cache.nwc_data, chip->nc_block_size);
+
+	return error;
+}
+
+void
+nand_sync_thread_stop(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_chip *chip = &sc->sc_chip;
+	struct nand_write_cache *wc = &sc->sc_cache;
+	
+	DPRINTF(("stopping nand io thread\n"));
+	
+	kmem_free(wc->nwc_data, chip->nc_block_size);
+	
+	sc->sc_io_running = false;
+	kthread_join(sc->sc_sync_thread);
+
+	bufq_free(wc->nwc_bufq);
+
+	mutex_destroy(&sc->sc_io_lock);
+	mutex_destroy(&sc->sc_waitq_lock);
+	mutex_destroy(&wc->nwc_lock);
+
+	cv_destroy(&sc->sc_io_cv);
+}
+
+int
+nand_io_submit(device_t self, struct buf *bp)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_write_cache *wc = &sc->sc_cache;
+
+	DPRINTF(("submitting job to nand io thread: %p\n", bp));
+
+	if (BUF_ISREAD(bp)) {
+		DPRINTF(("we have a read job\n"));
+		
+		mutex_enter(&wc->nwc_lock);
+		if (wc->nwc_write_pending)
+			nand_io_cache_sync(self);
+		mutex_exit(&wc->nwc_lock);
+			
+		nand_io_read(self, bp);
+	} else {
+		DPRINTF(("we have a write job\n"));
+
+		nand_io_write(self, bp);
+	}
+	
+	return 0;
+}
+
+int
+nand_io_cache_write(device_t self, daddr_t block, struct buf *bp)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_write_cache *wc = &sc->sc_cache;
+	struct nand_chip *chip = &sc->sc_chip;
+	size_t retlen;
+	daddr_t base, offset;
+	int error;
+
+	KASSERT(chip->nc_block_size != 0);
+	
+	base = block * chip->nc_block_size;
+	offset = bp->b_rawblkno * DEV_BSIZE - base;
+	
+	DPRINTF(("io cache write, offset: %jd\n", (intmax_t )offset));
+	
+	if (!wc->nwc_write_pending) {
+		wc->nwc_block = block;
+		/*
+		 * fill the cache with data from flash,
+		 * so we dont have to bother with gaps later
+		 */
+		DPRINTF(("filling buffer from offset %ju\n", (uintmax_t)base));
+		error = nand_flash_read(self,
+		    base, chip->nc_block_size,
+		    &retlen, wc->nwc_data);
+		DPRINTF(("cache filled\n"));
+
+		if (error)
+			return error;
+
+		wc->nwc_write_pending = true;
+		/* save creation time for aging */
+		binuptime(&sc->sc_cache.nwc_creation);
+	}
+	/* copy data to cache */
+	memcpy(wc->nwc_data + offset, bp->b_data, bp->b_resid);
+	bufq_put(wc->nwc_bufq, bp);
+
+	/* update timestamp */
+	binuptime(&wc->nwc_last_write);
+
+	return 0;
+}
+
+/* must be called with nwc_lock hold */
+void
+nand_io_cache_sync(device_t self)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_write_cache *wc = &sc->sc_cache;
+	struct nand_chip *chip = &sc->sc_chip;
+	struct flash_erase_instruction ei;
+	struct buf *bp;
+	size_t retlen;
+	daddr_t base;
+	int error;
+
+	if (!wc->nwc_write_pending) {
+		DPRINTF(("trying to sync with an invalid buffer\n"));
+		return;
+	}
+
+	base = wc->nwc_block * chip->nc_block_size;
+
+	DPRINTF(("eraseing block at 0x%jx\n", (uintmax_t )base));
+	ei.ei_addr = base;
+	ei.ei_len = chip->nc_block_size;
+	ei.ei_callback = NULL;
+	error = nand_flash_erase(self, &ei);
+
+	if (error) {
+		aprint_error_dev(self, "cannot erase nand flash!\n");
+		goto out;
+	}
+
+	DPRINTF(("writing %zu bytes to 0x%jx\n",
+		chip->nc_block_size, (uintmax_t )base));
+	
+	error = nand_flash_write(self,
+	    base, chip->nc_block_size, &retlen, wc->nwc_data);
+
+	if (error || retlen != chip->nc_block_size) {
+		aprint_error_dev(self, "can't sync write cache: %d\n", error);
+		goto out;
+	}
+
+out:
+	while ((bp = bufq_get(wc->nwc_bufq)) != NULL)
+		nand_io_done(self, error, bp);
+	
+	wc->nwc_block = -1;
+	wc->nwc_write_pending = false;
+}
+
+void
+nand_sync_thread(void * arg)
+{
+	device_t self = arg;
+	struct nand_softc *sc = device_private(self);
+	struct nand_write_cache *wc = &sc->sc_cache;
+	struct bintime now;
+
+	/* sync thread waking in every seconds */
+	while (sc->sc_io_running) {
+		mutex_enter(&sc->sc_io_lock);
+		cv_timedwait_sig(&sc->sc_io_cv, &sc->sc_io_lock, hz / 4);
+		mutex_exit(&sc->sc_io_lock);
+
+		mutex_enter(&wc->nwc_lock);
+	
+		if (!wc->nwc_write_pending) {
+			mutex_exit(&wc->nwc_lock);
+			continue;
+		}
+		
+		/* see if the cache is older than 3 seconds (safety limit),
+		 * or if we havent touched the cache since more than 1 ms
+		 */
+		binuptime(&now);
+		if (nand_timestamp_diff(&now, &wc->nwc_last_write)
+		    > hz / 5 ||
+		    nand_timestamp_diff(&now, &wc->nwc_creation)
+		    > 3 * hz) {
+			printf("syncing write cache after timeout\n");
+			nand_io_cache_sync(self);
+		}
+		mutex_exit(&wc->nwc_lock);
+	}
+
+	kthread_exit(0);
+}
+	
+void
+nand_io_read(device_t self, struct buf *bp)
+{
+	size_t retlen;
+	daddr_t offset;
+	int error;
+
+	DPRINTF(("nand io read\n"));
+
+	offset = bp->b_rawblkno * DEV_BSIZE;
+	
+	error = nand_flash_read(self, offset, bp->b_resid,
+	    &retlen, bp->b_data);
+	
+	nand_io_done(self, error, bp);
+}
+
+void
+nand_io_write(device_t self, struct buf *bp)
+{
+	struct nand_softc *sc = device_private(self);
+	struct nand_write_cache *wc = &sc->sc_cache;
+	daddr_t block;
+
+	DPRINTF(("nand io write\n"));
+
+	block = nand_io_getblock(self, bp);
+	DPRINTF(("write to block %jd\n", (intmax_t )block));
+	
+	mutex_enter(&wc->nwc_lock);
+
+	if (wc->nwc_write_pending && wc->nwc_block != block) {
+		DPRINTF(("writing to new block, syncing caches\n"));
+		nand_io_cache_sync(self);
+	}
+	
+	nand_io_cache_write(self, block, bp);
+	
+	mutex_exit(&wc->nwc_lock);
+}
+
+void
+nand_io_done(device_t self, int error, struct buf *bp)
+{
+	DPRINTF(("io done: %p\n", bp));
+	
+	if (error == 0)
+		bp->b_resid = 0;
+	
+	bp->b_error = error;
+	
+	biodone(bp);
+}
Index: sys/dev/nand/nandemulator.c
===================================================================
RCS file: sys/dev/nand/nandemulator.c
diff -N sys/dev/nand/nandemulator.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nandemulator.c	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,710 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/kernel.h>
+
+#include "nandemulator.h"
+
+#include <dev/nand/nand.h>
+#include <dev/nand/onfi.h>
+#include <dev/nand/nand_crc.h>
+
+extern struct cfdriver nandemulator_cd;
+void nandemulatorattach(int n);
+
+static int nandemulator_match(device_t, cfdata_t, void *);
+static void nandemulator_attach(device_t, device_t, void *);
+static int nandemulator_detach(device_t, int);
+
+static void nandemulator_device_reset(device_t);
+static void nandemulator_command(device_t, uint8_t);
+static void nandemulator_address(device_t, uint8_t);
+static void nandemulator_busy(device_t);
+static void nandemulator_read_byte(device_t, uint8_t *);
+static void nandemulator_write_byte(device_t, uint8_t);
+static void nandemulator_read_word(device_t, uint16_t *);
+static void nandemulator_write_word(device_t, uint16_t);
+static void nandemulator_read_buf_byte(device_t, void *, size_t);
+static void nandemulator_read_buf_word(device_t, void *, size_t);
+static void nandemulator_write_buf_byte(device_t, const void *, size_t);
+static void nandemulator_write_buf_word(device_t, const void *, size_t);
+
+static size_t nandemulator_address_to_page(device_t);
+static size_t nandemulator_page_to_backend_offset(device_t, size_t);
+static size_t nandemulator_column_address_to_subpage(device_t);
+/*
+#define NANDEMULATOR_DEBUG 1
+
+#ifdef NANDEMULATOR_DEBUG
+#warning debug enabled
+#define DPRINTF(x)	if (nandemulatordebug) printf x
+#define DPRINTFN(n,x)	if (nandemulatordebug>(n)) printf x
+#else
+#error no debug
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#ifdef NANDEMULATOR_DEBUG
+int	nandemulatordebug = NANDEMULATOR_DEBUG;
+#endif
+*/
+
+extern int nanddebug;
+
+enum {
+	NANDEMULATOR_8BIT,
+	NANDEMULATOR_16BIT
+};
+
+struct nandemulator_softc {
+	device_t		sc_dev;
+	device_t		sc_nanddev;
+
+	int			sc_buswidth;
+
+	struct nand_interface	sc_nand_if;
+
+	uint8_t			sc_command;
+	size_t			sc_io_len;
+	uint8_t			*sc_io_pointer;
+	uint64_t		sc_address;
+
+	uint8_t			*sc_backend;
+	size_t			sc_backend_size;
+	size_t			sc_device_size;
+	bool			sc_register_writable;
+
+	uint8_t			sc_status_register;
+	uint8_t			sc_ids[2];
+	uint8_t			sc_onfi[4];
+
+	size_t			sc_page_size;
+	size_t			sc_block_size;
+	size_t			sc_spare_size;
+	size_t			sc_lun_size;
+	uint8_t			sc_row_cycles;
+	uint8_t			sc_column_cycles;
+	uint64_t		sc_row_mask;
+
+	int			sc_address_counter;
+
+	struct onfi_parameter_page	*sc_parameter_page;
+};
+
+CFATTACH_DECL_NEW(nandemulator, sizeof(struct nandemulator_softc),
+    nandemulator_match, nandemulator_attach, nandemulator_detach, NULL);
+
+void
+nandemulatorattach(int n)
+{
+	int i, err;
+	cfdata_t cf;
+
+	aprint_debug("nandemulator: requested %d units\n", n);
+
+	err = config_cfattach_attach(nandemulator_cd.cd_name,
+	    &nandemulator_ca);
+	if (err) {
+		aprint_error("%s: couldn't register cfattach: %d\n",
+		    nandemulator_cd.cd_name, err);
+		config_cfdriver_detach(&nandemulator_cd);
+		return;
+	}
+	for (i = 0; i < n; i++) {
+		cf = kmem_alloc(sizeof(struct cfdata), KM_NOSLEEP);
+		if (cf == NULL) {
+			aprint_error("%s: couldn't allocate cfdata\n",
+			    nandemulator_cd.cd_name);
+			continue;
+		}
+		cf->cf_name = nandemulator_cd.cd_name;
+		cf->cf_atname = nandemulator_cd.cd_name;
+		cf->cf_unit = i;
+		cf->cf_fstate = FSTATE_STAR;
+
+		(void)config_attach_pseudo(cf);
+	}
+}
+
+/* ARGSUSED */
+static int
+nandemulator_match(device_t parent, cfdata_t match, void *aux)
+{
+	/* pseudo device, always attaches */
+	return 1;
+}
+
+static void
+nandemulator_attach(device_t parent, device_t self, void *aux)
+{
+	struct nandemulator_softc *sc = device_private(self);
+	int i;
+
+	aprint_normal_dev(self, "NAND emulator\n");
+
+	sc->sc_dev = self;
+
+	sc->sc_nand_if.select = &nand_default_select;
+	sc->sc_nand_if.command = &nandemulator_command;
+	sc->sc_nand_if.address = &nandemulator_address;
+	sc->sc_nand_if.read_buf_byte = &nandemulator_read_buf_byte;
+	sc->sc_nand_if.read_buf_word = &nandemulator_read_buf_word;
+	sc->sc_nand_if.read_byte = &nandemulator_read_byte;
+	sc->sc_nand_if.read_word = &nandemulator_read_word;
+	sc->sc_nand_if.write_buf_byte = &nandemulator_write_buf_byte;
+	sc->sc_nand_if.write_buf_word = &nandemulator_write_buf_word;
+	sc->sc_nand_if.write_byte = &nandemulator_write_byte;
+	sc->sc_nand_if.write_word = &nandemulator_write_word;
+	sc->sc_nand_if.busy = &nandemulator_busy;
+
+	sc->sc_nand_if.ecc_compute = &nand_default_ecc_compute;
+	sc->sc_nand_if.ecc_correct = &nand_default_ecc_correct;
+	sc->sc_nand_if.ecc_prepare = NULL;
+	sc->sc_nand_if.ecc.necc_code_size = 3;
+	sc->sc_nand_if.ecc.necc_block_size = 256;
+	sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_SW;
+
+	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL))
+		aprint_error_dev(sc->sc_dev,
+		    "couldn't establish power handler\n");
+
+	sc->sc_buswidth = NANDEMULATOR_16BIT;	/* 16bit for now */
+
+	/* hardcode these now, make it configurable later */
+	sc->sc_device_size = 32 * 1024 * 1024; /* 32MB */
+	sc->sc_page_size = 2048;
+	sc->sc_block_size = 64;
+	sc->sc_lun_size =
+	    sc->sc_device_size / (sc->sc_page_size * sc->sc_block_size);
+	KASSERT(sc->sc_device_size %
+	    (sc->sc_page_size * sc->sc_block_size) == 0);
+	sc->sc_spare_size = 64;
+
+	sc->sc_column_cycles = 2;
+	sc->sc_row_cycles = 3;
+
+	/* init the emulator data structures */
+	sc->sc_backend_size =
+	    sc->sc_device_size +
+	    sc->sc_device_size / sc->sc_page_size * sc->sc_spare_size;
+
+	sc->sc_backend = kmem_alloc(sc->sc_backend_size, KM_SLEEP);
+	memset(sc->sc_backend, 0xff, sc->sc_backend_size);
+
+	sc->sc_parameter_page =
+	    kmem_zalloc(sizeof(struct onfi_parameter_page) * 4, KM_SLEEP);
+
+	struct onfi_parameter_page *opp;
+	uint8_t sig[4] = { 'O', 'N', 'F', 'I' };
+
+	for (i = 0; i < 4; i++) {
+		opp = &sc->sc_parameter_page[i];
+
+		opp->param_signature = (uint32_t )sig;
+		opp->param_pagesize = sc->sc_page_size;
+		opp->param_blocksize = sc->sc_block_size;
+		opp->param_sparesize = sc->sc_spare_size;
+		opp->param_lunsize = sc->sc_lun_size;
+		opp->param_numluns = 1;
+
+		opp->param_manufacturer_id = 0x00;
+		memcpy(opp->param_manufacturer,
+		    "ACME CORP", strlen("ACME CORP"));
+		memcpy(opp->param_model,
+		    "ACME NAND V1", strlen("ACME NAND V1"));
+
+		opp->param_features = ONFI_FEATURE_16BIT;
+
+		/* the lower 4 bits contain the row address cycles
+		 * the upper 4 bits contain the column address cycles
+		 */
+		opp->param_addr_cycles = sc->sc_row_cycles;
+		opp->param_addr_cycles |= (sc->sc_column_cycles << 4);
+
+		opp->param_integrity_crc = nand_crc16((uint8_t *)opp, 254);
+//		printf("param page crc set to 0x%hx\n",
+//			 opp->param_integrity_crc);
+	}
+
+	sc->sc_ids[0] = 0x00;
+	sc->sc_ids[1] = 0x00;
+
+	sc->sc_onfi[0] = 'O';
+	sc->sc_onfi[1] = 'N';
+	sc->sc_onfi[2] = 'F';
+	sc->sc_onfi[3] = 'I';
+
+	sc->sc_row_mask = 0x00;
+	for (i = 0; i < sc->sc_row_cycles; i++) {
+		sc->sc_row_mask <<= 8;
+		sc->sc_row_mask |= 0xff;
+	}
+
+	nandemulator_device_reset(self);
+
+	sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev);
+}
+
+static int
+nandemulator_detach(device_t self, int flags)
+{
+	struct nandemulator_softc *sc = device_private(self);
+	int ret = 0;
+
+	aprint_normal_dev(sc->sc_dev, "detaching emulator\n");
+
+	pmf_device_deregister(sc->sc_dev);
+
+	if (sc->sc_nanddev != NULL)
+		ret = config_detach(sc->sc_nanddev, flags);
+
+	kmem_free(sc->sc_backend, sc->sc_backend_size);
+	kmem_free(sc->sc_parameter_page,
+	    sizeof(struct onfi_parameter_page) * 4);
+
+	return ret;
+}
+
+/**
+ * bring the emulated device to a known state
+ */
+static void
+nandemulator_device_reset(device_t self)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	sc->sc_command = 0;
+	sc->sc_register_writable = false;
+	sc->sc_io_len = 0;
+	sc->sc_io_pointer = NULL;
+	sc->sc_address = 0;
+	sc->sc_address_counter = 0;
+
+	sc->sc_status_register = ONFI_STATUS_RDY | ONFI_STATUS_WP;
+}
+
+static void
+nandemulator_address_chip(device_t self)
+{
+	struct nandemulator_softc *sc = device_private(self);
+	size_t page, offset;
+	
+	if (sc->sc_address_counter !=
+	    sc->sc_column_cycles + sc->sc_row_cycles) {
+		aprint_error_dev(self, "incorrect number of address cycles\n");
+		aprint_error_dev(self, "cc: %d, rc: %d, ac: %d\n",
+		    sc->sc_column_cycles, sc->sc_row_cycles,
+		    sc->sc_address_counter);
+	}
+
+	page = nandemulator_address_to_page(self);
+	offset = sc->sc_page_size * page;
+
+	DPRINTF(("READ/PROGRAM; page: 0x%jx (row addr: 0x%jx)\n",
+		(uintmax_t )page,
+		(uintmax_t )offset));
+
+	if (offset >= sc->sc_device_size) {
+		aprint_error_dev(self, "address > device size!\n");
+		sc->sc_io_len = 0;
+	} else {
+		size_t addr =
+		    nandemulator_page_to_backend_offset(self, page);
+		size_t pageoff =
+		    nandemulator_column_address_to_subpage(self);
+
+		DPRINTF(("subpage: 0x%jx\n", (uintmax_t )pageoff));
+
+		KASSERT(pageoff <
+		    sc->sc_page_size + sc->sc_spare_size);
+		KASSERT(addr < sc->sc_backend_size);
+
+		sc->sc_io_pointer = sc->sc_backend + addr + pageoff;
+		sc->sc_io_len =
+		    sc->sc_page_size + sc->sc_spare_size - pageoff;
+//		printf("io pointer set to: %p, len to: %zu\n",
+//		    sc->sc_io_pointer, sc->sc_io_len);
+	}
+}
+
+static void
+nandemulator_command(device_t self, uint8_t command)
+{
+	struct nandemulator_softc *sc = device_private(self);
+	size_t offset, page;
+
+	sc->sc_command = command;
+	sc->sc_register_writable = false;
+
+	DPRINTF(("nandemulator command: 0x%hhx\n", command));
+
+	switch (command) {
+	case ONFI_READ_STATUS:
+		sc->sc_io_pointer = &sc->sc_status_register;
+		sc->sc_io_len = 1;
+		break;
+	case ONFI_RESET:
+		nandemulator_device_reset(self);
+		break;
+	case ONFI_PAGE_PROGRAM:
+		sc->sc_register_writable = true;
+	case ONFI_READ:
+	case ONFI_BLOCK_ERASE:
+		sc->sc_address_counter = 0;
+	case ONFI_READ_ID:
+	case ONFI_READ_PARAMETER_PAGE:
+		sc->sc_io_len = 0;
+		sc->sc_address = 0;
+		break;
+	case ONFI_PAGE_PROGRAM_START:
+		/* XXX the program should only happen here */
+		break;
+	case ONFI_READ_START:
+		nandemulator_address_chip(self);
+		break;
+	case ONFI_BLOCK_ERASE_START:
+		page = nandemulator_address_to_page(self);
+		offset = sc->sc_page_size * page;
+
+		KASSERT(offset %
+		    (sc->sc_block_size * sc->sc_page_size) == 0);
+
+		if (offset >= sc->sc_device_size) {
+			aprint_error_dev(self, "address > device size!\n");
+		} else {
+			size_t addr =
+			    nandemulator_page_to_backend_offset(self, page);
+
+			size_t blocklen =
+			    sc->sc_block_size *
+			    (sc->sc_page_size + sc->sc_spare_size);
+
+			KASSERT(addr < sc->sc_backend_size);
+			uint8_t *block = sc->sc_backend + addr;
+
+			DPRINTF(("erasing block at 0x%jx\n",
+				(uintmax_t )offset));
+
+			memset(block, 0xff, blocklen);
+		}
+		sc->sc_io_len = 0;
+		break;
+	default:
+		aprint_error_dev(self,
+		    "invalid nand command (0x%hhx)\n", command);
+		sc->sc_io_len = 0;
+	}
+};
+
+static void
+nandemulator_address(device_t self, uint8_t address)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	/**
+	 * we have to handle read id/parameter page here,
+	 * as we can read right after giving the address.
+	 */
+	switch (sc->sc_command) {
+	case ONFI_READ_ID:
+		if (address == 0x00) {
+			sc->sc_io_len = 2;
+			sc->sc_io_pointer = sc->sc_ids;
+		} else if (address == 0x20) {
+			sc->sc_io_len = 4;
+			sc->sc_io_pointer = sc->sc_onfi;
+		} else {
+			sc->sc_io_len = 0;
+		}
+		break;
+	case ONFI_READ_PARAMETER_PAGE:
+		if (address == 0x00) {
+			sc->sc_io_len = sizeof(struct onfi_parameter_page) * 4;
+			sc->sc_io_pointer = (uint8_t *)sc->sc_parameter_page;
+		} else {
+			sc->sc_io_len = 0;
+		}
+		break;
+	case ONFI_PAGE_PROGRAM:
+		sc->sc_address <<= 8;
+		sc->sc_address |= address;
+		sc->sc_address_counter++;
+		
+		if (sc->sc_address_counter ==
+		    sc->sc_column_cycles + sc->sc_row_cycles) {
+			nandemulator_address_chip(self);
+		}
+		break;
+	default:
+		sc->sc_address <<= 8;
+		sc->sc_address |= address;
+		sc->sc_address_counter++;
+	}
+};
+
+static void
+nandemulator_busy(device_t self)
+{
+#ifdef NANDEMULATOR_DELAYS
+	struct nandemulator_softc *sc = device_private(self);
+
+	/* do some delay depending on command */
+	switch (sc->sc_command) {
+	case ONFI_PAGE_PROGRAM_START:
+	case ONFI_BLOCK_ERASE_START:
+		DELAY(10);
+		break;
+	case ONFI_READ_START:
+	default:
+		DELAY(1);
+	}
+#endif
+}
+
+static void
+nandemulator_read_byte(device_t self, uint8_t *data)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	if (sc->sc_io_len > 0) {
+		*data = *sc->sc_io_pointer;
+
+		sc->sc_io_pointer++;
+		sc->sc_io_len--;
+	} else {
+		aprint_error_dev(self, "reading byte from invalid location\n");
+		*data = 0xff;
+	}
+}
+
+static void
+nandemulator_write_byte(device_t self, uint8_t data)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	if (!sc->sc_register_writable) {
+		aprint_error_dev(self,
+		    "trying to write read only location without effect\n");
+		return;
+	}
+
+	if (sc->sc_io_len > 0) {
+		*sc->sc_io_pointer = data;
+
+		sc->sc_io_pointer++;
+		sc->sc_io_len--;
+	} else {
+		aprint_error_dev(self, "write to invalid location\n");
+	}
+}
+
+static void
+nandemulator_read_word(device_t self, uint16_t *data)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	if (sc->sc_buswidth != NANDEMULATOR_16BIT) {
+		aprint_error_dev(self,
+		    "trying to read a word on an 8bit chip\n");
+		return;
+	}
+
+	if (sc->sc_io_len > 1) {
+//		printf("reading word from %p, len is %zu\n",
+//		    sc->sc_io_pointer, sc->sc_io_len);
+
+		*data = *(uint16_t *)sc->sc_io_pointer;
+
+		sc->sc_io_pointer += 2;
+		sc->sc_io_len -= 2;
+	} else {
+		aprint_error_dev(self, "reading word from invalid location\n");
+		*data = 0xffff;
+	}
+}
+
+static void
+nandemulator_write_word(device_t self, uint16_t data)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	if (!sc->sc_register_writable) {
+		aprint_error_dev(self,
+		    "trying to write read only location without effect\n");
+		return;
+	}
+
+	if (sc->sc_buswidth != NANDEMULATOR_16BIT) {
+		aprint_error_dev(self,
+		    "trying to write a word to an 8bit chip");
+		return;
+	}
+
+	if (sc->sc_io_len > 1) {
+//		printf("writing word to %p, len is %zu\n",
+//		    sc->sc_io_pointer, sc->sc_io_len);
+		*(uint16_t *)sc->sc_io_pointer = data;
+
+		sc->sc_io_pointer += 2;
+		sc->sc_io_len -= 2;
+	} else {
+		aprint_error_dev(self, "writing to invalid location");
+	}
+}
+
+static void
+nandemulator_read_buf_byte(device_t self, void *buf, size_t len)
+{
+//	struct nandemulator_softc *sc = device_private(self);
+	uint8_t *addr;
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 1);
+
+	addr = buf;
+	while (len > 0) {
+		nandemulator_read_byte(self, addr);
+		addr++, len--;
+	}
+}
+
+static void
+nandemulator_read_buf_word(device_t self, void *buf, size_t len)
+{
+//	struct nandemulator_softc *sc = device_private(self);
+	uint16_t *addr;
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 2);
+	KASSERT(!(len & 0x01));
+
+	addr = buf;
+	len /= 2;
+	while (len > 0) {
+		nandemulator_read_word(self, addr);
+		addr++, len--;
+	}
+}
+
+static void
+nandemulator_write_buf_byte(device_t self, const void *buf, size_t len)
+{
+//	struct nandemulator_softc *sc = device_private(self);
+	const uint8_t *addr;
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 1);
+
+	addr = buf;
+	while (len > 0) {
+		nandemulator_write_byte(self, *addr);
+		addr++, len--;
+	}
+}
+
+static void
+nandemulator_write_buf_word(device_t self, const void *buf, size_t len)
+{
+//	struct nandemulator_softc *sc = device_private(self);
+	const uint16_t *addr;
+
+	KASSERT(buf != NULL);
+	KASSERT(len >= 2);
+	KASSERT(!(len & 0x01));
+
+	addr = buf;
+	len /= 2;
+	while (len > 0) {
+		nandemulator_write_word(self, *addr);
+		addr++, len--;
+	}
+}
+
+static size_t
+nandemulator_address_to_page(device_t self)
+{
+	struct nandemulator_softc *sc = device_private(self);
+	uint64_t address, offset;
+	int i;
+
+	address = htole64(sc->sc_address);
+	address &= sc->sc_row_mask;
+
+	offset = 0;
+	for (i = 0; i < sc->sc_row_cycles; i++) {
+		offset <<= 8;
+		offset |= (address & 0xff);
+		address >>= 8;
+	}
+
+	return le64toh(offset);
+}
+
+static size_t
+nandemulator_column_address_to_subpage(device_t self)
+{
+	struct nandemulator_softc *sc = device_private(self);
+	uint64_t address, offset;
+	int i;
+
+	address = htole64(sc->sc_address);
+	address >>= (8 * sc->sc_row_cycles);
+
+	offset = 0;
+	for (i = 0; i < sc->sc_column_cycles; i++) {
+		offset <<= 8;
+		offset |= (address & 0xff);
+		address >>= 8;
+	}
+
+	if (sc->sc_buswidth == NANDEMULATOR_16BIT)
+		return (size_t )le64toh(offset << 1);
+	else
+		return (size_t )le64toh(offset);
+}
+
+static size_t
+nandemulator_page_to_backend_offset(device_t self, size_t page)
+{
+	struct nandemulator_softc *sc = device_private(self);
+
+	return (sc->sc_page_size + sc->sc_spare_size) * page;
+}
Index: sys/dev/nand/nandemulator.h
===================================================================
RCS file: sys/dev/nand/nandemulator.h
diff -N sys/dev/nand/nandemulator.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/nandemulator.h	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,5 @@
+#ifndef _NANDEMULATOR_H_
+#define _NANDEMULATOR_H_
+#else
+
+#endif
Index: sys/dev/nand/onfi.h
===================================================================
RCS file: sys/dev/nand/onfi.h
diff -N sys/dev/nand/onfi.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/nand/onfi.h	16 Feb 2011 13:48:23 -0000
@@ -0,0 +1,180 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _ONFI_H_
+#define _ONFI_H_
+
+/**
+ * ONFI 2.2, Section 5.1: Command Set
+ *
+ * Indented ones are 2nd or 3rd cycle commands.
+ */
+
+enum {
+	ONFI_READ				= 0x00,	/* M */
+	 ONFI_READ_START			= 0x30,	/* M */
+	 ONFI_READ_INTERLEAVED			= 0x32,	/* O */
+	 ONFI_READ_COPYBACK			= 0x35,	/* O */
+	 ONFI_READ_CACHE_RANDOM			= 0x31,	/* O */
+
+	ONFI_CHANGE_READ_COLUMN			= 0x05,	/* M */
+	ONFI_CHANGE_READ_COLUMN_ENHANCED	= 0x06,	/* O */
+	 ONFI_CHANGE_READ_COLUMN_START		= 0xe0,	/* M */
+
+	ONFI_READ_CACHE_SEQUENTIAL		= 0x31,	/* O */
+	ONFI_READ_CACHE_END			= 0x3f,	/* O */
+
+	ONFI_BLOCK_ERASE			= 0x60,	/* M */
+	 ONFI_BLOCK_ERASE_START			= 0xd0,	/* M */
+	 ONFI_BLOCK_ERASE_INTERLEAVED		= 0xd1,	/* O */
+
+	ONFI_READ_STATUS			= 0x70,	/* M */
+	ONFI_READ_STATUS_ENHANCED		= 0x78,	/* O */
+
+	ONFI_PAGE_PROGRAM			= 0x80,	/* M */
+	 ONFI_PAGE_PROGRAM_START		= 0x10,	/* M */
+	 ONFI_PAGE_PROGRAM_INTERLEAVED		= 0x11,	/* O */
+	 ONFI_PAGE_CACHE_PROGRAM		= 0x15,	/* O */
+
+	ONFI_COPYBACK_PROGRAM			= 0x85,	/* O */
+	 ONFI_COPYBACK_PROGRAM_START		= 0x10,	/* O */ 
+	 ONFI_COPYBACK_PROGRAM_INTERLEAVED	= 0x11,	/* O */
+/*-
+ * Small Data's first opcode may be 80h if the operation is a program only
+ * with no data output. For the last second cycle of a Small Data Move,
+ * it is a 10h command to confirm the Program or Copyback operation
+ */
+	ONFI_SMALL_DATA_MOVE			= 0x85,	/* O */
+	ONFI_SMALL_DATA_MOVE_START		= 0x11,	/* O */
+
+	ONFI_CHANGE_WRITE_COLUMN		= 0x85,	/* M */
+	ONFI_CHANGE_ROW_ADDRESS			= 0x85,	/* O */
+
+	ONFI_READ_ID				= 0x90,	/* M */
+	ONFI_READ_PARAMETER_PAGE		= 0xec,	/* M */
+	ONFI_READ_UNIQUE_ID			= 0xed,	/* O */
+	ONFI_GET_FEATURES			= 0xee,	/* O */
+	ONFI_SET_FEATURES			= 0xef,	/* O */
+	ONFI_RESET_LUN				= 0xfa,	/* O */
+	ONFI_SYNCHRONOUS_RESET			= 0xfc,	/* O */
+	ONFI_RESET				= 0xff	/* M */
+};
+
+/**
+ * status codes from ONFI_READ_STATUS
+ */
+enum {
+	ONFI_STATUS_FAIL			= (1<<0),
+	ONFI_STATUS_FAILC			= (1<<1),
+	ONFI_STATUS_R				= (1<<2),
+	ONFI_STATUS_CSP				= (1<<3),
+	ONFI_STATUS_VSP				= (1<<4),
+	ONFI_STATUS_ARDY			= (1<<5),
+	ONFI_STATUS_RDY				= (1<<6),
+	ONFI_STATUS_WP				= (1<<7)
+};
+
+enum {
+	ONFI_FEATURE_16BIT			= (1<<0),
+	ONFI_FEATURE_EXTENDED_PARAM		= (1<<7)
+};
+	
+/* 5.7.1. Parameter Page Data Structure Definition */
+struct onfi_parameter_page {
+	/* Revision information and features block */
+	uint32_t param_signature; /* M: onfi signature ({'O','N','F','I'}) */
+	uint16_t param_revision;  /* M: revision number */
+	uint16_t param_features;  /* M: features supported */
+	uint16_t param_optional_cmds; /* M: optional commands */
+	uint16_t param_reserved_1;    /* R: reserved */
+	uint16_t param_extended_len;  /* O: extended parameter page lenght */
+	uint8_t param_num_param_pg;  /* O: number of parameter pages */
+	uint8_t param_reserved_2[17]; /* R: reserved */
+	/* Manufacturer information block */
+	uint8_t param_manufacturer[12]; /* M: device manufacturer (ASCII) */
+	uint8_t param_model[20];	      /* M: device model (ASCII) */
+	uint8_t param_manufacturer_id;  /* M: JEDEC ID of manufacturer */
+	uint16_t param_date;	      /* O: date code (BCD) */
+	uint8_t param_reserved_3[13];   /* R: reserved */
+	/* Memory organization block */
+	uint32_t param_pagesize; /* M: number of data bytes per page */
+	uint16_t param_sparesize; /* M: number of spare bytes per page */
+	uint32_t param_part_pagesize; /* O: obsolete */
+	uint16_t param_part_sparesize; /* O: obsolete */
+	uint32_t param_blocksize;      /* M: number of pages per block */
+	uint32_t param_lunsize;       /* M: number of blocks per LUN */
+	uint8_t param_numluns;	      /* M: number of LUNs */
+	uint8_t param_addr_cycles;   /* M: address cycles:
+					col: 4-7 (high), row: 0-3 (low) */
+	uint8_t param_cellsize;   /* M: number of bits per cell */
+	uint16_t param_lun_maxbad; /* M: maximum badblocks per LUN */
+	uint16_t param_block_endurance; /* M: block endurance */
+	uint8_t param_guaranteed_blocks; /* M: guaranteed valid blocks at
+					  begginning of target */
+	uint16_t param_guaranteed_endurance; /* M: block endurance of
+					      guranteed blocks */
+	uint8_t param_programs_per_page; /* M: number of programs per page */
+	uint8_t param_partial_programming_attr; /* O: obsolete */
+	uint8_t param_ecc_correctable_bits;     /* M: number of bits
+						 ECC correctability */
+	uint8_t param_interleaved_addr_bits; /* M: num of interleaved address
+					      bits (only low half is valid) */
+	uint8_t param_interleaved_op_attrs; /* O: obsolete */
+	uint8_t param_reserved_4[13];	  /* R: reserved */
+	/* Electrical parameters block */
+	uint8_t param_io_c_max; /* M: I/O pin capacitance, maximum */
+	uint16_t param_async_timing_mode; /* M: async timing mode support */
+	uint16_t param_async_progcache_timing_mode; /* O: obsolete */
+	uint16_t param_t_prog; /* M: maximum page program time (us) */
+	uint16_t param_t_bers; /* M: maximum block erase time (us) */
+	uint16_t param_t_r;    /* M: maximum page read time (us) */
+	uint16_t param_ccs;    /* M: minimum change column setup time (ns) */
+	uint16_t param_sync_timing_mode; /* source sync timing mode support */
+	uint8_t param_sync_features;     /* M: source sync features */
+	uint16_t param_clk_input_c;   /* O: CLK input pin cap., typical */
+	uint16_t param_io_c;	    /* O: I/O pin capacitance, typical */
+	uint16_t param_input_c;	    /* O: input pin capacitance, typical */
+	uint8_t param_input_c_max;    /* M: input pin capacitance, maximum */
+	uint8_t param_driver_strength; /* M: driver strength support */
+	uint16_t param_t_r_interleaved; /* O: maximum interleaved
+					 page read time (us) */
+	uint16_t param_t_adl;	/* O: program page register clear enhancement
+				 tADL value (ns) */
+	uint8_t param_reserved_5[8]; /* R: reserved */
+	/* Vendor block */
+	uint16_t param_vendor_revision; /* M: vendor specific rev number */
+	uint8_t param_vendor_specific[88]; /* vendor specific information */
+	uint16_t param_integrity_crc;	 /* M: integrity CRC */
+} __packed;
+
+#endif	/* _ONFI_H_ */
Index: sys/sys/Makefile
===================================================================
RCS file: /cvsroot/src/sys/sys/Makefile,v
retrieving revision 1.128
diff -u -r1.128 Makefile
--- sys/sys/Makefile	25 Sep 2010 01:42:40 -0000	1.128
+++ sys/sys/Makefile	16 Feb 2011 13:48:30 -0000
@@ -16,7 +16,7 @@
 	endian.h envsys.h errno.h evcnt.h event.h exec.h exec_aout.h \
 	exec_coff.h exec_ecoff.h exec_elf.h exec_script.h extattr.h extent.h \
 	fcntl.h fd_set.h fdio.h featuretest.h file.h filedesc.h filio.h \
-	float_ieee754.h fstypes.h gcq.h gmon.h gpio.h hash.h \
+	flashio.h float_ieee754.h fstypes.h gcq.h gmon.h gpio.h hash.h \
 	ieee754.h inttypes.h ioccom.h ioctl.h ioctl_compat.h iostat.h ipc.h \
 	joystick.h \
 	kcore.h kgdb.h kmem.h ksem.h ksyms.h ktrace.h \
Index: sys/sys/flashio.h
===================================================================
RCS file: sys/sys/flashio.h
diff -N sys/sys/flashio.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/sys/flashio.h	16 Feb 2011 13:48:30 -0000
@@ -0,0 +1,116 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FLASHIO_H_
+#define _FLASHIO_H_
+
+#include <sys/ioctl.h>
+
+/* this header may be used fron the kernel */
+#if defined(_KERNEL) || defined(_STANDALONE)
+#include <sys/types.h>
+#else
+#include <stdint.h>
+#include <stdbool.h>
+#endif
+
+enum {
+	FLASH_ERASE_DONE 	= 0x0,
+	FLASH_ERASE_FAILED	= 0x1
+};
+
+enum {
+	FLASH_TYPE_UNKNOWN	= 0x0,
+	FLASH_TYPE_NOR		= 0x1,
+	FLASH_TYPE_NAND		= 0x2
+};
+
+/* public userspace API */
+
+/* common integer type to address flash */
+typedef int64_t flash_addr_t;
+
+/**
+ * struct erase_params - for ioctl erase call
+ * @addr: start address of the erase
+ * @len: length of the erase
+ */
+struct flash_erase_params {
+	flash_addr_t ep_addr;
+	flash_addr_t ep_len;
+};
+
+struct flash_badblock_params {
+	flash_addr_t bbp_addr;
+	bool bbp_isbad;
+};
+
+struct flash_info_params {
+	flash_addr_t ip_flash_size;
+	size_t ip_page_size;
+	size_t ip_erase_size;
+	uint8_t ip_flash_type;
+};
+
+struct flash_dump_params {
+	flash_addr_t dp_block;
+	flash_addr_t dp_len;
+	uint8_t *dp_buf;
+};
+
+enum {
+	FLASH_IOCTL_ERASE_BLOCK,
+	FLASH_IOCTL_DUMP,
+	FLASH_IOCTL_GET_INFO,
+	FLASH_IOCTL_BLOCK_ISBAD,
+	FLASH_IOCTL_BLOCK_MARKBAD
+};
+
+#define FLASH_ERASE_BLOCK 	\
+	_IOW('&', FLASH_IOCTL_ERASE_BLOCK, struct flash_erase_params)
+
+#define FLASH_DUMP		\
+	_IOWR('&', FLASH_IOCTL_DUMP, struct flash_dump_params)
+
+#define FLASH_GET_INFO		\
+	_IOWR('&', FLASH_IOCTL_GET_INFO, struct flash_info_params)
+
+#define FLASH_BLOCK_ISBAD 	\
+	_IOWR('&', FLASH_IOCTL_BLOCK_ISBAD, struct flash_badblock_params)
+
+#define FLASH_BLOCK_MARKBAD	\
+	_IOW('&', FLASH_IOCTL_BLOCK_MARKBAD, struct flash_badblock_params)
+
+#endif
+
Index: usr.sbin/Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/Makefile,v
retrieving revision 1.251
diff -u -r1.251 Makefile
--- usr.sbin/Makefile	7 Feb 2011 18:11:29 -0000	1.251
+++ usr.sbin/Makefile	16 Feb 2011 13:48:36 -0000
@@ -7,8 +7,8 @@
 	btattach btconfig btdevctl bthcid btpand catman \
 	chroot chrtbl cnwctl cpuctl crash dev_mkdb \
 	dhcp diskpart dumpfs dumplfs edquota eeprom \
-	envstat eshconfig etcupdate extattrctl fssconfig fusermount fwctl \
-	gpioctl grfconfig gspa hdaudioctl ifwatchd inetd \
+	envstat eshconfig etcupdate extattrctl flashctl fssconfig fusermount \
+	fwctl gpioctl grfconfig gspa hdaudioctl ifwatchd inetd \
 	installboot \
 	iopctl iostat ipwctl irdaattach isdn iteconfig iwictl\
 	kgmon lastlogin ldpd link lmcconfig lockstat lpr mailwrapper makefs \
Index: usr.sbin/flashctl/Makefile
===================================================================
RCS file: usr.sbin/flashctl/Makefile
diff -N usr.sbin/flashctl/Makefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/flashctl/Makefile	16 Feb 2011 13:48:38 -0000
@@ -0,0 +1,10 @@
+# $NetBSD$
+
+SRCS=		flashctl.c
+
+PROG=		flashctl
+MAN=		# not yet
+
+WARNS=		4
+
+.include <bsd.prog.mk>
Index: usr.sbin/flashctl/flashctl.c
===================================================================
RCS file: usr.sbin/flashctl/flashctl.c
diff -N usr.sbin/flashctl/flashctl.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/flashctl/flashctl.c	16 Feb 2011 13:48:38 -0000
@@ -0,0 +1,249 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2010 Department of Software Engineering,
+ *		      University of Szeged, Hungary
+ * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/flashio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+
+
+void usage(void);
+int to_intmax(intmax_t *, const char *);
+
+int
+main(int argc, char **argv)
+{
+	char *device, *command;
+	int fd, error = 0;
+	intmax_t n = -1;
+
+	setprogname(argv[0]);
+
+	if (argc < 3)
+		usage();
+
+	device = argv[1];
+	command = argv[2];
+	argc -= 3;
+	argv += 3;
+
+	fd = open(device, O_RDWR, 0);
+	if (fd == -1)
+		err(EXIT_FAILURE, "can't open flash device");
+
+	if (!strcmp("erase", command)) {
+		struct flash_info_params ip;
+		struct flash_erase_params ep;
+
+		error = ioctl(fd, FLASH_GET_INFO, &ip);
+		if (error) {
+			warn("ioctl: FLASH_GET_INFO");
+			goto out;
+		}
+
+		if (argc == 2) {
+			error = to_intmax(&n, argv[0]);
+			if (error) {
+				warnx(strerror(error));
+				goto out;
+			}
+			ep.ep_addr = n;
+
+			if (!strcmp("all", argv[1])) {
+				ep.ep_len = ip.ip_flash_size;
+			} else {
+				error = to_intmax(&n, argv[1]);
+				if (error) {
+					warnx(strerror(error));
+					goto out;
+				}
+				ep.ep_len = n;
+			}
+		} else {
+			warnx("invalid number of arguments");
+			error = 1;
+			goto out;
+		}
+		
+		printf("Erasing %jx bytes starting from %jx\n",
+		    (uintmax_t )ep.ep_len, (uintmax_t )ep.ep_addr);
+		
+		error = ioctl(fd, FLASH_ERASE_BLOCK, &ep);
+		if (error) {
+			warn("ioctl: FLASH_ERASE_BLOCK");
+			goto out;
+		}
+	} else if (!strcmp("identify", command)) {
+		struct flash_info_params ip;
+		
+		error = ioctl(fd, FLASH_GET_INFO, &ip);
+		if (error) {
+			warn("ioctl: FLASH_GET_INFO");
+			goto out;
+		}
+
+		printf("Device type: ");
+		switch (ip.ip_flash_type) {
+		case FLASH_TYPE_NOR:
+			printf("NOR flash");
+			break;
+		case FLASH_TYPE_NAND:
+			printf("NAND flash");
+			break;
+		default:
+			printf("unknown (%d)", ip.ip_flash_type);
+		}
+		printf("\n");
+
+		/* TODO: humanize */
+		printf("Capacity %jd Mbytes, %jd pages, %zu bytes/page\n", 
+		    (intmax_t )ip.ip_flash_size / 1024 / 1024,
+		    (intmax_t )ip.ip_flash_size / ip.ip_page_size,
+		    ip.ip_page_size);
+
+		if (ip.ip_flash_type == FLASH_TYPE_NAND) {
+			printf("Block size %jd Kbytes, %jd pages/block\n",
+			    (intmax_t )ip.ip_erase_size / 1024,
+			    (intmax_t )ip.ip_erase_size / ip.ip_page_size);
+		}
+	} else if (!strcmp("badblocks", command)) {
+		struct flash_info_params ip;
+		struct flash_badblock_params bbp;
+		flash_addr_t addr;
+		bool hasbad = false;
+
+		error = ioctl(fd, FLASH_GET_INFO, &ip);
+		if (error) {
+			warn("ioctl: FLASH_GET_INFO");
+			goto out;
+		}
+
+		printf("Scanning for bad blocks: ");
+
+		addr = 0;
+		while (addr < ip.ip_flash_size) {
+			bbp.bbp_addr = addr;
+			
+			error = ioctl(fd, FLASH_BLOCK_ISBAD, &bbp);
+			if (error) {
+				warn("ioctl: FLASH_BLOCK_ISBAD");
+				goto out;
+			}
+
+			if (bbp.bbp_isbad) {
+				hasbad = true;
+				printf("0x%jx ", addr);
+			}
+
+			addr += ip.ip_erase_size;
+		}
+
+		if (hasbad)
+			printf("Done.\n");
+		else
+			printf("No bad blocks found.\n");
+	} else if (!strcmp("markbad", command)) {
+		flash_addr_t address;
+		
+		error = to_intmax(&n, argv[1]);
+		if (error) {
+			warnx(strerror(error));
+			goto out;
+		}
+
+		address = n;
+		
+		printf("Marking block 0x%jx as bad.\n",
+		    (intmax_t )address);
+
+		error = ioctl(fd, FLASH_BLOCK_MARKBAD, &address);
+		if (error) {
+			warn("ioctl: FLASH_BLOCK_MARKBAD");
+			goto out;
+		}
+	} else {
+		warnx("Unknown command");
+		error = 1;
+		goto out;
+	}
+
+out:
+	close(fd);
+	return error;
+}
+
+int
+to_intmax(intmax_t *num, const char *str)
+{
+	char *endptr;
+
+	errno = 0;
+	if (str[0] == '0' && tolower((int )str[1]) == 'x') {
+		if (!isxdigit((int )str[0]))
+			return EINVAL;
+		*num = strtoimax(str, &endptr, 16);
+	} else {
+		if (!isdigit((int )str[0]))
+			return EINVAL;
+		*num = strtoimax(str, &endptr, 10);
+	}
+
+	if (errno == ERANGE && (*num == INTMAX_MIN || *num == INTMAX_MAX)) {
+		return ERANGE;
+	}
+
+	return 0;
+}
+
+void
+usage(void)
+{
+	fprintf(stderr, "usage: %s device identify\n",
+	    getprogname());
+	fprintf(stderr, "       %s device erase <start address> <size>|all\n",
+	    getprogname());
+	fprintf(stderr, "       %s device badblocks\n",
+	    getprogname());
+	fprintf(stderr, "       %s device markbad <address>\n",
+	    getprogname());
+	
+	exit(1);
+}