Import the Flash and NAND subsytem code contributed by the University

of Szeged, Hungary.

The commit includes:
 - Flash layer, which gives a common API to access flash devices
 - NAND controller subsystem for the flash layer
 - An example OMAP driver which is used on BeagleBoard or alike ARM boards
This commit is contained in:
ahoka 2011-02-26 18:07:13 +00:00
parent 2bf60042d7
commit 2b6ee22130
51 changed files with 6079 additions and 30 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
;;

View File

@ -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
;;

View File

@ -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

View File

@ -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

View File

@ -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
;;

View File

@ -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

View File

@ -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 \

69
share/man/man4/flash.4 Normal file
View File

@ -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 <ahoka@NetBSD.org>
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by the Department of Software Engineering, University of Szeged, Hungary
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd January 21, 2010
.Dt FLASH 4
.Os
.Sh NAME
.Nm flash
.Nd device-independent flash layer
.Sh SYNOPSIS
.In sys/flash.h
.Sh DESCRIPTION
The
.Nm
driver provides a device-independent interface for using flash memory.
.Pp
The following
.Xr ioctl 2
operations are supported on
.Pa /dev/flash :
.Bl -tag -width indent
.It Dv FLASH_ERASE_BLOCK (struct flash_erase_params)
This command erases one or more blocks.
.It Dv FLASH_DUMP (struct flash_dump_params)
This command dumps a block.
.It Dv FLASH_GET_INFO (struct flash_info_params)
This command aquires information aboout the flash device.
.It Dv FLASH_BLOCK_ISBAD (struct flash_badblock_params)
This command checks if a block is marked as bad.
.It Dv FLASH_BLOCK_MARKBAD (struct flash_badblock_params)
This command marks a block as bad.
.El
.Sh FILES
.Bl -tag -width /dev/flash -compact
.It Pa /dev/flash
.El
.Sh SEE ALSO
.Xr flash 9
.Xr nand 9
.Sh AUTHORS
.An Adam Hoka Aq ahoka@NetBSD.org

View File

@ -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 \

84
share/man/man9/flash.9 Normal file
View File

@ -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 <ahoka@NetBSD.org>
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by the Department of Software Engineering, University of Szeged, Hungary
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd January 21, 2010
.Dt FLASH 9
.Os
.Sh NAME
.Nm flash
.Nd subsystem for flash-like memory devices
.Sh SYNOPSIS
.In dev/flash/flash.h
.Ft device_t
.Fn flash_attach_mi "const struct flash_interface *fl" "device_t dev"
.Sh DESCRIPTION
Flash-like devices can register themselves to the
.Nm
layer with the
.Fa flash_hw_if
structure.
This structure has function pointers and and other fields.
.Pp
The attachment can be done by calling
.Fa flash_attach_mi
with this structure and the device's device_t as argument.
Return value is the flash layer device.
The
.Fa flash_interface
struct is shown below.
.Bd -literal
struct flash_interface {
int (*erase) (device_t, struct flash_erase_instruction *);
int (*read) (device_t, off_t, size_t, size_t *, uint8_t *);
int (*write) (device_t, off_t, size_t, size_t *, const uint8_t *);
int (*block_markbad)(device_t, uint64_t);
int (*block_isbad)(device_t, uint64_t);
int (*sync) (device_t);
int (*submit)(device_t, struct buf *);
/* storage for partition info */
struct flash_partition partition;
/* total size of mtd */
flash_addr_t size;
uint32_t page_size;
uint32_t erasesize;
uint32_t writesize;
uint32_t minor;
uint8_t type;
};
.Ed
.Sh SEE ALSO
.Xr flash 4
.Xr nand 9
.Sh AUTHORS
.An Adam Hoka Aq ahoka@NetBSD.org

View File

@ -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

