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:
23
debuggers/openocd/src/flash/Makefile.am
Normal file
23
debuggers/openocd/src/flash/Makefile.am
Normal 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
debuggers/openocd/src/flash/Makefile.in
Normal file
697
debuggers/openocd/src/flash/Makefile.in
Normal 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
debuggers/openocd/src/flash/common.c
Normal file
49
debuggers/openocd/src/flash/common.c
Normal 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
debuggers/openocd/src/flash/common.h
Normal file
50
debuggers/openocd/src/flash/common.h
Normal 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 */
|
||||
1451
debuggers/openocd/src/flash/mflash.c
Normal file
1451
debuggers/openocd/src/flash/mflash.c
Normal file
File diff suppressed because it is too large
Load Diff
291
debuggers/openocd/src/flash/mflash.h
Normal file
291
debuggers/openocd/src/flash/mflash.h
Normal 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
|
||||
46
debuggers/openocd/src/flash/nand/Makefile.am
Normal file
46
debuggers/openocd/src/flash/nand/Makefile.am
Normal file
@ -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
|
||||
625
debuggers/openocd/src/flash/nand/Makefile.in
Normal file
625
debuggers/openocd/src/flash/nand/Makefile.in
Normal file
@ -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
debuggers/openocd/src/flash/nand/arm_io.c
Normal file
303
debuggers/openocd/src/flash/nand/arm_io.c
Normal 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(®_params[0], "r0", 32, PARAM_IN);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_IN);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_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(®_params[0], "r0", 32, PARAM_IN);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_IN);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
|
||||
/* read from work area to the host's memory */
|
||||
retval = target_read_buffer(target, target_buf, size, data);
|
||||
|
||||
return retval;
|
||||
}
|
||||
57
debuggers/openocd/src/flash/nand/arm_io.h
Normal file
57
debuggers/openocd/src/flash/nand/arm_io.h
Normal 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
debuggers/openocd/src/flash/nand/at91sam9.c
Normal file
724
debuggers/openocd/src/flash/nand/at91sam9.c
Normal 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
debuggers/openocd/src/flash/nand/core.c
Normal file
878
debuggers/openocd/src/flash/nand/core.c
Normal 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
debuggers/openocd/src/flash/nand/core.h
Normal file
234
debuggers/openocd/src/flash/nand/core.h
Normal 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
debuggers/openocd/src/flash/nand/davinci.c
Normal file
795
debuggers/openocd/src/flash/nand/davinci.c
Normal 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
debuggers/openocd/src/flash/nand/driver.c
Normal file
84
debuggers/openocd/src/flash/nand/driver.c
Normal 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
debuggers/openocd/src/flash/nand/driver.h
Normal file
105
debuggers/openocd/src/flash/nand/driver.h
Normal 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
debuggers/openocd/src/flash/nand/ecc.c
Normal file
183
debuggers/openocd/src/flash/nand/ecc.c
Normal 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
debuggers/openocd/src/flash/nand/ecc_kw.c
Normal file
171
debuggers/openocd/src/flash/nand/ecc_kw.c
Normal 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
debuggers/openocd/src/flash/nand/fileio.c
Normal file
228
debuggers/openocd/src/flash/nand/fileio.c
Normal 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
debuggers/openocd/src/flash/nand/fileio.h
Normal file
58
debuggers/openocd/src/flash/nand/fileio.h
Normal 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
debuggers/openocd/src/flash/nand/imp.h
Normal file
40
debuggers/openocd/src/flash/nand/imp.h
Normal 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 */
|
||||
1361
debuggers/openocd/src/flash/nand/lpc3180.c
Normal file
1361
debuggers/openocd/src/flash/nand/lpc3180.c
Normal file
File diff suppressed because it is too large
Load Diff
39
debuggers/openocd/src/flash/nand/lpc3180.h
Normal file
39
debuggers/openocd/src/flash/nand/lpc3180.h
Normal file
@ -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 */
|
||||
1823
debuggers/openocd/src/flash/nand/lpc32xx.c
Normal file
1823
debuggers/openocd/src/flash/nand/lpc32xx.c
Normal file
File diff suppressed because it is too large
Load Diff
38
debuggers/openocd/src/flash/nand/lpc32xx.h
Normal file
38
debuggers/openocd/src/flash/nand/lpc32xx.h
Normal 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 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
debuggers/openocd/src/flash/nand/mx3.c
Normal file
730
debuggers/openocd/src/flash/nand/mx3.c
Normal 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, ®ister_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
debuggers/openocd/src/flash/nand/mx3.h
Normal file
110
debuggers/openocd/src/flash/nand/mx3.h
Normal 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
debuggers/openocd/src/flash/nand/mxc.c
Normal file
971
debuggers/openocd/src/flash/nand/mxc.c
Normal 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
debuggers/openocd/src/flash/nand/mxc.h
Normal file
167
debuggers/openocd/src/flash/nand/mxc.h
Normal 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
debuggers/openocd/src/flash/nand/nonce.c
Normal file
75
debuggers/openocd/src/flash/nand/nonce.c
Normal 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
debuggers/openocd/src/flash/nand/nuc910.c
Normal file
229
debuggers/openocd/src/flash/nand/nuc910.c
Normal 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
debuggers/openocd/src/flash/nand/nuc910.h
Normal file
60
debuggers/openocd/src/flash/nand/nuc910.h
Normal 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
debuggers/openocd/src/flash/nand/orion.c
Normal file
163
debuggers/openocd/src/flash/nand/orion.c
Normal 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
debuggers/openocd/src/flash/nand/s3c2410.c
Normal file
118
debuggers/openocd/src/flash/nand/s3c2410.c
Normal 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,
|
||||
};
|
||||
77
debuggers/openocd/src/flash/nand/s3c2412.c
Normal file
77
debuggers/openocd/src/flash/nand/s3c2412.c
Normal file
@ -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
debuggers/openocd/src/flash/nand/s3c2440.c
Normal file
168
debuggers/openocd/src/flash/nand/s3c2440.c
Normal 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,
|
||||
};
|
||||
77
debuggers/openocd/src/flash/nand/s3c2443.c
Normal file
77
debuggers/openocd/src/flash/nand/s3c2443.c
Normal file
@ -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
debuggers/openocd/src/flash/nand/s3c24xx.c
Normal file
119
debuggers/openocd/src/flash/nand/s3c24xx.c
Normal 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;
|
||||
}
|
||||
80
debuggers/openocd/src/flash/nand/s3c24xx.h
Normal file
80
debuggers/openocd/src/flash/nand/s3c24xx.h
Normal file
@ -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 */
|
||||
132
debuggers/openocd/src/flash/nand/s3c24xx_regs.h
Normal file
132
debuggers/openocd/src/flash/nand/s3c24xx_regs.h
Normal file
@ -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 */
|
||||
|
||||
74
debuggers/openocd/src/flash/nand/s3c6400.c
Normal file
74
debuggers/openocd/src/flash/nand/s3c6400.c
Normal file
@ -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
debuggers/openocd/src/flash/nand/tcl.c
Normal file
628
debuggers/openocd/src/flash/nand/tcl.c
Normal 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);
|
||||
}
|
||||
51
debuggers/openocd/src/flash/nor/Makefile.am
Normal file
51
debuggers/openocd/src/flash/nor/Makefile.am
Normal file
@ -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
debuggers/openocd/src/flash/nor/Makefile.in
Normal file
645
debuggers/openocd/src/flash/nor/Makefile.in
Normal 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
debuggers/openocd/src/flash/nor/aduc702x.c
Normal file
411
debuggers/openocd/src/flash/nor/aduc702x.c
Normal 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(®_params[0], "r0", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[3], "r3", 32, PARAM_IN);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_params[3]);
|
||||
destroy_reg_param(®_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
|
||||
};
|
||||
3559
debuggers/openocd/src/flash/nor/at91sam3.c
Normal file
3559
debuggers/openocd/src/flash/nor/at91sam3.c
Normal file
File diff suppressed because it is too large
Load Diff
2380
debuggers/openocd/src/flash/nor/at91sam4.c
Normal file
2380
debuggers/openocd/src/flash/nor/at91sam4.c
Normal file
File diff suppressed because it is too large
Load Diff
1211
debuggers/openocd/src/flash/nor/at91sam7.c
Normal file
1211
debuggers/openocd/src/flash/nor/at91sam7.c
Normal file
File diff suppressed because it is too large
Load Diff
485
debuggers/openocd/src/flash/nor/avrf.c
Normal file
485
debuggers/openocd/src/flash/nor/avrf.c
Normal 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,
|
||||
};
|
||||
3052
debuggers/openocd/src/flash/nor/cfi.c
Normal file
3052
debuggers/openocd/src/flash/nor/cfi.c
Normal file
File diff suppressed because it is too large
Load Diff
162
debuggers/openocd/src/flash/nor/cfi.h
Normal file
162
debuggers/openocd/src/flash/nor/cfi.h
Normal 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
debuggers/openocd/src/flash/nor/core.c
Normal file
763
debuggers/openocd/src/flash/nor/core.c
Normal 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
debuggers/openocd/src/flash/nor/core.h
Normal file
207
debuggers/openocd/src/flash/nor/core.h
Normal 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
debuggers/openocd/src/flash/nor/driver.h
Normal file
226
debuggers/openocd/src/flash/nor/driver.h
Normal 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
debuggers/openocd/src/flash/nor/drivers.c
Normal file
97
debuggers/openocd/src/flash/nor/drivers.c
Normal 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;
|
||||
}
|
||||
281
debuggers/openocd/src/flash/nor/dsp5680xx_flash.c
Normal file
281
debuggers/openocd/src/flash/nor/dsp5680xx_flash.c
Normal file
@ -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
debuggers/openocd/src/flash/nor/efm32.c
Normal file
985
debuggers/openocd/src/flash/nor/efm32.c
Normal 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, ®_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(®_params[0], "r0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* count (word-32bit) */
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* buffer start */
|
||||
init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* buffer end */
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_params[3]);
|
||||
destroy_reg_param(®_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
debuggers/openocd/src/flash/nor/em357.c
Normal file
950
debuggers/openocd/src/flash/nor/em357.c
Normal 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(®_params[0], "r0", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_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
debuggers/openocd/src/flash/nor/faux.c
Normal file
142
debuggers/openocd/src/flash/nor/faux.c
Normal 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
debuggers/openocd/src/flash/nor/fm3.c
Normal file
909
debuggers/openocd/src/flash/nor/fm3.c
Normal 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(®_params[0], "r0", 32, PARAM_OUT); /* source start address */
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* target start address */
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* number of halfwords to program */
|
||||
init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* Flash Sequence address 1 */
|
||||
init_reg_param(®_params[4], "r4", 32, PARAM_OUT); /* Flash Sequence address 1 */
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_params[3]);
|
||||
destroy_reg_param(®_params[4]);
|
||||
destroy_reg_param(®_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
debuggers/openocd/src/flash/nor/imp.h
Normal file
51
debuggers/openocd/src/flash/nor/imp.h
Normal 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
debuggers/openocd/src/flash/nor/kinetis.c
Normal file
825
debuggers/openocd/src/flash/nor/kinetis.c
Normal 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
debuggers/openocd/src/flash/nor/lpc2000.c
Normal file
922
debuggers/openocd/src/flash/nor/lpc2000.c
Normal 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(®_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(®_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(®_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(®_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(®_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(®_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(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_params[3]);
|
||||
destroy_reg_param(®_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
debuggers/openocd/src/flash/nor/lpc288x.c
Normal file
454
debuggers/openocd/src/flash/nor/lpc288x.c
Normal 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,
|
||||
};
|
||||
1617
debuggers/openocd/src/flash/nor/lpc2900.c
Normal file
1617
debuggers/openocd/src/flash/nor/lpc2900.c
Normal file
File diff suppressed because it is too large
Load Diff
968
debuggers/openocd/src/flash/nor/lpcspifi.c
Normal file
968
debuggers/openocd/src/flash/nor/lpcspifi.c
Normal 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(®_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(®_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(®_params[0], "r0", 32, PARAM_IN_OUT); /* Start address */
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* Sector count */
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* Erase command */
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_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(®_params[0], "r0", 32, PARAM_IN_OUT); /* buffer start, status (out) */
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* buffer end */
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* target address */
|
||||
init_reg_param(®_params[3], "r3", 32, PARAM_OUT); /* count (halfword-16bit) */
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_params[3]);
|
||||
destroy_reg_param(®_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
debuggers/openocd/src/flash/nor/non_cfi.c
Normal file
563
debuggers/openocd/src/flash/nor/non_cfi.c
Normal 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
debuggers/openocd/src/flash/nor/non_cfi.h
Normal file
38
debuggers/openocd/src/flash/nor/non_cfi.h
Normal 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
debuggers/openocd/src/flash/nor/ocl.c
Normal file
351
debuggers/openocd/src/flash/nor/ocl.c
Normal 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
debuggers/openocd/src/flash/nor/ocl.h
Normal file
41
debuggers/openocd/src/flash/nor/ocl.h
Normal 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
debuggers/openocd/src/flash/nor/pic32mx.c
Normal file
934
debuggers/openocd/src/flash/nor/pic32mx.c
Normal 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(®_params[0], "a0", 32, PARAM_IN_OUT);
|
||||
init_reg_param(®_params[1], "a1", 32, PARAM_OUT);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_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
debuggers/openocd/src/flash/nor/spi.c
Normal file
71
debuggers/openocd/src/flash/nor/spi.c
Normal 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
debuggers/openocd/src/flash/nor/spi.h
Normal file
58
debuggers/openocd/src/flash/nor/spi.h
Normal 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 */
|
||||
1405
debuggers/openocd/src/flash/nor/stellaris.c
Normal file
1405
debuggers/openocd/src/flash/nor/stellaris.c
Normal file
File diff suppressed because it is too large
Load Diff
1592
debuggers/openocd/src/flash/nor/stm32f1x.c
Normal file
1592
debuggers/openocd/src/flash/nor/stm32f1x.c
Normal file
File diff suppressed because it is too large
Load Diff
1128
debuggers/openocd/src/flash/nor/stm32f2x.c
Normal file
1128
debuggers/openocd/src/flash/nor/stm32f2x.c
Normal file
File diff suppressed because it is too large
Load Diff
999
debuggers/openocd/src/flash/nor/stm32lx.c
Normal file
999
debuggers/openocd/src/flash/nor/stm32lx.c
Normal 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(®_params[0], "r0", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_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, ®32);
|
||||
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, ®32);
|
||||
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, ®32);
|
||||
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, ®32);
|
||||
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, ®32);
|
||||
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, ®32);
|
||||
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, ®32);
|
||||
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
debuggers/openocd/src/flash/nor/stmsmi.c
Normal file
659
debuggers/openocd/src/flash/nor/stmsmi.c
Normal 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
debuggers/openocd/src/flash/nor/str7x.c
Normal file
816
debuggers/openocd/src/flash/nor/str7x.c
Normal 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(®_params[0], "r0", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[3], "r3", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[4], "r4", 32, PARAM_IN);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_params[3]);
|
||||
destroy_reg_param(®_params[4]);
|
||||
destroy_reg_param(®_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), ®);
|
||||
|
||||
if (!(reg & str7x_info->disable_bit))
|
||||
ProtectionLevel = 1;
|
||||
|
||||
target_read_u32(target, str7x_get_flash_adr(bank, FLASH_NVAPR1), ®);
|
||||
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
debuggers/openocd/src/flash/nor/str9x.c
Normal file
690
debuggers/openocd/src/flash/nor/str9x.c
Normal 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(®_params[0], "r0", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_OUT);
|
||||
init_reg_param(®_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(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
destroy_reg_param(®_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,
|
||||
};
|
||||
1219
debuggers/openocd/src/flash/nor/str9xpec.c
Normal file
1219
debuggers/openocd/src/flash/nor/str9xpec.c
Normal file
File diff suppressed because it is too large
Load Diff
895
debuggers/openocd/src/flash/nor/tcl.c
Normal file
895
debuggers/openocd/src/flash/nor/tcl.c
Normal 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);
|
||||
}
|
||||
1191
debuggers/openocd/src/flash/nor/tms470.c
Normal file
1191
debuggers/openocd/src/flash/nor/tms470.c
Normal file
File diff suppressed because it is too large
Load Diff
234
debuggers/openocd/src/flash/nor/virtual.c
Normal file
234
debuggers/openocd/src/flash/nor/virtual.c
Normal 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
debuggers/openocd/src/flash/startup.tcl
Normal file
82
debuggers/openocd/src/flash/startup.tcl
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user