first commit
This commit is contained in:
commit
a96e96064a
50
.travis.yml
Normal file
50
.travis.yml
Normal file
@ -0,0 +1,50 @@
|
||||
# use container-based infrastructure
|
||||
sudo: false
|
||||
|
||||
language: c
|
||||
|
||||
# treat all warnings as errors, fake cross-toolchain (build everything on host)
|
||||
env:
|
||||
- CFLAGS="-g -O2 -Werror" CROSS_COMPILE=""
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
# OSX uses Apple's flavor of clang anyway, so there's no point in trying "gcc".
|
||||
# This excludes the "gcc" compiler from the build matrix for OSX:
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
# take care of the libusb dependency for Linux
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libusb-1.0-0-dev
|
||||
|
||||
# take care of the libusb dependency for Mac OS X; select make/install target
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
brew update;
|
||||
brew install libusb;
|
||||
export TARGET=tools;
|
||||
else
|
||||
export TARGET=all;
|
||||
fi
|
||||
|
||||
# build (and test) using the Makefile, with a single overall status
|
||||
script:
|
||||
- make ${TARGET} && make misc && make check
|
||||
|
||||
# run/simulate a test install
|
||||
after_success:
|
||||
- make install-${TARGET} install-misc DESTDIR=/tmp PREFIX=/sunxi-tools
|
||||
|
||||
# turn off email notifications
|
||||
notifications:
|
||||
- email: false
|
||||
361
LICENSE.md
Normal file
361
LICENSE.md
Normal file
@ -0,0 +1,361 @@
|
||||
### GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
### Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom
|
||||
to share and change it. By contrast, the GNU General Public License is
|
||||
intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on,
|
||||
we want its recipients to know that what they have is not the
|
||||
original, so that any problems introduced by others will not reflect
|
||||
on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at
|
||||
all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
**0.** This License applies to any program or other work which
|
||||
contains a notice placed by the copyright holder saying it may be
|
||||
distributed under the terms of this General Public License. The
|
||||
"Program", below, refers to any such program or work, and a "work
|
||||
based on the Program" means either the Program or any derivative work
|
||||
under copyright law: that is to say, a work containing the Program or
|
||||
a portion of it, either verbatim or with modifications and/or
|
||||
translated into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".) Each licensee
|
||||
is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the Program
|
||||
(independent of having been made by running the Program). Whether that
|
||||
is true depends on what the Program does.
|
||||
|
||||
**1.** You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
**2.** You may modify your copy or copies of the Program or any
|
||||
portion of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
|
||||
**a)** You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
|
||||
**b)** You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any part
|
||||
thereof, to be licensed as a whole at no charge to all third parties
|
||||
under the terms of this License.
|
||||
|
||||
|
||||
**c)** If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such interactive
|
||||
use in the most ordinary way, to print or display an announcement
|
||||
including an appropriate copyright notice and a notice that there is
|
||||
no warranty (or else, saying that you provide a warranty) and that
|
||||
users may redistribute the program under these conditions, and telling
|
||||
the user how to view a copy of this License. (Exception: if the
|
||||
Program itself is interactive but does not normally print such an
|
||||
announcement, your work based on the Program is not required to print
|
||||
an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
**3.** You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
|
||||
**a)** Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections 1
|
||||
and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
|
||||
**b)** Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your cost of
|
||||
physically performing source distribution, a complete machine-readable
|
||||
copy of the corresponding source code, to be distributed under the
|
||||
terms of Sections 1 and 2 above on a medium customarily used for
|
||||
software interchange; or,
|
||||
|
||||
|
||||
**c)** Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is allowed
|
||||
only for noncommercial distribution and only if you received the
|
||||
program in object code or executable form with such an offer, in
|
||||
accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
**4.** You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt otherwise
|
||||
to copy, modify, sublicense or distribute the Program is void, and
|
||||
will automatically terminate your rights under this License. However,
|
||||
parties who have received copies, or rights, from you under this
|
||||
License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
**5.** You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
**6.** Each time you redistribute the Program (or any work based on
|
||||
the Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
**7.** If, as a consequence of a court judgment or allegation of
|
||||
patent infringement or for any other reason (not limited to patent
|
||||
issues), conditions are imposed on you (whether by court order,
|
||||
agreement or otherwise) that contradict the conditions of this
|
||||
License, they do not excuse you from the conditions of this License.
|
||||
If you cannot distribute so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations,
|
||||
then as a consequence you may not distribute the Program at all. For
|
||||
example, if a patent license would not permit royalty-free
|
||||
redistribution of the Program by all those who receive copies directly
|
||||
or indirectly through you, then the only way you could satisfy both it
|
||||
and this License would be to refrain entirely from distribution of the
|
||||
Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
**8.** If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
**9.** The Free Software Foundation may publish revised and/or new
|
||||
versions of the General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Program does not specify a
|
||||
version number of this License, you may choose any version ever
|
||||
published by the Free Software Foundation.
|
||||
|
||||
**10.** If you wish to incorporate parts of the Program into other
|
||||
free programs whose distribution conditions are different, write to
|
||||
the author to ask for permission. For software which is copyrighted by
|
||||
the Free Software Foundation, write to the Free Software Foundation;
|
||||
we sometimes make exceptions for this. Our decision will be guided by
|
||||
the two goals of preserving the free status of all derivatives of our
|
||||
free software and of promoting the sharing and reuse of software
|
||||
generally.
|
||||
|
||||
**NO WARRANTY**
|
||||
|
||||
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
### END OF TERMS AND CONDITIONS
|
||||
|
||||
### How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these
|
||||
terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to
|
||||
attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the program's name and an idea of what it does.
|
||||
Copyright (C) yyyy name of author
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper
|
||||
mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
|
||||
type `show w'. This is free software, and you are welcome
|
||||
to redistribute it under certain conditions; type `show c'
|
||||
for details.
|
||||
|
||||
The hypothetical commands \`show w' and \`show c' should show the
|
||||
appropriate parts of the General Public License. Of course, the
|
||||
commands you use may be called something other than \`show w' and
|
||||
\`show c'; they could even be mouse-clicks or menu items--whatever
|
||||
suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or
|
||||
your school, if any, to sign a "copyright disclaimer" for the program,
|
||||
if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright
|
||||
interest in the program `Gnomovision'
|
||||
(which makes passes at compilers) written
|
||||
by James Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library,
|
||||
you may consider it more useful to permit linking proprietary
|
||||
applications with the library. If this is what you want to do, use the
|
||||
[GNU Lesser General Public
|
||||
License](http://www.gnu.org/licenses/lgpl.html) instead of this
|
||||
License.
|
||||
211
Makefile
Normal file
211
Makefile
Normal file
@ -0,0 +1,211 @@
|
||||
# Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
# Copyright (C) 2012,2013 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
# Copyright (C) 2013 Patrick Wood <patrickhwood@gmail.com>
|
||||
# Copyright (C) 2013 Pat Wood <Pat.Wood@efi.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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Windows predefines OS in the environment (to "Windows_NT"), otherwise use uname
|
||||
OS ?= $(shell uname)
|
||||
|
||||
CC ?= gcc
|
||||
DEFAULT_CFLAGS := -std=c99
|
||||
DEFAULT_CFLAGS += -Wall -Wextra -Wno-unused-result
|
||||
|
||||
DEFAULT_CFLAGS += -D_POSIX_C_SOURCE=200112L
|
||||
# Define _BSD_SOURCE, necessary to expose all endian conversions properly.
|
||||
# See http://linux.die.net/man/3/endian
|
||||
DEFAULT_CFLAGS += -D_BSD_SOURCE
|
||||
# glibc 2.20+ also requires _DEFAULT_SOURCE
|
||||
DEFAULT_CFLAGS += -D_DEFAULT_SOURCE
|
||||
ifeq ($(OS),NetBSD)
|
||||
# add explicit _NETBSD_SOURCE, see https://github.com/linux-sunxi/sunxi-tools/pull/22
|
||||
DEFAULT_CFLAGS += -D_NETBSD_SOURCE
|
||||
endif
|
||||
|
||||
DEFAULT_CFLAGS += -Iinclude/
|
||||
|
||||
PKG_CONFIG ?= pkg-config
|
||||
|
||||
# Tools useful on host and target
|
||||
TOOLS = sunxi-fexc sunxi-bootinfo sunxi-fel sunxi-nand-part sunxi-pio
|
||||
|
||||
# Symlinks to sunxi-fexc
|
||||
FEXC_LINKS = bin2fex fex2bin
|
||||
|
||||
# Tools which are only useful on the target
|
||||
TARGET_TOOLS = sunxi-meminfo
|
||||
|
||||
# Misc tools (of more "exotic" nature) not part of our default build / install
|
||||
MISC_TOOLS = phoenix_info sunxi-nand-image-builder
|
||||
|
||||
# ARM binaries and images
|
||||
# Note: To use this target, set/adjust CROSS_COMPILE and MKSUNXIBOOT if needed
|
||||
BINFILES = jtag-loop.sunxi fel-sdboot.sunxi uart0-helloworld-sdboot.sunxi
|
||||
|
||||
MKSUNXIBOOT ?= mksunxiboot
|
||||
PATH_DIRS := $(shell echo $$PATH | sed -e 's/:/ /g')
|
||||
# Try to guess a suitable default ARM cross toolchain
|
||||
CROSS_DEFAULT := arm-none-eabi-
|
||||
CROSS_COMPILE ?= $(or $(shell ./find-arm-gcc.sh),$(CROSS_DEFAULT))
|
||||
CROSS_CC := $(CROSS_COMPILE)gcc
|
||||
|
||||
DESTDIR ?=
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
MANDIR ?= $(PREFIX)/share/man/man1
|
||||
|
||||
.PHONY: all clean tools target-tools install install-tools install-target-tools
|
||||
.PHONY: check
|
||||
|
||||
tools: $(TOOLS) $(FEXC_LINKS)
|
||||
target-tools: $(TARGET_TOOLS)
|
||||
|
||||
all: tools target-tools
|
||||
|
||||
misc: $(MISC_TOOLS)
|
||||
|
||||
binfiles: $(BINFILES)
|
||||
|
||||
install: install-tools
|
||||
install-all: install-tools install-target-tools
|
||||
|
||||
install-tools: $(TOOLS)
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
@set -ex ; for t in $^ ; do \
|
||||
install -m0755 $$t $(DESTDIR)$(BINDIR)/$$t ; \
|
||||
done
|
||||
@set -ex ; for l in $(FEXC_LINKS) ; do \
|
||||
ln -nfs sunxi-fexc $(DESTDIR)$(BINDIR)/$$l ; \
|
||||
done
|
||||
install -D -m0644 -t $(DESTDIR)$(MANDIR) sunxi-fel.1
|
||||
|
||||
install-target-tools: $(TARGET_TOOLS)
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
@set -ex ; for t in $^ ; do \
|
||||
install -m0755 $$t $(DESTDIR)$(BINDIR)/$$t ; \
|
||||
done
|
||||
|
||||
install-misc: $(MISC_TOOLS)
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
@set -ex ; for t in $^ ; do \
|
||||
install -m0755 $$t $(DESTDIR)$(BINDIR)/$$t ; \
|
||||
done
|
||||
|
||||
|
||||
clean:
|
||||
make -C tests/ clean
|
||||
@rm -vf $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) $(MISC_TOOLS)
|
||||
@rm -vf version.h *.o *.elf *.sunxi *.bin *.nm *.orig
|
||||
|
||||
$(TOOLS) $(TARGET_TOOLS) $(MISC_TOOLS): Makefile common.h version.h
|
||||
|
||||
fex2bin bin2fex: sunxi-fexc
|
||||
ln -nsf $< $@
|
||||
|
||||
sunxi-fexc: fexc.h script.h script.c \
|
||||
script_uboot.h script_uboot.c \
|
||||
script_bin.h script_bin.c \
|
||||
script_fex.h script_fex.c
|
||||
|
||||
LIBUSB = libusb-1.0
|
||||
LIBUSB_CFLAGS ?= `$(PKG_CONFIG) --cflags $(LIBUSB)`
|
||||
LIBUSB_LIBS ?= `$(PKG_CONFIG) --libs $(LIBUSB)`
|
||||
|
||||
ZLIB = zlib
|
||||
ZLIB_CFLAGS ?= `$(PKG_CONFIG) --cflags $(ZLIB)`
|
||||
ZLIB_LIBS ?= `$(PKG_CONFIG) --libs $(ZLIB)`
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# Windows lacks mman.h / mmap()
|
||||
DEFAULT_CFLAGS += -DNO_MMAP
|
||||
# portable_endian.h relies on winsock2
|
||||
LIBS += -lws2_32
|
||||
endif
|
||||
|
||||
HOST_CFLAGS = $(DEFAULT_CFLAGS) $(CFLAGS)
|
||||
|
||||
PROGRESS := progress.c progress.h
|
||||
SOC_INFO := soc_info.c soc_info.h
|
||||
FEL_LIB := fel_lib.c fel_lib.h
|
||||
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
|
||||
|
||||
sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
|
||||
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \
|
||||
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) -lfdt
|
||||
|
||||
sunxi-nand-part: nand-part-main.c nand-part.c nand-part-a10.h nand-part-a20.h
|
||||
$(CC) $(HOST_CFLAGS) -c -o nand-part-main.o nand-part-main.c
|
||||
$(CC) $(HOST_CFLAGS) -c -o nand-part-a10.o nand-part.c -D A10
|
||||
$(CC) $(HOST_CFLAGS) -c -o nand-part-a20.o nand-part.c -D A20
|
||||
$(CC) $(LDFLAGS) -o $@ nand-part-main.o nand-part-a10.o nand-part-a20.o $(LIBS)
|
||||
|
||||
sunxi-%: %.c
|
||||
$(CC) $(HOST_CFLAGS) $(LDFLAGS) -o $@ $(filter %.c,$^) $(LIBS)
|
||||
phoenix_info: phoenix_info.c
|
||||
$(CC) $(HOST_CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
|
||||
|
||||
%.bin: %.elf
|
||||
$(CROSS_COMPILE)objcopy -O binary $< $@
|
||||
|
||||
%.sunxi: %.bin
|
||||
$(MKSUNXIBOOT) $< $@
|
||||
|
||||
ARM_ELF_FLAGS = -Os -marm -fpic -Wall
|
||||
ARM_ELF_FLAGS += -fno-common -fno-builtin -ffreestanding -nostdinc -fno-strict-aliasing
|
||||
ARM_ELF_FLAGS += -mno-thumb-interwork -fno-stack-protector -fno-toplevel-reorder
|
||||
ARM_ELF_FLAGS += -Wstrict-prototypes -Wno-format-nonliteral -Wno-format-security
|
||||
|
||||
jtag-loop.elf: jtag-loop.c jtag-loop.lds
|
||||
$(CROSS_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T jtag-loop.lds -Wl,-N
|
||||
|
||||
fel-sdboot.elf: fel-sdboot.S fel-sdboot.lds
|
||||
$(CROSS_CC) -march=armv5te -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T fel-sdboot.lds -Wl,-N
|
||||
|
||||
uart0-helloworld-sdboot.elf: uart0-helloworld-sdboot.c uart0-helloworld-sdboot.lds
|
||||
$(CROSS_CC) -march=armv5te -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T uart0-helloworld-sdboot.lds -Wl,-N
|
||||
|
||||
boot_head_sun3i.elf: boot_head.S boot_head.lds
|
||||
$(CROSS_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x1094
|
||||
|
||||
boot_head_sun4i.elf: boot_head.S boot_head.lds
|
||||
$(CROSS_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x1008
|
||||
|
||||
boot_head_sun5i.elf: boot_head.S boot_head.lds
|
||||
$(CROSS_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x102A
|
||||
|
||||
sunxi-bootinfo: bootinfo.c
|
||||
|
||||
# "preprocessed" .h files for inclusion of ARM thunk code
|
||||
headers:
|
||||
make -C thunks/ CROSS_COMPILE=$(CROSS_COMPILE)
|
||||
|
||||
|
||||
# target tools
|
||||
TARGET_CFLAGS = $(DEFAULT_CFLAGS) -static $(CFLAGS)
|
||||
sunxi-meminfo: meminfo.c
|
||||
$(CROSS_CC) $(TARGET_CFLAGS) -o $@ $<
|
||||
sunxi-script_extractor: script_extractor.c
|
||||
$(CROSS_CC) $(TARGET_CFLAGS) -o $@ $<
|
||||
|
||||
version.h:
|
||||
@./autoversion.sh > $@
|
||||
|
||||
.gitignore: Makefile
|
||||
@for x in $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) version.h '*.o' '*.swp'; do \
|
||||
echo "$$x"; \
|
||||
done | sort -V > $@
|
||||
|
||||
check: $(FEXC_LINKS)
|
||||
make -C tests/
|
||||
158
README.md
Normal file
158
README.md
Normal file
@ -0,0 +1,158 @@
|
||||
# sunxi-tools
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/linux-sunxi/sunxi-tools)
|
||||
[](https://github.com/linux-sunxi/sunxi-tools/releases)
|
||||
[](https://github.com/linux-sunxi/sunxi-tools/compare/v1.4...master)
|
||||
|
||||
Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
<br>For a full list of contributors, see
|
||||
[this link](https://github.com/linux-sunxi/sunxi-tools/contributors)
|
||||
or use the command `git shortlog -se --no-merges`.
|
||||
|
||||
Command line utilities to work with devices based on [Allwinner SoC]s:
|
||||
sun4i, sun5i, ... - that's why the 'x' in the package name.
|
||||
|
||||
### sunxi-fexc
|
||||
`.fex` file (de)compiler
|
||||
|
||||
Usage: ./sunxi-fexc [-vq] [-I <infmt>] [-O <outfmt>] [<input> [<output>]]
|
||||
|
||||
infmt: fex, bin (default:fex)
|
||||
outfmt: fex, bin (default:bin)
|
||||
|
||||
### bin2fex
|
||||
compatibility shortcut to call `sunxi-fexc` to decompile a _script.bin_
|
||||
blob back into `.fex` format used by Allwinner's SDK to configure
|
||||
the boards.
|
||||
|
||||
### fex2bin
|
||||
compatiblity shortcut to call `sunxi-fexc` to compile a `.fex` file
|
||||
into the binary form used by the legacy 3.4 kernel ("linux-sunxi").
|
||||
|
||||
### sunxi-fel
|
||||
script interface for USB communication with the FEL handler built in to
|
||||
the CPU. You usually activate [FEL mode] by pushing the _uboot_ / _recovery_
|
||||
button at poweron, or by having your device "fail over" to FEL when no other
|
||||
boot option is available. See http://linux-sunxi.org/FEL/USBBoot for a detailed
|
||||
usage guide.
|
||||
|
||||
When called with no arguments, _sunxi-fel_ will display a short usage summary.
|
||||
|
||||
_Note:_ Unless you select a specific device using the `--dev` or `--sid`
|
||||
options, the tool will access the first Allwinner device (in FEL mode) that it
|
||||
finds. You can print a list of all FEL devices currently connected/detected
|
||||
with `./sunxi-fel --list --verbose`.
|
||||
|
||||
### fel-gpio
|
||||
Simple wrapper (script) around `sunxi-pio` and `sunxi-fel`
|
||||
to allow GPIO manipulations via FEL
|
||||
|
||||
### fel-sdboot
|
||||
ARM native sdcard bootloader forcing the device into FEL mode
|
||||
|
||||
### uart0-helloworld-sdboot
|
||||
ARM native sdcard bootloader, which is only printing a short "hello"
|
||||
message to the UART0 serial console. Because it relies on runtime
|
||||
SoC type detection, this single image is bootable on a wide range of
|
||||
Allwinner devices and can be used for testing. Additionally, it may
|
||||
serve as a template/example for developing simple bare metal code
|
||||
(LED blinking and other similar GPIO related things).
|
||||
|
||||
### sunxi-pio
|
||||
Manipulate PIO registers/dumps
|
||||
|
||||
### sunxi-nand-part
|
||||
Tool for manipulating Allwinner NAND partition tables
|
||||
|
||||
### sunxi-nand-image-builder
|
||||
Tool used to create raw NAND images (including boot0 images)
|
||||
|
||||
### jtag-loop.sunxi
|
||||
ARM native boot helper to force the SD port into JTAG and then stop,
|
||||
to ease debugging of bootloaders.
|
||||
|
||||
### sunxi-bootinfo
|
||||
Dump information from Allwinner boot files (_boot0_ / _boot1_)
|
||||
|
||||
--type=sd include SD boot info
|
||||
--type=nand include NAND boot info (not implemented)
|
||||
|
||||
### phoenix_info
|
||||
gives information about a phoenix image created by the
|
||||
phoenixcard utility and optionally extracts the embedded boot
|
||||
code & firmware file from their hidden partitions.
|
||||
|
||||
### sunxi-meminfo
|
||||
Tool for reading DRAM settings from registers. Compiled as a
|
||||
static binary for use on android and other OSes.
|
||||
To build this, get a toolchain and run:
|
||||
|
||||
make CROSS_COMPILE=arm-linux-gnueabihf- sunxi-meminfo
|
||||
|
||||
### sunxi-script_extractor
|
||||
A simple tool, which can be executed on a rooted Android device
|
||||
to dump the _script.bin_ blob from RAM via reading _/dev/mem_.
|
||||
To build this, get a toolchain and run:
|
||||
|
||||
make CROSS_COMPILE=arm-linux-gnueabihf- sunxi-script_extractor
|
||||
---
|
||||
|
||||
## Building
|
||||
|
||||
Compilation requires the development version of *libusb-1.0* (include header
|
||||
and library) to be installed for `sunxi-fel`. Unless you explicitly pass
|
||||
*LIBUSB_CFLAGS* and *LIBUSB_LIBS* to the make utility, `pkg-config` is also
|
||||
needed. Development versions of zlib and libfdt are also required.
|
||||
|
||||
To install the dependencies on Ubuntu 20.04 using package manager:
|
||||
|
||||
```bash
|
||||
sudo apt install libusb-1.0-0-dev libz-dev libfdt-dev
|
||||
```
|
||||
|
||||
Available build targets:
|
||||
|
||||
* `make tools`
|
||||
builds tools that are useful on the host. This is what most people will want,
|
||||
and our default target (when simply using `make`).
|
||||
|
||||
* `make target-tools`
|
||||
builds tools that are intended for the target (Allwinner SoC), using a
|
||||
cross-compiler. The Makefile will try to auto-detect a suitable toolchain
|
||||
prefix, and falls back to `arm-none-eabi-` otherwise.
|
||||
If needed, you may override this by explicitly setting *CROSS_COMPILE*.
|
||||
<br>_Hint:_ When compiling 'natively' on the target platform you may
|
||||
simply use an empty toolchain prefix here (`make target-tools CROSS_COMPILE=`
|
||||
or `make all CROSS_COMPILE=`).
|
||||
|
||||
* `make all`
|
||||
builds both *tools* and *target-tools*.
|
||||
|
||||
* `make install-tools`
|
||||
builds *tools* and then copies/installs them to a filesystem location. The
|
||||
destination is affected by settings for `DESTDIR`, `PREFIX` and possibly
|
||||
`BINDIR`. For details, please refer to the *Makefile*.
|
||||
You may use `make install` as a shortcut for this.
|
||||
|
||||
* `make install-target-tools`
|
||||
builds *target-tools* and then copies/installs them to a filesystem location
|
||||
selected by `DESTDIR`, `PREFIX` and possibly `BINDIR` - see `make install-tools`
|
||||
above.
|
||||
|
||||
* `make install-all`
|
||||
builds and installs both *tools* and *target-tools*.
|
||||
|
||||
* `make misc`
|
||||
builds miscellaneous (host) utilities that are not part of our 'standard' suite.
|
||||
Currently this means `phoenix_info` and `sunxi-nand-image-builder`.
|
||||
|
||||
* `make install-misc`
|
||||
builds *misc* and installs the resulting binaries.
|
||||
|
||||
## License
|
||||
This software is licensed under the terms of GPLv2+ as defined by the
|
||||
Free Software Foundation, details can be read in the [LICENSE.md](LICENSE.md)
|
||||
file.
|
||||
|
||||
[allwinner soc]: http://linux-sunxi.org/Allwinner_SoC_Family
|
||||
[fel mode]: http://linux-sunxi.org/FEL
|
||||
50
adb-devprobe.sh
Executable file
50
adb-devprobe.sh
Executable file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
adb shell insmod /vendor/modules/sunxi-dbgreg.ko >/dev/null
|
||||
|
||||
dump_io()
|
||||
{
|
||||
module=$1
|
||||
addr=$2
|
||||
len=$3
|
||||
|
||||
for ((i = 0; i < len; i+=4)) {
|
||||
printf "%x %s " $((addr + i)) $module
|
||||
adb shell "echo `printf %x $((addr + i))` > /sys/devices/virtual/misc/sunxi-reg/rw/address; cat /sys/devices/virtual/misc/sunxi-reg/rw/value"
|
||||
echo
|
||||
}
|
||||
}
|
||||
|
||||
dump_io SRAM 0xf1c00000 0x100
|
||||
dump_io DRAM 0xf1c01000 0x400
|
||||
dump_io CCM 0xf1c20000 0x400
|
||||
dump_io PIO 0xf1c20800 0x400
|
||||
|
||||
dump_pmu()
|
||||
{
|
||||
for ((i = 0; i <0x100; i+=2)) {
|
||||
adb shell "echo `printf 0x%x $i` > /sys/bus/i2c/devices/0-0034/axp20_reg; cat /sys/bus/i2c/devices/0-0034/axp20_regs"
|
||||
}
|
||||
}
|
||||
|
||||
dump_pmu
|
||||
18
autoversion.sh
Executable file
18
autoversion.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#
|
||||
# This script auto-updates a VERSION string definition.
|
||||
# It outputs informational messages to stderr, while the actual
|
||||
# output (on stdout) can easily be redirected to a file.
|
||||
#
|
||||
|
||||
LATEST_RELEASE="v1.4.2"
|
||||
|
||||
if VER=`git describe --tags --dirty --always`; then
|
||||
echo "Setting version information: ${VER}" >&2
|
||||
else
|
||||
VER=${LATEST_RELEASE}
|
||||
echo "Unable to determine current version (using \"${VER}\" as fallback)" >&2
|
||||
fi
|
||||
echo >&2
|
||||
|
||||
echo "/* Auto-generated information. DO NOT EDIT */"
|
||||
echo "#define VERSION \"${VER}\""
|
||||
BIN
bin/fel-sdboot.sunxi
Normal file
BIN
bin/fel-sdboot.sunxi
Normal file
Binary file not shown.
BIN
bin/jtag-loop.sunxi
Normal file
BIN
bin/jtag-loop.sunxi
Normal file
Binary file not shown.
BIN
bin/ramboot.scr
Normal file
BIN
bin/ramboot.scr
Normal file
Binary file not shown.
7
bin/ramboot.uboot-sh
Normal file
7
bin/ramboot.uboot-sh
Normal file
@ -0,0 +1,7 @@
|
||||
# U-boot RAM boot script
|
||||
ramdisk=
|
||||
if iminfo 0x4c000000; then
|
||||
ramdisk=0x4c000000
|
||||
fi
|
||||
setenv bootargs console=ttyS0,115200 rdinit=/sbin/init panic=10
|
||||
bootm 0x44000000 $ramdisk
|
||||
BIN
bin/uart0-helloworld-sdboot.sunxi
Normal file
BIN
bin/uart0-helloworld-sdboot.sunxi
Normal file
Binary file not shown.
42
boot_head.S
Normal file
42
boot_head.S
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Boot header to work around broken Allwinner A1x boot loaders
|
||||
*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.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
|
||||
*
|
||||
*
|
||||
* This file is a workaround to broken bootloaders on Allwinner A1x
|
||||
* platform who do not provide correct machid or atags address
|
||||
*
|
||||
* Usage:
|
||||
* load the header at 0x40007000 and change the entry point of your
|
||||
* boot process to 0x40007000
|
||||
*
|
||||
* Detailed memory map:
|
||||
* 0x40000100 atags
|
||||
* 0x40007000 boot_head (entry point)
|
||||
* 0x40008000 kernel
|
||||
* 0x43000000 script.bin
|
||||
* If you have a ramdisk then load it at some higher address
|
||||
*/
|
||||
|
||||
_start:
|
||||
ldr r0, =0
|
||||
ldr r1, =MACHID
|
||||
ldr r2, =0x40000100
|
||||
ldr lr, =0x40008000
|
||||
bx lr
|
||||
28
boot_head.lds
Normal file
28
boot_head.lds
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x40007000;
|
||||
.text : { *(.text) }
|
||||
/DISCARD/ : { *(.dynstr*) }
|
||||
/DISCARD/ : { *(.dynamic*) }
|
||||
/DISCARD/ : { *(.plt*) }
|
||||
/DISCARD/ : { *(.interp*) }
|
||||
/DISCARD/ : { *(.gnu*) }
|
||||
/DISCARD/ : { *(.note*) }
|
||||
}
|
||||
384
bootinfo.c
Normal file
384
bootinfo.c
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
*
|
||||
* display information about sunxi boot headers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
/* boot_file_head copied from mksunxiboot */
|
||||
/* boot head definition from sun4i boot code */
|
||||
typedef struct boot_file_head
|
||||
{
|
||||
u32 jump_instruction; // one intruction jumping to real code
|
||||
u8 magic[8]; // ="eGON.BT0" or "eGON.BT1", not C-style string.
|
||||
u32 check_sum; // generated by PC
|
||||
u32 length; // generated by PC
|
||||
u32 pub_head_size; // the size of boot_file_head_t
|
||||
u8 pub_head_vsn[4]; // the version of boot_file_head_t
|
||||
u8 file_head_vsn[4]; // the version of boot0_file_head_t or boot1_file_head_t
|
||||
u8 Boot_vsn[4]; // Boot version
|
||||
u8 eGON_vsn[4]; // eGON version
|
||||
u8 platform[8]; // platform information
|
||||
} boot_file_head_t;
|
||||
|
||||
typedef struct brom_file_head
|
||||
{
|
||||
u32 jump_instruction; // one intruction jumping to real code
|
||||
u8 magic[8]; // ="eGON.BRM", not C-style string.
|
||||
u32 length; // generated by PC
|
||||
u8 Boot_vsn[4]; // Boot version
|
||||
u8 eGON_vsn[4]; // eGON version
|
||||
u8 platform[8]; // platform information
|
||||
} brom_file_head_t;
|
||||
|
||||
typedef struct _boot_dram_para_t {
|
||||
__u32 dram_baseaddr;
|
||||
__u32 dram_clk;
|
||||
__u32 dram_type;
|
||||
__u32 dram_rank_num;
|
||||
__u32 dram_chip_density;
|
||||
__u32 dram_io_width;
|
||||
__u32 dram_bus_width;
|
||||
__u32 dram_cas;
|
||||
__u32 dram_zq;
|
||||
__u32 dram_odt_en;
|
||||
__u32 dram_size;
|
||||
__u32 dram_tpr0;
|
||||
__u32 dram_tpr1;
|
||||
__u32 dram_tpr2;
|
||||
__u32 dram_tpr3;
|
||||
__u32 dram_tpr4;
|
||||
__u32 dram_tpr5;
|
||||
__u32 dram_emr1;
|
||||
__u32 dram_emr2;
|
||||
__u32 dram_emr3;
|
||||
} boot_dram_para_t;
|
||||
|
||||
typedef struct _normal_gpio_cfg {
|
||||
__u8 port;
|
||||
__u8 port_num;
|
||||
__u8 mul_sel;
|
||||
__u8 pull;
|
||||
__u8 drv_level;
|
||||
__u8 data;
|
||||
__u8 reserved[2];
|
||||
} normal_gpio_cfg;
|
||||
|
||||
typedef struct _boot0_private_head_t {
|
||||
__u32 prvt_head_size;
|
||||
char prvt_head_vsn[4];
|
||||
boot_dram_para_t dram_para;
|
||||
__s32 uart_port;
|
||||
normal_gpio_cfg uart_ctrl[2];
|
||||
__s32 enable_jtag;
|
||||
normal_gpio_cfg jtag_gpio[5];
|
||||
normal_gpio_cfg storage_gpio[32];
|
||||
__u8 storage_data[256];
|
||||
} boot0_private_head_t;
|
||||
|
||||
typedef struct _boot0_file_head_t {
|
||||
boot_file_head_t boot_head;
|
||||
boot0_private_head_t prvt_head;
|
||||
} boot0_file_head_t;
|
||||
|
||||
typedef struct _boot_core_para_t {
|
||||
__u32 user_set_clock;
|
||||
__u32 user_set_core_vol;
|
||||
__u32 vol_threshold;
|
||||
} boot_core_para_t;
|
||||
|
||||
typedef struct _boot1_private_head_t {
|
||||
__u32 prvt_head_size;
|
||||
__u8 prvt_head_vsn[4];
|
||||
__s32 uart_port;
|
||||
normal_gpio_cfg uart_ctrl[2];
|
||||
boot_dram_para_t dram_para;
|
||||
char script_buf[32768];
|
||||
boot_core_para_t core_para;
|
||||
__s32 twi_port;
|
||||
normal_gpio_cfg twi_ctrl[2];
|
||||
__s32 debug_enable;
|
||||
__s32 hold_key_min;
|
||||
__s32 hold_key_max;
|
||||
__u32 work_mode;
|
||||
__u32 storage_type;
|
||||
normal_gpio_cfg storage_gpio[32];
|
||||
__u8 storage_data[256];
|
||||
} boot1_private_head_t;
|
||||
|
||||
typedef struct _boot1_file_head_t {
|
||||
boot_file_head_t boot_head;
|
||||
boot1_private_head_t prvt_head;
|
||||
} boot1_file_head_t;
|
||||
|
||||
/* STORAGE DATA on SD loaders */
|
||||
typedef struct _boot_sdcard_info_t {
|
||||
__s32 card_ctrl_num;
|
||||
__s32 boot_offset;
|
||||
__s32 card_no[4];
|
||||
__s32 speed_mode[4];
|
||||
__s32 line_sel[4];
|
||||
__s32 line_count[4];
|
||||
} boot_sdcard_info_t;
|
||||
|
||||
#define BROM_MAGIC "eGON.BRM"
|
||||
#define BOOT0_MAGIC "eGON.BT0"
|
||||
#define BOOT1_MAGIC "eGON.BT1"
|
||||
|
||||
union {
|
||||
boot_file_head_t boot;
|
||||
boot0_file_head_t boot0;
|
||||
boot1_file_head_t boot1;
|
||||
brom_file_head_t brom;
|
||||
} boot_hdr;
|
||||
|
||||
typedef enum {
|
||||
ALLWINNER_UNKNOWN_LOADER=0,
|
||||
ALLWINNER_SD_LOADER,
|
||||
ALLWINNER_NAND_LOADER
|
||||
} loader_type;
|
||||
|
||||
void fail(char *msg) {
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void pprintf(void *addr, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
printf("%8x:\t", (unsigned)((char *)addr - (char *)&boot_hdr));
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void print_brom_file_head(brom_file_head_t *hdr)
|
||||
{
|
||||
pprintf(&hdr->magic, "Magic : %.8s\n", hdr->magic);
|
||||
pprintf(&hdr->length, "Length : %u\n", hdr->length);
|
||||
pprintf(&hdr->Boot_vsn, "BOOT ver : %.4s\n", hdr->Boot_vsn);
|
||||
pprintf(&hdr->eGON_vsn, "eGON ver : %.4s\n", hdr->eGON_vsn);
|
||||
pprintf(&hdr->platform, "Chip? : %.8s\n", hdr->platform);
|
||||
}
|
||||
|
||||
void print_boot_file_head(boot_file_head_t *hdr)
|
||||
{
|
||||
pprintf(&hdr->magic, "Magic : %.8s\n", hdr->magic);
|
||||
pprintf(&hdr->length, "Length : %u\n", hdr->length);
|
||||
pprintf(&hdr->pub_head_size, "HSize : %u\n", hdr->pub_head_size);
|
||||
pprintf(&hdr->pub_head_vsn, "HEAD ver : %.4s\n", hdr->pub_head_vsn);
|
||||
pprintf(&hdr->file_head_vsn, "FILE ver : %.4s\n", hdr->file_head_vsn);
|
||||
pprintf(&hdr->Boot_vsn, "BOOT ver : %.4s\n", hdr->Boot_vsn);
|
||||
pprintf(&hdr->eGON_vsn, "eGON ver : %.4s\n", hdr->eGON_vsn);
|
||||
pprintf(&hdr->platform, "platform : %c%c%c%c%c%c%c%c\n", hdr->platform[0], hdr->platform[1], hdr->platform[2], hdr->platform[3], hdr->platform[4], hdr->platform[5], hdr->platform[6], hdr->platform[7]);
|
||||
}
|
||||
|
||||
void print_boot_dram_para(boot_dram_para_t *dram)
|
||||
{
|
||||
pprintf(&dram->dram_baseaddr, "DRAM base : %p\n", (void *)(uintptr_t)dram->dram_baseaddr);
|
||||
pprintf(&dram->dram_clk, "DRAM clk : %d\n", dram->dram_clk);
|
||||
pprintf(&dram->dram_type, "DRAM type : %d\n", dram->dram_type);
|
||||
pprintf(&dram->dram_rank_num, "DRAM rank : %d\n", dram->dram_rank_num);
|
||||
pprintf(&dram->dram_chip_density,"DRAM den : %d\n", dram->dram_chip_density);
|
||||
pprintf(&dram->dram_io_width, "DRAM iow : %d\n", dram->dram_io_width);
|
||||
pprintf(&dram->dram_bus_width, "DRAM busw : %d\n", dram->dram_bus_width);
|
||||
pprintf(&dram->dram_cas, "DRAM cas : %d\n", dram->dram_cas);
|
||||
pprintf(&dram->dram_zq, "DRAM zq : %d\n", dram->dram_zq);
|
||||
pprintf(&dram->dram_odt_en, "DRAM odt : 0x%x\n", dram->dram_odt_en);
|
||||
pprintf(&dram->dram_size, "DRAM size : %d\n", dram->dram_size);
|
||||
pprintf(&dram->dram_tpr0, "DRAM tpr0 : 0x%x\n", dram->dram_tpr0);
|
||||
pprintf(&dram->dram_tpr1, "DRAM tpr1 : 0x%x\n", dram->dram_tpr1);
|
||||
pprintf(&dram->dram_tpr2, "DRAM tpr2 : 0x%x\n", dram->dram_tpr2);
|
||||
pprintf(&dram->dram_tpr3, "DRAM tpr3 : 0x%x\n", dram->dram_tpr3);
|
||||
pprintf(&dram->dram_tpr4, "DRAM tpr4 : 0x%x\n", dram->dram_tpr4);
|
||||
pprintf(&dram->dram_tpr5, "DRAM tpr5 : 0x%x\n", dram->dram_tpr5);
|
||||
pprintf(&dram->dram_emr1, "DRAM emr1 : 0x%x\n", dram->dram_emr1);
|
||||
pprintf(&dram->dram_emr2, "DRAM emr2 : 0x%x\n", dram->dram_emr2);
|
||||
pprintf(&dram->dram_emr3, "DRAM emr3 : 0x%x\n", dram->dram_emr3);
|
||||
}
|
||||
|
||||
void print_normal_gpio_cfg(normal_gpio_cfg *gpio, int count)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (gpio[i].port)
|
||||
pprintf(&gpio[i], " GPIO %d : port=%c%d, sel=%d, pull=%d, drv=%d, data=%d, reserved=%02x,%02x\n", i, 'A'+gpio[i].port-1, gpio[i].port_num, gpio[i].mul_sel, gpio[i].pull, gpio[i].drv_level, gpio[i].data, gpio[i].reserved[0], gpio[i].reserved[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void print_boot_sdcard_info(boot_sdcard_info_t *info)
|
||||
{
|
||||
pprintf(&info->card_ctrl_num, " CARD Ctrl Num: %d\n", info->card_ctrl_num);
|
||||
pprintf(&info->boot_offset, " BOOT Offset: %08x\n", info->boot_offset);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (info->card_no[i] == -1)
|
||||
continue;
|
||||
pprintf(&info->card_no[i], " CARD No : %d (%d)\n", info->card_no[i], i);
|
||||
pprintf(&info->speed_mode[i], " Speed : %d\n", info->speed_mode[i]);
|
||||
pprintf(&info->line_sel[i], " Line sel: %d\n", info->line_sel[i]);
|
||||
pprintf(&info->line_count[i], " Line cnt: %d\n", info->line_count[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void print_boot0_private_head(boot0_private_head_t *hdr, loader_type type)
|
||||
{
|
||||
pprintf(&hdr->prvt_head_size, "FHSize : %u\n", hdr->prvt_head_size);
|
||||
pprintf(&hdr->prvt_head_vsn, "FILE ver : %.4s\n", hdr->prvt_head_vsn);
|
||||
print_boot_dram_para(&hdr->dram_para);
|
||||
pprintf(&hdr->uart_port, "UART port : %d\n", hdr->uart_port);
|
||||
print_normal_gpio_cfg(hdr->uart_ctrl, 2);
|
||||
pprintf(&hdr->enable_jtag, "JTAG en : %d\n", hdr->enable_jtag);
|
||||
print_normal_gpio_cfg(hdr->jtag_gpio, 5);
|
||||
pprintf(&hdr->storage_gpio, "STORAGE :\n");
|
||||
print_normal_gpio_cfg(hdr->storage_gpio, 32);
|
||||
int i = 0;
|
||||
if (type == ALLWINNER_SD_LOADER) {
|
||||
print_boot_sdcard_info((boot_sdcard_info_t *)hdr->storage_data);
|
||||
i = sizeof(boot_sdcard_info_t);
|
||||
}
|
||||
for (int n = 0; i < 256; i++, n++) {
|
||||
if (n % 16 == 0) {
|
||||
if (n) {
|
||||
printf("\n");
|
||||
}
|
||||
pprintf(&hdr->storage_data[i], " DATA %02x :", i);
|
||||
}
|
||||
printf(" %02x", hdr->storage_data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print_script(void *UNUSED(script))
|
||||
{
|
||||
}
|
||||
|
||||
void print_core_para(boot_core_para_t *core)
|
||||
{
|
||||
pprintf(&core->user_set_clock, "Set Clock : %d\n", core->user_set_clock);
|
||||
pprintf(&core->user_set_core_vol, "Set Core Vol: %d\n", core->user_set_core_vol);
|
||||
pprintf(&core->vol_threshold, "Vol Threshold: %d\n", core->vol_threshold);
|
||||
}
|
||||
|
||||
void print_boot1_private_head(boot1_private_head_t *hdr, loader_type type)
|
||||
{
|
||||
pprintf(&hdr->prvt_head_size, "FHSize : %u\n", hdr->prvt_head_size);
|
||||
pprintf(&hdr->prvt_head_vsn, "FILE ver : %.4s\n", hdr->prvt_head_vsn);
|
||||
pprintf(&hdr->uart_port, "UART port : %d\n", hdr->uart_port);
|
||||
print_normal_gpio_cfg(hdr->uart_ctrl, 2);
|
||||
print_boot_dram_para(&hdr->dram_para);
|
||||
print_script(&hdr->script_buf);
|
||||
print_core_para(&hdr->core_para);
|
||||
pprintf(&hdr->twi_port, "TWI port : %d\n", hdr->twi_port);
|
||||
print_normal_gpio_cfg(hdr->twi_ctrl, 2);
|
||||
pprintf(&hdr->debug_enable, "Debug : %d\n", hdr->debug_enable);
|
||||
pprintf(&hdr->hold_key_min, "Hold key min : %d\n", hdr->hold_key_min);
|
||||
pprintf(&hdr->hold_key_max, "Hold key max : %d\n", hdr->hold_key_max);
|
||||
pprintf(&hdr->work_mode, "Work mode : %d\n", hdr->work_mode);
|
||||
pprintf(&hdr->storage_type, "STORAGE :\n");
|
||||
pprintf(&hdr->storage_type, " type : %d\n", hdr->storage_type);
|
||||
print_normal_gpio_cfg(hdr->storage_gpio, 32);
|
||||
int i = 0;
|
||||
if (type == ALLWINNER_SD_LOADER) {
|
||||
print_boot_sdcard_info((boot_sdcard_info_t *)hdr->storage_data);
|
||||
i = sizeof(boot_sdcard_info_t);
|
||||
}
|
||||
for (int n = 0; i < 256; i++, n++) {
|
||||
if (n % 16 == 0) {
|
||||
if (n) {
|
||||
printf("\n");
|
||||
}
|
||||
pprintf(&hdr->storage_data[i], " DATA %02x :", i);
|
||||
}
|
||||
printf(" %02x", hdr->storage_data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print_boot0_file_head(boot0_file_head_t *hdr, loader_type type)
|
||||
{
|
||||
print_boot_file_head(&hdr->boot_head);
|
||||
if (strncmp((char *)hdr->boot_head.file_head_vsn, "1230", 4) == 0)
|
||||
print_boot0_private_head(&hdr->prvt_head, type);
|
||||
else
|
||||
printf("Unknown boot0 header version\n");
|
||||
}
|
||||
|
||||
void print_boot1_file_head(boot1_file_head_t *hdr, loader_type type)
|
||||
{
|
||||
print_boot_file_head(&hdr->boot_head);
|
||||
if (strncmp((char *)hdr->boot_head.file_head_vsn, "1230", 4) == 0)
|
||||
print_boot1_private_head(&hdr->prvt_head, type);
|
||||
else
|
||||
printf("Unknown boot0 header version\n");
|
||||
}
|
||||
|
||||
static void usage(const char *cmd)
|
||||
{
|
||||
puts("sunxi-bootinfo " VERSION "\n");
|
||||
printf("Usage: %s [<filename>]\n", cmd);
|
||||
printf(" With no <filename> given, will read from stdin instead\n");
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
FILE *in = stdin;
|
||||
loader_type type = ALLWINNER_UNKNOWN_LOADER;
|
||||
if (argc > 1 && strcmp(argv[1], "--type=sd") == 0) {
|
||||
type = ALLWINNER_SD_LOADER;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
if (argc > 1 && strcmp(argv[1], "--type=nand") == 0) {
|
||||
type = ALLWINNER_NAND_LOADER;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
if (argc > 1) {
|
||||
in = fopen(argv[1], "rb");
|
||||
if (!in) {
|
||||
if (*argv[1] == '-')
|
||||
usage(argv[0]);
|
||||
fail("open input");
|
||||
}
|
||||
}
|
||||
int len;
|
||||
|
||||
len = fread(&boot_hdr, 1, sizeof(boot_hdr), in);
|
||||
if (len < (int)sizeof(boot_file_head_t))
|
||||
fail("Failed to read header:");
|
||||
if (strncmp((char *)boot_hdr.boot.magic, BOOT0_MAGIC, strlen(BOOT0_MAGIC)) == 0) {
|
||||
print_boot0_file_head(&boot_hdr.boot0, type);
|
||||
} else if (strncmp((char *)boot_hdr.boot.magic, BOOT1_MAGIC, strlen(BOOT1_MAGIC)) == 0) {
|
||||
print_boot1_file_head(&boot_hdr.boot1, type);
|
||||
} else if (strncmp((char *)boot_hdr.boot.magic, BROM_MAGIC, strlen(BROM_MAGIC)) == 0) {
|
||||
print_brom_file_head(&boot_hdr.brom);
|
||||
} else {
|
||||
fail("Invalid magic\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
48
common.h
Normal file
48
common.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_COMMON_H
|
||||
#define _SUNXI_TOOLS_COMMON_H
|
||||
|
||||
#include <stddef.h> /* offsetof */
|
||||
|
||||
#include "version.h" /* auto-generated VERSION string */
|
||||
|
||||
/** flag function argument as unused */
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
#else
|
||||
# define UNUSED(x) UNUSED_ ## x
|
||||
#endif
|
||||
|
||||
/** finds the parent of an struct member */
|
||||
#ifndef container_of
|
||||
#define container_of(P,T,M) (T *)((char *)(P) - offsetof(T, M))
|
||||
#endif
|
||||
|
||||
/** calculate number of elements of an array */
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(A) (sizeof(A)/sizeof((A)[0]))
|
||||
#endif
|
||||
|
||||
/** shortcut to "printf to stderr" */
|
||||
#define pr_error(...) fprintf(stderr, __VA_ARGS__)
|
||||
/** like pr_error(), but also exit program */
|
||||
#define pr_fatal(...) \
|
||||
do { pr_error(__VA_ARGS__); exit(EXIT_FAILURE); } while (0);
|
||||
|
||||
#endif /* _SUNXI_TOOLS_COMMON_H */
|
||||
44
fel-gpio
Executable file
44
fel-gpio
Executable file
@ -0,0 +1,44 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# Copyright (C) 2012,2013 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
pio_base=0x01c20800
|
||||
pio_size=0x228
|
||||
sram_addr=0x3000
|
||||
|
||||
# read PIO
|
||||
./sunxi-fel memmove $sram_addr $pio_base $pio_size
|
||||
./sunxi-fel read $sram_addr $pio_size pio.reg
|
||||
./sunxi-pio -i pio.reg print > pio.old
|
||||
cat pio.old | fgrep -v '<0><0><0><0>'
|
||||
|
||||
while read cmd; do
|
||||
./sunxi-pio -i pio.reg -o pio.reg $cmd
|
||||
# write PIO
|
||||
./sunxi-fel write $sram_addr pio.reg
|
||||
./sunxi-fel memmove $pio_base $sram_addr $pio_size
|
||||
# (re-)read PIO
|
||||
./sunxi-fel memmove $sram_addr $pio_base $pio_size
|
||||
./sunxi-fel read $sram_addr $pio_size pio.reg
|
||||
./sunxi-pio -i pio.reg print > pio.new
|
||||
diff -U0 pio.old pio.new || true
|
||||
mv -f pio.new pio.old
|
||||
done
|
||||
149
fel-remotefunc-compiler.rb
Executable file
149
fel-remotefunc-compiler.rb
Executable file
@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
if ARGV.size < 2
|
||||
printf("Usage: #{$PROGRAM_NAME} [c_source_input] [marshalled_header_output]\n\n")
|
||||
|
||||
printf("This script uses an ARM toolchain to compile native ARM code, and then\n")
|
||||
printf("automatically generates the necessary wrapper code for calling it from\n")
|
||||
printf("the sunxi-fel tool. Executing such compiled pieces of code natively on\n")
|
||||
printf("the device may be needed for the performance critical parts.\n")
|
||||
|
||||
printf("\nExample input file:\n\n")
|
||||
printf(" unsigned sum(unsigned a, unsigned b)\n")
|
||||
printf(" {\n")
|
||||
printf(" return a + b;\n")
|
||||
printf(" }\n")
|
||||
printf("\n")
|
||||
printf("Using this example code inside of sunxi-fel:\n")
|
||||
printf("\n")
|
||||
printf(" uint32_t a = 1, b = 2, c;\n")
|
||||
printf(" aw_fel_remotefunc_prepare_sum(dev, a, b);\n")
|
||||
printf(" aw_fel_remotefunc_execute(dev, &c);\n")
|
||||
printf(" printf(\"%%d + %%d = %%d\\n\", a, b, c);\n\n")
|
||||
|
||||
printf("If the returned result is not needed (a void function), then the second\n")
|
||||
printf("argument to the 'aw_fel_remotefunc_execute' function can be NULL.\n\n")
|
||||
exit(1)
|
||||
end
|
||||
|
||||
def tool_exists(tool_name)
|
||||
`which #{tool_name} > /dev/null 2>&1`
|
||||
return $?.to_i == 0
|
||||
end
|
||||
|
||||
def parse_stack_usage(filename)
|
||||
return unless File.exists?(filename)
|
||||
File.read(filename).strip.split("\n").map do |l|
|
||||
if l =~ /\:([^\:\s]+)\s+(\d+)\s+(\S+)/
|
||||
if $3 != "static"
|
||||
abort sprintf("Non-static stack usage for function '%s'\n", $1)
|
||||
end
|
||||
{function_name: $1, stack_usage: $2.to_i}
|
||||
else
|
||||
abort sprintf("Failed to parse stack usage information '%s'\n", l.strip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
toolchains = [
|
||||
"arm-none-eabi-",
|
||||
"arm-linux-gnueabihf-",
|
||||
"arm-none-linux-gnueabi-",
|
||||
"armv7a-hardfloat-linux-gnueabi-",
|
||||
]
|
||||
|
||||
toolchain = toolchains.find { |toolchain| tool_exists("#{toolchain}gcc") }
|
||||
abort "Can't find any usable ARM crosscompiler.\n" unless toolchain
|
||||
|
||||
# Compile the source file
|
||||
system("#{toolchain}gcc -c -Os -marm -march=armv5te -mfloat-abi=soft -fstack-usage -fpic -o #{ARGV[0]}.o #{ARGV[0]}")
|
||||
exit($?.to_i) if $?.to_i != 0
|
||||
|
||||
# Read the stack usage information
|
||||
stack_usage = parse_stack_usage("#{ARGV[0]}.su")
|
||||
if stack_usage.size != 1
|
||||
abort sprintf("Expected only one function in the source file, but got %s.\n",
|
||||
stack_usage.map {|a| "'" + a[:function_name] + "()'" }.join(", "))
|
||||
end
|
||||
|
||||
`#{toolchain}size -A #{ARGV[0]}.o`.each_line do |l|
|
||||
if l =~ /(\S+)\s+(\S+)/
|
||||
if ($1 == ".data" || $1 == ".bss" || $1 == ".rodata") && $2.to_i > 0
|
||||
abort "Can't have non-empty '.data', '.bss' or '.rodata' section."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
`#{toolchain}objdump -t #{ARGV[0]}.o`.each_line do |l|
|
||||
if l =~ /\*UND\*/
|
||||
abort "External references are not allowed: '#{l.strip}'.\n"
|
||||
end
|
||||
end
|
||||
|
||||
function_name = stack_usage[0][:function_name]
|
||||
|
||||
# Read the source file and strip multiline C comments
|
||||
sourcefile = File.read(ARGV[0]).gsub(/\/\*.*?\*\//m, "")
|
||||
|
||||
# Try to find the function and its arguments
|
||||
unless sourcefile =~ /#{function_name}\((.*?)\)/m
|
||||
abort sprintf("Can't find the function '%s()' in the source file.\n",
|
||||
function_name)
|
||||
end
|
||||
|
||||
# Extract the function argument names
|
||||
function_args = $1.split(",").map {|a| if a.strip =~ /([^\*\s]+)$/ then $1 end }
|
||||
|
||||
# Check if there is any return value
|
||||
have_retval = !(sourcefile =~ /void\s+#{function_name}/m)
|
||||
|
||||
###############################################################################
|
||||
# Generate output file
|
||||
###############################################################################
|
||||
|
||||
out = File.open(ARGV[1], "w")
|
||||
|
||||
out.printf("/* Automatically generated, do not edit! */\n\n")
|
||||
|
||||
out.printf("static void\n")
|
||||
funcdecl = sprintf("aw_fel_remotefunc_prepare_#{function_name}(feldev_handle *dev,")
|
||||
out.printf("%s\n", funcdecl)
|
||||
out.printf("%s", function_args.map {|a|
|
||||
" " * funcdecl.index("(") + " uint32_t " + a }.join(",\n"))
|
||||
out.printf(")\n{\n")
|
||||
|
||||
out.printf("\tstatic uint8_t arm_code[] = {\n")
|
||||
`#{toolchain}objdump -d #{ARGV[0]}.o`.each_line {|l|
|
||||
next unless l =~ /(\h+)\:\s+(\h+)\s+(\S+)\s+([^;]*)/
|
||||
addr = $1
|
||||
opcode = $2
|
||||
p1 = $3
|
||||
p2 = $4.strip
|
||||
opcode = opcode.scan(/../).map {|a| "0x" + a }.reverse.join(", ")
|
||||
out.printf("\t\t%s, /* %4s: %-8s %-34s \x2a/\n", opcode, addr, p1, p2)
|
||||
}
|
||||
out.printf("\t};\n")
|
||||
|
||||
out.printf("\tuint32_t args[] = {\n\t\t")
|
||||
out.printf("%s\n\t};\n", function_args.join(",\n\t\t"))
|
||||
|
||||
out.printf("\taw_fel_remotefunc_prepare(dev, %d, arm_code, sizeof(arm_code), %d, args);\n",
|
||||
stack_usage[0][:stack_usage], function_args.size)
|
||||
|
||||
out.printf("}\n")
|
||||
187
fel-remotefunc-spi-data-transfer.c
Normal file
187
fel-remotefunc-spi-data-transfer.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright © 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned char u8;
|
||||
|
||||
#define readl(addr) (*((volatile u32 *)(addr)))
|
||||
#define writel(v, addr) (*((volatile u32 *)(addr)) = (u32)(v))
|
||||
#define readb(addr) (*((volatile u8 *)(addr)))
|
||||
#define writeb(v, addr) (*((volatile u8 *)(addr)) = (u8)(v))
|
||||
|
||||
/*
|
||||
* This is a basic full-duplex SPI data transfer function (we are sending a
|
||||
* block of data and receiving the same amount of data back), doing the job
|
||||
* without any help from DMA. And because we can be running in some rather
|
||||
* adverse conditions (with default PMIC settings, low CPU clock speed and
|
||||
* CPU caches disabled), it is necessary to use 32-bit accesses to read/write
|
||||
* the FIFO buffers. As a result, Allwinner A13 with the default 408MHz CPU
|
||||
* clock speed can successfully handle at least 12 MHz SPI clock speed.
|
||||
*
|
||||
* Supports both sun4i and sun6i variants of the SPI controller (they only
|
||||
* need different hardware register addresses passed as arguments).
|
||||
*/
|
||||
static void inline __attribute((always_inline)) spi_data_transfer(void *buf,
|
||||
u32 bufsize,
|
||||
void *spi_ctl_reg,
|
||||
u32 spi_ctl_xch_bitmask,
|
||||
void *spi_fifo_reg,
|
||||
void *spi_tx_reg,
|
||||
void *spi_rx_reg,
|
||||
void *spi_bc_reg,
|
||||
void *spi_tc_reg,
|
||||
void *spi_bcc_reg)
|
||||
{
|
||||
u32 cnt;
|
||||
u32 rxsize = bufsize;
|
||||
u32 txsize = bufsize;
|
||||
u8 *rxbuf8 = buf;
|
||||
u8 *txbuf8 = buf;
|
||||
u32 *rxbuf;
|
||||
u32 *txbuf;
|
||||
u32 cpsr;
|
||||
|
||||
/* sun6i uses 3 registers, sun4i only needs 2 */
|
||||
writel(bufsize, spi_bc_reg);
|
||||
writel(bufsize, spi_tc_reg);
|
||||
if (spi_bcc_reg)
|
||||
writel(bufsize, spi_bcc_reg);
|
||||
|
||||
/* Fill the TX buffer with some initial data */
|
||||
cnt = (-(u32)txbuf8 & 3) + 60;
|
||||
if (cnt > txsize)
|
||||
cnt = txsize;
|
||||
while (cnt-- > 0) {
|
||||
writeb(*txbuf8++, spi_tx_reg);
|
||||
txsize--;
|
||||
}
|
||||
|
||||
/* Temporarily disable IRQ & FIQ */
|
||||
asm volatile("mrs %0, cpsr" : "=r" (cpsr));
|
||||
asm volatile("msr cpsr_c, %0" :: "r" (cpsr | 0xC0));
|
||||
|
||||
/* Start the data transfer */
|
||||
writel(readl(spi_ctl_reg) | spi_ctl_xch_bitmask, spi_ctl_reg);
|
||||
|
||||
/* Read the initial unaligned part of the data */
|
||||
cnt = (-(u32)rxbuf8 & 3);
|
||||
if (cnt > rxsize)
|
||||
cnt = rxsize;
|
||||
while (cnt > 0) {
|
||||
u32 fiforeg = readl(spi_fifo_reg);
|
||||
int rxfifo = fiforeg & 0x7F;
|
||||
if (rxfifo > 0) {
|
||||
*rxbuf8++ = readb(spi_rx_reg);
|
||||
cnt--;
|
||||
rxsize--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fast processing of the aligned part (read/write 32-bit at a time) */
|
||||
rxbuf = (u32 *)rxbuf8;
|
||||
txbuf = (u32 *)txbuf8;
|
||||
while (rxsize >= 4) {
|
||||
u32 fiforeg = readl(spi_fifo_reg);
|
||||
int rxfifo = fiforeg & 0x7F;
|
||||
int txfifo = (fiforeg >> 16) & 0x7F;
|
||||
if (rxfifo >= 4) {
|
||||
*rxbuf++ = readl(spi_rx_reg);
|
||||
rxsize -= 4;
|
||||
}
|
||||
if (txfifo < 60 && txsize >= 4) {
|
||||
writel(*txbuf++, spi_tx_reg);
|
||||
txsize -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the trailing part pf the data */
|
||||
rxbuf8 = (u8 *)rxbuf;
|
||||
txbuf8 = (u8 *)txbuf;
|
||||
while (rxsize >= 1) {
|
||||
u32 fiforeg = readl(spi_fifo_reg);
|
||||
int rxfifo = fiforeg & 0x7F;
|
||||
int txfifo = (fiforeg >> 16) & 0x7F;
|
||||
if (rxfifo >= 1) {
|
||||
*rxbuf8++ = readb(spi_rx_reg);
|
||||
rxsize -= 1;
|
||||
}
|
||||
if (txfifo < 60 && txsize >= 1) {
|
||||
writeb(*txbuf8++, spi_tx_reg);
|
||||
txsize -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore CPSR */
|
||||
asm volatile("msr cpsr_c, %0" :: "r" (cpsr));
|
||||
}
|
||||
|
||||
void spi_batch_data_transfer(u8 *buf,
|
||||
void *spi_ctl_reg,
|
||||
u32 spi_ctl_xch_bitmask,
|
||||
void *spi_fifo_reg,
|
||||
void *spi_tx_reg,
|
||||
void *spi_rx_reg,
|
||||
void *spi_bc_reg,
|
||||
void *spi_tc_reg,
|
||||
void *spi_bcc_reg)
|
||||
{
|
||||
u8 wait_for_completion_cmd[2];
|
||||
u8 *backup_buf;
|
||||
u32 bufsize;
|
||||
|
||||
while (1) {
|
||||
u32 code = (buf[0] << 8) | buf[1];
|
||||
|
||||
/* End of data */
|
||||
if (code == 0)
|
||||
return;
|
||||
|
||||
if (code == 0xFFFF) {
|
||||
/* Wait for completion, part 1 */
|
||||
backup_buf = buf;
|
||||
buf = wait_for_completion_cmd;
|
||||
wait_for_completion_cmd[0] = 0x05;
|
||||
bufsize = 2;
|
||||
} else {
|
||||
/* Normal buffer */
|
||||
buf += 2;
|
||||
bufsize = code;
|
||||
}
|
||||
|
||||
spi_data_transfer(buf, bufsize, spi_ctl_reg, spi_ctl_xch_bitmask,
|
||||
spi_fifo_reg, spi_tx_reg, spi_rx_reg,
|
||||
spi_bc_reg, spi_tc_reg, spi_bcc_reg);
|
||||
buf += bufsize;
|
||||
|
||||
if (code == 0xFFFF) {
|
||||
/* Wait for completion, part 2 */
|
||||
buf = backup_buf;
|
||||
if (wait_for_completion_cmd[1] & 1) {
|
||||
/* Still busy */
|
||||
continue;
|
||||
}
|
||||
/* Advance to the next code */
|
||||
buf = backup_buf + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
147
fel-remotefunc-spi-data-transfer.h
Normal file
147
fel-remotefunc-spi-data-transfer.h
Normal file
@ -0,0 +1,147 @@
|
||||
/* Automatically generated, do not edit! */
|
||||
|
||||
static void
|
||||
aw_fel_remotefunc_prepare_spi_batch_data_transfer(feldev_handle *dev,
|
||||
uint32_t buf,
|
||||
uint32_t spi_ctl_reg,
|
||||
uint32_t spi_ctl_xch_bitmask,
|
||||
uint32_t spi_fifo_reg,
|
||||
uint32_t spi_tx_reg,
|
||||
uint32_t spi_rx_reg,
|
||||
uint32_t spi_bc_reg,
|
||||
uint32_t spi_tc_reg,
|
||||
uint32_t spi_bcc_reg)
|
||||
{
|
||||
static uint8_t arm_code[] = {
|
||||
0xf0, 0x4f, 0x2d, 0xe9, /* 0: push {r4, r5, r6, r7, r8, r9, sl, fp, lr} */
|
||||
0xc8, 0x91, 0x9f, 0xe5, /* 4: ldr r9, [pc, #456] */
|
||||
0x14, 0xd0, 0x4d, 0xe2, /* 8: sub sp, sp, #20 */
|
||||
0x00, 0x20, 0x8d, 0xe5, /* c: str r2, [sp] */
|
||||
0x0c, 0x20, 0x8d, 0xe2, /* 10: add r2, sp, #12 */
|
||||
0x04, 0x20, 0x8d, 0xe5, /* 14: str r2, [sp, #4] */
|
||||
0x01, 0x60, 0xd0, 0xe5, /* 18: ldrb r6, [r0, #1] */
|
||||
0x00, 0x20, 0xd0, 0xe5, /* 1c: ldrb r2, [r0] */
|
||||
0x06, 0x24, 0x82, 0xe1, /* 20: orr r2, r2, r6, lsl #8 */
|
||||
0x22, 0x64, 0xa0, 0xe1, /* 24: lsr r6, r2, #8 */
|
||||
0x02, 0x64, 0x86, 0xe1, /* 28: orr r6, r6, r2, lsl #8 */
|
||||
0x06, 0x68, 0xa0, 0xe1, /* 2c: lsl r6, r6, #16 */
|
||||
0x26, 0x68, 0xa0, 0xe1, /* 30: lsr r6, r6, #16 */
|
||||
0x00, 0x00, 0x56, 0xe3, /* 34: cmp r6, #0 */
|
||||
0x63, 0x00, 0x00, 0x0a, /* 38: beq 1cc <spi_batch_data_transfer+0x1cc> */
|
||||
0x09, 0x00, 0x56, 0xe1, /* 3c: cmp r6, r9 */
|
||||
0x05, 0x20, 0xa0, 0x03, /* 40: moveq r2, #5 */
|
||||
0x0c, 0x20, 0xcd, 0x05, /* 44: strbeq r2, [sp, #12] */
|
||||
0x40, 0x20, 0x9d, 0xe5, /* 48: ldr r2, [sp, #64] */
|
||||
0x06, 0x50, 0xa0, 0x11, /* 4c: movne r5, r6 */
|
||||
0x02, 0x50, 0xa0, 0x03, /* 50: moveq r5, #2 */
|
||||
0x00, 0x50, 0x82, 0xe5, /* 54: str r5, [r2] */
|
||||
0x44, 0x20, 0x9d, 0xe5, /* 58: ldr r2, [sp, #68] */
|
||||
0x00, 0x70, 0xa0, 0x01, /* 5c: moveq r7, r0 */
|
||||
0x00, 0x50, 0x82, 0xe5, /* 60: str r5, [r2] */
|
||||
0x48, 0x20, 0x9d, 0xe5, /* 64: ldr r2, [sp, #72] */
|
||||
0x04, 0x00, 0x9d, 0x05, /* 68: ldreq r0, [sp, #4] */
|
||||
0x02, 0x00, 0x80, 0x12, /* 6c: addne r0, r0, #2 */
|
||||
0x00, 0x00, 0x52, 0xe3, /* 70: cmp r2, #0 */
|
||||
0x00, 0x50, 0x82, 0x15, /* 74: strne r5, [r2] */
|
||||
0x00, 0x20, 0x60, 0xe2, /* 78: rsb r2, r0, #0 */
|
||||
0x03, 0x20, 0x02, 0xe2, /* 7c: and r2, r2, #3 */
|
||||
0x3c, 0x40, 0x82, 0xe2, /* 80: add r4, r2, #60 */
|
||||
0x04, 0x00, 0x55, 0xe1, /* 84: cmp r5, r4 */
|
||||
0x05, 0x40, 0xa0, 0x31, /* 88: movcc r4, r5 */
|
||||
0x04, 0xe0, 0x80, 0xe0, /* 8c: add lr, r0, r4 */
|
||||
0x00, 0xc0, 0xa0, 0xe1, /* 90: mov ip, r0 */
|
||||
0x0e, 0x00, 0x5c, 0xe1, /* 94: cmp ip, lr */
|
||||
0x1b, 0x00, 0x00, 0x1a, /* 98: bne 10c <spi_batch_data_transfer+0x10c> */
|
||||
0x04, 0x40, 0x45, 0xe0, /* 9c: sub r4, r5, r4 */
|
||||
0x00, 0xa0, 0x0f, 0xe1, /* a0: mrs sl, CPSR */
|
||||
0xc0, 0xc0, 0x8a, 0xe3, /* a4: orr ip, sl, #192 */
|
||||
0x0c, 0xf0, 0x21, 0xe1, /* a8: msr CPSR_c, ip */
|
||||
0x00, 0xc0, 0x91, 0xe5, /* ac: ldr ip, [r1] */
|
||||
0x00, 0x80, 0x9d, 0xe5, /* b0: ldr r8, [sp] */
|
||||
0x02, 0x00, 0x55, 0xe1, /* b4: cmp r5, r2 */
|
||||
0x0c, 0xc0, 0x88, 0xe1, /* b8: orr ip, r8, ip */
|
||||
0x05, 0x20, 0xa0, 0x31, /* bc: movcc r2, r5 */
|
||||
0x00, 0xc0, 0x81, 0xe5, /* c0: str ip, [r1] */
|
||||
0x02, 0x80, 0x80, 0xe0, /* c4: add r8, r0, r2 */
|
||||
0x00, 0xc0, 0xa0, 0xe1, /* c8: mov ip, r0 */
|
||||
0x0c, 0x00, 0x58, 0xe1, /* cc: cmp r8, ip */
|
||||
0x11, 0x00, 0x00, 0x1a, /* d0: bne 11c <spi_batch_data_transfer+0x11c> */
|
||||
0x02, 0x20, 0x45, 0xe0, /* d4: sub r2, r5, r2 */
|
||||
0x03, 0x00, 0x52, 0xe3, /* d8: cmp r2, #3 */
|
||||
0x14, 0x00, 0x00, 0x8a, /* dc: bhi 134 <spi_batch_data_transfer+0x134> */
|
||||
0x00, 0x00, 0x52, 0xe3, /* e0: cmp r2, #0 */
|
||||
0x25, 0x00, 0x00, 0x1a, /* e4: bne 180 <spi_batch_data_transfer+0x180> */
|
||||
0x0a, 0xf0, 0x21, 0xe1, /* e8: msr CPSR_c, sl */
|
||||
0x09, 0x00, 0x56, 0xe1, /* ec: cmp r6, r9 */
|
||||
0x05, 0x00, 0x80, 0x10, /* f0: addne r0, r0, r5 */
|
||||
0xc7, 0xff, 0xff, 0x1a, /* f4: bne 18 <spi_batch_data_transfer+0x18> */
|
||||
0x0d, 0x20, 0xdd, 0xe5, /* f8: ldrb r2, [sp, #13] */
|
||||
0x01, 0x00, 0x12, 0xe3, /* fc: tst r2, #1 */
|
||||
0x02, 0x00, 0x87, 0x02, /* 100: addeq r0, r7, #2 */
|
||||
0x07, 0x00, 0xa0, 0x11, /* 104: movne r0, r7 */
|
||||
0xc2, 0xff, 0xff, 0xea, /* 108: b 18 <spi_batch_data_transfer+0x18> */
|
||||
0x38, 0xa0, 0x9d, 0xe5, /* 10c: ldr sl, [sp, #56] */
|
||||
0x01, 0x80, 0xdc, 0xe4, /* 110: ldrb r8, [ip], #1 */
|
||||
0x00, 0x80, 0xca, 0xe5, /* 114: strb r8, [sl] */
|
||||
0xdd, 0xff, 0xff, 0xea, /* 118: b 94 <spi_batch_data_transfer+0x94> */
|
||||
0x00, 0xb0, 0x93, 0xe5, /* 11c: ldr fp, [r3] */
|
||||
0x7f, 0x00, 0x1b, 0xe3, /* 120: tst fp, #127 */
|
||||
0x3c, 0xb0, 0x9d, 0x15, /* 124: ldrne fp, [sp, #60] */
|
||||
0x00, 0xb0, 0xdb, 0x15, /* 128: ldrbne fp, [fp] */
|
||||
0x01, 0xb0, 0xcc, 0x14, /* 12c: strbne fp, [ip], #1 */
|
||||
0xe5, 0xff, 0xff, 0xea, /* 130: b cc <spi_batch_data_transfer+0xcc> */
|
||||
0x00, 0xb0, 0x93, 0xe5, /* 134: ldr fp, [r3] */
|
||||
0x7c, 0x00, 0x1b, 0xe3, /* 138: tst fp, #124 */
|
||||
0x2b, 0x88, 0xa0, 0xe1, /* 13c: lsr r8, fp, #16 */
|
||||
0x3c, 0xb0, 0x9d, 0x15, /* 140: ldrne fp, [sp, #60] */
|
||||
0x7f, 0x80, 0x08, 0xe2, /* 144: and r8, r8, #127 */
|
||||
0x00, 0xb0, 0x9b, 0x15, /* 148: ldrne fp, [fp] */
|
||||
0x04, 0xb0, 0x8c, 0x14, /* 14c: strne fp, [ip], #4 */
|
||||
0x04, 0x20, 0x42, 0x12, /* 150: subne r2, r2, #4 */
|
||||
0x3b, 0x00, 0x58, 0xe3, /* 154: cmp r8, #59 */
|
||||
0x00, 0x80, 0xa0, 0xc3, /* 158: movgt r8, #0 */
|
||||
0x01, 0x80, 0xa0, 0xd3, /* 15c: movle r8, #1 */
|
||||
0x03, 0x00, 0x54, 0xe3, /* 160: cmp r4, #3 */
|
||||
0x00, 0x80, 0xa0, 0x93, /* 164: movls r8, #0 */
|
||||
0x00, 0x00, 0x58, 0xe3, /* 168: cmp r8, #0 */
|
||||
0x38, 0xb0, 0x9d, 0x15, /* 16c: ldrne fp, [sp, #56] */
|
||||
0x04, 0x80, 0x9e, 0x14, /* 170: ldrne r8, [lr], #4 */
|
||||
0x04, 0x40, 0x44, 0x12, /* 174: subne r4, r4, #4 */
|
||||
0x00, 0x80, 0x8b, 0x15, /* 178: strne r8, [fp] */
|
||||
0xd5, 0xff, 0xff, 0xea, /* 17c: b d8 <spi_batch_data_transfer+0xd8> */
|
||||
0x00, 0xb0, 0x93, 0xe5, /* 180: ldr fp, [r3] */
|
||||
0x7f, 0x00, 0x1b, 0xe3, /* 184: tst fp, #127 */
|
||||
0x2b, 0x88, 0xa0, 0xe1, /* 188: lsr r8, fp, #16 */
|
||||
0x3c, 0xb0, 0x9d, 0x15, /* 18c: ldrne fp, [sp, #60] */
|
||||
0x7f, 0x80, 0x08, 0xe2, /* 190: and r8, r8, #127 */
|
||||
0x00, 0xb0, 0xdb, 0x15, /* 194: ldrbne fp, [fp] */
|
||||
0x01, 0xb0, 0xcc, 0x14, /* 198: strbne fp, [ip], #1 */
|
||||
0x01, 0x20, 0x42, 0x12, /* 19c: subne r2, r2, #1 */
|
||||
0x3b, 0x00, 0x58, 0xe3, /* 1a0: cmp r8, #59 */
|
||||
0x00, 0x80, 0xa0, 0xc3, /* 1a4: movgt r8, #0 */
|
||||
0x01, 0x80, 0xa0, 0xd3, /* 1a8: movle r8, #1 */
|
||||
0x00, 0x00, 0x54, 0xe3, /* 1ac: cmp r4, #0 */
|
||||
0x00, 0x80, 0xa0, 0x03, /* 1b0: moveq r8, #0 */
|
||||
0x00, 0x00, 0x58, 0xe3, /* 1b4: cmp r8, #0 */
|
||||
0x38, 0xb0, 0x9d, 0x15, /* 1b8: ldrne fp, [sp, #56] */
|
||||
0x01, 0x80, 0xde, 0x14, /* 1bc: ldrbne r8, [lr], #1 */
|
||||
0x01, 0x40, 0x44, 0x12, /* 1c0: subne r4, r4, #1 */
|
||||
0x00, 0x80, 0xcb, 0x15, /* 1c4: strbne r8, [fp] */
|
||||
0xc4, 0xff, 0xff, 0xea, /* 1c8: b e0 <spi_batch_data_transfer+0xe0> */
|
||||
0x14, 0xd0, 0x8d, 0xe2, /* 1cc: add sp, sp, #20 */
|
||||
0xf0, 0x8f, 0xbd, 0xe8, /* 1d0: pop {r4, r5, r6, r7, r8, r9, sl, fp, pc} */
|
||||
0xff, 0xff, 0x00, 0x00, /* 1d4: .word 0x0000ffff */
|
||||
};
|
||||
uint32_t args[] = {
|
||||
buf,
|
||||
spi_ctl_reg,
|
||||
spi_ctl_xch_bitmask,
|
||||
spi_fifo_reg,
|
||||
spi_tx_reg,
|
||||
spi_rx_reg,
|
||||
spi_bc_reg,
|
||||
spi_tc_reg,
|
||||
spi_bcc_reg
|
||||
};
|
||||
aw_fel_remotefunc_prepare(dev, 56, arm_code, sizeof(arm_code), 9, args);
|
||||
}
|
||||
69
fel-sdboot.S
Normal file
69
fel-sdboot.S
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
|
||||
*
|
||||
* Based on previous works
|
||||
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.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
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is a utility stub (bootloader code) to force the device into
|
||||
* FEL mode, by jumping directly to the corresponding (N-)BROM entry point.
|
||||
*
|
||||
* Build instructions:
|
||||
* make fel-sdboot.sunxi
|
||||
*
|
||||
* If needed, adjust CROSS_COMPILE and MKSUNXIBOOT according to your
|
||||
* toolchain, e.g.
|
||||
* make fel-sdboot.sunxi CROSS_COMPILE=armv7a-hardfloat-linux-gnueabi- \
|
||||
* MKSUNXIBOOT=/usr/local/bin/mksunxiboot
|
||||
*
|
||||
*
|
||||
* Install instructions:
|
||||
* dd if=fel-sdboot.sunxi of=/dev/sdX bs=1024 seek=8
|
||||
*/
|
||||
|
||||
SCTRL .req r0
|
||||
.equ V_BIT, (1 << 13)
|
||||
|
||||
.equ BROM_ENTRY_LOW, 0x00000020
|
||||
.equ BROM_ENTRY_HIGH, 0xFFFF0020
|
||||
|
||||
/*
|
||||
* In cases where insufficient padding is added by an old mksunxiboot,
|
||||
* _start may be 0x20, which means that the instruction at 0x28 could get
|
||||
* corrupted by the BROM - see https://patchwork.ozlabs.org/patch/622173/
|
||||
*
|
||||
* Apply a workaround to avoid (= skip over) that memory location.
|
||||
* _main would be at 0x30 in that particular case. With newer (properly
|
||||
* fixed) versions of mksunxiboot, this code ends up at higher addresses
|
||||
* and will be moot, but harmless.
|
||||
*/
|
||||
_start:
|
||||
b _main
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
_main:
|
||||
mrc p15, 0, SCTRL, c1, c0, 0
|
||||
tst SCTRL, #V_BIT @ test SCTRL.V
|
||||
moveq lr, #BROM_ENTRY_LOW
|
||||
ldrne lr, =BROM_ENTRY_HIGH
|
||||
bx lr
|
||||
28
fel-sdboot.lds
Normal file
28
fel-sdboot.lds
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0030;
|
||||
.text : { *(.text) }
|
||||
/DISCARD/ : { *(.dynstr*) }
|
||||
/DISCARD/ : { *(.dynamic*) }
|
||||
/DISCARD/ : { *(.plt*) }
|
||||
/DISCARD/ : { *(.interp*) }
|
||||
/DISCARD/ : { *(.gnu*) }
|
||||
/DISCARD/ : { *(.note*) }
|
||||
}
|
||||
606
fel-spiflash.c
Normal file
606
fel-spiflash.c
Normal file
@ -0,0 +1,606 @@
|
||||
/*
|
||||
* (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fel_lib.h"
|
||||
#include "progress.h"
|
||||
|
||||
#include "fel-remotefunc-spi-data-transfer.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint8_t write_enable_cmd;
|
||||
uint8_t large_erase_cmd;
|
||||
uint32_t large_erase_size;
|
||||
uint8_t small_erase_cmd;
|
||||
uint32_t small_erase_size;
|
||||
uint8_t program_cmd;
|
||||
uint32_t program_size;
|
||||
char *text_description;
|
||||
} spi_flash_info_t;
|
||||
|
||||
spi_flash_info_t spi_flash_info[] = {
|
||||
{ .id = 0xEF40, .write_enable_cmd = 0x6,
|
||||
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
|
||||
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
|
||||
.program_cmd = 0x02, .program_size = 256,
|
||||
.text_description = "Winbond W25Qxx" },
|
||||
{ .id = 0xC220, .write_enable_cmd = 0x6,
|
||||
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
|
||||
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
|
||||
.program_cmd = 0x02, .program_size = 256,
|
||||
.text_description = "Macronix MX25Lxxxx" },
|
||||
{ .id = 0x1C70, .write_enable_cmd = 0x6,
|
||||
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
|
||||
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
|
||||
.program_cmd = 0x02, .program_size = 256,
|
||||
.text_description = "Eon EN25QHxx" },
|
||||
};
|
||||
|
||||
spi_flash_info_t default_spi_flash_info = {
|
||||
.id = 0x0000, .write_enable_cmd = 0x6,
|
||||
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
|
||||
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
|
||||
.program_cmd = 0x02, .program_size = 256,
|
||||
.text_description = "Unknown",
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
uint32_t fel_readl(feldev_handle *dev, uint32_t addr);
|
||||
void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val);
|
||||
#define readl(addr) fel_readl(dev, (addr))
|
||||
#define writel(val, addr) fel_writel(dev, (addr), (val))
|
||||
|
||||
#define PA (0)
|
||||
#define PB (1)
|
||||
#define PC (2)
|
||||
|
||||
#define CCM_SPI0_CLK (0x01C20000 + 0xA0)
|
||||
#define CCM_AHB_GATING0 (0x01C20000 + 0x60)
|
||||
#define CCM_AHB_GATE_SPI0 (1 << 20)
|
||||
#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0)
|
||||
#define SUN6I_SPI0_RST (1 << 20)
|
||||
#define SUNIV_PLL6_CTL (0x01c20000 + 0x28)
|
||||
#define SUNIV_AHB_APB_CFG (0x01c20000 + 0x54)
|
||||
|
||||
#define H6_CCM_SPI0_CLK (0x03001000 + 0x940)
|
||||
#define H6_CCM_SPI_BGR (0x03001000 + 0x96C)
|
||||
#define H6_CCM_SPI0_GATE_RESET (1 << 0 | 1 << 16)
|
||||
|
||||
#define SUNIV_GPC_SPI0 (2)
|
||||
#define SUNXI_GPC_SPI0 (3)
|
||||
#define SUN50I_GPC_SPI0 (4)
|
||||
|
||||
#define SUN4I_CTL_ENABLE (1 << 0)
|
||||
#define SUN4I_CTL_MASTER (1 << 1)
|
||||
#define SUN4I_CTL_TF_RST (1 << 8)
|
||||
#define SUN4I_CTL_RF_RST (1 << 9)
|
||||
#define SUN4I_CTL_XCH (1 << 10)
|
||||
|
||||
#define SUN6I_TCR_XCH (1U << 31)
|
||||
|
||||
#define SUN4I_SPI0_CCTL (spi_base(dev) + 0x1C)
|
||||
#define SUN4I_SPI0_CTL (spi_base(dev) + 0x08)
|
||||
#define SUN4I_SPI0_RX (spi_base(dev) + 0x00)
|
||||
#define SUN4I_SPI0_TX (spi_base(dev) + 0x04)
|
||||
#define SUN4I_SPI0_FIFO_STA (spi_base(dev) + 0x28)
|
||||
#define SUN4I_SPI0_BC (spi_base(dev) + 0x20)
|
||||
#define SUN4I_SPI0_TC (spi_base(dev) + 0x24)
|
||||
|
||||
#define SUN6I_SPI0_CCTL (spi_base(dev) + 0x24)
|
||||
#define SUN6I_SPI0_GCR (spi_base(dev) + 0x04)
|
||||
#define SUN6I_SPI0_TCR (spi_base(dev) + 0x08)
|
||||
#define SUN6I_SPI0_FIFO_STA (spi_base(dev) + 0x1C)
|
||||
#define SUN6I_SPI0_MBC (spi_base(dev) + 0x30)
|
||||
#define SUN6I_SPI0_MTC (spi_base(dev) + 0x34)
|
||||
#define SUN6I_SPI0_BCC (spi_base(dev) + 0x38)
|
||||
#define SUN6I_SPI0_TXD (spi_base(dev) + 0x200)
|
||||
#define SUN6I_SPI0_RXD (spi_base(dev) + 0x300)
|
||||
|
||||
#define CCM_SPI0_CLK_DIV_BY_2 (0x1000)
|
||||
#define CCM_SPI0_CLK_DIV_BY_4 (0x1001)
|
||||
#define CCM_SPI0_CLK_DIV_BY_6 (0x1002)
|
||||
#define CCM_SPI0_CLK_DIV_BY_32 (0x100f)
|
||||
|
||||
static uint32_t gpio_base(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1816: /* V536 */
|
||||
case 0x1817: /* V831 */
|
||||
case 0x1728: /* H6 */
|
||||
case 0x1823: /* H616 */
|
||||
return 0x0300B000;
|
||||
default:
|
||||
return 0x01C20800;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t spi_base(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1623: /* A10 */
|
||||
case 0x1625: /* A13 */
|
||||
case 0x1651: /* A20 */
|
||||
case 0x1663: /* F1C100s */
|
||||
case 0x1701: /* R40 */
|
||||
return 0x01C05000;
|
||||
case 0x1816: /* V536 */
|
||||
case 0x1817: /* V831 */
|
||||
case 0x1728: /* H6 */
|
||||
case 0x1823: /* H616 */
|
||||
return 0x05010000;
|
||||
default:
|
||||
return 0x01C68000;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure pin function on a GPIO port
|
||||
*/
|
||||
static void gpio_set_cfgpin(feldev_handle *dev, int port_num, int pin_num,
|
||||
int val)
|
||||
{
|
||||
uint32_t port_base = gpio_base(dev) + port_num * 0x24;
|
||||
uint32_t cfg_reg = port_base + 4 * (pin_num / 8);
|
||||
uint32_t pin_idx = pin_num % 8;
|
||||
uint32_t x = readl(cfg_reg);
|
||||
x &= ~(0x7 << (pin_idx * 4));
|
||||
x |= val << (pin_idx * 4);
|
||||
writel(x, cfg_reg);
|
||||
}
|
||||
|
||||
static bool spi_is_sun6i(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1623: /* A10 */
|
||||
case 0x1625: /* A13 */
|
||||
case 0x1651: /* A20 */
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool soc_is_h6_style(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1816: /* V536 */
|
||||
case 0x1817: /* V831 */
|
||||
case 0x1728: /* H6 */
|
||||
case 0x1823: /* H616 */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Init the SPI0 controller and setup pins muxing.
|
||||
*/
|
||||
static bool spi0_init(feldev_handle *dev)
|
||||
{
|
||||
uint32_t reg_val;
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
if (!soc_info) {
|
||||
printf("Unable to fetch device information. "
|
||||
"Possibly unknown device.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Setup SPI0 pins muxing */
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1663: /* Allwinner F1C100s/F1C600/R6/F1C100A/F1C500 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUNIV_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 1, SUNIV_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUNIV_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 3, SUNIV_GPC_SPI0);
|
||||
break;
|
||||
case 0x1625: /* Allwinner A13 */
|
||||
case 0x1680: /* Allwinner H3 */
|
||||
case 0x1681: /* Allwinner V3s */
|
||||
case 0x1718: /* Allwinner H5 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 1, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 3, SUNXI_GPC_SPI0);
|
||||
break;
|
||||
case 0x1623: /* Allwinner A10 */
|
||||
case 0x1651: /* Allwinner A20 */
|
||||
case 0x1701: /* Allwinner R40 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 1, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 23, SUNXI_GPC_SPI0);
|
||||
break;
|
||||
case 0x1689: /* Allwinner A64 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 1, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0);
|
||||
break;
|
||||
case 0x1816: /* Allwinner V536 */
|
||||
case 0x1817: /* Allwinner V831 */
|
||||
gpio_set_cfgpin(dev, PC, 1, SUN50I_GPC_SPI0); /* SPI0-CS */
|
||||
/* fall-through */
|
||||
case 0x1728: /* Allwinner H6 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0);
|
||||
/* PC5 is SPI0-CS on the H6, and SPI0-HOLD on the V831 */
|
||||
gpio_set_cfgpin(dev, PC, 5, SUN50I_GPC_SPI0);
|
||||
break;
|
||||
case 0x1823: /* Allwinner H616 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0); /* SPI0_CLK */
|
||||
gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0); /* SPI0_MOSI */
|
||||
gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0); /* SPI0_CS0 */
|
||||
gpio_set_cfgpin(dev, PC, 4, SUN50I_GPC_SPI0); /* SPI0_MISO */
|
||||
break;
|
||||
default: /* Unknown/Unsupported SoC */
|
||||
printf("SPI support not implemented yet for %x (%s)!\n",
|
||||
soc_info->soc_id, soc_info->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (soc_is_h6_style(dev)) {
|
||||
reg_val = readl(H6_CCM_SPI_BGR);
|
||||
reg_val |= H6_CCM_SPI0_GATE_RESET;
|
||||
writel(reg_val, H6_CCM_SPI_BGR);
|
||||
} else {
|
||||
if (spi_is_sun6i(dev)) {
|
||||
/* Deassert SPI0 reset */
|
||||
reg_val = readl(SUN6I_BUS_SOFT_RST_REG0);
|
||||
reg_val |= SUN6I_SPI0_RST;
|
||||
writel(reg_val, SUN6I_BUS_SOFT_RST_REG0);
|
||||
}
|
||||
|
||||
reg_val = readl(CCM_AHB_GATING0);
|
||||
reg_val |= CCM_AHB_GATE_SPI0;
|
||||
writel(reg_val, CCM_AHB_GATING0);
|
||||
}
|
||||
|
||||
if (soc_info->soc_id == 0x1663) { /* suniv F1C100s */
|
||||
/*
|
||||
* suniv doesn't have a module clock for SPI0 and the clock
|
||||
* source is always the AHB clock. Setup AHB to 200 MHz by
|
||||
* setting PLL6 to 600 MHz with a divider of 3, then program
|
||||
* the internal SPI dividier to 32.
|
||||
*/
|
||||
|
||||
/* Set PLL6 to 600MHz */
|
||||
writel(0x80041801, SUNIV_PLL6_CTL);
|
||||
/* PLL6:AHB:APB = 6:2:1 */
|
||||
writel(0x00003180, SUNIV_AHB_APB_CFG);
|
||||
/* divide by 32 */
|
||||
writel(CCM_SPI0_CLK_DIV_BY_32, SUN6I_SPI0_CCTL);
|
||||
} else {
|
||||
/* divide 24MHz OSC by 4 */
|
||||
writel(CCM_SPI0_CLK_DIV_BY_4,
|
||||
spi_is_sun6i(dev) ? SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL);
|
||||
/* Choose 24MHz from OSC24M and enable clock */
|
||||
writel(1U << 31,
|
||||
soc_is_h6_style(dev) ? H6_CCM_SPI0_CLK : CCM_SPI0_CLK);
|
||||
}
|
||||
|
||||
if (spi_is_sun6i(dev)) {
|
||||
/* Enable SPI in the master mode and do a soft reset */
|
||||
reg_val = readl(SUN6I_SPI0_GCR);
|
||||
reg_val |= (1U << 31) | 3;
|
||||
writel(reg_val, SUN6I_SPI0_GCR);
|
||||
/* Wait for completion */
|
||||
while (readl(SUN6I_SPI0_GCR) & (1U << 31)) {}
|
||||
} else {
|
||||
reg_val = readl(SUN4I_SPI0_CTL);
|
||||
reg_val |= SUN4I_CTL_MASTER;
|
||||
reg_val |= SUN4I_CTL_ENABLE | SUN4I_CTL_TF_RST | SUN4I_CTL_RF_RST;
|
||||
writel(reg_val, SUN4I_SPI0_CTL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Backup/restore the initial portion of the SRAM, which can be used as
|
||||
* a temporary data buffer.
|
||||
*/
|
||||
static void *backup_sram(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
size_t bufsize = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
void *buf = malloc(bufsize);
|
||||
aw_fel_read(dev, soc_info->spl_addr, buf, bufsize);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void restore_sram(feldev_handle *dev, void *buf)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
size_t bufsize = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
aw_fel_write(dev, buf, soc_info->spl_addr, bufsize);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void prepare_spi_batch_data_transfer(feldev_handle *dev, uint32_t buf)
|
||||
{
|
||||
if (spi_is_sun6i(dev)) {
|
||||
aw_fel_remotefunc_prepare_spi_batch_data_transfer(dev,
|
||||
buf,
|
||||
SUN6I_SPI0_TCR,
|
||||
SUN6I_TCR_XCH,
|
||||
SUN6I_SPI0_FIFO_STA,
|
||||
SUN6I_SPI0_TXD,
|
||||
SUN6I_SPI0_RXD,
|
||||
SUN6I_SPI0_MBC,
|
||||
SUN6I_SPI0_MTC,
|
||||
SUN6I_SPI0_BCC);
|
||||
} else {
|
||||
aw_fel_remotefunc_prepare_spi_batch_data_transfer(dev,
|
||||
buf,
|
||||
SUN4I_SPI0_CTL,
|
||||
SUN4I_CTL_XCH,
|
||||
SUN4I_SPI0_FIFO_STA,
|
||||
SUN4I_SPI0_TX,
|
||||
SUN4I_SPI0_RX,
|
||||
SUN4I_SPI0_BC,
|
||||
SUN4I_SPI0_TC,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from the SPI flash. Use the first 4KiB of SRAM as the data buffer.
|
||||
*/
|
||||
void aw_fel_spiflash_read(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
void *backup = backup_sram(dev);
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
size_t max_chunk_size = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
if (max_chunk_size > 0x1000)
|
||||
max_chunk_size = 0x1000;
|
||||
uint8_t *cmdbuf = malloc(max_chunk_size);
|
||||
memset(cmdbuf, 0, max_chunk_size);
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, max_chunk_size);
|
||||
|
||||
if (!spi0_init(dev))
|
||||
return;
|
||||
|
||||
prepare_spi_batch_data_transfer(dev, soc_info->spl_addr);
|
||||
|
||||
progress_start(progress, len);
|
||||
while (len > 0) {
|
||||
size_t chunk_size = len;
|
||||
if (chunk_size > max_chunk_size - 8)
|
||||
chunk_size = max_chunk_size - 8;
|
||||
|
||||
memset(cmdbuf, 0, max_chunk_size);
|
||||
cmdbuf[0] = (chunk_size + 4) >> 8;
|
||||
cmdbuf[1] = (chunk_size + 4);
|
||||
cmdbuf[2] = 3;
|
||||
cmdbuf[3] = offset >> 16;
|
||||
cmdbuf[4] = offset >> 8;
|
||||
cmdbuf[5] = offset;
|
||||
|
||||
if (chunk_size == max_chunk_size - 8)
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, 6);
|
||||
else
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, chunk_size + 8);
|
||||
aw_fel_remotefunc_execute(dev, NULL);
|
||||
aw_fel_read(dev, soc_info->spl_addr + 6, buf8, chunk_size);
|
||||
|
||||
len -= chunk_size;
|
||||
offset += chunk_size;
|
||||
buf8 += chunk_size;
|
||||
progress_update(chunk_size);
|
||||
}
|
||||
|
||||
free(cmdbuf);
|
||||
restore_sram(dev, backup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the SPI flash. Use the first 4KiB of SRAM as the data buffer.
|
||||
*/
|
||||
|
||||
#define CMD_WRITE_ENABLE 0x06
|
||||
|
||||
void aw_fel_spiflash_write_helper(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
size_t erase_size, uint8_t erase_cmd,
|
||||
size_t program_size, uint8_t program_cmd)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
size_t max_chunk_size = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
size_t cmd_idx;
|
||||
|
||||
if (max_chunk_size > 0x1000)
|
||||
max_chunk_size = 0x1000;
|
||||
uint8_t *cmdbuf = malloc(max_chunk_size);
|
||||
cmd_idx = 0;
|
||||
|
||||
prepare_spi_batch_data_transfer(dev, soc_info->spl_addr);
|
||||
|
||||
while (len > 0) {
|
||||
while (len > 0 && max_chunk_size - cmd_idx > program_size + 64) {
|
||||
if (offset % erase_size == 0) {
|
||||
/* Emit write enable command */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 1;
|
||||
cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE;
|
||||
/* Emit erase command */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 4;
|
||||
cmdbuf[cmd_idx++] = erase_cmd;
|
||||
cmdbuf[cmd_idx++] = offset >> 16;
|
||||
cmdbuf[cmd_idx++] = offset >> 8;
|
||||
cmdbuf[cmd_idx++] = offset;
|
||||
/* Emit wait for completion */
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
}
|
||||
/* Emit write enable command */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 1;
|
||||
cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE;
|
||||
/* Emit page program command */
|
||||
size_t write_count = program_size;
|
||||
if (write_count > len)
|
||||
write_count = len;
|
||||
cmdbuf[cmd_idx++] = (4 + write_count) >> 8;
|
||||
cmdbuf[cmd_idx++] = 4 + write_count;
|
||||
cmdbuf[cmd_idx++] = program_cmd;
|
||||
cmdbuf[cmd_idx++] = offset >> 16;
|
||||
cmdbuf[cmd_idx++] = offset >> 8;
|
||||
cmdbuf[cmd_idx++] = offset;
|
||||
memcpy(cmdbuf + cmd_idx, buf8, write_count);
|
||||
cmd_idx += write_count;
|
||||
buf8 += write_count;
|
||||
len -= write_count;
|
||||
offset += write_count;
|
||||
/* Emit wait for completion */
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
}
|
||||
/* Emit the end marker */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
|
||||
/* Flush */
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, cmd_idx);
|
||||
aw_fel_remotefunc_execute(dev, NULL);
|
||||
cmd_idx = 0;
|
||||
}
|
||||
|
||||
free(cmdbuf);
|
||||
}
|
||||
|
||||
void aw_fel_spiflash_write(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress)
|
||||
{
|
||||
void *backup = backup_sram(dev);
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
|
||||
spi_flash_info_t *flash_info = &default_spi_flash_info; /* FIXME */
|
||||
|
||||
if ((offset % flash_info->small_erase_size) != 0) {
|
||||
fprintf(stderr, "aw_fel_spiflash_write: 'addr' must be %d bytes aligned\n",
|
||||
flash_info->small_erase_size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!spi0_init(dev))
|
||||
return;
|
||||
|
||||
progress_start(progress, len);
|
||||
while (len > 0) {
|
||||
size_t write_count;
|
||||
if ((offset % flash_info->large_erase_size) != 0 ||
|
||||
len < flash_info->large_erase_size) {
|
||||
|
||||
write_count = flash_info->small_erase_size;
|
||||
if (write_count > len)
|
||||
write_count = len;
|
||||
aw_fel_spiflash_write_helper(dev, offset, buf8,
|
||||
write_count,
|
||||
flash_info->small_erase_size, flash_info->small_erase_cmd,
|
||||
flash_info->program_size, flash_info->program_cmd);
|
||||
} else {
|
||||
write_count = flash_info->large_erase_size;
|
||||
if (write_count > len)
|
||||
write_count = len;
|
||||
aw_fel_spiflash_write_helper(dev, offset, buf8,
|
||||
write_count,
|
||||
flash_info->large_erase_size, flash_info->large_erase_cmd,
|
||||
flash_info->program_size, flash_info->program_cmd);
|
||||
}
|
||||
|
||||
len -= write_count;
|
||||
offset += write_count;
|
||||
buf8 += write_count;
|
||||
progress_update(write_count);
|
||||
}
|
||||
|
||||
restore_sram(dev, backup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the read JEDEC ID (9Fh) command.
|
||||
*/
|
||||
void aw_fel_spiflash_info(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
const char *manufacturer;
|
||||
unsigned char buf[] = { 0, 4, 0x9F, 0, 0, 0, 0x0, 0x0 };
|
||||
void *backup = backup_sram(dev);
|
||||
|
||||
if (!spi0_init(dev))
|
||||
return;
|
||||
|
||||
aw_fel_write(dev, buf, soc_info->spl_addr, sizeof(buf));
|
||||
prepare_spi_batch_data_transfer(dev, soc_info->spl_addr);
|
||||
aw_fel_remotefunc_execute(dev, NULL);
|
||||
aw_fel_read(dev, soc_info->spl_addr, buf, sizeof(buf));
|
||||
|
||||
restore_sram(dev, backup);
|
||||
|
||||
/* Assume that the MISO pin is either pulled up or down */
|
||||
if (buf[5] == 0x00 || buf[5] == 0xFF) {
|
||||
printf("No SPI flash detected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (buf[3]) {
|
||||
case 0xEF:
|
||||
manufacturer = "Winbond";
|
||||
break;
|
||||
case 0xC2:
|
||||
manufacturer = "Macronix";
|
||||
break;
|
||||
case 0x1C:
|
||||
manufacturer = "Eon";
|
||||
break;
|
||||
default:
|
||||
manufacturer = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("Manufacturer: %s (%02Xh), model: %02Xh, size: %d bytes.\n",
|
||||
manufacturer, buf[3], buf[4], (1U << buf[5]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Show a help message about the available "spiflash-*" commands.
|
||||
*/
|
||||
void aw_fel_spiflash_help(void)
|
||||
{
|
||||
printf(" spiflash-info Retrieves basic information\n"
|
||||
" spiflash-read addr length file Write SPI flash contents into file\n"
|
||||
" spiflash-write addr file Store file contents into SPI flash\n");
|
||||
}
|
||||
34
fel-spiflash.h
Normal file
34
fel-spiflash.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SUNXI_TOOLS_FEL_SPIFLASH_H
|
||||
#define _SUNXI_TOOLS_FEL_SPIFLASH_H
|
||||
|
||||
#include "fel_lib.h"
|
||||
#include "progress.h"
|
||||
|
||||
void aw_fel_spiflash_read(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress);
|
||||
void aw_fel_spiflash_write(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress);
|
||||
void aw_fel_spiflash_info(feldev_handle *dev);
|
||||
void aw_fel_spiflash_help(void);
|
||||
void aw_fel_spi0_init(feldev_handle *dev);
|
||||
|
||||
#endif
|
||||
873
fel_lib.c
Normal file
873
fel_lib.c
Normal file
@ -0,0 +1,873 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
* Copyright (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**********************************************************************
|
||||
* USB library and helper functions for the FEL utility
|
||||
**********************************************************************/
|
||||
|
||||
#include "portable_endian.h"
|
||||
#include "fel_lib.h"
|
||||
#include <libusb.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define USB_TIMEOUT 10000 /* 10 seconds */
|
||||
|
||||
static bool fel_lib_initialized = false;
|
||||
|
||||
/* This is out 'private' data type that will be part of a "FEL device" handle */
|
||||
struct _felusb_handle {
|
||||
libusb_device_handle *handle;
|
||||
int endpoint_out, endpoint_in;
|
||||
bool iface_detached;
|
||||
bool icache_hacked;
|
||||
};
|
||||
|
||||
/* a helper function to report libusb errors */
|
||||
static void usb_error(int rc, const char *caption, int exitcode)
|
||||
{
|
||||
if (caption)
|
||||
fprintf(stderr, "%s ", caption);
|
||||
|
||||
#if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102)
|
||||
fprintf(stderr, "ERROR %d: %s\n", rc, libusb_strerror(rc));
|
||||
#else
|
||||
/* assume that libusb_strerror() is missing in the libusb API */
|
||||
fprintf(stderr, "ERROR %d\n", rc);
|
||||
#endif
|
||||
|
||||
if (exitcode != 0)
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
/*
|
||||
* AW_USB_MAX_BULK_SEND and the timeout constant USB_TIMEOUT are related.
|
||||
* Both need to be selected in a way that transferring the maximum chunk size
|
||||
* with (SoC-specific) slow transfer speed won't time out.
|
||||
*
|
||||
* The 512 KiB here are chosen based on the assumption that we want a 10 seconds
|
||||
* timeout, and "slow" transfers take place at approx. 64 KiB/sec - so we can
|
||||
* expect the maximum chunk being transmitted within 8 seconds or less.
|
||||
*/
|
||||
static const int AW_USB_MAX_BULK_SEND = 512 * 1024; /* 512 KiB per bulk request */
|
||||
|
||||
static void usb_bulk_send(libusb_device_handle *usb, int ep, const void *data,
|
||||
size_t length, bool progress)
|
||||
{
|
||||
/*
|
||||
* With no progress notifications, we'll use the maximum chunk size.
|
||||
* Otherwise, it's useful to lower the size (have more chunks) to get
|
||||
* more frequent status updates. 128 KiB per request seem suitable.
|
||||
* (Worst case of "slow" transfers -> one update every two seconds.)
|
||||
*/
|
||||
size_t max_chunk = progress ? 128 * 1024 : AW_USB_MAX_BULK_SEND;
|
||||
|
||||
size_t chunk;
|
||||
int rc, sent;
|
||||
while (length > 0) {
|
||||
chunk = length < max_chunk ? length : max_chunk;
|
||||
rc = libusb_bulk_transfer(usb, ep, (void *)data, chunk,
|
||||
&sent, USB_TIMEOUT);
|
||||
if (rc != 0)
|
||||
usb_error(rc, "usb_bulk_send()", 2);
|
||||
length -= sent;
|
||||
data += sent;
|
||||
|
||||
if (progress)
|
||||
progress_update(sent); /* notification after each chunk */
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_bulk_recv(libusb_device_handle *usb, int ep, void *data,
|
||||
int length)
|
||||
{
|
||||
int rc, recv;
|
||||
while (length > 0) {
|
||||
rc = libusb_bulk_transfer(usb, ep, data, length,
|
||||
&recv, USB_TIMEOUT);
|
||||
if (rc != 0)
|
||||
usb_error(rc, "usb_bulk_recv()", 2);
|
||||
length -= recv;
|
||||
data += recv;
|
||||
}
|
||||
}
|
||||
|
||||
struct aw_usb_request {
|
||||
char signature[8];
|
||||
uint32_t length;
|
||||
uint32_t unknown1; /* 0x0c000000 */
|
||||
uint16_t request;
|
||||
uint32_t length2; /* Same as length */
|
||||
char pad[10];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define AW_USB_READ 0x11
|
||||
#define AW_USB_WRITE 0x12
|
||||
|
||||
struct aw_fel_request {
|
||||
uint32_t request;
|
||||
uint32_t address;
|
||||
uint32_t length;
|
||||
uint32_t pad;
|
||||
};
|
||||
|
||||
/* FEL request types */
|
||||
#define AW_FEL_VERSION 0x001
|
||||
#define AW_FEL_1_WRITE 0x101
|
||||
#define AW_FEL_1_EXEC 0x102
|
||||
#define AW_FEL_1_READ 0x103
|
||||
|
||||
static void aw_send_usb_request(feldev_handle *dev, int type, int length)
|
||||
{
|
||||
struct aw_usb_request req = {
|
||||
.signature = "AWUC",
|
||||
.request = htole16(type),
|
||||
.length = htole32(length),
|
||||
.unknown1 = htole32(0x0c000000)
|
||||
};
|
||||
req.length2 = req.length;
|
||||
usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out,
|
||||
&req, sizeof(req), false);
|
||||
}
|
||||
|
||||
static void aw_read_usb_response(feldev_handle *dev)
|
||||
{
|
||||
char buf[13];
|
||||
usb_bulk_recv(dev->usb->handle, dev->usb->endpoint_in,
|
||||
buf, sizeof(buf));
|
||||
assert(strcmp(buf, "AWUS") == 0);
|
||||
}
|
||||
|
||||
static void aw_usb_write(feldev_handle *dev, const void *data, size_t len,
|
||||
bool progress)
|
||||
{
|
||||
aw_send_usb_request(dev, AW_USB_WRITE, len);
|
||||
usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out,
|
||||
data, len, progress);
|
||||
aw_read_usb_response(dev);
|
||||
}
|
||||
|
||||
static void aw_usb_read(feldev_handle *dev, void *data, size_t len)
|
||||
{
|
||||
aw_send_usb_request(dev, AW_USB_READ, len);
|
||||
usb_bulk_recv(dev->usb->handle, dev->usb->endpoint_in, data, len);
|
||||
aw_read_usb_response(dev);
|
||||
}
|
||||
|
||||
static void aw_send_fel_request(feldev_handle *dev, int type,
|
||||
uint32_t addr, uint32_t length)
|
||||
{
|
||||
struct aw_fel_request req = {
|
||||
.request = htole32(type),
|
||||
.address = htole32(addr),
|
||||
.length = htole32(length)
|
||||
};
|
||||
aw_usb_write(dev, &req, sizeof(req), false);
|
||||
}
|
||||
|
||||
static void aw_read_fel_status(feldev_handle *dev)
|
||||
{
|
||||
char buf[8];
|
||||
aw_usb_read(dev, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
/* AW_FEL_VERSION request */
|
||||
static void aw_fel_get_version(feldev_handle *dev, struct aw_fel_version *buf)
|
||||
{
|
||||
aw_send_fel_request(dev, AW_FEL_VERSION, 0, 0);
|
||||
aw_usb_read(dev, buf, sizeof(*buf));
|
||||
aw_read_fel_status(dev);
|
||||
|
||||
buf->soc_id = (le32toh(buf->soc_id) >> 8) & 0xFFFF;
|
||||
buf->unknown_0a = le32toh(buf->unknown_0a);
|
||||
buf->protocol = le32toh(buf->protocol);
|
||||
buf->scratchpad = le16toh(buf->scratchpad);
|
||||
buf->pad[0] = le32toh(buf->pad[0]);
|
||||
buf->pad[1] = le32toh(buf->pad[1]);
|
||||
}
|
||||
|
||||
/* AW_FEL_1_READ request */
|
||||
void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len)
|
||||
{
|
||||
aw_send_fel_request(dev, AW_FEL_1_READ, offset, len);
|
||||
aw_usb_read(dev, buf, len);
|
||||
aw_read_fel_status(dev);
|
||||
}
|
||||
|
||||
/* AW_FEL_1_WRITE request */
|
||||
static void aw_fel_write_raw(feldev_handle *dev, const void *buf, uint32_t offset, size_t len)
|
||||
{
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
|
||||
aw_usb_write(dev, buf, len, false);
|
||||
aw_read_fel_status(dev);
|
||||
}
|
||||
|
||||
/* AW_FEL_1_EXEC request */
|
||||
void aw_fel_execute(feldev_handle *dev, uint32_t offset)
|
||||
{
|
||||
aw_send_fel_request(dev, AW_FEL_1_EXEC, offset, 0);
|
||||
aw_read_fel_status(dev);
|
||||
}
|
||||
|
||||
static void aw_disable_icache(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
uint32_t arm_code[] = {
|
||||
/* Clear SCTLR.I */
|
||||
htole32(0xee110f10), /* mrc 15, 0, r0, cr1, cr0, {0} ;SCTLR */
|
||||
htole32(0xe3c00a01), /* bic r0, r0, #0x1000 */
|
||||
htole32(0xee010f10), /* mcr 15, 0, r0, cr1, cr0, {0} ;SCTLR */
|
||||
/* Invalidate I-Cache */
|
||||
htole32(0xee070f15), /* mcr 15, 0, r0, cr7, cr5, {0} ;ICIALLU */
|
||||
/* Barrier to force instruction refetching */
|
||||
htole32(0xf57ff06f), /* isb sy */
|
||||
htole32(0xe12fff1e), /* bx lr */
|
||||
};
|
||||
aw_fel_write_raw(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code));
|
||||
aw_fel_execute(dev, soc_info->scratch_addr);
|
||||
}
|
||||
|
||||
void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len)
|
||||
{
|
||||
if (dev->soc_info->icache_fix && !dev->usb->icache_hacked) {
|
||||
aw_disable_icache(dev);
|
||||
dev->usb->icache_hacked = true;
|
||||
}
|
||||
aw_fel_write_raw(dev, buf, offset, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is a higher-level wrapper for the FEL write functionality.
|
||||
* Unlike aw_fel_write() above - which is reserved for internal use - this
|
||||
* routine optionally allows progress callbacks.
|
||||
*/
|
||||
void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
|
||||
size_t len, bool progress)
|
||||
{
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
|
||||
aw_usb_write(dev, buf, len, progress);
|
||||
aw_read_fel_status(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't want the scratch code/buffer to exceed a maximum size of 0x400 bytes
|
||||
* (256 32-bit words) on readl_n/writel_n transfers. To guarantee this, we have
|
||||
* to account for the amount of space the ARM code uses.
|
||||
*/
|
||||
#define LCODE_ARM_WORDS 12 /* word count of the [read/write]l_n scratch code */
|
||||
#define LCODE_ARM_SIZE (LCODE_ARM_WORDS << 2) /* code size in bytes */
|
||||
#define LCODE_MAX_TOTAL 0x100 /* max. words in buffer */
|
||||
#define LCODE_MAX_WORDS (LCODE_MAX_TOTAL - LCODE_ARM_WORDS) /* data words */
|
||||
|
||||
/* multiple "readl" from sequential addresses to a destination buffer */
|
||||
static void aw_fel_readl_n(feldev_handle *dev, uint32_t addr,
|
||||
uint32_t *dst, size_t count)
|
||||
{
|
||||
if (count == 0) return;
|
||||
if (count > LCODE_MAX_WORDS) {
|
||||
fprintf(stderr,
|
||||
"ERROR: Max. word count exceeded, truncating aw_fel_readl_n() transfer\n");
|
||||
count = LCODE_MAX_WORDS;
|
||||
}
|
||||
|
||||
assert(LCODE_MAX_WORDS < 256); /* protect against corruption of ARM code */
|
||||
uint32_t arm_code[] = {
|
||||
htole32(0xe59f0020), /* ldr r0, [pc, #32] ; ldr r0,[read_addr] */
|
||||
htole32(0xe28f1024), /* add r1, pc, #36 ; adr r1, read_data */
|
||||
htole32(0xe59f201c), /* ldr r2, [pc, #28] ; ldr r2,[read_count] */
|
||||
htole32(0xe3520000 + LCODE_MAX_WORDS), /* cmp r2, #LCODE_MAX_WORDS */
|
||||
htole32(0xc3a02000 + LCODE_MAX_WORDS), /* movgt r2, #LCODE_MAX_WORDS */
|
||||
/* read_loop: */
|
||||
htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */
|
||||
htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */
|
||||
htole32(0xe4903004), /* ldr r3, [r0], #4 ; load and post-inc */
|
||||
htole32(0xe4813004), /* str r3, [r1], #4 ; store and post-inc */
|
||||
htole32(0xeafffffa), /* b read_loop */
|
||||
htole32(addr), /* read_addr */
|
||||
htole32(count) /* read_count */
|
||||
/* read_data (buffer) follows, i.e. values go here */
|
||||
};
|
||||
assert(sizeof(arm_code) == LCODE_ARM_SIZE);
|
||||
|
||||
/* scratch buffer setup: transfers ARM code, including addr and count */
|
||||
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code));
|
||||
/* execute code, read back the result */
|
||||
aw_fel_execute(dev, dev->soc_info->scratch_addr);
|
||||
uint32_t buffer[count];
|
||||
aw_fel_read(dev, dev->soc_info->scratch_addr + LCODE_ARM_SIZE,
|
||||
buffer, sizeof(buffer));
|
||||
/* extract values to destination buffer */
|
||||
uint32_t *val = buffer;
|
||||
while (count-- > 0)
|
||||
*dst++ = le32toh(*val++);
|
||||
}
|
||||
|
||||
/*
|
||||
* aw_fel_readl_n() wrapper that can handle large transfers. If necessary,
|
||||
* those will be done in separate 'chunks' of no more than LCODE_MAX_WORDS.
|
||||
*/
|
||||
void fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count)
|
||||
{
|
||||
while (count > 0) {
|
||||
size_t n = count > LCODE_MAX_WORDS ? LCODE_MAX_WORDS : count;
|
||||
aw_fel_readl_n(dev, addr, dst, n);
|
||||
addr += n * sizeof(uint32_t);
|
||||
dst += n;
|
||||
count -= n;
|
||||
}
|
||||
}
|
||||
|
||||
/* multiple "writel" from a source buffer to sequential addresses */
|
||||
static void aw_fel_writel_n(feldev_handle *dev, uint32_t addr,
|
||||
uint32_t *src, size_t count)
|
||||
{
|
||||
if (count == 0) return;
|
||||
if (count > LCODE_MAX_WORDS) {
|
||||
fprintf(stderr,
|
||||
"ERROR: Max. word count exceeded, truncating aw_fel_writel_n() transfer\n");
|
||||
count = LCODE_MAX_WORDS;
|
||||
}
|
||||
|
||||
assert(LCODE_MAX_WORDS < 256); /* protect against corruption of ARM code */
|
||||
/*
|
||||
* We need a fixed array size to allow for (partial) initialization,
|
||||
* so we'll claim the maximum total number of words (0x100) here.
|
||||
*/
|
||||
uint32_t arm_code[LCODE_MAX_TOTAL] = {
|
||||
htole32(0xe59f0020), /* ldr r0, [pc, #32] ; ldr r0,[write_addr] */
|
||||
htole32(0xe28f1024), /* add r1, pc, #36 ; adr r1, write_data */
|
||||
htole32(0xe59f201c), /* ldr r2, [pc, #28] ; ldr r2,[write_count]*/
|
||||
htole32(0xe3520000 + LCODE_MAX_WORDS), /* cmp r2, #LCODE_MAX_WORDS */
|
||||
htole32(0xc3a02000 + LCODE_MAX_WORDS), /* movgt r2, #LCODE_MAX_WORDS */
|
||||
/* write_loop: */
|
||||
htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */
|
||||
htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */
|
||||
htole32(0xe4913004), /* ldr r3, [r1], #4 ; load and post-inc */
|
||||
htole32(0xe4803004), /* str r3, [r0], #4 ; store and post-inc */
|
||||
htole32(0xeafffffa), /* b write_loop */
|
||||
htole32(addr), /* write_addr */
|
||||
htole32(count) /* write_count */
|
||||
/* write_data (buffer) follows, i.e. values taken from here */
|
||||
};
|
||||
|
||||
/* copy values from source buffer */
|
||||
size_t i;
|
||||
for (i = 0; i < count; i++)
|
||||
arm_code[LCODE_ARM_WORDS + i] = htole32(*src++);
|
||||
/* scratch buffer setup: transfers ARM code and data */
|
||||
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr,
|
||||
(LCODE_ARM_WORDS + count) * sizeof(uint32_t));
|
||||
/* execute, and we're done */
|
||||
aw_fel_execute(dev, dev->soc_info->scratch_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* aw_fel_writel_n() wrapper that can handle large transfers. If necessary,
|
||||
* those will be done in separate 'chunks' of no more than LCODE_MAX_WORDS.
|
||||
*/
|
||||
void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count)
|
||||
{
|
||||
while (count > 0) {
|
||||
size_t n = count > LCODE_MAX_WORDS ? LCODE_MAX_WORDS : count;
|
||||
aw_fel_writel_n(dev, addr, src, n);
|
||||
addr += n * sizeof(uint32_t);
|
||||
src += n;
|
||||
count -= n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* move (arbitrary byte count) data between addresses within SoC memory
|
||||
*
|
||||
* These functions try to copy as many bytes as possible using 32-bit word
|
||||
* transfers, and handle any unaligned bytes ('head' and 'tail') separately.
|
||||
*
|
||||
* This is useful for the same reasons that "readl"/"writel" were introduced:
|
||||
* Byte-oriented transfers ("string" copy) might not give the expected results
|
||||
* when accessing hardware registers, like e.g. the (G)PIO config/state.
|
||||
*
|
||||
* We have two different low-level functions, where the copy operation moves
|
||||
* upwards or downwards respectively. This allows a non-destructive "memmove"
|
||||
* wrapper to select the suitable one in case of memory overlap.
|
||||
*/
|
||||
|
||||
static void fel_memcpy_up(feldev_handle *dev,
|
||||
uint32_t dst_addr, uint32_t src_addr, size_t size)
|
||||
{
|
||||
if (size == 0) return;
|
||||
/*
|
||||
* copy "upwards", increasing destination and source addresses
|
||||
*/
|
||||
uint32_t arm_code[] = {
|
||||
htole32(0xe59f0054), /* ldr r0, [pc, #84] ; ldr r0, [dst_addr] */
|
||||
htole32(0xe59f1054), /* ldr r1, [pc, #84] ; ldr r1, [src_addr] */
|
||||
htole32(0xe59f2054), /* ldr r2, [pc, #84] ; ldr r2, [size] */
|
||||
htole32(0xe0413000), /* sub r3, r1, r0 ; r3 = r1 - r0 */
|
||||
htole32(0xe3130003), /* tst r3, #3 ; test lower bits */
|
||||
htole32(0x1a00000b), /* bne copyup_tail ; unaligned copying */
|
||||
/* copyup_head: */
|
||||
htole32(0xe3110003), /* tst r1, #3 ; word-aligned? */
|
||||
htole32(0x0a000004), /* beq copyup_loop */
|
||||
htole32(0xe4d13001), /* ldrb r3, [r1], #1 ; load and post-inc */
|
||||
htole32(0xe4c03001), /* strb r3, [r0], #1 ; store and post-inc */
|
||||
htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */
|
||||
htole32(0x5afffff9), /* bpl copyup_head ; while (r2 >= 0) */
|
||||
htole32(0xe12fff1e), /* bx lr ; early return */
|
||||
/* copyup_loop: */
|
||||
htole32(0xe2522004), /* subs r2, r2, #4 ; r2 -= 4 */
|
||||
htole32(0x54913004), /* ldrpl r3, [r1], #4 ; load and post-inc */
|
||||
htole32(0x54803004), /* strpl r3, [r0], #4 ; store and post-inc */
|
||||
htole32(0x5afffffb), /* bpl copyup_loop ; while (r2 >= 0) */
|
||||
htole32(0xe2822004), /* add r2, r2, #4 ; remaining bytes */
|
||||
/* copyup_tail: */
|
||||
htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */
|
||||
htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */
|
||||
htole32(0xe4d13001), /* ldrb r3, [r1], #1 ; load and post-inc */
|
||||
htole32(0xe4c03001), /* strb r3, [r0], #1 ; store and post-inc */
|
||||
htole32(0xeafffffa), /* b copyup_tail */
|
||||
|
||||
htole32(dst_addr), /* destination address */
|
||||
htole32(src_addr), /* source address */
|
||||
htole32(size), /* size (= byte count) */
|
||||
};
|
||||
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code));
|
||||
aw_fel_execute(dev, dev->soc_info->scratch_addr);
|
||||
}
|
||||
|
||||
static void fel_memcpy_down(feldev_handle *dev,
|
||||
uint32_t dst_addr, uint32_t src_addr, size_t size)
|
||||
{
|
||||
if (size == 0) return;
|
||||
/*
|
||||
* This ARM code makes use of decreasing values in r2
|
||||
* for memory indexing relative to the base addresses in r0 and r1.
|
||||
*/
|
||||
uint32_t arm_code[] = {
|
||||
htole32(0xe59f0058), /* ldr r0, [pc, #88] ; ldr r0, [dst_addr] */
|
||||
htole32(0xe59f1058), /* ldr r1, [pc, #88] ; ldr r1, [src_addr] */
|
||||
htole32(0xe59f2058), /* ldr r2, [pc, #88] ; ldr r2, [size] */
|
||||
htole32(0xe0403001), /* sub r3, r0, r1 ; r3 = r0 - r1 */
|
||||
htole32(0xe3130003), /* tst r3, #3 ; test lower bits */
|
||||
htole32(0x1a00000c), /* bne copydn_tail ; unaligned copying */
|
||||
/* copydn_head: */
|
||||
htole32(0xe0813002), /* add r3, r1, r2 ; r3 = r1 + r2 */
|
||||
htole32(0xe3130003), /* tst r3, #3 ; word-aligned? */
|
||||
htole32(0x0a000004), /* beq copydn_loop */
|
||||
htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */
|
||||
htole32(0x412fff1e), /* bxmi lr ; early return */
|
||||
htole32(0xe7d13002), /* ldrb r3, [r1, r2] ; load byte */
|
||||
htole32(0xe7c03002), /* strb r3, [r0, r2] ; store byte */
|
||||
htole32(0xeafffff7), /* b copydn_head */
|
||||
/* copydn_loop: */
|
||||
htole32(0xe2522004), /* subs r2, r2, #4 ; r2 -= 4 */
|
||||
htole32(0x57913002), /* ldrpl r3, [r1, r2] ; load word */
|
||||
htole32(0x57803002), /* strpl r3, [r0, r2] ; store word */
|
||||
htole32(0x5afffffb), /* bpl copydn_loop ; while (r2 >= 0) */
|
||||
htole32(0xe2822004), /* add r2, r2, #4 ; remaining bytes */
|
||||
/* copydn_tail: */
|
||||
htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */
|
||||
htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */
|
||||
htole32(0xe7d13002), /* ldrb r3, [r1, r2] ; load byte */
|
||||
htole32(0xe7c03002), /* strb r3, [r0, r2] ; store byte */
|
||||
htole32(0xeafffffa), /* b copydn_tail */
|
||||
|
||||
htole32(dst_addr), /* destination address */
|
||||
htole32(src_addr), /* source address */
|
||||
htole32(size), /* size (= byte count) */
|
||||
};
|
||||
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code));
|
||||
aw_fel_execute(dev, dev->soc_info->scratch_addr);
|
||||
}
|
||||
|
||||
void fel_memmove(feldev_handle *dev,
|
||||
uint32_t dst_addr, uint32_t src_addr, size_t size)
|
||||
{
|
||||
/*
|
||||
* To ensure non-destructive operation, we need to select "downwards"
|
||||
* copying if the destination overlaps the source region.
|
||||
*/
|
||||
if (dst_addr >= src_addr && dst_addr < (src_addr + size))
|
||||
fel_memcpy_down(dev, dst_addr, src_addr, size);
|
||||
else
|
||||
fel_memcpy_up(dev, dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitwise manipulation of a 32-bit word at given address, via bit masks that
|
||||
* specify which bits to clear and which to set.
|
||||
*/
|
||||
void fel_clrsetbits_le32(feldev_handle *dev,
|
||||
uint32_t addr, uint32_t clrbits, uint32_t setbits)
|
||||
{
|
||||
uint32_t arm_code[] = {
|
||||
htole32(0xe59f0018), /* 0: ldr r0, [addr] */
|
||||
htole32(0xe5901000), /* 4: ldr r1, [r0] */
|
||||
htole32(0xe59f2014), /* 8: ldr r2, [clrbits] */
|
||||
htole32(0xe1c11002), /* c: bic r1, r1, r2 */
|
||||
htole32(0xe59f2010), /* 10: ldr r2, [setbits] */
|
||||
htole32(0xe1811002), /* 14: orr r1, r1, r2 */
|
||||
htole32(0xe5801000), /* 18: str r1, [r0] */
|
||||
htole32(0xe12fff1e), /* 1c: bx lr */
|
||||
|
||||
htole32(addr), /* address */
|
||||
htole32(clrbits), /* bits to clear */
|
||||
htole32(setbits), /* bits to set */
|
||||
};
|
||||
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code));
|
||||
aw_fel_execute(dev, dev->soc_info->scratch_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory access to the SID (root) keys proved to be unreliable for certain
|
||||
* SoCs. This function uses an alternative, register-based approach to retrieve
|
||||
* the values.
|
||||
*/
|
||||
static void fel_get_sid_registers(feldev_handle *dev, uint32_t *result,
|
||||
uint32_t offset, uint32_t length)
|
||||
{
|
||||
uint32_t arm_code[] = {
|
||||
/* <sid_read_root_key>: */
|
||||
htole32(0xe59f0044), /* 0: ldr r0, [pc, #68] */
|
||||
htole32(0xe59f1044), /* 4: ldr r1, [pc, #68] */
|
||||
htole32(0xe28f3048), /* 8: add r3, pc, #72 */
|
||||
/* <sid_read_loop>: */
|
||||
htole32(0xe1a02801), /* c: lsl r2, r1, #16 */
|
||||
htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */
|
||||
htole32(0xe3822002), /* 14: orr r2, r2, #2 */
|
||||
htole32(0xe5802040), /* 18: str r2, [r0, #64] */
|
||||
/* <sid_read_wait>: */
|
||||
htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */
|
||||
htole32(0xe3120002), /* 20: tst r2, #2 */
|
||||
htole32(0x1afffffc), /* 24: bne 1c <sid_read_wait> */
|
||||
htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */
|
||||
htole32(0xe4832004), /* 2c: str r2, [r3], #4 */
|
||||
htole32(0xe2811004), /* 30: add r1, r1, #4 */
|
||||
htole32(0xe59f2018), /* 34: ldr r2, [pc, #24] */
|
||||
htole32(0xe1510002), /* 38: cmp r1, r2 */
|
||||
htole32(0x3afffff2), /* 3c: bcc c <sid_read_loop> */
|
||||
htole32(0xe3a02000), /* 40: mov r2, #0 */
|
||||
htole32(0xe5802040), /* 44: str r2, [r0, #64] */
|
||||
htole32(0xe12fff1e), /* 48: bx lr */
|
||||
/* <sid_base>: */
|
||||
htole32(dev->soc_info->sid_base), /* SID base addr */
|
||||
/* <offset>: */
|
||||
htole32(offset), /* first word to read */
|
||||
/* <end>: */
|
||||
htole32(offset + length), /* where to stop to read */
|
||||
/* retrieved SID values go here */
|
||||
};
|
||||
/* write and execute code */
|
||||
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code));
|
||||
aw_fel_execute(dev, dev->soc_info->scratch_addr);
|
||||
/* read back the result */
|
||||
aw_fel_read(dev, dev->soc_info->scratch_addr + sizeof(arm_code),
|
||||
result, length);
|
||||
for (unsigned i = 0; i < length / 4; i++)
|
||||
result[i] = le32toh(result[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* fel_read_sid() - Read the content of the SID eFuses.
|
||||
* @dev: device handle for the FEL device
|
||||
* @result: pointer of a buffer receiving the content of the eFuses
|
||||
* @offset: beginning of the eFuses area to read, in bytes
|
||||
* @length: length of the eFuses area to read, in bytes
|
||||
* @force_workaround: whether to use the MMIO register based read method
|
||||
*
|
||||
* Read the contents of the non-volatile eFuses stored in the SoC. The size
|
||||
* and supposed usage layout differs between SoCs, but the "root" key
|
||||
* (containing some unique serial number) is always in the first 128 bits.
|
||||
*
|
||||
* Return: 0 if the operation was successful, a negative error code otherwise.
|
||||
*/
|
||||
int fel_read_sid(feldev_handle *dev, uint32_t *result,
|
||||
unsigned int offset, unsigned int length,
|
||||
bool force_workaround)
|
||||
{
|
||||
const soc_info_t *soc = dev->soc_info;
|
||||
|
||||
if (!soc->sid_base) /* SID unavailable */
|
||||
return -2;
|
||||
if ((offset & 3) || (length & 3)) /* needs to be 32-bit aligned */
|
||||
return -3;
|
||||
|
||||
if (soc->sid_fix || force_workaround)
|
||||
/* Work around SID issues by using ARM thunk code */
|
||||
fel_get_sid_registers(dev, result, offset, length);
|
||||
else
|
||||
/* Read SID directly from memory */
|
||||
fel_readl_n(dev, soc->sid_base + soc->sid_offset + offset,
|
||||
result, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* general functions, "FEL device" management */
|
||||
|
||||
static int feldev_get_endpoint(feldev_handle *dev)
|
||||
{
|
||||
struct libusb_device *usb = libusb_get_device(dev->usb->handle);
|
||||
struct libusb_config_descriptor *config;
|
||||
int if_idx, set_idx, ep_idx, ret;
|
||||
const struct libusb_interface *iface;
|
||||
const struct libusb_interface_descriptor *setting;
|
||||
const struct libusb_endpoint_descriptor *ep;
|
||||
|
||||
ret = libusb_get_active_config_descriptor(usb, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (if_idx = 0; if_idx < config->bNumInterfaces; if_idx++) {
|
||||
iface = config->interface + if_idx;
|
||||
|
||||
for (set_idx = 0; set_idx < iface->num_altsetting; set_idx++) {
|
||||
setting = iface->altsetting + set_idx;
|
||||
|
||||
for (ep_idx = 0; ep_idx < setting->bNumEndpoints; ep_idx++) {
|
||||
ep = setting->endpoint + ep_idx;
|
||||
|
||||
/* Test for bulk transfer endpoint */
|
||||
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
|
||||
!= LIBUSB_TRANSFER_TYPE_BULK)
|
||||
continue;
|
||||
|
||||
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
|
||||
== LIBUSB_ENDPOINT_IN)
|
||||
dev->usb->endpoint_in = ep->bEndpointAddress;
|
||||
else
|
||||
dev->usb->endpoint_out = ep->bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_config_descriptor(config);
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
/* claim USB interface associated with the libusb handle for a FEL device */
|
||||
static void feldev_claim(feldev_handle *dev)
|
||||
{
|
||||
int rc = libusb_claim_interface(dev->usb->handle, 0);
|
||||
#if defined(__linux__)
|
||||
if (rc != LIBUSB_SUCCESS) {
|
||||
libusb_detach_kernel_driver(dev->usb->handle, 0);
|
||||
dev->usb->iface_detached = true;
|
||||
rc = libusb_claim_interface(dev->usb->handle, 0);
|
||||
}
|
||||
#endif
|
||||
if (rc)
|
||||
usb_error(rc, "libusb_claim_interface()", 1);
|
||||
|
||||
rc = feldev_get_endpoint(dev);
|
||||
if (rc)
|
||||
usb_error(rc, "FAILED to get FEL mode endpoint addresses!", 1);
|
||||
}
|
||||
|
||||
/* release USB interface associated with the libusb handle for a FEL device */
|
||||
static void feldev_release(feldev_handle *dev)
|
||||
{
|
||||
libusb_release_interface(dev->usb->handle, 0);
|
||||
#if defined(__linux__)
|
||||
if (dev->usb->iface_detached)
|
||||
libusb_attach_kernel_driver(dev->usb->handle, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* open handle to desired FEL device */
|
||||
feldev_handle *feldev_open(int busnum, int devnum,
|
||||
uint16_t vendor_id, uint16_t product_id)
|
||||
{
|
||||
if (!fel_lib_initialized) /* if not already done: auto-initialize */
|
||||
feldev_init();
|
||||
feldev_handle *result = calloc(1, sizeof(feldev_handle));
|
||||
if (!result) {
|
||||
fprintf(stderr, "FAILED to allocate feldev_handle memory.\n");
|
||||
exit(1);
|
||||
}
|
||||
result->usb = calloc(1, sizeof(felusb_handle));
|
||||
if (!result->usb) {
|
||||
fprintf(stderr, "FAILED to allocate felusb_handle memory.\n");
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (busnum < 0 || devnum < 0) {
|
||||
/* With the default values (busnum -1, devnum -1) we don't care
|
||||
* for a specific USB device; so let libusb open the first
|
||||
* device that matches VID/PID.
|
||||
*/
|
||||
result->usb->handle = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id);
|
||||
if (!result->usb->handle) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
fprintf(stderr, "ERROR: You don't have permission to access Allwinner USB FEL device\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "ERROR: Allwinner USB FEL device not found!\n");
|
||||
break;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
/* look for specific bus and device number */
|
||||
bool found = false;
|
||||
ssize_t rc, i;
|
||||
libusb_device **list;
|
||||
|
||||
rc = libusb_get_device_list(NULL, &list);
|
||||
if (rc < 0)
|
||||
usb_error(rc, "libusb_get_device_list()", 1);
|
||||
for (i = 0; i < rc; i++) {
|
||||
if (libusb_get_bus_number(list[i]) == busnum
|
||||
&& libusb_get_device_address(list[i]) == devnum) {
|
||||
found = true; /* bus:devnum matched */
|
||||
struct libusb_device_descriptor desc;
|
||||
libusb_get_device_descriptor(list[i], &desc);
|
||||
if (desc.idVendor != vendor_id
|
||||
|| desc.idProduct != product_id) {
|
||||
fprintf(stderr, "ERROR: Bus %03d Device %03d not a FEL device "
|
||||
"(expected %04x:%04x, got %04x:%04x)\n", busnum, devnum,
|
||||
vendor_id, product_id, desc.idVendor, desc.idProduct);
|
||||
exit(1);
|
||||
}
|
||||
/* open handle to this specific device (incrementing its refcount) */
|
||||
rc = libusb_open(list[i], &result->usb->handle);
|
||||
if (rc != 0)
|
||||
usb_error(rc, "libusb_open()", 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(list, true);
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "ERROR: Bus %03d Device %03d not found in libusb device list\n",
|
||||
busnum, devnum);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
feldev_claim(result); /* claim interface, detect USB endpoints */
|
||||
|
||||
/* retrieve BROM version and SoC information */
|
||||
aw_fel_get_version(result, &result->soc_version);
|
||||
get_soc_name_from_id(result->soc_name, result->soc_version.soc_id);
|
||||
result->soc_info = get_soc_info_from_version(&result->soc_version);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* close FEL device (optional, dev may be NULL) */
|
||||
void feldev_close(feldev_handle *dev)
|
||||
{
|
||||
if (dev) {
|
||||
if (dev->usb->handle) {
|
||||
feldev_release(dev);
|
||||
libusb_close(dev->usb->handle);
|
||||
}
|
||||
free(dev->usb); /* release memory allocated for felusb_handle */
|
||||
}
|
||||
}
|
||||
|
||||
void feldev_init(void)
|
||||
{
|
||||
int rc = libusb_init(NULL);
|
||||
if (rc != 0)
|
||||
usb_error(rc, "libusb_init()", 1);
|
||||
fel_lib_initialized = true;
|
||||
}
|
||||
|
||||
void feldev_done(feldev_handle *dev)
|
||||
{
|
||||
feldev_close(dev);
|
||||
free(dev);
|
||||
if (fel_lib_initialized) libusb_exit(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate (all) FEL devices. Allocates a list (array of feldev_list_entry)
|
||||
* and optionally returns the number of elements via "count". You may
|
||||
* alternatively detect the end of the list by checking the entry's soc_version
|
||||
* for a zero ID.
|
||||
* It's your responsibility to call free() on the result later.
|
||||
*/
|
||||
feldev_list_entry *list_fel_devices(size_t *count)
|
||||
{
|
||||
feldev_list_entry *list, *entry;
|
||||
ssize_t rc, i;
|
||||
libusb_context *ctx;
|
||||
libusb_device **usb;
|
||||
struct libusb_device_descriptor desc;
|
||||
feldev_handle *dev;
|
||||
size_t devices = 0;
|
||||
|
||||
libusb_init(&ctx);
|
||||
rc = libusb_get_device_list(ctx, &usb);
|
||||
if (rc < 0)
|
||||
usb_error(rc, "libusb_get_device_list()", 1);
|
||||
|
||||
/*
|
||||
* Size our array to hold entries for every USB device,
|
||||
* plus an empty one at the end (for list termination).
|
||||
*/
|
||||
list = calloc(rc + 1, sizeof(feldev_list_entry));
|
||||
if (!list) {
|
||||
fprintf(stderr, "list_fel_devices() FAILED to allocate list memory.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < rc; i++) {
|
||||
libusb_get_device_descriptor(usb[i], &desc);
|
||||
if (desc.idVendor != AW_USB_VENDOR_ID
|
||||
|| desc.idProduct != AW_USB_PRODUCT_ID)
|
||||
continue; /* not an Allwinner FEL device */
|
||||
|
||||
entry = list + devices; /* pointer to current feldev_list_entry */
|
||||
devices += 1;
|
||||
|
||||
entry->busnum = libusb_get_bus_number(usb[i]);
|
||||
entry->devnum = libusb_get_device_address(usb[i]);
|
||||
dev = feldev_open(entry->busnum, entry->devnum,
|
||||
AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID);
|
||||
|
||||
/* copy relevant fields */
|
||||
entry->soc_version = dev->soc_version;
|
||||
entry->soc_info = dev->soc_info;
|
||||
strncpy(entry->soc_name, dev->soc_name, sizeof(soc_name_t));
|
||||
|
||||
/* retrieve SID bits */
|
||||
fel_read_sid(dev, entry->SID, 0, 16, false);
|
||||
|
||||
feldev_close(dev);
|
||||
free(dev);
|
||||
}
|
||||
libusb_free_device_list(usb, true);
|
||||
libusb_exit(ctx);
|
||||
|
||||
if (count) *count = devices;
|
||||
return list;
|
||||
}
|
||||
92
fel_lib.h
Normal file
92
fel_lib.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_FEL_LIB_H
|
||||
#define _SUNXI_TOOLS_FEL_LIB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "progress.h"
|
||||
#include "soc_info.h"
|
||||
|
||||
/* USB identifiers for Allwinner device in FEL mode */
|
||||
#define AW_USB_VENDOR_ID 0x1F3A
|
||||
#define AW_USB_PRODUCT_ID 0xEFE8
|
||||
|
||||
typedef struct _felusb_handle felusb_handle; /* opaque data type */
|
||||
|
||||
/* More general FEL "device" handle, including version data and SoC info */
|
||||
typedef struct {
|
||||
felusb_handle *usb;
|
||||
struct aw_fel_version soc_version;
|
||||
soc_name_t soc_name;
|
||||
soc_info_t *soc_info;
|
||||
} feldev_handle;
|
||||
|
||||
/* list_fel_devices() will return an array of this type */
|
||||
typedef struct {
|
||||
int busnum, devnum;
|
||||
struct aw_fel_version soc_version;
|
||||
soc_name_t soc_name;
|
||||
soc_info_t *soc_info;
|
||||
uint32_t SID[4];
|
||||
} feldev_list_entry;
|
||||
|
||||
/* FEL device management */
|
||||
|
||||
void feldev_init(void);
|
||||
void feldev_done(feldev_handle *dev);
|
||||
|
||||
feldev_handle *feldev_open(int busnum, int devnum,
|
||||
uint16_t vendor_id, uint16_t product_id);
|
||||
void feldev_close(feldev_handle *dev);
|
||||
|
||||
feldev_list_entry *list_fel_devices(size_t *count);
|
||||
|
||||
/* FEL functions */
|
||||
|
||||
void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len);
|
||||
void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len);
|
||||
void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
|
||||
size_t len, bool progress);
|
||||
void aw_fel_execute(feldev_handle *dev, uint32_t offset);
|
||||
|
||||
void fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count);
|
||||
void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count);
|
||||
|
||||
void fel_memmove(feldev_handle *dev,
|
||||
uint32_t dst_addr, uint32_t src_addr, size_t size);
|
||||
|
||||
void fel_clrsetbits_le32(feldev_handle *dev,
|
||||
uint32_t addr, uint32_t clrbits, uint32_t setbits);
|
||||
#define fel_clrbits_le32(dev, addr, value) \
|
||||
fel_clrsetbits_le32(dev, addr, value, 0)
|
||||
#define fel_setbits_le32(dev, addr, value) \
|
||||
fel_clrsetbits_le32(dev, addr, 0, value)
|
||||
|
||||
int fel_read_sid(feldev_handle *dev, uint32_t *result,
|
||||
unsigned int offset, unsigned int length,
|
||||
bool force_workaround);
|
||||
|
||||
bool aw_fel_remotefunc_prepare(feldev_handle *dev,
|
||||
size_t stack_size,
|
||||
void *arm_code,
|
||||
size_t arm_code_size,
|
||||
size_t num_args,
|
||||
uint32_t *args);
|
||||
bool aw_fel_remotefunc_execute(feldev_handle *dev, uint32_t *result);
|
||||
|
||||
#endif /* _SUNXI_TOOLS_FEL_LIB_H */
|
||||
338
fexc.c
Normal file
338
fexc.c
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fexc.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef NO_MMAP
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define pr_info(...) pr_error("fexc: " __VA_ARGS__)
|
||||
#define pr_err(...) pr_error("E: fexc: " __VA_ARGS__)
|
||||
|
||||
enum script_format {
|
||||
FEX_SCRIPT_FORMAT,
|
||||
BIN_SCRIPT_FORMAT,
|
||||
UBOOT_HEADER_FORMAT,
|
||||
};
|
||||
|
||||
/*
|
||||
*/
|
||||
static inline char *read_all(int fd, const char *filename, size_t *size)
|
||||
{
|
||||
size_t buf_size = 4096, count = 0;
|
||||
char *p, *buf = malloc(buf_size);
|
||||
if (!buf) {
|
||||
pr_err("%s: %s\n", "malloc", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
p = buf;
|
||||
while (1) {
|
||||
ssize_t rc = read(fd, p, buf_size-count);
|
||||
if (rc == 0)
|
||||
break;
|
||||
else if (rc > 0) {
|
||||
count += rc;
|
||||
p += rc;
|
||||
|
||||
if (count == buf_size) {
|
||||
char *new;
|
||||
buf_size *= 2;
|
||||
new = realloc(buf, buf_size);
|
||||
if (!new) {
|
||||
pr_err("%s: %s\n", "realloc",
|
||||
strerror(errno));
|
||||
free(buf);
|
||||
return NULL;
|
||||
} else if (new != buf) {
|
||||
buf = new;
|
||||
p = buf + count;
|
||||
}
|
||||
}
|
||||
} else if (errno != EAGAIN && errno != EINTR) {
|
||||
pr_err("%s: %s: %s\n", filename,
|
||||
"read", strerror(errno));
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*size = count;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static inline int script_parse(enum script_format format,
|
||||
const char *filename,
|
||||
struct script *script)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (format) {
|
||||
case FEX_SCRIPT_FORMAT: {
|
||||
FILE *in = stdin;
|
||||
if (!filename)
|
||||
filename = "<stdin>";
|
||||
else if ((in = fopen(filename, "r")) == NULL) {
|
||||
pr_err("%s: %s\n", filename, strerror(errno));
|
||||
break;
|
||||
}
|
||||
ret = script_parse_fex(in, filename, script);
|
||||
fclose(in);
|
||||
}; break;
|
||||
case BIN_SCRIPT_FORMAT: {
|
||||
int in = 0; /* stdin */
|
||||
struct stat sb;
|
||||
void *bin = NULL;
|
||||
size_t bin_size;
|
||||
int allocated = 1;
|
||||
|
||||
if (!filename)
|
||||
filename = "<stdin>";
|
||||
else if ((in = open(filename, O_RDONLY)) < 0) {
|
||||
pr_err("%s: %s\n", filename, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
if (fstat(in, &sb) == -1) {
|
||||
pr_err("%s: %s: %s\n", filename,
|
||||
"fstat", strerror(errno));
|
||||
goto bin_close;
|
||||
#ifndef NO_MMAP
|
||||
} else if (S_ISREG(sb.st_mode)) {
|
||||
/* regular file, mmap it */
|
||||
bin = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
|
||||
if (bin == MAP_FAILED) {
|
||||
pr_err("%s: %s: %s\n", filename,
|
||||
"mmap", strerror(errno));
|
||||
goto bin_close;
|
||||
}
|
||||
bin_size = sb.st_size;
|
||||
allocated = 0;
|
||||
#endif
|
||||
} else {
|
||||
/* something else... just read it all! */
|
||||
bin = read_all(in, filename, &bin_size);
|
||||
if (bin == NULL)
|
||||
goto bin_close;
|
||||
allocated = 1;
|
||||
}
|
||||
|
||||
ret = script_decompile_bin(bin, bin_size, filename, script);
|
||||
if (allocated)
|
||||
free(bin);
|
||||
#ifndef NO_MMAP
|
||||
else if (munmap(bin, bin_size) == -1) {
|
||||
pr_err("%s: %s: %s\n", filename,
|
||||
"munmap", strerror(errno));
|
||||
}
|
||||
#endif
|
||||
bin_close:
|
||||
close(in);
|
||||
}; break;
|
||||
case UBOOT_HEADER_FORMAT: /* not valid input */
|
||||
;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static inline int script_generate(enum script_format format,
|
||||
const char *filename,
|
||||
struct script *script)
|
||||
{
|
||||
int ret = 0;
|
||||
static int (*text_gen[3]) (FILE *, const char *, struct script *) = {
|
||||
[FEX_SCRIPT_FORMAT] = script_generate_fex,
|
||||
[UBOOT_HEADER_FORMAT] = script_generate_uboot,
|
||||
};
|
||||
|
||||
if (text_gen[format]) {
|
||||
FILE *out = stdout;
|
||||
|
||||
if (!filename)
|
||||
filename = "<stdout>";
|
||||
else if ((out = fopen(filename, "w")) == NULL) {
|
||||
pr_err("%s: %s\n", filename, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = text_gen[format](out, filename, script);
|
||||
fclose(out);
|
||||
} else {
|
||||
int out = 1; /* stdout */
|
||||
size_t sections, entries, bin_size;
|
||||
void *bin;
|
||||
|
||||
if (!filename)
|
||||
filename = "<stdout>";
|
||||
else if ((out = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
|
||||
pr_err("%s: %s\n", filename, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
bin_size = script_bin_size(script, §ions, &entries);
|
||||
bin = calloc(1, bin_size);
|
||||
if (!bin)
|
||||
pr_err("%s: %s\n", "malloc", strerror(errno));
|
||||
else if (script_generate_bin(bin, bin_size, script, sections, entries)) {
|
||||
char *p = bin;
|
||||
while(bin_size) {
|
||||
ssize_t wc = write(out, p, bin_size);
|
||||
|
||||
if (wc>0) {
|
||||
p += wc;
|
||||
bin_size -= wc;
|
||||
} else if (wc < 0 && errno != EINTR) {
|
||||
pr_err("%s: %s: %s\n", filename,
|
||||
"write", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = (bin_size == 0);
|
||||
}
|
||||
free(bin);
|
||||
close(out);
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static inline void app_usage(const char *arg0, int mode)
|
||||
{
|
||||
fputs("sunxi-fexc " VERSION "\n\n", stderr);
|
||||
pr_error("Usage: %s [-vq]%s[<input> [<output>]]\n", arg0,
|
||||
mode ? " " : " [-I <infmt>] [-O <outfmt>] ");
|
||||
|
||||
if (mode == 0)
|
||||
fputs("\ninfmt: fex, bin (default:fex)"
|
||||
"\noutfmt: fex, bin, uboot (default:bin)\n",
|
||||
stderr);
|
||||
}
|
||||
|
||||
static inline int app_choose_mode(char *arg0)
|
||||
{
|
||||
const char *name = basename(arg0);
|
||||
if (strcmp(name, "fex2bin") == 0)
|
||||
return 1;
|
||||
else if (strcmp(name, "bin2fex") == 0)
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
static const char *formats[] = { "fex", "bin", "uboot", NULL };
|
||||
enum script_format infmt=FEX_SCRIPT_FORMAT;
|
||||
enum script_format outfmt=BIN_SCRIPT_FORMAT;
|
||||
const char *filename[] = { NULL /*stdin*/, NULL /*stdout*/};
|
||||
struct script *script;
|
||||
|
||||
int app_mode = app_choose_mode(argv[0]);
|
||||
|
||||
const char *opt_string = "I:O:vq?";
|
||||
if (app_mode != 0) opt_string += 4; /* disallow -I and -O */
|
||||
int opt, ret = 1;
|
||||
int verbose = 0;
|
||||
|
||||
if (app_mode == 2) { /* bin2fex */
|
||||
infmt = BIN_SCRIPT_FORMAT;
|
||||
outfmt = FEX_SCRIPT_FORMAT;
|
||||
}
|
||||
|
||||
while ((opt = getopt(argc, argv, opt_string)) != -1) {
|
||||
switch (opt) {
|
||||
case 'I':
|
||||
infmt=0;
|
||||
for (const char **f = formats; *f; f++, infmt++) {
|
||||
if (strcmp(*f, optarg) == 0)
|
||||
break;
|
||||
}
|
||||
switch (infmt) {
|
||||
case FEX_SCRIPT_FORMAT:
|
||||
case BIN_SCRIPT_FORMAT:
|
||||
break;
|
||||
default:
|
||||
pr_error("%s: invalid format -- \"%s\"\n",
|
||||
argv[0], optarg);
|
||||
goto show_usage;
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
outfmt=0;
|
||||
for (const char **f = formats; *f; f++, outfmt++) {
|
||||
if (strcmp(*f, optarg) == 0)
|
||||
break;
|
||||
}
|
||||
if (!formats[outfmt]) {
|
||||
pr_error("%s: invalid format -- \"%s\"\n",
|
||||
argv[0], optarg);
|
||||
goto show_usage;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'q':
|
||||
verbose--;
|
||||
break;
|
||||
default:
|
||||
show_usage:
|
||||
app_usage(argv[0], app_mode);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
switch (argc - optind) {
|
||||
case 2:
|
||||
filename[1] = argv[optind+1]; /* out */
|
||||
/* fall-through */
|
||||
case 1:
|
||||
if (strcmp(argv[optind], "-") != 0)
|
||||
filename[0] = argv[optind]; /* in */
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
goto show_usage;
|
||||
}
|
||||
|
||||
if (verbose>0)
|
||||
pr_error("%s: from %s:%s to %s:%s\n", argv[0],
|
||||
formats[infmt], filename[0]?filename[0]:"<stdin>",
|
||||
formats[outfmt], filename[1]?filename[1]:"<stdout>");
|
||||
|
||||
if ((script = script_new()) == NULL) {
|
||||
perror("malloc");
|
||||
goto done;
|
||||
} else if (script_parse(infmt, filename[0], script) &&
|
||||
script_generate(outfmt, filename[1], script)) {
|
||||
ret = 0;
|
||||
}
|
||||
script_delete(script);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
30
fexc.h
Normal file
30
fexc.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_FEXC_H
|
||||
#define _SUNXI_TOOLS_FEXC_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "script.h"
|
||||
#include "script_bin.h"
|
||||
#include "script_fex.h"
|
||||
#include "script_uboot.h"
|
||||
|
||||
#endif
|
||||
13
find-arm-gcc.sh
Executable file
13
find-arm-gcc.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# Try to locate suitable ARM cross compilers available via $PATH
|
||||
# If any are found, this function will output them as a TAB-delimited list
|
||||
#
|
||||
scan_path () {
|
||||
IFS=":"
|
||||
for path in $PATH; do
|
||||
find "$path" -maxdepth 1 -executable -name 'arm*-gcc' -printf '%f\t' 2>/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
# Use only the first field from result, and convert it to a toolchain prefix
|
||||
scan_path | cut -f 1 | sed -e 's/-gcc/-/'
|
||||
282
fit_image.c
Normal file
282
fit_image.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2020 Andre Przywara <osp@andrep.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; under 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "fel_lib.h"
|
||||
#include "fit_image.h"
|
||||
|
||||
/* defined in fel.c */
|
||||
extern bool verbose;
|
||||
|
||||
#define IH_ARCH_INVALID 0
|
||||
#define IH_ARCH_ARM 2
|
||||
#define IH_ARCH_ARM64 22
|
||||
|
||||
#define IH_OS_INVALID 0
|
||||
#define IH_OS_LINUX 5
|
||||
#define IH_OS_U_BOOT 17
|
||||
#define IH_OS_ARM_TRUSTED_FIRMWARE 25
|
||||
#define IH_OS_EFI 28
|
||||
|
||||
struct fit_image_info {
|
||||
const char *description;
|
||||
const char *data;
|
||||
uint32_t data_size;
|
||||
uint32_t load_addr;
|
||||
uint32_t entry_point;
|
||||
uint8_t os;
|
||||
uint8_t arch;
|
||||
};
|
||||
|
||||
static int fit_parse_os(const char *value)
|
||||
{
|
||||
if (!value || !*value)
|
||||
return IH_OS_INVALID;
|
||||
|
||||
if (!strcmp(value, "u-boot"))
|
||||
return IH_OS_U_BOOT;
|
||||
if (!strcmp(value, "linux"))
|
||||
return IH_OS_LINUX;
|
||||
if (!strcmp(value, "arm-trusted-firmware"))
|
||||
return IH_OS_ARM_TRUSTED_FIRMWARE;
|
||||
if (!strcmp(value, "efi"))
|
||||
return IH_OS_EFI;
|
||||
|
||||
return IH_OS_INVALID;
|
||||
}
|
||||
|
||||
static int fit_parse_arch(const char *value)
|
||||
{
|
||||
if (!value || !*value)
|
||||
return IH_ARCH_INVALID;
|
||||
|
||||
if (!strcmp(value, "arm"))
|
||||
return IH_ARCH_ARM;
|
||||
if (!strcmp(value, "arm64"))
|
||||
return IH_ARCH_ARM64;
|
||||
|
||||
return IH_ARCH_INVALID;
|
||||
}
|
||||
|
||||
static uint32_t fdt_getprop_u32(const void *fdt, int node, const char *name)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
|
||||
val = fdt_getprop(fdt, node, name, NULL);
|
||||
if (!val)
|
||||
return ~0U;
|
||||
|
||||
return fdt32_to_cpu(*val);
|
||||
}
|
||||
|
||||
static const char *fdt_getprop_str(const void *fdt, int node, const char *name)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
|
||||
prop = fdt_get_property(fdt, node, name, NULL);
|
||||
if (!prop)
|
||||
return NULL;
|
||||
|
||||
return prop->data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the image with the given name under the /images node, and parse
|
||||
* its information into the fit_image_info struct.
|
||||
* Returns 0 on success, and a negative error value otherwise.
|
||||
*/
|
||||
static int fit_get_image_info(const void *fit, const char *name,
|
||||
struct fit_image_info *info)
|
||||
{
|
||||
int node;
|
||||
const char *str;
|
||||
uint32_t data_offset;
|
||||
|
||||
node = fdt_path_offset(fit, "/images");
|
||||
if (node < 0)
|
||||
return -1;
|
||||
node = fdt_subnode_offset(fit, node, name);
|
||||
if (node < 0)
|
||||
return -1;
|
||||
|
||||
info->load_addr = fdt_getprop_u32(fit, node, "load");
|
||||
info->entry_point = fdt_getprop_u32(fit, node, "entry");
|
||||
info->description = fdt_getprop_str(fit, node, "description");
|
||||
/* properties used for FIT images with external data */
|
||||
info->data_size = fdt_getprop_u32(fit, node, "data-size");
|
||||
data_offset = fdt_getprop_u32(fit, node, "data-offset");
|
||||
|
||||
/* check for embedded data (when invalid external data properties) */
|
||||
if (info->data_size == ~0U || data_offset == ~0U) {
|
||||
const struct fdt_property *prop;
|
||||
int len;
|
||||
|
||||
prop = fdt_get_property(fit, node, "data", &len);
|
||||
info->data_size = len;
|
||||
info->data = prop->data;
|
||||
} else {
|
||||
/* external data is appended at the end of the FIT DTB blob */
|
||||
info->data = (const char *)fit + ((fdt_totalsize(fit) + 3) & ~3U);
|
||||
info->data += data_offset;
|
||||
}
|
||||
|
||||
info->os = fit_parse_os(fdt_getprop_str(fit, node, "os"));
|
||||
info->arch = fit_parse_arch(fdt_getprop_str(fit, node, "arch"));
|
||||
|
||||
str = fdt_getprop_str(fit, node, "compression");
|
||||
/* The current SPL does not support compression either. */
|
||||
if (str && strcmp(str, "none")) {
|
||||
printf("compression \"%s\" not supported for image \"%s\"\n",
|
||||
str, info->description);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_arch;
|
||||
static uint32_t dtb_addr;
|
||||
|
||||
/*
|
||||
* Upload the image described by its fit_image_info struct to the board.
|
||||
* Detect if an image contains an entry point and return that.
|
||||
* Set entry_arch to arm or arm64 on the way. Also detect the image
|
||||
* containing U-Boot and record its end address, so that the DTB can be
|
||||
* appended later on.
|
||||
* Returns the entry point if any is specified, or 0 otherwise.
|
||||
*/
|
||||
static uint32_t fit_load_image(feldev_handle *dev, struct fit_image_info *img)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
if (verbose)
|
||||
printf("loading image \"%s\" (%d bytes) to 0x%x\n",
|
||||
img->description, img->data_size, img->load_addr);
|
||||
aw_fel_write_buffer(dev, img->data,
|
||||
img->load_addr, img->data_size, true);
|
||||
|
||||
if (img->entry_point != ~0U) {
|
||||
ret = img->entry_point;
|
||||
entry_arch = img->arch;
|
||||
}
|
||||
/* either explicitly marked as U-Boot, or the first invalid one */
|
||||
if (img->os == IH_OS_U_BOOT ||
|
||||
(!dtb_addr && img->os == IH_OS_INVALID))
|
||||
dtb_addr = img->load_addr + img->data_size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t load_fit_images(feldev_handle *dev, const void *fit,
|
||||
const char *dt_name, bool *use_aarch64)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
struct fit_image_info img;
|
||||
const char *str;
|
||||
int node, len;
|
||||
uint32_t entry_point = 0;
|
||||
|
||||
node = fdt_path_offset(fit, "/configurations");
|
||||
if (node < 0) {
|
||||
pr_error("invalid FIT image, no /configurations node\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the right configuration node, either by using the provided
|
||||
* DT name as an identifier, falling back to the node titled "default",
|
||||
* or by using just the first node.
|
||||
*/
|
||||
if (dt_name) {
|
||||
for (node = fdt_first_subnode(fit, node);
|
||||
node >= 0;
|
||||
node = fdt_next_subnode(fit, node)) {
|
||||
prop = fdt_get_property(fit, node, "description", NULL);
|
||||
if (prop && !strcmp(prop->data, dt_name))
|
||||
break;
|
||||
}
|
||||
if (node < 0) {
|
||||
pr_error("no matching FIT configuration node for \"%s\"\n",
|
||||
dt_name);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
prop = fdt_get_property(fit, node, "default", NULL);
|
||||
if (!prop)
|
||||
node = fdt_first_subnode(fit, node);
|
||||
else
|
||||
node = fdt_subnode_offset(fit, node, prop->data);
|
||||
if (node < 0) {
|
||||
pr_error("no default FIT configuration node\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
entry_arch = IH_ARCH_INVALID;
|
||||
dtb_addr = 0;
|
||||
|
||||
/* Load the image described as "firmware". */
|
||||
str = fdt_getprop_str(fit, node, "firmware");
|
||||
if (str && !fit_get_image_info(fit, str, &img)) {
|
||||
uint32_t addr = fit_load_image(dev, &img);
|
||||
|
||||
if (addr != 0)
|
||||
entry_point = addr;
|
||||
} else {
|
||||
printf("WARNING: no valid \"firmware\" image entry in FIT\n");
|
||||
}
|
||||
|
||||
/* load all loadables, at their respective load addresses */
|
||||
prop = fdt_get_property(fit, node, "loadables", &len);
|
||||
for (str = prop ? prop->data : NULL;
|
||||
prop && (str - prop->data) < len && *str;
|
||||
str += strlen(str) + 1) {
|
||||
uint32_t addr;
|
||||
|
||||
if (fit_get_image_info(fit, str, &img)) {
|
||||
printf("Can't load loadable \"%s\", skipping.\n", str);
|
||||
continue;
|
||||
}
|
||||
addr = fit_load_image(dev, &img);
|
||||
if (addr != 0)
|
||||
entry_point = addr;
|
||||
}
|
||||
|
||||
if (use_aarch64)
|
||||
*use_aarch64 = (entry_arch == IH_ARCH_ARM64);
|
||||
|
||||
if (!dtb_addr) {
|
||||
printf("Warning: no U-Boot image found, not loading DTB\n");
|
||||
return entry_point;
|
||||
}
|
||||
|
||||
/* load .dtb right after the U-Boot image (appended DTB) */
|
||||
str = fdt_getprop_str(fit, node, "fdt");
|
||||
if (!str || fit_get_image_info(fit, str, &img)) {
|
||||
printf("Warning: no FDT found in FIT image\n");
|
||||
return entry_point;
|
||||
}
|
||||
if (verbose)
|
||||
printf("loading DTB \"%s\" (%d bytes)\n", img.description,
|
||||
img.data_size);
|
||||
aw_fel_write_buffer(dev, img.data, dtb_addr, img.data_size, false);
|
||||
|
||||
return entry_point;
|
||||
}
|
||||
32
fit_image.h
Normal file
32
fit_image.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2020 Andre Przywara <osp@andrep.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; under 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FIT_IMAGE_H__
|
||||
#define __FIT_IMAGE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "fel_lib.h"
|
||||
|
||||
/*
|
||||
* Load all images referenced in the given U-Boot FIT image. @dt_name will
|
||||
* be used to select one of the configurations. @use_aarch64 contains the
|
||||
* target architecture of the entry point.
|
||||
* Returns the entry point address of the image to be started.
|
||||
*/
|
||||
uint32_t load_fit_images(feldev_handle *dev, const void *fit,
|
||||
const char *dt_name, bool *use_aarch64);
|
||||
|
||||
#endif
|
||||
84
include/list.h
Normal file
84
include/list.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_LIST_H
|
||||
#define _SUNXI_TOOLS_LIST_H
|
||||
|
||||
/*
|
||||
* list
|
||||
*/
|
||||
|
||||
/** a list hook */
|
||||
struct list_entry {
|
||||
struct list_entry *prev;
|
||||
struct list_entry *next;
|
||||
};
|
||||
|
||||
/** initialize an empty list hook */
|
||||
static inline void list_init(struct list_entry *self)
|
||||
{
|
||||
self->prev = self->next = self;
|
||||
}
|
||||
|
||||
/** puts an entry between two other on a list */
|
||||
static inline void list_inject(struct list_entry *l,
|
||||
struct list_entry *prev,
|
||||
struct list_entry *next)
|
||||
{
|
||||
l->prev = prev;
|
||||
l->next = next;
|
||||
|
||||
next->prev = l;
|
||||
prev->next = l;
|
||||
}
|
||||
|
||||
#define list_insert(H, E) list_inject((E), (H), (H)->next)
|
||||
#define list_append(H, E) list_inject((E), (H)->prev, (H))
|
||||
|
||||
/** removes an entry for the list where it's contained */
|
||||
static inline void list_remove(struct list_entry *l)
|
||||
{
|
||||
struct list_entry *prev = l->prev, *next = l->next;
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/** returns first element of a list */
|
||||
static inline struct list_entry *list_first(struct list_entry *l)
|
||||
{
|
||||
return (l->next == l) ? NULL : l->next;
|
||||
}
|
||||
|
||||
/** returns last element of a list */
|
||||
static inline struct list_entry *list_last(struct list_entry *l)
|
||||
{
|
||||
return (l->prev == l) ? NULL : l->prev;
|
||||
}
|
||||
|
||||
/** returns next element on a list */
|
||||
static inline struct list_entry *list_next(struct list_entry *l,
|
||||
struct list_entry *e)
|
||||
{
|
||||
return (e->next == l) ? NULL : e->next;
|
||||
}
|
||||
|
||||
/** is list empty? */
|
||||
static inline int list_empty(struct list_entry *l)
|
||||
{
|
||||
return (l->prev == l);
|
||||
}
|
||||
|
||||
#endif /* _SUNXI_TOOLS_LIST_H */
|
||||
125
include/portable_endian.h
Normal file
125
include/portable_endian.h
Normal file
@ -0,0 +1,125 @@
|
||||
// "License": Public Domain
|
||||
// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
|
||||
// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to
|
||||
// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it
|
||||
// an example on how to get the endian conversion functions on different platforms.
|
||||
|
||||
#ifndef PORTABLE_ENDIAN_H__
|
||||
#define PORTABLE_ENDIAN_H__
|
||||
|
||||
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
|
||||
|
||||
# define __WINDOWS__
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
|
||||
# include <endian.h>
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
# include <libkern/OSByteOrder.h>
|
||||
|
||||
# define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
# define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
# define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
# define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
|
||||
# define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
# define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
# define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
# define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
|
||||
# define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
# define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
# define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
# define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
|
||||
# include <sys/endian.h>
|
||||
|
||||
/* For functions still missing, try to substitute 'historic' OpenBSD names */
|
||||
#ifndef be16toh
|
||||
# define be16toh(x) betoh16(x)
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
# define le16toh(x) letoh16(x)
|
||||
#endif
|
||||
#ifndef be32toh
|
||||
# define be32toh(x) betoh32(x)
|
||||
#endif
|
||||
#ifndef le32toh
|
||||
# define le32toh(x) letoh32(x)
|
||||
#endif
|
||||
#ifndef be64toh
|
||||
# define be64toh(x) betoh64(x)
|
||||
#endif
|
||||
#ifndef le64toh
|
||||
# define le64toh(x) letoh64(x)
|
||||
#endif
|
||||
|
||||
#elif defined(__WINDOWS__)
|
||||
|
||||
# include <winsock2.h>
|
||||
# include <sys/param.h>
|
||||
|
||||
# if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
||||
# define htobe16(x) htons(x)
|
||||
# define htole16(x) (x)
|
||||
# define be16toh(x) ntohs(x)
|
||||
# define le16toh(x) (x)
|
||||
|
||||
# define htobe32(x) htonl(x)
|
||||
# define htole32(x) (x)
|
||||
# define be32toh(x) ntohl(x)
|
||||
# define le32toh(x) (x)
|
||||
|
||||
# define htobe64(x) htonll(x)
|
||||
# define htole64(x) (x)
|
||||
# define be64toh(x) ntohll(x)
|
||||
# define le64toh(x) (x)
|
||||
|
||||
# elif BYTE_ORDER == BIG_ENDIAN
|
||||
|
||||
/* that would be xbox 360 */
|
||||
# define htobe16(x) (x)
|
||||
# define htole16(x) __builtin_bswap16(x)
|
||||
# define be16toh(x) (x)
|
||||
# define le16toh(x) __builtin_bswap16(x)
|
||||
|
||||
# define htobe32(x) (x)
|
||||
# define htole32(x) __builtin_bswap32(x)
|
||||
# define be32toh(x) (x)
|
||||
# define le32toh(x) __builtin_bswap32(x)
|
||||
|
||||
# define htobe64(x) (x)
|
||||
# define htole64(x) __builtin_bswap64(x)
|
||||
# define be64toh(x) (x)
|
||||
# define le64toh(x) __builtin_bswap64(x)
|
||||
|
||||
# else
|
||||
|
||||
# error byte order not supported
|
||||
|
||||
# endif
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#else
|
||||
|
||||
# error platform not supported
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
46
include/types.h
Normal file
46
include/types.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
* (C) Copyright 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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 SUNXI_TYPES_H
|
||||
#define SUNXI_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define __s8 int8_t
|
||||
#define __s16 int16_t
|
||||
#define __s32 int32_t
|
||||
#define __s64 int64_t
|
||||
|
||||
#define s8 int8_t
|
||||
#define s16 int16_t
|
||||
#define s32 int32_t
|
||||
#define s64 int64_t
|
||||
|
||||
#define __u8 uint8_t
|
||||
#define __u16 uint16_t
|
||||
#define __u32 uint32_t
|
||||
#define __u64 uint64_t
|
||||
|
||||
#define u8 uint8_t
|
||||
#define u16 uint16_t
|
||||
#define u32 uint32_t
|
||||
#define u64 uint64_t
|
||||
|
||||
#endif
|
||||
41
jtag-loop.S
Normal file
41
jtag-loop.S
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* (C) Copyright 2012 Jens Andersen <jens.andersen@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
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Build instructions:
|
||||
|
||||
arm-none-linux-gnueabi-gcc -g -fno-common -ffixed-r8 -msoft-float -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder jtag-loop.S -c
|
||||
|
||||
arm-none-linux-gnueabi-objcopy -O binary jtag-loop.o jtag-loop.bin
|
||||
|
||||
mksunxiboot jtag-loop.bin jtag-loop.sunxi
|
||||
*/
|
||||
|
||||
.file "fel-loop.S"
|
||||
.global entry
|
||||
.text
|
||||
.code 32
|
||||
.section ".start", "ax"
|
||||
|
||||
entry:
|
||||
ldr r0,=0x01c208b4
|
||||
ldr r1,=0x00444444
|
||||
str r1, [r0]
|
||||
b .
|
||||
36
jtag-loop.c
Normal file
36
jtag-loop.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* (C) Copyright 2012 Jens Andersen <jens.andersen@gmail.com>
|
||||
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.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
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Build instructions:
|
||||
|
||||
arm-none-linux-gnueabi-gcc -g -fno-common -ffixed-r8 -msoft-float -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder -Os jtag-loop.c -c
|
||||
|
||||
arm-none-linux-gnueabi-objcopy -O binary jtag-loop.o jtag-loop.bin
|
||||
|
||||
mksunxiboot jtag-loop.bin jtag-loop.sunxi
|
||||
*/
|
||||
|
||||
void _start(void)
|
||||
{
|
||||
*(volatile unsigned long *)0x01c208b4 = 0x00444444;
|
||||
while(1);
|
||||
}
|
||||
28
jtag-loop.lds
Normal file
28
jtag-loop.lds
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0030;
|
||||
.text : { *(.text) }
|
||||
/DISCARD/ : { *(.dynstr*) }
|
||||
/DISCARD/ : { *(.dynamic*) }
|
||||
/DISCARD/ : { *(.plt*) }
|
||||
/DISCARD/ : { *(.interp*) }
|
||||
/DISCARD/ : { *(.gnu*) }
|
||||
/DISCARD/ : { *(.note*) }
|
||||
}
|
||||
791
meminfo.c
Normal file
791
meminfo.c
Normal file
@ -0,0 +1,791 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Floris Bos <bos@je-eigen-domein.nl>
|
||||
* Copyright (c) 2014 Luc Verhaegen <libv@skynet.be>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef uint32_t u32;
|
||||
|
||||
/* from u-boot code: */
|
||||
struct sun4i_dram_para {
|
||||
u32 baseaddr;
|
||||
u32 clock;
|
||||
u32 type;
|
||||
u32 rank_num;
|
||||
u32 density;
|
||||
u32 io_width;
|
||||
u32 bus_width;
|
||||
u32 cas;
|
||||
u32 zq;
|
||||
u32 odt_en;
|
||||
u32 size;
|
||||
u32 tpr0;
|
||||
u32 tpr1;
|
||||
u32 tpr2;
|
||||
u32 tpr3;
|
||||
u32 tpr4;
|
||||
u32 tpr5;
|
||||
u32 emr1;
|
||||
u32 emr2;
|
||||
u32 emr3;
|
||||
};
|
||||
|
||||
#define DEVMEM_FILE "/dev/mem"
|
||||
static int devmem_fd;
|
||||
|
||||
enum sunxi_soc_version {
|
||||
SUNXI_SOC_SUN4I = 0x1623, /* A10 */
|
||||
SUNXI_SOC_SUN5I = 0x1625, /* A13, A10s */
|
||||
SUNXI_SOC_SUN6I = 0x1633, /* A31 */
|
||||
SUNXI_SOC_SUN7I = 0x1651, /* A20 */
|
||||
SUNXI_SOC_SUN8I = 0x1650, /* A23 */
|
||||
SUNXI_SOC_SUN9I = 0x1667, /* A33 */
|
||||
SUNXI_SOC_SUN10I = 0x1635, /* A80 */
|
||||
};
|
||||
|
||||
static enum sunxi_soc_version soc_version;
|
||||
|
||||
/*
|
||||
* Libv's favourite register handling calls.
|
||||
*/
|
||||
unsigned int
|
||||
sunxi_io_read(void *base, int offset)
|
||||
{
|
||||
return *(volatile unsigned int*) (base + offset);
|
||||
}
|
||||
|
||||
void
|
||||
sunxi_io_write(void *base, int offset, unsigned int value)
|
||||
{
|
||||
*(volatile unsigned int*) (base + offset) = value;
|
||||
}
|
||||
|
||||
void
|
||||
sunxi_io_mask(void *base, int offset, unsigned int value, unsigned int mask)
|
||||
{
|
||||
unsigned int tmp = sunxi_io_read(base, offset);
|
||||
|
||||
tmp &= ~mask;
|
||||
tmp |= value & mask;
|
||||
|
||||
sunxi_io_write(base, offset, tmp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find out exactly which SoC we are dealing with.
|
||||
*/
|
||||
#define SUNXI_IO_SRAM_BASE 0x01C00000
|
||||
#define SUNXI_IO_SRAM_SIZE 0x00001000
|
||||
|
||||
#define SUNXI_IO_SRAM_VERSION 0x24
|
||||
|
||||
static int
|
||||
soc_version_read(void)
|
||||
{
|
||||
void *base;
|
||||
unsigned int restore;
|
||||
|
||||
base = mmap(NULL, SUNXI_IO_SRAM_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, devmem_fd, SUNXI_IO_SRAM_BASE);
|
||||
if (base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to map sram registers: %s\n",
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
restore = sunxi_io_read(base, SUNXI_IO_SRAM_VERSION);
|
||||
|
||||
sunxi_io_mask(base, SUNXI_IO_SRAM_VERSION, 0x8000, 0x8000);
|
||||
|
||||
soc_version = sunxi_io_read(base, SUNXI_IO_SRAM_VERSION) >> 16;
|
||||
|
||||
sunxi_io_mask(base, SUNXI_IO_SRAM_VERSION, restore, 0x8000);
|
||||
|
||||
munmap(base, SUNXI_IO_SRAM_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read DRAM clock.
|
||||
*/
|
||||
#define SUNXI_IO_CCM_BASE 0x01C20000
|
||||
#define SUNXI_IO_CCM_SIZE 0x00001000
|
||||
|
||||
#define SUNXI_IO_CCM_PLL5_CFG 0x20
|
||||
|
||||
static int
|
||||
sunxi_dram_clock_read(unsigned int *clock)
|
||||
{
|
||||
void *base;
|
||||
unsigned int tmp;
|
||||
int n, k, m;
|
||||
|
||||
base = mmap(NULL, SUNXI_IO_CCM_SIZE, PROT_READ,
|
||||
MAP_SHARED, devmem_fd, SUNXI_IO_CCM_BASE);
|
||||
if (base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to map ccm registers: %s\n",
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
tmp = sunxi_io_read(base, SUNXI_IO_CCM_PLL5_CFG);
|
||||
|
||||
munmap(base, SUNXI_IO_CCM_SIZE);
|
||||
|
||||
n = (tmp >> 8) & 0x1F;
|
||||
k = ((tmp >> 4) & 0x03) + 1;
|
||||
m = (tmp & 0x03) + 1;
|
||||
|
||||
switch (soc_version) {
|
||||
case SUNXI_SOC_SUN6I:
|
||||
case SUNXI_SOC_SUN8I:
|
||||
n++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*clock = (24 * n * k) / m;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct regs {
|
||||
int offset;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static int
|
||||
dram_registers_print(unsigned int address, int size, const struct regs *regs,
|
||||
const char *description, const char *prefix)
|
||||
{
|
||||
void *base;
|
||||
int i, j;
|
||||
|
||||
base = mmap(NULL, size, PROT_READ, MAP_SHARED, devmem_fd, address);
|
||||
if (base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to map %s registers: %s\n",
|
||||
description, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
printf("/*\n");
|
||||
printf(" * %s Registers\n", description);
|
||||
printf(" */\n");
|
||||
|
||||
for (i = 0; i < size; i += 4) {
|
||||
unsigned int reg = sunxi_io_read(base, i);
|
||||
|
||||
for (j = 0; regs[j].name; j++)
|
||||
if (i == regs[j].offset) {
|
||||
printf("%s = 0x%08x;\n", regs[j].name, reg);
|
||||
}
|
||||
|
||||
if (reg && !regs[j].name)
|
||||
printf("%s_%03X = 0x%08x;\n", prefix, i, reg);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
munmap(base, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dram_register_range_print(unsigned int address, int size,
|
||||
const char *description, const char *prefix)
|
||||
{
|
||||
void *base;
|
||||
int i;
|
||||
|
||||
base = mmap(NULL, size, PROT_READ, MAP_SHARED, devmem_fd, address);
|
||||
if (base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to map %s registers: %s\n",
|
||||
description, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
printf("/*\n");
|
||||
printf(" * %s Registers\n", description);
|
||||
printf(" */\n");
|
||||
|
||||
for (i = 0; i < size; i += 4) {
|
||||
unsigned int reg = sunxi_io_read(base, i);
|
||||
|
||||
if (reg)
|
||||
printf("%s_%03X = 0x%08x;\n", prefix, i, reg);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
munmap(base, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read DRAM parameters.
|
||||
*/
|
||||
#define SUN4I_IO_DRAM_BASE 0x01C01000
|
||||
#define SUN4I_IO_DRAM_SIZE 0x00001000
|
||||
|
||||
#define SUN4I_IO_DRAM_CCR 0x000 /* controller configuration register */
|
||||
#define SUN4I_IO_DRAM_DCR 0x004 /* dram configuration */
|
||||
#define SUN4I_IO_DRAM_IOCR 0x008 /* i/o configuration */
|
||||
|
||||
#define SUN4I_IO_DRAM_TPR0 0x014 /* dram timing parameters register 0 */
|
||||
#define SUN4I_IO_DRAM_TPR1 0x018 /* dram timing parameters register 1 */
|
||||
#define SUN4I_IO_DRAM_TPR2 0x01C /* dram timing parameters register 2 */
|
||||
|
||||
#define SUN4I_IO_DRAM_ZQCR0 0x0A8 /* zq control register 0 */
|
||||
#define SUN4I_IO_DRAM_ZQCR1 0x0AC /* zq control register 1 */
|
||||
|
||||
#define SUN4I_IO_DRAM_MR 0x1F0 /* mode register */
|
||||
#define SUN4I_IO_DRAM_EMR 0x1F4 /* extended mode register */
|
||||
#define SUN4I_IO_DRAM_EMR2 0x1F8 /* extended mode register */
|
||||
#define SUN4I_IO_DRAM_EMR3 0x1FC /* extended mode register */
|
||||
|
||||
#define SUN4I_IO_DRAM_DLLCR0 0x204 /* dll control register 0(byte 0) */
|
||||
#define SUN4I_IO_DRAM_DLLCR1 0x208 /* dll control register 1(byte 1) */
|
||||
#define SUN4I_IO_DRAM_DLLCR2 0x20C /* dll control register 2(byte 2) */
|
||||
#define SUN4I_IO_DRAM_DLLCR3 0x210 /* dll control register 3(byte 3) */
|
||||
#define SUN4I_IO_DRAM_DLLCR4 0x214 /* dll control register 4(byte 4) */
|
||||
|
||||
static int
|
||||
sun4i_dram_parameters_read(struct sun4i_dram_para *dram_para)
|
||||
{
|
||||
void *base;
|
||||
unsigned int zqcr0, dcr;
|
||||
unsigned int dllcr0, dllcr1, dllcr2, dllcr3, dllcr4;
|
||||
|
||||
base = mmap(NULL, SUN4I_IO_DRAM_SIZE, PROT_READ,
|
||||
MAP_SHARED, devmem_fd, SUN4I_IO_DRAM_BASE);
|
||||
if (base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to map dram registers: %s\n",
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
dram_para->tpr0 = sunxi_io_read(base, SUN4I_IO_DRAM_TPR0);
|
||||
dram_para->tpr1 = sunxi_io_read(base, SUN4I_IO_DRAM_TPR1);
|
||||
dram_para->tpr2 = sunxi_io_read(base, SUN4I_IO_DRAM_TPR2);
|
||||
|
||||
dllcr0 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR0) >> 6) & 0x3F;
|
||||
dllcr1 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR1) >> 14) & 0x0F;
|
||||
dllcr2 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR2) >> 14) & 0x0F;
|
||||
dllcr3 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR3) >> 14) & 0x0F;
|
||||
dllcr4 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR4) >> 14) & 0x0F;
|
||||
|
||||
dram_para->tpr3 = (dllcr0 << 16) |
|
||||
(dllcr4 << 12) | (dllcr3 << 8) | (dllcr2 << 4) | dllcr1;
|
||||
|
||||
if (soc_version == SUNXI_SOC_SUN7I) {
|
||||
if (sunxi_io_read(base, SUN4I_IO_DRAM_CCR) & 0x20)
|
||||
dram_para->tpr4 |= 0x01;
|
||||
if (!(sunxi_io_read(base, SUN4I_IO_DRAM_ZQCR1) & 0x01000000))
|
||||
dram_para->tpr4 |= 0x02;
|
||||
}
|
||||
|
||||
dram_para->cas = (sunxi_io_read(base, SUN4I_IO_DRAM_MR) >> 4) & 0x0F;
|
||||
dram_para->emr1 = sunxi_io_read(base, SUN4I_IO_DRAM_EMR);
|
||||
dram_para->emr2 = sunxi_io_read(base, SUN4I_IO_DRAM_EMR2);
|
||||
dram_para->emr3 = sunxi_io_read(base, SUN4I_IO_DRAM_EMR3);
|
||||
|
||||
dram_para->odt_en = sunxi_io_read(base, SUN4I_IO_DRAM_IOCR) & 0x03;
|
||||
zqcr0 = sunxi_io_read(base, SUN4I_IO_DRAM_ZQCR0);
|
||||
dram_para->zq = (zqcr0 & 0xf0000000) |
|
||||
((zqcr0 >> 20) & 0xff) |
|
||||
((zqcr0 & 0xfffff) << 8);
|
||||
|
||||
dcr = sunxi_io_read(base, SUN4I_IO_DRAM_DCR);
|
||||
if (dcr & 0x01) {
|
||||
dram_para->cas += 4;
|
||||
dram_para->type = 3;
|
||||
} else
|
||||
dram_para->type = 2;
|
||||
|
||||
dram_para->density = (1 << ((dcr >> 3) & 0x07)) * 256;
|
||||
dram_para->rank_num = ((dcr >> 10) & 0x03) + 1;
|
||||
dram_para->io_width = ((dcr >> 1) & 0x03) * 8;
|
||||
dram_para->bus_width = (((dcr >> 6) & 3) + 1) * 8;
|
||||
|
||||
munmap(base, SUN4I_IO_DRAM_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a dram.c that can be stuck immediately into u-boot.
|
||||
*/
|
||||
void
|
||||
sun4i_dram_para_print_uboot(struct sun4i_dram_para *dram_para)
|
||||
{
|
||||
printf("// place this file in board/sunxi/ in u-boot\n");
|
||||
printf("/* this file is generated, don't edit it yourself */\n");
|
||||
printf("\n");
|
||||
printf("#include \"common.h\"\n");
|
||||
printf("#include <asm/arch/dram.h>\n");
|
||||
printf("\n");
|
||||
printf("static struct dram_para dram_para = {\n");
|
||||
printf("\t.clock = %d,\n", dram_para->clock);
|
||||
printf("\t.type = %d,\n", dram_para->type);
|
||||
printf("\t.rank_num = %d,\n", dram_para->rank_num);
|
||||
printf("\t.density = %d,\n", dram_para->density);
|
||||
printf("\t.io_width = %d,\n", dram_para->io_width);
|
||||
printf("\t.bus_width = %d,\n", dram_para->bus_width);
|
||||
printf("\t.cas = %d,\n", dram_para->cas);
|
||||
printf("\t.zq = 0x%02x,\n", dram_para->zq);
|
||||
printf("\t.odt_en = %d,\n", dram_para->odt_en);
|
||||
printf("\t.size = !!! FIXME !!!, /* in MiB */\n");
|
||||
printf("\t.tpr0 = 0x%08x,\n", dram_para->tpr0);
|
||||
printf("\t.tpr1 = 0x%04x,\n", dram_para->tpr1);
|
||||
printf("\t.tpr2 = 0x%05x,\n", dram_para->tpr2);
|
||||
printf("\t.tpr3 = 0x%02x,\n", dram_para->tpr3);
|
||||
printf("\t.tpr4 = 0x%02x,\n", dram_para->tpr4);
|
||||
printf("\t.tpr5 = 0x%02x,\n", dram_para->tpr5);
|
||||
printf("\t.emr1 = 0x%02x,\n", dram_para->emr1);
|
||||
printf("\t.emr2 = 0x%02x,\n", dram_para->emr2);
|
||||
printf("\t.emr3 = 0x%02x,\n", dram_para->emr3);
|
||||
printf("};\n");
|
||||
printf("\n");
|
||||
printf("unsigned long sunxi_dram_init(void)\n");
|
||||
printf("{\n");
|
||||
printf("\treturn dramc_init(&dram_para);\n");
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print output matching the .fex output, so it can be stuck in a
|
||||
* fex file directly.
|
||||
*/
|
||||
void
|
||||
sun4i_dram_para_print_fex(struct sun4i_dram_para *dram_para)
|
||||
{
|
||||
printf("; Insert this section into your .fex file\n");
|
||||
printf("[dram_para]\n");
|
||||
printf("dram_baseaddr = 0x40000000\n");
|
||||
printf("dram_clk = %d\n", dram_para->clock);
|
||||
printf("dram_type = %d\n", dram_para->type);
|
||||
printf("dram_rank_num = %d\n", dram_para->rank_num);
|
||||
printf("dram_chip_density = %d\n", dram_para->density);
|
||||
printf("dram_io_width = %d\n", dram_para->io_width);
|
||||
printf("dram_bus_width = %d\n", dram_para->bus_width);
|
||||
printf("dram_cas = %d\n", dram_para->cas);
|
||||
printf("dram_zq = 0x%02x\n", dram_para->zq);
|
||||
printf("dram_odt_en = %d\n", dram_para->odt_en);
|
||||
printf("dram_size = !!! FIXME !!!\n");
|
||||
printf("dram_tpr0 = 0x%08x\n", dram_para->tpr0);
|
||||
printf("dram_tpr1 = 0x%04x\n", dram_para->tpr1);
|
||||
printf("dram_tpr2 = 0x%05x\n", dram_para->tpr2);
|
||||
printf("dram_tpr3 = 0x%02x\n", dram_para->tpr3);
|
||||
printf("dram_tpr4 = 0x%02x\n", dram_para->tpr4);
|
||||
printf("dram_tpr5 = 0x%02x\n", dram_para->tpr5);
|
||||
printf("dram_emr1 = 0x%02x\n", dram_para->emr1);
|
||||
printf("dram_emr2 = 0x%02x\n", dram_para->emr2);
|
||||
printf("dram_emr3 = 0x%02x\n", dram_para->emr3);
|
||||
}
|
||||
|
||||
static int
|
||||
sun4i_dram_para_print(bool uboot)
|
||||
{
|
||||
struct sun4i_dram_para dram_para = { .baseaddr = 0 };
|
||||
int ret;
|
||||
|
||||
ret = sunxi_dram_clock_read(&dram_para.clock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sun4i_dram_parameters_read(&dram_para);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (uboot)
|
||||
sun4i_dram_para_print_uboot(&dram_para);
|
||||
else
|
||||
sun4i_dram_para_print_fex(&dram_para);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
#define SUN6I_IO_DRAMCOM_BASE 0x01C62000
|
||||
#define SUN6I_IO_DRAMCOM_SIZE 0x0300
|
||||
#define SUN6I_IO_DRAMCTL_BASE 0x01C63000
|
||||
#define SUN6I_IO_DRAMCTL_SIZE 0x0400
|
||||
#define SUN6I_IO_DRAMPHY_BASE 0x01C65000
|
||||
#define SUN6I_IO_DRAMPHY_SIZE 0x0400
|
||||
|
||||
static struct regs
|
||||
sun6i_dramcom_regs[] = {
|
||||
{0x00, "SDR_COM_CR"},
|
||||
{0x04, "SDR_COM_CCR"},
|
||||
{0x10, "SDR_COM_MFACR"},
|
||||
{0x30, "SDR_COM_MSACR"},
|
||||
{0x50, "SDR_COM_MBACR"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct regs
|
||||
sun6i_dramctl_regs[] = {
|
||||
{0x004, "SDR_SCTL"},
|
||||
{0x008, "SDR_SSTAT"},
|
||||
{0x040, "SDR_MCMD"},
|
||||
{0x04c, "SDR_CMDSTAT"},
|
||||
{0x050, "SDR_CMDSTATEN"},
|
||||
{0x060, "SDR_MRRCFG0"},
|
||||
{0x064, "SDR_MRRSTAT0"},
|
||||
{0x068, "SDR_MRRSTAT1"},
|
||||
{0x07c, "SDR_MCFG1"},
|
||||
{0x080, "SDR_MCFG"},
|
||||
{0x084, "SDR_PPCFG"},
|
||||
{0x088, "SDR_MSTAT"},
|
||||
{0x08c, "SDR_LP2ZQCFG"},
|
||||
{0x094, "SDR_DTUSTAT"},
|
||||
{0x098, "SDR_DTUNA"},
|
||||
{0x09c, "SDR_DTUNE"},
|
||||
{0x0a0, "SDR_DTUPRD0"},
|
||||
{0x0a4, "SDR_DTUPRD1"},
|
||||
{0x0a8, "SDR_DTUPRD2"},
|
||||
{0x0ac, "SDR_DTUPRD3"},
|
||||
{0x0b0, "SDR_DTUAWDT"},
|
||||
{0x0c0, "SDR_TOGCNT1U"},
|
||||
{0x0cc, "SDR_TOGCNT100N"},
|
||||
{0x0d0, "SDR_TREFI"},
|
||||
{0x0d4, "SDR_TMRD"},
|
||||
{0x0d8, "SDR_TRFC"},
|
||||
{0x0dc, "SDR_TRP"},
|
||||
{0x0e0, "SDR_TRTW"},
|
||||
{0x0e4, "SDR_TAL"},
|
||||
{0x0e8, "SDR_TCL"},
|
||||
{0x0ec, "SDR_TCWL"},
|
||||
{0x0f0, "SDR_TRAS"},
|
||||
{0x0f4, "SDR_TRC"},
|
||||
{0x0f8, "SDR_TRCD"},
|
||||
{0x0fc, "SDR_TRRD"},
|
||||
{0x100, "SDR_TRTP"},
|
||||
{0x104, "SDR_TWR"},
|
||||
{0x108, "SDR_TWTR"},
|
||||
{0x10c, "SDR_TEXSR"},
|
||||
{0x110, "SDR_TXP"},
|
||||
{0x114, "SDR_TXPDLL"},
|
||||
{0x118, "SDR_TZQCS"},
|
||||
{0x11c, "SDR_TZQCSI"},
|
||||
{0x120, "SDR_TDQS"},
|
||||
{0x124, "SDR_TCKSRE"},
|
||||
{0x128, "SDR_TCKSRX"},
|
||||
{0x12c, "SDR_TCKE"},
|
||||
{0x130, "SDR_TMOD"},
|
||||
{0x134, "SDR_TRSTL"},
|
||||
{0x138, "SDR_TZQCL"},
|
||||
{0x13c, "SDR_TMRR"},
|
||||
{0x140, "SDR_TCKESR"},
|
||||
{0x144, "SDR_TDPD"},
|
||||
{0x200, "SDR_DTUWACTL"},
|
||||
{0x204, "SDR_DTURACTL"},
|
||||
{0x208, "SDR_DTUCFG"},
|
||||
{0x20c, "SDR_DTUECTL"},
|
||||
{0x210, "SDR_DTUWD0"},
|
||||
{0x214, "SDR_DTUWD1"},
|
||||
{0x218, "SDR_DTUWD2"},
|
||||
{0x21c, "SDR_DTUWD3"},
|
||||
{0x220, "SDR_DTUWDM"},
|
||||
{0x224, "SDR_DTURD0"},
|
||||
{0x224, "SDR_DTURD1"},
|
||||
{0x22c, "SDR_DTURD2"},
|
||||
{0x230, "SDR_DTURD3"},
|
||||
{0x234, "SDR_DTULFSRWD"},
|
||||
{0x238, "SDR_DTULFSRRD"},
|
||||
{0x23c, "SDR_DTUEAF"},
|
||||
{0x240, "SDR_DFITCTLDLY"},
|
||||
{0x244, "SDR_DFIODTCFG"},
|
||||
{0x248, "SDR_DFIODTCFG1"},
|
||||
{0x24c, "SDR_DFIODTRMAP"},
|
||||
{0x250, "SDR_DFITPHYWRD"},
|
||||
{0x254, "SDR_DFITPHYWRL"},
|
||||
{0x260, "SDR_DFITRDDEN"},
|
||||
{0x264, "SDR_DFITPHYRDL"},
|
||||
{0x270, "SDR_DFITPHYUPDTYPE0"},
|
||||
{0x274, "SDR_DFITPHYUPDTYPE1"},
|
||||
{0x278, "SDR_DFITPHYUPDTYPE2"},
|
||||
{0x27c, "SDR_DFITPHYUPDTYPE3"},
|
||||
{0x280, "SDR_DFITCTRLUPDMIN"},
|
||||
{0x284, "SDR_DFITCTRLUPDMAX"},
|
||||
{0x288, "SDR_DFITCTRLUPDDLY"},
|
||||
{0x290, "SDR_DFIUPDCFG"},
|
||||
{0x294, "SDR_DFITREFMSKI"},
|
||||
{0x298, "SDR_DFITCRLUPDI"},
|
||||
{0x2ac, "SDR_DFITRCFG0"},
|
||||
{0x2b0, "SDR_DFITRSTAT0"},
|
||||
{0x2b4, "SDR_DFITRWRLVLEN"},
|
||||
{0x2b8, "SDR_DFITRRDLVLEN"},
|
||||
{0x2bc, "SDR_DFITRRDLVLGATEEN"},
|
||||
{0x2c4, "SDR_DFISTCFG0"},
|
||||
{0x2c8, "SDR_DFISTCFG1"},
|
||||
{0x2d0, "SDR_DFITDRAMCLKEN"},
|
||||
{0x2d4, "SDR_DFITDRAMCLKDIS"},
|
||||
{0x2f0, "SDR_DFILPCFG0"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct regs
|
||||
sun6i_dramphy_regs[] = {
|
||||
{0x004, "SDR_PIR"},
|
||||
{0x008, "SDR_PGCR"},
|
||||
{0x00c, "SDR_PGSR"},
|
||||
{0x010, "SDR_DLLGCR"},
|
||||
{0x014, "SDR_ACDLLCR"},
|
||||
{0x018, "SDR_PTR0"},
|
||||
{0x01c, "SDR_PTR1"},
|
||||
{0x020, "SDR_PTR2"},
|
||||
{0x024, "SDR_ACIOCR"},
|
||||
{0x028, "SDR_DXCCR"},
|
||||
{0x02c, "SDR_DSGCR"},
|
||||
{0x030, "SDR_DCR"},
|
||||
{0x034, "SDR_DTPR0"},
|
||||
{0x038, "SDR_DTPR1"},
|
||||
{0x03c, "SDR_DTPR2"},
|
||||
{0x040, "SDR_MR0"},
|
||||
{0x044, "SDR_MR1"},
|
||||
{0x048, "SDR_MR2"},
|
||||
{0x04c, "SDR_MR3"},
|
||||
{0x050, "SDR_ODTCR"},
|
||||
{0x054, "SDR_DTAR"},
|
||||
{0x058, "SDR_DTDT0"},
|
||||
{0x05c, "SDR_DTDT1"},
|
||||
{0x0c0, "SDR_DCUAR"},
|
||||
{0x0c4, "SDR_DCUDR"},
|
||||
{0x0c8, "SDR_DCURR"},
|
||||
{0x0cc, "SDR_DCULR"},
|
||||
{0x0d0, "SDR_DCUGCR"},
|
||||
{0x0d4, "SDR_DCUTPR"},
|
||||
{0x0d8, "SDR_DCUSR0"},
|
||||
{0x0dc, "SDR_DCUSR1"},
|
||||
{0x100, "SDR_BISTRR"},
|
||||
{0x104, "SDR_BISTMSKR0"},
|
||||
{0x108, "SDR_BISTMSKR1"},
|
||||
{0x10c, "SDR_BISTWCR"},
|
||||
{0x110, "SDR_BISTLSR"},
|
||||
{0x114, "SDR_BISTAR0"},
|
||||
{0x118, "SDR_BISTAR1"},
|
||||
{0x11c, "SDR_BISTAR2"},
|
||||
{0x120, "SDR_BISTUDPR"},
|
||||
{0x124, "SDR_BISTGSR"},
|
||||
{0x128, "SDR_BISTWER"},
|
||||
{0x12c, "SDR_BISTBER0"},
|
||||
{0x130, "SDR_BISTBER1"},
|
||||
{0x134, "SDR_BISTBER2"},
|
||||
{0x138, "SDR_BISTWCSR"},
|
||||
{0x13c, "SDR_BISTFWR0"},
|
||||
{0x140, "SDR_BISTFWR1"},
|
||||
{0x180, "SDR_ZQ0CR0"},
|
||||
{0x184, "SDR_ZQ0CR1"},
|
||||
{0x188, "SDR_ZQ0SR0"},
|
||||
{0x18c, "SDR_ZQ0SR1"},
|
||||
{0x1c0, "SDR_DX0GCR"},
|
||||
{0x1c4, "SDR_DX0GSR0"},
|
||||
{0x1c8, "SDR_DX0GSR1"},
|
||||
{0x1cc, "SDR_DX0DLLCR"},
|
||||
{0x1d0, "SDR_DX0DQTR"},
|
||||
{0x1d4, "SDR_DX0DQSTR"},
|
||||
{0x200, "SDR_DX1GCR"},
|
||||
{0x204, "SDR_DX1GSR0"},
|
||||
{0x208, "SDR_DX1GSR1"},
|
||||
{0x20c, "SDR_DX1DLLCR"},
|
||||
{0x210, "SDR_DX1DQTR"},
|
||||
{0x214, "SDR_DX1DQSTR"},
|
||||
{0x240, "SDR_DX2GCR"},
|
||||
{0x244, "SDR_DX2GSR0"},
|
||||
{0x248, "SDR_DX2GSR1"},
|
||||
{0x24c, "SDR_DX2DLLCR"},
|
||||
{0x250, "SDR_DX2DQTR"},
|
||||
{0x254, "SDR_DX2DQSTR"},
|
||||
{0x280, "SDR_DX3GCR"},
|
||||
{0x284, "SDR_DX3GSR0"},
|
||||
{0x288, "SDR_DX3GSR1"},
|
||||
{0x28c, "SDR_DX3DLLCR"},
|
||||
{0x290, "SDR_DX3DQTR"},
|
||||
{0x294, "SDR_DX3DQSTR"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static int
|
||||
sun6i_dram_regs_print(void)
|
||||
{
|
||||
unsigned int clock;
|
||||
int ret;
|
||||
|
||||
ret = sunxi_dram_clock_read(&clock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
printf("DRAM Clock: %dMHz\n", clock);
|
||||
|
||||
ret = dram_registers_print(SUN6I_IO_DRAMCOM_BASE,
|
||||
SUN6I_IO_DRAMCOM_SIZE,
|
||||
&sun6i_dramcom_regs[0],
|
||||
"DRAM COM", "SDR_COM");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dram_registers_print(SUN6I_IO_DRAMCTL_BASE,
|
||||
SUN6I_IO_DRAMCTL_SIZE,
|
||||
&sun6i_dramctl_regs[0],
|
||||
"DRAM CTL", "SDR_CTL");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dram_registers_print(SUN6I_IO_DRAMPHY_BASE,
|
||||
SUN6I_IO_DRAMPHY_SIZE,
|
||||
&sun6i_dramphy_regs[0],
|
||||
"DRAM PHY", "SDR_PHY");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int
|
||||
sun8i_dram_regs_print(void)
|
||||
{
|
||||
unsigned int clock;
|
||||
int ret;
|
||||
|
||||
ret = sunxi_dram_clock_read(&clock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
printf("DRAM Clock: %dMHz\n", clock);
|
||||
|
||||
ret = dram_register_range_print(SUN6I_IO_DRAMCOM_BASE,
|
||||
SUN6I_IO_DRAMCOM_SIZE,
|
||||
"DRAM COM", "SDR_COM");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
ret = dram_register_range_print(SUN6I_IO_DRAMCTL_BASE,
|
||||
SUN6I_IO_DRAMCTL_SIZE,
|
||||
"DRAM CTL", "SDR_CTL");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dram_register_range_print(SUN6I_IO_DRAMPHY_BASE,
|
||||
SUN6I_IO_DRAMPHY_SIZE,
|
||||
"DRAM PHY", "SDR_PHY");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(const char *name)
|
||||
{
|
||||
puts("sunxi-meminfo " VERSION "\n");
|
||||
printf("Utility to retrieve DRAM information from registers on "
|
||||
"Allwinner SoCs.\n");
|
||||
printf("\n");
|
||||
printf("This is part of the sunxi-tools package from the sunxi "
|
||||
"project. ");
|
||||
printf("For more \ninformation visit "
|
||||
"http://linux-sunxi.org/Sunxi-tools.\n");
|
||||
printf("\n");
|
||||
printf("Usage: %s [OPTION]\n", name);
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" -f: print in FEX format (default).\n");
|
||||
printf(" -u: print in sunxi U-Boot dram.c file format.\n");
|
||||
printf(" -h: print this usage information.\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
bool uboot;
|
||||
int ret;
|
||||
|
||||
if (argc == 2) {
|
||||
if (argv[1][0] == '-') {
|
||||
if (argv[1][1] == 'f')
|
||||
uboot = false;
|
||||
else if (argv[1][1] == 'u')
|
||||
uboot = true;
|
||||
else if (argv[1][1] == 'h')
|
||||
goto help;
|
||||
else if ((argv[1][1] == '-') && (argv[1][2] == 'h'))
|
||||
goto help;
|
||||
else
|
||||
goto usage;
|
||||
|
||||
if (argv[1][2] != 0)
|
||||
goto usage;
|
||||
} else
|
||||
goto usage;
|
||||
} else if (argc == 1)
|
||||
uboot = false;
|
||||
else
|
||||
goto usage;
|
||||
|
||||
devmem_fd = open(DEVMEM_FILE, O_RDWR);
|
||||
if (devmem_fd == -1) {
|
||||
fprintf(stderr, "Error: failed to open %s: %s\n", DEVMEM_FILE,
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
ret = soc_version_read();
|
||||
if (ret)
|
||||
return ret;
|
||||
switch (soc_version) {
|
||||
case SUNXI_SOC_SUN4I:
|
||||
case SUNXI_SOC_SUN5I:
|
||||
case SUNXI_SOC_SUN7I:
|
||||
return sun4i_dram_para_print(uboot);
|
||||
case SUNXI_SOC_SUN6I:
|
||||
return sun6i_dram_regs_print();
|
||||
case SUNXI_SOC_SUN8I:
|
||||
return sun8i_dram_regs_print();
|
||||
default:
|
||||
fprintf(stderr, "Error: unknown or unhandled Soc: 0x%04X\n",
|
||||
soc_version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "Error: wrong argument(s).\n");
|
||||
print_usage(argv[0]);
|
||||
return EINVAL;
|
||||
help:
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
29
nand-common.h
Normal file
29
nand-common.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Patrick H Wood, All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern int nand_part_a10 (int argc, char **argv, const char *cmd, int fd, int force);
|
||||
extern int nand_part_a20 (int argc, char **argv, const char *cmd, int fd, int force);
|
||||
extern int checkmbrs_a10 (int fd);
|
||||
extern int checkmbrs_a20 (int fd);
|
||||
extern void usage (const char *cmd);
|
||||
extern __u32 calc_crc32(void * buffer, __u32 length);
|
||||
1111
nand-image-builder.c
Normal file
1111
nand-image-builder.c
Normal file
File diff suppressed because it is too large
Load Diff
81
nand-part-a10.h
Normal file
81
nand-part-a10.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* drivers/block/sun4i_nand/nfd/mbr.h
|
||||
*
|
||||
* (C) Copyright 2007-2012
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.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 __MBR_H__
|
||||
#define __MBR_H__
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define MBR_MAGIC "softw311"
|
||||
#define MBR_VERSION 0x100
|
||||
#define nand_part nand_part_a10
|
||||
#define checkmbrs checkmbrs_a10
|
||||
|
||||
#define MAX_PART_COUNT 15 //max part count
|
||||
#define MBR_COPY_NUM 4 //mbr backup count
|
||||
|
||||
#define MBR_START_ADDRESS 0x0 //mbr start address
|
||||
#define MBR_SIZE 1024 //mbr size
|
||||
#define MBR_RESERVED (MBR_SIZE - 20 - (MAX_PART_COUNT * 64)) //mbr reserved space
|
||||
|
||||
// extern struct __NandDriverGlobal_t NandDriverInfo;
|
||||
|
||||
// extern struct __NandStorageInfo_t NandStorageInfo;
|
||||
|
||||
#define DiskSize (SECTOR_CNT_OF_SINGLE_PAGE * PAGE_CNT_OF_PHY_BLK * BLOCK_CNT_OF_DIE * \
|
||||
DIE_CNT_OF_CHIP * NandStorageInfo.ChipCnt / 1024 * DATA_BLK_CNT_OF_ZONE)
|
||||
|
||||
|
||||
struct nand_disk{
|
||||
unsigned long size;
|
||||
unsigned long offset;
|
||||
unsigned char type;
|
||||
};
|
||||
|
||||
/* part info */
|
||||
typedef struct tag_PARTITION{
|
||||
__u32 addrhi; //start address high 32 bit
|
||||
__u32 addrlo; //start address low 32 bit
|
||||
__u32 lenhi; //size high 32 bit
|
||||
__u32 lenlo; //size low 32 bit
|
||||
__u8 classname[12]; //major device name
|
||||
__u8 name[12]; //minor device name
|
||||
unsigned int user_type; //标志当前盘符所属于的用户
|
||||
unsigned int ro; //标志当前盘符的读写属性
|
||||
__u8 res[16]; //reserved
|
||||
}PARTITION;
|
||||
|
||||
/* mbr info */
|
||||
typedef struct tag_MBR{
|
||||
__u32 crc32; // crc, from byte 4 to mbr tail
|
||||
__u32 version; // version
|
||||
__u8 magic[8]; // magic number
|
||||
__u8 copy; // mbr backup count
|
||||
__u8 index; // current part no
|
||||
__u16 PartCount; // part counter
|
||||
PARTITION array[MAX_PART_COUNT];// part info
|
||||
__u8 res[MBR_RESERVED]; // reserved space
|
||||
}MBR;
|
||||
|
||||
int mbr2disks(struct nand_disk* disk_array);
|
||||
|
||||
#endif //__MBR_H__
|
||||
83
nand-part-a20.h
Normal file
83
nand-part-a20.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* drivers/block/sun4i_nand/nfd/mbr.h
|
||||
*
|
||||
* (C) Copyright 2007-2012
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.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 __MBR_H__
|
||||
#define __MBR_H__
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define MBR_MAGIC "softw411"
|
||||
#define MBR_VERSION 0x200
|
||||
#define nand_part nand_part_a20
|
||||
#define checkmbrs checkmbrs_a20
|
||||
|
||||
#define MAX_PART_COUNT 120 //max part count
|
||||
#define MBR_COPY_NUM 4 //mbr backup count
|
||||
|
||||
#define MBR_START_ADDRESS 0x0 //mbr start address
|
||||
#define MBR_SIZE 1024*16 //mbr size
|
||||
#define MBR_RESERVED (MBR_SIZE - 32 - (MAX_PART_COUNT * 128)) //mbr reserved space
|
||||
|
||||
// extern struct __NandDriverGlobal_t NandDriverInfo;
|
||||
|
||||
// extern struct __NandStorageInfo_t NandStorageInfo;
|
||||
|
||||
#define DiskSize (SECTOR_CNT_OF_SINGLE_PAGE * PAGE_CNT_OF_PHY_BLK * BLOCK_CNT_OF_DIE * \
|
||||
DIE_CNT_OF_CHIP * NandStorageInfo.ChipCnt / 1024 * DATA_BLK_CNT_OF_ZONE)
|
||||
|
||||
|
||||
struct nand_disk{
|
||||
unsigned long size;
|
||||
unsigned long offset;
|
||||
unsigned char type;
|
||||
};
|
||||
|
||||
/* part info */
|
||||
typedef struct nand_tag_PARTITION{
|
||||
unsigned int addrhi; //起始地址, 以扇区为单位
|
||||
unsigned int addrlo; //
|
||||
unsigned int lenhi; //长度
|
||||
unsigned int lenlo; //
|
||||
unsigned char classname[16]; //次设备名
|
||||
unsigned char name[16]; //主设备名
|
||||
unsigned int user_type; //用户类型
|
||||
unsigned int keydata; //关键数据,要求量产不丢失
|
||||
unsigned int ro; //读写属性
|
||||
unsigned char res[68]; //保留数据,匹配分区信息128字节
|
||||
}__attribute__ ((packed))PARTITION;
|
||||
|
||||
/* mbr info */
|
||||
typedef struct nand_tag_MBR{
|
||||
unsigned int crc32; // crc 1k - 4
|
||||
unsigned int version; // 版本信息, 0x00000100
|
||||
unsigned char magic[8]; //"softw411"
|
||||
unsigned int copy; //分数
|
||||
unsigned int index; //第几个MBR备份
|
||||
unsigned int PartCount; //分区个数
|
||||
unsigned int stamp[1]; //对齐
|
||||
PARTITION array[MAX_PART_COUNT]; //
|
||||
unsigned char res[MBR_RESERVED];
|
||||
}__attribute__ ((packed)) MBR;
|
||||
|
||||
int mbr2disks(struct nand_disk* disk_array);
|
||||
|
||||
#endif //__MBR_H__
|
||||
117
nand-part-main.c
Normal file
117
nand-part-main.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Patrick H Wood, All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <fcntl.h>
|
||||
#include "nand-common.h"
|
||||
#include "common.h"
|
||||
|
||||
void usage(const char *cmd)
|
||||
{
|
||||
puts("sunxi-nand-part " VERSION "\n");
|
||||
printf("usage: %s [-f a10|a20] nand-device\n", cmd);
|
||||
printf(" %s nand-device 'name2 len2 [usertype2]' ['name3 len3 [usertype3]'] ...\n", cmd);
|
||||
printf(" %s [-f a10|a20] nand-device start1 'name1 len1 [usertype1]' ['name2 len2 [usertype2]'] ...\n", cmd);
|
||||
}
|
||||
|
||||
typedef struct tag_CRC32_DATA
|
||||
{
|
||||
__u32 CRC; //int的大小是32位
|
||||
__u32 CRC_32_Tbl[256]; //用来保存码表
|
||||
}CRC32_DATA_t;
|
||||
|
||||
__u32 calc_crc32(void * buffer, __u32 length)
|
||||
{
|
||||
__u32 i, j;
|
||||
CRC32_DATA_t crc32; //
|
||||
__u32 CRC32 = 0xffffffff; //设置初始值
|
||||
crc32.CRC = 0;
|
||||
|
||||
for( i = 0; i < 256; ++i)//用++i以提高效率
|
||||
{
|
||||
crc32.CRC = i;
|
||||
for( j = 0; j < 8 ; ++j)
|
||||
{
|
||||
//这个循环实际上就是用"计算法"来求取CRC的校验码
|
||||
if(crc32.CRC & 1)
|
||||
crc32.CRC = (crc32.CRC >> 1) ^ 0xEDB88320;
|
||||
else //0xEDB88320就是CRC-32多项表达式的值
|
||||
crc32.CRC >>= 1;
|
||||
}
|
||||
crc32.CRC_32_Tbl[i] = crc32.CRC;
|
||||
}
|
||||
|
||||
CRC32 = 0xffffffff; //设置初始值
|
||||
for( i = 0; i < length; ++i)
|
||||
{
|
||||
CRC32 = crc32.CRC_32_Tbl[(CRC32^((unsigned char*)buffer)[i]) & 0xff] ^ (CRC32>>8);
|
||||
}
|
||||
//return CRC32;
|
||||
return CRC32^0xffffffff;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
char *nand = "/dev/nand";
|
||||
const char *cmd = argv[0];
|
||||
int fd;
|
||||
int force = 0; // force write even if magics and CRCs don't match
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strcmp(argv[0], "-f")) {
|
||||
if (!strcasecmp(argv[1], "a10"))
|
||||
force = 10;
|
||||
else if (!strcasecmp(argv[1], "a20"))
|
||||
force = 20;
|
||||
else {
|
||||
usage(cmd);
|
||||
return -1;
|
||||
}
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 0) {
|
||||
nand = argv[0];
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
fd = open(nand, O_RDWR);
|
||||
if (fd < 0) {
|
||||
usage(cmd);
|
||||
return -2;
|
||||
}
|
||||
if (force == 10)
|
||||
return nand_part_a10 (argc, argv, cmd, fd, force);
|
||||
if (force == 20)
|
||||
return nand_part_a20 (argc, argv, cmd, fd, force);
|
||||
|
||||
if (checkmbrs_a10(fd))
|
||||
return nand_part_a10 (argc, argv, cmd, fd, force);
|
||||
if (checkmbrs_a20(fd))
|
||||
return nand_part_a20 (argc, argv, cmd, fd, force);
|
||||
}
|
||||
327
nand-part.c
Normal file
327
nand-part.c
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* mbr.c
|
||||
* (C) Copyright 2012
|
||||
* Patrick H Wood, All rights reserved.
|
||||
* Heavily modified from the Allwinner file drivers/block/sun4i_nand/nfd/mbr.c.
|
||||
* (Allwinner copyright block retained below.)
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* drivers/block/sun4i_nand/nfd/mbr.c
|
||||
* (C) Copyright 2007-2012
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#ifdef __linux__
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/mount.h> /* BLKRRPART */
|
||||
#endif
|
||||
#include "nand-common.h"
|
||||
|
||||
// so far, only known formats are for A10 and A20
|
||||
#if defined(A10)
|
||||
# include "nand-part-a10.h"
|
||||
#elif defined(A20)
|
||||
# include "nand-part-a20.h"
|
||||
#endif
|
||||
|
||||
#define MAX_NAME 16
|
||||
|
||||
static void printmbrheader(MBR *mbr)
|
||||
{
|
||||
printf("mbr: version 0x%08x, magic %8.8s\n", mbr->version, mbr->magic);
|
||||
}
|
||||
|
||||
static MBR *_get_mbr(int fd, int mbr_num, int force)
|
||||
{
|
||||
MBR *mbr;
|
||||
|
||||
/*request mbr space*/
|
||||
mbr = malloc(sizeof(MBR));
|
||||
if(mbr == NULL)
|
||||
{
|
||||
printf("%s : request memory fail\n",__FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*get mbr from nand device*/
|
||||
lseek(fd,MBR_START_ADDRESS + MBR_SIZE*mbr_num,SEEK_SET);
|
||||
if(read(fd,mbr,MBR_SIZE) == MBR_SIZE)
|
||||
{
|
||||
/*checksum*/
|
||||
printf("check partition table copy %d: ", mbr_num);
|
||||
printmbrheader(mbr);
|
||||
if (force) {
|
||||
memcpy(mbr->magic, MBR_MAGIC, 8);
|
||||
mbr->version = MBR_VERSION;
|
||||
return mbr;
|
||||
}
|
||||
if(strncmp((char *)mbr->magic, MBR_MAGIC, 8))
|
||||
{
|
||||
printf("magic %8.8s is not %8s\n", mbr->magic, MBR_MAGIC);
|
||||
return NULL;
|
||||
}
|
||||
if(mbr->version != MBR_VERSION)
|
||||
{
|
||||
printf("version 0x%08x is not 0x%08x\n", mbr->version, MBR_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
if(*(__u32 *)mbr == calc_crc32((__u32 *)mbr + 1,MBR_SIZE - 4))
|
||||
{
|
||||
printf("OK\n");
|
||||
return mbr;
|
||||
}
|
||||
printf("BAD!\n");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static __s32 _free_mbr(MBR *mbr)
|
||||
{
|
||||
if(mbr)
|
||||
{
|
||||
free(mbr);
|
||||
mbr = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void printmbr(MBR *mbr)
|
||||
{
|
||||
unsigned int part_cnt;
|
||||
|
||||
printmbrheader(mbr);
|
||||
printf("%d partitions\n", mbr->PartCount);
|
||||
for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++)
|
||||
{
|
||||
printf("partition %2d: class = %12s, name = %12s, partition start = %8d, partition size = %8d user_type=%d\n",
|
||||
part_cnt + 1,
|
||||
mbr->array[part_cnt].classname,
|
||||
mbr->array[part_cnt].name,
|
||||
mbr->array[part_cnt].addrlo,
|
||||
mbr->array[part_cnt].lenlo,
|
||||
mbr->array[part_cnt].user_type);
|
||||
}
|
||||
}
|
||||
int checkmbrs(int fd)
|
||||
{
|
||||
int i;
|
||||
MBR *mbrs[MBR_COPY_NUM];
|
||||
MBR *mbr = NULL;
|
||||
|
||||
memset((void *) mbrs, 0, sizeof(mbrs));
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
mbrs[i] = _get_mbr(fd, i, 0);
|
||||
if (mbrs[i])
|
||||
mbr = mbrs[i];
|
||||
}
|
||||
if (!mbr) {
|
||||
printf("all partition tables are bad!\n");
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
if (mbrs[i])
|
||||
_free_mbr(mbrs[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
printmbr(mbr);
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
if (mbrs[i])
|
||||
_free_mbr(mbrs[i]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int writembrs(int fd, char names[][MAX_NAME], __u32 start, __u32 *lens, unsigned int *user_types, int nparts, int partoffset, int force)
|
||||
{
|
||||
unsigned int part_cnt = 0;
|
||||
int i;
|
||||
char yn = 'n';
|
||||
MBR *mbrs[MBR_COPY_NUM];
|
||||
MBR *mbr = NULL;
|
||||
FILE *backup;
|
||||
|
||||
memset((void *) mbrs, 0, sizeof(mbrs));
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
mbrs[i] = _get_mbr(fd, i, force);
|
||||
if (mbrs[i])
|
||||
mbr = mbrs[i];
|
||||
}
|
||||
if (!mbr) {
|
||||
printf("all partition tables are bad!\n");
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
if (mbrs[i])
|
||||
_free_mbr(mbrs[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// back up mbr data
|
||||
backup = fopen("nand_mbr.backup", "w");
|
||||
if (!backup) {
|
||||
printf("can't open nand_mbr.backup to back up mbr data\n");
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
if (mbrs[i])
|
||||
_free_mbr(mbrs[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(backup, "%d ", mbr->array[0].addrlo);
|
||||
for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++)
|
||||
{
|
||||
fprintf(backup, "'%s %d %d' ", mbr->array[part_cnt].name,
|
||||
mbr->array[part_cnt].lenlo, mbr->array[part_cnt].user_type);
|
||||
}
|
||||
fprintf(backup, "\n");
|
||||
fclose(backup);
|
||||
|
||||
mbr->PartCount = nparts + partoffset;
|
||||
if (partoffset)
|
||||
start = mbr->array[0].addrlo + mbr->array[0].lenlo;
|
||||
for(i = 0; i < nparts; i++) {
|
||||
strcpy((char *)mbr->array[i+partoffset].name, names[i]);
|
||||
strcpy((char *)mbr->array[i+partoffset].classname, "DISK");
|
||||
memset((void *) mbr->array[i+partoffset].res, 0, sizeof(mbr->array[i+partoffset].res));
|
||||
mbr->array[i+partoffset].user_type = user_types[i];
|
||||
mbr->array[i+partoffset].ro = 0;
|
||||
mbr->array[i+partoffset].addrhi = 0;
|
||||
mbr->array[i+partoffset].lenhi = 0;
|
||||
mbr->array[i+partoffset].addrlo = start;
|
||||
mbr->array[i+partoffset].lenlo = lens[i];
|
||||
start += lens[i];
|
||||
}
|
||||
|
||||
printf("\nready to write new partition tables:\n");
|
||||
printmbr(mbr);
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
if (mbrs[i])
|
||||
_free_mbr(mbrs[i]);
|
||||
}
|
||||
printf("\nwrite new partition tables? (Y/N)\n");
|
||||
read(0, &yn, 1);
|
||||
if (yn != 'Y' && yn != 'y') {
|
||||
printf("aborting\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < MBR_COPY_NUM; i++) {
|
||||
mbr->index = i;
|
||||
// calculate new checksum
|
||||
*(__u32 *)mbr = calc_crc32((__u32 *)mbr + 1,MBR_SIZE - 4);
|
||||
lseek(fd,MBR_START_ADDRESS + MBR_SIZE*i,SEEK_SET);
|
||||
write(fd,mbr,MBR_SIZE);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (ioctl(fd, BLKRRPART, NULL))
|
||||
perror("Failed rereading partition table");
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nand_part (int argc, char **argv, const char *cmd, int fd, int force)
|
||||
{
|
||||
int partoffset = 0;
|
||||
int i;
|
||||
char names[MAX_PART_COUNT][MAX_NAME];
|
||||
__u32 lens[MAX_PART_COUNT];
|
||||
unsigned int user_types[MAX_PART_COUNT];
|
||||
__u32 start;
|
||||
|
||||
|
||||
// parse name/len arguments
|
||||
memset((void *) user_types, 0, sizeof(user_types));
|
||||
if (argc > 0) {
|
||||
if (sscanf(argv[0], "%u", &start) != 1) {
|
||||
partoffset++;
|
||||
if (force) {
|
||||
printf("if using -f, must set info for first partition\n");
|
||||
usage(cmd);
|
||||
close(fd);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (start < MBR_SIZE * MBR_COPY_NUM / 512) {
|
||||
printf("Partition 1 starting offset must be at least %d\n", MBR_SIZE * MBR_COPY_NUM / 512);
|
||||
close(fd);
|
||||
return -3;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (sscanf(argv[i], "%s %d %d", names[i], &lens[i], &user_types[i]) < 2) {
|
||||
printf("bad 'name len' argument\n");
|
||||
usage(cmd);
|
||||
close(fd);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkmbrs(fd);
|
||||
|
||||
if (argc > MAX_PART_COUNT - partoffset) {
|
||||
printf("too many partitions specified (MAX 14)\n");
|
||||
usage(cmd);
|
||||
close(fd);
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
if (argc > 0) {
|
||||
if (writembrs(fd, names, start, lens, user_types, argc, partoffset, force)) {
|
||||
printf("\nverifying new partition tables:\n");
|
||||
checkmbrs(fd);
|
||||
#ifdef __linux__
|
||||
printf("rereading partition table... returned %d\n", ioctl(fd, BLKRRPART, 0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
173
phoenix_info.c
Normal file
173
phoenix_info.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "portable_endian.h"
|
||||
|
||||
struct phoenix_ptable {
|
||||
char signature[16]; /* "PHOENIX_CARD_IMG" */
|
||||
unsigned int unknown1; /* 0x00200100 */
|
||||
unsigned short parts; /* Number of partitions */
|
||||
unsigned short unknown2; /* 0x0001 */
|
||||
unsigned char pad[8];
|
||||
struct phoenix_entry {
|
||||
unsigned int start; /* 512 bytes blocks */
|
||||
unsigned int size; /* bytes */
|
||||
unsigned int unknown; /* ???? */
|
||||
unsigned int sig; /* "add\0" */
|
||||
} part[62];
|
||||
} ptable;
|
||||
|
||||
static int save_part(struct phoenix_ptable *ptable, int part, const char *dest, FILE *in)
|
||||
{
|
||||
int l = strlen(dest) + 16;
|
||||
char outname[l];
|
||||
FILE *out = stdout;
|
||||
char *buf = NULL;
|
||||
int ret = 0;
|
||||
snprintf(outname, l, dest, part);
|
||||
if (part > ptable->parts) {
|
||||
fprintf(stderr, "ERROR: Part index out of range\n");
|
||||
return -1;
|
||||
}
|
||||
buf = malloc(ptable->part[part].size);
|
||||
if (!buf)
|
||||
goto err;
|
||||
if (strcmp(outname, "-") != 0)
|
||||
out = fopen(outname, "wb");
|
||||
if (!out)
|
||||
goto err;
|
||||
if (fseek(in, ptable->part[part].start * 0x200, SEEK_SET) == -1)
|
||||
goto err;
|
||||
if (fread(buf, ptable->part[part].size, 1, in) != 1)
|
||||
goto err;
|
||||
if (fwrite(buf, ptable->part[part].size, 1, out) != 1)
|
||||
goto err;
|
||||
ret = 0;
|
||||
_exit:
|
||||
if (buf)
|
||||
free(buf);
|
||||
if (out != stdout)
|
||||
fclose(out);
|
||||
return ret;
|
||||
err:
|
||||
perror(NULL);
|
||||
ret = -1;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
static void usage(char **argv)
|
||||
{
|
||||
puts("phoenix-info " VERSION "\n");
|
||||
printf("Usage: %s [options] [phoenix_image]\n"
|
||||
" -v verbose\n"
|
||||
" -q quiet\n"
|
||||
" -p N part number\n"
|
||||
" -o X destination directory, file or pattern (%%d for part number)\n"
|
||||
" -s save all parts\n"
|
||||
, argv[0]
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
FILE *in = stdin;
|
||||
int verbose = 1;
|
||||
int save_parts = 0;
|
||||
int part = -1;
|
||||
int opt;
|
||||
const char *dest = "%d.img";
|
||||
|
||||
while ((opt = getopt(argc, argv, "vqso:p:?")) != -1) {
|
||||
switch(opt) {
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'q':
|
||||
if (verbose)
|
||||
verbose--;
|
||||
break;
|
||||
case 'o':
|
||||
dest = optarg;
|
||||
save_parts = 1;
|
||||
break;
|
||||
case 'p':
|
||||
save_parts = 1;
|
||||
part = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
save_parts = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (save_parts && !strchr(dest, '%')) {
|
||||
const char *t = dest;
|
||||
if (!*t)
|
||||
t = "./";
|
||||
if (t[strlen(t)-1] == '/' || !part) {
|
||||
int l = strlen(t) + strlen("/%d.img") + 1;
|
||||
char *tmp = malloc(l);
|
||||
snprintf(tmp, l, "%s/%%d.img", optarg);
|
||||
t = tmp;
|
||||
}
|
||||
dest = t;
|
||||
}
|
||||
if (argc > optind + 1) {
|
||||
usage(argv);
|
||||
exit(1);
|
||||
}
|
||||
if (optind < argc ) {
|
||||
in = fopen(argv[optind], "rb");
|
||||
}
|
||||
fseek(in, 0x1C00, SEEK_CUR);
|
||||
fread(&ptable, 1, 0x400, in);
|
||||
if (strncmp(ptable.signature, "PHOENIX_CARD_IMG", 16) != 0) {
|
||||
fprintf(stderr, "ERROR: Not a phoenix image\n");
|
||||
exit(1);
|
||||
}
|
||||
if (verbose > 1) {
|
||||
printf("???? : %08x\n", le32toh(ptable.unknown1));
|
||||
printf("Parts : %d\n", le16toh(ptable.parts));
|
||||
printf("???? : %08x\n", le16toh(ptable.unknown2));
|
||||
printf("pad : %02x%02x%02x%02x%02x%02x%02x%02x\n", ptable.pad[0], ptable.pad[1], ptable.pad[2], ptable.pad[3], ptable.pad[4], ptable.pad[5], ptable.pad[6], ptable.pad[7]);
|
||||
printf("\n");
|
||||
}
|
||||
for (i = 0; i < le16toh(ptable.parts); i++) {
|
||||
if (verbose && (part == -1 || part == i)) {
|
||||
printf("part %d:\n", i);
|
||||
printf("\tstart: 0x%08x (%u / 0x%08x)\n", le32toh(ptable.part[i].start)*512, le32toh(ptable.part[i].start), le32toh(ptable.part[i].start));
|
||||
printf("\tsize : %u\n", le32toh(ptable.part[i].size));
|
||||
printf("\t?????: %08x\n", le32toh(ptable.part[i].unknown));
|
||||
if (verbose > 1 || le32toh(ptable.part[i].sig) != 0x00646461)
|
||||
printf("\tsig??: %08x\n", le32toh(ptable.part[i].sig));
|
||||
printf("\n");
|
||||
}
|
||||
if (save_parts && (part == -1 || part == i)) {
|
||||
save_part(&ptable, i, dest, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
432
pio.c
Normal file
432
pio.c
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* (C) Copyright 2011 Henrik Nordstrom <henrik@henriknordstrom.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
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#ifndef NO_MMAP
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "portable_endian.h"
|
||||
|
||||
#define PIO_REG_SIZE 0x228 /*0x300*/
|
||||
#define PIO_PORT_SIZE 0x24
|
||||
|
||||
struct pio_status {
|
||||
int mul_sel;
|
||||
int pull;
|
||||
int drv_level;
|
||||
int data;
|
||||
};
|
||||
|
||||
#define PIO_REG_CFG(B, N, I) ((B) + (N)*0x24 + ((I)<<2) + 0x00)
|
||||
#define PIO_REG_DLEVEL(B, N, I) ((B) + (N)*0x24 + ((I)<<2) + 0x14)
|
||||
#define PIO_REG_PULL(B, N, I) ((B) + (N)*0x24 + ((I)<<2) + 0x1C)
|
||||
#define PIO_REG_DATA(B, N) ((B) + (N)*0x24 + 0x10)
|
||||
#define PIO_NR_PORTS 9 /* A-I */
|
||||
|
||||
#define LE32TOH(X) le32toh(*((uint32_t*)(X)))
|
||||
|
||||
static int pio_get(const char *buf, uint32_t port, uint32_t port_num, struct pio_status *pio)
|
||||
{
|
||||
uint32_t val;
|
||||
uint32_t port_num_func, port_num_pull;
|
||||
uint32_t offset_func, offset_pull;
|
||||
|
||||
port_num_func = port_num >> 3;
|
||||
offset_func = ((port_num & 0x07) << 2);
|
||||
|
||||
port_num_pull = port_num >> 4;
|
||||
offset_pull = ((port_num & 0x0f) << 1);
|
||||
|
||||
/* func */
|
||||
val = LE32TOH(PIO_REG_CFG(buf, port, port_num_func));
|
||||
pio->mul_sel = (val>>offset_func) & 0x07;
|
||||
|
||||
/* pull */
|
||||
val = LE32TOH(PIO_REG_PULL(buf, port, port_num_pull));
|
||||
pio->pull = (val>>offset_pull) & 0x03;
|
||||
|
||||
/* dlevel */
|
||||
val = LE32TOH(PIO_REG_DLEVEL(buf, port, port_num_pull));
|
||||
pio->drv_level = (val>>offset_pull) & 0x03;
|
||||
|
||||
/* i/o data */
|
||||
if (pio->mul_sel > 1)
|
||||
pio->data = -1;
|
||||
else {
|
||||
val = LE32TOH(PIO_REG_DATA(buf, port));
|
||||
pio->data = (val >> port_num) & 0x01;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pio_set(char *buf, uint32_t port, uint32_t port_num, struct pio_status *pio)
|
||||
{
|
||||
uint32_t *addr, val;
|
||||
uint32_t port_num_func, port_num_pull;
|
||||
uint32_t offset_func, offset_pull;
|
||||
|
||||
port_num_func = port_num >> 3;
|
||||
offset_func = ((port_num & 0x07) << 2);
|
||||
|
||||
port_num_pull = port_num >> 4;
|
||||
offset_pull = ((port_num & 0x0f) << 1);
|
||||
|
||||
/* func */
|
||||
if (pio->mul_sel >= 0) {
|
||||
addr = (uint32_t*)PIO_REG_CFG(buf, port, port_num_func);
|
||||
val = le32toh(*addr);
|
||||
val &= ~(0x07 << offset_func);
|
||||
val |= (pio->mul_sel & 0x07) << offset_func;
|
||||
*addr = htole32(val);
|
||||
}
|
||||
|
||||
/* pull */
|
||||
if (pio->pull >= 0) {
|
||||
addr = (uint32_t*)PIO_REG_PULL(buf, port, port_num_pull);
|
||||
val = le32toh(*addr);
|
||||
val &= ~(0x03 << offset_pull);
|
||||
val |= (pio->pull & 0x03) << offset_pull;
|
||||
*addr = htole32(val);
|
||||
}
|
||||
|
||||
/* dlevel */
|
||||
if (pio->drv_level >= 0) {
|
||||
addr = (uint32_t*)PIO_REG_DLEVEL(buf, port, port_num_pull);
|
||||
val = le32toh(*addr);
|
||||
val &= ~(0x03 << offset_pull);
|
||||
val |= (pio->drv_level & 0x03) << offset_pull;
|
||||
*addr = htole32(val);
|
||||
}
|
||||
|
||||
/* data */
|
||||
if (pio->data >= 0) {
|
||||
addr = (uint32_t*)PIO_REG_DATA(buf, port);
|
||||
val = le32toh(*addr);
|
||||
if (pio->data)
|
||||
val |= (0x01 << port_num);
|
||||
else
|
||||
val &= ~(0x01 << port_num);
|
||||
*addr = htole32(val);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pio_print(int port, int port_nr, struct pio_status *pio)
|
||||
{
|
||||
printf("P%c%d", 'A'+port, port_nr);
|
||||
printf("<%x>", pio->mul_sel);
|
||||
printf("<%x>", pio->pull);
|
||||
printf("<%x>", pio->drv_level);
|
||||
if (pio->data >= 0)
|
||||
printf("<%x>", pio->data);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
|
||||
static void print(const char *buf)
|
||||
{
|
||||
int port, i;
|
||||
struct pio_status pio;
|
||||
for (port=0; port < PIO_NR_PORTS; port++) {
|
||||
for (i=0; i<32; i++) {
|
||||
if (pio_get(buf, port, i, &pio)) {
|
||||
pio_print(port, i, &pio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *argv0;
|
||||
|
||||
static __attribute__((noreturn)) void usage(int rc )
|
||||
{
|
||||
fputs("sunxi-pio " VERSION "\n\n", stderr);
|
||||
fprintf(stderr, "usage: %s -m|-i input [-o output] pin..\n", argv0);
|
||||
fprintf(stderr," -m mmap - read pin state from system\n");
|
||||
fprintf(stderr," -i read pin state from file\n");
|
||||
fprintf(stderr," -o save pin state data to file\n");
|
||||
fprintf(stderr," print Show all pins\n");
|
||||
fprintf(stderr," Pxx Show pin\n");
|
||||
fprintf(stderr," Pxx<mode><pull><drive><data> Configure pin\n");
|
||||
fprintf(stderr," Pxx=data,drive Configure GPIO output\n");
|
||||
fprintf(stderr," Pxx*count Oscillate GPIO output (mmap mode only)\n");
|
||||
fprintf(stderr," Pxx?pull Configure GPIO input\n");
|
||||
fprintf(stderr," clean Clean input pins\n");
|
||||
fprintf(stderr, "\n mode 0-7, 0=input, 1=output, 2-7 I/O function\n");
|
||||
fprintf(stderr, " pull 0=none, 1=up, 2=down\n");
|
||||
fprintf(stderr, " drive 0-3, I/O drive level\n");
|
||||
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void parse_pin(int *port, int *pin, const char *name)
|
||||
{
|
||||
if (*name == 'P') name++;
|
||||
*port = *name++ - 'A';
|
||||
*pin = atoi(name);
|
||||
}
|
||||
|
||||
static void cmd_show_pin(char *buf, const char *pin)
|
||||
{
|
||||
int port, port_nr;
|
||||
struct pio_status pio;
|
||||
parse_pin(&port, &port_nr, pin);
|
||||
if (!pio_get(buf, port, port_nr, &pio))
|
||||
usage(1);
|
||||
pio_print(port, port_nr, &pio);
|
||||
}
|
||||
|
||||
static int parse_int(int *dst, const char *in)
|
||||
{
|
||||
int value;
|
||||
char *next;
|
||||
errno = 0;
|
||||
value = strtol(in, &next, 0);
|
||||
if (!errno && next != in) {
|
||||
*dst = value;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cmd_set_pin(char *buf, const char *pin)
|
||||
{
|
||||
int port, port_nr;
|
||||
const char *t = pin;
|
||||
struct pio_status pio;
|
||||
parse_pin(&port, &port_nr, pin);
|
||||
if (!pio_get(buf, port, port_nr, &pio))
|
||||
usage(1);
|
||||
if ((t = strchr(pin, '='))) {
|
||||
pio.mul_sel = 1;
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.data, t);
|
||||
}
|
||||
if (t)
|
||||
t = strchr(t, ',');
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.drv_level, t);
|
||||
}
|
||||
} else if ((t = strchr(pin, '?'))) {
|
||||
pio.mul_sel = 0;
|
||||
pio.data = 0;
|
||||
pio.drv_level = 0;
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.pull, t);
|
||||
}
|
||||
} else if ((t = strchr(pin, '<'))) {
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.mul_sel, t);
|
||||
}
|
||||
if (t)
|
||||
t = strchr(t, '<');
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.pull, t);
|
||||
}
|
||||
if (t)
|
||||
t = strchr(t, '<');
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.drv_level, t);
|
||||
}
|
||||
if (t)
|
||||
t = strchr(t, '<');
|
||||
if (t) {
|
||||
t++;
|
||||
parse_int(&pio.data, t);
|
||||
}
|
||||
}
|
||||
pio_set(buf, port, port_nr, &pio);
|
||||
}
|
||||
|
||||
static void cmd_oscillate(char *buf, const char *pin)
|
||||
{
|
||||
int port, port_nr;
|
||||
const char *t = pin;
|
||||
int i, n = 0;
|
||||
uint32_t *addr, val;
|
||||
|
||||
parse_pin(&port, &port_nr, pin);
|
||||
{
|
||||
struct pio_status pio;
|
||||
if (!pio_get(buf, port, port_nr, &pio))
|
||||
usage(1);
|
||||
pio.mul_sel = 1;
|
||||
pio_set(buf, port, port_nr, &pio);
|
||||
}
|
||||
|
||||
addr = (uint32_t*)PIO_REG_DATA(buf, port);
|
||||
t = strchr(pin, '*');
|
||||
parse_int(&n, t+1);
|
||||
val = le32toh(*addr);
|
||||
for (i = 0; i < n; i++) {
|
||||
val ^= 1 << port_nr;
|
||||
*addr = htole32(val);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_clean(char *buf)
|
||||
{
|
||||
int port, i;
|
||||
struct pio_status pio;
|
||||
for (port=0; port < PIO_NR_PORTS; port++) {
|
||||
for (i=0; i<32; i++) {
|
||||
if (pio_get(buf, port, i, &pio)) {
|
||||
if (pio.mul_sel == 0) {
|
||||
pio.data = 0;
|
||||
pio_set(buf, port, i, &pio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int do_command(char *buf, const char **args, int UNUSED(argc))
|
||||
{
|
||||
const char *command = args[0];
|
||||
if (*command == 'P') {
|
||||
if (strchr(command, '<'))
|
||||
cmd_set_pin(buf, command);
|
||||
else if (strchr(command, '='))
|
||||
cmd_set_pin(buf, command);
|
||||
else if (strchr(command, '?'))
|
||||
cmd_set_pin(buf, command);
|
||||
else if (strchr(command, '*'))
|
||||
cmd_oscillate(buf, command);
|
||||
else
|
||||
cmd_show_pin(buf, command);
|
||||
}
|
||||
else if (strcmp(command, "print") == 0)
|
||||
print(buf);
|
||||
else if (strcmp(command, "clean") == 0)
|
||||
cmd_clean(buf);
|
||||
else usage(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
FILE *in = NULL;
|
||||
FILE *out = NULL;
|
||||
const char *in_name = NULL;
|
||||
const char *out_name = NULL;
|
||||
char buf_[PIO_REG_SIZE];
|
||||
char *buf = buf_;
|
||||
int do_mmap = 0;
|
||||
|
||||
argv0 = argv[0];
|
||||
|
||||
while ((opt = getopt(argc, argv, "i:o:m")) != -1) {
|
||||
switch(opt) {
|
||||
case '?':
|
||||
usage(0);
|
||||
case 'm':
|
||||
do_mmap = 1;
|
||||
break;
|
||||
case 'i':
|
||||
in_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
out_name = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!in_name && !do_mmap)
|
||||
usage(1);
|
||||
if (do_mmap) {
|
||||
#ifdef NO_MMAP
|
||||
errno = ENOSYS; /* Function not implemented */
|
||||
perror("mmap PIO");
|
||||
#else
|
||||
int pagesize = sysconf(_SC_PAGESIZE);
|
||||
int fd = open("/dev/mem",O_RDWR);
|
||||
int addr = 0x01c20800 & ~(pagesize-1);
|
||||
int offset = 0x01c20800 & (pagesize-1);
|
||||
if (fd == -1) {
|
||||
perror("open /dev/mem");
|
||||
exit(1);
|
||||
}
|
||||
buf = mmap(NULL, (0x800 + pagesize - 1) & ~(pagesize-1), PROT_WRITE|PROT_READ, MAP_SHARED, fd, addr);
|
||||
if (!buf) {
|
||||
perror("mmap PIO");
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
buf += offset;
|
||||
#endif
|
||||
}
|
||||
if (in_name) {
|
||||
if (strcmp(in_name, "-") == 0) {
|
||||
in = stdin;
|
||||
} else {
|
||||
in = fopen(in_name, "rb");
|
||||
if (!in) {
|
||||
perror("open input");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (in) {
|
||||
if (fread(buf, PIO_REG_SIZE, 1, in) != 1) {
|
||||
perror("read input");
|
||||
exit(1);
|
||||
}
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
while(optind < argc) {
|
||||
optind += do_command(buf, (const char **)(argv + optind), argc - optind);
|
||||
}
|
||||
|
||||
if (out_name) {
|
||||
if (strcmp(out_name, "-") == 0) {
|
||||
out = stdout;
|
||||
} else {
|
||||
out = fopen(out_name, "wb");
|
||||
if (!out) {
|
||||
perror("open output");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (fwrite(buf, PIO_REG_SIZE, 1, out) != 1) {
|
||||
perror("write output");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
165
progress.c
Normal file
165
progress.c
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "progress.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Less reliable than clock_gettime, but does not require linking with -lrt */
|
||||
inline double gettime(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec + (double)tv.tv_usec / 1000000.;
|
||||
}
|
||||
|
||||
/* Calculate transfer rate (in bytes per second) */
|
||||
inline double rate(size_t transferred, double elapsed)
|
||||
{
|
||||
if (elapsed > 0)
|
||||
return (double)transferred / elapsed;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
/* Estimate remaining time ("ETA") for given transfer rate */
|
||||
inline double estimate(size_t remaining, double rate)
|
||||
{
|
||||
if (rate > 0)
|
||||
return (double)remaining / rate;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
/* Return ETA (in seconds) as string, formatted to minutes and seconds */
|
||||
const char *format_ETA(double remaining)
|
||||
{
|
||||
static char result[6] = "";
|
||||
|
||||
int seconds = remaining + 0.5; /* simplistic round() */
|
||||
if (seconds >= 0 && seconds < 6000) {
|
||||
snprintf(result, sizeof(result),
|
||||
"%02d:%02d", seconds / 60, seconds % 60);
|
||||
return result;
|
||||
}
|
||||
return "--:--";
|
||||
}
|
||||
|
||||
/* Private progress state variable */
|
||||
|
||||
typedef struct {
|
||||
progress_cb_t callback;
|
||||
size_t total;
|
||||
size_t done;
|
||||
double start; /* start point (timestamp) for rate and ETA calculation */
|
||||
} progress_private_t;
|
||||
|
||||
static progress_private_t progress = {
|
||||
.callback = NULL,
|
||||
.start = 0.
|
||||
};
|
||||
|
||||
/* 'External' API */
|
||||
|
||||
void progress_start(progress_cb_t callback, size_t expected_total)
|
||||
{
|
||||
progress.callback = callback;
|
||||
progress.total = expected_total;
|
||||
progress.done = 0;
|
||||
progress.start = gettime(); /* reset start time */
|
||||
}
|
||||
|
||||
/* Update progress status, passing information to the callback function. */
|
||||
void progress_update(size_t bytes_done)
|
||||
{
|
||||
progress.done += bytes_done;
|
||||
if (progress.callback)
|
||||
progress.callback(progress.total, progress.done);
|
||||
}
|
||||
|
||||
/* Return relative / "elapsed" time, since progress_start() */
|
||||
static inline double progress_elapsed(void)
|
||||
{
|
||||
if (progress.start != 0.)
|
||||
return gettime() - progress.start;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
/* Callback function implementing a simple progress bar written to stdout */
|
||||
void progress_bar(size_t total, size_t done)
|
||||
{
|
||||
static const int WIDTH = 48; /* # of characters to use for progress bar */
|
||||
|
||||
float ratio = total > 0 ? (float)done / total : 0;
|
||||
int i, pos = WIDTH * ratio;
|
||||
double speed = rate(done, progress_elapsed());
|
||||
double eta = estimate(total - done, speed);
|
||||
|
||||
printf("\r%3.0f%% [", ratio * 100); /* current percentage */
|
||||
for (i = 0; i < pos; i++) putchar('=');
|
||||
for (i = pos; i < WIDTH; i++) putchar(' ');
|
||||
if (done < total)
|
||||
printf("]%6.1f kB/s, ETA %s ", kilo(speed), format_ETA(eta));
|
||||
else
|
||||
/* transfer complete, output totals plus a newline */
|
||||
printf("] %5.0f kB, %6.1f kB/s\n", kilo(done), kilo(speed));
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/*
|
||||
* Progress callback that emits percentage numbers, each on a separate line.
|
||||
* The output is suitable for piping it into "dialog --gauge".
|
||||
*
|
||||
* sunxi-fel multiwrite-with-gauge <...> \
|
||||
* | dialog --title "FEL upload progress" \
|
||||
* --gauge "" 5 70
|
||||
*/
|
||||
void progress_gauge(size_t total, size_t done)
|
||||
{
|
||||
if (total > 0) {
|
||||
printf("%.0f\n", (float)done / total * 100);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A more sophisticated version of progress_gauge() that also updates the
|
||||
* prompt (caption) with additional information. This uses a feature of
|
||||
* the dialog utility that parses "XXX" delimiters - see 'man dialog'.
|
||||
*
|
||||
* sunxi-fel multiwrite-with-xgauge <...> \
|
||||
* | dialog --title "FEL upload progress" \
|
||||
* --backtitle "Please wait..." \
|
||||
* --gauge "" 6 70
|
||||
*/
|
||||
void progress_gauge_xxx(size_t total, size_t done)
|
||||
{
|
||||
if (total > 0) {
|
||||
double speed = rate(done, progress_elapsed());
|
||||
double eta = estimate(total - done, speed);
|
||||
printf("XXX\n");
|
||||
printf("%.0f\n", (float)done / total * 100);
|
||||
if (done < total)
|
||||
printf("%zu of %zu, %.1f kB/s, ETA %s\n",
|
||||
done, total, kilo(speed), format_ETA(eta));
|
||||
else
|
||||
printf("Done: %.1f kB, at %.1f kB/s\n",
|
||||
kilo(done), kilo(speed));
|
||||
printf("XXX\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
41
progress.h
Normal file
41
progress.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_PROGRESS_H
|
||||
#define _SUNXI_TOOLS_PROGRESS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* function pointer type for a progress callback / notification */
|
||||
typedef void (*progress_cb_t)(size_t total, size_t done);
|
||||
|
||||
/* conversion helper macros */
|
||||
#define kilo(value) ((double)(value) / 1000.) /* SI prefix "k" */
|
||||
#define kibi(value) ((double)(value) / 1024.) /* binary prefix "Ki", "K" */
|
||||
|
||||
double gettime(void);
|
||||
double rate(size_t transferred, double elapsed);
|
||||
double estimate(size_t remaining, double rate);
|
||||
|
||||
void progress_start(progress_cb_t callback, size_t expected_total);
|
||||
void progress_update(size_t bytes_done);
|
||||
|
||||
/* progress callback implementations for various display styles */
|
||||
void progress_bar(size_t total, size_t done);
|
||||
void progress_gauge(size_t total, size_t done);
|
||||
void progress_gauge_xxx(size_t total, size_t done);
|
||||
|
||||
#endif /* _SUNXI_TOOLS_PROGRESS_H */
|
||||
268
script.c
Normal file
268
script.c
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "script.h"
|
||||
|
||||
/*
|
||||
*/
|
||||
struct script *script_new(void)
|
||||
{
|
||||
struct script *script;
|
||||
if ((script = malloc(sizeof(*script))))
|
||||
list_init(&script->sections);
|
||||
return script;
|
||||
}
|
||||
|
||||
void script_delete(struct script *script)
|
||||
{
|
||||
struct list_entry *o;
|
||||
|
||||
assert(script);
|
||||
|
||||
while ((o = list_last(&script->sections))) {
|
||||
struct script_section *section = container_of(o,
|
||||
struct script_section, sections);
|
||||
|
||||
script_section_delete(section);
|
||||
}
|
||||
|
||||
free(script);
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
struct script_section *script_section_new(struct script *script,
|
||||
const char *name)
|
||||
{
|
||||
struct script_section *section;
|
||||
|
||||
assert(script);
|
||||
assert(name && *name);
|
||||
|
||||
if ((section = malloc(sizeof(*section)))) {
|
||||
size_t l = strlen(name);
|
||||
if (l>31) /* truncate */
|
||||
l=31;
|
||||
memcpy(section->name, name, l);
|
||||
section->name[l] = '\0';
|
||||
|
||||
list_init(§ion->entries);
|
||||
list_append(&script->sections, §ion->sections);
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
void script_section_delete(struct script_section *section)
|
||||
{
|
||||
struct list_entry *o;
|
||||
|
||||
assert(section);
|
||||
|
||||
while ((o = list_last(§ion->entries))) {
|
||||
struct script_entry *entry = container_of(o,
|
||||
struct script_entry, entries);
|
||||
|
||||
script_entry_delete(entry);
|
||||
}
|
||||
|
||||
if (!list_empty(§ion->sections))
|
||||
list_remove(§ion->sections);
|
||||
}
|
||||
|
||||
struct script_section *script_find_section(struct script *script,
|
||||
const char *name)
|
||||
{
|
||||
struct list_entry *o;
|
||||
struct script_section *section;
|
||||
|
||||
assert(script);
|
||||
assert(name);
|
||||
|
||||
for (o = list_first(&script->sections); o;
|
||||
o = list_next(&script->sections, o)) {
|
||||
section = container_of(o, struct script_section, sections);
|
||||
|
||||
if (strcmp(section->name, name) == 0)
|
||||
return section;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static inline void script_entry_append(struct script_section *section,
|
||||
struct script_entry *entry,
|
||||
enum script_value_type type,
|
||||
const char *name)
|
||||
{
|
||||
size_t l;
|
||||
|
||||
assert(section);
|
||||
assert(entry);
|
||||
assert(name);
|
||||
|
||||
l = strlen(name);
|
||||
if (l>31) /* truncate */
|
||||
l=31;
|
||||
memcpy(entry->name, name, l);
|
||||
entry->name[l] = '\0';
|
||||
|
||||
entry->type = type;
|
||||
|
||||
list_append(§ion->entries, &entry->entries);
|
||||
}
|
||||
|
||||
void script_entry_delete(struct script_entry *entry)
|
||||
{
|
||||
void *container;
|
||||
|
||||
assert(entry);
|
||||
assert(entry->type == SCRIPT_VALUE_TYPE_SINGLE_WORD ||
|
||||
entry->type == SCRIPT_VALUE_TYPE_STRING ||
|
||||
entry->type == SCRIPT_VALUE_TYPE_GPIO ||
|
||||
entry->type == SCRIPT_VALUE_TYPE_NULL);
|
||||
|
||||
if (!list_empty(&entry->entries))
|
||||
list_remove(&entry->entries);
|
||||
|
||||
switch(entry->type) {
|
||||
case SCRIPT_VALUE_TYPE_SINGLE_WORD:
|
||||
container = container_of(entry, struct script_single_entry, entry);
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_STRING:
|
||||
container = container_of(entry, struct script_string_entry, entry);
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_GPIO:
|
||||
container = container_of(entry, struct script_gpio_entry, entry);
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_NULL:
|
||||
container = container_of(entry, struct script_null_entry, entry);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
free(container);
|
||||
}
|
||||
|
||||
struct script_null_entry *script_null_entry_new(struct script_section *section,
|
||||
const char *name)
|
||||
{
|
||||
struct script_null_entry *entry;
|
||||
|
||||
assert(section);
|
||||
assert(name && *name);
|
||||
|
||||
if ((entry = malloc(sizeof(*entry)))) {
|
||||
script_entry_append(section, &entry->entry,
|
||||
SCRIPT_VALUE_TYPE_NULL, name);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct script_single_entry *script_single_entry_new(struct script_section *section,
|
||||
const char *name,
|
||||
uint32_t value)
|
||||
{
|
||||
struct script_single_entry *entry;
|
||||
|
||||
assert(section);
|
||||
assert(name && *name);
|
||||
|
||||
if ((entry = malloc(sizeof(*entry)))) {
|
||||
entry->value = value;
|
||||
|
||||
script_entry_append(section, &entry->entry,
|
||||
SCRIPT_VALUE_TYPE_SINGLE_WORD, name);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct script_string_entry *script_string_entry_new(struct script_section *section,
|
||||
const char *name,
|
||||
size_t l, const char *s)
|
||||
{
|
||||
struct script_string_entry *entry;
|
||||
|
||||
assert(section);
|
||||
assert(name);
|
||||
assert(s);
|
||||
|
||||
if ((entry = malloc(sizeof(*entry)+l+1))) {
|
||||
entry->l = l;
|
||||
memcpy(entry->string, s, l);
|
||||
entry->string[l] = '\0';
|
||||
|
||||
script_entry_append(section, &entry->entry,
|
||||
SCRIPT_VALUE_TYPE_STRING, name);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct script_gpio_entry *script_gpio_entry_new(struct script_section *section,
|
||||
const char *name,
|
||||
unsigned port, unsigned num,
|
||||
int32_t data[4])
|
||||
{
|
||||
struct script_gpio_entry *entry;
|
||||
|
||||
assert(section);
|
||||
assert(name && *name);
|
||||
|
||||
if ((entry = malloc(sizeof(*entry)))) {
|
||||
entry->port = port;
|
||||
entry->port_num = num;
|
||||
for (int i=0; i<4; i++)
|
||||
entry->data[i] = data[i];
|
||||
|
||||
script_entry_append(section, &entry->entry,
|
||||
SCRIPT_VALUE_TYPE_GPIO, name);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct script_entry *script_find_entry(struct script_section *section,
|
||||
const char *name)
|
||||
{
|
||||
struct list_entry *o;
|
||||
struct script_entry *ep;
|
||||
|
||||
assert(section);
|
||||
assert(name);
|
||||
|
||||
for (o = list_first(§ion->entries); o;
|
||||
o = list_next(§ion->entries, o)) {
|
||||
ep = container_of(o, struct script_entry, entries);
|
||||
|
||||
if (strcmp(ep->name, name) == 0)
|
||||
return ep;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
120
script.h
Normal file
120
script.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_SCRIPT_H
|
||||
#define _SUNXI_TOOLS_SCRIPT_H
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define GPIO_BANK_MAX 14 /* N, (zero-based) index 13 */
|
||||
|
||||
/** head of the data tree */
|
||||
struct script {
|
||||
struct list_entry sections;
|
||||
};
|
||||
|
||||
/** head of each section */
|
||||
struct script_section {
|
||||
char name[32];
|
||||
|
||||
struct list_entry sections;
|
||||
struct list_entry entries;
|
||||
};
|
||||
|
||||
/** types of values */
|
||||
enum script_value_type {
|
||||
SCRIPT_VALUE_TYPE_SINGLE_WORD = 1,
|
||||
SCRIPT_VALUE_TYPE_STRING,
|
||||
SCRIPT_VALUE_TYPE_MULTI_WORD,
|
||||
SCRIPT_VALUE_TYPE_GPIO,
|
||||
SCRIPT_VALUE_TYPE_NULL,
|
||||
};
|
||||
|
||||
/** generic entry */
|
||||
struct script_entry {
|
||||
char name[32];
|
||||
enum script_value_type type;
|
||||
|
||||
struct list_entry entries;
|
||||
};
|
||||
|
||||
/** null entry */
|
||||
struct script_null_entry {
|
||||
struct script_entry entry;
|
||||
};
|
||||
|
||||
/** entry with 32b value */
|
||||
struct script_single_entry {
|
||||
struct script_entry entry;
|
||||
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
/** entry with string value */
|
||||
struct script_string_entry {
|
||||
struct script_entry entry;
|
||||
|
||||
size_t l;
|
||||
char string[];
|
||||
};
|
||||
|
||||
/** entry describing a GPIO */
|
||||
struct script_gpio_entry {
|
||||
struct script_entry entry;
|
||||
|
||||
unsigned port, port_num;
|
||||
int32_t data[4];
|
||||
};
|
||||
|
||||
/** create a new script tree */
|
||||
struct script *script_new(void);
|
||||
/** deletes a tree recursively */
|
||||
void script_delete(struct script *);
|
||||
|
||||
/** create a new section appended to a given tree */
|
||||
struct script_section *script_section_new(struct script *script,
|
||||
const char *name);
|
||||
/** deletes a section recursvely and removes it from the script */
|
||||
void script_section_delete(struct script_section *section);
|
||||
|
||||
/** find existing section */
|
||||
struct script_section *script_find_section(struct script *script,
|
||||
const char *name);
|
||||
|
||||
/** deletes an entry and removes it from the section */
|
||||
void script_entry_delete(struct script_entry *entry);
|
||||
|
||||
/** create a new empty/null entry appended to a section */
|
||||
struct script_null_entry *script_null_entry_new(struct script_section *section,
|
||||
const char *name);
|
||||
/** create a new single word entry appended to a section */
|
||||
struct script_single_entry *script_single_entry_new(struct script_section *section,
|
||||
const char *name,
|
||||
uint32_t value);
|
||||
/** create a new string entry appended to a section */
|
||||
struct script_string_entry *script_string_entry_new(struct script_section *section,
|
||||
const char *name,
|
||||
size_t l, const char *s);
|
||||
/** create a new GPIO entry appended to a section */
|
||||
struct script_gpio_entry *script_gpio_entry_new(struct script_section *script,
|
||||
const char *name,
|
||||
unsigned port, unsigned num,
|
||||
int32_t data[4]);
|
||||
|
||||
/** find existing entry in a giving section */
|
||||
struct script_entry *script_find_entry(struct script_section *section,
|
||||
const char *name);
|
||||
#endif
|
||||
356
script_bin.c
Normal file
356
script_bin.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "script.h"
|
||||
#include "script_bin.h"
|
||||
|
||||
#define pr_info(...) pr_error("fexc-bin: " __VA_ARGS__)
|
||||
#define pr_err(...) pr_error("E: fexc-bin: " __VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define pr_debug(...) pr_error("D: fexc-bin: " __VA_ARGS__)
|
||||
#else
|
||||
#define pr_debug(...)
|
||||
#endif
|
||||
|
||||
#define PTR(B, OFF) (void*)((char*)(B)+(OFF))
|
||||
#define WORDS(S) (((S)+(sizeof(uint32_t)-1))/(sizeof(uint32_t)))
|
||||
|
||||
/*
|
||||
* generator
|
||||
*/
|
||||
size_t script_bin_size(struct script *script,
|
||||
size_t *sections, size_t *entries)
|
||||
{
|
||||
size_t words = 0, bin_size = 0;
|
||||
struct list_entry *ls, *le;
|
||||
struct script_section *section;
|
||||
struct script_entry *entry;
|
||||
struct script_string_entry *string;
|
||||
|
||||
*sections = *entries = 0;
|
||||
|
||||
/* count */
|
||||
for (ls = list_first(&script->sections); ls;
|
||||
ls = list_next(&script->sections, ls)) {
|
||||
section = container_of(ls, struct script_section, sections);
|
||||
size_t c = 0;
|
||||
|
||||
for (le = list_first(§ion->entries); le;
|
||||
le = list_next(§ion->entries, le)) {
|
||||
size_t size = 0;
|
||||
entry = container_of(le, struct script_entry, entries);
|
||||
c++;
|
||||
|
||||
switch(entry->type) {
|
||||
case SCRIPT_VALUE_TYPE_NULL:
|
||||
case SCRIPT_VALUE_TYPE_SINGLE_WORD:
|
||||
size = sizeof(uint32_t);
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_STRING:
|
||||
string = container_of(entry, struct script_string_entry,
|
||||
entry);
|
||||
size = string->l;
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_GPIO:
|
||||
size = sizeof(struct script_bin_gpio_value);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
words += WORDS(size);
|
||||
}
|
||||
*sections += 1;
|
||||
*entries += c;
|
||||
}
|
||||
|
||||
bin_size = sizeof(struct script_bin_head) +
|
||||
(*sections)*sizeof(struct script_bin_section) +
|
||||
(*entries)*sizeof(struct script_bin_entry) +
|
||||
words*sizeof(uint32_t);
|
||||
pr_debug("sections:%zu entries:%zu data:%zu/%zu -> %zu\n",
|
||||
*sections, *entries, words, words*sizeof(uint32_t),
|
||||
bin_size);
|
||||
return bin_size;
|
||||
}
|
||||
|
||||
int script_generate_bin(void *bin, size_t bin_size,
|
||||
struct script *script,
|
||||
size_t sections, size_t entries)
|
||||
{
|
||||
struct script_bin_head *head;
|
||||
struct script_bin_section *section;
|
||||
struct script_bin_entry *entry;
|
||||
void *data;
|
||||
|
||||
struct list_entry *ls, *le;
|
||||
|
||||
head = bin;
|
||||
section = head->section;
|
||||
entry = (void*)section+sections*sizeof(*section);
|
||||
data = (void*)entry+entries*sizeof(*entry);
|
||||
|
||||
pr_debug("head....:%p\n", head);
|
||||
pr_debug("section.:%p (offset:%zu, each:%zu)\n", section,
|
||||
(void*)section-bin, sizeof(*section));
|
||||
pr_debug("entry...:%p (offset:%zu, each:%zu)\n", entry,
|
||||
(void*)entry-bin, sizeof(*entry));
|
||||
pr_debug("data....:%p (offset:%zu)\n", data,
|
||||
(void*)data-bin);
|
||||
|
||||
head->sections = sections;
|
||||
head->filesize = bin_size;
|
||||
head->version[0] = 1;
|
||||
head->version[1] = 2;
|
||||
|
||||
for (ls = list_first(&script->sections); ls;
|
||||
ls = list_next(&script->sections, ls)) {
|
||||
struct script_section *s;
|
||||
size_t c = 0;
|
||||
s = container_of(ls, struct script_section, sections);
|
||||
|
||||
memcpy(section->name, s->name, strlen(s->name));
|
||||
section->offset = ((void*)entry-bin)>>2;
|
||||
|
||||
for (le = list_first(&s->entries); le;
|
||||
le = list_next(&s->entries, le)) {
|
||||
struct script_entry *e;
|
||||
e = container_of(le, struct script_entry, entries);
|
||||
size_t size = 0;
|
||||
|
||||
memcpy(entry->name, e->name, strlen(e->name));
|
||||
entry->offset = ((void*)data-bin)>>2;
|
||||
entry->pattern = (e->type<<16);
|
||||
|
||||
switch(e->type) {
|
||||
case SCRIPT_VALUE_TYPE_SINGLE_WORD: {
|
||||
struct script_single_entry *single;
|
||||
int32_t *bdata = data;
|
||||
single = container_of(e, struct script_single_entry, entry);
|
||||
|
||||
*bdata = single->value;
|
||||
size = sizeof(*bdata);
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_STRING: {
|
||||
struct script_string_entry *string;
|
||||
string = container_of(e, struct script_string_entry, entry);
|
||||
size = string->l;
|
||||
memcpy(data, string->string, size);
|
||||
/* align */
|
||||
size += sizeof(uint32_t)-1;
|
||||
size /= sizeof(uint32_t);
|
||||
size *= sizeof(uint32_t);
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_MULTI_WORD:
|
||||
abort();
|
||||
case SCRIPT_VALUE_TYPE_GPIO: {
|
||||
struct script_gpio_entry *gpio;
|
||||
struct script_bin_gpio_value *bdata = data;
|
||||
gpio = container_of(e, struct script_gpio_entry, entry);
|
||||
bdata->port = gpio->port;
|
||||
bdata->port_num = gpio->port_num;
|
||||
bdata->mul_sel = gpio->data[0];
|
||||
bdata->pull = gpio->data[1];
|
||||
bdata->drv_level = gpio->data[2];
|
||||
bdata->data = gpio->data[3];
|
||||
size = sizeof(*bdata);
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_NULL:
|
||||
size = sizeof(uint32_t);
|
||||
break;
|
||||
}
|
||||
|
||||
data += size;
|
||||
entry->pattern |= (size>>2);
|
||||
pr_debug("%s.%s <%p> (type:%d, words:%d (%zu), offset:%d)\n",
|
||||
section->name, entry->name, entry,
|
||||
(entry->pattern>>16) & 0xffff,
|
||||
(entry->pattern>>0) & 0xffff, size,
|
||||
entry->offset);
|
||||
c++;
|
||||
entry++;
|
||||
}
|
||||
|
||||
section->length = c;
|
||||
pr_debug("%s <%p> (length:%d, offset:%d)\n",
|
||||
section->name, section, section->length, section->offset);
|
||||
|
||||
section++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* decompiler
|
||||
*/
|
||||
static int decompile_section(void *bin, size_t bin_size,
|
||||
const char *filename,
|
||||
struct script_bin_section *section,
|
||||
struct script *script)
|
||||
{
|
||||
struct script_bin_entry *entry;
|
||||
struct script_section *s;
|
||||
int size;
|
||||
|
||||
if ((section->offset < 0) || (section->offset > (int)(bin_size / 4))) {
|
||||
pr_err("Malformed data: invalid section offset: %d\n",
|
||||
section->offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = bin_size - 4 * section->offset;
|
||||
|
||||
if ((section->length < 0) ||
|
||||
(section->length > (size / (int)sizeof(struct script_bin_entry)))) {
|
||||
pr_err("Malformed data: invalid section length: %d\n",
|
||||
section->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((s = script_section_new(script, section->name)) == NULL)
|
||||
goto malloc_error;
|
||||
|
||||
entry = PTR(bin, section->offset<<2);
|
||||
|
||||
for (int i = section->length; i--; entry++) {
|
||||
void *data = PTR(bin, entry->offset<<2);
|
||||
unsigned type, words;
|
||||
type = (entry->pattern >> 16) & 0xffff;
|
||||
words = (entry->pattern >> 0) & 0xffff;
|
||||
|
||||
for (char *p = entry->name; *p; p++)
|
||||
if (!(isalnum(*p) || *p == '_' || *p == '-')) {
|
||||
pr_info("Warning: Malformed entry key \"%s\"\n",
|
||||
entry->name);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case SCRIPT_VALUE_TYPE_SINGLE_WORD: {
|
||||
uint32_t *v = data;
|
||||
if (words != 1) {
|
||||
pr_err("%s: %s.%s: invalid length %d (assuming %d)\n",
|
||||
filename, section->name, entry->name, words, 1);
|
||||
}
|
||||
if (!script_single_entry_new(s, entry->name, *v))
|
||||
goto malloc_error;
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_STRING: {
|
||||
size_t bytes = words << 2;
|
||||
const char *p, *pe, *v = data;
|
||||
|
||||
for(p=v, pe=v+bytes; *p && p!=pe; p++)
|
||||
; /* seek end-of-string */
|
||||
|
||||
if (!script_string_entry_new(s, entry->name, p-v, v))
|
||||
goto malloc_error;
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_GPIO: {
|
||||
struct script_bin_gpio_value *gpio = data;
|
||||
int32_t v[4];
|
||||
if (words != 6) {
|
||||
pr_err("%s: %s.%s: invalid length %d (assuming %d)\n",
|
||||
filename, section->name, entry->name, words, 6);
|
||||
} else if (gpio->port == 0xffff) {
|
||||
; /* port:power */
|
||||
} else if (gpio->port < 1 || gpio->port > GPIO_BANK_MAX) {
|
||||
pr_err("%s: %s.%s: unknown GPIO port bank ",
|
||||
filename, section->name, entry->name);
|
||||
char c = 'A' + gpio->port - 1;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
pr_err("%c ", c);
|
||||
pr_err("(%u)\n", gpio->port);
|
||||
goto failure;
|
||||
}
|
||||
v[0] = gpio->mul_sel;
|
||||
v[1] = gpio->pull;
|
||||
v[2] = gpio->drv_level;
|
||||
v[3] = gpio->data;
|
||||
|
||||
if (!script_gpio_entry_new(s, entry->name,
|
||||
gpio->port, gpio->port_num,
|
||||
v))
|
||||
goto malloc_error;
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_NULL:
|
||||
if (!*entry->name) {
|
||||
pr_err("%s: empty entry in section: %s\n", filename, section->name);
|
||||
} else if (!script_null_entry_new(s, entry->name)) {
|
||||
goto malloc_error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: %s.%s: unknown type %d\n",
|
||||
filename, section->name, entry->name, type);
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
||||
malloc_error:
|
||||
pr_err("%s: %s\n", "malloc", strerror(errno));
|
||||
failure:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SCRIPT_BIN_VERSION_LIMIT 0x10
|
||||
#define SCRIPT_BIN_SECTION_LIMIT 0x100
|
||||
|
||||
int script_decompile_bin(void *bin, size_t bin_size,
|
||||
const char *filename,
|
||||
struct script *script)
|
||||
{
|
||||
unsigned int i;
|
||||
struct script_bin_head *head = bin;
|
||||
|
||||
if ((head->version[0] > SCRIPT_BIN_VERSION_LIMIT) ||
|
||||
(head->version[1] > SCRIPT_BIN_VERSION_LIMIT)) {
|
||||
pr_err("Malformed data: version %u.%u.\n",
|
||||
head->version[0], head->version[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (head->sections > SCRIPT_BIN_SECTION_LIMIT) {
|
||||
pr_err("Malformed data: too many sections (%u).\n",
|
||||
head->sections);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("%s: version: %u.%u\n", filename,
|
||||
head->version[0], head->version[1]);
|
||||
pr_info("%s: size: %zu (%u sections), header value: %u\n", filename,
|
||||
bin_size, head->sections, head->filesize);
|
||||
|
||||
/* TODO: SANITY: compare head.sections with bin_size */
|
||||
for (i=0; i < head->sections; i++) {
|
||||
struct script_bin_section *section = &head->section[i];
|
||||
|
||||
if (!decompile_section(bin, bin_size, filename,
|
||||
section, script))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
60
script_bin.h
Normal file
60
script_bin.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_SCRIPT_BIN_H
|
||||
#define _SUNXI_TOOLS_SCRIPT_BIN_H
|
||||
|
||||
/** binary representation of the head of a section */
|
||||
struct script_bin_section {
|
||||
char name[32];
|
||||
int32_t length;
|
||||
int32_t offset;
|
||||
};
|
||||
|
||||
/** binary representation of the head of the script file */
|
||||
struct script_bin_head {
|
||||
uint32_t sections;
|
||||
uint32_t filesize;
|
||||
uint32_t version[2];
|
||||
struct script_bin_section section[];
|
||||
};
|
||||
|
||||
/** binary representation of the head of an entry */
|
||||
struct script_bin_entry {
|
||||
char name[32];
|
||||
int32_t offset;
|
||||
int32_t pattern;
|
||||
};
|
||||
|
||||
/** binary representation of a GPIO */
|
||||
struct script_bin_gpio_value {
|
||||
int32_t port;
|
||||
int32_t port_num;
|
||||
int32_t mul_sel;
|
||||
int32_t pull;
|
||||
int32_t drv_level;
|
||||
int32_t data;
|
||||
};
|
||||
|
||||
size_t script_bin_size(struct script *script,
|
||||
size_t *sections, size_t *entries);
|
||||
|
||||
int script_generate_bin(void *bin, size_t bin_size, struct script *script,
|
||||
size_t sections, size_t entries);
|
||||
int script_decompile_bin(void *bin, size_t bin_size,
|
||||
const char *filename,
|
||||
struct script *script);
|
||||
#endif
|
||||
42
script_extractor.c
Normal file
42
script_extractor.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Olliver Schinagl <oliver@schinagl.nl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define SCRIPT_START 0x43000000
|
||||
#define SCRIPT_SIZE 0x20000
|
||||
|
||||
int main(void) {
|
||||
char *addr;
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
fd = open("/dev/mem", O_RDONLY);
|
||||
addr = (char *)mmap(NULL, SCRIPT_SIZE, PROT_READ, MAP_SHARED, fd, SCRIPT_START);
|
||||
for (i = 0; i < SCRIPT_SIZE; i++)
|
||||
putchar(addr[i]);
|
||||
munmap(addr, SCRIPT_SIZE);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
379
script_fex.c
Normal file
379
script_fex.c
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "common.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "script.h"
|
||||
#include "script_fex.h"
|
||||
|
||||
#define MAX_LINE 255
|
||||
|
||||
#define pr_info(...) pr_error("fexc-fex: " __VA_ARGS__)
|
||||
#define pr_err(...) pr_error("E: fexc-fex: " __VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define pr_debug(...) pr_error("D: fexc-fex: " __VA_ARGS__)
|
||||
#else
|
||||
#define pr_debug(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* generator
|
||||
*/
|
||||
static inline size_t strlen2(const char *s)
|
||||
{
|
||||
size_t l = strlen(s);
|
||||
const char *p = &s[l-1];
|
||||
while (l && *p >= '0' && *p <= '9') {
|
||||
l--;
|
||||
p--;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static int find_full_match(const char *s, size_t l, const char **list)
|
||||
{
|
||||
while (*list) {
|
||||
if (memcmp(s, *list, l) == 0)
|
||||
return 1;
|
||||
list++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
static int decompile_single_mode(const char *name)
|
||||
{
|
||||
static const char *hexa_entries[] = {
|
||||
"dram_baseaddr", "dram_zq", "dram_tpr", "dram_emr",
|
||||
"g2d_size",
|
||||
"rtp_press_threshold", "rtp_sensitive_level",
|
||||
"ctp_twi_addr", "csi_twi_addr", "csi_twi_addr_b", "tkey_twi_addr",
|
||||
"lcd_gamma_tbl_",
|
||||
"gsensor_twi_addr",
|
||||
NULL };
|
||||
size_t l = strlen2(name);
|
||||
|
||||
if (find_full_match(name, l, hexa_entries))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int script_generate_fex(FILE *out, const char *UNUSED(filename),
|
||||
struct script *script)
|
||||
{
|
||||
struct list_entry *ls, *le;
|
||||
struct script_section *section;
|
||||
struct script_entry *entry;
|
||||
|
||||
for (ls = list_first(&script->sections); ls;
|
||||
ls = list_next(&script->sections, ls)) {
|
||||
section = container_of(ls, struct script_section, sections);
|
||||
|
||||
fprintf(out, "[%s]\n", section->name);
|
||||
for (le = list_first(§ion->entries); le;
|
||||
le = list_next(§ion->entries, le)) {
|
||||
entry = container_of(le, struct script_entry, entries);
|
||||
|
||||
switch(entry->type) {
|
||||
case SCRIPT_VALUE_TYPE_SINGLE_WORD: {
|
||||
int mode = decompile_single_mode(entry->name);
|
||||
struct script_single_entry *single;
|
||||
single = container_of(entry, struct script_single_entry, entry);
|
||||
|
||||
fprintf(out, "%s = ", entry->name);
|
||||
if (mode < 0)
|
||||
fprintf(out, "%d", single->value);
|
||||
else if (mode > 0)
|
||||
fprintf(out, "0x%0*x", mode, single->value);
|
||||
else
|
||||
fprintf(out, "0x%x", single->value);
|
||||
fputc('\n', out);
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_STRING: {
|
||||
struct script_string_entry *string;
|
||||
string = container_of(entry, struct script_string_entry, entry);
|
||||
fprintf(out, "%s = \"%.*s\"\n", entry->name,
|
||||
(int)string->l, string->string);
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_MULTI_WORD:
|
||||
abort();
|
||||
case SCRIPT_VALUE_TYPE_GPIO: {
|
||||
char port = 'A'-1;
|
||||
struct script_gpio_entry *gpio;
|
||||
gpio = container_of(entry, struct script_gpio_entry, entry);
|
||||
|
||||
if (gpio->port == 0xffff) {
|
||||
fprintf(out, "%s = port:power%u", entry->name,
|
||||
gpio->port_num);
|
||||
} else {
|
||||
port += gpio->port;
|
||||
fprintf(out, "%s = port:P%c%02u", entry->name,
|
||||
port, gpio->port_num);
|
||||
}
|
||||
for (const int *p = gpio->data, *pe = p+4; p != pe; p++) {
|
||||
if (*p == -1)
|
||||
fputs("<default>", out);
|
||||
else
|
||||
fprintf(out, "<%d>", *p);
|
||||
}
|
||||
fputc('\n', out);
|
||||
}; break;
|
||||
case SCRIPT_VALUE_TYPE_NULL:
|
||||
fprintf(out, "%s =\n", entry->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputc('\n', out);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* parser
|
||||
*/
|
||||
|
||||
/** find first not blank char */
|
||||
static inline char *skip_blank(char *p)
|
||||
{
|
||||
while(isblank(*p))
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
/** trim out blank chars at the end of a string */
|
||||
static inline char *rtrim(const char *s, char *p)
|
||||
{
|
||||
if (p>s) {
|
||||
while (p!=s && isblank(*--p))
|
||||
;
|
||||
*++p='\0';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
int script_parse_fex(FILE *in, const char *filename, struct script *script)
|
||||
{
|
||||
char buffer[MAX_LINE+1];
|
||||
int ok = 1;
|
||||
struct script_section *last_section = NULL;
|
||||
|
||||
/* TODO: deal with longer lines correctly (specially in comments) */
|
||||
for(size_t line = 1; ok && fgets(buffer, sizeof(buffer), in); line++) {
|
||||
char *s = skip_blank(buffer); /* beginning */
|
||||
char *pe = s; /* \0... to be found */
|
||||
|
||||
if (*pe) while (*++pe)
|
||||
;
|
||||
|
||||
if (pe>s && pe[-1] == '\n') {
|
||||
if (pe>s+1 && pe[-2] == '\r')
|
||||
pe -= 2;
|
||||
else
|
||||
pe -= 1;
|
||||
*pe = '\0';
|
||||
}
|
||||
|
||||
pe = rtrim(s, pe);
|
||||
|
||||
/* Some lines end in a trailing semicolon. */
|
||||
if (pe > s && pe[-1] == ';')
|
||||
*--pe = '\0';
|
||||
|
||||
if (pe == s || *s == ';' || *s == '#')
|
||||
continue; /* empty */
|
||||
if (*s == ':') {
|
||||
/* see https://github.com/linux-sunxi/sunxi-boards/issues/50 */
|
||||
pr_error("Warning: %s:%zu: invalid line, suspecting typo/malformed comment.\n",
|
||||
filename, line);
|
||||
continue; /* ignore this line */
|
||||
}
|
||||
if (*s == '[') {
|
||||
/* section */
|
||||
char *p = ++s;
|
||||
while (isalnum(*p) || *p == '_' || *p == '-' || *p == '/')
|
||||
p++;
|
||||
|
||||
if (*p == ']' && *(p+1) == '\0') {
|
||||
*p = '\0';
|
||||
if ((last_section = script_section_new(script, s)))
|
||||
continue;
|
||||
|
||||
perror("malloc");
|
||||
} else if (*p) {
|
||||
pr_error("E: %s:%zu: invalid character at %zu.\n",
|
||||
filename, line, p-buffer+1);
|
||||
} else {
|
||||
pr_error("E: %s:%zu: incomplete section declaration.\n",
|
||||
filename, line);
|
||||
}
|
||||
ok = 0;
|
||||
} else {
|
||||
/* key = value */
|
||||
const char *key = s;
|
||||
char *mark, *p = s;
|
||||
|
||||
if (!last_section) {
|
||||
pr_error("E: %s:%zu: data must follow a section.\n",
|
||||
filename, line);
|
||||
goto parse_error;
|
||||
};
|
||||
|
||||
while (isalnum(*p) || *p == '_' || *p == '-')
|
||||
p++;
|
||||
mark = p;
|
||||
p = skip_blank(p);
|
||||
if (*p != '=')
|
||||
goto invalid_char_at_p;
|
||||
*mark = '\0'; /* truncate key */
|
||||
p = skip_blank(p+1);
|
||||
|
||||
if (*p == '\0') {
|
||||
/* NULL */
|
||||
if (script_null_entry_new(last_section, key))
|
||||
continue;
|
||||
perror("malloc");
|
||||
} else if (pe > p+1 && *p == '"' && pe[-1] == '"') {
|
||||
/* string */
|
||||
p++; *--pe = '\0';
|
||||
if (script_string_entry_new(last_section, key, pe-p, p)) {
|
||||
pr_debug("%s.%s = \"%.*s\"\n",
|
||||
last_section->name, key,
|
||||
(int)(pe-p), p);
|
||||
continue;
|
||||
}
|
||||
perror("malloc");
|
||||
} else if (memcmp("port:", p, 5) == 0) {
|
||||
/* GPIO */
|
||||
p += 5;
|
||||
if (p[0] == 'P' &&
|
||||
(p[1] < 'A' || p[1] > ('A' + GPIO_BANK_MAX)))
|
||||
;
|
||||
else if (*p != 'P' &&
|
||||
memcmp(p, "power", 5) != 0)
|
||||
;
|
||||
else {
|
||||
char *end;
|
||||
int port;
|
||||
long v;
|
||||
|
||||
if (*p == 'P') {
|
||||
/* port:PXN */
|
||||
port = p[1] - 'A' + 1;
|
||||
p += 2;
|
||||
} else {
|
||||
/* port:powerN */
|
||||
port = 0xffff;
|
||||
p += 5;
|
||||
}
|
||||
|
||||
v = strtol(p, &end, 10);
|
||||
if (end == p)
|
||||
goto invalid_char_at_p;
|
||||
else if (v<0 || v>255) {
|
||||
pr_error("E: %s:%zu: port out of range at %zu (%ld).\n",
|
||||
filename, line, p-buffer+1, v);
|
||||
} else {
|
||||
int data[] = {-1,-1,-1,-1};
|
||||
int port_num = v;
|
||||
p = end;
|
||||
for (int i=0; *p && i<4; i++) {
|
||||
if (memcmp(p, "<default>", 9) == 0) {
|
||||
p += 9;
|
||||
continue;
|
||||
} else if (*p == '<') {
|
||||
v = strtol(++p, &end, 10);
|
||||
if (end == p) {
|
||||
;
|
||||
} else if (v<0 || v>INT32_MAX) {
|
||||
pr_error("E: %s:%zu: value out of range at %zu (%ld).\n",
|
||||
filename, line, p-buffer+1, v);
|
||||
goto parse_error;
|
||||
} else if (*end != '>') {
|
||||
p = end;
|
||||
} else {
|
||||
p = end+1;
|
||||
data[i] = v;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (*p)
|
||||
goto invalid_char_at_p;
|
||||
if (script_gpio_entry_new(last_section, key,
|
||||
port, port_num, data)) {
|
||||
pr_debug("%s.%s = GPIO %d.%d (%d,%d,%d,%d)\n",
|
||||
last_section->name, key,
|
||||
port, port_num,
|
||||
data[0], data[1], data[2], data[3]);
|
||||
continue;
|
||||
}
|
||||
perror("malloc");
|
||||
}
|
||||
}
|
||||
} else if (isdigit(*p) || (*p == '-' && isdigit(*(p+1)))) {
|
||||
long long v = 0;
|
||||
char *end;
|
||||
v = strtoll(p, &end, 0);
|
||||
p = end;
|
||||
if (p != pe) {
|
||||
goto invalid_char_at_p;
|
||||
} else if (v > UINT32_MAX) {
|
||||
pr_error("E: %s:%zu: value out of range %lld.\n",
|
||||
filename, line, v);
|
||||
} else if (script_single_entry_new(last_section, key, v)) {
|
||||
pr_debug("%s.%s = %lld\n",
|
||||
last_section->name, key, v);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* goto invalid_char_at_p; */
|
||||
pr_error("Warning: %s:%zu: unquoted value '%s', assuming string\n",
|
||||
filename, line, p);
|
||||
if (script_string_entry_new(last_section, key, pe-p, p)) {
|
||||
pr_debug("%s.%s = \"%s\"\n",
|
||||
last_section->name, key, p);
|
||||
continue;
|
||||
}
|
||||
perror("malloc");
|
||||
}
|
||||
pr_error("E: %s:%zu: parse error at %zu.\n",
|
||||
filename, line, p-buffer+1);
|
||||
goto parse_error;
|
||||
invalid_char_at_p:
|
||||
pr_error("E: %s:%zu: invalid character at %zu.\n",
|
||||
filename, line, p-buffer+1);
|
||||
parse_error:
|
||||
ok = 0;
|
||||
}
|
||||
};
|
||||
|
||||
if (ferror(in))
|
||||
ok = 0;
|
||||
return ok;
|
||||
}
|
||||
23
script_fex.h
Normal file
23
script_fex.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUBXI_TOOLS_SCRIPT_FEX_H
|
||||
#define _SUBXI_TOOLS_SCRIPT_FEX_H
|
||||
|
||||
int script_parse_fex(FILE *in, const char *filename, struct script *script);
|
||||
int script_generate_fex(FILE *out, const char *filename, struct script *script);
|
||||
|
||||
#endif
|
||||
256
script_uboot.c
Normal file
256
script_uboot.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "script.h"
|
||||
#include "script_uboot.h"
|
||||
|
||||
#define pr_info(...) pr_error("fexc-uboot: " __VA_ARGS__)
|
||||
#define pr_err(...) pr_error("E: fexc-uboot: " __VA_ARGS__)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define pr_debug(...) pr_error("D: fexc-uboot: " __VA_ARGS__)
|
||||
#else
|
||||
#define pr_debug(...)
|
||||
#endif
|
||||
|
||||
struct members {
|
||||
const char *name;
|
||||
const char *translation;
|
||||
int mode;
|
||||
};
|
||||
#define foreach_member(I, T) for (const struct members *I = T; \
|
||||
I < T+ARRAY_SIZE(T); I++)
|
||||
|
||||
/*
|
||||
*/
|
||||
static inline void out_u32_member(FILE *out, const char *key, int hexa,
|
||||
struct script_single_entry *val)
|
||||
{
|
||||
const char *fmt;
|
||||
if (hexa)
|
||||
fmt = "\t.%s = %#x,\n";
|
||||
else
|
||||
fmt = "\t.%s = %u,\n";
|
||||
|
||||
fprintf(out, fmt, key, val->value);
|
||||
}
|
||||
|
||||
static inline void out_gpio_member(FILE *out, const char *key,
|
||||
struct script_gpio_entry *gpio)
|
||||
{
|
||||
fprintf(out, "\t.%s = ", key);
|
||||
|
||||
if (gpio->port == 0xffff)
|
||||
fprintf(out, "GPIO_AXP_CFG(%u", gpio->port_num);
|
||||
else
|
||||
fprintf(out, "GPIO_CFG(%u, %u", gpio->port, gpio->port_num);
|
||||
|
||||
for (const int *p = gpio->data, *pe = p+4; p != pe; p++) {
|
||||
if (*p == -1)
|
||||
fputs(", 0xff", out);
|
||||
else
|
||||
fprintf(out, ", %u", *p);
|
||||
}
|
||||
|
||||
fputs("),\n", out);
|
||||
}
|
||||
|
||||
static inline void out_null_member(FILE *out, const char *key)
|
||||
{
|
||||
fprintf(out, "\t/* %s is NULL */\n", key);
|
||||
}
|
||||
|
||||
static inline int out_member(FILE *out, const char *key, int mode,
|
||||
struct script_entry *ep)
|
||||
{
|
||||
switch (ep->type) {
|
||||
case SCRIPT_VALUE_TYPE_SINGLE_WORD:
|
||||
out_u32_member(out, key, mode,
|
||||
container_of(ep, struct script_single_entry, entry));
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_NULL:
|
||||
out_null_member(out, key);
|
||||
break;
|
||||
case SCRIPT_VALUE_TYPE_GPIO:
|
||||
out_gpio_member(out, key,
|
||||
container_of(ep, struct script_gpio_entry, entry));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* DRAM
|
||||
*/
|
||||
static struct members dram_members[] = {
|
||||
{ .name="dram_clock" },
|
||||
{ .name="dram_clk", .translation="clock" },
|
||||
{ .name="dram_type" },
|
||||
{ .name="dram_rank_num" },
|
||||
{ .name="dram_density" },
|
||||
{ .name="dram_chip_density", .translation="density" },
|
||||
{ .name="dram_io_width" },
|
||||
{ .name="dram_bus_width" },
|
||||
{ .name="dram_cas" },
|
||||
{ .name="dram_zq" },
|
||||
{ .name="dram_odt_en" },
|
||||
{ .name="dram_size" },
|
||||
{ .name="dram_tpr0", .mode=1 },
|
||||
{ .name="dram_tpr1", .mode=1 },
|
||||
{ .name="dram_tpr2", .mode=1 },
|
||||
{ .name="dram_tpr3", .mode=1 },
|
||||
{ .name="dram_tpr4", .mode=1 },
|
||||
{ .name="dram_tpr5", .mode=1 },
|
||||
{ .name="dram_emr1", .mode=1 },
|
||||
{ .name="dram_emr2", .mode=1 },
|
||||
{ .name="dram_emr3", .mode=1 },
|
||||
};
|
||||
|
||||
static int generate_dram_struct(FILE *out, struct script_section *sp)
|
||||
{
|
||||
struct script_entry *ep;
|
||||
const char *key;
|
||||
int ret = 1;
|
||||
|
||||
fprintf(out, "static struct dram_para dram_para = {\n");
|
||||
foreach_member(mp, dram_members) {
|
||||
ep = script_find_entry(sp, mp->name);
|
||||
if (!ep)
|
||||
continue;
|
||||
|
||||
key = (mp->translation) ? mp->translation : mp->name+5;
|
||||
if (!out_member(out, key, mp->mode, ep)) {
|
||||
pr_err("dram_para: %s: invalid field\n", ep->name);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
}
|
||||
fprintf(out, "};\n");
|
||||
fputs("\nunsigned long sunxi_dram_init(void)\n"
|
||||
"{\n\treturn dramc_init(&dram_para);\n}\n",
|
||||
out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* PMU
|
||||
*/
|
||||
static struct members pmu_members[] = {
|
||||
{ .name = "pmu_used2" },
|
||||
{ .name = "pmu_para" },
|
||||
{ .name = "pmu_adpdet" },
|
||||
{ .name = "pmu_shutdown_chgcur" },
|
||||
{ .name = "pmu_shutdown_chgcur2" },
|
||||
{ .name = "pmu_pwroff_vol" },
|
||||
{ .name = "pmu_pwron_vol" },
|
||||
};
|
||||
|
||||
static int generate_pmu_struct(FILE *out, struct script_section *target,
|
||||
struct script_section *pmu_para)
|
||||
{
|
||||
struct list_entry *le;
|
||||
struct script_section *sp;
|
||||
struct script_entry *ep;
|
||||
const char *key;
|
||||
int ret = 1;
|
||||
|
||||
fputs("\nstatic struct pmu_para pmu_para = {\n", out);
|
||||
|
||||
sp = target;
|
||||
for (le = list_first(&sp->entries); le;
|
||||
le = list_next(&sp->entries, le)) {
|
||||
ep = container_of(le, struct script_entry, entries);
|
||||
|
||||
if (!out_member(out, ep->name, 0, ep)) {
|
||||
pr_err("target: %s: invalid field\n", ep->name);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
foreach_member(mp, pmu_members) {
|
||||
ep = script_find_entry(pmu_para, mp->name);
|
||||
if (!ep)
|
||||
continue;
|
||||
|
||||
key = (mp->translation) ? mp->translation : mp->name+4;
|
||||
if (!out_member(out, key, mp->mode, ep)) {
|
||||
pr_err("pmu_para: %s: invalid field\n", mp->name);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fputs("};\n", out);
|
||||
fputs("\nint sunxi_pmu_init(void)\n"
|
||||
"{\n\treturn PMU_init(&pmu_para);\n}\n",
|
||||
out);
|
||||
return ret;
|
||||
|
||||
(void) pmu_para;
|
||||
}
|
||||
#endif
|
||||
|
||||
int script_generate_uboot(FILE *out, const char *UNUSED(filename),
|
||||
struct script *script)
|
||||
{
|
||||
struct {
|
||||
const char *name;
|
||||
struct script_section *sp;
|
||||
} sections[] = {
|
||||
{ "dram_para", NULL },
|
||||
#if 0
|
||||
{ "target", NULL },
|
||||
{ "pmu_para", NULL },
|
||||
#endif
|
||||
};
|
||||
|
||||
for (unsigned i=0; i<ARRAY_SIZE(sections); i++) {
|
||||
struct script_section *sp;
|
||||
|
||||
sp = script_find_section(script, sections[i].name);
|
||||
if (sp)
|
||||
sections[i].sp = sp;
|
||||
else {
|
||||
pr_err("%s: critical section missing",
|
||||
sections[i].name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fputs("/* this file is generated, don't edit it yourself */\n\n"
|
||||
"#include <common.h>\n"
|
||||
#if 0
|
||||
"#include <asm/arch/pmu.h>\n"
|
||||
#endif
|
||||
"#include <asm/arch/dram.h>\n\n",
|
||||
out);
|
||||
|
||||
generate_dram_struct(out, sections[0].sp);
|
||||
#if 0
|
||||
generate_pmu_struct(out, sections[1].sp, sections[2].sp);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
22
script_uboot.h
Normal file
22
script_uboot.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUBXI_TOOLS_SCRIPT_UBOOT_H
|
||||
#define _SUBXI_TOOLS_SCRIPT_UBOOT_H
|
||||
|
||||
int script_generate_uboot(FILE *out, const char *filename, struct script *script);
|
||||
|
||||
#endif
|
||||
602
soc_info.c
Normal file
602
soc_info.c
Normal file
@ -0,0 +1,602 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
* Copyright (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**********************************************************************
|
||||
* SoC information and retrieval of soc_sram_info
|
||||
**********************************************************************/
|
||||
#include "soc_info.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* The FEL code from BROM in A10/A13/A20 sets up two stacks for itself. One
|
||||
* at 0x2000 (and growing down) for the IRQ handler. And another one at 0x7000
|
||||
* (and also growing down) for the regular code. In order to use the whole
|
||||
* 32 KiB in the A1/A2 sections of SRAM, we need to temporarily move these
|
||||
* stacks elsewhere. And the addresses 0x7D00-0x7FFF contain something
|
||||
* important too (overwriting them kills FEL). On A10/A13/A20 we can use
|
||||
* the SRAM sections A3/A4 (0x8000-0xBFFF) for this purpose.
|
||||
*/
|
||||
sram_swap_buffers a10_a13_a20_sram_swap_buffers[] = {
|
||||
/* 0x1C00-0x1FFF (IRQ stack) */
|
||||
{ .buf1 = 0x1C00, .buf2 = 0xA400, .size = 0x0400 },
|
||||
/* 0x5C00-0x6FFF (Stack) */
|
||||
{ .buf1 = 0x5C00, .buf2 = 0xA800, .size = 0x1400 },
|
||||
/* 0x7C00-0x7FFF (Something important) */
|
||||
{ .buf1 = 0x7C00, .buf2 = 0xBC00, .size = 0x0400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* A31 is very similar to A10/A13/A20, except that it has no SRAM at 0x8000.
|
||||
* So we use the SRAM section B at 0x20000-0x2FFFF instead. In the FEL mode,
|
||||
* the MMU translation table is allocated by the BROM at 0x20000. But we can
|
||||
* also safely use it as the backup storage because the MMU is temporarily
|
||||
* disabled during the time of the SPL execution.
|
||||
*/
|
||||
sram_swap_buffers a31_sram_swap_buffers[] = {
|
||||
{ .buf1 = 0x1800, .buf2 = 0x20000, .size = 0x800 },
|
||||
{ .buf1 = 0x5C00, .buf2 = 0x20800, .size = 0x8000 - 0x5C00 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* A64 has 32KiB of SRAM A at 0x10000 and a large SRAM C at 0x18000. SRAM A
|
||||
* and SRAM C reside in the address space back-to-back without any gaps, thus
|
||||
* representing a singe large contiguous area. The BROM FEL code memory areas
|
||||
* are the same as on A10/A13/A20, but just shifted by 0x10000.
|
||||
* We put the backup buffers towards the end of SRAM C, in a location that
|
||||
* is also available on the H5.
|
||||
*/
|
||||
sram_swap_buffers a64_sram_swap_buffers[] = {
|
||||
/* 0x11C00-0x11FFF (IRQ stack) */
|
||||
{ .buf1 = 0x11C00, .buf2 = 0x31400, .size = 0x0400 },
|
||||
/* 0x15C00-0x16FFF (Stack) */
|
||||
{ .buf1 = 0x15C00, .buf2 = 0x31800, .size = 0x1400 },
|
||||
/* 0x17C00-0x17FFF (Something important) */
|
||||
{ .buf1 = 0x17C00, .buf2 = 0x32c00, .size = 0x0400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* Use the SRAM section at 0x44000 as the backup storage. This is the memory,
|
||||
* which is normally shared with the OpenRISC core (should we do an extra check
|
||||
* to ensure that this core is powered off and can't interfere?).
|
||||
*/
|
||||
sram_swap_buffers ar100_abusing_sram_swap_buffers[] = {
|
||||
{ .buf1 = 0x1800, .buf2 = 0x44000, .size = 0x800 },
|
||||
{ .buf1 = 0x5C00, .buf2 = 0x44800, .size = 0x8000 - 0x5C00 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* A80 has 40KiB SRAM A1 at 0x10000 where the SPL has to be loaded to. The
|
||||
* secure SRAM B at 0x20000 is used as backup area for FEL stacks and data.
|
||||
*/
|
||||
sram_swap_buffers a80_sram_swap_buffers[] = {
|
||||
{ .buf1 = 0x11800, .buf2 = 0x20000, .size = 0x800 },
|
||||
{ .buf1 = 0x15400, .buf2 = 0x20800, .size = 0x18000 - 0x15400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* H6 has 32KiB of SRAM A at 0x20000 and a large SRAM C at 0x28000. SRAM A
|
||||
* and SRAM C reside in the address space back-to-back without any gaps, thus
|
||||
* representing a singe large contiguous area. Everything is the same as on
|
||||
* A10/A13/A20, but just shifted by 0x20000.
|
||||
*/
|
||||
sram_swap_buffers h6_sram_swap_buffers[] = {
|
||||
/* 0x21C00-0x21FFF (IRQ stack) */
|
||||
{ .buf1 = 0x21C00, .buf2 = 0x42400, .size = 0x0400 },
|
||||
/* 0x25C00-0x26FFF (Stack) */
|
||||
{ .buf1 = 0x25C00, .buf2 = 0x42800, .size = 0x1400 },
|
||||
/* 0x27C00-0x27FFF (Something important) */
|
||||
{ .buf1 = 0x27C00, .buf2 = 0x43c00, .size = 0x0400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* V831 has 96KiB SRAM A1 at 0x20000 where the SPL has to be loaded to.
|
||||
* SRAM C is continuous with SRAM A1, and both SRAMs are tried to be used
|
||||
* by BROM. Memory space is allocated both from the start of SRAM A1 and
|
||||
* the end of SRAM C.
|
||||
* The start of SRAM C is in between these areas, and can serve as backup
|
||||
* of IRQ stack, which is inside the first 32KiB of SRAM A1. Other areas
|
||||
* that are critical on older SoCs seem to be already in SRAM C, which
|
||||
* we do not need to preserve.
|
||||
*/
|
||||
sram_swap_buffers v831_sram_swap_buffers[] = {
|
||||
{ .buf1 = 0x21000, .buf2 = 0x38000, .size = 0x1000 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/* H616 situation is the same as V831 one, except it has 32 KiB of SRAM A1. */
|
||||
sram_swap_buffers h616_sram_swap_buffers[] = {
|
||||
{ .buf1 = 0x21000, .buf2 = 0x52a00, .size = 0x1000 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* R329 has no SRAM A1, but a huge SRAM A2 at 0x100000. SPL and BROM uses
|
||||
* this SRAM A2's first part like how other SoCs use SRAM A1. The sp and
|
||||
* sp_irq values checked with thunk are 0x13c2c8 and 0x101400, which looks
|
||||
* similar to the situation of V831, in which the stack is quite high.
|
||||
*/
|
||||
sram_swap_buffers r329_sram_swap_buffers[] = {
|
||||
/* 0x101000-0x101400 (IRQ stack) */
|
||||
{ .buf1 = 0x101000, .buf2 = 0x13bc00, .size = 0x0400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
/*
|
||||
* The FEL code from BROM in F1C100s also uses SRAM A in a similar way
|
||||
* with A10/A13/A20.
|
||||
* Unfortunately the SRAM layout of F1C100s is not documented at all, so
|
||||
* we can only try by r/w under FEL mode.
|
||||
* The result is that there's a contingous SRAM zone from 0x8800 to 0xb5ff.
|
||||
*/
|
||||
sram_swap_buffers f1c100s_sram_swap_buffers[] = {
|
||||
/* 0x1C00-0x1FFF (IRQ stack) */
|
||||
{ .buf1 = 0x1C00, .buf2 = 0x9000, .size = 0x0400 },
|
||||
/* 0x5C00-0x6FFF (Stack) */
|
||||
{ .buf1 = 0x5C00, .buf2 = 0x9400, .size = 0x1400 },
|
||||
/* 0x7C00-0x7FFF (Something important) */
|
||||
{ .buf1 = 0x7C00, .buf2 = 0xa800, .size = 0x0400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
const watchdog_info wd_a10_compat = {
|
||||
.reg_mode = 0x01C20C94,
|
||||
.reg_mode_value = 3,
|
||||
};
|
||||
|
||||
const watchdog_info wd_h3_compat = {
|
||||
.reg_mode = 0x01C20Cb8,
|
||||
.reg_mode_value = 1,
|
||||
};
|
||||
|
||||
const watchdog_info wd_a80 = {
|
||||
.reg_mode = 0x06000CB8,
|
||||
.reg_mode_value = 1,
|
||||
};
|
||||
|
||||
const watchdog_info wd_h6_compat = {
|
||||
.reg_mode = 0x030090b8,
|
||||
.reg_mode_value = 1,
|
||||
};
|
||||
|
||||
const watchdog_info wd_v853_compat = {
|
||||
.reg_mode = 0x020500b8,
|
||||
.reg_mode_value = 0x16aa0001,
|
||||
};
|
||||
|
||||
static const sid_section r40_sid_maps[] = {
|
||||
SID_SECTION("chipid", 0x00, 128),
|
||||
SID_SECTION("in", 0x10, 256),
|
||||
SID_SECTION("ssk", 0x30, 128),
|
||||
SID_SECTION("thermal", 0x40, 32),
|
||||
SID_SECTION("ft_zone", 0x44, 64),
|
||||
SID_SECTION("tvout", 0x4c, 128),
|
||||
SID_SECTION("rssk", 0x5c, 256),
|
||||
SID_SECTION("hdcp_hash",0x7c, 128),
|
||||
SID_SECTION("reserved", 0x90, 896),
|
||||
SID_SECTION(NULL, 0, 0),
|
||||
};
|
||||
|
||||
static const sid_section h3_sid_maps[] = {
|
||||
SID_SECTION("chipid", 0x00, 128),
|
||||
SID_SECTION("oem_program", 0x10, 32),
|
||||
SID_SECTION("nv1", 0x14, 32),
|
||||
SID_SECTION("nv2", 0x18, 64),
|
||||
SID_SECTION("rsakey_hash", 0x20, 160),
|
||||
SID_SECTION("thermal", 0x34, 64),
|
||||
SID_SECTION("renewability", 0x3c, 64),
|
||||
SID_SECTION("huk", 0x44, 256),
|
||||
SID_SECTION("rotpk_hash", 0x64, 256),
|
||||
SID_SECTION("ssk", 0x84, 128),
|
||||
SID_SECTION("rssk", 0x94, 256),
|
||||
SID_SECTION("hdcp_hash", 0xb4, 128),
|
||||
SID_SECTION("ek_hash", 0xc4, 128),
|
||||
SID_SECTION("sn", 0xd4, 192),
|
||||
SID_SECTION("nv2_backup", 0xec, 64),
|
||||
SID_SECTION("lcjs", 0xf4, 32),
|
||||
SID_SECTION("debug", 0xf8, 32),
|
||||
SID_SECTION("chip_config", 0xfc, 32),
|
||||
SID_SECTION(NULL, 0, 0),
|
||||
};
|
||||
|
||||
static const sid_section h6_sid_maps[] = {
|
||||
SID_SECTION("chipid", 0x00, 128),
|
||||
SID_SECTION("brom_config", 0x10, 32),
|
||||
SID_SECTION("thermal", 0x14, 64),
|
||||
SID_SECTION("tf_zone", 0x1c, 128),
|
||||
SID_SECTION("oem_program", 0x2c, 96),
|
||||
SID_SECTION("mac-addr", 0x38, 64),
|
||||
SID_SECTION("write_protect", 0x40, 32),
|
||||
SID_SECTION("read-protect", 0x44, 32),
|
||||
SID_SECTION("lcjs", 0x48, 32),
|
||||
SID_SECTION("attr", 0x4c, 32),
|
||||
SID_SECTION("huk", 0x50, 96),
|
||||
SID_SECTION("vendor_id", 0x5c, 32),
|
||||
SID_SECTION("huk2", 0x60, 128),
|
||||
SID_SECTION("rotpk_hash", 0x70, 256),
|
||||
SID_SECTION("ssk", 0x90, 128),
|
||||
SID_SECTION("rssk", 0xa0, 256),
|
||||
SID_SECTION("hdcp_hash", 0xc0, 128),
|
||||
SID_SECTION("ek_hash", 0xd0, 128),
|
||||
SID_SECTION("sn", 0xe0, 192),
|
||||
SID_SECTION("nv1", 0xf8, 32),
|
||||
SID_SECTION("nv2", 0xfc, 224),
|
||||
SID_SECTION("hdcp_pkf", 0x118, 128),
|
||||
SID_SECTION("hdcp_duk", 0x128, 128),
|
||||
SID_SECTION("backup_key", 0x138, 576),
|
||||
SID_SECTION("sck0", 0x180, 256),
|
||||
SID_SECTION("sck0_mask", 0x1a0, 256),
|
||||
SID_SECTION("sck1", 0x1c0, 256),
|
||||
SID_SECTION("sck1_mask", 0x1e0, 256),
|
||||
SID_SECTION(NULL, 0, 0)
|
||||
};
|
||||
|
||||
/* Placeholder for SoCs without a known SID map */
|
||||
static const sid_section generic_2k_sid_maps[] = {
|
||||
SID_SECTION("chipid", 0x00, 128),
|
||||
SID_SECTION("unknown", 0x10, 1920),
|
||||
SID_SECTION(NULL, 0, 0),
|
||||
};
|
||||
|
||||
soc_info_t soc_info_table[] = {
|
||||
{
|
||||
.soc_id = 0x1623, /* Allwinner A10 */
|
||||
.name = "A10",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0xA200, .thunk_size = 0x200,
|
||||
.swap_buffers = a10_a13_a20_sram_swap_buffers,
|
||||
.sram_size = 48 * 1024,
|
||||
.needs_l2en = true,
|
||||
.sid_base = 0x01C23800,
|
||||
.watchdog = &wd_a10_compat,
|
||||
},{
|
||||
.soc_id = 0x1625, /* Allwinner A10s, A13, R8 */
|
||||
.name = "A13",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0xA200, .thunk_size = 0x200,
|
||||
.swap_buffers = a10_a13_a20_sram_swap_buffers,
|
||||
.sram_size = 48 * 1024,
|
||||
.needs_l2en = true,
|
||||
.sid_base = 0x01C23800,
|
||||
.watchdog = &wd_a10_compat,
|
||||
},{
|
||||
.soc_id = 0x1651, /* Allwinner A20 */
|
||||
.name = "A20",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0xA200, .thunk_size = 0x200,
|
||||
.swap_buffers = a10_a13_a20_sram_swap_buffers,
|
||||
.sram_size = 48 * 1024,
|
||||
.sid_base = 0x01C23800,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_a10_compat,
|
||||
},{
|
||||
.soc_id = 0x1650, /* Allwinner A23 */
|
||||
.name = "A23",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0x46E00, .thunk_size = 0x200,
|
||||
.swap_buffers = ar100_abusing_sram_swap_buffers,
|
||||
.sram_size = 64 * 1024,
|
||||
.sid_base = 0x01C23800,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1633, /* Allwinner A31 */
|
||||
.name = "A31",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0x22E00, .thunk_size = 0x200,
|
||||
.swap_buffers = a31_sram_swap_buffers,
|
||||
.sram_size = 32 * 1024,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1667, /* Allwinner A33, R16 */
|
||||
.name = "A33",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0x46E00, .thunk_size = 0x200,
|
||||
.swap_buffers = ar100_abusing_sram_swap_buffers,
|
||||
.sram_size = 32 * 1024,
|
||||
.sid_base = 0x01C23800,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1689, /* Allwinner A64 */
|
||||
.name = "A64",
|
||||
.spl_addr = 0x10000,
|
||||
.scratch_addr = 0x11000,
|
||||
.thunk_addr = 0x31200, .thunk_size = 0x200,
|
||||
.swap_buffers = a64_sram_swap_buffers,
|
||||
.sram_size = 140 * 1024,
|
||||
.sid_base = 0x01C14000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = h3_sid_maps,
|
||||
.rvbar_reg = 0x017000A0,
|
||||
/* Check L.NOP in the OpenRISC reset vector */
|
||||
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1639, /* Allwinner A80 */
|
||||
.name = "A80",
|
||||
.spl_addr = 0x10000,
|
||||
.scratch_addr = 0x11000,
|
||||
.thunk_addr = 0x23400, .thunk_size = 0x200,
|
||||
.swap_buffers = a80_sram_swap_buffers,
|
||||
.sram_size = 40 * 1024,
|
||||
.sid_base = 0X01C0E000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_a80,
|
||||
},{
|
||||
.soc_id = 0x1663, /* Allwinner F1C100s (all new sun3i?) */
|
||||
.name = "F1C100s",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0xb400, .thunk_size = 0x200,
|
||||
.swap_buffers = f1c100s_sram_swap_buffers,
|
||||
.sram_size = 32 * 1024,
|
||||
/* No SID */
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1673, /* Allwinner A83T */
|
||||
.name = "A83T",
|
||||
.scratch_addr = 0x1000,
|
||||
.mmu_tt_addr = 0x44000,
|
||||
.thunk_addr = 0x46E00, .thunk_size = 0x200,
|
||||
.swap_buffers = ar100_abusing_sram_swap_buffers,
|
||||
.sram_size = 32 * 1024,
|
||||
.sid_base = 0x01C14000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1680, /* Allwinner H3, H2+ */
|
||||
.name = "H3",
|
||||
.scratch_addr = 0x1000,
|
||||
.mmu_tt_addr = 0x8000,
|
||||
.thunk_addr = 0xA200, .thunk_size = 0x200,
|
||||
.swap_buffers = a10_a13_a20_sram_swap_buffers,
|
||||
.sram_size = 108 * 1024,
|
||||
.sid_base = 0x01C14000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_fix = true,
|
||||
.sid_sections = h3_sid_maps,
|
||||
/* Check L.NOP in the OpenRISC reset vector */
|
||||
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1681, /* Allwinner V3s */
|
||||
.name = "V3s",
|
||||
.scratch_addr = 0x1000,
|
||||
.mmu_tt_addr = 0x8000,
|
||||
.thunk_addr = 0xA200, .thunk_size = 0x200,
|
||||
.swap_buffers = a10_a13_a20_sram_swap_buffers,
|
||||
.sram_size = 60 * 1024,
|
||||
.sid_base = 0x01C23800,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1718, /* Allwinner H5 */
|
||||
.name = "H5",
|
||||
.spl_addr = 0x10000,
|
||||
.scratch_addr = 0x11000,
|
||||
.thunk_addr = 0x31200, .thunk_size = 0x200,
|
||||
.swap_buffers = a64_sram_swap_buffers,
|
||||
.sram_size = 140 * 1024,
|
||||
.sid_base = 0x01C14000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = h3_sid_maps,
|
||||
.rvbar_reg = 0x017000A0,
|
||||
/* Check L.NOP in the OpenRISC reset vector */
|
||||
.needs_smc_workaround_if_zero_word_at_addr = 0x40004,
|
||||
.watchdog = &wd_h3_compat,
|
||||
},{
|
||||
.soc_id = 0x1701, /* Allwinner R40 */
|
||||
.name = "R40",
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0xA200, .thunk_size = 0x200,
|
||||
.swap_buffers = a10_a13_a20_sram_swap_buffers,
|
||||
.sram_size = 48 * 1024,
|
||||
.sid_base = 0x01C1B000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = r40_sid_maps,
|
||||
.watchdog = &wd_a10_compat,
|
||||
},{
|
||||
.soc_id = 0x1719, /* Allwinner A63 */
|
||||
.name = "A63",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x42200, .thunk_size = 0x200,
|
||||
.swap_buffers = h6_sram_swap_buffers,
|
||||
.sram_size = 144 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.rvbar_reg = 0x09010040,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.soc_id = 0x1728, /* Allwinner H6 */
|
||||
.name = "H6",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x42200, .thunk_size = 0x200,
|
||||
.swap_buffers = h6_sram_swap_buffers,
|
||||
.sram_size = 144 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = h6_sid_maps,
|
||||
.rvbar_reg = 0x09010040,
|
||||
/* Check L.NOP in the OpenRISC reset vector */
|
||||
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.soc_id = 0x1816, /* Allwinner V536 */
|
||||
.name = "V536",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x2A200, .thunk_size = 0x200,
|
||||
.swap_buffers = v831_sram_swap_buffers,
|
||||
.sram_size = 228 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.soc_id = 0x1817, /* Allwinner V831 */
|
||||
.name = "V831",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x2A200, .thunk_size = 0x200,
|
||||
.swap_buffers = v831_sram_swap_buffers,
|
||||
.sram_size = 228 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.soc_id = 0x1823, /* Allwinner H616 */
|
||||
.name = "H616",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x53a00, .thunk_size = 0x200,
|
||||
.swap_buffers = h616_sram_swap_buffers,
|
||||
.sram_size = 207 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.rvbar_reg = 0x09010040,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.soc_id = 0x1851, /* Allwinner R329 */
|
||||
.name = "R329",
|
||||
.spl_addr = 0x100000,
|
||||
.scratch_addr = 0x101000,
|
||||
.mmu_tt_addr = 0x130000,
|
||||
.thunk_addr = 0x13ba00, .thunk_size = 0x200,
|
||||
.swap_buffers = r329_sram_swap_buffers,
|
||||
.sram_size = 1856 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.rvbar_reg = 0x08100040,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.soc_id = 0x1886, /* Allwinner V853 */
|
||||
.name = "V853",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x3A200, .thunk_size = 0x200,
|
||||
.swap_buffers = v831_sram_swap_buffers,
|
||||
.sram_size = 132 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.icache_fix = true,
|
||||
.watchdog = &wd_v853_compat,
|
||||
},{
|
||||
.soc_id = 0x1859, /* Allwinner D1/D1s/R528/T113-S3 */
|
||||
.name = "R528",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x3a200, .thunk_size = 0x200,
|
||||
.swap_buffers = v831_sram_swap_buffers,
|
||||
.sram_size = 160 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.icache_fix = true,
|
||||
.watchdog = &wd_v853_compat,
|
||||
},{
|
||||
.soc_id = 0x1721, /* Allwinner V5 */
|
||||
.name = "V5",
|
||||
.spl_addr = 0x20000,
|
||||
.scratch_addr = 0x21000,
|
||||
.thunk_addr = 0x42200, .thunk_size = 0x200,
|
||||
.swap_buffers = h6_sram_swap_buffers,
|
||||
.sram_size = 136 * 1024,
|
||||
.sid_base = 0x03006000,
|
||||
.sid_offset = 0x200,
|
||||
.sid_sections = generic_2k_sid_maps,
|
||||
.watchdog = &wd_h6_compat,
|
||||
},{
|
||||
.swap_buffers = NULL /* End of the table */
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This generic record assumes BROM with similar properties to A10/A13/A20/A31,
|
||||
* but no extra SRAM sections beyond 0x8000. It also assumes that the IRQ
|
||||
* handler stack usage never exceeds 0x400 bytes.
|
||||
*
|
||||
* The users may or may not hope that the 0x7000-0x8000 area is also unused
|
||||
* by the BROM and re-purpose it for the SPL stack.
|
||||
*
|
||||
* The size limit for the ".text + .data" sections is ~21 KiB.
|
||||
*/
|
||||
sram_swap_buffers generic_sram_swap_buffers[] = {
|
||||
{ .buf1 = 0x1C00, .buf2 = 0x5800, .size = 0x400 },
|
||||
{ .size = 0 } /* End of the table */
|
||||
};
|
||||
|
||||
soc_info_t generic_soc_info = {
|
||||
.scratch_addr = 0x1000,
|
||||
.thunk_addr = 0x5680, .thunk_size = 0x180,
|
||||
.swap_buffers = generic_sram_swap_buffers,
|
||||
};
|
||||
|
||||
/* functions to retrieve SoC information */
|
||||
|
||||
soc_info_t *get_soc_info_from_id(uint32_t soc_id)
|
||||
{
|
||||
soc_info_t *soc, *result = NULL;
|
||||
|
||||
for (soc = soc_info_table; soc->swap_buffers; soc++)
|
||||
if (soc->soc_id == soc_id) {
|
||||
result = soc;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
printf("Warning: no 'soc_sram_info' data for your SoC (id=%04X)\n",
|
||||
soc_id);
|
||||
result = &generic_soc_info;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
soc_info_t *get_soc_info_from_version(struct aw_fel_version *buf)
|
||||
{
|
||||
return get_soc_info_from_id(buf->soc_id);
|
||||
}
|
||||
|
||||
void get_soc_name_from_id(soc_name_t buffer, uint32_t soc_id)
|
||||
{
|
||||
soc_info_t *soc;
|
||||
for (soc = soc_info_table; soc->swap_buffers; soc++)
|
||||
if (soc->soc_id == soc_id && soc->name != NULL) {
|
||||
strncpy(buffer, soc->name, sizeof(soc_name_t) - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* unknown SoC (or name string missing), use the hexadecimal ID */
|
||||
snprintf(buffer, sizeof(soc_name_t) - 1, "0x%04X", soc_id);
|
||||
}
|
||||
145
soc_info.h
Normal file
145
soc_info.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _SUNXI_TOOLS_SOC_INFO_H
|
||||
#define _SUNXI_TOOLS_SOC_INFO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* SoC version information, as retrieved by the FEL protocol */
|
||||
struct aw_fel_version {
|
||||
char signature[8];
|
||||
uint32_t soc_id; /* 0x00162300 */
|
||||
uint32_t unknown_0a; /* 1 */
|
||||
uint16_t protocol; /* 1 */
|
||||
uint8_t unknown_12; /* 0x44 */
|
||||
uint8_t unknown_13; /* 0x08 */
|
||||
uint32_t scratchpad; /* 0x7e00 */
|
||||
uint32_t pad[2]; /* unused */
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Buffer for a SoC name string. We want at least 6 + 1 characters, to store
|
||||
* the hexadecimal ID "0xABCD" for unknown SoCs, plus the terminating NUL.
|
||||
*/
|
||||
typedef char soc_name_t[8];
|
||||
|
||||
/*
|
||||
* The 'sram_swap_buffers' structure is used to describe information about
|
||||
* pairwise memory regions in SRAM, the content of which needs to be exchanged
|
||||
* before calling the U-Boot SPL code and then exchanged again before returning
|
||||
* control back to the FEL code from the BROM.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t buf1; /* BROM buffer */
|
||||
uint32_t buf2; /* backup storage location */
|
||||
uint32_t size; /* buffer size */
|
||||
} sram_swap_buffers;
|
||||
|
||||
/*
|
||||
* Contains information on the watchdog peripheral, to enable reset
|
||||
*/
|
||||
typedef struct {
|
||||
/* Register that needs to be written to */
|
||||
uint32_t reg_mode;
|
||||
/* Value to write to trigger a reset */
|
||||
uint32_t reg_mode_value;
|
||||
} watchdog_info;
|
||||
|
||||
/*
|
||||
* sunxi sid sections
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name;
|
||||
uint32_t offset;
|
||||
uint32_t size_bits;
|
||||
} sid_section;
|
||||
|
||||
#define SID_SECTION(_name, _offset, _size_bits) { \
|
||||
.name = _name, \
|
||||
.offset = _offset, \
|
||||
.size_bits = _size_bits, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Each SoC variant may have its own list of memory buffers to be exchanged
|
||||
* and the information about the placement of the thunk code, which handles
|
||||
* the transition of execution from the BROM FEL code to the U-Boot SPL and
|
||||
* back.
|
||||
*
|
||||
* Note: the entries in the 'swap_buffers' tables need to be sorted by 'buf1'
|
||||
* addresses. And the 'buf1' addresses are the BROM data buffers, while 'buf2'
|
||||
* addresses are the intended backup locations.
|
||||
*
|
||||
* Also for performance reasons, we optionally want to have MMU enabled with
|
||||
* optimal section attributes configured (the code from the BROM should use
|
||||
* I-cache, writing data to the DRAM area should use write combining). The
|
||||
* reason is that the BROM FEL protocol implementation moves data using the
|
||||
* CPU somewhere on the performance critical path when transferring data over
|
||||
* USB. The older SoC variants (A10/A13/A20/A31/A23) already have MMU enabled
|
||||
* and we only need to adjust section attributes. The BROM in newer SoC variants
|
||||
* (A33/A83T/H3) doesn't enable MMU any more, so we need to find some 16K of
|
||||
* spare space in SRAM to place the translation table there and specify it as
|
||||
* the 'mmu_tt_addr' field in the 'soc_sram_info' structure. The 'mmu_tt_addr'
|
||||
* address must be 16K aligned.
|
||||
*
|
||||
* If an SoC has the "secure boot" fuse burned, it will enter FEL mode in
|
||||
* non-secure state, so with the SCR.NS bit set. Since in this mode the
|
||||
* secure/non-secure state restrictions are actually observed, we suffer
|
||||
* from several restrictions:
|
||||
* - No access to the SID information (both via memory mapped and "register").
|
||||
* - No access to secure SRAM (SRAM A2 on H3/A64/H5).
|
||||
* - No access to the secure side of the GIC, so it can't be configured to
|
||||
* be accessible from non-secure world.
|
||||
* - No RMR trigger on ARMv8 cores to bring the core into AArch64.
|
||||
* However it has been found out that a simple "smc" call will immediately
|
||||
* return from monitor mode, but with the NS bit cleared, so access to all
|
||||
* secure peripherals is suddenly possible.
|
||||
* The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to
|
||||
* have a check for this condition (reading from restricted addresses
|
||||
* typically returns zero) and then activate the SMC workaround if needed.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t soc_id; /* ID of the SoC */
|
||||
const char *name; /* human-readable SoC name string */
|
||||
uint32_t spl_addr; /* SPL load address */
|
||||
uint32_t scratch_addr; /* A safe place to upload & run code */
|
||||
uint32_t thunk_addr; /* Address of the thunk code */
|
||||
uint32_t thunk_size; /* Maximal size of the thunk code */
|
||||
bool needs_l2en; /* Set the L2EN bit */
|
||||
uint32_t mmu_tt_addr; /* MMU translation table address */
|
||||
uint32_t sid_base; /* base address for SID registers */
|
||||
uint32_t sid_offset; /* offset for SID_KEY[0-3], "root key" */
|
||||
const sid_section *sid_sections; /* sid memory maps */
|
||||
uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */
|
||||
const watchdog_info *watchdog; /* Used for reset */
|
||||
bool sid_fix; /* Use SID workaround (read via register) */
|
||||
/* Use I$ workaround (disable I$ before first write to prevent stale thunk */
|
||||
bool icache_fix;
|
||||
/* Use SMC workaround (enter secure mode) if can't read from this address */
|
||||
uint32_t needs_smc_workaround_if_zero_word_at_addr;
|
||||
uint32_t sram_size; /* Usable contiguous SRAM at spl_addr */
|
||||
sram_swap_buffers *swap_buffers;
|
||||
} soc_info_t;
|
||||
|
||||
|
||||
void get_soc_name_from_id(soc_name_t buffer, uint32_t soc_id);
|
||||
soc_info_t *get_soc_info_from_id(uint32_t soc_id);
|
||||
soc_info_t *get_soc_info_from_version(struct aw_fel_version *buf);
|
||||
|
||||
#endif /* _SUNXI_TOOLS_SOC_INFO_H */
|
||||
270
sunxi-fel.1
Normal file
270
sunxi-fel.1
Normal file
@ -0,0 +1,270 @@
|
||||
.\" Manpage for sunxi-fel
|
||||
.\" Copyright (C) 2018 by Andre Przywara <osp@andrep.de>
|
||||
.TH sunxi-fel 1 "14 Jan 2022" "1.5" "sunxi-fel man page"
|
||||
.SH NAME
|
||||
sunxi-fel \- controlling USB BootROM protocol for Allwinner CPUs
|
||||
.SH SYNOPSIS
|
||||
sunxi-fel [OPTIONS] COMMAND [ARGS]
|
||||
.SH DESCRIPTION
|
||||
sunxi-fel is a script interface for USB communication with the BootROM of
|
||||
Allwinner CPUs.
|
||||
|
||||
On explicit request (typically by pressing a button often labeled "uboot"
|
||||
or "recovery"), or when all other booting methods fail, the CPU's early ROM
|
||||
code enters the so called FEL mode, where it will wait for USB commands sent
|
||||
by some host to the Allwinner CPU's USB OTG interface. sunxi-fel implements
|
||||
this FEL protocol and communicates with the ROM code, to download or upload
|
||||
data and to start code execution.
|
||||
|
||||
Besides simply allowing to write to or to read from device memory, sunxi-fel
|
||||
also helps with more complex things, which involve uploading code, executing
|
||||
it and then returning to FEL mode, to allow further data transfer or inspection.
|
||||
In particular it supports loading and executing U-Boot, including the primary
|
||||
SPL stage.
|
||||
.SH "OPTIONS"
|
||||
Those options affect general execution and should be put first, before any
|
||||
actual commands.
|
||||
.sp
|
||||
.B \-h, \-\-help
|
||||
.RS 4
|
||||
Print a help message and exit.
|
||||
.RE
|
||||
.sp
|
||||
.B \-v, \-\-verbose
|
||||
.RS 4
|
||||
Enable verbose logging.
|
||||
.RE
|
||||
.sp
|
||||
.B \-p, \-\-progress
|
||||
.RS 4
|
||||
"write" transfers show a progress bar.
|
||||
.RE
|
||||
.sp
|
||||
.B \-l, \-\-list
|
||||
.RS 4
|
||||
Enumerate all (USB) FEL devices and exit.
|
||||
.RE
|
||||
.sp
|
||||
.B \-d, \-\-dev bus:devnum
|
||||
.RS 4
|
||||
Use specific USB bus and device number
|
||||
.RE
|
||||
.sp
|
||||
.B \-\-sid SID
|
||||
.RS 4
|
||||
Select a device by its SID key (exact match). The SID key of a particular
|
||||
device can be queried using the "sid" command.
|
||||
.RE
|
||||
.SH "SUNXI-FEL COMMANDS"
|
||||
sunxi-fel can take several commands, each followed by their parameters, and
|
||||
will execute them in order. The only exception is the "uboot" command,
|
||||
which will delay launching U-Boot until all other commands have been executed.
|
||||
.sp
|
||||
Please note that accessing any part of the DRAM will not work until the
|
||||
DRAM controller has been initialized. This can be achieved by uploading and
|
||||
executing a suitable U-Boot SPL image, using the "spl" command. Trying to
|
||||
access DRAM before that will most likely hang.
|
||||
.sp
|
||||
Any numbers given as parameters to those commands can be prefixed with "0x"
|
||||
to denote hexadecimal notation or "0" to start an octal number.
|
||||
They are interpretated as decimal numbers otherwise.
|
||||
.PP
|
||||
.B spl <filename>
|
||||
.RS 4
|
||||
Load and execute U-Boot SPL.
|
||||
.sp
|
||||
Upload the given binary file to the appropriate SRAM location, carefully
|
||||
moving the FEL stack out of the way in the process. The binary is then
|
||||
executed. When it returns, the FEL stack is restored and execution is
|
||||
transferred back to the BootROM's FEL routine.
|
||||
.sp
|
||||
If the file additionally contains a main U-Boot binary
|
||||
(u-boot-sunxi-with-spl.bin), this command also transfers that
|
||||
to memory, using the load address stored in the image file, but won't execute
|
||||
it.
|
||||
.RE
|
||||
.PP
|
||||
.B uboot <u-boot-sunxi-with-spl.bin>
|
||||
.RS 4
|
||||
like "spl", but actually starts U-Boot. U-Boot execution will take place
|
||||
when the fel utility exits. This allows combining "uboot" with further "write"
|
||||
commands, to transfer other files possibly needed for the boot.
|
||||
.RE
|
||||
.PP
|
||||
.B hex[dump] <address> <length>
|
||||
.RS 4
|
||||
Hexadecimal memory dump. Dumps <length> bytes of the memory region starting at
|
||||
<address>. The context will be displayed as a hexdump, suitable for human
|
||||
inspection.
|
||||
.RE
|
||||
.PP
|
||||
.B dump <address> <length>
|
||||
.RS 4
|
||||
Binary memory dump. Dumps <length> bytes of the memory region starting at
|
||||
<address>. The context will be dumped "as is" to the standard output, so it
|
||||
can be redirected to a file and processed as binary data.
|
||||
.RE
|
||||
.PP
|
||||
.B exe[cute] <address>
|
||||
.RS 4
|
||||
Start executing code at <address> in memory on the device.
|
||||
.RE
|
||||
.PP
|
||||
.B reset64 <address>
|
||||
.RS 4
|
||||
Request a warm reset of the core, starting execution in the 64-bit AArch64
|
||||
execution state, at <address>.
|
||||
.RE
|
||||
.PP
|
||||
.B wdreset
|
||||
.RS 4
|
||||
Reset the device by triggering the watchdog with the shortest possible period.
|
||||
This will reset the whole SoC, including all on-SoC peripherals, but might not
|
||||
affect on-board devices like PMICs or PHYs.
|
||||
.RE
|
||||
.PP
|
||||
.B memmove <dest> <source> <size>
|
||||
.RS 4
|
||||
Copy <size> bytes within device memory, from <source> to <dest>.
|
||||
.RE
|
||||
.PP
|
||||
.B readl <address>
|
||||
.RS 4
|
||||
Read a 32-bit value from device memory at <address>. The value will be output
|
||||
as a hexadecimal number, prefixed with "0x". The address needs to be 4-byte
|
||||
aligned.
|
||||
.RE
|
||||
.PP
|
||||
.B writel <address> <value>
|
||||
.RS 4
|
||||
Write the given 32-bit <value> to device memory at <address>.
|
||||
.RE
|
||||
.PP
|
||||
.B read <address> <length> <file>
|
||||
.RS 4
|
||||
Write memory contents into file. Reads <length> bytes from memory at <address>
|
||||
and writes the content into <file>.
|
||||
.RE
|
||||
.PP
|
||||
.B write <address> <file>
|
||||
.RS 4
|
||||
Store file contents into memory. Writes the entire content of <file> into
|
||||
memory at <address>.
|
||||
.RE
|
||||
.PP
|
||||
.B write-with-progress <addr> <file>
|
||||
.RS 4
|
||||
Display a textual progress bar while writing to the device. Same as "write"
|
||||
with the "-p" parameter.
|
||||
.RE
|
||||
.PP
|
||||
.B write-with-gauge <addr> <file>
|
||||
.RS 4
|
||||
Same as write, but write the progress in percentages to the standard output.
|
||||
This can be piped to any "dialog" compatible program, when using the --gauge
|
||||
widget.
|
||||
.RE
|
||||
.PP
|
||||
.B write-with-xgauge <addr> <file>
|
||||
.RS 4
|
||||
Same as write-with-gauge, but with extended gauge output. This can be piped to
|
||||
any "dialog" compatible program, using the --gauge widget. Aside from updating
|
||||
the current progress in percents, it also updates the number of bytes written
|
||||
and gives an estimated time to completion.
|
||||
.RE
|
||||
.PP
|
||||
.B multi[write] # <addr> <file> ...
|
||||
.RS 4
|
||||
Like "write-with-progress", but with multiple load adddresses and files,
|
||||
all sharing the same progress bar.
|
||||
.RE
|
||||
.PP
|
||||
.B multi[write]-with-gauge ...
|
||||
.RS 4
|
||||
Like "write-with-gauge", but with multiple load adddresses and files,
|
||||
all sharing the same progress bar.
|
||||
.RE
|
||||
.PP
|
||||
.B multi[write]-with-xgauge ...
|
||||
.RS 4
|
||||
Like "write-with-xgauge", but with multiple load adddresses and files,
|
||||
all sharing the same progress bar.
|
||||
.RE
|
||||
.PP
|
||||
.B echo-gauge "some text"
|
||||
.RS 4
|
||||
Update prompt/caption for gauge output. This outputs a command to be
|
||||
interpreted by "dialog" to change the caption text.
|
||||
.RE
|
||||
.PP
|
||||
.B ver[sion]
|
||||
.RS 4
|
||||
Show the BROM version. This prints some static data, among other things
|
||||
containing the detected SoC. Can be used to verify a FEL connection is working.
|
||||
.RE
|
||||
.PP
|
||||
.B sid
|
||||
.RS 4
|
||||
Retrieve and output the 128-bit SID key. This key contains some form of serial
|
||||
number, which should be unique to each chip (although there have been reports
|
||||
of same SIDs for particular batches of chips).
|
||||
.RE
|
||||
.PP
|
||||
.B sid-registers
|
||||
.RS 4
|
||||
As the "sid" command above, but use the alternative MMIO register access method
|
||||
on the device. There are SoCs that require this method due to bugs in the SID
|
||||
implementation, those known will automatically choose this workaround when using
|
||||
the "sid" command. This command here is to test new SoCs for compliance.
|
||||
.RE
|
||||
.PP
|
||||
.B sid-dump
|
||||
.RS 4
|
||||
Read the entire SID eFuses array and dump its content. For SoCs with a known
|
||||
eFuses layout, this will annotate the known regions.
|
||||
.RE
|
||||
.PP
|
||||
.B clear <address> <length>
|
||||
.RS 4
|
||||
Clear <length> bytes of memory starting at <address> (filling with zeroes).
|
||||
.RE
|
||||
.PP
|
||||
.B fill <address> <length> <value>
|
||||
.RS 4
|
||||
Fills <length> bytes of memory starting at <address> with the byte <value>.
|
||||
.RE
|
||||
.PP
|
||||
.B spiflash-info
|
||||
.RS 4
|
||||
Retrieves basic information about a SPI flash chip attached to the SPI0 pins.
|
||||
This is using the same method as the BootROM does, to accesses the same storage
|
||||
that the device could boot from.
|
||||
|
||||
Prints the manufacturer of the flash chip and
|
||||
its capacity. Should also be used to detect the presence of a SPI flash chip.
|
||||
.RE
|
||||
.PP
|
||||
.B spiflash-read <addr> <length> <file>
|
||||
.RS 4
|
||||
Reads <length> bytes starting from offset <addr> of a SPI flash chip, storing
|
||||
the result into <file>.
|
||||
.RE
|
||||
.PP
|
||||
.B spiflash-write <addr> <file>
|
||||
.RS 4
|
||||
Reads <file> and stores its content in the SPI flash, starting at offset <addr>.
|
||||
.RE
|
||||
.SH EXAMPLES
|
||||
.RS 4
|
||||
\fB$\fR sunxi-fel -v -p ver
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
\fB$\fR sunxi-fel uboot u-boot-sunxi-with-spl.bin
|
||||
.RE
|
||||
.sp
|
||||
.RS 4
|
||||
\fB$\fR sunxi-fel -v -p spl sunxi-spl.bin write 0x44000 bl31.bin write 0x4a000000 u-boot.bin reset64 0x44000
|
||||
.RE
|
||||
.SH AUTHOR
|
||||
Andre Przywara <osp@andrep.de>
|
||||
62
tests/Makefile
Normal file
62
tests/Makefile
Normal file
@ -0,0 +1,62 @@
|
||||
#
|
||||
# tests/Makefile
|
||||
#
|
||||
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
BOARDS_URL := https://github.com/linux-sunxi/sunxi-boards/archive/master.zip
|
||||
BOARDS_DIR := sunxi-boards
|
||||
|
||||
check: check_all_fex coverage
|
||||
|
||||
# Conversion cycle (.fex -> .bin -> .fex) test for all sunxi-boards
|
||||
check_all_fex: $(BOARDS_DIR)/README unify-fex
|
||||
./test_all_fex.sh $(BOARDS_DIR)
|
||||
|
||||
coverage: $(BOARDS_DIR)/README
|
||||
# Usage help / invocation with no args
|
||||
../sunxi-fexc -? 2> /dev/null ; exit 0
|
||||
# Improve code coverage for corner cases (e.g. erroneous parameters)
|
||||
./test_fex2bin_corner_cases.sh
|
||||
./test_bin2fex_corner_cases.sh
|
||||
|
||||
# Retrieve and extract sunxi-boards archive (containing all .fex)
|
||||
$(BOARDS_DIR).zip:
|
||||
curl -fLsS -o $@ $(BOARDS_URL)
|
||||
$(BOARDS_DIR)/README: $(BOARDS_DIR).zip
|
||||
@echo Extracting $< ...
|
||||
unzip -q $<
|
||||
mv sunxi-boards-master $(BOARDS_DIR)
|
||||
touch -r $(BOARDS_DIR) $<
|
||||
cat patches/*.patch | patch -p1
|
||||
|
||||
unify-fex: unify-fex.c
|
||||
$(CC) -Wall -Werror -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf $(BOARDS_DIR).zip $(BOARDS_DIR) unify-fex
|
||||
|
||||
#
|
||||
# Dedicated rule for Travis CI test of sunxi-boards. This assumes that the
|
||||
# sunxi-tools source (archive) was extracted into a subdir below the working
|
||||
# directory, meaning that BOARDS_DIR should be "../.."
|
||||
#
|
||||
sunxi-boards_CI: unify-fex
|
||||
# compile sunxi-fexc, link bin2fex and fex2bin
|
||||
make -C .. bin2fex fex2bin
|
||||
# apply patches to BOARDS_DIR, ignore mismatches
|
||||
cat patches/*.patch | patch --forward -r- -p2 -d $(BOARDS_DIR) || true
|
||||
# and finally run the tests
|
||||
./test_all_fex.sh $(BOARDS_DIR)
|
||||
35
tests/fextest.sh
Executable file
35
tests/fextest.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
echo $0 $*
|
||||
FEX2BIN=../fex2bin
|
||||
BIN2FEX=../bin2fex
|
||||
FEX=$1
|
||||
BIN=${FEX/%.fex/.bin}
|
||||
REVERSE=${FEX/%.fex/.new}
|
||||
${FEX2BIN} ${FEX} ${BIN}
|
||||
${BIN2FEX} ${BIN} ${REVERSE}
|
||||
# preprocess .fex, compare it to the bin2fex output
|
||||
if ./unify-fex ${FEX} | diff -uwB - ${REVERSE}; then
|
||||
# if successful, clean up the output files
|
||||
rm -f ${BIN} ${REVERSE}
|
||||
else
|
||||
echo '***'
|
||||
echo "*** ERROR processing ${FEX}"
|
||||
echo '***'
|
||||
exit 1
|
||||
fi
|
||||
23
tests/test_all_fex.sh
Executable file
23
tests/test_all_fex.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
FEXFILES=fexfiles.lst
|
||||
find $1 -name '*.fex' > ${FEXFILES}
|
||||
while read fex; do
|
||||
./fextest.sh ${fex} || exit
|
||||
done <${FEXFILES}
|
||||
rm -f ${FEXFILES}
|
||||
28
tests/test_bin2fex_corner_cases.sh
Executable file
28
tests/test_bin2fex_corner_cases.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# === Test errors / corner cases of "bin2fex", improving on code coverage ===
|
||||
#
|
||||
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
BIN2FEX=../bin2fex
|
||||
TESTFILE=sunxi-boards/sys_config/a10/a10-olinuxino-lime
|
||||
# use sunxi-fexc in "fex2bin" mode, testing explicit parameters at the same time
|
||||
FEX2BIN="../sunxi-fexc -v -q -I fex -O bin"
|
||||
|
||||
${FEX2BIN} ${TESTFILE}.fex ${TESTFILE}.bin
|
||||
# have bin2fex explicitly read /dev/stdin, to force use of fexc.c's "read_all()"
|
||||
cat ${TESTFILE}.bin | ${BIN2FEX} /dev/stdin > /dev/null
|
||||
rm -f ${TESTFILE}.bin
|
||||
89
tests/test_fex2bin_corner_cases.sh
Executable file
89
tests/test_fex2bin_corner_cases.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# === Test errors / corner cases of "fex2bin", improving on code coverage ===
|
||||
#
|
||||
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
FEX2BIN=../fex2bin
|
||||
|
||||
function expect () {
|
||||
OUT=`${FEX2BIN} 2>&1`
|
||||
if (! echo ${OUT} | grep -q "$1"); then
|
||||
echo ERROR: Expected substring \"$1\" not found in output:
|
||||
echo ${OUT}
|
||||
exit 1
|
||||
fi
|
||||
#echo ${OUT}
|
||||
}
|
||||
|
||||
# missing section, CRLF line ending
|
||||
echo -e "foobar\r\n" | expect "data must follow a section"
|
||||
|
||||
# malformed sections
|
||||
expect "incomplete section declaration" <<-EOF
|
||||
[foobar
|
||||
EOF
|
||||
expect "invalid character at 5" <<-EOF
|
||||
[foo#bar]
|
||||
EOF
|
||||
|
||||
# invalid entry
|
||||
expect "invalid character at 4" <<-EOF
|
||||
[foo]
|
||||
bar
|
||||
EOF
|
||||
|
||||
# bad port specifiers
|
||||
expect "parse error at 12" <<-EOF
|
||||
[foo]
|
||||
bar = port:P@0
|
||||
EOF
|
||||
expect "invalid character at 14" <<-EOF
|
||||
[foo]
|
||||
bar = port:PA*
|
||||
EOF
|
||||
expect "port out of range at 14" <<-EOF
|
||||
[foo]
|
||||
bar = port:PA666
|
||||
EOF
|
||||
expect "value out of range at 17" <<-EOF
|
||||
[foo]
|
||||
bar = port:PA00<-1>
|
||||
EOF
|
||||
expect "invalid character at 18" <<-EOF
|
||||
[foo]
|
||||
bar = port:PA00<0 >
|
||||
EOF
|
||||
|
||||
# bad <key> = <value> pairs
|
||||
expect "invalid character at 8" <<-EOF
|
||||
[foo]
|
||||
bar = 0*
|
||||
EOF
|
||||
expect "value out of range" <<-EOF
|
||||
[foo]
|
||||
bar = 4294967296
|
||||
EOF
|
||||
expect "unquoted value 'bad', assuming string" <<-EOF
|
||||
[foo]
|
||||
bar = bad
|
||||
EOF
|
||||
|
||||
# test truncation of very long identifiers
|
||||
${FEX2BIN} > /dev/null <<-EOF
|
||||
[an_overly_long_section_name_to_truncate]
|
||||
an_overly_long_entry_name_to_truncate = 0
|
||||
EOF
|
||||
148
tests/unify-fex.c
Normal file
148
tests/unify-fex.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* unify-fex.c
|
||||
*
|
||||
* A utility program to do some standard transformations on .fex files,
|
||||
* to allow simpler (diff) comparison with the output of bin2fex.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* string macro to determine if str starts with a given literal */
|
||||
#define starts(str, literal) \
|
||||
(strncmp(str, "" literal, sizeof(literal) - 1) == 0)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *input = stdin;
|
||||
char line[1024];
|
||||
char *c, *p;
|
||||
int64_t num;
|
||||
|
||||
if (argc >= 2) {
|
||||
input = fopen(argv[1], "r");
|
||||
if (!input) {
|
||||
perror("failed to open input file");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* loop over all input lines, output goes to stdout */
|
||||
while (fgets(line, sizeof(line), input)) {
|
||||
|
||||
/* strip all whitespace (even CR/LF) from the input line */
|
||||
for (c = p = line; *p; p++) {
|
||||
if (!isspace(*p))
|
||||
*c++ = *p;
|
||||
}
|
||||
*c = '\0';
|
||||
|
||||
if (*line == ';' || *line == '#')
|
||||
/* This is a comment line, simply ignore it */
|
||||
continue;
|
||||
if (*line == ':')
|
||||
continue; /* suspect malformed comment, ignore */
|
||||
|
||||
if ((p = strchr(line, '='))) {
|
||||
/* This is a <key> = <value> line, reformat it */
|
||||
c = strdup(p + 1);
|
||||
sprintf(p, " = %s", c);
|
||||
free(c);
|
||||
p += 3; /* have p point to the beginning of <value> */
|
||||
|
||||
if (starts(p, "port:")) {
|
||||
if (p[5] == 'P') { /* port:P... */
|
||||
/* get pin number (including bank) */
|
||||
num = ((p[6] - 'A') << 5) + strtoll(p + 7, &c, 10);
|
||||
c = strdup(c);
|
||||
sprintf(p, "port:P%c%02" PRId64 "%s", 'A' + (int)(num >> 5), num & 0x1F, c);
|
||||
free(c);
|
||||
|
||||
/* check angle brackets to determine options count */
|
||||
num = 0;
|
||||
for (c = p + 9; *c; c++) {
|
||||
if (*c == '<')
|
||||
num++;
|
||||
}
|
||||
/* append "<default>" for missing options */
|
||||
c = strrchr(p, '\0');
|
||||
while (num < 4) {
|
||||
c += sprintf(c, "<default>");
|
||||
num++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* fix formatting of numeric values, depending on the keyword
|
||||
* these are a bit nasty, since they vary wildly between hex
|
||||
* and decimal - see decompile_single_mode() in script_fex.c
|
||||
*/
|
||||
num = strtoll(p, NULL, 0);
|
||||
if (num || *p == '0') {
|
||||
int hex = starts(line, "csi_twi_addr");
|
||||
hex |= starts(line, "ctp_twi_addr");
|
||||
hex |= starts(line, "dram_baseaddr");
|
||||
hex |= starts(line, "dram_emr");
|
||||
hex |= starts(line, "dram_tpr");
|
||||
hex |= starts(line, "dram_zq");
|
||||
hex |= starts(line, "g2d_size");
|
||||
hex |= starts(line, "gsensor_twi_addr");
|
||||
hex |= starts(line, "lcd_gamma_tbl_");
|
||||
hex |= starts(line, "rtp_press_threshold ");
|
||||
hex |= starts(line, "rtp_sensitive_level");
|
||||
hex |= starts(line, "tkey_twi_addr");
|
||||
|
||||
/* large decimals will be decompiled as negative */
|
||||
if (!hex && num >= 2147483648LL)
|
||||
num -= 4294967296LL;
|
||||
|
||||
sprintf(p, hex ? "0x%" PRIx64 : "%" PRId64, num);
|
||||
} else {
|
||||
/*
|
||||
* We expect all other (= non-numeric) values
|
||||
* to be strings, always quote them.
|
||||
*/
|
||||
if (*p && (*p != '"')) {
|
||||
c = strdup(p);
|
||||
sprintf(p, "\"%s\"", c);
|
||||
free(c);
|
||||
}
|
||||
/* Remove a trailing semicolon. */
|
||||
c = strchr(p, 0);
|
||||
if (*--c == ';')
|
||||
*c = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puts(line);
|
||||
}
|
||||
|
||||
if (ferror(input)) {
|
||||
perror("file read error");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fclose(input);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
35
thunks/Makefile
Normal file
35
thunks/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
#
|
||||
# build "preprocessed" .h files for inclusion of ARM scratch code
|
||||
#
|
||||
|
||||
SPL_THUNK := fel-to-spl-thunk.h
|
||||
THUNKS := clrsetbits.h
|
||||
THUNKS += memcpy.h
|
||||
THUNKS += readl_writel.h
|
||||
THUNKS += rmr-thunk.h
|
||||
THUNKS += sid_read_root.h
|
||||
|
||||
all: $(SPL_THUNK) $(THUNKS)
|
||||
# clean up object files afterwards
|
||||
rm -f *.o
|
||||
|
||||
# This empty prerequisite enforces a rebuild of all the headers on every run
|
||||
FORCE:
|
||||
|
||||
# If not specified explicitly: try to guess a suitable ARM toolchain prefix
|
||||
CROSS_COMPILE ?= $(shell ../find-arm-gcc.sh)
|
||||
|
||||
AS := $(CROSS_COMPILE)as
|
||||
OBJDUMP := $(CROSS_COMPILE)objdump
|
||||
|
||||
AWK_O_TO_H := LC_ALL=C awk -f objdump_to_h.awk
|
||||
|
||||
# The SPL thunk requires a different output format. The "style" variable for
|
||||
# awk controls this, and causes the htole32() conversion to be omitted.
|
||||
fel-to-spl-thunk.h: fel-to-spl-thunk.S FORCE
|
||||
$(AS) -o $(subst .S,.o,$<) -march=armv5te $<
|
||||
$(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@
|
||||
|
||||
$(THUNKS): %.h: %.S FORCE
|
||||
$(AS) -o $(subst .S,.o,$<) -march=armv5te $<
|
||||
$(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) > $@
|
||||
20
thunks/README.md
Normal file
20
thunks/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
# thunks/README.md
|
||||
|
||||
This directory contains assembly sources for ARM [thunk] code, and
|
||||
a corresponding _Makefile_. The idea is that the resulting binary routines
|
||||
can be transferred to a suitable target device and then executed 'remotely',
|
||||
usually via `sunxi-fel`.
|
||||
|
||||
Normally you don't need to change or (re)build anything within this folder.
|
||||
Currently our main build process (via the parent directory's _Makefile_)
|
||||
only includes `fel-to-spl-thunk.h` directly. Other _.h_ files are provided
|
||||
**just for reference**. The main purpose of this folder is simply keeping
|
||||
track of _.S_ sources, to help with possible future maintenance of the
|
||||
various code snippets.
|
||||
|
||||
Please note that any files lacking explicit license information are intended
|
||||
to be covered by the project's [overall license](../LICENSE.md) (GPLv2).
|
||||
|
||||
|
||||
[thunk]: https://en.wikipedia.org/wiki/Thunk#Interoperability
|
||||
17
thunks/clrsetbits.S
Normal file
17
thunks/clrsetbits.S
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Thunk code to assist with bitwise operations (set/clear) via FEL
|
||||
*/
|
||||
|
||||
fel_clrsetbits_le32:
|
||||
ldr r0, 1f /* address */
|
||||
ldr r1, [r0] /* load value */
|
||||
ldr r2, 2f /* clrbits mask */
|
||||
bic r1, r2 /* clear bits, post-increment r1 */
|
||||
ldr r2, 3f /* setbits mask */
|
||||
orr r1, r2 /* set bits (logical "or") */
|
||||
str r1, [r0] /* store result */
|
||||
bx lr
|
||||
|
||||
1: .word 0 /* addr */
|
||||
2: .word 0 /* clrbits (= bits to clear) */
|
||||
3: .word 0 /* setbits (= bits to set) */
|
||||
9
thunks/clrsetbits.h
Normal file
9
thunks/clrsetbits.h
Normal file
@ -0,0 +1,9 @@
|
||||
/* <fel_clrsetbits_le32>: */
|
||||
htole32(0xe59f0018), /* 0: ldr r0, [pc, #24] */
|
||||
htole32(0xe5901000), /* 4: ldr r1, [r0] */
|
||||
htole32(0xe59f2014), /* 8: ldr r2, [pc, #20] */
|
||||
htole32(0xe1c11002), /* c: bic r1, r1, r2 */
|
||||
htole32(0xe59f2010), /* 10: ldr r2, [pc, #16] */
|
||||
htole32(0xe1811002), /* 14: orr r1, r1, r2 */
|
||||
htole32(0xe5801000), /* 18: str r1, [r0] */
|
||||
htole32(0xe12fff1e), /* 1c: bx lr */
|
||||
162
thunks/fel-to-spl-thunk.S
Normal file
162
thunks/fel-to-spl-thunk.S
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright © 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
.arm
|
||||
|
||||
BUF1 .req r0
|
||||
BUF2 .req r1
|
||||
TMP1 .req r2
|
||||
TMP2 .req r3
|
||||
SWAPTBL .req r4
|
||||
FULLSIZE .req r5
|
||||
BUFSIZE .req r6
|
||||
CHECKSUM .req r7
|
||||
SPL_ADDR .req r8
|
||||
|
||||
entry_point:
|
||||
b setup_stack
|
||||
|
||||
stack_begin:
|
||||
.space 32, 0xff
|
||||
stack_end:
|
||||
nop
|
||||
|
||||
/* A function, which walks the table and swaps all buffers */
|
||||
swap_all_buffers:
|
||||
adr SWAPTBL, appended_data + 4
|
||||
swap_next_buffer:
|
||||
ldr BUF1, [SWAPTBL], #4
|
||||
ldr BUF2, [SWAPTBL], #4
|
||||
ldr BUFSIZE, [SWAPTBL], #4
|
||||
cmp BUFSIZE, #0
|
||||
bxeq lr
|
||||
swap_next_word:
|
||||
ldr TMP1, [BUF1]
|
||||
ldr TMP2, [BUF2]
|
||||
subs BUFSIZE, BUFSIZE, #4
|
||||
str TMP1, [BUF2], #4
|
||||
str TMP2, [BUF1], #4
|
||||
bne swap_next_word
|
||||
b swap_next_buffer
|
||||
|
||||
setup_stack: /* Save the original SP, LR and CPSR to stack */
|
||||
ldr SPL_ADDR, appended_data
|
||||
adr BUF1, stack_end
|
||||
str sp, [BUF1, #-4]!
|
||||
mov sp, BUF1
|
||||
mrs TMP1, cpsr
|
||||
push {TMP1, lr}
|
||||
|
||||
/* Disable IRQ and FIQ */
|
||||
orr TMP1, #0xc0
|
||||
msr cpsr_c, TMP1
|
||||
|
||||
/* Check if the instructions or data cache is enabled */
|
||||
mrc p15, 0, TMP1, c1, c0, 0
|
||||
tst TMP1, #(1 << 2)
|
||||
tsteq TMP1, #(1 << 12)
|
||||
bne cache_is_unsupported
|
||||
|
||||
bl swap_all_buffers
|
||||
|
||||
verify_checksum:
|
||||
ldr CHECKSUM, checksum_seed
|
||||
mov BUF1, SPL_ADDR
|
||||
ldr FULLSIZE, [BUF1, #16]
|
||||
check_next_word:
|
||||
ldr TMP1, [BUF1], #4
|
||||
subs FULLSIZE, FULLSIZE, #4
|
||||
add CHECKSUM, CHECKSUM, TMP1
|
||||
bne check_next_word
|
||||
|
||||
ldr TMP1, [SPL_ADDR, #12]
|
||||
subs CHECKSUM, CHECKSUM, TMP1, lsl #1
|
||||
bne checksum_is_bad
|
||||
|
||||
/* Change 'eGON.BT0' -> 'eGON.FEL' */
|
||||
ldr TMP1, egon_fel_str
|
||||
str TMP1, [SPL_ADDR, #8]
|
||||
|
||||
/*
|
||||
* Call the SPL code, but before that make sure the CPU sees the
|
||||
* recently uploaded code. This requires a DSB and ISB.
|
||||
* The "dsb" and "isb" *instructions* are not available in ARMv5TE,
|
||||
* but at least for DSB we can use the CP15 register encoding. This
|
||||
* works for ARMv7 and v8 as well, because we have checked our SCTLR
|
||||
* before (in fel.c), so we know that CP15BEN is set.
|
||||
* The ARM926 core does not implement ISB, instead the TRM recommends
|
||||
* just a branch to achieve the same "flush the pipeline" effect.
|
||||
* As just this is not sufficient for later cores, check the MIDR
|
||||
* register, and do the DSB only for ARMv6 or later.
|
||||
* The input register for the CP15 instruction is ignored.
|
||||
*/
|
||||
mcr p15, 0, TMP1, c7, c10, 4 /* CP15DSB */
|
||||
mrc p15, 0, TMP1, c0, c0, 0 /* read MIDR */
|
||||
and TMP1, TMP1, #(0xf << 16) /* architecture */
|
||||
cmp TMP1, #(0x6 << 16) /* ARMv5TEJ */
|
||||
mcrgt p15, 0, TMP1, c7, c5, 4 /* CP15ISB, if > ARMv5TEJ */
|
||||
blx SPL_ADDR
|
||||
|
||||
/* Return back to FEL */
|
||||
b return_to_fel
|
||||
|
||||
cache_is_unsupported:
|
||||
/* Bail out if cache is enabled and change 'eGON.BT0' -> 'eGON.???' */
|
||||
ldr TMP1, cache_enabled_str
|
||||
str TMP1, [SPL_ADDR, #8]
|
||||
b return_to_fel_noswap
|
||||
|
||||
checksum_is_bad:
|
||||
/* The checksum test failed, so change 'eGON.BT0' -> 'eGON.BAD' */
|
||||
ldr TMP1, checksum_failed_str
|
||||
str TMP1, [SPL_ADDR, #8]
|
||||
|
||||
return_to_fel:
|
||||
bl swap_all_buffers
|
||||
return_to_fel_noswap:
|
||||
pop {TMP1, lr}
|
||||
msr cpsr_c, TMP1 /* Restore the original CPSR */
|
||||
ldr sp, [sp]
|
||||
bx lr
|
||||
|
||||
checksum_seed:
|
||||
.word 0x5f0a6c39
|
||||
egon_fel_str:
|
||||
.ascii ".FEL"
|
||||
cache_enabled_str:
|
||||
.ascii ".???"
|
||||
checksum_failed_str:
|
||||
.ascii ".BAD"
|
||||
|
||||
appended_data:
|
||||
/*
|
||||
* The appended data uses the following format:
|
||||
*
|
||||
* struct {
|
||||
* uint32_t spl_addr;
|
||||
* sram_swap_buffers swaptbl[];
|
||||
* };
|
||||
*
|
||||
* More details about the 'spl_addr' variable and the 'sram_swap_buffers'
|
||||
* struct can be found in the 'fel.c' source file.
|
||||
*/
|
||||
86
thunks/fel-to-spl-thunk.h
Normal file
86
thunks/fel-to-spl-thunk.h
Normal file
@ -0,0 +1,86 @@
|
||||
/* <entry_point>: */
|
||||
0xea000015, /* 0: b 5c <setup_stack> */
|
||||
/* <stack_begin>: */
|
||||
0xffffffff, /* 4: .word 0xffffffff */
|
||||
0xffffffff, /* 8: .word 0xffffffff */
|
||||
0xffffffff, /* c: .word 0xffffffff */
|
||||
0xffffffff, /* 10: .word 0xffffffff */
|
||||
0xffffffff, /* 14: .word 0xffffffff */
|
||||
0xffffffff, /* 18: .word 0xffffffff */
|
||||
0xffffffff, /* 1c: .word 0xffffffff */
|
||||
0xffffffff, /* 20: .word 0xffffffff */
|
||||
/* <stack_end>: */
|
||||
0xe1a00000, /* 24: nop */
|
||||
/* <swap_all_buffers>: */
|
||||
0xe28f40e8, /* 28: add r4, pc, #232 */
|
||||
/* <swap_next_buffer>: */
|
||||
0xe4940004, /* 2c: ldr r0, [r4], #4 */
|
||||
0xe4941004, /* 30: ldr r1, [r4], #4 */
|
||||
0xe4946004, /* 34: ldr r6, [r4], #4 */
|
||||
0xe3560000, /* 38: cmp r6, #0 */
|
||||
0x012fff1e, /* 3c: bxeq lr */
|
||||
/* <swap_next_word>: */
|
||||
0xe5902000, /* 40: ldr r2, [r0] */
|
||||
0xe5913000, /* 44: ldr r3, [r1] */
|
||||
0xe2566004, /* 48: subs r6, r6, #4 */
|
||||
0xe4812004, /* 4c: str r2, [r1], #4 */
|
||||
0xe4803004, /* 50: str r3, [r0], #4 */
|
||||
0x1afffff9, /* 54: bne 40 <swap_next_word> */
|
||||
0xeafffff3, /* 58: b 2c <swap_next_buffer> */
|
||||
/* <setup_stack>: */
|
||||
0xe59f80b0, /* 5c: ldr r8, [pc, #176] */
|
||||
0xe24f0044, /* 60: sub r0, pc, #68 */
|
||||
0xe520d004, /* 64: str sp, [r0, #-4]! */
|
||||
0xe1a0d000, /* 68: mov sp, r0 */
|
||||
0xe10f2000, /* 6c: mrs r2, CPSR */
|
||||
0xe92d4004, /* 70: push {r2, lr} */
|
||||
0xe38220c0, /* 74: orr r2, r2, #192 */
|
||||
0xe121f002, /* 78: msr CPSR_c, r2 */
|
||||
0xee112f10, /* 7c: mrc 15, 0, r2, cr1, cr0, {0} */
|
||||
0xe3120004, /* 80: tst r2, #4 */
|
||||
0x03120a01, /* 84: tsteq r2, #4096 */
|
||||
0x1a000013, /* 88: bne dc <cache_is_unsupported> */
|
||||
0xebffffe5, /* 8c: bl 28 <swap_all_buffers> */
|
||||
/* <verify_checksum>: */
|
||||
0xe59f706c, /* 90: ldr r7, [pc, #108] */
|
||||
0xe1a00008, /* 94: mov r0, r8 */
|
||||
0xe5905010, /* 98: ldr r5, [r0, #16] */
|
||||
/* <check_next_word>: */
|
||||
0xe4902004, /* 9c: ldr r2, [r0], #4 */
|
||||
0xe2555004, /* a0: subs r5, r5, #4 */
|
||||
0xe0877002, /* a4: add r7, r7, r2 */
|
||||
0x1afffffb, /* a8: bne 9c <check_next_word> */
|
||||
0xe598200c, /* ac: ldr r2, [r8, #12] */
|
||||
0xe0577082, /* b0: subs r7, r7, r2, lsl #1 */
|
||||
0x1a00000b, /* b4: bne e8 <checksum_is_bad> */
|
||||
0xe59f2048, /* b8: ldr r2, [pc, #72] */
|
||||
0xe5882008, /* bc: str r2, [r8, #8] */
|
||||
0xee072f9a, /* c0: mcr 15, 0, r2, cr7, cr10, {4} */
|
||||
0xee102f10, /* c4: mrc 15, 0, r2, cr0, cr0, {0} */
|
||||
0xe202280f, /* c8: and r2, r2, #983040 */
|
||||
0xe3520806, /* cc: cmp r2, #393216 */
|
||||
0xce072f95, /* d0: mcrgt 15, 0, r2, cr7, cr5, {4} */
|
||||
0xe12fff38, /* d4: blx r8 */
|
||||
0xea000004, /* d8: b f0 <return_to_fel> */
|
||||
/* <cache_is_unsupported>: */
|
||||
0xe59f2028, /* dc: ldr r2, [pc, #40] */
|
||||
0xe5882008, /* e0: str r2, [r8, #8] */
|
||||
0xea000002, /* e4: b f4 <return_to_fel_noswap> */
|
||||
/* <checksum_is_bad>: */
|
||||
0xe59f2020, /* e8: ldr r2, [pc, #32] */
|
||||
0xe5882008, /* ec: str r2, [r8, #8] */
|
||||
/* <return_to_fel>: */
|
||||
0xebffffcc, /* f0: bl 28 <swap_all_buffers> */
|
||||
/* <return_to_fel_noswap>: */
|
||||
0xe8bd4004, /* f4: pop {r2, lr} */
|
||||
0xe121f002, /* f8: msr CPSR_c, r2 */
|
||||
0xe59dd000, /* fc: ldr sp, [sp] */
|
||||
0xe12fff1e, /* 100: bx lr */
|
||||
/* <checksum_seed>: */
|
||||
0x5f0a6c39, /* 104: .word 0x5f0a6c39 */
|
||||
/* <egon_fel_str>: */
|
||||
0x4c45462e, /* 108: .word 0x4c45462e */
|
||||
/* <cache_enabled_str>: */
|
||||
0x3f3f3f2e, /* 10c: .word 0x3f3f3f2e */
|
||||
/* <checksum_failed_str>: */
|
||||
0x4441422e, /* 110: .word 0x4441422e */
|
||||
70
thunks/memcpy.S
Normal file
70
thunks/memcpy.S
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* copy "upwards", increasing destination and source addresses
|
||||
*/
|
||||
fel_memcpy_up:
|
||||
ldr r0, 1f /* dst_addr */
|
||||
ldr r1, 2f /* src_addr */
|
||||
ldr r2, 3f /* bytes */
|
||||
sub r3, r1, r0
|
||||
tst r3, #3 /* test LSB for word alignment */
|
||||
bne copyup_tail /* unaligned access, copy byte-wise */
|
||||
copyup_head:
|
||||
tst r1, #3 /* word boundary? */
|
||||
beq copyup_loop
|
||||
ldrb r3, [r1], #1 /* load and post-inc */
|
||||
strb r3, [r0], #1 /* store and post-inc */
|
||||
subs r2, #1 /* r2 -= 1 */
|
||||
bpl copyup_head
|
||||
bx lr /* early return on small byte count (r2 < 0) */
|
||||
copyup_loop:
|
||||
subs r2, #4 /* r2 -= 4 */
|
||||
ldrpl r3, [r1], #4 /* load and post-inc */
|
||||
strpl r3, [r0], #4 /* store and post-inc */
|
||||
bpl copyup_loop /* while (r2 >= 0) */
|
||||
add r2, #4 /* r2 = remaining byte count */
|
||||
copyup_tail:
|
||||
subs r2, #1 /* r2 -= 1 */
|
||||
bxmi lr /* return on (r2 < 0) */
|
||||
ldrb r3, [r1], #1 /* load and post-inc */
|
||||
strb r3, [r0], #1 /* store and post-inc */
|
||||
b copyup_tail
|
||||
|
||||
1: .word 0 /* dst_addr */
|
||||
2: .word 0 /* src_addr */
|
||||
3: .word 0 /* bytes */
|
||||
|
||||
/*
|
||||
* copy "downwards", using base-relative indexing
|
||||
*/
|
||||
fel_memcpy_down:
|
||||
ldr r0, 1f /* dst_addr */
|
||||
ldr r1, 2f /* src_addr */
|
||||
ldr r2, 3f /* bytes */
|
||||
sub r3, r0, r1
|
||||
tst r3, #3 /* test LSB for word alignment */
|
||||
bne copydn_tail /* unaligned access, copy byte-wise */
|
||||
copydn_head:
|
||||
add r3, r1, r2 /* r3 = r1 + r2, for alignment check */
|
||||
tst r3, #3 /* word boundary? */
|
||||
beq copydn_loop
|
||||
subs r2, #1 /* r2 -= 1 */
|
||||
bxmi lr /* early return on small byte count (r2 < 0) */
|
||||
ldrb r3, [r1, r2] /* load byte */
|
||||
strb r3, [r0, r2] /* store byte */
|
||||
b copydn_head
|
||||
copydn_loop:
|
||||
subs r2, #4 /* r2 -= 4 */
|
||||
ldrpl r3, [r1, r2] /* load word */
|
||||
strpl r3, [r0, r2] /* store word */
|
||||
bpl copydn_loop /* while (r2 >= 0) */
|
||||
add r2, #4 /* r2 = remaining byte count */
|
||||
copydn_tail:
|
||||
subs r2, #1 /* r2 -= 1 */
|
||||
bxmi lr /* return on (r2 < 0) */
|
||||
ldrb r3, [r1, r2] /* load byte */
|
||||
strb r3, [r0, r2] /* store byte */
|
||||
b copydn_tail
|
||||
|
||||
1: .word 0 /* dst_addr */
|
||||
2: .word 0 /* src_addr */
|
||||
3: .word 0 /* bytes */
|
||||
55
thunks/memcpy.h
Normal file
55
thunks/memcpy.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* <fel_memcpy_up>: */
|
||||
htole32(0xe59f0054), /* 0: ldr r0, [pc, #84] */
|
||||
htole32(0xe59f1054), /* 4: ldr r1, [pc, #84] */
|
||||
htole32(0xe59f2054), /* 8: ldr r2, [pc, #84] */
|
||||
htole32(0xe0413000), /* c: sub r3, r1, r0 */
|
||||
htole32(0xe3130003), /* 10: tst r3, #3 */
|
||||
htole32(0x1a00000b), /* 14: bne 48 <copyup_tail> */
|
||||
/* <copyup_head>: */
|
||||
htole32(0xe3110003), /* 18: tst r1, #3 */
|
||||
htole32(0x0a000004), /* 1c: beq 34 <copyup_loop> */
|
||||
htole32(0xe4d13001), /* 20: ldrb r3, [r1], #1 */
|
||||
htole32(0xe4c03001), /* 24: strb r3, [r0], #1 */
|
||||
htole32(0xe2522001), /* 28: subs r2, r2, #1 */
|
||||
htole32(0x5afffff9), /* 2c: bpl 18 <copyup_head> */
|
||||
htole32(0xe12fff1e), /* 30: bx lr */
|
||||
/* <copyup_loop>: */
|
||||
htole32(0xe2522004), /* 34: subs r2, r2, #4 */
|
||||
htole32(0x54913004), /* 38: ldrpl r3, [r1], #4 */
|
||||
htole32(0x54803004), /* 3c: strpl r3, [r0], #4 */
|
||||
htole32(0x5afffffb), /* 40: bpl 34 <copyup_loop> */
|
||||
htole32(0xe2822004), /* 44: add r2, r2, #4 */
|
||||
/* <copyup_tail>: */
|
||||
htole32(0xe2522001), /* 48: subs r2, r2, #1 */
|
||||
htole32(0x412fff1e), /* 4c: bxmi lr */
|
||||
htole32(0xe4d13001), /* 50: ldrb r3, [r1], #1 */
|
||||
htole32(0xe4c03001), /* 54: strb r3, [r0], #1 */
|
||||
htole32(0xeafffffa), /* 58: b 48 <copyup_tail> */
|
||||
/* <fel_memcpy_down>: */
|
||||
htole32(0xe59f0058), /* 68: ldr r0, [pc, #88] */
|
||||
htole32(0xe59f1058), /* 6c: ldr r1, [pc, #88] */
|
||||
htole32(0xe59f2058), /* 70: ldr r2, [pc, #88] */
|
||||
htole32(0xe0403001), /* 74: sub r3, r0, r1 */
|
||||
htole32(0xe3130003), /* 78: tst r3, #3 */
|
||||
htole32(0x1a00000c), /* 7c: bne b4 <copydn_tail> */
|
||||
/* <copydn_head>: */
|
||||
htole32(0xe0813002), /* 80: add r3, r1, r2 */
|
||||
htole32(0xe3130003), /* 84: tst r3, #3 */
|
||||
htole32(0x0a000004), /* 88: beq a0 <copydn_loop> */
|
||||
htole32(0xe2522001), /* 8c: subs r2, r2, #1 */
|
||||
htole32(0x412fff1e), /* 90: bxmi lr */
|
||||
htole32(0xe7d13002), /* 94: ldrb r3, [r1, r2] */
|
||||
htole32(0xe7c03002), /* 98: strb r3, [r0, r2] */
|
||||
htole32(0xeafffff7), /* 9c: b 80 <copydn_head> */
|
||||
/* <copydn_loop>: */
|
||||
htole32(0xe2522004), /* a0: subs r2, r2, #4 */
|
||||
htole32(0x57913002), /* a4: ldrpl r3, [r1, r2] */
|
||||
htole32(0x57803002), /* a8: strpl r3, [r0, r2] */
|
||||
htole32(0x5afffffb), /* ac: bpl a0 <copydn_loop> */
|
||||
htole32(0xe2822004), /* b0: add r2, r2, #4 */
|
||||
/* <copydn_tail>: */
|
||||
htole32(0xe2522001), /* b4: subs r2, r2, #1 */
|
||||
htole32(0x412fff1e), /* b8: bxmi lr */
|
||||
htole32(0xe7d13002), /* bc: ldrb r3, [r1, r2] */
|
||||
htole32(0xe7c03002), /* c0: strb r3, [r0, r2] */
|
||||
htole32(0xeafffffa), /* c4: b b4 <copydn_tail> */
|
||||
33
thunks/objdump_to_h.awk
Normal file
33
thunks/objdump_to_h.awk
Normal file
@ -0,0 +1,33 @@
|
||||
# labels
|
||||
/[[:xdigit:]]+ <\w+>:/ {
|
||||
# (Note: using $0 instead of $2 would also include the address)
|
||||
if (style=="old")
|
||||
printf "\t/* %s */\n", $2
|
||||
else
|
||||
printf "\t\t/* %s */\n", $2
|
||||
}
|
||||
|
||||
# disassembly lines
|
||||
/[[:xdigit:]]+:/ {
|
||||
if (style=="old")
|
||||
printf "\t0x%s, /* %9s %-10s", $2, $1, $3
|
||||
else
|
||||
printf "\t\thtole32(0x%s), /* %5s %-5s", $2, $1, $3
|
||||
|
||||
for (i = 4; i <= NF; i++)
|
||||
if ($i == ";") {
|
||||
# strip comment (anything after and including ';')
|
||||
NF = i - 1
|
||||
break
|
||||
}
|
||||
# clear $1 to $3, which re-calculates $0 (= remainder of line)
|
||||
$3 = ""
|
||||
$2 = ""
|
||||
$1 = ""
|
||||
gsub("^\\s+", "") # strip leading whitespace
|
||||
|
||||
if (style=="old")
|
||||
printf " %-28s */\n", $0
|
||||
else
|
||||
printf " %-23s */\n", $0
|
||||
}
|
||||
41
thunks/readl_writel.S
Normal file
41
thunks/readl_writel.S
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Thunk code for buffered 'long' (i.e. 32-bit) read and write operations
|
||||
*/
|
||||
|
||||
.equ MAX_WORDS, 0x100 - 12
|
||||
|
||||
fel_readl_n:
|
||||
ldr r0, 1f /* read_addr */
|
||||
adr r1, 3f /* read_data */
|
||||
ldr r2, 2f /* read_count */
|
||||
/* limit word count to a maximum value */
|
||||
cmp r2, #MAX_WORDS
|
||||
movgt r2, #MAX_WORDS
|
||||
read_loop:
|
||||
subs r2, #1
|
||||
bxmi lr
|
||||
ldr r3, [r0], #4
|
||||
str r3, [r1], #4
|
||||
b read_loop
|
||||
|
||||
1: .word 0 /* read_addr */
|
||||
2: .word 0 /* read_count */
|
||||
3: .word 0 /* read_data */
|
||||
|
||||
fel_writel_n:
|
||||
ldr r0, 1f /* write_addr */
|
||||
adr r1, 3f /* write_data */
|
||||
ldr r2, 2f /* write_count */
|
||||
/* limit word count to a maximum value */
|
||||
cmp r2, #MAX_WORDS
|
||||
movgt r2, #MAX_WORDS
|
||||
write_loop:
|
||||
subs r2, #1
|
||||
bxmi lr
|
||||
ldr r3, [r1], #4
|
||||
str r3, [r0], #4
|
||||
b write_loop
|
||||
|
||||
1: .word 0 /* write_addr */
|
||||
2: .word 0 /* write_count */
|
||||
3: .word 0 /* write_data */
|
||||
24
thunks/readl_writel.h
Normal file
24
thunks/readl_writel.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* <fel_readl_n>: */
|
||||
htole32(0xe59f0020), /* 0: ldr r0, [pc, #32] */
|
||||
htole32(0xe28f1024), /* 4: add r1, pc, #36 */
|
||||
htole32(0xe59f201c), /* 8: ldr r2, [pc, #28] */
|
||||
htole32(0xe35200f4), /* c: cmp r2, #244 */
|
||||
htole32(0xc3a020f4), /* 10: movgt r2, #244 */
|
||||
/* <read_loop>: */
|
||||
htole32(0xe2522001), /* 14: subs r2, r2, #1 */
|
||||
htole32(0x412fff1e), /* 18: bxmi lr */
|
||||
htole32(0xe4903004), /* 1c: ldr r3, [r0], #4 */
|
||||
htole32(0xe4813004), /* 20: str r3, [r1], #4 */
|
||||
htole32(0xeafffffa), /* 24: b 14 <read_loop> */
|
||||
/* <fel_writel_n>: */
|
||||
htole32(0xe59f0020), /* 34: ldr r0, [pc, #32] */
|
||||
htole32(0xe28f1024), /* 38: add r1, pc, #36 */
|
||||
htole32(0xe59f201c), /* 3c: ldr r2, [pc, #28] */
|
||||
htole32(0xe35200f4), /* 40: cmp r2, #244 */
|
||||
htole32(0xc3a020f4), /* 44: movgt r2, #244 */
|
||||
/* <write_loop>: */
|
||||
htole32(0xe2522001), /* 48: subs r2, r2, #1 */
|
||||
htole32(0x412fff1e), /* 4c: bxmi lr */
|
||||
htole32(0xe4913004), /* 50: ldr r3, [r1], #4 */
|
||||
htole32(0xe4803004), /* 54: str r3, [r0], #4 */
|
||||
htole32(0xeafffffa), /* 58: b 48 <write_loop> */
|
||||
26
thunks/rmr-thunk.S
Normal file
26
thunks/rmr-thunk.S
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Request AArch32/AArch64 warm reset, using RVBAR and Reset Management Register
|
||||
* This is used on ARMv8 cores only, so force v7 code to allow dsb and isb.
|
||||
*/
|
||||
.arch armv7-a
|
||||
|
||||
rmr_request:
|
||||
ldr r0, 1f /* RVBAR register address */
|
||||
ldr r1, 2f /* desired entry point (reset vector) */
|
||||
str r1, [r0]
|
||||
dsb
|
||||
isb /* make sure we write the address */
|
||||
|
||||
ldr r1, 3f /* RMR mode: bit 1 = RR, bit 0 = AA64 */
|
||||
mrc p15, 0, r0, c12, c0, 2 /* read RMR */
|
||||
orr r0, r0, r1 /* request warm reset (according to rmr_mode) */
|
||||
mcr p15, 0, r0, c12, c0, 2 /* write RMR, trigger reset */
|
||||
|
||||
isb
|
||||
0:
|
||||
wfi
|
||||
b 0b /* loop */
|
||||
|
||||
1: .word 0 /* rvbar_reg */
|
||||
2: .word 0 /* entry_point */
|
||||
3: .word 0 /* rmr_mode (2 = AArch32, 3 = AArch64) */
|
||||
13
thunks/rmr-thunk.h
Normal file
13
thunks/rmr-thunk.h
Normal file
@ -0,0 +1,13 @@
|
||||
/* <rmr_request>: */
|
||||
htole32(0xe59f0028), /* 0: ldr r0, [pc, #40] */
|
||||
htole32(0xe59f1028), /* 4: ldr r1, [pc, #40] */
|
||||
htole32(0xe5801000), /* 8: str r1, [r0] */
|
||||
htole32(0xf57ff04f), /* c: dsb sy */
|
||||
htole32(0xf57ff06f), /* 10: isb sy */
|
||||
htole32(0xe59f101c), /* 14: ldr r1, [pc, #28] */
|
||||
htole32(0xee1c0f50), /* 18: mrc 15, 0, r0, cr12, cr0, {2} */
|
||||
htole32(0xe1800001), /* 1c: orr r0, r0, r1 */
|
||||
htole32(0xee0c0f50), /* 20: mcr 15, 0, r0, cr12, cr0, {2} */
|
||||
htole32(0xf57ff06f), /* 24: isb sy */
|
||||
htole32(0xe320f003), /* 28: wfi */
|
||||
htole32(0xeafffffd), /* 2c: b 28 <rmr_request+0x28> */
|
||||
72
thunks/sid_read_root.S
Normal file
72
thunks/sid_read_root.S
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ARM thunk code to read the SID root key using register-based access.
|
||||
*
|
||||
* This is necessary for certain SoCs (e.g. H3), as the values read via
|
||||
* memory mapping might not be consistent. For background information see
|
||||
* https://groups.google.com/forum/#!topic/linux-sunxi/ynyIP8c61Qs
|
||||
*/
|
||||
|
||||
SID_BASE .req r0
|
||||
sid_key_index .req r1
|
||||
|
||||
.set SID_PRCTL, 0x40 /* SID program/read control register */
|
||||
.set SID_PRKEY, 0x50 /* SID program key value register */
|
||||
.set SID_RDKEY, 0x60 /* SID read key value register */
|
||||
|
||||
.set SID_OP_LOCK, 0xAC /* Efuse operation lock value */
|
||||
.set SID_READ_START, (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */
|
||||
.set SID_PG_START, (1 << 0) /* bit 0 of SID_PRCTL, Software Program Start */
|
||||
|
||||
sid_read_root_key:
|
||||
ldr SID_BASE, sid_base
|
||||
ldr sid_key_index, offset
|
||||
adr r3, sid_result /* result pointer */
|
||||
sid_read_loop:
|
||||
mov r2, sid_key_index, lsl #16 /* PG_INDEX value */
|
||||
orr r2, #SID_OP_LOCK << 8 /* OP_LOCK to enable SID_READ_START */
|
||||
orr r2, #SID_READ_START
|
||||
str r2, [SID_BASE, #SID_PRCTL] /* write SID_PRCTL */
|
||||
sid_read_wait:
|
||||
ldr r2, [SID_BASE, #SID_PRCTL] /* read SID_PRCTL */
|
||||
tst r2, #SID_READ_START /* check if read operation completed */
|
||||
bne sid_read_wait /* loop while bit 1 still set */
|
||||
|
||||
ldr r2, [SID_BASE, #SID_RDKEY] /* read SID key value */
|
||||
str r2, [r3], #4 /* store SID value */
|
||||
|
||||
add sid_key_index, #4
|
||||
ldr r2, end
|
||||
cmp sid_key_index, r2
|
||||
blo sid_read_loop /* loop while (sid_key_index < 0x10) */
|
||||
|
||||
mov r2, #0
|
||||
str r2, [SID_BASE, #SID_PRCTL] /* clear SID_PRCTL */
|
||||
bx lr
|
||||
|
||||
sid_base: .word 0
|
||||
offset: .word 0
|
||||
end: .word 0
|
||||
sid_result: /* receives the values read from the SID registers */
|
||||
28
thunks/sid_read_root.h
Normal file
28
thunks/sid_read_root.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* <sid_read_root_key>: */
|
||||
htole32(0xe59f0044), /* 0: ldr r0, [pc, #68] */
|
||||
htole32(0xe59f1044), /* 4: ldr r1, [pc, #68] */
|
||||
htole32(0xe28f3048), /* 8: add r3, pc, #72 */
|
||||
/* <sid_read_loop>: */
|
||||
htole32(0xe1a02801), /* c: lsl r2, r1, #16 */
|
||||
htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */
|
||||
htole32(0xe3822002), /* 14: orr r2, r2, #2 */
|
||||
htole32(0xe5802040), /* 18: str r2, [r0, #64] */
|
||||
/* <sid_read_wait>: */
|
||||
htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */
|
||||
htole32(0xe3120002), /* 20: tst r2, #2 */
|
||||
htole32(0x1afffffc), /* 24: bne 1c <sid_read_wait> */
|
||||
htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */
|
||||
htole32(0xe4832004), /* 2c: str r2, [r3], #4 */
|
||||
htole32(0xe2811004), /* 30: add r1, r1, #4 */
|
||||
htole32(0xe59f2018), /* 34: ldr r2, [pc, #24] */
|
||||
htole32(0xe1510002), /* 38: cmp r1, r2 */
|
||||
htole32(0x3afffff2), /* 3c: bcc c <sid_read_loop> */
|
||||
htole32(0xe3a02000), /* 40: mov r2, #0 */
|
||||
htole32(0xe5802040), /* 44: str r2, [r0, #64] */
|
||||
htole32(0xe12fff1e), /* 48: bx lr */
|
||||
/* <sid_base>: */
|
||||
htole32(0x00000000), /* 4c: .word 0x00000000 */
|
||||
/* <offset>: */
|
||||
htole32(0x00000000), /* 50: .word 0x00000000 */
|
||||
/* <end>: */
|
||||
htole32(0x00000000), /* 54: .word 0x00000000 */
|
||||
712
uart0-helloworld-sdboot.c
Normal file
712
uart0-helloworld-sdboot.c
Normal file
@ -0,0 +1,712 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Partially based on the uart code from ar100-info
|
||||
*
|
||||
* (C) Copyright 2013 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Partially based on the sunxi gpio code from U-Boot
|
||||
*
|
||||
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
*
|
||||
* Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
|
||||
*
|
||||
* (C) Copyright 2007-2011
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
* Tom Cubie <tangliang@allwinnertech.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
typedef unsigned int u32;
|
||||
|
||||
#define set_wbit(addr, v) (*((volatile unsigned long *)(addr)) |= (unsigned long)(v))
|
||||
#define readl(addr) (*((volatile unsigned long *)(addr)))
|
||||
#define writel(v, addr) (*((volatile unsigned long *)(addr)) = (unsigned long)(v))
|
||||
|
||||
#define SUNXI_UART0_BASE 0x01C28000
|
||||
#define SUNXI_PIO_BASE 0x01C20800
|
||||
#define AW_CCM_BASE 0x01c20000
|
||||
#define AW_SRAMCTRL_BASE 0x01c00000
|
||||
|
||||
#define H6_UART0_BASE 0x05000000
|
||||
#define H6_PIO_BASE 0x0300B000
|
||||
#define H6_CCM_BASE 0x03001000
|
||||
#define H6_SRAMCTRL_BASE 0x03000000
|
||||
|
||||
#define R329_UART0_BASE 0x02500000
|
||||
#define R329_PIO_BASE 0x02000400
|
||||
#define R329_CCM_BASE 0x02001000
|
||||
|
||||
#define V853_PIO_BASE 0x02000000
|
||||
|
||||
#define SUNIV_UART0_BASE 0x01c25000
|
||||
/*****************************************************************************
|
||||
* GPIO code, borrowed from U-Boot *
|
||||
*****************************************************************************/
|
||||
|
||||
#define SUNXI_GPIO_A 0
|
||||
#define SUNXI_GPIO_B 1
|
||||
#define SUNXI_GPIO_C 2
|
||||
#define SUNXI_GPIO_D 3
|
||||
#define SUNXI_GPIO_E 4
|
||||
#define SUNXI_GPIO_F 5
|
||||
#define SUNXI_GPIO_G 6
|
||||
#define SUNXI_GPIO_H 7
|
||||
#define SUNXI_GPIO_I 8
|
||||
|
||||
#define GPIO_BANK(pin) ((pin) >> 5)
|
||||
#define GPIO_NUM(pin) ((pin) & 0x1F)
|
||||
|
||||
#define GPIO_CFG_BASE(bank) ((u32 *)(pio_base + (bank) * pio_bank_size))
|
||||
#define GPIO_CFG_INDEX(pin) (((pin) & 0x1F) >> 3)
|
||||
#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1F) & 0x7) << 2)
|
||||
|
||||
#define GPIO_PULL_BASE(bank) ((u32 *)(pio_base + (bank) * pio_bank_size + pio_pull_off))
|
||||
#define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
|
||||
#define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
|
||||
|
||||
#define GPIO_DAT_BASE(bank) ((u32 *)(pio_base + (bank) * pio_bank_size + pio_dat_off))
|
||||
|
||||
/* GPIO bank sizes */
|
||||
#define SUNXI_GPIO_A_NR (32)
|
||||
#define SUNXI_GPIO_B_NR (32)
|
||||
#define SUNXI_GPIO_C_NR (32)
|
||||
#define SUNXI_GPIO_D_NR (32)
|
||||
#define SUNXI_GPIO_E_NR (32)
|
||||
#define SUNXI_GPIO_F_NR (32)
|
||||
#define SUNXI_GPIO_G_NR (32)
|
||||
#define SUNXI_GPIO_H_NR (32)
|
||||
#define SUNXI_GPIO_I_NR (32)
|
||||
|
||||
#define SUNXI_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) + 0)
|
||||
|
||||
enum sunxi_gpio_number {
|
||||
SUNXI_GPIO_A_START = 0,
|
||||
SUNXI_GPIO_B_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_A),
|
||||
SUNXI_GPIO_C_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_B),
|
||||
SUNXI_GPIO_D_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_C),
|
||||
SUNXI_GPIO_E_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_D),
|
||||
SUNXI_GPIO_F_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_E),
|
||||
SUNXI_GPIO_G_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_F),
|
||||
SUNXI_GPIO_H_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_G),
|
||||
SUNXI_GPIO_I_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_H),
|
||||
};
|
||||
|
||||
/* SUNXI GPIO number definitions */
|
||||
#define SUNXI_GPA(_nr) (SUNXI_GPIO_A_START + (_nr))
|
||||
#define SUNXI_GPB(_nr) (SUNXI_GPIO_B_START + (_nr))
|
||||
#define SUNXI_GPC(_nr) (SUNXI_GPIO_C_START + (_nr))
|
||||
#define SUNXI_GPD(_nr) (SUNXI_GPIO_D_START + (_nr))
|
||||
#define SUNXI_GPE(_nr) (SUNXI_GPIO_E_START + (_nr))
|
||||
#define SUNXI_GPF(_nr) (SUNXI_GPIO_F_START + (_nr))
|
||||
#define SUNXI_GPG(_nr) (SUNXI_GPIO_G_START + (_nr))
|
||||
#define SUNXI_GPH(_nr) (SUNXI_GPIO_H_START + (_nr))
|
||||
#define SUNXI_GPI(_nr) (SUNXI_GPIO_I_START + (_nr))
|
||||
|
||||
/* GPIO pin function config */
|
||||
#define SUNXI_GPIO_INPUT (0)
|
||||
#define SUNXI_GPIO_OUTPUT (1)
|
||||
#define SUNIV_GPE_UART0 (5)
|
||||
#define SUN4I_GPB_UART0 (2)
|
||||
#define SUN5I_GPB_UART0 (2)
|
||||
#define SUN6I_GPH_UART0 (2)
|
||||
#define SUN8I_H3_GPA_UART0 (2)
|
||||
#define SUN8I_R528_GPE_UART0 (6)
|
||||
#define SUN8I_V3S_GPB_UART0 (3)
|
||||
#define SUN8I_V5_GPB_UART0 (2)
|
||||
#define SUN8I_V831_GPH_UART0 (5)
|
||||
#define SUN8I_V853_GPH_UART0 (5)
|
||||
#define SUN50I_H5_GPA_UART0 (2)
|
||||
#define SUN50I_H6_GPH_UART0 (2)
|
||||
#define SUN50I_H616_GPH_UART0 (2)
|
||||
#define SUN50I_R329_GPB_UART0 (2)
|
||||
#define SUN50I_A64_GPB_UART0 (4)
|
||||
#define SUNXI_GPF_UART0 (4)
|
||||
|
||||
/* GPIO pin pull-up/down config */
|
||||
#define SUNXI_GPIO_PULL_DISABLE (0)
|
||||
#define SUNXI_GPIO_PULL_UP (1)
|
||||
#define SUNXI_GPIO_PULL_DOWN (2)
|
||||
|
||||
static u32 pio_base;
|
||||
static u32 pio_bank_size, pio_dat_off, pio_pull_off;
|
||||
|
||||
int sunxi_gpio_set_cfgpin(u32 pin, u32 val)
|
||||
{
|
||||
u32 cfg;
|
||||
u32 bank = GPIO_BANK(pin);
|
||||
u32 index = GPIO_CFG_INDEX(pin);
|
||||
u32 offset = GPIO_CFG_OFFSET(pin);
|
||||
u32 *addr = GPIO_CFG_BASE(bank) + index;
|
||||
cfg = readl(addr);
|
||||
cfg &= ~(0xf << offset);
|
||||
cfg |= val << offset;
|
||||
writel(cfg, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sunxi_gpio_set_pull(u32 pin, u32 val)
|
||||
{
|
||||
u32 cfg;
|
||||
u32 bank = GPIO_BANK(pin);
|
||||
u32 index = GPIO_PULL_INDEX(pin);
|
||||
u32 offset = GPIO_PULL_OFFSET(pin);
|
||||
u32 *addr = GPIO_PULL_BASE(bank) + index;
|
||||
cfg = readl(addr);
|
||||
cfg &= ~(0x3 << offset);
|
||||
cfg |= val << offset;
|
||||
writel(cfg, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sunxi_gpio_output(u32 pin, u32 val)
|
||||
{
|
||||
u32 dat;
|
||||
u32 bank = GPIO_BANK(pin);
|
||||
u32 num = GPIO_NUM(pin);
|
||||
u32 *addr = GPIO_DAT_BASE(bank);
|
||||
dat = readl(addr);
|
||||
if(val)
|
||||
dat |= 1 << num;
|
||||
else
|
||||
dat &= ~(1 << num);
|
||||
writel(dat, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sunxi_gpio_input(u32 pin)
|
||||
{
|
||||
u32 dat;
|
||||
u32 bank = GPIO_BANK(pin);
|
||||
u32 num = GPIO_NUM(pin);
|
||||
u32 *addr = GPIO_DAT_BASE(bank);
|
||||
dat = readl(addr);
|
||||
dat >>= num;
|
||||
return (dat & 0x1);
|
||||
}
|
||||
|
||||
int gpio_direction_input(unsigned gpio)
|
||||
{
|
||||
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
|
||||
return sunxi_gpio_input(gpio);
|
||||
}
|
||||
|
||||
int gpio_direction_output(unsigned gpio, int value)
|
||||
{
|
||||
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
|
||||
return sunxi_gpio_output(gpio, value);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Nearly all the Allwinner SoCs are using the same VER_REG register for *
|
||||
* runtime SoC type identification. For additional details see: *
|
||||
* *
|
||||
* https://linux-sunxi.org/SRAM_Controller_Register_Guide *
|
||||
* *
|
||||
* Allwinner A80 is an oddball and has a non-standard address of the VER_REG *
|
||||
* *
|
||||
* Allwinner A10s and A13 are using the same SoC type id, but they can be *
|
||||
* differentiated using a certain part of the SID register. *
|
||||
* *
|
||||
* Allwinner H6 has its memory map totally reworked, but the SRAM controller *
|
||||
* remains similar; the base of it is moved to 0x03000000. *
|
||||
*****************************************************************************/
|
||||
|
||||
#define VER_REG (AW_SRAMCTRL_BASE + 0x24)
|
||||
#define H6_VER_REG (H6_SRAMCTRL_BASE + 0x24)
|
||||
#define SUN4I_SID_BASE 0x01C23800
|
||||
#define SUN8I_SID_BASE 0x01C14000
|
||||
|
||||
#define SID_PRCTL 0x40 /* SID program/read control register */
|
||||
#define SID_RDKEY 0x60 /* SID read key value register */
|
||||
|
||||
#define SID_OP_LOCK 0xAC /* Efuse operation lock value */
|
||||
#define SID_READ_START (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */
|
||||
|
||||
u32 sid_read_key(u32 sid_base, u32 offset)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = (offset & 0x1FF) << 16; /* PG_INDEX value */
|
||||
reg_val |= (SID_OP_LOCK << 8) | SID_READ_START; /* request read access */
|
||||
writel(reg_val, sid_base + SID_PRCTL);
|
||||
|
||||
while (readl(sid_base + SID_PRCTL) & SID_READ_START) ; /* wait while busy */
|
||||
|
||||
reg_val = readl(sid_base + SID_RDKEY); /* read SID key value */
|
||||
writel(0, sid_base + SID_PRCTL); /* clear SID_PRCTL (removing SID_OP_LOCK) */
|
||||
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
static u32 soc_id;
|
||||
|
||||
void soc_detection_init(void)
|
||||
{
|
||||
u32 midr;
|
||||
asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (midr));
|
||||
|
||||
if (((midr >> 4) & 0xFFF) == 0xC0F) {
|
||||
soc_id = 0x1639; /* ARM Cortex-A15, so likely Allwinner A80 */
|
||||
} else {
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* This register is GICD_IIDR on H6, but unmapped according to
|
||||
* other known SoCs' user manuals.
|
||||
*/
|
||||
reg = readl(0x03021008);
|
||||
|
||||
if ((reg & 0xfff) == 0x43b) /* Found GICv2 here, so it's a H6 */
|
||||
reg = H6_VER_REG;
|
||||
else
|
||||
reg = VER_REG;
|
||||
|
||||
set_wbit(reg, 1 << 15);
|
||||
soc_id = readl(reg) >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* Most SoCs can reliably be distinguished by simply checking their ID value */
|
||||
|
||||
#define soc_is_a10() (soc_id == 0x1623)
|
||||
#define soc_is_a20() (soc_id == 0x1651)
|
||||
#define soc_is_a31() (soc_id == 0x1633)
|
||||
#define soc_is_a80() (soc_id == 0x1639)
|
||||
#define soc_is_a64() (soc_id == 0x1689)
|
||||
#define soc_is_h5() (soc_id == 0x1718)
|
||||
#define soc_is_a63() (soc_id == 0x1719)
|
||||
#define soc_is_h6() (soc_id == 0x1728)
|
||||
#define soc_is_h616() (soc_id == 0x1823)
|
||||
#define soc_is_r329() (soc_id == 0x1851)
|
||||
#define soc_is_r40() (soc_id == 0x1701)
|
||||
#define soc_is_v3s() (soc_id == 0x1681)
|
||||
#define soc_is_v831() (soc_id == 0x1817)
|
||||
#define soc_is_v853() (soc_id == 0x1886)
|
||||
#define soc_is_r528() (soc_id == 0x1859)
|
||||
#define soc_is_v5() (soc_id == 0x1721)
|
||||
#define soc_is_suniv() (soc_id == 0x1663)
|
||||
|
||||
/* A10s and A13 share the same ID, so we need a little more effort on those */
|
||||
|
||||
int soc_is_a10s(void)
|
||||
{
|
||||
return soc_id == 0x1625 &&
|
||||
(readl(SUN4I_SID_BASE + 8) & 0xf000) == 0x7000;
|
||||
}
|
||||
|
||||
int soc_is_a13(void)
|
||||
{
|
||||
return soc_id == 0x1625 &&
|
||||
(readl(SUN4I_SID_BASE + 8) & 0xf000) != 0x7000;
|
||||
}
|
||||
|
||||
/* H2+ and H3 share the same ID, we can differentiate them by SID_RKEY0 */
|
||||
|
||||
int soc_is_h2_plus(void)
|
||||
{
|
||||
if (soc_id != 0x1680) return 0;
|
||||
|
||||
u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0);
|
||||
return (sid0 & 0xff) == 0x42 || (sid0 & 0xff) == 0x83;
|
||||
}
|
||||
|
||||
int soc_is_h3(void)
|
||||
{
|
||||
if (soc_id != 0x1680) return 0;
|
||||
|
||||
u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0);
|
||||
/*
|
||||
* Note: according to Allwinner sources, H3 is expected
|
||||
* to show up as 0x00, 0x81 or ("H3D") 0x58 here.
|
||||
*/
|
||||
return (sid0 & 0xff) != 0x42 && (sid0 & 0xff) != 0x83;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* UART is mostly the same on A10/A13/A20/A31/H3/A64, except that newer SoCs *
|
||||
* have changed the APB numbering scheme (A10/A13/A20 used to have APB0 and *
|
||||
* APB1 names, but newer SoCs just have renamed them into APB1 and APB2). *
|
||||
* The constants below are using the new APB numbering convention. *
|
||||
* Also the newer SoCs have introduced the APB2_RESET register, but writing *
|
||||
* to it effectively goes nowhere on older SoCs and is harmless. *
|
||||
*****************************************************************************/
|
||||
|
||||
#define CONFIG_CONS_INDEX 1
|
||||
#define APB2_CFG (AW_CCM_BASE + 0x058)
|
||||
#define APB1_GATE (AW_CCM_BASE + 0x068)
|
||||
#define APB2_GATE (AW_CCM_BASE + 0x06C)
|
||||
#define APB1_RESET (AW_CCM_BASE + 0x2D0)
|
||||
#define APB2_RESET (AW_CCM_BASE + 0x2D8)
|
||||
#define APB2_GATE_UART_SHIFT (16)
|
||||
#define APB1_GATE_UART_SHIFT 20
|
||||
#define APB2_RESET_UART_SHIFT (16)
|
||||
#define APB1_RESET_UART_SHIFT 20
|
||||
|
||||
#define H6_UART_GATE_RESET (H6_CCM_BASE + 0x90C)
|
||||
#define R329_UART_GATE_RESET (R329_CCM_BASE + 0x90C)
|
||||
#define H6_UART_GATE_SHIFT (0)
|
||||
#define H6_UART_RESET_SHIFT (16)
|
||||
|
||||
void clock_init_uart_legacy(void)
|
||||
{
|
||||
/* Open the clock gate for UART0 */
|
||||
set_wbit(APB2_GATE, 1 << (APB2_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
/* Deassert UART0 reset (only needed on A31/A64/H3) */
|
||||
set_wbit(APB2_RESET, 1 << (APB2_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
}
|
||||
|
||||
void clock_init_uart_suniv(void)
|
||||
{
|
||||
/* open the clock for uart */
|
||||
set_wbit(APB1_GATE,
|
||||
1U << (APB1_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
|
||||
/* deassert uart reset */
|
||||
set_wbit(APB1_RESET,
|
||||
1U << (APB1_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
}
|
||||
|
||||
void clock_init_uart_h6(void)
|
||||
{
|
||||
/* Open the clock gate for UART0 */
|
||||
set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_GATE_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
/* Deassert UART0 reset */
|
||||
set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_RESET_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
}
|
||||
|
||||
void clock_init_uart_r329(void)
|
||||
{
|
||||
/* Open the clock gate for UART0 */
|
||||
set_wbit(R329_UART_GATE_RESET, 1 << (H6_UART_GATE_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
/* Deassert UART0 reset */
|
||||
set_wbit(R329_UART_GATE_RESET, 1 << (H6_UART_RESET_SHIFT + CONFIG_CONS_INDEX - 1));
|
||||
}
|
||||
|
||||
void clock_init_uart(void)
|
||||
{
|
||||
if (soc_is_h6() || soc_is_v831() || soc_is_h616() || soc_is_v5() ||
|
||||
soc_is_a63())
|
||||
clock_init_uart_h6();
|
||||
else if (soc_is_r329() || soc_is_v853() || soc_is_r528())
|
||||
clock_init_uart_r329();
|
||||
else if (soc_is_suniv())
|
||||
clock_init_uart_suniv();
|
||||
else
|
||||
clock_init_uart_legacy();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* UART0 pins muxing is different for different SoC variants. *
|
||||
* Allwinner A13 is a bit special, because there are no dedicated UART0 pins *
|
||||
* and they are shared with MMC0. *
|
||||
*****************************************************************************/
|
||||
|
||||
void gpio_init(void)
|
||||
{
|
||||
if (soc_is_v853() || soc_is_r528()) {
|
||||
/* GPIO V2 */
|
||||
pio_bank_size = 0x30;
|
||||
pio_dat_off = 0x10;
|
||||
pio_pull_off = 0x24;
|
||||
} else {
|
||||
/* GPIO V1 */
|
||||
pio_bank_size = 0x24;
|
||||
pio_dat_off = 0x10;
|
||||
pio_pull_off = 0x1c;
|
||||
}
|
||||
|
||||
if (soc_is_a10() || soc_is_a20() || soc_is_r40()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(23), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_a10s()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(20), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_a13()) {
|
||||
/* Disable PB19/PB20 as UART0 to avoid conflict */
|
||||
gpio_direction_input(SUNXI_GPB(19));
|
||||
gpio_direction_input(SUNXI_GPB(20));
|
||||
/* Use SD breakout board to access UART0 on MMC0 pins */
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPF(4), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_a31()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(20), SUN6I_GPH_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(21), SUN6I_GPH_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPH(21), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_a64()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_A64_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_h3() || soc_is_h2_plus()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_h5()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN50I_H5_GPA_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN50I_H5_GPA_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_a63()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_A64_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_h6()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_h616()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_r329()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(4), SUN50I_R329_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(5), SUN50I_R329_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(5), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_v3s()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN8I_V3S_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V3S_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_v831()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(9), SUN8I_V831_GPH_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(10), SUN8I_V831_GPH_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPH(10), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_v853()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(9), SUN8I_V853_GPH_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPH(10), SUN8I_V853_GPH_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPH(10), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_r528()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPE(2), SUN8I_R528_GPE_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPE(3), SUN8I_R528_GPE_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPE(3), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_v5()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V5_GPB_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_V5_GPB_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
|
||||
} else if (soc_is_suniv()) {
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPE(0), SUNIV_GPE_UART0);
|
||||
sunxi_gpio_set_cfgpin(SUNXI_GPE(1), SUNIV_GPE_UART0);
|
||||
sunxi_gpio_set_pull(SUNXI_GPE(1), SUNXI_GPIO_PULL_UP);
|
||||
} else {
|
||||
/* Unknown SoC */
|
||||
while (1) {}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static u32 uart0_base;
|
||||
|
||||
#define UART0_RBR (uart0_base + 0x0) /* receive buffer register */
|
||||
#define UART0_THR (uart0_base + 0x0) /* transmit holding register */
|
||||
#define UART0_DLL (uart0_base + 0x0) /* divisor latch low register */
|
||||
|
||||
#define UART0_DLH (uart0_base + 0x4) /* divisor latch high register */
|
||||
#define UART0_IER (uart0_base + 0x4) /* interrupt enable reigster */
|
||||
|
||||
#define UART0_IIR (uart0_base + 0x8) /* interrupt identity register */
|
||||
#define UART0_FCR (uart0_base + 0x8) /* fifo control register */
|
||||
|
||||
#define UART0_LCR (uart0_base + 0xc) /* line control register */
|
||||
|
||||
#define UART0_LSR (uart0_base + 0x14) /* line status register */
|
||||
|
||||
#define BAUD_115200 13 /* 24 * 1000 * 1000 / 16 / 115200 */
|
||||
/* The BROM sets the CPU clock to 204MHz, AHB=CPU/2, APB=AHB/2 => 51 MHz */
|
||||
#define BAUD_115200_SUNIV 28 /* 51 * 1000 * 1000 / 16 / 115200 */
|
||||
#define NO_PARITY (0)
|
||||
#define ONE_STOP_BIT (0)
|
||||
#define DAT_LEN_8_BITS (3)
|
||||
#define LC_8_N_1 (NO_PARITY << 3 | ONE_STOP_BIT << 2 | DAT_LEN_8_BITS)
|
||||
|
||||
void uart0_init(void)
|
||||
{
|
||||
clock_init_uart();
|
||||
|
||||
/* select dll dlh */
|
||||
writel(0x80, UART0_LCR);
|
||||
/* set baudrate */
|
||||
writel(0, UART0_DLH);
|
||||
if (soc_is_suniv())
|
||||
writel(BAUD_115200_SUNIV, UART0_DLL);
|
||||
else
|
||||
writel(BAUD_115200, UART0_DLL);
|
||||
/* set line control */
|
||||
writel(LC_8_N_1, UART0_LCR);
|
||||
}
|
||||
|
||||
void uart0_putc(char c)
|
||||
{
|
||||
while (!(readl(UART0_LSR) & (1 << 6))) {}
|
||||
writel(c, UART0_THR);
|
||||
}
|
||||
|
||||
void uart0_puts(const char *s)
|
||||
{
|
||||
while (*s) {
|
||||
if (*s == '\n')
|
||||
uart0_putc('\r');
|
||||
uart0_putc(*s++);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* A workaround for https://patchwork.ozlabs.org/patch/622173 */
|
||||
void __attribute__((section(".start"))) __attribute__((naked)) start(void)
|
||||
{
|
||||
asm volatile("b main \n"
|
||||
".long 0xffffffff \n"
|
||||
".long 0xffffffff \n"
|
||||
".long 0xffffffff \n");
|
||||
}
|
||||
|
||||
enum { BOOT_DEVICE_UNK, BOOT_DEVICE_FEL, BOOT_DEVICE_MMC0, BOOT_DEVICE_SPI };
|
||||
|
||||
int get_boot_device(void)
|
||||
{
|
||||
u32 *spl_signature = (void *)0x4;
|
||||
if (soc_is_a64() || soc_is_a80() || soc_is_h5())
|
||||
spl_signature = (void *)0x10004;
|
||||
if (soc_is_h6() || soc_is_v831() || soc_is_h616() || soc_is_v853() ||
|
||||
soc_is_a63())
|
||||
spl_signature = (void *)0x20004;
|
||||
if (soc_is_r329())
|
||||
spl_signature = (void *)0x100004;
|
||||
|
||||
/* Check the eGON.BT0 magic in the SPL header */
|
||||
if (spl_signature[0] != 0x4E4F4765 || spl_signature[1] != 0x3054422E)
|
||||
return BOOT_DEVICE_FEL;
|
||||
|
||||
u32 boot_dev = spl_signature[9] & 0xFF; /* offset into SPL = 0x28 */
|
||||
if (boot_dev == 0)
|
||||
return BOOT_DEVICE_MMC0;
|
||||
if (boot_dev == 3)
|
||||
return BOOT_DEVICE_SPI;
|
||||
|
||||
return BOOT_DEVICE_UNK;
|
||||
}
|
||||
|
||||
void bases_init(void)
|
||||
{
|
||||
if (soc_is_h6() || soc_is_v831() || soc_is_h616() || soc_is_v5() ||
|
||||
soc_is_a63()) {
|
||||
pio_base = H6_PIO_BASE;
|
||||
uart0_base = H6_UART0_BASE;
|
||||
} else if (soc_is_r329()) {
|
||||
pio_base = R329_PIO_BASE;
|
||||
uart0_base = R329_UART0_BASE;
|
||||
} else if (soc_is_v853() || soc_is_r528()) {
|
||||
pio_base = V853_PIO_BASE;
|
||||
uart0_base = R329_UART0_BASE;
|
||||
} else if (soc_is_suniv()) {
|
||||
pio_base = SUNXI_PIO_BASE;
|
||||
uart0_base = SUNIV_UART0_BASE;
|
||||
} else {
|
||||
pio_base = SUNXI_PIO_BASE;
|
||||
uart0_base = SUNXI_UART0_BASE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
soc_detection_init();
|
||||
bases_init();
|
||||
gpio_init();
|
||||
uart0_init();
|
||||
|
||||
uart0_puts("\nHello from ");
|
||||
if (soc_is_a10())
|
||||
uart0_puts("Allwinner A10!\n");
|
||||
else if (soc_is_a10s())
|
||||
uart0_puts("Allwinner A10s!\n");
|
||||
else if (soc_is_a13())
|
||||
uart0_puts("Allwinner A13!\n");
|
||||
else if (soc_is_a20())
|
||||
uart0_puts("Allwinner A20!\n");
|
||||
else if (soc_is_a31())
|
||||
uart0_puts("Allwinner A31/A31s!\n");
|
||||
else if (soc_is_a64())
|
||||
uart0_puts("Allwinner A64!\n");
|
||||
else if (soc_is_h2_plus())
|
||||
uart0_puts("Allwinner H2+!\n");
|
||||
else if (soc_is_h3())
|
||||
uart0_puts("Allwinner H3!\n");
|
||||
else if (soc_is_h5())
|
||||
uart0_puts("Allwinner H5!\n");
|
||||
else if (soc_is_a63())
|
||||
uart0_puts("Allwinner A63!\n");
|
||||
else if (soc_is_h6())
|
||||
uart0_puts("Allwinner H6!\n");
|
||||
else if (soc_is_h616())
|
||||
uart0_puts("Allwinner H616!\n");
|
||||
else if (soc_is_r329())
|
||||
uart0_puts("Allwinner R329!\n");
|
||||
else if (soc_is_r40())
|
||||
uart0_puts("Allwinner R40!\n");
|
||||
else if (soc_is_v3s())
|
||||
uart0_puts("Allwinner V3s!\n");
|
||||
else if (soc_is_v831())
|
||||
uart0_puts("Allwinner V831!\n");
|
||||
else if (soc_is_v853())
|
||||
uart0_puts("Allwinner V853!\n");
|
||||
else if (soc_is_r528())
|
||||
uart0_puts("Allwinner R528/T113!\n");
|
||||
else if (soc_is_v5())
|
||||
uart0_puts("Allwinner V5!\n");
|
||||
else if (soc_is_suniv())
|
||||
uart0_puts("Allwinner F1C100s!\n");
|
||||
else
|
||||
uart0_puts("unknown Allwinner SoC!\n");
|
||||
|
||||
switch (get_boot_device()) {
|
||||
case BOOT_DEVICE_FEL:
|
||||
uart0_puts("Returning back to FEL.\n");
|
||||
return 0;
|
||||
case BOOT_DEVICE_MMC0:
|
||||
uart0_puts("Booted from MMC0, entering an infinite loop.\n");
|
||||
while (1) {}
|
||||
case BOOT_DEVICE_SPI:
|
||||
uart0_puts("Booted from SPI0, entering an infinite loop.\n");
|
||||
while (1) {}
|
||||
default:
|
||||
uart0_puts("Booted from unknown media, entering an infinite loop.\n");
|
||||
while (1) {}
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
29
uart0-helloworld-sdboot.lds
Normal file
29
uart0-helloworld-sdboot.lds
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0000;
|
||||
.start : { *(.start) }
|
||||
.text : { *(.text) }
|
||||
/DISCARD/ : { *(.dynstr*) }
|
||||
/DISCARD/ : { *(.dynamic*) }
|
||||
/DISCARD/ : { *(.plt*) }
|
||||
/DISCARD/ : { *(.interp*) }
|
||||
/DISCARD/ : { *(.gnu*) }
|
||||
/DISCARD/ : { *(.note*) }
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user