View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Device driver for the NAND controller found in Texas Instruments OMAP2
* and later SOCs.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/cdefs.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <arch/arm/omap/omap2_gpmcvar.h>
#include <arch/arm/omap/omap2_gpmcreg.h>
#include <dev/nand/nand.h>
#include <dev/nand/onfi.h>
/* GPMC_STATUS */
#define WAIT0 __BIT(8) /* active low */
/* GPMC_ECC_CONTROL */
#define ECCCLEAR __BIT(8)
#define ECCPOINTER __BITS(3,0)
/* GPMC_ECC_CONFIG */
#define ECCALGORITHM __BIT(16)
#define ECCCS __BITS(3,1)
#define ECC16B __BIT(7)
#define ECCENABLE __BIT(0)
/* GPMC_ECC_SIZE_CONFIG */
#define ECCSIZE1 __BITS(29,22)
/* GPMC_CONFIG1_i */
#define DEVICETYPE __BITS(11,10)
#define DEVICESIZE __BITS(13,12)
#define MASKEDINT(mask, integer) ((integer) << (ffs(mask) - 1) & mask)
/* NAND status register */
#define NAND_WP_BIT __BIT(4)
static int omap2_nand_match(struct device *, struct cfdata *, void *);
static void omap2_nand_attach(struct device *, struct device *, void *);
static int omap2_nand_detach(device_t, int);
void omap2_nand_command(device_t self, uint8_t command);
void omap2_nand_address(device_t self, uint8_t address);
void omap2_nand_busy(device_t self);
void omap2_nand_read_byte(device_t self, uint8_t *data);
void omap2_nand_write_byte(device_t self, uint8_t data);
void omap2_nand_read_word(device_t self, uint16_t *data);
void omap2_nand_write_word(device_t self, uint16_t data);
bool omap2_nand_isbusy(device_t self);
void omap2_nand_read_buf_byte(device_t self, void *buf, size_t len);
void omap2_nand_read_buf_word(device_t self, void *buf, size_t len);
void omap2_nand_write_buf_byte(device_t self, const void *buf, size_t len);
void omap2_nand_write_buf_word(device_t self, const void *buf, size_t len);
int omap2_nand_ecc_init(device_t self);
int omap2_nand_ecc_prepare(device_t self, int mode);
int omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc);
int omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc,
const uint8_t *calcecc);
struct omap2_nand_softc {
device_t sc_dev;
device_t sc_nanddev;
struct gpmc_softc *sc_gpmcsc;
int sc_cs;
int sc_buswidth; /* 0: 8bit, 1: 16bit */
struct nand_interface sc_nand_if;
bus_space_handle_t sc_ioh;
bus_space_tag_t sc_iot;
bus_size_t sc_cmd_reg;
bus_size_t sc_addr_reg;
bus_size_t sc_data_reg;
};
CFATTACH_DECL_NEW(omapnand, sizeof(struct omap2_nand_softc), omap2_nand_match,
omap2_nand_attach, omap2_nand_detach, NULL);
void
omap2_nand_command(device_t self, uint8_t command)
{
struct omap2_nand_softc *sc = device_private(self);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, command);
};
void
omap2_nand_address(device_t self, uint8_t address)
{
struct omap2_nand_softc *sc = device_private(self);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_addr_reg, address);
};
bool
omap2_nand_isbusy(device_t self)
{
struct omap2_nand_softc *sc = device_private(self);
uint8_t status;
DELAY(1); /* just to be sure we are not early */
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
sc->sc_cmd_reg, ONFI_READ_STATUS);
DELAY(1);
status = bus_space_read_1(sc->sc_iot,
sc->sc_ioh, sc->sc_data_reg);
return !(status & ONFI_STATUS_RDY);
};
static int
omap2_nand_match(struct device *parent, struct cfdata *match, void *aux)
{
struct gpmc_attach_args *gpmc = aux;
bus_space_tag_t iot;
bus_space_handle_t ioh;
bus_size_t cs_offset;
uint32_t result;
int ret = 0;
iot = gpmc->gpmc_iot;
cs_offset = GPMC_CS_CONFIG_BASE(gpmc->gpmc_cs);
/* map i/o space */
if (bus_space_map(iot, cs_offset, GPMC_CS_SIZE, 0, &ioh) != 0) {
aprint_error("omap2_nand_match: can't map i/o space");
return 1;
}
/* read GPMC_CONFIG1_i */
result = bus_space_read_4(iot, ioh, GPMC_CONFIG1_i);
/* check if memory device is NAND type */
if ((result & DEVICETYPE) == MASKEDINT(DEVICETYPE, 0x02)) {
/* we got NAND, report positive match */
ret = 1;
}
bus_space_unmap(iot, ioh, GPMC_CS_SIZE);
return ret;
}
static void
omap2_nand_attach(device_t parent, device_t self, void *aux)
{
struct omap2_nand_softc *sc = device_private(self);
sc->sc_gpmcsc = device_private(parent);
struct gpmc_attach_args *gpmc = aux;
bus_size_t cs_offset;
uint32_t val;
aprint_normal("\n");
sc->sc_iot = gpmc->gpmc_iot;
sc->sc_dev = self;
sc->sc_cs = gpmc->gpmc_cs;
// cs_offset = GPMC_BASE + GPMC_CONFIG1_0 + sc->sc_cs * GPMC_CS_SIZE;
cs_offset = GPMC_CS_CONFIG_BASE(sc->sc_cs);
/* map i/o space */
if (bus_space_map(sc->sc_iot, cs_offset, GPMC_CS_SIZE, 0,
&sc->sc_ioh) != 0) {
aprint_error(": omap2_nand_attach: can't map i/o space");
return;
}
sc->sc_cmd_reg = GPMC_NAND_COMMAND_0 - GPMC_CONFIG1_0;
sc->sc_addr_reg = GPMC_NAND_ADDRESS_0 - GPMC_CONFIG1_0;
sc->sc_data_reg = GPMC_NAND_DATA_0 - GPMC_CONFIG1_0;
/* turn off write protection if enabled */
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_CONFIG);
val |= NAND_WP_BIT;
gpmc_register_write(sc->sc_gpmcsc, GPMC_CONFIG, val);
/*
* do the reset dance for NAND
*/
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
sc->sc_cmd_reg, ONFI_RESET);
omap2_nand_busy(self);
/* read GPMC_CONFIG1_i to get buswidth */
val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPMC_CONFIG1_i);
if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x01)) {
/* 16bit */
sc->sc_buswidth = 1;
} else if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x00)) {
/* 8bit */
sc->sc_buswidth = 0;
} else {
panic("invalid buswidth reported by config1");
}
sc->sc_nand_if.select = &nand_default_select;
sc->sc_nand_if.command = &omap2_nand_command;
sc->sc_nand_if.address = &omap2_nand_address;
sc->sc_nand_if.read_buf_byte = &omap2_nand_read_buf_byte;
sc->sc_nand_if.read_buf_word = &omap2_nand_read_buf_word;
sc->sc_nand_if.read_byte = &omap2_nand_read_byte;
sc->sc_nand_if.read_word = &omap2_nand_read_word;
sc->sc_nand_if.write_buf_byte = &omap2_nand_write_buf_byte;
sc->sc_nand_if.write_buf_word = &omap2_nand_write_buf_word;
sc->sc_nand_if.write_byte = &omap2_nand_write_byte;
sc->sc_nand_if.write_word = &omap2_nand_write_word;
sc->sc_nand_if.busy = &omap2_nand_busy;
#ifdef OMAP2_NAND_HARDWARE_ECC
omap2_nand_ecc_init(self);
sc->sc_nand_if.ecc_compute = &omap2_nand_ecc_compute;
sc->sc_nand_if.ecc_correct = &omap2_nand_ecc_correct;
sc->sc_nand_if.ecc_prepare = &omap2_nand_ecc_prepare;
sc->sc_nand_if.ecc.necc_code_size = 3;
sc->sc_nand_if.ecc.necc_block_size = 512;
sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_HW;
#else
sc->sc_nand_if.ecc_compute = &nand_default_ecc_compute;
sc->sc_nand_if.ecc_correct = &nand_default_ecc_correct;
sc->sc_nand_if.ecc_prepare = NULL;
sc->sc_nand_if.ecc.necc_code_size = 3;
sc->sc_nand_if.ecc.necc_block_size = 256;
sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_SW;
#endif /* OMAP2_NAND_HARDWARE_ECC */
if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL))
aprint_error_dev(sc->sc_dev,
"couldn't establish power handler\n");
sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev);
}
static int
omap2_nand_detach(device_t device, int flags)
{
struct omap2_nand_softc *sc = device_private(device);
int ret = 0;
bus_space_unmap(sc->sc_iot, sc->sc_ioh, GPMC_CS_SIZE);
pmf_device_deregister(sc->sc_dev);
if (sc->sc_nanddev != NULL)
ret = config_detach(sc->sc_nanddev, flags);
return ret;
}
void
omap2_nand_busy(device_t self)
{
struct omap2_nand_softc *sc = device_private(self);
while (!(gpmc_register_read(sc->sc_gpmcsc, GPMC_STATUS) & WAIT0)) {
DELAY(1);
}
}
void
omap2_nand_read_byte(device_t self, uint8_t *data)
{
struct omap2_nand_softc *sc = device_private(self);
*data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg);
}
void
omap2_nand_write_byte(device_t self, uint8_t data)
{
struct omap2_nand_softc *sc = device_private(self);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data);
}
void
omap2_nand_read_word(device_t self, uint16_t *data)
{
struct omap2_nand_softc *sc = device_private(self);
*data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg);
}
void
omap2_nand_write_word(device_t self, uint16_t data)
{
struct omap2_nand_softc *sc = device_private(self);
bus_space_write_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data);
}
void
omap2_nand_read_buf_byte(device_t self, void *buf, size_t len)
{
struct omap2_nand_softc *sc = device_private(self);
KASSERT(buf != NULL);
KASSERT(len >= 1);
bus_space_read_multi_1(sc->sc_iot, sc->sc_ioh,
sc->sc_data_reg, buf, len);
}
void
omap2_nand_read_buf_word(device_t self, void *buf, size_t len)
{
struct omap2_nand_softc *sc = device_private(self);
KASSERT(buf != NULL);
KASSERT(len >= 2);
KASSERT(!(len & 0x01));
bus_space_read_multi_2(sc->sc_iot, sc->sc_ioh,
sc->sc_data_reg, buf, len / 2);
}
void
omap2_nand_write_buf_byte(device_t self, const void *buf, size_t len)
{
struct omap2_nand_softc *sc = device_private(self);
KASSERT(buf != NULL);
KASSERT(len >= 1);
bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh,
sc->sc_data_reg, buf, len);
}
void
omap2_nand_write_buf_word(device_t self, const void *buf, size_t len)
{
struct omap2_nand_softc *sc = device_private(self);
KASSERT(buf != NULL);
KASSERT(len >= 2);
KASSERT(!(len & 0x01));
bus_space_write_multi_2(sc->sc_iot, sc->sc_ioh,
sc->sc_data_reg, buf, len / 2);
}
static uint32_t
convert_ecc(const uint8_t *ecc)
{
return ecc[0] | (ecc[1] << 16) | ((ecc[2] & 0xf0) << 20) |
((ecc[2] & 0x0f) << 8);
}
int
omap2_nand_ecc_init(device_t self)
{
struct omap2_nand_softc *sc = device_private(self);
uint32_t val;
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL);
/* clear ecc, select ecc register 1 */
val &= ~ECCPOINTER;
val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1);
gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val);
/* XXX too many MAGIC */
/* set ecc size to 512, set all regs to eccsize1*/
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_SIZE_CONFIG);
val &= ~ECCSIZE1;
val |= MASKEDINT(ECCSIZE1, 512) | 0x0f;
gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val);
return 0;
}
int
omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc)
{
struct omap2_nand_softc *sc = device_private(self);
uint32_t val;
/* read ecc result register */
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC1_RESULT);
ecc[0] = val & 0xff;
ecc[1] = (val >> 16) & 0xff;
ecc[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
/* disable ecc engine */
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG);
val &= ~ECCENABLE;
gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val);
return 0;
}
int
omap2_nand_ecc_prepare(device_t self, int mode)
{
struct omap2_nand_softc *sc = device_private(self);
uint32_t val;
/* same for read/write */
switch (mode) {
case NAND_ECC_READ:
case NAND_ECC_WRITE:
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL);
/* clear ecc, select ecc register 1 */
val &= ~ECCPOINTER;
val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1);
gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val);
val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG);
val &= ~ECCCS;
val |= ECCENABLE | MASKEDINT(ECCCS, sc->sc_cs);
if (sc->sc_buswidth == 1)
val |= ECC16B;
else
val &= ~ECC16B;
gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val);
break;
default:
aprint_error_dev(self, "invalid i/o mode for ecc prepare\n");
return -1;
}
return 0;
}
int
omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc,
const uint8_t *calcecc)
{
uint32_t oecc, cecc, xor;
uint16_t parity, offset;
uint8_t bit;
oecc = convert_ecc(oldecc);
cecc = convert_ecc(calcecc);
/* get the difference */
xor = oecc ^ cecc;
/* the data was correct if all bits are zero */
if (xor == 0x00)
return NAND_ECC_OK;
switch (popcount32(xor)) {
case 12:
/* single byte error */
parity = xor >> 16;
bit = (parity & 0x07);
offset = (parity >> 3) & 0x01ff;
/* correct bit */
data[offset] ^= (0x01 << bit);
return NAND_ECC_CORRECTED;
case 1:
return NAND_ECC_INVALID;
default:
/* erased page! */
if ((oecc == 0x0fff0fff) && (cecc == 0x00000000))
return NAND_ECC_OK;
return NAND_ECC_TWOBIT;
}
}

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

