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:
parent
2bf60042d7
commit
2b6ee22130
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
;;
|
||||
|
|
|
@ -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
|
||||
;;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
;;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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_ */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _NANDEMULATOR_H_
|
||||
#define _NANDEMULATOR_H_
|
||||
#else
|
||||
|
||||
#endif
|
|
@ -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_ */
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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?
|
|
@ -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>
|
|
@ -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?
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef NAND_BBT
|
||||
#define NAND_BBT
|
||||
#endif
|
|
@ -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>
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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 \
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue