debuggers: import openocd-0.7.0

Initial check-in of openocd-0.7.0 as it can be downloaded from
http://sourceforge.net/projects/openocd/files/openocd/0.7.0/

Any modifications will follow.

Change-Id: I6949beaefd589e046395ea0cb80f4e1ab1654d55
This commit is contained in:
Lars Rademacher
2013-12-02 14:53:22 +01:00
parent 85fffe007e
commit 83d72a091e
1148 changed files with 571445 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
include $(top_srcdir)/common.mk
SUBDIRS = \
jtag \
helper \
target \
transport \
flash \
svf \
xsvf \
pld \
server \
rtos
lib_LTLIBRARIES = libopenocd.la
bin_PROGRAMS = openocd
MAINFILE = main.c
openocd_SOURCES = $(MAINFILE)
openocd_LDADD = libopenocd.la
if INTERNAL_JIMTCL
openocd_LDADD += $(top_builddir)/jimtcl/libjim.a
else
openocd_LDADD += -ljim
endif
if ULINK
openocd_LDADD += -lm
endif
libopenocd_la_SOURCES = \
hello.c \
openocd.c \
startup_tcl.c
noinst_HEADERS = \
hello.h \
openocd.h
libopenocd_la_CPPFLAGS = -DPKGBLDDATE=\"`date +%F-%R`\"
# banner output includes RELSTR appended to $VERSION from the configure script
# guess-rev.sh returns either a repository version ID or "-snapshot"
if RELEASE
libopenocd_la_CPPFLAGS += -DRELSTR=\"\"
libopenocd_la_CPPFLAGS += -DGITVERSION=\"\"
else
libopenocd_la_CPPFLAGS += -DRELSTR=\"`$(top_srcdir)/guess-rev.sh $(top_srcdir)`\"
libopenocd_la_CPPFLAGS += -DGITVERSION=\"`cd $(top_srcdir) && git describe`\"
endif
# add default CPPFLAGS
libopenocd_la_CPPFLAGS += $(AM_CPPFLAGS) $(CPPFLAGS)
# the library search path.
libopenocd_la_LDFLAGS = $(all_libraries)
if IS_MINGW
MINGWLDADD = -lws2_32
else
MINGWLDADD =
endif
if FT2232_LIBFTDI
FTDI2232LIB = -lftdi -lusb
else
if USB_BLASTER_LIBFTDI
FTDI2232LIB = -lftdi -lusb
else
if PRESTO_LIBFTDI
FTDI2232LIB = -lftdi -lusb
else
FTDI2232LIB =
endif
endif
endif
LIBUSB =
if USE_LIBUSB1
LIBUSB += -lusb-1.0
endif
if USE_LIBUSB0
LIBUSB += -lusb
endif
libopenocd_la_LIBADD = \
$(top_builddir)/src/xsvf/libxsvf.la \
$(top_builddir)/src/svf/libsvf.la \
$(top_builddir)/src/pld/libpld.la \
$(top_builddir)/src/jtag/libjtag.la \
$(top_builddir)/src/transport/libtransport.la \
$(top_builddir)/src/flash/libflash.la \
$(top_builddir)/src/target/libtarget.la \
$(top_builddir)/src/server/libserver.la \
$(top_builddir)/src/rtos/librtos.la \
$(top_builddir)/src/helper/libhelper.la \
$(FTDI2232LIB) $(MINGWLDADD) $(LIBUSB)
STARTUP_TCL_SRCS = \
$(srcdir)/helper/startup.tcl \
$(srcdir)/jtag/startup.tcl \
$(srcdir)/target/startup.tcl \
$(srcdir)/flash/startup.tcl \
$(srcdir)/server/startup.tcl
EXTRA_DIST = $(STARTUP_TCL_SRCS)
BUILT_SOURCES = startup.tcl
startup.tcl: $(STARTUP_TCL_SRCS)
cat $^ > $@
BIN2C = $(top_builddir)/src/helper/bin2char$(EXEEXT_FOR_BUILD)
# Convert .tcl to cfile
startup_tcl.c: startup.tcl $(BIN2C)
$(BIN2C) openocd_startup_tcl < $< > $@ || rm -f $@
# add startup_tcl.c to make clean list
CLEANFILES = startup.tcl startup_tcl.c
# we do not want generated file in the dist
dist-hook:
rm -f $(distdir)/startup_tcl.c
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
# The "quick" target builds executables & reinstalls the executables
# Primary use: developer types to quicken the edit/compile/debug
# cycle. by not requiring a "full build and full install". Note the
# assumption is: You are only rebuilding the EXE.... and everything
# else is/was previously installed.
#
# use at your own risk
quick: all install-binPROGRAMS
+941
View File
@@ -0,0 +1,941 @@
# Makefile.in generated by automake 1.13.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__make_dryrun = \
{ \
am__dry=no; \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
*) \
for am__flg in $$MAKEFLAGS; do \
case $$am__flg in \
*=*|--*) ;; \
*n*) am__dry=yes; break;; \
esac; \
done;; \
esac; \
test $$am__dry = yes; \
}
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
DIST_COMMON = $(top_srcdir)/common.mk $(srcdir)/Makefile.in \
$(srcdir)/Makefile.am $(top_srcdir)/depcomp $(noinst_HEADERS)
@INTERNAL_JIMTCL_TRUE@am__append_1 = -I$(top_srcdir)/jimtcl \
@INTERNAL_JIMTCL_TRUE@ -I$(top_builddir)/jimtcl
bin_PROGRAMS = openocd$(EXEEXT)
@INTERNAL_JIMTCL_TRUE@am__append_2 = $(top_builddir)/jimtcl/libjim.a
@INTERNAL_JIMTCL_FALSE@am__append_3 = -ljim
@ULINK_TRUE@am__append_4 = -lm
# banner output includes RELSTR appended to $VERSION from the configure script
# guess-rev.sh returns either a repository version ID or "-snapshot"
@RELEASE_TRUE@am__append_5 = -DRELSTR=\"\" -DGITVERSION=\"\"
@RELEASE_FALSE@am__append_6 = -DRELSTR=\"`$(top_srcdir)/guess-rev.sh \
@RELEASE_FALSE@ $(top_srcdir)`\" -DGITVERSION=\"`cd \
@RELEASE_FALSE@ $(top_srcdir) && git describe`\"
@USE_LIBUSB1_TRUE@am__append_7 = -lusb-1.0
@USE_LIBUSB0_TRUE@am__append_8 = -lusb
subdir = src
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config_subdir.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
libopenocd_la_DEPENDENCIES = $(top_builddir)/src/xsvf/libxsvf.la \
$(top_builddir)/src/svf/libsvf.la \
$(top_builddir)/src/pld/libpld.la \
$(top_builddir)/src/jtag/libjtag.la \
$(top_builddir)/src/transport/libtransport.la \
$(top_builddir)/src/flash/libflash.la \
$(top_builddir)/src/target/libtarget.la \
$(top_builddir)/src/server/libserver.la \
$(top_builddir)/src/rtos/librtos.la \
$(top_builddir)/src/helper/libhelper.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
am_libopenocd_la_OBJECTS = libopenocd_la-hello.lo \
libopenocd_la-openocd.lo libopenocd_la-startup_tcl.lo
libopenocd_la_OBJECTS = $(am_libopenocd_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
libopenocd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libopenocd_la_LDFLAGS) $(LDFLAGS) -o $@
PROGRAMS = $(bin_PROGRAMS)
am__objects_1 = main.$(OBJEXT)
am_openocd_OBJECTS = $(am__objects_1)
openocd_OBJECTS = $(am_openocd_OBJECTS)
openocd_DEPENDENCIES = libopenocd.la $(am__append_2) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libopenocd_la_SOURCES) $(openocd_SOURCES)
DIST_SOURCES = $(libopenocd_la_SOURCES) $(openocd_SOURCES)
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
ctags-recursive dvi-recursive html-recursive info-recursive \
install-data-recursive install-dvi-recursive \
install-exec-recursive install-html-recursive \
install-info-recursive install-pdf-recursive \
install-ps-recursive install-recursive installcheck-recursive \
installdirs-recursive pdf-recursive ps-recursive \
tags-recursive uninstall-recursive
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(noinst_HEADERS)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
$(RECURSIVE_TARGETS) \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
distdir
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
DIST_SUBDIRS = $(SUBDIRS)
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
am__relativize = \
dir0=`pwd`; \
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
sed_rest='s,^[^/]*/*,,'; \
sed_last='s,^.*/\([^/]*\)$$,\1,'; \
sed_butlast='s,/*[^/]*$$,,'; \
while test -n "$$dir1"; do \
first=`echo "$$dir1" | sed -e "$$sed_first"`; \
if test "$$first" != "."; then \
if test "$$first" = ".."; then \
dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
else \
first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
if test "$$first2" = "$$first"; then \
dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
else \
dir2="../$$dir2"; \
fi; \
dir0="$$dir0"/"$$first"; \
fi; \
fi; \
dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
done; \
reldir="$$dir2"
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CC_FOR_BUILD = @CC_FOR_BUILD@
CFLAGS = @CFLAGS@
CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
FGREP = @FGREP@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
doxygen_as_html = @doxygen_as_html@
doxygen_as_pdf = @doxygen_as_pdf@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
# common flags used in openocd build
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
-I$(top_srcdir)/src/helper -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPKGLIBDIR=\"$(pkglibdir)\" $(am__append_1)
SUBDIRS = \
jtag \
helper \
target \
transport \
flash \
svf \
xsvf \
pld \
server \
rtos
lib_LTLIBRARIES = libopenocd.la
MAINFILE = main.c
openocd_SOURCES = $(MAINFILE)
openocd_LDADD = libopenocd.la $(am__append_2) $(am__append_3) \
$(am__append_4)
libopenocd_la_SOURCES = \
hello.c \
openocd.c \
startup_tcl.c
noinst_HEADERS = \
hello.h \
openocd.h
# add default CPPFLAGS
libopenocd_la_CPPFLAGS = -DPKGBLDDATE=\"`date +%F-%R`\" \
$(am__append_5) $(am__append_6) $(AM_CPPFLAGS) $(CPPFLAGS)
# the library search path.
libopenocd_la_LDFLAGS = $(all_libraries)
@IS_MINGW_FALSE@MINGWLDADD =
@IS_MINGW_TRUE@MINGWLDADD = -lws2_32
@FT2232_LIBFTDI_FALSE@@PRESTO_LIBFTDI_FALSE@@USB_BLASTER_LIBFTDI_FALSE@FTDI2232LIB =
@FT2232_LIBFTDI_FALSE@@PRESTO_LIBFTDI_TRUE@@USB_BLASTER_LIBFTDI_FALSE@FTDI2232LIB = -lftdi -lusb
@FT2232_LIBFTDI_FALSE@@USB_BLASTER_LIBFTDI_TRUE@FTDI2232LIB = -lftdi -lusb
@FT2232_LIBFTDI_TRUE@FTDI2232LIB = -lftdi -lusb
LIBUSB = $(am__append_7) $(am__append_8)
libopenocd_la_LIBADD = \
$(top_builddir)/src/xsvf/libxsvf.la \
$(top_builddir)/src/svf/libsvf.la \
$(top_builddir)/src/pld/libpld.la \
$(top_builddir)/src/jtag/libjtag.la \
$(top_builddir)/src/transport/libtransport.la \
$(top_builddir)/src/flash/libflash.la \
$(top_builddir)/src/target/libtarget.la \
$(top_builddir)/src/server/libserver.la \
$(top_builddir)/src/rtos/librtos.la \
$(top_builddir)/src/helper/libhelper.la \
$(FTDI2232LIB) $(MINGWLDADD) $(LIBUSB)
STARTUP_TCL_SRCS = \
$(srcdir)/helper/startup.tcl \
$(srcdir)/jtag/startup.tcl \
$(srcdir)/target/startup.tcl \
$(srcdir)/flash/startup.tcl \
$(srcdir)/server/startup.tcl
EXTRA_DIST = $(STARTUP_TCL_SRCS)
BUILT_SOURCES = startup.tcl
BIN2C = $(top_builddir)/src/helper/bin2char$(EXEEXT_FOR_BUILD)
# add startup_tcl.c to make clean list
CLEANFILES = startup.tcl startup_tcl.c
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-recursive
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common.mk $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_srcdir)/common.mk:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
list2="$$list2 $$p"; \
else :; fi; \
done; \
test -z "$$list2" || { \
echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
uninstall-libLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
for p in $$list; do \
$(am__strip_dir) \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
done
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
libopenocd.la: $(libopenocd_la_OBJECTS) $(libopenocd_la_DEPENDENCIES) $(EXTRA_libopenocd_la_DEPENDENCIES)
$(AM_V_CCLD)$(libopenocd_la_LINK) -rpath $(libdir) $(libopenocd_la_OBJECTS) $(libopenocd_la_LIBADD) $(LIBS)
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
|| test -f $$p1 \
; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) files[d] = files[d] " " $$1; \
else { print "f", $$3 "/" $$4, $$1; } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
$(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
} \
; done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
clean-binPROGRAMS:
@list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
echo " rm -f" $$list; \
rm -f $$list || exit $$?; \
test -n "$(EXEEXT)" || exit 0; \
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
openocd$(EXEEXT): $(openocd_OBJECTS) $(openocd_DEPENDENCIES) $(EXTRA_openocd_DEPENDENCIES)
@rm -f openocd$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(openocd_OBJECTS) $(openocd_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenocd_la-hello.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenocd_la-openocd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenocd_la-startup_tcl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libopenocd_la-hello.lo: hello.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libopenocd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libopenocd_la-hello.lo -MD -MP -MF $(DEPDIR)/libopenocd_la-hello.Tpo -c -o libopenocd_la-hello.lo `test -f 'hello.c' || echo '$(srcdir)/'`hello.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenocd_la-hello.Tpo $(DEPDIR)/libopenocd_la-hello.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hello.c' object='libopenocd_la-hello.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libopenocd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libopenocd_la-hello.lo `test -f 'hello.c' || echo '$(srcdir)/'`hello.c
libopenocd_la-openocd.lo: openocd.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libopenocd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libopenocd_la-openocd.lo -MD -MP -MF $(DEPDIR)/libopenocd_la-openocd.Tpo -c -o libopenocd_la-openocd.lo `test -f 'openocd.c' || echo '$(srcdir)/'`openocd.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenocd_la-openocd.Tpo $(DEPDIR)/libopenocd_la-openocd.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openocd.c' object='libopenocd_la-openocd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libopenocd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libopenocd_la-openocd.lo `test -f 'openocd.c' || echo '$(srcdir)/'`openocd.c
libopenocd_la-startup_tcl.lo: startup_tcl.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libopenocd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libopenocd_la-startup_tcl.lo -MD -MP -MF $(DEPDIR)/libopenocd_la-startup_tcl.Tpo -c -o libopenocd_la-startup_tcl.lo `test -f 'startup_tcl.c' || echo '$(srcdir)/'`startup_tcl.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenocd_la-startup_tcl.Tpo $(DEPDIR)/libopenocd_la-startup_tcl.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='startup_tcl.c' object='libopenocd_la-startup_tcl.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libopenocd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libopenocd_la-startup_tcl.lo `test -f 'startup_tcl.c' || echo '$(srcdir)/'`startup_tcl.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
# This directory's subdirectories are mostly independent; you can cd
# into them and run 'make' without going through this Makefile.
# To change the values of 'make' variables: instead of editing Makefiles,
# (1) if the variable is set in 'config.status', edit 'config.status'
# (which will cause the Makefiles to be regenerated when you run 'make');
# (2) otherwise, pass the desired values on the 'make' command line.
$(am__recursive_targets):
@fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
*k*) failcom='fail=yes';; \
esac; \
done; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
case "$@" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-recursive
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test ! -f $$subdir/TAGS || \
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-recursive
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-recursive
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
$(am__make_dryrun) \
|| test -d "$(distdir)/$$subdir" \
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|| exit 1; \
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
$(am__relativize); \
new_distdir=$$reldir; \
dir1=$$subdir; dir2="$(top_distdir)"; \
$(am__relativize); \
new_top_distdir=$$reldir; \
echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
($(am__cd) $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$$new_top_distdir" \
distdir="$$new_distdir" \
am__remove_distdir=: \
am__skip_length_check=: \
am__skip_mode_fix=: \
distdir) \
|| exit 1; \
fi; \
done
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$(top_distdir)" distdir="$(distdir)" \
dist-hook
check-am: all-am
check: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) check-recursive
all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS)
install-binPROGRAMS: install-libLTLIBRARIES
installdirs: installdirs-recursive
installdirs-am:
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-recursive
install-exec: install-exec-recursive
install-data: install-data-recursive
uninstall: uninstall-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-recursive
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
clean: clean-recursive
clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
clean-libtool mostlyclean-am
distclean: distclean-recursive
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-recursive
dvi-am:
html: html-recursive
html-am:
info: info-recursive
info-am:
install-data-am:
install-dvi: install-dvi-recursive
install-dvi-am:
install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
install-html: install-html-recursive
install-html-am:
install-info: install-info-recursive
install-info-am:
install-man:
install-pdf: install-pdf-recursive
install-pdf-am:
install-ps: install-ps-recursive
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-recursive
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-recursive
pdf-am:
ps: ps-recursive
ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES
.MAKE: $(am__recursive_targets) all check install install-am \
install-strip
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
check-am clean clean-binPROGRAMS clean-generic \
clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
ctags-am dist-hook distclean distclean-compile \
distclean-generic distclean-libtool distclean-tags distdir dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am \
install-libLTLIBRARIES install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs installdirs-am maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-libLTLIBRARIES
startup.tcl: $(STARTUP_TCL_SRCS)
cat $^ > $@
# Convert .tcl to cfile
startup_tcl.c: startup.tcl $(BIN2C)
$(BIN2C) openocd_startup_tcl < $< > $@ || rm -f $@
# we do not want generated file in the dist
dist-hook:
rm -f $(distdir)/startup_tcl.c
# The "quick" target builds executables & reinstalls the executables
# Primary use: developer types to quicken the edit/compile/debug
# cycle. by not requiring a "full build and full install". Note the
# assumption is: You are only rebuilding the EXE.... and everything
# else is/was previously installed.
#
# use at your own risk
quick: all install-binPROGRAMS
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+23
View File
@@ -0,0 +1,23 @@
include $(top_srcdir)/common.mk
SUBDIRS = \
nor \
nand
METASOURCES = AUTO
noinst_LTLIBRARIES = libflash.la
libflash_la_SOURCES = \
common.c \
mflash.c
libflash_la_LIBADD = \
$(top_builddir)/src/flash/nor/libocdflashnor.la \
$(top_builddir)/src/flash/nand/libocdflashnand.la
noinst_HEADERS = \
common.h \
mflash.h
EXTRA_DIST = startup.tcl
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+697
View File
@@ -0,0 +1,697 @@
# Makefile.in generated by automake 1.13.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__make_dryrun = \
{ \
am__dry=no; \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
*) \
for am__flg in $$MAKEFLAGS; do \
case $$am__flg in \
*=*|--*) ;; \
*n*) am__dry=yes; break;; \
esac; \
done;; \
esac; \
test $$am__dry = yes; \
}
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
DIST_COMMON = $(top_srcdir)/common.mk $(srcdir)/Makefile.in \
$(srcdir)/Makefile.am $(top_srcdir)/depcomp $(noinst_HEADERS)
@INTERNAL_JIMTCL_TRUE@am__append_1 = -I$(top_srcdir)/jimtcl \
@INTERNAL_JIMTCL_TRUE@ -I$(top_builddir)/jimtcl
subdir = src/flash
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config_subdir.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libflash_la_DEPENDENCIES = \
$(top_builddir)/src/flash/nor/libocdflashnor.la \
$(top_builddir)/src/flash/nand/libocdflashnand.la
am_libflash_la_OBJECTS = common.lo mflash.lo
libflash_la_OBJECTS = $(am_libflash_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libflash_la_SOURCES)
DIST_SOURCES = $(libflash_la_SOURCES)
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
ctags-recursive dvi-recursive html-recursive info-recursive \
install-data-recursive install-dvi-recursive \
install-exec-recursive install-html-recursive \
install-info-recursive install-pdf-recursive \
install-ps-recursive install-recursive installcheck-recursive \
installdirs-recursive pdf-recursive ps-recursive \
tags-recursive uninstall-recursive
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(noinst_HEADERS)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
$(RECURSIVE_TARGETS) \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
distdir
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
DIST_SUBDIRS = $(SUBDIRS)
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
am__relativize = \
dir0=`pwd`; \
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
sed_rest='s,^[^/]*/*,,'; \
sed_last='s,^.*/\([^/]*\)$$,\1,'; \
sed_butlast='s,/*[^/]*$$,,'; \
while test -n "$$dir1"; do \
first=`echo "$$dir1" | sed -e "$$sed_first"`; \
if test "$$first" != "."; then \
if test "$$first" = ".."; then \
dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
else \
first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
if test "$$first2" = "$$first"; then \
dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
else \
dir2="../$$dir2"; \
fi; \
dir0="$$dir0"/"$$first"; \
fi; \
fi; \
dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
done; \
reldir="$$dir2"
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CC_FOR_BUILD = @CC_FOR_BUILD@
CFLAGS = @CFLAGS@
CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
FGREP = @FGREP@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
doxygen_as_html = @doxygen_as_html@
doxygen_as_pdf = @doxygen_as_pdf@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
# common flags used in openocd build
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
-I$(top_srcdir)/src/helper -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPKGLIBDIR=\"$(pkglibdir)\" $(am__append_1)
SUBDIRS = \
nor \
nand
METASOURCES = AUTO
noinst_LTLIBRARIES = libflash.la
libflash_la_SOURCES = \
common.c \
mflash.c
libflash_la_LIBADD = \
$(top_builddir)/src/flash/nor/libocdflashnor.la \
$(top_builddir)/src/flash/nand/libocdflashnand.la
noinst_HEADERS = \
common.h \
mflash.h
EXTRA_DIST = startup.tcl
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
all: all-recursive
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common.mk $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/flash/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/flash/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_srcdir)/common.mk:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
@list='$(noinst_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
libflash.la: $(libflash_la_OBJECTS) $(libflash_la_DEPENDENCIES) $(EXTRA_libflash_la_DEPENDENCIES)
$(AM_V_CCLD)$(LINK) $(libflash_la_OBJECTS) $(libflash_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mflash.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
# This directory's subdirectories are mostly independent; you can cd
# into them and run 'make' without going through this Makefile.
# To change the values of 'make' variables: instead of editing Makefiles,
# (1) if the variable is set in 'config.status', edit 'config.status'
# (which will cause the Makefiles to be regenerated when you run 'make');
# (2) otherwise, pass the desired values on the 'make' command line.
$(am__recursive_targets):
@fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
*k*) failcom='fail=yes';; \
esac; \
done; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
case "$@" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-recursive
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test ! -f $$subdir/TAGS || \
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-recursive
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-recursive
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
$(am__make_dryrun) \
|| test -d "$(distdir)/$$subdir" \
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|| exit 1; \
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
$(am__relativize); \
new_distdir=$$reldir; \
dir1=$$subdir; dir2="$(top_distdir)"; \
$(am__relativize); \
new_top_distdir=$$reldir; \
echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
($(am__cd) $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$$new_top_distdir" \
distdir="$$new_distdir" \
am__remove_distdir=: \
am__skip_length_check=: \
am__skip_mode_fix=: \
distdir) \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-recursive
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs: installdirs-recursive
installdirs-am:
install: install-recursive
install-exec: install-exec-recursive
install-data: install-data-recursive
uninstall: uninstall-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-recursive
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
clean: clean-recursive
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
mostlyclean-am
distclean: distclean-recursive
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-recursive
dvi-am:
html: html-recursive
html-am:
info: info-recursive
info-am:
install-data-am:
install-dvi: install-dvi-recursive
install-dvi-am:
install-exec-am:
install-html: install-html-recursive
install-html-am:
install-info: install-info-recursive
install-info-am:
install-man:
install-pdf: install-pdf-recursive
install-pdf-am:
install-ps: install-ps-recursive
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-recursive
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-recursive
pdf-am:
ps: ps-recursive
ps-am:
uninstall-am:
.MAKE: $(am__recursive_targets) install-am install-strip
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
check-am clean clean-generic clean-libtool \
clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
distclean-compile distclean-generic distclean-libtool \
distclean-tags distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-ps install-ps-am \
install-strip installcheck installcheck-am installdirs \
installdirs-am maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+49
View File
@@ -0,0 +1,49 @@
/***************************************************************************
* Copyright (C) 2009 by Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "common.h"
#include <helper/log.h>
unsigned get_flash_name_index(const char *name)
{
const char *name_index = strrchr(name, '.');
if (NULL == name_index)
return 0;
if (name_index[1] < '0' || name_index[1] > '9')
return ~0U;
unsigned requested;
int retval = parse_uint(name_index + 1, &requested);
/* detect parsing error by forcing past end of bank list */
return (ERROR_OK == retval) ? requested : ~0U;
}
bool flash_driver_name_matches(const char *name, const char *expected)
{
unsigned blen = strlen(name);
/* only match up to the length of the driver name... */
if (strncmp(name, expected, blen) != 0)
return false;
/* ...then check that name terminates at this spot. */
return expected[blen] == '.' || expected[blen] == '\0';
}
+50
View File
@@ -0,0 +1,50 @@
/***************************************************************************
* Copyright (C) 2009 by Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_COMMON_H
#define FLASH_COMMON_H
#include <helper/log.h>
/**
* Parses the optional '.index' portion of a flash bank identifier.
* @param name The desired driver name, passed by the user.
* @returns The parsed index request, or 0 if not present. If the
* name provides a suffix but it does not parse as an unsigned integer,
* the routine returns ~0U. This will prevent further matching.
*/
unsigned get_flash_name_index(const char *name);
/**
* Attempt to match the @c expected name with the @c name of a driver.
* @param name The name of the driver (from the bank's device structure).
* @param expected The expected driver name, passed by the user.
*/
bool flash_driver_name_matches(const char *name, const char *expected);
#define ERROR_FLASH_BANK_INVALID (-900)
#define ERROR_FLASH_SECTOR_INVALID (-901)
#define ERROR_FLASH_OPERATION_FAILED (-902)
#define ERROR_FLASH_DST_OUT_OF_BANK (-903)
#define ERROR_FLASH_DST_BREAKS_ALIGNMENT (-904)
#define ERROR_FLASH_BUSY (-905)
#define ERROR_FLASH_SECTOR_NOT_ERASED (-906)
#define ERROR_FLASH_BANK_NOT_PROBED (-907)
#define ERROR_FLASH_OPER_UNSUPPORTED (-908)
#endif /* FLASH_COMMON_H */
File diff suppressed because it is too large Load Diff
+291
View File
@@ -0,0 +1,291 @@
/***************************************************************************
* Copyright (C) 2007-2008 by unsik Kim <donari75@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef _MFLASH_H
#define _MFLASH_H
struct command_context;
typedef unsigned long mg_io_uint32;
typedef unsigned short mg_io_uint16;
typedef unsigned char mg_io_uint8;
struct mflash_gpio_num {
char port[2];
signed short num;
};
struct mflash_gpio_drv {
const char *name;
int (*set_gpio_to_output)(struct mflash_gpio_num gpio);
int (*set_gpio_output_val)(struct mflash_gpio_num gpio, uint8_t val);
};
typedef struct _mg_io_type_drv_info {
mg_io_uint16 general_configuration; /* 00 */
mg_io_uint16 number_of_cylinders; /* 01 */
mg_io_uint16 reserved1; /* 02 */
mg_io_uint16 number_of_heads; /* 03 */
mg_io_uint16 unformatted_bytes_per_track; /* 04 */
mg_io_uint16 unformatted_bytes_per_sector; /* 05 */
mg_io_uint16 sectors_per_track; /* 06 */
mg_io_uint16 vendor_unique1[3]; /* 07/08/09 */
mg_io_uint8 serial_number[20]; /* 10~19 */
mg_io_uint16 buffer_type; /* 20 */
mg_io_uint16 buffer_sector_size; /* 21 */
mg_io_uint16 number_of_ecc_bytes; /* 22 */
mg_io_uint8 firmware_revision[8]; /* 23~26 */
mg_io_uint8 model_number[40]; /* 27 */
mg_io_uint8 maximum_block_transfer; /* 47 low byte */
mg_io_uint8 vendor_unique2; /* 47 high byte */
mg_io_uint16 dword_io; /* 48 */
mg_io_uint16 capabilities; /* 49 */
mg_io_uint16 reserved2; /* 50 */
mg_io_uint8 vendor_unique3; /* 51 low byte */
mg_io_uint8 pio_cycle_timing_mode; /* 51 high byte */
mg_io_uint8 vendor_unique4; /* 52 low byte */
mg_io_uint8 dma_cycle_timing_mode; /* 52 high byte */
mg_io_uint16 translation_fields_valid; /* 53 (low bit) */
mg_io_uint16 number_of_current_cylinders; /* 54 */
mg_io_uint16 number_of_current_heads; /* 55 */
mg_io_uint16 current_sectors_per_track; /* 56 */
mg_io_uint16 current_sector_capacity_lo; /* 57 & 58 */
mg_io_uint16 current_sector_capacity_hi; /* 57 & 58 */
mg_io_uint8 multi_sector_count; /* 59 low */
mg_io_uint8 multi_sector_setting_valid; /* 59 high (low bit) */
mg_io_uint16 total_user_addressable_sectors_lo; /* 60 & 61 */
mg_io_uint16 total_user_addressable_sectors_hi; /* 60 & 61 */
mg_io_uint8 single_dma_modes_supported; /* 62 low byte */
mg_io_uint8 single_dma_transfer_active; /* 62 high byte */
mg_io_uint8 multi_dma_modes_supported; /* 63 low byte */
mg_io_uint8 multi_dma_transfer_active; /* 63 high byte */
mg_io_uint16 adv_pio_mode;
mg_io_uint16 min_dma_cyc;
mg_io_uint16 recommend_dma_cyc;
mg_io_uint16 min_pio_cyc_no_iordy;
mg_io_uint16 min_pio_cyc_with_iordy;
mg_io_uint8 reserved3[22];
mg_io_uint16 major_ver_num;
mg_io_uint16 minor_ver_num;
mg_io_uint16 feature_cmd_set_suprt0;
mg_io_uint16 feature_cmd_set_suprt1;
mg_io_uint16 feature_cmd_set_suprt2;
mg_io_uint16 feature_cmd_set_en0;
mg_io_uint16 feature_cmd_set_en1;
mg_io_uint16 feature_cmd_set_en2;
mg_io_uint16 reserved4;
mg_io_uint16 req_time_for_security_er_done;
mg_io_uint16 req_time_for_enhan_security_er_done;
mg_io_uint16 adv_pwr_mgm_lvl_val;
mg_io_uint16 reserved5;
mg_io_uint16 re_of_hw_rst;
mg_io_uint8 reserved6[68];
mg_io_uint16 security_stas;
mg_io_uint8 vendor_uniq_bytes[62];
mg_io_uint16 cfa_pwr_mode;
mg_io_uint8 reserved7[186];
mg_io_uint16 scts_per_secure_data_unit;
mg_io_uint16 integrity_word;
} mg_io_type_drv_info;
typedef struct _mg_pll_t {
unsigned int lock_cyc;
unsigned short feedback_div; /* 9bit divider */
unsigned char input_div; /* 5bit divider */
unsigned char output_div; /* 2bit divider */
} mg_pll_t;
struct mg_drv_info {
mg_io_type_drv_info drv_id;
uint32_t tot_sects;
};
struct mflash_bank {
uint32_t base;
struct mflash_gpio_num rst_pin;
struct mflash_gpio_drv *gpio_drv;
struct target *target;
struct mg_drv_info *drv_info;
};
int mflash_register_commands(struct command_context *cmd_ctx);
#define MG_MFLASH_SECTOR_SIZE (0x200) /* 512Bytes = 2^9 */
#define MG_MFLASH_SECTOR_SIZE_MASK (0x200-1)
#define MG_MFLASH_SECTOR_SIZE_SHIFT (9)
#define MG_BUFFER_OFFSET 0x8000
#define MG_REG_OFFSET 0xC000
#define MG_REG_FEATURE 0x2 /* write case */
#define MG_REG_ERROR 0x2 /* read case */
#define MG_REG_SECT_CNT 0x4
#define MG_REG_SECT_NUM 0x6
#define MG_REG_CYL_LOW 0x8
#define MG_REG_CYL_HIGH 0xA
#define MG_REG_DRV_HEAD 0xC
#define MG_REG_COMMAND 0xE /* write case */
#define MG_REG_STATUS 0xE /* read case */
#define MG_REG_DRV_CTRL 0x10
#define MG_REG_BURST_CTRL 0x12
#define MG_OEM_DISK_WAIT_TIME_LONG 15000 /* msec */
#define MG_OEM_DISK_WAIT_TIME_NORMAL 3000 /* msec */
#define MG_OEM_DISK_WAIT_TIME_SHORT 1000 /* msec */
#define MG_PLL_CLK_OUT 66000000.0 /* 66Mhz */
#define MG_PLL_MAX_FEEDBACKDIV_VAL 512
#define MG_PLL_MAX_INPUTDIV_VAL 32
#define MG_PLL_MAX_OUTPUTDIV_VAL 4
#define MG_PLL_STD_INPUTCLK 12000000.0 /* 12Mhz */
#define MG_PLL_STD_LOCKCYCLE 10000
#define MG_UNLOCK_OTP_AREA 0xFF
#define MG_FILEIO_CHUNK 1048576
#define ERROR_MG_IO (-1600)
#define ERROR_MG_TIMEOUT (-1601)
#define ERROR_MG_INVALID_PLL (-1603)
#define ERROR_MG_INTERFACE (-1604)
#define ERROR_MG_INVALID_OSC (-1605)
#define ERROR_MG_UNSUPPORTED_SOC (-1606)
typedef enum _mg_io_type_wait {
mg_io_wait_bsy = 1,
mg_io_wait_not_bsy = 2,
mg_io_wait_rdy = 3,
mg_io_wait_drq = 4, /* wait for data request */
mg_io_wait_drq_noerr = 5, /* wait for DRQ but ignore the error status bit */
mg_io_wait_rdy_noerr = 6 /* wait for ready, but ignore error status bit */
} mg_io_type_wait;
/*= "Status Register" bit masks. */
typedef enum _mg_io_type_rbit_status {
mg_io_rbit_status_error = 0x01, /* error bit in status register */
mg_io_rbit_status_corrected_error = 0x04, /* corrected error in status register */
mg_io_rbit_status_data_req = 0x08, /* data request bit in status register */
mg_io_rbit_status_seek_done = 0x10, /* DSC - Drive Seek Complete */
mg_io_rbit_status_write_fault = 0x20, /* DWF - Drive Write Fault */
mg_io_rbit_status_ready = 0x40,
mg_io_rbit_status_busy = 0x80
} mg_io_type_rbit_status;
/*= "Error Register" bit masks. */
typedef enum _mg_io_type_rbit_error {
mg_io_rbit_err_general = 0x01,
mg_io_rbit_err_aborted = 0x04,
mg_io_rbit_err_bad_sect_num = 0x10,
mg_io_rbit_err_uncorrectable = 0x40,
mg_io_rbit_err_bad_block = 0x80
} mg_io_type_rbit_error;
/* = "Device Control Register" bit. */
typedef enum _mg_io_type_rbit_devc {
mg_io_rbit_devc_intr = 0x02, /* interrupt enable bit (1:disable, 0:enable) */
mg_io_rbit_devc_srst = 0x04 /* softwrae reset bit (1:assert, 0:de-assert) */
} mg_io_type_rbit_devc;
/* "Drive Select/Head Register" values. */
typedef enum _mg_io_type_rval_dev {
mg_io_rval_dev_must_be_on = 0x80, /* These 1 bits are always on */
mg_io_rval_dev_drv_master = (0x00 | mg_io_rval_dev_must_be_on), /* Master */
mg_io_rval_dev_drv_slave0 = (0x10 | mg_io_rval_dev_must_be_on), /* Slave0 */
mg_io_rval_dev_drv_slave1 = (0x20 | mg_io_rval_dev_must_be_on), /* Slave1 */
mg_io_rval_dev_drv_slave2 = (0x30 | mg_io_rval_dev_must_be_on), /* Slave2 */
mg_io_rval_dev_lba_mode = (0x40 | mg_io_rval_dev_must_be_on)
} mg_io_type_rval_dev;
typedef enum _mg_io_type_cmd {
mg_io_cmd_read = 0x20,
mg_io_cmd_write = 0x30,
mg_io_cmd_setmul = 0xC6,
mg_io_cmd_readmul = 0xC4,
mg_io_cmd_writemul = 0xC5,
mg_io_cmd_idle = 0x97, /* 0xE3 */
mg_io_cmd_idle_immediate = 0x95, /* 0xE1 */
mg_io_cmd_setsleep = 0x99, /* 0xE6 */
mg_io_cmd_stdby = 0x96, /* 0xE2 */
mg_io_cmd_stdby_immediate = 0x94, /* 0xE0 */
mg_io_cmd_identify = 0xEC,
mg_io_cmd_set_feature = 0xEF,
mg_io_cmd_confirm_write = 0x3C,
mg_io_cmd_confirm_read = 0x40,
mg_io_cmd_wakeup = 0xC3
} mg_io_type_cmd;
typedef enum _mg_feature_id {
mg_feature_id_transmode = 0x3
} mg_feature_id;
typedef enum _mg_feature_val {
mg_feature_val_trans_default = 0x0,
mg_feature_val_trans_vcmd = 0x3,
mg_feature_val_trand_vcmds = 0x2
} mg_feature_val;
typedef enum _mg_vcmd {
mg_vcmd_update_xipinfo = 0xFA, /* FWPATCH commmand through IOM I/O */
mg_vcmd_verify_fwpatch = 0xFB, /* FWPATCH commmand through IOM I/O */
mg_vcmd_update_stgdrvinfo = 0xFC, /* IOM identificatin info program command */
mg_vcmd_prep_fwpatch = 0xFD, /* FWPATCH commmand through IOM I/O */
mg_vcmd_exe_fwpatch = 0xFE, /* FWPATCH commmand through IOM I/O */
mg_vcmd_wr_pll = 0x8B,
mg_vcmd_purge_nand = 0x8C, /* Only for Seagle */
mg_vcmd_lock_otp = 0x8D,
mg_vcmd_rd_otp = 0x8E,
mg_vcmd_wr_otp = 0x8F
} mg_vcmd;
typedef enum _mg_opmode {
mg_op_mode_xip = 1, /* TRUE XIP */
mg_op_mode_snd = 2, /* BOOT + Storage */
mg_op_mode_stg = 0 /* Only Storage */
} mg_opmode;
#endif
@@ -0,0 +1,46 @@
include $(top_srcdir)/common.mk
noinst_LTLIBRARIES = libocdflashnand.la
libocdflashnand_la_SOURCES = \
ecc.c \
ecc_kw.c \
core.c \
fileio.c \
tcl.c \
arm_io.c \
$(NAND_DRIVERS) \
driver.c
NAND_DRIVERS = \
nonce.c \
davinci.c \
lpc3180.c \
lpc32xx.c \
mxc.c \
mx3.c \
orion.c \
s3c24xx.c \
s3c2410.c \
s3c2412.c \
s3c2440.c \
s3c2443.c \
s3c6400.c \
at91sam9.c \
nuc910.c
noinst_HEADERS = \
arm_io.h \
core.h \
driver.h \
fileio.h \
imp.h \
lpc3180.h \
lpc32xx.h \
mxc.h \
mx3.h \
s3c24xx.h \
s3c24xx_regs.h \
nuc910.h
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
@@ -0,0 +1,625 @@
# Makefile.in generated by automake 1.13.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__make_dryrun = \
{ \
am__dry=no; \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
*) \
for am__flg in $$MAKEFLAGS; do \
case $$am__flg in \
*=*|--*) ;; \
*n*) am__dry=yes; break;; \
esac; \
done;; \
esac; \
test $$am__dry = yes; \
}
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
DIST_COMMON = $(top_srcdir)/common.mk $(srcdir)/Makefile.in \
$(srcdir)/Makefile.am $(top_srcdir)/depcomp $(noinst_HEADERS)
@INTERNAL_JIMTCL_TRUE@am__append_1 = -I$(top_srcdir)/jimtcl \
@INTERNAL_JIMTCL_TRUE@ -I$(top_builddir)/jimtcl
subdir = src/flash/nand
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config_subdir.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libocdflashnand_la_LIBADD =
am__objects_1 = nonce.lo davinci.lo lpc3180.lo lpc32xx.lo mxc.lo \
mx3.lo orion.lo s3c24xx.lo s3c2410.lo s3c2412.lo s3c2440.lo \
s3c2443.lo s3c6400.lo at91sam9.lo nuc910.lo
am_libocdflashnand_la_OBJECTS = ecc.lo ecc_kw.lo core.lo fileio.lo \
tcl.lo arm_io.lo $(am__objects_1) driver.lo
libocdflashnand_la_OBJECTS = $(am_libocdflashnand_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libocdflashnand_la_SOURCES)
DIST_SOURCES = $(libocdflashnand_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(noinst_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CC_FOR_BUILD = @CC_FOR_BUILD@
CFLAGS = @CFLAGS@
CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
FGREP = @FGREP@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
doxygen_as_html = @doxygen_as_html@
doxygen_as_pdf = @doxygen_as_pdf@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
# common flags used in openocd build
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
-I$(top_srcdir)/src/helper -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPKGLIBDIR=\"$(pkglibdir)\" $(am__append_1)
noinst_LTLIBRARIES = libocdflashnand.la
libocdflashnand_la_SOURCES = \
ecc.c \
ecc_kw.c \
core.c \
fileio.c \
tcl.c \
arm_io.c \
$(NAND_DRIVERS) \
driver.c
NAND_DRIVERS = \
nonce.c \
davinci.c \
lpc3180.c \
lpc32xx.c \
mxc.c \
mx3.c \
orion.c \
s3c24xx.c \
s3c2410.c \
s3c2412.c \
s3c2440.c \
s3c2443.c \
s3c6400.c \
at91sam9.c \
nuc910.c
noinst_HEADERS = \
arm_io.h \
core.h \
driver.h \
fileio.h \
imp.h \
lpc3180.h \
lpc32xx.h \
mxc.h \
mx3.h \
s3c24xx.h \
s3c24xx_regs.h \
nuc910.h
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common.mk $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/flash/nand/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/flash/nand/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_srcdir)/common.mk:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
@list='$(noinst_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
libocdflashnand.la: $(libocdflashnand_la_OBJECTS) $(libocdflashnand_la_DEPENDENCIES) $(EXTRA_libocdflashnand_la_DEPENDENCIES)
$(AM_V_CCLD)$(LINK) $(libocdflashnand_la_OBJECTS) $(libocdflashnand_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arm_io.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/at91sam9.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/davinci.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecc_kw.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileio.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpc3180.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpc32xx.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mx3.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mxc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nonce.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nuc910.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/orion.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s3c2410.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s3c2412.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s3c2440.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s3c2443.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s3c24xx.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s3c6400.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcl.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs:
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
clean: clean-am
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am:
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+303
View File
@@ -0,0 +1,303 @@
/*
* Copyright (C) 2009 by Marvell Semiconductors, Inc.
* Written by Nicolas Pitre <nico at marvell.com>
*
* Copyright (C) 2009 by David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "core.h"
#include "arm_io.h"
#include <helper/binarybuffer.h>
#include <target/arm.h>
#include <target/armv7m.h>
#include <target/algorithm.h>
/**
* Copies code to a working area. This will allocate room for the code plus the
* additional amount requested if the working area pointer is null.
*
* @param target Pointer to the target to copy code to
* @param code Pointer to the code area to be copied
* @param code_size Size of the code being copied
* @param additional Size of the additional area to be allocated in addition to
* code
* @param area Pointer to a pointer to a working area to copy code to
* @return Success or failure of the operation
*/
static int arm_code_to_working_area(struct target *target,
const uint32_t *code, unsigned code_size,
unsigned additional, struct working_area **area)
{
uint8_t code_buf[code_size];
unsigned i;
int retval;
unsigned size = code_size + additional;
/* REVISIT this assumes size doesn't ever change.
* That's usually correct; but there are boards with
* both large and small page chips, where it won't be...
*/
/* make sure we have a working area */
if (NULL == *area) {
retval = target_alloc_working_area(target, size, area);
if (retval != ERROR_OK) {
LOG_DEBUG("%s: no %d byte buffer", __func__, (int) size);
return ERROR_NAND_NO_BUFFER;
}
}
/* buffer code in target endianness */
for (i = 0; i < code_size / 4; i++)
target_buffer_set_u32(target, code_buf + i * 4, code[i]);
/* copy code to work area */
retval = target_write_memory(target, (*area)->address,
4, code_size / 4, code_buf);
return retval;
}
/**
* ARM-specific bulk write from buffer to address of 8-bit wide NAND.
* For now this supports ARMv4,ARMv5 and ARMv7-M cores.
*
* Enhancements to target_run_algorithm() could enable:
* - ARMv6 and ARMv7 cores in ARM mode
*
* Different code fragments could handle:
* - 16-bit wide data (needs different setup)
*
* @param nand Pointer to the arm_nand_data struct that defines the I/O
* @param data Pointer to the data to be copied to flash
* @param size Size of the data being copied
* @return Success or failure of the operation
*/
int arm_nandwrite(struct arm_nand_data *nand, uint8_t *data, int size)
{
struct target *target = nand->target;
struct arm_algorithm armv4_5_algo;
struct armv7m_algorithm armv7m_algo;
void *arm_algo;
struct arm *arm = target->arch_info;
struct reg_param reg_params[3];
uint32_t target_buf;
uint32_t exit_var = 0;
int retval;
/* Inputs:
* r0 NAND data address (byte wide)
* r1 buffer address
* r2 buffer length
*/
static const uint32_t code_armv4_5[] = {
0xe4d13001, /* s: ldrb r3, [r1], #1 */
0xe5c03000, /* strb r3, [r0] */
0xe2522001, /* subs r2, r2, #1 */
0x1afffffb, /* bne s */
/* exit: ARMv4 needs hardware breakpoint */
0xe1200070, /* e: bkpt #0 */
};
/* Inputs:
* r0 NAND data address (byte wide)
* r1 buffer address
* r2 buffer length
*
* see contrib/loaders/flash/armv7m_io.s for src
*/
static const uint32_t code_armv7m[] = {
0x3b01f811,
0x3a017003,
0xaffaf47f,
0xbf00be00,
};
int target_code_size = 0;
const uint32_t *target_code_src = NULL;
/* set up algorithm */
if (is_armv7m(target_to_armv7m(target))) { /* armv7m target */
armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_algo.core_mode = ARM_MODE_THREAD;
arm_algo = &armv7m_algo;
target_code_size = sizeof(code_armv7m);
target_code_src = code_armv7m;
} else {
armv4_5_algo.common_magic = ARM_COMMON_MAGIC;
armv4_5_algo.core_mode = ARM_MODE_SVC;
armv4_5_algo.core_state = ARM_STATE_ARM;
arm_algo = &armv4_5_algo;
target_code_size = sizeof(code_armv4_5);
target_code_src = code_armv4_5;
}
if (nand->op != ARM_NAND_WRITE || !nand->copy_area) {
retval = arm_code_to_working_area(target, target_code_src, target_code_size,
nand->chunk_size, &nand->copy_area);
if (retval != ERROR_OK)
return retval;
}
nand->op = ARM_NAND_WRITE;
/* copy data to work area */
target_buf = nand->copy_area->address + target_code_size;
retval = target_write_buffer(target, target_buf, size, data);
if (retval != ERROR_OK)
return retval;
/* set up parameters */
init_reg_param(&reg_params[0], "r0", 32, PARAM_IN);
init_reg_param(&reg_params[1], "r1", 32, PARAM_IN);
init_reg_param(&reg_params[2], "r2", 32, PARAM_IN);
buf_set_u32(reg_params[0].value, 0, 32, nand->data);
buf_set_u32(reg_params[1].value, 0, 32, target_buf);
buf_set_u32(reg_params[2].value, 0, 32, size);
/* armv4 must exit using a hardware breakpoint */
if (arm->is_armv4)
exit_var = nand->copy_area->address + target_code_size - 4;
/* use alg to write data from work area to NAND chip */
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
nand->copy_area->address, exit_var, 1000, arm_algo);
if (retval != ERROR_OK)
LOG_ERROR("error executing hosted NAND write");
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
return retval;
}
/**
* Uses an on-chip algorithm for an ARM device to read from a NAND device and
* store the data into the host machine's memory.
*
* @param nand Pointer to the arm_nand_data struct that defines the I/O
* @param data Pointer to the data buffer to store the read data
* @param size Amount of data to be stored to the buffer.
* @return Success or failure of the operation
*/
int arm_nandread(struct arm_nand_data *nand, uint8_t *data, uint32_t size)
{
struct target *target = nand->target;
struct arm_algorithm armv4_5_algo;
struct armv7m_algorithm armv7m_algo;
void *arm_algo;
struct arm *arm = target->arch_info;
struct reg_param reg_params[3];
uint32_t target_buf;
uint32_t exit_var = 0;
int retval;
/* Inputs:
* r0 buffer address
* r1 NAND data address (byte wide)
* r2 buffer length
*/
static const uint32_t code_armv4_5[] = {
0xe5d13000, /* s: ldrb r3, [r1] */
0xe4c03001, /* strb r3, [r0], #1 */
0xe2522001, /* subs r2, r2, #1 */
0x1afffffb, /* bne s */
/* exit: ARMv4 needs hardware breakpoint */
0xe1200070, /* e: bkpt #0 */
};
/* Inputs:
* r0 buffer address
* r1 NAND data address (byte wide)
* r2 buffer length
*
* see contrib/loaders/flash/armv7m_io.s for src
*/
static const uint32_t code_armv7m[] = {
0xf800780b,
0x3a013b01,
0xaffaf47f,
0xbf00be00,
};
int target_code_size = 0;
const uint32_t *target_code_src = NULL;
/* set up algorithm */
if (is_armv7m(target_to_armv7m(target))) { /* armv7m target */
armv7m_algo.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_algo.core_mode = ARM_MODE_THREAD;
arm_algo = &armv7m_algo;
target_code_size = sizeof(code_armv7m);
target_code_src = code_armv7m;
} else {
armv4_5_algo.common_magic = ARM_COMMON_MAGIC;
armv4_5_algo.core_mode = ARM_MODE_SVC;
armv4_5_algo.core_state = ARM_STATE_ARM;
arm_algo = &armv4_5_algo;
target_code_size = sizeof(code_armv4_5);
target_code_src = code_armv4_5;
}
/* create the copy area if not yet available */
if (nand->op != ARM_NAND_READ || !nand->copy_area) {
retval = arm_code_to_working_area(target, target_code_src, target_code_size,
nand->chunk_size, &nand->copy_area);
if (retval != ERROR_OK)
return retval;
}
nand->op = ARM_NAND_READ;
target_buf = nand->copy_area->address + target_code_size;
/* set up parameters */
init_reg_param(&reg_params[0], "r0", 32, PARAM_IN);
init_reg_param(&reg_params[1], "r1", 32, PARAM_IN);
init_reg_param(&reg_params[2], "r2", 32, PARAM_IN);
buf_set_u32(reg_params[0].value, 0, 32, target_buf);
buf_set_u32(reg_params[1].value, 0, 32, nand->data);
buf_set_u32(reg_params[2].value, 0, 32, size);
/* armv4 must exit using a hardware breakpoint */
if (arm->is_armv4)
exit_var = nand->copy_area->address + target_code_size - 4;
/* use alg to write data from NAND chip to work area */
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
nand->copy_area->address, exit_var, 1000, arm_algo);
if (retval != ERROR_OK)
LOG_ERROR("error executing hosted NAND read");
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
/* read from work area to the host's memory */
retval = target_read_buffer(target, target_buf, size, data);
return retval;
}
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2009 by David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __ARM_NANDIO_H
#define __ARM_NANDIO_H
/**
* Available operational states the arm_nand_data struct can be in.
*/
enum arm_nand_op {
ARM_NAND_NONE, /**< No operation performed. */
ARM_NAND_READ, /**< Read operation performed. */
ARM_NAND_WRITE, /**< Write operation performed. */
};
/**
* The arm_nand_data struct is used for defining NAND I/O operations on an ARM
* core.
*/
struct arm_nand_data {
/** Target is proxy for some ARM core. */
struct target *target;
/** The copy area holds code loop and data for I/O operations. */
struct working_area *copy_area;
/** The chunk size is the page size or ECC chunk. */
unsigned chunk_size;
/** Where data is read from or written to. */
uint32_t data;
/** Last operation executed using this struct. */
enum arm_nand_op op;
/* currently implicit: data width == 8 bits (not 16) */
};
int arm_nandwrite(struct arm_nand_data *nand, uint8_t *data, int size);
int arm_nandread(struct arm_nand_data *nand, uint8_t *data, uint32_t size);
#endif /* __ARM_NANDIO_H */
+724
View File
@@ -0,0 +1,724 @@
/*
* Copyright (C) 2009 by Dean Glazeski
* dnglaze@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <target/arm.h>
#include <helper/log.h>
#include "imp.h"
#include "arm_io.h"
#define AT91C_PIOx_SODR (0x30) /**< Offset to PIO SODR. */
#define AT91C_PIOx_CODR (0x34) /**< Offset to PIO CODR. */
#define AT91C_PIOx_PDSR (0x3C) /**< Offset to PIO PDSR. */
#define AT91C_ECCx_CR (0x00) /**< Offset to ECC CR. */
#define AT91C_ECCx_SR (0x08) /**< Offset to ECC SR. */
#define AT91C_ECCx_PR (0x0C) /**< Offset to ECC PR. */
#define AT91C_ECCx_NPR (0x10) /**< Offset to ECC NPR. */
/**
* Representation of a pin on an AT91SAM9 chip.
*/
struct at91sam9_pin {
/** Address of the PIO controller. */
uint32_t pioc;
/** Pin number. */
uint32_t num;
};
/**
* Private data for the controller that is stored in the NAND device structure.
*/
struct at91sam9_nand {
/** Address of the ECC controller for NAND. */
uint32_t ecc;
/** Address data is written to. */
uint32_t data;
/** Address commands are written to. */
uint32_t cmd;
/** Address addresses are written to. */
uint32_t addr;
/** I/O structure for hosted reads/writes. */
struct arm_nand_data io;
/** Pin representing the ready/~busy line. */
struct at91sam9_pin busy;
/** Pin representing the chip enable. */
struct at91sam9_pin ce;
};
/**
* Checks if the target is halted and prints an error message if it isn't.
*
* @param target Target to be checked.
* @param label String label for where function is called from.
* @return True if the target is halted.
*/
static int at91sam9_halted(struct target *target, const char *label)
{
if (target->state == TARGET_HALTED)
return true;
LOG_ERROR("Target must be halted to use NAND controller (%s)", label);
return false;
}
/**
* Initialize the AT91SAM9 NAND controller.
*
* @param nand NAND device the controller is attached to.
* @return Success or failure of initialization.
*/
static int at91sam9_init(struct nand_device *nand)
{
struct target *target = nand->target;
if (!at91sam9_halted(target, "init"))
return ERROR_NAND_OPERATION_FAILED;
return ERROR_OK;
}
/**
* Enable NAND device attached to a controller.
*
* @param info NAND controller information for controlling NAND device.
* @return Success or failure of the enabling.
*/
static int at91sam9_enable(struct nand_device *nand)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
return target_write_u32(target, info->ce.pioc + AT91C_PIOx_CODR, 1 << info->ce.num);
}
/**
* Disable NAND device attached to a controller.
*
* @param info NAND controller information for controlling NAND device.
* @return Success or failure of the disabling.
*/
static int at91sam9_disable(struct nand_device *nand)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
return target_write_u32(target, info->ce.pioc + AT91C_PIOx_SODR, 1 << info->ce.num);
}
/**
* Send a command to the NAND device.
*
* @param nand NAND device to write the command to.
* @param command Command to be written.
* @return Success or failure of writing the command.
*/
static int at91sam9_command(struct nand_device *nand, uint8_t command)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!at91sam9_halted(target, "command"))
return ERROR_NAND_OPERATION_FAILED;
at91sam9_enable(nand);
return target_write_u8(target, info->cmd, command);
}
/**
* Reset the AT91SAM9 NAND controller.
*
* @param nand NAND device to be reset.
* @return Success or failure of reset.
*/
static int at91sam9_reset(struct nand_device *nand)
{
if (!at91sam9_halted(nand->target, "reset"))
return ERROR_NAND_OPERATION_FAILED;
return at91sam9_disable(nand);
}
/**
* Send an address to the NAND device attached to an AT91SAM9 NAND controller.
*
* @param nand NAND device to send the address to.
* @param address Address to be sent.
* @return Success or failure of sending the address.
*/
static int at91sam9_address(struct nand_device *nand, uint8_t address)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!at91sam9_halted(nand->target, "address"))
return ERROR_NAND_OPERATION_FAILED;
return target_write_u8(target, info->addr, address);
}
/**
* Read data directly from the NAND device attached to an AT91SAM9 NAND
* controller.
*
* @param nand NAND device to read from.
* @param data Pointer to where the data should be put.
* @return Success or failure of reading the data.
*/
static int at91sam9_read_data(struct nand_device *nand, void *data)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!at91sam9_halted(nand->target, "read data"))
return ERROR_NAND_OPERATION_FAILED;
return target_read_u8(target, info->data, data);
}
/**
* Write data directly to the NAND device attached to an AT91SAM9 NAND
* controller.
*
* @param nand NAND device to be written to.
* @param data Data to be written.
* @return Success or failure of the data write.
*/
static int at91sam9_write_data(struct nand_device *nand, uint16_t data)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!at91sam9_halted(target, "write data"))
return ERROR_NAND_OPERATION_FAILED;
return target_write_u8(target, info->data, data);
}
/**
* Determine if the NAND device is ready by looking at the ready/~busy pin.
*
* @param nand NAND device to check.
* @param timeout Time in milliseconds to wait for NAND to be ready.
* @return True if the NAND is ready in the timeout period.
*/
static int at91sam9_nand_ready(struct nand_device *nand, int timeout)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint32_t status;
if (!at91sam9_halted(target, "nand ready"))
return 0;
do {
target_read_u32(target, info->busy.pioc + AT91C_PIOx_PDSR, &status);
if (status & (1 << info->busy.num))
return 1;
alive_sleep(1);
} while (timeout-- > 0);
return 0;
}
/**
* Read a block of data from the NAND device attached to an AT91SAM9. This
* utilizes the ARM hosted NAND read function.
*
* @param nand NAND device to read from.
* @param data Pointer to where the read data should be placed.
* @param size Size of the data being read.
* @return Success or failure of the hosted read.
*/
static int at91sam9_read_block_data(struct nand_device *nand, uint8_t *data, int size)
{
struct at91sam9_nand *info = nand->controller_priv;
struct arm_nand_data *io = &info->io;
int status;
if (!at91sam9_halted(nand->target, "read block"))
return ERROR_NAND_OPERATION_FAILED;
io->chunk_size = nand->page_size;
status = arm_nandread(io, data, size);
return status;
}
/**
* Write a block of data to a NAND device attached to an AT91SAM9. This uses
* the ARM hosted write function to write the data.
*
* @param nand NAND device to write to.
* @param data Data to be written to device.
* @param size Size of the data being written.
* @return Success or failure of the hosted write.
*/
static int at91sam9_write_block_data(struct nand_device *nand, uint8_t *data, int size)
{
struct at91sam9_nand *info = nand->controller_priv;
struct arm_nand_data *io = &info->io;
int status;
if (!at91sam9_halted(nand->target, "write block"))
return ERROR_NAND_OPERATION_FAILED;
io->chunk_size = nand->page_size;
status = arm_nandwrite(io, data, size);
return status;
}
/**
* Initialize the ECC controller on the AT91SAM9.
*
* @param target Target to configure ECC on.
* @param info NAND controller information for where the ECC is.
* @return Success or failure of initialization.
*/
static int at91sam9_ecc_init(struct target *target, struct at91sam9_nand *info)
{
if (!info->ecc) {
LOG_ERROR("ECC controller address must be set when not reading raw NAND data");
return ERROR_NAND_OPERATION_FAILED;
}
/* reset ECC parity registers */
return target_write_u32(target, info->ecc + AT91C_ECCx_CR, 1);
}
/**
* Initialize an area for the OOB based on whether a user is requesting the OOB
* data. This determines the size of the OOB and allocates the space in case
* the user has not requested the OOB data.
*
* @param nand NAND device we are creating an OOB for.
* @param oob Pointer to the user supplied OOB area.
* @param size Size of the OOB.
* @return Pointer to an area to store OOB data.
*/
static uint8_t *at91sam9_oob_init(struct nand_device *nand, uint8_t *oob, uint32_t *size)
{
if (!oob) {
/* user doesn't want OOB, allocate it */
if (nand->page_size == 512)
*size = 16;
else if (nand->page_size == 2048)
*size = 64;
oob = malloc(*size);
if (!oob) {
LOG_ERROR("Unable to allocate space for OOB");
return NULL;
}
memset(oob, 0xFF, *size);
}
return oob;
}
/**
* Reads a page from an AT91SAM9 NAND controller and verifies using 1-bit ECC
* controller on chip. This makes an attempt to correct any errors that are
* encountered while reading the page of data.
*
* @param nand NAND device to read from
* @param page Page to be read.
* @param data Pointer to where data should be read to.
* @param data_size Size of the data to be read.
* @param oob Pointer to where OOB data should be read to.
* @param oob_size Size of the OOB data to be read.
* @return Success or failure of reading the NAND page.
*/
static int at91sam9_read_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
int retval;
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint8_t *oob_data;
uint32_t status;
retval = at91sam9_ecc_init(target, info);
if (ERROR_OK != retval)
return retval;
retval = nand_page_command(nand, page, NAND_CMD_READ0, !data);
if (ERROR_OK != retval)
return retval;
if (data) {
retval = nand_read_data_page(nand, data, data_size);
if (ERROR_OK != retval)
return retval;
}
oob_data = at91sam9_oob_init(nand, oob, &oob_size);
retval = nand_read_data_page(nand, oob_data, oob_size);
if (ERROR_OK == retval && data) {
target_read_u32(target, info->ecc + AT91C_ECCx_SR, &status);
if (status & 1) {
LOG_ERROR("Error detected!");
if (status & 4)
LOG_ERROR("Multiple errors encountered; unrecoverable!");
else {
/* attempt recovery */
uint32_t parity;
target_read_u32(target,
info->ecc + AT91C_ECCx_PR,
&parity);
uint32_t word = (parity & 0x0000FFF0) >> 4;
uint32_t bit = parity & 0x0F;
data[word] ^= (0x1) << bit;
LOG_INFO("Data word %d, bit %d corrected.",
(unsigned) word,
(unsigned) bit);
}
}
if (status & 2) {
/* we could write back correct ECC data */
LOG_ERROR("Error in ECC bytes detected");
}
}
if (!oob) {
/* if it wasn't asked for, free it */
free(oob_data);
}
return retval;
}
/**
* Write a page of data including 1-bit ECC information to a NAND device
* attached to an AT91SAM9 controller. If there is OOB data to be written,
* this will ignore the computed ECC from the ECC controller.
*
* @param nand NAND device to write to.
* @param page Page to write.
* @param data Pointer to data being written.
* @param data_size Size of the data being written.
* @param oob Pointer to OOB data being written.
* @param oob_size Size of the OOB data.
* @return Success or failure of the page write.
*/
static int at91sam9_write_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
struct at91sam9_nand *info = nand->controller_priv;
struct target *target = nand->target;
int retval;
uint8_t *oob_data = oob;
uint32_t parity, nparity;
retval = at91sam9_ecc_init(target, info);
if (ERROR_OK != retval)
return retval;
retval = nand_page_command(nand, page, NAND_CMD_SEQIN, !data);
if (ERROR_OK != retval)
return retval;
if (data) {
retval = nand_write_data_page(nand, data, data_size);
if (ERROR_OK != retval) {
LOG_ERROR("Unable to write data to NAND device");
return retval;
}
}
oob_data = at91sam9_oob_init(nand, oob, &oob_size);
if (!oob) {
/* no OOB given, so read in the ECC parity from the ECC controller */
target_read_u32(target, info->ecc + AT91C_ECCx_PR, &parity);
target_read_u32(target, info->ecc + AT91C_ECCx_NPR, &nparity);
oob_data[0] = (uint8_t) parity;
oob_data[1] = (uint8_t) (parity >> 8);
oob_data[2] = (uint8_t) nparity;
oob_data[3] = (uint8_t) (nparity >> 8);
}
retval = nand_write_data_page(nand, oob_data, oob_size);
if (!oob)
free(oob_data);
if (ERROR_OK != retval) {
LOG_ERROR("Unable to write OOB data to NAND");
return retval;
}
retval = nand_write_finish(nand);
return retval;
}
/**
* Handle the initial NAND device command for AT91SAM9 controllers. This
* initializes much of the controller information struct to be ready for future
* reads and writes.
*/
NAND_DEVICE_COMMAND_HANDLER(at91sam9_nand_device_command)
{
unsigned long chip = 0, ecc = 0;
struct at91sam9_nand *info = NULL;
LOG_DEBUG("AT91SAM9 NAND Device Command");
if (CMD_ARGC < 3 || CMD_ARGC > 4) {
LOG_ERROR("parameters: %s target chip_addr", CMD_ARGV[0]);
return ERROR_NAND_OPERATION_FAILED;
}
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[2], chip);
if (chip == 0) {
LOG_ERROR("invalid NAND chip address: %s", CMD_ARGV[2]);
return ERROR_NAND_OPERATION_FAILED;
}
if (CMD_ARGC == 4) {
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[3], ecc);
if (ecc == 0) {
LOG_ERROR("invalid ECC controller address: %s", CMD_ARGV[3]);
return ERROR_NAND_OPERATION_FAILED;
}
}
info = calloc(1, sizeof(*info));
if (!info) {
LOG_ERROR("unable to allocate space for controller private data");
return ERROR_NAND_OPERATION_FAILED;
}
info->data = chip;
info->cmd = chip | (1 << 22);
info->addr = chip | (1 << 21);
info->ecc = ecc;
nand->controller_priv = info;
info->io.target = nand->target;
info->io.data = info->data;
info->io.op = ARM_NAND_NONE;
return ERROR_OK;
}
/**
* Handle the AT91SAM9 CLE command for specifying the address line to use for
* writing commands to a NAND device.
*/
COMMAND_HANDLER(handle_at91sam9_cle_command)
{
struct nand_device *nand = NULL;
struct at91sam9_nand *info = NULL;
unsigned num, address_line;
if (CMD_ARGC != 2) {
command_print(CMD_CTX, "incorrect number of arguments for 'at91sam9 cle' command");
return ERROR_OK;
}
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], num);
nand = get_nand_device_by_num(num);
if (!nand) {
command_print(CMD_CTX, "invalid nand device number: %s", CMD_ARGV[0]);
return ERROR_OK;
}
info = nand->controller_priv;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], address_line);
info->cmd = info->data | (1 << address_line);
return ERROR_OK;
}
/**
* Handle the AT91SAM9 ALE command for specifying the address line to use for
* writing addresses to the NAND device.
*/
COMMAND_HANDLER(handle_at91sam9_ale_command)
{
struct nand_device *nand = NULL;
struct at91sam9_nand *info = NULL;
unsigned num, address_line;
if (CMD_ARGC != 2)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], num);
nand = get_nand_device_by_num(num);
if (!nand) {
command_print(CMD_CTX, "invalid nand device number: %s", CMD_ARGV[0]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
info = nand->controller_priv;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], address_line);
info->addr = info->data | (1 << address_line);
return ERROR_OK;
}
/**
* Handle the AT91SAM9 RDY/~BUSY command for specifying the pin that watches the
* RDY/~BUSY line from the NAND device.
*/
COMMAND_HANDLER(handle_at91sam9_rdy_busy_command)
{
struct nand_device *nand = NULL;
struct at91sam9_nand *info = NULL;
unsigned num, base_pioc, pin_num;
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], num);
nand = get_nand_device_by_num(num);
if (!nand) {
command_print(CMD_CTX, "invalid nand device number: %s", CMD_ARGV[0]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
info = nand->controller_priv;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], base_pioc);
info->busy.pioc = base_pioc;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[2], pin_num);
info->busy.num = pin_num;
return ERROR_OK;
}
/**
* Handle the AT91SAM9 CE command for specifying the pin that is used to enable
* or disable the NAND device.
*/
COMMAND_HANDLER(handle_at91sam9_ce_command)
{
struct nand_device *nand = NULL;
struct at91sam9_nand *info = NULL;
unsigned num, base_pioc, pin_num;
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], num);
nand = get_nand_device_by_num(num);
if (!nand) {
command_print(CMD_CTX, "invalid nand device number: %s", CMD_ARGV[0]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
info = nand->controller_priv;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], base_pioc);
info->ce.pioc = base_pioc;
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[2], pin_num);
info->ce.num = pin_num;
return ERROR_OK;
}
static const struct command_registration at91sam9_sub_command_handlers[] = {
{
.name = "cle",
.handler = handle_at91sam9_cle_command,
.mode = COMMAND_CONFIG,
.help = "set command latch enable address line (default is 22)",
.usage = "bank_id address_line",
},
{
.name = "ale",
.handler = handle_at91sam9_ale_command,
.mode = COMMAND_CONFIG,
.help = "set address latch enable address line (default is 21)",
.usage = "bank_id address_line",
},
{
.name = "rdy_busy",
.handler = handle_at91sam9_rdy_busy_command,
.mode = COMMAND_CONFIG,
.help = "set the GPIO input pin connected to "
"the RDY/~BUSY signal (no default)",
.usage = "bank_id pio_base_addr pin_num",
},
{
.name = "ce",
.handler = handle_at91sam9_ce_command,
.mode = COMMAND_CONFIG,
.help = "set the GPIO output pin connected to "
"the chip enable signal (no default)",
.usage = "bank_id pio_base_addr pin_num",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration at91sam9_command_handler[] = {
{
.name = "at91sam9",
.mode = COMMAND_ANY,
.help = "AT91SAM9 NAND flash controller commands",
.usage = "",
.chain = at91sam9_sub_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
/**
* Structure representing the AT91SAM9 NAND controller.
*/
struct nand_flash_controller at91sam9_nand_controller = {
.name = "at91sam9",
.nand_device_command = at91sam9_nand_device_command,
.commands = at91sam9_command_handler,
.init = at91sam9_init,
.command = at91sam9_command,
.reset = at91sam9_reset,
.address = at91sam9_address,
.read_data = at91sam9_read_data,
.write_data = at91sam9_write_data,
.nand_ready = at91sam9_nand_ready,
.read_block_data = at91sam9_read_block_data,
.write_block_data = at91sam9_write_block_data,
.read_page = at91sam9_read_page,
.write_page = at91sam9_write_page,
};
+878
View File
@@ -0,0 +1,878 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2002 Thomas Gleixner <tglx@linutronix.de> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* Partially based on drivers/mtd/nand_ids.c from Linux. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
/* configured NAND devices and NAND Flash command handler */
struct nand_device *nand_devices;
void nand_device_add(struct nand_device *c)
{
if (nand_devices) {
struct nand_device *p = nand_devices;
while (p && p->next)
p = p->next;
p->next = c;
} else
nand_devices = c;
}
/* Chip ID list
*
* Manufacturer, ID code, pagesize, chipsize in MegaByte, eraseblock size,
* options, name
*
* Pagesize; 0, 256, 512
* 0 get this information from the extended chip ID
* 256 256 Byte page size
* 512 512 Byte page size
*/
static struct nand_info nand_flash_ids[] = {
/* Vendor Specific Entries */
{ NAND_MFR_SAMSUNG, 0xD5, 8192, 2048, 0x100000, LP_OPTIONS,
"K9GAG08 2GB NAND 3.3V x8 MLC 2b/cell"},
{ NAND_MFR_SAMSUNG, 0xD7, 8192, 4096, 0x100000, LP_OPTIONS,
"K9LBG08 4GB NAND 3.3V x8 MLC 2b/cell"},
/* start "museum" IDs */
{ 0x0, 0x6e, 256, 1, 0x1000, 0, "NAND 1MiB 5V 8-bit"},
{ 0x0, 0x64, 256, 2, 0x1000, 0, "NAND 2MiB 5V 8-bit"},
{ 0x0, 0x6b, 512, 4, 0x2000, 0, "NAND 4MiB 5V 8-bit"},
{ 0x0, 0xe8, 256, 1, 0x1000, 0, "NAND 1MiB 3.3V 8-bit"},
{ 0x0, 0xec, 256, 1, 0x1000, 0, "NAND 1MiB 3.3V 8-bit"},
{ 0x0, 0xea, 256, 2, 0x1000, 0, "NAND 2MiB 3.3V 8-bit"},
{ 0x0, 0xd5, 512, 4, 0x2000, 0, "NAND 4MiB 3.3V 8-bit"},
{ 0x0, 0xe3, 512, 4, 0x2000, 0, "NAND 4MiB 3.3V 8-bit"},
{ 0x0, 0xe5, 512, 4, 0x2000, 0, "NAND 4MiB 3.3V 8-bit"},
{ 0x0, 0xd6, 512, 8, 0x2000, 0, "NAND 8MiB 3.3V 8-bit"},
{ 0x0, 0x39, 512, 8, 0x2000, 0, "NAND 8MiB 1.8V 8-bit"},
{ 0x0, 0xe6, 512, 8, 0x2000, 0, "NAND 8MiB 3.3V 8-bit"},
{ 0x0, 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16, "NAND 8MiB 1.8V 16-bit"},
{ 0x0, 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16, "NAND 8MiB 3.3V 16-bit"},
/* end "museum" IDs */
{ 0x0, 0x33, 512, 16, 0x4000, 0, "NAND 16MiB 1.8V 8-bit"},
{ 0x0, 0x73, 512, 16, 0x4000, 0, "NAND 16MiB 3.3V 8-bit"},
{ 0x0, 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16, "NAND 16MiB 1.8V 16-bit"},
{ 0x0, 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16, "NAND 16MiB 3.3V 16-bit"},
{ 0x0, 0x35, 512, 32, 0x4000, 0, "NAND 32MiB 1.8V 8-bit"},
{ 0x0, 0x75, 512, 32, 0x4000, 0, "NAND 32MiB 3.3V 8-bit"},
{ 0x0, 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16, "NAND 32MiB 1.8V 16-bit"},
{ 0x0, 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16, "NAND 32MiB 3.3V 16-bit"},
{ 0x0, 0x36, 512, 64, 0x4000, 0, "NAND 64MiB 1.8V 8-bit"},
{ 0x0, 0x76, 512, 64, 0x4000, 0, "NAND 64MiB 3.3V 8-bit"},
{ 0x0, 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16, "NAND 64MiB 1.8V 16-bit"},
{ 0x0, 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16, "NAND 64MiB 3.3V 16-bit"},
{ 0x0, 0x78, 512, 128, 0x4000, 0, "NAND 128MiB 1.8V 8-bit"},
{ 0x0, 0x39, 512, 128, 0x4000, 0, "NAND 128MiB 1.8V 8-bit"},
{ 0x0, 0x79, 512, 128, 0x4000, 0, "NAND 128MiB 3.3V 8-bit"},
{ 0x0, 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16, "NAND 128MiB 1.8V 16-bit"},
{ 0x0, 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16, "NAND 128MiB 1.8V 16-bit"},
{ 0x0, 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16, "NAND 128MiB 3.3V 16-bit"},
{ 0x0, 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16, "NAND 128MiB 3.3V 16-bit"},
{ 0x0, 0x71, 512, 256, 0x4000, 0, "NAND 256MiB 3.3V 8-bit"},
{ 0x0, 0xA2, 0, 64, 0, LP_OPTIONS, "NAND 64MiB 1.8V 8-bit"},
{ 0x0, 0xF2, 0, 64, 0, LP_OPTIONS, "NAND 64MiB 3.3V 8-bit"},
{ 0x0, 0xB2, 0, 64, 0, LP_OPTIONS16, "NAND 64MiB 1.8V 16-bit"},
{ 0x0, 0xC2, 0, 64, 0, LP_OPTIONS16, "NAND 64MiB 3.3V 16-bit"},
{ 0x0, 0xA1, 0, 128, 0, LP_OPTIONS, "NAND 128MiB 1.8V 8-bit"},
{ 0x0, 0xF1, 0, 128, 0, LP_OPTIONS, "NAND 128MiB 3.3V 8-bit"},
{ 0x0, 0xB1, 0, 128, 0, LP_OPTIONS16, "NAND 128MiB 1.8V 16-bit"},
{ 0x0, 0xC1, 0, 128, 0, LP_OPTIONS16, "NAND 128MiB 3.3V 16-bit"},
{ 0x0, 0xAA, 0, 256, 0, LP_OPTIONS, "NAND 256MiB 1.8V 8-bit"},
{ 0x0, 0xDA, 0, 256, 0, LP_OPTIONS, "NAND 256MiB 3.3V 8-bit"},
{ 0x0, 0xBA, 0, 256, 0, LP_OPTIONS16, "NAND 256MiB 1.8V 16-bit"},
{ 0x0, 0xCA, 0, 256, 0, LP_OPTIONS16, "NAND 256MiB 3.3V 16-bit"},
{ 0x0, 0xAC, 0, 512, 0, LP_OPTIONS, "NAND 512MiB 1.8V 8-bit"},
{ 0x0, 0xDC, 0, 512, 0, LP_OPTIONS, "NAND 512MiB 3.3V 8-bit"},
{ 0x0, 0xBC, 0, 512, 0, LP_OPTIONS16, "NAND 512MiB 1.8V 16-bit"},
{ 0x0, 0xCC, 0, 512, 0, LP_OPTIONS16, "NAND 512MiB 3.3V 16-bit"},
{ 0x0, 0xA3, 0, 1024, 0, LP_OPTIONS, "NAND 1GiB 1.8V 8-bit"},
{ 0x0, 0xD3, 0, 1024, 0, LP_OPTIONS, "NAND 1GiB 3.3V 8-bit"},
{ 0x0, 0xB3, 0, 1024, 0, LP_OPTIONS16, "NAND 1GiB 1.8V 16-bit"},
{ 0x0, 0xC3, 0, 1024, 0, LP_OPTIONS16, "NAND 1GiB 3.3V 16-bit"},
{ 0x0, 0xA5, 0, 2048, 0, LP_OPTIONS, "NAND 2GiB 1.8V 8-bit"},
{ 0x0, 0xD5, 0, 8192, 0, LP_OPTIONS, "NAND 2GiB 3.3V 8-bit"},
{ 0x0, 0xB5, 0, 2048, 0, LP_OPTIONS16, "NAND 2GiB 1.8V 16-bit"},
{ 0x0, 0xC5, 0, 2048, 0, LP_OPTIONS16, "NAND 2GiB 3.3V 16-bit"},
{ 0x0, 0x48, 0, 2048, 0, LP_OPTIONS, "NAND 2GiB 3.3V 8-bit"},
{0, 0, 0, 0, 0, 0, NULL}
};
/* Manufacturer ID list
*/
static struct nand_manufacturer nand_manuf_ids[] = {
{0x0, "unknown"},
{NAND_MFR_TOSHIBA, "Toshiba"},
{NAND_MFR_SAMSUNG, "Samsung"},
{NAND_MFR_FUJITSU, "Fujitsu"},
{NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
{0x0, NULL},
};
/*
* Define default oob placement schemes for large and small page devices
*/
#if 0
static struct nand_ecclayout nand_oob_8 = {
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = {
{.offset = 3,
.length = 2},
{.offset = 6,
.length = 2}
}
};
#endif
/**
* Returns the flash bank specified by @a name, which matches the
* driver name and a suffix (option) specify the driver-specific
* bank number. The suffix consists of the '.' and the driver-specific
* bank number: when two davinci banks are defined, then 'davinci.1' refers
* to the second (e.g. DM355EVM).
*/
static struct nand_device *get_nand_device_by_name(const char *name)
{
unsigned requested = get_flash_name_index(name);
unsigned found = 0;
struct nand_device *nand;
for (nand = nand_devices; NULL != nand; nand = nand->next) {
if (strcmp(nand->name, name) == 0)
return nand;
if (!flash_driver_name_matches(nand->controller->name, name))
continue;
if (++found < requested)
continue;
return nand;
}
return NULL;
}
struct nand_device *get_nand_device_by_num(int num)
{
struct nand_device *p;
int i = 0;
for (p = nand_devices; p; p = p->next) {
if (i++ == num)
return p;
}
return NULL;
}
COMMAND_HELPER(nand_command_get_device, unsigned name_index,
struct nand_device **nand)
{
const char *str = CMD_ARGV[name_index];
*nand = get_nand_device_by_name(str);
if (*nand)
return ERROR_OK;
unsigned num;
COMMAND_PARSE_NUMBER(uint, str, num);
*nand = get_nand_device_by_num(num);
if (!*nand) {
command_print(CMD_CTX, "NAND flash device '%s' not found", str);
return ERROR_COMMAND_SYNTAX_ERROR;
}
return ERROR_OK;
}
int nand_build_bbt(struct nand_device *nand, int first, int last)
{
uint32_t page;
int i;
int pages_per_block = (nand->erase_size / nand->page_size);
uint8_t oob[6];
int ret;
if ((first < 0) || (first >= nand->num_blocks))
first = 0;
if ((last >= nand->num_blocks) || (last == -1))
last = nand->num_blocks - 1;
page = first * pages_per_block;
for (i = first; i <= last; i++) {
ret = nand_read_page(nand, page, NULL, 0, oob, 6);
if (ret != ERROR_OK)
return ret;
if (((nand->device->options & NAND_BUSWIDTH_16) && ((oob[0] & oob[1]) != 0xff))
|| (((nand->page_size == 512) && (oob[5] != 0xff)) ||
((nand->page_size == 2048) && (oob[0] != 0xff)))) {
LOG_WARNING("bad block: %i", i);
nand->blocks[i].is_bad = 1;
} else
nand->blocks[i].is_bad = 0;
page += pages_per_block;
}
return ERROR_OK;
}
int nand_read_status(struct nand_device *nand, uint8_t *status)
{
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
/* Send read status command */
nand->controller->command(nand, NAND_CMD_STATUS);
alive_sleep(1);
/* read status */
if (nand->device->options & NAND_BUSWIDTH_16) {
uint16_t data;
nand->controller->read_data(nand, &data);
*status = data & 0xff;
} else
nand->controller->read_data(nand, status);
return ERROR_OK;
}
static int nand_poll_ready(struct nand_device *nand, int timeout)
{
uint8_t status;
nand->controller->command(nand, NAND_CMD_STATUS);
do {
if (nand->device->options & NAND_BUSWIDTH_16) {
uint16_t data;
nand->controller->read_data(nand, &data);
status = data & 0xff;
} else
nand->controller->read_data(nand, &status);
if (status & NAND_STATUS_READY)
break;
alive_sleep(1);
} while (timeout--);
return (status & NAND_STATUS_READY) != 0;
}
int nand_probe(struct nand_device *nand)
{
uint8_t manufacturer_id, device_id;
uint8_t id_buff[6];
int retval;
int i;
/* clear device data */
nand->device = NULL;
nand->manufacturer = NULL;
/* clear device parameters */
nand->bus_width = 0;
nand->address_cycles = 0;
nand->page_size = 0;
nand->erase_size = 0;
/* initialize controller (device parameters are zero, use controller default) */
retval = nand->controller->init(nand);
if (retval != ERROR_OK) {
switch (retval) {
case ERROR_NAND_OPERATION_FAILED:
LOG_DEBUG("controller initialization failed");
return ERROR_NAND_OPERATION_FAILED;
case ERROR_NAND_OPERATION_NOT_SUPPORTED:
LOG_ERROR(
"BUG: controller reported that it doesn't support default parameters");
return ERROR_NAND_OPERATION_FAILED;
default:
LOG_ERROR("BUG: unknown controller initialization failure");
return ERROR_NAND_OPERATION_FAILED;
}
}
nand->controller->command(nand, NAND_CMD_RESET);
nand->controller->reset(nand);
nand->controller->command(nand, NAND_CMD_READID);
nand->controller->address(nand, 0x0);
if (nand->bus_width == 8) {
nand->controller->read_data(nand, &manufacturer_id);
nand->controller->read_data(nand, &device_id);
} else {
uint16_t data_buf;
nand->controller->read_data(nand, &data_buf);
manufacturer_id = data_buf & 0xff;
nand->controller->read_data(nand, &data_buf);
device_id = data_buf & 0xff;
}
for (i = 0; nand_flash_ids[i].name; i++) {
if (nand_flash_ids[i].id == device_id &&
(nand_flash_ids[i].mfr_id == manufacturer_id ||
nand_flash_ids[i].mfr_id == 0)) {
nand->device = &nand_flash_ids[i];
break;
}
}
for (i = 0; nand_manuf_ids[i].name; i++) {
if (nand_manuf_ids[i].id == manufacturer_id) {
nand->manufacturer = &nand_manuf_ids[i];
break;
}
}
if (!nand->manufacturer) {
nand->manufacturer = &nand_manuf_ids[0];
nand->manufacturer->id = manufacturer_id;
}
if (!nand->device) {
LOG_ERROR(
"unknown NAND flash device found, manufacturer id: 0x%2.2x device id: 0x%2.2x",
manufacturer_id,
device_id);
return ERROR_NAND_OPERATION_FAILED;
}
LOG_DEBUG("found %s (%s)", nand->device->name, nand->manufacturer->name);
/* initialize device parameters */
/* bus width */
if (nand->device->options & NAND_BUSWIDTH_16)
nand->bus_width = 16;
else
nand->bus_width = 8;
/* Do we need extended device probe information? */
if (nand->device->page_size == 0 ||
nand->device->erase_size == 0) {
if (nand->bus_width == 8) {
nand->controller->read_data(nand, id_buff + 3);
nand->controller->read_data(nand, id_buff + 4);
nand->controller->read_data(nand, id_buff + 5);
} else {
uint16_t data_buf;
nand->controller->read_data(nand, &data_buf);
id_buff[3] = data_buf;
nand->controller->read_data(nand, &data_buf);
id_buff[4] = data_buf;
nand->controller->read_data(nand, &data_buf);
id_buff[5] = data_buf >> 8;
}
}
/* page size */
if (nand->device->page_size == 0)
nand->page_size = 1 << (10 + (id_buff[4] & 3));
else if (nand->device->page_size == 256) {
LOG_ERROR("NAND flashes with 256 byte pagesize are not supported");
return ERROR_NAND_OPERATION_FAILED;
} else
nand->page_size = nand->device->page_size;
/* number of address cycles */
if (nand->page_size <= 512) {
/* small page devices */
if (nand->device->chip_size <= 32)
nand->address_cycles = 3;
else if (nand->device->chip_size <= 8*1024)
nand->address_cycles = 4;
else {
LOG_ERROR("BUG: small page NAND device with more than 8 GiB encountered");
nand->address_cycles = 5;
}
} else {
/* large page devices */
if (nand->device->chip_size <= 128)
nand->address_cycles = 4;
else if (nand->device->chip_size <= 32*1024)
nand->address_cycles = 5;
else {
LOG_ERROR("BUG: large page NAND device with more than 32 GiB encountered");
nand->address_cycles = 6;
}
}
/* erase size */
if (nand->device->erase_size == 0) {
switch ((id_buff[4] >> 4) & 3) {
case 0:
nand->erase_size = 64 << 10;
break;
case 1:
nand->erase_size = 128 << 10;
break;
case 2:
nand->erase_size = 256 << 10;
break;
case 3:
nand->erase_size = 512 << 10;
break;
}
} else
nand->erase_size = nand->device->erase_size;
/* initialize controller, but leave parameters at the controllers default */
retval = nand->controller->init(nand);
if (retval != ERROR_OK) {
switch (retval) {
case ERROR_NAND_OPERATION_FAILED:
LOG_DEBUG("controller initialization failed");
return ERROR_NAND_OPERATION_FAILED;
case ERROR_NAND_OPERATION_NOT_SUPPORTED:
LOG_ERROR(
"controller doesn't support requested parameters (buswidth: %i, address cycles: %i, page size: %i)",
nand->bus_width,
nand->address_cycles,
nand->page_size);
return ERROR_NAND_OPERATION_FAILED;
default:
LOG_ERROR("BUG: unknown controller initialization failure");
return ERROR_NAND_OPERATION_FAILED;
}
}
nand->num_blocks = (nand->device->chip_size * 1024) / (nand->erase_size / 1024);
nand->blocks = malloc(sizeof(struct nand_block) * nand->num_blocks);
for (i = 0; i < nand->num_blocks; i++) {
nand->blocks[i].size = nand->erase_size;
nand->blocks[i].offset = i * nand->erase_size;
nand->blocks[i].is_erased = -1;
nand->blocks[i].is_bad = -1;
}
return ERROR_OK;
}
int nand_erase(struct nand_device *nand, int first_block, int last_block)
{
int i;
uint32_t page;
uint8_t status;
int retval;
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if ((first_block < 0) || (last_block >= nand->num_blocks))
return ERROR_COMMAND_SYNTAX_ERROR;
/* make sure we know if a block is bad before erasing it */
for (i = first_block; i <= last_block; i++) {
if (nand->blocks[i].is_bad == -1) {
nand_build_bbt(nand, i, last_block);
break;
}
}
for (i = first_block; i <= last_block; i++) {
/* Send erase setup command */
nand->controller->command(nand, NAND_CMD_ERASE1);
page = i * (nand->erase_size / nand->page_size);
/* Send page address */
if (nand->page_size <= 512) {
/* row */
nand->controller->address(nand, page & 0xff);
nand->controller->address(nand, (page >> 8) & 0xff);
/* 3rd cycle only on devices with more than 32 MiB */
if (nand->address_cycles >= 4)
nand->controller->address(nand, (page >> 16) & 0xff);
/* 4th cycle only on devices with more than 8 GiB */
if (nand->address_cycles >= 5)
nand->controller->address(nand, (page >> 24) & 0xff);
} else {
/* row */
nand->controller->address(nand, page & 0xff);
nand->controller->address(nand, (page >> 8) & 0xff);
/* 3rd cycle only on devices with more than 128 MiB */
if (nand->address_cycles >= 5)
nand->controller->address(nand, (page >> 16) & 0xff);
}
/* Send erase confirm command */
nand->controller->command(nand, NAND_CMD_ERASE2);
retval = nand->controller->nand_ready ?
nand->controller->nand_ready(nand, 1000) :
nand_poll_ready(nand, 1000);
if (!retval) {
LOG_ERROR("timeout waiting for NAND flash block erase to complete");
return ERROR_NAND_OPERATION_TIMEOUT;
}
retval = nand_read_status(nand, &status);
if (retval != ERROR_OK) {
LOG_ERROR("couldn't read status");
return ERROR_NAND_OPERATION_FAILED;
}
if (status & 0x1) {
LOG_ERROR("didn't erase %sblock %d; status: 0x%2.2x",
(nand->blocks[i].is_bad == 1)
? "bad " : "",
i, status);
/* continue; other blocks might still be erasable */
}
nand->blocks[i].is_erased = 1;
}
return ERROR_OK;
}
#if 0
static int nand_read_plain(struct nand_device *nand,
uint32_t address,
uint8_t *data,
uint32_t data_size)
{
uint8_t *page;
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if (address % nand->page_size) {
LOG_ERROR("reads need to be page aligned");
return ERROR_NAND_OPERATION_FAILED;
}
page = malloc(nand->page_size);
while (data_size > 0) {
uint32_t thisrun_size = (data_size > nand->page_size) ? nand->page_size : data_size;
uint32_t page_address;
page_address = address / nand->page_size;
nand_read_page(nand, page_address, page, nand->page_size, NULL, 0);
memcpy(data, page, thisrun_size);
address += thisrun_size;
data += thisrun_size;
data_size -= thisrun_size;
}
free(page);
return ERROR_OK;
}
static int nand_write_plain(struct nand_device *nand,
uint32_t address,
uint8_t *data,
uint32_t data_size)
{
uint8_t *page;
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if (address % nand->page_size) {
LOG_ERROR("writes need to be page aligned");
return ERROR_NAND_OPERATION_FAILED;
}
page = malloc(nand->page_size);
while (data_size > 0) {
uint32_t thisrun_size = (data_size > nand->page_size) ? nand->page_size : data_size;
uint32_t page_address;
memset(page, 0xff, nand->page_size);
memcpy(page, data, thisrun_size);
page_address = address / nand->page_size;
nand_write_page(nand, page_address, page, nand->page_size, NULL, 0);
address += thisrun_size;
data += thisrun_size;
data_size -= thisrun_size;
}
free(page);
return ERROR_OK;
}
#endif
int nand_write_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size)
{
uint32_t block;
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
block = page / (nand->erase_size / nand->page_size);
if (nand->blocks[block].is_erased == 1)
nand->blocks[block].is_erased = 0;
if (nand->use_raw || nand->controller->write_page == NULL)
return nand_write_page_raw(nand, page, data, data_size, oob, oob_size);
else
return nand->controller->write_page(nand, page, data, data_size, oob, oob_size);
}
int nand_read_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size)
{
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if (nand->use_raw || nand->controller->read_page == NULL)
return nand_read_page_raw(nand, page, data, data_size, oob, oob_size);
else
return nand->controller->read_page(nand, page, data, data_size, oob, oob_size);
}
int nand_page_command(struct nand_device *nand, uint32_t page,
uint8_t cmd, bool oob_only)
{
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if (oob_only && NAND_CMD_READ0 == cmd && nand->page_size <= 512)
cmd = NAND_CMD_READOOB;
nand->controller->command(nand, cmd);
if (nand->page_size <= 512) {
/* small page device */
/* column (always 0, we start at the beginning of a page/OOB area) */
nand->controller->address(nand, 0x0);
/* row */
nand->controller->address(nand, page & 0xff);
nand->controller->address(nand, (page >> 8) & 0xff);
/* 4th cycle only on devices with more than 32 MiB */
if (nand->address_cycles >= 4)
nand->controller->address(nand, (page >> 16) & 0xff);
/* 5th cycle only on devices with more than 8 GiB */
if (nand->address_cycles >= 5)
nand->controller->address(nand, (page >> 24) & 0xff);
} else {
/* large page device */
/* column (0 when we start at the beginning of a page,
* or 2048 for the beginning of OOB area)
*/
nand->controller->address(nand, 0x0);
if (oob_only)
nand->controller->address(nand, 0x8);
else
nand->controller->address(nand, 0x0);
/* row */
nand->controller->address(nand, page & 0xff);
nand->controller->address(nand, (page >> 8) & 0xff);
/* 5th cycle only on devices with more than 128 MiB */
if (nand->address_cycles >= 5)
nand->controller->address(nand, (page >> 16) & 0xff);
/* large page devices need a start command if reading */
if (NAND_CMD_READ0 == cmd)
nand->controller->command(nand, NAND_CMD_READSTART);
}
if (nand->controller->nand_ready) {
if (!nand->controller->nand_ready(nand, 100))
return ERROR_NAND_OPERATION_TIMEOUT;
} else {
/* nand_poll_read() cannot be used during nand read */
alive_sleep(1);
}
return ERROR_OK;
}
int nand_read_data_page(struct nand_device *nand, uint8_t *data, uint32_t size)
{
int retval = ERROR_NAND_NO_BUFFER;
if (nand->controller->read_block_data != NULL)
retval = (nand->controller->read_block_data)(nand, data, size);
if (ERROR_NAND_NO_BUFFER == retval) {
uint32_t i;
int incr = (nand->device->options & NAND_BUSWIDTH_16) ? 2 : 1;
retval = ERROR_OK;
for (i = 0; retval == ERROR_OK && i < size; i += incr) {
retval = nand->controller->read_data(nand, data);
data += incr;
}
}
return retval;
}
int nand_read_page_raw(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size)
{
int retval;
retval = nand_page_command(nand, page, NAND_CMD_READ0, !data);
if (ERROR_OK != retval)
return retval;
if (data)
nand_read_data_page(nand, data, data_size);
if (oob)
nand_read_data_page(nand, oob, oob_size);
return ERROR_OK;
}
int nand_write_data_page(struct nand_device *nand, uint8_t *data, uint32_t size)
{
int retval = ERROR_NAND_NO_BUFFER;
if (nand->controller->write_block_data != NULL)
retval = (nand->controller->write_block_data)(nand, data, size);
if (ERROR_NAND_NO_BUFFER == retval) {
bool is16bit = nand->device->options & NAND_BUSWIDTH_16;
uint32_t incr = is16bit ? 2 : 1;
uint16_t write_data;
uint32_t i;
for (i = 0; i < size; i += incr) {
if (is16bit)
write_data = le_to_h_u16(data);
else
write_data = *data;
retval = nand->controller->write_data(nand, write_data);
if (ERROR_OK != retval)
break;
data += incr;
}
}
return retval;
}
int nand_write_finish(struct nand_device *nand)
{
int retval;
uint8_t status;
nand->controller->command(nand, NAND_CMD_PAGEPROG);
retval = nand->controller->nand_ready ?
nand->controller->nand_ready(nand, 100) :
nand_poll_ready(nand, 100);
if (!retval)
return ERROR_NAND_OPERATION_TIMEOUT;
retval = nand_read_status(nand, &status);
if (ERROR_OK != retval) {
LOG_ERROR("couldn't read status");
return ERROR_NAND_OPERATION_FAILED;
}
if (status & NAND_STATUS_FAIL) {
LOG_ERROR("write operation didn't pass, status: 0x%2.2x",
status);
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
int nand_write_page_raw(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size)
{
int retval;
retval = nand_page_command(nand, page, NAND_CMD_SEQIN, !data);
if (ERROR_OK != retval)
return retval;
if (data) {
retval = nand_write_data_page(nand, data, data_size);
if (ERROR_OK != retval) {
LOG_ERROR("Unable to write data to NAND device");
return retval;
}
}
if (oob) {
retval = nand_write_data_page(nand, oob, oob_size);
if (ERROR_OK != retval) {
LOG_ERROR("Unable to write OOB data to NAND device");
return retval;
}
}
return nand_write_finish(nand);
}
+234
View File
@@ -0,0 +1,234 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* Partially based on linux/include/linux/mtd/nand.h *
* Copyright (C) 2000 David Woodhouse <dwmw2@mvhi.com> *
* Copyright (C) 2000 Steven J. Hill <sjhill@realitydiluted.com> *
* Copyright (C) 2000 Thomas Gleixner <tglx@linutronix.de> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NAND_CORE_H
#define FLASH_NAND_CORE_H
#include <flash/common.h>
/**
* Representation of a single NAND block in a NAND device.
*/
struct nand_block {
/** Offset to the block. */
uint32_t offset;
/** Size of the block. */
uint32_t size;
/** True if the block has been erased. */
int is_erased;
/** True if the block is bad. */
int is_bad;
};
struct nand_oobfree {
int offset;
int length;
};
struct nand_ecclayout {
int eccbytes;
int eccpos[64];
int oobavail;
struct nand_oobfree oobfree[2];
};
struct nand_device {
const char *name;
struct target *target;
struct nand_flash_controller *controller;
void *controller_priv;
struct nand_manufacturer *manufacturer;
struct nand_info *device;
int bus_width;
int address_cycles;
int page_size;
int erase_size;
int use_raw;
int num_blocks;
struct nand_block *blocks;
struct nand_device *next;
};
/* NAND Flash Manufacturer ID Codes
*/
enum {
NAND_MFR_TOSHIBA = 0x98,
NAND_MFR_SAMSUNG = 0xec,
NAND_MFR_FUJITSU = 0x04,
NAND_MFR_NATIONAL = 0x8f,
NAND_MFR_RENESAS = 0x07,
NAND_MFR_STMICRO = 0x20,
NAND_MFR_HYNIX = 0xad,
NAND_MFR_MICRON = 0x2c,
};
struct nand_manufacturer {
int id;
const char *name;
};
struct nand_info {
int mfr_id;
int id;
int page_size;
int chip_size;
int erase_size;
int options;
const char *name;
};
/* Option constants for bizarre disfunctionality and real features
*/
enum {
/* Chip can not auto increment pages */
NAND_NO_AUTOINCR = 0x00000001,
/* Buswitdh is 16 bit */
NAND_BUSWIDTH_16 = 0x00000002,
/* Device supports partial programming without padding */
NAND_NO_PADDING = 0x00000004,
/* Chip has cache program function */
NAND_CACHEPRG = 0x00000008,
/* Chip has copy back function */
NAND_COPYBACK = 0x00000010,
/* AND Chip which has 4 banks and a confusing page / block
* assignment. See Renesas datasheet for further information */
NAND_IS_AND = 0x00000020,
/* Chip has a array of 4 pages which can be read without
* additional ready /busy waits */
NAND_4PAGE_ARRAY = 0x00000040,
/* Chip requires that BBT is periodically rewritten to prevent
* bits from adjacent blocks from 'leaking' in altering data.
* This happens with the Renesas AG-AND chips, possibly others. */
BBT_AUTO_REFRESH = 0x00000080,
/* Chip does not require ready check on read. True
* for all large page devices, as they do not support
* autoincrement.*/
NAND_NO_READRDY = 0x00000100,
/* Options valid for Samsung large page devices */
NAND_SAMSUNG_LP_OPTIONS = (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK),
/* Options for new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes
*/
LP_OPTIONS = (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR),
LP_OPTIONS16 = (LP_OPTIONS | NAND_BUSWIDTH_16),
};
enum {
/* Standard NAND flash commands */
NAND_CMD_READ0 = 0x0,
NAND_CMD_READ1 = 0x1,
NAND_CMD_RNDOUT = 0x5,
NAND_CMD_PAGEPROG = 0x10,
NAND_CMD_READOOB = 0x50,
NAND_CMD_ERASE1 = 0x60,
NAND_CMD_STATUS = 0x70,
NAND_CMD_STATUS_MULTI = 0x71,
NAND_CMD_SEQIN = 0x80,
NAND_CMD_RNDIN = 0x85,
NAND_CMD_READID = 0x90,
NAND_CMD_ERASE2 = 0xd0,
NAND_CMD_RESET = 0xff,
/* Extended commands for large page devices */
NAND_CMD_READSTART = 0x30,
NAND_CMD_RNDOUTSTART = 0xE0,
NAND_CMD_CACHEDPROG = 0x15,
};
/* Status bits */
enum {
NAND_STATUS_FAIL = 0x01,
NAND_STATUS_FAIL_N1 = 0x02,
NAND_STATUS_TRUE_READY = 0x20,
NAND_STATUS_READY = 0x40,
NAND_STATUS_WP = 0x80,
};
/* OOB (spare) data formats */
enum oob_formats {
NAND_OOB_NONE = 0x0, /* no OOB data at all */
NAND_OOB_RAW = 0x1, /* raw OOB data (16 bytes for 512b page sizes, 64 bytes for
*2048b page sizes) */
NAND_OOB_ONLY = 0x2, /* only OOB data */
NAND_OOB_SW_ECC = 0x10, /* when writing, use SW ECC (as opposed to no ECC) */
NAND_OOB_HW_ECC = 0x20, /* when writing, use HW ECC (as opposed to no ECC) */
NAND_OOB_SW_ECC_KW = 0x40, /* when writing, use Marvell's Kirkwood bootrom format */
NAND_OOB_JFFS2 = 0x100, /* when writing, use JFFS2 OOB layout */
NAND_OOB_YAFFS2 = 0x100,/* when writing, use YAFFS2 OOB layout */
};
struct nand_device *get_nand_device_by_num(int num);
int nand_page_command(struct nand_device *nand, uint32_t page,
uint8_t cmd, bool oob_only);
int nand_read_data_page(struct nand_device *nand, uint8_t *data, uint32_t size);
int nand_write_data_page(struct nand_device *nand,
uint8_t *data, uint32_t size);
int nand_write_finish(struct nand_device *nand);
int nand_read_page_raw(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size);
int nand_write_page_raw(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size);
int nand_read_status(struct nand_device *nand, uint8_t *status);
int nand_calculate_ecc(struct nand_device *nand,
const uint8_t *dat, uint8_t *ecc_code);
int nand_calculate_ecc_kw(struct nand_device *nand,
const uint8_t *dat, uint8_t *ecc_code);
int nand_register_commands(struct command_context *cmd_ctx);
/** helper for parsing a nand device command argument string */
COMMAND_HELPER(nand_command_get_device, unsigned name_index,
struct nand_device **nand);
#define ERROR_NAND_DEVICE_INVALID (-1100)
#define ERROR_NAND_OPERATION_FAILED (-1101)
#define ERROR_NAND_OPERATION_TIMEOUT (-1102)
#define ERROR_NAND_OPERATION_NOT_SUPPORTED (-1103)
#define ERROR_NAND_DEVICE_NOT_PROBED (-1104)
#define ERROR_NAND_ERROR_CORRECTION_FAILED (-1105)
#define ERROR_NAND_NO_BUFFER (-1106)
#endif /* FLASH_NAND_CORE_H */
+795
View File
@@ -0,0 +1,795 @@
/***************************************************************************
* Copyright (C) 2009 by David Brownell *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* DaVinci family NAND controller support for OpenOCD.
*
* This driver uses hardware ECC (1-bit or 4-bit) unless
* the chip is accessed in "raw" mode.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "arm_io.h"
#include <target/target.h>
enum ecc {
HWECC1, /* all controllers support 1-bit ECC */
HWECC4, /* newer chips also have 4-bit ECC hardware */
HWECC4_INFIX, /* avoid this layout, except maybe for boot code */
};
struct davinci_nand {
uint8_t chipsel; /* chipselect 0..3 == CS2..CS5 */
uint8_t eccmode;
/* Async EMIF controller base */
uint32_t aemif;
/* NAND chip addresses */
uint32_t data; /* without CLE or ALE */
uint32_t cmd; /* with CLE */
uint32_t addr; /* with ALE */
/* write acceleration */
struct arm_nand_data io;
/* page i/o for the relevant flavor of hardware ECC */
int (*read_page)(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size);
int (*write_page)(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size);
};
#define NANDFCR 0x60 /* flash control register */
#define NANDFSR 0x64 /* flash status register */
#define NANDFECC 0x70 /* 1-bit ECC data, CS0, 1st of 4 */
#define NAND4BITECCLOAD 0xbc /* 4-bit ECC, load saved values */
#define NAND4BITECC 0xc0 /* 4-bit ECC data, 1st of 4 */
#define NANDERRADDR 0xd0 /* 4-bit ECC err addr, 1st of 2 */
#define NANDERRVAL 0xd8 /* 4-bit ECC err value, 1st of 2 */
static int halted(struct target *target, const char *label)
{
if (target->state == TARGET_HALTED)
return true;
LOG_ERROR("Target must be halted to use NAND controller (%s)", label);
return false;
}
static int davinci_init(struct nand_device *nand)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint32_t nandfcr;
if (!halted(target, "init"))
return ERROR_NAND_OPERATION_FAILED;
/* We require something else to have configured AEMIF to talk
* to NAND chip in this range (including timings and width).
*/
target_read_u32(target, info->aemif + NANDFCR, &nandfcr);
if (!(nandfcr & (1 << info->chipsel))) {
LOG_ERROR("chip address %08" PRIx32 " not NAND-enabled?", info->data);
return ERROR_NAND_OPERATION_FAILED;
}
/* REVISIT verify: AxCR must be in 8-bit mode, since that's all we
* tested. 16 bit support should work too; but not with 4-bit ECC.
*/
return ERROR_OK;
}
static int davinci_reset(struct nand_device *nand)
{
return ERROR_OK;
}
static int davinci_nand_ready(struct nand_device *nand, int timeout)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint32_t nandfsr;
/* NOTE: return code is zero/error, else success; not ERROR_* */
if (!halted(target, "ready"))
return 0;
do {
target_read_u32(target, info->aemif + NANDFSR, &nandfsr);
if (nandfsr & 0x01)
return 1;
alive_sleep(1);
} while (timeout-- > 0);
return 0;
}
static int davinci_command(struct nand_device *nand, uint8_t command)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!halted(target, "command"))
return ERROR_NAND_OPERATION_FAILED;
target_write_u8(target, info->cmd, command);
return ERROR_OK;
}
static int davinci_address(struct nand_device *nand, uint8_t address)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!halted(target, "address"))
return ERROR_NAND_OPERATION_FAILED;
target_write_u8(target, info->addr, address);
return ERROR_OK;
}
static int davinci_write_data(struct nand_device *nand, uint16_t data)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!halted(target, "write_data"))
return ERROR_NAND_OPERATION_FAILED;
target_write_u8(target, info->data, data);
return ERROR_OK;
}
static int davinci_read_data(struct nand_device *nand, void *data)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
if (!halted(target, "read_data"))
return ERROR_NAND_OPERATION_FAILED;
target_read_u8(target, info->data, data);
return ERROR_OK;
}
/* REVISIT a bit of native code should let block reads be MUCH faster */
static int davinci_read_block_data(struct nand_device *nand,
uint8_t *data, int data_size)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint32_t nfdata = info->data;
uint32_t tmp;
if (!halted(target, "read_block"))
return ERROR_NAND_OPERATION_FAILED;
while (data_size >= 4) {
target_read_u32(target, nfdata, &tmp);
data[0] = tmp;
data[1] = tmp >> 8;
data[2] = tmp >> 16;
data[3] = tmp >> 24;
data_size -= 4;
data += 4;
}
while (data_size > 0) {
target_read_u8(target, nfdata, data);
data_size -= 1;
data += 1;
}
return ERROR_OK;
}
static int davinci_write_block_data(struct nand_device *nand,
uint8_t *data, int data_size)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint32_t nfdata = info->data;
uint32_t tmp;
int status;
if (!halted(target, "write_block"))
return ERROR_NAND_OPERATION_FAILED;
/* try the fast way first */
status = arm_nandwrite(&info->io, data, data_size);
if (status != ERROR_NAND_NO_BUFFER)
return status;
/* else do it slowly */
while (data_size >= 4) {
tmp = le_to_h_u32(data);
target_write_u32(target, nfdata, tmp);
data_size -= 4;
data += 4;
}
while (data_size > 0) {
target_write_u8(target, nfdata, *data);
data_size -= 1;
data += 1;
}
return ERROR_OK;
}
static int davinci_write_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
struct davinci_nand *info = nand->controller_priv;
uint8_t *ooballoc = NULL;
int status;
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if (!halted(nand->target, "write_page"))
return ERROR_NAND_OPERATION_FAILED;
/* Always write both data and OOB ... we are not "raw" I/O! */
if (!data) {
LOG_ERROR("Missing NAND data; try 'nand raw_access enable'");
return ERROR_NAND_OPERATION_FAILED;
}
/* If we're not given OOB, write 0xff where we don't write ECC codes. */
switch (nand->page_size) {
case 512:
oob_size = 16;
break;
case 2048:
oob_size = 64;
break;
case 4096:
oob_size = 128;
break;
default:
return ERROR_NAND_OPERATION_FAILED;
}
if (!oob) {
ooballoc = malloc(oob_size);
if (!ooballoc)
return ERROR_NAND_OPERATION_FAILED;
oob = ooballoc;
memset(oob, 0x0ff, oob_size);
}
/* REVISIT avoid wasting SRAM: unless nand->use_raw is set,
* use 512 byte chunks. Read side support will often want
* to include oob_size ...
*/
info->io.chunk_size = nand->page_size;
status = info->write_page(nand, page, data, data_size, oob, oob_size);
free(ooballoc);
return status;
}
static int davinci_read_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
struct davinci_nand *info = nand->controller_priv;
if (!nand->device)
return ERROR_NAND_DEVICE_NOT_PROBED;
if (!halted(nand->target, "read_page"))
return ERROR_NAND_OPERATION_FAILED;
return info->read_page(nand, page, data, data_size, oob, oob_size);
}
static void davinci_write_pagecmd(struct nand_device *nand, uint8_t cmd, uint32_t page)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
int page3 = nand->address_cycles - (nand->page_size == 512);
/* write command ({page,otp}x{read,program} */
target_write_u8(target, info->cmd, cmd);
/* column address (beginning-of-page) */
target_write_u8(target, info->addr, 0);
if (nand->page_size > 512)
target_write_u8(target, info->addr, 0);
/* page address */
target_write_u8(target, info->addr, page);
target_write_u8(target, info->addr, page >> 8);
if (page3)
target_write_u8(target, info->addr, page >> 16);
if (page3 == 2)
target_write_u8(target, info->addr, page >> 24);
}
static int davinci_seek_column(struct nand_device *nand, uint16_t column)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
/* Random read, we must have issued a page read already */
target_write_u8(target, info->cmd, NAND_CMD_RNDOUT);
target_write_u8(target, info->addr, column);
if (nand->page_size > 512) {
target_write_u8(target, info->addr, column >> 8);
target_write_u8(target, info->cmd, NAND_CMD_RNDOUTSTART);
}
if (!davinci_nand_ready(nand, 100))
return ERROR_NAND_OPERATION_TIMEOUT;
return ERROR_OK;
}
static int davinci_writepage_tail(struct nand_device *nand,
uint8_t *oob, uint32_t oob_size)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
uint8_t status;
if (oob_size)
davinci_write_block_data(nand, oob, oob_size);
/* non-cachemode page program */
target_write_u8(target, info->cmd, NAND_CMD_PAGEPROG);
if (!davinci_nand_ready(nand, 100))
return ERROR_NAND_OPERATION_TIMEOUT;
if (nand_read_status(nand, &status) != ERROR_OK) {
LOG_ERROR("couldn't read status");
return ERROR_NAND_OPERATION_FAILED;
}
if (status & NAND_STATUS_FAIL) {
LOG_ERROR("write operation failed, status: 0x%02x", status);
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
/*
* All DaVinci family chips support 1-bit ECC on a per-chipselect basis.
*/
static int davinci_write_page_ecc1(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
unsigned oob_offset;
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
const uint32_t fcr_addr = info->aemif + NANDFCR;
const uint32_t ecc1_addr = info->aemif + NANDFECC + (4 * info->chipsel);
uint32_t fcr, ecc1;
/* Write contiguous ECC bytes starting at specified offset.
* NOTE: Linux reserves twice as many bytes as we need; and
* for 16-bit OOB, those extra bytes are discontiguous.
*/
switch (nand->page_size) {
case 512:
oob_offset = 0;
break;
case 2048:
oob_offset = 40;
break;
default:
oob_offset = 80;
break;
}
davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);
/* scrub any old ECC state */
target_read_u32(target, ecc1_addr, &ecc1);
target_read_u32(target, fcr_addr, &fcr);
fcr |= 1 << (8 + info->chipsel);
do {
/* set "start csX 1bit ecc" bit */
target_write_u32(target, fcr_addr, fcr);
/* write 512 bytes */
davinci_write_block_data(nand, data, 512);
data += 512;
data_size -= 512;
/* read the ecc, pack to 3 bytes, and invert so the ecc
* in an erased block is correct
*/
target_read_u32(target, ecc1_addr, &ecc1);
ecc1 = (ecc1 & 0x0fff) | ((ecc1 & 0x0fff0000) >> 4);
ecc1 = ~ecc1;
/* save correct ECC code into oob data */
oob[oob_offset++] = (uint8_t)(ecc1);
oob[oob_offset++] = (uint8_t)(ecc1 >> 8);
oob[oob_offset++] = (uint8_t)(ecc1 >> 16);
} while (data_size);
/* write OOB into spare area */
return davinci_writepage_tail(nand, oob, oob_size);
}
/*
* Preferred "new style" ECC layout for use with 4-bit ECC. This somewhat
* slows down large page reads done with error correction (since the OOB
* is read first, so its ECC data can be used incrementally), but the
* manufacturer bad block markers are safe. Contrast: old "infix" style.
*/
static int davinci_write_page_ecc4(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
static const uint8_t ecc512[] = {
0, 1, 2, 3, 4, /* 5== mfr badblock */
6, 7, /* 8..12 for BBT or JFFS2 */ 13, 14, 15,
};
static const uint8_t ecc2048[] = {
24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
};
static const uint8_t ecc4096[] = {
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
};
struct davinci_nand *info = nand->controller_priv;
const uint8_t *l;
struct target *target = nand->target;
const uint32_t fcr_addr = info->aemif + NANDFCR;
const uint32_t ecc4_addr = info->aemif + NAND4BITECC;
uint32_t fcr, ecc4;
/* Use the same ECC layout Linux uses. For small page chips
* it's a bit cramped.
*
* NOTE: at this writing, 4KB pages have issues in Linux
* because they need more than 64 bytes of ECC data, which
* the standard ECC logic can't handle.
*/
switch (nand->page_size) {
case 512:
l = ecc512;
break;
case 2048:
l = ecc2048;
break;
default:
l = ecc4096;
break;
}
davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);
/* scrub any old ECC state */
target_read_u32(target, info->aemif + NANDERRVAL, &ecc4);
target_read_u32(target, fcr_addr, &fcr);
fcr &= ~(0x03 << 4);
fcr |= (1 << 12) | (info->chipsel << 4);
do {
uint32_t raw_ecc[4], *p;
int i;
/* start 4bit ecc on csX */
target_write_u32(target, fcr_addr, fcr);
/* write 512 bytes */
davinci_write_block_data(nand, data, 512);
data += 512;
data_size -= 512;
/* read the ecc, then save it into 10 bytes in the oob */
for (i = 0; i < 4; i++) {
target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]);
raw_ecc[i] &= 0x03ff03ff;
}
for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
oob[*l++] = p[0] & 0xff;
oob[*l++] = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
oob[*l++] = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
oob[*l++] = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
oob[*l++] = (p[1] >> 18) & 0xff;
}
} while (data_size);
/* write OOB into spare area */
return davinci_writepage_tail(nand, oob, oob_size);
}
/*
* "Infix" OOB ... like Linux ECC_HW_SYNDROME. Avoided because it trashes
* manufacturer bad block markers, except on small page chips. Once you
* write to a page using this scheme, you need specialized code to update
* it (code which ignores now-invalid bad block markers).
*
* This is needed *only* to support older firmware. Older ROM Boot Loaders
* need it to read their second stage loader (UBL) into SRAM, but from then
* on the whole system can use the cleaner non-infix layouts. Systems with
* older second stage loaders (ABL/U-Boot, etc) or other system software
* (MVL 4.x/5.x kernels, filesystems, etc) may need it more generally.
*/
static int davinci_write_page_ecc4infix(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
struct davinci_nand *info = nand->controller_priv;
struct target *target = nand->target;
const uint32_t fcr_addr = info->aemif + NANDFCR;
const uint32_t ecc4_addr = info->aemif + NAND4BITECC;
uint32_t fcr, ecc4;
davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);
/* scrub any old ECC state */
target_read_u32(target, info->aemif + NANDERRVAL, &ecc4);
target_read_u32(target, fcr_addr, &fcr);
fcr &= ~(0x03 << 4);
fcr |= (1 << 12) | (info->chipsel << 4);
do {
uint32_t raw_ecc[4], *p;
uint8_t *l;
int i;
/* start 4bit ecc on csX */
target_write_u32(target, fcr_addr, fcr);
/* write 512 bytes */
davinci_write_block_data(nand, data, 512);
data += 512;
data_size -= 512;
/* read the ecc */
for (i = 0; i < 4; i++) {
target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]);
raw_ecc[i] &= 0x03ff03ff;
}
/* skip 6 bytes of prepad, then pack 10 packed ecc bytes */
for (i = 0, l = oob + 6, p = raw_ecc; i < 2; i++, p += 2) {
*l++ = p[0] & 0xff;
*l++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
*l++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
*l++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
*l++ = (p[1] >> 18) & 0xff;
}
/* write this "out-of-band" data -- infix */
davinci_write_block_data(nand, oob, 16);
oob += 16;
oob_size -= 16;
} while (data_size);
/* the last data and OOB writes included the spare area */
return davinci_writepage_tail(nand, NULL, 0);
}
static int davinci_read_page_ecc4infix(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob, uint32_t oob_size)
{
int read_size;
int want_col, at_col;
int ret;
davinci_write_pagecmd(nand, NAND_CMD_READ0, page);
/* large page devices need a start command */
if (nand->page_size > 512)
davinci_command(nand, NAND_CMD_READSTART);
if (!davinci_nand_ready(nand, 100))
return ERROR_NAND_OPERATION_TIMEOUT;
/* NOTE: not bothering to compute and use ECC data for now */
want_col = 0;
at_col = 0;
while ((data && data_size) || (oob && oob_size)) {
if (data && data_size) {
if (want_col != at_col) {
/* Reads are slow, so seek past them when we can */
ret = davinci_seek_column(nand, want_col);
if (ret != ERROR_OK)
return ret;
at_col = want_col;
}
/* read 512 bytes or data_size, whichever is smaller*/
read_size = data_size > 512 ? 512 : data_size;
davinci_read_block_data(nand, data, read_size);
data += read_size;
data_size -= read_size;
at_col += read_size;
}
want_col += 512;
if (oob && oob_size) {
if (want_col != at_col) {
ret = davinci_seek_column(nand, want_col);
if (ret != ERROR_OK)
return ret;
at_col = want_col;
}
/* read this "out-of-band" data -- infix */
read_size = oob_size > 16 ? 16 : oob_size;
davinci_read_block_data(nand, oob, read_size);
oob += read_size;
oob_size -= read_size;
at_col += read_size;
}
want_col += 16;
}
return ERROR_OK;
}
NAND_DEVICE_COMMAND_HANDLER(davinci_nand_device_command)
{
struct davinci_nand *info;
unsigned long chip, aemif;
enum ecc eccmode;
int chipsel;
/* arguments:
* - "davinci"
* - target
* - nand chip address
* - ecc mode
* - aemif address
* Plus someday, optionally, ALE and CLE masks.
*/
if (CMD_ARGC < 5)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[2], chip);
if (chip == 0) {
LOG_ERROR("Invalid NAND chip address %s", CMD_ARGV[2]);
goto fail;
}
if (strcmp(CMD_ARGV[3], "hwecc1") == 0)
eccmode = HWECC1;
else if (strcmp(CMD_ARGV[3], "hwecc4") == 0)
eccmode = HWECC4;
else if (strcmp(CMD_ARGV[3], "hwecc4_infix") == 0)
eccmode = HWECC4_INFIX;
else {
LOG_ERROR("Invalid ecc mode %s", CMD_ARGV[3]);
goto fail;
}
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[4], aemif);
if (aemif == 0) {
LOG_ERROR("Invalid AEMIF controller address %s", CMD_ARGV[4]);
goto fail;
}
/* REVISIT what we'd *like* to do is look up valid ranges using
* target-specific declarations, and not even need to pass the
* AEMIF controller address.
*/
if (aemif == 0x01e00000 /* dm6446, dm357 */
|| aemif == 0x01e10000 /* dm335, dm355 */
|| aemif == 0x01d10000 /* dm365 */
) {
if (chip < 0x02000000 || chip >= 0x0a000000) {
LOG_ERROR("NAND address %08lx out of range?", chip);
goto fail;
}
chipsel = (chip - 0x02000000) >> 25;
} else {
LOG_ERROR("unrecognized AEMIF controller address %08lx", aemif);
goto fail;
}
info = calloc(1, sizeof *info);
if (info == NULL)
goto fail;
info->eccmode = eccmode;
info->chipsel = chipsel;
info->aemif = aemif;
info->data = chip;
info->cmd = chip | 0x10;
info->addr = chip | 0x08;
nand->controller_priv = info;
info->io.target = nand->target;
info->io.data = info->data;
info->io.op = ARM_NAND_NONE;
/* NOTE: for now we don't do any error correction on read.
* Nothing else in OpenOCD currently corrects read errors,
* and in any case it's *writing* that we care most about.
*/
info->read_page = nand_read_page_raw;
switch (eccmode) {
case HWECC1:
/* ECC_HW, 1-bit corrections, 3 bytes ECC per 512 data bytes */
info->write_page = davinci_write_page_ecc1;
break;
case HWECC4:
/* ECC_HW, 4-bit corrections, 10 bytes ECC per 512 data bytes */
info->write_page = davinci_write_page_ecc4;
break;
case HWECC4_INFIX:
/* Same 4-bit ECC HW, with problematic page/ecc layout */
info->read_page = davinci_read_page_ecc4infix;
info->write_page = davinci_write_page_ecc4infix;
break;
}
return ERROR_OK;
fail:
return ERROR_NAND_OPERATION_FAILED;
}
struct nand_flash_controller davinci_nand_controller = {
.name = "davinci",
.usage = "chip_addr hwecc_mode aemif_addr",
.nand_device_command = davinci_nand_device_command,
.init = davinci_init,
.reset = davinci_reset,
.command = davinci_command,
.address = davinci_address,
.write_data = davinci_write_data,
.read_data = davinci_read_data,
.write_page = davinci_write_page,
.read_page = davinci_read_page,
.write_block_data = davinci_write_block_data,
.read_block_data = davinci_read_block_data,
.nand_ready = davinci_nand_ready,
};
+84
View File
@@ -0,0 +1,84 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "core.h"
#include "driver.h"
/* NAND flash controller
*/
extern struct nand_flash_controller nonce_nand_controller;
extern struct nand_flash_controller davinci_nand_controller;
extern struct nand_flash_controller lpc3180_nand_controller;
extern struct nand_flash_controller lpc32xx_nand_controller;
extern struct nand_flash_controller orion_nand_controller;
extern struct nand_flash_controller s3c2410_nand_controller;
extern struct nand_flash_controller s3c2412_nand_controller;
extern struct nand_flash_controller s3c2440_nand_controller;
extern struct nand_flash_controller s3c2443_nand_controller;
extern struct nand_flash_controller s3c6400_nand_controller;
extern struct nand_flash_controller mxc_nand_flash_controller;
extern struct nand_flash_controller imx31_nand_flash_controller;
extern struct nand_flash_controller at91sam9_nand_controller;
extern struct nand_flash_controller nuc910_nand_controller;
/* extern struct nand_flash_controller boundary_scan_nand_controller; */
static struct nand_flash_controller *nand_flash_controllers[] = {
&nonce_nand_controller,
&davinci_nand_controller,
&lpc3180_nand_controller,
&lpc32xx_nand_controller,
&orion_nand_controller,
&s3c2410_nand_controller,
&s3c2412_nand_controller,
&s3c2440_nand_controller,
&s3c2443_nand_controller,
&s3c6400_nand_controller,
&mxc_nand_flash_controller,
&imx31_nand_flash_controller,
&at91sam9_nand_controller,
&nuc910_nand_controller,
/* &boundary_scan_nand_controller, */
NULL
};
struct nand_flash_controller *nand_driver_find_by_name(const char *name)
{
for (unsigned i = 0; nand_flash_controllers[i]; i++) {
struct nand_flash_controller *controller = nand_flash_controllers[i];
if (strcmp(name, controller->name) == 0)
return controller;
}
return NULL;
}
int nand_driver_walk(nand_driver_walker_t f, void *x)
{
for (unsigned i = 0; nand_flash_controllers[i]; i++) {
int retval = (*f)(nand_flash_controllers[i], x);
if (ERROR_OK != retval)
return retval;
}
return ERROR_OK;
}
+105
View File
@@ -0,0 +1,105 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NAND_DRIVER_H
#define FLASH_NAND_DRIVER_H
struct nand_device;
#define __NAND_DEVICE_COMMAND(name) \
COMMAND_HELPER(name, struct nand_device *nand)
/**
* Interface for NAND flash controllers. Not all of these functions are
* required for full functionality of the NAND driver, but better performance
* can be achieved by implementing each function.
*/
struct nand_flash_controller {
/** Driver name that is used to select it from configuration files. */
const char *name;
/** Usage of flash command registration. */
const char *usage;
const struct command_registration *commands;
/** NAND device command called when driver is instantiated during configuration. */
__NAND_DEVICE_COMMAND((*nand_device_command));
/** Initialize the NAND device. */
int (*init)(struct nand_device *nand);
/** Reset the NAND device. */
int (*reset)(struct nand_device *nand);
/** Issue a command to the NAND device. */
int (*command)(struct nand_device *nand, uint8_t command);
/** Write an address to the NAND device. */
int (*address)(struct nand_device *nand, uint8_t address);
/** Write word of data to the NAND device. */
int (*write_data)(struct nand_device *nand, uint16_t data);
/** Read word of data from the NAND device. */
int (*read_data)(struct nand_device *nand, void *data);
/** Write a block of data to the NAND device. */
int (*write_block_data)(struct nand_device *nand, uint8_t *data, int size);
/** Read a block of data from the NAND device. */
int (*read_block_data)(struct nand_device *nand, uint8_t *data, int size);
/** Write a page to the NAND device. */
int (*write_page)(struct nand_device *nand, uint32_t page, uint8_t *data,
uint32_t data_size, uint8_t *oob, uint32_t oob_size);
/** Read a page from the NAND device. */
int (*read_page)(struct nand_device *nand, uint32_t page, uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size);
/** Check if the NAND device is ready for more instructions with timeout. */
int (*nand_ready)(struct nand_device *nand, int timeout);
};
#define NAND_DEVICE_COMMAND_HANDLER(name) static __NAND_DEVICE_COMMAND(name)
/**
* Find a NAND flash controller by name.
* @param name Identifies the NAND controller to find.
* @returns The nand_flash_controller named @c name, or NULL if not found.
*/
struct nand_flash_controller *nand_driver_find_by_name(const char *name);
/** Signature for callback functions passed to nand_driver_walk */
typedef int (*nand_driver_walker_t)(struct nand_flash_controller *c, void *);
/**
* Walk the list of drivers, encapsulating the data structure type.
* Application state/context can be passed through the @c x pointer.
* @param f The callback function to invoke for each function.
* @param x For use as private data storate, passed directly to @c f.
* @returns ERROR_OK if successful, or the non-zero return value of @c f.
* This allows a walker to terminate the loop early.
*/
int nand_driver_walk(nand_driver_walker_t f, void *x);
#endif /* FLASH_NAND_DRIVER_H */
+183
View File
@@ -0,0 +1,183 @@
/*
* This file contains an ECC algorithm from Toshiba that allows for detection
* and correction of 1-bit errors in a 256 byte block of data.
*
* [ Extracted from the initial code found in some early Linux versions.
* The current Linux code is bigger while being faster, but this is of
* no real benefit when the bottleneck largely remains the JTAG link. ]
*
* Copyright (C) 2000-2004 Steven J. Hill (sjhill at realitydiluted.com)
* Toshiba America Electronics Components, Inc.
*
* Copyright (C) 2006 Thomas Gleixner <tglx at linutronix.de>
*
* This file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 or (at your option) any
* later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this file; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* As a special exception, if other files instantiate templates or use
* macros or inline functions from these files, or you compile these
* files and link them with other works to produce a work based on these
* files, these files do not by themselves cause the resulting work to be
* covered by the GNU General Public License. However the source code for
* these files must still be made available in accordance with section (3)
* of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work based on
* this file might be covered by the GNU General Public License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "core.h"
/*
* Pre-calculated 256-way 1 byte column parity
*/
static const uint8_t nand_ecc_precalc_table[] = {
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
};
/*
* nand_calculate_ecc - Calculate 3-byte ECC for 256-byte block
*/
int nand_calculate_ecc(struct nand_device *nand, const uint8_t *dat, uint8_t *ecc_code)
{
uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
int i;
/* Initialize variables */
reg1 = reg2 = reg3 = 0;
/* Build up column parity */
for (i = 0; i < 256; i++) {
/* Get CP0 - CP5 from table */
idx = nand_ecc_precalc_table[*dat++];
reg1 ^= (idx & 0x3f);
/* All bit XOR = 1 ? */
if (idx & 0x40) {
reg3 ^= (uint8_t) i;
reg2 ^= ~((uint8_t) i);
}
}
/* Create non-inverted ECC code from line parity */
tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
/* Calculate final ECC code */
#ifdef NAND_ECC_SMC
ecc_code[0] = ~tmp2;
ecc_code[1] = ~tmp1;
#else
ecc_code[0] = ~tmp1;
ecc_code[1] = ~tmp2;
#endif
ecc_code[2] = ((~reg1) << 2) | 0x03;
return 0;
}
static inline int countbits(uint32_t b)
{
int res = 0;
for (; b; b >>= 1)
res += b & 0x01;
return res;
}
/**
* nand_correct_data - Detect and correct a 1 bit error for 256 byte block
*/
int nand_correct_data(struct nand_device *nand, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
uint8_t s0, s1, s2;
#ifdef NAND_ECC_SMC
s0 = calc_ecc[0] ^ read_ecc[0];
s1 = calc_ecc[1] ^ read_ecc[1];
s2 = calc_ecc[2] ^ read_ecc[2];
#else
s1 = calc_ecc[0] ^ read_ecc[0];
s0 = calc_ecc[1] ^ read_ecc[1];
s2 = calc_ecc[2] ^ read_ecc[2];
#endif
if ((s0 | s1 | s2) == 0)
return 0;
/* Check for a single bit error */
if (((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
uint32_t byteoffs, bitnum;
byteoffs = (s1 << 0) & 0x80;
byteoffs |= (s1 << 1) & 0x40;
byteoffs |= (s1 << 2) & 0x20;
byteoffs |= (s1 << 3) & 0x10;
byteoffs |= (s0 >> 4) & 0x08;
byteoffs |= (s0 >> 3) & 0x04;
byteoffs |= (s0 >> 2) & 0x02;
byteoffs |= (s0 >> 1) & 0x01;
bitnum = (s2 >> 5) & 0x04;
bitnum |= (s2 >> 4) & 0x02;
bitnum |= (s2 >> 3) & 0x01;
dat[byteoffs] ^= (1 << bitnum);
return 1;
}
if (countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 << 16)) == 1)
return 1;
return -1;
}
+171
View File
@@ -0,0 +1,171 @@
/*
* Reed-Solomon ECC handling for the Marvell Kirkwood SOC
* Copyright (C) 2009 Marvell Semiconductor, Inc.
*
* Authors: Lennert Buytenhek <buytenh@wantstofly.org>
* Nicolas Pitre <nico@fluxnic.net>
*
* This file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 or (at your option) any
* later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "core.h"
/*****************************************************************************
* Arithmetic in GF(2^10) ("F") modulo x^10 + x^3 + 1.
*
* For multiplication, a discrete log/exponent table is used, with
* primitive element x (F is a primitive field, so x is primitive).
*/
#define MODPOLY 0x409 /* x^10 + x^3 + 1 in binary */
/*
* Maps an integer a [0..1022] to a polynomial b = gf_exp[a] in
* GF(2^10) mod x^10 + x^3 + 1 such that b = x ^ a. There's two
* identical copies of this array back-to-back so that we can save
* the mod 1023 operation when doing a GF multiplication.
*/
static uint16_t gf_exp[1023 + 1023];
/*
* Maps a polynomial b in GF(2^10) mod x^10 + x^3 + 1 to an index
* a = gf_log[b] in [0..1022] such that b = x ^ a.
*/
static uint16_t gf_log[1024];
static void gf_build_log_exp_table(void)
{
int i;
int p_i;
/*
* p_i = x ^ i
*
* Initialise to 1 for i = 0.
*/
p_i = 1;
for (i = 0; i < 1023; i++) {
gf_exp[i] = p_i;
gf_exp[i + 1023] = p_i;
gf_log[p_i] = i;
/*
* p_i = p_i * x
*/
p_i <<= 1;
if (p_i & (1 << 10))
p_i ^= MODPOLY;
}
}
/*****************************************************************************
* Reed-Solomon code
*
* This implements a (1023,1015) Reed-Solomon ECC code over GF(2^10)
* mod x^10 + x^3 + 1, shortened to (520,512). The ECC data consists
* of 8 10-bit symbols, or 10 8-bit bytes.
*
* Given 512 bytes of data, computes 10 bytes of ECC.
*
* This is done by converting the 512 bytes to 512 10-bit symbols
* (elements of F), interpreting those symbols as a polynomial in F[X]
* by taking symbol 0 as the coefficient of X^8 and symbol 511 as the
* coefficient of X^519, and calculating the residue of that polynomial
* divided by the generator polynomial, which gives us the 8 ECC symbols
* as the remainder. Finally, we convert the 8 10-bit ECC symbols to 10
* 8-bit bytes.
*
* The generator polynomial is hardcoded, as that is faster, but it
* can be computed by taking the primitive element a = x (in F), and
* constructing a polynomial in F[X] with roots a, a^2, a^3, ..., a^8
* by multiplying the minimal polynomials for those roots (which are
* just 'x - a^i' for each i).
*
* Note: due to unfortunate circumstances, the bootrom in the Kirkwood SOC
* expects the ECC to be computed backward, i.e. from the last byte down
* to the first one.
*/
int nand_calculate_ecc_kw(struct nand_device *nand, const uint8_t *data, uint8_t *ecc)
{
unsigned int r7, r6, r5, r4, r3, r2, r1, r0;
int i;
static int tables_initialized;
if (!tables_initialized) {
gf_build_log_exp_table();
tables_initialized = 1;
}
/*
* Load bytes 504..511 of the data into r.
*/
r0 = data[504];
r1 = data[505];
r2 = data[506];
r3 = data[507];
r4 = data[508];
r5 = data[509];
r6 = data[510];
r7 = data[511];
/*
* Shift bytes 503..0 (in that order) into r0, followed
* by eight zero bytes, while reducing the polynomial by the
* generator polynomial in every step.
*/
for (i = 503; i >= -8; i--) {
unsigned int d;
d = 0;
if (i >= 0)
d = data[i];
if (r7) {
uint16_t *t = gf_exp + gf_log[r7];
r7 = r6 ^ t[0x21c];
r6 = r5 ^ t[0x181];
r5 = r4 ^ t[0x18e];
r4 = r3 ^ t[0x25f];
r3 = r2 ^ t[0x197];
r2 = r1 ^ t[0x193];
r1 = r0 ^ t[0x237];
r0 = d ^ t[0x024];
} else {
r7 = r6;
r6 = r5;
r5 = r4;
r4 = r3;
r3 = r2;
r2 = r1;
r1 = r0;
r0 = d;
}
}
ecc[0] = r0;
ecc[1] = (r0 >> 8) | (r1 << 2);
ecc[2] = (r1 >> 6) | (r2 << 4);
ecc[3] = (r2 >> 4) | (r3 << 6);
ecc[4] = (r3 >> 2);
ecc[5] = r4;
ecc[6] = (r4 >> 8) | (r5 << 2);
ecc[7] = (r5 >> 6) | (r6 << 4);
ecc[8] = (r6 >> 4) | (r7 << 6);
ecc[9] = (r7 >> 2);
return 0;
}
+228
View File
@@ -0,0 +1,228 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2002 Thomas Gleixner <tglx@linutronix.de> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* Partially based on drivers/mtd/nand_ids.c from Linux. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "core.h"
#include "fileio.h"
static struct nand_ecclayout nand_oob_16 = {
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 6, 7},
.oobfree = {
{.offset = 8,
.length = 8}
}
};
static struct nand_ecclayout nand_oob_64 = {
.eccbytes = 24,
.eccpos = {
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63
},
.oobfree = {
{.offset = 2,
.length = 38}
}
};
void nand_fileio_init(struct nand_fileio_state *state)
{
memset(state, 0, sizeof(*state));
state->oob_format = NAND_OOB_NONE;
}
int nand_fileio_start(struct command_context *cmd_ctx,
struct nand_device *nand, const char *filename, int filemode,
struct nand_fileio_state *state)
{
if (state->address % nand->page_size) {
command_print(cmd_ctx, "only page-aligned addresses are supported");
return ERROR_COMMAND_SYNTAX_ERROR;
}
duration_start(&state->bench);
if (NULL != filename) {
int retval = fileio_open(&state->fileio, filename, filemode, FILEIO_BINARY);
if (ERROR_OK != retval) {
const char *msg = (FILEIO_READ == filemode) ? "read" : "write";
command_print(cmd_ctx, "failed to open '%s' for %s access",
filename, msg);
return retval;
}
state->file_opened = true;
}
if (!(state->oob_format & NAND_OOB_ONLY)) {
state->page_size = nand->page_size;
state->page = malloc(nand->page_size);
}
if (state->oob_format & (NAND_OOB_RAW | NAND_OOB_SW_ECC | NAND_OOB_SW_ECC_KW)) {
if (nand->page_size == 512) {
state->oob_size = 16;
state->eccpos = nand_oob_16.eccpos;
} else if (nand->page_size == 2048) {
state->oob_size = 64;
state->eccpos = nand_oob_64.eccpos;
}
state->oob = malloc(state->oob_size);
}
return ERROR_OK;
}
int nand_fileio_cleanup(struct nand_fileio_state *state)
{
if (state->file_opened)
fileio_close(&state->fileio);
if (state->oob) {
free(state->oob);
state->oob = NULL;
}
if (state->page) {
free(state->page);
state->page = NULL;
}
return ERROR_OK;
}
int nand_fileio_finish(struct nand_fileio_state *state)
{
nand_fileio_cleanup(state);
return duration_measure(&state->bench);
}
COMMAND_HELPER(nand_fileio_parse_args, struct nand_fileio_state *state,
struct nand_device **dev, enum fileio_access filemode,
bool need_size, bool sw_ecc)
{
nand_fileio_init(state);
unsigned minargs = need_size ? 4 : 3;
if (CMD_ARGC < minargs)
return ERROR_COMMAND_SYNTAX_ERROR;
struct nand_device *nand;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &nand);
if (ERROR_OK != retval)
return retval;
if (NULL == nand->device) {
command_print(CMD_CTX, "#%s: not probed", CMD_ARGV[0]);
return ERROR_OK;
}
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], state->address);
if (need_size) {
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], state->size);
if (state->size % nand->page_size) {
command_print(CMD_CTX, "only page-aligned sizes are supported");
return ERROR_COMMAND_SYNTAX_ERROR;
}
}
if (CMD_ARGC > minargs) {
for (unsigned i = minargs; i < CMD_ARGC; i++) {
if (!strcmp(CMD_ARGV[i], "oob_raw"))
state->oob_format |= NAND_OOB_RAW;
else if (!strcmp(CMD_ARGV[i], "oob_only"))
state->oob_format |= NAND_OOB_RAW | NAND_OOB_ONLY;
else if (sw_ecc && !strcmp(CMD_ARGV[i], "oob_softecc"))
state->oob_format |= NAND_OOB_SW_ECC;
else if (sw_ecc && !strcmp(CMD_ARGV[i], "oob_softecc_kw"))
state->oob_format |= NAND_OOB_SW_ECC_KW;
else {
command_print(CMD_CTX, "unknown option: %s", CMD_ARGV[i]);
return ERROR_COMMAND_SYNTAX_ERROR;
}
}
}
retval = nand_fileio_start(CMD_CTX, nand, CMD_ARGV[1], filemode, state);
if (ERROR_OK != retval)
return retval;
if (!need_size) {
int filesize;
retval = fileio_size(&state->fileio, &filesize);
if (retval != ERROR_OK)
return retval;
state->size = filesize;
}
*dev = nand;
return ERROR_OK;
}
/**
* @returns If no error occurred, returns number of bytes consumed;
* otherwise, returns a negative error code.)
*/
int nand_fileio_read(struct nand_device *nand, struct nand_fileio_state *s)
{
size_t total_read = 0;
size_t one_read;
if (NULL != s->page) {
fileio_read(&s->fileio, s->page_size, s->page, &one_read);
if (one_read < s->page_size)
memset(s->page + one_read, 0xff, s->page_size - one_read);
total_read += one_read;
}
if (s->oob_format & NAND_OOB_SW_ECC) {
uint8_t ecc[3];
memset(s->oob, 0xff, s->oob_size);
for (uint32_t i = 0, j = 0; i < s->page_size; i += 256) {
nand_calculate_ecc(nand, s->page + i, ecc);
s->oob[s->eccpos[j++]] = ecc[0];
s->oob[s->eccpos[j++]] = ecc[1];
s->oob[s->eccpos[j++]] = ecc[2];
}
} else if (s->oob_format & NAND_OOB_SW_ECC_KW) {
/*
* In this case eccpos is not used as
* the ECC data is always stored contigously
* at the end of the OOB area. It consists
* of 10 bytes per 512-byte data block.
*/
uint8_t *ecc = s->oob + s->oob_size - s->page_size / 512 * 10;
memset(s->oob, 0xff, s->oob_size);
for (uint32_t i = 0; i < s->page_size; i += 512) {
nand_calculate_ecc_kw(nand, s->page + i, ecc);
ecc += 10;
}
} else if (NULL != s->oob) {
fileio_read(&s->fileio, s->oob_size, s->oob, &one_read);
if (one_read < s->oob_size)
memset(s->oob + one_read, 0xff, s->oob_size - one_read);
total_read += one_read;
}
return total_read;
}
+58
View File
@@ -0,0 +1,58 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NAND_FILEIO_H
#define FLASH_NAND_FILEIO_H
#include <helper/time_support.h>
#include <helper/fileio.h>
struct nand_fileio_state {
uint32_t address;
uint32_t size;
uint8_t *page;
uint32_t page_size;
enum oob_formats oob_format;
uint8_t *oob;
uint32_t oob_size;
const int *eccpos;
bool file_opened;
struct fileio fileio;
struct duration bench;
};
void nand_fileio_init(struct nand_fileio_state *state);
int nand_fileio_start(struct command_context *cmd_ctx,
struct nand_device *nand, const char *filename, int filemode,
struct nand_fileio_state *state);
int nand_fileio_cleanup(struct nand_fileio_state *state);
int nand_fileio_finish(struct nand_fileio_state *state);
COMMAND_HELPER(nand_fileio_parse_args, struct nand_fileio_state *state,
struct nand_device **dev, enum fileio_access filemode,
bool need_size, bool sw_ecc);
int nand_fileio_read(struct nand_device *nand, struct nand_fileio_state *s);
#endif /* FLASH_NAND_FILEIO_H */
+40
View File
@@ -0,0 +1,40 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NAND_IMP_H
#define FLASH_NAND_IMP_H
#include "core.h"
#include "driver.h"
void nand_device_add(struct nand_device *c);
int nand_write_page(struct nand_device *nand,
uint32_t page, uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size);
int nand_read_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size);
int nand_probe(struct nand_device *nand);
int nand_erase(struct nand_device *nand, int first_block, int last_block);
int nand_build_bbt(struct nand_device *nand, int first, int last);
#endif /* FLASH_NAND_IMP_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef LPC3180_NAND_CONTROLLER_H
#define LPC3180_NAND_CONTROLLER_H
enum lpc3180_selected_controller {
LPC3180_NO_CONTROLLER,
LPC3180_MLC_CONTROLLER,
LPC3180_SLC_CONTROLLER,
};
struct lpc3180_nand_controller {
int osc_freq;
enum lpc3180_selected_controller selected_controller;
int is_bulk;
int sw_write_protection;
uint32_t sw_wp_lower_bound;
uint32_t sw_wp_upper_bound;
};
#endif /*LPC3180_NAND_CONTROLLER_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,38 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef LPC32xx_NAND_CONTROLLER_H
#define LPC32xx_NAND_CONTROLLER_H
enum lpc32xx_selected_controller {
LPC32xx_NO_CONTROLLER,
LPC32xx_MLC_CONTROLLER,
LPC32xx_SLC_CONTROLLER,
};
struct lpc32xx_nand_controller {
int osc_freq;
enum lpc32xx_selected_controller selected_controller;
int sw_write_protection;
uint32_t sw_wp_lower_bound;
uint32_t sw_wp_upper_bound;
};
#endif /*LPC32xx_NAND_CONTROLLER_H */
+730
View File
@@ -0,0 +1,730 @@
/***************************************************************************
* Copyright (C) 2009 by Alexei Babich *
* Rezonans plc., Chelyabinsk, Russia *
* impatt@mail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* Freescale iMX3* OpenOCD NAND Flash controller support.
*
* Many thanks to Ben Dooks for writing s3c24xx driver.
*/
/*
driver tested with STMicro NAND512W3A @imx31
tested "nand probe #", "nand erase # 0 #", "nand dump # file 0 #", "nand write # file 0"
get_next_halfword_from_sram_buffer() not tested
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "mx3.h"
#include <target/target.h>
static const char target_not_halted_err_msg[] =
"target must be halted to use mx3 NAND flash controller";
static const char data_block_size_err_msg[] =
"minimal granularity is one half-word, %" PRId32 " is incorrect";
static const char sram_buffer_bounds_err_msg[] =
"trying to access out of SRAM buffer bound (addr=0x%" PRIx32 ")";
static const char get_status_register_err_msg[] = "can't get NAND status";
static uint32_t in_sram_address;
static unsigned char sign_of_sequental_byte_read;
static int test_iomux_settings(struct target *target, uint32_t value,
uint32_t mask, const char *text);
static int initialize_nf_controller(struct nand_device *nand);
static int get_next_byte_from_sram_buffer(struct target *target, uint8_t *value);
static int get_next_halfword_from_sram_buffer(struct target *target,
uint16_t *value);
static int poll_for_complete_op(struct target *target, const char *text);
static int validate_target_state(struct nand_device *nand);
static int do_data_output(struct nand_device *nand);
static int imx31_command(struct nand_device *nand, uint8_t command);
static int imx31_address(struct nand_device *nand, uint8_t address);
NAND_DEVICE_COMMAND_HANDLER(imx31_nand_device_command)
{
struct mx3_nf_controller *mx3_nf_info;
mx3_nf_info = malloc(sizeof(struct mx3_nf_controller));
if (mx3_nf_info == NULL) {
LOG_ERROR("no memory for nand controller");
return ERROR_FAIL;
}
nand->controller_priv = mx3_nf_info;
if (CMD_ARGC < 3)
return ERROR_COMMAND_SYNTAX_ERROR;
/*
* check hwecc requirements
*/
{
int hwecc_needed;
hwecc_needed = strcmp(CMD_ARGV[2], "hwecc");
if (hwecc_needed == 0)
mx3_nf_info->flags.hw_ecc_enabled = 1;
else
mx3_nf_info->flags.hw_ecc_enabled = 0;
}
mx3_nf_info->optype = MX3_NF_DATAOUT_PAGE;
mx3_nf_info->fin = MX3_NF_FIN_NONE;
mx3_nf_info->flags.target_little_endian =
(nand->target->endianness == TARGET_LITTLE_ENDIAN);
/*
* testing host endianness
*/
{
int x = 1;
if (*(char *) &x == 1)
mx3_nf_info->flags.host_little_endian = 1;
else
mx3_nf_info->flags.host_little_endian = 0;
}
return ERROR_OK;
}
static int imx31_init(struct nand_device *nand)
{
struct mx3_nf_controller *mx3_nf_info = nand->controller_priv;
struct target *target = nand->target;
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
}
{
uint16_t buffsize_register_content;
target_read_u16(target, MX3_NF_BUFSIZ, &buffsize_register_content);
mx3_nf_info->flags.one_kb_sram = !(buffsize_register_content & 0x000f);
}
{
uint32_t pcsr_register_content;
target_read_u32(target, MX3_PCSR, &pcsr_register_content);
if (!nand->bus_width) {
nand->bus_width = (pcsr_register_content & 0x80000000) ? 16 : 8;
} else {
pcsr_register_content |= ((nand->bus_width == 16) ? 0x80000000 : 0x00000000);
target_write_u32(target, MX3_PCSR, pcsr_register_content);
}
if (!nand->page_size) {
nand->page_size = (pcsr_register_content & 0x40000000) ? 2048 : 512;
} else {
pcsr_register_content |= ((nand->page_size == 2048) ? 0x40000000 : 0x00000000);
target_write_u32(target, MX3_PCSR, pcsr_register_content);
}
if (mx3_nf_info->flags.one_kb_sram && (nand->page_size == 2048)) {
LOG_ERROR("NAND controller have only 1 kb SRAM, "
"so pagesize 2048 is incompatible with it");
}
}
{
uint32_t cgr_register_content;
target_read_u32(target, MX3_CCM_CGR2, &cgr_register_content);
if (!(cgr_register_content & 0x00000300)) {
LOG_ERROR("clock gating to EMI disabled");
return ERROR_FAIL;
}
}
{
uint32_t gpr_register_content;
target_read_u32(target, MX3_GPR, &gpr_register_content);
if (gpr_register_content & 0x00000060) {
LOG_ERROR("pins mode overrided by GPR");
return ERROR_FAIL;
}
}
{
/*
* testing IOMUX settings; must be in "functional-mode output and
* functional-mode input" mode
*/
int test_iomux;
test_iomux = ERROR_OK;
test_iomux |= test_iomux_settings(target, 0x43fac0c0, 0x7f7f7f00, "d0,d1,d2");
test_iomux |= test_iomux_settings(target, 0x43fac0c4, 0x7f7f7f7f, "d3,d4,d5,d6");
test_iomux |= test_iomux_settings(target, 0x43fac0c8, 0x0000007f, "d7");
if (nand->bus_width == 16) {
test_iomux |= test_iomux_settings(target, 0x43fac0c8, 0x7f7f7f00, "d8,d9,d10");
test_iomux |= test_iomux_settings(target, 0x43fac0cc, 0x7f7f7f7f, "d11,d12,d13,d14");
test_iomux |= test_iomux_settings(target, 0x43fac0d0, 0x0000007f, "d15");
}
test_iomux |= test_iomux_settings(target, 0x43fac0d0, 0x7f7f7f00, "nfwp,nfce,nfrb");
test_iomux |= test_iomux_settings(target, 0x43fac0d4, 0x7f7f7f7f,
"nfwe,nfre,nfale,nfcle");
if (test_iomux != ERROR_OK)
return ERROR_FAIL;
}
initialize_nf_controller(nand);
{
int retval;
uint16_t nand_status_content;
retval = ERROR_OK;
retval |= imx31_command(nand, NAND_CMD_STATUS);
retval |= imx31_address(nand, 0x00);
retval |= do_data_output(nand);
if (retval != ERROR_OK) {
LOG_ERROR(get_status_register_err_msg);
return ERROR_FAIL;
}
target_read_u16(target, MX3_NF_MAIN_BUFFER0, &nand_status_content);
if (!(nand_status_content & 0x0080)) {
/*
* is host-big-endian correctly ??
*/
LOG_INFO("NAND read-only");
mx3_nf_info->flags.nand_readonly = 1;
} else
mx3_nf_info->flags.nand_readonly = 0;
}
return ERROR_OK;
}
static int imx31_read_data(struct nand_device *nand, void *data)
{
struct target *target = nand->target;
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
}
{
/*
* get data from nand chip
*/
int try_data_output_from_nand_chip;
try_data_output_from_nand_chip = do_data_output(nand);
if (try_data_output_from_nand_chip != ERROR_OK)
return try_data_output_from_nand_chip;
}
if (nand->bus_width == 16)
get_next_halfword_from_sram_buffer(target, data);
else
get_next_byte_from_sram_buffer(target, data);
return ERROR_OK;
}
static int imx31_write_data(struct nand_device *nand, uint16_t data)
{
LOG_ERROR("write_data() not implemented");
return ERROR_NAND_OPERATION_FAILED;
}
static int imx31_reset(struct nand_device *nand)
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
initialize_nf_controller(nand);
return ERROR_OK;
}
static int imx31_command(struct nand_device *nand, uint8_t command)
{
struct mx3_nf_controller *mx3_nf_info = nand->controller_priv;
struct target *target = nand->target;
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
}
switch (command) {
case NAND_CMD_READOOB:
command = NAND_CMD_READ0;
in_sram_address = MX3_NF_SPARE_BUFFER0; /* set read point for
* data_read() and
* read_block_data() to
* spare area in SRAM
* buffer */
break;
case NAND_CMD_READ1:
command = NAND_CMD_READ0;
/*
* offset == one half of page size
*/
in_sram_address = MX3_NF_MAIN_BUFFER0 + (nand->page_size >> 1);
default:
in_sram_address = MX3_NF_MAIN_BUFFER0;
}
target_write_u16(target, MX3_NF_FCMD, command);
/*
* start command input operation (set MX3_NF_BIT_OP_DONE==0)
*/
target_write_u16(target, MX3_NF_CFG2, MX3_NF_BIT_OP_FCI);
{
int poll_result;
poll_result = poll_for_complete_op(target, "command");
if (poll_result != ERROR_OK)
return poll_result;
}
/*
* reset cursor to begin of the buffer
*/
sign_of_sequental_byte_read = 0;
switch (command) {
case NAND_CMD_READID:
mx3_nf_info->optype = MX3_NF_DATAOUT_NANDID;
mx3_nf_info->fin = MX3_NF_FIN_DATAOUT;
break;
case NAND_CMD_STATUS:
mx3_nf_info->optype = MX3_NF_DATAOUT_NANDSTATUS;
mx3_nf_info->fin = MX3_NF_FIN_DATAOUT;
break;
case NAND_CMD_READ0:
mx3_nf_info->fin = MX3_NF_FIN_DATAOUT;
mx3_nf_info->optype = MX3_NF_DATAOUT_PAGE;
break;
default:
mx3_nf_info->optype = MX3_NF_DATAOUT_PAGE;
}
return ERROR_OK;
}
static int imx31_address(struct nand_device *nand, uint8_t address)
{
struct target *target = nand->target;
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
}
target_write_u16(target, MX3_NF_FADDR, address);
/*
* start address input operation (set MX3_NF_BIT_OP_DONE==0)
*/
target_write_u16(target, MX3_NF_CFG2, MX3_NF_BIT_OP_FAI);
{
int poll_result;
poll_result = poll_for_complete_op(target, "address");
if (poll_result != ERROR_OK)
return poll_result;
}
return ERROR_OK;
}
static int imx31_nand_ready(struct nand_device *nand, int tout)
{
uint16_t poll_complete_status;
struct target *target = nand->target;
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
}
do {
target_read_u16(target, MX3_NF_CFG2, &poll_complete_status);
if (poll_complete_status & MX3_NF_BIT_OP_DONE)
return tout;
alive_sleep(1);
} while (tout-- > 0);
return tout;
}
static int imx31_write_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob,
uint32_t oob_size)
{
struct mx3_nf_controller *mx3_nf_info = nand->controller_priv;
struct target *target = nand->target;
if (data_size % 2) {
LOG_ERROR(data_block_size_err_msg, data_size);
return ERROR_NAND_OPERATION_FAILED;
}
if (oob_size % 2) {
LOG_ERROR(data_block_size_err_msg, oob_size);
return ERROR_NAND_OPERATION_FAILED;
}
if (!data) {
LOG_ERROR("nothing to program");
return ERROR_NAND_OPERATION_FAILED;
}
{
/*
* validate target state
*/
int retval;
retval = validate_target_state(nand);
if (retval != ERROR_OK)
return retval;
}
{
int retval = ERROR_OK;
retval |= imx31_command(nand, NAND_CMD_SEQIN);
retval |= imx31_address(nand, 0x00);
retval |= imx31_address(nand, page & 0xff);
retval |= imx31_address(nand, (page >> 8) & 0xff);
if (nand->address_cycles >= 4) {
retval |= imx31_address(nand, (page >> 16) & 0xff);
if (nand->address_cycles >= 5)
retval |= imx31_address(nand, (page >> 24) & 0xff);
}
target_write_buffer(target, MX3_NF_MAIN_BUFFER0, data_size, data);
if (oob) {
if (mx3_nf_info->flags.hw_ecc_enabled) {
/*
* part of spare block will be overrided by hardware
* ECC generator
*/
LOG_DEBUG("part of spare block will be overrided by hardware ECC generator");
}
target_write_buffer(target, MX3_NF_SPARE_BUFFER0, oob_size, oob);
}
/*
* start data input operation (set MX3_NF_BIT_OP_DONE==0)
*/
target_write_u16(target, MX3_NF_CFG2, MX3_NF_BIT_OP_FDI);
{
int poll_result;
poll_result = poll_for_complete_op(target, "data input");
if (poll_result != ERROR_OK)
return poll_result;
}
retval |= imx31_command(nand, NAND_CMD_PAGEPROG);
if (retval != ERROR_OK)
return retval;
/*
* check status register
*/
{
uint16_t nand_status_content;
retval = ERROR_OK;
retval |= imx31_command(nand, NAND_CMD_STATUS);
retval |= imx31_address(nand, 0x00);
retval |= do_data_output(nand);
if (retval != ERROR_OK) {
LOG_ERROR(get_status_register_err_msg);
return retval;
}
target_read_u16(target, MX3_NF_MAIN_BUFFER0, &nand_status_content);
if (nand_status_content & 0x0001) {
/*
* is host-big-endian correctly ??
*/
return ERROR_NAND_OPERATION_FAILED;
}
}
}
return ERROR_OK;
}
static int imx31_read_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size, uint8_t *oob,
uint32_t oob_size)
{
struct target *target = nand->target;
if (data_size % 2) {
LOG_ERROR(data_block_size_err_msg, data_size);
return ERROR_NAND_OPERATION_FAILED;
}
if (oob_size % 2) {
LOG_ERROR(data_block_size_err_msg, oob_size);
return ERROR_NAND_OPERATION_FAILED;
}
{
/*
* validate target state
*/
int retval;
retval = validate_target_state(nand);
if (retval != ERROR_OK)
return retval;
}
{
int retval = ERROR_OK;
retval |= imx31_command(nand, NAND_CMD_READ0);
retval |= imx31_address(nand, 0x00);
retval |= imx31_address(nand, page & 0xff);
retval |= imx31_address(nand, (page >> 8) & 0xff);
if (nand->address_cycles >= 4) {
retval |= imx31_address(nand, (page >> 16) & 0xff);
if (nand->address_cycles >= 5) {
retval |= imx31_address(nand, (page >> 24) & 0xff);
retval |= imx31_command(nand, NAND_CMD_READSTART);
}
}
retval |= do_data_output(nand);
if (retval != ERROR_OK)
return retval;
if (data) {
target_read_buffer(target, MX3_NF_MAIN_BUFFER0, data_size,
data);
}
if (oob) {
target_read_buffer(target, MX3_NF_SPARE_BUFFER0, oob_size,
oob);
}
}
return ERROR_OK;
}
static int test_iomux_settings(struct target *target, uint32_t address,
uint32_t mask, const char *text)
{
uint32_t register_content;
target_read_u32(target, address, &register_content);
if ((register_content & mask) != (0x12121212 & mask)) {
LOG_ERROR("IOMUX for {%s} is bad", text);
return ERROR_FAIL;
}
return ERROR_OK;
}
static int initialize_nf_controller(struct nand_device *nand)
{
struct mx3_nf_controller *mx3_nf_info = nand->controller_priv;
struct target *target = nand->target;
/*
* resets NAND flash controller in zero time ? I dont know.
*/
target_write_u16(target, MX3_NF_CFG1, MX3_NF_BIT_RESET_EN);
{
uint16_t work_mode;
work_mode = MX3_NF_BIT_INT_DIS; /* disable interrupt */
if (target->endianness == TARGET_BIG_ENDIAN)
work_mode |= MX3_NF_BIT_BE_EN;
if (mx3_nf_info->flags.hw_ecc_enabled)
work_mode |= MX3_NF_BIT_ECC_EN;
target_write_u16(target, MX3_NF_CFG1, work_mode);
}
/*
* unlock SRAM buffer for write; 2 mean "Unlock", other values means "Lock"
*/
target_write_u16(target, MX3_NF_BUFCFG, 2);
{
uint16_t temp;
target_read_u16(target, MX3_NF_FWP, &temp);
if ((temp & 0x0007) == 1) {
LOG_ERROR("NAND flash is tight-locked, reset needed");
return ERROR_FAIL;
}
}
/*
* unlock NAND flash for write
*/
target_write_u16(target, MX3_NF_FWP, 4);
target_write_u16(target, MX3_NF_LOCKSTART, 0x0000);
target_write_u16(target, MX3_NF_LOCKEND, 0xFFFF);
/*
* 0x0000 means that first SRAM buffer @0xB800_0000 will be used
*/
target_write_u16(target, MX3_NF_BUFADDR, 0x0000);
/*
* address of SRAM buffer
*/
in_sram_address = MX3_NF_MAIN_BUFFER0;
sign_of_sequental_byte_read = 0;
return ERROR_OK;
}
static int get_next_byte_from_sram_buffer(struct target *target, uint8_t *value)
{
static uint8_t even_byte;
/*
* host-big_endian ??
*/
if (sign_of_sequental_byte_read == 0)
even_byte = 0;
if (in_sram_address > MX3_NF_LAST_BUFFER_ADDR) {
LOG_ERROR(sram_buffer_bounds_err_msg, in_sram_address);
*value = 0;
sign_of_sequental_byte_read = 0;
even_byte = 0;
return ERROR_NAND_OPERATION_FAILED;
} else {
uint16_t temp;
target_read_u16(target, in_sram_address, &temp);
if (even_byte) {
*value = temp >> 8;
even_byte = 0;
in_sram_address += 2;
} else {
*value = temp & 0xff;
even_byte = 1;
}
}
sign_of_sequental_byte_read = 1;
return ERROR_OK;
}
static int get_next_halfword_from_sram_buffer(struct target *target,
uint16_t *value)
{
if (in_sram_address > MX3_NF_LAST_BUFFER_ADDR) {
LOG_ERROR(sram_buffer_bounds_err_msg, in_sram_address);
*value = 0;
return ERROR_NAND_OPERATION_FAILED;
} else {
target_read_u16(target, in_sram_address, value);
in_sram_address += 2;
}
return ERROR_OK;
}
static int poll_for_complete_op(struct target *target, const char *text)
{
uint16_t poll_complete_status;
for (int poll_cycle_count = 0; poll_cycle_count < 100; poll_cycle_count++) {
usleep(25);
target_read_u16(target, MX3_NF_CFG2, &poll_complete_status);
if (poll_complete_status & MX3_NF_BIT_OP_DONE)
break;
}
if (!(poll_complete_status & MX3_NF_BIT_OP_DONE)) {
LOG_ERROR("%s sending timeout", text);
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
static int validate_target_state(struct nand_device *nand)
{
struct mx3_nf_controller *mx3_nf_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR(target_not_halted_err_msg);
return ERROR_NAND_OPERATION_FAILED;
}
if (mx3_nf_info->flags.target_little_endian !=
(target->endianness == TARGET_LITTLE_ENDIAN)) {
/*
* endianness changed after NAND controller probed
*/
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
static int do_data_output(struct nand_device *nand)
{
struct mx3_nf_controller *mx3_nf_info = nand->controller_priv;
struct target *target = nand->target;
switch (mx3_nf_info->fin) {
case MX3_NF_FIN_DATAOUT:
/*
* start data output operation (set MX3_NF_BIT_OP_DONE==0)
*/
target_write_u16 (target, MX3_NF_CFG2,
MX3_NF_BIT_DATAOUT_TYPE(mx3_nf_info->optype));
{
int poll_result;
poll_result = poll_for_complete_op(target, "data output");
if (poll_result != ERROR_OK)
return poll_result;
}
mx3_nf_info->fin = MX3_NF_FIN_NONE;
/*
* ECC stuff
*/
if ((mx3_nf_info->optype == MX3_NF_DATAOUT_PAGE)
&& mx3_nf_info->flags.hw_ecc_enabled) {
uint16_t ecc_status;
target_read_u16 (target, MX3_NF_ECCSTATUS, &ecc_status);
switch (ecc_status & 0x000c) {
case 1 << 2:
LOG_DEBUG("main area readed with 1 (correctable) error");
break;
case 2 << 2:
LOG_DEBUG("main area readed with more than 1 (incorrectable) error");
return ERROR_NAND_OPERATION_FAILED;
break;
}
switch (ecc_status & 0x0003) {
case 1:
LOG_DEBUG("spare area readed with 1 (correctable) error");
break;
case 2:
LOG_DEBUG("main area readed with more than 1 (incorrectable) error");
return ERROR_NAND_OPERATION_FAILED;
break;
}
}
break;
case MX3_NF_FIN_NONE:
break;
}
return ERROR_OK;
}
struct nand_flash_controller imx31_nand_flash_controller = {
.name = "imx31",
.usage = "nand device imx31 target noecc|hwecc",
.nand_device_command = &imx31_nand_device_command,
.init = &imx31_init,
.reset = &imx31_reset,
.command = &imx31_command,
.address = &imx31_address,
.write_data = &imx31_write_data,
.read_data = &imx31_read_data,
.write_page = &imx31_write_page,
.read_page = &imx31_read_page,
.nand_ready = &imx31_nand_ready,
};
+110
View File
@@ -0,0 +1,110 @@
/***************************************************************************
* Copyright (C) 2009 by Alexei Babich *
* Rezonans plc., Chelyabinsk, Russia *
* impatt@mail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* Freescale iMX3* OpenOCD NAND Flash controller support.
*
* Many thanks to Ben Dooks for writing s3c24xx driver.
*/
#define MX3_NF_BASE_ADDR 0xb8000000
#define MX3_NF_BUFSIZ (MX3_NF_BASE_ADDR + 0xe00)
#define MX3_NF_BUFADDR (MX3_NF_BASE_ADDR + 0xe04)
#define MX3_NF_FADDR (MX3_NF_BASE_ADDR + 0xe06)
#define MX3_NF_FCMD (MX3_NF_BASE_ADDR + 0xe08)
#define MX3_NF_BUFCFG (MX3_NF_BASE_ADDR + 0xe0a)
#define MX3_NF_ECCSTATUS (MX3_NF_BASE_ADDR + 0xe0c)
#define MX3_NF_ECCMAINPOS (MX3_NF_BASE_ADDR + 0xe0e)
#define MX3_NF_ECCSPAREPOS (MX3_NF_BASE_ADDR + 0xe10)
#define MX3_NF_FWP (MX3_NF_BASE_ADDR + 0xe12)
#define MX3_NF_LOCKSTART (MX3_NF_BASE_ADDR + 0xe14)
#define MX3_NF_LOCKEND (MX3_NF_BASE_ADDR + 0xe16)
#define MX3_NF_FWPSTATUS (MX3_NF_BASE_ADDR + 0xe18)
/*
* all bits not marked as self-clearing bit
*/
#define MX3_NF_CFG1 (MX3_NF_BASE_ADDR + 0xe1a)
#define MX3_NF_CFG2 (MX3_NF_BASE_ADDR + 0xe1c)
#define MX3_NF_MAIN_BUFFER0 (MX3_NF_BASE_ADDR + 0x0000)
#define MX3_NF_MAIN_BUFFER1 (MX3_NF_BASE_ADDR + 0x0200)
#define MX3_NF_MAIN_BUFFER2 (MX3_NF_BASE_ADDR + 0x0400)
#define MX3_NF_MAIN_BUFFER3 (MX3_NF_BASE_ADDR + 0x0600)
#define MX3_NF_SPARE_BUFFER0 (MX3_NF_BASE_ADDR + 0x0800)
#define MX3_NF_SPARE_BUFFER1 (MX3_NF_BASE_ADDR + 0x0810)
#define MX3_NF_SPARE_BUFFER2 (MX3_NF_BASE_ADDR + 0x0820)
#define MX3_NF_SPARE_BUFFER3 (MX3_NF_BASE_ADDR + 0x0830)
#define MX3_NF_MAIN_BUFFER_LEN 512
#define MX3_NF_SPARE_BUFFER_LEN 16
#define MX3_NF_LAST_BUFFER_ADDR ((MX3_NF_SPARE_BUFFER3) + MX3_NF_SPARE_BUFFER_LEN - 2)
/* bits in MX3_NF_CFG1 register */
#define MX3_NF_BIT_SPARE_ONLY_EN (1<<2)
#define MX3_NF_BIT_ECC_EN (1<<3)
#define MX3_NF_BIT_INT_DIS (1<<4)
#define MX3_NF_BIT_BE_EN (1<<5)
#define MX3_NF_BIT_RESET_EN (1<<6)
#define MX3_NF_BIT_FORCE_CE (1<<7)
/* bits in MX3_NF_CFG2 register */
/*Flash Command Input*/
#define MX3_NF_BIT_OP_FCI (1<<0)
/*
* Flash Address Input
*/
#define MX3_NF_BIT_OP_FAI (1<<1)
/*
* Flash Data Input
*/
#define MX3_NF_BIT_OP_FDI (1<<2)
/* see "enum mx_dataout_type" below */
#define MX3_NF_BIT_DATAOUT_TYPE(x) ((x)<<3)
#define MX3_NF_BIT_OP_DONE (1<<15)
#define MX3_CCM_CGR2 0x53f80028
#define MX3_GPR 0x43fac008
#define MX3_PCSR 0x53f8000c
enum mx_dataout_type {
MX3_NF_DATAOUT_PAGE = 1,
MX3_NF_DATAOUT_NANDID = 2,
MX3_NF_DATAOUT_NANDSTATUS = 4,
};
enum mx_nf_finalize_action {
MX3_NF_FIN_NONE,
MX3_NF_FIN_DATAOUT,
};
struct mx3_nf_flags {
unsigned host_little_endian:1;
unsigned target_little_endian:1;
unsigned nand_readonly:1;
unsigned one_kb_sram:1;
unsigned hw_ecc_enabled:1;
};
struct mx3_nf_controller {
enum mx_dataout_type optype;
enum mx_nf_finalize_action fin;
struct mx3_nf_flags flags;
};
+971
View File
@@ -0,0 +1,971 @@
/***************************************************************************
* Copyright (C) 2009 by Alexei Babich *
* Rezonans plc., Chelyabinsk, Russia *
* impatt@mail.ru *
* *
* Copyright (C) 2010 by Gaetan CARLIER *
* Trump s.a., Belgium *
* *
* Copyright (C) 2011 by Erik Ahlen *
* Avalon Innovation, Sweden *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* Freescale iMX OpenOCD NAND Flash controller support.
* based on Freescale iMX2* and iMX3* OpenOCD NAND Flash controller support.
*/
/*
* driver tested with Samsung K9F2G08UXA and Numonyx/ST NAND02G-B2D @mxc
* tested "nand probe #", "nand erase # 0 #", "nand dump # file 0 #",
* "nand write # file 0", "nand verify"
*
* get_next_halfword_from_sram_buffer() not tested
* !! all function only tested with 2k page nand device; mxc_write_page
* writes the 4 MAIN_BUFFER's and is not compatible with < 2k page
* !! oob must be be used due to NFS bug
* !! oob must be 64 bytes per 2KiB page
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "mxc.h"
#include <target/target.h>
#define OOB_SIZE 64
#define nfc_is_v1() (mxc_nf_info->mxc_version == MXC_VERSION_MX27 || \
mxc_nf_info->mxc_version == MXC_VERSION_MX31)
#define nfc_is_v2() (mxc_nf_info->mxc_version == MXC_VERSION_MX25 || \
mxc_nf_info->mxc_version == MXC_VERSION_MX35)
/* This permits to print (in LOG_INFO) how much bytes
* has been written after a page read or write.
* This is useful when OpenOCD is used with a graphical
* front-end to estimate progression of the global read/write
*/
#undef _MXC_PRINT_STAT
/* #define _MXC_PRINT_STAT */
static const char target_not_halted_err_msg[] =
"target must be halted to use mxc NAND flash controller";
static const char data_block_size_err_msg[] =
"minimal granularity is one half-word, %" PRId32 " is incorrect";
static const char sram_buffer_bounds_err_msg[] =
"trying to access out of SRAM buffer bound (addr=0x%" PRIx32 ")";
static const char get_status_register_err_msg[] = "can't get NAND status";
static uint32_t in_sram_address;
static unsigned char sign_of_sequental_byte_read;
static uint32_t align_address_v2(struct nand_device *nand, uint32_t addr);
static int initialize_nf_controller(struct nand_device *nand);
static int get_next_byte_from_sram_buffer(struct nand_device *nand, uint8_t *value);
static int get_next_halfword_from_sram_buffer(struct nand_device *nand, uint16_t *value);
static int poll_for_complete_op(struct nand_device *nand, const char *text);
static int validate_target_state(struct nand_device *nand);
static int do_data_output(struct nand_device *nand);
static int mxc_command(struct nand_device *nand, uint8_t command);
static int mxc_address(struct nand_device *nand, uint8_t address);
NAND_DEVICE_COMMAND_HANDLER(mxc_nand_device_command)
{
struct mxc_nf_controller *mxc_nf_info;
int hwecc_needed;
int x;
mxc_nf_info = malloc(sizeof(struct mxc_nf_controller));
if (mxc_nf_info == NULL) {
LOG_ERROR("no memory for nand controller");
return ERROR_FAIL;
}
nand->controller_priv = mxc_nf_info;
if (CMD_ARGC < 4) {
LOG_ERROR("use \"nand device mxc target mx25|mx27|mx31|mx35 noecc|hwecc [biswap]\"");
return ERROR_FAIL;
}
/*
* check board type
*/
if (strcmp(CMD_ARGV[2], "mx25") == 0) {
mxc_nf_info->mxc_version = MXC_VERSION_MX25;
mxc_nf_info->mxc_base_addr = 0xBB000000;
mxc_nf_info->mxc_regs_addr = mxc_nf_info->mxc_base_addr + 0x1E00;
} else if (strcmp(CMD_ARGV[2], "mx27") == 0) {
mxc_nf_info->mxc_version = MXC_VERSION_MX27;
mxc_nf_info->mxc_base_addr = 0xD8000000;
mxc_nf_info->mxc_regs_addr = mxc_nf_info->mxc_base_addr + 0x0E00;
} else if (strcmp(CMD_ARGV[2], "mx31") == 0) {
mxc_nf_info->mxc_version = MXC_VERSION_MX31;
mxc_nf_info->mxc_base_addr = 0xB8000000;
mxc_nf_info->mxc_regs_addr = mxc_nf_info->mxc_base_addr + 0x0E00;
} else if (strcmp(CMD_ARGV[2], "mx35") == 0) {
mxc_nf_info->mxc_version = MXC_VERSION_MX35;
mxc_nf_info->mxc_base_addr = 0xBB000000;
mxc_nf_info->mxc_regs_addr = mxc_nf_info->mxc_base_addr + 0x1E00;
}
/*
* check hwecc requirements
*/
hwecc_needed = strcmp(CMD_ARGV[3], "hwecc");
if (hwecc_needed == 0)
mxc_nf_info->flags.hw_ecc_enabled = 1;
else
mxc_nf_info->flags.hw_ecc_enabled = 0;
mxc_nf_info->optype = MXC_NF_DATAOUT_PAGE;
mxc_nf_info->fin = MXC_NF_FIN_NONE;
mxc_nf_info->flags.target_little_endian =
(nand->target->endianness == TARGET_LITTLE_ENDIAN);
/*
* should factory bad block indicator be swaped
* as a workaround for how the nfc handles pages.
*/
if (CMD_ARGC > 4 && strcmp(CMD_ARGV[4], "biswap") == 0) {
LOG_DEBUG("BI-swap enabled");
mxc_nf_info->flags.biswap_enabled = 1;
}
/*
* testing host endianness
*/
x = 1;
if (*(char *) &x == 1)
mxc_nf_info->flags.host_little_endian = 1;
else
mxc_nf_info->flags.host_little_endian = 0;
return ERROR_OK;
}
COMMAND_HANDLER(handle_mxc_biswap_command)
{
struct nand_device *nand = NULL;
struct mxc_nf_controller *mxc_nf_info = NULL;
if (CMD_ARGC < 1 || CMD_ARGC > 2)
return ERROR_COMMAND_SYNTAX_ERROR;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &nand);
if (retval != ERROR_OK) {
command_print(CMD_CTX, "invalid nand device number or name: %s", CMD_ARGV[0]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
mxc_nf_info = nand->controller_priv;
if (CMD_ARGC == 2) {
if (strcmp(CMD_ARGV[1], "enable") == 0)
mxc_nf_info->flags.biswap_enabled = true;
else
mxc_nf_info->flags.biswap_enabled = false;
}
if (mxc_nf_info->flags.biswap_enabled)
command_print(CMD_CTX, "BI-swapping enabled on %s", nand->name);
else
command_print(CMD_CTX, "BI-swapping disabled on %s", nand->name);
return ERROR_OK;
}
static const struct command_registration mxc_sub_command_handlers[] = {
{
.name = "biswap",
.handler = handle_mxc_biswap_command,
.help = "Turns on/off bad block information swaping from main area, "
"without parameter query status.",
.usage = "bank_id ['enable'|'disable']",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration mxc_nand_command_handler[] = {
{
.name = "mxc",
.mode = COMMAND_ANY,
.help = "MXC NAND flash controller commands",
.chain = mxc_sub_command_handlers
},
COMMAND_REGISTRATION_DONE
};
static int mxc_init(struct nand_device *nand)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
int validate_target_result;
uint16_t buffsize_register_content;
uint32_t sreg_content;
uint32_t SREG = MX2_FMCR;
uint32_t SEL_16BIT = MX2_FMCR_NF_16BIT_SEL;
uint32_t SEL_FMS = MX2_FMCR_NF_FMS;
int retval;
uint16_t nand_status_content;
/*
* validate target state
*/
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
if (nfc_is_v1()) {
target_read_u16(target, MXC_NF_BUFSIZ, &buffsize_register_content);
mxc_nf_info->flags.one_kb_sram = !(buffsize_register_content & 0x000f);
} else
mxc_nf_info->flags.one_kb_sram = 0;
if (mxc_nf_info->mxc_version == MXC_VERSION_MX31) {
SREG = MX3_PCSR;
SEL_16BIT = MX3_PCSR_NF_16BIT_SEL;
SEL_FMS = MX3_PCSR_NF_FMS;
} else if (mxc_nf_info->mxc_version == MXC_VERSION_MX25) {
SREG = MX25_RCSR;
SEL_16BIT = MX25_RCSR_NF_16BIT_SEL;
SEL_FMS = MX25_RCSR_NF_FMS;
} else if (mxc_nf_info->mxc_version == MXC_VERSION_MX35) {
SREG = MX35_RCSR;
SEL_16BIT = MX35_RCSR_NF_16BIT_SEL;
SEL_FMS = MX35_RCSR_NF_FMS;
}
target_read_u32(target, SREG, &sreg_content);
if (!nand->bus_width) {
/* bus_width not yet defined. Read it from MXC_FMCR */
nand->bus_width = (sreg_content & SEL_16BIT) ? 16 : 8;
} else {
/* bus_width forced in soft. Sync it to MXC_FMCR */
sreg_content |= ((nand->bus_width == 16) ? SEL_16BIT : 0x00000000);
target_write_u32(target, SREG, sreg_content);
}
if (nand->bus_width == 16)
LOG_DEBUG("MXC_NF : bus is 16-bit width");
else
LOG_DEBUG("MXC_NF : bus is 8-bit width");
if (!nand->page_size)
nand->page_size = (sreg_content & SEL_FMS) ? 2048 : 512;
else {
sreg_content |= ((nand->page_size == 2048) ? SEL_FMS : 0x00000000);
target_write_u32(target, SREG, sreg_content);
}
if (mxc_nf_info->flags.one_kb_sram && (nand->page_size == 2048)) {
LOG_ERROR("NAND controller have only 1 kb SRAM, so "
"pagesize 2048 is incompatible with it");
} else
LOG_DEBUG("MXC_NF : NAND controller can handle pagesize of 2048");
if (nfc_is_v2() && sreg_content & MX35_RCSR_NF_4K)
LOG_ERROR("MXC driver does not have support for 4k pagesize.");
initialize_nf_controller(nand);
retval = ERROR_OK;
retval |= mxc_command(nand, NAND_CMD_STATUS);
retval |= mxc_address(nand, 0x00);
retval |= do_data_output(nand);
if (retval != ERROR_OK) {
LOG_ERROR(get_status_register_err_msg);
return ERROR_FAIL;
}
target_read_u16(target, MXC_NF_MAIN_BUFFER0, &nand_status_content);
if (!(nand_status_content & 0x0080)) {
LOG_INFO("NAND read-only");
mxc_nf_info->flags.nand_readonly = 1;
} else
mxc_nf_info->flags.nand_readonly = 0;
return ERROR_OK;
}
static int mxc_read_data(struct nand_device *nand, void *data)
{
int validate_target_result;
int try_data_output_from_nand_chip;
/*
* validate target state
*/
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
/*
* get data from nand chip
*/
try_data_output_from_nand_chip = do_data_output(nand);
if (try_data_output_from_nand_chip != ERROR_OK) {
LOG_ERROR("mxc_read_data : read data failed : '%x'",
try_data_output_from_nand_chip);
return try_data_output_from_nand_chip;
}
if (nand->bus_width == 16)
get_next_halfword_from_sram_buffer(nand, data);
else
get_next_byte_from_sram_buffer(nand, data);
return ERROR_OK;
}
static int mxc_write_data(struct nand_device *nand, uint16_t data)
{
LOG_ERROR("write_data() not implemented");
return ERROR_NAND_OPERATION_FAILED;
}
static int mxc_reset(struct nand_device *nand)
{
/*
* validate target state
*/
int validate_target_result;
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
initialize_nf_controller(nand);
return ERROR_OK;
}
static int mxc_command(struct nand_device *nand, uint8_t command)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
int validate_target_result;
int poll_result;
/*
* validate target state
*/
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
switch (command) {
case NAND_CMD_READOOB:
command = NAND_CMD_READ0;
/* set read point for data_read() and read_block_data() to
* spare area in SRAM buffer
*/
if (nfc_is_v1())
in_sram_address = MXC_NF_V1_SPARE_BUFFER0;
else
in_sram_address = MXC_NF_V2_SPARE_BUFFER0;
break;
case NAND_CMD_READ1:
command = NAND_CMD_READ0;
/*
* offset == one half of page size
*/
in_sram_address = MXC_NF_MAIN_BUFFER0 + (nand->page_size >> 1);
break;
default:
in_sram_address = MXC_NF_MAIN_BUFFER0;
break;
}
target_write_u16(target, MXC_NF_FCMD, command);
/*
* start command input operation (set MXC_NF_BIT_OP_DONE==0)
*/
target_write_u16(target, MXC_NF_CFG2, MXC_NF_BIT_OP_FCI);
poll_result = poll_for_complete_op(nand, "command");
if (poll_result != ERROR_OK)
return poll_result;
/*
* reset cursor to begin of the buffer
*/
sign_of_sequental_byte_read = 0;
/* Handle special read command and adjust NF_CFG2(FDO) */
switch (command) {
case NAND_CMD_READID:
mxc_nf_info->optype = MXC_NF_DATAOUT_NANDID;
mxc_nf_info->fin = MXC_NF_FIN_DATAOUT;
break;
case NAND_CMD_STATUS:
mxc_nf_info->optype = MXC_NF_DATAOUT_NANDSTATUS;
mxc_nf_info->fin = MXC_NF_FIN_DATAOUT;
target_write_u16 (target, MXC_NF_BUFADDR, 0);
in_sram_address = 0;
break;
case NAND_CMD_READ0:
mxc_nf_info->fin = MXC_NF_FIN_DATAOUT;
mxc_nf_info->optype = MXC_NF_DATAOUT_PAGE;
break;
default:
/* Ohter command use the default 'One page data out' FDO */
mxc_nf_info->optype = MXC_NF_DATAOUT_PAGE;
break;
}
return ERROR_OK;
}
static int mxc_address(struct nand_device *nand, uint8_t address)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
int validate_target_result;
int poll_result;
/*
* validate target state
*/
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
target_write_u16(target, MXC_NF_FADDR, address);
/*
* start address input operation (set MXC_NF_BIT_OP_DONE==0)
*/
target_write_u16(target, MXC_NF_CFG2, MXC_NF_BIT_OP_FAI);
poll_result = poll_for_complete_op(nand, "address");
if (poll_result != ERROR_OK)
return poll_result;
return ERROR_OK;
}
static int mxc_nand_ready(struct nand_device *nand, int tout)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
uint16_t poll_complete_status;
int validate_target_result;
/*
* validate target state
*/
validate_target_result = validate_target_state(nand);
if (validate_target_result != ERROR_OK)
return validate_target_result;
do {
target_read_u16(target, MXC_NF_CFG2, &poll_complete_status);
if (poll_complete_status & MXC_NF_BIT_OP_DONE)
return tout;
alive_sleep(1);
} while (tout-- > 0);
return tout;
}
static int mxc_write_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
int retval;
uint16_t nand_status_content;
uint16_t swap1, swap2, new_swap1;
uint8_t bufs;
int poll_result;
if (data_size % 2) {
LOG_ERROR(data_block_size_err_msg, data_size);
return ERROR_NAND_OPERATION_FAILED;
}
if (oob_size % 2) {
LOG_ERROR(data_block_size_err_msg, oob_size);
return ERROR_NAND_OPERATION_FAILED;
}
if (!data) {
LOG_ERROR("nothing to program");
return ERROR_NAND_OPERATION_FAILED;
}
/*
* validate target state
*/
retval = validate_target_state(nand);
if (retval != ERROR_OK)
return retval;
in_sram_address = MXC_NF_MAIN_BUFFER0;
sign_of_sequental_byte_read = 0;
retval = ERROR_OK;
retval |= mxc_command(nand, NAND_CMD_SEQIN);
retval |= mxc_address(nand, 0); /* col */
retval |= mxc_address(nand, 0); /* col */
retval |= mxc_address(nand, page & 0xff); /* page address */
retval |= mxc_address(nand, (page >> 8) & 0xff);/* page address */
retval |= mxc_address(nand, (page >> 16) & 0xff); /* page address */
target_write_buffer(target, MXC_NF_MAIN_BUFFER0, data_size, data);
if (oob) {
if (mxc_nf_info->flags.hw_ecc_enabled) {
/*
* part of spare block will be overrided by hardware
* ECC generator
*/
LOG_DEBUG("part of spare block will be overrided "
"by hardware ECC generator");
}
if (nfc_is_v1())
target_write_buffer(target, MXC_NF_V1_SPARE_BUFFER0, oob_size, oob);
else {
uint32_t addr = MXC_NF_V2_SPARE_BUFFER0;
while (oob_size > 0) {
uint8_t len = MIN(oob_size, MXC_NF_SPARE_BUFFER_LEN);
target_write_buffer(target, addr, len, oob);
addr = align_address_v2(nand, addr + len);
oob += len;
oob_size -= len;
}
}
}
if (nand->page_size > 512 && mxc_nf_info->flags.biswap_enabled) {
/* BI-swap - work-around of i.MX NFC for NAND device with page == 2kb*/
target_read_u16(target, MXC_NF_MAIN_BUFFER3 + 464, &swap1);
if (oob) {
LOG_ERROR("Due to NFC Bug, oob is not correctly implemented in mxc driver");
return ERROR_NAND_OPERATION_FAILED;
}
swap2 = 0xffff; /* Spare buffer unused forced to 0xffff */
new_swap1 = (swap1 & 0xFF00) | (swap2 >> 8);
swap2 = (swap1 << 8) | (swap2 & 0xFF);
target_write_u16(target, MXC_NF_MAIN_BUFFER3 + 464, new_swap1);
if (nfc_is_v1())
target_write_u16(target, MXC_NF_V1_SPARE_BUFFER3, swap2);
else
target_write_u16(target, MXC_NF_V2_SPARE_BUFFER3, swap2);
}
/*
* start data input operation (set MXC_NF_BIT_OP_DONE==0)
*/
if (nfc_is_v1() && nand->page_size > 512)
bufs = 4;
else
bufs = 1;
for (uint8_t i = 0; i < bufs; ++i) {
target_write_u16(target, MXC_NF_BUFADDR, i);
target_write_u16(target, MXC_NF_CFG2, MXC_NF_BIT_OP_FDI);
poll_result = poll_for_complete_op(nand, "data input");
if (poll_result != ERROR_OK)
return poll_result;
}
retval |= mxc_command(nand, NAND_CMD_PAGEPROG);
if (retval != ERROR_OK)
return retval;
/*
* check status register
*/
retval = ERROR_OK;
retval |= mxc_command(nand, NAND_CMD_STATUS);
target_write_u16 (target, MXC_NF_BUFADDR, 0);
mxc_nf_info->optype = MXC_NF_DATAOUT_NANDSTATUS;
mxc_nf_info->fin = MXC_NF_FIN_DATAOUT;
retval |= do_data_output(nand);
if (retval != ERROR_OK) {
LOG_ERROR(get_status_register_err_msg);
return retval;
}
target_read_u16(target, MXC_NF_MAIN_BUFFER0, &nand_status_content);
if (nand_status_content & 0x0001) {
/*
* page not correctly written
*/
return ERROR_NAND_OPERATION_FAILED;
}
#ifdef _MXC_PRINT_STAT
LOG_INFO("%d bytes newly written", data_size);
#endif
return ERROR_OK;
}
static int mxc_read_page(struct nand_device *nand, uint32_t page,
uint8_t *data, uint32_t data_size,
uint8_t *oob, uint32_t oob_size)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
int retval;
uint8_t bufs;
uint16_t swap1, swap2, new_swap1;
if (data_size % 2) {
LOG_ERROR(data_block_size_err_msg, data_size);
return ERROR_NAND_OPERATION_FAILED;
}
if (oob_size % 2) {
LOG_ERROR(data_block_size_err_msg, oob_size);
return ERROR_NAND_OPERATION_FAILED;
}
/*
* validate target state
*/
retval = validate_target_state(nand);
if (retval != ERROR_OK)
return retval;
/* Reset address_cycles before mxc_command ?? */
retval = mxc_command(nand, NAND_CMD_READ0);
if (retval != ERROR_OK)
return retval;
retval = mxc_address(nand, 0); /* col */
if (retval != ERROR_OK)
return retval;
retval = mxc_address(nand, 0); /* col */
if (retval != ERROR_OK)
return retval;
retval = mxc_address(nand, page & 0xff);/* page address */
if (retval != ERROR_OK)
return retval;
retval = mxc_address(nand, (page >> 8) & 0xff); /* page address */
if (retval != ERROR_OK)
return retval;
retval = mxc_address(nand, (page >> 16) & 0xff);/* page address */
if (retval != ERROR_OK)
return retval;
retval = mxc_command(nand, NAND_CMD_READSTART);
if (retval != ERROR_OK)
return retval;
if (nfc_is_v1() && nand->page_size > 512)
bufs = 4;
else
bufs = 1;
for (uint8_t i = 0; i < bufs; ++i) {
target_write_u16(target, MXC_NF_BUFADDR, i);
mxc_nf_info->fin = MXC_NF_FIN_DATAOUT;
retval = do_data_output(nand);
if (retval != ERROR_OK) {
LOG_ERROR("MXC_NF : Error reading page %d", i);
return retval;
}
}
if (nand->page_size > 512 && mxc_nf_info->flags.biswap_enabled) {
uint32_t SPARE_BUFFER3;
/* BI-swap - work-around of mxc NFC for NAND device with page == 2k */
target_read_u16(target, MXC_NF_MAIN_BUFFER3 + 464, &swap1);
if (nfc_is_v1())
SPARE_BUFFER3 = MXC_NF_V1_SPARE_BUFFER3;
else
SPARE_BUFFER3 = MXC_NF_V2_SPARE_BUFFER3;
target_read_u16(target, SPARE_BUFFER3, &swap2);
new_swap1 = (swap1 & 0xFF00) | (swap2 >> 8);
swap2 = (swap1 << 8) | (swap2 & 0xFF);
target_write_u16(target, MXC_NF_MAIN_BUFFER3 + 464, new_swap1);
target_write_u16(target, SPARE_BUFFER3, swap2);
}
if (data)
target_read_buffer(target, MXC_NF_MAIN_BUFFER0, data_size, data);
if (oob) {
if (nfc_is_v1())
target_read_buffer(target, MXC_NF_V1_SPARE_BUFFER0, oob_size, oob);
else {
uint32_t addr = MXC_NF_V2_SPARE_BUFFER0;
while (oob_size > 0) {
uint8_t len = MIN(oob_size, MXC_NF_SPARE_BUFFER_LEN);
target_read_buffer(target, addr, len, oob);
addr = align_address_v2(nand, addr + len);
oob += len;
oob_size -= len;
}
}
}
#ifdef _MXC_PRINT_STAT
if (data_size > 0) {
/* When Operation Status is read (when page is erased),
* this function is used but data_size is null.
*/
LOG_INFO("%d bytes newly read", data_size);
}
#endif
return ERROR_OK;
}
static uint32_t align_address_v2(struct nand_device *nand, uint32_t addr)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
uint32_t ret = addr;
if (addr > MXC_NF_V2_SPARE_BUFFER0 &&
(addr & 0x1F) == MXC_NF_SPARE_BUFFER_LEN)
ret += MXC_NF_SPARE_BUFFER_MAX - MXC_NF_SPARE_BUFFER_LEN;
else if (addr >= (mxc_nf_info->mxc_base_addr + (uint32_t)nand->page_size))
ret = MXC_NF_V2_SPARE_BUFFER0;
return ret;
}
static int initialize_nf_controller(struct nand_device *nand)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
uint16_t work_mode = 0;
uint16_t temp;
/*
* resets NAND flash controller in zero time ? I dont know.
*/
target_write_u16(target, MXC_NF_CFG1, MXC_NF_BIT_RESET_EN);
if (mxc_nf_info->mxc_version == MXC_VERSION_MX27)
work_mode = MXC_NF_BIT_INT_DIS; /* disable interrupt */
if (target->endianness == TARGET_BIG_ENDIAN) {
LOG_DEBUG("MXC_NF : work in Big Endian mode");
work_mode |= MXC_NF_BIT_BE_EN;
} else
LOG_DEBUG("MXC_NF : work in Little Endian mode");
if (mxc_nf_info->flags.hw_ecc_enabled) {
LOG_DEBUG("MXC_NF : work with ECC mode");
work_mode |= MXC_NF_BIT_ECC_EN;
} else
LOG_DEBUG("MXC_NF : work without ECC mode");
if (nfc_is_v2()) {
target_write_u16(target, MXC_NF_V2_SPAS, OOB_SIZE / 2);
if (nand->page_size) {
uint16_t pages_per_block = nand->erase_size / nand->page_size;
work_mode |= MXC_NF_V2_CFG1_PPB(ffs(pages_per_block) - 6);
}
work_mode |= MXC_NF_BIT_ECC_4BIT;
}
target_write_u16(target, MXC_NF_CFG1, work_mode);
/*
* unlock SRAM buffer for write; 2 mean "Unlock", other values means "Lock"
*/
target_write_u16(target, MXC_NF_BUFCFG, 2);
target_read_u16(target, MXC_NF_FWP, &temp);
if ((temp & 0x0007) == 1) {
LOG_ERROR("NAND flash is tight-locked, reset needed");
return ERROR_FAIL;
}
/*
* unlock NAND flash for write
*/
if (nfc_is_v1()) {
target_write_u16(target, MXC_NF_V1_UNLOCKSTART, 0x0000);
target_write_u16(target, MXC_NF_V1_UNLOCKEND, 0xFFFF);
} else {
target_write_u16(target, MXC_NF_V2_UNLOCKSTART0, 0x0000);
target_write_u16(target, MXC_NF_V2_UNLOCKSTART1, 0x0000);
target_write_u16(target, MXC_NF_V2_UNLOCKSTART2, 0x0000);
target_write_u16(target, MXC_NF_V2_UNLOCKSTART3, 0x0000);
target_write_u16(target, MXC_NF_V2_UNLOCKEND0, 0xFFFF);
target_write_u16(target, MXC_NF_V2_UNLOCKEND1, 0xFFFF);
target_write_u16(target, MXC_NF_V2_UNLOCKEND2, 0xFFFF);
target_write_u16(target, MXC_NF_V2_UNLOCKEND3, 0xFFFF);
}
target_write_u16(target, MXC_NF_FWP, 4);
/*
* 0x0000 means that first SRAM buffer @base_addr will be used
*/
target_write_u16(target, MXC_NF_BUFADDR, 0x0000);
/*
* address of SRAM buffer
*/
in_sram_address = MXC_NF_MAIN_BUFFER0;
sign_of_sequental_byte_read = 0;
return ERROR_OK;
}
static int get_next_byte_from_sram_buffer(struct nand_device *nand, uint8_t *value)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
static uint8_t even_byte;
uint16_t temp;
/*
* host-big_endian ??
*/
if (sign_of_sequental_byte_read == 0)
even_byte = 0;
if (in_sram_address > (nfc_is_v1() ? MXC_NF_V1_LAST_BUFFADDR : MXC_NF_V2_LAST_BUFFADDR)) {
LOG_ERROR(sram_buffer_bounds_err_msg, in_sram_address);
*value = 0;
sign_of_sequental_byte_read = 0;
even_byte = 0;
return ERROR_NAND_OPERATION_FAILED;
} else {
if (nfc_is_v2())
in_sram_address = align_address_v2(nand, in_sram_address);
target_read_u16(target, in_sram_address, &temp);
if (even_byte) {
*value = temp >> 8;
even_byte = 0;
in_sram_address += 2;
} else {
*value = temp & 0xff;
even_byte = 1;
}
}
sign_of_sequental_byte_read = 1;
return ERROR_OK;
}
static int get_next_halfword_from_sram_buffer(struct nand_device *nand, uint16_t *value)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
if (in_sram_address > (nfc_is_v1() ? MXC_NF_V1_LAST_BUFFADDR : MXC_NF_V2_LAST_BUFFADDR)) {
LOG_ERROR(sram_buffer_bounds_err_msg, in_sram_address);
*value = 0;
return ERROR_NAND_OPERATION_FAILED;
} else {
if (nfc_is_v2())
in_sram_address = align_address_v2(nand, in_sram_address);
target_read_u16(target, in_sram_address, value);
in_sram_address += 2;
}
return ERROR_OK;
}
static int poll_for_complete_op(struct nand_device *nand, const char *text)
{
if (mxc_nand_ready(nand, 1000) == -1) {
LOG_ERROR("%s sending timeout", text);
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
static int validate_target_state(struct nand_device *nand)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR(target_not_halted_err_msg);
return ERROR_NAND_OPERATION_FAILED;
}
if (mxc_nf_info->flags.target_little_endian !=
(target->endianness == TARGET_LITTLE_ENDIAN)) {
/*
* endianness changed after NAND controller probed
*/
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
int ecc_status_v1(struct nand_device *nand)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
uint16_t ecc_status;
target_read_u16(target, MXC_NF_ECCSTATUS, &ecc_status);
switch (ecc_status & 0x000c) {
case 1 << 2:
LOG_INFO("main area read with 1 (correctable) error");
break;
case 2 << 2:
LOG_INFO("main area read with more than 1 (incorrectable) error");
return ERROR_NAND_OPERATION_FAILED;
break;
}
switch (ecc_status & 0x0003) {
case 1:
LOG_INFO("spare area read with 1 (correctable) error");
break;
case 2:
LOG_INFO("main area read with more than 1 (incorrectable) error");
return ERROR_NAND_OPERATION_FAILED;
break;
}
return ERROR_OK;
}
int ecc_status_v2(struct nand_device *nand)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
uint16_t ecc_status;
uint8_t no_subpages;
uint8_t err;
no_subpages = nand->page_size >> 9;
target_read_u16(target, MXC_NF_ECCSTATUS, &ecc_status);
do {
err = ecc_status & 0xF;
if (err > 4) {
LOG_INFO("UnCorrectable RS-ECC Error");
return ERROR_NAND_OPERATION_FAILED;
} else if (err > 0)
LOG_INFO("%d Symbol Correctable RS-ECC Error", err);
ecc_status >>= 4;
} while (--no_subpages);
return ERROR_OK;
}
static int do_data_output(struct nand_device *nand)
{
struct mxc_nf_controller *mxc_nf_info = nand->controller_priv;
struct target *target = nand->target;
int poll_result;
switch (mxc_nf_info->fin) {
case MXC_NF_FIN_DATAOUT:
/*
* start data output operation (set MXC_NF_BIT_OP_DONE==0)
*/
target_write_u16(target, MXC_NF_CFG2, MXC_NF_BIT_DATAOUT_TYPE(mxc_nf_info->optype));
poll_result = poll_for_complete_op(nand, "data output");
if (poll_result != ERROR_OK)
return poll_result;
mxc_nf_info->fin = MXC_NF_FIN_NONE;
/*
* ECC stuff
*/
if (mxc_nf_info->optype == MXC_NF_DATAOUT_PAGE && mxc_nf_info->flags.hw_ecc_enabled) {
int ecc_status;
if (nfc_is_v1())
ecc_status = ecc_status_v1(nand);
else
ecc_status = ecc_status_v2(nand);
if (ecc_status != ERROR_OK)
return ecc_status;
}
break;
case MXC_NF_FIN_NONE:
break;
}
return ERROR_OK;
}
struct nand_flash_controller mxc_nand_flash_controller = {
.name = "mxc",
.nand_device_command = &mxc_nand_device_command,
.commands = mxc_nand_command_handler,
.init = &mxc_init,
.reset = &mxc_reset,
.command = &mxc_command,
.address = &mxc_address,
.write_data = &mxc_write_data,
.read_data = &mxc_read_data,
.write_page = &mxc_write_page,
.read_page = &mxc_read_page,
.nand_ready = &mxc_nand_ready,
};
+167
View File
@@ -0,0 +1,167 @@
/***************************************************************************
* Copyright (C) 2009 by Alexei Babich *
* Rezonans plc., Chelyabinsk, Russia *
* impatt@mail.ru *
* *
* Copyright (C) 2011 by Erik Ahlen *
* Avalon Innovation, Sweden *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* Freescale iMX OpenOCD NAND Flash controller support.
* based on Freescale iMX2* and iMX3* OpenOCD NAND Flash controller support.
*
* Many thanks to Ben Dooks for writing s3c24xx driver.
*/
#define MXC_NF_BUFSIZ (mxc_nf_info->mxc_regs_addr + 0x00)
#define MXC_NF_BUFADDR (mxc_nf_info->mxc_regs_addr + 0x04)
#define MXC_NF_FADDR (mxc_nf_info->mxc_regs_addr + 0x06)
#define MXC_NF_FCMD (mxc_nf_info->mxc_regs_addr + 0x08)
#define MXC_NF_BUFCFG (mxc_nf_info->mxc_regs_addr + 0x0a)
#define MXC_NF_ECCSTATUS (mxc_nf_info->mxc_regs_addr + 0x0c)
#define MXC_NF_ECCMAINPOS (mxc_nf_info->mxc_regs_addr + 0x0e)
#define MXC_NF_V1_ECCSPAREPOS (mxc_nf_info->mxc_regs_addr + 0x10)
#define MXC_NF_V2_SPAS (mxc_nf_info->mxc_regs_addr + 0x10)
#define MXC_NF_FWP (mxc_nf_info->mxc_regs_addr + 0x12)
#define MXC_NF_V1_UNLOCKSTART (mxc_nf_info->mxc_regs_addr + 0x14)
#define MXC_NF_V1_UNLOCKEND (mxc_nf_info->mxc_regs_addr + 0x16)
#define MXC_NF_V2_UNLOCKSTART0 (mxc_nf_info->mxc_regs_addr + 0x20)
#define MXC_NF_V2_UNLOCKSTART1 (mxc_nf_info->mxc_regs_addr + 0x24)
#define MXC_NF_V2_UNLOCKSTART2 (mxc_nf_info->mxc_regs_addr + 0x28)
#define MXC_NF_V2_UNLOCKSTART3 (mxc_nf_info->mxc_regs_addr + 0x2c)
#define MXC_NF_V2_UNLOCKEND0 (mxc_nf_info->mxc_regs_addr + 0x22)
#define MXC_NF_V2_UNLOCKEND1 (mxc_nf_info->mxc_regs_addr + 0x26)
#define MXC_NF_V2_UNLOCKEND2 (mxc_nf_info->mxc_regs_addr + 0x2a)
#define MXC_NF_V2_UNLOCKEND3 (mxc_nf_info->mxc_regs_addr + 0x2e)
#define MXC_NF_FWPSTATUS (mxc_nf_info->mxc_regs_addr + 0x18)
/*
* all bits not marked as self-clearing bit
*/
#define MXC_NF_CFG1 (mxc_nf_info->mxc_regs_addr + 0x1a)
#define MXC_NF_CFG2 (mxc_nf_info->mxc_regs_addr + 0x1c)
#define MXC_NF_MAIN_BUFFER0 (mxc_nf_info->mxc_base_addr + 0x0000)
#define MXC_NF_MAIN_BUFFER1 (mxc_nf_info->mxc_base_addr + 0x0200)
#define MXC_NF_MAIN_BUFFER2 (mxc_nf_info->mxc_base_addr + 0x0400)
#define MXC_NF_MAIN_BUFFER3 (mxc_nf_info->mxc_base_addr + 0x0600)
#define MXC_NF_V1_SPARE_BUFFER0 (mxc_nf_info->mxc_base_addr + 0x0800)
#define MXC_NF_V1_SPARE_BUFFER1 (mxc_nf_info->mxc_base_addr + 0x0810)
#define MXC_NF_V1_SPARE_BUFFER2 (mxc_nf_info->mxc_base_addr + 0x0820)
#define MXC_NF_V1_SPARE_BUFFER3 (mxc_nf_info->mxc_base_addr + 0x0830)
#define MXC_NF_V2_MAIN_BUFFER4 (mxc_nf_info->mxc_base_addr + 0x0800)
#define MXC_NF_V2_MAIN_BUFFER5 (mxc_nf_info->mxc_base_addr + 0x0a00)
#define MXC_NF_V2_MAIN_BUFFER6 (mxc_nf_info->mxc_base_addr + 0x0c00)
#define MXC_NF_V2_MAIN_BUFFER7 (mxc_nf_info->mxc_base_addr + 0x0e00)
#define MXC_NF_V2_SPARE_BUFFER0 (mxc_nf_info->mxc_base_addr + 0x1000)
#define MXC_NF_V2_SPARE_BUFFER1 (mxc_nf_info->mxc_base_addr + 0x1040)
#define MXC_NF_V2_SPARE_BUFFER2 (mxc_nf_info->mxc_base_addr + 0x1080)
#define MXC_NF_V2_SPARE_BUFFER3 (mxc_nf_info->mxc_base_addr + 0x10c0)
#define MXC_NF_V2_SPARE_BUFFER4 (mxc_nf_info->mxc_base_addr + 0x1100)
#define MXC_NF_V2_SPARE_BUFFER5 (mxc_nf_info->mxc_base_addr + 0x1140)
#define MXC_NF_V2_SPARE_BUFFER6 (mxc_nf_info->mxc_base_addr + 0x1180)
#define MXC_NF_V2_SPARE_BUFFER7 (mxc_nf_info->mxc_base_addr + 0x11c0)
#define MXC_NF_MAIN_BUFFER_LEN 512
#define MXC_NF_SPARE_BUFFER_LEN 16
#define MXC_NF_SPARE_BUFFER_MAX 64
#define MXC_NF_V1_LAST_BUFFADDR ((MXC_NF_V1_SPARE_BUFFER3) + \
MXC_NF_SPARE_BUFFER_LEN - 2)
#define MXC_NF_V2_LAST_BUFFADDR ((MXC_NF_V2_SPARE_BUFFER7) + \
MXC_NF_SPARE_BUFFER_LEN - 2)
/* bits in MXC_NF_CFG1 register */
#define MXC_NF_BIT_ECC_4BIT (1<<0)
#define MXC_NF_BIT_SPARE_ONLY_EN (1<<2)
#define MXC_NF_BIT_ECC_EN (1<<3)
#define MXC_NF_BIT_INT_DIS (1<<4)
#define MXC_NF_BIT_BE_EN (1<<5)
#define MXC_NF_BIT_RESET_EN (1<<6)
#define MXC_NF_BIT_FORCE_CE (1<<7)
#define MXC_NF_V2_CFG1_PPB(x) (((x) & 0x3) << 9)
/* bits in MXC_NF_CFG2 register */
/*Flash Command Input*/
#define MXC_NF_BIT_OP_FCI (1<<0)
/*
* Flash Address Input
*/
#define MXC_NF_BIT_OP_FAI (1<<1)
/*
* Flash Data Input
*/
#define MXC_NF_BIT_OP_FDI (1<<2)
/* see "enum mx_dataout_type" below */
#define MXC_NF_BIT_DATAOUT_TYPE(x) ((x)<<3)
#define MXC_NF_BIT_OP_DONE (1<<15)
#define MXC_CCM_CGR2 0x53f80028
#define MXC_GPR 0x43fac008
#define MX2_FMCR 0x10027814
#define MX2_FMCR_NF_16BIT_SEL (1<<4)
#define MX2_FMCR_NF_FMS (1<<5)
#define MX25_RCSR 0x53f80018
#define MX25_RCSR_NF_16BIT_SEL (1<<14)
#define MX25_RCSR_NF_FMS (1<<8)
#define MX25_RCSR_NF_4K (1<<9)
#define MX3_PCSR 0x53f8000c
#define MX3_PCSR_NF_16BIT_SEL (1<<31)
#define MX3_PCSR_NF_FMS (1<<30)
#define MX35_RCSR 0x53f80018
#define MX35_RCSR_NF_16BIT_SEL (1<<14)
#define MX35_RCSR_NF_FMS (1<<8)
#define MX35_RCSR_NF_4K (1<<9)
enum mxc_version {
MXC_VERSION_UKWN = 0,
MXC_VERSION_MX25 = 1,
MXC_VERSION_MX27 = 2,
MXC_VERSION_MX31 = 3,
MXC_VERSION_MX35 = 4
};
enum mxc_dataout_type {
MXC_NF_DATAOUT_PAGE = 1,
MXC_NF_DATAOUT_NANDID = 2,
MXC_NF_DATAOUT_NANDSTATUS = 4,
};
enum mxc_nf_finalize_action {
MXC_NF_FIN_NONE,
MXC_NF_FIN_DATAOUT,
};
struct mxc_nf_flags {
unsigned host_little_endian:1;
unsigned target_little_endian:1;
unsigned nand_readonly:1;
unsigned one_kb_sram:1;
unsigned hw_ecc_enabled:1;
unsigned biswap_enabled:1;
};
struct mxc_nf_controller {
enum mxc_version mxc_version;
uint32_t mxc_base_addr;
uint32_t mxc_regs_addr;
enum mxc_dataout_type optype;
enum mxc_nf_finalize_action fin;
struct mxc_nf_flags flags;
};
+75
View File
@@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "hello.h"
static int nonce_nand_command(struct nand_device *nand, uint8_t command)
{
return ERROR_OK;
}
static int nonce_nand_address(struct nand_device *nand, uint8_t address)
{
return ERROR_OK;
}
static int nonce_nand_read(struct nand_device *nand, void *data)
{
return ERROR_OK;
}
static int nonce_nand_write(struct nand_device *nand, uint16_t data)
{
return ERROR_OK;
}
static int nonce_nand_fast_block_write(struct nand_device *nand,
uint8_t *data, int size)
{
return ERROR_OK;
}
static int nonce_nand_reset(struct nand_device *nand)
{
return nonce_nand_command(nand, NAND_CMD_RESET);
}
NAND_DEVICE_COMMAND_HANDLER(nonce_nand_device_command)
{
return ERROR_OK;
}
static int nonce_nand_init(struct nand_device *nand)
{
return ERROR_OK;
}
struct nand_flash_controller nonce_nand_controller = {
.name = "nonce",
.commands = hello_command_handlers,
.nand_device_command = &nonce_nand_device_command,
.init = &nonce_nand_init,
.reset = &nonce_nand_reset,
.command = &nonce_nand_command,
.address = &nonce_nand_address,
.read_data = &nonce_nand_read,
.write_data = &nonce_nand_write,
.write_block_data = &nonce_nand_fast_block_write,
};
+229
View File
@@ -0,0 +1,229 @@
/***************************************************************************
* Copyright (C) 2010 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* NAND controller interface for Nuvoton NUC910
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "nuc910.h"
#include "arm_io.h"
#include <target/arm.h>
struct nuc910_nand_controller {
struct arm_nand_data io;
};
static int validate_target_state(struct nand_device *nand)
{
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_NAND_OPERATION_FAILED;
}
return ERROR_OK;
}
static int nuc910_nand_command(struct nand_device *nand, uint8_t command)
{
struct target *target = nand->target;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
target_write_u8(target, NUC910_SMCMD, command);
return ERROR_OK;
}
static int nuc910_nand_address(struct nand_device *nand, uint8_t address)
{
struct target *target = nand->target;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
target_write_u32(target, NUC910_SMADDR, ((address & 0xff) | NUC910_SMADDR_EOA));
return ERROR_OK;
}
static int nuc910_nand_read(struct nand_device *nand, void *data)
{
struct target *target = nand->target;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
target_read_u8(target, NUC910_SMDATA, data);
return ERROR_OK;
}
static int nuc910_nand_write(struct nand_device *nand, uint16_t data)
{
struct target *target = nand->target;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
target_write_u8(target, NUC910_SMDATA, data);
return ERROR_OK;
}
static int nuc910_nand_read_block_data(struct nand_device *nand,
uint8_t *data, int data_size)
{
struct nuc910_nand_controller *nuc910_nand = nand->controller_priv;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
nuc910_nand->io.chunk_size = nand->page_size;
/* try the fast way first */
result = arm_nandread(&nuc910_nand->io, data, data_size);
if (result != ERROR_NAND_NO_BUFFER)
return result;
/* else do it slowly */
while (data_size--)
nuc910_nand_read(nand, data++);
return ERROR_OK;
}
static int nuc910_nand_write_block_data(struct nand_device *nand,
uint8_t *data, int data_size)
{
struct nuc910_nand_controller *nuc910_nand = nand->controller_priv;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
nuc910_nand->io.chunk_size = nand->page_size;
/* try the fast way first */
result = arm_nandwrite(&nuc910_nand->io, data, data_size);
if (result != ERROR_NAND_NO_BUFFER)
return result;
/* else do it slowly */
while (data_size--)
nuc910_nand_write(nand, *data++);
return ERROR_OK;
}
static int nuc910_nand_reset(struct nand_device *nand)
{
return nuc910_nand_command(nand, NAND_CMD_RESET);
}
static int nuc910_nand_ready(struct nand_device *nand, int timeout)
{
struct target *target = nand->target;
uint32_t status;
do {
target_read_u32(target, NUC910_SMISR, &status);
if (status & NUC910_SMISR_RB_)
return 1;
alive_sleep(1);
} while (timeout-- > 0);
return 0;
}
NAND_DEVICE_COMMAND_HANDLER(nuc910_nand_device_command)
{
struct nuc910_nand_controller *nuc910_nand;
nuc910_nand = calloc(1, sizeof(struct nuc910_nand_controller));
if (!nuc910_nand) {
LOG_ERROR("no memory for nand controller");
return ERROR_NAND_DEVICE_INVALID;
}
nand->controller_priv = nuc910_nand;
return ERROR_OK;
}
static int nuc910_nand_init(struct nand_device *nand)
{
struct nuc910_nand_controller *nuc910_nand = nand->controller_priv;
struct target *target = nand->target;
int bus_width = nand->bus_width ? : 8;
int result;
result = validate_target_state(nand);
if (result != ERROR_OK)
return result;
/* nuc910 only supports 8bit */
if (bus_width != 8) {
LOG_ERROR("nuc910 only supports 8 bit bus width, not %i", bus_width);
return ERROR_NAND_OPERATION_NOT_SUPPORTED;
}
/* inform calling code about selected bus width */
nand->bus_width = bus_width;
nuc910_nand->io.target = target;
nuc910_nand->io.data = NUC910_SMDATA;
nuc910_nand->io.op = ARM_NAND_NONE;
/* configure nand controller */
target_write_u32(target, NUC910_FMICSR, NUC910_FMICSR_SM_EN);
target_write_u32(target, NUC910_SMCSR, 0x010000a8); /* 2048 page size */
target_write_u32(target, NUC910_SMTCR, 0x00010204);
target_write_u32(target, NUC910_SMIER, 0x00000000);
return ERROR_OK;
}
struct nand_flash_controller nuc910_nand_controller = {
.name = "nuc910",
.command = nuc910_nand_command,
.address = nuc910_nand_address,
.read_data = nuc910_nand_read,
.write_data = nuc910_nand_write,
.write_block_data = nuc910_nand_write_block_data,
.read_block_data = nuc910_nand_read_block_data,
.nand_ready = nuc910_nand_ready,
.reset = nuc910_nand_reset,
.nand_device_command = nuc910_nand_device_command,
.init = nuc910_nand_init,
};
+60
View File
@@ -0,0 +1,60 @@
/***************************************************************************
* Copyright (C) 2010 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* NAND controller interface for Nuvoton NUC910
*/
#ifndef NUC910_H
#define NUC910_H
#define NUC910_FMICSR 0xB000D000
#define NUC910_SMCSR 0xB000D0A0
#define NUC910_SMTCR 0xB000D0A4
#define NUC910_SMIER 0xB000D0A8
#define NUC910_SMISR 0xB000D0AC
#define NUC910_SMCMD 0xB000D0B0
#define NUC910_SMADDR 0xB000D0B4
#define NUC910_SMDATA 0xB000D0B8
#define NUC910_SMECC0 0xB000D0BC
#define NUC910_SMECC1 0xB000D0C0
#define NUC910_SMECC2 0xB000D0C4
#define NUC910_SMECC3 0xB000D0C8
#define NUC910_ECC4ST 0xB000D114
/* Global Control and Status Register (FMICSR) */
#define NUC910_FMICSR_SM_EN (1<<3)
/* NAND Flash Address Port Register (SMADDR) */
#define NUC910_SMADDR_EOA (1<<31)
/* NAND Flash Control and Status Register (SMCSR) */
#define NUC910_SMCSR_PSIZE (1<<3)
#define NUC910_SMCSR_DBW (1<<4)
/* NAND Flash Interrupt Status Register (SMISR) */
#define NUC910_SMISR_ECC_IF (1<<2)
#define NUC910_SMISR_RB_ (1<<18)
/* ECC4 Correction Status (ECC4ST) */
#endif /* NUC910_H */
+163
View File
@@ -0,0 +1,163 @@
/***************************************************************************
* Copyright (C) 2009 by Marvell Semiconductors, Inc. *
* Written by Nicolas Pitre <nico at marvell.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* NAND controller interface for Marvell Orion/Kirkwood SoCs.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "arm_io.h"
#include <target/arm.h>
struct orion_nand_controller {
struct arm_nand_data io;
uint32_t cmd;
uint32_t addr;
uint32_t data;
};
#define CHECK_HALTED \
do { \
if (target->state != TARGET_HALTED) { \
LOG_ERROR("NAND flash access requires halted target"); \
return ERROR_NAND_OPERATION_FAILED; \
} \
} while (0)
static int orion_nand_command(struct nand_device *nand, uint8_t command)
{
struct orion_nand_controller *hw = nand->controller_priv;
struct target *target = nand->target;
CHECK_HALTED;
target_write_u8(target, hw->cmd, command);
return ERROR_OK;
}
static int orion_nand_address(struct nand_device *nand, uint8_t address)
{
struct orion_nand_controller *hw = nand->controller_priv;
struct target *target = nand->target;
CHECK_HALTED;
target_write_u8(target, hw->addr, address);
return ERROR_OK;
}
static int orion_nand_read(struct nand_device *nand, void *data)
{
struct orion_nand_controller *hw = nand->controller_priv;
struct target *target = nand->target;
CHECK_HALTED;
target_read_u8(target, hw->data, data);
return ERROR_OK;
}
static int orion_nand_write(struct nand_device *nand, uint16_t data)
{
struct orion_nand_controller *hw = nand->controller_priv;
struct target *target = nand->target;
CHECK_HALTED;
target_write_u8(target, hw->data, data);
return ERROR_OK;
}
static int orion_nand_slow_block_write(struct nand_device *nand, uint8_t *data, int size)
{
while (size--)
orion_nand_write(nand, *data++);
return ERROR_OK;
}
static int orion_nand_fast_block_write(struct nand_device *nand, uint8_t *data, int size)
{
struct orion_nand_controller *hw = nand->controller_priv;
int retval;
hw->io.chunk_size = nand->page_size;
retval = arm_nandwrite(&hw->io, data, size);
if (retval == ERROR_NAND_NO_BUFFER)
retval = orion_nand_slow_block_write(nand, data, size);
return retval;
}
static int orion_nand_reset(struct nand_device *nand)
{
return orion_nand_command(nand, NAND_CMD_RESET);
}
NAND_DEVICE_COMMAND_HANDLER(orion_nand_device_command)
{
struct orion_nand_controller *hw;
uint32_t base;
uint8_t ale, cle;
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
hw = calloc(1, sizeof(*hw));
if (!hw) {
LOG_ERROR("no memory for nand controller");
return ERROR_NAND_DEVICE_INVALID;
}
nand->controller_priv = hw;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], base);
cle = 0;
ale = 1;
hw->data = base;
hw->cmd = base + (1 << cle);
hw->addr = base + (1 << ale);
hw->io.target = nand->target;
hw->io.data = hw->data;
hw->io.op = ARM_NAND_NONE;
return ERROR_OK;
}
static int orion_nand_init(struct nand_device *nand)
{
return ERROR_OK;
}
struct nand_flash_controller orion_nand_controller = {
.name = "orion",
.usage = "<target_id> <NAND_address>",
.command = orion_nand_command,
.address = orion_nand_address,
.read_data = orion_nand_read,
.write_data = orion_nand_write,
.write_block_data = orion_nand_fast_block_write,
.reset = orion_nand_reset,
.nand_device_command = orion_nand_device_command,
.init = orion_nand_init,
};
+118
View File
@@ -0,0 +1,118 @@
/***************************************************************************
* Copyright (C) 2007, 2008 by Ben Dooks *
* ben@fluff.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* S3C2410 OpenOCD NAND Flash controller support.
*
* Many thanks to Simtec Electronics for sponsoring this work.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "s3c24xx.h"
NAND_DEVICE_COMMAND_HANDLER(s3c2410_nand_device_command)
{
struct s3c24xx_nand_controller *info;
CALL_S3C24XX_DEVICE_COMMAND(nand, &info);
/* fill in the address fields for the core device */
info->cmd = S3C2410_NFCMD;
info->addr = S3C2410_NFADDR;
info->data = S3C2410_NFDATA;
info->nfstat = S3C2410_NFSTAT;
return ERROR_OK;
}
static int s3c2410_init(struct nand_device *nand)
{
struct target *target = nand->target;
target_write_u32(target, S3C2410_NFCONF,
S3C2410_NFCONF_EN | S3C2410_NFCONF_TACLS(3) |
S3C2410_NFCONF_TWRPH0(5) | S3C2410_NFCONF_TWRPH1(3));
return ERROR_OK;
}
static int s3c2410_write_data(struct nand_device *nand, uint16_t data)
{
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_write_u32(target, S3C2410_NFDATA, data);
return ERROR_OK;
}
static int s3c2410_read_data(struct nand_device *nand, void *data)
{
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_read_u8(target, S3C2410_NFDATA, data);
return ERROR_OK;
}
static int s3c2410_nand_ready(struct nand_device *nand, int timeout)
{
struct target *target = nand->target;
uint8_t status;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
do {
target_read_u8(target, S3C2410_NFSTAT, &status);
if (status & S3C2410_NFSTAT_BUSY)
return 1;
alive_sleep(1);
} while (timeout-- > 0);
return 0;
}
struct nand_flash_controller s3c2410_nand_controller = {
.name = "s3c2410",
.nand_device_command = &s3c2410_nand_device_command,
.init = &s3c2410_init,
.reset = &s3c24xx_reset,
.command = &s3c24xx_command,
.address = &s3c24xx_address,
.write_data = &s3c2410_write_data,
.read_data = &s3c2410_read_data,
.write_page = s3c24xx_write_page,
.read_page = s3c24xx_read_page,
.nand_ready = &s3c2410_nand_ready,
};
@@ -0,0 +1,77 @@
/***************************************************************************
* Copyright (C) 2007, 2008 by Ben Dooks *
* ben@fluff.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* S3C2412 OpenOCD NAND Flash controller support.
*
* Many thanks to Simtec Electronics for sponsoring this work.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "s3c24xx.h"
NAND_DEVICE_COMMAND_HANDLER(s3c2412_nand_device_command)
{
struct s3c24xx_nand_controller *info;
CALL_S3C24XX_DEVICE_COMMAND(nand, &info);
/* fill in the address fields for the core device */
info->cmd = S3C2440_NFCMD;
info->addr = S3C2440_NFADDR;
info->data = S3C2440_NFDATA;
info->nfstat = S3C2412_NFSTAT;
return ERROR_OK;
}
static int s3c2412_init(struct nand_device *nand)
{
struct target *target = nand->target;
target_write_u32(target, S3C2410_NFCONF,
S3C2440_NFCONF_TACLS(3) |
S3C2440_NFCONF_TWRPH0(7) |
S3C2440_NFCONF_TWRPH1(7));
target_write_u32(target, S3C2440_NFCONT,
S3C2412_NFCONT_INIT_MAIN_ECC |
S3C2440_NFCONT_ENABLE);
return ERROR_OK;
}
struct nand_flash_controller s3c2412_nand_controller = {
.name = "s3c2412",
.nand_device_command = &s3c2412_nand_device_command,
.init = &s3c2412_init,
.reset = &s3c24xx_reset,
.command = &s3c24xx_command,
.address = &s3c24xx_address,
.write_data = &s3c24xx_write_data,
.read_data = &s3c24xx_read_data,
.write_page = s3c24xx_write_page,
.read_page = s3c24xx_read_page,
.write_block_data = &s3c2440_write_block_data,
.read_block_data = &s3c2440_read_block_data,
.nand_ready = &s3c2440_nand_ready,
};
+168
View File
@@ -0,0 +1,168 @@
/***************************************************************************
* Copyright (C) 2007, 2008 by Ben Dooks *
* ben@fluff.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* S3C2440 OpenOCD NAND Flash controller support.
*
* Many thanks to Simtec Electronics for sponsoring this work.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "s3c24xx.h"
NAND_DEVICE_COMMAND_HANDLER(s3c2440_nand_device_command)
{
struct s3c24xx_nand_controller *info;
CALL_S3C24XX_DEVICE_COMMAND(nand, &info);
/* fill in the address fields for the core device */
info->cmd = S3C2440_NFCMD;
info->addr = S3C2440_NFADDR;
info->data = S3C2440_NFDATA;
info->nfstat = S3C2440_NFSTAT;
return ERROR_OK;
}
static int s3c2440_init(struct nand_device *nand)
{
struct target *target = nand->target;
target_write_u32(target, S3C2410_NFCONF,
S3C2440_NFCONF_TACLS(3) |
S3C2440_NFCONF_TWRPH0(7) |
S3C2440_NFCONF_TWRPH1(7));
target_write_u32(target, S3C2440_NFCONT,
S3C2440_NFCONT_INITECC | S3C2440_NFCONT_ENABLE);
return ERROR_OK;
}
int s3c2440_nand_ready(struct nand_device *nand, int timeout)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
uint8_t status;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
do {
target_read_u8(target, s3c24xx_info->nfstat, &status);
if (status & S3C2440_NFSTAT_READY)
return 1;
alive_sleep(1);
} while (timeout-- > 0);
return 0;
}
/* use the fact we can read/write 4 bytes in one go via a single 32bit op */
int s3c2440_read_block_data(struct nand_device *nand, uint8_t *data, int data_size)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
uint32_t nfdata = s3c24xx_info->data;
uint32_t tmp;
LOG_INFO("%s: reading data: %p, %p, %d", __func__, nand, data, data_size);
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
while (data_size >= 4) {
target_read_u32(target, nfdata, &tmp);
data[0] = tmp;
data[1] = tmp >> 8;
data[2] = tmp >> 16;
data[3] = tmp >> 24;
data_size -= 4;
data += 4;
}
while (data_size > 0) {
target_read_u8(target, nfdata, data);
data_size -= 1;
data += 1;
}
return ERROR_OK;
}
int s3c2440_write_block_data(struct nand_device *nand, uint8_t *data, int data_size)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
uint32_t nfdata = s3c24xx_info->data;
uint32_t tmp;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
while (data_size >= 4) {
tmp = le_to_h_u32(data);
target_write_u32(target, nfdata, tmp);
data_size -= 4;
data += 4;
}
while (data_size > 0) {
target_write_u8(target, nfdata, *data);
data_size -= 1;
data += 1;
}
return ERROR_OK;
}
struct nand_flash_controller s3c2440_nand_controller = {
.name = "s3c2440",
.nand_device_command = &s3c2440_nand_device_command,
.init = &s3c2440_init,
.reset = &s3c24xx_reset,
.command = &s3c24xx_command,
.address = &s3c24xx_address,
.write_data = &s3c24xx_write_data,
.read_data = &s3c24xx_read_data,
.write_page = s3c24xx_write_page,
.read_page = s3c24xx_read_page,
.write_block_data = &s3c2440_write_block_data,
.read_block_data = &s3c2440_read_block_data,
.nand_ready = &s3c2440_nand_ready,
};
@@ -0,0 +1,77 @@
/***************************************************************************
* Copyright (C) 2007, 2008 by Ben Dooks *
* ben@fluff.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* S3C2443 OpenOCD NAND Flash controller support.
*
* Many thanks to Simtec Electronics for sponsoring this work.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "s3c24xx.h"
NAND_DEVICE_COMMAND_HANDLER(s3c2443_nand_device_command)
{
struct s3c24xx_nand_controller *info;
CALL_S3C24XX_DEVICE_COMMAND(nand, &info);
/* fill in the address fields for the core device */
info->cmd = S3C2440_NFCMD;
info->addr = S3C2440_NFADDR;
info->data = S3C2440_NFDATA;
info->nfstat = S3C2412_NFSTAT;
return ERROR_OK;
}
static int s3c2443_init(struct nand_device *nand)
{
struct target *target = nand->target;
target_write_u32(target, S3C2410_NFCONF,
S3C2440_NFCONF_TACLS(3) |
S3C2440_NFCONF_TWRPH0(7) |
S3C2440_NFCONF_TWRPH1(7));
target_write_u32(target, S3C2440_NFCONT,
S3C2412_NFCONT_INIT_MAIN_ECC |
S3C2440_NFCONT_ENABLE);
return ERROR_OK;
}
struct nand_flash_controller s3c2443_nand_controller = {
.name = "s3c2443",
.nand_device_command = &s3c2443_nand_device_command,
.init = &s3c2443_init,
.reset = &s3c24xx_reset,
.command = &s3c24xx_command,
.address = &s3c24xx_address,
.write_data = &s3c24xx_write_data,
.read_data = &s3c24xx_read_data,
.write_page = s3c24xx_write_page,
.read_page = s3c24xx_read_page,
.write_block_data = &s3c2440_write_block_data,
.read_block_data = &s3c2440_read_block_data,
.nand_ready = &s3c2440_nand_ready,
};
+119
View File
@@ -0,0 +1,119 @@
/***************************************************************************
* Copyright (C) 2007, 2008 by Ben Dooks *
* ben@fluff.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* S3C24XX Series OpenOCD NAND Flash controller support.
*
* Many thanks to Simtec Electronics for sponsoring this work.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "s3c24xx.h"
S3C24XX_DEVICE_COMMAND()
{
*info = NULL;
struct s3c24xx_nand_controller *s3c24xx_info;
s3c24xx_info = malloc(sizeof(struct s3c24xx_nand_controller));
if (s3c24xx_info == NULL) {
LOG_ERROR("no memory for nand controller");
return -ENOMEM;
}
nand->controller_priv = s3c24xx_info;
*info = s3c24xx_info;
return ERROR_OK;
}
int s3c24xx_reset(struct nand_device *nand)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_write_u32(target, s3c24xx_info->cmd, 0xff);
return ERROR_OK;
}
int s3c24xx_command(struct nand_device *nand, uint8_t command)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_write_u16(target, s3c24xx_info->cmd, command);
return ERROR_OK;
}
int s3c24xx_address(struct nand_device *nand, uint8_t address)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_write_u16(target, s3c24xx_info->addr, address);
return ERROR_OK;
}
int s3c24xx_write_data(struct nand_device *nand, uint16_t data)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_write_u8(target, s3c24xx_info->data, data);
return ERROR_OK;
}
int s3c24xx_read_data(struct nand_device *nand, void *data)
{
struct s3c24xx_nand_controller *s3c24xx_info = nand->controller_priv;
struct target *target = nand->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("target must be halted to use S3C24XX NAND flash controller");
return ERROR_NAND_OPERATION_FAILED;
}
target_read_u8(target, s3c24xx_info->data, data);
return ERROR_OK;
}
@@ -0,0 +1,80 @@
/***************************************************************************
* Copyright (C) 2007, 2008 by Ben Dooks *
* ben@fluff.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef S3C24xx_NAND_H
#define S3C24xx_NAND_H
/*
* S3C24XX Series OpenOCD NAND Flash controller support.
*
* Many thanks to Simtec Electronics for sponsoring this work.
*/
#include "imp.h"
#include "s3c24xx_regs.h"
#include <target/target.h>
struct s3c24xx_nand_controller {
/* register addresses */
uint32_t cmd;
uint32_t addr;
uint32_t data;
uint32_t nfstat;
};
/* Default to using the un-translated NAND register based address */
#undef S3C2410_NFREG
#define S3C2410_NFREG(x) ((x) + 0x4e000000)
#define S3C24XX_DEVICE_COMMAND() \
COMMAND_HELPER(s3c24xx_nand_device_command, \
struct nand_device *nand, \
struct s3c24xx_nand_controller **info)
S3C24XX_DEVICE_COMMAND();
#define CALL_S3C24XX_DEVICE_COMMAND(d, i) \
do { \
int retval = CALL_COMMAND_HANDLER(s3c24xx_nand_device_command, d, i); \
if (ERROR_OK != retval) \
return retval; \
} while (0)
int s3c24xx_reset(struct nand_device *nand);
int s3c24xx_command(struct nand_device *nand, uint8_t command);
int s3c24xx_address(struct nand_device *nand, uint8_t address);
int s3c24xx_write_data(struct nand_device *nand, uint16_t data);
int s3c24xx_read_data(struct nand_device *nand, void *data);
#define s3c24xx_write_page NULL
#define s3c24xx_read_page NULL
/* code shared between different controllers */
int s3c2440_nand_ready(struct nand_device *nand, int timeout);
int s3c2440_read_block_data(struct nand_device *nand,
uint8_t *data, int data_size);
int s3c2440_write_block_data(struct nand_device *nand,
uint8_t *data, int data_size);
#endif /* S3C24xx_NAND_H */
@@ -0,0 +1,132 @@
/***************************************************************************
* Copyright (C) 2004, 2005 by Simtec Electronics *
* linux@simtec.co.uk *
* http://www.simtec.co.uk/products/SWLINUX/ *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
* S3C2410 NAND register definitions
*/
#ifndef __ASM_ARM_REGS_NAND
#define __ASM_ARM_REGS_NAND
#define S3C2410_NFREG(x) (x)
#define S3C2410_NFCONF S3C2410_NFREG(0x00)
#define S3C2410_NFCMD S3C2410_NFREG(0x04)
#define S3C2410_NFADDR S3C2410_NFREG(0x08)
#define S3C2410_NFDATA S3C2410_NFREG(0x0C)
#define S3C2410_NFSTAT S3C2410_NFREG(0x10)
#define S3C2410_NFECC S3C2410_NFREG(0x14)
#define S3C2440_NFCONT S3C2410_NFREG(0x04)
#define S3C2440_NFCMD S3C2410_NFREG(0x08)
#define S3C2440_NFADDR S3C2410_NFREG(0x0C)
#define S3C2440_NFDATA S3C2410_NFREG(0x10)
#define S3C2440_NFECCD0 S3C2410_NFREG(0x14)
#define S3C2440_NFECCD1 S3C2410_NFREG(0x18)
#define S3C2440_NFECCD S3C2410_NFREG(0x1C)
#define S3C2440_NFSTAT S3C2410_NFREG(0x20)
#define S3C2440_NFESTAT0 S3C2410_NFREG(0x24)
#define S3C2440_NFESTAT1 S3C2410_NFREG(0x28)
#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C)
#define S3C2440_NFMECC1 S3C2410_NFREG(0x30)
#define S3C2440_NFSECC S3C2410_NFREG(0x34)
#define S3C2440_NFSBLK S3C2410_NFREG(0x38)
#define S3C2440_NFEBLK S3C2410_NFREG(0x3C)
#define S3C2412_NFSBLK S3C2410_NFREG(0x20)
#define S3C2412_NFEBLK S3C2410_NFREG(0x24)
#define S3C2412_NFSTAT S3C2410_NFREG(0x28)
#define S3C2412_NFMECC_ERR0 S3C2410_NFREG(0x2C)
#define S3C2412_NFMECC_ERR1 S3C2410_NFREG(0x30)
#define S3C2412_NFMECC0 S3C2410_NFREG(0x34)
#define S3C2412_NFMECC1 S3C2410_NFREG(0x38)
#define S3C2412_NFSECC S3C2410_NFREG(0x3C)
#define S3C2410_NFCONF_EN (1 << 15)
#define S3C2410_NFCONF_512BYTE (1 << 14)
#define S3C2410_NFCONF_4STEP (1 << 13)
#define S3C2410_NFCONF_INITECC (1 << 12)
#define S3C2410_NFCONF_nFCE (1 << 11)
#define S3C2410_NFCONF_TACLS(x) ((x) << 8)
#define S3C2410_NFCONF_TWRPH0(x) ((x) << 4)
#define S3C2410_NFCONF_TWRPH1(x) ((x) << 0)
#define S3C2410_NFSTAT_BUSY (1 << 0)
#define S3C2440_NFCONF_BUSWIDTH_8 (0 << 0)
#define S3C2440_NFCONF_BUSWIDTH_16 (1 << 0)
#define S3C2440_NFCONF_ADVFLASH (1 << 3)
#define S3C2440_NFCONF_TACLS(x) ((x) << 12)
#define S3C2440_NFCONF_TWRPH0(x) ((x) << 8)
#define S3C2440_NFCONF_TWRPH1(x) ((x) << 4)
#define S3C2440_NFCONT_LOCKTIGHT (1 << 13)
#define S3C2440_NFCONT_SOFTLOCK (1 << 12)
#define S3C2440_NFCONT_ILLEGALACC_EN (1 << 10)
#define S3C2440_NFCONT_RNBINT_EN (1 << 9)
#define S3C2440_NFCONT_RN_FALLING (1 << 8)
#define S3C2440_NFCONT_SPARE_ECCLOCK (1 << 6)
#define S3C2440_NFCONT_MAIN_ECCLOCK (1 << 5)
#define S3C2440_NFCONT_INITECC (1 << 4)
#define S3C2440_NFCONT_nFCE (1 << 1)
#define S3C2440_NFCONT_ENABLE (1 << 0)
#define S3C2440_NFSTAT_READY (1 << 0)
#define S3C2440_NFSTAT_nCE (1 << 1)
#define S3C2440_NFSTAT_RnB_CHANGE (1 << 2)
#define S3C2440_NFSTAT_ILLEGAL_ACCESS (1 << 3)
#define S3C2412_NFCONF_NANDBOOT (1 << 31)
#define S3C2412_NFCONF_ECCCLKCON (1 << 30)
#define S3C2412_NFCONF_ECC_MLC (1 << 24)
#define S3C2412_NFCONF_TACLS_MASK (7 << 12) /* 1 extra bit of Tacls */
#define S3C2412_NFCONT_ECC4_DIRWR (1 << 18)
#define S3C2412_NFCONT_LOCKTIGHT (1 << 17)
#define S3C2412_NFCONT_SOFTLOCK (1 << 16)
#define S3C2412_NFCONT_ECC4_ENCINT (1 << 13)
#define S3C2412_NFCONT_ECC4_DECINT (1 << 12)
#define S3C2412_NFCONT_MAIN_ECC_LOCK (1 << 7)
#define S3C2412_NFCONT_INIT_MAIN_ECC (1 << 5)
#define S3C2412_NFCONT_nFCE1 (1 << 2)
#define S3C2412_NFCONT_nFCE0 (1 << 1)
#define S3C2412_NFSTAT_ECC_ENCDONE (1 << 7)
#define S3C2412_NFSTAT_ECC_DECDONE (1 << 6)
#define S3C2412_NFSTAT_ILLEGAL_ACCESS (1 << 5)
#define S3C2412_NFSTAT_RnB_CHANGE (1 << 4)
#define S3C2412_NFSTAT_nFCE1 (1 << 3)
#define S3C2412_NFSTAT_nFCE0 (1 << 2)
#define S3C2412_NFSTAT_Res1 (1 << 1)
#define S3C2412_NFSTAT_READY (1 << 0)
#define S3C2412_NFECCERR_SERRDATA(x) (((x) >> 21) & 0xf)
#define S3C2412_NFECCERR_SERRBIT(x) (((x) >> 18) & 0x7)
#define S3C2412_NFECCERR_MERRDATA(x) (((x) >> 7) & 0x3ff)
#define S3C2412_NFECCERR_MERRBIT(x) (((x) >> 4) & 0x7)
#define S3C2412_NFECCERR_SPARE_ERR(x) (((x) >> 2) & 0x3)
#define S3C2412_NFECCERR_MAIN_ERR(x) (((x) >> 2) & 0x3)
#define S3C2412_NFECCERR_NONE (0)
#define S3C2412_NFECCERR_1BIT (1)
#define S3C2412_NFECCERR_MULTIBIT (2)
#define S3C2412_NFECCERR_ECCAREA (3)
#endif /* __ASM_ARM_REGS_NAND */
@@ -0,0 +1,74 @@
/***************************************************************************
* Copyright (C) 2010 by Peter Korsgaard <jacmet@sunsite.dk> *
* Heavily based on s3c2412.c by Ben Dooks <ben@fluff.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "s3c24xx.h"
/* s3c64xx uses another base address for the nand controller than 24xx */
#undef S3C2410_NFREG
#define S3C2410_NFREG(x) ((x) + 0x70200000)
NAND_DEVICE_COMMAND_HANDLER(s3c6400_nand_device_command)
{
struct s3c24xx_nand_controller *info;
CALL_S3C24XX_DEVICE_COMMAND(nand, &info);
/* fill in the address fields for the core device */
info->cmd = S3C2440_NFCMD;
info->addr = S3C2440_NFADDR;
info->data = S3C2440_NFDATA;
info->nfstat = S3C2412_NFSTAT;
return ERROR_OK;
}
static int s3c6400_init(struct nand_device *nand)
{
struct target *target = nand->target;
target_write_u32(target, S3C2410_NFCONF,
S3C2440_NFCONF_TACLS(3) |
S3C2440_NFCONF_TWRPH0(7) |
S3C2440_NFCONF_TWRPH1(7) | 4);
target_write_u32(target, S3C2440_NFCONT,
S3C2412_NFCONT_INIT_MAIN_ECC |
S3C2440_NFCONT_ENABLE);
return ERROR_OK;
}
struct nand_flash_controller s3c6400_nand_controller = {
.name = "s3c6400",
.nand_device_command = &s3c6400_nand_device_command,
.init = &s3c6400_init,
.reset = &s3c24xx_reset,
.command = &s3c24xx_command,
.address = &s3c24xx_address,
.write_data = &s3c24xx_write_data,
.read_data = &s3c24xx_read_data,
.write_page = s3c24xx_write_page,
.read_page = s3c24xx_read_page,
.write_block_data = &s3c2440_write_block_data,
.read_block_data = &s3c2440_read_block_data,
.nand_ready = &s3c2440_nand_ready,
};
+628
View File
@@ -0,0 +1,628 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2002 Thomas Gleixner <tglx@linutronix.de> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* Partially based on drivers/mtd/nand_ids.c from Linux. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "core.h"
#include "imp.h"
#include "fileio.h"
#include <target/target.h>
/* to be removed */
extern struct nand_device *nand_devices;
COMMAND_HANDLER(handle_nand_list_command)
{
struct nand_device *p;
int i;
if (!nand_devices) {
command_print(CMD_CTX, "no NAND flash devices configured");
return ERROR_OK;
}
for (p = nand_devices, i = 0; p; p = p->next, i++) {
if (p->device)
command_print(CMD_CTX, "#%i: %s (%s) "
"pagesize: %i, buswidth: %i,\n\t"
"blocksize: %i, blocks: %i",
i, p->device->name, p->manufacturer->name,
p->page_size, p->bus_width,
p->erase_size, p->num_blocks);
else
command_print(CMD_CTX, "#%i: not probed", i);
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_nand_info_command)
{
int i = 0;
int j = 0;
int first = -1;
int last = -1;
switch (CMD_ARGC) {
default:
return ERROR_COMMAND_SYNTAX_ERROR;
case 1:
first = 0;
last = INT32_MAX;
break;
case 2:
COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], i);
first = last = i;
i = 0;
break;
case 3:
COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], first);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], last);
break;
}
struct nand_device *p;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &p);
if (ERROR_OK != retval)
return retval;
if (NULL == p->device) {
command_print(CMD_CTX, "#%s: not probed", CMD_ARGV[0]);
return ERROR_OK;
}
if (first >= p->num_blocks)
first = p->num_blocks - 1;
if (last >= p->num_blocks)
last = p->num_blocks - 1;
command_print(CMD_CTX,
"#%i: %s (%s) pagesize: %i, buswidth: %i, erasesize: %i",
i++,
p->device->name,
p->manufacturer->name,
p->page_size,
p->bus_width,
p->erase_size);
for (j = first; j <= last; j++) {
char *erase_state, *bad_state;
if (p->blocks[j].is_erased == 0)
erase_state = "not erased";
else if (p->blocks[j].is_erased == 1)
erase_state = "erased";
else
erase_state = "erase state unknown";
if (p->blocks[j].is_bad == 0)
bad_state = "";
else if (p->blocks[j].is_bad == 1)
bad_state = " (marked bad)";
else
bad_state = " (block condition unknown)";
command_print(CMD_CTX,
"\t#%i: 0x%8.8" PRIx32 " (%" PRId32 "kB) %s%s",
j,
p->blocks[j].offset,
p->blocks[j].size / 1024,
erase_state,
bad_state);
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_nand_probe_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct nand_device *p;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &p);
if (ERROR_OK != retval)
return retval;
retval = nand_probe(p);
if (retval == ERROR_OK) {
command_print(CMD_CTX, "NAND flash device '%s (%s)' found",
p->device->name, p->manufacturer->name);
}
return retval;
}
COMMAND_HANDLER(handle_nand_erase_command)
{
if (CMD_ARGC != 1 && CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
struct nand_device *p;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &p);
if (ERROR_OK != retval)
return retval;
unsigned long offset;
unsigned long length;
/* erase specified part of the chip; or else everything */
if (CMD_ARGC == 3) {
unsigned long size = p->erase_size * p->num_blocks;
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[1], offset);
if ((offset % p->erase_size) != 0 || offset >= size)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[2], length);
if ((length == 0) || (length % p->erase_size) != 0
|| (length + offset) > size)
return ERROR_COMMAND_SYNTAX_ERROR;
offset /= p->erase_size;
length /= p->erase_size;
} else {
offset = 0;
length = p->num_blocks;
}
retval = nand_erase(p, offset, offset + length - 1);
if (retval == ERROR_OK) {
command_print(CMD_CTX, "erased blocks %lu to %lu "
"on NAND flash device #%s '%s'",
offset, offset + length - 1,
CMD_ARGV[0], p->device->name);
}
return retval;
}
COMMAND_HANDLER(handle_nand_check_bad_blocks_command)
{
int first = -1;
int last = -1;
if ((CMD_ARGC < 1) || (CMD_ARGC > 3) || (CMD_ARGC == 2))
return ERROR_COMMAND_SYNTAX_ERROR;
struct nand_device *p;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &p);
if (ERROR_OK != retval)
return retval;
if (CMD_ARGC == 3) {
unsigned long offset;
unsigned long length;
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[1], offset);
if (offset % p->erase_size)
return ERROR_COMMAND_SYNTAX_ERROR;
offset /= p->erase_size;
COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[2], length);
if (length % p->erase_size)
return ERROR_COMMAND_SYNTAX_ERROR;
length -= 1;
length /= p->erase_size;
first = offset;
last = offset + length;
}
retval = nand_build_bbt(p, first, last);
if (retval == ERROR_OK) {
command_print(CMD_CTX, "checked NAND flash device for bad blocks, "
"use \"nand info\" command to list blocks");
}
return retval;
}
COMMAND_HANDLER(handle_nand_write_command)
{
struct nand_device *nand = NULL;
struct nand_fileio_state s;
int retval = CALL_COMMAND_HANDLER(nand_fileio_parse_args,
&s, &nand, FILEIO_READ, false, true);
if (ERROR_OK != retval)
return retval;
uint32_t total_bytes = s.size;
while (s.size > 0) {
int bytes_read = nand_fileio_read(nand, &s);
if (bytes_read <= 0) {
command_print(CMD_CTX, "error while reading file");
return nand_fileio_cleanup(&s);
}
s.size -= bytes_read;
retval = nand_write_page(nand, s.address / nand->page_size,
s.page, s.page_size, s.oob, s.oob_size);
if (ERROR_OK != retval) {
command_print(CMD_CTX, "failed writing file %s "
"to NAND flash %s at offset 0x%8.8" PRIx32,
CMD_ARGV[1], CMD_ARGV[0], s.address);
return nand_fileio_cleanup(&s);
}
s.address += s.page_size;
}
if (nand_fileio_finish(&s) == ERROR_OK) {
command_print(CMD_CTX, "wrote file %s to NAND flash %s up to "
"offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)",
CMD_ARGV[1], CMD_ARGV[0], s.address, duration_elapsed(&s.bench),
duration_kbps(&s.bench, total_bytes));
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_nand_verify_command)
{
struct nand_device *nand = NULL;
struct nand_fileio_state file;
int retval = CALL_COMMAND_HANDLER(nand_fileio_parse_args,
&file, &nand, FILEIO_READ, false, true);
if (ERROR_OK != retval)
return retval;
struct nand_fileio_state dev;
nand_fileio_init(&dev);
dev.address = file.address;
dev.size = file.size;
dev.oob_format = file.oob_format;
retval = nand_fileio_start(CMD_CTX, nand, NULL, FILEIO_NONE, &dev);
if (ERROR_OK != retval)
return retval;
while (file.size > 0) {
retval = nand_read_page(nand, dev.address / dev.page_size,
dev.page, dev.page_size, dev.oob, dev.oob_size);
if (ERROR_OK != retval) {
command_print(CMD_CTX, "reading NAND flash page failed");
nand_fileio_cleanup(&dev);
nand_fileio_cleanup(&file);
return retval;
}
int bytes_read = nand_fileio_read(nand, &file);
if (bytes_read <= 0) {
command_print(CMD_CTX, "error while reading file");
nand_fileio_cleanup(&dev);
nand_fileio_cleanup(&file);
return ERROR_FAIL;
}
if ((dev.page && memcmp(dev.page, file.page, dev.page_size)) ||
(dev.oob && memcmp(dev.oob, file.oob, dev.oob_size))) {
command_print(CMD_CTX, "NAND flash contents differ "
"at 0x%8.8" PRIx32, dev.address);
nand_fileio_cleanup(&dev);
nand_fileio_cleanup(&file);
return ERROR_FAIL;
}
file.size -= bytes_read;
dev.address += nand->page_size;
}
if (nand_fileio_finish(&file) == ERROR_OK) {
command_print(CMD_CTX, "verified file %s in NAND flash %s "
"up to offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)",
CMD_ARGV[1], CMD_ARGV[0], dev.address, duration_elapsed(&file.bench),
duration_kbps(&file.bench, dev.size));
}
return nand_fileio_cleanup(&dev);
}
COMMAND_HANDLER(handle_nand_dump_command)
{
int filesize;
struct nand_device *nand = NULL;
struct nand_fileio_state s;
int retval = CALL_COMMAND_HANDLER(nand_fileio_parse_args,
&s, &nand, FILEIO_WRITE, true, false);
if (ERROR_OK != retval)
return retval;
while (s.size > 0) {
size_t size_written;
retval = nand_read_page(nand, s.address / nand->page_size,
s.page, s.page_size, s.oob, s.oob_size);
if (ERROR_OK != retval) {
command_print(CMD_CTX, "reading NAND flash page failed");
nand_fileio_cleanup(&s);
return retval;
}
if (NULL != s.page)
fileio_write(&s.fileio, s.page_size, s.page, &size_written);
if (NULL != s.oob)
fileio_write(&s.fileio, s.oob_size, s.oob, &size_written);
s.size -= nand->page_size;
s.address += nand->page_size;
}
retval = fileio_size(&s.fileio, &filesize);
if (retval != ERROR_OK)
return retval;
if (nand_fileio_finish(&s) == ERROR_OK) {
command_print(CMD_CTX, "dumped %ld bytes in %fs (%0.3f KiB/s)",
(long)filesize, duration_elapsed(&s.bench),
duration_kbps(&s.bench, filesize));
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_nand_raw_access_command)
{
if ((CMD_ARGC < 1) || (CMD_ARGC > 2))
return ERROR_COMMAND_SYNTAX_ERROR;
struct nand_device *p;
int retval = CALL_COMMAND_HANDLER(nand_command_get_device, 0, &p);
if (ERROR_OK != retval)
return retval;
if (NULL == p->device) {
command_print(CMD_CTX, "#%s: not probed", CMD_ARGV[0]);
return ERROR_OK;
}
if (CMD_ARGC == 2)
COMMAND_PARSE_ENABLE(CMD_ARGV[1], p->use_raw);
const char *msg = p->use_raw ? "enabled" : "disabled";
command_print(CMD_CTX, "raw access is %s", msg);
return ERROR_OK;
}
static const struct command_registration nand_exec_command_handlers[] = {
{
.name = "list",
.handler = handle_nand_list_command,
.mode = COMMAND_EXEC,
.help = "list configured NAND flash devices",
},
{
.name = "info",
.handler = handle_nand_info_command,
.mode = COMMAND_EXEC,
.usage = "[banknum | first_bank_num last_bank_num]",
.help = "print info about one or more NAND flash devices",
},
{
.name = "probe",
.handler = handle_nand_probe_command,
.mode = COMMAND_EXEC,
.usage = "bank_id",
.help = "identify NAND flash device",
},
{
.name = "check_bad_blocks",
.handler = handle_nand_check_bad_blocks_command,
.mode = COMMAND_EXEC,
.usage = "bank_id [offset length]",
.help = "check all or part of NAND flash device for bad blocks",
},
{
.name = "erase",
.handler = handle_nand_erase_command,
.mode = COMMAND_EXEC,
.usage = "bank_id [offset length]",
.help = "erase all or subset of blocks on NAND flash device",
},
{
.name = "dump",
.handler = handle_nand_dump_command,
.mode = COMMAND_EXEC,
.usage = "bank_id filename offset length "
"['oob_raw'|'oob_only']",
.help = "dump from NAND flash device",
},
{
.name = "verify",
.handler = handle_nand_verify_command,
.mode = COMMAND_EXEC,
.usage = "bank_id filename offset "
"['oob_raw'|'oob_only'|'oob_softecc'|'oob_softecc_kw']",
.help = "verify NAND flash device",
},
{
.name = "write",
.handler = handle_nand_write_command,
.mode = COMMAND_EXEC,
.usage = "bank_id filename offset "
"['oob_raw'|'oob_only'|'oob_softecc'|'oob_softecc_kw']",
.help = "write to NAND flash device",
},
{
.name = "raw_access",
.handler = handle_nand_raw_access_command,
.mode = COMMAND_EXEC,
.usage = "bank_id ['enable'|'disable']",
.help = "raw access to NAND flash device",
},
COMMAND_REGISTRATION_DONE
};
static int nand_init(struct command_context *cmd_ctx)
{
if (!nand_devices)
return ERROR_OK;
struct command *parent = command_find_in_context(cmd_ctx, "nand");
return register_commands(cmd_ctx, parent, nand_exec_command_handlers);
}
COMMAND_HANDLER(handle_nand_init_command)
{
if (CMD_ARGC != 0)
return ERROR_COMMAND_SYNTAX_ERROR;
static bool nand_initialized;
if (nand_initialized) {
LOG_INFO("'nand init' has already been called");
return ERROR_OK;
}
nand_initialized = true;
LOG_DEBUG("Initializing NAND devices...");
return nand_init(CMD_CTX);
}
static int nand_list_walker(struct nand_flash_controller *c, void *x)
{
struct command_context *cmd_ctx = (struct command_context *)x;
command_print(cmd_ctx, " %s", c->name);
return ERROR_OK;
}
COMMAND_HANDLER(handle_nand_list_drivers)
{
command_print(CMD_CTX, "Available NAND flash controller drivers:");
return nand_driver_walk(&nand_list_walker, CMD_CTX);
}
static COMMAND_HELPER(create_nand_device, const char *bank_name,
struct nand_flash_controller *controller)
{
struct nand_device *c;
struct target *target;
int retval;
if (CMD_ARGC < 2)
return ERROR_COMMAND_SYNTAX_ERROR;
target = get_target(CMD_ARGV[1]);
if (!target) {
LOG_ERROR("invalid target %s", CMD_ARGV[1]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
if (NULL != controller->commands) {
retval = register_commands(CMD_CTX, NULL,
controller->commands);
if (ERROR_OK != retval)
return retval;
}
c = malloc(sizeof(struct nand_device));
if (c == NULL) {
LOG_ERROR("End of memory");
return ERROR_FAIL;
}
c->name = strdup(bank_name);
c->target = target;
c->controller = controller;
c->controller_priv = NULL;
c->manufacturer = NULL;
c->device = NULL;
c->bus_width = 0;
c->address_cycles = 0;
c->page_size = 0;
c->use_raw = 0;
c->next = NULL;
retval = CALL_COMMAND_HANDLER(controller->nand_device_command, c);
if (ERROR_OK != retval) {
LOG_ERROR("'%s' driver rejected nand flash. Usage: %s",
controller->name,
controller->usage);
free(c);
return retval;
}
if (controller->usage == NULL)
LOG_DEBUG("'%s' driver usage field missing", controller->name);
nand_device_add(c);
return ERROR_OK;
}
COMMAND_HANDLER(handle_nand_device_command)
{
if (CMD_ARGC < 2)
return ERROR_COMMAND_SYNTAX_ERROR;
/* save name and increment (for compatibility) with drivers */
const char *bank_name = *CMD_ARGV++;
CMD_ARGC--;
const char *driver_name = CMD_ARGV[0];
struct nand_flash_controller *controller;
controller = nand_driver_find_by_name(CMD_ARGV[0]);
if (NULL == controller) {
LOG_ERROR("No valid NAND flash driver found (%s)", driver_name);
return CALL_COMMAND_HANDLER(handle_nand_list_drivers);
}
return CALL_COMMAND_HANDLER(create_nand_device, bank_name, controller);
}
static const struct command_registration nand_config_command_handlers[] = {
{
.name = "device",
.handler = &handle_nand_device_command,
.mode = COMMAND_CONFIG,
.help = "defines a new NAND bank",
.usage = "bank_id driver target [driver_options ...]",
},
{
.name = "drivers",
.handler = &handle_nand_list_drivers,
.mode = COMMAND_ANY,
.help = "lists available NAND drivers",
.usage = ""
},
{
.name = "init",
.mode = COMMAND_CONFIG,
.handler = &handle_nand_init_command,
.help = "initialize NAND devices",
.usage = ""
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration nand_command_handlers[] = {
{
.name = "nand",
.mode = COMMAND_ANY,
.help = "NAND flash command group",
.usage = "",
.chain = nand_config_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
int nand_register_commands(struct command_context *cmd_ctx)
{
return register_commands(cmd_ctx, NULL, nand_command_handlers);
}
@@ -0,0 +1,51 @@
include $(top_srcdir)/common.mk
noinst_LTLIBRARIES = libocdflashnor.la
libocdflashnor_la_SOURCES = \
core.c \
tcl.c \
$(NOR_DRIVERS) \
drivers.c
NOR_DRIVERS = \
aduc702x.c \
at91sam4.c \
at91sam3.c \
at91sam7.c \
avrf.c \
cfi.c \
efm32.c \
em357.c \
faux.c \
lpc2000.c \
lpc288x.c \
lpc2900.c \
lpcspifi.c \
non_cfi.c \
ocl.c \
pic32mx.c \
spi.c \
stmsmi.c \
stellaris.c \
stm32f1x.c \
stm32f2x.c \
stm32lx.c \
str7x.c \
str9x.c \
str9xpec.c \
tms470.c \
virtual.c \
fm3.c \
dsp5680xx_flash.c \
kinetis.c
noinst_HEADERS = \
core.h \
cfi.h \
driver.h \
imp.h \
non_cfi.h \
ocl.h \
spi.h
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+645
View File
@@ -0,0 +1,645 @@
# Makefile.in generated by automake 1.13.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__make_dryrun = \
{ \
am__dry=no; \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
*) \
for am__flg in $$MAKEFLAGS; do \
case $$am__flg in \
*=*|--*) ;; \
*n*) am__dry=yes; break;; \
esac; \
done;; \
esac; \
test $$am__dry = yes; \
}
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
DIST_COMMON = $(top_srcdir)/common.mk $(srcdir)/Makefile.in \
$(srcdir)/Makefile.am $(top_srcdir)/depcomp $(noinst_HEADERS)
@INTERNAL_JIMTCL_TRUE@am__append_1 = -I$(top_srcdir)/jimtcl \
@INTERNAL_JIMTCL_TRUE@ -I$(top_builddir)/jimtcl
subdir = src/flash/nor
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config_subdir.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libocdflashnor_la_LIBADD =
am__objects_1 = aduc702x.lo at91sam4.lo at91sam3.lo at91sam7.lo \
avrf.lo cfi.lo efm32.lo em357.lo faux.lo lpc2000.lo lpc288x.lo \
lpc2900.lo lpcspifi.lo non_cfi.lo ocl.lo pic32mx.lo spi.lo \
stmsmi.lo stellaris.lo stm32f1x.lo stm32f2x.lo stm32lx.lo \
str7x.lo str9x.lo str9xpec.lo tms470.lo virtual.lo fm3.lo \
dsp5680xx_flash.lo kinetis.lo
am_libocdflashnor_la_OBJECTS = core.lo tcl.lo $(am__objects_1) \
drivers.lo
libocdflashnor_la_OBJECTS = $(am_libocdflashnor_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libocdflashnor_la_SOURCES)
DIST_SOURCES = $(libocdflashnor_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(noinst_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CC_FOR_BUILD = @CC_FOR_BUILD@
CFLAGS = @CFLAGS@
CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
FGREP = @FGREP@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
doxygen_as_html = @doxygen_as_html@
doxygen_as_pdf = @doxygen_as_pdf@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
# common flags used in openocd build
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
-I$(top_srcdir)/src/helper -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPKGLIBDIR=\"$(pkglibdir)\" $(am__append_1)
noinst_LTLIBRARIES = libocdflashnor.la
libocdflashnor_la_SOURCES = \
core.c \
tcl.c \
$(NOR_DRIVERS) \
drivers.c
NOR_DRIVERS = \
aduc702x.c \
at91sam4.c \
at91sam3.c \
at91sam7.c \
avrf.c \
cfi.c \
efm32.c \
em357.c \
faux.c \
lpc2000.c \
lpc288x.c \
lpc2900.c \
lpcspifi.c \
non_cfi.c \
ocl.c \
pic32mx.c \
spi.c \
stmsmi.c \
stellaris.c \
stm32f1x.c \
stm32f2x.c \
stm32lx.c \
str7x.c \
str9x.c \
str9xpec.c \
tms470.c \
virtual.c \
fm3.c \
dsp5680xx_flash.c \
kinetis.c
noinst_HEADERS = \
core.h \
cfi.h \
driver.h \
imp.h \
non_cfi.h \
ocl.h \
spi.h
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common.mk $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/flash/nor/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/flash/nor/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_srcdir)/common.mk:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
@list='$(noinst_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
libocdflashnor.la: $(libocdflashnor_la_OBJECTS) $(libocdflashnor_la_DEPENDENCIES) $(EXTRA_libocdflashnor_la_DEPENDENCIES)
$(AM_V_CCLD)$(LINK) $(libocdflashnor_la_OBJECTS) $(libocdflashnor_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aduc702x.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/at91sam3.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/at91sam4.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/at91sam7.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/avrf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cfi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drivers.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp5680xx_flash.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efm32.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/em357.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/faux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fm3.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kinetis.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpc2000.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpc288x.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpc2900.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpcspifi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/non_cfi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pic32mx.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stellaris.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stm32f1x.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stm32f2x.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stm32lx.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stmsmi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str7x.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str9x.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str9xpec.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tms470.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs:
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
clean: clean-am
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am:
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+411
View File
@@ -0,0 +1,411 @@
/***************************************************************************
* Copyright (C) 2008 by Kevin McGuire *
* Copyright (C) 2008 by Marcel Wijlaars *
* Copyright (C) 2009 by Michael Ashton *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include <target/arm.h>
static int aduc702x_build_sector_list(struct flash_bank *bank);
static int aduc702x_check_flash_completion(struct target *target, unsigned int timeout_ms);
static int aduc702x_set_write_enable(struct target *target, int enable);
#define ADUC702x_FLASH 0xfffff800
#define ADUC702x_FLASH_FEESTA (0*4)
#define ADUC702x_FLASH_FEEMOD (1*4)
#define ADUC702x_FLASH_FEECON (2*4)
#define ADUC702x_FLASH_FEEDAT (3*4)
#define ADUC702x_FLASH_FEEADR (4*4)
#define ADUC702x_FLASH_FEESIGN (5*4)
#define ADUC702x_FLASH_FEEPRO (6*4)
#define ADUC702x_FLASH_FEEHIDE (7*4)
/* flash bank aduc702x 0 0 0 0 <target#>
* The ADC7019-28 devices all have the same flash layout */
FLASH_BANK_COMMAND_HANDLER(aduc702x_flash_bank_command)
{
bank->base = 0x80000;
bank->size = 0xF800; /* top 4k not accessible */
aduc702x_build_sector_list(bank);
return ERROR_OK;
}
static int aduc702x_build_sector_list(struct flash_bank *bank)
{
/* aduc7026_struct flash_bank *aduc7026_info = bank->driver_priv; */
int i = 0;
uint32_t offset = 0;
/* sector size is 512 */
bank->num_sectors = bank->size / 512;
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
for (i = 0; i < bank->num_sectors; ++i) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 512;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 0;
}
return ERROR_OK;
}
static int aduc702x_protect_check(struct flash_bank *bank)
{
printf("aduc702x_protect_check not implemented yet.\n");
return ERROR_OK;
}
static int aduc702x_erase(struct flash_bank *bank, int first, int last)
{
/* int res; */
int x;
int count;
/* uint32_t v; */
struct target *target = bank->target;
aduc702x_set_write_enable(target, 1);
/* mass erase */
if (((first | last) == 0) || ((first == 0) && (last >= bank->num_sectors))) {
LOG_DEBUG("performing mass erase.");
target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEDAT, 0x3cff);
target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, 0xffc3);
target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x06);
if (aduc702x_check_flash_completion(target, 3500) != ERROR_OK) {
LOG_ERROR("mass erase failed");
aduc702x_set_write_enable(target, 0);
return ERROR_FLASH_OPERATION_FAILED;
}
LOG_DEBUG("mass erase successful.");
return ERROR_OK;
} else {
unsigned long adr;
count = last - first + 1;
for (x = 0; x < count; ++x) {
adr = bank->base + ((first + x) * 512);
target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, adr);
target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x05);
if (aduc702x_check_flash_completion(target, 50) != ERROR_OK) {
LOG_ERROR("failed to erase sector at address 0x%08lX", adr);
aduc702x_set_write_enable(target, 0);
return ERROR_FLASH_SECTOR_NOT_ERASED;
}
LOG_DEBUG("erased sector at address 0x%08lX", adr);
}
}
aduc702x_set_write_enable(target, 0);
return ERROR_OK;
}
static int aduc702x_protect(struct flash_bank *bank, int set, int first, int last)
{
printf("aduc702x_protect not implemented yet.\n");
return ERROR_FLASH_OPERATION_FAILED;
}
/* If this fn returns ERROR_TARGET_RESOURCE_NOT_AVAILABLE, then the caller can fall
* back to another mechanism that does not require onboard RAM
*
* Caller should not check for other return values specifically
*/
static int aduc702x_write_block(struct flash_bank *bank,
uint8_t *buffer,
uint32_t offset,
uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 7000;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[6];
struct arm_algorithm arm_algo;
int retval = ERROR_OK;
if (((count%2) != 0) || ((offset%2) != 0)) {
LOG_ERROR("write block must be multiple of two bytes in offset & length");
return ERROR_FAIL;
}
/* parameters:
r0 - address of source data (absolute)
r1 - number of halfwords to be copied
r2 - start address in flash (offset from beginning of flash memory)
r3 - exit code
r4 - base address of flash controller (0xFFFFF800)
registers:
r5 - scratch
r6 - set to 2, used to write flash command
*/
static const uint32_t aduc702x_flash_write_code[] = {
/* <_start>: */
0xe3a05008, /* mov r5, #8 ; 0x8 */
0xe5845004, /* str r5, [r4, #4] */
0xe3a06002, /* mov r6, #2 ; 0x2 */
/* <next>: */
0xe1c421b0, /* strh r2, [r4, #16] */
0xe0d050b2, /* ldrh r5, [r0], #2 */
0xe1c450bc, /* strh r5, [r4, #12] */
0xe5c46008, /* strb r6, [r4, #8] */
/* <wait_complete>: */
0xe1d430b0, /* ldrh r3, [r4] */
0xe3130004, /* tst r3, #4 ; 0x4 */
0x1afffffc, /* bne 1001c <wait_complete> */
0xe2822002, /* add r2, r2, #2 ; 0x2 */
0xe2511001, /* subs r1, r1, #1 ; 0x1 */
0x0a000001, /* beq 1003c <done> */
0xe3130001, /* tst r3, #1 ; 0x1 */
0x1afffff3, /* bne 1000c <next> */
/* <done>: */
0xeafffffe /* b 1003c <done> */
};
/* flash write code */
if (target_alloc_working_area(target, sizeof(aduc702x_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
retval = target_write_buffer(target, write_algorithm->address,
sizeof(aduc702x_flash_write_code), (uint8_t *)aduc702x_flash_write_code);
if (retval != ERROR_OK)
return retval;
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a buffer,
*free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING("no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
arm_algo.common_magic = ARM_COMMON_MAGIC;
arm_algo.core_mode = ARM_MODE_SVC;
arm_algo.core_state = ARM_STATE_ARM;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
while (count > 0) {
uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count;
retval = target_write_buffer(target, source->address, thisrun_count, buffer);
if (retval != ERROR_OK)
break;
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, thisrun_count/2);
buf_set_u32(reg_params[2].value, 0, 32, address);
buf_set_u32(reg_params[4].value, 0, 32, 0xFFFFF800);
retval = target_run_algorithm(target, 0, NULL, 5,
reg_params, write_algorithm->address,
write_algorithm->address +
sizeof(aduc702x_flash_write_code) - 4,
10000, &arm_algo);
if (retval != ERROR_OK) {
LOG_ERROR("error executing aduc702x flash write algorithm");
break;
}
if ((buf_get_u32(reg_params[3].value, 0, 32) & 1) != 1) {
/* FIX!!!! what does this mean??? replace w/sensible error message */
LOG_ERROR("aduc702x detected error writing flash");
retval = ERROR_FAIL;
break;
}
buffer += thisrun_count;
address += thisrun_count;
count -= thisrun_count;
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
return retval;
}
/* All-JTAG, single-access method. Very slow. Used only if there is no
* working area available. */
static int aduc702x_write_single(struct flash_bank *bank,
uint8_t *buffer,
uint32_t offset,
uint32_t count)
{
uint32_t x;
uint8_t b;
struct target *target = bank->target;
aduc702x_set_write_enable(target, 1);
for (x = 0; x < count; x += 2) {
/* FEEADR = address */
target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEADR, offset + x);
/* set up data */
if ((x + 1) == count) {
/* last byte */
target_read_u8(target, offset + x + 1, &b);
} else
b = buffer[x + 1];
target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEDAT, buffer[x] | (b << 8));
/* do single-write command */
target_write_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEECON, 0x02);
if (aduc702x_check_flash_completion(target, 1) != ERROR_OK) {
LOG_ERROR("single write failed for address 0x%08lX",
(unsigned long)(offset + x));
aduc702x_set_write_enable(target, 0);
return ERROR_FLASH_OPERATION_FAILED;
}
}
LOG_DEBUG("wrote %d bytes at address 0x%08lX", (int)count, (unsigned long)(offset + x));
aduc702x_set_write_enable(target, 0);
return ERROR_OK;
}
static int aduc702x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
int retval;
/* try using a block write */
retval = aduc702x_write_block(bank, buffer, offset, count);
if (retval != ERROR_OK) {
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* use normal (slow) JTAG method */
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
retval = aduc702x_write_single(bank, buffer, offset, count);
if (retval != ERROR_OK) {
LOG_ERROR("slow write failed");
return ERROR_FLASH_OPERATION_FAILED;
}
}
}
return retval;
}
static int aduc702x_probe(struct flash_bank *bank)
{
return ERROR_OK;
}
static int aduc702x_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "aduc702x flash driver info");
return ERROR_OK;
}
/* sets FEEMOD bit 3
* enable = 1 enables writes & erases, 0 disables them */
static int aduc702x_set_write_enable(struct target *target, int enable)
{
/* don't bother to preserve int enable bit here */
target_write_u16(target, ADUC702x_FLASH + ADUC702x_FLASH_FEEMOD, enable ? 8 : 0);
return ERROR_OK;
}
/* wait up to timeout_ms for controller to not be busy,
* then check whether the command passed or failed.
*
* this function sleeps 1ms between checks (after the first one),
* so in some cases may slow things down without a usleep after the first read */
static int aduc702x_check_flash_completion(struct target *target, unsigned int timeout_ms)
{
uint8_t v = 4;
long long endtime = timeval_ms() + timeout_ms;
while (1) {
target_read_u8(target, ADUC702x_FLASH + ADUC702x_FLASH_FEESTA, &v);
if ((v & 4) == 0)
break;
alive_sleep(1);
if (timeval_ms() >= endtime)
break;
}
if (v & 2)
return ERROR_FAIL;
/* if a command is ignored, both the success and fail bits may be 0 */
else if ((v & 3) == 0)
return ERROR_FAIL;
else
return ERROR_OK;
}
struct flash_driver aduc702x_flash = {
.name = "aduc702x",
.flash_bank_command = aduc702x_flash_bank_command,
.erase = aduc702x_erase,
.protect = aduc702x_protect,
.write = aduc702x_write,
.read = default_flash_read,
.probe = aduc702x_probe,
.auto_probe = aduc702x_probe,
.erase_check = default_flash_blank_check,
.protect_check = aduc702x_protect_check,
.info = aduc702x_info
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+485
View File
@@ -0,0 +1,485 @@
/***************************************************************************
* Copyright (C) 2009 by Simon Qian *
* SimonQian@SimonQian.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <target/avrt.h>
/* AVR_JTAG_Instructions */
#define AVR_JTAG_INS_LEN 4
/* Public Instructions: */
#define AVR_JTAG_INS_EXTEST 0x00
#define AVR_JTAG_INS_IDCODE 0x01
#define AVR_JTAG_INS_SAMPLE_PRELOAD 0x02
#define AVR_JTAG_INS_BYPASS 0x0F
/* AVR Specified Public Instructions: */
#define AVR_JTAG_INS_AVR_RESET 0x0C
#define AVR_JTAG_INS_PROG_ENABLE 0x04
#define AVR_JTAG_INS_PROG_COMMANDS 0x05
#define AVR_JTAG_INS_PROG_PAGELOAD 0x06
#define AVR_JTAG_INS_PROG_PAGEREAD 0x07
/* Data Registers: */
#define AVR_JTAG_REG_Bypass_Len 1
#define AVR_JTAG_REG_DeviceID_Len 32
#define AVR_JTAG_REG_Reset_Len 1
#define AVR_JTAG_REG_JTAGID_Len 32
#define AVR_JTAG_REG_ProgrammingEnable_Len 16
#define AVR_JTAG_REG_ProgrammingCommand_Len 15
#define AVR_JTAG_REG_FlashDataByte_Len 16
struct avrf_type {
char name[15];
uint16_t chip_id;
int flash_page_size;
int flash_page_num;
int eeprom_page_size;
int eeprom_page_num;
};
struct avrf_flash_bank {
int ppage_size;
int probed;
};
static struct avrf_type avft_chips_info[] = {
/* name, chip_id, flash_page_size, flash_page_num,
* eeprom_page_size, eeprom_page_num
*/
{"atmega128", 0x9702, 256, 512, 8, 512},
{"at90can128", 0x9781, 256, 512, 8, 512},
};
/* avr program functions */
static int avr_jtag_reset(struct avr_common *avr, uint32_t reset)
{
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_AVR_RESET);
avr_jtag_senddat(avr->jtag_info.tap, NULL, reset, AVR_JTAG_REG_Reset_Len);
return ERROR_OK;
}
static int avr_jtag_read_jtagid(struct avr_common *avr, uint32_t *id)
{
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_IDCODE);
avr_jtag_senddat(avr->jtag_info.tap, id, 0, AVR_JTAG_REG_JTAGID_Len);
return ERROR_OK;
}
static int avr_jtagprg_enterprogmode(struct avr_common *avr)
{
avr_jtag_reset(avr, 1);
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_ENABLE);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0xA370, AVR_JTAG_REG_ProgrammingEnable_Len);
return ERROR_OK;
}
static int avr_jtagprg_leaveprogmode(struct avr_common *avr)
{
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2300, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3300, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_ENABLE);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0, AVR_JTAG_REG_ProgrammingEnable_Len);
avr_jtag_reset(avr, 0);
return ERROR_OK;
}
static int avr_jtagprg_chiperase(struct avr_common *avr)
{
uint32_t poll_value;
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2380, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3180, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3380, AVR_JTAG_REG_ProgrammingCommand_Len);
do {
poll_value = 0;
avr_jtag_senddat(avr->jtag_info.tap,
&poll_value,
0x3380,
AVR_JTAG_REG_ProgrammingCommand_Len);
if (ERROR_OK != mcu_execute_queue())
return ERROR_FAIL;
LOG_DEBUG("poll_value = 0x%04" PRIx32 "", poll_value);
} while (!(poll_value & 0x0200));
return ERROR_OK;
}
static int avr_jtagprg_writeflashpage(struct avr_common *avr,
uint8_t *page_buf,
uint32_t buf_size,
uint32_t addr,
uint32_t page_size)
{
uint32_t i, poll_value;
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x2310, AVR_JTAG_REG_ProgrammingCommand_Len);
/* load addr high byte */
avr_jtag_senddat(avr->jtag_info.tap,
NULL,
0x0700 | ((addr >> 9) & 0xFF),
AVR_JTAG_REG_ProgrammingCommand_Len);
/* load addr low byte */
avr_jtag_senddat(avr->jtag_info.tap,
NULL,
0x0300 | ((addr >> 1) & 0xFF),
AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_PAGELOAD);
for (i = 0; i < page_size; i++) {
if (i < buf_size)
avr_jtag_senddat(avr->jtag_info.tap, NULL, page_buf[i], 8);
else
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0xFF, 8);
}
avr_jtag_sendinstr(avr->jtag_info.tap, NULL, AVR_JTAG_INS_PROG_COMMANDS);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3500, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len);
avr_jtag_senddat(avr->jtag_info.tap, NULL, 0x3700, AVR_JTAG_REG_ProgrammingCommand_Len);
do {
poll_value = 0;
avr_jtag_senddat(avr->jtag_info.tap,
&poll_value,
0x3700,
AVR_JTAG_REG_ProgrammingCommand_Len);
if (ERROR_OK != mcu_execute_queue())
return ERROR_FAIL;
LOG_DEBUG("poll_value = 0x%04" PRIx32 "", poll_value);
} while (!(poll_value & 0x0200));
return ERROR_OK;
}
FLASH_BANK_COMMAND_HANDLER(avrf_flash_bank_command)
{
struct avrf_flash_bank *avrf_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
avrf_info = malloc(sizeof(struct avrf_flash_bank));
bank->driver_priv = avrf_info;
avrf_info->probed = 0;
return ERROR_OK;
}
static int avrf_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
struct avr_common *avr = target->arch_info;
int status;
LOG_DEBUG("%s", __func__);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
status = avr_jtagprg_enterprogmode(avr);
if (status != ERROR_OK)
return status;
status = avr_jtagprg_chiperase(avr);
if (status != ERROR_OK)
return status;
return avr_jtagprg_leaveprogmode(avr);
}
static int avrf_protect(struct flash_bank *bank, int set, int first, int last)
{
LOG_INFO("%s", __func__);
return ERROR_OK;
}
static int avrf_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
struct avr_common *avr = target->arch_info;
uint32_t cur_size, cur_buffer_size, page_size;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
page_size = bank->sectors[0].size;
if ((offset % page_size) != 0) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required %" PRIu32 "-byte alignment",
offset,
page_size);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
LOG_DEBUG("offset is 0x%08" PRIx32 "", offset);
LOG_DEBUG("count is %" PRId32 "", count);
if (ERROR_OK != avr_jtagprg_enterprogmode(avr))
return ERROR_FAIL;
cur_size = 0;
while (count > 0) {
if (count > page_size)
cur_buffer_size = page_size;
else
cur_buffer_size = count;
avr_jtagprg_writeflashpage(avr,
buffer + cur_size,
cur_buffer_size,
offset + cur_size,
page_size);
count -= cur_buffer_size;
cur_size += cur_buffer_size;
keep_alive();
}
return avr_jtagprg_leaveprogmode(avr);
}
#define EXTRACT_MFG(X) (((X) & 0xffe) >> 1)
#define EXTRACT_PART(X) (((X) & 0xffff000) >> 12)
#define EXTRACT_VER(X) (((X) & 0xf0000000) >> 28)
static int avrf_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct avrf_flash_bank *avrf_info = bank->driver_priv;
struct avr_common *avr = target->arch_info;
struct avrf_type *avr_info = NULL;
int i;
uint32_t device_id;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
avrf_info->probed = 0;
avr_jtag_read_jtagid(avr, &device_id);
if (ERROR_OK != mcu_execute_queue())
return ERROR_FAIL;
LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
if (EXTRACT_MFG(device_id) != 0x1F)
LOG_ERROR("0x%" PRIx32 " is invalid Manufacturer for avr, 0x%X is expected",
EXTRACT_MFG(device_id),
0x1F);
for (i = 0; i < (int)ARRAY_SIZE(avft_chips_info); i++) {
if (avft_chips_info[i].chip_id == EXTRACT_PART(device_id)) {
avr_info = &avft_chips_info[i];
LOG_INFO("target device is %s", avr_info->name);
break;
}
}
if (avr_info != NULL) {
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
/* chip found */
bank->base = 0x00000000;
bank->size = (avr_info->flash_page_size * avr_info->flash_page_num);
bank->num_sectors = avr_info->flash_page_num;
bank->sectors = malloc(sizeof(struct flash_sector) * avr_info->flash_page_num);
for (i = 0; i < avr_info->flash_page_num; i++) {
bank->sectors[i].offset = i * avr_info->flash_page_size;
bank->sectors[i].size = avr_info->flash_page_size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
avrf_info->probed = 1;
return ERROR_OK;
} else {
/* chip not supported */
LOG_ERROR("0x%" PRIx32 " is not support for avr", EXTRACT_PART(device_id));
avrf_info->probed = 1;
return ERROR_FAIL;
}
}
static int avrf_auto_probe(struct flash_bank *bank)
{
struct avrf_flash_bank *avrf_info = bank->driver_priv;
if (avrf_info->probed)
return ERROR_OK;
return avrf_probe(bank);
}
static int avrf_protect_check(struct flash_bank *bank)
{
LOG_INFO("%s", __func__);
return ERROR_OK;
}
static int avrf_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct target *target = bank->target;
struct avr_common *avr = target->arch_info;
struct avrf_type *avr_info = NULL;
int i;
uint32_t device_id;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
avr_jtag_read_jtagid(avr, &device_id);
if (ERROR_OK != mcu_execute_queue())
return ERROR_FAIL;
LOG_INFO("device id = 0x%08" PRIx32 "", device_id);
if (EXTRACT_MFG(device_id) != 0x1F)
LOG_ERROR("0x%" PRIx32 " is invalid Manufacturer for avr, 0x%X is expected",
EXTRACT_MFG(device_id),
0x1F);
for (i = 0; i < (int)ARRAY_SIZE(avft_chips_info); i++) {
if (avft_chips_info[i].chip_id == EXTRACT_PART(device_id)) {
avr_info = &avft_chips_info[i];
LOG_INFO("target device is %s", avr_info->name);
break;
}
}
if (avr_info != NULL) {
/* chip found */
snprintf(buf, buf_size, "%s - Rev: 0x%" PRIx32 "", avr_info->name,
EXTRACT_VER(device_id));
return ERROR_OK;
} else {
/* chip not supported */
snprintf(buf, buf_size, "Cannot identify target as a avr\n");
return ERROR_FLASH_OPERATION_FAILED;
}
}
static int avrf_mass_erase(struct flash_bank *bank)
{
struct target *target = bank->target;
struct avr_common *avr = target->arch_info;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((ERROR_OK != avr_jtagprg_enterprogmode(avr))
|| (ERROR_OK != avr_jtagprg_chiperase(avr))
|| (ERROR_OK != avr_jtagprg_leaveprogmode(avr)))
return ERROR_FAIL;
return ERROR_OK;
}
COMMAND_HANDLER(avrf_handle_mass_erase_command)
{
int i;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
if (avrf_mass_erase(bank) == ERROR_OK) {
/* set all sectors as erased */
for (i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
command_print(CMD_CTX, "avr mass erase complete");
} else
command_print(CMD_CTX, "avr mass erase failed");
LOG_DEBUG("%s", __func__);
return ERROR_OK;
}
static const struct command_registration avrf_exec_command_handlers[] = {
{
.name = "mass_erase",
.usage = "<bank>",
.handler = avrf_handle_mass_erase_command,
.mode = COMMAND_EXEC,
.help = "erase entire device",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration avrf_command_handlers[] = {
{
.name = "avrf",
.mode = COMMAND_ANY,
.help = "AVR flash command group",
.usage = "",
.chain = avrf_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver avr_flash = {
.name = "avr",
.commands = avrf_command_handlers,
.flash_bank_command = avrf_flash_bank_command,
.erase = avrf_erase,
.protect = avrf_protect,
.write = avrf_write,
.read = default_flash_read,
.probe = avrf_probe,
.auto_probe = avrf_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = avrf_protect_check,
.info = avrf_info,
};
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CFI_H
#define CFI_H
#define CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7 0xE0 /* DQ5..DQ7 */
#define CFI_STATUS_POLL_MASK_DQ6_DQ7 0xC0 /* DQ6..DQ7 */
struct cfi_flash_bank {
int x16_as_x8;
int jedec_probe;
int not_cfi;
int probed;
uint16_t manufacturer;
uint16_t device_id;
uint8_t qry[3];
/* identification string */
uint16_t pri_id;
uint16_t pri_addr;
uint16_t alt_id;
uint16_t alt_addr;
/* device-system interface */
uint8_t vcc_min;
uint8_t vcc_max;
uint8_t vpp_min;
uint8_t vpp_max;
uint8_t word_write_timeout_typ;
uint8_t buf_write_timeout_typ;
uint8_t block_erase_timeout_typ;
uint8_t chip_erase_timeout_typ;
uint8_t word_write_timeout_max;
uint8_t buf_write_timeout_max;
uint8_t block_erase_timeout_max;
uint8_t chip_erase_timeout_max;
uint8_t status_poll_mask;
/* flash geometry */
uint32_t dev_size;
uint16_t interface_desc;
uint16_t max_buf_write_size;
uint8_t num_erase_regions;
uint32_t *erase_region_info;
void *pri_ext;
void *alt_ext;
/* calculated timeouts */
unsigned word_write_timeout;
unsigned buf_write_timeout;
unsigned block_erase_timeout;
unsigned chip_erase_timeout;
};
/* Intel primary extended query table
* as defined for the Advanced+ Boot Block Flash Memory (C3)
* and used by the linux kernel cfi driver (as of 2.6.14)
*/
struct cfi_intel_pri_ext {
uint8_t pri[3];
uint8_t major_version;
uint8_t minor_version;
uint32_t feature_support;
uint8_t suspend_cmd_support;
uint16_t blk_status_reg_mask;
uint8_t vcc_optimal;
uint8_t vpp_optimal;
uint8_t num_protection_fields;
uint16_t prot_reg_addr;
uint8_t fact_prot_reg_size;
uint8_t user_prot_reg_size;
uint8_t extra[0];
};
/* Spansion primary extended query table as defined for and used by
* the linux kernel cfi driver (as of 2.6.15)
*/
struct cfi_spansion_pri_ext {
uint8_t pri[3];
uint8_t major_version;
uint8_t minor_version;
uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
uint8_t EraseSuspend;
uint8_t BlkProt;
uint8_t TmpBlkUnprotect;
uint8_t BlkProtUnprot;
uint8_t SimultaneousOps;
uint8_t BurstMode;
uint8_t PageMode;
uint8_t VppMin;
uint8_t VppMax;
uint8_t TopBottom;
int _reversed_geometry;
uint32_t _unlock1;
uint32_t _unlock2;
};
/* Atmel primary extended query table as defined for and used by
* the linux kernel cfi driver (as of 2.6.20+)
*/
struct cfi_atmel_pri_ext {
uint8_t pri[3];
uint8_t major_version;
uint8_t minor_version;
uint8_t features;
uint8_t bottom_boot;
uint8_t burst_mode;
uint8_t page_mode;
};
enum {
CFI_UNLOCK_555_2AA,
CFI_UNLOCK_5555_2AAA,
};
struct cfi_unlock_addresses {
uint32_t unlock1;
uint32_t unlock2;
};
struct cfi_fixup {
uint16_t mfr;
uint16_t id;
void (*fixup)(struct flash_bank *bank, void *param);
void *param;
};
#define CFI_MFR_AMD 0x0001
#define CFI_MFR_FUJITSU 0x0004
#define CFI_MFR_ATMEL 0x001F
#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
#define CFI_MFR_AMIC 0x0037
#define CFI_MFR_SST 0x00BF
#define CFI_MFR_MX 0x00C2
#define CFI_MFR_EON 0x007F
#define CFI_MFR_ANY 0xffff
#define CFI_ID_ANY 0xffff
#endif /* CFI_H */
+763
View File
@@ -0,0 +1,763 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2007-2010 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <flash/common.h>
#include <flash/nor/core.h>
#include <flash/nor/imp.h>
#include <target/image.h>
/**
* @file
* Upper level of NOR flash framework.
* The lower level interfaces are to drivers. These upper level ones
* primarily support access from Tcl scripts or from GDB.
*/
static struct flash_bank *flash_banks;
int flash_driver_erase(struct flash_bank *bank, int first, int last)
{
int retval;
retval = bank->driver->erase(bank, first, last);
if (retval != ERROR_OK)
LOG_ERROR("failed erasing sectors %d to %d", first, last);
return retval;
}
int flash_driver_protect(struct flash_bank *bank, int set, int first, int last)
{
int retval;
/* callers may not supply illegal parameters ... */
if (first < 0 || first > last || last >= bank->num_sectors) {
LOG_ERROR("illegal sector range");
return ERROR_FAIL;
}
/* force "set" to 0/1 */
set = !!set;
/* DANGER!
*
* We must not use any cached information about protection state!!!!
*
* There are a million things that could change the protect state:
*
* the target could have reset, power cycled, been hot plugged,
* the application could have run, etc.
*
* Drivers only receive valid sector range.
*/
retval = bank->driver->protect(bank, set, first, last);
if (retval != ERROR_OK)
LOG_ERROR("failed setting protection for areas %d to %d", first, last);
return retval;
}
int flash_driver_write(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
int retval;
retval = bank->driver->write(bank, buffer, offset, count);
if (retval != ERROR_OK) {
LOG_ERROR(
"error writing to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32,
bank->base,
offset);
}
return retval;
}
int flash_driver_read(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
int retval;
LOG_DEBUG("call flash_driver_read()");
retval = bank->driver->read(bank, buffer, offset, count);
if (retval != ERROR_OK) {
LOG_ERROR(
"error reading to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32,
bank->base,
offset);
}
return retval;
}
int default_flash_read(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
return target_read_buffer(bank->target, offset + bank->base, count, buffer);
}
void flash_bank_add(struct flash_bank *bank)
{
/* put flash bank in linked list */
unsigned bank_num = 0;
if (flash_banks) {
/* find last flash bank */
struct flash_bank *p = flash_banks;
while (NULL != p->next) {
bank_num += 1;
p = p->next;
}
p->next = bank;
bank_num += 1;
} else
flash_banks = bank;
bank->bank_number = bank_num;
}
struct flash_bank *flash_bank_list(void)
{
return flash_banks;
}
struct flash_bank *get_flash_bank_by_num_noprobe(int num)
{
struct flash_bank *p;
int i = 0;
for (p = flash_banks; p; p = p->next) {
if (i++ == num)
return p;
}
LOG_ERROR("flash bank %d does not exist", num);
return NULL;
}
int flash_get_bank_count(void)
{
struct flash_bank *p;
int i = 0;
for (p = flash_banks; p; p = p->next)
i++;
return i;
}
struct flash_bank *get_flash_bank_by_name_noprobe(const char *name)
{
unsigned requested = get_flash_name_index(name);
unsigned found = 0;
struct flash_bank *bank;
for (bank = flash_banks; NULL != bank; bank = bank->next) {
if (strcmp(bank->name, name) == 0)
return bank;
if (!flash_driver_name_matches(bank->driver->name, name))
continue;
if (++found < requested)
continue;
return bank;
}
return NULL;
}
int get_flash_bank_by_name(const char *name, struct flash_bank **bank_result)
{
struct flash_bank *bank;
int retval;
bank = get_flash_bank_by_name_noprobe(name);
if (bank != NULL) {
retval = bank->driver->auto_probe(bank);
if (retval != ERROR_OK) {
LOG_ERROR("auto_probe failed");
return retval;
}
}
*bank_result = bank;
return ERROR_OK;
}
int get_flash_bank_by_num(int num, struct flash_bank **bank)
{
struct flash_bank *p = get_flash_bank_by_num_noprobe(num);
int retval;
if (p == NULL)
return ERROR_FAIL;
retval = p->driver->auto_probe(p);
if (retval != ERROR_OK) {
LOG_ERROR("auto_probe failed");
return retval;
}
*bank = p;
return ERROR_OK;
}
/* lookup flash bank by address, bank not found is success, but
* result_bank is set to NULL. */
int get_flash_bank_by_addr(struct target *target,
uint32_t addr,
bool check,
struct flash_bank **result_bank)
{
struct flash_bank *c;
/* cycle through bank list */
for (c = flash_banks; c; c = c->next) {
int retval;
retval = c->driver->auto_probe(c);
if (retval != ERROR_OK) {
LOG_ERROR("auto_probe failed");
return retval;
}
/* check whether address belongs to this flash bank */
if ((addr >= c->base) && (addr <= c->base + (c->size - 1)) && target == c->target) {
*result_bank = c;
return ERROR_OK;
}
}
*result_bank = NULL;
if (check) {
LOG_ERROR("No flash at address 0x%08" PRIx32, addr);
return ERROR_FAIL;
}
return ERROR_OK;
}
static int default_flash_mem_blank_check(struct flash_bank *bank)
{
struct target *target = bank->target;
const int buffer_size = 1024;
int i;
uint32_t nBytes;
int retval = ERROR_OK;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
uint8_t *buffer = malloc(buffer_size);
for (i = 0; i < bank->num_sectors; i++) {
uint32_t j;
bank->sectors[i].is_erased = 1;
for (j = 0; j < bank->sectors[i].size; j += buffer_size) {
uint32_t chunk;
chunk = buffer_size;
if (chunk > (j - bank->sectors[i].size))
chunk = (j - bank->sectors[i].size);
retval = target_read_memory(target,
bank->base + bank->sectors[i].offset + j,
4,
chunk/4,
buffer);
if (retval != ERROR_OK)
goto done;
for (nBytes = 0; nBytes < chunk; nBytes++) {
if (buffer[nBytes] != 0xFF) {
bank->sectors[i].is_erased = 0;
break;
}
}
}
}
done:
free(buffer);
return retval;
}
int default_flash_blank_check(struct flash_bank *bank)
{
struct target *target = bank->target;
int i;
int retval;
int fast_check = 0;
uint32_t blank;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
for (i = 0; i < bank->num_sectors; i++) {
uint32_t address = bank->base + bank->sectors[i].offset;
uint32_t size = bank->sectors[i].size;
retval = target_blank_check_memory(target, address, size, &blank);
if (retval != ERROR_OK) {
fast_check = 0;
break;
}
if (blank == 0xFF)
bank->sectors[i].is_erased = 1;
else
bank->sectors[i].is_erased = 0;
fast_check = 1;
}
if (!fast_check) {
LOG_USER("Running slow fallback erase check - add working memory");
return default_flash_mem_blank_check(bank);
}
return ERROR_OK;
}
/* Manipulate given flash region, selecting the bank according to target
* and address. Maps an address range to a set of sectors, and issues
* the callback() on that set ... e.g. to erase or unprotect its members.
*
* (Note a current bad assumption: that protection operates on the same
* size sectors as erase operations use.)
*
* The "pad_reason" parameter is a kind of boolean: when it's NULL, the
* range must fit those sectors exactly. This is clearly safe; it can't
* erase data which the caller said to leave alone, for example. If it's
* non-NULL, rather than failing, extra data in the first and/or last
* sectors will be added to the range, and that reason string is used when
* warning about those additions.
*/
static int flash_iterate_address_range_inner(struct target *target,
char *pad_reason, uint32_t addr, uint32_t length,
int (*callback)(struct flash_bank *bank, int first, int last))
{
struct flash_bank *c;
uint32_t last_addr = addr + length; /* first address AFTER end */
int first = -1;
int last = -1;
int i;
int retval = get_flash_bank_by_addr(target, addr, true, &c);
if (retval != ERROR_OK)
return retval;
if (c->size == 0 || c->num_sectors == 0) {
LOG_ERROR("Bank is invalid");
return ERROR_FLASH_BANK_INVALID;
}
if (length == 0) {
/* special case, erase whole bank when length is zero */
if (addr != c->base) {
LOG_ERROR("Whole bank access must start at beginning of bank.");
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
return callback(c, 0, c->num_sectors - 1);
}
/* check whether it all fits in this bank */
if (addr + length - 1 > c->base + c->size - 1) {
LOG_ERROR("Flash access does not fit into bank.");
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
/** @todo: handle erasures that cross into adjacent banks */
addr -= c->base;
last_addr -= c->base;
for (i = 0; i < c->num_sectors; i++) {
struct flash_sector *f = c->sectors + i;
uint32_t end = f->offset + f->size;
/* start only on a sector boundary */
if (first < 0) {
/* scanned past the first sector? */
if (addr < f->offset)
break;
/* is this the first sector? */
if (addr == f->offset)
first = i;
/* Does this need head-padding? If so, pad and warn;
* or else force an error.
*
* Such padding can make trouble, since *WE* can't
* ever know if that data was in use. The warning
* should help users sort out messes later.
*/
else if (addr < end && pad_reason) {
/* FIXME say how many bytes (e.g. 80 KB) */
LOG_WARNING("Adding extra %s range, "
"%#8.8x to %#8.8x",
pad_reason,
(unsigned) f->offset,
(unsigned) addr - 1);
first = i;
} else
continue;
}
/* is this (also?) the last sector? */
if (last_addr == end) {
last = i;
break;
}
/* Does this need tail-padding? If so, pad and warn;
* or else force an error.
*/
if (last_addr < end && pad_reason) {
/* FIXME say how many bytes (e.g. 80 KB) */
LOG_WARNING("Adding extra %s range, "
"%#8.8x to %#8.8x",
pad_reason,
(unsigned) last_addr,
(unsigned) end - 1);
last = i;
break;
}
/* MUST finish on a sector boundary */
if (last_addr <= f->offset)
break;
}
/* invalid start or end address? */
if (first == -1 || last == -1) {
LOG_ERROR("address range 0x%8.8x .. 0x%8.8x "
"is not sector-aligned",
(unsigned) (c->base + addr),
(unsigned) (c->base + last_addr - 1));
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
/* The NOR driver may trim this range down, based on what
* sectors are already erased/unprotected. GDB currently
* blocks such optimizations.
*/
return callback(c, first, last);
}
/* The inner fn only handles a single bank, we could be spanning
* multiple chips.
*/
static int flash_iterate_address_range(struct target *target,
char *pad_reason, uint32_t addr, uint32_t length,
int (*callback)(struct flash_bank *bank, int first, int last))
{
struct flash_bank *c;
int retval = ERROR_OK;
/* Danger! zero-length iterations means entire bank! */
do {
retval = get_flash_bank_by_addr(target, addr, true, &c);
if (retval != ERROR_OK)
return retval;
uint32_t cur_length = length;
/* check whether it all fits in this bank */
if (addr + length - 1 > c->base + c->size - 1) {
LOG_DEBUG("iterating over more than one flash bank.");
cur_length = c->base + c->size - addr;
}
retval = flash_iterate_address_range_inner(target,
pad_reason, addr, cur_length,
callback);
if (retval != ERROR_OK)
break;
length -= cur_length;
addr += cur_length;
} while (length > 0);
return retval;
}
int flash_erase_address_range(struct target *target,
bool pad, uint32_t addr, uint32_t length)
{
return flash_iterate_address_range(target, pad ? "erase" : NULL,
addr, length, &flash_driver_erase);
}
static int flash_driver_unprotect(struct flash_bank *bank, int first, int last)
{
return flash_driver_protect(bank, 0, first, last);
}
int flash_unlock_address_range(struct target *target, uint32_t addr, uint32_t length)
{
/* By default, pad to sector boundaries ... the real issue here
* is that our (only) caller *permanently* removes protection,
* and doesn't restore it.
*/
return flash_iterate_address_range(target, "unprotect",
addr, length, &flash_driver_unprotect);
}
static int compare_section(const void *a, const void *b)
{
struct imagesection *b1, *b2;
b1 = *((struct imagesection **)a);
b2 = *((struct imagesection **)b);
if (b1->base_address == b2->base_address)
return 0;
else if (b1->base_address > b2->base_address)
return 1;
else
return -1;
}
int flash_write_unlock(struct target *target, struct image *image,
uint32_t *written, int erase, bool unlock)
{
int retval = ERROR_OK;
int section;
uint32_t section_offset;
struct flash_bank *c;
int *padding;
section = 0;
section_offset = 0;
if (written)
*written = 0;
if (erase) {
/* assume all sectors need erasing - stops any problems
* when flash_write is called multiple times */
flash_set_dirty();
}
/* allocate padding array */
padding = calloc(image->num_sections, sizeof(*padding));
/* This fn requires all sections to be in ascending order of addresses,
* whereas an image can have sections out of order. */
struct imagesection **sections = malloc(sizeof(struct imagesection *) *
image->num_sections);
int i;
for (i = 0; i < image->num_sections; i++)
sections[i] = &image->sections[i];
qsort(sections, image->num_sections, sizeof(struct imagesection *),
compare_section);
/* loop until we reach end of the image */
while (section < image->num_sections) {
uint32_t buffer_size;
uint8_t *buffer;
int section_last;
uint32_t run_address = sections[section]->base_address + section_offset;
uint32_t run_size = sections[section]->size - section_offset;
int pad_bytes = 0;
if (sections[section]->size == 0) {
LOG_WARNING("empty section %d", section);
section++;
section_offset = 0;
continue;
}
/* find the corresponding flash bank */
retval = get_flash_bank_by_addr(target, run_address, false, &c);
if (retval != ERROR_OK)
goto done;
if (c == NULL) {
LOG_WARNING("no flash bank found for address %x", run_address);
section++; /* and skip it */
section_offset = 0;
continue;
}
/* collect consecutive sections which fall into the same bank */
section_last = section;
padding[section] = 0;
while ((run_address + run_size - 1 < c->base + c->size - 1) &&
(section_last + 1 < image->num_sections)) {
/* sections are sorted */
assert(sections[section_last + 1]->base_address >= c->base);
if (sections[section_last + 1]->base_address >= (c->base + c->size)) {
/* Done with this bank */
break;
}
/* FIXME This needlessly touches sectors BETWEEN the
* sections it's writing. Without auto erase, it just
* writes ones. That WILL INVALIDATE data in cases
* like Stellaris Tempest chips, corrupting internal
* ECC codes; and at least FreeScale suggests issues
* with that approach (in HC11 documentation).
*
* With auto erase enabled, data in those sectors will
* be needlessly destroyed; and some of the limited
* number of flash erase cycles will be wasted...
*
* In both cases, the extra writes slow things down.
*/
/* if we have multiple sections within our image,
* flash programming could fail due to alignment issues
* attempt to rebuild a consecutive buffer for the flash loader */
pad_bytes = (sections[section_last + 1]->base_address) - (run_address + run_size);
padding[section_last] = pad_bytes;
run_size += sections[++section_last]->size;
run_size += pad_bytes;
if (pad_bytes > 0)
LOG_INFO("Padding image section %d with %d bytes",
section_last-1,
pad_bytes);
}
if (run_address + run_size - 1 > c->base + c->size - 1) {
/* If we have more than one flash chip back to back, then we limit
* the current write operation to the current chip.
*/
LOG_DEBUG("Truncate flash run size to the current flash chip.");
run_size = c->base + c->size - run_address;
assert(run_size > 0);
}
/* If we're applying any sector automagic, then pad this
* (maybe-combined) segment to the end of its last sector.
*/
if (unlock || erase) {
int sector;
uint32_t offset_start = run_address - c->base;
uint32_t offset_end = offset_start + run_size;
uint32_t end = offset_end, delta;
for (sector = 0; sector < c->num_sectors; sector++) {
end = c->sectors[sector].offset
+ c->sectors[sector].size;
if (offset_end <= end)
break;
}
delta = end - offset_end;
padding[section_last] += delta;
run_size += delta;
}
/* allocate buffer */
buffer = malloc(run_size);
if (buffer == NULL) {
LOG_ERROR("Out of memory for flash bank buffer");
retval = ERROR_FAIL;
goto done;
}
buffer_size = 0;
/* read sections to the buffer */
while (buffer_size < run_size) {
size_t size_read;
size_read = run_size - buffer_size;
if (size_read > sections[section]->size - section_offset)
size_read = sections[section]->size - section_offset;
/* KLUDGE!
*
* #¤%#"%¤% we have to figure out the section # from the sorted
* list of pointers to sections to invoke image_read_section()...
*/
intptr_t diff = (intptr_t)sections[section] - (intptr_t)image->sections;
int t_section_num = diff / sizeof(struct imagesection);
LOG_DEBUG("image_read_section: section = %d, t_section_num = %d, "
"section_offset = %d, buffer_size = %d, size_read = %d",
(int)section, (int)t_section_num, (int)section_offset,
(int)buffer_size, (int)size_read);
retval = image_read_section(image, t_section_num, section_offset,
size_read, buffer + buffer_size, &size_read);
if (retval != ERROR_OK || size_read == 0) {
free(buffer);
goto done;
}
/* see if we need to pad the section */
while (padding[section]--)
(buffer + buffer_size)[size_read++] = 0xff;
buffer_size += size_read;
section_offset += size_read;
if (section_offset >= sections[section]->size) {
section++;
section_offset = 0;
}
}
retval = ERROR_OK;
if (unlock)
retval = flash_unlock_address_range(target, run_address, run_size);
if (retval == ERROR_OK) {
if (erase) {
/* calculate and erase sectors */
retval = flash_erase_address_range(target,
true, run_address, run_size);
}
}
if (retval == ERROR_OK) {
/* write flash sectors */
retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
}
free(buffer);
if (retval != ERROR_OK) {
/* abort operation */
goto done;
}
if (written != NULL)
*written += run_size; /* add run size to total written counter */
}
done:
free(sections);
free(padding);
return retval;
}
int flash_write(struct target *target, struct image *image,
uint32_t *written, int erase)
{
return flash_write_unlock(target, image, written, erase, false);
}
+207
View File
@@ -0,0 +1,207 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NOR_CORE_H
#define FLASH_NOR_CORE_H
#include <flash/common.h>
/**
* @file
* Upper level NOR flash interfaces.
*/
struct image;
#define FLASH_MAX_ERROR_STR (128)
/**
* Describes the geometry and status of a single flash sector
* within a flash bank. A single bank typically consists of multiple
* sectors, each of which can be erased and protected independently.
*/
struct flash_sector {
/** Bus offset from start of the flash chip (in bytes). */
uint32_t offset;
/** Number of bytes in this flash sector. */
uint32_t size;
/**
* Indication of erasure status: 0 = not erased, 1 = erased,
* other = unknown. Set by @c flash_driver_s::erase_check.
*/
int is_erased;
/**
* Indication of protection status: 0 = unprotected/unlocked,
* 1 = protected/locked, other = unknown. Set by
* @c flash_driver_s::protect_check.
*
* This information must be considered stale immediately.
* A million things could make it stale: power cycle,
* reset of target, code running on target, etc.
*/
int is_protected;
};
/**
* Provides details of a flash bank, available either on-chip or through
* a major interface.
*
* This structure will be passed as a parameter to the callbacks in the
* flash_driver_s structure, some of which may modify the contents of
* this structure of the area of flash that it defines. Driver writers
* may use the @c driver_priv member to store additional data on a
* per-bank basis, if required.
*/
struct flash_bank {
const char *name;
struct target *target; /**< Target to which this bank belongs. */
struct flash_driver *driver; /**< Driver for this bank. */
void *driver_priv; /**< Private driver storage pointer */
int bank_number; /**< The 'bank' (or chip number) of this instance. */
uint32_t base; /**< The base address of this bank */
uint32_t size; /**< The size of this chip bank, in bytes */
int chip_width; /**< Width of the chip in bytes (1,2,4 bytes) */
int bus_width; /**< Maximum bus width, in bytes (1,2,4 bytes) */
/**
* The number of sectors on this chip. This value will
* be set intially to 0, and the flash driver must set this to
* some non-zero value during "probe()" or "auto_probe()".
*/
int num_sectors;
/** Array of sectors, allocated and initilized by the flash driver */
struct flash_sector *sectors;
struct flash_bank *next; /**< The next flash bank on this chip */
};
/** Registers the 'flash' subsystem commands */
int flash_register_commands(struct command_context *cmd_ctx);
/**
* Erases @a length bytes in the @a target flash, starting at @a addr.
* The range @a addr to @a addr + @a length - 1 must be strictly
* sector aligned, unless @a pad is true. Setting @a pad true extends
* the range, at beginning and/or end, if needed for sector alignment.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int flash_erase_address_range(struct target *target,
bool pad, uint32_t addr, uint32_t length);
int flash_unlock_address_range(struct target *target, uint32_t addr,
uint32_t length);
/**
* Writes @a image into the @a target flash. The @a written parameter
* will contain the
* @param target The target with the flash to be programmed.
* @param image The image that will be programmed to flash.
* @param written On return, contains the number of bytes written.
* @param erase If non-zero, indicates the flash driver should first
* erase the corresponding banks or sectors before programming.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int flash_write(struct target *target,
struct image *image, uint32_t *written, int erase);
/**
* Forces targets to re-examine their erase/protection state.
* This routine must be called when the system may modify the status.
*/
void flash_set_dirty(void);
/** @returns The number of flash banks currently defined. */
int flash_get_bank_count(void);
/**
* Provides default read implementation for flash memory.
* @param bank The bank to read.
* @param buffer The data bytes read.
* @param offset The offset into the chip to read.
* @param count The number of bytes to read.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int default_flash_read(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count);
/**
* Provides default erased-bank check handling. Checks to see if
* the flash driver knows they are erased; if things look uncertain,
* this routine will call default_flash_mem_blank_check() to confirm.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int default_flash_blank_check(struct flash_bank *bank);
/**
* Returns the flash bank specified by @a name, which matches the
* driver name and a suffix (option) specify the driver-specific
* bank number. The suffix consists of the '.' and the driver-specific
* bank number: when two str9x banks are defined, then 'str9x.1' refers
* to the second.
*/
int get_flash_bank_by_name(const char *name, struct flash_bank **bank_result);
/**
* Returns the flash bank specified by @a name, which matches the
* driver name and a suffix (option) specify the driver-specific
* bank number. The suffix consists of the '.' and the driver-specific
* bank number: when two str9x banks are defined, then 'str9x.1' refers
* to the second.
*/
struct flash_bank *get_flash_bank_by_name_noprobe(const char *name);
/**
* Returns the flash bank like get_flash_bank_by_name(), without probing.
* @param num The flash bank number.
* @param bank returned bank if fn returns ERROR_OK
* @returns ERROR_OK if successful
*/
int get_flash_bank_by_num(int num, struct flash_bank **bank);
/**
* Retreives @a bank from a command argument, reporting errors parsing
* the bank identifier or retreiving the specified bank. The bank
* may be identified by its bank number or by @c name.instance, where
* @a instance is driver-specific.
* @param name_index The index to the string in args containing the
* bank identifier.
* @param bank On output, contians a pointer to the bank or NULL.
* @returns ERROR_OK on success, or an error indicating the problem.
*/
COMMAND_HELPER(flash_command_get_bank, unsigned name_index,
struct flash_bank **bank);
/**
* Returns the flash bank like get_flash_bank_by_num(), without probing.
* @param num The flash bank number.
* @returns A struct flash_bank for flash bank @a num, or NULL.
*/
struct flash_bank *get_flash_bank_by_num_noprobe(int num);
/**
* Returns the flash bank located at a specified address.
* @param target The target, presumed to contain one or more banks.
* @param addr An address that is within the range of the bank.
* @param check return ERROR_OK and result_bank NULL if the bank does not exist
* @returns The struct flash_bank located at @a addr, or NULL.
*/
int get_flash_bank_by_addr(struct target *target, uint32_t addr, bool check,
struct flash_bank **result_bank);
#endif /* FLASH_NOR_CORE_H */
+226
View File
@@ -0,0 +1,226 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NOR_DRIVER_H
#define FLASH_NOR_DRIVER_H
struct flash_bank;
#define __FLASH_BANK_COMMAND(name) \
COMMAND_HELPER(name, struct flash_bank *bank)
/**
* @brief Provides the implementation-independent structure that defines
* all of the callbacks required by OpenOCD flash drivers.
*
* Driver authors must implement the routines defined here, providing an
* instance with the fields filled out. After that, the instance must
* be registered in flash.c, so it can be used by the driver lookup system.
*
* Specifically, the user can issue the command: @par
* @code
* flash bank DRIVERNAME ...parameters...
* @endcode
*
* OpenOCD will search for the driver with a @c flash_driver_s::name
* that matches @c DRIVERNAME.
*
* The flash subsystem calls some of the other drivers routines a using
* corresponding static <code>flash_driver_<i>callback</i>()</code>
* routine in flash.c.
*/
struct flash_driver {
/**
* Gives a human-readable name of this flash driver,
* This field is used to select and initialize the driver.
*/
const char *name;
/**
* Gives a human-readable description of arguments.
*/
const char *usage;
/**
* An array of driver-specific commands to register. When called
* during the "flash bank" command, the driver can register addition
* commands to support new flash chip functions.
*/
const struct command_registration *commands;
/**
* Finish the "flash bank" command for @a bank. The
* @a bank parameter will have been filled in by the core flash
* layer when this routine is called, and the driver can store
* additional information in its struct flash_bank::driver_priv field.
*
* The CMD_ARGV are: @par
* @code
* CMD_ARGV[0] = bank
* CMD_ARGV[1] = drivername {name above}
* CMD_ARGV[2] = baseaddress
* CMD_ARGV[3] = lengthbytes
* CMD_ARGV[4] = chip_width_in bytes
* CMD_ARGV[5] = bus_width_in_bytes
* CMD_ARGV[6] = driver-specific parameters
* @endcode
*
* For example, CMD_ARGV[4] = 2 (for 16 bit flash),
* CMD_ARGV[5] = 4 (for 32 bit bus).
*
* If extra arguments are provided (@a CMD_ARGC > 6), they will
* start in @a CMD_ARGV[6]. These can be used to implement
* driver-specific extensions.
*
* @returns ERROR_OK if successful; otherwise, an error code.
*/
__FLASH_BANK_COMMAND((*flash_bank_command));
/**
* Bank/sector erase routine (target-specific). When
* called, the flash driver should erase the specified sectors
* using whatever means are at its disposal.
*
* @param bank The bank of flash to be erased.
* @param first The number of the first sector to erase, typically 0.
* @param last The number of the last sector to erase, typically N-1.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*erase)(struct flash_bank *bank, int first, int last);
/**
* Bank/sector protection routine (target-specific).
*
* When called, the driver should enable/disable protection
* for MINIMUM the range covered by first..last sectors
* inclusive. Some chips have alignment requirements will
* cause the actual range to be protected / unprotected to
* be larger than the first..last range.
*
* @param bank The bank to protect or unprotect.
* @param set If non-zero, enable protection; if 0, disable it.
* @param first The first sector to (un)protect, typicaly 0.
* @param last The last sector to (un)project, typically N-1.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*protect)(struct flash_bank *bank, int set, int first, int last);
/**
* Program data into the flash. Note CPU address will be
* "bank->base + offset", while the physical address is
* dependent upon current target MMU mappings.
*
* @param bank The bank to program
* @param buffer The data bytes to write.
* @param offset The offset into the chip to program.
* @param count The number of bytes to write.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*write)(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count);
/**
* Read data from the flash. Note CPU address will be
* "bank->base + offset", while the physical address is
* dependent upon current target MMU mappings.
*
* @param bank The bank to read.
* @param buffer The data bytes read.
* @param offset The offset into the chip to read.
* @param count The number of bytes to read.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*read)(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count);
/**
* Probe to determine what kind of flash is present.
* This is invoked by the "probe" script command.
*
* @param bank The bank to probe
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*probe)(struct flash_bank *bank);
/**
* Check the erasure status of a flash bank.
* When called, the driver routine must perform the required
* checks and then set the @c flash_sector_s::is_erased field
* for each of the flash banks's sectors.
*
* @param bank The bank to check
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*erase_check)(struct flash_bank *bank);
/**
* Determine if the specific bank is "protected" or not.
* When called, the driver routine must must perform the
* required protection check(s) and then set the @c
* flash_sector_s::is_protected field for each of the flash
* bank's sectors.
*
* @param bank - the bank to check
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*protect_check)(struct flash_bank *bank);
/**
* Display human-readable information about the flash
* bank into the given buffer. Drivers must be careful to avoid
* overflowing the buffer.
*
* @param bank - the bank to get info about
* @param char - where to put the text for the human to read
* @param buf_size - the size of the human buffer.
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*info)(struct flash_bank *bank, char *buf, int buf_size);
/**
* A more gentle flavor of filash_driver_s::probe, performing
* setup with less noise. Generally, driver routines should test
* to see if the bank has already been probed; if it has, the
* driver probably should not perform its probe a second time.
*
* This callback is often called from the inside of other
* routines (e.g. GDB flash downloads) to autoprobe the flash as
* it is programing the flash.
*
* @param bank - the bank to probe
* @returns ERROR_OK if successful; otherwise, an error code.
*/
int (*auto_probe)(struct flash_bank *bank);
};
#define FLASH_BANK_COMMAND_HANDLER(name) \
static __FLASH_BANK_COMMAND(name)
/**
* Find a NOR flash driver by its name.
* @param name The name of the requested driver.
* @returns The flash_driver called @c name, or NULL if not found.
*/
struct flash_driver *flash_driver_find_by_name(const char *name);
#endif /* FLASH_NOR_DRIVER_H */
+97
View File
@@ -0,0 +1,97 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
extern struct flash_driver lpc2000_flash;
extern struct flash_driver lpc288x_flash;
extern struct flash_driver lpc2900_flash;
extern struct flash_driver lpcspifi_flash;
extern struct flash_driver cfi_flash;
extern struct flash_driver at91sam3_flash;
extern struct flash_driver at91sam4_flash;
extern struct flash_driver at91sam7_flash;
extern struct flash_driver str7x_flash;
extern struct flash_driver str9x_flash;
extern struct flash_driver aduc702x_flash;
extern struct flash_driver stellaris_flash;
extern struct flash_driver str9xpec_flash;
extern struct flash_driver stm32f1x_flash;
extern struct flash_driver stm32f2x_flash;
extern struct flash_driver stm32lx_flash;
extern struct flash_driver tms470_flash;
extern struct flash_driver ocl_flash;
extern struct flash_driver pic32mx_flash;
extern struct flash_driver avr_flash;
extern struct flash_driver faux_flash;
extern struct flash_driver virtual_flash;
extern struct flash_driver stmsmi_flash;
extern struct flash_driver em357_flash;
extern struct flash_driver dsp5680xx_flash;
extern struct flash_driver fm3_flash;
extern struct flash_driver kinetis_flash;
extern struct flash_driver efm32_flash;
/**
* The list of built-in flash drivers.
* @todo Make this dynamically extendable with loadable modules.
*/
static struct flash_driver *flash_drivers[] = {
&lpc2000_flash,
&lpc288x_flash,
&lpc2900_flash,
&lpcspifi_flash,
&cfi_flash,
&at91sam7_flash,
&at91sam3_flash,
&at91sam4_flash,
&str7x_flash,
&str9x_flash,
&aduc702x_flash,
&stellaris_flash,
&str9xpec_flash,
&stm32f1x_flash,
&stm32f2x_flash,
&stm32lx_flash,
&tms470_flash,
&ocl_flash,
&pic32mx_flash,
&avr_flash,
&faux_flash,
&virtual_flash,
&stmsmi_flash,
&em357_flash,
&fm3_flash,
&dsp5680xx_flash,
&kinetis_flash,
&efm32_flash,
NULL,
};
struct flash_driver *flash_driver_find_by_name(const char *name)
{
for (unsigned i = 0; flash_drivers[i]; i++) {
if (strcmp(name, flash_drivers[i]->name) == 0)
return flash_drivers[i];
}
return NULL;
}
@@ -0,0 +1,281 @@
/***************************************************************************
* Copyright (C) 2011 by Rodrigo L. Rosa *
* rodrigorosa.LG@gmail.com *
* *
* Based on a file written by: *
* Kevin McGuire *
* Marcel Wijlaars *
* Michael Ashton *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/**
* @file dsp5680xx_flash.c
* @author Rodrigo L. Rosa <rodrigorosa.LG@gmail.com>
* @date Thu Jun 9 18:21:58 2011
*
* @brief This file implements the basic functions to run flashing commands
* from the TCL interface.
* It allows the user to flash the Freescale 5680xx DSP.
*
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include <target/dsp5680xx.h>
static int dsp5680xx_build_sector_list(struct flash_bank *bank)
{
uint32_t offset = HFM_FLASH_BASE_ADDR;
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
int i;
for (i = 0; i < bank->num_sectors; ++i) {
bank->sectors[i].offset = i * HFM_SECTOR_SIZE;
bank->sectors[i].size = HFM_SECTOR_SIZE;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = -1;
}
LOG_USER("%s not tested yet.", __func__);
return ERROR_OK;
}
/* flash bank dsp5680xx 0 0 0 0 <target#> */
FLASH_BANK_COMMAND_HANDLER(dsp5680xx_flash_bank_command)
{
bank->base = HFM_FLASH_BASE_ADDR;
bank->size = HFM_SIZE_BYTES; /* top 4k not accessible */
bank->num_sectors = HFM_SECTOR_COUNT;
dsp5680xx_build_sector_list(bank);
return ERROR_OK;
}
/**
* A memory mapped register (PROT) holds information regarding sector protection.
* Protection refers to undesired core access.
* The value in this register is loaded from flash upon reset.
*
* @param bank
*
* @return
*/
static int dsp5680xx_flash_protect_check(struct flash_bank *bank)
{
int retval = ERROR_OK;
uint16_t protected = 0;
retval = dsp5680xx_f_protect_check(bank->target, &protected);
if (retval != ERROR_OK) {
for (int i = 0; i < HFM_SECTOR_COUNT; i++)
bank->sectors[i].is_protected = -1;
return ERROR_OK;
}
for (int i = 0; i < HFM_SECTOR_COUNT / 2; i++) {
if (protected & 1) {
bank->sectors[2 * i].is_protected = 1;
bank->sectors[2 * i + 1].is_protected = 1;
} else {
bank->sectors[2 * i].is_protected = 0;
bank->sectors[2 * i + 1].is_protected = 0;
}
protected = (protected >> 1);
}
return retval;
}
/**
* Protection funcionality is not implemented.
* The current implementation applies/removes security on the chip.
* The chip is effectively secured/unsecured after the first reset
* following the execution of this function.
*
* @param bank
* @param set Apply or remove security on the chip.
* @param first This parameter is ignored.
* @param last This parameter is ignored.
*
* @return
*/
static int dsp5680xx_flash_protect(struct flash_bank *bank, int set, int first,
int last)
{
/**
* This applies security to flash module after next reset, it does
* not actually apply protection (protection refers to undesired access from the core)
*/
int retval;
if (set)
retval = dsp5680xx_f_lock(bank->target);
else {
retval = dsp5680xx_f_unlock(bank->target);
if (retval == ERROR_OK) {
/* mark all as erased */
for (int i = 0; i <= (HFM_SECTOR_COUNT - 1); i++)
/* FM does not recognize it as erased if erased via JTAG. */
bank->sectors[i].is_erased = 1;
}
}
return retval;
}
/**
* The dsp5680xx use word addressing. The "/2" that appear in the following code
* are a workaround for the fact that OpenOCD uses byte addressing.
*
* @param bank
* @param buffer Data to write to flash.
* @param offset
* @param count In bytes (2 bytes per address).
*
* @return
*/
static int dsp5680xx_flash_write(struct flash_bank *bank, uint8_t * buffer,
uint32_t offset, uint32_t count)
{
int retval;
if ((offset + count / 2) > bank->size) {
LOG_ERROR("%s: Flash bank cannot fit data.", __func__);
return ERROR_FAIL;
}
if (offset % 2) {
/**
* Writing to odd addresses not supported.
* This chip uses word addressing, Openocd only supports byte addressing.
* The workaround results in disabling writing to odd byte addresses
*/
LOG_ERROR("%s: Writing to odd addresses not supported for this target", __func__);
return ERROR_FAIL;
}
retval = dsp5680xx_f_wr(bank->target, buffer, bank->base + offset / 2, count, 0);
uint32_t addr_word;
for (addr_word = bank->base + offset / 2; addr_word < count / 2;
addr_word += (HFM_SECTOR_SIZE / 2)) {
if (retval == ERROR_OK)
bank->sectors[addr_word / (HFM_SECTOR_SIZE / 2)].is_erased = 0;
else
bank->sectors[addr_word / (HFM_SECTOR_SIZE / 2)].is_erased = -1;
}
return retval;
}
static int dsp5680xx_probe(struct flash_bank *bank)
{
LOG_DEBUG("%s not implemented", __func__);
return ERROR_OK;
}
static int dsp5680xx_flash_info(struct flash_bank *bank, char *buf,
int buf_size)
{
snprintf(buf, buf_size,
"\ndsp5680xx flash driver info:\n - See comments in code.");
return ERROR_OK;
}
/**
* The flash module (FM) on the dsp5680xx supports both individual sector
* and mass erase of the flash memory.
* If this function is called with @first == @last == 0 or if @first is the
* first sector (#0) and @last is the last sector then the mass erase command
* is executed (much faster than erasing each sector individually).
*
* @param bank
* @param first
* @param last
*
* @return
*/
static int dsp5680xx_flash_erase(struct flash_bank *bank, int first, int last)
{
int retval;
retval = dsp5680xx_f_erase(bank->target, (uint32_t) first, (uint32_t) last);
if ((!(first | last)) || ((first == 0) && (last == (HFM_SECTOR_COUNT - 1))))
last = HFM_SECTOR_COUNT - 1;
if (retval == ERROR_OK)
for (int i = first; i <= last; i++)
bank->sectors[i].is_erased = 1;
else
/**
* If an error occurred unknown status
*is set even though some sector could have been correctly erased.
*/
for (int i = first; i <= last; i++)
bank->sectors[i].is_erased = -1;
return retval;
}
/**
* The flash module (FM) on the dsp5680xx support a blank check function.
* This function executes the FM's blank check functionality on each and every sector.
*
* @param bank
*
* @return
*/
static int dsp5680xx_flash_erase_check(struct flash_bank *bank)
{
int retval = ERROR_OK;
uint8_t erased = 0;
uint32_t i;
for (i = 0; i < HFM_SECTOR_COUNT; i++) {
if (bank->sectors[i].is_erased == -1) {
retval = dsp5680xx_f_erase_check(bank->target, &erased, i);
if (retval != ERROR_OK) {
bank->sectors[i].is_erased = -1;
} else {
if (erased)
bank->sectors[i].is_erased = 1;
else
bank->sectors[i].is_erased = 0;
}
}
}
return retval;
}
struct flash_driver dsp5680xx_flash = {
.name = "dsp5680xx_flash",
.flash_bank_command = dsp5680xx_flash_bank_command,
.erase = dsp5680xx_flash_erase,
.protect = dsp5680xx_flash_protect,
.write = dsp5680xx_flash_write,
/* .read = default_flash_read, */
.probe = dsp5680xx_probe,
.auto_probe = dsp5680xx_probe,
.erase_check = dsp5680xx_flash_erase_check,
.protect_check = dsp5680xx_flash_protect_check,
.info = dsp5680xx_flash_info
};
+985
View File
@@ -0,0 +1,985 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2011 by Andreas Fritiofson *
* andreas.fritiofson@gmail.com *
* *
* Copyright (C) 2013 by Roman Dmitrienko *
* me@iamroman.org *
*
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#include <target/cortex_m.h>
/* keep family IDs in decimal */
#define EFM_FAMILY_ID_GECKO 71
#define EFM_FAMILY_ID_GIANT_GECKO 72
#define EFM_FAMILY_ID_TINY_GECKO 73
#define EFM_FAMILY_ID_LEOPARD_GECKO 74
#define EFM32_FLASH_ERASE_TMO 100
#define EFM32_FLASH_WDATAREADY_TMO 100
#define EFM32_FLASH_WRITE_TMO 100
/* size in bytes, not words; must fit all Gecko devices */
#define LOCKBITS_PAGE_SZ 512
#define EFM32_MSC_INFO_BASE 0x0fe00000
#define EFM32_MSC_USER_DATA EFM32_MSC_INFO_BASE
#define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000)
#define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000)
/* PAGE_SIZE is only present in Leopard and Giant Gecko MCUs */
#define EFM32_MSC_DI_PAGE_SIZE (EFM32_MSC_DEV_INFO+0x1e7)
#define EFM32_MSC_DI_FLASH_SZ (EFM32_MSC_DEV_INFO+0x1f8)
#define EFM32_MSC_DI_RAM_SZ (EFM32_MSC_DEV_INFO+0x1fa)
#define EFM32_MSC_DI_PART_NUM (EFM32_MSC_DEV_INFO+0x1fc)
#define EFM32_MSC_DI_PART_FAMILY (EFM32_MSC_DEV_INFO+0x1fe)
#define EFM32_MSC_DI_PROD_REV (EFM32_MSC_DEV_INFO+0x1ff)
#define EFM32_MSC_REGBASE 0x400c0000
#define EFM32_MSC_WRITECTRL (EFM32_MSC_REGBASE+0x008)
#define EFM32_MSC_WRITECTRL_WREN_MASK 0x1
#define EFM32_MSC_WRITECMD (EFM32_MSC_REGBASE+0x00c)
#define EFM32_MSC_WRITECMD_LADDRIM_MASK 0x1
#define EFM32_MSC_WRITECMD_ERASEPAGE_MASK 0x2
#define EFM32_MSC_WRITECMD_WRITEONCE_MASK 0x8
#define EFM32_MSC_ADDRB (EFM32_MSC_REGBASE+0x010)
#define EFM32_MSC_WDATA (EFM32_MSC_REGBASE+0x018)
#define EFM32_MSC_STATUS (EFM32_MSC_REGBASE+0x01c)
#define EFM32_MSC_STATUS_BUSY_MASK 0x1
#define EFM32_MSC_STATUS_LOCKED_MASK 0x2
#define EFM32_MSC_STATUS_INVADDR_MASK 0x4
#define EFM32_MSC_STATUS_WDATAREADY_MASK 0x8
#define EFM32_MSC_STATUS_WORDTIMEOUT_MASK 0x10
#define EFM32_MSC_STATUS_ERASEABORTED_MASK 0x20
#define EFM32_MSC_LOCK (EFM32_MSC_REGBASE+0x03c)
#define EFM32_MSC_LOCK_LOCKKEY 0x1b71
struct efm32x_flash_bank {
int probed;
uint8_t lb_page[LOCKBITS_PAGE_SZ];
};
struct efm32_info {
uint16_t flash_sz_kib;
uint16_t ram_sz_kib;
uint16_t part_num;
uint8_t part_family;
uint8_t prod_rev;
uint16_t page_size;
};
static int efm32x_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count);
static int efm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_sz)
{
return target_read_u16(bank->target, EFM32_MSC_DI_FLASH_SZ, flash_sz);
}
static int efm32x_get_ram_size(struct flash_bank *bank, uint16_t *ram_sz)
{
return target_read_u16(bank->target, EFM32_MSC_DI_RAM_SZ, ram_sz);
}
static int efm32x_get_part_num(struct flash_bank *bank, uint16_t *pnum)
{
return target_read_u16(bank->target, EFM32_MSC_DI_PART_NUM, pnum);
}
static int efm32x_get_part_family(struct flash_bank *bank, uint8_t *pfamily)
{
return target_read_u8(bank->target, EFM32_MSC_DI_PART_FAMILY, pfamily);
}
static int efm32x_get_prod_rev(struct flash_bank *bank, uint8_t *prev)
{
return target_read_u8(bank->target, EFM32_MSC_DI_PROD_REV, prev);
}
static int efm32x_read_info(struct flash_bank *bank,
struct efm32_info *efm32_info)
{
int ret;
uint32_t cpuid = 0;
memset(efm32_info, 0, sizeof(struct efm32_info));
ret = target_read_u32(bank->target, CPUID, &cpuid);
if (ERROR_OK != ret)
return ret;
if (((cpuid >> 4) & 0xfff) == 0xc23) {
/* Cortex M3 device */
} else {
LOG_ERROR("Target is not CortexM3");
return ERROR_FAIL;
}
ret = efm32x_get_flash_size(bank, &(efm32_info->flash_sz_kib));
if (ERROR_OK != ret)
return ret;
ret = efm32x_get_ram_size(bank, &(efm32_info->ram_sz_kib));
if (ERROR_OK != ret)
return ret;
ret = efm32x_get_part_num(bank, &(efm32_info->part_num));
if (ERROR_OK != ret)
return ret;
ret = efm32x_get_part_family(bank, &(efm32_info->part_family));
if (ERROR_OK != ret)
return ret;
ret = efm32x_get_prod_rev(bank, &(efm32_info->prod_rev));
if (ERROR_OK != ret)
return ret;
if (EFM_FAMILY_ID_GECKO == efm32_info->part_family ||
EFM_FAMILY_ID_TINY_GECKO == efm32_info->part_family)
efm32_info->page_size = 512;
else if (EFM_FAMILY_ID_GIANT_GECKO == efm32_info->part_family ||
EFM_FAMILY_ID_LEOPARD_GECKO == efm32_info->part_family) {
if (efm32_info->prod_rev >= 18) {
uint8_t pg_size = 0;
ret = target_read_u8(bank->target, EFM32_MSC_DI_PAGE_SIZE,
&pg_size);
if (ERROR_OK != ret)
return ret;
efm32_info->page_size = (1 << ((pg_size+10) & 0xff));
} else {
/* EFM32 GG/LG errata: MEM_INFO_PAGE_SIZE is invalid
for MCUs with PROD_REV < 18 */
if (efm32_info->flash_sz_kib < 512)
efm32_info->page_size = 2048;
else
efm32_info->page_size = 4096;
}
if ((2048 != efm32_info->page_size) &&
(4096 != efm32_info->page_size)) {
LOG_ERROR("Invalid page size %u", efm32_info->page_size);
return ERROR_FAIL;
}
} else {
LOG_ERROR("Unknown MCU family %d", efm32_info->part_family);
return ERROR_FAIL;
}
return ERROR_OK;
}
/* flash bank efm32 <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(efm32x_flash_bank_command)
{
struct efm32x_flash_bank *efm32x_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
efm32x_info = malloc(sizeof(struct efm32x_flash_bank));
bank->driver_priv = efm32x_info;
efm32x_info->probed = 0;
memset(efm32x_info->lb_page, 0xff, LOCKBITS_PAGE_SZ);
return ERROR_OK;
}
/* set or reset given bits in a register */
static int efm32x_set_reg_bits(struct flash_bank *bank, uint32_t reg,
uint32_t bitmask, int set)
{
int ret = 0;
uint32_t reg_val = 0;
ret = target_read_u32(bank->target, reg, &reg_val);
if (ERROR_OK != ret)
return ret;
if (set)
reg_val |= bitmask;
else
reg_val &= ~bitmask;
return target_write_u32(bank->target, reg, reg_val);
}
static int efm32x_set_wren(struct flash_bank *bank, int write_enable)
{
return efm32x_set_reg_bits(bank, EFM32_MSC_WRITECTRL,
EFM32_MSC_WRITECTRL_WREN_MASK, write_enable);
}
static int efm32x_msc_lock(struct flash_bank *bank, int lock)
{
return target_write_u32(bank->target, EFM32_MSC_LOCK,
(lock ? 0 : EFM32_MSC_LOCK_LOCKKEY));
}
static int efm32x_wait_status(struct flash_bank *bank, int timeout,
uint32_t wait_mask, int wait_for_set)
{
int ret = 0;
uint32_t status = 0;
while (1) {
ret = target_read_u32(bank->target, EFM32_MSC_STATUS, &status);
if (ERROR_OK != ret)
break;
LOG_DEBUG("status: 0x%" PRIx32 "", status);
if (((status & wait_mask) == 0) && (0 == wait_for_set))
break;
else if (((status & wait_mask) != 0) && wait_for_set)
break;
if (timeout-- <= 0) {
LOG_ERROR("timed out waiting for MSC status");
return ERROR_FAIL;
}
alive_sleep(1);
}
if (status & EFM32_MSC_STATUS_ERASEABORTED_MASK)
LOG_WARNING("page erase was aborted");
return ret;
}
static int efm32x_erase_page(struct flash_bank *bank, uint32_t addr)
{
/* this function DOES NOT set WREN; must be set already */
/* 1. write address to ADDRB
2. write LADDRIM
3. check status (INVADDR, LOCKED)
4. write ERASEPAGE
5. wait until !STATUS_BUSY
*/
int ret = 0;
uint32_t status = 0;
LOG_DEBUG("erasing flash page at 0x%08x", addr);
ret = target_write_u32(bank->target, EFM32_MSC_ADDRB, addr);
if (ERROR_OK != ret)
return ret;
ret = efm32x_set_reg_bits(bank, EFM32_MSC_WRITECMD,
EFM32_MSC_WRITECMD_LADDRIM_MASK, 1);
if (ERROR_OK != ret)
return ret;
ret = target_read_u32(bank->target, EFM32_MSC_STATUS, &status);
if (ERROR_OK != ret)
return ret;
LOG_DEBUG("status 0x%x", status);
if (status & EFM32_MSC_STATUS_LOCKED_MASK) {
LOG_ERROR("Page is locked");
return ERROR_FAIL;
} else if (status & EFM32_MSC_STATUS_INVADDR_MASK) {
LOG_ERROR("Invalid address 0x%x", addr);
return ERROR_FAIL;
}
ret = efm32x_set_reg_bits(bank, EFM32_MSC_WRITECMD,
EFM32_MSC_WRITECMD_ERASEPAGE_MASK, 1);
if (ERROR_OK != ret)
return ret;
return efm32x_wait_status(bank, EFM32_FLASH_ERASE_TMO,
EFM32_MSC_STATUS_BUSY_MASK, 0);
}
static int efm32x_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
int i = 0;
int ret = 0;
if (TARGET_HALTED != target->state) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
efm32x_msc_lock(bank, 0);
ret = efm32x_set_wren(bank, 1);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to enable MSC write");
return ret;
}
for (i = first; i <= last; i++) {
ret = efm32x_erase_page(bank, bank->sectors[i].offset);
if (ERROR_OK != ret)
LOG_ERROR("Failed to erase page %d", i);
}
ret = efm32x_set_wren(bank, 0);
efm32x_msc_lock(bank, 1);
return ret;
}
static int efm32x_read_lock_data(struct flash_bank *bank)
{
struct efm32x_flash_bank *efm32x_info = bank->driver_priv;
struct target *target = bank->target;
int i = 0;
int data_size = 0;
uint32_t *ptr = NULL;
int ret = 0;
assert(!(bank->num_sectors & 0x1f));
data_size = bank->num_sectors / 8; /* number of data bytes */
data_size /= 4; /* ...and data dwords */
ptr = (uint32_t *)efm32x_info->lb_page;
for (i = 0; i < data_size; i++, ptr++) {
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+i*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read PLW %d", i);
return ret;
}
}
/* also, read ULW, DLW and MLW */
/* ULW, word 126 */
ptr = ((uint32_t *)efm32x_info->lb_page) + 126;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+126*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read ULW");
return ret;
}
/* DLW, word 127 */
ptr = ((uint32_t *)efm32x_info->lb_page) + 127;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+127*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read DLW");
return ret;
}
/* MLW, word 125, present in GG and LG */
ptr = ((uint32_t *)efm32x_info->lb_page) + 125;
ret = target_read_u32(target, EFM32_MSC_LOCK_BITS+125*4, ptr);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read MLW");
return ret;
}
return ERROR_OK;
}
static int efm32x_write_lock_data(struct flash_bank *bank)
{
struct efm32x_flash_bank *efm32x_info = bank->driver_priv;
int ret = 0;
ret = efm32x_erase_page(bank, EFM32_MSC_LOCK_BITS);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to erase LB page");
return ret;
}
return efm32x_write(bank, efm32x_info->lb_page, EFM32_MSC_LOCK_BITS,
LOCKBITS_PAGE_SZ);
}
static int efm32x_get_page_lock(struct flash_bank *bank, size_t page)
{
struct efm32x_flash_bank *efm32x_info = bank->driver_priv;
uint32_t dw = ((uint32_t *)efm32x_info->lb_page)[page >> 5];
uint32_t mask = 0;
mask = 1 << (page & 0x1f);
return (dw & mask) ? 0 : 1;
}
static int efm32x_set_page_lock(struct flash_bank *bank, size_t page, int set)
{
struct efm32x_flash_bank *efm32x_info = bank->driver_priv;
uint32_t *dw = &((uint32_t *)efm32x_info->lb_page)[page >> 5];
uint32_t mask = 0;
mask = 1 << (page & 0x1f);
if (!set)
*dw |= mask;
else
*dw &= ~mask;
return ERROR_OK;
}
static int efm32x_protect(struct flash_bank *bank, int set, int first, int last)
{
struct target *target = bank->target;
int i = 0;
int ret = 0;
if (!set) {
LOG_ERROR("Erase device data to reset page locks");
return ERROR_FAIL;
}
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
for (i = first; i <= last; i++) {
ret = efm32x_set_page_lock(bank, i, set);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to set lock on page %d", i);
return ret;
}
}
ret = efm32x_write_lock_data(bank);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to write LB page");
return ret;
}
return ERROR_OK;
}
static int efm32x_write_block(struct flash_bank *bank, uint8_t *buf,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 16384;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[5];
struct armv7m_algorithm armv7m_info;
int ret = ERROR_OK;
/* see contrib/loaders/flash/efm32.S for src */
static const uint8_t efm32x_flash_write_code[] = {
/* #define EFM32_MSC_WRITECTRL_OFFSET 0x008 */
/* #define EFM32_MSC_WRITECMD_OFFSET 0x00c */
/* #define EFM32_MSC_ADDRB_OFFSET 0x010 */
/* #define EFM32_MSC_WDATA_OFFSET 0x018 */
/* #define EFM32_MSC_STATUS_OFFSET 0x01c */
/* #define EFM32_MSC_LOCK_OFFSET 0x03c */
0x15, 0x4e, /* ldr r6, =#0x1b71 */
0xc6, 0x63, /* str r6, [r0, #EFM32_MSC_LOCK_OFFSET] */
0x01, 0x26, /* movs r6, #1 */
0x86, 0x60, /* str r6, [r0, #EFM32_MSC_WRITECTRL_OFFSET] */
/* wait_fifo: */
0x16, 0x68, /* ldr r6, [r2, #0] */
0x00, 0x2e, /* cmp r6, #0 */
0x22, 0xd0, /* beq exit */
0x55, 0x68, /* ldr r5, [r2, #4] */
0xb5, 0x42, /* cmp r5, r6 */
0xf9, 0xd0, /* beq wait_fifo */
0x04, 0x61, /* str r4, [r0, #EFM32_MSC_ADDRB_OFFSET] */
0x01, 0x26, /* movs r6, #1 */
0xc6, 0x60, /* str r6, [r0, #EFM32_MSC_WRITECMD_OFFSET] */
0xc6, 0x69, /* ldr r6, [r0, #EFM32_MSC_STATUS_OFFSET] */
0x06, 0x27, /* movs r7, #6 */
0x3e, 0x42, /* tst r6, r7 */
0x16, 0xd1, /* bne error */
/* wait_wdataready: */
0xc6, 0x69, /* ldr r6, [r0, #EFM32_MSC_STATUS_OFFSET] */
0x08, 0x27, /* movs r7, #8 */
0x3e, 0x42, /* tst r6, r7 */
0xfb, 0xd0, /* beq wait_wdataready */
0x2e, 0x68, /* ldr r6, [r5] */
0x86, 0x61, /* str r6, [r0, #EFM32_MSC_WDATA_OFFSET] */
0x08, 0x26, /* movs r6, #8 */
0xc6, 0x60, /* str r6, [r0, #EFM32_MSC_WRITECMD_OFFSET] */
0x04, 0x35, /* adds r5, #4 */
0x04, 0x34, /* adds r4, #4 */
/* busy: */
0xc6, 0x69, /* ldr r6, [r0, #EFM32_MSC_STATUS_OFFSET] */
0x01, 0x27, /* movs r7, #1 */
0x3e, 0x42, /* tst r6, r7 */
0xfb, 0xd1, /* bne busy */
0x9d, 0x42, /* cmp r5, r3 */
0x01, 0xd3, /* bcc no_wrap */
0x15, 0x46, /* mov r5, r2 */
0x08, 0x35, /* adds r5, #8 */
/* no_wrap: */
0x55, 0x60, /* str r5, [r2, #4] */
0x01, 0x39, /* subs r1, r1, #1 */
0x00, 0x29, /* cmp r1, #0 */
0x02, 0xd0, /* beq exit */
0xdb, 0xe7, /* b wait_fifo */
/* error: */
0x00, 0x20, /* movs r0, #0 */
0x50, 0x60, /* str r0, [r2, #4] */
/* exit: */
0x30, 0x46, /* mov r0, r6 */
0x00, 0xbe, /* bkpt #0 */
/* LOCKKEY */
0x71, 0x1b, 0x00, 0x00
};
/* flash write code */
if (target_alloc_working_area(target, sizeof(efm32x_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
ret = target_write_buffer(target, write_algorithm->address,
sizeof(efm32x_flash_write_code),
(uint8_t *)efm32x_flash_write_code);
if (ret != ERROR_OK)
return ret;
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a
* buffer, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING("no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
};
init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /* count (word-32bit) */
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /* buffer start */
init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /* buffer end */
init_reg_param(&reg_params[4], "r4", 32, PARAM_IN_OUT); /* target address */
buf_set_u32(reg_params[0].value, 0, 32, EFM32_MSC_REGBASE);
buf_set_u32(reg_params[1].value, 0, 32, count);
buf_set_u32(reg_params[2].value, 0, 32, source->address);
buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size);
buf_set_u32(reg_params[4].value, 0, 32, address);
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
ret = target_run_flash_async_algorithm(target, buf, count, 4,
0, NULL,
5, reg_params,
source->address, source->size,
write_algorithm->address, 0,
&armv7m_info);
if (ret == ERROR_FLASH_OPERATION_FAILED) {
LOG_ERROR("flash write failed at address 0x%"PRIx32,
buf_get_u32(reg_params[4].value, 0, 32));
if (buf_get_u32(reg_params[0].value, 0, 32) &
EFM32_MSC_STATUS_LOCKED_MASK) {
LOG_ERROR("flash memory write protected");
}
if (buf_get_u32(reg_params[0].value, 0, 32) &
EFM32_MSC_STATUS_INVADDR_MASK) {
LOG_ERROR("invalid flash memory write address");
}
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
return ret;
}
static int efm32x_write_word(struct flash_bank *bank, uint32_t addr,
uint32_t val)
{
/* this function DOES NOT set WREN; must be set already */
/* 1. write address to ADDRB
2. write LADDRIM
3. check status (INVADDR, LOCKED)
4. wait for WDATAREADY
5. write data to WDATA
6. write WRITECMD_WRITEONCE to WRITECMD
7. wait until !STATUS_BUSY
*/
/* FIXME: EFM32G ref states (7.3.2) that writes should be
* performed twice per dword */
int ret = 0;
uint32_t status = 0;
/* if not called, GDB errors will be reported during large writes */
keep_alive();
ret = target_write_u32(bank->target, EFM32_MSC_ADDRB, addr);
if (ERROR_OK != ret)
return ret;
ret = efm32x_set_reg_bits(bank, EFM32_MSC_WRITECMD,
EFM32_MSC_WRITECMD_LADDRIM_MASK, 1);
if (ERROR_OK != ret)
return ret;
ret = target_read_u32(bank->target, EFM32_MSC_STATUS, &status);
if (ERROR_OK != ret)
return ret;
LOG_DEBUG("status 0x%x", status);
if (status & EFM32_MSC_STATUS_LOCKED_MASK) {
LOG_ERROR("Page is locked");
return ERROR_FAIL;
} else if (status & EFM32_MSC_STATUS_INVADDR_MASK) {
LOG_ERROR("Invalid address 0x%x", addr);
return ERROR_FAIL;
}
ret = efm32x_wait_status(bank, EFM32_FLASH_WDATAREADY_TMO,
EFM32_MSC_STATUS_WDATAREADY_MASK, 1);
if (ERROR_OK != ret) {
LOG_ERROR("Wait for WDATAREADY failed");
return ret;
}
ret = target_write_u32(bank->target, EFM32_MSC_WDATA, val);
if (ERROR_OK != ret) {
LOG_ERROR("WDATA write failed");
return ret;
}
ret = target_write_u32(bank->target, EFM32_MSC_WRITECMD,
EFM32_MSC_WRITECMD_WRITEONCE_MASK);
if (ERROR_OK != ret) {
LOG_ERROR("WRITECMD write failed");
return ret;
}
ret = efm32x_wait_status(bank, EFM32_FLASH_WRITE_TMO,
EFM32_MSC_STATUS_BUSY_MASK, 0);
if (ERROR_OK != ret) {
LOG_ERROR("Wait for BUSY failed");
return ret;
}
return ERROR_OK;
}
static int efm32x_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint8_t *new_buffer = NULL;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset & 0x3) {
LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte "
"alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
if (count & 0x3) {
uint32_t old_count = count;
count = (old_count | 3) + 1;
new_buffer = malloc(count);
if (new_buffer == NULL) {
LOG_ERROR("odd number of bytes to write and no memory "
"for padding buffer");
return ERROR_FAIL;
}
LOG_INFO("odd number of bytes to write (%d), extending to %d "
"and padding with 0xff", old_count, count);
memset(buffer, 0xff, count);
buffer = memcpy(new_buffer, buffer, old_count);
}
uint32_t words_remaining = count / 4;
int retval, retval2;
/* unlock flash registers */
efm32x_msc_lock(bank, 0);
retval = efm32x_set_wren(bank, 1);
if (retval != ERROR_OK)
goto cleanup;
/* try using a block write */
retval = efm32x_write_block(bank, buffer, offset, words_remaining);
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single word accesses */
LOG_WARNING("couldn't use block writes, falling back to single "
"memory accesses");
while (words_remaining > 0) {
uint32_t value;
memcpy(&value, buffer, sizeof(uint32_t));
retval = efm32x_write_word(bank, offset, value);
if (retval != ERROR_OK)
goto reset_pg_and_lock;
words_remaining--;
buffer += 4;
offset += 4;
}
}
reset_pg_and_lock:
retval2 = efm32x_set_wren(bank, 0);
efm32x_msc_lock(bank, 1);
if (retval == ERROR_OK)
retval = retval2;
cleanup:
if (new_buffer)
free(new_buffer);
return retval;
}
static int efm32x_probe(struct flash_bank *bank)
{
struct efm32x_flash_bank *efm32x_info = bank->driver_priv;
struct efm32_info efm32_mcu_info;
int ret;
int i;
uint32_t base_address = 0x00000000;
efm32x_info->probed = 0;
memset(efm32x_info->lb_page, 0xff, LOCKBITS_PAGE_SZ);
ret = efm32x_read_info(bank, &efm32_mcu_info);
if (ERROR_OK != ret)
return ret;
switch (efm32_mcu_info.part_family) {
case EFM_FAMILY_ID_GECKO:
LOG_INFO("Gecko MCU detected");
break;
case EFM_FAMILY_ID_GIANT_GECKO:
LOG_INFO("Giant Gecko MCU detected");
break;
case EFM_FAMILY_ID_TINY_GECKO:
LOG_INFO("Tiny Gecko MCU detected");
break;
case EFM_FAMILY_ID_LEOPARD_GECKO:
LOG_INFO("Leopard Gecko MCU detected");
break;
default:
LOG_ERROR("Unsupported MCU family %d",
efm32_mcu_info.part_family);
return ERROR_FAIL;
}
LOG_INFO("flash size = %dkbytes", efm32_mcu_info.flash_sz_kib);
LOG_INFO("flash page size = %dbytes", efm32_mcu_info.page_size);
assert(0 != efm32_mcu_info.page_size);
int num_pages = efm32_mcu_info.flash_sz_kib * 1024 /
efm32_mcu_info.page_size;
assert(num_pages > 0);
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
bank->base = base_address;
bank->size = (num_pages * efm32_mcu_info.page_size);
bank->num_sectors = num_pages;
ret = efm32x_read_lock_data(bank);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read LB data");
return ret;
}
bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
for (i = 0; i < num_pages; i++) {
bank->sectors[i].offset = i * efm32_mcu_info.page_size;
bank->sectors[i].size = efm32_mcu_info.page_size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
efm32x_info->probed = 1;
return ERROR_OK;
}
static int efm32x_auto_probe(struct flash_bank *bank)
{
struct efm32x_flash_bank *efm32x_info = bank->driver_priv;
if (efm32x_info->probed)
return ERROR_OK;
return efm32x_probe(bank);
}
static int efm32x_protect_check(struct flash_bank *bank)
{
struct target *target = bank->target;
int ret = 0;
int i = 0;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
ret = efm32x_read_lock_data(bank);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read LB data");
return ret;
}
assert(NULL != bank->sectors);
for (i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_protected = efm32x_get_page_lock(bank, i);
return ERROR_OK;
}
static int get_efm32x_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct efm32_info info;
int ret = 0;
int printed = 0;
ret = efm32x_read_info(bank, &info);
if (ERROR_OK != ret) {
LOG_ERROR("Failed to read EFM32 info");
return ret;
}
printed = snprintf(buf, buf_size, "EFM32 ");
buf += printed;
buf_size -= printed;
if (0 >= buf_size)
return ERROR_BUF_TOO_SMALL;
switch (info.part_family) {
case EFM_FAMILY_ID_GECKO:
printed = snprintf(buf, buf_size, "Gecko");
break;
case EFM_FAMILY_ID_GIANT_GECKO:
printed = snprintf(buf, buf_size, "Giant Gecko");
break;
case EFM_FAMILY_ID_TINY_GECKO:
printed = snprintf(buf, buf_size, "Tiny Gecko");
break;
case EFM_FAMILY_ID_LEOPARD_GECKO:
printed = snprintf(buf, buf_size, "Leopard Gecko");
break;
}
buf += printed;
buf_size -= printed;
if (0 >= buf_size)
return ERROR_BUF_TOO_SMALL;
printed = snprintf(buf, buf_size, " - Rev: %d", info.prod_rev);
buf += printed;
buf_size -= printed;
if (0 >= buf_size)
return ERROR_BUF_TOO_SMALL;
return ERROR_OK;
}
static const struct command_registration efm32x_exec_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
static const struct command_registration efm32x_command_handlers[] = {
{
.name = "efm32",
.mode = COMMAND_ANY,
.help = "efm32 flash command group",
.usage = "",
.chain = efm32x_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver efm32_flash = {
.name = "efm32",
.commands = efm32x_command_handlers,
.flash_bank_command = efm32x_flash_bank_command,
.erase = efm32x_erase,
.protect = efm32x_protect,
.write = efm32x_write,
.read = default_flash_read,
.probe = efm32x_probe,
.auto_probe = efm32x_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = efm32x_protect_check,
.info = get_efm32x_info,
};
+950
View File
@@ -0,0 +1,950 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
*
* Copyright (C) 2011 by Erik Botö
* erik.boto@pelagicore.com
*
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
/* em357 register locations */
#define EM357_FLASH_ACR 0x40008000
#define EM357_FLASH_KEYR 0x40008004
#define EM357_FLASH_OPTKEYR 0x40008008
#define EM357_FLASH_SR 0x4000800C
#define EM357_FLASH_CR 0x40008010
#define EM357_FLASH_AR 0x40008014
#define EM357_FLASH_OBR 0x4000801C
#define EM357_FLASH_WRPR 0x40008020
#define EM357_FPEC_CLK 0x4000402c
/* option byte location */
#define EM357_OB_RDP 0x08040800
#define EM357_OB_WRP0 0x08040808
#define EM357_OB_WRP1 0x0804080A
#define EM357_OB_WRP2 0x0804080C
/* FLASH_CR register bits */
#define FLASH_PG (1 << 0)
#define FLASH_PER (1 << 1)
#define FLASH_MER (1 << 2)
#define FLASH_OPTPG (1 << 4)
#define FLASH_OPTER (1 << 5)
#define FLASH_STRT (1 << 6)
#define FLASH_LOCK (1 << 7)
#define FLASH_OPTWRE (1 << 9)
/* FLASH_SR register bits */
#define FLASH_BSY (1 << 0)
#define FLASH_PGERR (1 << 2)
#define FLASH_WRPRTERR (1 << 4)
#define FLASH_EOP (1 << 5)
/* EM357_FLASH_OBR bit definitions (reading) */
#define OPT_ERROR 0
#define OPT_READOUT 1
/* register unlock keys */
#define KEY1 0x45670123
#define KEY2 0xCDEF89AB
struct em357_options {
uint16_t RDP;
uint16_t user_options;
uint16_t protection[3];
};
struct em357_flash_bank {
struct em357_options option_bytes;
int ppage_size;
int probed;
};
static int em357_mass_erase(struct flash_bank *bank);
/* flash bank em357 <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(em357_flash_bank_command)
{
struct em357_flash_bank *em357_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
em357_info = malloc(sizeof(struct em357_flash_bank));
bank->driver_priv = em357_info;
em357_info->probed = 0;
return ERROR_OK;
}
static inline int em357_get_flash_status(struct flash_bank *bank, uint32_t *status)
{
struct target *target = bank->target;
return target_read_u32(target, EM357_FLASH_SR, status);
}
static int em357_wait_status_busy(struct flash_bank *bank, int timeout)
{
struct target *target = bank->target;
uint32_t status;
int retval = ERROR_OK;
/* wait for busy to clear */
for (;; ) {
retval = em357_get_flash_status(bank, &status);
if (retval != ERROR_OK)
return retval;
LOG_DEBUG("status: 0x%" PRIx32 "", status);
if ((status & FLASH_BSY) == 0)
break;
if (timeout-- <= 0) {
LOG_ERROR("timed out waiting for flash");
return ERROR_FAIL;
}
alive_sleep(1);
}
if (status & FLASH_WRPRTERR) {
LOG_ERROR("em357 device protected");
retval = ERROR_FAIL;
}
if (status & FLASH_PGERR) {
LOG_ERROR("em357 device programming failed");
retval = ERROR_FAIL;
}
/* Clear but report errors */
if (status & (FLASH_WRPRTERR | FLASH_PGERR)) {
/* If this operation fails, we ignore it and report the original
* retval
*/
target_write_u32(target, EM357_FLASH_SR, FLASH_WRPRTERR | FLASH_PGERR);
}
return retval;
}
static int em357_read_options(struct flash_bank *bank)
{
uint32_t optiondata;
struct em357_flash_bank *em357_info = NULL;
struct target *target = bank->target;
em357_info = bank->driver_priv;
/* read current option bytes */
int retval = target_read_u32(target, EM357_FLASH_OBR, &optiondata);
if (retval != ERROR_OK)
return retval;
em357_info->option_bytes.user_options = (uint16_t)0xFFFC | ((optiondata >> 2) & 0x03);
em357_info->option_bytes.RDP = (optiondata & (1 << OPT_READOUT)) ? 0xFFFF : 0x5AA5;
if (optiondata & (1 << OPT_READOUT))
LOG_INFO("Device Security Bit Set");
/* each bit refers to a 4bank protection */
retval = target_read_u32(target, EM357_FLASH_WRPR, &optiondata);
if (retval != ERROR_OK)
return retval;
em357_info->option_bytes.protection[0] = (uint16_t)optiondata;
em357_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8);
em357_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16);
return ERROR_OK;
}
static int em357_erase_options(struct flash_bank *bank)
{
struct em357_flash_bank *em357_info = NULL;
struct target *target = bank->target;
em357_info = bank->driver_priv;
/* read current options */
em357_read_options(bank);
/* unlock flash registers */
int retval = target_write_u32(target, EM357_FLASH_KEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_KEYR, KEY2);
if (retval != ERROR_OK)
return retval;
/* unlock option flash registers */
retval = target_write_u32(target, EM357_FLASH_OPTKEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_OPTKEYR, KEY2);
if (retval != ERROR_OK)
return retval;
/* erase option bytes */
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_OPTER | FLASH_OPTWRE);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_OPTER | FLASH_STRT | FLASH_OPTWRE);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 10);
if (retval != ERROR_OK)
return retval;
/* clear readout protection and complementary option bytes
* this will also force a device unlock if set */
em357_info->option_bytes.RDP = 0x5AA5;
return ERROR_OK;
}
static int em357_write_options(struct flash_bank *bank)
{
struct em357_flash_bank *em357_info = NULL;
struct target *target = bank->target;
em357_info = bank->driver_priv;
/* unlock flash registers */
int retval = target_write_u32(target, EM357_FLASH_KEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_KEYR, KEY2);
if (retval != ERROR_OK)
return retval;
/* unlock option flash registers */
retval = target_write_u32(target, EM357_FLASH_OPTKEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_OPTKEYR, KEY2);
if (retval != ERROR_OK)
return retval;
/* program option bytes */
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_OPTPG | FLASH_OPTWRE);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 10);
if (retval != ERROR_OK)
return retval;
/* write protection byte 1 */
retval = target_write_u16(target, EM357_OB_WRP0, em357_info->option_bytes.protection[0]);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 10);
if (retval != ERROR_OK)
return retval;
/* write protection byte 2 */
retval = target_write_u16(target, EM357_OB_WRP1, em357_info->option_bytes.protection[1]);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 10);
if (retval != ERROR_OK)
return retval;
/* write protection byte 3 */
retval = target_write_u16(target, EM357_OB_WRP2, em357_info->option_bytes.protection[2]);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 10);
if (retval != ERROR_OK)
return retval;
/* write readout protection bit */
retval = target_write_u16(target, EM357_OB_RDP, em357_info->option_bytes.RDP);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 10);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_LOCK);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int em357_protect_check(struct flash_bank *bank)
{
struct target *target = bank->target;
struct em357_flash_bank *em357_info = bank->driver_priv;
uint32_t protection;
int i, s;
int num_bits;
int set;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* each bit refers to a 4bank protection (bit 0-23) */
int retval = target_read_u32(target, EM357_FLASH_WRPR, &protection);
if (retval != ERROR_OK)
return retval;
/* each protection bit is for 4 * 2K pages */
num_bits = (bank->num_sectors / em357_info->ppage_size);
for (i = 0; i < num_bits; i++) {
set = 1;
if (protection & (1 << i))
set = 0;
for (s = 0; s < em357_info->ppage_size; s++)
bank->sectors[(i * em357_info->ppage_size) + s].is_protected = set;
}
return ERROR_OK;
}
static int em357_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
int i;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first == 0) && (last == (bank->num_sectors - 1)))
return em357_mass_erase(bank);
/* Enable FPEC clock */
target_write_u32(target, EM357_FPEC_CLK, 0x00000001);
/* unlock flash registers */
int retval = target_write_u32(target, EM357_FLASH_KEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_KEYR, KEY2);
if (retval != ERROR_OK)
return retval;
for (i = first; i <= last; i++) {
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_PER);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_AR,
bank->base + bank->sectors[i].offset);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_PER | FLASH_STRT);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 100);
if (retval != ERROR_OK)
return retval;
bank->sectors[i].is_erased = 1;
}
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_LOCK);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int em357_protect(struct flash_bank *bank, int set, int first, int last)
{
struct em357_flash_bank *em357_info = NULL;
struct target *target = bank->target;
uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
int i, reg, bit;
int status;
uint32_t protection;
em357_info = bank->driver_priv;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first % em357_info->ppage_size) != 0) {
LOG_WARNING("aligned start protect sector to a %d sector boundary",
em357_info->ppage_size);
first = first - (first % em357_info->ppage_size);
}
if (((last + 1) % em357_info->ppage_size) != 0) {
LOG_WARNING("aligned end protect sector to a %d sector boundary",
em357_info->ppage_size);
last++;
last = last - (last % em357_info->ppage_size);
last--;
}
/* each bit refers to a 4bank protection */
int retval = target_read_u32(target, EM357_FLASH_WRPR, &protection);
if (retval != ERROR_OK)
return retval;
prot_reg[0] = (uint16_t)protection;
prot_reg[1] = (uint16_t)(protection >> 8);
prot_reg[2] = (uint16_t)(protection >> 16);
for (i = first; i <= last; i++) {
reg = (i / em357_info->ppage_size) / 8;
bit = (i / em357_info->ppage_size) - (reg * 8);
LOG_WARNING("reg, bit: %d, %d", reg, bit);
if (set)
prot_reg[reg] &= ~(1 << bit);
else
prot_reg[reg] |= (1 << bit);
}
status = em357_erase_options(bank);
if (retval != ERROR_OK)
return status;
em357_info->option_bytes.protection[0] = prot_reg[0];
em357_info->option_bytes.protection[1] = prot_reg[1];
em357_info->option_bytes.protection[2] = prot_reg[2];
return em357_write_options(bank);
}
static int em357_write_block(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 16384;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[4];
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
/* see contib/loaders/flash/stm32x.s for src, the same is used here except for
* a modified *_FLASH_BASE */
static const uint8_t em357_flash_write_code[] = {
/* #define EM357_FLASH_CR_OFFSET 0x10
* #define EM357_FLASH_SR_OFFSET 0x0C
* write: */
0x08, 0x4c, /* ldr r4, EM357_FLASH_BASE */
0x1c, 0x44, /* add r4, r3 */
/* write_half_word: */
0x01, 0x23, /* movs r3, #0x01 */
0x23, 0x61, /* str r3, [r4,
*#EM357_FLASH_CR_OFFSET] */
0x30, 0xf8, 0x02, 0x3b, /* ldrh r3, [r0], #0x02 */
0x21, 0xf8, 0x02, 0x3b, /* strh r3, [r1], #0x02 */
/* busy: */
0xe3, 0x68, /* ldr r3, [r4,
*#EM357_FLASH_SR_OFFSET] */
0x13, 0xf0, 0x01, 0x0f, /* tst r3, #0x01 */
0xfb, 0xd0, /* beq busy */
0x13, 0xf0, 0x14, 0x0f, /* tst r3, #0x14 */
0x01, 0xd1, /* bne exit */
0x01, 0x3a, /* subs r2, r2, #0x01 */
0xf0, 0xd1, /* bne write_half_word */
/* exit: */
0x00, 0xbe, /* bkpt #0x00 */
0x00, 0x80, 0x00, 0x40, /* EM357_FLASH_BASE: .word 0x40008000 */
};
/* flash write code */
if (target_alloc_working_area(target, sizeof(em357_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
;
retval = target_write_buffer(target, write_algorithm->address,
sizeof(em357_flash_write_code), (uint8_t *)em357_flash_write_code);
if (retval != ERROR_OK)
return retval;
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a
* buffer, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING(
"no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
init_reg_param(&reg_params[3], "r3", 32, PARAM_IN_OUT);
while (count > 0) {
uint32_t thisrun_count = (count > (buffer_size / 2)) ?
(buffer_size / 2) : count;
retval = target_write_buffer(target, source->address, thisrun_count * 2, buffer);
if (retval != ERROR_OK)
break;
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, address);
buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
buf_set_u32(reg_params[3].value, 0, 32, 0);
retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
write_algorithm->address, 0, 10000, &armv7m_info);
if (retval != ERROR_OK) {
LOG_ERROR("error executing em357 flash write algorithm");
break;
}
if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_PGERR) {
LOG_ERROR("flash memory not erased before writing");
/* Clear but report errors */
target_write_u32(target, EM357_FLASH_SR, FLASH_PGERR);
retval = ERROR_FAIL;
break;
}
if (buf_get_u32(reg_params[3].value, 0, 32) & FLASH_WRPRTERR) {
LOG_ERROR("flash memory write protected");
/* Clear but report errors */
target_write_u32(target, EM357_FLASH_SR, FLASH_WRPRTERR);
retval = ERROR_FAIL;
break;
}
buffer += thisrun_count * 2;
address += thisrun_count * 2;
count -= thisrun_count;
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
return retval;
}
static int em357_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t words_remaining = (count / 2);
uint32_t bytes_remaining = (count & 0x00000001);
uint32_t address = bank->base + offset;
uint32_t bytes_written = 0;
int retval;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset & 0x1) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
/* unlock flash registers */
retval = target_write_u32(target, EM357_FLASH_KEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_KEYR, KEY2);
if (retval != ERROR_OK)
return retval;
target_write_u32(target, EM357_FPEC_CLK, 0x00000001);
/* multiple half words (2-byte) to be programmed? */
if (words_remaining > 0) {
/* try using a block write */
retval = em357_write_block(bank, buffer, offset, words_remaining);
if (retval != ERROR_OK) {
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single dword accesses */
LOG_WARNING(
"couldn't use block writes, falling back to single memory accesses");
}
} else {
buffer += words_remaining * 2;
address += words_remaining * 2;
words_remaining = 0;
}
}
if ((retval != ERROR_OK) && (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE))
return retval;
while (words_remaining > 0) {
uint16_t value;
memcpy(&value, buffer + bytes_written, sizeof(uint16_t));
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_PG);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, address, value);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 5);
if (retval != ERROR_OK)
return retval;
bytes_written += 2;
words_remaining--;
address += 2;
}
if (bytes_remaining) {
uint16_t value = 0xffff;
memcpy(&value, buffer + bytes_written, bytes_remaining);
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_PG);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, address, value);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 5);
if (retval != ERROR_OK)
return retval;
}
return target_write_u32(target, EM357_FLASH_CR, FLASH_LOCK);
}
static int em357_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct em357_flash_bank *em357_info = bank->driver_priv;
int i;
uint16_t num_pages;
int page_size;
uint32_t base_address = 0x08000000;
em357_info->probed = 0;
switch (bank->size) {
case 0x10000:
/* 64k -- 64 1k pages */
num_pages = 64;
page_size = 1024;
break;
case 0x20000:
/* 128k -- 128 1k pages */
num_pages = 128;
page_size = 1024;
break;
case 0x30000:
/* 192k -- 96 2k pages */
num_pages = 96;
page_size = 2048;
break;
case 0x40000:
/* 256k -- 128 2k pages */
num_pages = 128;
page_size = 2048;
break;
default:
LOG_WARNING("No size specified for em357 flash driver, assuming 192k!");
num_pages = 96;
page_size = 2048;
break;
}
/* Enable FPEC CLK */
int retval = target_write_u32(target, EM357_FPEC_CLK, 0x00000001);
if (retval != ERROR_OK)
return retval;
em357_info->ppage_size = 4;
LOG_INFO("flash size = %dkbytes", num_pages*page_size/1024);
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
bank->base = base_address;
bank->size = (num_pages * page_size);
bank->num_sectors = num_pages;
bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
for (i = 0; i < num_pages; i++) {
bank->sectors[i].offset = i * page_size;
bank->sectors[i].size = page_size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
em357_info->probed = 1;
return ERROR_OK;
}
static int em357_auto_probe(struct flash_bank *bank)
{
struct em357_flash_bank *em357_info = bank->driver_priv;
if (em357_info->probed)
return ERROR_OK;
return em357_probe(bank);
}
static int get_em357_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "em357\n");
return ERROR_OK;
}
COMMAND_HANDLER(em357_handle_lock_command)
{
struct target *target = NULL;
struct em357_flash_bank *em357_info = NULL;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
em357_info = bank->driver_priv;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (em357_erase_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "em357 failed to erase options");
return ERROR_OK;
}
/* set readout protection */
em357_info->option_bytes.RDP = 0;
if (em357_write_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "em357 failed to lock device");
return ERROR_OK;
}
command_print(CMD_CTX, "em357 locked");
return ERROR_OK;
}
COMMAND_HANDLER(em357_handle_unlock_command)
{
struct target *target = NULL;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (em357_erase_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "em357 failed to unlock device");
return ERROR_OK;
}
if (em357_write_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "em357 failed to lock device");
return ERROR_OK;
}
command_print(CMD_CTX, "em357 unlocked.\n"
"INFO: a reset or power cycle is required "
"for the new settings to take effect.");
return ERROR_OK;
}
static int em357_mass_erase(struct flash_bank *bank)
{
struct target *target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Make sure the flash clock is on */
target_write_u32(target, EM357_FPEC_CLK, 0x00000001);
/* unlock option flash registers */
int retval = target_write_u32(target, EM357_FLASH_KEYR, KEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_KEYR, KEY2);
if (retval != ERROR_OK)
return retval;
/* mass erase flash memory */
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_MER);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_MER | FLASH_STRT);
if (retval != ERROR_OK)
return retval;
retval = em357_wait_status_busy(bank, 100);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, EM357_FLASH_CR, FLASH_LOCK);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
COMMAND_HANDLER(em357_handle_mass_erase_command)
{
int i;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
retval = em357_mass_erase(bank);
if (retval == ERROR_OK) {
/* set all sectors as erased */
for (i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
command_print(CMD_CTX, "em357 mass erase complete");
} else
command_print(CMD_CTX, "em357 mass erase failed");
return retval;
}
static const struct command_registration em357_exec_command_handlers[] = {
{
.name = "lock",
.usage = "<bank>",
.handler = em357_handle_lock_command,
.mode = COMMAND_EXEC,
.help = "Lock entire flash device.",
},
{
.name = "unlock",
.usage = "<bank>",
.handler = em357_handle_unlock_command,
.mode = COMMAND_EXEC,
.help = "Unlock entire protected flash device.",
},
{
.name = "mass_erase",
.usage = "<bank>",
.handler = em357_handle_mass_erase_command,
.mode = COMMAND_EXEC,
.help = "Erase entire flash device.",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration em357_command_handlers[] = {
{
.name = "em357",
.mode = COMMAND_ANY,
.help = "em357 flash command group",
.usage = "",
.chain = em357_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver em357_flash = {
.name = "em357",
.commands = em357_command_handlers,
.flash_bank_command = em357_flash_bank_command,
.erase = em357_erase,
.protect = em357_protect,
.write = em357_write,
.read = default_flash_read,
.probe = em357_probe,
.auto_probe = em357_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = em357_protect_check,
.info = get_em357_info,
};
+142
View File
@@ -0,0 +1,142 @@
/***************************************************************************
* Copyright (C) 2009 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <target/image.h>
#include "hello.h"
struct faux_flash_bank {
struct target *target;
uint8_t *memory;
uint32_t start_address;
};
static const int sectorSize = 0x10000;
/* flash bank faux <base> <size> <chip_width> <bus_width> <target#> <driverPath>
*/
FLASH_BANK_COMMAND_HANDLER(faux_flash_bank_command)
{
struct faux_flash_bank *info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
info = malloc(sizeof(struct faux_flash_bank));
if (info == NULL) {
LOG_ERROR("no memory for flash bank info");
return ERROR_FAIL;
}
info->memory = malloc(bank->size);
if (info == NULL) {
free(info);
LOG_ERROR("no memory for flash bank info");
return ERROR_FAIL;
}
bank->driver_priv = info;
/* Use 0x10000 as a fixed sector size. */
int i = 0;
uint32_t offset = 0;
bank->num_sectors = bank->size/sectorSize;
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
for (i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = sectorSize;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 0;
}
info->target = get_target(CMD_ARGV[5]);
if (info->target == NULL) {
LOG_ERROR("target '%s' not defined", CMD_ARGV[5]);
free(info->memory);
free(info);
return ERROR_FAIL;
}
return ERROR_OK;
}
static int faux_erase(struct flash_bank *bank, int first, int last)
{
struct faux_flash_bank *info = bank->driver_priv;
memset(info->memory + first*sectorSize, 0xff, sectorSize*(last-first + 1));
return ERROR_OK;
}
static int faux_protect(struct flash_bank *bank, int set, int first, int last)
{
LOG_USER("set protection sector %d to %d to %s", first, last, set ? "on" : "off");
return ERROR_OK;
}
static int faux_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct faux_flash_bank *info = bank->driver_priv;
memcpy(info->memory + offset, buffer, count);
return ERROR_OK;
}
static int faux_protect_check(struct flash_bank *bank)
{
return ERROR_OK;
}
static int faux_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "faux flash driver");
return ERROR_OK;
}
static int faux_probe(struct flash_bank *bank)
{
return ERROR_OK;
}
static const struct command_registration faux_command_handlers[] = {
{
.name = "faux",
.mode = COMMAND_ANY,
.help = "faux flash command group",
.chain = hello_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver faux_flash = {
.name = "faux",
.commands = faux_command_handlers,
.flash_bank_command = faux_flash_bank_command,
.erase = faux_erase,
.protect = faux_protect,
.write = faux_write,
.read = default_flash_read,
.probe = faux_probe,
.auto_probe = faux_probe,
.erase_check = default_flash_blank_check,
.protect_check = faux_protect_check,
.info = faux_info
};
+909
View File
@@ -0,0 +1,909 @@
/***************************************************************************
* Copyright (C) 2011 by Marc Willam, Holger Wech *
* openOCD.fseu(AT)de.fujitsu.com *
* Copyright (C) 2011 Ronny Strutz *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#define FLASH_DQ6 0x00000040 /* Data toggle flag bit (TOGG) position */
#define FLASH_DQ5 0x00000020 /* Time limit exceeding flag bit (TLOV) position */
enum fm3_variant {
mb9bfxx1, /* Flash Type '1' */
mb9bfxx2,
mb9bfxx3,
mb9bfxx4,
mb9bfxx5,
mb9bfxx6,
mb9bfxx7,
mb9bfxx8,
mb9afxx1, /* Flash Type '2' */
mb9afxx2,
mb9afxx3,
mb9afxx4,
mb9afxx5,
mb9afxx6,
mb9afxx7,
mb9afxx8,
};
enum fm3_flash_type {
fm3_no_flash_type = 0,
fm3_flash_type1 = 1,
fm3_flash_type2 = 2
};
struct fm3_flash_bank {
enum fm3_variant variant;
enum fm3_flash_type flashtype;
int probed;
};
FLASH_BANK_COMMAND_HANDLER(fm3_flash_bank_command)
{
struct fm3_flash_bank *fm3_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
fm3_info = malloc(sizeof(struct fm3_flash_bank));
bank->driver_priv = fm3_info;
/* Flash type '1' */
if (strcmp(CMD_ARGV[5], "mb9bfxx1.cpu") == 0) {
fm3_info->variant = mb9bfxx1;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx2.cpu") == 0) {
fm3_info->variant = mb9bfxx2;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx3.cpu") == 0) {
fm3_info->variant = mb9bfxx3;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx4.cpu") == 0) {
fm3_info->variant = mb9bfxx4;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx5.cpu") == 0) {
fm3_info->variant = mb9bfxx5;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx6.cpu") == 0) {
fm3_info->variant = mb9bfxx6;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx7.cpu") == 0) {
fm3_info->variant = mb9bfxx7;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9bfxx8.cpu") == 0) {
fm3_info->variant = mb9bfxx8;
fm3_info->flashtype = fm3_flash_type1;
} else if (strcmp(CMD_ARGV[5], "mb9afxx1.cpu") == 0) { /* Flash type '2' */
fm3_info->variant = mb9afxx1;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx2.cpu") == 0) {
fm3_info->variant = mb9afxx2;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx3.cpu") == 0) {
fm3_info->variant = mb9afxx3;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx4.cpu") == 0) {
fm3_info->variant = mb9afxx4;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx5.cpu") == 0) {
fm3_info->variant = mb9afxx5;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx6.cpu") == 0) {
fm3_info->variant = mb9afxx6;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx7.cpu") == 0) {
fm3_info->variant = mb9afxx7;
fm3_info->flashtype = fm3_flash_type2;
} else if (strcmp(CMD_ARGV[5], "mb9afxx8.cpu") == 0) {
fm3_info->variant = mb9afxx8;
fm3_info->flashtype = fm3_flash_type2;
}
/* unknown Flash type */
else {
LOG_ERROR("unknown fm3 variant: %s", CMD_ARGV[5]);
free(fm3_info);
return ERROR_FLASH_BANK_INVALID;
}
fm3_info->probed = 0;
return ERROR_OK;
}
/* Data polling algorithm */
static int fm3_busy_wait(struct target *target, uint32_t offset, int timeout_ms)
{
int retval = ERROR_OK;
uint16_t state1, state2;
int ms = 0;
/* While(1) loop exit via "break" and "return" on error */
while (1) {
/* dummy-read - see flash manual */
retval = target_read_u16(target, offset, &state1);
if (retval != ERROR_OK)
return retval;
/* Data polling 1 */
retval = target_read_u16(target, offset, &state1);
if (retval != ERROR_OK)
return retval;
/* Data polling 2 */
retval = target_read_u16(target, offset, &state2);
if (retval != ERROR_OK)
return retval;
/* Flash command finished via polled data equal? */
if ((state1 & FLASH_DQ6) == (state2 & FLASH_DQ6))
break;
/* Timeout Flag? */
else if (state1 & FLASH_DQ5) {
/* Retry data polling */
/* Data polling 1 */
retval = target_read_u16(target, offset, &state1);
if (retval != ERROR_OK)
return retval;
/* Data polling 2 */
retval = target_read_u16(target, offset, &state2);
if (retval != ERROR_OK)
return retval;
/* Flash command finished via polled data equal? */
if ((state1 & FLASH_DQ6) != (state2 & FLASH_DQ6))
return ERROR_FLASH_OPERATION_FAILED;
/* finish anyway */
break;
}
usleep(1000);
++ms;
/* Polling time exceeded? */
if (ms > timeout_ms) {
LOG_ERROR("Polling data reading timed out!");
return ERROR_FLASH_OPERATION_FAILED;
}
}
if (retval == ERROR_OK)
LOG_DEBUG("fm3_busy_wait(%x) needs about %d ms", offset, ms);
return retval;
}
static int fm3_erase(struct flash_bank *bank, int first, int last)
{
struct fm3_flash_bank *fm3_info = bank->driver_priv;
struct target *target = bank->target;
int retval = ERROR_OK;
uint32_t u32DummyRead;
int sector, odd;
uint32_t u32FlashType;
uint32_t u32FlashSeqAddress1;
uint32_t u32FlashSeqAddress2;
u32FlashType = (uint32_t) fm3_info->flashtype;
if (u32FlashType == fm3_flash_type1) {
u32FlashSeqAddress1 = 0x00001550;
u32FlashSeqAddress2 = 0x00000AA8;
} else if (u32FlashType == fm3_flash_type2) {
u32FlashSeqAddress1 = 0x00000AA8;
u32FlashSeqAddress2 = 0x00000554;
} else {
LOG_ERROR("Flash/Device type unknown!");
return ERROR_FLASH_OPERATION_FAILED;
}
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
LOG_INFO("Fujitsu MB9Bxxx: Sector Erase ... (%d to %d)", first, last);
/* FASZR = 0x01, Enables CPU Programming Mode (16-bit Flash acccess) */
retval = target_write_u32(target, 0x40000000, 0x0001);
if (retval != ERROR_OK)
return retval;
/* dummy read of FASZR */
retval = target_read_u32(target, 0x40000000, &u32DummyRead);
if (retval != ERROR_OK)
return retval;
for (sector = first ; sector <= last ; sector++) {
uint32_t offset = bank->sectors[sector].offset;
for (odd = 0; odd < 2 ; odd++) {
if (odd)
offset += 4;
/* Flash unlock sequence */
retval = target_write_u16(target, u32FlashSeqAddress1, 0x00AA);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress2, 0x0055);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress1, 0x0080);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress1, 0x00AA);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress2, 0x0055);
if (retval != ERROR_OK)
return retval;
/* Sector erase command (0x0030) */
retval = target_write_u16(target, offset, 0x0030);
if (retval != ERROR_OK)
return retval;
retval = fm3_busy_wait(target, offset, 500);
if (retval != ERROR_OK)
return retval;
}
bank->sectors[sector].is_erased = 1;
}
/* FASZR = 0x02, Enables CPU Run Mode (32-bit Flash acccess) */
retval = target_write_u32(target, 0x40000000, 0x0002);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(target, 0x40000000, &u32DummyRead); /* dummy read of FASZR */
return retval;
}
static int fm3_write_block(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct fm3_flash_bank *fm3_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t buffer_size = 2048; /* 8192 for MB9Bxx6! */
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[6];
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
uint32_t u32FlashType;
uint32_t u32FlashSeqAddress1;
uint32_t u32FlashSeqAddress2;
u32FlashType = (uint32_t) fm3_info->flashtype;
if (u32FlashType == fm3_flash_type1) {
u32FlashSeqAddress1 = 0x00001550;
u32FlashSeqAddress2 = 0x00000AA8;
} else if (u32FlashType == fm3_flash_type2) {
u32FlashSeqAddress1 = 0x00000AA8;
u32FlashSeqAddress2 = 0x00000554;
} else {
LOG_ERROR("Flash/Device type unknown!");
return ERROR_FLASH_OPERATION_FAILED;
}
/* RAMCODE used for fm3 Flash programming: */
/* R0 keeps source start address (u32Source) */
/* R1 keeps target start address (u32Target) */
/* R2 keeps number of halfwords to write (u32Count) */
/* R3 keeps Flash Sequence address 1 (u32FlashSeq1) */
/* R4 keeps Flash Sequence address 2 (u32FlashSeq2) */
/* R5 returns result value (u32FlashResult) */
const uint8_t fm3_flash_write_code[] = {
/* fm3_FLASH_IF->FASZ &= 0xFFFD; */
0x5F, 0xF0, 0x80, 0x45, /* MOVS.W R5, #(fm3_FLASH_IF->FASZ) */
0x2D, 0x68, /* LDR R5, [R5] */
0x4F, 0xF6, 0xFD, 0x76, /* MOVW R6, #0xFFFD */
0x35, 0x40, /* ANDS R5, R5, R6 */
0x5F, 0xF0, 0x80, 0x46, /* MOVS.W R6, #(fm3_FLASH_IF->FASZ) */
0x35, 0x60, /* STR R5, [R6] */
/* fm3_FLASH_IF->FASZ |= 1; */
0x5F, 0xF0, 0x80, 0x45, /* MOVS.W R5, #(fm3_FLASH_IF->FASZ) */
0x2D, 0x68, /* LDR R5, [R3] */
0x55, 0xF0, 0x01, 0x05, /* ORRS.W R5, R5, #1 */
0x5F, 0xF0, 0x80, 0x46, /* MOVS.W R6, #(fm3_FLASH_IF->FASZ) */
0x35, 0x60, /* STR R5, [R6] */
/* u32DummyRead = fm3_FLASH_IF->FASZ; */
0x28, 0x4D, /* LDR.N R5, ??u32DummyRead */
0x5F, 0xF0, 0x80, 0x46, /* MOVS.W R6, #(fm3_FLASH_IF->FASZ) */
0x36, 0x68, /* LDR R6, [R6] */
0x2E, 0x60, /* STR R6, [R5] */
/* u32FlashResult = FLASH_WRITE_NO_RESULT */
0x26, 0x4D, /* LDR.N R5, ??u32FlashResult */
0x00, 0x26, /* MOVS R6, #0 */
0x2E, 0x60, /* STR R6, [R5] */
/* while ((u32Count > 0 ) */
/* && (u32FlashResult */
/* == FLASH_WRITE_NO_RESULT)) */
0x01, 0x2A, /* L0: CMP R2, #1 */
0x2C, 0xDB, /* BLT.N L1 */
0x24, 0x4D, /* LDR.N R5, ??u32FlashResult */
0x2D, 0x68, /* LDR R5, [R5] */
0x00, 0x2D, /* CMP R5, #0 */
0x28, 0xD1, /* BNE.N L1 */
/* *u32FlashSeq1 = FLASH_WRITE_1; */
0xAA, 0x25, /* MOVS R5, #0xAA */
0x1D, 0x60, /* STR R5, [R3] */
/* *u32FlashSeq2 = FLASH_WRITE_2; */
0x55, 0x25, /* MOVS R5, #0x55 */
0x25, 0x60, /* STR R5, [R4] */
/* *u32FlashSeq1 = FLASH_WRITE_3; */
0xA0, 0x25, /* MOVS R5, #0xA0 */
0x1D, 0x60, /* STRH R5, [R3] */
/* *(volatile uint16_t*)u32Target */
/* = *(volatile uint16_t*)u32Source; */
0x05, 0x88, /* LDRH R5, [R0] */
0x0D, 0x80, /* STRH R5, [R1] */
/* while (u32FlashResult */
/* == FLASH_WRITE_NO_RESTULT) */
0x1E, 0x4D, /* L2: LDR.N R5, ??u32FlashResult */
0x2D, 0x68, /* LDR R5, [R5] */
0x00, 0x2D, /* CMP R5, #0 */
0x11, 0xD1, /* BNE.N L3 */
/* if ((*(volatile uint16_t*)u32Target */
/* & FLASH_DQ5) == FLASH_DQ5) */
0x0D, 0x88, /* LDRH R5, [R1] */
0xAD, 0x06, /* LSLS R5, R5, #0x1A */
0x02, 0xD5, /* BPL.N L4 */
/* u32FlashResult = FLASH_WRITE_TIMEOUT */
0x1A, 0x4D, /* LDR.N R5, ??u32FlashResult */
0x02, 0x26, /* MOVS R6, #2 */
0x2E, 0x60, /* STR R6, [R5] */
/* if ((*(volatile uint16_t *)u32Target */
/* & FLASH_DQ7) */
/* == (*(volatile uint16_t*)u32Source */
/* & FLASH_DQ7)) */
0x0D, 0x88, /* L4: LDRH R5, [R1] */
0x15, 0xF0, 0x80, 0x05, /* ANDS.W R5, R5, #0x80 */
0x06, 0x88, /* LDRH R6, [R0] */
0x16, 0xF0, 0x80, 0x06, /* ANDS.W R6, R6, #0x80 */
0xB5, 0x42, /* CMP R5, R6 */
0xED, 0xD1, /* BNE.N L2 */
/* u32FlashResult = FLASH_WRITE_OKAY */
0x15, 0x4D, /* LDR.N R5, ??u32FlashResult */
0x01, 0x26, /* MOVS R6, #1 */
0x2E, 0x60, /* STR R6, [R5] */
0xE9, 0xE7, /* B.N L2 */
/* if (u32FlashResult */
/* != FLASH_WRITE_TIMEOUT) */
0x13, 0x4D, /* LDR.N R5, ??u32FlashResult */
0x2D, 0x68, /* LDR R5, [R5] */
0x02, 0x2D, /* CMP R5, #2 */
0x02, 0xD0, /* BEQ.N L5 */
/* u32FlashResult = FLASH_WRITE_NO_RESULT */
0x11, 0x4D, /* LDR.N R5, ??u32FlashResult */
0x00, 0x26, /* MOVS R6, #0 */
0x2E, 0x60, /* STR R6, [R5] */
/* u32Count--; */
0x52, 0x1E, /* L5: SUBS R2, R2, #1 */
/* u32Source += 2; */
0x80, 0x1C, /* ADDS R0, R0, #2 */
/* u32Target += 2; */
0x89, 0x1C, /* ADDS R1, R1, #2 */
0xD0, 0xE7, /* B.N L0 */
/* fm3_FLASH_IF->FASZ &= 0xFFFE; */
0x5F, 0xF0, 0x80, 0x45, /* L1: MOVS.W R5, #(fm3_FLASH_IF->FASZ) */
0x2D, 0x68, /* LDR R5, [R5] */
0x4F, 0xF6, 0xFE, 0x76, /* MOVW R6, #0xFFFE */
0x35, 0x40, /* ANDS R5, R5, R6 */
0x5F, 0xF0, 0x80, 0x46, /* MOVS.W R6, #(fm3_FLASH_IF->FASZ) */
0x35, 0x60, /* STR R5, [R6] */
/* fm3_FLASH_IF->FASZ |= 2; */
0x5F, 0xF0, 0x80, 0x45, /* MOVS.W R5, #(fm3_FLASH_IF->FASZ) */
0x2D, 0x68, /* LDR R5, [R5] */
0x55, 0xF0, 0x02, 0x05, /* ORRS.W R5, R5, #2 */
0x5F, 0xF0, 0x80, 0x46, /* MOVS.W R6, #(fm3_FLASH_IF->FASZ) */
0x35, 0x60, /* STR R5, [R6] */
/* u32DummyRead = fm3_FLASH_IF->FASZ; */
0x04, 0x4D, /* LDR.N R5, ??u32DummyRead */
0x5F, 0xF0, 0x80, 0x46, /* MOVS.W R6, #(fm3_FLASH_IF->FASZ) */
0x36, 0x68, /* LDR R6, [R6] */
0x2E, 0x60, /* STR R6, [R5] */
/* copy u32FlashResult to R3 for return */
/* value */
0xDF, 0xF8, 0x08, 0x50, /* LDR.W R5, ??u32FlashResult */
0x2D, 0x68, /* LDR R5, [R5] */
/* Breakpoint here */
0x00, 0xBE, /* BKPT #0 */
/* The following address pointers assume, that the code is running from */
/* address 0x1FFF8008. These address pointers will be patched, if a */
/* different start address in RAM is used (e.g. for Flash type 2)! */
0x00, 0x80, 0xFF, 0x1F, /* u32DummyRead address in RAM (0x1FFF8000) */
0x04, 0x80, 0xFF, 0x1F /* u32FlashResult address in RAM (0x1FFF8004) */
};
LOG_INFO("Fujitsu MB9B500: FLASH Write ...");
/* disable HW watchdog */
retval = target_write_u32(target, 0x40011C00, 0x1ACCE551);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, 0x40011C00, 0xE5331AAE);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, 0x40011008, 0x00000000);
if (retval != ERROR_OK)
return retval;
count = count / 2; /* number bytes -> number halfwords */
/* check code alignment */
if (offset & 0x1) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
/* allocate working area with flash programming code */
if (target_alloc_working_area(target, sizeof(fm3_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
retval = target_write_buffer(target, write_algorithm->address,
sizeof(fm3_flash_write_code), fm3_flash_write_code);
if (retval != ERROR_OK)
return retval;
/* memory buffer */
while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
if (buffer_size <= 256) {
/* free working area, write algorithm already allocated */
target_free_working_area(target, write_algorithm);
LOG_WARNING("No large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /* source start address */
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /* target start address */
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /* number of halfwords to program */
init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /* Flash Sequence address 1 */
init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT); /* Flash Sequence address 1 */
init_reg_param(&reg_params[5], "r5", 32, PARAM_IN); /* result */
/* write code buffer and use Flash programming code within fm3 */
/* Set breakpoint to 0 with time-out of 1000 ms */
while (count > 0) {
uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
retval = target_write_buffer(target, write_algorithm->address, 8,
fm3_flash_write_code);
if (retval != ERROR_OK)
break;
/* Patching 'local variable address' for different RAM addresses */
if (write_algorithm->address != 0x1FFF8008) {
/* Algorithm: u32DummyRead: */
retval = target_write_u32(target, (write_algorithm->address)
+ sizeof(fm3_flash_write_code) - 8, (write_algorithm->address) - 8);
if (retval != ERROR_OK)
break;
/* Algorithm: u32FlashResult: */
retval = target_write_u32(target, (write_algorithm->address)
+ sizeof(fm3_flash_write_code) - 4, (write_algorithm->address) - 4);
if (retval != ERROR_OK)
break;
}
retval = target_write_buffer(target, source->address, thisrun_count * 2, buffer);
if (retval != ERROR_OK)
break;
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, address);
buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
buf_set_u32(reg_params[3].value, 0, 32, u32FlashSeqAddress1);
buf_set_u32(reg_params[4].value, 0, 32, u32FlashSeqAddress2);
retval = target_run_algorithm(target, 0, NULL, 6, reg_params,
write_algorithm->address, 0, 1000, &armv7m_info);
if (retval != ERROR_OK) {
LOG_ERROR("Error executing fm3 Flash programming algorithm");
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
if (buf_get_u32(reg_params[5].value, 0, 32) != ERROR_OK) {
LOG_ERROR("Fujitsu MB9[A/B]FXXX: Flash programming ERROR (Timeout) -> Reg R3: %x",
buf_get_u32(reg_params[5].value, 0, 32));
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
buffer += thisrun_count * 2;
address += thisrun_count * 2;
count -= thisrun_count;
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
destroy_reg_param(&reg_params[5]);
return retval;
}
static int fm3_probe(struct flash_bank *bank)
{
struct fm3_flash_bank *fm3_info = bank->driver_priv;
uint16_t num_pages;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/*
-- page-- start -- blocksize - mpu - totalFlash --
page0 0x00000 16k
page1 0x04000 16k
page2 0x08000 96k ___ fxx3 128k Flash
page3 0x20000 128k ___ fxx4 256k Flash
page4 0x40000 128k ___ fxx5 384k Flash
page5 0x60000 128k ___ fxx6 512k Flash
-----------------------
page6 0x80000 128k
page7 0xa0000 128k ___ fxx7 256k Flash
page8 0xc0000 128k
page9 0xe0000 128k ___ fxx8 256k Flash
*/
num_pages = 10; /* max number of Flash pages for malloc */
fm3_info->probed = 0;
bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
bank->base = 0x00000000;
bank->size = 32 * 1024; /* bytes */
bank->sectors[0].offset = 0;
bank->sectors[0].size = 16 * 1024;
bank->sectors[0].is_erased = -1;
bank->sectors[0].is_protected = -1;
bank->sectors[1].offset = 0x4000;
bank->sectors[1].size = 16 * 1024;
bank->sectors[1].is_erased = -1;
bank->sectors[1].is_protected = -1;
if ((fm3_info->variant == mb9bfxx1)
|| (fm3_info->variant == mb9afxx1)) {
num_pages = 3;
bank->size = 64 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[2].offset = 0x8000;
bank->sectors[2].size = 32 * 1024;
bank->sectors[2].is_erased = -1;
bank->sectors[2].is_protected = -1;
}
if ((fm3_info->variant == mb9bfxx2)
|| (fm3_info->variant == mb9bfxx4)
|| (fm3_info->variant == mb9bfxx5)
|| (fm3_info->variant == mb9bfxx6)
|| (fm3_info->variant == mb9bfxx7)
|| (fm3_info->variant == mb9bfxx8)
|| (fm3_info->variant == mb9afxx2)
|| (fm3_info->variant == mb9afxx4)
|| (fm3_info->variant == mb9afxx5)
|| (fm3_info->variant == mb9afxx6)
|| (fm3_info->variant == mb9afxx7)
|| (fm3_info->variant == mb9afxx8)) {
num_pages = 3;
bank->size = 128 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[2].offset = 0x8000;
bank->sectors[2].size = 96 * 1024;
bank->sectors[2].is_erased = -1;
bank->sectors[2].is_protected = -1;
}
if ((fm3_info->variant == mb9bfxx4)
|| (fm3_info->variant == mb9bfxx5)
|| (fm3_info->variant == mb9bfxx6)
|| (fm3_info->variant == mb9bfxx7)
|| (fm3_info->variant == mb9bfxx8)
|| (fm3_info->variant == mb9afxx4)
|| (fm3_info->variant == mb9afxx5)
|| (fm3_info->variant == mb9afxx6)
|| (fm3_info->variant == mb9afxx7)
|| (fm3_info->variant == mb9afxx8)) {
num_pages = 4;
bank->size = 256 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[3].offset = 0x20000;
bank->sectors[3].size = 128 * 1024;
bank->sectors[3].is_erased = -1;
bank->sectors[3].is_protected = -1;
}
if ((fm3_info->variant == mb9bfxx5)
|| (fm3_info->variant == mb9bfxx6)
|| (fm3_info->variant == mb9bfxx7)
|| (fm3_info->variant == mb9bfxx8)
|| (fm3_info->variant == mb9afxx5)
|| (fm3_info->variant == mb9afxx6)
|| (fm3_info->variant == mb9afxx7)
|| (fm3_info->variant == mb9afxx8)) {
num_pages = 5;
bank->size = 384 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[4].offset = 0x40000;
bank->sectors[4].size = 128 * 1024;
bank->sectors[4].is_erased = -1;
bank->sectors[4].is_protected = -1;
}
if ((fm3_info->variant == mb9bfxx6)
|| (fm3_info->variant == mb9bfxx7)
|| (fm3_info->variant == mb9bfxx8)
|| (fm3_info->variant == mb9afxx6)
|| (fm3_info->variant == mb9afxx7)
|| (fm3_info->variant == mb9afxx8)) {
num_pages = 6;
bank->size = 512 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[5].offset = 0x60000;
bank->sectors[5].size = 128 * 1024;
bank->sectors[5].is_erased = -1;
bank->sectors[5].is_protected = -1;
}
if ((fm3_info->variant == mb9bfxx7)
|| (fm3_info->variant == mb9bfxx8)
|| (fm3_info->variant == mb9afxx7)
|| (fm3_info->variant == mb9afxx8)) {
num_pages = 8;
bank->size = 768 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[6].offset = 0x80000;
bank->sectors[6].size = 128 * 1024;
bank->sectors[6].is_erased = -1;
bank->sectors[6].is_protected = -1;
bank->sectors[7].offset = 0xa0000;
bank->sectors[7].size = 128 * 1024;
bank->sectors[7].is_erased = -1;
bank->sectors[7].is_protected = -1;
}
if ((fm3_info->variant == mb9bfxx8)
|| (fm3_info->variant == mb9afxx8)) {
num_pages = 10;
bank->size = 1024 * 1024; /* bytes */
bank->num_sectors = num_pages;
bank->sectors[8].offset = 0xc0000;
bank->sectors[8].size = 128 * 1024;
bank->sectors[8].is_erased = -1;
bank->sectors[8].is_protected = -1;
bank->sectors[9].offset = 0xe0000;
bank->sectors[9].size = 128 * 1024;
bank->sectors[9].is_erased = -1;
bank->sectors[9].is_protected = -1;
}
fm3_info->probed = 1;
return ERROR_OK;
}
static int fm3_auto_probe(struct flash_bank *bank)
{
struct fm3_flash_bank *fm3_info = bank->driver_priv;
if (fm3_info->probed)
return ERROR_OK;
return fm3_probe(bank);
}
static int fm3_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "Fujitsu fm3 Device does not support Chip-ID (Type unknown)");
return ERROR_OK;
}
/* Chip erase */
static int fm3_chip_erase(struct flash_bank *bank)
{
struct target *target = bank->target;
struct fm3_flash_bank *fm3_info2 = bank->driver_priv;
int retval = ERROR_OK;
uint32_t u32DummyRead;
uint32_t u32FlashType;
uint32_t u32FlashSeqAddress1;
uint32_t u32FlashSeqAddress2;
u32FlashType = (uint32_t) fm3_info2->flashtype;
if (u32FlashType == fm3_flash_type1) {
LOG_INFO("*** Erasing mb9bfxxx type");
u32FlashSeqAddress1 = 0x00001550;
u32FlashSeqAddress2 = 0x00000AA8;
} else if (u32FlashType == fm3_flash_type2) {
LOG_INFO("*** Erasing mb9afxxx type");
u32FlashSeqAddress1 = 0x00000AA8;
u32FlashSeqAddress2 = 0x00000554;
} else {
LOG_ERROR("Flash/Device type unknown!");
return ERROR_FLASH_OPERATION_FAILED;
}
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
LOG_INFO("Fujitsu MB9[AB]xxx: Chip Erase ... (may take several seconds)");
/* Implement Flash chip erase (mass erase) completely on host */
/* FASZR = 0x01, Enables CPU Programming Mode (16-bit Flash access) */
retval = target_write_u32(target, 0x40000000, 0x0001);
if (retval != ERROR_OK)
return retval;
/* dummy read of FASZR */
retval = target_read_u32(target, 0x40000000, &u32DummyRead);
if (retval != ERROR_OK)
return retval;
/* Flash unlock sequence */
retval = target_write_u16(target, u32FlashSeqAddress1, 0x00AA);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress2, 0x0055);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress1, 0x0080);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress1, 0x00AA);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, u32FlashSeqAddress2, 0x0055);
if (retval != ERROR_OK)
return retval;
/* Chip Erase command (0x0010) */
retval = target_write_u16(target, u32FlashSeqAddress1, 0x0010);
if (retval != ERROR_OK)
return retval;
retval = fm3_busy_wait(target, u32FlashSeqAddress2, 20000); /* 20s timeout */
if (retval != ERROR_OK)
return retval;
/* FASZR = 0x02, Re-enables CPU Run Mode (32-bit Flash access) */
retval = target_write_u32(target, 0x40000000, 0x0002);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(target, 0x40000000, &u32DummyRead); /* dummy read of FASZR */
return retval;
}
COMMAND_HANDLER(fm3_handle_chip_erase_command)
{
int i;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
if (fm3_chip_erase(bank) == ERROR_OK) {
/* set all sectors as erased */
for (i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
command_print(CMD_CTX, "fm3 chip erase complete");
} else {
command_print(CMD_CTX, "fm3 chip erase failed");
}
return ERROR_OK;
}
static const struct command_registration fm3_exec_command_handlers[] = {
{
.name = "chip_erase",
.usage = "<bank>",
.handler = fm3_handle_chip_erase_command,
.mode = COMMAND_EXEC,
.help = "Erase entire Flash device.",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration fm3_command_handlers[] = {
{
.name = "fm3",
.mode = COMMAND_ANY,
.help = "fm3 Flash command group",
.usage = "",
.chain = fm3_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver fm3_flash = {
.name = "fm3",
.commands = fm3_command_handlers,
.flash_bank_command = fm3_flash_bank_command,
.erase = fm3_erase,
.write = fm3_write_block,
.probe = fm3_probe,
.auto_probe = fm3_auto_probe,
.erase_check = default_flash_blank_check,
.info = fm3_info,
};
+51
View File
@@ -0,0 +1,51 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FLASH_NOR_IMP_H
#define FLASH_NOR_IMP_H
/* this is an internal header */
#include "core.h"
#include "driver.h"
/* almost all drivers will need this file */
#include <target/target.h>
/**
* Adds a new NOR bank to the global list of banks.
* @param bank The bank that should be added.
*/
void flash_bank_add(struct flash_bank *bank);
/**
* @return The first bank in the global list.
*/
struct flash_bank *flash_bank_list(void);
int flash_driver_erase(struct flash_bank *bank, int first, int last);
int flash_driver_protect(struct flash_bank *bank, int set, int first, int last);
int flash_driver_write(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count);
int flash_driver_read(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count);
/* write (optional verify) an image to flash memory of the given target */
int flash_write_unlock(struct target *target, struct image *image,
uint32_t *written, int erase, bool unlock);
#endif /* FLASH_NOR_IMP_H */
+825
View File
@@ -0,0 +1,825 @@
/***************************************************************************
* Copyright (C) 2011 by Mathias Kuester *
* kesmtp@freenet.de *
* *
* Copyright (C) 2011 sleep(5) ltd *
* tomas@sleepfive.com *
* *
* Copyright (C) 2012 by Christopher D. Kilgour *
* techie at whiterocker.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "helper/binarybuffer.h"
/*
* Implementation Notes
*
* The persistent memories in the Kinetis chip families K10 through
* K70 are all manipulated with the Flash Memory Module. Some
* variants call this module the FTFE, others call it the FTFL. To
* indicate that both are considered here, we use FTFX.
*
* Within the module, according to the chip variant, the persistent
* memory is divided into what Freescale terms Program Flash, FlexNVM,
* and FlexRAM. All chip variants have Program Flash. Some chip
* variants also have FlexNVM and FlexRAM, which always appear
* together.
*
* A given Kinetis chip may have 2 or 4 blocks of flash. Here we map
* each block to a separate bank. Each block size varies by chip and
* may be determined by the read-only SIM_FCFG1 register. The sector
* size within each bank/block varies by the chip granularity as
* described below.
*
* Kinetis offers four different of flash granularities applicable
* across the chip families. The granularity is apparently reflected
* by at least the reference manual suffix. For example, for chip
* MK60FN1M0VLQ12, reference manual K60P144M150SF3RM ends in "SF3RM",
* where the "3" indicates there are four flash blocks with 4kiB
* sectors. All possible granularities are indicated below.
*
* The first half of the flash (1 or 2 blocks, depending on the
* granularity) is always Program Flash and always starts at address
* 0x00000000. The "PFLSH" flag, bit 23 of the read-only SIM_FCFG2
* register, determines whether the second half of the flash is also
* Program Flash or FlexNVM+FlexRAM. When PFLSH is set, the second
* half of flash is Program Flash and is contiguous in the memory map
* from the first half. When PFLSH is clear, the second half of flash
* is FlexNVM and always starts at address 0x10000000. FlexRAM, which
* is also present when PFLSH is clear, always starts at address
* 0x14000000.
*
* The Flash Memory Module provides a register set where flash
* commands are loaded to perform flash operations like erase and
* program. Different commands are available depending on whether
* Program Flash or FlexNVM/FlexRAM is being manipulated. Although
* the commands used are quite consistent between flash blocks, the
* parameters they accept differ according to the flash granularity.
* Some Kinetis chips have different granularity between Program Flash
* and FlexNVM/FlexRAM, so flash command arguments may differ between
* blocks in the same chip.
*
* Although not documented as such by Freescale, it appears that bits
* 8:7 of the read-only SIM_SDID register reflect the granularity
* settings 0..3, so sector sizes and block counts are applicable
* according to the following table.
*/
const struct {
unsigned pflash_sector_size_bytes;
unsigned nvm_sector_size_bytes;
unsigned num_blocks;
} kinetis_flash_params[4] = {
{ 1<<10, 1<<10, 2 },
{ 2<<10, 1<<10, 2 },
{ 2<<10, 2<<10, 2 },
{ 4<<10, 4<<10, 4 }
};
/* Addressess */
#define FLEXRAM 0x14000000
#define FTFx_FSTAT 0x40020000
#define FTFx_FCNFG 0x40020001
#define FTFx_FCCOB3 0x40020004
#define FTFx_FPROT3 0x40020010
#define SIM_SDID 0x40048024
#define SIM_FCFG1 0x4004804c
#define SIM_FCFG2 0x40048050
/* Commands */
#define FTFx_CMD_BLOCKSTAT 0x00
#define FTFx_CMD_SECTSTAT 0x01
#define FTFx_CMD_LWORDPROG 0x06
#define FTFx_CMD_SECTERASE 0x09
#define FTFx_CMD_SECTWRITE 0x0b
#define FTFx_CMD_SETFLEXRAM 0x81
struct kinetis_flash_bank {
unsigned granularity;
unsigned bank_ordinal;
uint32_t sector_size;
uint32_t protection_size;
uint32_t sim_sdid;
uint32_t sim_fcfg1;
uint32_t sim_fcfg2;
enum {
FC_AUTO = 0,
FC_PFLASH,
FC_FLEX_NVM,
FC_FLEX_RAM,
} flash_class;
};
FLASH_BANK_COMMAND_HANDLER(kinetis_flash_bank_command)
{
struct kinetis_flash_bank *bank_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
LOG_INFO("add flash_bank kinetis %s", bank->name);
bank_info = malloc(sizeof(struct kinetis_flash_bank));
memset(bank_info, 0, sizeof(struct kinetis_flash_bank));
bank->driver_priv = bank_info;
return ERROR_OK;
}
static int kinetis_protect(struct flash_bank *bank, int set, int first,
int last)
{
LOG_WARNING("kinetis_protect not supported yet");
/* FIXME: TODO */
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
return ERROR_FLASH_BANK_INVALID;
}
static int kinetis_protect_check(struct flash_bank *bank)
{
struct kinetis_flash_bank *kinfo = bank->driver_priv;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (kinfo->flash_class == FC_PFLASH) {
int result;
uint8_t buffer[4];
uint32_t fprot, psec;
int i, b;
/* read protection register */
result = target_read_memory(bank->target, FTFx_FPROT3, 1, 4, buffer);
if (result != ERROR_OK)
return result;
fprot = target_buffer_get_u32(bank->target, buffer);
/*
* Every bit protects 1/32 of the full flash (not necessarily
* just this bank), but we enforce the bank ordinals for
* PFlash to start at zero.
*/
b = kinfo->bank_ordinal * (bank->size / kinfo->protection_size);
for (psec = 0, i = 0; i < bank->num_sectors; i++) {
if ((fprot >> b) & 1)
bank->sectors[i].is_protected = 0;
else
bank->sectors[i].is_protected = 1;
psec += bank->sectors[i].size;
if (psec >= kinfo->protection_size) {
psec = 0;
b++;
}
}
} else {
LOG_ERROR("Protection checks for FlexNVM not yet supported");
return ERROR_FLASH_BANK_INVALID;
}
return ERROR_OK;
}
static int kinetis_ftfx_command(struct flash_bank *bank, uint8_t fcmd, uint32_t faddr,
uint8_t fccob4, uint8_t fccob5, uint8_t fccob6, uint8_t fccob7,
uint8_t fccob8, uint8_t fccob9, uint8_t fccoba, uint8_t fccobb,
uint8_t *ftfx_fstat)
{
uint8_t command[12] = {faddr & 0xff, (faddr >> 8) & 0xff, (faddr >> 16) & 0xff, fcmd,
fccob7, fccob6, fccob5, fccob4,
fccobb, fccoba, fccob9, fccob8};
int result, i;
uint8_t buffer;
/* wait for done */
for (i = 0; i < 50; i++) {
result =
target_read_memory(bank->target, FTFx_FSTAT, 1, 1, &buffer);
if (result != ERROR_OK)
return result;
if (buffer & 0x80)
break;
buffer = 0x00;
}
if (buffer != 0x80) {
/* reset error flags */
buffer = 0x30;
result =
target_write_memory(bank->target, FTFx_FSTAT, 1, 1, &buffer);
if (result != ERROR_OK)
return result;
}
result = target_write_memory(bank->target, FTFx_FCCOB3, 4, 3, command);
if (result != ERROR_OK)
return result;
/* start command */
buffer = 0x80;
result = target_write_memory(bank->target, FTFx_FSTAT, 1, 1, &buffer);
if (result != ERROR_OK)
return result;
/* wait for done */
for (i = 0; i < 50; i++) {
result =
target_read_memory(bank->target, FTFx_FSTAT, 1, 1, ftfx_fstat);
if (result != ERROR_OK)
return result;
if (*ftfx_fstat & 0x80)
break;
}
if ((*ftfx_fstat & 0xf0) != 0x80) {
LOG_ERROR
("ftfx command failed FSTAT: %02X FCCOB: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
*ftfx_fstat, command[3], command[2], command[1], command[0],
command[7], command[6], command[5], command[4],
command[11], command[10], command[9], command[8]);
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int kinetis_erase(struct flash_bank *bank, int first, int last)
{
int result, i;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first > bank->num_sectors) || (last > bank->num_sectors))
return ERROR_FLASH_OPERATION_FAILED;
/*
* FIXME: TODO: use the 'Erase Flash Block' command if the
* requested erase is PFlash or NVM and encompasses the entire
* block. Should be quicker.
*/
for (i = first; i <= last; i++) {
uint8_t ftfx_fstat;
/* set command and sector address */
result = kinetis_ftfx_command(bank, FTFx_CMD_SECTERASE, bank->base + bank->sectors[i].offset,
0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
if (result != ERROR_OK) {
LOG_WARNING("erase sector %d failed", i);
return ERROR_FLASH_OPERATION_FAILED;
}
bank->sectors[i].is_erased = 1;
}
if (first == 0) {
LOG_WARNING
("flash configuration field erased, please reset the device");
}
return ERROR_OK;
}
static int kinetis_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
unsigned int i, result, fallback = 0;
uint8_t buf[8];
uint32_t wc;
struct kinetis_flash_bank *kinfo = bank->driver_priv;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (kinfo->flash_class == FC_FLEX_NVM) {
uint8_t ftfx_fstat;
LOG_DEBUG("flash write into FlexNVM @%08X", offset);
/* make flex ram available */
result = kinetis_ftfx_command(bank, FTFx_CMD_SETFLEXRAM, 0x00ff0000, 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
if (result != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
/* check if ram ready */
result = target_read_memory(bank->target, FTFx_FCNFG, 1, 1, buf);
if (result != ERROR_OK)
return result;
if (!(buf[0] & (1 << 1))) {
/* fallback to longword write */
fallback = 1;
LOG_WARNING("ram not ready, fallback to slow longword write (FCNFG: %02X)",
buf[0]);
}
} else {
LOG_DEBUG("flash write into PFLASH @08%X", offset);
}
/* program section command */
if (fallback == 0) {
/*
* Kinetis uses different terms for the granularity of
* sector writes, e.g. "phrase" or "128 bits". We use
* the generic term "chunk". The largest possible
* Kinetis "chunk" is 16 bytes (128 bits).
*/
unsigned prog_section_chunk_bytes = kinfo->sector_size >> 8;
/* assume the NVM sector size is half the FlexRAM size */
unsigned prog_size_bytes = MIN(kinfo->sector_size,
kinetis_flash_params[kinfo->granularity].nvm_sector_size_bytes);
for (i = 0; i < count; i += prog_size_bytes) {
uint8_t residual_buffer[16];
uint8_t ftfx_fstat;
uint32_t section_count = prog_size_bytes / prog_section_chunk_bytes;
uint32_t residual_wc = 0;
/*
* Assume the word count covers an entire
* sector.
*/
wc = prog_size_bytes / 4;
/*
* If bytes to be programmed are less than the
* full sector, then determine the number of
* full-words to program, and put together the
* residual buffer so that a full "section"
* may always be programmed.
*/
if ((count - i) < prog_size_bytes) {
/* number of bytes to program beyond full section */
unsigned residual_bc = (count-i) % prog_section_chunk_bytes;
/* number of complete words to copy directly from buffer */
wc = (count - i) / 4;
/* number of total sections to write, including residual */
section_count = DIV_ROUND_UP((count-i), prog_section_chunk_bytes);
/* any residual bytes delivers a whole residual section */
residual_wc = (residual_bc ? prog_section_chunk_bytes : 0)/4;
/* clear residual buffer then populate residual bytes */
(void) memset(residual_buffer, 0xff, prog_section_chunk_bytes);
(void) memcpy(residual_buffer, &buffer[i+4*wc], residual_bc);
}
LOG_DEBUG("write section @ %08X with length %d bytes",
offset + i, wc*4);
/* write data to flexram as whole-words */
result = target_write_memory(bank->target, FLEXRAM, 4, wc,
buffer + i);
if (result != ERROR_OK) {
LOG_ERROR("target_write_memory failed");
return result;
}
/* write the residual words to the flexram */
if (residual_wc) {
result = target_write_memory(bank->target,
FLEXRAM+4*wc,
4, residual_wc,
residual_buffer);
if (result != ERROR_OK) {
LOG_ERROR("target_write_memory failed");
return result;
}
}
/* execute section-write command */
result = kinetis_ftfx_command(bank, FTFx_CMD_SECTWRITE, bank->base + offset + i,
section_count>>8, section_count, 0, 0,
0, 0, 0, 0, &ftfx_fstat);
if (result != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
}
}
/* program longword command, not supported in "SF3" devices */
else if (kinfo->granularity != 3) {
for (i = 0; i < count; i += 4) {
uint8_t ftfx_fstat;
LOG_DEBUG("write longword @ %08X", offset + i);
uint8_t padding[4] = {0xff, 0xff, 0xff, 0xff};
memcpy(padding, buffer + i, MIN(4, count-i));
result = kinetis_ftfx_command(bank, FTFx_CMD_LWORDPROG, bank->base + offset + i,
padding[3], padding[2], padding[1], padding[0],
0, 0, 0, 0, &ftfx_fstat);
if (result != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
}
} else {
LOG_ERROR("Flash write strategy not implemented");
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int kinetis_read_part_info(struct flash_bank *bank)
{
int result, i;
uint8_t buf[4];
uint32_t offset = 0;
uint8_t fcfg1_nvmsize, fcfg1_pfsize, fcfg1_eesize, fcfg2_pflsh;
uint32_t nvm_size = 0, pf_size = 0, ee_size = 0;
unsigned granularity, num_blocks = 0, num_pflash_blocks = 0, num_nvm_blocks = 0,
first_nvm_bank = 0, reassign = 0;
struct kinetis_flash_bank *kinfo = bank->driver_priv;
result = target_read_memory(bank->target, SIM_SDID, 1, 4, buf);
if (result != ERROR_OK)
return result;
kinfo->sim_sdid = target_buffer_get_u32(bank->target, buf);
granularity = (kinfo->sim_sdid >> 7) & 0x03;
result = target_read_memory(bank->target, SIM_FCFG1, 1, 4, buf);
if (result != ERROR_OK)
return result;
kinfo->sim_fcfg1 = target_buffer_get_u32(bank->target, buf);
result = target_read_memory(bank->target, SIM_FCFG2, 1, 4, buf);
if (result != ERROR_OK)
return result;
kinfo->sim_fcfg2 = target_buffer_get_u32(bank->target, buf);
fcfg2_pflsh = (kinfo->sim_fcfg2 >> 23) & 0x01;
LOG_DEBUG("SDID: %08X FCFG1: %08X FCFG2: %08X", kinfo->sim_sdid,
kinfo->sim_fcfg1, kinfo->sim_fcfg2);
fcfg1_nvmsize = (uint8_t)((kinfo->sim_fcfg1 >> 28) & 0x0f);
fcfg1_pfsize = (uint8_t)((kinfo->sim_fcfg1 >> 24) & 0x0f);
fcfg1_eesize = (uint8_t)((kinfo->sim_fcfg1 >> 16) & 0x0f);
/* when the PFLSH bit is set, there is no FlexNVM/FlexRAM */
if (!fcfg2_pflsh) {
switch (fcfg1_nvmsize) {
case 0x03:
case 0x07:
case 0x09:
case 0x0b:
nvm_size = 1 << (14 + (fcfg1_nvmsize >> 1));
break;
case 0x0f:
if (granularity == 3)
nvm_size = 512<<10;
else
nvm_size = 256<<10;
break;
default:
nvm_size = 0;
break;
}
switch (fcfg1_eesize) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
ee_size = (16 << (10 - fcfg1_eesize));
break;
default:
ee_size = 0;
break;
}
}
switch (fcfg1_pfsize) {
case 0x03:
case 0x05:
case 0x07:
case 0x09:
case 0x0b:
case 0x0d:
pf_size = 1 << (14 + (fcfg1_pfsize >> 1));
break;
case 0x0f:
if (granularity == 3)
pf_size = 1024<<10;
else if (fcfg2_pflsh)
pf_size = 512<<10;
else
pf_size = 256<<10;
break;
default:
pf_size = 0;
break;
}
LOG_DEBUG("FlexNVM: %d PFlash: %d FlexRAM: %d PFLSH: %d",
nvm_size, pf_size, ee_size, fcfg2_pflsh);
num_blocks = kinetis_flash_params[granularity].num_blocks;
num_pflash_blocks = num_blocks / (2 - fcfg2_pflsh);
first_nvm_bank = num_pflash_blocks;
num_nvm_blocks = num_blocks - num_pflash_blocks;
LOG_DEBUG("%d blocks total: %d PFlash, %d FlexNVM",
num_blocks, num_pflash_blocks, num_nvm_blocks);
/*
* If the flash class is already assigned, verify the
* parameters.
*/
if (kinfo->flash_class != FC_AUTO) {
if (kinfo->bank_ordinal != (unsigned) bank->bank_number) {
LOG_WARNING("Flash ordinal/bank number mismatch");
reassign = 1;
} else if (kinfo->granularity != granularity) {
LOG_WARNING("Flash granularity mismatch");
reassign = 1;
} else {
switch (kinfo->flash_class) {
case FC_PFLASH:
if (kinfo->bank_ordinal >= first_nvm_bank) {
LOG_WARNING("Class mismatch, bank %d is not PFlash",
bank->bank_number);
reassign = 1;
} else if (bank->size != (pf_size / num_pflash_blocks)) {
LOG_WARNING("PFlash size mismatch");
reassign = 1;
} else if (bank->base !=
(0x00000000 + bank->size * kinfo->bank_ordinal)) {
LOG_WARNING("PFlash address range mismatch");
reassign = 1;
} else if (kinfo->sector_size !=
kinetis_flash_params[granularity].pflash_sector_size_bytes) {
LOG_WARNING("PFlash sector size mismatch");
reassign = 1;
} else {
LOG_DEBUG("PFlash bank %d already configured okay",
kinfo->bank_ordinal);
}
break;
case FC_FLEX_NVM:
if ((kinfo->bank_ordinal >= num_blocks) ||
(kinfo->bank_ordinal < first_nvm_bank)) {
LOG_WARNING("Class mismatch, bank %d is not FlexNVM",
bank->bank_number);
reassign = 1;
} else if (bank->size != (nvm_size / num_nvm_blocks)) {
LOG_WARNING("FlexNVM size mismatch");
reassign = 1;
} else if (bank->base !=
(0x10000000 + bank->size * kinfo->bank_ordinal)) {
LOG_WARNING("FlexNVM address range mismatch");
reassign = 1;
} else if (kinfo->sector_size !=
kinetis_flash_params[granularity].nvm_sector_size_bytes) {
LOG_WARNING("FlexNVM sector size mismatch");
reassign = 1;
} else {
LOG_DEBUG("FlexNVM bank %d already configured okay",
kinfo->bank_ordinal);
}
break;
case FC_FLEX_RAM:
if (kinfo->bank_ordinal != num_blocks) {
LOG_WARNING("Class mismatch, bank %d is not FlexRAM",
bank->bank_number);
reassign = 1;
} else if (bank->size != ee_size) {
LOG_WARNING("FlexRAM size mismatch");
reassign = 1;
} else if (bank->base != FLEXRAM) {
LOG_WARNING("FlexRAM address mismatch");
reassign = 1;
} else if (kinfo->sector_size !=
kinetis_flash_params[granularity].nvm_sector_size_bytes) {
LOG_WARNING("FlexRAM sector size mismatch");
reassign = 1;
} else {
LOG_DEBUG("FlexRAM bank %d already configured okay",
kinfo->bank_ordinal);
}
break;
default:
LOG_WARNING("Unknown or inconsistent flash class");
reassign = 1;
break;
}
}
} else {
LOG_INFO("Probing flash info for bank %d", bank->bank_number);
reassign = 1;
}
if (!reassign)
return ERROR_OK;
kinfo->granularity = granularity;
if ((unsigned)bank->bank_number < num_pflash_blocks) {
/* pflash, banks start at address zero */
kinfo->flash_class = FC_PFLASH;
bank->size = (pf_size / num_pflash_blocks);
bank->base = 0x00000000 + bank->size * bank->bank_number;
kinfo->sector_size = kinetis_flash_params[granularity].pflash_sector_size_bytes;
kinfo->protection_size = pf_size / 32;
} else if ((unsigned)bank->bank_number < num_blocks) {
/* nvm, banks start at address 0x10000000 */
kinfo->flash_class = FC_FLEX_NVM;
bank->size = (nvm_size / num_nvm_blocks);
bank->base = 0x10000000 + bank->size * (bank->bank_number - first_nvm_bank);
kinfo->sector_size = kinetis_flash_params[granularity].nvm_sector_size_bytes;
kinfo->protection_size = 0; /* FIXME: TODO: depends on DEPART bits, chip */
} else if ((unsigned)bank->bank_number == num_blocks) {
LOG_ERROR("FlexRAM support not yet implemented");
return ERROR_FLASH_OPER_UNSUPPORTED;
} else {
LOG_ERROR("Cannot determine parameters for bank %d, only %d banks on device",
bank->bank_number, num_blocks);
return ERROR_FLASH_BANK_INVALID;
}
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
bank->num_sectors = bank->size / kinfo->sector_size;
assert(bank->num_sectors > 0);
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
for (i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = kinfo->sector_size;
offset += kinfo->sector_size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
return ERROR_OK;
}
static int kinetis_probe(struct flash_bank *bank)
{
if (bank->target->state != TARGET_HALTED) {
LOG_WARNING("Cannot communicate... target not halted.");
return ERROR_TARGET_NOT_HALTED;
}
return kinetis_read_part_info(bank);
}
static int kinetis_auto_probe(struct flash_bank *bank)
{
struct kinetis_flash_bank *kinfo = bank->driver_priv;
if (kinfo->sim_sdid)
return ERROR_OK;
return kinetis_probe(bank);
}
static int kinetis_info(struct flash_bank *bank, char *buf, int buf_size)
{
const char *bank_class_names[] = {
"(ANY)", "PFlash", "FlexNVM", "FlexRAM"
};
struct kinetis_flash_bank *kinfo = bank->driver_priv;
(void) snprintf(buf, buf_size,
"%s driver for %s flash bank %s at 0x%8.8" PRIx32 "",
bank->driver->name, bank_class_names[kinfo->flash_class],
bank->name, bank->base);
return ERROR_OK;
}
static int kinetis_blank_check(struct flash_bank *bank)
{
struct kinetis_flash_bank *kinfo = bank->driver_priv;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (kinfo->flash_class == FC_PFLASH) {
int result;
uint8_t ftfx_fstat;
/* check if whole bank is blank */
result = kinetis_ftfx_command(bank, FTFx_CMD_BLOCKSTAT, bank->base, 0, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
if (result != ERROR_OK)
return result;
if (ftfx_fstat & 0x01) {
/* the whole bank is not erased, check sector-by-sector */
int i;
for (i = 0; i < bank->num_sectors; i++) {
/* normal margin */
result = kinetis_ftfx_command(bank, FTFx_CMD_SECTSTAT, bank->base + bank->sectors[i].offset,
1, 0, 0, 0, 0, 0, 0, 0, &ftfx_fstat);
if (result == ERROR_OK) {
bank->sectors[i].is_erased = !(ftfx_fstat & 0x01);
} else {
LOG_DEBUG("Ignoring errored PFlash sector blank-check");
bank->sectors[i].is_erased = -1;
}
}
} else {
/* the whole bank is erased, update all sectors */
int i;
for (i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
}
} else {
LOG_WARNING("kinetis_blank_check not supported yet for FlexNVM");
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int kinetis_flash_read(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
LOG_WARNING("kinetis_flash_read not supported yet");
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
return ERROR_FLASH_OPERATION_FAILED;
}
struct flash_driver kinetis_flash = {
.name = "kinetis",
.flash_bank_command = kinetis_flash_bank_command,
.erase = kinetis_erase,
.protect = kinetis_protect,
.write = kinetis_write,
.read = kinetis_flash_read,
.probe = kinetis_probe,
.auto_probe = kinetis_auto_probe,
.erase_check = kinetis_blank_check,
.protect_check = kinetis_protect_check,
.info = kinetis_info,
};
+922
View File
@@ -0,0 +1,922 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* LPC1700 support Copyright (C) 2009 by Audrius Urmanavicius *
* didele.deze@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/arm_opcodes.h>
#include <target/armv7m.h>
/**
* @file
* flash programming support for NXP LPC17xx and LPC2xxx devices.
*
* @todo Provide a way to update CCLK after declaring the flash bank. The value which is correct after chip reset will
* rarely still work right after the clocks switch to use the PLL (e.g. 4MHz --> 100 MHz).
*/
/*
* currently supported devices:
* variant 1 (lpc2000_v1):
* - 2104 | 5 | 6
* - 2114 | 9
* - 2124 | 9
* - 2194
* - 2212 | 4
* - 2292 | 4
*
* variant 2 (lpc2000_v2):
* - 213x
* - 214x
* - 2101 | 2 | 3
* - 2364 | 6 | 8
* - 2378
*
* lpc1700:
* - 175x
* - 176x (tested with LPC1768)
*
* lpc4300 (also available as lpc1800 - alias)
* - 43x2 | 3 | 5 | 7 (tested with 4337)
* - 18x2 | 3 | 5 | 7
*/
typedef enum {
lpc2000_v1,
lpc2000_v2,
lpc1700,
lpc4300,
} lpc2000_variant;
struct lpc2000_flash_bank {
lpc2000_variant variant;
uint32_t cclk;
int cmd51_dst_boundary;
int cmd51_can_256b;
int cmd51_can_8192b;
int calc_checksum;
uint32_t cmd51_max_buffer;
int checksum_vector;
uint32_t iap_max_stack;
uint32_t cmd51_src_offset;
uint32_t lpc4300_bank;
};
enum lpc2000_status_codes {
LPC2000_CMD_SUCCESS = 0,
LPC2000_INVALID_COMMAND = 1,
LPC2000_SRC_ADDR_ERROR = 2,
LPC2000_DST_ADDR_ERROR = 3,
LPC2000_SRC_ADDR_NOT_MAPPED = 4,
LPC2000_DST_ADDR_NOT_MAPPED = 5,
LPC2000_COUNT_ERROR = 6,
LPC2000_INVALID_SECTOR = 7,
LPC2000_SECTOR_NOT_BLANK = 8,
LPC2000_SECTOR_NOT_PREPARED = 9,
LPC2000_COMPARE_ERROR = 10,
LPC2000_BUSY = 11,
LPC2000_PARAM_ERROR = 12,
LPC2000_ADDR_ERROR = 13,
LPC2000_ADDR_NOT_MAPPED = 14,
LPC2000_CMD_NOT_LOCKED = 15,
LPC2000_INVALID_CODE = 16,
LPC2000_INVALID_BAUD_RATE = 17,
LPC2000_INVALID_STOP_BIT = 18,
LPC2000_CRP_ENABLED = 19,
LPC2000_INVALID_FLASH_UNIT = 20,
LPC2000_USER_CODE_CHECKSUM = 21,
LCP2000_ERROR_SETTING_ACTIVE_PARTITION = 22,
};
static int lpc2000_build_sector_list(struct flash_bank *bank)
{
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
uint32_t offset = 0;
/* default to a 4096 write buffer */
lpc2000_info->cmd51_max_buffer = 4096;
if (lpc2000_info->variant == lpc2000_v1) {
/* variant 1 has different layout for 128kb and 256kb flashes */
if (bank->size == 128 * 1024) {
bank->num_sectors = 16;
bank->sectors = malloc(sizeof(struct flash_sector) * 16);
for (int i = 0; i < 16; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 8 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
} else if (bank->size == 256 * 1024) {
bank->num_sectors = 18;
bank->sectors = malloc(sizeof(struct flash_sector) * 18);
for (int i = 0; i < 8; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 8 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
for (int i = 8; i < 10; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 64 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
for (int i = 10; i < 18; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 8 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
} else {
LOG_ERROR("BUG: unknown bank->size encountered");
exit(-1);
}
} else if (lpc2000_info->variant == lpc2000_v2) {
/* variant 2 has a uniform layout, only number of sectors differs */
switch (bank->size) {
case 4 * 1024:
lpc2000_info->cmd51_max_buffer = 1024;
bank->num_sectors = 1;
break;
case 8 * 1024:
lpc2000_info->cmd51_max_buffer = 1024;
bank->num_sectors = 2;
break;
case 16 * 1024:
bank->num_sectors = 4;
break;
case 32 * 1024:
bank->num_sectors = 8;
break;
case 64 * 1024:
bank->num_sectors = 9;
break;
case 128 * 1024:
bank->num_sectors = 11;
break;
case 256 * 1024:
bank->num_sectors = 15;
break;
case 500 * 1024:
bank->num_sectors = 27;
break;
case 512 * 1024:
case 504 * 1024:
bank->num_sectors = 28;
break;
default:
LOG_ERROR("BUG: unknown bank->size encountered");
exit(-1);
break;
}
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
for (int i = 0; i < bank->num_sectors; i++) {
if (i < 8) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 4 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
} else if (i < 22) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 32 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
} else if (i < 28) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 4 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
}
} else if (lpc2000_info->variant == lpc1700) {
switch (bank->size) {
case 32 * 1024:
bank->num_sectors = 8;
break;
case 64 * 1024:
bank->num_sectors = 16;
break;
case 128 * 1024:
bank->num_sectors = 18;
break;
case 256 * 1024:
bank->num_sectors = 22;
break;
case 512 * 1024:
bank->num_sectors = 30;
break;
default:
LOG_ERROR("BUG: unknown bank->size encountered");
exit(-1);
}
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
for (int i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = offset;
/* sectors 0-15 are 4kB-sized, 16 and above are 32kB-sized for LPC17xx devices */
bank->sectors[i].size = (i < 16) ? 4 * 1024 : 32 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
} else if (lpc2000_info->variant == lpc4300) {
switch (bank->size) {
case 256 * 1024:
bank->num_sectors = 11;
break;
case 384 * 1024:
bank->num_sectors = 13;
break;
case 512 * 1024:
bank->num_sectors = 15;
break;
default:
LOG_ERROR("BUG: unknown bank->size encountered");
exit(-1);
}
bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
for (int i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = offset;
/* sectors 0-7 are 8kB-sized, 8 and above are 64kB-sized for LPC43xx devices */
bank->sectors[i].size = (i < 8) ? 8 * 1024 : 64 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
} else {
LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
exit(-1);
}
return ERROR_OK;
}
/* this function allocates and initializes working area used for IAP algorithm
* uses 52 + max IAP stack bytes working area
* 0x0 to 0x7: jump gate (BX to thumb state, b -2 to wait)
* 0x8 to 0x1f: command parameter table (1+5 words)
* 0x20 to 0x33: command result table (1+4 words)
* 0x34 to 0xb3|0x104: stack (only 128b needed for lpc17xx/2000, 208 for lpc43xx)
*/
static int lpc2000_iap_working_area_init(struct flash_bank *bank, struct working_area **iap_working_area)
{
struct target *target = bank->target;
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
if (target_alloc_working_area(target, 0x34 + lpc2000_info->iap_max_stack, iap_working_area) != ERROR_OK) {
LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
return ERROR_FLASH_OPERATION_FAILED;
}
uint8_t jump_gate[8];
/* write IAP code to working area */
switch (lpc2000_info->variant) {
case lpc1700:
case lpc4300:
target_buffer_set_u32(target, jump_gate, ARMV4_5_T_BX(12));
target_buffer_set_u32(target, jump_gate + 4, ARMV5_T_BKPT(0));
break;
case lpc2000_v1:
case lpc2000_v2:
target_buffer_set_u32(target, jump_gate, ARMV4_5_BX(12));
target_buffer_set_u32(target, jump_gate + 4, ARMV4_5_B(0xfffffe, 0));
break;
default:
LOG_ERROR("BUG: unknown lpc2000_info->variant encountered");
exit(-1);
}
int retval = target_write_memory(target, (*iap_working_area)->address, 4, 2, jump_gate);
if (retval != ERROR_OK)
LOG_ERROR("Write memory at address 0x%8.8" PRIx32 " failed (check work_area definition)",
(*iap_working_area)->address);
return retval;
}
/* call LPC1700/LPC2000 IAP function */
static int lpc2000_iap_call(struct flash_bank *bank, struct working_area *iap_working_area, int code,
uint32_t param_table[5], uint32_t result_table[4])
{
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
struct target *target = bank->target;
struct arm_algorithm arm_algo; /* for LPC2000 */
struct armv7m_algorithm armv7m_info; /* for LPC1700 */
uint32_t iap_entry_point = 0; /* to make compiler happier */
switch (lpc2000_info->variant) {
case lpc1700:
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
iap_entry_point = 0x1fff1ff1;
break;
case lpc2000_v1:
case lpc2000_v2:
arm_algo.common_magic = ARM_COMMON_MAGIC;
arm_algo.core_mode = ARM_MODE_SVC;
arm_algo.core_state = ARM_STATE_ARM;
iap_entry_point = 0x7ffffff1;
break;
case lpc4300:
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
/* read out IAP entry point from ROM driver table at 0x10400100 */
target_read_u32(target, 0x10400100, &iap_entry_point);
break;
default:
LOG_ERROR("BUG: unknown lpc2000->variant encountered");
exit(-1);
}
struct mem_param mem_params[2];
/* command parameter table */
init_mem_param(&mem_params[0], iap_working_area->address + 8, 6 * 4, PARAM_OUT);
target_buffer_set_u32(target, mem_params[0].value, code);
target_buffer_set_u32(target, mem_params[0].value + 0x04, param_table[0]);
target_buffer_set_u32(target, mem_params[0].value + 0x08, param_table[1]);
target_buffer_set_u32(target, mem_params[0].value + 0x0c, param_table[2]);
target_buffer_set_u32(target, mem_params[0].value + 0x10, param_table[3]);
target_buffer_set_u32(target, mem_params[0].value + 0x14, param_table[4]);
struct reg_param reg_params[5];
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
buf_set_u32(reg_params[0].value, 0, 32, iap_working_area->address + 0x08);
/* command result table */
init_mem_param(&mem_params[1], iap_working_area->address + 0x20, 5 * 4, PARAM_IN);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
buf_set_u32(reg_params[1].value, 0, 32, iap_working_area->address + 0x20);
/* IAP entry point */
init_reg_param(&reg_params[2], "r12", 32, PARAM_OUT);
buf_set_u32(reg_params[2].value, 0, 32, iap_entry_point);
switch (lpc2000_info->variant) {
case lpc1700:
case lpc4300:
/* IAP stack */
init_reg_param(&reg_params[3], "sp", 32, PARAM_OUT);
buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + lpc2000_info->cmd51_src_offset);
/* return address */
init_reg_param(&reg_params[4], "lr", 32, PARAM_OUT);
buf_set_u32(reg_params[4].value, 0, 32, (iap_working_area->address + 0x04) | 1);
/* bit0 of LR = 1 to return in Thumb mode */
target_run_algorithm(target, 2, mem_params, 5, reg_params, iap_working_area->address, 0, 10000,
&armv7m_info);
break;
case lpc2000_v1:
case lpc2000_v2:
/* IAP stack */
init_reg_param(&reg_params[3], "sp_svc", 32, PARAM_OUT);
buf_set_u32(reg_params[3].value, 0, 32, iap_working_area->address + lpc2000_info->cmd51_src_offset);
/* return address */
init_reg_param(&reg_params[4], "lr_svc", 32, PARAM_OUT);
buf_set_u32(reg_params[4].value, 0, 32, iap_working_area->address + 0x04);
target_run_algorithm(target, 2, mem_params, 5, reg_params, iap_working_area->address,
iap_working_area->address + 0x4, 10000, &arm_algo);
break;
default:
LOG_ERROR("BUG: unknown lpc2000->variant encountered");
exit(-1);
}
int status_code = target_buffer_get_u32(target, mem_params[1].value);
result_table[0] = target_buffer_get_u32(target, mem_params[1].value + 0x04);
result_table[1] = target_buffer_get_u32(target, mem_params[1].value + 0x08);
result_table[2] = target_buffer_get_u32(target, mem_params[1].value + 0x0c);
result_table[3] = target_buffer_get_u32(target, mem_params[1].value + 0x10);
LOG_DEBUG("IAP command = %i (0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32 ", 0x%8.8" PRIx32
") completed with result = %8.8" PRIx32,
code, param_table[0], param_table[1], param_table[2], param_table[3], param_table[4], status_code);
destroy_mem_param(&mem_params[0]);
destroy_mem_param(&mem_params[1]);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
return status_code;
}
static int lpc2000_iap_blank_check(struct flash_bank *bank, int first, int last)
{
if ((first < 0) || (last >= bank->num_sectors))
return ERROR_FLASH_SECTOR_INVALID;
uint32_t param_table[5] = {0};
uint32_t result_table[4];
struct working_area *iap_working_area;
int retval = lpc2000_iap_working_area_init(bank, &iap_working_area);
if (retval != ERROR_OK)
return retval;
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
if (lpc2000_info->variant == lpc4300)
param_table[2] = lpc2000_info->lpc4300_bank;
for (int i = first; i <= last && retval == ERROR_OK; i++) {
/* check single sector */
param_table[0] = param_table[1] = i;
int status_code = lpc2000_iap_call(bank, iap_working_area, 53, param_table, result_table);
switch (status_code) {
case ERROR_FLASH_OPERATION_FAILED:
retval = ERROR_FLASH_OPERATION_FAILED;
break;
case LPC2000_CMD_SUCCESS:
bank->sectors[i].is_erased = 1;
break;
case LPC2000_SECTOR_NOT_BLANK:
bank->sectors[i].is_erased = 0;
break;
case LPC2000_INVALID_SECTOR:
bank->sectors[i].is_erased = 0;
break;
case LPC2000_BUSY:
retval = ERROR_FLASH_BUSY;
break;
default:
LOG_ERROR("BUG: unknown LPC2000 status code %i", status_code);
exit(-1);
}
}
struct target *target = bank->target;
target_free_working_area(target, iap_working_area);
return retval;
}
/*
* flash bank lpc2000 <base> <size> 0 0 <target#> <lpc_variant> <cclk> [calc_checksum]
*/
FLASH_BANK_COMMAND_HANDLER(lpc2000_flash_bank_command)
{
if (CMD_ARGC < 8)
return ERROR_COMMAND_SYNTAX_ERROR;
struct lpc2000_flash_bank *lpc2000_info = malloc(sizeof(struct lpc2000_flash_bank));
bank->driver_priv = lpc2000_info;
if (strcmp(CMD_ARGV[6], "lpc2000_v1") == 0) {
lpc2000_info->variant = lpc2000_v1;
lpc2000_info->cmd51_dst_boundary = 512;
lpc2000_info->cmd51_can_256b = 0;
lpc2000_info->cmd51_can_8192b = 1;
lpc2000_info->checksum_vector = 5;
lpc2000_info->iap_max_stack = 128;
} else if (strcmp(CMD_ARGV[6], "lpc2000_v2") == 0) {
lpc2000_info->variant = lpc2000_v2;
lpc2000_info->cmd51_dst_boundary = 256;
lpc2000_info->cmd51_can_256b = 1;
lpc2000_info->cmd51_can_8192b = 0;
lpc2000_info->checksum_vector = 5;
lpc2000_info->iap_max_stack = 128;
} else if (strcmp(CMD_ARGV[6], "lpc1700") == 0) {
lpc2000_info->variant = lpc1700;
lpc2000_info->cmd51_dst_boundary = 256;
lpc2000_info->cmd51_can_256b = 1;
lpc2000_info->cmd51_can_8192b = 0;
lpc2000_info->checksum_vector = 7;
lpc2000_info->iap_max_stack = 128;
} else if (strcmp(CMD_ARGV[6], "lpc1800") == 0 || strcmp(CMD_ARGV[6], "lpc4300") == 0) {
lpc2000_info->variant = lpc4300;
lpc2000_info->cmd51_dst_boundary = 512;
lpc2000_info->cmd51_can_256b = 0;
lpc2000_info->cmd51_can_8192b = 0;
lpc2000_info->checksum_vector = 7;
lpc2000_info->iap_max_stack = 208;
} else {
LOG_ERROR("unknown LPC2000 variant: %s", CMD_ARGV[6]);
free(lpc2000_info);
return ERROR_FLASH_BANK_INVALID;
}
/* see lpc2000_iap_working_area_init() for the reason behind the 0x34 value */
lpc2000_info->cmd51_src_offset = 0x34 + lpc2000_info->iap_max_stack;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], lpc2000_info->cclk);
lpc2000_info->calc_checksum = 0;
lpc2000_build_sector_list(bank);
uint32_t temp_base = 0;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], temp_base);
if (temp_base >= 0x1B000000)
lpc2000_info->lpc4300_bank = 1; /* bank B */
else
lpc2000_info->lpc4300_bank = 0; /* bank A */
if (CMD_ARGC >= 9) {
if (strcmp(CMD_ARGV[8], "calc_checksum") == 0)
lpc2000_info->calc_checksum = 1;
}
return ERROR_OK;
}
static int lpc2000_erase(struct flash_bank *bank, int first, int last)
{
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
uint32_t param_table[5] = {0};
param_table[0] = first;
param_table[1] = last;
if (lpc2000_info->variant == lpc4300)
param_table[2] = lpc2000_info->lpc4300_bank;
else
param_table[2] = lpc2000_info->cclk;
uint32_t result_table[4];
struct working_area *iap_working_area;
int retval = lpc2000_iap_working_area_init(bank, &iap_working_area);
if (retval != ERROR_OK)
return retval;
/* Prepare sectors */
int status_code = lpc2000_iap_call(bank, iap_working_area, 50, param_table, result_table);
switch (status_code) {
case ERROR_FLASH_OPERATION_FAILED:
retval = ERROR_FLASH_OPERATION_FAILED;
break;
case LPC2000_CMD_SUCCESS:
break;
case LPC2000_INVALID_SECTOR:
retval = ERROR_FLASH_SECTOR_INVALID;
break;
default:
LOG_WARNING("lpc2000 prepare sectors returned %i", status_code);
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
if (retval == ERROR_OK) {
/* Erase sectors */
param_table[2] = lpc2000_info->cclk;
if (lpc2000_info->variant == lpc4300)
param_table[3] = lpc2000_info->lpc4300_bank;
status_code = lpc2000_iap_call(bank, iap_working_area, 52, param_table, result_table);
switch (status_code) {
case ERROR_FLASH_OPERATION_FAILED:
retval = ERROR_FLASH_OPERATION_FAILED;
break;
case LPC2000_CMD_SUCCESS:
break;
case LPC2000_INVALID_SECTOR:
retval = ERROR_FLASH_SECTOR_INVALID;
break;
default:
LOG_WARNING("lpc2000 erase sectors returned %i", status_code);
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
}
struct target *target = bank->target;
target_free_working_area(target, iap_working_area);
return retval;
}
static int lpc2000_protect(struct flash_bank *bank, int set, int first, int last)
{
/* can't protect/unprotect on the lpc2000 */
return ERROR_OK;
}
static int lpc2000_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset + count > bank->size)
return ERROR_FLASH_DST_OUT_OF_BANK;
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
uint32_t dst_min_alignment = lpc2000_info->cmd51_dst_boundary;
if (offset % dst_min_alignment) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32, offset, dst_min_alignment);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
int first_sector = 0;
int last_sector = 0;
for (int i = 0; i < bank->num_sectors; i++) {
if (offset >= bank->sectors[i].offset)
first_sector = i;
if (offset + DIV_ROUND_UP(count, dst_min_alignment) * dst_min_alignment > bank->sectors[i].offset)
last_sector = i;
}
LOG_DEBUG("first_sector: %i, last_sector: %i", first_sector, last_sector);
/* check if exception vectors should be flashed */
if ((offset == 0) && (count >= 0x20) && lpc2000_info->calc_checksum) {
uint32_t checksum = 0;
for (int i = 0; i < 8; i++) {
LOG_DEBUG("Vector 0x%2.2x: 0x%8.8" PRIx32, i * 4, buf_get_u32(buffer + (i * 4), 0, 32));
if (i != lpc2000_info->checksum_vector)
checksum += buf_get_u32(buffer + (i * 4), 0, 32);
}
checksum = 0 - checksum;
LOG_DEBUG("checksum: 0x%8.8" PRIx32, checksum);
uint32_t original_value = buf_get_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32);
if (original_value != checksum) {
LOG_WARNING("Verification will fail since checksum in image (0x%8.8" PRIx32 ") to be written to flash is "
"different from calculated vector checksum (0x%8.8" PRIx32 ").", original_value, checksum);
LOG_WARNING("To remove this warning modify build tools on developer PC to inject correct LPC vector "
"checksum.");
}
buf_set_u32(buffer + (lpc2000_info->checksum_vector * 4), 0, 32, checksum);
}
struct working_area *iap_working_area;
int retval = lpc2000_iap_working_area_init(bank, &iap_working_area);
if (retval != ERROR_OK)
return retval;
struct working_area *download_area;
/* allocate a working area */
if (target_alloc_working_area(target, lpc2000_info->cmd51_max_buffer, &download_area) != ERROR_OK) {
LOG_ERROR("no working area specified, can't write LPC2000 internal flash");
target_free_working_area(target, iap_working_area);
return ERROR_FLASH_OPERATION_FAILED;
}
uint32_t bytes_remaining = count;
uint32_t bytes_written = 0;
uint32_t param_table[5] = {0};
uint32_t result_table[4];
while (bytes_remaining > 0) {
uint32_t thisrun_bytes;
if (bytes_remaining >= lpc2000_info->cmd51_max_buffer)
thisrun_bytes = lpc2000_info->cmd51_max_buffer;
else if (bytes_remaining >= 1024)
thisrun_bytes = 1024;
else if ((bytes_remaining >= 512) || (!lpc2000_info->cmd51_can_256b))
thisrun_bytes = 512;
else
thisrun_bytes = 256;
/* Prepare sectors */
param_table[0] = first_sector;
param_table[1] = last_sector;
if (lpc2000_info->variant == lpc4300)
param_table[2] = lpc2000_info->lpc4300_bank;
else
param_table[2] = lpc2000_info->cclk;
int status_code = lpc2000_iap_call(bank, iap_working_area, 50, param_table, result_table);
switch (status_code) {
case ERROR_FLASH_OPERATION_FAILED:
retval = ERROR_FLASH_OPERATION_FAILED;
break;
case LPC2000_CMD_SUCCESS:
break;
case LPC2000_INVALID_SECTOR:
retval = ERROR_FLASH_SECTOR_INVALID;
break;
default:
LOG_WARNING("lpc2000 prepare sectors returned %i", status_code);
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
/* Exit if error occured */
if (retval != ERROR_OK)
break;
if (bytes_remaining >= thisrun_bytes) {
retval = target_write_buffer(bank->target, download_area->address, thisrun_bytes, buffer + bytes_written);
if (retval != ERROR_OK) {
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
} else {
uint8_t *last_buffer = malloc(thisrun_bytes);
memcpy(last_buffer, buffer + bytes_written, bytes_remaining);
memset(last_buffer + bytes_remaining, 0xff, thisrun_bytes - bytes_remaining);
target_write_buffer(bank->target, download_area->address, thisrun_bytes, last_buffer);
free(last_buffer);
}
LOG_DEBUG("writing 0x%" PRIx32 " bytes to address 0x%" PRIx32, thisrun_bytes,
bank->base + offset + bytes_written);
/* Write data */
param_table[0] = bank->base + offset + bytes_written;
param_table[1] = download_area->address;
param_table[2] = thisrun_bytes;
param_table[3] = lpc2000_info->cclk;
status_code = lpc2000_iap_call(bank, iap_working_area, 51, param_table, result_table);
switch (status_code) {
case ERROR_FLASH_OPERATION_FAILED:
retval = ERROR_FLASH_OPERATION_FAILED;
break;
case LPC2000_CMD_SUCCESS:
break;
case LPC2000_INVALID_SECTOR:
retval = ERROR_FLASH_SECTOR_INVALID;
break;
default:
LOG_WARNING("lpc2000 returned %i", status_code);
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
/* Exit if error occured */
if (retval != ERROR_OK)
break;
if (bytes_remaining > thisrun_bytes)
bytes_remaining -= thisrun_bytes;
else
bytes_remaining = 0;
bytes_written += thisrun_bytes;
}
target_free_working_area(target, iap_working_area);
target_free_working_area(target, download_area);
return retval;
}
static int lpc2000_probe(struct flash_bank *bank)
{
/* we can't probe on an lpc2000 if this is an lpc2xxx, it has the configured flash */
return ERROR_OK;
}
static int lpc2000_erase_check(struct flash_bank *bank)
{
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
return lpc2000_iap_blank_check(bank, 0, bank->num_sectors - 1);
}
static int lpc2000_protect_check(struct flash_bank *bank)
{
/* sectors are always protected */
return ERROR_OK;
}
static int get_lpc2000_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct lpc2000_flash_bank *lpc2000_info = bank->driver_priv;
snprintf(buf, buf_size, "lpc2000 flash driver variant: %i, clk: %" PRIi32 "kHz", lpc2000_info->variant,
lpc2000_info->cclk);
return ERROR_OK;
}
COMMAND_HANDLER(lpc2000_handle_part_id_command)
{
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
uint32_t param_table[5] = {0};
uint32_t result_table[4];
struct working_area *iap_working_area;
retval = lpc2000_iap_working_area_init(bank, &iap_working_area);
if (retval != ERROR_OK)
return retval;
int status_code = lpc2000_iap_call(bank, iap_working_area, 54, param_table, result_table);
if (status_code != 0x0) {
if (status_code == ERROR_FLASH_OPERATION_FAILED) {
command_print(CMD_CTX, "no sufficient working area specified, can't access LPC2000 IAP interface");
} else
command_print(CMD_CTX, "lpc2000 IAP returned status code %i", status_code);
} else
command_print(CMD_CTX, "lpc2000 part id: 0x%8.8" PRIx32, result_table[0]);
return retval;
}
static const struct command_registration lpc2000_exec_command_handlers[] = {
{
.name = "part_id",
.handler = lpc2000_handle_part_id_command,
.mode = COMMAND_EXEC,
.help = "print part id of lpc2000 flash bank <num>",
.usage = "<bank>",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration lpc2000_command_handlers[] = {
{
.name = "lpc2000",
.mode = COMMAND_ANY,
.help = "lpc2000 flash command group",
.usage = "",
.chain = lpc2000_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver lpc2000_flash = {
.name = "lpc2000",
.commands = lpc2000_command_handlers,
.flash_bank_command = lpc2000_flash_bank_command,
.erase = lpc2000_erase,
.protect = lpc2000_protect,
.write = lpc2000_write,
.read = default_flash_read,
.probe = lpc2000_probe,
.auto_probe = lpc2000_probe,
.erase_check = lpc2000_erase_check,
.protect_check = lpc2000_protect_check,
.info = get_lpc2000_info,
};
+454
View File
@@ -0,0 +1,454 @@
/***************************************************************************
* Copyright (C) 2008 by *
* Karl RobinSod <karl.robinsod@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/***************************************************************************
* There are some things to notice
*
* You need to unprotect flash sectors each time you connect the OpenOCD
* Dumping 1MB takes about 60 Seconds
* Full erase (sectors 0-22 inclusive) takes 2-4 seconds
* Writing 1MB takes 88 seconds
*
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#define LOAD_TIMER_ERASE 0
#define LOAD_TIMER_WRITE 1
#define FLASH_PAGE_SIZE 512
/* LPC288X control registers */
#define DBGU_CIDR 0x8000507C
/* LPC288X flash registers */
#define F_CTRL 0x80102000 /* Flash control register R/W 0x5 */
#define F_STAT 0x80102004 /* Flash status register RO 0x45 */
#define F_PROG_TIME 0x80102008 /* Flash program time register R/W 0 */
#define F_WAIT 0x80102010 /* Flash read wait state register R/W 0xC004 */
#define F_CLK_TIME 0x8010201C /* Flash clock divider for 66 kHz generation R/W 0
**/
#define F_INTEN_CLR 0x80102FD8 /* Clear interrupt enable bits WO - */
#define F_INTEN_SET 0x80102FDC /* Set interrupt enable bits WO - */
#define F_INT_STAT 0x80102FE0 /* Interrupt status bits RO 0 */
#define F_INTEN 0x80102FE4 /* Interrupt enable bits RO 0 */
#define F_INT_CLR 0x80102FE8 /* Clear interrupt status bits WO */
#define F_INT_SET 0x80102FEC /* Set interrupt status bits WO - */
#define FLASH_PD 0x80005030 /* Allows turning off the Flash memory for power
*savings. R/W 1*/
#define FLASH_INIT 0x80005034 /* Monitors Flash readiness, such as recovery from
*Power Down mode. R/W -*/
/* F_CTRL bits */
#define FC_CS 0x0001
#define FC_FUNC 0x0002
#define FC_WEN 0x0004
#define FC_RD_LATCH 0x0020
#define FC_PROTECT 0x0080
#define FC_SET_DATA 0x0400
#define FC_RSSL 0x0800
#define FC_PROG_REQ 0x1000
#define FC_CLR_BUF 0x4000
#define FC_LOAD_REQ 0x8000
/* F_STAT bits */
#define FS_DONE 0x0001
#define FS_PROGGNT 0x0002
#define FS_RDY 0x0004
#define FS_ERR 0x0020
/* F_PROG_TIME */
#define FPT_TIME_MASK 0x7FFF
#define FPT_ENABLE 0x8000
/* F_WAIT */
#define FW_WAIT_STATES_MASK 0x00FF
#define FW_SET_MASK 0xC000
/* F_CLK_TIME */
#define FCT_CLK_DIV_MASK 0x0FFF
struct lpc288x_flash_bank {
uint32_t working_area;
uint32_t working_area_size;
/* chip id register */
uint32_t cidr;
const char *target_name;
uint32_t cclk;
uint32_t sector_size_break;
};
static uint32_t lpc288x_wait_status_busy(struct flash_bank *bank, int timeout);
static void lpc288x_load_timer(int erase, struct target *target);
static void lpc288x_set_flash_clk(struct flash_bank *bank);
static uint32_t lpc288x_system_ready(struct flash_bank *bank);
static uint32_t lpc288x_wait_status_busy(struct flash_bank *bank, int timeout)
{
uint32_t status;
struct target *target = bank->target;
do {
alive_sleep(1);
timeout--;
target_read_u32(target, F_STAT, &status);
} while (((status & FS_DONE) == 0) && timeout);
if (timeout == 0) {
LOG_DEBUG("Timedout!");
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
/* Read device id register and fill in driver info structure */
static int lpc288x_read_part_info(struct flash_bank *bank)
{
struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t cidr;
int i = 0;
uint32_t offset;
if (lpc288x_info->cidr == 0x0102100A)
return ERROR_OK;/* already probed, multiple probes may cause memory leak, not
*allowed */
/* Read and parse chip identification register */
target_read_u32(target, DBGU_CIDR, &cidr);
if (cidr != 0x0102100A) {
LOG_WARNING("Cannot identify target as an LPC288X (%08" PRIx32 ")", cidr);
return ERROR_FLASH_OPERATION_FAILED;
}
lpc288x_info->cidr = cidr;
lpc288x_info->sector_size_break = 0x000F0000;
lpc288x_info->target_name = "LPC288x";
/* setup the sector info... */
offset = bank->base;
bank->num_sectors = 23;
bank->sectors = malloc(sizeof(struct flash_sector) * 23);
for (i = 0; i < 15; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 64 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
for (i = 15; i < 23; i++) {
bank->sectors[i].offset = offset;
bank->sectors[i].size = 8 * 1024;
offset += bank->sectors[i].size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
return ERROR_OK;
}
static int lpc288x_protect_check(struct flash_bank *bank)
{
return ERROR_OK;
}
/* flash_bank LPC288x 0 0 0 0 <target#> <cclk> */
FLASH_BANK_COMMAND_HANDLER(lpc288x_flash_bank_command)
{
struct lpc288x_flash_bank *lpc288x_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
lpc288x_info = malloc(sizeof(struct lpc288x_flash_bank));
bank->driver_priv = lpc288x_info;
/* part wasn't probed for info yet */
lpc288x_info->cidr = 0;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], lpc288x_info->cclk);
return ERROR_OK;
}
/* The frequency is the AHB clock frequency divided by (CLK_DIV ×3) + 1.
* This must be programmed such that the Flash Programming clock frequency is 66 kHz ± 20%.
* AHB = 12 MHz ?
* 12000000/66000 = 182
* CLK_DIV = 60 ? */
static void lpc288x_set_flash_clk(struct flash_bank *bank)
{
uint32_t clk_time;
struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv;
clk_time = (lpc288x_info->cclk / 66000) / 3;
target_write_u32(bank->target, F_CTRL, FC_CS | FC_WEN);
target_write_u32(bank->target, F_CLK_TIME, clk_time);
}
/* AHB tcyc (in ns) 83 ns
* LOAD_TIMER_ERASE FPT_TIME = ((400,000,000 / AHB tcyc (in ns)) - 2) / 512
* = 9412 (9500) (AN10548 9375)
* LOAD_TIMER_WRITE FPT_TIME = ((1,000,000 / AHB tcyc (in ns)) - 2) / 512
* = 23 (75) (AN10548 72 - is this wrong?)
* TODO: Sort out timing calcs ;) */
static void lpc288x_load_timer(int erase, struct target *target)
{
if (erase == LOAD_TIMER_ERASE)
target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 9500);
else
target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 75);
}
static uint32_t lpc288x_system_ready(struct flash_bank *bank)
{
struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv;
if (lpc288x_info->cidr == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
return ERROR_OK;
}
static int lpc288x_erase_check(struct flash_bank *bank)
{
uint32_t status = lpc288x_system_ready(bank); /* probed? halted? */
if (status != ERROR_OK) {
LOG_INFO("Processor not halted/not probed");
return status;
}
return ERROR_OK;
}
static int lpc288x_erase(struct flash_bank *bank, int first, int last)
{
uint32_t status;
int sector;
struct target *target = bank->target;
status = lpc288x_system_ready(bank); /* probed? halted? */
if (status != ERROR_OK)
return status;
if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
LOG_INFO("Bad sector range");
return ERROR_FLASH_SECTOR_INVALID;
}
/* Configure the flash controller timing */
lpc288x_set_flash_clk(bank);
for (sector = first; sector <= last; sector++) {
if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
lpc288x_load_timer(LOAD_TIMER_ERASE, target);
target_write_u32(target, bank->sectors[sector].offset, 0x00);
target_write_u32(target, F_CTRL, FC_PROG_REQ | FC_PROTECT | FC_CS);
}
if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
return ERROR_OK;
}
static int lpc288x_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
uint8_t page_buffer[FLASH_PAGE_SIZE];
uint32_t status, source_offset, dest_offset;
struct target *target = bank->target;
uint32_t bytes_remaining = count;
uint32_t first_sector, last_sector, sector, page;
int i;
/* probed? halted? */
status = lpc288x_system_ready(bank);
if (status != ERROR_OK)
return status;
/* Initialise search indices */
first_sector = last_sector = 0xffffffff;
/* validate the write range... */
for (i = 0; i < bank->num_sectors; i++) {
if ((offset >= bank->sectors[i].offset) &&
(offset < (bank->sectors[i].offset + bank->sectors[i].size)) &&
(first_sector == 0xffffffff)) {
first_sector = i;
/* all writes must start on a sector boundary... */
if (offset % bank->sectors[i].size) {
LOG_INFO(
"offset 0x%" PRIx32 " breaks required alignment 0x%" PRIx32 "",
offset,
bank->sectors[i].size);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
}
if (((offset + count) > bank->sectors[i].offset) &&
((offset + count) <= (bank->sectors[i].offset + bank->sectors[i].size)) &&
(last_sector == 0xffffffff))
last_sector = i;
}
/* Range check... */
if (first_sector == 0xffffffff || last_sector == 0xffffffff) {
LOG_INFO("Range check failed %" PRIx32 " %" PRIx32 "", offset, count);
return ERROR_FLASH_DST_OUT_OF_BANK;
}
/* Configure the flash controller timing */
lpc288x_set_flash_clk(bank);
/* initialise the offsets */
source_offset = 0;
dest_offset = 0;
for (sector = first_sector; sector <= last_sector; sector++) {
for (page = 0; page < bank->sectors[sector].size / FLASH_PAGE_SIZE; page++) {
if (bytes_remaining == 0) {
count = 0;
memset(page_buffer, 0xFF, FLASH_PAGE_SIZE);
} else if (bytes_remaining < FLASH_PAGE_SIZE) {
count = bytes_remaining;
memset(page_buffer, 0xFF, FLASH_PAGE_SIZE);
memcpy(page_buffer, &buffer[source_offset], count);
} else {
count = FLASH_PAGE_SIZE;
memcpy(page_buffer, &buffer[source_offset], count);
}
/* Wait for flash to become ready */
if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK)
return ERROR_FLASH_OPERATION_FAILED;
/* fill flash data latches with 1's */
target_write_u32(target, F_CTRL, FC_CS | FC_SET_DATA | FC_WEN | FC_FUNC);
target_write_u32(target, F_CTRL, FC_CS | FC_WEN | FC_FUNC);
/*would be better to use the clean target_write_buffer() interface but
* it seems not to be a LOT slower....
* bulk_write_memory() is no quicker :(*/
#if 1
if (target_write_memory(target, offset + dest_offset, 4, 128,
page_buffer) != ERROR_OK) {
LOG_ERROR("Write failed s %" PRIx32 " p %" PRIx32 "", sector, page);
return ERROR_FLASH_OPERATION_FAILED;
}
#else
if (target_write_buffer(target, offset + dest_offset, FLASH_PAGE_SIZE,
page_buffer) != ERROR_OK) {
LOG_INFO("Write to flash buffer failed");
return ERROR_FLASH_OPERATION_FAILED;
}
#endif
dest_offset += FLASH_PAGE_SIZE;
source_offset += count;
bytes_remaining -= count;
lpc288x_load_timer(LOAD_TIMER_WRITE, target);
target_write_u32(target, F_CTRL, FC_PROG_REQ | FC_PROTECT | FC_FUNC |
FC_CS);
}
}
return ERROR_OK;
}
static int lpc288x_probe(struct flash_bank *bank)
{
/* we only deal with LPC2888 so flash config is fixed */
struct lpc288x_flash_bank *lpc288x_info = bank->driver_priv;
int retval;
if (lpc288x_info->cidr != 0)
return ERROR_OK;/* already probed */
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
retval = lpc288x_read_part_info(bank);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int lpc288x_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "lpc288x flash driver");
return ERROR_OK;
}
static int lpc288x_protect(struct flash_bank *bank, int set, int first, int last)
{
int lockregion, status;
uint32_t value;
struct target *target = bank->target;
/* probed? halted? */
status = lpc288x_system_ready(bank);
if (status != ERROR_OK)
return status;
if ((first < 0) || (last < first) || (last >= bank->num_sectors))
return ERROR_FLASH_SECTOR_INVALID;
/* Configure the flash controller timing */
lpc288x_set_flash_clk(bank);
for (lockregion = first; lockregion <= last; lockregion++) {
if (set) {
/* write an odd value to base addy to protect... */
value = 0x01;
} else {
/* write an even value to base addy to unprotect... */
value = 0x00;
}
target_write_u32(target, bank->sectors[lockregion].offset, value);
target_write_u32(target, F_CTRL, FC_LOAD_REQ | FC_PROTECT | FC_WEN | FC_FUNC |
FC_CS);
}
return ERROR_OK;
}
struct flash_driver lpc288x_flash = {
.name = "lpc288x",
.flash_bank_command = lpc288x_flash_bank_command,
.erase = lpc288x_erase,
.protect = lpc288x_protect,
.write = lpc288x_write,
.read = default_flash_read,
.probe = lpc288x_probe,
.auto_probe = lpc288x_probe,
.erase_check = lpc288x_erase_check,
.protect_check = lpc288x_protect_check,
.info = lpc288x_info,
};
File diff suppressed because it is too large Load Diff
+968
View File
@@ -0,0 +1,968 @@
/***************************************************************************
* Copyright (C) 2012 by George Harris *
* george@luminairecoffee.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "spi.h"
#include <jtag/jtag.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
/* Offsets from ssp_base into config & data registers */
#define SSP_CR0 (0x00) /* Control register 0 */
#define SSP_CR1 (0x04) /* Control register 1 */
#define SSP_DATA (0x08) /* Data register (TX and RX) */
#define SSP_SR (0x0C) /* Status register */
#define SSP_CPSR (0x10) /* Clock prescale register */
/* Status register fields */
#define SSP_BSY (0x00000010)
/* Timeout in ms */
#define SSP_CMD_TIMEOUT (100)
#define SSP_PROBE_TIMEOUT (100)
#define SSP_MAX_TIMEOUT (3000)
struct lpcspifi_flash_bank {
int probed;
uint32_t ssp_base;
uint32_t io_base;
uint32_t ioconfig_base;
uint32_t bank_num;
uint32_t max_spi_clock_mhz;
struct flash_device *dev;
};
struct lpcspifi_target {
char *name;
uint32_t tap_idcode;
uint32_t spifi_base;
uint32_t ssp_base;
uint32_t io_base;
uint32_t ioconfig_base; /* base address for the port word pin registers */
};
static struct lpcspifi_target target_devices[] = {
/* name, tap_idcode, spifi_base, ssp_base, io_base, ioconfig_base */
{ "LPC43xx/18xx", 0x4ba00477, 0x14000000, 0x40083000, 0x400F4000, 0x40086000 },
{ NULL, 0, 0, 0, 0, 0 }
};
/* flash_bank lpcspifi <base> <size> <chip_width> <bus_width> <target>
*/
FLASH_BANK_COMMAND_HANDLER(lpcspifi_flash_bank_command)
{
struct lpcspifi_flash_bank *lpcspifi_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
lpcspifi_info = malloc(sizeof(struct lpcspifi_flash_bank));
if (lpcspifi_info == NULL) {
LOG_ERROR("not enough memory");
return ERROR_FAIL;
}
bank->driver_priv = lpcspifi_info;
lpcspifi_info->probed = 0;
return ERROR_OK;
}
static inline int ioconfig_write_reg(struct target *target, uint32_t ioconfig_base, uint32_t offset, uint32_t value)
{
return target_write_u32(target, ioconfig_base + offset, value);
}
static inline int ssp_write_reg(struct target *target, uint32_t ssp_base, uint32_t offset, uint32_t value)
{
return target_write_u32(target, ssp_base + offset, value);
}
static inline int io_write_reg(struct target *target, uint32_t io_base, uint32_t offset, uint32_t value)
{
return target_write_u32(target, io_base + offset, value);
}
static inline int ssp_read_reg(struct target *target, uint32_t ssp_base, uint32_t offset, uint32_t *value)
{
return target_read_u32(target, ssp_base + offset, value);
}
static int ssp_setcs(struct target *target, uint32_t io_base, unsigned int value)
{
return io_write_reg(target, io_base, 0x12ac, value ? 0xffffffff : 0x00000000);
}
/* Poll the SSP busy flag. When this comes back as 0, the transfer is complete
* and the controller is idle. */
static int poll_ssp_busy(struct target *target, uint32_t ssp_base, int timeout)
{
long long endtime;
uint32_t value;
int retval;
retval = ssp_read_reg(target, ssp_base, SSP_SR, &value);
if ((retval == ERROR_OK) && (value & SSP_BSY) == 0)
return ERROR_OK;
else if (retval != ERROR_OK)
return retval;
endtime = timeval_ms() + timeout;
do {
alive_sleep(1);
retval = ssp_read_reg(target, ssp_base, SSP_SR, &value);
if ((retval == ERROR_OK) && (value & SSP_BSY) == 0)
return ERROR_OK;
else if (retval != ERROR_OK)
return retval;
} while (timeval_ms() < endtime);
LOG_ERROR("Timeout while polling BSY");
return ERROR_FLASH_OPERATION_FAILED;
}
/* Un-initialize the ssp module and initialize the SPIFI module */
static int lpcspifi_set_hw_mode(struct flash_bank *bank)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base = lpcspifi_info->ssp_base;
struct armv7m_algorithm armv7m_info;
struct working_area *spifi_init_algorithm;
struct reg_param reg_params[1];
int retval = ERROR_OK;
LOG_DEBUG("Uninitializing LPC43xx SSP");
/* Turn off the SSP module */
retval = ssp_write_reg(target, ssp_base, SSP_CR1, 0x00000000);
if (retval != ERROR_OK)
return retval;
/* see contrib/loaders/flash/lpcspifi_init.S for src */
static const uint8_t spifi_init_code[] = {
0x4f, 0xea, 0x00, 0x08, 0xa1, 0xb0, 0x00, 0xaf,
0x4f, 0xf4, 0xc0, 0x43, 0xc4, 0xf2, 0x08, 0x03,
0x4f, 0xf0, 0xf3, 0x02, 0xc3, 0xf8, 0x8c, 0x21,
0x4f, 0xf4, 0xc0, 0x43, 0xc4, 0xf2, 0x08, 0x03,
0x4f, 0xf4, 0xc0, 0x42, 0xc4, 0xf2, 0x08, 0x02,
0x4f, 0xf4, 0xc0, 0x41, 0xc4, 0xf2, 0x08, 0x01,
0x4f, 0xf4, 0xc0, 0x40, 0xc4, 0xf2, 0x08, 0x00,
0x4f, 0xf0, 0xd3, 0x04, 0xc0, 0xf8, 0x9c, 0x41,
0x20, 0x46, 0xc1, 0xf8, 0x98, 0x01, 0x01, 0x46,
0xc2, 0xf8, 0x94, 0x11, 0xc3, 0xf8, 0x90, 0x11,
0x4f, 0xf4, 0xc0, 0x43, 0xc4, 0xf2, 0x08, 0x03,
0x4f, 0xf0, 0x13, 0x02, 0xc3, 0xf8, 0xa0, 0x21,
0x40, 0xf2, 0x18, 0x13, 0xc1, 0xf2, 0x40, 0x03,
0x1b, 0x68, 0x1c, 0x68, 0x40, 0xf2, 0xb4, 0x30,
0xc1, 0xf2, 0x00, 0x00, 0x4f, 0xf0, 0x03, 0x01,
0x4f, 0xf0, 0xc0, 0x02, 0x4f, 0xea, 0x08, 0x03,
0xa0, 0x47, 0x00, 0xf0, 0x00, 0xb8, 0x00, 0xbe
};
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
LOG_DEBUG("Allocating working area for SPIFI init algorithm");
/* Get memory for spifi initialization algorithm */
retval = target_alloc_working_area(target, sizeof(spifi_init_code),
&spifi_init_algorithm);
if (retval != ERROR_OK) {
LOG_ERROR("Insufficient working area to initialize SPIFI "\
"module. You must allocate at least %zdB of working "\
"area in order to use this driver.",
sizeof(spifi_init_code)
);
return retval;
}
LOG_DEBUG("Writing algorithm to working area at 0x%08x",
spifi_init_algorithm->address);
/* Write algorithm to working area */
retval = target_write_buffer(target,
spifi_init_algorithm->address,
sizeof(spifi_init_code),
spifi_init_code
);
if (retval != ERROR_OK) {
target_free_working_area(target, spifi_init_algorithm);
return retval;
}
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT); /* spifi clk speed */
/* For now, the algorithm will set up the SPIFI module
* @ the IRC clock speed. In the future, it could be made
* a bit smarter to use other clock sources if the user has
* already configured them in order to speed up memory-
* mapped reads. */
buf_set_u32(reg_params[0].value, 0, 32, 12);
/* Run the algorithm */
LOG_DEBUG("Running SPIFI init algorithm");
retval = target_run_algorithm(target, 0 , NULL, 1, reg_params,
spifi_init_algorithm->address,
spifi_init_algorithm->address + sizeof(spifi_init_code) - 2,
1000, &armv7m_info);
if (retval != ERROR_OK)
LOG_ERROR("Error executing SPIFI init algorithm");
target_free_working_area(target, spifi_init_algorithm);
destroy_reg_param(&reg_params[0]);
return retval;
}
/* Initialize the ssp module */
static int lpcspifi_set_sw_mode(struct flash_bank *bank)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base = lpcspifi_info->ssp_base;
uint32_t io_base = lpcspifi_info->io_base;
uint32_t ioconfig_base = lpcspifi_info->ioconfig_base;
int retval = ERROR_OK;
/* Re-initialize SPIFI. There are a couple of errata on this, so this makes
sure that nothing's in an unhappy state. */
retval = lpcspifi_set_hw_mode(bank);
/* If we couldn't initialize hardware mode, don't even bother continuing */
if (retval != ERROR_OK)
return retval;
/* Initialize the pins */
retval = ioconfig_write_reg(target, ioconfig_base, 0x194, 0x00000040);
if (retval == ERROR_OK)
retval = ioconfig_write_reg(target, ioconfig_base, 0x1a0, 0x00000044);
if (retval == ERROR_OK)
retval = ioconfig_write_reg(target, ioconfig_base, 0x190, 0x00000040);
if (retval == ERROR_OK)
retval = ioconfig_write_reg(target, ioconfig_base, 0x19c, 0x000000ed);
if (retval == ERROR_OK)
retval = ioconfig_write_reg(target, ioconfig_base, 0x198, 0x000000ed);
if (retval == ERROR_OK)
retval = ioconfig_write_reg(target, ioconfig_base, 0x18c, 0x000000ea);
/* Set CS high & as an output */
if (retval == ERROR_OK)
retval = io_write_reg(target, io_base, 0x12ac, 0xffffffff);
if (retval == ERROR_OK)
retval = io_write_reg(target, io_base, 0x2014, 0x00000800);
/* Initialize the module */
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_CR0, 0x00000007);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_CR1, 0x00000000);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_CPSR, 0x00000008);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_CR1, 0x00000002);
/* If something didn't work out, attempt to return SPIFI to HW mode */
if (retval != ERROR_OK)
lpcspifi_set_hw_mode(bank);
return retval;
}
/* Read the status register of the external SPI flash chip. */
static int read_status_reg(struct flash_bank *bank, uint32_t *status)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base = lpcspifi_info->ssp_base;
uint32_t io_base = lpcspifi_info->io_base;
uint32_t value;
int retval = ERROR_OK;
retval = ssp_setcs(target, io_base, 0);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, SPIFLASH_READ_STATUS);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
/* Dummy write to clock in the register */
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_setcs(target, io_base, 1);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
if (retval == ERROR_OK)
*status = value;
return retval;
}
/* check for BSY bit in flash status register */
/* timeout in ms */
static int wait_till_ready(struct flash_bank *bank, int timeout)
{
uint32_t status;
int retval;
long long endtime;
endtime = timeval_ms() + timeout;
do {
/* read flash status register */
retval = read_status_reg(bank, &status);
if (retval != ERROR_OK)
return retval;
if ((status & SPIFLASH_BSY_BIT) == 0)
return ERROR_OK;
alive_sleep(1);
} while (timeval_ms() < endtime);
LOG_ERROR("timeout waiting for flash to finish write/erase operation");
return ERROR_FAIL;
}
/* Send "write enable" command to SPI flash chip. */
static int lpcspifi_write_enable(struct flash_bank *bank)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base = lpcspifi_info->ssp_base;
uint32_t io_base = lpcspifi_info->io_base;
uint32_t status, value;
int retval = ERROR_OK;
retval = ssp_setcs(target, io_base, 0);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, SPIFLASH_WRITE_ENABLE);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
if (retval == ERROR_OK)
retval = ssp_setcs(target, io_base, 1);
/* read flash status register */
if (retval == ERROR_OK)
retval = read_status_reg(bank, &status);
if (retval != ERROR_OK)
return retval;
/* Check write enabled */
if ((status & SPIFLASH_WE_BIT) == 0) {
LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status);
return ERROR_FAIL;
}
return retval;
}
static int lpcspifi_bulk_erase(struct flash_bank *bank)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base = lpcspifi_info->ssp_base;
uint32_t io_base = lpcspifi_info->io_base;
uint32_t value;
int retval = ERROR_OK;
retval = lpcspifi_set_sw_mode(bank);
if (retval == ERROR_OK)
retval = lpcspifi_write_enable(bank);
/* send SPI command "bulk erase" */
if (retval == ERROR_OK)
ssp_setcs(target, io_base, 0);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, lpcspifi_info->dev->chip_erase_cmd);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
if (retval == ERROR_OK)
retval = ssp_setcs(target, io_base, 1);
/* poll flash BSY for self-timed bulk erase */
if (retval == ERROR_OK)
retval = wait_till_ready(bank, bank->num_sectors*SSP_MAX_TIMEOUT);
return retval;
}
static int lpcspifi_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
struct reg_param reg_params[4];
struct armv7m_algorithm armv7m_info;
struct working_area *erase_algorithm;
int retval = ERROR_OK;
int sector;
LOG_DEBUG("erase from sector %d to sector %d", first, last);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
LOG_ERROR("Flash sector invalid");
return ERROR_FLASH_SECTOR_INVALID;
}
if (!(lpcspifi_info->probed)) {
LOG_ERROR("Flash bank not probed");
return ERROR_FLASH_BANK_NOT_PROBED;
}
for (sector = first; sector <= last; sector++) {
if (bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
/* If we're erasing the entire chip and the flash supports
* it, use a bulk erase instead of going sector-by-sector. */
if (first == 0 && last == (bank->num_sectors - 1)
&& lpcspifi_info->dev->chip_erase_cmd != lpcspifi_info->dev->erase_cmd) {
LOG_DEBUG("Chip supports the bulk erase command."\
" Will use bulk erase instead of sector-by-sector erase.");
retval = lpcspifi_bulk_erase(bank);
if (retval == ERROR_OK) {
retval = lpcspifi_set_hw_mode(bank);
return retval;
} else
LOG_WARNING("Bulk flash erase failed. Falling back to sector-by-sector erase.");
}
retval = lpcspifi_set_hw_mode(bank);
if (retval != ERROR_OK)
return retval;
/* see contrib/loaders/flash/lpcspifi_erase.S for src */
static const uint8_t lpcspifi_flash_erase_code[] = {
0x4f, 0xf4, 0xc0, 0x4a, 0xc4, 0xf2, 0x08, 0x0a,
0x4f, 0xf0, 0xea, 0x08, 0xca, 0xf8, 0x8c, 0x81,
0x4f, 0xf0, 0x40, 0x08, 0xca, 0xf8, 0x90, 0x81,
0x4f, 0xf0, 0x40, 0x08, 0xca, 0xf8, 0x94, 0x81,
0x4f, 0xf0, 0xed, 0x08, 0xca, 0xf8, 0x98, 0x81,
0x4f, 0xf0, 0xed, 0x08, 0xca, 0xf8, 0x9c, 0x81,
0x4f, 0xf0, 0x44, 0x08, 0xca, 0xf8, 0xa0, 0x81,
0x4f, 0xf4, 0xc0, 0x4a, 0xc4, 0xf2, 0x0f, 0x0a,
0x4f, 0xf4, 0x00, 0x68, 0xca, 0xf8, 0x14, 0x80,
0x4f, 0xf4, 0x80, 0x4a, 0xc4, 0xf2, 0x0f, 0x0a,
0x4f, 0xf0, 0xff, 0x08, 0xca, 0xf8, 0xab, 0x80,
0x4f, 0xf0, 0x00, 0x0a, 0xc4, 0xf2, 0x05, 0x0a,
0x4f, 0xf0, 0x00, 0x08, 0xc0, 0xf2, 0x00, 0x18,
0xca, 0xf8, 0x94, 0x80, 0x4f, 0xf4, 0x00, 0x5a,
0xc4, 0xf2, 0x05, 0x0a, 0x4f, 0xf0, 0x01, 0x08,
0xca, 0xf8, 0x00, 0x87, 0x4f, 0xf4, 0x40, 0x5a,
0xc4, 0xf2, 0x08, 0x0a, 0x4f, 0xf0, 0x07, 0x08,
0xca, 0xf8, 0x00, 0x80, 0x4f, 0xf0, 0x02, 0x08,
0xca, 0xf8, 0x10, 0x80, 0xca, 0xf8, 0x04, 0x80,
0x00, 0xf0, 0x52, 0xf8, 0x4f, 0xf0, 0x06, 0x09,
0x00, 0xf0, 0x3b, 0xf8, 0x00, 0xf0, 0x48, 0xf8,
0x00, 0xf0, 0x4a, 0xf8, 0x4f, 0xf0, 0x05, 0x09,
0x00, 0xf0, 0x33, 0xf8, 0x4f, 0xf0, 0x00, 0x09,
0x00, 0xf0, 0x2f, 0xf8, 0x00, 0xf0, 0x3c, 0xf8,
0x19, 0xf0, 0x02, 0x0f, 0x00, 0xf0, 0x45, 0x80,
0x00, 0xf0, 0x3a, 0xf8, 0x4f, 0xea, 0x02, 0x09,
0x00, 0xf0, 0x23, 0xf8, 0x4f, 0xea, 0x10, 0x49,
0x00, 0xf0, 0x1f, 0xf8, 0x4f, 0xea, 0x10, 0x29,
0x00, 0xf0, 0x1b, 0xf8, 0x4f, 0xea, 0x00, 0x09,
0x00, 0xf0, 0x17, 0xf8, 0x00, 0xf0, 0x24, 0xf8,
0x00, 0xf0, 0x26, 0xf8, 0x4f, 0xf0, 0x05, 0x09,
0x00, 0xf0, 0x0f, 0xf8, 0x4f, 0xf0, 0x00, 0x09,
0x00, 0xf0, 0x0b, 0xf8, 0x00, 0xf0, 0x18, 0xf8,
0x19, 0xf0, 0x01, 0x0f, 0x7f, 0xf4, 0xf0, 0xaf,
0x01, 0x39, 0xf9, 0xb1, 0x18, 0x44, 0xff, 0xf7,
0xbf, 0xbf, 0x4f, 0xf4, 0x40, 0x5a, 0xc4, 0xf2,
0x08, 0x0a, 0xca, 0xf8, 0x08, 0x90, 0xda, 0xf8,
0x0c, 0x90, 0x19, 0xf0, 0x10, 0x0f, 0x7f, 0xf4,
0xfa, 0xaf, 0xda, 0xf8, 0x08, 0x90, 0x70, 0x47,
0x4f, 0xf0, 0xff, 0x08, 0x00, 0xf0, 0x02, 0xb8,
0x4f, 0xf0, 0x00, 0x08, 0x4f, 0xf4, 0x80, 0x4a,
0xc4, 0xf2, 0x0f, 0x0a, 0xca, 0xf8, 0xab, 0x80,
0x70, 0x47, 0x00, 0x20, 0x00, 0xbe, 0xff, 0xff
};
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
/* Get memory for spifi initialization algorithm */
retval = target_alloc_working_area(target, sizeof(lpcspifi_flash_erase_code),
&erase_algorithm);
if (retval != ERROR_OK) {
LOG_ERROR("Insufficient working area. You must configure a working"\
" area of at least %zdB in order to erase SPIFI flash.",
sizeof(lpcspifi_flash_erase_code));
return retval;
}
/* Write algorithm to working area */
retval = target_write_buffer(target, erase_algorithm->address,
sizeof(lpcspifi_flash_erase_code), lpcspifi_flash_erase_code);
if (retval != ERROR_OK) {
target_free_working_area(target, erase_algorithm);
return retval;
}
init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* Start address */
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /* Sector count */
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /* Erase command */
init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /* Sector size */
buf_set_u32(reg_params[0].value, 0, 32, bank->sectors[first].offset);
buf_set_u32(reg_params[1].value, 0, 32, last - first + 1);
buf_set_u32(reg_params[2].value, 0, 32, lpcspifi_info->dev->erase_cmd);
buf_set_u32(reg_params[3].value, 0, 32, bank->sectors[first].size);
/* Run the algorithm */
retval = target_run_algorithm(target, 0 , NULL, 4, reg_params,
erase_algorithm->address,
erase_algorithm->address + sizeof(lpcspifi_flash_erase_code) - 4,
3000*(last - first + 1), &armv7m_info);
if (retval != ERROR_OK)
LOG_ERROR("Error executing flash erase algorithm");
target_free_working_area(target, erase_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
retval = lpcspifi_set_hw_mode(bank);
return retval;
}
static int lpcspifi_protect(struct flash_bank *bank, int set,
int first, int last)
{
int sector;
for (sector = first; sector <= last; sector++)
bank->sectors[sector].is_protected = set;
return ERROR_OK;
}
static int lpcspifi_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t page_size, fifo_size;
struct working_area *fifo;
struct reg_param reg_params[5];
struct armv7m_algorithm armv7m_info;
struct working_area *write_algorithm;
int sector;
int retval = ERROR_OK;
LOG_DEBUG("offset=0x%08" PRIx32 " count=0x%08" PRIx32,
offset, count);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset + count > lpcspifi_info->dev->size_in_bytes) {
LOG_WARNING("Writes past end of flash. Extra data discarded.");
count = lpcspifi_info->dev->size_in_bytes - offset;
}
/* Check sector protection */
for (sector = 0; sector < bank->num_sectors; sector++) {
/* Start offset in or before this sector? */
/* End offset in or behind this sector? */
if ((offset <
(bank->sectors[sector].offset + bank->sectors[sector].size))
&& ((offset + count - 1) >= bank->sectors[sector].offset)
&& bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
page_size = lpcspifi_info->dev->pagesize;
retval = lpcspifi_set_hw_mode(bank);
if (retval != ERROR_OK)
return retval;
/* see contrib/loaders/flash/lpcspifi_write.S for src */
static const uint8_t lpcspifi_flash_write_code[] = {
0x4f, 0xf4, 0xc0, 0x4a, 0xc4, 0xf2, 0x08, 0x0a,
0x4f, 0xf0, 0xea, 0x08, 0xca, 0xf8, 0x8c, 0x81,
0x4f, 0xf0, 0x40, 0x08, 0xca, 0xf8, 0x90, 0x81,
0x4f, 0xf0, 0x40, 0x08, 0xca, 0xf8, 0x94, 0x81,
0x4f, 0xf0, 0xed, 0x08, 0xca, 0xf8, 0x98, 0x81,
0x4f, 0xf0, 0xed, 0x08, 0xca, 0xf8, 0x9c, 0x81,
0x4f, 0xf0, 0x44, 0x08, 0xca, 0xf8, 0xa0, 0x81,
0x4f, 0xf4, 0xc0, 0x4a, 0xc4, 0xf2, 0x0f, 0x0a,
0x4f, 0xf4, 0x00, 0x68, 0xca, 0xf8, 0x14, 0x80,
0x4f, 0xf4, 0x80, 0x4a, 0xc4, 0xf2, 0x0f, 0x0a,
0x4f, 0xf0, 0xff, 0x08, 0xca, 0xf8, 0xab, 0x80,
0x4f, 0xf0, 0x00, 0x0a, 0xc4, 0xf2, 0x05, 0x0a,
0x4f, 0xf0, 0x00, 0x08, 0xc0, 0xf2, 0x00, 0x18,
0xca, 0xf8, 0x94, 0x80, 0x4f, 0xf4, 0x00, 0x5a,
0xc4, 0xf2, 0x05, 0x0a, 0x4f, 0xf0, 0x01, 0x08,
0xca, 0xf8, 0x00, 0x87, 0x4f, 0xf4, 0x40, 0x5a,
0xc4, 0xf2, 0x08, 0x0a, 0x4f, 0xf0, 0x07, 0x08,
0xca, 0xf8, 0x00, 0x80, 0x4f, 0xf0, 0x02, 0x08,
0xca, 0xf8, 0x10, 0x80, 0xca, 0xf8, 0x04, 0x80,
0x4f, 0xf0, 0x00, 0x0b, 0xa3, 0x44, 0x93, 0x45,
0x7f, 0xf6, 0xfc, 0xaf, 0x00, 0xf0, 0x6a, 0xf8,
0x4f, 0xf0, 0x06, 0x09, 0x00, 0xf0, 0x53, 0xf8,
0x00, 0xf0, 0x60, 0xf8, 0x00, 0xf0, 0x62, 0xf8,
0x4f, 0xf0, 0x05, 0x09, 0x00, 0xf0, 0x4b, 0xf8,
0x4f, 0xf0, 0x00, 0x09, 0x00, 0xf0, 0x47, 0xf8,
0x00, 0xf0, 0x54, 0xf8, 0x19, 0xf0, 0x02, 0x0f,
0x00, 0xf0, 0x5d, 0x80, 0x00, 0xf0, 0x52, 0xf8,
0x4f, 0xf0, 0x02, 0x09, 0x00, 0xf0, 0x3b, 0xf8,
0x4f, 0xea, 0x12, 0x49, 0x00, 0xf0, 0x37, 0xf8,
0x4f, 0xea, 0x12, 0x29, 0x00, 0xf0, 0x33, 0xf8,
0x4f, 0xea, 0x02, 0x09, 0x00, 0xf0, 0x2f, 0xf8,
0xd0, 0xf8, 0x00, 0x80, 0xb8, 0xf1, 0x00, 0x0f,
0x00, 0xf0, 0x47, 0x80, 0x47, 0x68, 0x47, 0x45,
0x3f, 0xf4, 0xf6, 0xaf, 0x17, 0xf8, 0x01, 0x9b,
0x00, 0xf0, 0x21, 0xf8, 0x8f, 0x42, 0x28, 0xbf,
0x00, 0xf1, 0x08, 0x07, 0x47, 0x60, 0x01, 0x3b,
0xbb, 0xb3, 0x02, 0xf1, 0x01, 0x02, 0x93, 0x45,
0x7f, 0xf4, 0xe6, 0xaf, 0x00, 0xf0, 0x22, 0xf8,
0xa3, 0x44, 0x00, 0xf0, 0x23, 0xf8, 0x4f, 0xf0,
0x05, 0x09, 0x00, 0xf0, 0x0c, 0xf8, 0x4f, 0xf0,
0x00, 0x09, 0x00, 0xf0, 0x08, 0xf8, 0x00, 0xf0,
0x15, 0xf8, 0x19, 0xf0, 0x01, 0x0f, 0x7f, 0xf4,
0xf0, 0xaf, 0xff, 0xf7, 0xa7, 0xbf, 0x4f, 0xf4,
0x40, 0x5a, 0xc4, 0xf2, 0x08, 0x0a, 0xca, 0xf8,
0x08, 0x90, 0xda, 0xf8, 0x0c, 0x90, 0x19, 0xf0,
0x10, 0x0f, 0x7f, 0xf4, 0xfa, 0xaf, 0xda, 0xf8,
0x08, 0x90, 0x70, 0x47, 0x4f, 0xf0, 0xff, 0x08,
0x00, 0xf0, 0x02, 0xb8, 0x4f, 0xf0, 0x00, 0x08,
0x4f, 0xf4, 0x80, 0x4a, 0xc4, 0xf2, 0x0f, 0x0a,
0xca, 0xf8, 0xab, 0x80, 0x70, 0x47, 0x00, 0x20,
0x50, 0x60, 0x30, 0x46, 0x00, 0xbe, 0xff, 0xff
};
if (target_alloc_working_area(target, sizeof(lpcspifi_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_ERROR("Insufficient working area. You must configure"\
" a working area > %zdB in order to write to SPIFI flash.",
sizeof(lpcspifi_flash_write_code));
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
retval = target_write_buffer(target, write_algorithm->address,
sizeof(lpcspifi_flash_write_code),
lpcspifi_flash_write_code);
if (retval != ERROR_OK) {
target_free_working_area(target, write_algorithm);
return retval;
}
/* FIFO allocation */
fifo_size = target_get_working_area_avail(target);
if (fifo_size == 0) {
/* if we already allocated the writing code but failed to get fifo
* space, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_ERROR("Insufficient working area. Please allocate at least"\
" %zdB of working area to enable flash writes.",
sizeof(lpcspifi_flash_write_code) + 1
);
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
} else if (fifo_size < page_size)
LOG_WARNING("Working area size is limited; flash writes may be"\
" slow. Increase working area size to at least %zdB"\
" to reduce write times.",
sizeof(lpcspifi_flash_write_code) + page_size
);
else if (fifo_size > 0x2000) /* Beyond this point, we start to get diminishing returns */
fifo_size = 0x2000;
if (target_alloc_working_area(target, fifo_size, &fifo) != ERROR_OK) {
target_free_working_area(target, write_algorithm);
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /* buffer end */
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /* target address */
init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT); /* count (halfword-16bit) */
init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT); /* page size */
buf_set_u32(reg_params[0].value, 0, 32, fifo->address);
buf_set_u32(reg_params[1].value, 0, 32, fifo->address + fifo->size);
buf_set_u32(reg_params[2].value, 0, 32, offset);
buf_set_u32(reg_params[3].value, 0, 32, count);
buf_set_u32(reg_params[4].value, 0, 32, page_size);
retval = target_run_flash_async_algorithm(target, buffer, count, 1,
0, NULL,
5, reg_params,
fifo->address, fifo->size,
write_algorithm->address, 0,
&armv7m_info
);
if (retval != ERROR_OK)
LOG_ERROR("Error executing flash write algorithm");
target_free_working_area(target, fifo);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
/* Switch to HW mode before return to prompt */
retval = lpcspifi_set_hw_mode(bank);
return retval;
}
/* Return ID of flash device */
/* On exit, SW mode is kept */
static int lpcspifi_read_flash_id(struct flash_bank *bank, uint32_t *id)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base = lpcspifi_info->ssp_base;
uint32_t io_base = lpcspifi_info->io_base;
uint32_t value;
int retval;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
LOG_DEBUG("Getting ID");
retval = lpcspifi_set_sw_mode(bank);
if (retval != ERROR_OK)
return retval;
/* poll WIP */
if (retval == ERROR_OK)
retval = wait_till_ready(bank, SSP_PROBE_TIMEOUT);
/* Send SPI command "read ID" */
if (retval == ERROR_OK)
retval = ssp_setcs(target, io_base, 0);
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, SPIFLASH_READ_ID);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
/* Dummy write to clock in data */
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
if (retval == ERROR_OK)
((uint8_t *)id)[0] = value;
/* Dummy write to clock in data */
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
if (retval == ERROR_OK)
((uint8_t *)id)[1] = value;
/* Dummy write to clock in data */
if (retval == ERROR_OK)
retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00);
if (retval == ERROR_OK)
retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT);
if (retval == ERROR_OK)
retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value);
if (retval == ERROR_OK)
((uint8_t *)id)[2] = value;
if (retval == ERROR_OK)
retval = ssp_setcs(target, io_base, 1);
return retval;
}
static int lpcspifi_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
uint32_t ssp_base;
uint32_t io_base;
uint32_t ioconfig_base;
struct flash_sector *sectors;
uint32_t id = 0; /* silence uninitialized warning */
struct lpcspifi_target *target_device;
int retval;
/* If we've already probed, we should be fine to skip this time. */
if (lpcspifi_info->probed)
return ERROR_OK;
lpcspifi_info->probed = 0;
for (target_device = target_devices ; target_device->name ; ++target_device)
if (target_device->tap_idcode == target->tap->idcode)
break;
if (!target_device->name) {
LOG_ERROR("Device ID 0x%" PRIx32 " is not known as SPIFI capable",
target->tap->idcode);
return ERROR_FAIL;
}
ssp_base = target_device->ssp_base;
io_base = target_device->io_base;
ioconfig_base = target_device->ioconfig_base;
lpcspifi_info->ssp_base = ssp_base;
lpcspifi_info->io_base = io_base;
lpcspifi_info->ioconfig_base = ioconfig_base;
lpcspifi_info->bank_num = bank->bank_number;
LOG_DEBUG("Valid SPIFI on device %s at address 0x%" PRIx32,
target_device->name, bank->base);
/* read and decode flash ID; returns in SW mode */
retval = lpcspifi_read_flash_id(bank, &id);
if (retval != ERROR_OK)
return retval;
retval = lpcspifi_set_hw_mode(bank);
if (retval != ERROR_OK)
return retval;
lpcspifi_info->dev = NULL;
for (struct flash_device *p = flash_devices; p->name ; p++)
if (p->device_id == id) {
lpcspifi_info->dev = p;
break;
}
if (!lpcspifi_info->dev) {
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
return ERROR_FAIL;
}
LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
lpcspifi_info->dev->name, lpcspifi_info->dev->device_id);
/* Set correct size value */
bank->size = lpcspifi_info->dev->size_in_bytes;
/* create and fill sectors array */
bank->num_sectors =
lpcspifi_info->dev->size_in_bytes / lpcspifi_info->dev->sectorsize;
sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
if (sectors == NULL) {
LOG_ERROR("not enough memory");
return ERROR_FAIL;
}
for (int sector = 0; sector < bank->num_sectors; sector++) {
sectors[sector].offset = sector * lpcspifi_info->dev->sectorsize;
sectors[sector].size = lpcspifi_info->dev->sectorsize;
sectors[sector].is_erased = -1;
sectors[sector].is_protected = 1;
}
bank->sectors = sectors;
lpcspifi_info->probed = 1;
return ERROR_OK;
}
static int lpcspifi_auto_probe(struct flash_bank *bank)
{
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
if (lpcspifi_info->probed)
return ERROR_OK;
return lpcspifi_probe(bank);
}
static int lpcspifi_protect_check(struct flash_bank *bank)
{
/* Nothing to do. Protection is only handled in SW. */
return ERROR_OK;
}
static int get_lpcspifi_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv;
if (!(lpcspifi_info->probed)) {
snprintf(buf, buf_size,
"\nSPIFI flash bank not probed yet\n");
return ERROR_OK;
}
snprintf(buf, buf_size, "\nSPIFI flash information:\n"
" Device \'%s\' (ID 0x%08x)\n",
lpcspifi_info->dev->name, lpcspifi_info->dev->device_id);
return ERROR_OK;
}
struct flash_driver lpcspifi_flash = {
.name = "lpcspifi",
.flash_bank_command = lpcspifi_flash_bank_command,
.erase = lpcspifi_erase,
.protect = lpcspifi_protect,
.write = lpcspifi_write,
.read = default_flash_read,
.probe = lpcspifi_probe,
.auto_probe = lpcspifi_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = lpcspifi_protect_check,
.info = get_lpcspifi_info,
};
+563
View File
@@ -0,0 +1,563 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath *
* Dominic.Rath@gmx.de *
* Copyright (C) 2009 Michael Schwingen *
* michael@schwingen.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "cfi.h"
#include "non_cfi.h"
#define KB 1024
#define MB (1024*1024)
#define ERASE_REGION(num, size) (((size/256) << 16) | (num-1))
/* non-CFI compatible flashes */
static struct non_cfi non_cfi_flashes[] = {
{
.mfr = CFI_MFR_SST,
.id = 0xd4,
.pri_id = 0x02,
.dev_size = 64*KB,
.interface_desc = 0x0, /* x8 only device */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(16, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0xd5,
.pri_id = 0x02,
.dev_size = 128*KB,
.interface_desc = 0x0, /* x8 only device */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(32, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0xd6,
.pri_id = 0x02,
.dev_size = 256*KB,
.interface_desc = 0x0, /* x8 only device */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(64, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0xd7,
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x0, /* x8 only device */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(128, 4*KB)
}
},
{
.mfr = CFI_MFR_AMD, /* Spansion AM29LV040B */
.id = 0x4f,
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x0, /* x8 only device */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(8, 64*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x2780,
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x2, /* x8 or x16 device */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(128, 4*KB)
}
},
{
.mfr = CFI_MFR_ST,
.id = 0xd6, /* ST29F400BB */
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(7, 64*KB)
}
},
{
.mfr = CFI_MFR_ST,
.id = 0xd5, /* ST29F400BT */
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(7, 64*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 16*KB)
}
},
/* SST 39VF* do not support DQ5 status polling - this currently is
only supported by the host algorithm, not by the target code using
the work area.
Only true for 8-bit and 32-bit wide memories. 16-bit wide memories
without DQ5 status polling are supported by the target code.
*/
{
.mfr = CFI_MFR_SST,
.id = 0x2782, /* SST39xF160 */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(512, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x2783, /* SST39VF320 */
.pri_id = 0x02,
.dev_size = 4*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(1024, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x234b, /* SST39VF1601 */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(512, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x274b, /* SST39WF1601 */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(512, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x234a, /* SST39VF1602 */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(512, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x235b, /* SST39VF3201 */
.pri_id = 0x02,
.dev_size = 4*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(1024, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x235a, /* SST39VF3202 */
.pri_id = 0x02,
.dev_size = 4*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(1024, 4*KB)
}
},
{
.mfr = CFI_MFR_SST,
.id = 0x236d, /* SST39VF6401B */
.pri_id = 0x02,
.dev_size = 8*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ6_DQ7,
.num_erase_regions = 1,
.erase_region_info = {
ERASE_REGION(2048, 4*KB)
}
},
{
.mfr = CFI_MFR_AMD,
.id = 0x22ab, /* AM29F400BB */
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(7, 64*KB)
}
},
{
.mfr = CFI_MFR_AMD,
.id = 0x2223, /* AM29F400BT */
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(7, 64*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 16*KB)
}
},
{
.mfr = CFI_MFR_FUJITSU,
.id = 0x226b, /* AM29SL800DB */
.pri_id = 0x02,
.dev_size = 1*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(15, 64*KB)
}
},
{
.mfr = CFI_MFR_FUJITSU,
.id = 0x22ea, /* MBM29SL800TE */
.pri_id = 0x02,
.dev_size = 1*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(15, 64*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 16*KB)
}
},
{
.mfr = CFI_MFR_FUJITSU,
.id = 0xba, /* 29LV400BC */
.pri_id = 0x02,
.dev_size = 512*KB,
.interface_desc = 0x1, /* x8 or x16 device w/ nBYTE */
.max_buf_write_size = 0x00,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(7, 64*KB)
}
},
{
.mfr = CFI_MFR_AMIC,
.id = 0xb31a, /* A29L800A */
.pri_id = 0x02,
.dev_size = 1*MB,
.interface_desc = 0x2,
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(15, 64*KB)
}
},
{
.mfr = CFI_MFR_MX,
.id = 0x225b, /* MX29LV800B */
.pri_id = 0x02,
.dev_size = 1*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(15, 64*KB)
}
},
{
.mfr = CFI_MFR_MX,
.id = 0x2249, /* MX29LV160AB: 2MB */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(31, 64*KB)
}
},
{
.mfr = CFI_MFR_MX,
.id = 0x22C4, /* MX29LV160AT: 2MB */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(31, 64*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 16*KB)
}
},
{
.mfr = CFI_MFR_EON,
.id = 0x225b, /* EN29LV800BB */
.pri_id = 0x02,
.dev_size = 1*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(15, 64*KB)
}
},
{
.mfr = CFI_MFR_ATMEL,
.id = 0x00c0, /* Atmel 49BV1614 */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 3,
.erase_region_info = {
ERASE_REGION(8, 8*KB),
ERASE_REGION(2, 32*KB),
ERASE_REGION(30, 64*KB)
}
},
{
.mfr = CFI_MFR_ATMEL,
.id = 0xC2, /* Atmel 49BV1614T */
.pri_id = 0x02,
.dev_size = 2*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 3,
.erase_region_info = {
ERASE_REGION(30, 64*KB),
ERASE_REGION(2, 32*KB),
ERASE_REGION(8, 8*KB)
}
},
{
.mfr = CFI_MFR_AMD,
.id = 0x225b, /* S29AL008D */
.pri_id = 0x02,
.dev_size = 1*MB,
.interface_desc = 0x2, /* x8 or x16 device with nBYTE */
.max_buf_write_size = 0x0,
.status_poll_mask = CFI_STATUS_POLL_MASK_DQ5_DQ6_DQ7,
.num_erase_regions = 4,
.erase_region_info = {
ERASE_REGION(1, 16*KB),
ERASE_REGION(2, 8*KB),
ERASE_REGION(1, 32*KB),
ERASE_REGION(15, 64*KB)
}
},
{
.mfr = 0,
.id = 0,
}
};
void cfi_fixup_non_cfi(struct flash_bank *bank)
{
unsigned int mask;
struct cfi_flash_bank *cfi_info = bank->driver_priv;
struct non_cfi *non_cfi = non_cfi_flashes;
if (cfi_info->x16_as_x8)
mask = 0xFF;
else
mask = 0xFFFF;
for (non_cfi = non_cfi_flashes; non_cfi->mfr; non_cfi++) {
if ((cfi_info->manufacturer == non_cfi->mfr)
&& (cfi_info->device_id == (non_cfi->id & mask)))
break;
}
/* only fixup jedec flashs found in table */
if (!non_cfi->mfr)
return;
cfi_info->not_cfi = 1;
/* fill in defaults for non-critical data */
cfi_info->vcc_min = 0x0;
cfi_info->vcc_max = 0x0;
cfi_info->vpp_min = 0x0;
cfi_info->vpp_max = 0x0;
/* these are used for timeouts - use vales that should be long enough
for normal operation. */
cfi_info->word_write_timeout_typ = 0x0a;
cfi_info->buf_write_timeout_typ = 0x0d;
cfi_info->block_erase_timeout_typ = 0x0d;
cfi_info->chip_erase_timeout_typ = 0x10;
cfi_info->word_write_timeout_max = 0x0;
cfi_info->buf_write_timeout_max = 0x0;
cfi_info->block_erase_timeout_max = 0x0;
cfi_info->chip_erase_timeout_max = 0x0;
cfi_info->qry[0] = 'Q';
cfi_info->qry[1] = 'R';
cfi_info->qry[2] = 'Y';
cfi_info->pri_id = non_cfi->pri_id;
cfi_info->pri_addr = 0x0;
cfi_info->alt_id = 0x0;
cfi_info->alt_addr = 0x0;
cfi_info->alt_ext = NULL;
cfi_info->interface_desc = non_cfi->interface_desc;
cfi_info->max_buf_write_size = non_cfi->max_buf_write_size;
cfi_info->status_poll_mask = non_cfi->status_poll_mask;
cfi_info->num_erase_regions = non_cfi->num_erase_regions;
size_t erase_region_info_size = sizeof(*cfi_info->erase_region_info) *
cfi_info->num_erase_regions;
cfi_info->erase_region_info = malloc(erase_region_info_size);
memcpy(cfi_info->erase_region_info,
non_cfi->erase_region_info, erase_region_info_size);
cfi_info->dev_size = non_cfi->dev_size;
if (cfi_info->pri_id == 0x2) {
struct cfi_spansion_pri_ext *pri_ext = malloc(sizeof(struct cfi_spansion_pri_ext));
pri_ext->pri[0] = 'P';
pri_ext->pri[1] = 'R';
pri_ext->pri[2] = 'I';
pri_ext->major_version = '1';
pri_ext->minor_version = '0';
pri_ext->SiliconRevision = 0x0;
pri_ext->EraseSuspend = 0x0;
pri_ext->EraseSuspend = 0x0;
pri_ext->BlkProt = 0x0;
pri_ext->TmpBlkUnprotect = 0x0;
pri_ext->BlkProtUnprot = 0x0;
pri_ext->SimultaneousOps = 0x0;
pri_ext->BurstMode = 0x0;
pri_ext->PageMode = 0x0;
pri_ext->VppMin = 0x0;
pri_ext->VppMax = 0x0;
pri_ext->TopBottom = 0x0;
pri_ext->_unlock1 = 0x5555;
pri_ext->_unlock2 = 0x2AAA;
pri_ext->_reversed_geometry = 0;
cfi_info->pri_ext = pri_ext;
} else if ((cfi_info->pri_id == 0x1) || (cfi_info->pri_id == 0x3)) {
LOG_ERROR("BUG: non-CFI flashes using the Intel commandset are not yet supported");
exit(-1);
}
}
+38
View File
@@ -0,0 +1,38 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef NON_CFI_H
#define NON_CFI_H
struct non_cfi {
uint16_t mfr;
uint16_t id;
uint16_t pri_id;
uint32_t dev_size;
uint16_t interface_desc;
uint16_t max_buf_write_size;
uint8_t num_erase_regions;
uint32_t erase_region_info[6];
uint8_t status_poll_mask;
};
void cfi_fixup_non_cfi(struct flash_bank *bank);
#endif /* NON_CFI_H */
+351
View File
@@ -0,0 +1,351 @@
/***************************************************************************
* Copyright (C) 2007 by Pavel Chromy *
* chromy@asix.cz *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "ocl.h"
#include <target/embeddedice.h>
struct ocl_priv {
struct arm_jtag *jtag_info;
unsigned int buflen;
unsigned int bufalign;
};
static int ocl_erase_check(struct flash_bank *bank)
{
return ERROR_OK;
}
static int ocl_protect_check(struct flash_bank *bank)
{
return ERROR_OK;
}
/* flash_bank ocl 0 0 0 0 <target#> */
FLASH_BANK_COMMAND_HANDLER(ocl_flash_bank_command)
{
struct arm7_9_common *arm7_9;
struct ocl_priv *ocl;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
arm7_9 = target_to_arm7_9(bank->target);
if (!is_arm7_9(arm7_9))
return ERROR_TARGET_INVALID;
ocl = bank->driver_priv = malloc(sizeof(struct ocl_priv));
ocl->jtag_info = &arm7_9->jtag_info;
ocl->buflen = 0;
ocl->bufalign = 1;
return ERROR_OK;
}
static int ocl_erase(struct flash_bank *bank, int first, int last)
{
struct ocl_priv *ocl = bank->driver_priv;
int retval;
uint32_t dcc_buffer[3];
/* check preconditions */
if (bank->num_sectors == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if (bank->target->state != TARGET_RUNNING) {
LOG_ERROR("target has to be running to communicate with the loader");
return ERROR_TARGET_NOT_RUNNING;
}
if ((first == 0) && (last == bank->num_sectors - 1)) {
dcc_buffer[0] = OCL_ERASE_ALL;
retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
} else {
dcc_buffer[0] = OCL_ERASE_BLOCK;
dcc_buffer[1] = first;
dcc_buffer[2] = last;
retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 3);
if (retval != ERROR_OK)
return retval;
}
/* wait for response, fixed timeout of 1 s */
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000);
if (retval != ERROR_OK)
return retval;
/* receive response */
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer + 1, 1);
if (retval != ERROR_OK)
return retval;
if (dcc_buffer[1] != OCL_CMD_DONE) {
if (dcc_buffer[0] == OCL_ERASE_ALL)
LOG_ERROR("loader response to OCL_ERASE_ALL 0x%08" PRIx32 "", dcc_buffer[1]);
else
LOG_ERROR("loader response to OCL_ERASE_BLOCK 0x%08" PRIx32 "", dcc_buffer[1]);
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int ocl_protect(struct flash_bank *bank, int set, int first, int last)
{
return ERROR_OK;
}
static int ocl_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct ocl_priv *ocl = bank->driver_priv;
int retval;
uint32_t *dcc_buffer;
uint32_t *dcc_bufptr;
int byteofs;
int runlen;
uint32_t chksum;
int i;
/* check preconditions */
if (ocl->buflen == 0 || ocl->bufalign == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
if (bank->target->state != TARGET_RUNNING) {
LOG_ERROR("target has to be running to communicate with the loader");
return ERROR_TARGET_NOT_RUNNING;
}
/* allocate buffer for max. ocl buffer + overhead */
dcc_buffer = malloc(sizeof(uint32_t)*(ocl->buflen/4 + 3));
while (count) {
if (count + (offset % ocl->bufalign) > ocl->buflen)
runlen = ocl->buflen - (offset % ocl->bufalign);
else
runlen = count;
dcc_buffer[0] = OCL_FLASH_BLOCK | runlen;
dcc_buffer[1] = offset;
dcc_bufptr = &dcc_buffer[2];
*dcc_bufptr = 0xffffffff;
byteofs = (offset % ocl->bufalign) % 4;
chksum = OCL_CHKS_INIT;
/* copy data to DCC buffer in proper byte order and properly aligned */
for (i = 0; i < runlen; i++) {
switch (byteofs++) {
case 0:
*dcc_bufptr &= *(buffer++) | 0xffffff00;
break;
case 1:
*dcc_bufptr &= ((*(buffer++)) << 8) | 0xffff00ff;
break;
case 2:
*dcc_bufptr &= ((*(buffer++)) << 16) | 0xff00ffff;
break;
case 3:
*dcc_bufptr &= ((*(buffer++)) << 24) | 0x00ffffff;
chksum ^= *(dcc_bufptr++);
*dcc_bufptr = 0xffffffff;
byteofs = 0;
break;
}
}
/* add the remaining word to checksum */
if (byteofs)
chksum ^= *(dcc_bufptr++);
*(dcc_bufptr++) = chksum;
/* send the data */
retval = embeddedice_send(ocl->jtag_info, dcc_buffer, dcc_bufptr-dcc_buffer);
if (retval != ERROR_OK) {
free(dcc_buffer);
return retval;
}
/* wait for response, fixed timeout of 1 s */
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000);
if (retval != ERROR_OK) {
free(dcc_buffer);
return retval;
}
/* receive response */
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK) {
free(dcc_buffer);
return retval;
}
if (dcc_buffer[0] != OCL_CMD_DONE) {
LOG_ERROR("loader response to OCL_FLASH_BLOCK 0x%08" PRIx32 "", dcc_buffer[0]);
free(dcc_buffer);
return ERROR_FLASH_OPERATION_FAILED;
}
count -= runlen;
offset += runlen;
}
free(dcc_buffer);
return ERROR_OK;
}
static int ocl_probe(struct flash_bank *bank)
{
struct ocl_priv *ocl = bank->driver_priv;
int retval;
uint32_t dcc_buffer[1];
int sectsize;
int i;
/* purge pending data in DCC */
embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
dcc_buffer[0] = OCL_PROBE;
retval = embeddedice_send(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
/* wait for response, fixed timeout of 1 s */
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 1000);
if (retval != ERROR_OK)
return retval;
/* receive response */
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
if (dcc_buffer[0] != OCL_CMD_DONE) {
LOG_ERROR("loader response to OCL_PROBE 0x%08" PRIx32 "", dcc_buffer[0]);
return ERROR_FLASH_OPERATION_FAILED;
}
/* receive and fill in parameters, detection of loader is important, receive it one by one */
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0);
if (retval != ERROR_OK)
return retval;
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
bank->base = dcc_buffer[0];
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0);
if (retval != ERROR_OK)
return retval;
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
bank->size = dcc_buffer[0];
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0);
if (retval != ERROR_OK)
return retval;
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
bank->num_sectors = dcc_buffer[0];
retval = embeddedice_handshake(ocl->jtag_info, EICE_COMM_CTRL_WBIT, 0);
if (retval != ERROR_OK)
return retval;
retval = embeddedice_receive(ocl->jtag_info, dcc_buffer, 1);
if (retval != ERROR_OK)
return retval;
ocl->buflen = dcc_buffer[0] & 0xffff;
ocl->bufalign = dcc_buffer[0] >> 16;
bank->sectors = realloc(bank->sectors, sizeof(struct flash_sector)*bank->num_sectors);
if (bank->num_sectors == 0) {
LOG_ERROR("number of sectors shall be non zero value");
return ERROR_FLASH_BANK_INVALID;
}
if (bank->size % bank->num_sectors) {
LOG_ERROR("bank size not divisible by number of sectors");
return ERROR_FLASH_BANK_INVALID;
}
sectsize = bank->size / bank->num_sectors;
for (i = 0; i < bank->num_sectors; i++) {
bank->sectors[i].offset = i * sectsize;
bank->sectors[i].size = sectsize;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = -1;
}
if (ocl->bufalign == 0)
ocl->bufalign = 1;
if (ocl->buflen == 0) {
LOG_ERROR("buflen shall be non zero value");
return ERROR_FLASH_BANK_INVALID;
}
if ((ocl->bufalign > ocl->buflen) || (ocl->buflen % ocl->bufalign)) {
LOG_ERROR("buflen is not multiple of bufalign");
return ERROR_FLASH_BANK_INVALID;
}
if (ocl->buflen % 4) {
LOG_ERROR("buflen shall be divisible by 4");
return ERROR_FLASH_BANK_INVALID;
}
return ERROR_OK;
}
static int ocl_info(struct flash_bank *bank, char *buf, int buf_size)
{
return ERROR_OK;
}
static int ocl_auto_probe(struct flash_bank *bank)
{
struct ocl_priv *ocl = bank->driver_priv;
if (ocl->buflen == 0 || ocl->bufalign == 0)
return ERROR_FLASH_BANK_NOT_PROBED;
return ERROR_OK;
}
struct flash_driver ocl_flash = {
.name = "ocl",
.flash_bank_command = ocl_flash_bank_command,
.erase = ocl_erase,
.protect = ocl_protect,
.write = ocl_write,
.read = default_flash_read,
.probe = ocl_probe,
.erase_check = ocl_erase_check,
.protect_check = ocl_protect_check,
.info = ocl_info,
.auto_probe = ocl_auto_probe,
};
+41
View File
@@ -0,0 +1,41 @@
/***************************************************************************
* Copyright (C) 2007 by Pavel Chromy *
* chromy@asix.cz *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef OCL_H
#define OCL_H
/* command/response mask */
#define OCL_CMD_MASK 0xFFFF0000L
/* commads */
#define OCL_FLASH_BLOCK 0x0CFB0000L
#define OCL_ERASE_BLOCK 0x0CEB0000L
#define OCL_ERASE_ALL 0x0CEA0000L
#define OCL_PROBE 0x0CBE0000L
/* responses */
#define OCL_CMD_DONE 0x0ACD0000L
#define OCL_CMD_ERR 0x0ACE0000L
#define OCL_CHKS_FAIL 0x0ACF0000L
#define OCL_BUFF_OVER 0x0AB00000L
#define OCL_CHKS_INIT 0xC100CD0CL
#endif /* OCL_H */
+934
View File
@@ -0,0 +1,934 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2008 by John McCarthy *
* jgmcc@magma.ca *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <jtag/jtag.h>
#include "imp.h"
#include <target/algorithm.h>
#include <target/mips32.h>
#include <target/mips_m4k.h>
#define PIC32MX_MANUF_ID 0x029
/* pic32mx memory locations */
#define PIC32MX_PHYS_RAM 0x00000000
#define PIC32MX_PHYS_PGM_FLASH 0x1D000000
#define PIC32MX_PHYS_PERIPHERALS 0x1F800000
#define PIC32MX_PHYS_BOOT_FLASH 0x1FC00000
/*
* Translate Virtual and Physical addresses.
* Note: These macros only work for KSEG0/KSEG1 addresses.
*/
#define Virt2Phys(v) ((v) & 0x1FFFFFFF)
/* pic32mx configuration register locations */
#define PIC32MX_DEVCFG0_1_2 0xBFC00BFC
#define PIC32MX_DEVCFG0 0xBFC02FFC
#define PIC32MX_DEVCFG1 0xBFC02FF8
#define PIC32MX_DEVCFG2 0xBFC02FF4
#define PIC32MX_DEVCFG3 0xBFC02FF0
#define PIC32MX_DEVID 0xBF80F220
#define PIC32MX_BMXPFMSZ 0xBF882060
#define PIC32MX_BMXBOOTSZ 0xBF882070
#define PIC32MX_BMXDRMSZ 0xBF882040
/* pic32mx flash controller register locations */
#define PIC32MX_NVMCON 0xBF80F400
#define PIC32MX_NVMCONCLR 0xBF80F404
#define PIC32MX_NVMCONSET 0xBF80F408
#define PIC32MX_NVMCONINV 0xBF80F40C
#define NVMCON_NVMWR (1 << 15)
#define NVMCON_NVMWREN (1 << 14)
#define NVMCON_NVMERR (1 << 13)
#define NVMCON_LVDERR (1 << 12)
#define NVMCON_LVDSTAT (1 << 11)
#define NVMCON_OP_PFM_ERASE 0x5
#define NVMCON_OP_PAGE_ERASE 0x4
#define NVMCON_OP_ROW_PROG 0x3
#define NVMCON_OP_WORD_PROG 0x1
#define NVMCON_OP_NOP 0x0
#define PIC32MX_NVMKEY 0xBF80F410
#define PIC32MX_NVMADDR 0xBF80F420
#define PIC32MX_NVMADDRCLR 0xBF80F424
#define PIC32MX_NVMADDRSET 0xBF80F428
#define PIC32MX_NVMADDRINV 0xBF80F42C
#define PIC32MX_NVMDATA 0xBF80F430
#define PIC32MX_NVMSRCADDR 0xBF80F440
/* flash unlock keys */
#define NVMKEY1 0xAA996655
#define NVMKEY2 0x556699AA
#define MX_1_2 1 /* PIC32mx1xx/2xx */
struct pic32mx_flash_bank {
int probed;
int dev_type; /* Default 0. 1 for Pic32MX1XX/2XX variant */
};
/*
* DEVID values as per PIC32MX Flash Programming Specification Rev J
*/
static const struct pic32mx_devs_s {
uint32_t devid;
const char *name;
} pic32mx_devs[] = {
{0x04A07053, "110F016B"},
{0x04A09053, "110F016C"},
{0x04A0B053, "110F016D"},
{0x04A06053, "120F032B"},
{0x04A08053, "120F032C"},
{0x04A0A053, "120F032D"},
{0x04D07053, "130F064B"},
{0x04D09053, "130F064C"},
{0x04D0B053, "130F064D"},
{0x04D06053, "150F128B"},
{0x04D08053, "150F128C"},
{0x04D0A053, "150F128D"},
{0x04A01053, "210F016B"},
{0x04A03053, "210F016C"},
{0x04A05053, "210F016D"},
{0x04A00053, "220F032B"},
{0x04A02053, "220F032C"},
{0x04A04053, "220F032D"},
{0x04D01053, "230F064B"},
{0x04D03053, "230F064C"},
{0x04D05053, "230F064D"},
{0x04D00053, "250F128B"},
{0x04D02053, "250F128C"},
{0x04D04053, "250F128D"},
{0x00938053, "360F512L"},
{0x00934053, "360F256L"},
{0x0092D053, "340F128L"},
{0x0092A053, "320F128L"},
{0x00916053, "340F512H"},
{0x00912053, "340F256H"},
{0x0090D053, "340F128H"},
{0x0090A053, "320F128H"},
{0x00906053, "320F064H"},
{0x00902053, "320F032H"},
{0x00978053, "460F512L"},
{0x00974053, "460F256L"},
{0x0096D053, "440F128L"},
{0x00952053, "440F256H"},
{0x00956053, "440F512H"},
{0x0094D053, "440F128H"},
{0x00942053, "420F032H"},
{0x04307053, "795F512L"},
{0x0430E053, "795F512H"},
{0x04306053, "775F512L"},
{0x0430D053, "775F512H"},
{0x04312053, "775F256L"},
{0x04303053, "775F256H"},
{0x04417053, "764F128L"},
{0x0440B053, "764F128H"},
{0x04341053, "695F512L"},
{0x04325053, "695F512H"},
{0x04311053, "675F512L"},
{0x0430C053, "675F512H"},
{0x04305053, "675F256L"},
{0x0430B053, "675F256H"},
{0x04413053, "664F128L"},
{0x04407053, "664F128H"},
{0x04411053, "664F064L"},
{0x04405053, "664F064H"},
{0x0430F053, "575F512L"},
{0x04309053, "575F512H"},
{0x04333053, "575F256L"},
{0x04317053, "575F256H"},
{0x0440F053, "564F128L"},
{0x04403053, "564F128H"},
{0x0440D053, "564F064L"},
{0x04401053, "564F064H"},
{0x04400053, "534F064H"},
{0x0440C053, "534F064L"},
{0x00000000, NULL}
};
/* flash bank pic32mx <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(pic32mx_flash_bank_command)
{
struct pic32mx_flash_bank *pic32mx_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
pic32mx_info = malloc(sizeof(struct pic32mx_flash_bank));
bank->driver_priv = pic32mx_info;
pic32mx_info->probed = 0;
pic32mx_info->dev_type = 0;
return ERROR_OK;
}
static uint32_t pic32mx_get_flash_status(struct flash_bank *bank)
{
struct target *target = bank->target;
uint32_t status;
target_read_u32(target, PIC32MX_NVMCON, &status);
return status;
}
static uint32_t pic32mx_wait_status_busy(struct flash_bank *bank, int timeout)
{
uint32_t status;
/* wait for busy to clear */
while (((status = pic32mx_get_flash_status(bank)) & NVMCON_NVMWR) && (timeout-- > 0)) {
LOG_DEBUG("status: 0x%" PRIx32, status);
alive_sleep(1);
}
if (timeout <= 0)
LOG_DEBUG("timeout: status: 0x%" PRIx32, status);
return status;
}
static int pic32mx_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t timeout)
{
struct target *target = bank->target;
uint32_t status;
target_write_u32(target, PIC32MX_NVMCON, NVMCON_NVMWREN | op);
/* unlock flash registers */
target_write_u32(target, PIC32MX_NVMKEY, NVMKEY1);
target_write_u32(target, PIC32MX_NVMKEY, NVMKEY2);
/* start operation */
target_write_u32(target, PIC32MX_NVMCONSET, NVMCON_NVMWR);
status = pic32mx_wait_status_busy(bank, timeout);
/* lock flash registers */
target_write_u32(target, PIC32MX_NVMCONCLR, NVMCON_NVMWREN);
return status;
}
static int pic32mx_protect_check(struct flash_bank *bank)
{
struct target *target = bank->target;
struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
uint32_t config0_address;
uint32_t devcfg0;
int s;
int num_pages;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (pic32mx_info->dev_type == MX_1_2)
config0_address = PIC32MX_DEVCFG0_1_2;
else
config0_address = PIC32MX_DEVCFG0;
target_read_u32(target, config0_address, &devcfg0);
if ((devcfg0 & (1 << 28)) == 0) /* code protect bit */
num_pages = 0xffff; /* All pages protected */
else if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH) {
if (devcfg0 & (1 << 24))
num_pages = 0; /* All pages unprotected */
else
num_pages = 0xffff; /* All pages protected */
} else {
/* pgm flash */
if (pic32mx_info->dev_type == MX_1_2)
num_pages = (~devcfg0 >> 10) & 0x3f;
else
num_pages = (~devcfg0 >> 12) & 0xff;
}
for (s = 0; s < bank->num_sectors && s < num_pages; s++)
bank->sectors[s].is_protected = 1;
for (; s < bank->num_sectors; s++)
bank->sectors[s].is_protected = 0;
return ERROR_OK;
}
static int pic32mx_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
int i;
uint32_t status;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first == 0) && (last == (bank->num_sectors - 1))
&& (Virt2Phys(bank->base) == PIC32MX_PHYS_PGM_FLASH)) {
/* this will only erase the Program Flash (PFM), not the Boot Flash (BFM)
* we need to use the MTAP to perform a full erase */
LOG_DEBUG("Erasing entire program flash");
status = pic32mx_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50);
if (status & NVMCON_NVMERR)
return ERROR_FLASH_OPERATION_FAILED;
if (status & NVMCON_LVDERR)
return ERROR_FLASH_OPERATION_FAILED;
return ERROR_OK;
}
for (i = first; i <= last; i++) {
target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(bank->base + bank->sectors[i].offset));
status = pic32mx_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10);
if (status & NVMCON_NVMERR)
return ERROR_FLASH_OPERATION_FAILED;
if (status & NVMCON_LVDERR)
return ERROR_FLASH_OPERATION_FAILED;
bank->sectors[i].is_erased = 1;
}
return ERROR_OK;
}
static int pic32mx_protect(struct flash_bank *bank, int set, int first, int last)
{
struct target *target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
return ERROR_OK;
}
/* see contib/loaders/flash/pic32mx.s for src */
static uint32_t pic32mx_flash_write_code[] = {
/* write: */
0x3C08AA99, /* lui $t0, 0xaa99 */
0x35086655, /* ori $t0, 0x6655 */
0x3C095566, /* lui $t1, 0x5566 */
0x352999AA, /* ori $t1, 0x99aa */
0x3C0ABF80, /* lui $t2, 0xbf80 */
0x354AF400, /* ori $t2, 0xf400 */
0x340B4003, /* ori $t3, $zero, 0x4003 */
0x340C8000, /* ori $t4, $zero, 0x8000 */
/* write_row: */
0x2CD30080, /* sltiu $s3, $a2, 128 */
0x16600008, /* bne $s3, $zero, write_word */
0x340D4000, /* ori $t5, $zero, 0x4000 */
0xAD450020, /* sw $a1, 32($t2) */
0xAD440040, /* sw $a0, 64($t2) */
0x04110016, /* bal progflash */
0x24840200, /* addiu $a0, $a0, 512 */
0x24A50200, /* addiu $a1, $a1, 512 */
0x1000FFF7, /* beq $zero, $zero, write_row */
0x24C6FF80, /* addiu $a2, $a2, -128 */
/* write_word: */
0x3C15A000, /* lui $s5, 0xa000 */
0x36B50000, /* ori $s5, $s5, 0x0 */
0x00952025, /* or $a0, $a0, $s5 */
0x10000008, /* beq $zero, $zero, next_word */
0x340B4001, /* ori $t3, $zero, 0x4001 */
/* prog_word: */
0x8C940000, /* lw $s4, 0($a0) */
0xAD540030, /* sw $s4, 48($t2) */
0xAD450020, /* sw $a1, 32($t2) */
0x04110009, /* bal progflash */
0x24840004, /* addiu $a0, $a0, 4 */
0x24A50004, /* addiu $a1, $a1, 4 */
0x24C6FFFF, /* addiu $a2, $a2, -1 */
/* next_word: */
0x14C0FFF8, /* bne $a2, $zero, prog_word */
0x00000000, /* nop */
/* done: */
0x10000002, /* beq $zero, $zero, exit */
0x24040000, /* addiu $a0, $zero, 0 */
/* error: */
0x26240000, /* addiu $a0, $s1, 0 */
/* exit: */
0x7000003F, /* sdbbp */
/* progflash: */
0xAD4B0000, /* sw $t3, 0($t2) */
0xAD480010, /* sw $t0, 16($t2) */
0xAD490010, /* sw $t1, 16($t2) */
0xAD4C0008, /* sw $t4, 8($t2) */
/* waitflash: */
0x8D500000, /* lw $s0, 0($t2) */
0x020C8024, /* and $s0, $s0, $t4 */
0x1600FFFD, /* bne $s0, $zero, waitflash */
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
0x00000000, /* nop */
0x8D510000, /* lw $s1, 0($t2) */
0x30113000, /* andi $s1, $zero, 0x3000 */
0x1620FFEF, /* bne $s1, $zero, error */
0xAD4D0004, /* sw $t5, 4($t2) */
0x03E00008, /* jr $ra */
0x00000000 /* nop */
};
static int pic32mx_write_block(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 16384;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[3];
uint32_t row_size;
int retval = ERROR_OK;
struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
struct mips32_algorithm mips32_info;
/* flash write code */
if (target_alloc_working_area(target, sizeof(pic32mx_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
/* Change values for counters and row size, depending on variant */
if (pic32mx_info->dev_type == MX_1_2) {
/* 128 byte row */
pic32mx_flash_write_code[8] = 0x2CD30020;
pic32mx_flash_write_code[14] = 0x24840080;
pic32mx_flash_write_code[15] = 0x24A50080;
pic32mx_flash_write_code[17] = 0x24C6FFE0;
row_size = 128;
} else {
/* 512 byte row */
pic32mx_flash_write_code[8] = 0x2CD30080;
pic32mx_flash_write_code[14] = 0x24840200;
pic32mx_flash_write_code[15] = 0x24A50200;
pic32mx_flash_write_code[17] = 0x24C6FF80;
row_size = 512;
}
retval = target_write_buffer(target, write_algorithm->address,
sizeof(pic32mx_flash_write_code), (uint8_t *)pic32mx_flash_write_code);
if (retval != ERROR_OK)
return retval;
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a
* buffer, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING("no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
mips32_info.common_magic = MIPS32_COMMON_MAGIC;
mips32_info.isa_mode = MIPS32_ISA_MIPS32;
init_reg_param(&reg_params[0], "a0", 32, PARAM_IN_OUT);
init_reg_param(&reg_params[1], "a1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "a2", 32, PARAM_OUT);
int row_offset = offset % row_size;
uint8_t *new_buffer = NULL;
if (row_offset && (count >= (row_size / 4))) {
new_buffer = malloc(buffer_size);
if (new_buffer == NULL) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
memset(new_buffer, 0xff, row_offset);
address -= row_offset;
} else
row_offset = 0;
while (count > 0) {
uint32_t status;
uint32_t thisrun_count;
if (row_offset) {
thisrun_count = (count > ((buffer_size - row_offset) / 4)) ?
((buffer_size - row_offset) / 4) : count;
memcpy(new_buffer + row_offset, buffer, thisrun_count * 4);
retval = target_write_buffer(target, source->address,
row_offset + thisrun_count * 4, new_buffer);
if (retval != ERROR_OK)
break;
} else {
thisrun_count = (count > (buffer_size / 4)) ?
(buffer_size / 4) : count;
retval = target_write_buffer(target, source->address,
thisrun_count * 4, buffer);
if (retval != ERROR_OK)
break;
}
buf_set_u32(reg_params[0].value, 0, 32, Virt2Phys(source->address));
buf_set_u32(reg_params[1].value, 0, 32, Virt2Phys(address));
buf_set_u32(reg_params[2].value, 0, 32, thisrun_count + row_offset / 4);
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
write_algorithm->address,
0, 10000, &mips32_info);
if (retval != ERROR_OK) {
LOG_ERROR("error executing pic32mx flash write algorithm");
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
status = buf_get_u32(reg_params[0].value, 0, 32);
if (status & NVMCON_NVMERR) {
LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
if (status & NVMCON_LVDERR) {
LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
buffer += thisrun_count * 4;
address += thisrun_count * 4;
count -= thisrun_count;
if (row_offset) {
address += row_offset;
row_offset = 0;
}
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
if (new_buffer != NULL)
free(new_buffer);
return retval;
}
static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word)
{
struct target *target = bank->target;
target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(address));
target_write_u32(target, PIC32MX_NVMDATA, word);
return pic32mx_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5);
}
static int pic32mx_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
{
uint32_t words_remaining = (count / 4);
uint32_t bytes_remaining = (count & 0x00000003);
uint32_t address = bank->base + offset;
uint32_t bytes_written = 0;
uint32_t status;
int retval;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
LOG_DEBUG("writing to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32
" count: 0x%8.8" PRIx32 "", bank->base, offset, count);
if (offset & 0x3) {
LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
/* multiple words (4-byte) to be programmed? */
if (words_remaining > 0) {
/* try using a block write */
retval = pic32mx_write_block(bank, buffer, offset, words_remaining);
if (retval != ERROR_OK) {
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single dword accesses */
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
} else if (retval == ERROR_FLASH_OPERATION_FAILED) {
LOG_ERROR("flash writing failed");
return retval;
}
} else {
buffer += words_remaining * 4;
address += words_remaining * 4;
words_remaining = 0;
}
}
while (words_remaining > 0) {
uint32_t value;
memcpy(&value, buffer + bytes_written, sizeof(uint32_t));
status = pic32mx_write_word(bank, address, value);
if (status & NVMCON_NVMERR) {
LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
return ERROR_FLASH_OPERATION_FAILED;
}
if (status & NVMCON_LVDERR) {
LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
return ERROR_FLASH_OPERATION_FAILED;
}
bytes_written += 4;
words_remaining--;
address += 4;
}
if (bytes_remaining) {
uint32_t value = 0xffffffff;
memcpy(&value, buffer + bytes_written, bytes_remaining);
status = pic32mx_write_word(bank, address, value);
if (status & NVMCON_NVMERR) {
LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
return ERROR_FLASH_OPERATION_FAILED;
}
if (status & NVMCON_LVDERR) {
LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
return ERROR_FLASH_OPERATION_FAILED;
}
}
return ERROR_OK;
}
static int pic32mx_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
struct mips32_common *mips32 = target->arch_info;
struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
int i;
uint32_t num_pages = 0;
uint32_t device_id;
int page_size;
pic32mx_info->probed = 0;
device_id = ejtag_info->idcode;
LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%04x, ver 0x%02x)",
device_id,
(unsigned)((device_id >> 1) & 0x7ff),
(unsigned)((device_id >> 12) & 0xffff),
(unsigned)((device_id >> 28) & 0xf));
if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
LOG_WARNING("Cannot identify target as a PIC32MX family.");
return ERROR_FLASH_OPERATION_FAILED;
}
/* Check for PIC32mx1xx/2xx */
for (i = 0; pic32mx_devs[i].name != NULL; i++) {
if (pic32mx_devs[i].devid == (device_id & 0x0fffffff)) {
if ((*(pic32mx_devs[i].name) == '1') || (*(pic32mx_devs[i].name) == '2'))
pic32mx_info->dev_type = MX_1_2;
break;
}
}
if (pic32mx_info->dev_type == MX_1_2)
page_size = 1024;
else
page_size = 4096;
if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH) {
/* 0x1FC00000: Boot flash size */
#if 0
/* for some reason this register returns 8k for the boot bank size
* this does not match the docs, so for now set the boot bank at a
* fixed 12k */
if (target_read_u32(target, PIC32MX_BMXBOOTSZ, &num_pages) != ERROR_OK) {
LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 12k flash");
num_pages = (12 * 1024);
}
#else
/* fixed 12k boot bank - see comments above */
if (pic32mx_info->dev_type == MX_1_2)
num_pages = (3 * 1024);
else
num_pages = (12 * 1024);
#endif
} else {
/* read the flash size from the device */
if (target_read_u32(target, PIC32MX_BMXPFMSZ, &num_pages) != ERROR_OK) {
if (pic32mx_info->dev_type == MX_1_2) {
LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 32k flash");
num_pages = (32 * 1024);
} else {
LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 512k flash");
num_pages = (512 * 1024);
}
}
}
LOG_INFO("flash size = %" PRId32 "kbytes", num_pages / 1024);
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
/* calculate numbers of pages */
num_pages /= page_size;
bank->size = (num_pages * page_size);
bank->num_sectors = num_pages;
bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
for (i = 0; i < (int)num_pages; i++) {
bank->sectors[i].offset = i * page_size;
bank->sectors[i].size = page_size;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
pic32mx_info->probed = 1;
return ERROR_OK;
}
static int pic32mx_auto_probe(struct flash_bank *bank)
{
struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
if (pic32mx_info->probed)
return ERROR_OK;
return pic32mx_probe(bank);
}
static int pic32mx_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct target *target = bank->target;
struct mips32_common *mips32 = target->arch_info;
struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
uint32_t device_id;
int printed = 0, i;
device_id = ejtag_info->idcode;
if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
snprintf(buf, buf_size,
"Cannot identify target as a PIC32MX family (manufacturer 0x%03d != 0x%03d)\n",
(unsigned)((device_id >> 1) & 0x7ff),
PIC32MX_MANUF_ID);
return ERROR_FLASH_OPERATION_FAILED;
}
for (i = 0; pic32mx_devs[i].name != NULL; i++) {
if (pic32mx_devs[i].devid == (device_id & 0x0fffffff)) {
printed = snprintf(buf, buf_size, "PIC32MX%s", pic32mx_devs[i].name);
break;
}
}
if (pic32mx_devs[i].name == NULL)
printed = snprintf(buf, buf_size, "Unknown");
buf += printed;
buf_size -= printed;
snprintf(buf, buf_size, " Ver: 0x%02x",
(unsigned)((device_id >> 28) & 0xf));
return ERROR_OK;
}
COMMAND_HANDLER(pic32mx_handle_pgm_word_command)
{
uint32_t address, value;
int status, res;
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank);
if (ERROR_OK != retval)
return retval;
if (address < bank->base || address >= (bank->base + bank->size)) {
command_print(CMD_CTX, "flash address '%s' is out of bounds", CMD_ARGV[0]);
return ERROR_OK;
}
res = ERROR_OK;
status = pic32mx_write_word(bank, address, value);
if (status & NVMCON_NVMERR)
res = ERROR_FLASH_OPERATION_FAILED;
if (status & NVMCON_LVDERR)
res = ERROR_FLASH_OPERATION_FAILED;
if (res == ERROR_OK)
command_print(CMD_CTX, "pic32mx pgm word complete");
else
command_print(CMD_CTX, "pic32mx pgm word failed (status = 0x%x)", status);
return ERROR_OK;
}
COMMAND_HANDLER(pic32mx_handle_unlock_command)
{
uint32_t mchip_cmd;
struct target *target = NULL;
struct mips_m4k_common *mips_m4k;
struct mips_ejtag *ejtag_info;
int timeout = 10;
if (CMD_ARGC < 1) {
command_print(CMD_CTX, "pic32mx unlock <bank>");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
target = bank->target;
mips_m4k = target_to_m4k(target);
ejtag_info = &mips_m4k->mips32.ejtag_info;
/* we have to use the MTAP to perform a full erase */
mips_ejtag_set_instr(ejtag_info, MTAP_SW_MTAP);
mips_ejtag_set_instr(ejtag_info, MTAP_COMMAND);
/* first check status of device */
mchip_cmd = MCHP_STATUS;
mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);
if (mchip_cmd & (1 << 7)) {
/* device is not locked */
command_print(CMD_CTX, "pic32mx is already unlocked, erasing anyway");
}
/* unlock/erase device */
mips_ejtag_drscan_8_out(ejtag_info, MCHP_ASERT_RST);
jtag_add_sleep(200);
mips_ejtag_drscan_8_out(ejtag_info, MCHP_ERASE);
do {
mchip_cmd = MCHP_STATUS;
mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);
if (timeout-- == 0) {
LOG_DEBUG("timeout waiting for unlock: 0x%" PRIx32 "", mchip_cmd);
break;
}
alive_sleep(1);
} while ((mchip_cmd & (1 << 2)) || (!(mchip_cmd & (1 << 3))));
mips_ejtag_drscan_8_out(ejtag_info, MCHP_DE_ASSERT_RST);
/* select ejtag tap */
mips_ejtag_set_instr(ejtag_info, MTAP_SW_ETAP);
command_print(CMD_CTX, "pic32mx unlocked.\n"
"INFO: a reset or power cycle is required "
"for the new settings to take effect.");
return ERROR_OK;
}
static const struct command_registration pic32mx_exec_command_handlers[] = {
{
.name = "pgm_word",
.usage = "<addr> <value> <bank>",
.handler = pic32mx_handle_pgm_word_command,
.mode = COMMAND_EXEC,
.help = "program a word",
},
{
.name = "unlock",
.handler = pic32mx_handle_unlock_command,
.mode = COMMAND_EXEC,
.usage = "[bank_id]",
.help = "Unlock/Erase entire device.",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration pic32mx_command_handlers[] = {
{
.name = "pic32mx",
.mode = COMMAND_ANY,
.help = "pic32mx flash command group",
.usage = "",
.chain = pic32mx_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver pic32mx_flash = {
.name = "pic32mx",
.commands = pic32mx_command_handlers,
.flash_bank_command = pic32mx_flash_bank_command,
.erase = pic32mx_erase,
.protect = pic32mx_protect,
.write = pic32mx_write,
.read = default_flash_read,
.probe = pic32mx_probe,
.auto_probe = pic32mx_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = pic32mx_protect_check,
.info = pic32mx_info,
};
+71
View File
@@ -0,0 +1,71 @@
/***************************************************************************
* Copyright (C) 2012 by George Harris *
* george@luminairecoffee.com *
* *
* Copyright (C) 2010 by Antonio Borneo *
* borneo.antonio@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "spi.h"
#include <jtag/jtag.h>
/* Shared table of known SPI flash devices for SPI-based flash drivers. Taken
* from device datasheets and Linux SPI flash drivers. */
struct flash_device flash_devices[] = {
/* name, erase_cmd, chip_erase_cmd, device_id, pagesize, sectorsize, size_in_bytes */
FLASH_ID("st m25p05", 0xd8, 0xC7, 0x00102020, 0x80, 0x8000, 0x10000),
FLASH_ID("st m25p10", 0xd8, 0xC7, 0x00112020, 0x80, 0x8000, 0x20000),
FLASH_ID("st m25p20", 0xd8, 0xC7, 0x00122020, 0x100, 0x10000, 0x40000),
FLASH_ID("st m25p40", 0xd8, 0xC7, 0x00132020, 0x100, 0x10000, 0x80000),
FLASH_ID("st m25p80", 0xd8, 0xC7, 0x00142020, 0x100, 0x10000, 0x100000),
FLASH_ID("st m25p16", 0xd8, 0xC7, 0x00152020, 0x100, 0x10000, 0x200000),
FLASH_ID("st m25p32", 0xd8, 0xC7, 0x00162020, 0x100, 0x10000, 0x400000),
FLASH_ID("st m25p64", 0xd8, 0xC7, 0x00172020, 0x100, 0x10000, 0x800000),
FLASH_ID("st m25p128", 0xd8, 0xC7, 0x00182020, 0x100, 0x40000, 0x1000000),
FLASH_ID("st m45pe10", 0xd8, 0xd8, 0x00114020, 0x100, 0x10000, 0x20000),
FLASH_ID("st m45pe20", 0xd8, 0xd8, 0x00124020, 0x100, 0x10000, 0x40000),
FLASH_ID("st m45pe40", 0xd8, 0xd8, 0x00134020, 0x100, 0x10000, 0x80000),
FLASH_ID("st m45pe80", 0xd8, 0xd8, 0x00144020, 0x100, 0x10000, 0x100000),
FLASH_ID("sp s25fl004", 0xd8, 0xC7, 0x00120201, 0x100, 0x10000, 0x80000),
FLASH_ID("sp s25fl008", 0xd8, 0xC7, 0x00130201, 0x100, 0x10000, 0x100000),
FLASH_ID("sp s25fl016", 0xd8, 0xC7, 0x00140201, 0x100, 0x10000, 0x200000),
FLASH_ID("sp s25fl032", 0xd8, 0xC7, 0x00150201, 0x100, 0x10000, 0x400000),
FLASH_ID("sp s25fl064", 0xd8, 0xC7, 0x00160201, 0x100, 0x10000, 0x800000),
FLASH_ID("atmel 25f512", 0x52, 0xC7, 0x0065001f, 0x80, 0x8000, 0x10000),
FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000),
FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000),
FLASH_ID("atmel 25f4096", 0x52, 0x62, 0x0064001f, 0x100, 0x10000, 0x80000),
FLASH_ID("atmel 25fs040", 0xd7, 0xC7, 0x0004661f, 0x100, 0x10000, 0x80000),
FLASH_ID("mac 25l512", 0xd8, 0xC7, 0x001020c2, 0x010, 0x10000, 0x10000),
FLASH_ID("mac 25l1005", 0xd8, 0xd8, 0x001120c2, 0x010, 0x10000, 0x20000),
FLASH_ID("mac 25l2005", 0xd8, 0xC7, 0x001220c2, 0x010, 0x10000, 0x40000),
FLASH_ID("mac 25l4005", 0xd8, 0xC7, 0x001320c2, 0x010, 0x10000, 0x80000),
FLASH_ID("mac 25l8005", 0xd8, 0xC7, 0x001420c2, 0x010, 0x10000, 0x100000),
FLASH_ID("mac 25l1605", 0xd8, 0xC7, 0x001520c2, 0x100, 0x10000, 0x200000),
FLASH_ID("mac 25l3205", 0xd8, 0xC7, 0x001620c2, 0x100, 0x10000, 0x400000),
FLASH_ID("mac 25l6405", 0xd8, 0xC7, 0x001720c2, 0x100, 0x10000, 0x800000),
FLASH_ID("win w25q32dw", 0xd8, 0xC7, 0x001660ef, 0x100, 0x10000, 0x400000),
FLASH_ID("win w25q64cv", 0xd8, 0xC7, 0x001740ef, 0x100, 0x10000, 0x800000),
FLASH_ID(NULL, 0, 0, 0, 0, 0, 0)
};
+58
View File
@@ -0,0 +1,58 @@
/***************************************************************************
* Copyright (C) 2012 by George Harris *
* george@luminairecoffee.com *
* *
* Copyright (C) 2010 by Antonio Borneo *
* borneo.antonio@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/* data structure to maintain flash ids from different vendors */
struct flash_device {
char *name;
uint8_t erase_cmd;
uint8_t chip_erase_cmd;
uint32_t device_id;
uint32_t pagesize;
unsigned long sectorsize;
unsigned long size_in_bytes;
};
#define FLASH_ID(n, es, ces, id, psize, ssize, size) \
{ \
.name = n, \
.erase_cmd = es, \
.chip_erase_cmd = ces, \
.device_id = id, \
.pagesize = psize, \
.sectorsize = ssize, \
.size_in_bytes = size \
}
extern struct flash_device flash_devices[];
/* fields in SPI flash status register */
#define SPIFLASH_BSY_BIT 0x00000001 /* WIP Bit of SPI SR on SMI SR */
#define SPIFLASH_WE_BIT 0x00000002 /* WEL Bit of SPI SR on SMI SR */
/* SPI Flash Commands */
#define SPIFLASH_READ_ID 0x9F /* Read Flash Identification */
#define SPIFLASH_READ_STATUS 0x05 /* Read Status Register */
#define SPIFLASH_WRITE_ENABLE 0x06 /* Write Enable */
#define SPIFLASH_PAGE_PROGRAM 0x02 /* Page Program */
#define SPIFLASH_FAST_READ 0x0B /* Fast Read */
#define SPIFLASH_READ 0x03 /* Normal Read */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+999
View File
@@ -0,0 +1,999 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2011 by Clement Burin des Roziers *
* clement.burin-des-roziers@hikob.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
#include <target/cortex_m.h>
/* stm32lx flash register locations */
#define FLASH_BASE 0x40023C00
#define FLASH_ACR 0x40023C00
#define FLASH_PECR 0x40023C04
#define FLASH_PDKEYR 0x40023C08
#define FLASH_PEKEYR 0x40023C0C
#define FLASH_PRGKEYR 0x40023C10
#define FLASH_OPTKEYR 0x40023C14
#define FLASH_SR 0x40023C18
#define FLASH_OBR 0x40023C1C
#define FLASH_WRPR 0x40023C20
/* FLASH_ACR bites */
#define FLASH_ACR__LATENCY (1<<0)
#define FLASH_ACR__PRFTEN (1<<1)
#define FLASH_ACR__ACC64 (1<<2)
#define FLASH_ACR__SLEEP_PD (1<<3)
#define FLASH_ACR__RUN_PD (1<<4)
/* FLASH_PECR bits */
#define FLASH_PECR__PELOCK (1<<0)
#define FLASH_PECR__PRGLOCK (1<<1)
#define FLASH_PECR__OPTLOCK (1<<2)
#define FLASH_PECR__PROG (1<<3)
#define FLASH_PECR__DATA (1<<4)
#define FLASH_PECR__FTDW (1<<8)
#define FLASH_PECR__ERASE (1<<9)
#define FLASH_PECR__FPRG (1<<10)
#define FLASH_PECR__EOPIE (1<<16)
#define FLASH_PECR__ERRIE (1<<17)
#define FLASH_PECR__OBL_LAUNCH (1<<18)
/* FLASH_SR bits */
#define FLASH_SR__BSY (1<<0)
#define FLASH_SR__EOP (1<<1)
#define FLASH_SR__ENDHV (1<<2)
#define FLASH_SR__READY (1<<3)
#define FLASH_SR__WRPERR (1<<8)
#define FLASH_SR__PGAERR (1<<9)
#define FLASH_SR__SIZERR (1<<10)
#define FLASH_SR__OPTVERR (1<<11)
/* Unlock keys */
#define PEKEY1 0x89ABCDEF
#define PEKEY2 0x02030405
#define PRGKEY1 0x8C9DAEBF
#define PRGKEY2 0x13141516
#define OPTKEY1 0xFBEAD9C8
#define OPTKEY2 0x24252627
/* other registers */
#define DBGMCU_IDCODE 0xE0042000
#define F_SIZE 0x1FF8004C
/* Constants */
#define FLASH_PAGE_SIZE 256
#define FLASH_SECTOR_SIZE 4096
#define FLASH_PAGES_PER_SECTOR 16
#define FLASH_BANK0_ADDRESS 0x08000000
/* stm32lx option byte register location */
#define OB_RDP 0x1FF80000
#define OB_USER 0x1FF80004
#define OB_WRP0_1 0x1FF80008
#define OB_WRP2_3 0x1FF8000C
/* OB_RDP values */
#define OB_RDP__LEVEL0 0xFF5500AA
#define OB_RDP__LEVEL1 0xFFFF0000
/* stm32lx RCC register locations */
#define RCC_CR 0x40023800
#define RCC_ICSCR 0x40023804
#define RCC_CFGR 0x40023808
/* RCC_ICSCR bits */
#define RCC_ICSCR__MSIRANGE_MASK (7<<13)
static int stm32lx_unlock_program_memory(struct flash_bank *bank);
static int stm32lx_lock_program_memory(struct flash_bank *bank);
static int stm32lx_enable_write_half_page(struct flash_bank *bank);
static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
struct stm32lx_flash_bank {
int probed;
bool has_dual_banks;
uint32_t user_bank_size;
};
/* flash bank stm32lx <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
{
struct stm32lx_flash_bank *stm32lx_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
/* Create the bank structure */
stm32lx_info = malloc(sizeof(struct stm32lx_flash_bank));
/* Check allocation */
if (stm32lx_info == NULL) {
LOG_ERROR("failed to allocate bank structure");
return ERROR_FAIL;
}
bank->driver_priv = stm32lx_info;
stm32lx_info->probed = 0;
stm32lx_info->has_dual_banks = false;
stm32lx_info->user_bank_size = bank->size;
return ERROR_OK;
}
static int stm32lx_protect_check(struct flash_bank *bank)
{
int retval;
struct target *target = bank->target;
uint32_t wrpr;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/*
* Read the WRPR word, and check each bit (corresponding to each
* flash sector
*/
retval = target_read_u32(target, FLASH_WRPR, &wrpr);
if (retval != ERROR_OK)
return retval;
for (int i = 0; i < 32; i++) {
if (wrpr & (1 << i))
bank->sectors[i].is_protected = 1;
else
bank->sectors[i].is_protected = 0;
}
return ERROR_OK;
}
static int stm32lx_erase(struct flash_bank *bank, int first, int last)
{
int retval;
/*
* It could be possible to do a mass erase if all sectors must be
* erased, but it is not implemented yet.
*/
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/*
* Loop over the selected sectors and erase them
*/
for (int i = first; i <= last; i++) {
retval = stm32lx_erase_sector(bank, i);
if (retval != ERROR_OK)
return retval;
bank->sectors[i].is_erased = 1;
}
return ERROR_OK;
}
static int stm32lx_protect(struct flash_bank *bank, int set, int first,
int last)
{
LOG_WARNING("protection of the STM32L flash is not implemented");
return ERROR_OK;
}
static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 16384;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[3];
struct armv7m_algorithm armv7m_info;
int retval = ERROR_OK;
/* see contib/loaders/flash/stm32lx.S for src */
static const uint8_t stm32lx_flash_write_code[] = {
/* write_word: */
0x00, 0x23, /* movs r3, #0 */
0x04, 0xe0, /* b test_done */
/* write_word: */
0x51, 0xf8, 0x04, 0xcb, /* ldr ip, [r1], #4 */
0x40, 0xf8, 0x04, 0xcb, /* str ip, [r0], #4 */
0x01, 0x33, /* adds r3, #1 */
/* test_done: */
0x93, 0x42, /* cmp r3, r2 */
0xf8, 0xd3, /* bcc write_word */
0x00, 0xbe, /* bkpt 0 */
};
/* Check if there is an even number of half pages (128bytes) */
if (count % 128) {
LOG_ERROR("there should be an even number "
"of half pages = 128 bytes (count = %" PRIi32 " bytes)", count);
return ERROR_FAIL;
}
/* flash write code */
if (target_alloc_working_area(target, sizeof(stm32lx_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_DEBUG("no working area for block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
/* Write the flashing code */
retval = target_write_buffer(target,
write_algorithm->address,
sizeof(stm32lx_flash_write_code),
(uint8_t *)stm32lx_flash_write_code);
if (retval != ERROR_OK) {
target_free_working_area(target, write_algorithm);
return retval;
}
/* Allocate half pages memory */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
if (buffer_size > 1024)
buffer_size -= 1024;
else
buffer_size /= 2;
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a
* buffer, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING("no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
armv7m_info.core_mode = ARM_MODE_THREAD;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
/* Enable half-page write */
retval = stm32lx_enable_write_half_page(bank);
if (retval != ERROR_OK) {
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
return retval;
}
struct armv7m_common *armv7m = target_to_armv7m(target);
if (armv7m == NULL) {
/* something is very wrong if armv7m is NULL */
LOG_ERROR("unable to get armv7m target");
return retval;
}
/* save any DEMCR flags and configure target to catch any Hard Faults */
uint32_t demcr_save = armv7m->demcr;
armv7m->demcr = VC_HARDERR;
/* Loop while there are bytes to write */
while (count > 0) {
uint32_t this_count;
this_count = (count > buffer_size) ? buffer_size : count;
/* Write the next half pages */
retval = target_write_buffer(target, source->address, this_count, buffer);
if (retval != ERROR_OK)
break;
/* 4: Store useful information in the registers */
/* the destination address of the copy (R0) */
buf_set_u32(reg_params[0].value, 0, 32, address);
/* The source address of the copy (R1) */
buf_set_u32(reg_params[1].value, 0, 32, source->address);
/* The length of the copy (R2) */
buf_set_u32(reg_params[2].value, 0, 32, this_count / 4);
/* 5: Execute the bunch of code */
retval = target_run_algorithm(target, 0, NULL, sizeof(reg_params)
/ sizeof(*reg_params), reg_params,
write_algorithm->address, 0, 10000, &armv7m_info);
if (retval != ERROR_OK)
break;
/* check for Hard Fault */
if (armv7m->exception_number == 3)
break;
/* 6: Wait while busy */
retval = stm32lx_wait_until_bsy_clear(bank);
if (retval != ERROR_OK)
break;
buffer += this_count;
address += this_count;
count -= this_count;
}
/* restore previous flags */
armv7m->demcr = demcr_save;
if (armv7m->exception_number == 3) {
/* the stm32l15x devices seem to have an issue when blank.
* if a ram loader is executed on a blank device it will
* Hard Fault, this issue does not happen for a already programmed device.
* A related issue is described in the stm32l151xx errata (Doc ID 17721 Rev 6 - 2.1.3).
* The workaround of handling the Hard Fault exception does work, but makes the
* loader more complicated, as a compromise we manually write the pages, programming time
* is reduced by 50% using this slower method.
*/
LOG_WARNING("couldn't use loader, falling back to page memory writes");
while (count > 0) {
uint32_t this_count;
this_count = (count > 128) ? 128 : count;
/* Write the next half pages */
retval = target_write_buffer(target, address, this_count, buffer);
if (retval != ERROR_OK)
break;
/* Wait while busy */
retval = stm32lx_wait_until_bsy_clear(bank);
if (retval != ERROR_OK)
break;
buffer += this_count;
address += this_count;
count -= this_count;
}
}
if (retval == ERROR_OK)
retval = stm32lx_lock_program_memory(bank);
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
return retval;
}
static int stm32lx_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t halfpages_number;
uint32_t bytes_remaining = 0;
uint32_t address = bank->base + offset;
uint32_t bytes_written = 0;
int retval, retval2;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset & 0x3) {
LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
retval = stm32lx_unlock_program_memory(bank);
if (retval != ERROR_OK)
return retval;
/* first we need to write any unaligned head bytes upto
* the next 128 byte page */
if (offset % 128)
bytes_remaining = MIN(count, 128 - (offset % 128));
while (bytes_remaining > 0) {
uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
/* copy remaining bytes into the write buffer */
uint32_t bytes_to_write = MIN(4, bytes_remaining);
memcpy(value, buffer + bytes_written, bytes_to_write);
retval = target_write_buffer(target, address, 4, value);
if (retval != ERROR_OK)
goto reset_pg_and_lock;
bytes_written += bytes_to_write;
bytes_remaining -= bytes_to_write;
address += 4;
retval = stm32lx_wait_until_bsy_clear(bank);
if (retval != ERROR_OK)
goto reset_pg_and_lock;
}
offset += bytes_written;
count -= bytes_written;
/* this should always pass this check here */
assert((offset % 128) == 0);
/* calculate half pages */
halfpages_number = count / 128;
if (halfpages_number) {
retval = stm32lx_write_half_pages(bank, buffer + bytes_written, offset, 128 * halfpages_number);
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* attempt slow memory writes */
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
halfpages_number = 0;
} else {
if (retval != ERROR_OK)
return ERROR_FAIL;
}
}
/* write any remaining bytes */
uint32_t page_bytes_written = 128 * halfpages_number;
bytes_written += page_bytes_written;
address += page_bytes_written;
bytes_remaining = count - page_bytes_written;
retval = stm32lx_unlock_program_memory(bank);
if (retval != ERROR_OK)
return retval;
while (bytes_remaining > 0) {
uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
/* copy remaining bytes into the write buffer */
uint32_t bytes_to_write = MIN(4, bytes_remaining);
memcpy(value, buffer + bytes_written, bytes_to_write);
retval = target_write_buffer(target, address, 4, value);
if (retval != ERROR_OK)
goto reset_pg_and_lock;
bytes_written += bytes_to_write;
bytes_remaining -= bytes_to_write;
address += 4;
retval = stm32lx_wait_until_bsy_clear(bank);
if (retval != ERROR_OK)
goto reset_pg_and_lock;
}
reset_pg_and_lock:
retval2 = stm32lx_lock_program_memory(bank);
if (retval == ERROR_OK)
retval = retval2;
return retval;
}
static int stm32lx_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
int i;
uint16_t flash_size_in_kb;
uint16_t max_flash_size_in_kb;
uint32_t device_id;
uint32_t base_address = FLASH_BANK0_ADDRESS;
uint32_t second_bank_base;
uint32_t first_bank_size_in_kb;
stm32lx_info->probed = 0;
/* read stm32 device id register */
int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
if (retval != ERROR_OK)
return retval;
LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id);
/* set max flash size depending on family */
switch (device_id & 0xfff) {
case 0x416:
max_flash_size_in_kb = 128;
break;
case 0x427:
/* single bank, high density */
max_flash_size_in_kb = 256;
break;
case 0x436:
/* According to ST, the devices with id 0x436 have dual bank flash and comes with
* a total flash size of 384k or 256kb. However, the first bank is always 192kb,
* and second one holds the rest. The reason is that the 256kb version is actually
* the same physical flash but only the first 256kb are verified.
*/
max_flash_size_in_kb = 384;
first_bank_size_in_kb = 192;
stm32lx_info->has_dual_banks = true;
break;
default:
LOG_WARNING("Cannot identify target as a STM32L family.");
return ERROR_FAIL;
}
/* Get the flash size from target. */
retval = target_read_u16(target, F_SIZE, &flash_size_in_kb);
/* Failed reading flash size or flash size invalid (early silicon),
* default to max target family */
if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
LOG_WARNING("STM32L flash size failed, probe inaccurate - assuming %dk flash",
max_flash_size_in_kb);
flash_size_in_kb = max_flash_size_in_kb;
} else if (flash_size_in_kb > max_flash_size_in_kb) {
LOG_WARNING("STM32L probed flash size assumed incorrect since FLASH_SIZE=%dk > %dk, - assuming %dk flash",
flash_size_in_kb, max_flash_size_in_kb, max_flash_size_in_kb);
flash_size_in_kb = max_flash_size_in_kb;
}
if (stm32lx_info->has_dual_banks) {
/* Use the configured base address to determine if this is the first or second flash bank.
* Verify that the base address is reasonably correct and determine the flash bank size
*/
second_bank_base = base_address + first_bank_size_in_kb * 1024;
if (bank->base == second_bank_base) {
/* This is the second bank */
base_address = second_bank_base;
flash_size_in_kb = flash_size_in_kb - first_bank_size_in_kb;
} else if (bank->base == 0 || bank->base == base_address) {
/* This is the first bank */
flash_size_in_kb = first_bank_size_in_kb;
} else {
LOG_WARNING("STM32L flash bank base address config is incorrect. 0x%x but should rather be 0x%x or 0x%x",
bank->base, base_address, second_bank_base);
return ERROR_FAIL;
}
LOG_INFO("STM32L flash has dual banks. Bank (%d) size is %dkb, base address is 0x%x",
bank->bank_number, flash_size_in_kb, base_address);
} else {
LOG_INFO("STM32L flash size is %dkb, base address is 0x%x", flash_size_in_kb, base_address);
}
/* if the user sets the size manually then ignore the probed value
* this allows us to work around devices that have a invalid flash size register value */
if (stm32lx_info->user_bank_size) {
flash_size_in_kb = stm32lx_info->user_bank_size / 1024;
LOG_INFO("ignoring flash probed value, using configured bank size: %dkbytes", flash_size_in_kb);
}
/* STM32L - we have 32 sectors, 16 pages per sector -> 512 pages
* 16 pages for a protection area */
/* calculate numbers of sectors (4kB per sector) */
int num_sectors = (flash_size_in_kb * 1024) / FLASH_SECTOR_SIZE;
if (bank->sectors) {
free(bank->sectors);
bank->sectors = NULL;
}
bank->size = flash_size_in_kb * 1024;
bank->base = base_address;
bank->num_sectors = num_sectors;
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
if (bank->sectors == NULL) {
LOG_ERROR("failed to allocate bank sectors");
return ERROR_FAIL;
}
for (i = 0; i < num_sectors; i++) {
bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
bank->sectors[i].size = FLASH_SECTOR_SIZE;
bank->sectors[i].is_erased = -1;
bank->sectors[i].is_protected = 1;
}
stm32lx_info->probed = 1;
return ERROR_OK;
}
static int stm32lx_auto_probe(struct flash_bank *bank)
{
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
if (stm32lx_info->probed)
return ERROR_OK;
return stm32lx_probe(bank);
}
static int stm32lx_erase_check(struct flash_bank *bank)
{
struct target *target = bank->target;
const int buffer_size = 4096;
int i;
uint32_t nBytes;
int retval = ERROR_OK;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
uint8_t *buffer = malloc(buffer_size);
if (buffer == NULL) {
LOG_ERROR("failed to allocate read buffer");
return ERROR_FAIL;
}
for (i = 0; i < bank->num_sectors; i++) {
uint32_t j;
bank->sectors[i].is_erased = 1;
/* Loop chunk by chunk over the sector */
for (j = 0; j < bank->sectors[i].size; j += buffer_size) {
uint32_t chunk;
chunk = buffer_size;
if (chunk > (j - bank->sectors[i].size))
chunk = (j - bank->sectors[i].size);
retval = target_read_memory(target, bank->base
+ bank->sectors[i].offset + j, 4, chunk / 4, buffer);
if (retval != ERROR_OK)
break;
for (nBytes = 0; nBytes < chunk; nBytes++) {
if (buffer[nBytes] != 0x00) {
bank->sectors[i].is_erased = 0;
break;
}
}
}
if (retval != ERROR_OK)
break;
}
free(buffer);
return retval;
}
static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
{
/* This method must return a string displaying information about the bank */
struct target *target = bank->target;
uint32_t device_id;
int printed;
/* read stm32 device id register */
int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
if (retval != ERROR_OK)
return retval;
if ((device_id & 0xfff) == 0x416) {
printed = snprintf(buf, buf_size, "stm32lx - Rev: ");
buf += printed;
buf_size -= printed;
switch (device_id >> 16) {
case 0x1000:
snprintf(buf, buf_size, "A");
break;
case 0x1008:
snprintf(buf, buf_size, "Y");
break;
case 0x1018:
snprintf(buf, buf_size, "X");
break;
case 0x1038:
snprintf(buf, buf_size, "W");
break;
case 0x1078:
snprintf(buf, buf_size, "V");
break;
default:
snprintf(buf, buf_size, "unknown");
break;
}
} else if ((device_id & 0xfff) == 0x436) {
printed = snprintf(buf, buf_size, "stm32lx (HD) - Rev: ");
buf += printed;
buf_size -= printed;
switch (device_id >> 16) {
case 0x1000:
snprintf(buf, buf_size, "A");
break;
case 0x1008:
snprintf(buf, buf_size, "Z");
break;
case 0x1018:
snprintf(buf, buf_size, "Y");
break;
default:
snprintf(buf, buf_size, "unknown");
break;
}
} else {
snprintf(buf, buf_size, "Cannot identify target as a stm32lx");
return ERROR_FAIL;
}
return ERROR_OK;
}
static const struct command_registration stm32lx_exec_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
static const struct command_registration stm32lx_command_handlers[] = {
{
.name = "stm32lx",
.mode = COMMAND_ANY,
.help = "stm32lx flash command group",
.usage = "",
.chain = stm32lx_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver stm32lx_flash = {
.name = "stm32lx",
.commands = stm32lx_command_handlers,
.flash_bank_command = stm32lx_flash_bank_command,
.erase = stm32lx_erase,
.protect = stm32lx_protect,
.write = stm32lx_write,
.read = default_flash_read,
.probe = stm32lx_probe,
.auto_probe = stm32lx_auto_probe,
.erase_check = stm32lx_erase_check,
.protect_check = stm32lx_protect_check,
.info = stm32lx_get_info,
};
/* Static methods implementation */
static int stm32lx_unlock_program_memory(struct flash_bank *bank)
{
struct target *target = bank->target;
int retval;
uint32_t reg32;
/*
* Unlocking the program memory is done by unlocking the PECR,
* then by writing the 2 PRGKEY to the PRGKEYR register
*/
/* check flash is not already unlocked */
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
if ((reg32 & FLASH_PECR__PRGLOCK) == 0)
return ERROR_OK;
/* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
if (retval != ERROR_OK)
return retval;
/* Make sure it worked */
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
if (reg32 & FLASH_PECR__PELOCK) {
LOG_ERROR("PELOCK is not cleared :(");
return ERROR_FLASH_OPERATION_FAILED;
}
retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY1);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY2);
if (retval != ERROR_OK)
return retval;
/* Make sure it worked */
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
if (reg32 & FLASH_PECR__PRGLOCK) {
LOG_ERROR("PRGLOCK is not cleared :(");
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int stm32lx_enable_write_half_page(struct flash_bank *bank)
{
struct target *target = bank->target;
int retval;
uint32_t reg32;
/**
* Unlock the program memory, then set the FPRG bit in the PECR register.
*/
retval = stm32lx_unlock_program_memory(bank);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
reg32 |= FLASH_PECR__FPRG;
retval = target_write_u32(target, FLASH_PECR, reg32);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
reg32 |= FLASH_PECR__PROG;
retval = target_write_u32(target, FLASH_PECR, reg32);
return retval;
}
static int stm32lx_lock_program_memory(struct flash_bank *bank)
{
struct target *target = bank->target;
int retval;
uint32_t reg32;
/* To lock the program memory, simply set the lock bit and lock PECR */
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
reg32 |= FLASH_PECR__PRGLOCK;
retval = target_write_u32(target, FLASH_PECR, reg32);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(target, FLASH_PECR, &reg32);
if (retval != ERROR_OK)
return retval;
reg32 |= FLASH_PECR__PELOCK;
retval = target_write_u32(target, FLASH_PECR, reg32);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
{
struct target *target = bank->target;
int retval;
uint32_t reg32;
/*
* To erase a sector (i.e. FLASH_PAGES_PER_SECTOR pages),
* first unlock the memory, loop over the pages of this sector
* and write 0x0 to its first word.
*/
retval = stm32lx_unlock_program_memory(bank);
if (retval != ERROR_OK)
return retval;
for (int page = 0; page < FLASH_PAGES_PER_SECTOR; page++) {
reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
retval = target_write_u32(target, FLASH_PECR, reg32);
if (retval != ERROR_OK)
return retval;
retval = stm32lx_wait_until_bsy_clear(bank);
if (retval != ERROR_OK)
return retval;
uint32_t addr = bank->base + bank->sectors[sector].offset + (page
* FLASH_PAGE_SIZE);
retval = target_write_u32(target, addr, 0x0);
if (retval != ERROR_OK)
return retval;
retval = stm32lx_wait_until_bsy_clear(bank);
if (retval != ERROR_OK)
return retval;
}
retval = stm32lx_lock_program_memory(bank);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
{
struct target *target = bank->target;
uint32_t status;
int retval = ERROR_OK;
int timeout = 100;
/* wait for busy to clear */
for (;;) {
retval = target_read_u32(target, FLASH_SR, &status);
if (retval != ERROR_OK)
return retval;
if ((status & FLASH_SR__BSY) == 0)
break;
if (timeout-- <= 0) {
LOG_ERROR("timed out waiting for flash");
return ERROR_FAIL;
}
alive_sleep(1);
}
if (status & FLASH_SR__WRPERR) {
LOG_ERROR("access denied / write protected");
retval = ERROR_FAIL;
}
if (status & FLASH_SR__PGAERR) {
LOG_ERROR("invalid program address");
retval = ERROR_FAIL;
}
return retval;
}
+659
View File
@@ -0,0 +1,659 @@
/***************************************************************************
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/* STM Serial Memory Interface (SMI) controller is a SPI bus controller
* specifically designed for SPI memories.
* Only SPI "mode 3" (CPOL=1 and CPHA=1) is supported.
* Two working modes are available:
* - SW mode: the SPI is controlled by SW. Any custom commands can be sent
* on the bus.
* - HW mode: the SPI but is under SMI control. Memory content is directly
* accessible in CPU memory space. CPU can read, write and execute memory
* content. */
/* ATTENTION:
* To have flash memory mapped in CPU memory space, the SMI controller
* have to be in "HW mode". This requires following constraints:
* 1) The command "reset init" have to initialize SMI controller and put
* it in HW mode;
* 2) every command in this file have to return to prompt in HW mode. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "spi.h"
#include <jtag/jtag.h>
#include <helper/time_support.h>
#define SMI_READ_REG(a) (_SMI_READ_REG(a))
#define _SMI_READ_REG(a) \
{ \
int __a; \
uint32_t __v; \
\
__a = target_read_u32(target, io_base + (a), &__v); \
if (__a != ERROR_OK) \
return __a; \
__v; \
}
#define SMI_WRITE_REG(a, v) \
{ \
int __r; \
\
__r = target_write_u32(target, io_base + (a), (v)); \
if (__r != ERROR_OK) \
return __r; \
}
#define SMI_POLL_TFF(timeout) \
{ \
int __r; \
\
__r = poll_tff(target, io_base, timeout); \
if (__r != ERROR_OK) \
return __r; \
}
#define SMI_SET_SW_MODE() SMI_WRITE_REG(SMI_CR1, \
SMI_READ_REG(SMI_CR1) | SMI_SW_MODE)
#define SMI_SET_HWWB_MODE() SMI_WRITE_REG(SMI_CR1, \
(SMI_READ_REG(SMI_CR1) | SMI_WB_MODE) & ~SMI_SW_MODE)
#define SMI_SET_HW_MODE() SMI_WRITE_REG(SMI_CR1, \
SMI_READ_REG(SMI_CR1) & ~(SMI_SW_MODE | SMI_WB_MODE))
#define SMI_CLEAR_TFF() SMI_WRITE_REG(SMI_SR, ~SMI_TFF)
#define SMI_BANK_SIZE (0x01000000)
#define SMI_CR1 (0x00) /* Control register 1 */
#define SMI_CR2 (0x04) /* Control register 2 */
#define SMI_SR (0x08) /* Status register */
#define SMI_TR (0x0c) /* TX */
#define SMI_RR (0x10) /* RX */
/* fields in SMI_CR1 */
#define SMI_SW_MODE 0x10000000 /* set to enable SW Mode */
#define SMI_WB_MODE 0x20000000 /* Write Burst Mode */
/* fields in SMI_CR2 */
#define SMI_TX_LEN_1 0x00000001 /* data length = 1 byte */
#define SMI_TX_LEN_4 0x00000004 /* data length = 4 byte */
#define SMI_RX_LEN_3 0x00000030 /* data length = 3 byte */
#define SMI_SEND 0x00000080 /* Send data */
#define SMI_RSR 0x00000400 /* reads status reg */
#define SMI_WE 0x00000800 /* Write Enable */
#define SMI_SEL_BANK0 0x00000000 /* Select Bank0 */
#define SMI_SEL_BANK1 0x00001000 /* Select Bank1 */
#define SMI_SEL_BANK2 0x00002000 /* Select Bank2 */
#define SMI_SEL_BANK3 0x00003000 /* Select Bank3 */
/* fields in SMI_SR */
#define SMI_TFF 0x00000100 /* Transfer Finished Flag */
/* Commands */
#define SMI_READ_ID 0x0000009F /* Read Flash Identification */
/* Timeout in ms */
#define SMI_CMD_TIMEOUT (100)
#define SMI_PROBE_TIMEOUT (100)
#define SMI_MAX_TIMEOUT (3000)
struct stmsmi_flash_bank {
int probed;
uint32_t io_base;
uint32_t bank_num;
struct flash_device *dev;
};
struct stmsmi_target {
char *name;
uint32_t tap_idcode;
uint32_t smi_base;
uint32_t io_base;
};
static struct stmsmi_target target_devices[] = {
/* name, tap_idcode, smi_base, io_base */
{ "SPEAr3xx/6xx", 0x07926041, 0xf8000000, 0xfc000000 },
{ "STR75x", 0x4f1f0041, 0x80000000, 0x90000000 },
{ NULL, 0, 0, 0 }
};
FLASH_BANK_COMMAND_HANDLER(stmsmi_flash_bank_command)
{
struct stmsmi_flash_bank *stmsmi_info;
LOG_DEBUG("%s", __func__);
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
stmsmi_info = malloc(sizeof(struct stmsmi_flash_bank));
if (stmsmi_info == NULL) {
LOG_ERROR("not enough memory");
return ERROR_FAIL;
}
bank->driver_priv = stmsmi_info;
stmsmi_info->probed = 0;
return ERROR_OK;
}
/* Poll transmit finished flag */
/* timeout in ms */
static int poll_tff(struct target *target, uint32_t io_base, int timeout)
{
long long endtime;
if (SMI_READ_REG(SMI_SR) & SMI_TFF)
return ERROR_OK;
endtime = timeval_ms() + timeout;
do {
alive_sleep(1);
if (SMI_READ_REG(SMI_SR) & SMI_TFF)
return ERROR_OK;
} while (timeval_ms() < endtime);
LOG_ERROR("Timeout while polling TFF");
return ERROR_FLASH_OPERATION_FAILED;
}
/* Read the status register of the external SPI flash chip.
* The operation is triggered by setting SMI_RSR bit.
* SMI sends the proper SPI command (0x05) and returns value in SMI_SR */
static int read_status_reg(struct flash_bank *bank, uint32_t *status)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
/* clear transmit finished flag */
SMI_CLEAR_TFF();
/* Read status */
SMI_WRITE_REG(SMI_CR2, stmsmi_info->bank_num | SMI_RSR);
/* Poll transmit finished flag */
SMI_POLL_TFF(SMI_CMD_TIMEOUT);
/* clear transmit finished flag */
SMI_CLEAR_TFF();
*status = SMI_READ_REG(SMI_SR) & 0x0000ffff;
/* clean-up SMI_CR2 */
SMI_WRITE_REG(SMI_CR2, 0); /* AB: Required ? */
return ERROR_OK;
}
/* check for WIP (write in progress) bit in status register */
/* timeout in ms */
static int wait_till_ready(struct flash_bank *bank, int timeout)
{
uint32_t status;
int retval;
long long endtime;
endtime = timeval_ms() + timeout;
do {
/* read flash status register */
retval = read_status_reg(bank, &status);
if (retval != ERROR_OK)
return retval;
if ((status & SPIFLASH_BSY_BIT) == 0)
return ERROR_OK;
alive_sleep(1);
} while (timeval_ms() < endtime);
LOG_ERROR("timeout");
return ERROR_FAIL;
}
/* Send "write enable" command to SPI flash chip.
* The operation is triggered by setting SMI_WE bit, and SMI sends
* the proper SPI command (0x06) */
static int smi_write_enable(struct flash_bank *bank)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
uint32_t status;
int retval;
/* Enter in HW mode */
SMI_SET_HW_MODE(); /* AB: is this correct ?*/
/* clear transmit finished flag */
SMI_CLEAR_TFF();
/* Send write enable command */
SMI_WRITE_REG(SMI_CR2, stmsmi_info->bank_num | SMI_WE);
/* Poll transmit finished flag */
SMI_POLL_TFF(SMI_CMD_TIMEOUT);
/* read flash status register */
retval = read_status_reg(bank, &status);
if (retval != ERROR_OK)
return retval;
/* Check write enabled */
if ((status & SPIFLASH_WE_BIT) == 0) {
LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status);
return ERROR_FAIL;
}
return ERROR_OK;
}
static uint32_t erase_command(struct stmsmi_flash_bank *stmsmi_info,
uint32_t offset)
{
union {
uint32_t command;
uint8_t x[4];
} cmd;
cmd.x[0] = stmsmi_info->dev->erase_cmd;
cmd.x[1] = offset >> 16;
cmd.x[2] = offset >> 8;
cmd.x[3] = offset;
return cmd.command;
}
static int smi_erase_sector(struct flash_bank *bank, int sector)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
uint32_t cmd;
int retval;
retval = smi_write_enable(bank);
if (retval != ERROR_OK)
return retval;
/* Switch to SW mode to send sector erase command */
SMI_SET_SW_MODE();
/* clear transmit finished flag */
SMI_CLEAR_TFF();
/* send SPI command "block erase" */
cmd = erase_command(stmsmi_info, bank->sectors[sector].offset);
SMI_WRITE_REG(SMI_TR, cmd);
SMI_WRITE_REG(SMI_CR2, stmsmi_info->bank_num | SMI_SEND | SMI_TX_LEN_4);
/* Poll transmit finished flag */
SMI_POLL_TFF(SMI_CMD_TIMEOUT);
/* poll WIP for end of self timed Sector Erase cycle */
retval = wait_till_ready(bank, SMI_MAX_TIMEOUT);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int stmsmi_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
int retval = ERROR_OK;
int sector;
LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
LOG_ERROR("Flash sector invalid");
return ERROR_FLASH_SECTOR_INVALID;
}
if (!(stmsmi_info->probed)) {
LOG_ERROR("Flash bank not probed");
return ERROR_FLASH_BANK_NOT_PROBED;
}
for (sector = first; sector <= last; sector++) {
if (bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
for (sector = first; sector <= last; sector++) {
retval = smi_erase_sector(bank, sector);
if (retval != ERROR_OK)
break;
keep_alive();
}
/* Switch to HW mode before return to prompt */
SMI_SET_HW_MODE();
return retval;
}
static int stmsmi_protect(struct flash_bank *bank, int set,
int first, int last)
{
int sector;
for (sector = first; sector <= last; sector++)
bank->sectors[sector].is_protected = set;
return ERROR_OK;
}
static int smi_write_buffer(struct flash_bank *bank, uint8_t *buffer,
uint32_t address, uint32_t len)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
int retval;
LOG_DEBUG("%s: address=0x%08" PRIx32 " len=0x%08" PRIx32,
__func__, address, len);
retval = smi_write_enable(bank);
if (retval != ERROR_OK)
return retval;
/* HW mode, write burst mode */
SMI_SET_HWWB_MODE();
retval = target_write_buffer(target, address, len, buffer);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int stmsmi_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
uint32_t cur_count, page_size, page_offset;
int sector;
int retval = ERROR_OK;
LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
__func__, offset, count);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset + count > stmsmi_info->dev->size_in_bytes) {
LOG_WARNING("Write pasts end of flash. Extra data discarded.");
count = stmsmi_info->dev->size_in_bytes - offset;
}
/* Check sector protection */
for (sector = 0; sector < bank->num_sectors; sector++) {
/* Start offset in or before this sector? */
/* End offset in or behind this sector? */
if ((offset <
(bank->sectors[sector].offset + bank->sectors[sector].size))
&& ((offset + count - 1) >= bank->sectors[sector].offset)
&& bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
page_size = stmsmi_info->dev->pagesize;
/* unaligned buffer head */
if (count > 0 && (offset & 3) != 0) {
cur_count = 4 - (offset & 3);
if (cur_count > count)
cur_count = count;
retval = smi_write_buffer(bank, buffer, bank->base + offset,
cur_count);
if (retval != ERROR_OK)
goto err;
offset += cur_count;
buffer += cur_count;
count -= cur_count;
}
page_offset = offset % page_size;
/* central part, aligned words */
while (count >= 4) {
/* clip block at page boundary */
if (page_offset + count > page_size)
cur_count = page_size - page_offset;
else
cur_count = count & ~3;
retval = smi_write_buffer(bank, buffer, bank->base + offset,
cur_count);
if (retval != ERROR_OK)
goto err;
page_offset = 0;
buffer += cur_count;
offset += cur_count;
count -= cur_count;
keep_alive();
}
/* buffer tail */
if (count > 0)
retval = smi_write_buffer(bank, buffer, bank->base + offset, count);
err:
/* Switch to HW mode before return to prompt */
SMI_SET_HW_MODE();
return retval;
}
/* Return ID of flash device */
/* On exit, SW mode is kept */
static int read_flash_id(struct flash_bank *bank, uint32_t *id)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base = stmsmi_info->io_base;
int retval;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* poll WIP */
retval = wait_till_ready(bank, SMI_PROBE_TIMEOUT);
if (retval != ERROR_OK)
return retval;
/* enter in SW mode */
SMI_SET_SW_MODE();
/* clear transmit finished flag */
SMI_CLEAR_TFF();
/* Send SPI command "read ID" */
SMI_WRITE_REG(SMI_TR, SMI_READ_ID);
SMI_WRITE_REG(SMI_CR2,
stmsmi_info->bank_num | SMI_SEND | SMI_RX_LEN_3 | SMI_TX_LEN_1);
/* Poll transmit finished flag */
SMI_POLL_TFF(SMI_CMD_TIMEOUT);
/* clear transmit finished flag */
SMI_CLEAR_TFF();
/* read ID from Receive Register */
*id = SMI_READ_REG(SMI_RR) & 0x00ffffff;
return ERROR_OK;
}
static int stmsmi_probe(struct flash_bank *bank)
{
struct target *target = bank->target;
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
uint32_t io_base;
struct flash_sector *sectors;
uint32_t id = 0; /* silence uninitialized warning */
struct stmsmi_target *target_device;
int retval;
if (stmsmi_info->probed)
free(bank->sectors);
stmsmi_info->probed = 0;
for (target_device = target_devices ; target_device->name ; ++target_device)
if (target_device->tap_idcode == target->tap->idcode)
break;
if (!target_device->name) {
LOG_ERROR("Device ID 0x%" PRIx32 " is not known as SMI capable",
target->tap->idcode);
return ERROR_FAIL;
}
switch (bank->base - target_device->smi_base) {
case 0:
stmsmi_info->bank_num = SMI_SEL_BANK0;
break;
case SMI_BANK_SIZE:
stmsmi_info->bank_num = SMI_SEL_BANK1;
break;
case 2*SMI_BANK_SIZE:
stmsmi_info->bank_num = SMI_SEL_BANK2;
break;
case 3*SMI_BANK_SIZE:
stmsmi_info->bank_num = SMI_SEL_BANK3;
break;
default:
LOG_ERROR("Invalid SMI base address 0x%" PRIx32, bank->base);
return ERROR_FAIL;
}
io_base = target_device->io_base;
stmsmi_info->io_base = io_base;
LOG_DEBUG("Valid SMI on device %s at address 0x%" PRIx32,
target_device->name, bank->base);
/* read and decode flash ID; returns in SW mode */
retval = read_flash_id(bank, &id);
SMI_SET_HW_MODE();
if (retval != ERROR_OK)
return retval;
stmsmi_info->dev = NULL;
for (struct flash_device *p = flash_devices; p->name ; p++)
if (p->device_id == id) {
stmsmi_info->dev = p;
break;
}
if (!stmsmi_info->dev) {
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
return ERROR_FAIL;
}
LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
stmsmi_info->dev->name, stmsmi_info->dev->device_id);
/* Set correct size value */
bank->size = stmsmi_info->dev->size_in_bytes;
/* create and fill sectors array */
bank->num_sectors =
stmsmi_info->dev->size_in_bytes / stmsmi_info->dev->sectorsize;
sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
if (sectors == NULL) {
LOG_ERROR("not enough memory");
return ERROR_FAIL;
}
for (int sector = 0; sector < bank->num_sectors; sector++) {
sectors[sector].offset = sector * stmsmi_info->dev->sectorsize;
sectors[sector].size = stmsmi_info->dev->sectorsize;
sectors[sector].is_erased = -1;
sectors[sector].is_protected = 1;
}
bank->sectors = sectors;
stmsmi_info->probed = 1;
return ERROR_OK;
}
static int stmsmi_auto_probe(struct flash_bank *bank)
{
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
if (stmsmi_info->probed)
return ERROR_OK;
return stmsmi_probe(bank);
}
static int stmsmi_protect_check(struct flash_bank *bank)
{
/* Nothing to do. Protection is only handled in SW. */
return ERROR_OK;
}
static int get_stmsmi_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct stmsmi_flash_bank *stmsmi_info = bank->driver_priv;
if (!(stmsmi_info->probed)) {
snprintf(buf, buf_size,
"\nSMI flash bank not probed yet\n");
return ERROR_OK;
}
snprintf(buf, buf_size, "\nSMI flash information:\n"
" Device \'%s\' (ID 0x%08x)\n",
stmsmi_info->dev->name, stmsmi_info->dev->device_id);
return ERROR_OK;
}
struct flash_driver stmsmi_flash = {
.name = "stmsmi",
.flash_bank_command = stmsmi_flash_bank_command,
.erase = stmsmi_erase,
.protect = stmsmi_protect,
.write = stmsmi_write,
.read = default_flash_read,
.probe = stmsmi_probe,
.auto_probe = stmsmi_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = stmsmi_protect_check,
.info = get_stmsmi_info,
};
+816
View File
@@ -0,0 +1,816 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2010 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <target/arm.h>
#include <helper/binarybuffer.h>
#include <target/algorithm.h>
/* Flash registers */
#define FLASH_CR0 0x00000000
#define FLASH_CR1 0x00000004
#define FLASH_DR0 0x00000008
#define FLASH_DR1 0x0000000C
#define FLASH_AR 0x00000010
#define FLASH_ER 0x00000014
#define FLASH_NVWPAR 0x0000DFB0
#define FLASH_NVAPR0 0x0000DFB8
#define FLASH_NVAPR1 0x0000DFBC
/* FLASH_CR0 register bits */
#define FLASH_WMS 0x80000000
#define FLASH_SUSP 0x40000000
#define FLASH_WPG 0x20000000
#define FLASH_DWPG 0x10000000
#define FLASH_SER 0x08000000
#define FLASH_SPR 0x01000000
#define FLASH_BER 0x04000000
#define FLASH_MER 0x02000000
#define FLASH_LOCK 0x00000010
#define FLASH_BSYA1 0x00000004
#define FLASH_BSYA0 0x00000002
/* FLASH_CR1 register bits */
#define FLASH_B1S 0x02000000
#define FLASH_B0S 0x01000000
#define FLASH_B1F1 0x00020000
#define FLASH_B1F0 0x00010000
#define FLASH_B0F7 0x00000080
#define FLASH_B0F6 0x00000040
#define FLASH_B0F5 0x00000020
#define FLASH_B0F4 0x00000010
#define FLASH_B0F3 0x00000008
#define FLASH_B0F2 0x00000004
#define FLASH_B0F1 0x00000002
#define FLASH_B0F0 0x00000001
/* FLASH_ER register bits */
#define FLASH_WPF 0x00000100
#define FLASH_RESER 0x00000080
#define FLASH_SEQER 0x00000040
#define FLASH_10ER 0x00000008
#define FLASH_PGER 0x00000004
#define FLASH_ERER 0x00000002
#define FLASH_ERR 0x00000001
struct str7x_flash_bank {
uint32_t *sector_bits;
uint32_t disable_bit;
uint32_t busy_bits;
uint32_t register_base;
};
struct str7x_mem_layout {
uint32_t sector_start;
uint32_t sector_size;
uint32_t sector_bit;
};
enum str7x_status_codes {
STR7X_CMD_SUCCESS = 0,
STR7X_INVALID_COMMAND = 1,
STR7X_SRC_ADDR_ERROR = 2,
STR7X_DST_ADDR_ERROR = 3,
STR7X_SRC_ADDR_NOT_MAPPED = 4,
STR7X_DST_ADDR_NOT_MAPPED = 5,
STR7X_COUNT_ERROR = 6,
STR7X_INVALID_SECTOR = 7,
STR7X_SECTOR_NOT_BLANK = 8,
STR7X_SECTOR_NOT_PREPARED = 9,
STR7X_COMPARE_ERROR = 10,
STR7X_BUSY = 11
};
static struct str7x_mem_layout mem_layout_str7bank0[] = {
{0x00000000, 0x02000, 0x01},
{0x00002000, 0x02000, 0x02},
{0x00004000, 0x02000, 0x04},
{0x00006000, 0x02000, 0x08},
{0x00008000, 0x08000, 0x10},
{0x00010000, 0x10000, 0x20},
{0x00020000, 0x10000, 0x40},
{0x00030000, 0x10000, 0x80}
};
static struct str7x_mem_layout mem_layout_str7bank1[] = {
{0x00000000, 0x02000, 0x10000},
{0x00002000, 0x02000, 0x20000}
};
static int str7x_get_flash_adr(struct flash_bank *bank, uint32_t reg)
{
struct str7x_flash_bank *str7x_info = bank->driver_priv;
return str7x_info->register_base | reg;
}
static int str7x_build_block_list(struct flash_bank *bank)
{
struct str7x_flash_bank *str7x_info = bank->driver_priv;
int i;
int num_sectors;
int b0_sectors = 0, b1_sectors = 0;
switch (bank->size) {
case 16 * 1024:
b1_sectors = 2;
break;
case 64 * 1024:
b0_sectors = 5;
break;
case 128 * 1024:
b0_sectors = 6;
break;
case 256 * 1024:
b0_sectors = 8;
break;
default:
LOG_ERROR("BUG: unknown bank->size encountered");
exit(-1);
}
num_sectors = b0_sectors + b1_sectors;
bank->num_sectors = num_sectors;
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
str7x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
num_sectors = 0;
for (i = 0; i < b0_sectors; i++) {
bank->sectors[num_sectors].offset = mem_layout_str7bank0[i].sector_start;
bank->sectors[num_sectors].size = mem_layout_str7bank0[i].sector_size;
bank->sectors[num_sectors].is_erased = -1;
/* the reset_init handler marks all the sectors unprotected,
* matching hardware after reset; keep the driver in sync
*/
bank->sectors[num_sectors].is_protected = 0;
str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank0[i].sector_bit;
}
for (i = 0; i < b1_sectors; i++) {
bank->sectors[num_sectors].offset = mem_layout_str7bank1[i].sector_start;
bank->sectors[num_sectors].size = mem_layout_str7bank1[i].sector_size;
bank->sectors[num_sectors].is_erased = -1;
/* the reset_init handler marks all the sectors unprotected,
* matching hardware after reset; keep the driver in sync
*/
bank->sectors[num_sectors].is_protected = 0;
str7x_info->sector_bits[num_sectors++] = mem_layout_str7bank1[i].sector_bit;
}
return ERROR_OK;
}
/* flash bank str7x <base> <size> 0 0 <target#> <str71_variant>
*/
FLASH_BANK_COMMAND_HANDLER(str7x_flash_bank_command)
{
struct str7x_flash_bank *str7x_info;
if (CMD_ARGC < 7)
return ERROR_COMMAND_SYNTAX_ERROR;
str7x_info = malloc(sizeof(struct str7x_flash_bank));
bank->driver_priv = str7x_info;
/* set default bits for str71x flash */
str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA1 | FLASH_BSYA0);
str7x_info->disable_bit = (1 << 1);
if (strcmp(CMD_ARGV[6], "STR71x") == 0)
str7x_info->register_base = 0x40100000;
else if (strcmp(CMD_ARGV[6], "STR73x") == 0) {
str7x_info->register_base = 0x80100000;
str7x_info->busy_bits = (FLASH_LOCK | FLASH_BSYA0);
} else if (strcmp(CMD_ARGV[6], "STR75x") == 0) {
str7x_info->register_base = 0x20100000;
str7x_info->disable_bit = (1 << 0);
} else {
LOG_ERROR("unknown STR7x variant: '%s'", CMD_ARGV[6]);
free(str7x_info);
return ERROR_FLASH_BANK_INVALID;
}
str7x_build_block_list(bank);
return ERROR_OK;
}
/* wait for flash to become idle or report errors.
FIX!!! what's the maximum timeout??? The documentation doesn't
state any maximum time.... by inspection it seems > 1000ms is to be
expected.
10000ms is long enough that it should cover anything, yet not
quite be equivalent to an infinite loop.
*/
static int str7x_waitbusy(struct flash_bank *bank)
{
int err;
int i;
struct target *target = bank->target;
struct str7x_flash_bank *str7x_info = bank->driver_priv;
for (i = 0 ; i < 10000; i++) {
uint32_t retval;
err = target_read_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), &retval);
if (err != ERROR_OK)
return err;
if ((retval & str7x_info->busy_bits) == 0)
return ERROR_OK;
alive_sleep(1);
}
LOG_ERROR("Timed out waiting for str7x flash");
return ERROR_FAIL;
}
static int str7x_result(struct flash_bank *bank)
{
struct target *target = bank->target;
uint32_t flash_flags;
int retval;
retval = target_read_u32(target, str7x_get_flash_adr(bank, FLASH_ER), &flash_flags);
if (retval != ERROR_OK)
return retval;
if (flash_flags & FLASH_WPF) {
LOG_ERROR("str7x hw write protection set");
retval = ERROR_FAIL;
}
if (flash_flags & FLASH_RESER) {
LOG_ERROR("str7x suspended program erase not resumed");
retval = ERROR_FAIL;
}
if (flash_flags & FLASH_10ER) {
LOG_ERROR("str7x trying to set bit to 1 when it is already 0");
retval = ERROR_FAIL;
}
if (flash_flags & FLASH_PGER) {
LOG_ERROR("str7x program error");
retval = ERROR_FAIL;
}
if (flash_flags & FLASH_ERER) {
LOG_ERROR("str7x erase error");
retval = ERROR_FAIL;
}
if (retval == ERROR_OK) {
if (flash_flags & FLASH_ERR) {
/* this should always be set if one of the others are set... */
LOG_ERROR("str7x write operation failed / bad setup");
retval = ERROR_FAIL;
}
}
return retval;
}
static int str7x_protect_check(struct flash_bank *bank)
{
struct str7x_flash_bank *str7x_info = bank->driver_priv;
struct target *target = bank->target;
int i;
uint32_t flash_flags;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
int retval;
retval = target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVWPAR), &flash_flags);
if (retval != ERROR_OK)
return retval;
for (i = 0; i < bank->num_sectors; i++) {
if (flash_flags & str7x_info->sector_bits[i])
bank->sectors[i].is_protected = 0;
else
bank->sectors[i].is_protected = 1;
}
return ERROR_OK;
}
static int str7x_erase(struct flash_bank *bank, int first, int last)
{
struct str7x_flash_bank *str7x_info = bank->driver_priv;
struct target *target = bank->target;
int i;
uint32_t cmd;
uint32_t sectors = 0;
int err;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
for (i = first; i <= last; i++)
sectors |= str7x_info->sector_bits[i];
LOG_DEBUG("sectors: 0x%" PRIx32 "", sectors);
/* clear FLASH_ER register */
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
if (err != ERROR_OK)
return err;
cmd = FLASH_SER;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
if (err != ERROR_OK)
return err;
cmd = sectors;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR1), cmd);
if (err != ERROR_OK)
return err;
cmd = FLASH_SER | FLASH_WMS;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
if (err != ERROR_OK)
return err;
err = str7x_waitbusy(bank);
if (err != ERROR_OK)
return err;
err = str7x_result(bank);
if (err != ERROR_OK)
return err;
for (i = first; i <= last; i++)
bank->sectors[i].is_erased = 1;
return ERROR_OK;
}
static int str7x_protect(struct flash_bank *bank, int set, int first, int last)
{
struct str7x_flash_bank *str7x_info = bank->driver_priv;
struct target *target = bank->target;
int i;
uint32_t cmd;
uint32_t protect_blocks;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
protect_blocks = 0xFFFFFFFF;
if (set) {
for (i = first; i <= last; i++)
protect_blocks &= ~(str7x_info->sector_bits[i]);
}
/* clear FLASH_ER register */
int err;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
if (err != ERROR_OK)
return err;
cmd = FLASH_SPR;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
if (err != ERROR_OK)
return err;
cmd = str7x_get_flash_adr(bank, FLASH_NVWPAR);
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), cmd);
if (err != ERROR_OK)
return err;
cmd = protect_blocks;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), cmd);
if (err != ERROR_OK)
return err;
cmd = FLASH_SPR | FLASH_WMS;
err = target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
if (err != ERROR_OK)
return err;
err = str7x_waitbusy(bank);
if (err != ERROR_OK)
return err;
err = str7x_result(bank);
if (err != ERROR_OK)
return err;
return ERROR_OK;
}
static int str7x_write_block(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct str7x_flash_bank *str7x_info = bank->driver_priv;
struct target *target = bank->target;
uint32_t buffer_size = 32768;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[6];
struct arm_algorithm arm_algo;
int retval = ERROR_OK;
/* see contib/loaders/flash/str7x.s for src */
static const uint32_t str7x_flash_write_code[] = {
/* write: */
0xe3a04201, /* mov r4, #0x10000000 */
0xe5824000, /* str r4, [r2, #0x0] */
0xe5821010, /* str r1, [r2, #0x10] */
0xe4904004, /* ldr r4, [r0], #4 */
0xe5824008, /* str r4, [r2, #0x8] */
0xe4904004, /* ldr r4, [r0], #4 */
0xe582400c, /* str r4, [r2, #0xc] */
0xe3a04209, /* mov r4, #0x90000000 */
0xe5824000, /* str r4, [r2, #0x0] */
/* busy: */
0xe5924000, /* ldr r4, [r2, #0x0] */
0xe1140005, /* tst r4, r5 */
0x1afffffc, /* bne busy */
0xe5924014, /* ldr r4, [r2, #0x14] */
0xe31400ff, /* tst r4, #0xff */
0x03140c01, /* tsteq r4, #0x100 */
0x1a000002, /* bne exit */
0xe2811008, /* add r1, r1, #0x8 */
0xe2533001, /* subs r3, r3, #1 */
0x1affffec, /* bne write */
/* exit: */
0xeafffffe, /* b exit */
};
/* flash write code */
if (target_alloc_working_area_try(target, sizeof(str7x_flash_write_code),
&write_algorithm) != ERROR_OK) {
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
target_write_buffer(target, write_algorithm->address,
sizeof(str7x_flash_write_code),
(uint8_t *)str7x_flash_write_code);
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a
* buffer, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING("no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
arm_algo.common_magic = ARM_COMMON_MAGIC;
arm_algo.core_mode = ARM_MODE_SVC;
arm_algo.core_state = ARM_STATE_ARM;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
init_reg_param(&reg_params[3], "r3", 32, PARAM_OUT);
init_reg_param(&reg_params[4], "r4", 32, PARAM_IN);
init_reg_param(&reg_params[5], "r5", 32, PARAM_OUT);
while (count > 0) {
uint32_t thisrun_count = (count > (buffer_size / 8)) ? (buffer_size / 8) : count;
target_write_buffer(target, source->address, thisrun_count * 8, buffer);
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, address);
buf_set_u32(reg_params[2].value, 0, 32, str7x_get_flash_adr(bank, FLASH_CR0));
buf_set_u32(reg_params[3].value, 0, 32, thisrun_count);
buf_set_u32(reg_params[5].value, 0, 32, str7x_info->busy_bits);
retval = target_run_algorithm(target, 0, NULL, 6, reg_params,
write_algorithm->address,
write_algorithm->address + (sizeof(str7x_flash_write_code) - 4),
10000, &arm_algo);
if (retval != ERROR_OK)
break;
if (buf_get_u32(reg_params[4].value, 0, 32) != 0x00) {
retval = str7x_result(bank);
break;
}
buffer += thisrun_count * 8;
address += thisrun_count * 8;
count -= thisrun_count;
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
destroy_reg_param(&reg_params[4]);
destroy_reg_param(&reg_params[5]);
return retval;
}
static int str7x_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t dwords_remaining = (count / 8);
uint32_t bytes_remaining = (count & 0x00000007);
uint32_t address = bank->base + offset;
uint32_t bytes_written = 0;
uint32_t cmd;
int retval;
uint32_t check_address = offset;
int i;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset & 0x7) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required 8-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
for (i = 0; i < bank->num_sectors; i++) {
uint32_t sec_start = bank->sectors[i].offset;
uint32_t sec_end = sec_start + bank->sectors[i].size;
/* check if destination falls within the current sector */
if ((check_address >= sec_start) && (check_address < sec_end)) {
/* check if destination ends in the current sector */
if (offset + count < sec_end)
check_address = offset + count;
else
check_address = sec_end;
}
}
if (check_address != offset + count)
return ERROR_FLASH_DST_OUT_OF_BANK;
/* clear FLASH_ER register */
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_ER), 0x0);
/* multiple dwords (8-byte) to be programmed? */
if (dwords_remaining > 0) {
/* try using a block write */
retval = str7x_write_block(bank, buffer, offset, dwords_remaining);
if (retval != ERROR_OK) {
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single dword accesses */
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
} else {
return retval;
}
} else {
buffer += dwords_remaining * 8;
address += dwords_remaining * 8;
dwords_remaining = 0;
}
}
while (dwords_remaining > 0) {
/* command */
cmd = FLASH_DWPG;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
/* address */
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);
/* data word 1 */
target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0),
4, 1, buffer + bytes_written);
bytes_written += 4;
/* data word 2 */
target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1),
4, 1, buffer + bytes_written);
bytes_written += 4;
/* start programming cycle */
cmd = FLASH_DWPG | FLASH_WMS;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
int err;
err = str7x_waitbusy(bank);
if (err != ERROR_OK)
return err;
err = str7x_result(bank);
if (err != ERROR_OK)
return err;
dwords_remaining--;
address += 8;
}
if (bytes_remaining) {
uint8_t last_dword[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/* copy the last remaining bytes into the write buffer */
memcpy(last_dword, buffer+bytes_written, bytes_remaining);
/* command */
cmd = FLASH_DWPG;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
/* address */
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), address);
/* data word 1 */
target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR0),
4, 1, last_dword);
/* data word 2 */
target_write_memory(target, str7x_get_flash_adr(bank, FLASH_DR1),
4, 1, last_dword + 4);
/* start programming cycle */
cmd = FLASH_DWPG | FLASH_WMS;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), cmd);
int err;
err = str7x_waitbusy(bank);
if (err != ERROR_OK)
return err;
err = str7x_result(bank);
if (err != ERROR_OK)
return err;
}
return ERROR_OK;
}
static int str7x_probe(struct flash_bank *bank)
{
return ERROR_OK;
}
#if 0
COMMAND_HANDLER(str7x_handle_part_id_command)
{
return ERROR_OK;
}
#endif
static int get_str7x_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "str7x flash driver info");
/* STR7x flash doesn't support sector protection interrogation.
* FLASH_NVWPAR acts as a write only register; its read value
* doesn't reflect the actual protection state of the sectors.
*/
LOG_WARNING("STR7x flash lock information might not be correct "
"due to hardware limitations.");
return ERROR_OK;
}
COMMAND_HANDLER(str7x_handle_disable_jtag_command)
{
struct target *target = NULL;
struct str7x_flash_bank *str7x_info = NULL;
uint32_t flash_cmd;
uint16_t ProtectionLevel = 0;
uint16_t ProtectionRegs;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
str7x_info = bank->driver_priv;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* first we get protection status */
uint32_t reg;
target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR0), &reg);
if (!(reg & str7x_info->disable_bit))
ProtectionLevel = 1;
target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), &reg);
ProtectionRegs = ~(reg >> 16);
while (((ProtectionRegs) != 0) && (ProtectionLevel < 16)) {
ProtectionRegs >>= 1;
ProtectionLevel++;
}
if (ProtectionLevel == 0) {
flash_cmd = FLASH_SPR;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFB8);
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0), 0xFFFFFFFD);
flash_cmd = FLASH_SPR | FLASH_WMS;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
} else {
flash_cmd = FLASH_SPR;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_AR), 0x4010DFBC);
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_DR0),
~(1 << (15 + ProtectionLevel)));
flash_cmd = FLASH_SPR | FLASH_WMS;
target_write_u32(target, str7x_get_flash_adr(bank, FLASH_CR0), flash_cmd);
}
return ERROR_OK;
}
static const struct command_registration str7x_exec_command_handlers[] = {
{
.name = "disable_jtag",
.usage = "<bank>",
.handler = str7x_handle_disable_jtag_command,
.mode = COMMAND_EXEC,
.help = "disable jtag access",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration str7x_command_handlers[] = {
{
.name = "str7x",
.mode = COMMAND_ANY,
.help = "str7x flash command group",
.usage = "",
.chain = str7x_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver str7x_flash = {
.name = "str7x",
.commands = str7x_command_handlers,
.flash_bank_command = str7x_flash_bank_command,
.erase = str7x_erase,
.protect = str7x_protect,
.write = str7x_write,
.read = default_flash_read,
.probe = str7x_probe,
.auto_probe = str7x_probe,
.erase_check = default_flash_blank_check,
.protect_check = str7x_protect_check,
.info = get_str7x_info,
};
+690
View File
@@ -0,0 +1,690 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
*
* Copyright (C) 2008 by Oyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <target/arm966e.h>
#include <target/algorithm.h>
/* Flash registers */
#define FLASH_BBSR 0x54000000 /* Boot Bank Size Register */
#define FLASH_NBBSR 0x54000004 /* Non-Boot Bank Size Register */
#define FLASH_BBADR 0x5400000C /* Boot Bank Base Address Register */
#define FLASH_NBBADR 0x54000010 /* Non-Boot Bank Base Address Register */
#define FLASH_CR 0x54000018 /* Control Register */
#define FLASH_SR 0x5400001C /* Status Register */
#define FLASH_BCE5ADDR 0x54000020 /* BC Fifth Entry Target Address Register */
struct str9x_flash_bank {
uint32_t *sector_bits;
int variant;
int bank1;
};
enum str9x_status_codes {
STR9X_CMD_SUCCESS = 0,
STR9X_INVALID_COMMAND = 1,
STR9X_SRC_ADDR_ERROR = 2,
STR9X_DST_ADDR_ERROR = 3,
STR9X_SRC_ADDR_NOT_MAPPED = 4,
STR9X_DST_ADDR_NOT_MAPPED = 5,
STR9X_COUNT_ERROR = 6,
STR9X_INVALID_SECTOR = 7,
STR9X_SECTOR_NOT_BLANK = 8,
STR9X_SECTOR_NOT_PREPARED = 9,
STR9X_COMPARE_ERROR = 10,
STR9X_BUSY = 11
};
static uint32_t bank1start = 0x00080000;
static int str9x_build_block_list(struct flash_bank *bank)
{
struct str9x_flash_bank *str9x_info = bank->driver_priv;
int i;
int num_sectors;
int b0_sectors = 0, b1_sectors = 0;
uint32_t offset = 0;
/* set if we have large flash str9 */
str9x_info->variant = 0;
str9x_info->bank1 = 0;
switch (bank->size) {
case (256 * 1024):
b0_sectors = 4;
break;
case (512 * 1024):
b0_sectors = 8;
break;
case (1024 * 1024):
bank1start = 0x00100000;
str9x_info->variant = 1;
b0_sectors = 16;
break;
case (2048 * 1024):
bank1start = 0x00200000;
str9x_info->variant = 1;
b0_sectors = 32;
break;
case (128 * 1024):
str9x_info->variant = 1;
str9x_info->bank1 = 1;
b1_sectors = 8;
bank1start = bank->base;
break;
case (32 * 1024):
str9x_info->bank1 = 1;
b1_sectors = 4;
bank1start = bank->base;
break;
default:
LOG_ERROR("BUG: unknown bank->size encountered");
exit(-1);
}
num_sectors = b0_sectors + b1_sectors;
bank->num_sectors = num_sectors;
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
str9x_info->sector_bits = malloc(sizeof(uint32_t) * num_sectors);
num_sectors = 0;
for (i = 0; i < b0_sectors; i++) {
bank->sectors[num_sectors].offset = offset;
bank->sectors[num_sectors].size = 0x10000;
offset += bank->sectors[i].size;
bank->sectors[num_sectors].is_erased = -1;
bank->sectors[num_sectors].is_protected = 1;
str9x_info->sector_bits[num_sectors++] = (1 << i);
}
for (i = 0; i < b1_sectors; i++) {
bank->sectors[num_sectors].offset = offset;
bank->sectors[num_sectors].size = str9x_info->variant == 0 ? 0x2000 : 0x4000;
offset += bank->sectors[i].size;
bank->sectors[num_sectors].is_erased = -1;
bank->sectors[num_sectors].is_protected = 1;
if (str9x_info->variant)
str9x_info->sector_bits[num_sectors++] = (1 << i);
else
str9x_info->sector_bits[num_sectors++] = (1 << (i + 8));
}
return ERROR_OK;
}
/* flash bank str9x <base> <size> 0 0 <target#>
*/
FLASH_BANK_COMMAND_HANDLER(str9x_flash_bank_command)
{
struct str9x_flash_bank *str9x_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
str9x_info = malloc(sizeof(struct str9x_flash_bank));
bank->driver_priv = str9x_info;
str9x_build_block_list(bank);
return ERROR_OK;
}
static int str9x_protect_check(struct flash_bank *bank)
{
int retval;
struct str9x_flash_bank *str9x_info = bank->driver_priv;
struct target *target = bank->target;
int i;
uint32_t adr;
uint32_t status = 0;
uint16_t hstatus = 0;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* read level one protection */
if (str9x_info->variant) {
if (str9x_info->bank1) {
adr = bank1start + 0x18;
retval = target_write_u16(target, adr, 0x90);
if (retval != ERROR_OK)
return retval;
retval = target_read_u16(target, adr, &hstatus);
if (retval != ERROR_OK)
return retval;
status = hstatus;
} else {
adr = bank1start + 0x14;
retval = target_write_u16(target, adr, 0x90);
if (retval != ERROR_OK)
return retval;
retval = target_read_u32(target, adr, &status);
if (retval != ERROR_OK)
return retval;
}
} else {
adr = bank1start + 0x10;
retval = target_write_u16(target, adr, 0x90);
if (retval != ERROR_OK)
return retval;
retval = target_read_u16(target, adr, &hstatus);
if (retval != ERROR_OK)
return retval;
status = hstatus;
}
/* read array command */
retval = target_write_u16(target, adr, 0xFF);
if (retval != ERROR_OK)
return retval;
for (i = 0; i < bank->num_sectors; i++) {
if (status & str9x_info->sector_bits[i])
bank->sectors[i].is_protected = 1;
else
bank->sectors[i].is_protected = 0;
}
return ERROR_OK;
}
static int str9x_erase(struct flash_bank *bank, int first, int last)
{
struct target *target = bank->target;
int i;
uint32_t adr;
uint8_t status;
uint8_t erase_cmd;
int total_timeout;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* Check if we can erase whole bank */
if ((first == 0) && (last == (bank->num_sectors - 1))) {
/* Optimize to run erase bank command instead of sector */
erase_cmd = 0x80;
/* Add timeout duration since erase bank takes more time */
total_timeout = 1000 * bank->num_sectors;
} else {
/* Erase sector command */
erase_cmd = 0x20;
total_timeout = 1000;
}
/* this is so the compiler can *know* */
assert(total_timeout > 0);
for (i = first; i <= last; i++) {
int retval;
adr = bank->base + bank->sectors[i].offset;
/* erase sectors or block */
retval = target_write_u16(target, adr, erase_cmd);
if (retval != ERROR_OK)
return retval;
retval = target_write_u16(target, adr, 0xD0);
if (retval != ERROR_OK)
return retval;
/* get status */
retval = target_write_u16(target, adr, 0x70);
if (retval != ERROR_OK)
return retval;
int timeout;
for (timeout = 0; timeout < total_timeout; timeout++) {
retval = target_read_u8(target, adr, &status);
if (retval != ERROR_OK)
return retval;
if (status & 0x80)
break;
alive_sleep(1);
}
if (timeout == total_timeout) {
LOG_ERROR("erase timed out");
return ERROR_FAIL;
}
/* clear status, also clear read array */
retval = target_write_u16(target, adr, 0x50);
if (retval != ERROR_OK)
return retval;
/* read array command */
retval = target_write_u16(target, adr, 0xFF);
if (retval != ERROR_OK)
return retval;
if (status & 0x22) {
LOG_ERROR("error erasing flash bank, status: 0x%x", status);
return ERROR_FLASH_OPERATION_FAILED;
}
/* If we ran erase bank command, we are finished */
if (erase_cmd == 0x80)
break;
}
for (i = first; i <= last; i++)
bank->sectors[i].is_erased = 1;
return ERROR_OK;
}
static int str9x_protect(struct flash_bank *bank,
int set, int first, int last)
{
struct target *target = bank->target;
int i;
uint32_t adr;
uint8_t status;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
for (i = first; i <= last; i++) {
/* Level One Protection */
adr = bank->base + bank->sectors[i].offset;
target_write_u16(target, adr, 0x60);
if (set)
target_write_u16(target, adr, 0x01);
else
target_write_u16(target, adr, 0xD0);
/* query status */
target_read_u8(target, adr, &status);
/* clear status, also clear read array */
target_write_u16(target, adr, 0x50);
/* read array command */
target_write_u16(target, adr, 0xFF);
}
return ERROR_OK;
}
static int str9x_write_block(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t buffer_size = 32768;
struct working_area *write_algorithm;
struct working_area *source;
uint32_t address = bank->base + offset;
struct reg_param reg_params[4];
struct arm_algorithm arm_algo;
int retval = ERROR_OK;
/* see contib/loaders/flash/str9x.s for src */
static const uint32_t str9x_flash_write_code[] = {
/* write: */
0xe3c14003, /* bic r4, r1, #3 */
0xe3a03040, /* mov r3, #0x40 */
0xe1c430b0, /* strh r3, [r4, #0] */
0xe0d030b2, /* ldrh r3, [r0], #2 */
0xe0c130b2, /* strh r3, [r1], #2 */
0xe3a03070, /* mov r3, #0x70 */
0xe1c430b0, /* strh r3, [r4, #0] */
/* busy: */
0xe5d43000, /* ldrb r3, [r4, #0] */
0xe3130080, /* tst r3, #0x80 */
0x0afffffc, /* beq busy */
0xe3a05050, /* mov r5, #0x50 */
0xe1c450b0, /* strh r5, [r4, #0] */
0xe3a050ff, /* mov r5, #0xFF */
0xe1c450b0, /* strh r5, [r4, #0] */
0xe3130012, /* tst r3, #0x12 */
0x1a000001, /* bne exit */
0xe2522001, /* subs r2, r2, #1 */
0x1affffed, /* bne write */
/* exit: */
0xe1200070, /* bkpt #0 */
};
/* flash write code */
if (target_alloc_working_area(target, sizeof(str9x_flash_write_code),
&write_algorithm) != ERROR_OK) {
LOG_WARNING("no working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
};
target_write_buffer(target, write_algorithm->address,
sizeof(str9x_flash_write_code),
(uint8_t *)str9x_flash_write_code);
/* memory buffer */
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
buffer_size /= 2;
if (buffer_size <= 256) {
/* we already allocated the writing code, but failed to get a
* buffer, free the algorithm */
target_free_working_area(target, write_algorithm);
LOG_WARNING("no large enough working area available, can't do block memory writes");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
}
arm_algo.common_magic = ARM_COMMON_MAGIC;
arm_algo.core_mode = ARM_MODE_SVC;
arm_algo.core_state = ARM_STATE_ARM;
init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
init_reg_param(&reg_params[3], "r3", 32, PARAM_IN);
while (count > 0) {
uint32_t thisrun_count = (count > (buffer_size / 2)) ? (buffer_size / 2) : count;
target_write_buffer(target, source->address, thisrun_count * 2, buffer);
buf_set_u32(reg_params[0].value, 0, 32, source->address);
buf_set_u32(reg_params[1].value, 0, 32, address);
buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
retval = target_run_algorithm(target, 0, NULL, 4, reg_params,
write_algorithm->address,
0, 10000, &arm_algo);
if (retval != ERROR_OK) {
LOG_ERROR("error executing str9x flash write algorithm");
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
if (buf_get_u32(reg_params[3].value, 0, 32) != 0x80) {
retval = ERROR_FLASH_OPERATION_FAILED;
break;
}
buffer += thisrun_count * 2;
address += thisrun_count * 2;
count -= thisrun_count;
}
target_free_working_area(target, source);
target_free_working_area(target, write_algorithm);
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
destroy_reg_param(&reg_params[2]);
destroy_reg_param(&reg_params[3]);
return retval;
}
static int str9x_write(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
uint32_t words_remaining = (count / 2);
uint32_t bytes_remaining = (count & 0x00000001);
uint32_t address = bank->base + offset;
uint32_t bytes_written = 0;
uint8_t status;
int retval;
uint32_t check_address = offset;
uint32_t bank_adr;
int i;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset & 0x1) {
LOG_WARNING("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset);
return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
}
for (i = 0; i < bank->num_sectors; i++) {
uint32_t sec_start = bank->sectors[i].offset;
uint32_t sec_end = sec_start + bank->sectors[i].size;
/* check if destination falls within the current sector */
if ((check_address >= sec_start) && (check_address < sec_end)) {
/* check if destination ends in the current sector */
if (offset + count < sec_end)
check_address = offset + count;
else
check_address = sec_end;
}
}
if (check_address != offset + count)
return ERROR_FLASH_DST_OUT_OF_BANK;
/* multiple half words (2-byte) to be programmed? */
if (words_remaining > 0) {
/* try using a block write */
retval = str9x_write_block(bank, buffer, offset, words_remaining);
if (retval != ERROR_OK) {
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
/* if block write failed (no sufficient working area),
* we use normal (slow) single dword accesses */
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
} else if (retval == ERROR_FLASH_OPERATION_FAILED) {
LOG_ERROR("flash writing failed");
return ERROR_FLASH_OPERATION_FAILED;
}
} else {
buffer += words_remaining * 2;
address += words_remaining * 2;
words_remaining = 0;
}
}
while (words_remaining > 0) {
bank_adr = address & ~0x03;
/* write data command */
target_write_u16(target, bank_adr, 0x40);
target_write_memory(target, address, 2, 1, buffer + bytes_written);
/* get status command */
target_write_u16(target, bank_adr, 0x70);
int timeout;
for (timeout = 0; timeout < 1000; timeout++) {
target_read_u8(target, bank_adr, &status);
if (status & 0x80)
break;
alive_sleep(1);
}
if (timeout == 1000) {
LOG_ERROR("write timed out");
return ERROR_FAIL;
}
/* clear status reg and read array */
target_write_u16(target, bank_adr, 0x50);
target_write_u16(target, bank_adr, 0xFF);
if (status & 0x10)
return ERROR_FLASH_OPERATION_FAILED;
else if (status & 0x02)
return ERROR_FLASH_OPERATION_FAILED;
bytes_written += 2;
words_remaining--;
address += 2;
}
if (bytes_remaining) {
uint8_t last_halfword[2] = {0xff, 0xff};
/* copy the last remaining bytes into the write buffer */
memcpy(last_halfword, buffer+bytes_written, bytes_remaining);
bank_adr = address & ~0x03;
/* write data command */
target_write_u16(target, bank_adr, 0x40);
target_write_memory(target, address, 2, 1, last_halfword);
/* query status command */
target_write_u16(target, bank_adr, 0x70);
int timeout;
for (timeout = 0; timeout < 1000; timeout++) {
target_read_u8(target, bank_adr, &status);
if (status & 0x80)
break;
alive_sleep(1);
}
if (timeout == 1000) {
LOG_ERROR("write timed out");
return ERROR_FAIL;
}
/* clear status reg and read array */
target_write_u16(target, bank_adr, 0x50);
target_write_u16(target, bank_adr, 0xFF);
if (status & 0x10)
return ERROR_FLASH_OPERATION_FAILED;
else if (status & 0x02)
return ERROR_FLASH_OPERATION_FAILED;
}
return ERROR_OK;
}
static int str9x_probe(struct flash_bank *bank)
{
return ERROR_OK;
}
#if 0
COMMAND_HANDLER(str9x_handle_part_id_command)
{
return ERROR_OK;
}
#endif
static int get_str9x_info(struct flash_bank *bank, char *buf, int buf_size)
{
snprintf(buf, buf_size, "str9x flash driver info");
return ERROR_OK;
}
COMMAND_HANDLER(str9x_handle_flash_config_command)
{
struct target *target = NULL;
if (CMD_ARGC < 5)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
uint32_t bbsr, nbbsr, bbadr, nbbadr;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bbsr);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], nbbsr);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], bbadr);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], nbbadr);
target = bank->target;
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* config flash controller */
target_write_u32(target, FLASH_BBSR, bbsr);
target_write_u32(target, FLASH_NBBSR, nbbsr);
target_write_u32(target, FLASH_BBADR, bbadr >> 2);
target_write_u32(target, FLASH_NBBADR, nbbadr >> 2);
/* set bit 18 instruction TCM order as per flash programming manual */
arm966e_write_cp15(target, 62, 0x40000);
/* enable flash bank 1 */
target_write_u32(target, FLASH_CR, 0x18);
return ERROR_OK;
}
static const struct command_registration str9x_config_command_handlers[] = {
{
.name = "flash_config",
.handler = str9x_handle_flash_config_command,
.mode = COMMAND_EXEC,
.help = "Configure str9x flash controller, prior to "
"programming the flash.",
.usage = "bank_id BBSR NBBSR BBADR NBBADR",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration str9x_command_handlers[] = {
{
.name = "str9x",
.mode = COMMAND_ANY,
.help = "str9x flash command group",
.usage = "",
.chain = str9x_config_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver str9x_flash = {
.name = "str9x",
.commands = str9x_command_handlers,
.flash_bank_command = str9x_flash_bank_command,
.erase = str9x_erase,
.protect = str9x_protect,
.write = str9x_write,
.read = default_flash_read,
.probe = str9x_probe,
.auto_probe = str9x_probe,
.erase_check = default_flash_blank_check,
.protect_check = str9x_protect_check,
.info = get_str9x_info,
};
File diff suppressed because it is too large Load Diff
+895
View File
@@ -0,0 +1,895 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
* Copyright (C) 2007,2008 Øyvind Harboe <oyvind.harboe@zylin.com> *
* Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/time_support.h>
#include <target/image.h>
/**
* @file
* Implements Tcl commands used to access NOR flash facilities.
*/
COMMAND_HELPER(flash_command_get_bank, unsigned name_index,
struct flash_bank **bank)
{
const char *name = CMD_ARGV[name_index];
int retval = get_flash_bank_by_name(name, bank);
if (retval != ERROR_OK)
return retval;
if (*bank)
return ERROR_OK;
unsigned bank_num;
COMMAND_PARSE_NUMBER(uint, name, bank_num);
return get_flash_bank_by_num(bank_num, bank);
}
COMMAND_HANDLER(handle_flash_info_command)
{
struct flash_bank *p;
int j = 0;
int retval;
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (retval != ERROR_OK)
return retval;
if (p != NULL) {
char buf[1024];
/* attempt auto probe */
retval = p->driver->auto_probe(p);
if (retval != ERROR_OK)
return retval;
/* We must query the hardware to avoid printing stale information! */
retval = p->driver->protect_check(p);
if (retval != ERROR_OK)
return retval;
command_print(CMD_CTX,
"#%" PRIu32 " : %s at 0x%8.8" PRIx32 ", size 0x%8.8" PRIx32
", buswidth %i, chipwidth %i",
p->bank_number,
p->driver->name,
p->base,
p->size,
p->bus_width,
p->chip_width);
for (j = 0; j < p->num_sectors; j++) {
char *protect_state;
if (p->sectors[j].is_protected == 0)
protect_state = "not protected";
else if (p->sectors[j].is_protected == 1)
protect_state = "protected";
else
protect_state = "protection state unknown";
command_print(CMD_CTX,
"\t#%3i: 0x%8.8" PRIx32 " (0x%" PRIx32 " %" PRIi32 "kB) %s",
j,
p->sectors[j].offset,
p->sectors[j].size,
p->sectors[j].size >> 10,
protect_state);
}
*buf = '\0'; /* initialize buffer, otherwise it migh contain garbage if driver
*function fails */
retval = p->driver->info(p, buf, sizeof(buf));
command_print(CMD_CTX, "%s", buf);
if (retval != ERROR_OK)
LOG_ERROR("error retrieving flash info");
}
return retval;
}
COMMAND_HANDLER(handle_flash_probe_command)
{
struct flash_bank *p;
int retval;
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (retval != ERROR_OK)
return retval;
if (p) {
retval = p->driver->probe(p);
if (retval == ERROR_OK)
command_print(CMD_CTX,
"flash '%s' found at 0x%8.8" PRIx32,
p->driver->name,
p->base);
} else {
command_print(CMD_CTX, "flash bank '#%s' is out of bounds", CMD_ARGV[0]);
retval = ERROR_FAIL;
}
return retval;
}
COMMAND_HANDLER(handle_flash_erase_check_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *p;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (ERROR_OK != retval)
return retval;
int j;
retval = p->driver->erase_check(p);
if (retval == ERROR_OK)
command_print(CMD_CTX, "successfully checked erase state");
else {
command_print(CMD_CTX,
"unknown error when checking erase state of flash bank #%s at 0x%8.8" PRIx32,
CMD_ARGV[0],
p->base);
}
for (j = 0; j < p->num_sectors; j++) {
char *erase_state;
if (p->sectors[j].is_erased == 0)
erase_state = "not erased";
else if (p->sectors[j].is_erased == 1)
erase_state = "erased";
else
erase_state = "erase state unknown";
command_print(CMD_CTX,
"\t#%3i: 0x%8.8" PRIx32 " (0x%" PRIx32 " %" PRIi32 "kB) %s",
j,
p->sectors[j].offset,
p->sectors[j].size,
p->sectors[j].size >> 10,
erase_state);
}
return retval;
}
COMMAND_HANDLER(handle_flash_erase_address_command)
{
struct flash_bank *p;
int retval = ERROR_OK;
uint32_t address;
uint32_t length;
bool do_pad = false;
bool do_unlock = false;
struct target *target = get_current_target(CMD_CTX);
while (CMD_ARGC >= 3) {
/* Optionally pad out the address range to block/sector
* boundaries. We can't know if there's data in that part
* of the flash; only do padding if we're told to.
*/
if (strcmp("pad", CMD_ARGV[0]) == 0)
do_pad = true;
else if (strcmp("unlock", CMD_ARGV[0]) == 0)
do_unlock = true;
else
return ERROR_COMMAND_SYNTAX_ERROR;
CMD_ARGC--;
CMD_ARGV++;
}
if (CMD_ARGC != 2)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], length);
if (length <= 0) {
command_print(CMD_CTX, "Length must be >0");
return ERROR_COMMAND_SYNTAX_ERROR;
}
retval = get_flash_bank_by_addr(target, address, true, &p);
if (retval != ERROR_OK)
return retval;
/* We can't know if we did a resume + halt, in which case we no longer know the erased state
**/
flash_set_dirty();
struct duration bench;
duration_start(&bench);
if (do_unlock)
retval = flash_unlock_address_range(target, address, length);
if (retval == ERROR_OK)
retval = flash_erase_address_range(target, do_pad, address, length);
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "erased address 0x%8.8x (length %i)"
" in %fs (%0.3f KiB/s)", address, length,
duration_elapsed(&bench), duration_kbps(&bench, length));
}
return retval;
}
static int flash_check_sector_parameters(struct command_context *cmd_ctx,
uint32_t first, uint32_t last, uint32_t num_sectors)
{
if (!(first <= last)) {
command_print(cmd_ctx, "ERROR: "
"first sector must be <= last sector");
return ERROR_FAIL;
}
if (!(last <= (num_sectors - 1))) {
command_print(cmd_ctx, "ERROR: last sector must be <= %d",
(int) num_sectors - 1);
return ERROR_FAIL;
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_flash_erase_command)
{
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
uint32_t first;
uint32_t last;
struct flash_bank *p;
int retval;
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (retval != ERROR_OK)
return retval;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], first);
if (strcmp(CMD_ARGV[2], "last") == 0)
last = p->num_sectors - 1;
else
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors);
if (retval != ERROR_OK)
return retval;
struct duration bench;
duration_start(&bench);
retval = flash_driver_erase(p, first, last);
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "erased sectors %" PRIu32 " "
"through %" PRIu32 " on flash bank %" PRIu32 " "
"in %fs", first, last, p->bank_number, duration_elapsed(&bench));
}
return ERROR_OK;
}
COMMAND_HANDLER(handle_flash_protect_command)
{
if (CMD_ARGC != 4)
return ERROR_COMMAND_SYNTAX_ERROR;
uint32_t first;
uint32_t last;
struct flash_bank *p;
int retval;
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (retval != ERROR_OK)
return retval;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], first);
if (strcmp(CMD_ARGV[2], "last") == 0)
last = p->num_sectors - 1;
else
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
bool set;
COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set);
retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors);
if (retval != ERROR_OK)
return retval;
retval = flash_driver_protect(p, set, first, last);
if (retval == ERROR_OK) {
command_print(CMD_CTX, "%s protection for sectors %i "
"through %i on flash bank %" PRIu32 "",
(set) ? "set" : "cleared", (int) first,
(int) last, p->bank_number);
}
return retval;
}
COMMAND_HANDLER(handle_flash_write_image_command)
{
struct target *target = get_current_target(CMD_CTX);
struct image image;
uint32_t written;
int retval;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
/* flash auto-erase is disabled by default*/
int auto_erase = 0;
bool auto_unlock = false;
for (;; ) {
if (strcmp(CMD_ARGV[0], "erase") == 0) {
auto_erase = 1;
CMD_ARGV++;
CMD_ARGC--;
command_print(CMD_CTX, "auto erase enabled");
} else if (strcmp(CMD_ARGV[0], "unlock") == 0) {
auto_unlock = true;
CMD_ARGV++;
CMD_ARGC--;
command_print(CMD_CTX, "auto unlock enabled");
} else
break;
}
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
if (!target) {
LOG_ERROR("no target selected");
return ERROR_FAIL;
}
struct duration bench;
duration_start(&bench);
if (CMD_ARGC >= 2) {
image.base_address_set = 1;
COMMAND_PARSE_NUMBER(llong, CMD_ARGV[1], image.base_address);
} else {
image.base_address_set = 0;
image.base_address = 0x0;
}
image.start_address_set = 0;
retval = image_open(&image, CMD_ARGV[0], (CMD_ARGC == 3) ? CMD_ARGV[2] : NULL);
if (retval != ERROR_OK)
return retval;
retval = flash_write_unlock(target, &image, &written, auto_erase, auto_unlock);
if (retval != ERROR_OK) {
image_close(&image);
return retval;
}
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "wrote %" PRIu32 " bytes from file %s "
"in %fs (%0.3f KiB/s)", written, CMD_ARGV[0],
duration_elapsed(&bench), duration_kbps(&bench, written));
}
image_close(&image);
return retval;
}
COMMAND_HANDLER(handle_flash_fill_command)
{
int err = ERROR_OK;
uint32_t address;
uint32_t pattern;
uint32_t count;
uint32_t wrote = 0;
uint32_t cur_size = 0;
uint32_t chunk_count;
struct target *target = get_current_target(CMD_CTX);
unsigned i;
uint32_t wordsize;
int retval = ERROR_OK;
static size_t const chunksize = 1024;
uint8_t *chunk = NULL, *readback = NULL;
if (CMD_ARGC != 3) {
retval = ERROR_COMMAND_SYNTAX_ERROR;
goto done;
}
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], pattern);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], count);
chunk = malloc(chunksize);
if (chunk == NULL)
return ERROR_FAIL;
readback = malloc(chunksize);
if (readback == NULL) {
free(chunk);
return ERROR_FAIL;
}
if (count == 0)
goto done;
switch (CMD_NAME[4]) {
case 'w':
wordsize = 4;
break;
case 'h':
wordsize = 2;
break;
case 'b':
wordsize = 1;
break;
default:
retval = ERROR_COMMAND_SYNTAX_ERROR;
goto done;
}
chunk_count = MIN(count, (chunksize / wordsize));
switch (wordsize) {
case 4:
for (i = 0; i < chunk_count; i++)
target_buffer_set_u32(target, chunk + i * wordsize, pattern);
break;
case 2:
for (i = 0; i < chunk_count; i++)
target_buffer_set_u16(target, chunk + i * wordsize, pattern);
break;
case 1:
memset(chunk, pattern, chunk_count);
break;
default:
LOG_ERROR("BUG: can't happen");
exit(-1);
}
struct duration bench;
duration_start(&bench);
for (wrote = 0; wrote < (count*wordsize); wrote += cur_size) {
struct flash_bank *bank;
retval = get_flash_bank_by_addr(target, address, true, &bank);
if (retval != ERROR_OK)
goto done;
cur_size = MIN((count * wordsize - wrote), chunksize);
err = flash_driver_write(bank, chunk, address - bank->base + wrote, cur_size);
if (err != ERROR_OK) {
retval = err;
goto done;
}
err = flash_driver_read(bank, readback, address - bank->base + wrote, cur_size);
if (err != ERROR_OK) {
retval = err;
goto done;
}
for (i = 0; i < cur_size; i++) {
if (readback[i] != chunk[i]) {
LOG_ERROR(
"Verification error address 0x%08" PRIx32 ", read back 0x%02x, expected 0x%02x",
address + wrote + i,
readback[i],
chunk[i]);
retval = ERROR_FAIL;
goto done;
}
}
}
if ((retval == ERROR_OK) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "wrote %" PRIu32 " bytes to 0x%8.8" PRIx32
" in %fs (%0.3f KiB/s)", wrote, address,
duration_elapsed(&bench), duration_kbps(&bench, wrote));
}
done:
free(readback);
free(chunk);
return retval;
}
COMMAND_HANDLER(handle_flash_write_bank_command)
{
uint32_t offset;
uint8_t *buffer;
struct fileio fileio;
if (CMD_ARGC != 3)
return ERROR_COMMAND_SYNTAX_ERROR;
struct duration bench;
duration_start(&bench);
struct flash_bank *p;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p);
if (ERROR_OK != retval)
return retval;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], offset);
if (fileio_open(&fileio, CMD_ARGV[1], FILEIO_READ, FILEIO_BINARY) != ERROR_OK)
return ERROR_OK;
int filesize;
retval = fileio_size(&fileio, &filesize);
if (retval != ERROR_OK) {
fileio_close(&fileio);
return retval;
}
buffer = malloc(filesize);
if (buffer == NULL) {
fileio_close(&fileio);
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
size_t buf_cnt;
if (fileio_read(&fileio, filesize, buffer, &buf_cnt) != ERROR_OK) {
free(buffer);
fileio_close(&fileio);
return ERROR_OK;
}
retval = flash_driver_write(p, buffer, offset, buf_cnt);
free(buffer);
buffer = NULL;
if ((ERROR_OK == retval) && (duration_measure(&bench) == ERROR_OK)) {
command_print(CMD_CTX, "wrote %ld bytes from file %s to flash bank %u"
" at offset 0x%8.8" PRIx32 " in %fs (%0.3f KiB/s)",
(long)filesize, CMD_ARGV[1], p->bank_number, offset,
duration_elapsed(&bench), duration_kbps(&bench, filesize));
}
fileio_close(&fileio);
return retval;
}
void flash_set_dirty(void)
{
struct flash_bank *c;
int i;
/* set all flash to require erasing */
for (c = flash_bank_list(); c; c = c->next) {
for (i = 0; i < c->num_sectors; i++)
c->sectors[i].is_erased = 0;
}
}
static const struct command_registration flash_exec_command_handlers[] = {
{
.name = "probe",
.handler = handle_flash_probe_command,
.mode = COMMAND_EXEC,
.usage = "bank_id",
.help = "Identify a flash bank.",
},
{
.name = "info",
.handler = handle_flash_info_command,
.mode = COMMAND_EXEC,
.usage = "bank_id",
.help = "Print information about a flash bank.",
},
{
.name = "erase_check",
.handler = handle_flash_erase_check_command,
.mode = COMMAND_EXEC,
.usage = "bank_id",
.help = "Check erase state of all blocks in a "
"flash bank.",
},
{
.name = "erase_sector",
.handler = handle_flash_erase_command,
.mode = COMMAND_EXEC,
.usage = "bank_id first_sector_num last_sector_num",
.help = "Erase a range of sectors in a flash bank.",
},
{
.name = "erase_address",
.handler = handle_flash_erase_address_command,
.mode = COMMAND_EXEC,
.usage = "['pad'] ['unlock'] address length",
.help = "Erase flash sectors starting at address and "
"continuing for length bytes. If 'pad' is specified, "
"data outside that range may also be erased: the start "
"address may be decreased, and length increased, so "
"that all of the first and last sectors are erased. "
"If 'unlock' is specified, then the flash is unprotected "
"before erasing.",
},
{
.name = "fillw",
.handler = handle_flash_fill_command,
.mode = COMMAND_EXEC,
.usage = "address value n",
.help = "Fill n words with 32-bit value, starting at "
"word address. (No autoerase.)",
},
{
.name = "fillh",
.handler = handle_flash_fill_command,
.mode = COMMAND_EXEC,
.usage = "address value n",
.help = "Fill n halfwords with 16-bit value, starting at "
"word address. (No autoerase.)",
},
{
.name = "fillb",
.handler = handle_flash_fill_command,
.mode = COMMAND_EXEC,
.usage = "address value n",
.help = "Fill n bytes with 8-bit value, starting at "
"word address. (No autoerase.)",
},
{
.name = "write_bank",
.handler = handle_flash_write_bank_command,
.mode = COMMAND_EXEC,
.usage = "bank_id filename offset",
.help = "Write binary data from file to flash bank, "
"starting at specified byte offset from the "
"beginning of the bank.",
},
{
.name = "write_image",
.handler = handle_flash_write_image_command,
.mode = COMMAND_EXEC,
.usage = "[erase] [unlock] filename [offset [file_type]]",
.help = "Write an image to flash. Optionally first unprotect "
"and/or erase the region to be used. Allow optional "
"offset from beginning of bank (defaults to zero)",
},
{
.name = "protect",
.handler = handle_flash_protect_command,
.mode = COMMAND_EXEC,
.usage = "bank_id first_sector [last_sector|'last'] "
"('on'|'off')",
.help = "Turn protection on or off for a range of sectors "
"in a given flash bank.",
},
COMMAND_REGISTRATION_DONE
};
static int flash_init_drivers(struct command_context *cmd_ctx)
{
if (!flash_bank_list())
return ERROR_OK;
struct command *parent = command_find_in_context(cmd_ctx, "flash");
return register_commands(cmd_ctx, parent, flash_exec_command_handlers);
}
COMMAND_HANDLER(handle_flash_bank_command)
{
if (CMD_ARGC < 7) {
LOG_ERROR("usage: flash bank <name> <driver> "
"<base> <size> <chip_width> <bus_width> <target>");
return ERROR_COMMAND_SYNTAX_ERROR;
}
/* save bank name and advance arguments for compatibility */
const char *bank_name = *CMD_ARGV++;
CMD_ARGC--;
struct target *target = get_target(CMD_ARGV[5]);
if (target == NULL) {
LOG_ERROR("target '%s' not defined", CMD_ARGV[5]);
return ERROR_FAIL;
}
const char *driver_name = CMD_ARGV[0];
struct flash_driver *driver = flash_driver_find_by_name(driver_name);
if (NULL == driver) {
/* no matching flash driver found */
LOG_ERROR("flash driver '%s' not found", driver_name);
return ERROR_FAIL;
}
/* check the flash bank name is unique */
if (get_flash_bank_by_name_noprobe(bank_name) != NULL) {
/* flash bank name already exists */
LOG_ERROR("flash bank name '%s' already exists", bank_name);
return ERROR_FAIL;
}
/* register flash specific commands */
if (NULL != driver->commands) {
int retval = register_commands(CMD_CTX, NULL,
driver->commands);
if (ERROR_OK != retval) {
LOG_ERROR("couldn't register '%s' commands",
driver_name);
return ERROR_FAIL;
}
}
struct flash_bank *c = malloc(sizeof(*c));
c->name = strdup(bank_name);
c->target = target;
c->driver = driver;
c->driver_priv = NULL;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], c->base);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], c->size);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], c->chip_width);
COMMAND_PARSE_NUMBER(int, CMD_ARGV[4], c->bus_width);
c->num_sectors = 0;
c->sectors = NULL;
c->next = NULL;
int retval;
retval = CALL_COMMAND_HANDLER(driver->flash_bank_command, c);
if (ERROR_OK != retval) {
LOG_ERROR("'%s' driver rejected flash bank at 0x%8.8" PRIx32 "Usage %s",
driver_name, c->base, driver->usage);
free(c);
return retval;
}
if (driver->usage == NULL)
LOG_DEBUG("'%s' driver usage field missing", driver_name);
flash_bank_add(c);
return ERROR_OK;
}
COMMAND_HANDLER(handle_flash_banks_command)
{
if (CMD_ARGC != 0)
return ERROR_COMMAND_SYNTAX_ERROR;
unsigned n = 0;
for (struct flash_bank *p = flash_bank_list(); p; p = p->next, n++) {
LOG_USER("#%" PRIu32 " : %s (%s) at 0x%8.8" PRIx32 ", size 0x%8.8" PRIx32 ", "
"buswidth %u, chipwidth %u", p->bank_number,
p->name, p->driver->name, p->base, p->size,
p->bus_width, p->chip_width);
}
return ERROR_OK;
}
static int jim_flash_list(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv,
"no arguments to 'flash list' command");
return JIM_ERR;
}
Jim_Obj *list = Jim_NewListObj(interp, NULL, 0);
for (struct flash_bank *p = flash_bank_list(); p; p = p->next) {
Jim_Obj *elem = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "name", -1));
Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, p->driver->name, -1));
Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "base", -1));
Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->base));
Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "size", -1));
Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->size));
Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "bus_width", -1));
Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->bus_width));
Jim_ListAppendElement(interp, elem, Jim_NewStringObj(interp, "chip_width", -1));
Jim_ListAppendElement(interp, elem, Jim_NewIntObj(interp, p->chip_width));
Jim_ListAppendElement(interp, list, elem);
}
Jim_SetResult(interp, list);
return JIM_OK;
}
COMMAND_HANDLER(handle_flash_init_command)
{
if (CMD_ARGC != 0)
return ERROR_COMMAND_SYNTAX_ERROR;
static bool flash_initialized;
if (flash_initialized) {
LOG_INFO("'flash init' has already been called");
return ERROR_OK;
}
flash_initialized = true;
LOG_DEBUG("Initializing flash devices...");
return flash_init_drivers(CMD_CTX);
}
static const struct command_registration flash_config_command_handlers[] = {
{
.name = "bank",
.handler = handle_flash_bank_command,
.mode = COMMAND_CONFIG,
.usage = "bank_id driver_name base_address size_bytes "
"chip_width_bytes bus_width_bytes target "
"[driver_options ...]",
.help = "Define a new bank with the given name, "
"using the specified NOR flash driver.",
},
{
.name = "init",
.mode = COMMAND_CONFIG,
.handler = handle_flash_init_command,
.help = "Initialize flash devices.",
},
{
.name = "banks",
.mode = COMMAND_ANY,
.handler = handle_flash_banks_command,
.help = "Display table with information about flash banks.",
},
{
.name = "list",
.mode = COMMAND_ANY,
.jim_handler = jim_flash_list,
.help = "Returns a list of details about the flash banks.",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration flash_command_handlers[] = {
{
.name = "flash",
.mode = COMMAND_ANY,
.help = "NOR flash command group",
.chain = flash_config_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
int flash_register_commands(struct command_context *cmd_ctx)
{
return register_commands(cmd_ctx, NULL, flash_command_handlers);
}
File diff suppressed because it is too large Load Diff
+234
View File
@@ -0,0 +1,234 @@
/***************************************************************************
* Copyright (C) 2010 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
static struct flash_bank *virtual_get_master_bank(struct flash_bank *bank)
{
struct flash_bank *master_bank;
master_bank = get_flash_bank_by_name_noprobe(bank->driver_priv);
if (master_bank == NULL)
LOG_ERROR("master flash bank '%s' does not exist", (char *)bank->driver_priv);
return master_bank;
}
static void virtual_update_bank_info(struct flash_bank *bank)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
if (master_bank == NULL)
return;
/* update the info we do not have */
bank->size = master_bank->size;
bank->chip_width = master_bank->chip_width;
bank->bus_width = master_bank->bus_width;
bank->num_sectors = master_bank->num_sectors;
bank->sectors = master_bank->sectors;
}
FLASH_BANK_COMMAND_HANDLER(virtual_flash_bank_command)
{
if (CMD_ARGC < 7)
return ERROR_COMMAND_SYNTAX_ERROR;
/* get the master flash bank */
const char *bank_name = CMD_ARGV[6];
struct flash_bank *master_bank = get_flash_bank_by_name_noprobe(bank_name);
if (master_bank == NULL) {
LOG_ERROR("master flash bank '%s' does not exist", bank_name);
return ERROR_FLASH_OPERATION_FAILED;
}
/* save master bank name - use this to get settings later */
bank->driver_priv = strdup(bank_name);
return ERROR_OK;
}
static int virtual_protect(struct flash_bank *bank, int set, int first, int last)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->protect(master_bank, set, first, last);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int virtual_protect_check(struct flash_bank *bank)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->protect_check(master_bank);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int virtual_erase(struct flash_bank *bank, int first, int last)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->erase(master_bank, first, last);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int virtual_write(struct flash_bank *bank, uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->write(master_bank, buffer, offset, count);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int virtual_probe(struct flash_bank *bank)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->probe(master_bank);
if (retval != ERROR_OK)
return retval;
/* update the info we do not have */
virtual_update_bank_info(bank);
return ERROR_OK;
}
static int virtual_auto_probe(struct flash_bank *bank)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->auto_probe(master_bank);
if (retval != ERROR_OK)
return retval;
/* update the info we do not have */
virtual_update_bank_info(bank);
return ERROR_OK;
}
static int virtual_info(struct flash_bank *bank, char *buf, int buf_size)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
snprintf(buf, buf_size, "%s driver for flash bank %s at 0x%8.8" PRIx32 "",
bank->driver->name, master_bank->name, master_bank->base);
return ERROR_OK;
}
static int virtual_blank_check(struct flash_bank *bank)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->erase_check(master_bank);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
static int virtual_flash_read(struct flash_bank *bank,
uint8_t *buffer, uint32_t offset, uint32_t count)
{
struct flash_bank *master_bank = virtual_get_master_bank(bank);
int retval;
if (master_bank == NULL)
return ERROR_FLASH_OPERATION_FAILED;
/* call master handler */
retval = master_bank->driver->read(master_bank, buffer, offset, count);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
}
struct flash_driver virtual_flash = {
.name = "virtual",
.flash_bank_command = virtual_flash_bank_command,
.erase = virtual_erase,
.protect = virtual_protect,
.write = virtual_write,
.read = virtual_flash_read,
.probe = virtual_probe,
.auto_probe = virtual_auto_probe,
.erase_check = virtual_blank_check,
.protect_check = virtual_protect_check,
.info = virtual_info,
};
+82
View File
@@ -0,0 +1,82 @@
# Defines basic Tcl procs for OpenOCD flash module
#
# program utility proc
# usage: program filename
# optional args: verify, reset and address
#
proc program {filename args} {
foreach arg $args {
if {[string equal $arg "verify"]} {
set verify 1
} elseif {[string equal $arg "reset"]} {
set reset 1
} else {
set address $arg
}
}
# make sure init is called
if {[catch {init}] != 0} {
echo "** OpenOCD init Failed **"
shutdown
return
}
# reset target and call any init scripts
if {[catch {reset init}] != 0} {
echo "** Unable to reset target **"
shutdown
return
}
# start programming phase
echo "** Programming Started **"
if {[info exists address]} {
set flash_args "$filename $address"
} else {
set flash_args "$filename"
}
if {[catch {eval flash write_image erase $flash_args}] == 0} {
echo "** Programming Finished **"
if {[info exists verify]} {
# verify phase
echo "** Verify Started **"
if {[catch {eval verify_image $flash_args}] == 0} {
echo "** Verified OK **"
} else {
echo "** Verify Failed **"
}
}
if {[info exists reset]} {
# reset target if requested
# also disable target polling, we are shutting down anyway
poll off
echo "** Resetting Target **"
reset run
}
} else {
echo "** Programming Failed **"
}
# shutdown OpenOCD
shutdown
}
add_help_text program "write an image to flash, address is only required for binary images. verify, reset are optional"
add_usage_text program "<filename> \[address\] \[verify\] \[reset\]"
# ease migration to updated flash driver
proc stm32x args {
echo "DEPRECATED! use 'stm32f1x $args' not 'stm32x $args'"
eval stm32f1x $args
}
proc stm32f2xxx args {
echo "DEPRECATED! use 'stm32f2x $args' not 'stm32f2xxx $args'"
eval stm32f2x $args
}
+114
View File
@@ -0,0 +1,114 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <helper/log.h>
COMMAND_HANDLER(handle_foo_command)
{
if (CMD_ARGC < 1 || CMD_ARGC > 2)
return ERROR_COMMAND_SYNTAX_ERROR;
uint32_t address;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
const char *msg = "<unchanged>";
if (CMD_ARGC == 2) {
bool enable;
COMMAND_PARSE_ENABLE(CMD_ARGV[1], enable);
msg = enable ? "enable" : "disable";
}
LOG_INFO("%s: address=0x%8.8" PRIx32 " enabled=%s", CMD_NAME, address, msg);
return ERROR_OK;
}
static bool foo_flag;
COMMAND_HANDLER(handle_flag_command)
{
return CALL_COMMAND_HANDLER(handle_command_parse_bool,
&foo_flag, "foo flag");
}
static const struct command_registration foo_command_handlers[] = {
{
.name = "bar",
.handler = &handle_foo_command,
.mode = COMMAND_ANY,
.usage = "address ['enable'|'disable']",
.help = "an example command",
},
{
.name = "baz",
.handler = &handle_foo_command,
.mode = COMMAND_ANY,
.usage = "address ['enable'|'disable']",
.help = "a sample command",
},
{
.name = "flag",
.handler = &handle_flag_command,
.mode = COMMAND_ANY,
.usage = "[on|off]",
.help = "set a flag",
},
COMMAND_REGISTRATION_DONE
};
static COMMAND_HELPER(handle_hello_args, const char **sep, const char **name)
{
if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
if (1 == CMD_ARGC) {
*sep = " ";
*name = CMD_ARGV[0];
} else
*sep = *name = "";
return ERROR_OK;
}
COMMAND_HANDLER(handle_hello_command)
{
const char *sep, *name;
int retval = CALL_COMMAND_HANDLER(handle_hello_args, &sep, &name);
if (ERROR_OK == retval)
command_print(CMD_CTX, "Greetings%s%s!", sep, name);
return retval;
}
const struct command_registration hello_command_handlers[] = {
{
.name = "hello",
.handler = handle_hello_command,
.mode = COMMAND_ANY,
.help = "prints a warm welcome",
.usage = "[name]",
},
{
.name = "foo",
.mode = COMMAND_ANY,
.help = "example command handler skeleton",
.chain = foo_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
+31
View File
@@ -0,0 +1,31 @@
/***************************************************************************
* Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef OPENOCD_HELLO_H
#define OPENOCD_HELLO_H
struct command_registration;
/**
* Export the registration for the hello command group, so it can be
* embedded in example drivers.
*/
extern const struct command_registration hello_command_handlers[];
#endif /* OPENOCD_HELLO_H */
+59
View File
@@ -0,0 +1,59 @@
include $(top_srcdir)/common.mk
METASOURCES = AUTO
noinst_LTLIBRARIES = libhelper.la
CONFIGFILES = options.c time_support_common.c
libhelper_la_SOURCES = \
binarybuffer.c \
$(CONFIGFILES) \
configuration.c \
log.c \
command.c \
time_support.c \
replacements.c \
fileio.c \
util.c \
jim-nvp.c
if IOUTIL
libhelper_la_SOURCES += ioutil.c
else
libhelper_la_SOURCES += ioutil_stubs.c
endif
libhelper_la_CFLAGS =
if IS_MINGW
# FD_* macros are sloppy with their signs on MinGW32 platform
libhelper_la_CFLAGS += -Wno-sign-compare
endif
noinst_HEADERS = \
binarybuffer.h \
configuration.h \
ioutil.h \
list.h \
util.h \
types.h \
log.h \
command.h \
time_support.h \
replacements.h \
fileio.h \
system.h \
bin2char.c \
jim-nvp.h
EXTRA_DIST = startup.tcl
BIN2C = bin2char$(EXEEXT_FOR_BUILD)
BUILT_SOURCES = $(BIN2C)
$(BIN2C): bin2char.c
${CC_FOR_BUILD} ${CFLAGS_FOR_BUILD} $(srcdir)/bin2char.c -o $@
CLEANFILES = bin2char$(EXEEXT_FOR_BUILD)
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+716
View File
@@ -0,0 +1,716 @@
# Makefile.in generated by automake 1.13.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2012 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__make_dryrun = \
{ \
am__dry=no; \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
*) \
for am__flg in $$MAKEFLAGS; do \
case $$am__flg in \
*=*|--*) ;; \
*n*) am__dry=yes; break;; \
esac; \
done;; \
esac; \
test $$am__dry = yes; \
}
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
DIST_COMMON = $(top_srcdir)/common.mk $(srcdir)/Makefile.in \
$(srcdir)/Makefile.am $(top_srcdir)/depcomp $(noinst_HEADERS)
@INTERNAL_JIMTCL_TRUE@am__append_1 = -I$(top_srcdir)/jimtcl \
@INTERNAL_JIMTCL_TRUE@ -I$(top_builddir)/jimtcl
@IOUTIL_TRUE@am__append_2 = ioutil.c
@IOUTIL_FALSE@am__append_3 = ioutil_stubs.c
# FD_* macros are sloppy with their signs on MinGW32 platform
@IS_MINGW_TRUE@am__append_4 = -Wno-sign-compare
subdir = src/helper
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config_subdir.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libhelper_la_LIBADD =
am__libhelper_la_SOURCES_DIST = binarybuffer.c options.c \
time_support_common.c configuration.c log.c command.c \
time_support.c replacements.c fileio.c util.c jim-nvp.c \
ioutil.c ioutil_stubs.c
am__objects_1 = libhelper_la-options.lo \
libhelper_la-time_support_common.lo
@IOUTIL_TRUE@am__objects_2 = libhelper_la-ioutil.lo
@IOUTIL_FALSE@am__objects_3 = libhelper_la-ioutil_stubs.lo
am_libhelper_la_OBJECTS = libhelper_la-binarybuffer.lo \
$(am__objects_1) libhelper_la-configuration.lo \
libhelper_la-log.lo libhelper_la-command.lo \
libhelper_la-time_support.lo libhelper_la-replacements.lo \
libhelper_la-fileio.lo libhelper_la-util.lo \
libhelper_la-jim-nvp.lo $(am__objects_2) $(am__objects_3)
libhelper_la_OBJECTS = $(am_libhelper_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
libhelper_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libhelper_la_CFLAGS) \
$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libhelper_la_SOURCES)
DIST_SOURCES = $(am__libhelper_la_SOURCES_DIST)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(noinst_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CC_FOR_BUILD = @CC_FOR_BUILD@
CFLAGS = @CFLAGS@
CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
FGREP = @FGREP@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
doxygen_as_html = @doxygen_as_html@
doxygen_as_pdf = @doxygen_as_pdf@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
# common flags used in openocd build
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
-I$(top_srcdir)/src/helper -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPKGLIBDIR=\"$(pkglibdir)\" $(am__append_1)
METASOURCES = AUTO
noinst_LTLIBRARIES = libhelper.la
CONFIGFILES = options.c time_support_common.c
libhelper_la_SOURCES = binarybuffer.c $(CONFIGFILES) configuration.c \
log.c command.c time_support.c replacements.c fileio.c util.c \
jim-nvp.c $(am__append_2) $(am__append_3)
libhelper_la_CFLAGS = $(am__append_4)
noinst_HEADERS = \
binarybuffer.h \
configuration.h \
ioutil.h \
list.h \
util.h \
types.h \
log.h \
command.h \
time_support.h \
replacements.h \
fileio.h \
system.h \
bin2char.c \
jim-nvp.h
EXTRA_DIST = startup.tcl
BIN2C = bin2char$(EXEEXT_FOR_BUILD)
BUILT_SOURCES = $(BIN2C)
CLEANFILES = bin2char$(EXEEXT_FOR_BUILD)
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common.mk $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/helper/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/helper/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_srcdir)/common.mk:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
@list='$(noinst_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
test -z "$$locs" || { \
echo rm -f $${locs}; \
rm -f $${locs}; \
}
libhelper.la: $(libhelper_la_OBJECTS) $(libhelper_la_DEPENDENCIES) $(EXTRA_libhelper_la_DEPENDENCIES)
$(AM_V_CCLD)$(libhelper_la_LINK) $(libhelper_la_OBJECTS) $(libhelper_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-binarybuffer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-command.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-configuration.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-fileio.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-ioutil.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-ioutil_stubs.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-jim-nvp.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-log.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-options.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-replacements.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-time_support.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-time_support_common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhelper_la-util.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libhelper_la-binarybuffer.lo: binarybuffer.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-binarybuffer.lo -MD -MP -MF $(DEPDIR)/libhelper_la-binarybuffer.Tpo -c -o libhelper_la-binarybuffer.lo `test -f 'binarybuffer.c' || echo '$(srcdir)/'`binarybuffer.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-binarybuffer.Tpo $(DEPDIR)/libhelper_la-binarybuffer.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='binarybuffer.c' object='libhelper_la-binarybuffer.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-binarybuffer.lo `test -f 'binarybuffer.c' || echo '$(srcdir)/'`binarybuffer.c
libhelper_la-options.lo: options.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-options.lo -MD -MP -MF $(DEPDIR)/libhelper_la-options.Tpo -c -o libhelper_la-options.lo `test -f 'options.c' || echo '$(srcdir)/'`options.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-options.Tpo $(DEPDIR)/libhelper_la-options.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='options.c' object='libhelper_la-options.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-options.lo `test -f 'options.c' || echo '$(srcdir)/'`options.c
libhelper_la-time_support_common.lo: time_support_common.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-time_support_common.lo -MD -MP -MF $(DEPDIR)/libhelper_la-time_support_common.Tpo -c -o libhelper_la-time_support_common.lo `test -f 'time_support_common.c' || echo '$(srcdir)/'`time_support_common.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-time_support_common.Tpo $(DEPDIR)/libhelper_la-time_support_common.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='time_support_common.c' object='libhelper_la-time_support_common.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-time_support_common.lo `test -f 'time_support_common.c' || echo '$(srcdir)/'`time_support_common.c
libhelper_la-configuration.lo: configuration.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-configuration.lo -MD -MP -MF $(DEPDIR)/libhelper_la-configuration.Tpo -c -o libhelper_la-configuration.lo `test -f 'configuration.c' || echo '$(srcdir)/'`configuration.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-configuration.Tpo $(DEPDIR)/libhelper_la-configuration.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configuration.c' object='libhelper_la-configuration.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-configuration.lo `test -f 'configuration.c' || echo '$(srcdir)/'`configuration.c
libhelper_la-log.lo: log.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-log.lo -MD -MP -MF $(DEPDIR)/libhelper_la-log.Tpo -c -o libhelper_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-log.Tpo $(DEPDIR)/libhelper_la-log.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='libhelper_la-log.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
libhelper_la-command.lo: command.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-command.lo -MD -MP -MF $(DEPDIR)/libhelper_la-command.Tpo -c -o libhelper_la-command.lo `test -f 'command.c' || echo '$(srcdir)/'`command.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-command.Tpo $(DEPDIR)/libhelper_la-command.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='command.c' object='libhelper_la-command.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-command.lo `test -f 'command.c' || echo '$(srcdir)/'`command.c
libhelper_la-time_support.lo: time_support.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-time_support.lo -MD -MP -MF $(DEPDIR)/libhelper_la-time_support.Tpo -c -o libhelper_la-time_support.lo `test -f 'time_support.c' || echo '$(srcdir)/'`time_support.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-time_support.Tpo $(DEPDIR)/libhelper_la-time_support.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='time_support.c' object='libhelper_la-time_support.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-time_support.lo `test -f 'time_support.c' || echo '$(srcdir)/'`time_support.c
libhelper_la-replacements.lo: replacements.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-replacements.lo -MD -MP -MF $(DEPDIR)/libhelper_la-replacements.Tpo -c -o libhelper_la-replacements.lo `test -f 'replacements.c' || echo '$(srcdir)/'`replacements.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-replacements.Tpo $(DEPDIR)/libhelper_la-replacements.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='replacements.c' object='libhelper_la-replacements.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-replacements.lo `test -f 'replacements.c' || echo '$(srcdir)/'`replacements.c
libhelper_la-fileio.lo: fileio.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-fileio.lo -MD -MP -MF $(DEPDIR)/libhelper_la-fileio.Tpo -c -o libhelper_la-fileio.lo `test -f 'fileio.c' || echo '$(srcdir)/'`fileio.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-fileio.Tpo $(DEPDIR)/libhelper_la-fileio.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileio.c' object='libhelper_la-fileio.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-fileio.lo `test -f 'fileio.c' || echo '$(srcdir)/'`fileio.c
libhelper_la-util.lo: util.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-util.lo -MD -MP -MF $(DEPDIR)/libhelper_la-util.Tpo -c -o libhelper_la-util.lo `test -f 'util.c' || echo '$(srcdir)/'`util.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-util.Tpo $(DEPDIR)/libhelper_la-util.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='util.c' object='libhelper_la-util.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-util.lo `test -f 'util.c' || echo '$(srcdir)/'`util.c
libhelper_la-jim-nvp.lo: jim-nvp.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-jim-nvp.lo -MD -MP -MF $(DEPDIR)/libhelper_la-jim-nvp.Tpo -c -o libhelper_la-jim-nvp.lo `test -f 'jim-nvp.c' || echo '$(srcdir)/'`jim-nvp.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-jim-nvp.Tpo $(DEPDIR)/libhelper_la-jim-nvp.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jim-nvp.c' object='libhelper_la-jim-nvp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-jim-nvp.lo `test -f 'jim-nvp.c' || echo '$(srcdir)/'`jim-nvp.c
libhelper_la-ioutil.lo: ioutil.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-ioutil.lo -MD -MP -MF $(DEPDIR)/libhelper_la-ioutil.Tpo -c -o libhelper_la-ioutil.lo `test -f 'ioutil.c' || echo '$(srcdir)/'`ioutil.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-ioutil.Tpo $(DEPDIR)/libhelper_la-ioutil.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ioutil.c' object='libhelper_la-ioutil.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-ioutil.lo `test -f 'ioutil.c' || echo '$(srcdir)/'`ioutil.c
libhelper_la-ioutil_stubs.lo: ioutil_stubs.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -MT libhelper_la-ioutil_stubs.lo -MD -MP -MF $(DEPDIR)/libhelper_la-ioutil_stubs.Tpo -c -o libhelper_la-ioutil_stubs.lo `test -f 'ioutil_stubs.c' || echo '$(srcdir)/'`ioutil_stubs.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhelper_la-ioutil_stubs.Tpo $(DEPDIR)/libhelper_la-ioutil_stubs.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ioutil_stubs.c' object='libhelper_la-ioutil_stubs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhelper_la_CFLAGS) $(CFLAGS) -c -o libhelper_la-ioutil_stubs.lo `test -f 'ioutil_stubs.c' || echo '$(srcdir)/'`ioutil_stubs.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) check-am
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs:
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
clean: clean-am
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am:
.MAKE: all check install install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am
$(BIN2C): bin2char.c
${CC_FOR_BUILD} ${CFLAGS_FOR_BUILD} $(srcdir)/bin2char.c -o $@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+58
View File
@@ -0,0 +1,58 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <fcntl.h>
#endif
int main(int argc, char **argv)
{
int c;
unsigned int n;
const char *name;
if (argc == 1) {
fprintf(stderr, "bin2char <varname>\n");
fprintf(stderr, "read from standard input and write a char"
" array out to standard output\n");
exit(1);
}
#ifdef _WIN32
/* for win32 set stdin/stdout to binary mode */
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
#endif
n = 0;
name = argv[1];
fprintf(stdout, "/* autogenerated from %s */\n", argv[0]);
fprintf(stdout, "unsigned const char %s[] = {\n", name);
while ((c = getc(stdin)) != EOF) {
fprintf(stdout, "0x%02x,", c & 0xff);
if ((++n % 16) == 0)
fprintf(stdout, "\n");
}
fprintf(stdout, "0 /* terminate with a null */};\n");
return 0;
}
+399
View File
@@ -0,0 +1,399 @@
/***************************************************************************
* Copyright (C) 2004, 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "log.h"
#include "binarybuffer.h"
static const unsigned char bit_reverse_table256[] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
void *buf_cpy(const void *from, void *_to, unsigned size)
{
if (NULL == from || NULL == _to)
return NULL;
/* copy entire buffer */
memcpy(_to, from, DIV_ROUND_UP(size, 8));
/* mask out bits that don't belong to the buffer */
unsigned trailing_bits = size % 8;
if (trailing_bits) {
uint8_t *to = _to;
to[size / 8] &= (1 << trailing_bits) - 1;
}
return _to;
}
static bool buf_cmp_masked(uint8_t a, uint8_t b, uint8_t m)
{
return (a & m) != (b & m);
}
static bool buf_cmp_trailing(uint8_t a, uint8_t b, uint8_t m, unsigned trailing)
{
uint8_t mask = (1 << trailing) - 1;
return buf_cmp_masked(a, b, mask & m);
}
bool buf_cmp(const void *_buf1, const void *_buf2, unsigned size)
{
if (!_buf1 || !_buf2)
return _buf1 != _buf2;
unsigned last = size / 8;
if (memcmp(_buf1, _buf2, last) != 0)
return false;
unsigned trailing = size % 8;
if (!trailing)
return false;
const uint8_t *buf1 = _buf1, *buf2 = _buf2;
return buf_cmp_trailing(buf1[last], buf2[last], 0xff, trailing);
}
bool buf_cmp_mask(const void *_buf1, const void *_buf2,
const void *_mask, unsigned size)
{
if (!_buf1 || !_buf2)
return _buf1 != _buf2 || _buf1 != _mask;
const uint8_t *buf1 = _buf1, *buf2 = _buf2, *mask = _mask;
unsigned last = size / 8;
for (unsigned i = 0; i < last; i++) {
if (buf_cmp_masked(buf1[i], buf2[i], mask[i]))
return true;
}
unsigned trailing = size % 8;
if (!trailing)
return false;
return buf_cmp_trailing(buf1[last], buf2[last], mask[last], trailing);
}
void *buf_set_ones(void *_buf, unsigned size)
{
uint8_t *buf = _buf;
if (!buf)
return NULL;
memset(buf, 0xff, size / 8);
unsigned trailing_bits = size % 8;
if (trailing_bits)
buf[size / 8] = (1 << trailing_bits) - 1;
return buf;
}
void *buf_set_buf(const void *_src, unsigned src_start,
void *_dst, unsigned dst_start, unsigned len)
{
const uint8_t *src = _src;
uint8_t *dst = _dst;
unsigned i, sb, db, sq, dq, lb, lq;
sb = src_start / 8;
db = dst_start / 8;
sq = src_start % 8;
dq = dst_start % 8;
lb = len / 8;
lq = len % 8;
src += sb;
dst += db;
/* check if both buffers are on byte boundary and
* len is a multiple of 8bit so we can simple copy
* the buffer */
if ((sq == 0) && (dq == 0) && (lq == 0)) {
for (i = 0; i < lb; i++)
*dst++ = *src++;
return (uint8_t *)_dst;
}
/* fallback to slow bit copy */
for (i = 0; i < len; i++) {
if (((*src >> (sq&7)) & 1) == 1)
*dst |= 1 << (dq&7);
else
*dst &= ~(1 << (dq&7));
if (sq++ == 7) {
sq = 0;
src++;
}
if (dq++ == 7) {
dq = 0;
dst++;
}
}
return (uint8_t *)_dst;
}
uint32_t flip_u32(uint32_t value, unsigned int num)
{
uint32_t c = (bit_reverse_table256[value & 0xff] << 24) |
(bit_reverse_table256[(value >> 8) & 0xff] << 16) |
(bit_reverse_table256[(value >> 16) & 0xff] << 8) |
(bit_reverse_table256[(value >> 24) & 0xff]);
if (num < 32)
c = c >> (32 - num);
return c;
}
static int ceil_f_to_u32(float x)
{
if (x < 0) /* return zero for negative numbers */
return 0;
uint32_t y = x; /* cut off fraction */
if ((x - y) > 0.0) /* if there was a fractional part, increase by one */
y++;
return y;
}
char *buf_to_str(const void *_buf, unsigned buf_len, unsigned radix)
{
float factor;
switch (radix) {
case 16:
factor = 2.0; /* log(256) / log(16) = 2.0 */
break;
case 10:
factor = 2.40824; /* log(256) / log(10) = 2.40824 */
break;
case 8:
factor = 2.66667; /* log(256) / log(8) = 2.66667 */
break;
default:
return NULL;
}
unsigned str_len = ceil_f_to_u32(DIV_ROUND_UP(buf_len, 8) * factor);
char *str = calloc(str_len + 1, 1);
const uint8_t *buf = _buf;
int b256_len = DIV_ROUND_UP(buf_len, 8);
for (int i = b256_len - 1; i >= 0; i--) {
uint32_t tmp = buf[i];
if (((unsigned)i == (buf_len / 8)) && (buf_len % 8))
tmp &= (0xff >> (8 - (buf_len % 8)));
/* base-256 digits */
for (unsigned j = str_len; j > 0; j--) {
tmp += (uint32_t)str[j-1] * 256;
str[j-1] = (uint8_t)(tmp % radix);
tmp /= radix;
}
}
const char *DIGITS = "0123456789ABCDEF";
for (unsigned j = 0; j < str_len; j++)
str[j] = DIGITS[(int)str[j]];
return str;
}
/** identify radix, and skip radix-prefix (0, 0x or 0X) */
static void str_radix_guess(const char **_str, unsigned *_str_len,
unsigned *_radix)
{
unsigned radix = *_radix;
if (0 != radix)
return;
const char *str = *_str;
unsigned str_len = *_str_len;
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
radix = 16;
str += 2;
str_len -= 2;
} else if ((str[0] == '0') && (str_len != 1)) {
radix = 8;
str += 1;
str_len -= 1;
} else
radix = 10;
*_str = str;
*_str_len = str_len;
*_radix = radix;
}
int str_to_buf(const char *str, unsigned str_len,
void *_buf, unsigned buf_len, unsigned radix)
{
str_radix_guess(&str, &str_len, &radix);
float factor;
if (radix == 16)
factor = 0.5; /* log(16) / log(256) = 0.5 */
else if (radix == 10)
factor = 0.41524; /* log(10) / log(256) = 0.41524 */
else if (radix == 8)
factor = 0.375; /* log(8) / log(256) = 0.375 */
else
return 0;
/* copy to zero-terminated buffer */
char *charbuf = strndup(str, str_len);
/* number of digits in base-256 notation */
unsigned b256_len = ceil_f_to_u32(str_len * factor);
uint8_t *b256_buf = calloc(b256_len, 1);
/* go through zero terminated buffer
* input digits (ASCII) */
unsigned i;
for (i = 0; charbuf[i]; i++) {
uint32_t tmp = charbuf[i];
if ((tmp >= '0') && (tmp <= '9'))
tmp = (tmp - '0');
else if ((tmp >= 'a') && (tmp <= 'f'))
tmp = (tmp - 'a' + 10);
else if ((tmp >= 'A') && (tmp <= 'F'))
tmp = (tmp - 'A' + 10);
else
continue; /* skip characters other than [0-9,a-f,A-F] */
if (tmp >= radix)
continue; /* skip digits invalid for the current radix */
/* base-256 digits */
for (unsigned j = 0; j < b256_len; j++) {
tmp += (uint32_t)b256_buf[j] * radix;
b256_buf[j] = (uint8_t)(tmp & 0xFF);
tmp >>= 8;
}
}
uint8_t *buf = _buf;
for (unsigned j = 0; j < DIV_ROUND_UP(buf_len, 8); j++) {
if (j < b256_len)
buf[j] = b256_buf[j];
else
buf[j] = 0;
}
/* mask out bits that don't belong to the buffer */
if (buf_len % 8)
buf[(buf_len / 8)] &= 0xff >> (8 - (buf_len % 8));
free(b256_buf);
free(charbuf);
return i;
}
void bit_copy_queue_init(struct bit_copy_queue *q)
{
INIT_LIST_HEAD(&q->list);
}
int bit_copy_queued(struct bit_copy_queue *q, uint8_t *dst, unsigned dst_offset, const uint8_t *src,
unsigned src_offset, unsigned bit_count)
{
struct bit_copy_queue_entry *qe = malloc(sizeof(*qe));
if (!qe)
return ERROR_FAIL;
qe->dst = dst;
qe->dst_offset = dst_offset;
qe->src = src;
qe->src_offset = src_offset;
qe->bit_count = bit_count;
list_add_tail(&qe->list, &q->list);
return ERROR_OK;
}
void bit_copy_execute(struct bit_copy_queue *q)
{
struct bit_copy_queue_entry *qe;
struct bit_copy_queue_entry *tmp;
list_for_each_entry_safe(qe, tmp, &q->list, list) {
bit_copy(qe->dst, qe->dst_offset, qe->src, qe->src_offset, qe->bit_count);
list_del(&qe->list);
free(qe);
}
}
void bit_copy_discard(struct bit_copy_queue *q)
{
struct bit_copy_queue_entry *qe;
struct bit_copy_queue_entry *tmp;
list_for_each_entry_safe(qe, tmp, &q->list, list) {
list_del(&qe->list);
free(qe);
}
}
int unhexify(char *bin, const char *hex, int count)
{
int i, tmp;
for (i = 0; i < count; i++) {
if (sscanf(hex + (2 * i), "%02x", &tmp) != 1)
return i;
bin[i] = tmp;
}
return i;
}
int hexify(char *hex, const char *bin, int count, int out_maxlen)
{
int i, cmd_len = 0;
/* May use a length, or a null-terminated string as input. */
if (count == 0)
count = strlen(bin);
for (i = 0; i < count; i++)
cmd_len += snprintf(hex + cmd_len, out_maxlen - cmd_len, "%02x", bin[i] & 0xff);
return cmd_len;
}
+164
View File
@@ -0,0 +1,164 @@
/***************************************************************************
* Copyright (C) 2004, 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef BINARYBUFFER_H
#define BINARYBUFFER_H
#include "list.h"
/** @file
* Support functions to access arbitrary bits in a byte array
*/
/**
* Sets @c num bits in @c _buffer, starting at the @c first bit,
* using the bits in @c value. This routine fast-paths writes
* of little-endian, byte-aligned, 32-bit words.
* @param _buffer The buffer whose bits will be set.
* @param first The bit offset in @c _buffer to start writing (0-31).
* @param num The number of bits from @c value to copy (1-32).
* @param value Up to 32 bits that will be copied to _buffer.
*/
static inline void buf_set_u32(void *_buffer,
unsigned first, unsigned num, uint32_t value)
{
uint8_t *buffer = (uint8_t *)_buffer;
if ((num == 32) && (first == 0)) {
buffer[3] = (value >> 24) & 0xff;
buffer[2] = (value >> 16) & 0xff;
buffer[1] = (value >> 8) & 0xff;
buffer[0] = (value >> 0) & 0xff;
} else {
for (unsigned i = first; i < first + num; i++) {
if (((value >> (i - first)) & 1) == 1)
buffer[i / 8] |= 1 << (i % 8);
else
buffer[i / 8] &= ~(1 << (i % 8));
}
}
}
/**
* Retrieves @c num bits from @c _buffer, starting at the @c first bit,
* returning the bits in a 32-bit word. This routine fast-paths reads
* of little-endian, byte-aligned, 32-bit words.
* @param _buffer The buffer whose bits will be read.
* @param first The bit offset in @c _buffer to start reading (0-31).
* @param num The number of bits from @c _buffer to read (1-32).
* @returns Up to 32-bits that were read from @c _buffer.
*/
static inline uint32_t buf_get_u32(const void *_buffer,
unsigned first, unsigned num)
{
uint8_t *buffer = (uint8_t *)_buffer;
if ((num == 32) && (first == 0)) {
return (((uint32_t)buffer[3]) << 24) |
(((uint32_t)buffer[2]) << 16) |
(((uint32_t)buffer[1]) << 8) |
(((uint32_t)buffer[0]) << 0);
} else {
uint32_t result = 0;
for (unsigned i = first; i < first + num; i++) {
if (((buffer[i / 8] >> (i % 8)) & 1) == 1)
result |= 1 << (i - first);
}
return result;
}
}
/**
* Inverts the ordering of bits inside a 32-bit word (e.g. 31..0 -> 0..31).
* This routine can be used to flip smaller data types by using smaller
* values for @c width.
* @param value The word to flip.
* @param width The number of bits in value (2-32).
* @returns A 32-bit word with @c value in reversed bit-order.
*/
uint32_t flip_u32(uint32_t value, unsigned width);
bool buf_cmp(const void *buf1, const void *buf2, unsigned size);
bool buf_cmp_mask(const void *buf1, const void *buf2,
const void *mask, unsigned size);
/**
* Copies @c size bits out of @c from and into @c to. Any extra
* bits in the final byte will be set to zero.
* @param from The buffer to copy into @c to.
* @param to The buffer that will receive the copy of @c from.
* @param size The number of bits to copy.
*/
void *buf_cpy(const void *from, void *to, unsigned size);
/**
* Set the contents of @c buf with @c count bits, all set to 1.
* @param buf The buffer to fill with ones.
* @param size The number of bits.
* @returns The original buffer (@c buf).
*/
void *buf_set_ones(void *buf, unsigned size);
void *buf_set_buf(const void *src, unsigned src_start,
void *dst, unsigned dst_start, unsigned len);
int str_to_buf(const char *str, unsigned len,
void *bin_buf, unsigned buf_size, unsigned radix);
char *buf_to_str(const void *buf, unsigned size, unsigned radix);
/* read a uint32_t from a buffer in target memory endianness */
static inline uint32_t fast_target_buffer_get_u32(const void *p, bool le)
{
return le ? le_to_h_u32(p) : be_to_h_u32(p);
}
static inline void bit_copy(uint8_t *dst, unsigned dst_offset, const uint8_t *src,
unsigned src_offset, unsigned bit_count)
{
buf_set_buf(src, src_offset, dst, dst_offset, bit_count);
}
struct bit_copy_queue {
struct list_head list;
};
struct bit_copy_queue_entry {
uint8_t *dst;
unsigned dst_offset;
const uint8_t *src;
unsigned src_offset;
unsigned bit_count;
struct list_head list;
};
void bit_copy_queue_init(struct bit_copy_queue *q);
int bit_copy_queued(struct bit_copy_queue *q, uint8_t *dst, unsigned dst_offset, const uint8_t *src,
unsigned src_offset, unsigned bit_count);
void bit_copy_execute(struct bit_copy_queue *q);
void bit_copy_discard(struct bit_copy_queue *q);
/* functions to convert to/from hex encoded buffer
* used in ti-icdi driver and gdb server */
int unhexify(char *bin, const char *hex, int count);
int hexify(char *hex, const char *bin, int count, int out_maxlen);
#endif /* BINARYBUFFER_H */
File diff suppressed because it is too large Load Diff
+419
View File
@@ -0,0 +1,419 @@
/***************************************************************************
* Copyright (C) 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef COMMAND_H
#define COMMAND_H
#include <jim-nvp.h>
/* To achieve C99 printf compatibility in MinGW, gnu_printf should be
* used for __attribute__((format( ... ))), with GCC v4.4 or later
*/
#if (defined(IS_MINGW) && (((__GNUC__ << 16) + __GNUC_MINOR__) >= 0x00040004))
#define PRINTF_ATTRIBUTE_FORMAT gnu_printf
#else
#define PRINTF_ATTRIBUTE_FORMAT printf
#endif
enum command_mode {
COMMAND_EXEC,
COMMAND_CONFIG,
COMMAND_ANY,
};
struct command_context;
/** The type signature for command context's output handler. */
typedef int (*command_output_handler_t)(struct command_context *context,
const char *line);
struct command_context {
Jim_Interp *interp;
enum command_mode mode;
struct command *commands;
int current_target;
command_output_handler_t output_handler;
void *output_handler_priv;
};
struct command;
/**
* When run_command is called, a new instance will be created on the
* stack, filled with the proper values, and passed by reference to the
* required COMMAND_HANDLER routine.
*/
struct command_invocation {
struct command_context *ctx;
struct command *current;
const char *name;
unsigned argc;
const char **argv;
};
/**
* Command handlers may be defined with more parameters than the base
* set provided by command.c. This macro uses C99 magic to allow
* defining all such derivative types using this macro.
*/
#define __COMMAND_HANDLER(name, extra ...) \
int name(struct command_invocation *cmd, ## extra)
/**
* Use this to macro to call a command helper (or a nested handler).
* It provides command handler authors protection against reordering or
* removal of unused parameters.
*
* @b Note: This macro uses lexical capture to provide some arguments.
* As a result, this macro should be used @b only within functions
* defined by the COMMAND_HANDLER or COMMAND_HELPER macros. Those
* macros provide the expected lexical context captured by this macro.
* Furthermore, it should be used only from the top-level of handler or
* helper function, or care must be taken to avoid redefining the same
* variables in intervening scope(s) by accident.
*/
#define CALL_COMMAND_HANDLER(name, extra ...) \
name(cmd, ## extra)
/**
* Always use this macro to define new command handler functions.
* It ensures the parameters are ordered, typed, and named properly, so
* they be can be used by other macros (e.g. COMMAND_PARSE_NUMBER).
* All command handler functions must be defined as static in scope.
*/
#define COMMAND_HANDLER(name) \
static __COMMAND_HANDLER(name)
/**
* Similar to COMMAND_HANDLER, except some parameters are expected.
* A helper is globally-scoped because it may be shared between several
* source files (e.g. the s3c24xx device command helper).
*/
#define COMMAND_HELPER(name, extra ...) __COMMAND_HANDLER(name, extra)
/**
* Use this macro to access the context of the command being handled,
* rather than accessing the variable directly. It may be moved.
*/
#define CMD_CTX (cmd->ctx)
/**
* Use this macro to access the number of arguments for the command being
* handled, rather than accessing the variable directly. It may be moved.
*/
#define CMD_ARGC (cmd->argc)
/**
* Use this macro to access the arguments for the command being handled,
* rather than accessing the variable directly. It may be moved.
*/
#define CMD_ARGV (cmd->argv)
/**
* Use this macro to access the name of the command being handled,
* rather than accessing the variable directly. It may be moved.
*/
#define CMD_NAME (cmd->name)
/**
* Use this macro to access the current command being handled,
* rather than accessing the variable directly. It may be moved.
*/
#define CMD_CURRENT (cmd->current)
/**
* Use this macro to access the invoked command handler's data pointer,
* rather than accessing the variable directly. It may be moved.
*/
#define CMD_DATA (CMD_CURRENT->jim_handler_data)
/**
* The type signature for command handling functions. They are
* usually registered as part of command_registration, providing
* a high-level means for executing a command.
*
* If the command fails, it *MUST* return a value != ERROR_OK
* (many commands break this rule, patches welcome!)
*
* This is *especially* important for commands such as writing
* to flash or verifying memory. The reason is that those commands
* can be used by programs to determine if the operation succeded
* or not. If the operation failed, then a program can try
* an alternative approach.
*
* Returning ERROR_COMMAND_SYNTAX_ERROR will have the effect of
* printing out the syntax of the command.
*/
typedef __COMMAND_HANDLER((*command_handler_t));
struct command {
const char *name;
const char *help;
const char *usage;
struct command *parent;
struct command *children;
command_handler_t handler;
Jim_CmdProc jim_handler;
void *jim_handler_data;
enum command_mode mode;
struct command *next;
};
/**
* @param c The command to be named.
* @param delim The character to place between command names.
* @returns A malloc'd string containing the full command name,
* which may include one or more ancestor components. Multiple names
* are separated by single spaces. The caller must free() the string
* when done with it.
*/
char *command_name(struct command *c, char delim);
/*
* Commands should be registered by filling in one or more of these
* structures and passing them to register_command().
*
* A conventioal format should be used for help strings, to provide both
* usage and basic information:
* @code
* "@<options@> ... - some explanation text"
* @endcode
*
* @param name The name of the command to register, which must not have
* been registered previously in the intended context.
* @param handler The callback function that will be called. If NULL,
* then the command serves as a placeholder for its children or a script.
* @param mode The command mode(s) in which this command may be run.
* @param help The help text that will be displayed to the user.
*/
struct command_registration {
const char *name;
command_handler_t handler;
Jim_CmdProc jim_handler;
void *jim_handler_data;
enum command_mode mode;
const char *help;
/** a string listing the options and arguments, required or optional */
const char *usage;
/**
* If non-NULL, the commands in @c chain will be registered in
* the same context and scope of this registration record.
* This allows modules to inherit lists commands from other
* modules.
*/
const struct command_registration *chain;
};
/** Use this as the last entry in an array of command_registration records. */
#define COMMAND_REGISTRATION_DONE { .name = NULL, .chain = NULL }
/**
* Register a command @c handler that can be called from scripts during
* the execution @c mode specified.
*
* If @c parent is non-NULL, the new command will be registered as a
* sub-command under it; otherwise, it will be available as a top-level
* command.
*
* @param cmd_ctx The command_context in which to register the command.
* @param parent Register this command as a child of this, or NULL to
* register a top-level command.
* @param rec A command_registration record that contains the desired
* command parameters.
* @returns The new command, if successful; otherwise, NULL.
*/
struct command *register_command(struct command_context *cmd_ctx,
struct command *parent, const struct command_registration *rec);
/**
* Register one or more commands in the specified context, as children
* of @c parent (or top-level commends, if NULL). In a registration's
* record contains a non-NULL @c chain member and name is NULL, the
* commands on the chain will be registered in the same context.
* Otherwise, the chained commands are added as children of the command.
*
* @param cmd_ctx The command_context in which to register the command.
* @param parent Register this command as a child of this, or NULL to
* register a top-level command.
* @param cmds Pointer to an array of command_registration records that
* contains the desired command parameters. The last record must have
* NULL for all fields.
* @returns ERROR_OK on success; ERROR_FAIL if any registration fails.
*/
int register_commands(struct command_context *cmd_ctx, struct command *parent,
const struct command_registration *cmds);
/**
* Unregisters command @c name from the given context, @c cmd_ctx.
* @param cmd_ctx The context of the registered command.
* @param parent The parent of the given command, or NULL.
* @param name The name of the command to unregister.
* @returns ERROR_OK on success, or an error code.
*/
int unregister_command(struct command_context *cmd_ctx,
struct command *parent, const char *name);
/**
* Unregisters all commands from the specfied context.
* @param cmd_ctx The context that will be cleared of registered commands.
* @param parent If given, only clear commands from under this one command.
* @returns ERROR_OK on success, or an error code.
*/
int unregister_all_commands(struct command_context *cmd_ctx,
struct command *parent);
struct command *command_find_in_context(struct command_context *cmd_ctx,
const char *name);
struct command *command_find_in_parent(struct command *parent,
const char *name);
/**
* Update the private command data field for a command and all descendents.
* This is used when creating a new heirarchy of commands that depends
* on obtaining a dynamically created context. The value will be available
* in command handlers by using the CMD_DATA macro.
* @param c The command (group) whose data pointer(s) will be updated.
* @param p The new data pointer to use for the command or its descendents.
*/
void command_set_handler_data(struct command *c, void *p);
void command_set_output_handler(struct command_context *context,
command_output_handler_t output_handler, void *priv);
int command_context_mode(struct command_context *context, enum command_mode mode);
/* Return the current command context associated with the Jim interpreter or
* alternatively the global default command interpreter
*/
struct command_context *current_command_context(Jim_Interp *interp);
/**
* Creates a new command context using the startup TCL provided and
* the existing Jim interpreter, if any. If interp == NULL, then command_init
* creates a command interpreter.
*/
struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp);
/**
* Creates a copy of an existing command context. This does not create
* a deep copy of the command list, so modifications in one context will
* affect all shared contexts. The caller must track reference counting
* and ensure the commands are freed before destroying the last instance.
* @param cmd_ctx The command_context that will be copied.
* @returns A new command_context with the same state as the original.
*/
struct command_context *copy_command_context(struct command_context *cmd_ctx);
/**
* Frees the resources associated with a command context. The commands
* are not removed, so unregister_all_commands() must be called first.
* @param context The command_context that will be destroyed.
*/
void command_done(struct command_context *context);
void command_print(struct command_context *context, const char *format, ...)
__attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 2, 3)));
void command_print_sameline(struct command_context *context, const char *format, ...)
__attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 2, 3)));
int command_run_line(struct command_context *context, char *line);
int command_run_linef(struct command_context *context, const char *format, ...)
__attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 2, 3)));
void command_output_text(struct command_context *context, const char *data);
void process_jim_events(struct command_context *cmd_ctx);
#define ERROR_COMMAND_CLOSE_CONNECTION (-600)
#define ERROR_COMMAND_SYNTAX_ERROR (-601)
#define ERROR_COMMAND_NOTFOUND (-602)
#define ERROR_COMMAND_ARGUMENT_INVALID (-603)
#define ERROR_COMMAND_ARGUMENT_OVERFLOW (-604)
#define ERROR_COMMAND_ARGUMENT_UNDERFLOW (-605)
int parse_ulong(const char *str, unsigned long *ul);
int parse_ullong(const char *str, unsigned long long *ul);
int parse_long(const char *str, long *ul);
int parse_llong(const char *str, long long *ul);
#define DECLARE_PARSE_WRAPPER(name, type) \
int parse ## name(const char *str, type * ul)
DECLARE_PARSE_WRAPPER(_uint, unsigned);
DECLARE_PARSE_WRAPPER(_u32, uint32_t);
DECLARE_PARSE_WRAPPER(_u16, uint16_t);
DECLARE_PARSE_WRAPPER(_u8, uint8_t);
DECLARE_PARSE_WRAPPER(_int, int);
DECLARE_PARSE_WRAPPER(_s32, int32_t);
DECLARE_PARSE_WRAPPER(_s16, int16_t);
DECLARE_PARSE_WRAPPER(_s8, int8_t);
/**
* @brief parses the string @a in into @a out as a @a type, or prints
* a command error and passes the error code to the caller. If an error
* does occur, the calling function will return the error code produced
* by the parsing function (one of ERROR_COMMAND_ARGUMENT_*).
*
* This function may cause the calling function to return immediately,
* so it should be used carefully to avoid leaking resources. In most
* situations, parsing should be completed in full before proceding
* to allocate resources, and this strategy will most prevents leaks.
*/
#define COMMAND_PARSE_NUMBER(type, in, out) \
do { \
int retval_macro_tmp = parse_ ## type(in, &(out)); \
if (ERROR_OK != retval_macro_tmp) { \
command_print(CMD_CTX, stringify(out) \
" option value ('%s') is not valid", in); \
return retval_macro_tmp; \
} \
} while (0)
/**
* Parse the string @c as a binary parameter, storing the boolean value
* in @c out. The strings @c on and @c off are used to match different
* strings for true and false options (e.g. "on" and "off" or
* "enable" and "disable").
*/
#define COMMAND_PARSE_BOOL(in, out, on, off) \
do { \
bool value; \
int retval_macro_tmp = command_parse_bool_arg(in, &value); \
if (ERROR_OK != retval_macro_tmp) { \
command_print(CMD_CTX, stringify(out) \
" option value ('%s') is not valid", in); \
command_print(CMD_CTX, " choices are '%s' or '%s'", \
on, off); \
return retval_macro_tmp; \
} \
out = value; \
} while (0)
int command_parse_bool_arg(const char *in, bool *out);
COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label);
/** parses an on/off command argument */
#define COMMAND_PARSE_ON_OFF(in, out) \
COMMAND_PARSE_BOOL(in, out, "on", "off")
/** parses an enable/disable command argument */
#define COMMAND_PARSE_ENABLE(in, out) \
COMMAND_PARSE_BOOL(in, out, "enable", "disable")
void script_debug(Jim_Interp *interp, const char *cmd,
unsigned argc, Jim_Obj * const *argv);
#endif /* COMMAND_H */
@@ -0,0 +1,174 @@
/***************************************************************************
* Copyright (C) 2004, 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "configuration.h"
#include "log.h"
static size_t num_config_files;
static char **config_file_names;
static size_t num_script_dirs;
static char **script_search_dirs;
void add_script_search_dir(const char *dir)
{
num_script_dirs++;
script_search_dirs =
(char **)realloc(script_search_dirs, (num_script_dirs + 1) * sizeof(char *));
script_search_dirs[num_script_dirs-1] = strdup(dir);
script_search_dirs[num_script_dirs] = NULL;
LOG_DEBUG("adding %s", dir);
}
void add_config_command(const char *cfg)
{
num_config_files++;
config_file_names =
(char **)realloc(config_file_names, (num_config_files + 1) * sizeof(char *));
config_file_names[num_config_files-1] = strdup(cfg);
config_file_names[num_config_files] = NULL;
}
/* return full path or NULL according to search rules */
char *find_file(const char *file)
{
FILE *fp = NULL;
char **search_dirs = script_search_dirs;
char *dir;
char const *mode = "r";
char *full_path;
/* Check absolute and relative to current working dir first.
* This keeps full_path reporting belowing working. */
full_path = alloc_printf("%s", file);
fp = fopen(full_path, mode);
while (!fp) {
free(full_path);
full_path = NULL;
dir = *search_dirs++;
if (!dir)
break;
full_path = alloc_printf("%s/%s", dir, file);
fp = fopen(full_path, mode);
}
if (fp) {
fclose(fp);
LOG_DEBUG("found %s", full_path);
return full_path;
}
free(full_path);
return NULL;
}
FILE *open_file_from_path(const char *file, const char *mode)
{
if (mode[0] != 'r')
return fopen(file, mode);
else {
char *full_path = find_file(file);
if (full_path == NULL)
return NULL;
FILE *fp = NULL;
fp = fopen(full_path, mode);
free(full_path);
return fp;
}
}
int parse_config_file(struct command_context *cmd_ctx)
{
int retval;
char **cfg;
if (!config_file_names) {
command_run_line(cmd_ctx, "script openocd.cfg");
return ERROR_OK;
}
cfg = config_file_names;
while (*cfg) {
retval = command_run_line(cmd_ctx, *cfg);
if (retval != ERROR_OK)
return retval;
cfg++;
}
return ERROR_OK;
}
#ifndef _WIN32
#include <pwd.h>
#endif
char *get_home_dir(const char *append_path)
{
char *home = getenv("HOME");
if (home == NULL) {
#ifdef _WIN32
home = getenv("USERPROFILE");
if (home == NULL) {
char homepath[MAX_PATH];
char *drive = getenv("HOMEDRIVE");
char *path = getenv("HOMEPATH");
if (drive && path) {
snprintf(homepath, MAX_PATH, "%s/%s", drive, path);
home = homepath;
}
}
#else
struct passwd *pwd = getpwuid(getuid());
if (pwd)
home = pwd->pw_dir;
#endif
}
if (home == NULL)
return home;
char *home_path;
if (append_path)
home_path = alloc_printf("%s/%s", home, append_path);
else
home_path = alloc_printf("%s", home);
return home_path;
}
@@ -0,0 +1,45 @@
/***************************************************************************
* Copyright (C) 2004, 2005 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include <helper/command.h>
int parse_cmdline_args(struct command_context *cmd_ctx,
int argc, char *argv[]);
int parse_config_file(struct command_context *cmd_ctx);
void add_config_command(const char *cfg);
void add_script_search_dir(const char *dir);
int configuration_output_handler(struct command_context *cmd_ctx,
const char *line);
FILE *open_file_from_path(const char *file, const char *mode);
char *find_file(const char *name);
char *get_home_dir(const char *append_path);
#endif /* CONFIGURATION_H */
+253
View File
@@ -0,0 +1,253 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "log.h"
#include "configuration.h"
#include "fileio.h"
struct fileio_internal {
const char *url;
ssize_t size;
enum fileio_type type;
enum fileio_access access;
FILE *file;
};
static inline int fileio_close_local(struct fileio_internal *fileio);
static inline int fileio_open_local(struct fileio_internal *fileio)
{
char file_access[4];
switch (fileio->access) {
case FILEIO_READ:
strcpy(file_access, "r");
break;
case FILEIO_WRITE:
strcpy(file_access, "w");
break;
case FILEIO_READWRITE:
strcpy(file_access, "w+");
break;
case FILEIO_APPEND:
strcpy(file_access, "a");
break;
case FILEIO_APPENDREAD:
strcpy(file_access, "a+");
break;
default:
LOG_ERROR("BUG: access neither read, write nor readwrite");
return ERROR_COMMAND_SYNTAX_ERROR;
}
/* win32 always opens in binary mode */
#ifndef _WIN32
if (fileio->type == FILEIO_BINARY)
#endif
strcat(file_access, "b");
fileio->file = open_file_from_path(fileio->url, file_access);
if (!fileio->file) {
LOG_ERROR("couldn't open %s", fileio->url);
return ERROR_FILEIO_OPERATION_FAILED;
}
if ((fileio->access != FILEIO_WRITE) || (fileio->access == FILEIO_READWRITE)) {
/* NB! Here we use fseek() instead of stat(), since stat is a
* more advanced operation that might not apply to e.g. a disk path
* that refers to e.g. a tftp client */
int result, result2;
result = fseek(fileio->file, 0, SEEK_END);
fileio->size = ftell(fileio->file);
result2 = fseek(fileio->file, 0, SEEK_SET);
if ((fileio->size < 0) || (result < 0) || (result2 < 0)) {
fileio_close_local(fileio);
return ERROR_FILEIO_OPERATION_FAILED;
}
} else
fileio->size = 0x0;
return ERROR_OK;
}
int fileio_open(struct fileio *fileio_p,
const char *url,
enum fileio_access access_type,
enum fileio_type type)
{
int retval = ERROR_OK;
struct fileio_internal *fileio = malloc(sizeof(struct fileio_internal));
fileio_p->fp = fileio;
fileio->type = type;
fileio->access = access_type;
fileio->url = strdup(url);
retval = fileio_open_local(fileio);
return retval;
}
static inline int fileio_close_local(struct fileio_internal *fileio)
{
int retval = fclose(fileio->file);
if (retval != 0) {
if (retval == EBADF)
LOG_ERROR("BUG: fileio_local->file not a valid file descriptor");
else
LOG_ERROR("couldn't close %s: %s", fileio->url, strerror(errno));
return ERROR_FILEIO_OPERATION_FAILED;
}
return ERROR_OK;
}
int fileio_close(struct fileio *fileio_p)
{
int retval;
struct fileio_internal *fileio = fileio_p->fp;
retval = fileio_close_local(fileio);
free((void *)fileio->url);
fileio->url = NULL;
free(fileio);
fileio_p->fp = NULL;
return retval;
}
int fileio_seek(struct fileio *fileio_p, size_t position)
{
int retval;
struct fileio_internal *fileio = fileio_p->fp;
retval = fseek(fileio->file, position, SEEK_SET);
if (retval != 0) {
LOG_ERROR("couldn't seek file %s: %s", fileio->url, strerror(errno));
return ERROR_FILEIO_OPERATION_FAILED;
}
return ERROR_OK;
}
static int fileio_local_read(struct fileio_internal *fileio,
size_t size, void *buffer, size_t *size_read)
{
ssize_t retval = fread(buffer, 1, size, fileio->file);
*size_read = (retval >= 0) ? retval : 0;
return (retval < 0) ? retval : ERROR_OK;
}
int fileio_read(struct fileio *fileio_p, size_t size, void *buffer,
size_t *size_read)
{
struct fileio_internal *fileio = fileio_p->fp;
return fileio_local_read(fileio, size, buffer, size_read);
}
int fileio_read_u32(struct fileio *fileio_p, uint32_t *data)
{
uint8_t buf[4];
size_t size_read;
struct fileio_internal *fileio = fileio_p->fp;
int retval = fileio_local_read(fileio, sizeof(uint32_t), buf, &size_read);
if (ERROR_OK == retval && sizeof(uint32_t) != size_read)
retval = -EIO;
if (ERROR_OK == retval)
*data = be_to_h_u32(buf);
return retval;
}
static int fileio_local_fgets(struct fileio_internal *fileio,
size_t size, void *buffer)
{
if (fgets(buffer, size, fileio->file) == NULL)
return ERROR_FILEIO_OPERATION_FAILED;
return ERROR_OK;
}
int fileio_fgets(struct fileio *fileio_p, size_t size, void *buffer)
{
struct fileio_internal *fileio = fileio_p->fp;
return fileio_local_fgets(fileio, size, buffer);
}
static int fileio_local_write(struct fileio_internal *fileio,
size_t size, const void *buffer, size_t *size_written)
{
ssize_t retval = fwrite(buffer, 1, size, fileio->file);
*size_written = (retval >= 0) ? retval : 0;
return (retval < 0) ? retval : ERROR_OK;
}
int fileio_write(struct fileio *fileio_p,
size_t size, const void *buffer, size_t *size_written)
{
struct fileio_internal *fileio = fileio_p->fp;
int retval = fileio_local_write(fileio, size, buffer, size_written);
if (retval == ERROR_OK)
fileio->size += *size_written;
return retval;
}
int fileio_write_u32(struct fileio *fileio_p, uint32_t data)
{
uint8_t buf[4];
h_u32_to_be(buf, data);
size_t size_written;
int retval = fileio_write(fileio_p, 4, buf, &size_written);
if (ERROR_OK == retval && size_written != sizeof(uint32_t))
retval = -EIO;
return retval;
}
/**
* FIX!!!!
*
* For now this can not fail, but that's because a seek was executed
* on startup.
*
* Avoiding the seek on startup opens up for using streams.
*
*/
int fileio_size(struct fileio *fileio_p, int *size)
{
struct fileio_internal *fileio = fileio_p->fp;
*size = fileio->size;
return ERROR_OK;
}
+74
View File
@@ -0,0 +1,74 @@
/***************************************************************************
* Copyright (C) 2007 by Dominic Rath *
* Dominic.Rath@gmx.de *
* *
* Copyright (C) 2007,2008 Øyvind Harboe *
* oyvind.harboe@zylin.com *
* *
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef FILEIO_H
#define FILEIO_H
#define FILEIO_MAX_ERROR_STRING (128)
enum fileio_type {
FILEIO_TEXT,
FILEIO_BINARY,
};
enum fileio_access {
FILEIO_NONE, /* open without any access (invalid mode) */
FILEIO_READ, /* open for reading, position at beginning */
FILEIO_WRITE, /* open for writing, position at beginning */
FILEIO_READWRITE, /* open for writing, position at beginning, allow reading */
FILEIO_APPEND, /* open for writing, position at end */
FILEIO_APPENDREAD, /* open for writing, position at end, allow reading */
};
struct fileio {
/* The structure is opaque */
struct fileio_internal *fp;
};
int fileio_open(struct fileio *fileio,
const char *url, enum fileio_access access_type, enum fileio_type type);
int fileio_close(struct fileio *fileio);
int fileio_seek(struct fileio *fileio, size_t position);
int fileio_fgets(struct fileio *fileio, size_t size, void *buffer);
int fileio_read(struct fileio *fileio,
size_t size, void *buffer, size_t *size_read);
int fileio_write(struct fileio *fileio,
size_t size, const void *buffer, size_t *size_written);
int fileio_read_u32(struct fileio *fileio, uint32_t *data);
int fileio_write_u32(struct fileio *fileio, uint32_t data);
int fileio_size(struct fileio *fileio, int *size);
#define ERROR_FILEIO_LOCATION_UNKNOWN (-1200)
#define ERROR_FILEIO_NOT_FOUND (-1201)
#define ERROR_FILEIO_OPERATION_FAILED (-1202)
#define ERROR_FILEIO_ACCESS_NOT_SUPPORTED (-1203)
#define ERROR_FILEIO_RESOURCE_TYPE_UNKNOWN (-1204)
#define ERROR_FILEIO_OPERATION_NOT_SUPPORTED (-1205)
#endif /* FILEIO_H */

Some files were not shown because too many files have changed in this diff Show More