695
sys/dev/flash/flash.c Normal file
View File

@ -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 <ahoka@NetBSD.org>
* Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*-
* Framework for storage devices based on Flash technology
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/atomic.h>
#include <sys/buf.h>
#include <sys/bufq.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/malloc.h>
#include <sys/reboot.h>
#include <sys/flashio.h>
#include "flash.h"
#define FLASH_DEBUG 1
#ifdef FLASH_DEBUG
#define DPRINTF(x) if (flashdebug) printf x
#define DPRINTFN(n,x) if (flashdebug>(n)) printf x
int flashdebug = FLASH_DEBUG;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
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;
}
}

168
sys/dev/flash/flash.h Normal file
View File

@ -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 <ahoka@NetBSD.org>
* Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _FLASH_H_
#define _FLASH_H_
#include <sys/param.h>
#include <sys/device.h>
#include <sys/module.h>
#include <sys/buf.h>
#include <sys/flashio.h>
/**
* flash_softc - private structure for flash layer driver
*/
struct flash_softc {
device_t sc_dev;
device_t sc_parent_dev; /* Hardware (parent) device */
void *hw_softc; /* Hardware device private softc */
struct flash_interface *flash_if; /* Hardware interface */
bool sc_readonly; /* read only flash device */
};
struct flash_attach_args {
struct flash_interface *flash_if; /* Hardware interface */
};
device_t flash_attach_mi(struct flash_interface *, device_t);
const struct flash_interface *flash_get_interface(dev_t);
const struct flash_softc *flash_get_softc(dev_t);
device_t flash_get_device(dev_t);
/**
* struct erase_instruction - instructions to erase a flash eraseblock
* @fd: flash descriptor
* @addr: start address of the erase operation
* @len: the erase length
* @callback: callback operation, called when erase finished
* @priv: private data
* @state: the erase operation's result
*/
struct flash_erase_instruction {
flash_addr_t ei_addr;
flash_addr_t ei_len;
void (*ei_callback) (struct flash_erase_instruction *);
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_ */

16
sys/dev/nand/files.nand Normal file
View File

@ -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

232
sys/dev/nand/hamming.c Normal file
View File

@ -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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hamming.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $");
#include <sys/param.h>
#include <lib/libkern/libkern.h>
#include "hamming.h"
/**
* Calculates the 22-bit hamming code for a 256-bytes block of data.
* \param data Data buffer to calculate code for.
* \param code Pointer to a buffer where the code should be stored.
*/
void
hamming_compute_256(const uint8_t *data, uint8_t *code)
{
unsigned int i;
uint8_t column_sum = 0;
uint8_t even_line_code = 0;
uint8_t odd_line_code = 0;
uint8_t even_column_code = 0;
uint8_t odd_column_code = 0;
/*-
* Xor all bytes together to get the column sum;
* At the same time, calculate the even and odd line codes
*/
for (i = 0; i < 256; i++) {
column_sum ^= data[i];
/*-
* If the xor sum of the byte is 0, then this byte has no
* incidence on the computed code; so check if the sum is 1.
*/
if ((popcount(data[i]) & 1) == 1) {
/*-
* Parity groups are formed by forcing a particular
* index bit to 0 (even) or 1 (odd).
* Example on one byte:
*
* bits (dec) 7 6 5 4 3 2 1 0
* (bin) 111 110 101 100 011 010 001 000
* '---'---'---'----------.
* |
* groups P4' ooooooooooooooo eeeeeeeeeeeeeee P4 |
* P2' ooooooo eeeeeee ooooooo eeeeeee P2 |
* P1' ooo eee ooo eee ooo eee ooo eee P1 |
* |
* We can see that: |
* - P4 -> bit 2 of index is 0 --------------------'
* - P4' -> bit 2 of index is 1.
* - P2 -> bit 1 of index if 0.
* - etc...
* We deduce that a bit position has an impact on all
* even Px if the log2(x)nth bit of its index is 0
* ex: log2(4) = 2,
* bit2 of the index must be 0 (-> 0 1 2 3)
* and on all odd Px' if the log2(x)nth bit
* of its index is 1
* ex: log2(2) = 1,
* bit1 of the index must be 1 (-> 0 1 4 5)
*
* As such, we calculate all the possible Px and Px'
* values at the same time in two variables,
* even_line_code and odd_line_code, such as
* even_line_code bits: P128 P64 P32
* P16 P8 P4 P2 P1
* odd_line_code bits: P128' P64' P32' P16'
* P8' P4' P2' P1'
*/
even_line_code ^= (255 - i);
odd_line_code ^= i;
}
}
/*-
* At this point, we have the line parities, and the column sum.
* First, We must caculate the parity group values on the column sum.
*/
for (i = 0; i < 8; i++) {
if (column_sum & 1) {
even_column_code ^= (7 - i);
odd_column_code ^= i;
}
column_sum >>= 1;
}
/*-
* Now, we must interleave the parity values,
* to obtain the following layout:
* Code[0] = Line1
* Code[1] = Line2
* Code[2] = Column
* Line = Px' Px P(x-1)- P(x-1) ...
* Column = P4' P4 P2' P2 P1' P1 PadBit PadBit
*/
code[0] = 0;
code[1] = 0;
code[2] = 0;
for (i = 0; i < 4; i++) {
code[0] <<= 2;
code[1] <<= 2;
code[2] <<= 2;
/* Line 1 */
if ((odd_line_code & 0x80) != 0) {
code[0] |= 2;
}
if ((even_line_code & 0x80) != 0) {
code[0] |= 1;
}
/* Line 2 */
if ((odd_line_code & 0x08) != 0) {
code[1] |= 2;
}
if ((even_line_code & 0x08) != 0) {
code[1] |= 1;
}
/* Column */
if ((odd_column_code & 0x04) != 0) {
code[2] |= 2;
}
if ((even_column_code & 0x04) != 0) {
code[2] |= 1;
}
odd_line_code <<= 1;
even_line_code <<= 1;
odd_column_code <<= 1;
even_column_code <<= 1;
}
/* Invert codes (linux compatibility) */
code[0] = ~code[0];
code[1] = ~code[1];
code[2] = ~code[2];
}
/**
* Verifies and corrects a 256-bytes block of data using the given 22-bits
* hamming code.
* Returns 0 if there is no error, otherwise returns a HAMMING_ERROR code.
* param data Data buffer to check.
* \param original_code Hamming code to use for verifying the data.
*/
uint8_t
hamming_correct_256(uint8_t *data, const uint8_t *original_code,
const uint8_t *computed_code)
{
/* Calculate new code */
/* we allocate 4 bytes so we can use popcount32 in one step */
uint8_t correction_code[4];
/* this byte should remain zero all the time */
correction_code[3] = 0;
/* Xor both codes together */
correction_code[0] = computed_code[0] ^ original_code[0];
correction_code[1] = computed_code[1] ^ original_code[1];
correction_code[2] = computed_code[2] ^ original_code[2];
/* If all bytes are 0, there is no error */
if (*(uint32_t *)correction_code == 0) {
return 0;
}
/* If there is a single bit error, there are 11 bits set to 1 */
if (popcount32(*(uint32_t *)correction_code) == 11) {
/* Get byte and bit indexes */
uint8_t byte = correction_code[0] & 0x80;
byte |= (correction_code[0] << 1) & 0x40;
byte |= (correction_code[0] << 2) & 0x20;
byte |= (correction_code[0] << 3) & 0x10;
byte |= (correction_code[1] >> 4) & 0x08;
byte |= (correction_code[1] >> 3) & 0x04;
byte |= (correction_code[1] >> 2) & 0x02;
byte |= (correction_code[1] >> 1) & 0x01;
uint8_t bit = (correction_code[2] >> 5) & 0x04;
bit |= (correction_code[2] >> 4) & 0x02;
bit |= (correction_code[2] >> 3) & 0x01;
/* Correct bit */
data[byte] ^= (1 << bit);
return HAMMING_ERROR_SINGLEBIT;
}
/* Check if ECC has been corrupted */
if (popcount32(*(uint32_t *)correction_code) == 1) {
return HAMMING_ERROR_ECC;
} else {
/* Otherwise, this is a multi-bit error */
return HAMMING_ERROR_MULTIPLEBITS;
}
}

56
sys/dev/nand/hamming.h Normal file
View File

@ -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 */

1478
sys/dev/nand/nand.c Normal file

File diff suppressed because it is too large Load Diff

465
sys/dev/nand/nand.h Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _NAND_H_
#define _NAND_H_
#include <sys/param.h>
#include <sys/cdefs.h>
#include <sys/bufq.h>
#include <sys/buf.h>
#include <sys/time.h>
#include <dev/flash/flash.h>
/* flash interface implementation */
int nand_flash_isbad(device_t, uint64_t);
int nand_flash_markbad(device_t, uint64_t);
int nand_flash_write(device_t, off_t, size_t, size_t *, const u_char *);
int nand_flash_read(device_t, off_t, size_t, size_t *, uint8_t *);
int nand_flash_erase(device_t, struct flash_erase_instruction *);
/* nand specific functions */
int nand_erase_block(device_t, size_t);
int nand_io_submit(device_t, struct buf *);
void nand_sync_thread(void *);
int nand_sync_thread_start(device_t);
void nand_sync_thread_stop(device_t);
bool nand_isfactorybad(device_t, flash_addr_t);
bool nand_iswornoutbad(device_t, flash_addr_t);
bool nand_isbad(device_t, flash_addr_t);
void nand_markbad(device_t, size_t);
int nand_read_page(device_t, size_t, uint8_t *);
int nand_read_oob(device_t self, size_t page, void *oob);
/*
* default functions for driver development
*/
void nand_default_select(device_t, bool);
int nand_default_ecc_compute(device_t, const uint8_t *, uint8_t *);
int nand_default_ecc_correct(device_t, uint8_t *, const uint8_t *,
const uint8_t *);
static inline void nand_busy(device_t);
static inline void nand_select(device_t, bool);
static inline void nand_command(device_t, uint8_t);
static inline void nand_address(device_t, uint32_t);
static inline void nand_read_buf_byte(device_t, void *, size_t);
static inline void nand_read_buf_word(device_t, void *, size_t);
static inline void nand_read_byte(device_t, uint8_t *);
static inline void nand_write_buf_byte(device_t, const void *, size_t);
static inline void nand_write_buf_word(device_t, const void *, size_t);
//static inline bool nand_block_isbad(device_t, off_t);
//static inline void nand_block_markbad(device_t, off_t);
//static inline bool nand_isbusy(device_t);
//#define NAND_DEBUG 1
#ifdef NAND_DEBUG
#define DPRINTF(x) if (nanddebug) printf x
#define DPRINTFN(n,x) if (nanddebug>(n)) printf x
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define NAND_VERBOSE
/* same as in linux for compatibility */
enum {
NAND_BAD_MARKER_OFFSET = 0,
NAND_BAD_MARKER_OFFSET_SMALL = 5
};
/* feature flags use in nc_flags */
enum {
NC_BUSWIDTH_16 = (1<<0),
NC_SOURCE_SYNC = (1<<2),
NC_INTERLEAVED_PE = (1<<1),
NC_INTERLEAVED_R = (1<<3),
NC_EXTENDED_PARAM = (1<<4)
};
/* various quirks used in nc_quirks */
enum {
NC_QUIRK_NO_READ_START = (1<<0)
};
enum {
NAND_ECC_READ,
NAND_ECC_WRITE
};
enum {
NAND_ECC_OK,
NAND_ECC_CORRECTED,
NAND_ECC_INVALID,
NAND_ECC_TWOBIT
};
enum {
NAND_ECC_TYPE_HW,
NAND_ECC_TYPE_SW
};
struct nand_bbt {
uint8_t *nbbt_bitmap;
size_t nbbt_size;
};
struct nand_ecc {
size_t necc_offset; /* offset of ecc data in oob */
size_t necc_size; /* size of ecc data in oob */
size_t necc_block_size; /* block size used in ecc calc */
size_t necc_code_size; /* reduntant bytes per block */
int necc_steps; /* pagesize / code size */
int necc_type; /* type of the ecc engine */
};
/**
* nand_chip: structure containing the required information
* about the NAND chip.
*/
struct nand_chip {
uint8_t *nc_oob_cache; /* buffer for oob cache */
uint8_t *nc_page_cache; /* buffer for page cache */
uint8_t *nc_ecc_cache;
size_t nc_size; /* storage size in bytes */
size_t nc_page_size; /* page size in bytes */
size_t nc_block_pages; /* block size in pages */
size_t nc_block_size; /* block size in bytes */
size_t nc_spare_size; /* spare (oob) size in bytes */
uint32_t nc_flags; /* bitfield flags */
uint32_t nc_quirks; /* bitfield quirks */
unsigned int nc_page_shift; /* page shift for page alignment */
unsigned int nc_page_mask; /* page mask for page alignment */
unsigned int nc_block_shift; /* write shift */
unsigned int nc_block_mask; /* write mask */
uint8_t nc_manf_id; /* manufacturer id */
uint8_t nc_dev_id; /* device id */
uint8_t nc_addr_cycles_row; /* row cycles for addressing */
uint8_t nc_addr_cycles_column; /* column cycles for addressing */
uint8_t nc_badmarker_offs; /* offset for marking bad blocks */
struct nand_ecc *nc_ecc;
};
struct nand_write_cache {
struct bintime nwc_creation;
struct bintime nwc_last_write;
struct bufq_state *nwc_bufq;
uint8_t *nwc_data;
daddr_t nwc_block;
kmutex_t nwc_lock;
bool nwc_write_pending;
};
/* driver softc for nand */
struct nand_softc {
device_t sc_dev;
device_t nand_dev;
struct nand_interface *nand_if;
void *nand_softc;
struct nand_chip sc_chip;
struct nand_bbt sc_bbt;
size_t sc_part_offset;
size_t sc_part_size;
kmutex_t sc_device_lock; /* serialize access to chip */
/* for the i/o thread */
struct lwp *sc_sync_thread;
struct nand_write_cache sc_cache;
kmutex_t sc_io_lock;
kmutex_t sc_waitq_lock;
kcondvar_t sc_io_cv;
bool sc_io_running;
};
/* structure holding the nand api */
struct nand_interface
{
void (*select) (device_t, bool);
void (*command) (device_t, uint8_t);
void (*address) (device_t, uint8_t);
void (*read_buf_byte) (device_t, void *, size_t);
void (*read_buf_word) (device_t, void *, size_t);
void (*read_byte) (device_t, uint8_t *);
void (*read_word) (device_t, uint16_t *);
void (*write_buf_byte) (device_t, const void *, size_t);
void (*write_buf_word) (device_t, const void *, size_t);
void (*write_byte) (device_t, uint8_t);
void (*write_word) (device_t, uint16_t);
void (*busy) (device_t);
/* functions specific to ecc computation */
int (*ecc_prepare)(device_t, int);
int (*ecc_compute)(device_t, const uint8_t *, uint8_t *);
int (*ecc_correct)(device_t, uint8_t *, const uint8_t *,
const uint8_t *);
struct nand_ecc ecc;
/* flash partition information */
const struct flash_partition *part_info;
int part_num;
};
/* attach args */
struct nand_attach_args {
struct nand_interface *naa_nand_if;
};
device_t nand_attach_mi(struct nand_interface *nand_if, device_t dev);
static inline void
nand_busy(device_t device)
{
struct nand_softc *sc = device_private(device);
KASSERT(sc->nand_if->select != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->select(sc->nand_dev, true);
if (sc->nand_if->busy != NULL) {
sc->nand_if->busy(sc->nand_dev);
}
sc->nand_if->select(sc->nand_dev, false);
}
static inline void
nand_select(device_t self, bool enable)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->select != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->select(sc->nand_dev, enable);
}
static inline void
nand_address(device_t self, uint32_t address)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->address != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->address(sc->nand_dev, address);
}
static inline void
nand_command(device_t self, uint8_t command)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->command != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->command(sc->nand_dev, command);
}
static inline void
nand_read_byte(device_t self, uint8_t *data)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->read_byte != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->read_byte(sc->nand_dev, data);
}
static inline void
nand_write_byte(device_t self, uint8_t data)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->write_byte != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->write_byte(sc->nand_dev, data);
}
static inline void
nand_read_word(device_t self, uint16_t *data)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->read_word != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->read_word(sc->nand_dev, data);
}
static inline void
nand_write_word(device_t self, uint16_t data)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->write_word != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->write_word(sc->nand_dev, data);
}
static inline void
nand_read_buf_byte(device_t self, void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->read_buf_byte != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->read_buf_byte(sc->nand_dev, buf, size);
}
static inline void
nand_read_buf_word(device_t self, void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->read_buf_word != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->read_buf_word(sc->nand_dev, buf, size);
}
static inline void
nand_write_buf_byte(device_t self, const void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->write_buf_byte != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->write_buf_byte(sc->nand_dev, buf, size);
}
static inline void
nand_write_buf_word(device_t self, const void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->write_buf_word != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->write_buf_word(sc->nand_dev, buf, size);
}
static inline int
nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldcode,
const uint8_t *newcode)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->ecc_correct != NULL);
KASSERT(sc->nand_dev != NULL);
return sc->nand_if->ecc_correct(sc->nand_dev, data, oldcode, newcode);
}
static inline void
nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->ecc_compute != NULL);
KASSERT(sc->nand_dev != NULL);
sc->nand_if->ecc_compute(sc->nand_dev, data, code);
}
static inline void
nand_ecc_prepare(device_t self, int mode)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_dev != NULL);
if (sc->nand_if->ecc_prepare != NULL)
sc->nand_if->ecc_prepare(sc->nand_dev, mode);
}
#if 0
static inline bool
nand_block_isbad(device_t self, off_t block)
{
struct nand_softc *sc = device_private(self);
KASSERT(sc->nand_if->block_isbad != NULL);
KASSERT(sc->nand_dev != NULL);
return sc->nand_if->block_isbad(sc->nand_dev, block);
}
#endif
/* Manufacturer IDs defined by JEDEC */
enum {
NAND_MFR_UNKNOWN = 0x00,
NAND_MFR_AMD = 0x01,
NAND_MFR_FUJITSU = 0x04,
NAND_MFR_RENESAS = 0x07,
NAND_MFR_STMICRO = 0x20,
NAND_MFR_MICRON = 0x2c,
NAND_MFR_NATIONAL = 0x8f,
NAND_MFR_TOSHIBA = 0x98,
NAND_MFR_HYNIX = 0xad,
NAND_MFR_SAMSUNG = 0xec
};
struct nand_manufacturer {
int id;
const char *name;
};
extern const struct nand_manufacturer nand_mfrs[];
static inline void
nand_dump_data(const char *name, void *data, size_t len)
{
printf("dumping %s\n--------------\n", name);
uint8_t *dump = data;
for (int i = 0; i < len; i++) {
printf("0x%.2hhx ", *dump);
dump++;
}
printf("\n--------------\n");
}
#endif /* _NAND_H_ */

