diff --git a/distrib/sets/lists/base/mi b/distrib/sets/lists/base/mi index db3b39f14aea..f040bddb8fbd 100644 --- a/distrib/sets/lists/base/mi +++ b/distrib/sets/lists/base/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.921 2011/02/08 03:20:13 haad Exp $ +# $NetBSD: mi,v 1.922 2011/02/26 18:07:13 ahoka Exp $ # # Note: Don't delete entries from here - mark them as "obsolete" instead, # unless otherwise stated below. @@ -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 diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index 0be4b0137adc..c1ddcc7e8af1 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1589 2011/02/17 19:58:34 rmind Exp $ +# $NetBSD: mi,v 1.1590 2011/02/26 18:07:14 ahoka Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -2055,6 +2055,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 @@ -9369,6 +9370,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 @@ -15303,6 +15305,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 @@ -21388,6 +21391,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 diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi index e2a0c6e38dcd..a75dca26ddfd 100644 --- a/distrib/sets/lists/man/mi +++ b/distrib/sets/lists/man/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1295 2011/02/24 14:20:28 jruoho Exp $ +# $NetBSD: mi,v 1.1296 2011/02/26 18:07:15 ahoka Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -967,6 +967,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 @@ -3734,6 +3735,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 @@ -6276,6 +6278,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 diff --git a/distrib/sets/lists/modules/mi b/distrib/sets/lists/modules/mi index 897298d594fd..7b93c2e87df8 100644 --- a/distrib/sets/lists/modules/mi +++ b/distrib/sets/lists/modules/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.20 2010/12/29 13:05:42 jmcneill Exp $ +# $NetBSD: mi,v 1.21 2011/02/26 18:07:15 ahoka Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -48,6 +48,8 @@ ./@MODULEDIR@/ffs/ffs.kmod base-kernel-modules kmod ./@MODULEDIR@/filecore base-kernel-modules kmod ./@MODULEDIR@/filecore/filecore.kmod base-kernel-modules kmod +./@MODULEDIR@/flash base-kernel-modules kmod +./@MODULEDIR@/flash/flash.kmod base-kernel-modules kmod ./@MODULEDIR@/fss base-kernel-modules kmod ./@MODULEDIR@/fss/fss.kmod base-kernel-modules kmod ./@MODULEDIR@/hfs base-kernel-modules kmod @@ -70,6 +72,10 @@ ./@MODULEDIR@/mqueue/mqueue.kmod base-kernel-modules kmod ./@MODULEDIR@/msdos base-kernel-modules kmod ./@MODULEDIR@/msdos/msdos.kmod base-kernel-modules kmod +./@MODULEDIR@/nand base-kernel-modules kmod +./@MODULEDIR@/nand/nand.kmod base-kernel-modules kmod +./@MODULEDIR@/nandemulator base-kernel-modules kmod +./@MODULEDIR@/nandemulator/nandemulator.kmod base-kernel-modules kmod ./@MODULEDIR@/nfs base-kernel-modules kmod ./@MODULEDIR@/nfs/nfs.kmod base-kernel-modules kmod ./@MODULEDIR@/nfsserver base-kernel-modules kmod diff --git a/doc/CHANGES b/doc/CHANGES index ec5ef060b38f..f03a7a99925e 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -1,4 +1,4 @@ -# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.1514 $> +# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.1515 $> # # # [Note: This file does not mention every change made to the NetBSD source tree. @@ -872,3 +872,6 @@ Changes from NetBSD 5.0 to NetBSD 6.0: DIGEST-MD5, EXTERNAL, GSSAPI, LOGIN, PLAIN [christos 20110221] alc(4): Add a driver for Atheros AR813x/AR815x Ethernet. [jmcneill 20110222] + flash(9): Add subsystem to handle Flash devices. [ahoka 20110226] + nand(9): Add subsystem to handle NAND controllers. [ahoka 20110226] + evbarm: Add driver for OMAP NAND controllers. [ahoka 20110226] diff --git a/doc/RESPONSIBLE b/doc/RESPONSIBLE index 74405c852f3c..8192ee22ac15 100644 --- a/doc/RESPONSIBLE +++ b/doc/RESPONSIBLE @@ -1,4 +1,4 @@ -# $NetBSD: RESPONSIBLE,v 1.94 2010/06/09 21:54:21 tron Exp $ +# $NetBSD: RESPONSIBLE,v 1.95 2011/02/26 18:07:15 ahoka Exp $ List of sections of the system, and who is responsible for them (or at least considered an expert on them). @@ -103,12 +103,14 @@ acpi thorpej, kochi, jmcneill, joerg audio augustss, jmcneill cgd elric firewire jmc +flash ahoka gpio mbalmer ide/atapi bouyer irda augustss, kenh isdn martin keylock mbalmer mca jdolecek +nand ahoka net80211 dyoung onewire riz podulebus bjh21 diff --git a/etc/MAKEDEV.tmpl b/etc/MAKEDEV.tmpl index 651846af7648..ff072af86d9c 100644 --- a/etc/MAKEDEV.tmpl +++ b/etc/MAKEDEV.tmpl @@ -1,5 +1,5 @@ #!/bin/sh - -# $NetBSD: MAKEDEV.tmpl,v 1.137 2011/01/26 01:18:47 pooka Exp $ +# $NetBSD: MAKEDEV.tmpl,v 1.138 2011/02/26 18:07:16 ahoka Exp $ # # Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -1042,6 +1042,13 @@ ld*) %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 diff --git a/etc/etc.amd64/MAKEDEV.conf b/etc/etc.amd64/MAKEDEV.conf index 2f2a4cc199c2..5dbe82bc844e 100644 --- a/etc/etc.amd64/MAKEDEV.conf +++ b/etc/etc.amd64/MAKEDEV.conf @@ -1,10 +1,11 @@ -# $NetBSD: MAKEDEV.conf,v 1.16 2008/11/04 14:25:10 joerg Exp $ +# $NetBSD: MAKEDEV.conf,v 1.17 2011/02/26 18:07:16 ahoka Exp $ # As of 2003-04-17, the "init" case must not create more than 890 entries. all_md) 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 diff --git a/etc/etc.evbarm/MAKEDEV.conf b/etc/etc.evbarm/MAKEDEV.conf index 5a74fe440a92..bb62a50c7b78 100644 --- a/etc/etc.evbarm/MAKEDEV.conf +++ b/etc/etc.evbarm/MAKEDEV.conf @@ -1,7 +1,8 @@ -# $NetBSD: MAKEDEV.conf,v 1.3 2008/09/13 11:46:18 tsutsui Exp $ +# $NetBSD: MAKEDEV.conf,v 1.4 2011/02/26 18:07:16 ahoka Exp $ 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 @@ all_md) 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 ;; diff --git a/etc/etc.evbmips/MAKEDEV.conf b/etc/etc.evbmips/MAKEDEV.conf index 06d7878096e3..0676da0a0fd9 100644 --- a/etc/etc.evbmips/MAKEDEV.conf +++ b/etc/etc.evbmips/MAKEDEV.conf @@ -1,7 +1,8 @@ -# $NetBSD: MAKEDEV.conf,v 1.4 2007/01/15 23:35:11 hubertf Exp $ +# $NetBSD: MAKEDEV.conf,v 1.5 2011/02/26 18:07:16 ahoka Exp $ 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 @@ minimal) 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 ;; diff --git a/etc/etc.evbppc/MAKEDEV.conf b/etc/etc.evbppc/MAKEDEV.conf index 9b6b3337d27b..b26ce2a4d6e3 100644 --- a/etc/etc.evbppc/MAKEDEV.conf +++ b/etc/etc.evbppc/MAKEDEV.conf @@ -1,7 +1,8 @@ -# $NetBSD: MAKEDEV.conf,v 1.6 2008/09/13 11:46:18 tsutsui Exp $ +# $NetBSD: MAKEDEV.conf,v 1.7 2011/02/26 18:07:16 ahoka Exp $ 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 diff --git a/etc/etc.hpcarm/MAKEDEV.conf b/etc/etc.hpcarm/MAKEDEV.conf index c180020a0fd7..a7fc6062e666 100644 --- a/etc/etc.hpcarm/MAKEDEV.conf +++ b/etc/etc.hpcarm/MAKEDEV.conf @@ -1,7 +1,8 @@ -# $NetBSD: MAKEDEV.conf,v 1.11 2010/08/01 04:08:27 tsutsui Exp $ +# $NetBSD: MAKEDEV.conf,v 1.12 2011/02/26 18:07:17 ahoka Exp $ 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 @@ std_hpcarm) 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 diff --git a/etc/etc.hpcmips/MAKEDEV.conf b/etc/etc.hpcmips/MAKEDEV.conf index 1b9681990b65..5546d23664af 100644 --- a/etc/etc.hpcmips/MAKEDEV.conf +++ b/etc/etc.hpcmips/MAKEDEV.conf @@ -1,7 +1,8 @@ -# $NetBSD: MAKEDEV.conf,v 1.4 2007/01/15 23:35:12 hubertf Exp $ +# $NetBSD: MAKEDEV.conf,v 1.5 2011/02/26 18:07:17 ahoka Exp $ 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 @@ all_md) 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 ;; diff --git a/etc/etc.i386/MAKEDEV.conf b/etc/etc.i386/MAKEDEV.conf index 11bc6eb32e10..358368e9b032 100644 --- a/etc/etc.i386/MAKEDEV.conf +++ b/etc/etc.i386/MAKEDEV.conf @@ -1,10 +1,11 @@ -# $NetBSD: MAKEDEV.conf,v 1.20 2008/11/04 14:25:10 joerg Exp $ +# $NetBSD: MAKEDEV.conf,v 1.21 2011/02/26 18:07:17 ahoka Exp $ # As of 2005-03-15, the "init" case must not create more than 1024 entries. all_md) 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 diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index a8b4226b9516..e74ffb0b2b6f 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.554 2011/02/23 08:52:16 jruoho Exp $ +# $NetBSD: Makefile,v 1.555 2011/02/26 18:07:17 ahoka Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -24,7 +24,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ 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 fujbp.4 fxp.4 \ + fast_ipsec.4 fd.4 finsio.4 flash.4 fpa.4 fms.4 fss.4 fujbp.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 \ diff --git a/share/man/man4/flash.4 b/share/man/man4/flash.4 new file mode 100644 index 000000000000..aa40d250258b --- /dev/null +++ b/share/man/man4/flash.4 @@ -0,0 +1,69 @@ +.\" $NetBSD: flash.4,v 1.2 2011/02/26 18:07:17 ahoka Exp $ +.\" +.\" Copyright (c) 2010 Department of Software Engineering, +.\" University of Szeged, Hungary +.\" Copyright (c) 2010 Adam Hoka +.\" 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 diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 37488c7983e5..19bef047a188 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.344 2011/01/27 14:48:37 wiz Exp $ +# $NetBSD: Makefile,v 1.345 2011/02/26 18:07:17 ahoka Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -18,8 +18,8 @@ MAN= accept_filter.9 accf_data.9 accf_http.9 \ 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 \ diff --git a/share/man/man9/flash.9 b/share/man/man9/flash.9 new file mode 100644 index 000000000000..778a44905c5b --- /dev/null +++ b/share/man/man9/flash.9 @@ -0,0 +1,84 @@ +.\" $NetBSD: flash.9,v 1.1 2011/02/26 18:07:17 ahoka Exp $ +.\" +.\" Copyright (c) 2010 Department of Software Engineering, +.\" University of Szeged, Hungary +.\" Copyright (c) 2010 Adam Hoka +.\" 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 diff --git a/sys/arch/arm/omap/files.omap2 b/sys/arch/arm/omap/files.omap2 index 937b922fb505..6a6b7861c509 100644 --- a/sys/arch/arm/omap/files.omap2 +++ b/sys/arch/arm/omap/files.omap2 @@ -1,4 +1,4 @@ -# $NetBSD: files.omap2,v 1.6 2010/09/01 06:23:59 kiyohara Exp $ +# $NetBSD: files.omap2,v 1.7 2011/02/26 18:07:18 ahoka Exp $ # # Configuration info for Texas Instruments OMAP2/OMAP3 CPU support # Based on xscale/files.pxa2x0 @@ -79,6 +79,11 @@ device gpmc { [addr=-1], [size=0], [intr=-1], [mult=1], [nobyteacc=0] 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 diff --git a/sys/arch/arm/omap/omap2_nand.c b/sys/arch/arm/omap/omap2_nand.c new file mode 100644 index 000000000000..844f5da4607c --- /dev/null +++ b/sys/arch/arm/omap/omap2_nand.c @@ -0,0 +1,517 @@ +/* $NetBSD: omap2_nand.c,v 1.1 2011/02/26 18:07:18 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 +__KERNEL_RCSID(0, "$NetBSD: omap2_nand.c,v 1.1 2011/02/26 18:07:18 ahoka Exp $"); + +#include "opt_omap.h" +#include "opt_flash.h" + +/* TODO move to opt_* */ +#undef OMAP2_NAND_HARDWARE_ECC + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +/* 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; + } +} diff --git a/sys/arch/evbarm/conf/BEAGLEBOARD b/sys/arch/evbarm/conf/BEAGLEBOARD index 6d895741efc4..2fd31af134d8 100644 --- a/sys/arch/evbarm/conf/BEAGLEBOARD +++ b/sys/arch/evbarm/conf/BEAGLEBOARD @@ -1,5 +1,5 @@ # -# $NetBSD: BEAGLEBOARD,v 1.14 2011/01/04 11:38:58 matt Exp $ +# $NetBSD: BEAGLEBOARD,v 1.15 2011/02/26 18:07:18 ahoka Exp $ # # BEAGLEBOARD -- TI OMAP 3530 Eval Board Kernel # @@ -183,6 +183,19 @@ obio2 at mainbus? base 0x49000000 size 0x0100000 # L4 PERIPHERAL # 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 diff --git a/sys/arch/i386/conf/ALL b/sys/arch/i386/conf/ALL index 4c1e487944db..20ff0fdcfa74 100644 --- a/sys/arch/i386/conf/ALL +++ b/sys/arch/i386/conf/ALL @@ -1,4 +1,4 @@ -# $NetBSD: ALL,v 1.297 2011/02/24 17:42:16 macallan Exp $ +# $NetBSD: ALL,v 1.298 2011/02/26 18:07:30 ahoka Exp $ # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp # # ALL machine description file @@ -17,7 +17,7 @@ include "arch/i386/conf/std.i386" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "ALL-$Revision: 1.297 $" +#ident "ALL-$Revision: 1.298 $" maxusers 64 # estimated number of users @@ -1584,6 +1584,12 @@ satlink0 at isa? port 0x300 drq 1 # 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" @@ -1703,6 +1709,8 @@ pseudo-device pud # Hardware-assisted data mover interface pseudo-device dmoverio +pseudo-device nandemulator + options FILEASSOC # fileassoc(9) - required for Veriexec # Veriexec diff --git a/sys/conf/files b/sys/conf/files index b882e9a6dc00..7a1d9d508e4d 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1002 2011/02/20 15:40:21 jmcneill Exp $ +# $NetBSD: files,v 1.1003 2011/02/26 18:07:30 ahoka Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20100430 @@ -1705,3 +1705,14 @@ include "dev/tprof/files.tprof" # alternate memory device # include "dev/altmem/files.altmem" + +# +# Flash subsystem +# +include "dev/flash/files.flash" + +# +# NAND subsytem +# +include "dev/nand/files.nand" + diff --git a/sys/dev/flash/files.flash b/sys/dev/flash/files.flash new file mode 100644 index 000000000000..7871e2d90318 --- /dev/null +++ b/sys/dev/flash/files.flash @@ -0,0 +1,9 @@ +# $NetBSD: files.flash,v 1.1 2011/02/26 18:07:30 ahoka Exp $ + +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 diff --git a/sys/dev/flash/flash.c b/sys/dev/flash/flash.c new file mode 100644 index 000000000000..33007cfe3391 --- /dev/null +++ b/sys/dev/flash/flash.c @@ -0,0 +1,695 @@ +/* $NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $ */ + +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * Copyright (c) 2010 David Tengeri + * 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 +__KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#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 + +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; + } + + 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); + + /* freeing flash_if is our responsibility */ + printf("freeing flash_if..."); + kmem_free(sc->flash_if, sizeof(*sc->flash_if)); + printf("done!\n"); + + 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 *l) +{ + 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 *l) +{ + 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 *l) +{ + struct flash_erase_params *ep; + struct flash_info_params *ip; + struct flash_dump_params *dp; + struct flash_badblock_params *bbp; + struct flash_erase_instruction ei; + 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.ei_addr = ep->ep_addr; + ei.ei_len = ep->ep_len; + 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; +} + +MODULE(MODULE_CLASS_DRIVER, flash, NULL); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +flash_modcmd(modcmd_t cmd, void *opaque) +{ + int error = 0; +#ifdef _MODULE + int bmaj = -1, cmaj = -1; +#endif + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + error = config_init_component(cfdriver_ioconf_flash, + cfattach_ioconf_flash, cfdata_ioconf_flash); + if (error) + return error; + error = devsw_attach("flash", &flash_bdevsw, &bmaj, + &flash_cdevsw, &cmaj); + if (error) + config_fini_component(cfdriver_ioconf_flash, + cfattach_ioconf_flash, cfdata_ioconf_flash); +#endif + return error; + case MODULE_CMD_FINI: +#ifdef _MODULE + devsw_detach(&flash_bdevsw, &flash_cdevsw); + error = config_fini_component(cfdriver_ioconf_flash, + cfattach_ioconf_flash, cfdata_ioconf_flash); +#endif + return error; + default: + return ENOTTY; + } +} diff --git a/sys/dev/flash/flash.h b/sys/dev/flash/flash.h new file mode 100644 index 000000000000..ead0b3cda271 --- /dev/null +++ b/sys/dev/flash/flash.h @@ -0,0 +1,168 @@ +/* $NetBSD: flash.h,v 1.1 2011/02/26 18:07:30 ahoka Exp $ */ + +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * Copyright (c) 2010 David Tengeri + * 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 +#include +#include +#include +#include + +/** + * 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 *); + 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_ */ diff --git a/sys/dev/nand/files.nand b/sys/dev/nand/files.nand new file mode 100644 index 000000000000..4ab562e4103c --- /dev/null +++ b/sys/dev/nand/files.nand @@ -0,0 +1,16 @@ +# $NetBSD: files.nand,v 1.1 2011/02/26 18:07:31 ahoka Exp $ + +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 diff --git a/sys/dev/nand/hamming.c b/sys/dev/nand/hamming.c new file mode 100644 index 000000000000..eb2cc7ab253b --- /dev/null +++ b/sys/dev/nand/hamming.c @@ -0,0 +1,232 @@ +/* $NetBSD: hamming.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/* + * 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 +__KERNEL_RCSID(0, "$NetBSD: hamming.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $"); + +#include +#include +#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; + } +} + diff --git a/sys/dev/nand/hamming.h b/sys/dev/nand/hamming.h new file mode 100644 index 000000000000..4c3d569c9cf4 --- /dev/null +++ b/sys/dev/nand/hamming.h @@ -0,0 +1,56 @@ +/* $NetBSD: hamming.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/* + * 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 */ + diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c new file mode 100644 index 000000000000..e999b28fd731 --- /dev/null +++ b/sys/dev/nand/nand.c @@ -0,0 +1,1478 @@ +/* $NetBSD: nand.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 +__KERNEL_RCSID(0, "$NetBSD: nand.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $"); + +#include "locators.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 *)¶ms; + 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"); + /* TODO: we should read the next parameter page copy */ + } + + /* 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); +} + +MODULE(MODULE_CLASS_DRIVER, nand, "flash"); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +nand_modcmd(modcmd_t cmd, void *opaque) +{ + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + return config_init_component(cfdriver_ioconf_nand, + cfattach_ioconf_nand, cfdata_ioconf_nand); +#else + return 0; +#endif + case MODULE_CMD_FINI: +#ifdef _MODULE + return config_fini_component(cfdriver_ioconf_nand, + cfattach_ioconf_nand, cfdata_ioconf_nand); +#else + return 0; +#endif + default: + return ENOTTY; + } +} diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h new file mode 100644 index 000000000000..a9e8c1f295aa --- /dev/null +++ b/sys/dev/nand/nand.h @@ -0,0 +1,465 @@ +/* $NetBSD: nand.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 +#include + +#include +#include +#include + +#include + +/* 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_ */ diff --git a/sys/dev/nand/nand_bbt.c b/sys/dev/nand/nand_bbt.c new file mode 100644 index 000000000000..362a1d52378a --- /dev/null +++ b/sys/dev/nand/nand_bbt.c @@ -0,0 +1,250 @@ +/* $NetBSD: nand_bbt.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * 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 +#include + +#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; + + printf("freeing bbt bitmap..."); + kmem_free(bbt->nbbt_bitmap, bbt->nbbt_size); + printf("done!\n"); +} + +void +nand_bbt_scan(device_t self) +{ + struct nand_softc *sc = device_private(self); + struct nand_chip *chip = &sc->sc_chip; + 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); +#ifdef DIAGNOSTIC + struct nand_chip *chip = &sc->sc_chip; +#endif + struct nand_bbt *bbt = &sc->sc_bbt; + uint8_t clean; + + 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); +#ifdef DIAGNOSTIC + struct nand_chip *chip = &sc->sc_chip; +#endif + struct nand_bbt *bbt = &sc->sc_bbt; + uint8_t byte, marker; + bool result; + + 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; +} diff --git a/sys/dev/nand/nand_bbt.h b/sys/dev/nand/nand_bbt.h new file mode 100644 index 000000000000..dec73fff4652 --- /dev/null +++ b/sys/dev/nand/nand_bbt.h @@ -0,0 +1,58 @@ +/* $NetBSD: nand_bbt.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 diff --git a/sys/dev/nand/nand_crc.c b/sys/dev/nand/nand_crc.c new file mode 100644 index 000000000000..490c07bc77c3 --- /dev/null +++ b/sys/dev/nand/nand_crc.c @@ -0,0 +1,63 @@ +/* $NetBSD: nand_crc.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 +__KERNEL_RCSID(0, "$NetBSD: nand_crc.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $"); + +#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; +} diff --git a/sys/dev/nand/nand_crc.h b/sys/dev/nand/nand_crc.h new file mode 100644 index 000000000000..eea450c32eb8 --- /dev/null +++ b/sys/dev/nand/nand_crc.h @@ -0,0 +1,42 @@ +/* $NetBSD: nand_crc.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 +#include + +uint16_t nand_crc16(uint8_t *, size_t); + +#endif diff --git a/sys/dev/nand/nand_io.c b/sys/dev/nand/nand_io.c new file mode 100644 index 000000000000..b883943bfd4f --- /dev/null +++ b/sys/dev/nand/nand_io.c @@ -0,0 +1,373 @@ +/* $NetBSD: nand_io.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * 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 +#include +#include +#include +#include +#include +#include + +#include +#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 | KTHREAD_MPSAFE, + 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); +} diff --git a/sys/dev/nand/nandemulator.c b/sys/dev/nand/nandemulator.c new file mode 100644 index 000000000000..51bbb65a8e22 --- /dev/null +++ b/sys/dev/nand/nandemulator.c @@ -0,0 +1,779 @@ +/* $NetBSD: nandemulator.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * 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 +__KERNEL_RCSID(0, "$NetBSD: nandemulator.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $"); + +#include +#include +#include +#include +#include + +#include "nandemulator.h" + +#include +#include +#include + +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, + "NETBSD", strlen("NETBSD")); + memcpy(opp->param_model, + "NANDEMULATOR", strlen("NANDEMULATOR")); + + 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); + } + + 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; + } +} + +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) { + *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) { + *(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) +{ + 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) +{ + 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) +{ + 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) +{ + 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; +} + +#ifdef _MODULE + +MODULE(MODULE_CLASS_DRIVER, nandemulator, "nand"); + +static const struct cfiattrdata nandbuscf_iattrdata = { + "nandbus", 0, { { NULL, NULL, 0 }, } +}; +static const struct cfiattrdata * const nandemulator_attrs[] = { + &nandbuscf_iattrdata, NULL +}; + +CFDRIVER_DECL(nandemulator, DV_DULL, nandemulator_attrs); +extern struct cfattach nandemulator_ca; +static int nandemulatorloc[] = { -1, -1 }; + +static struct cfdata nandemulator_cfdata[] = { + { + .cf_name = "nandemulator", + .cf_atname = "nandemulator", + .cf_unit = 0, + .cf_fstate = FSTATE_STAR, + .cf_loc = nandemulatorloc, + .cf_flags = 0, + .cf_pspec = NULL, + }, + { NULL, NULL, 0, 0, NULL, 0, NULL } +}; + +static int +nandemulator_modcmd(modcmd_t cmd, void *arg) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = config_cfdriver_attach(&nandemulator_cd); + if (error) { + return error; + } + + error = config_cfattach_attach(nandemulator_cd.cd_name, + &nandemulator_ca); + if (error) { + config_cfdriver_detach(&nandemulator_cd); + aprint_error("%s: unable to register cfattach\n", + nandemulator_cd.cd_name); + + return error; + } + + error = config_cfdata_attach(nandemulator_cfdata, 1); + if (error) { + config_cfattach_detach(nandemulator_cd.cd_name, + &nandemulator_ca); + config_cfdriver_detach(&nandemulator_cd); + aprint_error("%s: unable to register cfdata\n", + nandemulator_cd.cd_name); + + return error; + } + + (void)config_attach_pseudo(nandemulator_cfdata); + + return 0; + case MODULE_CMD_FINI: + error = config_cfdata_detach(nandemulator_cfdata); + if (error) { + return error; + } + + config_cfattach_detach(nandemulator_cd.cd_name, + &nandemulator_ca); + config_cfdriver_detach(&nandemulator_cd); + + return 0; + default: + return ENOTTY; + } +} + +#endif diff --git a/sys/dev/nand/nandemulator.h b/sys/dev/nand/nandemulator.h new file mode 100644 index 000000000000..155921e943e7 --- /dev/null +++ b/sys/dev/nand/nandemulator.h @@ -0,0 +1,5 @@ +#ifndef _NANDEMULATOR_H_ +#define _NANDEMULATOR_H_ +#else + +#endif diff --git a/sys/dev/nand/onfi.h b/sys/dev/nand/onfi.h new file mode 100644 index 000000000000..113edc7f629c --- /dev/null +++ b/sys/dev/nand/onfi.h @@ -0,0 +1,180 @@ +/* $NetBSD: onfi.h,v 1.1 2011/02/26 18:07:31 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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_ */ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 113dfac51fbf..654ec878cc3c 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.67 2011/02/24 14:08:51 jruoho Exp $ +# $NetBSD: Makefile,v 1.68 2011/02/26 18:07:31 ahoka Exp $ .include @@ -21,6 +21,7 @@ SUBDIR+= exec_script SUBDIR+= fdesc SUBDIR+= ffs SUBDIR+= filecore +SUBDIR+= flash SUBDIR+= fss SUBDIR+= hfs SUBDIR+= kernfs @@ -32,6 +33,8 @@ SUBDIR+= miiverbose SUBDIR+= miniroot SUBDIR+= mqueue SUBDIR+= msdos +SUBDIR+= nand +SUBDIR+= nandemulator SUBDIR+= nfs SUBDIR+= nfsserver SUBDIR+= nilfs diff --git a/sys/modules/flash/Makefile b/sys/modules/flash/Makefile new file mode 100644 index 000000000000..63bd8d050476 --- /dev/null +++ b/sys/modules/flash/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.1 2011/02/26 18:07:31 ahoka Exp $ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/flash + +KMOD= flash +IOCONF= flash.ioconf +SRCS= flash.c + +.include diff --git a/sys/modules/flash/flash.ioconf b/sys/modules/flash/flash.ioconf new file mode 100644 index 000000000000..34eac06786b6 --- /dev/null +++ b/sys/modules/flash/flash.ioconf @@ -0,0 +1,9 @@ +# $NetBSD: flash.ioconf,v 1.1 2011/02/26 18:07:31 ahoka Exp $ + +ioconf flash + +include "conf/files" + +pseudo-root flashbus* + +flash* at flashbus? diff --git a/sys/modules/nand/Makefile b/sys/modules/nand/Makefile new file mode 100644 index 000000000000..dfaee0cb9edf --- /dev/null +++ b/sys/modules/nand/Makefile @@ -0,0 +1,15 @@ +# $NetBSD: Makefile,v 1.1 2011/02/26 18:07:31 ahoka Exp $ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/nand + +KMOD= nand +IOCONF= nand.ioconf +SRCS= nand.c +SRCS+= nand_io.c +SRCS+= nand_bbt.c +SRCS+= nand_crc.c +SRCS+= hamming.c + +.include diff --git a/sys/modules/nand/nand.ioconf b/sys/modules/nand/nand.ioconf new file mode 100644 index 000000000000..1182afaa3ae5 --- /dev/null +++ b/sys/modules/nand/nand.ioconf @@ -0,0 +1,9 @@ +# $NetBSD: nand.ioconf,v 1.1 2011/02/26 18:07:31 ahoka Exp $ + +ioconf nand + +include "conf/files" + +pseudo-root nandbus* + +nand* at nandbus? diff --git a/sys/modules/nand/opt_nand.h b/sys/modules/nand/opt_nand.h new file mode 100644 index 000000000000..531c586a1933 --- /dev/null +++ b/sys/modules/nand/opt_nand.h @@ -0,0 +1,3 @@ +#ifndef NAND_BBT +#define NAND_BBT +#endif diff --git a/sys/modules/nandemulator/Makefile b/sys/modules/nandemulator/Makefile new file mode 100644 index 000000000000..ae58b3c3f598 --- /dev/null +++ b/sys/modules/nandemulator/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.1 2011/02/26 18:07:32 ahoka Exp $ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/nand + +KMOD= nandemulator +#IOCONF= nandemulator.ioconf +SRCS= nandemulator.c + +.include diff --git a/sys/sys/Makefile b/sys/sys/Makefile index 95400744d2d9..7fca862beb80 100644 --- a/sys/sys/Makefile +++ b/sys/sys/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.129 2011/02/17 19:58:34 rmind Exp $ +# $NetBSD: Makefile,v 1.130 2011/02/26 18:07:32 ahoka Exp $ .include @@ -16,7 +16,7 @@ INCS= acct.h agpio.h aio.h ansi.h aout_mids.h ataio.h atomic.h audioio.h \ 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 \ diff --git a/sys/sys/flashio.h b/sys/sys/flashio.h new file mode 100644 index 000000000000..1e3057a21f84 --- /dev/null +++ b/sys/sys/flashio.h @@ -0,0 +1,116 @@ +/* $NetBSD: flashio.h,v 1.1 2011/02/26 18:07:32 ahoka Exp $ */ + +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * Copyright (c) 2010 David Tengeri + * 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 + +/* this header may be used fron the kernel */ +#if defined(_KERNEL) || defined(_STANDALONE) +#include +#else +#include +#include +#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 + diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 2727bd83e22a..be23fff9b7f4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.251 2011/02/07 18:11:29 tsutsui Exp $ +# $NetBSD: Makefile,v 1.252 2011/02/26 18:07:32 ahoka Exp $ # from: @(#)Makefile 5.20 (Berkeley) 6/12/93 .include @@ -7,8 +7,8 @@ SUBDIR= ac accton acpitools altq apm apmd arp bad144 bootp \ 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 \ diff --git a/usr.sbin/flashctl/Makefile b/usr.sbin/flashctl/Makefile new file mode 100644 index 000000000000..41db911634b3 --- /dev/null +++ b/usr.sbin/flashctl/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.1 2011/02/26 18:07:32 ahoka Exp $ + +SRCS= flashctl.c + +PROG= flashctl +MAN= # not yet + +WARNS= 4 + +.include diff --git a/usr.sbin/flashctl/flashctl.c b/usr.sbin/flashctl/flashctl.c new file mode 100644 index 000000000000..b89adb6abd8b --- /dev/null +++ b/usr.sbin/flashctl/flashctl.c @@ -0,0 +1,249 @@ +/* $NetBSD: flashctl.c,v 1.1 2011/02/26 18:07:32 ahoka Exp $ */ + +/*- + * Copyright (c) 2010 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2010 Adam Hoka + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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 |all\n", + getprogname()); + fprintf(stderr, " %s device badblocks\n", + getprogname()); + fprintf(stderr, " %s device markbad
\n", + getprogname()); + + exit(1); +}