250
sys/dev/nand/nand_bbt.c Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Support for Bad Block Tables (BBTs) */
#include <sys/param.h>
#include <sys/kmem.h>
#include "nand.h"
#include "nand_bbt.h"
void
nand_bbt_init(device_t self)
{
struct nand_softc *sc = device_private(self);
struct nand_chip *chip = &sc->sc_chip;
struct nand_bbt *bbt = &sc->sc_bbt;
bbt->nbbt_size = chip->nc_size / chip->nc_block_size / 4;
bbt->nbbt_bitmap = kmem_alloc(bbt->nbbt_size, KM_SLEEP);
memset(bbt->nbbt_bitmap, 0xff, bbt->nbbt_size);
}
void
nand_bbt_detach(device_t self)
{
struct nand_softc *sc = device_private(self);
struct nand_bbt *bbt = &sc->sc_bbt;
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;
}

58
sys/dev/nand/nand_bbt.h Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _NAND_BBT_H_
#define _NAND_BBT_H_
enum {
NAND_BBT_MARKER_FACTORY_BAD = 0x00, /* 00 */
NAND_BBT_MARKER_WORNOUT_BAD = 0x01, /* 01 */
NAND_BBT_MARKER_RESERVED = 0x02, /* 10 */
NAND_BBT_MARKER_GOOD = 0x03 /* 11 */
};
enum {
NAND_BBT_OFFSET = 0x0e
};
void nand_bbt_init(device_t);
void nand_bbt_detach(device_t);
void nand_bbt_scan(device_t);
bool nand_bbt_update(device_t);
bool nand_bbt_load(device_t);
void nand_bbt_block_mark(device_t, flash_addr_t, uint8_t);
void nand_bbt_block_markbad(device_t, flash_addr_t);
void nand_bbt_block_markfactorybad(device_t, flash_addr_t);
bool nand_bbt_block_isbad(device_t, flash_addr_t);
#endif

63
sys/dev/nand/nand_crc.c Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Implements CRC-16 as required by the ONFI 2.3 specification */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: 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;
}

42
sys/dev/nand/nand_crc.h Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _NAND_CRC_H_
#define _NAND_CRC_H_
#include <sys/param.h>
#include <sys/types.h>
uint16_t nand_crc16(uint8_t *, size_t);
#endif

373
sys/dev/nand/nand_io.c Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Inspired by the similar code in the NetBSD SPI driver, but I
* decided to do a rewrite from scratch to be suitable for NAND.
*/
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/bufq.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/kthread.h>
#include <sys/mutex.h>
#include <dev/flash/flash.h>
#include "nand.h"
extern int nanddebug;
extern int nand_cachesync_timeout;
void nand_io_read(device_t, struct buf *);
void nand_io_write(device_t, struct buf *);
void nand_io_done(device_t, int error, struct buf *);
int nand_io_cache_write(device_t, daddr_t, struct buf *);
void nand_io_cache_sync(device_t);
static int
nand_timestamp_diff(struct bintime *bt, struct bintime *b2)
{
struct bintime b1 = *bt;
struct timeval tv;
bintime_sub(&b1, b2);
bintime2timeval(&b1, &tv);
return tvtohz(&tv);
}
static daddr_t
nand_io_getblock(device_t self, struct buf *bp)
{
struct nand_softc *sc = device_private(self);
struct nand_chip *chip = &sc->sc_chip;
flash_addr_t block, last;
/* get block number of first byte */
block = bp->b_rawblkno * DEV_BSIZE / chip->nc_block_size;
/* block of the last bite */
last = (bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1)
/ chip->nc_block_size;
/* spans trough multiple blocks, needs special handling */
if (last != block) {
printf("0x%jx -> 0x%jx\n",
bp->b_rawblkno * DEV_BSIZE,
bp->b_rawblkno * DEV_BSIZE + bp->b_resid - 1);
panic("TODO: multiple block write. last: %jd, current: %jd",
(intmax_t )last, (intmax_t )block);
}
return block;
}
int
nand_sync_thread_start(device_t self)
{
struct nand_softc *sc = device_private(self);
struct nand_chip *chip = &sc->sc_chip;
struct nand_write_cache *wc = &sc->sc_cache;
int error;
DPRINTF(("starting nand io thread\n"));
sc->sc_cache.nwc_data = kmem_alloc(chip->nc_block_size, KM_SLEEP);
mutex_init(&sc->sc_io_lock, MUTEX_DEFAULT, IPL_NONE);
mutex_init(&wc->nwc_lock, MUTEX_DEFAULT, IPL_NONE);
cv_init(&sc->sc_io_cv, "nandcv");
error = bufq_alloc(&wc->nwc_bufq, "fcfs", BUFQ_SORT_RAWBLOCK);
if (error)
goto err_bufq;
sc->sc_io_running = true;
wc->nwc_write_pending = false;
/* arrange to allocate the kthread */
error = kthread_create(PRI_NONE, KTHREAD_JOINABLE | 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);
}

779
sys/dev/nand/nandemulator.c Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nandemulator.c,v 1.1 2011/02/26 18:07:31 ahoka Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/kernel.h>
#include "nandemulator.h"
#include <dev/nand/nand.h>
#include <dev/nand/onfi.h>
#include <dev/nand/nand_crc.h>
extern struct cfdriver nandemulator_cd;
void nandemulatorattach(int n);
static int nandemulator_match(device_t, cfdata_t, void *);
static void nandemulator_attach(device_t, device_t, void *);
static int nandemulator_detach(device_t, int);
static void nandemulator_device_reset(device_t);
static void nandemulator_command(device_t, uint8_t);
static void nandemulator_address(device_t, uint8_t);
static void nandemulator_busy(device_t);
static void nandemulator_read_byte(device_t, uint8_t *);
static void nandemulator_write_byte(device_t, uint8_t);
static void nandemulator_read_word(device_t, uint16_t *);
static void nandemulator_write_word(device_t, uint16_t);
static void nandemulator_read_buf_byte(device_t, void *, size_t);
static void nandemulator_read_buf_word(device_t, void *, size_t);
static void nandemulator_write_buf_byte(device_t, const void *, size_t);
static void nandemulator_write_buf_word(device_t, const void *, size_t);
static size_t nandemulator_address_to_page(device_t);
static size_t nandemulator_page_to_backend_offset(device_t, size_t);
static size_t nandemulator_column_address_to_subpage(device_t);
/*
#define NANDEMULATOR_DEBUG 1
#ifdef NANDEMULATOR_DEBUG
#warning debug enabled
#define DPRINTF(x) if (nandemulatordebug) printf x
#define DPRINTFN(n,x) if (nandemulatordebug>(n)) printf x
#else
#error no debug
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#ifdef NANDEMULATOR_DEBUG
int nandemulatordebug = NANDEMULATOR_DEBUG;
#endif
*/
extern int nanddebug;
enum {
NANDEMULATOR_8BIT,
NANDEMULATOR_16BIT
};
struct nandemulator_softc {
device_t sc_dev;
device_t sc_nanddev;
int sc_buswidth;
struct nand_interface sc_nand_if;
uint8_t sc_command;
size_t sc_io_len;
uint8_t *sc_io_pointer;
uint64_t sc_address;
uint8_t *sc_backend;
size_t sc_backend_size;
size_t sc_device_size;
bool sc_register_writable;
uint8_t sc_status_register;
uint8_t sc_ids[2];
uint8_t sc_onfi[4];
size_t sc_page_size;
size_t sc_block_size;
size_t sc_spare_size;
size_t sc_lun_size;
uint8_t sc_row_cycles;
uint8_t sc_column_cycles;
uint64_t sc_row_mask;
int sc_address_counter;
struct onfi_parameter_page *sc_parameter_page;
};
CFATTACH_DECL_NEW(nandemulator, sizeof(struct nandemulator_softc),
nandemulator_match, nandemulator_attach, nandemulator_detach, NULL);
void
nandemulatorattach(int n)
{
int i, err;
cfdata_t cf;
aprint_debug("nandemulator: requested %d units\n", n);
err = config_cfattach_attach(nandemulator_cd.cd_name,
&nandemulator_ca);
if (err) {
aprint_error("%s: couldn't register cfattach: %d\n",
nandemulator_cd.cd_name, err);
config_cfdriver_detach(&nandemulator_cd);
return;
}
for (i = 0; i < n; i++) {
cf = kmem_alloc(sizeof(struct cfdata), KM_NOSLEEP);
if (cf == NULL) {
aprint_error("%s: couldn't allocate cfdata\n",
nandemulator_cd.cd_name);
continue;
}
cf->cf_name = nandemulator_cd.cd_name;
cf->cf_atname = nandemulator_cd.cd_name;
cf->cf_unit = i;
cf->cf_fstate = FSTATE_STAR;
(void)config_attach_pseudo(cf);
}
}
/* ARGSUSED */
static int
nandemulator_match(device_t parent, cfdata_t match, void *aux)
{
/* pseudo device, always attaches */
return 1;
}
static void
nandemulator_attach(device_t parent, device_t self, void *aux)
{
struct nandemulator_softc *sc = device_private(self);
int i;
aprint_normal_dev(self, "NAND emulator\n");
sc->sc_dev = self;
sc->sc_nand_if.select = &nand_default_select;
sc->sc_nand_if.command = &nandemulator_command;
sc->sc_nand_if.address = &nandemulator_address;
sc->sc_nand_if.read_buf_byte = &nandemulator_read_buf_byte;
sc->sc_nand_if.read_buf_word = &nandemulator_read_buf_word;
sc->sc_nand_if.read_byte = &nandemulator_read_byte;
sc->sc_nand_if.read_word = &nandemulator_read_word;
sc->sc_nand_if.write_buf_byte = &nandemulator_write_buf_byte;
sc->sc_nand_if.write_buf_word = &nandemulator_write_buf_word;
sc->sc_nand_if.write_byte = &nandemulator_write_byte;
sc->sc_nand_if.write_word = &nandemulator_write_word;
sc->sc_nand_if.busy = &nandemulator_busy;
sc->sc_nand_if.ecc_compute = &nand_default_ecc_compute;
sc->sc_nand_if.ecc_correct = &nand_default_ecc_correct;
sc->sc_nand_if.ecc_prepare = NULL;
sc->sc_nand_if.ecc.necc_code_size = 3;
sc->sc_nand_if.ecc.necc_block_size = 256;
sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_SW;
if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL))
aprint_error_dev(sc->sc_dev,
"couldn't establish power handler\n");
sc->sc_buswidth = NANDEMULATOR_16BIT; /* 16bit for now */
/* hardcode these now, make it configurable later */
sc->sc_device_size = 32 * 1024 * 1024; /* 32MB */
sc->sc_page_size = 2048;
sc->sc_block_size = 64;
sc->sc_lun_size =
sc->sc_device_size / (sc->sc_page_size * sc->sc_block_size);
KASSERT(sc->sc_device_size %
(sc->sc_page_size * sc->sc_block_size) == 0);
sc->sc_spare_size = 64;
sc->sc_column_cycles = 2;
sc->sc_row_cycles = 3;
/* init the emulator data structures */
sc->sc_backend_size =
sc->sc_device_size +
sc->sc_device_size / sc->sc_page_size * sc->sc_spare_size;
sc->sc_backend = kmem_alloc(sc->sc_backend_size, KM_SLEEP);
memset(sc->sc_backend, 0xff, sc->sc_backend_size);
sc->sc_parameter_page =
kmem_zalloc(sizeof(struct onfi_parameter_page) * 4, KM_SLEEP);
struct onfi_parameter_page *opp;
uint8_t sig[4] = { 'O', 'N', 'F', 'I' };
for (i = 0; i < 4; i++) {
opp = &sc->sc_parameter_page[i];
opp->param_signature = *(uint32_t *)sig;
opp->param_pagesize = sc->sc_page_size;
opp->param_blocksize = sc->sc_block_size;
opp->param_sparesize = sc->sc_spare_size;
opp->param_lunsize = sc->sc_lun_size;
opp->param_numluns = 1;
opp->param_manufacturer_id = 0x00;
memcpy(opp->param_manufacturer,
"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

View File

@ -0,0 +1,5 @@
#ifndef _NANDEMULATOR_H_
#define _NANDEMULATOR_H_
#else
#endif

180
sys/dev/nand/onfi.h Normal file
View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _ONFI_H_
#define _ONFI_H_
/**
* ONFI 2.2, Section 5.1: Command Set
*
* Indented ones are 2nd or 3rd cycle commands.
*/
enum {
ONFI_READ = 0x00, /* M */
ONFI_READ_START = 0x30, /* M */
ONFI_READ_INTERLEAVED = 0x32, /* O */
ONFI_READ_COPYBACK = 0x35, /* O */
ONFI_READ_CACHE_RANDOM = 0x31, /* O */
ONFI_CHANGE_READ_COLUMN = 0x05, /* M */
ONFI_CHANGE_READ_COLUMN_ENHANCED = 0x06, /* O */
ONFI_CHANGE_READ_COLUMN_START = 0xe0, /* M */
ONFI_READ_CACHE_SEQUENTIAL = 0x31, /* O */
ONFI_READ_CACHE_END = 0x3f, /* O */
ONFI_BLOCK_ERASE = 0x60, /* M */
ONFI_BLOCK_ERASE_START = 0xd0, /* M */
ONFI_BLOCK_ERASE_INTERLEAVED = 0xd1, /* O */
ONFI_READ_STATUS = 0x70, /* M */
ONFI_READ_STATUS_ENHANCED = 0x78, /* O */
ONFI_PAGE_PROGRAM = 0x80, /* M */
ONFI_PAGE_PROGRAM_START = 0x10, /* M */
ONFI_PAGE_PROGRAM_INTERLEAVED = 0x11, /* O */
ONFI_PAGE_CACHE_PROGRAM = 0x15, /* O */
ONFI_COPYBACK_PROGRAM = 0x85, /* O */
ONFI_COPYBACK_PROGRAM_START = 0x10, /* O */
ONFI_COPYBACK_PROGRAM_INTERLEAVED = 0x11, /* O */
/*-
* Small Data's first opcode may be 80h if the operation is a program only
* with no data output. For the last second cycle of a Small Data Move,
* it is a 10h command to confirm the Program or Copyback operation
*/
ONFI_SMALL_DATA_MOVE = 0x85, /* O */
ONFI_SMALL_DATA_MOVE_START = 0x11, /* O */
ONFI_CHANGE_WRITE_COLUMN = 0x85, /* M */
ONFI_CHANGE_ROW_ADDRESS = 0x85, /* O */
ONFI_READ_ID = 0x90, /* M */
ONFI_READ_PARAMETER_PAGE = 0xec, /* M */
ONFI_READ_UNIQUE_ID = 0xed, /* O */
ONFI_GET_FEATURES = 0xee, /* O */
ONFI_SET_FEATURES = 0xef, /* O */
ONFI_RESET_LUN = 0xfa, /* O */
ONFI_SYNCHRONOUS_RESET = 0xfc, /* O */
ONFI_RESET = 0xff /* M */
};
/**
* status codes from ONFI_READ_STATUS
*/
enum {
ONFI_STATUS_FAIL = (1<<0),
ONFI_STATUS_FAILC = (1<<1),
ONFI_STATUS_R = (1<<2),
ONFI_STATUS_CSP = (1<<3),
ONFI_STATUS_VSP = (1<<4),
ONFI_STATUS_ARDY = (1<<5),
ONFI_STATUS_RDY = (1<<6),
ONFI_STATUS_WP = (1<<7)
};
enum {
ONFI_FEATURE_16BIT = (1<<0),
ONFI_FEATURE_EXTENDED_PARAM = (1<<7)
};
/* 5.7.1. Parameter Page Data Structure Definition */
struct onfi_parameter_page {
/* Revision information and features block */
uint32_t param_signature; /* M: onfi signature ({'O','N','F','I'}) */
uint16_t param_revision; /* M: revision number */
uint16_t param_features; /* M: features supported */
uint16_t param_optional_cmds; /* M: optional commands */
uint16_t param_reserved_1; /* R: reserved */
uint16_t param_extended_len; /* O: extended parameter page lenght */
uint8_t param_num_param_pg; /* O: number of parameter pages */
uint8_t param_reserved_2[17]; /* R: reserved */
/* Manufacturer information block */
uint8_t param_manufacturer[12]; /* M: device manufacturer (ASCII) */
uint8_t param_model[20]; /* M: device model (ASCII) */
uint8_t param_manufacturer_id; /* M: JEDEC ID of manufacturer */
uint16_t param_date; /* O: date code (BCD) */
uint8_t param_reserved_3[13]; /* R: reserved */
/* Memory organization block */
uint32_t param_pagesize; /* M: number of data bytes per page */
uint16_t param_sparesize; /* M: number of spare bytes per page */
uint32_t param_part_pagesize; /* O: obsolete */
uint16_t param_part_sparesize; /* O: obsolete */
uint32_t param_blocksize; /* M: number of pages per block */
uint32_t param_lunsize; /* M: number of blocks per LUN */
uint8_t param_numluns; /* M: number of LUNs */
uint8_t param_addr_cycles; /* M: address cycles:
col: 4-7 (high), row: 0-3 (low) */
uint8_t param_cellsize; /* M: number of bits per cell */
uint16_t param_lun_maxbad; /* M: maximum badblocks per LUN */
uint16_t param_block_endurance; /* M: block endurance */
uint8_t param_guaranteed_blocks; /* M: guaranteed valid blocks at
begginning of target */
uint16_t param_guaranteed_endurance; /* M: block endurance of
guranteed blocks */
uint8_t param_programs_per_page; /* M: number of programs per page */
uint8_t param_partial_programming_attr; /* O: obsolete */
uint8_t param_ecc_correctable_bits; /* M: number of bits
ECC correctability */
uint8_t param_interleaved_addr_bits; /* M: num of interleaved address
bits (only low half is valid) */
uint8_t param_interleaved_op_attrs; /* O: obsolete */
uint8_t param_reserved_4[13]; /* R: reserved */
/* Electrical parameters block */
uint8_t param_io_c_max; /* M: I/O pin capacitance, maximum */
uint16_t param_async_timing_mode; /* M: async timing mode support */
uint16_t param_async_progcache_timing_mode; /* O: obsolete */
uint16_t param_t_prog; /* M: maximum page program time (us) */
uint16_t param_t_bers; /* M: maximum block erase time (us) */
uint16_t param_t_r; /* M: maximum page read time (us) */
uint16_t param_ccs; /* M: minimum change column setup time (ns) */
uint16_t param_sync_timing_mode; /* source sync timing mode support */
uint8_t param_sync_features; /* M: source sync features */
uint16_t param_clk_input_c; /* O: CLK input pin cap., typical */
uint16_t param_io_c; /* O: I/O pin capacitance, typical */
uint16_t param_input_c; /* O: input pin capacitance, typical */
uint8_t param_input_c_max; /* M: input pin capacitance, maximum */
uint8_t param_driver_strength; /* M: driver strength support */
uint16_t param_t_r_interleaved; /* O: maximum interleaved
page read time (us) */
uint16_t param_t_adl; /* O: program page register clear enhancement
tADL value (ns) */
uint8_t param_reserved_5[8]; /* R: reserved */
/* Vendor block */
uint16_t param_vendor_revision; /* M: vendor specific rev number */
uint8_t param_vendor_specific[88]; /* vendor specific information */
uint16_t param_integrity_crc; /* M: integrity CRC */
} __packed;
#endif /* _ONFI_H_ */

View File

@ -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 <bsd.own.mk>
@ -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

View File

@ -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 <bsd.kmodule.mk>

View File

@ -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?

15
sys/modules/nand/Makefile Normal file
View File

@ -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 <bsd.kmodule.mk>

View File

@ -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?

View File

@ -0,0 +1,3 @@
#ifndef NAND_BBT
#define NAND_BBT
#endif

View File

@ -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 <bsd.kmodule.mk>

View File

@ -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 <bsd.sys.mk>
@ -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 \

116
sys/sys/flashio.h Normal file
View File

@ -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 <ahoka@NetBSD.org>
* Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _FLASHIO_H_
#define _FLASHIO_H_
#include <sys/ioctl.h>
/* this header may be used fron the kernel */
#if defined(_KERNEL) || defined(_STANDALONE)
#include <sys/types.h>
#else
#include <stdint.h>
#include <stdbool.h>
#endif
enum {
FLASH_ERASE_DONE = 0x0,
FLASH_ERASE_FAILED = 0x1
};
enum {
FLASH_TYPE_UNKNOWN = 0x0,
FLASH_TYPE_NOR = 0x1,
FLASH_TYPE_NAND = 0x2
};
/* public userspace API */
/* common integer type to address flash */
typedef int64_t flash_addr_t;
/**
* struct erase_params - for ioctl erase call
* @addr: start address of the erase
* @len: length of the erase
*/
struct flash_erase_params {
flash_addr_t ep_addr;
flash_addr_t ep_len;
};
struct flash_badblock_params {
flash_addr_t bbp_addr;
bool bbp_isbad;
};
struct flash_info_params {
flash_addr_t ip_flash_size;
size_t ip_page_size;
size_t ip_erase_size;
uint8_t ip_flash_type;
};
struct flash_dump_params {
flash_addr_t dp_block;
flash_addr_t dp_len;
uint8_t *dp_buf;
};
enum {
FLASH_IOCTL_ERASE_BLOCK,
FLASH_IOCTL_DUMP,
FLASH_IOCTL_GET_INFO,
FLASH_IOCTL_BLOCK_ISBAD,
FLASH_IOCTL_BLOCK_MARKBAD
};
#define FLASH_ERASE_BLOCK \
_IOW('&', FLASH_IOCTL_ERASE_BLOCK, struct flash_erase_params)
#define FLASH_DUMP \
_IOWR('&', FLASH_IOCTL_DUMP, struct flash_dump_params)
#define FLASH_GET_INFO \
_IOWR('&', FLASH_IOCTL_GET_INFO, struct flash_info_params)
#define FLASH_BLOCK_ISBAD \
_IOWR('&', FLASH_IOCTL_BLOCK_ISBAD, struct flash_badblock_params)
#define FLASH_BLOCK_MARKBAD \
_IOW('&', FLASH_IOCTL_BLOCK_MARKBAD, struct flash_badblock_params)
#endif

View File

@ -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 <bsd.own.mk>
@ -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 \

View File

@ -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 <bsd.prog.mk>

View File

@ -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 <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/ioctl.h>
#include <sys/flashio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <inttypes.h>
#include <ctype.h>
#include <errno.h>
void usage(void);
int to_intmax(intmax_t *, const char *);
int
main(int argc, char **argv)
{
char *device, *command;
int fd, error = 0;
intmax_t n = -1;
setprogname(argv[0]);
if (argc < 3)
usage();
device = argv[1];
command = argv[2];
argc -= 3;
argv += 3;
fd = open(device, O_RDWR, 0);
if (fd == -1)
err(EXIT_FAILURE, "can't open flash device");
if (!strcmp("erase", command)) {
struct flash_info_params ip;
struct flash_erase_params ep;
error = ioctl(fd, FLASH_GET_INFO, &ip);
if (error) {
warn("ioctl: FLASH_GET_INFO");
goto out;
}
if (argc == 2) {
error = to_intmax(&n, argv[0]);
if (error) {
warnx(strerror(error));
goto out;
}
ep.ep_addr = n;
if (!strcmp("all", argv[1])) {
ep.ep_len = ip.ip_flash_size;
} else {
error = to_intmax(&n, argv[1]);
if (error) {
warnx(strerror(error));
goto out;
}
ep.ep_len = n;
}
} else {
warnx("invalid number of arguments");
error = 1;
goto out;
}
printf("Erasing %jx bytes starting from %jx\n",
(uintmax_t )ep.ep_len, (uintmax_t )ep.ep_addr);
error = ioctl(fd, FLASH_ERASE_BLOCK, &ep);
if (error) {
warn("ioctl: FLASH_ERASE_BLOCK");
goto out;
}
} else if (!strcmp("identify", command)) {
struct flash_info_params ip;
error = ioctl(fd, FLASH_GET_INFO, &ip);
if (error) {
warn("ioctl: FLASH_GET_INFO");
goto out;
}
printf("Device type: ");
switch (ip.ip_flash_type) {
case FLASH_TYPE_NOR:
printf("NOR flash");
break;
case FLASH_TYPE_NAND:
printf("NAND flash");
break;
default:
printf("unknown (%d)", ip.ip_flash_type);
}
printf("\n");
/* TODO: humanize */
printf("Capacity %jd Mbytes, %jd pages, %zu bytes/page\n",
(intmax_t )ip.ip_flash_size / 1024 / 1024,
(intmax_t )ip.ip_flash_size / ip.ip_page_size,
ip.ip_page_size);
if (ip.ip_flash_type == FLASH_TYPE_NAND) {
printf("Block size %jd Kbytes, %jd pages/block\n",
(intmax_t )ip.ip_erase_size / 1024,
(intmax_t )ip.ip_erase_size / ip.ip_page_size);
}
} else if (!strcmp("badblocks", command)) {
struct flash_info_params ip;
struct flash_badblock_params bbp;
flash_addr_t addr;
bool hasbad = false;
error = ioctl(fd, FLASH_GET_INFO, &ip);
if (error) {
warn("ioctl: FLASH_GET_INFO");
goto out;
}
printf("Scanning for bad blocks: ");
addr = 0;
while (addr < ip.ip_flash_size) {
bbp.bbp_addr = addr;
error = ioctl(fd, FLASH_BLOCK_ISBAD, &bbp);
if (error) {
warn("ioctl: FLASH_BLOCK_ISBAD");
goto out;
}
if (bbp.bbp_isbad) {
hasbad = true;
printf("0x%jx ", addr);
}
addr += ip.ip_erase_size;
}
if (hasbad)
printf("Done.\n");
else
printf("No bad blocks found.\n");
} else if (!strcmp("markbad", command)) {
flash_addr_t address;
error = to_intmax(&n, argv[1]);
if (error) {
warnx(strerror(error));
goto out;
}
address = n;
printf("Marking block 0x%jx as bad.\n",
(intmax_t )address);
error = ioctl(fd, FLASH_BLOCK_MARKBAD, &address);
if (error) {
warn("ioctl: FLASH_BLOCK_MARKBAD");
goto out;
}
} else {
warnx("Unknown command");
error = 1;
goto out;
}
out:
close(fd);
return error;
}
int
to_intmax(intmax_t *num, const char *str)
{
char *endptr;
errno = 0;
if (str[0] == '0' && tolower((int )str[1]) == 'x') {
if (!isxdigit((int )str[0]))
return EINVAL;
*num = strtoimax(str, &endptr, 16);
} else {
if (!isdigit((int )str[0]))
return EINVAL;
*num = strtoimax(str, &endptr, 10);
}
if (errno == ERANGE && (*num == INTMAX_MIN || *num == INTMAX_MAX)) {
return ERANGE;
}
return 0;
}
void
usage(void)
{
fprintf(stderr, "usage: %s device identify\n",
getprogname());
fprintf(stderr, " %s device erase <start address> <size>|all\n",
getprogname());
fprintf(stderr, " %s device badblocks\n",
getprogname());
fprintf(stderr, " %s device markbad <address>\n",
getprogname());
exit(1);
}